Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * ZTE U733 Framebuffer
- *
- *
- * Original: Copyright (c) 2009 Jean-Christian de Rivaz
- *
- * SPI mods, console support, 400x240 instead of 240x320:
- * Copyright (c) 2012 Jeroen Domburg <jeroen@spritesmods.com>
- *
- * Bits and pieces borrowed from the fsl-zteu733.c:
- * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
- * Author: Alison Wang <b18965@freescale.com>
- * Jason Jin <Jason.jin@freescale.com>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * The Solomon Systech ZTEU733 chip drive TFT screen up to 240x400.
- *
- * This driver uses only SPI mode:
- *
- * A SPI port with modalias 'zteu733' should exist. The LCD should be in 16-bit
- * mode (which they usually are hardwired to be...) and connected to three
- * CD4094s and a CD4020. For schematic, check out
- * http://spritesmods.com/?art=spitft
- *
- * LCDs in their own, native SPI mode aren't supported yet, mostly because I
- * can't get my hands on a cheap one.
- */
- #define DEBUG
- #include <linux/kernel.h>
- #include <linux/device.h>
- #include <linux/module.h>
- #include <linux/platform_device.h>
- #include <linux/mm.h>
- #include <linux/vmalloc.h>
- #include <linux/fb.h>
- #include <asm/io.h>
- #include <linux/spi/spi.h>
- #include <linux/delay.h>
- #define ZTEU733_REG_SLEEP_MODE 0x10
- #define ZTEU733_REG_SET_COLUMN_ADDR 0x2A
- #define ZTEU733_REG_SET_PAGE_ADDR 0x2B
- #define ZTEU733_REG_SET_WRMEM_START 0x2C
- struct zteu733_page {
- unsigned short x;
- unsigned short y;
- unsigned short *buffer;
- unsigned short len;
- int must_update;
- };
- struct zteu733 {
- struct device *dev;
- struct spi_device *spidev;
- struct fb_info *info;
- unsigned int pages_count;
- struct zteu733_page *pages;
- unsigned long pseudo_palette[17];
- int backlight;
- };
- /*
- These two routines will write to the SPI-port, shifting data into the 4094 shift
- registers.
- */
- #define CD4094_RD (1<<0)
- #define CD4094_WR (1<<1)
- #define CD4094_BCNT (1<<2)
- #define CD4094_RST (1<<3)
- #define CD4094_DC (1<<4)
- #define CD4094_INVERT (CD4094_RD|CD4094_WR|CD4094_RST)
- /*
- This routine will write a single 16-bit value, either as data or as a command
- (depends on isdata). The LCD will clock this in because the SPIs /CS goes high.
- */
- static int zteu733_spi_write(struct zteu733 *item, unsigned short value,
- unsigned int isdata)
- {
- u8 buf[3];
- buf[0]=((isdata?CD4094_DC:0)|CD4094_WR|(item->backlight?CD4094_BCNT:0))^CD4094_INVERT;
- buf[1]=(value>>8)&0xff;
- buf[2]=(value)&0xff;
- spi_write(item->spidev, buf, 3);
- return 0;
- }
- #define BLOCKLEN (4096)
- static u8 zteu733_spiblock[BLOCKLEN*4];
- /*
- This routine will clock in len words of data. The LCD will clock this in every 4th byte written
- because a 4020 will pull down the /cs at that moment.
- */
- static int zteu733_spi_write_datablock(struct zteu733 *item,
- unsigned short *block, int len)
- {
- int x;
- unsigned short value;
- //ToDo: send in parts if needed
- if (len>BLOCKLEN) {
- dev_err(item->dev, "%s: len > blocklen (%i > %i)\n",
- __func__, len, BLOCKLEN);
- len=BLOCKLEN;
- }
- for (x=0; x<len; x++) {
- value=block[x];
- zteu733_spiblock[(x*4)]=0; //dummy
- zteu733_spiblock[(x*4)+1]=(CD4094_DC|CD4094_WR|(item->backlight?CD4094_BCNT:0))^CD4094_INVERT;
- zteu733_spiblock[(x*4)+2]=(value>>8)&0xff;
- zteu733_spiblock[(x*4)+3]=(value)&0xff;
- }
- spi_write(item->spidev, zteu733_spiblock, len*4);
- return 0;
- }
- static inline void zteu733_write_reg(struct zteu733 *item, unsigned char reg)
- {
- dev_dbg(item->dev, "%s: Register 0x%02x \n", __func__, reg);
- zteu733_spi_write(item, reg&0xff, 0);
- }
- static inline void zteu733_reg_set(struct zteu733 *item, unsigned char reg,
- unsigned short value)
- {
- dev_dbg(item->dev, "%s: Register 0x%02x = 0x%04x \n", __func__, reg, value);
- zteu733_spi_write(item, reg&0xff, 0);
- zteu733_spi_write(item, value, 1);
- }
- static void zteu733_copy(struct zteu733 *item, unsigned int index)
- {
- unsigned short x;
- unsigned short y;
- unsigned short *buffer;
- unsigned int len;
- x = item->pages[index].x;
- y = item->pages[index].y;
- buffer = item->pages[index].buffer;
- len = item->pages[index].len;
- dev_dbg(item->dev,
- "%s: page[%u]: x=%3hu y=%3hu buffer=0x%p len=%3hu\n",
- __func__, index, x, y, buffer, len);
- zteu733_reg_set(item, ZTEU733_REG_SET_COLUMN_ADDR, (item->info->var.yres - 1)-y);
- zteu733_reg_set(item, ZTEU733_REG_SET_PAGE_ADDR, x);
- // zteu733_reg_set(item, ZTEU733_REG_GDDRAM_X_ADDR, (item->info->var.yres - 1)-y);
- // zteu733_reg_set(item, ZTEU733_REG_GDDRAM_Y_ADDR, x);
- zteu733_spi_write(item, ZTEU733_REG_SET_WRMEM_START, 0);
- // zteu733_spi_write(item, ZTEU733_REG_GDDRAM_DATA, 0);
- zteu733_spi_write_datablock(item, buffer, len);
- /*
- //The write_datablock can also be exchanged with this code, which is slower but
- //doesn't require the CD4020 counter IC.
- for (count = 0; count < len; count++) {
- zteu733_spi_write(item, buffer[count], 1);
- }
- */
- }
- static void zteu733_update_all(struct zteu733 *item)
- {
- unsigned short i;
- struct fb_deferred_io *fbdefio = item->info->fbdefio;
- for (i = 0; i < item->pages_count; i++) {
- item->pages[i].must_update=1;
- }
- schedule_delayed_work(&item->info->deferred_work, fbdefio->delay);
- }
- static void zteu733_update(struct fb_info *info, struct list_head *pagelist)
- {
- struct zteu733 *item = (struct zteu733 *)info->par;
- struct page *page;
- int i;
- //We can be called because of pagefaults (mmap'ed framebuffer, pages
- //returned in *pagelist) or because of kernel activity
- //(pages[i]/must_update!=0). Add the former to the list of the latter.
- list_for_each_entry(page, pagelist, lru) {
- item->pages[page->index].must_update=1;
- }
- //Copy changed pages.
- for (i=0; i<item->pages_count; i++) {
- //ToDo: Small race here between checking and setting must_update,
- //maybe lock?
- if (item->pages[i].must_update) {
- item->pages[i].must_update=0;
- zteu733_copy(item, i);
- }
- }
- }
- static void __init zteu733_setup(struct zteu733 *item)
- {
- dev_dbg(item->dev, "%s: item=0x%p\n", __func__, (void *)item);
- dev_dbg(item->dev, "%s: ------------------------------------------------------------------", __func__);
- zteu733_write_reg(item, 0xE9); // LSI Test Register
- zteu733_spi_write(item, 0x20, 1);
- mdelay(20);
- zteu733_write_reg(item, 0x01); // Sotf Reset
- mdelay(150);
- zteu733_write_reg(item, 0x11); // Exit Sleep mode
- mdelay(500);
- zteu733_write_reg(item, 0x53); // Write CTRL Display, Display Dimming = 1, Backlight = ON
- zteu733_spi_write(item, 0x08, 1);
- zteu733_write_reg(item, 0x51); // Write Display Brightness = 0x80
- zteu733_spi_write(item, 0xFF, 1);
- mdelay(1000);
- zteu733_write_reg(item, 0xD1);
- zteu733_spi_write(item, 0x00, 1);
- zteu733_spi_write(item, 0x71, 1);
- zteu733_spi_write(item, 0x19, 1);
- zteu733_write_reg(item, 0xD0);
- zteu733_spi_write(item, 0x07, 1);
- zteu733_spi_write(item, 0x01, 1);
- zteu733_spi_write(item, 0x08, 1);
- zteu733_write_reg(item, 0x36); // Set Address Mode
- zteu733_spi_write(item, 0x00, 1);
- zteu733_write_reg(item, 0x3A); // Set Pixel Format : 0x11 = 8bpp, 0x55 = 16bpp, 0x66 = 18bpp
- zteu733_spi_write(item, 0x55, 1);
- zteu733_write_reg(item, 0xC1);
- zteu733_spi_write(item, 0x10, 1);
- zteu733_spi_write(item, 0x10, 1);
- zteu733_spi_write(item, 0x02, 1);
- zteu733_spi_write(item, 0x02, 1);
- zteu733_write_reg(item, 0xC0);
- zteu733_spi_write(item, 0x00, 1);
- zteu733_spi_write(item, 0x35, 1);
- zteu733_spi_write(item, 0x00, 1);
- zteu733_spi_write(item, 0x00, 1);
- zteu733_spi_write(item, 0x01, 1);
- zteu733_spi_write(item, 0x02, 1);
- zteu733_write_reg(item, 0xC5);
- zteu733_spi_write(item, 0x04, 1);
- zteu733_write_reg(item, 0xC5);
- zteu733_spi_write(item, 0x04, 1);
- zteu733_write_reg(item, 0xD2);
- zteu733_spi_write(item, 0x01, 1);
- zteu733_spi_write(item, 0x44, 1);
- // GAMA TODO
- zteu733_write_reg(item, 0x2A);
- zteu733_spi_write(item, 0x00, 1);
- zteu733_spi_write(item, 0x00, 1);
- zteu733_spi_write(item, 0x00, 1);
- zteu733_spi_write(item, 0xEF, 1);
- zteu733_write_reg(item, 0x2B);
- zteu733_spi_write(item, 0x00, 1);
- zteu733_spi_write(item, 0x00, 1);
- zteu733_spi_write(item, 0x01, 1);
- zteu733_spi_write(item, 0x8F, 1);
- zteu733_write_reg(item, 0x29); // Display ON
- zteu733_write_reg(item, 0x20); //Exit Invert Mode
- // zteu733_write_reg(item, 0x2c); //Exit Invert Mode
- dev_dbg(item->dev, "%s: ------------------------------------------------------------------", __func__);
- }
- //This routine will allocate the buffer for the complete framebuffer. This
- //is one continuous chunk of 16-bit pixel values; userspace programs
- //will write here.
- static int __init zteu733_video_alloc(struct zteu733 *item)
- {
- unsigned int frame_size;
- dev_dbg(item->dev, "%s: item=0x%p\n", __func__, (void *)item);
- frame_size = item->info->fix.line_length * item->info->var.yres;
- dev_dbg(item->dev, "%s: item=0x%p frame_size=%u\n",
- __func__, (void *)item, frame_size);
- item->pages_count = frame_size / PAGE_SIZE;
- if ((item->pages_count * PAGE_SIZE) < frame_size) {
- item->pages_count++;
- }
- dev_dbg(item->dev, "%s: item=0x%p pages_count=%u\n",
- __func__, (void *)item, item->pages_count);
- item->info->fix.smem_len = item->pages_count * PAGE_SIZE;
- item->info->fix.smem_start =
- (unsigned long)vmalloc(item->info->fix.smem_len);
- if (!item->info->fix.smem_start) {
- dev_err(item->dev, "%s: unable to vmalloc\n", __func__);
- return -ENOMEM;
- }
- memset((void *)item->info->fix.smem_start, 0, item->info->fix.smem_len);
- return 0;
- }
- static void zteu733_video_free(struct zteu733 *item)
- {
- dev_dbg(item->dev, "%s: item=0x%p\n", __func__, (void *)item);
- vfree((void *)item->info->fix.smem_start);
- }
- //This routine will allocate a zteu733_page struct for each vm page in the
- //main framebuffer memory. Each struct will contain a pointer to the page
- //start, an x- and y-offset, and the length of the pagebuffer which is in the framebuffer.
- static int __init zteu733_pages_alloc(struct zteu733 *item)
- {
- unsigned short pixels_per_page;
- unsigned short yoffset_per_page;
- unsigned short xoffset_per_page;
- unsigned short index;
- unsigned short x = 0;
- unsigned short y = 0;
- unsigned short *buffer;
- unsigned int len;
- dev_dbg(item->dev, "%s: item=0x%p\n", __func__, (void *)item);
- item->pages = kmalloc(item->pages_count * sizeof(struct zteu733_page),
- GFP_KERNEL);
- if (!item->pages) {
- dev_err(item->dev, "%s: unable to kmalloc for zteu733_page\n",
- __func__);
- return -ENOMEM;
- }
- pixels_per_page = PAGE_SIZE / (item->info->var.bits_per_pixel / 8);
- yoffset_per_page = pixels_per_page / item->info->var.xres;
- xoffset_per_page = pixels_per_page -
- (yoffset_per_page * item->info->var.xres);
- dev_dbg(item->dev, "%s: item=0x%p pixels_per_page=%hu "
- "yoffset_per_page=%hu xoffset_per_page=%hu\n",
- __func__, (void *)item, pixels_per_page,
- yoffset_per_page, xoffset_per_page);
- buffer = (unsigned short *)item->info->fix.smem_start;
- for (index = 0; index < item->pages_count; index++) {
- len = (item->info->var.xres * item->info->var.yres) -
- (index * pixels_per_page);
- if (len > pixels_per_page) {
- len = pixels_per_page;
- }
- dev_dbg(item->dev,
- "%s: page[%d]: x=%3hu y=%3hu buffer=0x%p len=%3hu\n",
- __func__, index, x, y, buffer, len);
- item->pages[index].x = x;
- item->pages[index].y = y;
- item->pages[index].buffer = buffer;
- item->pages[index].len = len;
- x += xoffset_per_page;
- if (x >= item->info->var.xres) {
- y++;
- x -= item->info->var.xres;
- }
- y += yoffset_per_page;
- buffer += pixels_per_page;
- }
- return 0;
- }
- static void zteu733_pages_free(struct zteu733 *item)
- {
- dev_dbg(item->dev, "%s: item=0x%p\n", __func__, (void *)item);
- kfree(item->pages);
- }
- static inline __u32 CNVT_TOHW(__u32 val, __u32 width)
- {
- return ((val<<width) + 0x7FFF - val)>>16;
- }
- //This routine is needed because the console driver won't work without it.
- static int zteu733_setcolreg(unsigned regno,
- unsigned red, unsigned green, unsigned blue,
- unsigned transp, struct fb_info *info)
- {
- int ret = 1;
- /*
- * If greyscale is true, then we convert the RGB value
- * to greyscale no matter what visual we are using.
- */
- if (info->var.grayscale)
- red = green = blue = (19595 * red + 38470 * green +
- 7471 * blue) >> 16;
- switch (info->fix.visual) {
- case FB_VISUAL_TRUECOLOR:
- if (regno < 16) {
- u32 *pal = info->pseudo_palette;
- u32 value;
- red = CNVT_TOHW(red, info->var.red.length);
- green = CNVT_TOHW(green, info->var.green.length);
- blue = CNVT_TOHW(blue, info->var.blue.length);
- transp = CNVT_TOHW(transp, info->var.transp.length);
- value = (red << info->var.red.offset) |
- (green << info->var.green.offset) |
- (blue << info->var.blue.offset) |
- (transp << info->var.transp.offset);
- pal[regno] = value;
- ret = 0;
- }
- break;
- case FB_VISUAL_STATIC_PSEUDOCOLOR:
- case FB_VISUAL_PSEUDOCOLOR:
- break;
- }
- return ret;
- }
- static int zteu733_blank(int blank_mode, struct fb_info *info)
- {
- struct zteu733 *item = (struct zteu733 *)info->par;
- if (blank_mode == FB_BLANK_UNBLANK)
- item->backlight=1;
- else
- item->backlight=0;
- //Item->backlight won't take effect until the LCD is written to. Force that
- //by dirty'ing a page.
- item->pages[0].must_update=1;
- schedule_delayed_work(&info->deferred_work, 0);
- return 0;
- }
- static void zteu733_touch(struct fb_info *info, int x, int y, int w, int h)
- {
- struct fb_deferred_io *fbdefio = info->fbdefio;
- struct zteu733 *item = (struct zteu733 *)info->par;
- int i, ystart, yend;
- if (fbdefio) {
- //Touch the pages the y-range hits, so the deferred io will update them.
- for (i=0; i<item->pages_count; i++) {
- ystart=item->pages[i].y;
- yend=item->pages[i].y+(item->pages[i].len/info->fix.line_length)+1;
- if (!((y+h)<ystart || y>yend)) {
- item->pages[i].must_update=1;
- }
- }
- //Schedule the deferred IO to kick in after a delay.
- schedule_delayed_work(&info->deferred_work, fbdefio->delay);
- }
- }
- static void zteu733_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
- {
- sys_fillrect(p, rect);
- zteu733_touch(p, rect->dx, rect->dy, rect->width, rect->height);
- }
- static void zteu733_imageblit(struct fb_info *p, const struct fb_image *image)
- {
- sys_imageblit(p, image);
- zteu733_touch(p, image->dx, image->dy, image->width, image->height);
- }
- static void zteu733_copyarea(struct fb_info *p, const struct fb_copyarea *area)
- {
- sys_copyarea(p, area);
- zteu733_touch(p, area->dx, area->dy, area->width, area->height);
- }
- static ssize_t zteu733_write(struct fb_info *p, const char __user *buf,
- size_t count, loff_t *ppos)
- {
- ssize_t res;
- res = fb_sys_write(p, buf, count, ppos);
- zteu733_touch(p, 0, 0, p->var.xres, p->var.yres);
- return res;
- }
- static struct fb_ops zteu733_fbops = {
- .owner = THIS_MODULE,
- .fb_read = fb_sys_read,
- .fb_write = zteu733_write,
- .fb_fillrect = zteu733_fillrect,
- .fb_copyarea = zteu733_copyarea,
- .fb_imageblit = zteu733_imageblit,
- .fb_setcolreg = zteu733_setcolreg,
- .fb_blank = zteu733_blank,
- };
- static struct fb_fix_screeninfo zteu733_fix __initdata = {
- .id = "ZTEU733",
- .type = FB_TYPE_PACKED_PIXELS,
- .visual = FB_VISUAL_TRUECOLOR,
- .accel = FB_ACCEL_NONE,
- .line_length = 400 * 2,
- };
- static struct fb_var_screeninfo zteu733_var __initdata = {
- .xres = 400,
- .yres = 240,
- .xres_virtual = 400,
- .yres_virtual = 240,
- .width = 400,
- .height = 240,
- .bits_per_pixel = 16,
- .red = {11, 5, 0},
- .green = {5, 6, 0},
- .blue = {0, 5, 0},
- .activate = FB_ACTIVATE_NOW,
- .vmode = FB_VMODE_NONINTERLACED,
- };
- static struct fb_deferred_io zteu733_defio = {
- .delay = HZ / 20,
- .deferred_io = &zteu733_update,
- };
- static int __devinit zteu733_probe(struct spi_device *dev)
- {
- int ret = 0;
- struct zteu733 *item;
- struct fb_info *info;
- dev_dbg(&dev->dev, "%s\n", __func__);
- item = kzalloc(sizeof(struct zteu733), GFP_KERNEL);
- if (!item) {
- dev_err(&dev->dev,
- "%s: unable to kzalloc for zteu733\n", __func__);
- ret = -ENOMEM;
- goto out;
- }
- item->dev = &dev->dev;
- dev_set_drvdata(&dev->dev, item);
- item->backlight=1;
- item->spidev=dev;
- item->dev=&dev->dev;
- dev_set_drvdata(&dev->dev, item);
- dev_info(&dev->dev, "spi registered, item=0x%p\n", (void *)item);
- info = framebuffer_alloc(sizeof(struct zteu733), &dev->dev);
- if (!info) {
- ret = -ENOMEM;
- dev_err(&dev->dev,
- "%s: unable to framebuffer_alloc\n", __func__);
- goto out_item;
- }
- info->pseudo_palette = &item->pseudo_palette;
- item->info = info;
- info->par = item;
- info->dev = &dev->dev;
- info->fbops = &zteu733_fbops;
- info->flags = FBINFO_FLAG_DEFAULT|FBINFO_VIRTFB;
- info->fix = zteu733_fix;
- info->var = zteu733_var;
- ret = zteu733_video_alloc(item);
- if (ret) {
- dev_err(&dev->dev,
- "%s: unable to zteu733_video_alloc\n", __func__);
- goto out_info;
- }
- info->screen_base = (char __iomem *)item->info->fix.smem_start;
- ret = zteu733_pages_alloc(item);
- if (ret < 0) {
- dev_err(&dev->dev,
- "%s: unable to zteu733_pages_init\n", __func__);
- goto out_video;
- }
- info->fbdefio = &zteu733_defio;
- fb_deferred_io_init(info);
- ret = register_framebuffer(info);
- if (ret < 0) {
- dev_err(&dev->dev,
- "%s: unable to register_frambuffer\n", __func__);
- goto out_pages;
- }
- zteu733_setup(item);
- zteu733_update_all(item);
- return ret;
- out_pages:
- zteu733_pages_free(item);
- out_video:
- zteu733_video_free(item);
- out_info:
- framebuffer_release(info);
- out_item:
- kfree(item);
- out:
- return ret;
- }
- static int __devexit zteu733_remove(struct spi_device *dev)
- {
- struct zteu733 *item = dev_get_drvdata(&dev->dev);
- struct fb_info *info;
- dev_dbg(&dev->dev, "%s\n", __func__);
- dev_set_drvdata(&dev->dev, NULL);
- if (item) {
- info = item->info;
- if (info)
- unregister_framebuffer(info);
- zteu733_pages_free(item);
- zteu733_video_free(item);
- kfree(item);
- if (info)
- framebuffer_release(info);
- }
- return 0;
- }
- #ifdef CONFIG_PM
- static int zteu733_suspend(struct spi_device *spi, pm_message_t state)
- {
- struct fb_info *info = dev_get_drvdata(&spi->dev);
- struct zteu733 *item = (struct zteu733 *)info->par;
- /* enter into sleep mode */
- zteu733_reg_set(item, ZTEU733_REG_SLEEP_MODE, 0x0001);
- return 0;
- }
- static int zteu733_resume(struct spi_device *spi)
- {
- struct fb_info *info = dev_get_drvdata(&spi->dev);
- struct zteu733 *item = (struct zteu733 *)info->par;
- /* leave sleep mode */
- zteu733_reg_set(item, ZTEU733_REG_SLEEP_MODE, 0x0000);
- return 0;
- }
- #else
- #define zteu733_suspend NULL
- #define zteu733_resume NULL
- #endif
- static struct spi_driver spi_zteu733_driver = {
- .driver = {
- .name = "spi-zteu733",
- .bus = &spi_bus_type,
- .owner = THIS_MODULE,
- },
- .probe = zteu733_probe,
- .remove = zteu733_remove,
- .suspend = zteu733_suspend,
- .resume = zteu733_resume,
- };
- static int __init zteu733_init(void)
- {
- int ret = 0;
- pr_debug("%s\n", __func__);
- ret = spi_register_driver(&spi_zteu733_driver);
- if (ret) {
- pr_err("%s: unable to platform_driver_register\n", __func__);
- }
- return ret;
- }
- static void __exit zteu733_exit(void)
- {
- pr_debug("%s\n", __func__);
- spi_unregister_driver(&spi_zteu733_driver);
- }
- module_init(zteu733_init);
- module_exit(zteu733_exit);
- MODULE_DESCRIPTION("ZTEU733 LCD Driver");
- MODULE_AUTHOR("Jeroen Domburg <jeroen@spritesmods.com>");
- MODULE_LICENSE("GPL");
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement