Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * FB driver for the ZTE U733 3.5" LCD display
- *
- * Copyright (C) 2013 Petr Blaha
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/gpio.h>
- #include <linux/spi/spi.h>
- #include <linux/delay.h>
- #include "fbtft.h"
- #define DRVNAME "zteu733fb"
- #define WIDTH 400
- #define HEIGHT 240
- /* Module Parameter: debug (also available through sysfs) */
- MODULE_PARM_DEBUG;
- /* Module Parameter: rotate */
- static unsigned rotate = 0;
- module_param(rotate, uint, 0);
- MODULE_PARM_DESC(rotate, "Rotate display (0=normal, 1=clockwise, 2=upside down, 3=counterclockwise)");
- static inline int my_spi_write(struct fbtft_par *par, struct spi_device *spi, const void *buf, size_t len)
- {
- struct spi_transfer t = {
- .tx_buf = buf,
- .len = len,
- };
- struct spi_message m;
- int ret=0;
- spi_message_init(&m);
- spi_message_add_tail(&t, &m);
- ret = spi_sync(spi, &m);
- fbtft_fbtft_dev_dbg(DEBUG_UPDATE_DISPLAY, par, par->info->device, "%s: buffer size =%d return is=%d \n", __func__, len, ret);
- return ret;
- }
- static int zteu733fb_init_display(struct fbtft_par *par)
- {
- fbtft_dev_dbg(DEBUG_INIT_DISPLAY, par->info->device, "%s()\n", __func__);
- par->fbtftops.reset(par);
- write_cmd(par, 0xE9); // LSI Test Register
- write_data(par, 0x20);
- mdelay(20);
- write_cmd(par, 0x01); // Sotf Reset
- mdelay(150);
- write_cmd(par, 0x11); // Exit Sleep mode
- mdelay(500);
- write_cmd(par, 0x53); // Write CTRL Display, Display Dimming = 1, Backlight = ON
- write_data(par, 0x08);
- write_cmd(par, 0x51); // Write Display Brightness = 0xFF
- write_data(par, 0xFF);
- write_cmd(par, 0xD1);
- write_data(par, 0x00);
- write_data(par, 0x71);
- write_data(par, 0x19);
- write_cmd(par, 0xD0);
- write_data(par, 0x07);
- write_data(par, 0x01);
- write_data(par, 0x08);
- write_cmd(par, 0x36); // Set Address Mode :
- write_data(par, 0x28); // bit 3 0=RGB, 1=BGR
- write_cmd(par, 0x3A); // Set Pixel Format : 0x11 = 8bpp, 0x55 = 16bpp, 0x66 = 18bpp
- write_data(par, 0x55);
- write_cmd(par, 0xC1);
- write_data(par, 0x10);
- write_data(par, 0x10);
- write_data(par, 0x02);
- write_data(par, 0x02);
- write_cmd(par, 0xC0);
- write_data(par, 0x00);
- write_data(par, 0x35);
- write_data(par, 0x00);
- write_data(par, 0x00);
- write_data(par, 0x01);
- write_data(par, 0x02);
- write_cmd(par, 0xC5);
- write_data(par, 0x04);
- write_cmd(par, 0xD2);
- write_data(par, 0x01);
- write_data(par, 0x44);
- // GAMA TODO
- write_cmd(par, 0x29); // Display ON
- write_cmd(par, 0x20); // Exit Invert Mode
- return 0;
- }
- void zteu733fb_set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
- {
- fbtft_fbtft_dev_dbg(DEBUG_SET_ADDR_WIN, par, par->info->device, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
- /* Set Column Address */
- write_cmd(par, 0x2A);
- write_data(par, (xs >> 8) & 0xFF);
- write_data(par, xs & 0xFF);
- write_data(par, (xe >> 8) & 0xFF);
- write_data(par, xe & 0xFF);
- /* Set Page Address */
- write_cmd(par, 0x2B);
- write_data(par, (ys >> 8) & 0xFF);
- write_data(par, ys & 0xFF);
- write_data(par, (ye >> 8) & 0xFF);
- write_data(par, ye & 0xFF);
- /* 0x2C - Write Memory Start */
- write_cmd(par, 0x2C);
- }
- void zteu733fb_update_display(struct fbtft_par *par)
- {
- struct timespec ts_start,ts_end,test_of_time;
- long ms, us, ns;
- bool timeit = false;
- int ret = 0;
- if (unlikely(*par->debug & (DEBUG_TIME_FIRST_UPDATE | DEBUG_TIME_EACH_UPDATE))) {
- if ( (*par->debug & DEBUG_TIME_EACH_UPDATE) || \
- ((*par->debug & DEBUG_TIME_FIRST_UPDATE) && !par->first_update_done) )
- {
- getnstimeofday(&ts_start);
- timeit = true;
- }
- }
- // Sanity checks
- if (par->dirty_lines_start > par->dirty_lines_end) {
- fbtft_fbtft_dev_dbg(0xFFFFFFFF, par, par->info->device,
- "%s: dirty_lines_start=%d is larger than dirty_lines_end=%d. Shouldn't happen, will do full display update\n",
- __func__, par->dirty_lines_start, par->dirty_lines_end);
- par->dirty_lines_start = 0;
- par->dirty_lines_end = par->info->var.yres - 1;
- }
- if (par->dirty_lines_start > par->info->var.yres - 1 || par->dirty_lines_end > par->info->var.yres - 1) {
- dev_warn(par->info->device,
- "%s: dirty_lines_start=%d or dirty_lines_end=%d larger than max=%d. Shouldn't happen, will do full display update\n",
- __func__, par->dirty_lines_start, par->dirty_lines_end, par->info->var.yres - 1);
- par->dirty_lines_start = 0;
- par->dirty_lines_end = par->info->var.yres - 1;
- }
- fbtft_fbtft_dev_dbg(DEBUG_UPDATE_DISPLAY, par, par->info->device, "%s: dirty_lines_start=%d dirty_lines_end=%d\n", __func__, par->dirty_lines_start, par->dirty_lines_end);
- if (par->fbtftops.set_addr_win)
- par->fbtftops.set_addr_win(par, 0, par->dirty_lines_start, par->info->var.xres-1, par->dirty_lines_end);
- ret = par->fbtftops.write_vmem(par);
- if (ret < 0)
- dev_err(par->info->device, "%s: write_vmem failed to update display buffer\n", __func__);
- if (unlikely(timeit)) {
- getnstimeofday(&ts_end);
- test_of_time = timespec_sub(ts_end,ts_start);
- us = (test_of_time.tv_nsec / 1000) % 1000;
- ms = (test_of_time.tv_sec * 1000) + ((test_of_time.tv_nsec / 1000000) % 1000);
- ns = test_of_time.tv_nsec % 1000;
- dev_info(par->info->device, "Elapsed time for display update: %4lu.%.3lu%.3lu ms (fps: %2lu, lines=%u)\n",
- ms, us, ns, test_of_time.tv_nsec ? 1000000000 / test_of_time.tv_nsec : 0, par->dirty_lines_end - par->dirty_lines_start + 1);
- par->first_update_done = true;
- }
- // set display line markers as clean
- par->dirty_lines_start = par->info->var.yres - 1;
- par->dirty_lines_end = 0;
- }
- int zteu733fb_write_vmem(struct fbtft_par *par) {
- u8 *vmem8;
- u8 *txbuf8 = par->txbuf.buf;
- size_t remain;
- size_t to_copy;
- size_t tx_array_size;
- int i;
- int ret = 0;
- size_t offset, len;
- offset = par->dirty_lines_start * par->info->fix.line_length;
- len = (par->dirty_lines_end - par->dirty_lines_start + 1) * par->info->fix.line_length;
- remain = len;
- vmem8 = par->info->screen_base + offset;
- fbtft_fbtft_dev_dbg(DEBUG_UPDATE_DISPLAY, par, par->info->device, "%s: offset=%d, len=%d\n", __func__, offset, len);
- if (par->gpio.dc != -1)
- gpio_set_value(par->gpio.dc, 1);
- // non buffered write
- if (!par->txbuf.buf)
- return par->fbtftops.write(par, vmem8, len);
- // buffered write
- tx_array_size = par->txbuf.len;
- while (remain) {
- to_copy = remain > tx_array_size ? tx_array_size : remain;
- dev_dbg(par->info->device, " to_copy=%d, remain=%d\n", to_copy, remain - to_copy);
- fbtft_fbtft_dev_dbg(DEBUG_UPDATE_DISPLAY, par, par->info->device, "%s: to copy=%d, remain=%d\n", __func__, to_copy, remain-to_copy);
- #ifdef __LITTLE_ENDIAN
- for (i=0;i<to_copy;i+=2) {
- txbuf8[i] = vmem8[i+1];
- txbuf8[i+1] = vmem8[i];
- // write_data(par, 256*vmem8[i+1] + vmem8[i] );
- }
- #else
- for (i=0;i<to_copy;i++) {
- txbuf8[i] = vmem8[i];
- write_data(par, txbuf8[i]);
- }
- #endif
- vmem8 = vmem8 + to_copy;
- ret = par->fbtftops.write(par, par->txbuf.buf, to_copy);
- if (ret < 0)
- return ret;
- remain -= to_copy;
- }
- return 0;
- }
- int zteu733fb_write_spi(struct fbtft_par *par, void *buf, size_t len)
- {
- // fbtft_dev_dbg(DEBUG_UPDATE_DISPLAY, par, par->info->device, u8, buf, len, "%s(len=%d): ", __func__, len);
- // fbtft_fbtft_dev_dbg(DEBUG_UPDATE_DISPLAY, par, par->info->device, "%s: buffer size =%d\n", __func__, len);
- if (!par->spi) {
- dev_err(par->info->device, "%s: par->spi is unexpectedly NULL\n", __func__);
- return -1;
- }
- return my_spi_write(par, par->spi, buf, len);
- }
- static int zteu733fb_verify_gpios(struct fbtft_par *par)
- {
- int i;
- fbtft_dev_dbg(DEBUG_VERIFY_GPIOS, par->info->device, "%s()\n", __func__);
- if (par->gpio.dc < 0) {
- dev_err(par->info->device, "Missing info about 'dc' gpio. Aborting.\n");
- return -EINVAL;
- }
- if (par->pdev) {
- if (par->gpio.wr < 0) {
- dev_err(par->info->device, "Missing info about 'wr' gpio. Aborting.\n");
- return -EINVAL;
- }
- for (i=0;i < 16;i++) {
- if (par->gpio.db[i] < 0) {
- dev_err(par->info->device, "Missing info about 'db%02d' gpio. Aborting.\n", i);
- return -EINVAL;
- }
- }
- }
- return 0;
- }
- struct fbtft_display zteu733fb_display = {
- .width = WIDTH,
- .height = HEIGHT,
- };
- static int zteu733fb_probe_common(struct spi_device *sdev, struct platform_device *pdev)
- {
- struct device *dev;
- struct fb_info *info;
- struct fbtft_par *par;
- int ret;
- if (sdev)
- dev = &sdev->dev;
- else
- dev = &pdev->dev;
- fbtft_dev_dbg(DEBUG_DRIVER_INIT_FUNCTIONS, dev, "%s()\n", __func__);
- if (rotate > 3) {
- dev_warn(dev, "module parameter 'rotate' illegal value: %d. Can only be 0,1,2,3. Setting it to 0.\n", rotate);
- rotate = 0;
- }
- switch (rotate) {
- case 0:
- case 2:
- zteu733fb_display.width = WIDTH;
- zteu733fb_display.height = HEIGHT;
- break;
- case 1:
- case 3:
- zteu733fb_display.width = HEIGHT;
- zteu733fb_display.height = WIDTH;
- break;
- }
- info = fbtft_framebuffer_alloc(&zteu733fb_display, dev);
- if (!info)
- return -ENOMEM;
- info->var.rotate = rotate;
- par = info->par;
- if (sdev)
- par->spi = sdev;
- else
- par->pdev = pdev;
- fbtft_debug_init(par);
- par->fbtftops.init_display = zteu733fb_init_display;
- // par->fbtftops.update_display = zteu733fb_update_display;
- par->fbtftops.write_vmem = zteu733fb_write_vmem;
- // par->fbtftops.register_backlight = fbtft_register_backlight;
- // par->fbtftops.write_reg = fbtft_write_reg16_bus8;
- par->fbtftops.set_addr_win = zteu733fb_set_addr_win;
- // par->fbtftops.write_data_command = fbtft_write_data_command16_bus8;
- par->fbtftops.verify_gpios = zteu733fb_verify_gpios;
- par->fbtftops.write = zteu733fb_write_spi;
- /*
- if (pdev)
- par->fbtftops.write = fbtft_write_gpio16_wr;
- */
- ret = fbtft_register_framebuffer(info);
- if (ret < 0)
- goto out_release;
- return 0;
- out_release:
- fbtft_framebuffer_release(info);
- return ret;
- }
- static int zteu733fb_remove_common(struct device *dev, struct fb_info *info)
- {
- fbtft_dev_dbg(DEBUG_DRIVER_INIT_FUNCTIONS, dev, "%s()\n", __func__);
- if (info) {
- fbtft_unregister_framebuffer(info);
- fbtft_framebuffer_release(info);
- }
- return 0;
- }
- static int zteu733fb_probe_spi(struct spi_device *spi)
- {
- fbtft_dev_dbg(DEBUG_DRIVER_INIT_FUNCTIONS, &spi->dev, "%s()\n", __func__);
- return zteu733fb_probe_common(spi, NULL);
- }
- static int zteu733fb_remove_spi(struct spi_device *spi)
- {
- struct fb_info *info = spi_get_drvdata(spi);
- fbtft_dev_dbg(DEBUG_DRIVER_INIT_FUNCTIONS, &spi->dev, "%s()\n", __func__);
- return zteu733fb_remove_common(&spi->dev, info);
- }
- static int zteu733fb_probe_pdev(struct platform_device *pdev)
- {
- fbtft_dev_dbg(DEBUG_DRIVER_INIT_FUNCTIONS, &pdev->dev, "%s()\n", __func__);
- return zteu733fb_probe_common(NULL, pdev);
- }
- static int zteu733fb_remove_pdev(struct platform_device *pdev)
- {
- struct fb_info *info = platform_get_drvdata(pdev);
- fbtft_dev_dbg(DEBUG_DRIVER_INIT_FUNCTIONS, &pdev->dev, "%s()\n", __func__);
- /* not supported, the RPi doesn't have that many GPIOs, and thus no 16-bit write function */
- return -1;
- return zteu733fb_remove_common(&pdev->dev, info);
- }
- static const struct spi_device_id zteu733fb_platform_ids[] = {
- { "zteu733fb", 0 },
- { },
- };
- static struct spi_driver zteu733fb_spi_driver = {
- .driver = {
- .name = DRVNAME,
- .owner = THIS_MODULE,
- },
- .id_table = zteu733fb_platform_ids,
- .probe = zteu733fb_probe_spi,
- .remove = zteu733fb_remove_spi,
- };
- static struct platform_driver zteu733fb_platform_driver = {
- .driver = {
- .name = DRVNAME,
- .owner = THIS_MODULE,
- },
- .probe = zteu733fb_probe_pdev,
- .remove = zteu733fb_remove_pdev,
- };
- static int __init zteu733fb_init(void)
- {
- int ret;
- fbtft_pr_debug("\n\n"DRVNAME": %s()\n", __func__);
- ret = spi_register_driver(&zteu733fb_spi_driver);
- if (ret < 0)
- return ret;
- return platform_driver_register(&zteu733fb_platform_driver);
- }
- static void __exit zteu733fb_exit(void)
- {
- fbtft_pr_debug(DRVNAME": %s()\n", __func__);
- spi_unregister_driver(&zteu733fb_spi_driver);
- platform_driver_unregister(&zteu733fb_platform_driver);
- }
- /* ------------------------------------------------------------------------- */
- module_init(zteu733fb_init);
- module_exit(zteu733fb_exit);
- MODULE_DESCRIPTION("FB driver for the ZTE U733 3.5 LCD display");
- MODULE_AUTHOR("Petr Blaha");
- MODULE_LICENSE("GPL");
Advertisement
Add Comment
Please, Sign In to add comment