From: linjh Date: Fri, 11 Oct 2013 06:15:57 +0000 (+0800) Subject: androidcomputer: commit scaler code X-Git-Tag: firefly_0821_release~6574 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=985bb0a74d187856066c69ab20c4a0254bee6145;p=firefly-linux-kernel-4.4.55.git androidcomputer: commit scaler code [reference files] [modify files] arch/arm/configs/rk3188_ac_defconfig arch/arm/mach-rk3188/board-rk3188-ac.c drivers/misc/Kconfig drivers/misc/Makefile [new files] drivers/misc/scaler/Kconfig drivers/misc/scaler/Makefile drivers/misc/scaler/chips/Kconfig drivers/misc/scaler/chips/Makefile drivers/misc/scaler/chips/test.c drivers/misc/scaler/scaler-core.c drivers/misc/scaler/scaler-sysfs.c drivers/misc/scaler/scaler-vga.c include/linux/scaler-core.h --- diff --git a/arch/arm/configs/rk3188_ac_defconfig b/arch/arm/configs/rk3188_ac_defconfig index 9d1815dfc8db..ea407b8f1ef1 100644 --- a/arch/arm/configs/rk3188_ac_defconfig +++ b/arch/arm/configs/rk3188_ac_defconfig @@ -194,6 +194,8 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_MISC_DEVICES=y CONFIG_UID_STAT=y CONFIG_APANIC=y +CONFIG_SCALER_DEVICE=y +CONFIG_SCALER_TEST=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_SCSI_MULTI_LUN=y @@ -309,7 +311,7 @@ CONFIG_DUAL_LCDC_DUAL_DISP_IN_KERNEL=y CONFIG_LCDC_RK3188=y CONFIG_LCDC0_RK3188=y CONFIG_LCDC1_RK3188=y -CONFIG_LCD_DS1006H=y +CONFIG_LCD_E242868_1024X600=y CONFIG_RK_HDMI=y CONFIG_HDMI_CAT66121=y CONFIG_RK_HDMI_CTL_CODEC=y @@ -401,7 +403,6 @@ CONFIG_MMC_UNSAFE_RESUME=y CONFIG_MMC_EMBEDDED_SDIO=y CONFIG_MMC_PARANOID_SD_INIT=y CONFIG_SDMMC_RK29=y -CONFIG_USE_SDMMC0_FOR_WIFI_DEVELOP_BOARD=y # CONFIG_SDMMC1_RK29 is not set CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y diff --git a/arch/arm/mach-rk3188/board-rk3188-ac.c b/arch/arm/mach-rk3188/board-rk3188-ac.c index 57ef89fee3d0..999ce00d9bbb 100755 --- a/arch/arm/mach-rk3188/board-rk3188-ac.c +++ b/arch/arm/mach-rk3188/board-rk3188-ac.c @@ -75,6 +75,7 @@ #include "../mach-rk30/board-rk3168-ds1006h-camera.c" #include +#include static struct rk29_keys_button key_button[] = { { @@ -505,8 +506,8 @@ static int rk_fb_io_enable(void) return 0; } -#if defined(CONFIG_LCDC0_RK3188) -struct rk29fb_info lcdc0_screen_info = { +#if defined(CONFIG_LCDC1_RK3188) +struct rk29fb_info lcdc1_screen_info = { .prop = EXTEND, //extend display device .lcd_info = NULL, .set_screen_info = hdmi_init_lcdc, @@ -514,8 +515,8 @@ struct rk29fb_info lcdc0_screen_info = { }; #endif -#if defined(CONFIG_LCDC1_RK3188) -struct rk29fb_info lcdc1_screen_info = { +#if defined(CONFIG_LCDC0_RK3188) +struct rk29fb_info lcdc0_screen_info = { .prop = PRMRY, //primary display device .io_init = rk_fb_io_init, .io_disable = rk_fb_io_disable, @@ -1748,7 +1749,50 @@ static struct pmu_info tps65910_ldo_info[] = { #include "../mach-rk30/board-pmu-tps65910.c" #endif +#if defined(CONFIG_SCALER_TEST) +//the fisrt port is default +struct scaler_output_port tst_oports[] ={ + { + .led_gpio = INVALID_GPIO, + .type = SCALER_OUT_VGA, + }, +}; + +//the fisrt port is default +struct scaler_input_port tst_iports[] = { + { + //RK + .led_gpio = RK30_PIN0_PD4, + .type = SCALER_IN_VGA, + }, + { + //pc + .led_gpio = RK30_PIN0_PD5, + .type = SCALER_IN_VGA, + }, +}; + +struct scaler_platform_data test_data = { + .func_type = SCALER_FUNC_SWITCH, + + .iports = tst_iports, + .iport_size = ARRAY_SIZE(tst_iports), + .oports = tst_oports, + .oport_size = ARRAY_SIZE(tst_oports), + + .power_gpio = RK30_PIN2_PD7, +}; +#endif + static struct i2c_board_info __initdata i2c1_info[] = { +#if defined(CONFIG_SCALER_TEST) + { + .type = "aswitch", + .addr = 0x57, + .flags = 0, + .platform_data = &test_data, + }, +#endif #if defined (CONFIG_REGULATOR_ACT8846) { .type = "act8846", @@ -1871,6 +1915,13 @@ void rk30_pwm_resume_voltage_set(void) #ifdef CONFIG_I2C2_RK30 static struct i2c_board_info __initdata i2c2_info[] = { +#if defined(CONFIG_SCALER_DEVICE) + { + .type = "vga_i2c", + .addr = 0x50, + .flags = 0, + }, +#endif #if defined (CONFIG_CT36X_TS) { .type = CT36X_NAME, diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 3034dceb187d..4e90a6d47c23 100755 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -576,4 +576,5 @@ source "drivers/misc/carma/Kconfig" source "drivers/misc/bp/Kconfig" source "drivers/misc/rk2928_callpad_misc/Kconfig" source "drivers/misc/3g_module/Kconfig" +source "drivers/misc/scaler/Kconfig" endif # MISC_DEVICES diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 4614251b5d6f..ff2e22c7a33d 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -63,3 +63,4 @@ obj-$(CONFIG_RK29_SC8800) += sc8800.o obj-y += rk2928_callpad_misc/ obj-$(CONFIG_MODEM_SOUND) += modem_sound.o obj-$(CONFIG_TCC_BT_DEV) += tcc_bt_dev.o +obj-$(CONFIG_SCALER_DEVICE) += scaler/ diff --git a/drivers/misc/scaler/Kconfig b/drivers/misc/scaler/Kconfig new file mode 100755 index 000000000000..4cb0b09bf58a --- /dev/null +++ b/drivers/misc/scaler/Kconfig @@ -0,0 +1,11 @@ +# +# all auto modem control drivers configuration +# + +menuconfig SCALER_DEVICE + bool "Scaler Device Support" + default n + +if SCALER_DEVICE +source "drivers/misc/scaler/chips/Kconfig" +endif diff --git a/drivers/misc/scaler/Makefile b/drivers/misc/scaler/Makefile new file mode 100644 index 000000000000..9f813fde01fa --- /dev/null +++ b/drivers/misc/scaler/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the display core. +# + +obj-$(CONFIG_SCALER_DEVICE) += chips/ +obj-$(CONFIG_SCALER_DEVICE) += scaler-core.o +obj-$(CONFIG_SCALER_DEVICE) += scaler-sysfs.o +obj-$(CONFIG_SCALER_DEVICE) += scaler-vga.o diff --git a/drivers/misc/scaler/chips/Kconfig b/drivers/misc/scaler/chips/Kconfig new file mode 100644 index 000000000000..c6a828263f1a --- /dev/null +++ b/drivers/misc/scaler/chips/Kconfig @@ -0,0 +1,11 @@ +# +#config tv5735 support inport type and output type +# + +config SCALER_TV5735 + bool "Support tv5735 chip" + default n + +config SCALER_TEST + bool "test chip" + default n diff --git a/drivers/misc/scaler/chips/Makefile b/drivers/misc/scaler/chips/Makefile new file mode 100644 index 000000000000..3667cad99485 --- /dev/null +++ b/drivers/misc/scaler/chips/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_SCALER_TV5735) += tv5735.o +obj-$(CONFIG_SCALER_TEST) += test.o diff --git a/drivers/misc/scaler/chips/test.c b/drivers/misc/scaler/chips/test.c new file mode 100644 index 000000000000..19069b637db6 --- /dev/null +++ b/drivers/misc/scaler/chips/test.c @@ -0,0 +1,168 @@ +/* + Copyright (c) 2010 by Rockchip. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#include +#endif +#include + +struct scaler_chip_dev *chip = NULL; +extern char *scaler_input_name[]; + + +//enbale chip to process image +static void set_cur_inport(void) +{ + struct scaler_input_port *iport = NULL; + + list_for_each_entry(iport, &chip->iports, next) { + if (iport->id == chip->cur_inport_id) { + gpio_set_value(iport->led_gpio, GPIO_HIGH); + }else { + gpio_set_value(iport->led_gpio, GPIO_LOW); + } + } + + //pc + if (chip->cur_inport_id == 2) + gpio_set_value(RK30_PIN0_PB4, GPIO_LOW); + else + //rk + gpio_set_value(RK30_PIN0_PB4, GPIO_HIGH); +} + +static int parse_cmd(unsigned int cmd, unsigned long arg) +{ + printk("test: parse scaler cmd %u\n",cmd); + + switch (cmd) { + case SCALER_IOCTL_SET_CUR_INPUT: + set_cur_inport(); + break; + default: + break; + } + + return 0; +} + +static int test_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct scaler_platform_data *pdata = client->dev.platform_data; + + if (!pdata) { + printk("%s: client private data not define\n", __func__); + return -1; + } + + chip = alloc_scaler_chip(); + if (!chip) { + printk("%s: alloc scaler chip memory failed.\n", __func__); + return -1; + } + chip->client = client; + memcpy((void *)chip->name, (void *)client->name, (strlen(client->name) + 1)); + //implement parse cmd function + init_scaler_chip(chip, pdata); + chip->parse_cmd = parse_cmd; + + //vga 5v en + if (!gpio_request(RK30_PIN3_PD7, NULL)) + gpio_direction_output(RK30_PIN3_PD7, GPIO_HIGH); + else + printk("%s: request vga5ven power gpio failed\n", __func__); + msleep(20); + + //power + if (!gpio_request(RK30_PIN2_PD7, NULL)) + gpio_direction_output(RK30_PIN2_PD7, GPIO_HIGH); + else + printk("%s: request vga power gpio failed\n", __func__); + msleep(20); + + //vga sel + if (!gpio_request(RK30_PIN0_PB4, NULL)) + gpio_direction_output(RK30_PIN0_PB4, GPIO_HIGH); //rk output + else + printk("%s: request vga switch gpio failed\n", __func__); + msleep(20); + + // + if (!gpio_request(RK30_PIN1_PD6, NULL)) + gpio_direction_output(RK30_PIN1_PD6, GPIO_HIGH); + else + printk("%s: request XNN223_PWN gpio failed\n", __func__); + msleep(20); + + //register + register_scaler_chip(chip); + + return 0; +} + +static int test_i2c_remove(struct i2c_client *client) +{ + + printk("%s: \n", __func__); + unregister_scaler_chip(chip); + free_scaler_chip(chip); + chip = NULL; + return 0; +} + + +static const struct i2c_device_id test_i2c_id[] ={ + {"aswitch", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, test_i2c_id); + +static struct i2c_driver test_i2c_driver = { + .driver = { + .name = "aswitch" + }, + .probe = test_i2c_probe, + .remove = test_i2c_remove, + .id_table = test_i2c_id, +}; + +static int __init test_init(void) +{ + int ret = 0; + printk("%s: \n", __func__); + + ret = i2c_add_driver(&test_i2c_driver); + if(ret < 0){ + printk("%s, register i2c device, error\n", __func__); + return ret; + } + + return 0; +} + +static void __exit test_exit(void) +{ + printk("%s: \n", __func__); +} + +module_init(test_init); +module_exit(test_exit); + diff --git a/drivers/misc/scaler/scaler-core.c b/drivers/misc/scaler/scaler-core.c new file mode 100644 index 000000000000..57f509152305 --- /dev/null +++ b/drivers/misc/scaler/scaler-core.c @@ -0,0 +1,504 @@ +/* + * + * Copyright (C) 2012 Rockchip + * + *--------------------------------- + * version 1.0 2012-9-13 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SCALER_CORE_VERSION "v1.0.0" +#define SCALER_DEV_NAME "scaler-ctrl" + +static DEFINE_MUTEX(mutex_chips); +static DEFINE_MUTEX(mutex_ports); + +static struct scaler_device *sdev = NULL; +static unsigned short chip_ids = 0; //只增不减 +static unsigned int port_ids = 0; +extern int scaler_sysfs_create(struct scaler_device *sdev); +extern int scaler_sysfs_remove(struct scaler_device *sdev); + +static const char const *scaler_output_name[] = { + "output type", + "LVDS", + "VGA", + "RGB", + "HDMI", + "DP", +}; + +const char const *scaler_input_name[] = { + "input tpye", + "VGA", + "RGB", + "HDMI", + "DP", + "DVI", + "YPBPR", + "YCBCR", + "MYDP", + "IDP", +}; + +static const char const *scaler_func_name[] ={ + "This Scaler Function Type Is:", + "convertor", + "switch", + "full function", +}; + +static void scaler_led_on(int gpio) +{ + gpio_set_value(gpio, GPIO_HIGH); +} +static void scaler_led_off(int gpio) +{ + gpio_set_value(gpio, GPIO_LOW); +} + +static void default_start(void) +{ +} + +static void default_stop(void) +{ +} + +static int default_read(unsigned short reg, int bytes, void *desc) +{ + return 0; +} + +static int default_write(unsigned short reg, int bytes, void *desc) +{ + return 0; +} + +static int default_parse_cmd(unsigned int cmd, unsigned long arg) +{ + return 0; +} + +//alloc chip dev memory and init default value +struct scaler_chip_dev *alloc_scaler_chip(void) +{ + struct scaler_chip_dev *p = kzalloc(sizeof(struct scaler_chip_dev), GFP_KERNEL); + if (p) { + strcpy(p->name, "unknown"); + //init default operation function + p->start = default_start; + p->stop = default_stop; + p->read = default_read; + p->write = default_write; + p->parse_cmd = default_parse_cmd; + //init list head + INIT_LIST_HEAD(&p->next); + INIT_LIST_HEAD(&p->oports); + INIT_LIST_HEAD(&p->iports); + } + return p; +} + +int init_scaler_chip(struct scaler_chip_dev *chip, struct scaler_platform_data *pdata) +{ + int i; + struct scaler_output_port *oport = NULL; + struct scaler_input_port *iport = NULL; + + if (!chip || !pdata) { + printk("%s: chip or pdata is null.\n", __func__); + return -1; + } + + //set scaler function type + if (pdata->func_type > SCALER_FUNC_INVALID && + pdata->func_type < SCALER_FUNC_NUMS) { + chip->func_type = pdata->func_type; + }else { + printk("%s: not defined scaer function type\n", __func__); + chip->func_type = SCALER_FUNC_FULL; + } + printk("%s: %s %s\n", chip->name, scaler_func_name[0], scaler_func_name[chip->func_type]); + + //set scaler support input type + for (i = 0; i < pdata->iport_size; i++) { + iport = kzalloc(sizeof(struct scaler_input_port), GFP_KERNEL); + if (!iport) { + printk("%s: kzalloc input port memeory failed.\n", __func__); + return -1; + }else { + iport->max_hres = 1920; + iport->max_vres = 1080; + iport->freq = 60; + //input port id + mutex_lock(&mutex_ports); + iport->id = ++port_ids; + mutex_unlock(&mutex_ports); + //input port type + iport->type = pdata->iports[i].type; + //the first input port is default + if (!chip->cur_inport_id) { + chip->cur_in_type = iport->type; + chip->cur_inport_id = iport->id; + printk("%s: default %s %s\n", chip->name, scaler_input_name[0], scaler_input_name[iport->type]); + } + + //init and request input gpio of indicator lamp + if (pdata->iports[i].led_gpio > 0) { + iport->led_gpio = pdata->iports[i].led_gpio; + if (!gpio_request(iport->led_gpio, NULL)) { + gpio_direction_output(iport->led_gpio, GPIO_HIGH); + }else { + printk("%s: request %s gpio failed\n", chip->name, + scaler_input_name[pdata->iports[i].type], iport->id); + } + } + + //add input of chip + list_add_tail(&iport->next, &chip->iports); + printk("%s: support %s %s led_gpio %d\n", chip->name, scaler_input_name[0], + scaler_input_name[pdata->iports[i].type], iport->id, iport->led_gpio); + }//if (!iport) + }//for() + + //set scaler output type + for (i = 0; i < pdata->oport_size; i++) { + oport = kzalloc(sizeof(struct scaler_output_port), GFP_KERNEL); + if (!oport) { + printk("%s: kzalloc output port memeory failed.\n", __func__); + return -1; + }else { + oport->max_hres = 1920; + oport->max_vres = 1080; + oport->freq = 60; + //output port id + mutex_lock(&mutex_ports); + oport->id = ++port_ids; + mutex_unlock(&mutex_ports); + //output port type + oport->type = pdata->oports[i].type; + //the first output port is default + if (!chip->cur_outport_id) { + chip->cur_out_type = oport->type; + chip->cur_outport_id = oport->id; + printk("%s: default %s %s\n", chip->name, scaler_output_name[0], scaler_output_name[oport->type]); + } + + //init and request output gpio of indicator lamp + if (pdata->oports[i].led_gpio > 0) { + oport->led_gpio = pdata->oports[i].led_gpio; + if (!gpio_request(oport->led_gpio, NULL)) { + gpio_direction_output(oport->led_gpio, GPIO_HIGH); + }else { + printk("%s: request %s gpio failed\n", chip->name, scaler_output_name[pdata->oports[i].type], oport->id); + } + } + + list_add_tail(&oport->next, &chip->oports); + printk("%s: support %s %s led_gpio %d\n", chip->name, scaler_output_name[0], + scaler_output_name[pdata->oports[i].type], oport->id, oport->led_gpio); + }// if (!oport) + }//for() + + return 0; +} + +//free scaler chip memory +void free_scaler_chip(struct scaler_chip_dev *chip) +{ + struct scaler_output_port *out = NULL; + struct scaler_input_port *in = NULL; + if (chip) { + + list_for_each_entry(out, &chip->oports, next) { + kfree(out); + } + + list_for_each_entry(in, &chip->iports, next) { + if (in->led_gpio > 0) + gpio_free(in->led_gpio); + kfree(in); + } + + kfree(chip); + } +} + +//register chip to scaler core +int register_scaler_chip(struct scaler_chip_dev *chip) +{ + int res = -1; + struct scaler_input_port *in; + struct scaler_output_port *out; + + if (chip && sdev) { + res = 0; + + //start chip to process + chip->start(); + + //power on input indicator lamp + list_for_each_entry(in, &chip->iports, next){ + if (in->id == chip->cur_inport_id) + scaler_led_on(in->led_gpio); + else + scaler_led_off(in->led_gpio); + } + + //power on output indicator lamp + list_for_each_entry(out, &chip->oports, next){ + if (out->id == chip->cur_outport_id) + scaler_led_on(out->led_gpio); + else + scaler_led_off(out->led_gpio); + } + //add chip to scaler core + mutex_lock(&mutex_chips); + chip->id = ++chip_ids; //chip id only grow + list_add_tail(&chip->next, &sdev->chips); + mutex_unlock(&mutex_chips); + printk("%s: register scaler chip %s success.\n", __func__, chip->name); + } + + return res; +} + +//unregister chip to scaler core +int unregister_scaler_chip(struct scaler_chip_dev *chip) +{ + int res = -1; + struct scaler_input_port *in; + struct scaler_output_port *out; + + if (chip && sdev) { + res = 0; + + chip->stop(); + + //power off input indicator lamp + list_for_each_entry(in, &chip->iports, next){ + if (in->id == chip->cur_inport_id) + scaler_led_off(in->led_gpio); + } + + //power off output indicator lamp + list_for_each_entry(out, &chip->oports, next){ + if (out->id == chip->cur_outport_id) + scaler_led_off(out->led_gpio); + } + + //del chip from scaler core + mutex_lock(&mutex_chips); + list_del(&chip->next); + mutex_unlock(&mutex_chips); + printk("%s: unregister scaler chip %s success.\n", __func__, chip->name); + } + + return res; +} + +/*** cdev operate ***/ +static int scaler_file_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +static int scaler_file_close(struct inode *inode, struct file *filp) +{ + return 0; +} + +static ssize_t scaler_file_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) +{ + return 0; +} + +static ssize_t scaler_file_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos) +{ + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) +static long scaler_file_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#else +static int scaler_file_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ + int iport_id; + struct scaler_chip_dev *chip = NULL; + struct scaler_input_port *iport = NULL; + + + switch(cmd) { + case 0: + printk("get cur input cmd %#x\n", SCALER_IOCTL_GET_CUR_INPUT); + printk("set cur input cmd %#x\n", SCALER_IOCTL_SET_CUR_INPUT); + break; + case SCALER_IOCTL_SET_CUR_INPUT: + //choose input channel; + copy_from_user(&iport_id, arg, sizeof(int)); + + list_for_each_entry(chip, &sdev->chips, next) { + if (chip->cur_inport_id != iport_id) { + list_for_each_entry(iport, &chip->iports, next) {//if iport belong to this chip + if (iport->id == iport_id) { + chip->cur_inport_id = iport_id; + chip->parse_cmd(cmd, arg); + break; + } + }//list iports + } + }//list chips + + break; + case SCALER_IOCTL_GET_CUR_INPUT: + list_for_each_entry(chip, &sdev->chips, next) { + iport_id = chip->cur_inport_id; + }//list chips + copy_to_user(arg, &iport_id, sizeof(int)); + printk("current input port id %d\n", iport_id); + break; + default: + //default_parse_cmd(cmd, arg); + break; + } + + + return 0; +} + +struct file_operations scaler_fops = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) + .unlocked_ioctl =scaler_file_ioctl, +#else + .ioctl =scaler_file_ioctl, //定义所有对scaler操作的cmd +#endif + .read =scaler_file_read, + .write = scaler_file_write, + .open = scaler_file_open, + .release = scaler_file_close, +}; + +//注册一个scaler的字符设备 供别的设备操作scaler +static int scaler_register_chrdev(void) +{ + int ret = 0; + + ret = alloc_chrdev_region(&sdev->devno, 0, 1, SCALER_DEV_NAME); + if(ret != 0){ + printk("%s, can't allocate chrdev devno.\n", __func__); + return ret; + }else { + printk("%s: Scaler chrdev devno(%d,%d).\n", __func__, MAJOR(sdev->devno), MINOR(sdev->devno)); + } + + // initialize character device driver + sdev->cdev = cdev_alloc(); + if (!sdev->cdev) { + printk("%s: cdev alloc failed.\n", __func__); + goto err1; + } + + cdev_init(sdev->cdev, &scaler_fops); + sdev->cdev->owner = THIS_MODULE; + ret = cdev_add(sdev->cdev, sdev->devno, 1); + if(ret < 0){ + printk("%s, add character device error, ret %d\n", __func__, ret); + goto err2; + } + + sdev->class = class_create(THIS_MODULE, "scaler"); + if(!sdev->class){ + printk("%s, create class failed\n", __func__); + goto err3; + } + + sdev->dev = device_create(sdev->class, NULL, sdev->devno, sdev, SCALER_DEV_NAME); + if (!sdev->dev) { + printk("%s: create device failed\n", __func__); + goto err4; + } + + + return 0; + +err4: + class_destroy(sdev->class); +err3: + cdev_del(sdev->cdev); +err2: + kfree(sdev->cdev); +err1: + unregister_chrdev_region(sdev->devno, 1); + + return -1; +} + +static void scaler_unregister_chrdev(void) +{ + cdev_del(sdev->cdev); + unregister_chrdev_region(sdev->devno, 1); + kfree(sdev->cdev); + device_destroy(sdev->class, sdev->devno); + class_destroy(sdev->class); +} + +static int __init rk_scaler_init(void) +{ + printk("%s: SCALER CORE VERSION: %s\n", __func__, SCALER_CORE_VERSION); + + sdev = kzalloc(sizeof(struct scaler_device), GFP_KERNEL); + if (!sdev) { + printk("%s: malloc scaler devices failed.\n", __func__); + return -1; + }else { + INIT_LIST_HEAD(&sdev->chips); + } + + if (scaler_register_chrdev() < 0) { + printk("%s: scaler register chrdev failed.\n", __func__); + goto err1; + } + + if (scaler_sysfs_create(sdev) < 0) { + printk("%s: scaler sysfs create faild.\n", __func__); + goto err2; + } + + return 0; +err2: + scaler_unregister_chrdev(); +err1: + kfree(sdev); + + return -1; +} + +static void __exit rk_scaler_exit(void) +{ + printk("%s\n", __func__); + scaler_sysfs_remove(sdev); + scaler_unregister_chrdev(); + kfree(sdev); +} + +subsys_initcall(rk_scaler_init); +module_exit(rk_scaler_exit); + +MODULE_AUTHOR("linjh "); +MODULE_DESCRIPTION("RK Scaler Device Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/scaler/scaler-sysfs.c b/drivers/misc/scaler/scaler-sysfs.c new file mode 100644 index 000000000000..a84701fe5e70 --- /dev/null +++ b/drivers/misc/scaler/scaler-sysfs.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include +extern const char const *scaler_input_name[]; +static ssize_t scaler_iport_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int iports = 0; + struct scaler_chip_dev *chip = NULL; + struct scaler_input_port *in = NULL; + struct scaler_device *sdev = dev_get_drvdata(dev); + + list_for_each_entry(chip, &sdev->chips, next) { + + list_for_each_entry(in, &chip->iports, next) { + iports++; + printk("id = %d type = %s gpio = %d\n", in->id, + scaler_input_name[in->type], in->led_gpio); + } + } + + return sprintf(buf, "%d\n", iports); +} + +static ssize_t scaler_iport_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return 0; +} +static DEVICE_ATTR(iports, 0664, scaler_iport_show, NULL); + +static ssize_t scaler_cur_iport_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct scaler_chip_dev *chip = NULL; + struct scaler_input_port *in = NULL; + struct scaler_device *sdev = dev_get_drvdata(dev); + + list_for_each_entry(chip, &sdev->chips, next) { + + printk("id = %d type = %s\n", chip->cur_inport_id, + scaler_input_name[chip->cur_in_type]); + } + return 0; +} +static DEVICE_ATTR(cur_iport, 0664, scaler_cur_iport_show, NULL); + +static ssize_t scaler_oport_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + printk("%s: scaler sysfs test.\n", __func__); + return 0; +} + +static ssize_t scaler_oport_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return 0; +} + +static DEVICE_ATTR(oports, 0664, scaler_oport_show, NULL); + + +static struct attribute *scaler_attributes[] = { + &dev_attr_iports.attr, + &dev_attr_cur_iport.attr, + &dev_attr_oports.attr, + NULL +}; + +static mode_t scaler_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + mode_t mode = attr->mode; + + return mode; +} + +static const struct attribute_group scaler_attr_group = { + .is_visible = scaler_attr_is_visible, + .attrs = scaler_attributes, +}; + + +int scaler_sysfs_create(struct scaler_device *sdev) +{ + int err; + + //sdev->kobj = kobject_create_and_add("attr", &sdev->dev->kobj); + //err = sysfs_create_group(sdev->kobj, &scaler_attr_group); + err = sysfs_create_group(&sdev->dev->kobj, &scaler_attr_group); + + return err; +} + +int scaler_sysfs_remove(struct scaler_device *sdev) +{ + sysfs_remove_group(&sdev->dev->kobj, &scaler_attr_group); + //dev_set_drvdata(sdev->dev, NULL); + //kobject_put(sdev->kobj); + return 0; +} diff --git a/drivers/misc/scaler/scaler-vga.c b/drivers/misc/scaler/scaler-vga.c new file mode 100644 index 000000000000..61c147cdfea4 --- /dev/null +++ b/drivers/misc/scaler/scaler-vga.c @@ -0,0 +1,480 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../../video/edid.h" + +#define DDC_I2C_BUS 1 +#define DDC_ADDR 0x50 +#define DDC_I2C_RATE 100*1000 + +static const struct fb_videomode rk29_mode[] = { + //name refresh xres yres pixclock h_bp h_fp v_bp v_fp h_pw v_pw polariry PorI flag(used for vic) +#if defined(CONFIG_CLK_RK30_BOX) +{ "1024x768p@60Hz", 60, 1024, 768, KHZ2PICOS(65000), 160, 24, 29, 3, 136, 6, 0, 0, 0 }, +{ "1280x720p@60Hz", 60, 1280, 720, KHZ2PICOS(74250), 220, 110, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0 }, +{ "1280x1024p@60Hz", 60, 1280, 1024, KHZ2PICOS(108000), 248, 48, 38, 1, 112, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0 }, +{ "1366x768p@60Hz", 60, 1366, 768, KHZ2PICOS(85500), 213, 70, 24, 3, 143, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0 }, +{ "1440x900p@60Hz", 60, 1440, 900, KHZ2PICOS(106500), 232, 80, 25, 3, 152, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0 }, +{ "1680x1050p@60Hz", 60, 1680, 1050, KHZ2PICOS(146250), 280, 104, 30, 3, 176, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0 }, +{ "1920x1080p@60Hz", 60, 1920, 1080, KHZ2PICOS(148500), 148, 88, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0 }, +#else +//{ "640x480p@60Hz", 60, 640, 480, KHZ2PICOS(25175), 48, 16, 33, 10, 96, 2, 0, 0, 1 }, +{ "720x480p@60Hz", 60, 720, 480, KHZ2PICOS(27000), 60, 16, 30, 9, 62, 6, 0, 0, 2 }, +{ "720x576p@50Hz", 50, 720, 576, KHZ2PICOS(27000), 68, 12, 39, 5, 64, 5, 0, 0, 17 }, +{ "1280x720p@50Hz", 50, 1280, 720, KHZ2PICOS(74250), 220, 440, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 19 }, +{ "1280x720p@60Hz", 60, 1280, 720, KHZ2PICOS(74250), 220, 110, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 4 }, +{ "1920x1080p@50Hz", 50, 1920, 1080, KHZ2PICOS(148500), 148, 528, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 31 }, +{ "1920x1080p@60Hz", 60, 1920, 1080, KHZ2PICOS(148500), 148, 88, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 16 }, +#endif +}; + +struct rk29_monspecs { + struct i2c_client *i2c_client; + struct rk_display_device *ddev; + int io_enable_pin; + int video_source; + int property; + struct fb_monspecs monspecs; + struct list_head modelist; + struct fb_videomode *mode; + int enable; +}; + +static struct rk29_monspecs rk29_monspec; + +#ifdef CONFIG_ARCH_RK29 +extern int FB_Switch_Screen( struct rk29fb_screen *screen, u32 enable ); +#else +static int FB_Switch_Screen( struct rk29fb_screen *screen, u32 enable ) +{ + return rk_fb_switch_screen(screen, enable , rk29_monspec.video_source); +} +#endif + +static unsigned char *rk29fb_ddc_read(struct i2c_client *client) +{ + unsigned char *buf = kmalloc(EDID_LENGTH, GFP_KERNEL); + int rc; + + if (!buf) { + dev_warn(&client->dev, "unable to allocate memory for EDID " + "block.\n"); + return NULL; + } + // Check ddc i2c communication is available or not. + memset(buf, 0, EDID_LENGTH); + rc = i2c_master_reg8_recv(client, 0, buf, 6, DDC_I2C_RATE); + if(rc == 6) + { + // Read EDID. + memset(buf, 0, EDID_LENGTH); + rc = i2c_master_reg8_recv(client, 0, buf, EDID_LENGTH, DDC_I2C_RATE); + if(rc == EDID_LENGTH) + return buf; + } + + dev_warn(&client->dev, "unable to read EDID block.\n"); + kfree(buf); + return NULL; +} + +static struct fb_videomode *rk29fb_set_default_modelist(void) +{ + int i; + struct fb_videomode *mode = NULL; + struct list_head *modelist = &rk29_monspec.modelist; + + fb_destroy_modelist(modelist); + for(i = 0; i < ARRAY_SIZE(rk29_mode); i++) + { + mode = (struct fb_videomode *)&rk29_mode[i]; + //display_add_videomode(mode, modelist); + fb_add_videomode(mode, modelist); + } + rk29_monspec.mode = (struct fb_videomode *)&rk29_mode[3]; + return rk29_monspec.mode; +} + +/* + * Find monitor prefered video mode. If not find, + * set first mode as default mode. + */ +static struct fb_videomode *rk29fb_find_default_mode(void) +{ + struct fb_monspecs *specs = &rk29_monspec.monspecs; + struct fb_videomode *modedb = NULL; + int i, pixclock; + + if(specs->modedb_len) { +#if 1 + /* Get max resolution timing */ + modedb = &specs->modedb[0]; + for (i = 0; i < specs->modedb_len; i++) { + if(specs->modedb[i].xres > modedb->xres) + modedb = &specs->modedb[i]; + else if( (specs->modedb[i].xres == modedb->xres) && (specs->modedb[i].yres > modedb->yres) ) + modedb = &specs->modedb[i]; + } + // For some monitor, the max pixclock read from EDID is smaller + // than the clock of max resolution mode supported. We fix it. + pixclock = PICOS2KHZ(modedb->pixclock); + pixclock /= 250; + pixclock *= 250; + pixclock *= 1000; + if(pixclock == 148250000) + pixclock = 148500000; + if(pixclock > specs->dclkmax) + specs->dclkmax = pixclock; +#else + /* get preferred timing */ + if (specs->misc & FB_MISC_1ST_DETAIL) { + + for (i = 0; i < specs->modedb_len; i++) { + if (specs->modedb[i].flag & FB_MODE_IS_FIRST) { + modedb = &specs->modedb[i]; + break; + } + } + } else { + /* otherwise, get first mode in database */ + modedb = &specs->modedb[0]; + } +#endif + } + else + modedb = rk29fb_set_default_modelist(); + return modedb; +} + +/* + * Check mode 1920x1080p@60Hz is in modedb or not. + * If exist, set it as output moe. + * If not exist, try mode 1280x720p@60Hz. + * If both mode not exist, try 720x480p@60Hz. + */ +static int rk29fb_check_mode(void) +{ + struct fb_monspecs *specs = &rk29_monspec.monspecs; + struct list_head *modelist = &rk29_monspec.modelist; + struct fb_videomode *modedb = NULL, *mode = NULL; + unsigned int pixclock; + + fb_destroy_modelist(modelist); + modedb = rk29fb_find_default_mode(); + + if(modedb) + { + int i; + + for(i = 0; i < ARRAY_SIZE(rk29_mode); i++) + { + pixclock = PICOS2KHZ(rk29_mode[i].pixclock); + pixclock /= 250; + pixclock *= 250; + pixclock *= 1000; + if( (pixclock <= specs->dclkmax) && + (rk29_mode[i].xres <= modedb->xres) && + (rk29_mode[i].yres <= modedb->yres) && + (rk29_mode[i].refresh <= specs->vfmax) && + (rk29_mode[i].refresh >= specs->vfmin) + ) + { + mode = (struct fb_videomode *)&rk29_mode[i]; + //display_add_videomode(mode, modelist); + fb_add_videomode(mode, modelist); + } + } + } + + return 0; +} + +/* + * Probe monitor information using E-EDID. + */ +static int rk29fb_probe_screens(struct i2c_client *client) +{ + struct fb_monspecs *spec = &rk29_monspec.monspecs; + struct list_head *modelist = &rk29_monspec.modelist; + u8 *edid; + struct fb_videomode *defaultmode, *mode; + + if (client) + edid = rk29fb_ddc_read(client); + else + edid = NULL; + + fb_destroy_modelist(modelist); + INIT_LIST_HEAD(modelist); + if(spec->modedb) + kfree(spec->modedb); + memset(spec, 0, sizeof(struct fb_monspecs)); + if(edid) + { + fb_edid_to_monspecs(edid, spec); + kfree(edid); + rk29fb_check_mode(); + defaultmode = rk29fb_find_default_mode(); + if(defaultmode) + mode = (struct fb_videomode *)fb_find_nearest_mode(defaultmode, &rk29_monspec.modelist); + else + mode = (struct fb_videomode *)&rk29_mode[3]; + rk29_monspec.mode = mode; + return 0; + } + else + { + rk29fb_set_default_modelist(); + return 1; + } +} + +static int rk29_mode2screen(struct fb_videomode *modedb, struct rk29fb_screen *screen) +{ + if(modedb == NULL || screen == NULL) + return -1; + + memset(screen, 0, sizeof(struct rk29fb_screen)); + /* screen type & face */ + screen->type = SCREEN_HDMI; + screen->face = OUT_P888; + + /* Screen size */ + screen->x_res = modedb->xres; + screen->y_res = modedb->yres; +// screen->xpos = 0; +// screen->ypos = 0; + /* Timing */ + screen->pixclock = PICOS2KHZ(modedb->pixclock); + screen->pixclock /= 250; + screen->pixclock *= 250; + screen->pixclock *= 1000; + printk("pixclock is %d\n", screen->pixclock); + screen->lcdc_aclk = 500000000; + screen->left_margin = modedb->left_margin; + screen->right_margin = modedb->right_margin; + screen->hsync_len = modedb->hsync_len; + screen->upper_margin = modedb->upper_margin; + screen->lower_margin = modedb->lower_margin; + screen->vsync_len = modedb->vsync_len; + + /* Pin polarity */ + if(FB_SYNC_HOR_HIGH_ACT & modedb->sync) + screen->pin_hsync = 1; + else + screen->pin_hsync = 0; + if(FB_SYNC_VERT_HIGH_ACT & modedb->sync) + screen->pin_vsync = 1; + else + screen->pin_vsync = 0; + screen->pin_den = 0; + screen->pin_dclk = 1; + + /* Swap rule */ + screen->swap_rb = 0; + screen->swap_rg = 0; + screen->swap_gb = 0; + screen->swap_delta = 0; + screen->swap_dumy = 0; + + /* Operation function*/ + screen->init = NULL; + screen->standby = NULL; + return 0; +} + +static int rk29_set_enable(struct rk_display_device *device, int enable) +{ + struct rk29_monspecs *rk29_monspec = device->priv_data; + printk("[%s] set enable %d\n", __FUNCTION__, enable); + if(enable != rk29_monspec->enable) + { + if(rk29_monspec->io_enable_pin != INVALID_GPIO) { + gpio_set_value(rk29_monspec->io_enable_pin, enable?GPIO_HIGH:GPIO_LOW); + } + rk29_monspec->enable = enable; + } + + return 0; +} + +static int rk29_get_enable(struct rk_display_device *device) +{ + struct rk29_monspecs *rk29_monspec = device->priv_data; + return rk29_monspec->enable; +} + +static int rk29_get_status(struct rk_display_device *device) +{ + return (rk29fb_probe_screens(rk29_monspec.i2c_client))? 0:1; +} + +static int rk29_get_modelist(struct rk_display_device *device, struct list_head **modelist) +{ + *modelist = &rk29_monspec.modelist; + return 0; +} + +static int rk29_get_mode(struct rk_display_device *device, struct fb_videomode *mode) +{ + if(rk29_monspec.mode) + { + memcpy(mode, rk29_monspec.mode, sizeof(struct fb_videomode)); + return 0; + } + else + return -1; +} + +static int rk29_set_mode(struct rk_display_device *device, struct fb_videomode *mode) +{ + int i; + for(i = 0; i < ARRAY_SIZE(rk29_mode); i++) + { + if(fb_mode_is_equal(&rk29_mode[i], mode)) + { + struct rk29fb_screen screen; + rk29_mode2screen(mode, &screen); + FB_Switch_Screen(&screen, 1); + rk29_monspec.mode = mode; + return 0; + } + } + return -1; +} +struct rk_display_ops rk29_display_ops = { + .setenable = rk29_set_enable, + .getenable = rk29_get_enable, + .getstatus = rk29_get_status, + .getmodelist = rk29_get_modelist, + .setmode = rk29_set_mode, + .getmode = rk29_get_mode, +}; + +static int rk29_display_probe(struct rk_display_device *device, void *devdata) +{ + printk("%s: >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n", __func__); + device->owner = THIS_MODULE; + strcpy(device->type, "VGA"); + device->name = "vga"; + //device->property = rk29_monspec.property; + device->priority = DISPLAY_PRIORITY_VGA; + device->priv_data = devdata; + device->ops = &rk29_display_ops; + + return 1; +} + +static struct rk_display_driver display_rk29 = { + .probe = rk29_display_probe, +}; + +static int vga_i2c_probe(struct i2c_client *client,const struct i2c_device_id *id) +{ + int ret; + //struct rkdisplay_platform_data *vga_data; + printk("%s: >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n", __func__); + + memset(&rk29_monspec, 0, sizeof(struct rk29_monspecs)); + rk29_monspec.i2c_client = client; + rk29_monspec.enable = 0; + + /*if(client->dev.platform_data) { + vga_data = client->dev.platform_data; + rk29_monspec.io_enable_pin = vga_data->io_switch_pin; + rk29_monspec.video_source = vga_data->video_source; + rk29_monspec.property = vga_data->property; + } + else*/ { + rk29_monspec.io_enable_pin = INVALID_GPIO; + rk29_monspec.video_source = 0; + rk29_monspec.property = 0; + } + if(rk29_monspec.io_enable_pin != INVALID_GPIO) + { + ret = gpio_request(rk29_monspec.io_enable_pin, NULL); + if(ret != 0) + { + gpio_free(rk29_monspec.io_enable_pin); + printk(">>>>>> vag enable gpio_request err \n "); + return -1; + } + gpio_direction_output(rk29_monspec.io_enable_pin, GPIO_LOW); + } + INIT_LIST_HEAD(&rk29_monspec.modelist); + rk29_monspec.ddev = rk_display_device_register(&display_rk29, &client->dev, &rk29_monspec); + if(rk29_monspec.ddev == NULL) + { + printk("[%s] registor display error\n", __FUNCTION__); + return -1; + } + rk_display_device_enable(rk29_monspec.ddev); + //if(rk29_monspec.enable) + if(1) + { + struct fb_videomode *defaultmode, *mode; + defaultmode = rk29fb_find_default_mode(); + if(defaultmode) + mode = (struct fb_videomode *)fb_find_nearest_mode(defaultmode, &rk29_monspec.modelist); + else + mode = (struct fb_videomode *)&rk29_mode[0]; +printk("%s: xres,yres(%d, %d)\n=============================\n\n\n", __func__, mode->xres, mode->yres); + if(mode) + { + struct rk29fb_screen screen; + rk29_mode2screen(mode, &screen); + printk("%s: lcdc id = %d video_source = %d\n", __func__, screen.lcdc_id, rk29_monspec.video_source); + FB_Switch_Screen(&screen, 1); + rk29_monspec.mode = mode; + } + } + return 0; +} + +static int __devexit vga_i2c_remove(struct i2c_client *client) +{ + struct fb_monspecs *spec = &rk29_monspec.monspecs; + struct list_head *modelist = &rk29_monspec.modelist; + fb_destroy_modelist(modelist); + if(spec->modedb) + kfree(spec->modedb); + rk_display_device_unregister(rk29_monspec.ddev); + return 0; +} + +static const struct i2c_device_id vga_id[] = { + { "vga_i2c", 0 }, + { } +}; + +static struct i2c_driver vga_i2c_driver = { + .driver = { + .name = "vga_i2c", + .owner = THIS_MODULE, + }, + .probe = &vga_i2c_probe, + .remove = &vga_i2c_remove, + .id_table = vga_id, +}; + +static int __init rk29_vga_init(void) +{ + printk("%s: >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n", __func__); + return i2c_add_driver(&vga_i2c_driver); +} + +static void __exit rk29_vga_exit(void) +{ + i2c_del_driver(&vga_i2c_driver); +} + +module_init(rk29_vga_init); +module_exit(rk29_vga_exit); diff --git a/include/linux/scaler-core.h b/include/linux/scaler-core.h new file mode 100644 index 000000000000..cc9d36d42776 --- /dev/null +++ b/include/linux/scaler-core.h @@ -0,0 +1,164 @@ +#ifndef __SCALER_CORE_H__ +#define __SCALER_CORE_H__ +#include +#include +#include +#include +#include + + +struct display_edid { + char *data; + char *ext_data; +}; + +enum scaler_output_type { + SCALER_OUT_INVALID = 0, + SCALER_OUT_LVDS, + SCALER_OUT_VGA, + SCALER_OUT_RGB, + SCALER_OUT_HDMI, + SCALER_OUT_DP, + SCALER_OUT_NUMS, +}; + +enum scaler_input_type { + SCALER_IN_INVALID = 0, + SCALER_IN_VGA, + SCALER_IN_RGB, + SCALER_IN_HDMI, + SCALER_IN_DP, + SCALER_IN_DVI, + SCALER_IN_YPBPR, + SCALER_IN_YCBCR, + SCALER_IN_MYDP, + SCALER_IN_IDP, + SCALER_IN_NUMS, +}; + +enum scaler_bus_type { + SCALER_BUS_TYPE_INVALID = 0, + SCALER_BUS_TYPE_UART, + SCALER_BUS_TYPE_I2C, + SCALER_BUS_TYPE_SPI, + SCALER_BUS_TYPE_NUMS, +}; + +/* + * the function of scaler, for example convertor or switch +*/ +enum scaler_function_type { + SCALER_FUNC_INVALID = 0, + SCALER_FUNC_CONVERTOR, //转换器 + SCALER_FUNC_SWITCH, //切换开关多选一输出 + SCALER_FUNC_FULL, //全功能 + SCALER_FUNC_NUMS, +}; + +struct scaler_output_port { + int id; + int max_hres; + int max_vres; + int freq; + int led_gpio; //working led + enum scaler_output_type type; + struct list_head next; +}; + +struct scaler_input_port { + int id; + int max_hres; + int max_vres; + int freq; //HZ + int led_gpio; //working led + enum scaler_input_type type; + struct list_head next; +}; + +struct scaler_chip_dev { + char id; + char name[I2C_NAME_SIZE]; + struct i2c_client *client; + + enum scaler_function_type func_type; + + int int_gpio; + int reset_gpio; + int power_gpio; + int status_gpio; + char reg_size; //8bit = 1, 16bit = 2, 24bit = 3,32bit = 4. + + struct list_head iports; //config all support input type by make menuconfig + struct list_head oports; //config all support output type by make menuconfig + enum scaler_input_type cur_in_type; + int cur_inport_id; + enum scaler_output_type cur_out_type; + int cur_outport_id; + + //init hardware(gpio etc.) + int (*init_hw)(void); + int (*exit_hw)(void); + + //enable chip to process image + void (*start)(void); + //disable chip to process image + void (*stop)(void); + void (*reset)(char active); + void (*suspend)(void); + void (*resume)(void); + + // + int (*read)(unsigned short reg, int bytes, void *dest); + int (*write)(unsigned short reg, int bytes, void *src); + int (*parse_cmd)(unsigned int cmd, unsigned long arg); + int (*update_firmware)(void *data); + + //scaler chip dev list + struct list_head next; +}; + +struct scaler_platform_data { + int int_gpio; + int reset_gpio; + int power_gpio; + int status_gpio; //check chip if work on lower power mode or normal mode. + + char *firmware; + //function type + enum scaler_function_type func_type; + + //config in and out + struct scaler_input_port *iports; + struct scaler_output_port *oports; + int iport_size; + int oport_size; + + int (*init_hw)(void); + int (*exit_hw)(void); +}; + +struct scaler_device { + struct class *class; + struct device *dev; + struct cdev *cdev; + dev_t devno; + + struct display_edid edid; + + struct list_head chips; +}; + +struct scaler_chip_dev *alloc_scaler_chip(void); +//free chip memory and port memory +void free_scaler_chip(struct scaler_chip_dev *chip); +int init_scaler_chip(struct scaler_chip_dev *chip, struct scaler_platform_data *pdata); +// +int register_scaler_chip(struct scaler_chip_dev *chip); +int unregister_scaler_chip(struct scaler_chip_dev *chip); + +#define SCALER_IOCTL_MAGIC 's' +#define SCALER_IOCTL_POWER _IOW(SCALER_IOCTL_MAGIC, 0x00, char) +#define SCALER_IOCTL_GET_CUR_INPUT _IOR(SCALER_IOCTL_MAGIC, 0x01, int) +#define SCALER_IOCTL_SET_CUR_INPUT _IOW(SCALER_IOCTL_MAGIC, 0x02, int) + +#endif