#error "please port this file to linux 2.6.18" /* * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. */ /* * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ /*!@example slcdctest.c test driver using EPSON L2F50032T00 serial mode */ /** * * @defgroup SLCDC SLCDC driver **/ /**@{*/ /** * @file mx2fb_epson.c * @brief slcdc driver source code * * This is the basic release for SLCDC driver, which is to support EPSON * L2F50032T00 16bit seria mode5-6-5 and parallel mode. This driver acts as a * standard character device, which can be dinamically loaded. * For example, you can refer to slcdctest.c file. * * Modification History: * 15,Dec,2003 Karen Kang * * 21,Feb,2006 Vasanthan S * * @bug **/ #ifndef __KERNEL__ # define __KERNEL__ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../console/fbcon.h" #include "../../mxc/mempool/mempool.h" #include #include #include #include /* get_user,copy_to_user */ #include #include #include #include #include #include #include #ifdef CONFIG_ARCH_MX2ADS #include #endif #define MODULE_NAME "slcdc" #define DBMX_DEBUG 1 #ifdef DBMX_DEBUG #define TRACE(fmt, args...) \ { \ printk("\n %s:%d:%s:",__FILE__, __LINE__,__FUNCTION__); \ printk(fmt, ## args);\ } #else #define TRACE(fmt, args...) #endif #define FAILED(fmt, args...) \ { \ printk("\n %s:%d:%s:",__FILE__, __LINE__,__FUNCTION__); \ printk(fmt, ## args);\ } #define INFO(fmt, args...) \ { \ printk("\n"); \ printk(fmt, ## args);\ } /*@brief definition for SLCDC_LCD_TXCONFIG */ #define SLCDC_IMG_8L 0x00020000 /*@name SLCDC connection configuration *@brief config macro for serial mode or parallel mode. SLCDC can operate in * serial or parallel mode. This macro reflects the hardware configuration * and is not a software configuration. */ /**@{*/ #define SLCDC_SERIAL_MODE /* #define SLCDC_PARALLE_MODE */ /**@}*/ /*@name SLCDC IO MUX configuration *@brief configuration macro for the pin mux detail. This tells which pins are * configured for SLCDC. For more information refer processor data sheet. * Most cases only one of the following macros should be enabled. */ /**@{*/ #define SLCDC_MUX_SSI3_SLCDC2 /*!< 1. SLCDC is mux.ed with SSI3 */ //#define SLCDC_MUX_SD_SLCDC1 /*!< 2. SLCDC is mux.ed with SD2 */ //#define SLCDC_MUX_LCD_SLCDC1 /*!< 3. SLCDC is mux.ed with LCD */ /**@}*/ /*@brief configuration macro which tells whether the interrupt should be used * or not for SLCDC. SLCDC will provide an interrupt on a completed * transfer, which can be used for refreshing the data. */ #define USING_INTERRUPT_SLCDC /** *@name ioctl command macro definitions *@brief definition for SLCDC ioctl cmd,these command definition is different * for serial \nmode and paralel mode ,in this driver, we have not * supported all these commands, \n please check the ioctl function for * details. **/ /**@{*/ /* definition for SLCDC cmd */ /* same as Epson 10043 & 50052 */ #ifdef SLCDC_SERIAL_MODE #define SLCDC_CMD_DISON 0xaf00 /*!< 1. display on */ #define SLCDC_CMD_DISOFF 0xae00 /*!< 2. display off */ #define SLCDC_CMD_DISNOR 0xa600 /*!< 3. normal display */ #define SLCDC_CMD_DISINV 0xa700 /*!< 4. inverse display */ #define SLCDC_CMD_DISCTL 0xca00 /*!< 5. display control */ #define SLCDC_CMD_SLPIN 0x9500 /*!< 10.sleep in */ #define SLCDC_CMD_SLPOUT 0x9400 /*!< 11.sleep out */ #define SLCDC_CMD_SD_PSET 0x7500 /*!< 12.page address set */ #define SLCDC_CMD_SD_CSET 0x1500 /*!< 14.column address set */ #define SLCDC_CMD_DATCTL 0xbc /*!< 16.data scan direction, etc. */ #define SLCDC_CMD_RAMWR 0x5c00 /*!< 17.writing to memory */ #define SLCDC_CMD_PTLIN 0xa800 /*!< 19.partial display in */ #define SLCDC_CMD_PTLOUT 0xa900 /*!< 20.partial display out */ /* value different from 10043 but same as 50052 */ #define SLCDC_CMD_VOLCTR 0xc600 /*!< 25.Electronic volume control */ /* commands not found in 10043 but in 50052*/ #define SLCDC_CMD_GCP64 0xcb00 /*!< 6. 64 grayscale pulse positon set */ #define SLCDC_CMD_GCP16 0xcc00 /*!< 7. 16 grayscale pulse positon set */ #define SLCDC_CMD_GSSET 0xcd00 /*!< 8. grayscale set */ #define SLCDC_CMD_RAMRD 0x5d00 /*!< 18.memory read */ #define SLCDC_CMD_ASCSET 0xaa00 /*!< 21.area scroll set */ #define SLCDC_CMD_SCSTART 0xab00 /*!< 22.scroll start set */ #define SLCDC_CMD_EPCTIN 0x6100 /*!< 26.Power IC control for EVR */ #define SLCDC_CMD_EPCTOUT 0x6200 /*!< 27.Power IC control for EVR */ #else #define SLCDC_CMD_DISON 0xaf /*!<1. display on */ #define SLCDC_CMD_DISOFF 0xae /*!<2. display off */ #define SLCDC_CMD_DISNOR 0xa6 /*!<3. normal display */ #define SLCDC_CMD_DISINV 0xa7 /*!<4. inverse display */ #define SLCDC_CMD_DISCTL 0xca /*!<5. display control */ #define SLCDC_CMD_SLPIN 0x95 /*!<10.sleep in */ #define SLCDC_CMD_SLPOUT 0x94 /*!<11.sleep out */ #define SLCDC_CMD_SD_PSET 0x75 /*!<12.page address set */ #define SLCDC_CMD_SD_CSET 0x15 /*!<14.column address set */ #define SLCDC_CMD_DATCTL 0xbc /*!<16.data scan direction, etc. */ //#define SLCDC_CMD_DATCTL 0xbc00 /*!<16.data scan direction, etc.*/ #define SLCDC_CMD_RAMWR 0x5c /*!<17.writing to memory */ #define SLCDC_CMD_PTLIN 0xa8 /*!<19.partial display in */ #define SLCDC_CMD_PTLOUT 0xa9 /*!<20.partial display out */ /* value different from 10043 but same as 50052 */ #define SLCDC_CMD_VOLCTR 0xc6 /*!<25.Electronic volume control */ /* commands not found in 10043 but in 50052*/ #define SLCDC_CMD_GCP64 0xcb /*!<6. 64 grayscale pulse positon set */ #define SLCDC_CMD_GCP16 0xcc /*!<7. 16 grayscale pulse positon set */ #define SLCDC_CMD_GSSET 0xcd /*!<8. grayscale set */ #define SLCDC_CMD_RAMRD 0x5d /*!<18.memory read */ #define SLCDC_CMD_ASCSET 0xaa /*!<21.area scroll set */ #define SLCDC_CMD_SCSTART 0xab /*!<22.scroll start set */ #define SLCDC_CMD_EPCTIN 0x61 /*!<26.Power IC control for EVR */ #define SLCDC_CMD_EPCTOUT 0x62 /*!<27.Power IC control for EVR */ #endif /**@}*/ #define SLCDC_IRQ INT_SLCDC #define SLCDC_CMD_MEM_SIZE 4 #define SLCDC_WIDTH 176 #define SLCDC_HIGH 220 #define SLCDC_BPP 16 #define SLCDC_PIXEL_MEM_SIZE (SLCDC_WIDTH*SLCDC_HIGH*SLCDC_BPP)/8 #define SLCDC_MEM_SIZE (SLCDC_WIDTH*SLCDC_HIGH) #define _SLCDC_DATA_SIZE_ (SLCDC_PIXEL_MEM_SIZE + 32) #define SLCDC_DATA_MEM_SIZE \ ((unsigned)(PAGE_ALIGN(_SLCDC_DATA_SIZE_ + PAGE_SIZE * 2))) //bit mask definition in STAT/CTRL register #define SLCDC_TRANSFER_BUSY 0x4 #define SLCDC_TRANSFER_ERROR 0x10 //<<<<<< Global Variable /*used for SLCDC data buffer */ __attribute__ ((aligned(4))) u16 *g_slcdc_dbuffer_address; /* physical address for SLCDC data buffer*/ __attribute__ ((aligned(4))) u16 *g_slcdc_dbuffer_phyaddress; /* used for SLCDC command buffer */ __attribute__ ((aligned(4))) u16 *g_slcdc_cbuffer_address; /* physical address for SLCDC command buffer */ __attribute__ ((aligned(4))) u16 *g_slcdc_cbuffer_phyaddress; static wait_queue_head_t slcdc_wait; static int slcdc_device_num; static int g_slcdc_status; static int slcdc_major; module_param(slcdc_major, int, 0444); MODULE_PARM_DESC(slcdc_major, "slcdc char device major number. If this number is" " set to zero, then automatic major number allocation will be done"); static int slcdc_minor; module_param(slcdc_minor, int, 0444); MODULE_PARM_DESC(slcdc_minor, "slcdc char device minor number"); #define SLCDC_OPEN_STATUS 0x0001 #define SLCDC_SUSPEND_STATUS 0x0002 #define SLCDC_MAJOR_NUM 10 /*!< SLCDC char dev default major number */ #define SLCDC_MINOR_NUM 156 /*!< SLCDC char dev default minor number */ #define FBCON_HAS_CFB4 #define FBCON_HAS_CFB8 #define FBCON_HAS_CFB16 static struct cdev slcdc_dev; extern void gpio_slcdc_active(int type); extern void gpio_slcdc_inactive(int type); int slcdc_open(struct inode *inode, struct file *filp); int slcdc_release(struct inode *inode, struct file *filp); static int slcdc_ioctl(struct inode *inode, struct file *filp, u_int cmd, u_long arg); static int slcdc_mmap(struct file *filp, struct vm_area_struct *vma); static void __init _init_fbinfo(void); typedef struct { u16 *screen_start_address; u16 *v_screen_start_address; } slcdc_par_t; slcdc_par_t slcdc_par; struct file_operations g_slcdc_fops = { open:slcdc_open, release:slcdc_release, ioctl:slcdc_ioctl, mmap:slcdc_mmap, }; //>>>>>> Global Variable #ifdef DBMX_DEBUG #define FUNC_START TRACE(KERN_ERR"start of %s\n", __FUNCTION__); #define FUNC_END TRACE(KERN_ERR"end of %s\n", __FUNCTION__); #else #define FUNC_START #define FUNC_END #endif #define RED 0xf00 #define GREEN 0xf0 #define BLUE 0x0f /**@brief Local LCD controller parameters*/ struct slcdcfb_par { u_char *screen_start_address; /*!< Screen Start Address */ u_char *v_screen_start_address; /*!< Virtul Screen Start Address */ unsigned long screen_memory_size; /*!< Screen memory size */ unsigned int palette_size; /*!vm_start; unsigned long size = (unsigned long)(vma->vm_end - vma->vm_start); if (size > SLCDC_DATA_MEM_SIZE) return -EINVAL; TRACE("slcdc_mmap is called 1\n"); pos = (unsigned long)slcdc_par.v_screen_start_address; while (size > 0) { page = virt_to_phys((void *)pos); vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); /* This is an IO map - tell maydump to skip this VMA */ vma->vm_flags |= VM_IO; if (remap_pfn_range (vma, start, page >> PAGE_SHIFT, PAGE_SIZE, PAGE_SHARED)) return -EAGAIN; start += PAGE_SIZE; pos += PAGE_SIZE; size -= PAGE_SIZE; } return 0; } /** *@brief slcdc mmap function called from framebuffer * * Function Name: slcdcfb_mmap * * * Description: This is the memory map routine for this driver,will setup the * memory map used in this driver. * *@return 0 on success any other value for failure **/ static int slcdcfb_mmap(struct fb_info *info, struct file *file, struct vm_area_struct *vma) { return slcdc_mmap(file, vma); } /** *@brief slcdc display-on function * * Function Name: slcdc_display_on * * Description:This helper routine will send the command to the SLCD for display * on. * **/ void slcdc_display_on(void) { u16 *databuffer = NULL; __raw_writel((u32) g_slcdc_cbuffer_phyaddress, IO_ADDRESS(SLCDC_BASE_ADDR + 0x00)); /* put cmd into cmd buffer */ databuffer = g_slcdc_cbuffer_address; /* put cmd into cmd buffer */ *databuffer = SLCDC_CMD_DISON; /* send cmd */ slcdc_send_cmd(1); /* delay */ slcdc_delay(0xffff); /* send cmd */ slcdc_send_cmd(1); /* delay */ slcdc_delay(0xffff); } /** *@brief slcdc display-off function * * Function Name: slcdc_display_off * * Description:This helper routine will send the command to the SLCD for display * off. * **/ void slcdc_display_off(void) { u16 *databuffer = NULL; /* Cause LCD module to enter sleep mode.Bu sure to input display OFF command to turn off the display before inputting the SLPIN command Keep the logic power supply turned on for 40ms after the LCD module enters the sleep mode. */ /* put cmd into cmd buffer */ databuffer = slcdc_par.v_screen_start_address; /* send cmd SLPIN */ *databuffer = SLCDC_CMD_SLPIN; slcdc_send_cmd(1); /* put reset high */ #if 0 #ifdef SLCDC_MUX_SSI3_SLCDC2 /* set reset pin to high */ _reg_GPIO_DR(GPIOA) |= 0x80000000; #endif #ifdef SLCDC_MUX_SD_SLCDC1 /* set reset pin to high */ _reg_GPIO_DR(GPIOB) |= 0x00000020; #endif #endif /* delay for 50ms */ slcdc_delay(0xffff); /* put cmd into cmd buffer */ *databuffer = SLCDC_CMD_DISOFF; /* send cmd DISOFF */ slcdc_send_cmd(1); TRACE("disoff \n"); slcdc_delay(0xffff); } /** *@brief slcdc display-off function * * Function Name: slcdc_display_normal * * Description:This helper routine will send the command to the SLCD for display * normal. * **/ void slcdc_display_normal(void) { u16 *databuffer = NULL; databuffer = slcdc_par.v_screen_start_address; *databuffer = SLCDC_CMD_DISNOR; /* send cmd */ slcdc_send_cmd(1); /* delay */ slcdc_delay(100); } /** *@brief slcdc display sleep out function * * Function Name: slcdc_sleep_out * * Description:This helper routine will send the command to the SLCD for wakeup * from sleep. * **/ void slcdc_sleep_out(void) { u16 *databuffer = NULL; databuffer = slcdc_par.v_screen_start_address; *databuffer = SLCDC_CMD_SLPOUT; /* send cmd */ slcdc_send_cmd(1); /* delay */ slcdc_delay(0xffff); } /** @brief sends control command to SLCD */ void slcdc_display_ctl(void) { int i; #ifdef SLCDC_SERIAL_MODE u16 disctl[11] = { 0x1c00, 0x0200, 0x8200, 0x0000, 0x1e00, 0xe000, 0x0000, 0xdc00, 0x0000, 0x0200, 0x0000 }; #else u16 disctl[11] = { 0x1c, 0x02, 0x82, 0x00, 0x1e, 0xe0, 0x00, 0xdc, 0x00, 0x02, 0x00 }; #endif u16 *databuffer = NULL; /* It make various display timing settings. */ databuffer = slcdc_par.v_screen_start_address; /* put cmd into cmd buffer */ *databuffer = SLCDC_CMD_DISCTL; /* send cmd */ slcdc_send_cmd(1); slcdc_delay(2000); /* put parameter into data buffer */ for (i = 0; i < 11; i++) { *databuffer = disctl[i]; databuffer++; } /* send data */ slcdc_send_data(11); } /** *@brief slcd page set function * * Function Name: slcdc_page_set * * Description:This helper routine will send the command to the SLCD to set the * page inside video ram * **/ void slcdc_page_set(void) { /* It specify a page address area in order to access the display data RAM from the MPU. Be sure to set both starting and ending pages. */ int i; u16 *databuffer = NULL; #ifdef SLCDC_SERIAL_MODE u16 pset[4] = { 0x0000, 0x0000, 0xb100, 0x0000 }; #else u16 pset[4] = { 0x00, 0x00, 0xb1, 0x00 }; #endif databuffer = (u16 *) (slcdc_par.v_screen_start_address); /* put cmd into cmd buffer */ *databuffer = SLCDC_CMD_SD_PSET; /* send cmd */ slcdc_send_cmd(1); slcdc_delay(20000); /* put parameter into data buffer */ for (i = 0; i < 4; i++) { *databuffer = pset[i]; databuffer++; } /* send data */ slcdc_send_data(4); slcdc_delay(20000); } /** *@brief slcd column set function * * Function Name: slcdc_col_set * * Description:This helper routine will send the command to the SLCD to set the * column inside video ram * **/ void slcdc_col_set(void) { /* It specify starting and ending collumns. */ int i; #ifdef SLCDC_SERIAL_MODE u16 cset[4] = { 0x0200, 0x0000, 0xb100, 0x0000 }; #else u16 cset[4] = { 0x02, 0x00, 0xb1, 0x00 }; #endif u16 *databuffer = NULL; databuffer = (u16 *) (slcdc_par.v_screen_start_address); /* put cmd into cmd buffer */ *databuffer = SLCDC_CMD_SD_CSET; /* send cmd */ slcdc_send_cmd(1); slcdc_delay(20000); /* put parameter into data buffer */ for (i = 0; i < 4; i++) { *databuffer = cset[i]; databuffer++; } /* send data */ slcdc_send_data(4); slcdc_delay(20000); } void slcdc_data_ctl(void) { u8 *databuffer = NULL; databuffer = (u8 *) (slcdc_par.v_screen_start_address); *databuffer = SLCDC_CMD_DATCTL; /* send cmd */ slcdc_send_cmd(1); slcdc_delay(20000); *databuffer = 0x28; /* send data */ slcdc_send_data(1); slcdc_delay(20000); } void slcdc_volctl(void) { u16 *databuffer = NULL; databuffer = slcdc_par.v_screen_start_address; *databuffer = SLCDC_CMD_VOLCTR; /* send cmd */ slcdc_send_cmd(1); slcdc_delay(20000); #ifdef SLCDC_SERIAL_MODE *databuffer = 0x9f00; #else *databuffer = 0x9f; #endif /* send data */ slcdc_send_data(1); slcdc_delay(20000); } void slcdc_ascset(void) { int i; #ifdef SLCDC_SERIAL_MODE u16 lcd_para_ASCSET[7] = { 0x0000, 0x0000, 0x1f00, 0x0100, 0x1f00, 0x0100, 0x0300 }; #else u16 lcd_para_ASCSET[7] = { 0x00, 0x00, 0x1f, 0x01, 0x1f, 0x01, 0x03 }; #endif u16 *databuffer = NULL; databuffer = (u16 *) (slcdc_par.v_screen_start_address); /* put cmd into cmd buffer */ *databuffer = SLCDC_CMD_ASCSET; /* put parameter into data buffer */ /* send cmd */ slcdc_send_cmd(1); slcdc_delay(2000); for (i = 0; i < 7; i++) { *databuffer = lcd_para_ASCSET[i]; databuffer++; } /* send data */ slcdc_send_data(7); slcdc_delay(20000); } void slcdc_scstart(void) { int i; u16 lcd_para_SCSTART[2] = { 0x00, 0x00 }; u16 *databuffer = NULL; databuffer = (u16 *) (slcdc_par.v_screen_start_address); /* put cmd into cmd buffer */ *databuffer = SLCDC_CMD_SCSTART; /* put parameter into data buffer */ /* send cmd */ slcdc_send_cmd(1); slcdc_delay(10000); for (i = 0; i < 2; i++) { *databuffer = lcd_para_SCSTART[i]; databuffer++; } /* send data */ slcdc_send_data(2); slcdc_delay(20000); } void slcdc_config_panel(void) { int i; #if 1 /* set data format */ slcdc_config(8); slcdc_delay(0xffff); slcdc_reset(1); /* pull reset signal high */ /* set data format */ slcdc_delay(20000); slcdc_data_ctl(); slcdc_delay(1000); slcdc_config(16); /* sleep out */ slcdc_sleep_out(); slcdc_delay(20000); for (i = 0; i < 8; i++) { slcdc_volctl(); slcdc_delay(2000); } slcdc_delay(0xffff); slcdc_delay(0xffff); slcdc_delay(0xffff); slcdc_display_on(); slcdc_delay(0xffff); /* set col address */ slcdc_col_set(); /* set page address */ slcdc_page_set(); /* set area in screen to be used for scrolling */ slcdc_ascset(); slcdc_delay(20000); /* set top scroll page within the scroll area */ slcdc_scstart(); mdelay(4); #endif #if 0 //set data format slcdc_config(8); slcdc_delay(0xffff); slcdc_reset(1); //pull reset signal high //set data format slcdc_delay(20000); slcdc_data_ctl(); slcdc_delay(1000); slcdc_config(16); //sleep out slcdc_sleep_out(); slcdc_delay(0xffff); //set col address slcdc_col_set(); //set page address slcdc_page_set(); //set area in screen to be used for scrolling slcdc_ascset(); slcdc_delay(20000); //set top scroll page within the scroll area slcdc_scstart(); slcdc_delay(0xffff); //turn on slcd display slcdc_display_on(); slcdc_delay(0xffff); slcdc_delay(0xffff); for (i = 0; i < 8; i++) { slcdc_volctl(); slcdc_delay(2000); } #endif } /** *@brief slcdc ioctl routine * * Function Name: slcdc_ioctl * Description:This routine will implement driver-specific functions * *@param inode : the pointer to driver-related inode. *@param filp : the pointer to driver-related file structure. *@param cmd : the command number. *@param arg: argument which depends on command. * *@return int return status * @li 0 sucess * @li 1 failure * * Modification History: * Dec,2003 Karen first version for MX21 TO2 * **/ static int slcdc_ioctl(struct inode *inode, struct file *filp, u_int cmd, u_long arg) { u16 *databuffer = NULL; switch (cmd) { case SLCDC_CMD_DISON: slcdc_display_on(); break; case SLCDC_CMD_DISOFF: slcdc_display_off(); break; case SLCDC_CMD_DISNOR: slcdc_display_normal(); break; case SLCDC_CMD_DISINV: /* put cmd into cmd buffer */ databuffer = slcdc_par.v_screen_start_address; *databuffer = SLCDC_CMD_DISINV; /* send cmd */ slcdc_send_cmd(1); slcdc_delay(100); break; case SLCDC_CMD_DATCTL: break; case SLCDC_CMD_RAMWR: /* Causes the MPU to be a data entry mode,allowing it to serite data in the display memory. Inputting any other cmds other than NOP cancels the data entry mode. */ __raw_writel((u32) g_slcdc_cbuffer_phyaddress, IO_ADDRESS(SLCDC_BASE_ADDR + 0x00)); /* _reg_SLCDC_DBADDR */ /* put cmd into cmd buffer */ databuffer = g_slcdc_cbuffer_address; /* put cmd into cmd buffer */ *databuffer = SLCDC_CMD_RAMWR; /* send cmd */ slcdc_send_cmd(1); slcdc_delay(2000); /* this is to display one data per time, it is ok. */ slcdc_delay(0xffff); __raw_writel((u32) slcdc_par.screen_start_address, IO_ADDRESS(SLCDC_BASE_ADDR + 0x00)); /* _reg_SLCDC_DBADDR */ slcdc_delay(0xffff); slcdc_send_data(SLCDC_MEM_SIZE); slcdc_delay(0xffff); break; case SLCDC_CMD_RAMRD: break; case SLCDC_CMD_PTLIN: /* This command is used to display a partial screen for power saving. */ break; case SLCDC_CMD_PTLOUT: /* This command is used to exit the partila diaplay mode. */ break; case SLCDC_CMD_GCP64: /* make 63 pulse position settings of GCP for 64 gray scales. */ /* send cmd into cmd buffer send parameter into data buffer send cmd send data */ break; case SLCDC_CMD_GCP16: break; case SLCDC_CMD_GSSET: break; case SLCDC_CMD_ASCSET: /* make partial screen scroll settings. */ /* send cmd into cmd buffer send parameter into data buffer send cmd delay send data delay */ break; case SLCDC_CMD_SCSTART: /* set a scroll starting page in the scrolling area.Be sure to send this cmd after ASCSET . */ /* send cmd into cmd buffer send parameter into data buffer send cmd delay send data delay */ break; default: break; } return 0; } static int slcdcfb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg, struct fb_info *info) { return slcdc_ioctl(inode, file, cmd, arg); } /** *@brief slcdc close function * * Function Name: slcdc_release * * * Description: This is the release routine for the driver. And this function \n * will be called while the module being closed. In this function, it will\n * unregister the apm * *@param inode the pointer to the inode descripter *@param filp the pointer to the file descripter * *@return int return status * @li 0 sucessful * @li other failed * Modification History: * Dec,2003 Karen first version * **/ int slcdc_release(struct inode *inode, struct file *filp) { TRACE("slcdc_release: ----\n"); del_timer_sync(&slcdc_timer); return 0; } /** *@brief slcdc timer call back function * * Function Name: slcdc_timer_func * * Description:This helper routine prepare the SLCDC for data transfer * **/ static void slcdc_timer_func(unsigned long args) { volatile unsigned long reg; /* Causes the MPU to be a data entry mode,allowing it to serite data in the display memory. Inputting any other cmds other than NOP cancels the data entry mode. */ __raw_writel((u32) g_slcdc_cbuffer_phyaddress, IO_ADDRESS(SLCDC_BASE_ADDR + 0x00)); /* _reg_SLCDC_DBADDR */ /* put cmd into cmd buffer */ *g_slcdc_cbuffer_address = SLCDC_CMD_RAMWR; /* send cmd */ slcdc_send_cmd(1); slcdc_delay(2000); /* this is to display one data per time, it is ok. */ slcdc_delay(0xffff); __raw_writel((u32) slcdc_par.screen_start_address, IO_ADDRESS(SLCDC_BASE_ADDR + 0x00)); /* _reg_SLCDC_DBADDR */ slcdc_delay(0xffff); /* slcdc_send_data( SLCDC_MEM_SIZE); */ /* enable interrupt */ #ifdef USING_INTERRUPT_SLCDC reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20)); /*_reg_SLCDC_LCD_CTRL_STAT*/ reg |= ~0xffffff7f; __raw_writel(reg, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20)); #endif /* set length */ __raw_writel(SLCDC_MEM_SIZE, IO_ADDRESS(SLCDC_BASE_ADDR + 0x04)); /* _reg_SLCDC_DBUF_SIZE */ /* set automode 01 for data */ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20)); /*_reg_SLCDC_LCD_CTRL_STAT*/ reg |= 0x00000800; __raw_writel(reg, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20)); /* set GO */ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20)); /*_reg_SLCDC_LCD_CTRL_STAT*/ reg |= 0x1; __raw_writel(reg, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20)); slcdc_delay(0xffff); return; } /** *@brief slcdc timer initialization * * Function Name: init_slcdc_timer * * Description:This helper routine prepare the SLCDC timer for refreshing the * screen * **/ static void init_slcdc_timer(void) { init_timer(&slcdc_timer); slcdc_timer.expires = jiffies + SLCDC_REFRESH_RATE; slcdc_timer.data = 0; slcdc_timer.function = slcdc_timer_func; add_timer(&slcdc_timer); start_fb_timer_flag = 1; } /** *@brief slcdc open function * * Function Name: slcdc_open * * * Description: This is the open routine for the driver. And this function \n * will be called while the module being opened. In this function, it will\n * @li configure GPIO for serial/parallel * @li slcdc reset * @li init slcd registers * @li init waitqueue * @li get rca, select the card * @li send some command for panel configuration * *@param inode the pointer to the inode descripter *@param filp the pointer to the file descripter * *@return int return status * @li 0 sucessful * @li other failed * Modification History: * Dec,2003, Karen first version * June,2004, Shirley update for Parallel mode * **/ int slcdc_open(struct inode *inode, struct file *filp) { volatile unsigned long reg; TRACE("slcdc_open: ----\n"); /* init dev */ #ifdef SLCDC_SERIAL_MODE slcdc_gpio_serial(); #else slcdc_gpio_paralle(); #endif slcdc_init_dev(); slcdc_reset(0); /*pull reset low */ /* init slcd registers */ slcdc_init_reg(); /* init waitqueue */ init_waitqueue_head(&slcdc_wait); /* send some command for panel configuration */ slcdc_config_panel(); reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x1C)); /*_reg_SLCDC_LCD_TXCONFIG*/ TRACE("TRANS_CONFIG_REG=%x \n", (unsigned int)reg); /* init slcdc timer, and start timer */ init_slcdc_timer(); return 0; } /** *@brief slcdc init function * * Function Name: slcdc_init * * *@return int return status * @li 0 sucess * @li other failure * * Description: This is the initialization routine for the driver. And this * function \n will be called while the module being installed. * In this function, it will \n register char device,request * slcdc irq, initialize the buffer,register to\npower management. * * Modification History: * Dec,2003, Karen update for MX21 TO2 * **/ int __init slcdc_init(void) { int tmp, err; INFO("SLCDC Driver \n"); INFO("Motorola SPS-SuZhou \n"); _init_fbinfo(); if (slcdcfb_set_var(&slcdc_fb_info)) ; /* current_par.allow_modeset = 0; */ register_framebuffer(&slcdc_fb_info); devfs_mk_cdev(slcdc_device_num, S_IFCHR | S_IRUGO | S_IWUSR, "slcdc"); if (slcdc_major != 0) { /* use user supplied major number */ slcdc_device_num = MKDEV(slcdc_major, slcdc_minor); err = register_chrdev_region(slcdc_device_num, 1, MODULE_NAME); } else { /* auto create device major number */ err = alloc_chrdev_region(&slcdc_device_num, slcdc_minor, 1, MODULE_NAME); } if (err < 0) { TRACE("%s driver: Unable to register chrdev region\n", MODULE_NAME); return err; } cdev_init(&slcdc_dev, &g_slcdc_fops); slcdc_dev.owner = THIS_MODULE; slcdc_dev.ops = &g_slcdc_fops; if ((err = cdev_add(&slcdc_dev, slcdc_device_num, 1))) { TRACE ("%s driver: Unable to create character device. Error code=%d\n", MODULE_NAME, err); return -ENODEV; } /* init interrupt */ tmp = request_irq(SLCDC_IRQ, (void *)slcdc_isr, SA_INTERRUPT | SA_SHIRQ, MODULE_NAME, MODULE_NAME); if (tmp) { printk("slcdc_init:cannot init major= %d irq=%d\n", MAJOR(slcdc_device_num), SLCDC_IRQ); devfs_remove(MODULE_NAME); cdev_del(&slcdc_dev); return -1; } /* init buffer */ /* initialize buffer address */ g_slcdc_dbuffer_address = NULL; g_slcdc_dbuffer_phyaddress = NULL; g_slcdc_cbuffer_address = NULL; g_slcdc_cbuffer_phyaddress = NULL; slcdc_init_buffer(); g_slcdc_status = 0; return 0; } /** *@brief slcdc cleanup function * * Function Name: slcdc_cleanup * *@return None * * Description: This is the cleanup routine for the driver. And this function \n * will be called while the module being removed. In this function, it will \n * cleanup all the registered entries * * Modification History: * Dec 2003, Karen update for MX21 TO2 * **/ void __exit slcdc_cleanup(void) { unregister_chrdev_region(slcdc_device_num, 1); devfs_remove(MODULE_NAME); /*Do some cleanup work */ free_irq(SLCDC_IRQ, MODULE_NAME); unregister_framebuffer(&slcdc_fb_info); slcdc_free_buffer(); cdev_del(&slcdc_dev); return; } module_init(slcdc_init); module_exit(slcdc_cleanup); /*////////////// Frame Buffer Suport /////////////////////////////////// */ /** * _check_var - Validates a var passed in. * @var: frame buffer variable screen structure * @info: frame buffer structure that represents a single frame buffer * * Checks to see if the hardware supports the state requested by var passed * in. This function does not alter the hardware state! If the var passed in * is slightly off by what the hardware can support then we alter the var * PASSED in to what we can do. If the hardware doesn't support mode change * a -EINVAL will be returned by the upper layers. * * Returns negative errno on error, or zero on success. */ static int _check_var(struct fb_var_screeninfo *var, struct fb_info *info) { const struct slcdcfb_par *par = (const struct slcdcfb_par *)info->par; if (var->xres > current_par.max_xres) var->xres = current_par.max_xres; if (var->yres > current_par.max_yres) var->yres = current_par.max_yres; var->xres_virtual = var->xres_virtual < par->xres ? par->xres : var->xres_virtual; var->yres_virtual = var->yres_virtual < par->yres ? par->yres : var->yres_virtual; var->bits_per_pixel = par->bits_per_pixel; switch (var->bits_per_pixel) { case 2: case 4: case 8: var->red.length = 4; var->green = var->red; var->blue = var->red; var->transp.length = 0; break; case 12: /* RGB 444 */ case 16: /* RGB 565 */ TRACE("16->a\n"); var->red.length = 4; var->blue.length = 4; var->green.length = 4; var->transp.length = 0; #ifdef __LITTLE_ENDIAN TRACE("16->b\n"); var->red.offset = 8; var->green.offset = 4; var->blue.offset = 0; var->transp.offset = 0; #endif /* __LITTLE_ENDIAN */ break; default: return -EINVAL; } /* *var->screen_start_address=(u_char*)((u_long)g_slcdc_dbuffer_phyaddress ); *var->v_screen_start_address=(u_char*)((u_long)g_slcdc_dbuffer_address ); */ var->height = -1; var->width = -1; var->grayscale = 0; var->nonstd = 0; var->pixclock = -1; var->left_margin = -1; var->right_margin = -1; var->upper_margin = -1; var->lower_margin = -1; var->hsync_len = -1; var->vsync_len = -1; var->vmode = FB_VMODE_NONINTERLACED; var->sync = 0; return 0; } /** *@brief Use current_par to set a var structure * *@param var Input var data *@param par LCD controller parameters * *@return If no error, return 0 * */ static int _encode_var(struct fb_var_screeninfo *var, struct slcdcfb_par *par) { FUNC_START /* Don't know if really want to zero var on entry. Look at set_var to see. If so, may need to add extra params to par */ memset(var, 0, sizeof(*var)); var->xres = par->xres; TRACE("var->xress=%d\n", var->xres); var->yres = par->yres; TRACE("var->yres=%d\n", var->yres); var->xres_virtual = par->xres_virtual; TRACE("var->xres_virtual=%d\n", var->xres_virtual); var->yres_virtual = par->yres_virtual; TRACE("var->yres_virtual=%d\n", var->yres_virtual); var->bits_per_pixel = par->bits_per_pixel; TRACE("var->bits_per_pixel=%d\n", var->bits_per_pixel); switch (var->bits_per_pixel) { case 2: case 4: case 8: var->red.length = 4; var->green = var->red; var->blue = var->red; var->transp.length = 0; break; case 12: /* This case should differ for Active/Passive mode */ case 16: TRACE("16->a\n"); var->red.length = 4; var->blue.length = 4; var->green.length = 4; var->transp.length = 0; #ifdef __LITTLE_ENDIAN TRACE("16->b\n"); var->red.offset = 8; var->green.offset = 4; var->blue.offset = 0; var->transp.offset = 0; #endif /* __LITTLE_ENDIAN */ break; } FUNC_END return 0; } /** *@brief Get the video params out of 'var'. If a value doesn't fit, * round it up,if it's too big, return -EINVAL. * *@warning Round up in the following order: bits_per_pixel, xres, * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale, * bitfields, horizontal timing, vertical timing. * *@param var Input var data *@param par LCD controller parameters * *@return If no error, return 0 */ static int _decode_var(struct fb_info *info) { struct fb_var_screeninfo *var = &info->var; struct slcdcfb_par par_var; struct slcdcfb_par *par = &par_var; FUNC_START *par = current_par; if ((par->xres = var->xres) < MIN_XRES) par->xres = MIN_XRES; if ((par->yres = var->yres) < MIN_YRES) par->yres = MIN_YRES; if (par->xres > current_par.max_xres) par->xres = current_par.max_xres; if (par->yres > current_par.max_yres) par->yres = current_par.max_yres; par->xres_virtual = var->xres_virtual < par->xres ? par->xres : var->xres_virtual; par->yres_virtual = var->yres_virtual < par->yres ? par->yres : var->yres_virtual; par->bits_per_pixel = var->bits_per_pixel; switch (par->bits_per_pixel) { case 4: par->visual = FB_VISUAL_PSEUDOCOLOR; par->palette_size = 16; break; case 8: par->visual = FB_VISUAL_PSEUDOCOLOR; par->palette_size = 256; break; case 12: /* RGB 444 */ case 16: /* RGB 565 */ par->visual = FB_VISUAL_TRUECOLOR; par->palette_size = 0; break; default: return -EINVAL; } par->screen_start_address = (u_char *) ((u_long) g_slcdc_dbuffer_phyaddress); par->v_screen_start_address = (u_char *) ((u_long) g_slcdc_dbuffer_address); /* update_lcd ? */ FUNC_END return 0; } /** *@brief Set current_par by var, also set display data, specially the console * related file operations, then enable the SLCD controller, and set cmap to * hardware. * *@param var Iuput data pointer *@param con Console ID *@param info Frame buffer information * *@return If no error, return 0. * **/ static int slcdcfb_set_var(struct fb_info *info) { struct display *display; int err; struct slcdcfb_par par; struct fb_var_screeninfo *var = &info->var; FUNC_START; display = &global_disp; /* Default display settings */ /* Decode var contents into a par structure, adjusting any */ /* out of range values. */ if ((err = _decode_var(info))) { TRACE("decode var error!"); return err; } /* Store adjusted par values into var structure */ _encode_var(var, &par); if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_TEST) return 0; else if (((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) && ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NXTOPEN)) return -EINVAL; display->inverse = 0; init_var = *var; /* TODO:gcc support structure copy? */ FUNC_END; return 0; } /** *@brief Blank the screen, if blank, disable LCD controller, while if no blank * set cmap and enable LCD controller * *@param blank Blank flag *@param info Frame buffer database * *@return VOID */ static int slcdcfb_blank(int blank, struct fb_info *info) { if (blank) { slcdc_display_off(); } else { slcdc_display_normal(); } return 0; } /** *@brief Initialize frame buffer. While 16bpp is used to store a 12 bits pixels * packet, it is not a really 16bpp system, maybe in-compatiable with * other system or GUI.There are some field in var which specify * the red/green/blue offset in a 16bit word, just little endian is * concerned * *@return VOID **/ static void __init _init_fbinfo(void) { FUNC_START; slcdc_fb_info.node = -1; slcdc_fb_info.flags = 0; /* Low-level driver is not a module */ slcdc_fb_info.fbops = &slcdcfb_ops; slcdc_fb_info.monspecs = monspecs; strcpy(slcdc_fb_info.fix.id, "SLCDC"); /* FIXME... set fix parameters */ /* * * setup initial parameters * */ memset(&init_var, 0, sizeof(init_var)); memset(¤t_par, 0, sizeof(current_par)); init_var.transp.length = 0; init_var.nonstd = 0; init_var.activate = FB_ACTIVATE_NOW; init_var.xoffset = 0; init_var.yoffset = 0; init_var.height = -1; init_var.width = -1; init_var.vmode = FB_VMODE_NONINTERLACED; /*xres and yres might be set when loading the module, if this driver is built as module */ current_par.max_xres = SLCDC_WIDTH; current_par.max_yres = SLCDC_HIGH; current_par.max_bpp = SLCDC_BPP; init_var.red.length = 5; init_var.green.length = 6; init_var.blue.length = 5; #ifdef __LITTLE_ENDIAN init_var.red.offset = 11; init_var.green.offset = 5; init_var.blue.offset = 0; #endif /* __LITTLE_ENDIAN */ init_var.grayscale = 16; init_var.sync = 0; init_var.pixclock = 171521; current_par.screen_start_address = NULL; current_par.v_screen_start_address = NULL; current_par.screen_memory_size = MAX_PIXEL_MEM_SIZE; current_par.currcon = -1; init_var.xres = current_par.max_xres; init_var.yres = current_par.max_yres; init_var.xres_virtual = init_var.xres; init_var.yres_virtual = init_var.yres; init_var.bits_per_pixel = current_par.max_bpp; current_par.xres = init_var.xres; current_par.yres = init_var.yres; current_par.xres_virtual = init_var.xres; current_par.yres_virtual = init_var.yres; /* initialize current screen information */ memcpy(&slcdc_fb_info.var, &init_var, sizeof(init_var)); FUNC_END; } /** *@brief slcdc framebuffer open call * * Function Name: slcdcfb_open * * Description : This function is called by the framebuffer when the * application requests. * *@return 0 on success any other value for failure **/ static int slcdcfb_open(struct fb_info *info, int user) { return slcdc_open(NULL, 0); } /** *@brief slcdc framebuffer release call * * Function Name: slcdcfb_release * * Description : This function is called by the framebuffer when the * application closes the link to framebuffer. * *@return 0 on success any other value for failure **/ static int slcdcfb_release(struct fb_info *info, int user) { start_fb_timer_flag = 0; return slcdc_release(NULL, 0); } /*\@}*/ MODULE_PARM(irq_mask, "i"); MODULE_PARM(irq_list, "1-4i"); MODULE_LICENSE("GPL");