diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/video/sprdfb/sprdfb_panel.c | |
Diffstat (limited to 'drivers/video/sprdfb/sprdfb_panel.c')
| -rw-r--r-- | drivers/video/sprdfb/sprdfb_panel.c | 683 |
1 files changed, 683 insertions, 0 deletions
diff --git a/drivers/video/sprdfb/sprdfb_panel.c b/drivers/video/sprdfb/sprdfb_panel.c new file mode 100644 index 00000000..1d53bbe8 --- /dev/null +++ b/drivers/video/sprdfb/sprdfb_panel.c @@ -0,0 +1,683 @@ +/* + * Copyright (C) 2012 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/mutex.h> +#include <linux/list.h> +#include <linux/delay.h> +#include <linux/fb.h> + +#include "sprdfb.h" +#include "sprdfb_panel.h" +#include "sprdfb_dispc_reg.h" + +static LIST_HEAD(panel_list_main);/* for main_lcd*/ +static LIST_HEAD(panel_list_sub);/* for sub_lcd */ +static DEFINE_MUTEX(panel_mutex); + +uint32_t lcd_id_from_uboot = 0; +unsigned long lcd_base_from_uboot = 0; + +extern struct panel_if_ctrl sprdfb_mcu_ctrl; +extern struct panel_if_ctrl sprdfb_rgb_ctrl; +#ifndef CONFIG_FB_SCX15 +extern struct panel_if_ctrl sprdfb_mipi_ctrl; +#endif +extern void sprdfb_panel_remove(struct sprdfb_device *dev); + +static int __init lcd_id_get(char *str) +{ + if ((str != NULL) && (str[0] == 'I') && (str[1] == 'D')) { + sscanf(&str[2], "%x", &lcd_id_from_uboot); + } + + printk(KERN_INFO "sprdfb: [%s]LCD Panel ID from uboot: 0x%x\n", __FUNCTION__, lcd_id_from_uboot); + + return 1; +} +__setup("lcd_id=", lcd_id_get); +static int __init lcd_base_get(char *str) +{ + if (str != NULL) { + sscanf(&str[0], "%lx", &lcd_base_from_uboot); + } + printk(KERN_INFO "sprdfb: [%s]LCD Panel Base from uboot: 0x%lx\n", __FUNCTION__, lcd_base_from_uboot); + return 1; +} +__setup("lcd_base=", lcd_base_get); + +static int32_t panel_reset_dispc(struct panel_spec *self) +{ + uint16_t timing1, timing2, timing3; + + if((NULL != self) && (0 != self->reset_timing.time1) && + (0 != self->reset_timing.time2) && (0 != self->reset_timing.time3)) { + timing1 = self->reset_timing.time1; + timing2 = self->reset_timing.time2; + timing3 = self->reset_timing.time3; + }else { + timing1 = 20; + timing2 = 20; + timing3 = 120; + } + + dispc_write(1, DISPC_RSTN); + usleep_range(timing1*1000, timing1*1000+500); + dispc_write(0, DISPC_RSTN); + usleep_range(timing2*1000, timing2*1000+500); + dispc_write(1, DISPC_RSTN); + + /* wait 10ms util the lcd is stable */ + usleep_range(timing3*1000, timing3*1000+500); + + return 0; +} + +static int32_t panel_set_resetpin_dispc( uint32_t status) +{ + if(0 == status){ + dispc_write(0, DISPC_RSTN); + }else{ + dispc_write(1, DISPC_RSTN); + } + return 0; +} + +static int panel_reset(struct sprdfb_device *dev) +{ + if((NULL == dev) || (NULL == dev->panel)){ + printk(KERN_ERR "sprdfb: [%s]: Invalid param\n", __FUNCTION__); + return -1; + } + + pr_debug("sprdfb: [%s], enter\n",__FUNCTION__); + + //clk/data lane enter LP + if(NULL != dev->panel->if_ctrl->panel_if_before_panel_reset){ + dev->panel->if_ctrl->panel_if_before_panel_reset(dev); + } + usleep_range(5000, 5500); + + //reset panel + dev->panel->ops->panel_reset(dev->panel); + + return 0; +} + +static int panel_sleep(struct sprdfb_device *dev) +{ + if((NULL == dev) || (NULL == dev->panel)){ + printk(KERN_ERR "sprdfb: [%s]: Invalid param\n", __FUNCTION__); + return -1; + } + + pr_debug("sprdfb: [%s], enter\n",__FUNCTION__); + + //send sleep cmd to lcd + if (dev->panel->ops->panel_enter_sleep != NULL) { + dev->panel->ops->panel_enter_sleep(dev->panel,1); + } + msleep(100); + //clk/data lane enter LP + if((NULL != dev->panel->if_ctrl->panel_if_before_panel_reset) + &&(SPRDFB_PANEL_TYPE_MIPI == dev->panel->type)) + { + dev->panel->if_ctrl->panel_if_before_panel_reset(dev); + } + return 0; +} + +static void panel_set_resetpin(uint16_t dev_id, uint32_t status, struct panel_spec *panel ) +{ + pr_debug("sprdfb: [%s].\n",__FUNCTION__); + + /*panel set reset pin status*/ + if(SPRDFB_MAINLCD_ID == dev_id){ + panel_set_resetpin_dispc(status); + } +} + + +static int32_t panel_before_resume(struct sprdfb_device *dev) +{ + /*restore the reset pin to high*/ + panel_set_resetpin(dev->dev_id, 1, dev->panel); + return 0; +} + +static int32_t panel_after_suspend(struct sprdfb_device *dev) +{ + /*set the reset pin to low*/ + panel_set_resetpin(dev->dev_id, 0, dev->panel); + return 0; +} + +static bool panel_check(struct panel_cfg *cfg) +{ + bool rval = true; + + if(NULL == cfg || NULL == cfg->panel){ + printk(KERN_ERR "sprdfb: [%s] :Invalid Param!\n", __FUNCTION__); + return false; + } + + pr_debug("sprdfb: [%s], dev_id = %d, lcd_id = 0x%x, type = %d\n",__FUNCTION__, cfg->dev_id, cfg->lcd_id, cfg->panel->type); + + switch(cfg->panel->type){ + case SPRDFB_PANEL_TYPE_MCU: + cfg->panel->if_ctrl = &sprdfb_mcu_ctrl; + break; + case SPRDFB_PANEL_TYPE_RGB: + case SPRDFB_PANEL_TYPE_LVDS: + cfg->panel->if_ctrl = &sprdfb_rgb_ctrl; + break; +#ifndef CONFIG_FB_SCX15 + case SPRDFB_PANEL_TYPE_MIPI: + cfg->panel->if_ctrl = &sprdfb_mipi_ctrl; + break; +#endif + default: + printk("sprdfb: [%s]: erro panel type.(%d,%d, %d)",__FUNCTION__, cfg->dev_id, cfg->lcd_id, cfg->panel->type); + cfg->panel->if_ctrl = NULL; + break; + }; + + if(cfg->panel->if_ctrl->panel_if_check){ + rval = cfg->panel->if_ctrl->panel_if_check(cfg->panel); + } + return rval; +} + +static int panel_mount(struct sprdfb_device *dev, struct panel_spec *panel) +{ + printk("sprdfb: [%s], dev_id = %d\n",__FUNCTION__, dev->dev_id); + + /* TODO: check whether the mode/res are supported */ + dev->panel = panel; + + if(NULL == dev->panel->ops->panel_reset){ + if(SPRDFB_MAINLCD_ID == dev->dev_id){ + dev->panel->ops->panel_reset = panel_reset_dispc; + } + } + + panel->if_ctrl->panel_if_mount(dev); + + return 0; +} + + +int panel_init(struct sprdfb_device *dev) +{ + if((NULL == dev) || (NULL == dev->panel)){ + printk(KERN_ERR "sprdfb: [%s]: Invalid param\n", __FUNCTION__); + return -1; + } + + pr_debug("sprdfb: [%s], dev_id= %d, type = %d\n",__FUNCTION__, dev->dev_id, dev->panel->type); + + if(!dev->panel->if_ctrl->panel_if_init(dev)){ + printk(KERN_ERR "sprdfb: [%s]: panel_if_init fail!\n", __FUNCTION__); + return -1; + } + + return 0; +} + +int panel_ready(struct sprdfb_device *dev) +{ + if((NULL == dev) || (NULL == dev->panel)){ + printk(KERN_ERR "sprdfb: [%s]: Invalid param\n", __FUNCTION__); + return -1; + } + + pr_debug("sprdfb: [%s], dev_id= %d, type = %d\n",__FUNCTION__, dev->dev_id, dev->panel->type); + + if(NULL != dev->panel->if_ctrl->panel_if_ready){ + dev->panel->if_ctrl->panel_if_ready(dev); + } + + return 0; +} + + +static struct panel_spec *adapt_panel_from_uboot(uint16_t dev_id) +{ + struct panel_cfg *cfg; + struct list_head *panel_list; + + pr_debug("sprdfb: [%s], dev_id = %d\n",__FUNCTION__, dev_id); + + if (lcd_id_from_uboot == 0) { + printk("sprdfb: [%s]: Not got lcd id from uboot\n", __FUNCTION__); + return NULL; + } + + if(SPRDFB_MAINLCD_ID == dev_id){ + panel_list = &panel_list_main; + }else{ + panel_list = &panel_list_sub; + } + + list_for_each_entry(cfg, panel_list, list) { + if(lcd_id_from_uboot == cfg->lcd_id) { + printk(KERN_INFO "sprdfb: [%s]: LCD Panel 0x%x is attached!\n", __FUNCTION__,cfg->lcd_id); + + return cfg->panel; + } + } + printk(KERN_ERR "sprdfb: [%s]: Failed to match LCD Panel from uboot!\n", __FUNCTION__); + + return NULL; +} + +static struct panel_spec *adapt_panel_from_readid(struct sprdfb_device *dev) +{ + struct panel_cfg *cfg; + struct panel_cfg *dummy_cfg = NULL; + struct list_head *panel_list; + uint32_t id; + + printk("sprdfb: [%s], dev_id = %d\n",__FUNCTION__, dev->dev_id); + + if(SPRDFB_MAINLCD_ID == dev->dev_id){ + panel_list = &panel_list_main; + }else{ + panel_list = &panel_list_sub; + } + + list_for_each_entry(cfg, panel_list, list) { + if(0xFFFFFFFF == cfg->lcd_id){ + dummy_cfg = cfg; + continue; + } + printk("sprdfb: [%s]: try panel 0x%x\n", __FUNCTION__, cfg->lcd_id); + panel_mount(dev, cfg->panel); +#ifndef CONFIG_SC_FPGA + dev->ctrl->update_clk(dev); +#endif + panel_init(dev); + panel_reset(dev); + id = dev->panel->ops->panel_readid(dev->panel); + if(id == cfg->lcd_id) { + pr_debug(KERN_INFO "sprdfb: [%s]: LCD Panel 0x%x is attached!\n", __FUNCTION__, cfg->lcd_id); + dev->panel->ops->panel_init(dev->panel); + panel_ready(dev); + return cfg->panel; + } + sprdfb_panel_remove(dev); + } + if(dummy_cfg != NULL){ + printk("sprdfb: [%s]: Can't find read panel, Use dummy panel!\n", __FUNCTION__); + panel_mount(dev, dummy_cfg->panel); +#ifndef CONFIG_SC_FPGA + dev->ctrl->update_clk(dev); +#endif + panel_init(dev); + panel_reset(dev); + id = dev->panel->ops->panel_readid(dev->panel); + if(id == dummy_cfg->lcd_id) { + pr_debug(KERN_INFO "sprdfb: [%s]: LCD Panel 0x%x is attached!\n", __FUNCTION__, dummy_cfg->lcd_id); + dev->panel->ops->panel_init(dev->panel); + panel_ready(dev); + return dummy_cfg->panel; + } + sprdfb_panel_remove(dev); + } + printk(KERN_ERR "sprdfb: [%s]: failed to attach LCD Panel!\n", __FUNCTION__); + return NULL; +} + + +bool sprdfb_panel_get(struct sprdfb_device *dev) +{ + struct panel_spec *panel = NULL; + + if(NULL == dev){ + printk("sprdfb: [%s]: Invalid param\n", __FUNCTION__); + return false; + } + + printk("sprdfb: [%s], dev_id = %d\n",__FUNCTION__, dev->dev_id); + + panel = adapt_panel_from_uboot(dev->dev_id); + if (panel) { + dev->panel_ready = true; + panel_mount(dev, panel); + panel_init(dev); + printk("sprdfb: [%s] got panel\n", __FUNCTION__); + return true; + } + + printk("sprdfb: [%s] can not got panel\n", __FUNCTION__); + + return false; +} + + +bool sprdfb_panel_probe(struct sprdfb_device *dev) +{ + struct panel_spec *panel; + + if(NULL == dev){ + printk("sprdfb: [%s]: Invalid param\n", __FUNCTION__); + return false; + } + + printk("sprdfb: [%s], dev_id = %d\n",__FUNCTION__, dev->dev_id); + + /* can not be here in normal; we should get correct device id from uboot */ + panel = adapt_panel_from_readid(dev); + + if (panel) { + printk("sprdfb: [%s] got panel\n", __FUNCTION__); + return true; + } + + printk("sprdfb: [%s] can not got panel\n", __FUNCTION__); + + return false; +} + +void sprdfb_panel_invalidate_rect(struct panel_spec *self, + uint16_t left, uint16_t top, + uint16_t right, uint16_t bottom) +{ + /*Jessica TODO: */ + if(NULL != self->ops->panel_invalidate_rect){ + self->ops->panel_invalidate_rect(self, left, top, right, bottom); + } + /*Jessica TODO: Need set timing to GRAM timing*/ +} + +void sprdfb_panel_invalidate(struct panel_spec *self) +{ + /*Jessica TODO:*/ + if(NULL != self->ops->panel_invalidate){ + self->ops->panel_invalidate(self); + } + /*Jessica TODO: Need set timing to GRAM timing*/ +} + +void sprdfb_panel_before_refresh(struct sprdfb_device *dev) +{ + if(NULL != dev->panel->if_ctrl->panel_if_before_refresh){ + dev->panel->if_ctrl->panel_if_before_refresh(dev); + } +} + +void sprdfb_panel_after_refresh(struct sprdfb_device *dev) +{ + if(NULL != dev->panel->if_ctrl->panel_if_after_refresh){ + dev->panel->if_ctrl->panel_if_after_refresh(dev); + } +} + +#ifdef CONFIG_FB_DYNAMIC_FREQ_SCALING +void sprdfb_panel_change_fps(struct sprdfb_device *dev, int fps_level) +{ + if (dev->panel->ops->panel_change_fps!= NULL) { + printk("sprdfb: [%s] fps_level= %d\n", __FUNCTION__,fps_level); + dev->panel->ops->panel_change_fps(dev->panel,fps_level); + } +} +#endif + +#ifdef CONFIG_FB_ESD_SUPPORT +/*return value: 0--panel OK.1-panel has been reset*/ +uint32_t sprdfb_panel_ESD_check(struct sprdfb_device *dev) +{ + int32_t result = 0; + uint32_t if_status = 0; + +// printk("sprdfb: [%s] (%d, %d, %d)\n",__FUNCTION__, dev->check_esd_time, dev->panel_reset_time, dev->reset_dsi_time); + + dev->check_esd_time++; + + if(SPRDFB_PANEL_IF_EDPI == dev->panel_if_type){ + if (dev->panel->ops->panel_esd_check != NULL) { + result = dev->panel->ops->panel_esd_check(dev->panel); + pr_debug("sprdfb: [%s] panel check return %d\n", __FUNCTION__, result); + } + }else if(SPRDFB_PANEL_IF_DPI == dev->panel_if_type){ +#ifdef FB_CHECK_ESD_BY_TE_SUPPORT + dev->esd_te_waiter++; + dev->esd_te_done = 0; + dispc_set_bits(BIT(1), DISPC_INT_EN); + result = wait_event_interruptible_timeout(dev->esd_te_queue, + dev->esd_te_done, msecs_to_jiffies(600)); + pr_debug("sprdfb: after wait (%d)\n", result); + dispc_clear_bits(BIT(1), DISPC_INT_EN); + if(!result){ /*time out*/ + printk("sprdfb: [%s] esd check not got te signal!!!!\n", __FUNCTION__); + dev->esd_te_waiter = 0; + result = 0; + }else{ + pr_debug("sprdfb: [%s] esd check got te signal!\n", __FUNCTION__); + result = 1; + } +#else + if (dev->panel->ops->panel_esd_check != NULL) { + result = dev->panel->ops->panel_esd_check(dev->panel); +// pr_debug("sprdfb: [%s] panel check return %d\n", __FUNCTION__, result); + } + +#endif + } + + + if(0 == dev->enable){ + printk("sprdfb: [%s] leave (Invalid device status)!\n", __FUNCTION__); + return 0; + } + + if(result == 0){ + dev->panel_reset_time++; + + if(SPRDFB_PANEL_IF_EDPI == dev->panel_if_type){ + if(NULL != dev->panel->if_ctrl->panel_if_get_status){ + if_status = dev->panel->if_ctrl->panel_if_get_status(dev); + } + }else if(SPRDFB_PANEL_IF_DPI == dev->panel_if_type){ + if_status = 2; /*need reset dsi as default for dpi mode*/ + } + + if(0 == if_status){ + printk("sprdfb: [%s] fail! Need reset panel.(%d,%d,%d)\n", __FUNCTION__, + dev->check_esd_time, dev->panel_reset_time, dev->reset_dsi_time); + panel_reset(dev); + + if(0 == dev->enable){ + printk("sprdfb: [%s] leave (Invalid device status)!\n", __FUNCTION__); + return 0; + } + + dev->panel->ops->panel_init(dev->panel); + panel_ready(dev); + }else{ + printk("sprdfb: [%s] fail! Need reset panel and panel if!!!!(%d,%d,%d)\n",__FUNCTION__, + dev->check_esd_time, dev->panel_reset_time, dev->reset_dsi_time); + dev->reset_dsi_time++; + if(NULL != dev->panel->if_ctrl->panel_if_suspend){ + dev->panel->if_ctrl->panel_if_suspend(dev); + } + + mdelay(10); + + if(0 == dev->enable){ + printk("sprdfb: [%s] leave (Invalid device status)!\n", __FUNCTION__); + return 0; + } + + panel_init(dev); + panel_reset(dev); + + if(0 == dev->enable){ + printk("sprdfb: [%s] leave (Invalid device status)!\n", __FUNCTION__); + return 0; + } + + dev->panel->ops->panel_init(dev->panel); + panel_ready(dev); + } + pr_debug("sprdfb: [%s]return 1\n",__FUNCTION__); + return 1; + } +// pr_debug("sprdfb: [%s]return 0\n",__FUNCTION__); + return 0; +} +#endif + +void sprdfb_panel_suspend(struct sprdfb_device *dev) +{ + if(NULL == dev->panel){ + return; + } + + printk("sprdfb: [%s], dev_id = %d\n",__FUNCTION__, dev->dev_id); +#if 0 + //step1-1 clk/data lane enter LP + if(NULL != dev->panel->if_ctrl->panel_if_before_panel_reset){ + dev->panel->if_ctrl->panel_if_before_panel_reset(dev); + } + + //step1-2 enter sleep (another way : reset panel) + /*Jessica TODO: Need do some I2c, SPI, mipi sleep here*/ + /* let lcdc sleep in */ + + if (dev->panel->ops->panel_enter_sleep != NULL) { + dev->panel->ops->panel_enter_sleep(dev->panel,1); + } + msleep(100); +#else + //step1 send lcd sleep cmd or reset panel directly + if(dev->panel->suspend_mode == SEND_SLEEP_CMD){ + panel_sleep(dev); + }else{ + panel_reset(dev); + } +#endif + + //step2 clk/data lane enter ulps + if(NULL != dev->panel->if_ctrl->panel_if_enter_ulps){ + dev->panel->if_ctrl->panel_if_enter_ulps(dev); + } + + //step3 turn off mipi + if(NULL != dev->panel->if_ctrl->panel_if_suspend){ + dev->panel->if_ctrl->panel_if_suspend(dev); + } + + //step4 reset pin to low + if (dev->panel->ops->panel_after_suspend != NULL) { + //himax mipi lcd may define empty function + dev->panel->ops->panel_after_suspend(dev->panel); + } + else{ + panel_after_suspend(dev); + } +} + +void sprdfb_panel_resume(struct sprdfb_device *dev, bool from_deep_sleep) +{ + if(NULL == dev->panel){ + return; + } + + printk(KERN_INFO "sprdfb: [%s], dev->enable= %d, from_deep_sleep = %d\n",__FUNCTION__, dev->enable, from_deep_sleep); +#if 0 + /*Jessica TODO: resume i2c, spi, mipi*/ + if(NULL != dev->panel->if_ctrl->panel_if_resume){ + dev->panel->if_ctrl->panel_if_resume(dev); + } + panel_ready(dev); +#endif + //step1 reset pin to high + if (dev->panel->ops->panel_before_resume != NULL) { + //himax mipi lcd may define empty function + dev->panel->ops->panel_before_resume(dev->panel); + } + else{ + panel_before_resume(dev); + } + + if(from_deep_sleep){ + //step2 turn on mipi + panel_init(dev); + + //step3 reset panel + panel_reset(dev); + + //step4 panel init + dev->panel->ops->panel_init(dev->panel); + + //step5 clk/data lane enter HS + panel_ready(dev); + }else{ + //step2 turn on mipi + /*Jessica TODO: resume i2c, spi, mipi*/ + if(NULL != dev->panel->if_ctrl->panel_if_resume){ + dev->panel->if_ctrl->panel_if_resume(dev); + } + + //step3 sleep out + if(NULL != dev->panel->ops->panel_enter_sleep){ + dev->panel->ops->panel_enter_sleep(dev->panel,0); + } + + //step4 clk/data lane enter HS + panel_ready(dev); + } + +} + +void sprdfb_panel_remove(struct sprdfb_device *dev) +{ + if(NULL == dev->panel){ + return; + } + + /*Jessica TODO:close panel, i2c, spi, mipi*/ + if(NULL != dev->panel->if_ctrl->panel_if_uninit){ + dev->panel->if_ctrl->panel_if_uninit(dev); + } + dev->panel = NULL; +} + + +int sprdfb_panel_register(struct panel_cfg *cfg) +{ + pr_debug("sprdfb: [%s], panel id = %d\n",__FUNCTION__, cfg->dev_id); + + if(!panel_check(cfg)){ + printk("sprdfb: [%s]: panel check fail!id = %d\n",__FUNCTION__, cfg->dev_id); + return -1; + } + + mutex_lock(&panel_mutex); + + if (cfg->dev_id == SPRDFB_MAINLCD_ID) { + list_add_tail(&cfg->list, &panel_list_main); + } else if (cfg->dev_id == SPRDFB_SUBLCD_ID) { + list_add_tail(&cfg->list, &panel_list_sub); + } else { + list_add_tail(&cfg->list, &panel_list_main); + list_add_tail(&cfg->list, &panel_list_sub); + } + + mutex_unlock(&panel_mutex); + + return 0; +} + + |
