summaryrefslogtreecommitdiff
path: root/drivers/video/sprdfb/sprdfb_mcu.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/sprdfb/sprdfb_mcu.c')
-rw-r--r--drivers/video/sprdfb/sprdfb_mcu.c563
1 files changed, 563 insertions, 0 deletions
diff --git a/drivers/video/sprdfb/sprdfb_mcu.c b/drivers/video/sprdfb/sprdfb_mcu.c
new file mode 100644
index 00000000..ff4f91fc
--- /dev/null
+++ b/drivers/video/sprdfb/sprdfb_mcu.c
@@ -0,0 +1,563 @@
+/*
+ * 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/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#ifdef CONFIG_OF
+#include <linux/fb.h>
+#endif
+
+#include "sprdfb.h"
+#include "sprdfb_panel.h"
+#include "sprdfb_dispc_reg.h"
+
+//#define CONFIG_FB_NO_FMARK /*Jessica for FPGA test*/
+
+static int32_t dispc_mcu_send_cmd(uint32_t cmd)
+{
+ int wait_count = 0;
+ /* busy wait for ahb fifo full sign's disappearance */
+ while((dispc_read(DISPC_DBI_QUEUE) & BIT(5)) && (wait_count < 20000)){
+ udelay(5);
+ wait_count++;
+ }
+ if(wait_count >= 20000){
+ printk("sprdfb: [%s] send cmd not finish!!!\n", __FUNCTION__);
+ return -1;
+ }
+
+ dispc_write(cmd, DISPC_DBI_CMD);
+
+ return 0;
+}
+
+static int32_t dispc_mcu_send_cmd_data(uint32_t cmd, uint32_t data)
+{
+ int wait_count = 0;
+ /* busy wait for ahb fifo full sign's disappearance */
+ while((dispc_read(DISPC_DBI_QUEUE) & BIT(5)) && (wait_count < 20000)){
+ udelay(5);
+ wait_count++;
+ }
+ if(wait_count >= 20000){
+ printk("sprdfb: [%s] send cmd data not finish 1!!!\n", __FUNCTION__);
+ return -1;
+ }
+
+ dispc_write(cmd, DISPC_DBI_CMD);
+
+ wait_count = 0;
+ /* busy wait for ahb fifo full sign's disappearance */
+ while((dispc_read(DISPC_DBI_QUEUE) & BIT(5)) && (wait_count < 20000)){
+ udelay(5);
+ wait_count++;
+ }
+ if(wait_count >= 20000){
+ printk("sprdfb: [%s] send cmd data not finish 2!!!\n", __FUNCTION__);
+ return -1;
+ }
+
+ dispc_write(data, DISPC_DBI_DATA);
+
+ return 0;
+}
+
+static int32_t dispc_mcu_send_data(uint32_t data)
+{
+ int wait_count = 0;
+ /* busy wait for ahb fifo full sign's disappearance */
+ while((dispc_read(DISPC_DBI_QUEUE) & BIT(5)) && (wait_count < 20000)){
+ udelay(5);
+ wait_count++;
+ }
+ if(wait_count >= 20000){
+ printk("sprdfb: [%s] send data not finish!!!\n", __FUNCTION__);
+ return -1;
+ }
+
+ dispc_write(data, DISPC_DBI_DATA);
+
+ return 0;
+}
+
+static uint32_t dispc_mcu_read_data(void)
+{
+ int wait_count = 0;
+ /* busy wait for ahb fifo full sign's disappearance */
+ while((dispc_read(DISPC_DBI_QUEUE) & BIT(5)) && (wait_count < 20000)){
+ udelay(5);
+ wait_count++;
+ }
+ if(wait_count >= 20000){
+ printk("sprdfb: [%s] read data not finish!!!\n", __FUNCTION__);
+ return -1;
+ }
+ dispc_write(1 << 24, DISPC_DBI_DATA);
+ udelay(50);
+ return dispc_read(DISPC_DBI_RDATA);
+}
+
+static struct ops_mcu dispc_mcu_ops = {
+ .send_cmd = dispc_mcu_send_cmd,
+ .send_cmd_data = dispc_mcu_send_cmd_data,
+ .send_data = dispc_mcu_send_data,
+ .read_data = dispc_mcu_read_data,
+};
+
+#ifdef CONFIG_OF
+static uint32_t mcu_calc_timing(struct timing_mcu *timing, struct sprdfb_device *dev)
+#else
+static uint32_t mcu_calc_timing(struct timing_mcu *timing, uint16_t dev_id)
+#endif
+{
+ uint32_t clk_rate;
+ uint32_t rcss, rlpw, rhpw, wcss, wlpw, whpw;
+ struct clk * clk = NULL;
+
+ if(NULL == timing){
+ printk(KERN_ERR "sprdfb: [%s]: Invalid Param\n", __FUNCTION__);
+ return 0;
+ }
+
+#ifdef CONFIG_OF
+ if(SPRDFB_MAINLCD_ID == dev->dev_id){
+ clk = of_clk_get_by_name(dev->of_dev->of_node,"dispc_dbi_clk");
+#else
+ if(SPRDFB_MAINLCD_ID == dev_id){
+ clk = clk_get(NULL,"clk_dispc_dbi");
+#endif
+ if (IS_ERR(clk)) {
+ printk(KERN_WARNING "sprdfb: get clk_dispc_dbi fail!\n");
+ } else {
+ pr_debug(KERN_INFO "sprdfb: get clk_dispc_dbi ok!\n");
+ }
+ }
+// clk_rate = clk_get_rate(clk) / 1000000;
+ clk_rate = 250; // dummy 250M Hz
+
+#ifdef CONFIG_OF
+ pr_debug(KERN_INFO "sprdfb: [%s] clk_rate: 0x%x, dev_id = %d\n", __FUNCTION__, clk_rate, dev->dev_id);
+#else
+ pr_debug(KERN_INFO "sprdfb: [%s] clk_rate: 0x%x, dev_id = %d\n", __FUNCTION__, clk_rate, dev_id);
+#endif
+ /********************************************************
+ * we assume : t = ? ns, dispc_dbi = ? MHz so
+ * 1ns need cycle : dispc_dbi /1000
+ * tns need cycles : t * dispc_dbi / 1000
+ *
+ ********************************************************/
+#define MAX_DBI_RWCSS_TIMING_VALUE 15
+#define MAX_DBI_RWLPW_TIMING_VALUE 63
+#define MAX_DBI_RWHPW_TIMING_VALUE 63
+#define DBI_CYCLES(ns) (( (ns) * clk_rate + 1000 - 1)/ 1000)
+
+ /* ceiling*/
+ rcss = DBI_CYCLES(timing->rcss);
+ if (rcss > MAX_DBI_RWCSS_TIMING_VALUE) {
+ rcss = MAX_DBI_RWCSS_TIMING_VALUE ;
+ }
+
+ rlpw = DBI_CYCLES(timing->rlpw);
+ if (rlpw > MAX_DBI_RWLPW_TIMING_VALUE) {
+ rlpw = MAX_DBI_RWLPW_TIMING_VALUE ;
+ }
+
+ rhpw = DBI_CYCLES (timing->rhpw);
+ if (rhpw > MAX_DBI_RWHPW_TIMING_VALUE) {
+ rhpw = MAX_DBI_RWHPW_TIMING_VALUE ;
+ }
+
+ wcss = DBI_CYCLES(timing->wcss);
+ if (wcss > MAX_DBI_RWCSS_TIMING_VALUE) {
+ wcss = MAX_DBI_RWCSS_TIMING_VALUE ;
+ }
+
+ wlpw = DBI_CYCLES(timing->wlpw);
+ if (wlpw > MAX_DBI_RWLPW_TIMING_VALUE) {
+ wlpw = MAX_DBI_RWLPW_TIMING_VALUE ;
+ }
+
+#ifndef CONFIG_LCD_CS_ALWAYS_LOW
+ /* dispc/lcdc will waste one cycle because CS pulse will use one cycle*/
+ whpw = DBI_CYCLES (timing->whpw) - 1;
+#else
+ whpw = DBI_CYCLES (timing->whpw) ;
+#endif
+ if (whpw > MAX_DBI_RWHPW_TIMING_VALUE) {
+ whpw = MAX_DBI_RWHPW_TIMING_VALUE ;
+ }
+
+ return (whpw | (wlpw << 6) | (wcss << 12)
+ | (rhpw << 16) |(rlpw << 22) | (rcss << 28));
+}
+
+static uint32_t mcu_readid(struct panel_spec *self)
+{
+ uint32_t id = 0;
+
+ /* default id reg is 0 */
+ self->info.mcu->ops->send_cmd(0x0);
+
+ if(self->info.mcu->bus_width == 8) {
+ id = (self->info.mcu->ops->read_data()) & 0xff;
+ id <<= 8;
+ id |= (self->info.mcu->ops->read_data()) & 0xff;
+ } else {
+ id = self->info.mcu->ops->read_data();
+ }
+
+ return id;
+}
+
+#ifdef CONFIG_FB_LCD_CS1
+/*cs1*/
+void mcu_dispc_init_config(struct panel_spec *panel)
+{
+ uint32_t reg_val = 0;
+
+ pr_debug("sprdfb: [%s] for cs1\n", __FUNCTION__);
+
+ if(NULL == panel){
+ printk(KERN_ERR "sprdfb: [%s] fail.(Invalid Param)\n", __FUNCTION__);
+ return;
+ }
+
+ if(SPRDFB_PANEL_TYPE_MCU != panel->type){
+ printk(KERN_ERR "sprdfb: [%s] fail.(not mcu panel)\n", __FUNCTION__);
+ return;
+ }
+
+ /*use dbi as interface*/
+ dispc_set_bits((2<<1), DISPC_CTRL);
+
+ /* CS1 bus mode [BIT8]: 8080/6800 */
+ switch (panel->info.mcu->bus_mode) {
+ case LCD_BUS_8080:
+ break;
+ case LCD_BUS_6800:
+ reg_val |= (1<<8);
+ break;
+ default:
+ break;
+ }
+ /* CS1 bus width [BIT11:9] */
+ switch (panel->info.mcu->bus_width) {
+ case 8:
+ break;
+ case 9:
+ reg_val |= (1 << 9);
+ break;
+ case 16:
+ reg_val |= (2 << 9);
+ break;
+ case 18:
+ reg_val |= (3 << 9) ;
+ break;
+ case 24:
+ reg_val |= (4 << 9);
+ break;
+ default:
+ break;
+ }
+
+ /*CS1 pixel bits [BIT13:12]*/
+ switch (panel->info.mcu->bpp) {
+ case 16:
+ break;
+ case 18:
+ reg_val |= (1 << 12) ;
+ break;
+ case 24:
+ reg_val |= (2 << 12);
+ break;
+ default:
+ break;
+ }
+
+#ifndef CONFIG_FB_NO_FMARK
+ /*TE enable*/
+ reg_val |= (1 << 16);
+ if(SPRDFB_POLARITY_NEG == panel->info.mcu->te_pol){
+ reg_val |= (1<< 17);
+ }
+ dispc_write(panel->info.mcu->te_sync_delay, DISPC_TE_SYNC_DELAY);
+#endif
+
+#ifdef CONFIG_LCD_CS_ALWAYS_LOW
+ /*CS alway low mode*/
+ reg_val |= (1<<21);
+#else
+ /*CS not alway low mode*/
+#endif
+
+ /*CS1 selected*/
+ reg_val |= (1 << 20);
+
+ dispc_write(reg_val, DISPC_DBI_CTRL);
+
+ pr_debug("sprdfb: [%s] DISPC_DBI_CTRL = %d\n", __FUNCTION__, dispc_read(DISPC_DBI_CTRL));
+}
+
+void mcu_dispc_set_timing(struct sprdfb_device *dev, uint32_t type)
+{
+ pr_debug("sprdfb: [%s] for cs1, type = %d\n", __FUNCTION__, type);
+
+ switch (type)
+ {
+ case MCU_LCD_REGISTER_TIMING:
+ dispc_write(dev->panel_timing.mcu_timing[MCU_LCD_REGISTER_TIMING],DISPC_DBI_TIMING1);
+ break;
+
+ case MCU_LCD_GRAM_TIMING:
+ dispc_write(dev->panel_timing.mcu_timing[MCU_LCD_GRAM_TIMING],DISPC_DBI_TIMING1);
+ break;
+ default:
+ break;
+ }
+}
+
+#else
+/*cs0*/
+void mcu_dispc_init_config(struct panel_spec *panel)
+{
+ uint32_t reg_val = 0;
+
+ pr_debug("sprdfb: [%s] for cs0\n", __FUNCTION__);
+
+ if(NULL == panel){
+ printk(KERN_ERR "sprdfb: [%s] fail.(Invalid Param)\n", __FUNCTION__);
+ return;
+ }
+
+ if(SPRDFB_PANEL_TYPE_MCU != panel->type){
+ printk(KERN_ERR "sprdfb: [%s] fail.(not mcu panel)\n", __FUNCTION__);
+ return;
+ }
+
+ /*use dbi as interface*/
+ dispc_set_bits((2<<1), DISPC_CTRL);
+
+ /* CS0 bus mode [BIT0]: 8080/6800 */
+ switch (panel->info.mcu->bus_mode) {
+ case LCD_BUS_8080:
+ break;
+ case LCD_BUS_6800:
+ reg_val |= 1;
+ break;
+ default:
+ break;
+ }
+ /* CS0 bus width [BIT3:1] */
+ switch (panel->info.mcu->bus_width) {
+ case 8:
+ break;
+ case 9:
+ reg_val |= (1 << 1);
+ break;
+ case 16:
+ reg_val |= (2 << 1);
+ break;
+ case 18:
+ reg_val |= (3 << 1) ;
+ break;
+ case 24:
+ reg_val |= (4 << 1);
+ break;
+ default:
+ break;
+ }
+
+ /*CS0 pixel bits [BIT5:4]*/
+ switch (panel->info.mcu->bpp) {
+ case 16:
+ break;
+ case 18:
+ reg_val |= (1 << 4) ;
+ break;
+ case 24:
+ reg_val |= (2 << 4);
+ break;
+ default:
+ break;
+ }
+
+#ifndef CONFIG_FB_NO_FMARK
+ /*TE enable*/
+ reg_val |= (1 << 16);
+ if(SPRDFB_POLARITY_NEG == panel->info.mcu->te_pol){
+ reg_val |= (1<< 17);
+ }
+ dispc_write(panel->info.mcu->te_sync_delay, DISPC_TE_SYNC_DELAY);
+#endif
+
+#ifdef CONFIG_LCD_CS_ALWAYS_LOW
+ /*CS alway low mode*/
+ reg_val |= (1<<21);
+#else
+ /*CS not alway low mode*/
+#endif
+
+ /*CS0 selected*/
+
+ dispc_write(reg_val, DISPC_DBI_CTRL);
+
+ pr_debug("sprdfb: [%s] DISPC_DBI_CTRL = %d\n", __FUNCTION__, dispc_read(DISPC_DBI_CTRL));
+}
+
+void mcu_dispc_set_timing(struct sprdfb_device *dev, uint32_t type)
+{
+ pr_debug("sprdfb: [%s] for cs0, type = %d\n", __FUNCTION__, type);
+
+ switch (type)
+ {
+ case MCU_LCD_REGISTER_TIMING:
+ dispc_write(dev->panel_timing.mcu_timing[MCU_LCD_REGISTER_TIMING],DISPC_DBI_TIMING0);
+ break;
+
+ case MCU_LCD_GRAM_TIMING:
+ dispc_write(dev->panel_timing.mcu_timing[MCU_LCD_GRAM_TIMING],DISPC_DBI_TIMING0);
+ break;
+ default:
+ break;
+ }
+}
+
+#endif
+
+static int32_t sprdfb_mcu_panel_check(struct panel_spec *panel)
+{
+ struct info_mcu* mcu_info = NULL;
+ bool rval = true;
+
+ if(NULL == panel){
+ printk("sprdfb: [%s] fail. (Invalid param)\n", __FUNCTION__);
+ return false;
+ }
+
+ if(SPRDFB_PANEL_TYPE_MCU != panel->type){
+ printk("sprdfb: [%s] fail. (not mcu param)\n", __FUNCTION__);
+ return false;
+ }
+
+ mcu_info = panel->info.mcu;
+
+ pr_debug("sprdfb: [%s]: bus width= %d, bpp = %d\n",__FUNCTION__, mcu_info->bus_width, mcu_info->bpp);
+
+ switch(mcu_info->bus_width){
+ case 8:
+ if((16 != mcu_info->bpp) && (24 != mcu_info->bpp)){
+ rval = false;
+ }
+ break;
+ case 9:
+ if(18 != mcu_info->bpp) {
+ rval = false;
+ }
+ break;
+ case 16:
+ if((16 != mcu_info->bpp) && (18 != mcu_info->bpp) &&
+ (24 != mcu_info->bpp)){
+ rval = false;
+ }
+ break;
+ case 18:
+ if(18 != mcu_info->bpp){
+ rval = false;
+ }
+ break;
+ case 24:
+ if(24 != mcu_info->bpp){
+ rval = false;
+ }
+ break;
+ default:
+ rval = false;
+ break;
+ }
+
+ if(!rval){
+ printk(KERN_ERR "sprdfb: mcu_panel_check return false!\n");
+ }
+
+ return rval;
+}
+
+static void sprdfb_mcu_panel_mount(struct sprdfb_device *dev)
+{
+ struct timing_mcu* timing = NULL;
+
+ if((NULL == dev) || (NULL == dev->panel)){
+ printk(KERN_ERR "sprdfb: [%s]: Invalid Param\n", __FUNCTION__);
+ return;
+ }
+
+ pr_debug(KERN_INFO "sprdfb: [%s], dev_id = %d\n",__FUNCTION__, dev->dev_id);
+
+ dev->panel_if_type = SPRDFB_PANEL_IF_DBI;
+
+ if(SPRDFB_MAINLCD_ID == dev->dev_id){
+ dev->panel->info.mcu->ops = &dispc_mcu_ops;
+ }
+
+ if(NULL == dev->panel->ops->panel_readid){
+ dev->panel->ops->panel_readid = mcu_readid;
+ }
+
+ timing = dev->panel->info.mcu->timing;
+#ifdef CONFIG_OF
+ dev->panel_timing.mcu_timing[MCU_LCD_REGISTER_TIMING] = mcu_calc_timing(timing, dev);
+#else
+ dev->panel_timing.mcu_timing[MCU_LCD_REGISTER_TIMING] = mcu_calc_timing(timing, dev->dev_id);
+#endif
+ timing++;
+#ifdef CONFIG_OF
+ dev->panel_timing.mcu_timing[MCU_LCD_GRAM_TIMING] = mcu_calc_timing(timing, dev);
+#else
+ dev->panel_timing.mcu_timing[MCU_LCD_GRAM_TIMING] = mcu_calc_timing(timing, dev->dev_id);
+#endif
+}
+
+static bool sprdfb_mcu_panel_init(struct sprdfb_device *dev)
+{
+ if(SPRDFB_MAINLCD_ID == dev->dev_id){
+ mcu_dispc_init_config(dev->panel);
+ mcu_dispc_set_timing(dev, MCU_LCD_REGISTER_TIMING);
+ }
+ return true;
+}
+
+static void sprdfb_mcu_panel_before_refresh(struct sprdfb_device *dev)
+{
+ if(SPRDFB_MAINLCD_ID == dev->dev_id){
+ mcu_dispc_set_timing(dev, MCU_LCD_GRAM_TIMING);
+ }
+}
+
+static void sprdfb_mcu_panel_after_refresh(struct sprdfb_device *dev)
+{
+ if(SPRDFB_MAINLCD_ID == dev->dev_id){
+ mcu_dispc_set_timing(dev, MCU_LCD_REGISTER_TIMING);
+ }
+}
+
+struct panel_if_ctrl sprdfb_mcu_ctrl = {
+ .if_name = "mcu",
+ .panel_if_check = sprdfb_mcu_panel_check,
+ .panel_if_mount = sprdfb_mcu_panel_mount,
+ .panel_if_init = sprdfb_mcu_panel_init,
+ .panel_if_before_refresh = sprdfb_mcu_panel_before_refresh,
+ .panel_if_after_refresh = sprdfb_mcu_panel_after_refresh,
+};
+