From 5c105d9f3fd086aff195d3849dcf847d6b0bd927 Mon Sep 17 00:00:00 2001 From: blogic Date: Fri, 5 Oct 2012 10:12:53 +0000 Subject: branch Attitude Adjustment git-svn-id: svn://svn.openwrt.org/openwrt/branches/attitude_adjustment@33625 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- .../files/drivers/video/backlight/ubicom32lcd.c | 372 +++++++++++++++++++++ 1 file changed, 372 insertions(+) create mode 100644 target/linux/ubicom32/files/drivers/video/backlight/ubicom32lcd.c (limited to 'target/linux/ubicom32/files/drivers/video/backlight/ubicom32lcd.c') diff --git a/target/linux/ubicom32/files/drivers/video/backlight/ubicom32lcd.c b/target/linux/ubicom32/files/drivers/video/backlight/ubicom32lcd.c new file mode 100644 index 000000000..e4f8c713e --- /dev/null +++ b/target/linux/ubicom32/files/drivers/video/backlight/ubicom32lcd.c @@ -0,0 +1,372 @@ +/* + * drivers/video/ubicom32lcd.c + * LCD initilization code + * + * (C) Copyright 2009, Ubicom, Inc. + * + * This file is part of the Ubicom32 Linux Kernel Port. + * + * The Ubicom32 Linux Kernel Port 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. + * + * The Ubicom32 Linux Kernel Port 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 the Ubicom32 Linux Kernel Port. If not, + * see . + */ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ubicom32lcd.h" + +#define DRIVER_NAME "ubicom32lcd" + +struct ubicom32lcd_data { + const struct ubicom32lcd_panel *panel; + + int pin_cs; + int pin_rd; + int pin_rs; + int pin_wr; + int pin_reset; + struct ubicom32_io_port *port_data; + int data_shift; +}; + +/* + * ubicom32lcd_write + * Performs a write cycle on the bus (assumes CS asserted, RD & WR set) + */ +static void ubicom32lcd_write(struct ubicom32lcd_data *ud, int command, u16 data) +{ + if (command) { + UBICOM32_GPIO_SET_PIN_LOW(ud->pin_rs); + } else { + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rs); + } + + asm volatile ( + "or.4 4(%[port]), 4(%[port]), %[mask] \n\t" + "not.4 %[mask], %[mask] \n\t" + "and.4 8(%[port]), 8(%[port]), %[mask] \n\t" + "or.4 8(%[port]), 8(%[port]), %[cmd] \n\t" + : + : [port] "a" (ud->port_data), + [mask] "d" (0xFFFF << ud->data_shift), + [cmd] "d" (data << ud->data_shift) + : "cc" + ); + + UBICOM32_GPIO_SET_PIN_LOW(ud->pin_wr); + + //ndelay(50); + udelay(1); + + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_wr); + + udelay(1); + //ndelay(50); +} + +/* + * ubicom32lcd_read_data + * Performs a read cycle on the bus (assumes CS asserted, RD & WR set) + */ +static u16 ubicom32lcd_read_data(struct ubicom32lcd_data *ud) +{ + u32_t data; + + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rs); + + asm volatile ( + "and.4 4(%[port]), 4(%[port]), %[mask]\n\t" + : + : [port] "a" (ud->port_data), + [mask] "d" (~(0xFFFF << ud->data_shift)) + : "cc" + ); + + UBICOM32_GPIO_SET_PIN_LOW(ud->pin_rd); + + ndelay(300); + + asm volatile ( + "lsr.4 %[data], 12(%[port]), %[shamt] \n\t" + "and.4 %[data], %[data], %[mask] \n\t" + : [data] "=d" (data) + : [port] "a" (ud->port_data), + [mask] "d" (0xFFFF), + [shamt] "d" (ud->data_shift) + : "cc" + ); + + ndelay(200); + + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rd); + + ndelay(500); + + return data; +} + +/* + * ubicom32lcd_execute + * Executes a script for performing operations on the LCD (assumes CS set) + */ +static void ubicom32lcd_execute(struct ubicom32lcd_data *ud, const struct ubicom32lcd_step *script) +{ + while (1) { + switch (script->op) { + case LCD_STEP_CMD: + ubicom32lcd_write(ud, 1, script->cmd); + break; + + case LCD_STEP_DATA: + ubicom32lcd_write(ud, 0, script->data); + break; + + case LCD_STEP_CMD_DATA: + ubicom32lcd_write(ud, 1, script->cmd); + ubicom32lcd_write(ud, 0, script->data); + break; + + case LCD_STEP_SLEEP: + udelay(script->data); + break; + + case LCD_STEP_DONE: + return; + } + script++; + } +} + +/* + * ubicom32lcd_goto + * Places the gram pointer at a specific X, Y address + */ +static void ubicom32lcd_goto(struct ubicom32lcd_data *ud, int x, int y) +{ + ubicom32lcd_write(ud, 1, ud->panel->horz_reg); + ubicom32lcd_write(ud, 0, x); + ubicom32lcd_write(ud, 1, ud->panel->vert_reg); + ubicom32lcd_write(ud, 0, y); + ubicom32lcd_write(ud, 1, ud->panel->gram_reg); +} + +/* + * ubicom32lcd_panel_init + * Initializes the lcd panel. + */ +static int ubicom32lcd_panel_init(struct ubicom32lcd_data *ud) +{ + u16 id; + + UBICOM32_GPIO_SET_PIN_LOW(ud->pin_reset); + UBICOM32_GPIO_SET_PIN_OUTPUT(ud->pin_reset); + UBICOM32_GPIO_ENABLE(ud->pin_reset); + + asm volatile ( + "or.4 0x50(%[port]), 0x50(%[port]), %[mask] \n\t" + "not.4 %[mask], %[mask] \n\t" + "and.4 0x04(%[port]), 0x04(%[port]), %[mask] \n\t" + : + : [port] "a" (ud->port_data), + [mask] "d" (0xFFFF << ud->data_shift) + : "cc" + ); + + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rs); + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rd); + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_wr); + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_cs); + + UBICOM32_GPIO_SET_PIN_OUTPUT(ud->pin_rs); + UBICOM32_GPIO_SET_PIN_OUTPUT(ud->pin_rd); + UBICOM32_GPIO_SET_PIN_OUTPUT(ud->pin_wr); + UBICOM32_GPIO_SET_PIN_OUTPUT(ud->pin_cs); + + UBICOM32_GPIO_ENABLE(ud->pin_rs); + UBICOM32_GPIO_ENABLE(ud->pin_rd); + UBICOM32_GPIO_ENABLE(ud->pin_wr); + UBICOM32_GPIO_ENABLE(ud->pin_cs); + + udelay(20); + + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_reset); + + udelay(20); + + UBICOM32_GPIO_SET_PIN_LOW(ud->pin_cs); + + id = ubicom32lcd_read_data(ud); + + /* + * We will try to figure out what kind of panel we have if we were not told. + */ + if (!ud->panel) { + const struct ubicom32lcd_panel **p = ubicom32lcd_panels; + while (*p) { + if ((*p)->id && ((*p)->id == id)) { + break; + } + p++; + } + if (!*p) { + printk(KERN_WARNING DRIVER_NAME ":Could not find compatible panel, id=%x\n", id); + return -ENODEV; + } + ud->panel = *p; + } + + /* + * Make sure panel ID matches if we were supplied a panel type + */ + if (ud->panel->id && (ud->panel->id != id)) { + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_cs); + + return -ENODEV; + } + + ubicom32lcd_execute(ud, ud->panel->init_seq); + + ubicom32lcd_goto(ud, 0, 0); + + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_cs); + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rd); + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_wr); + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rs); + + printk(KERN_INFO DRIVER_NAME ": Initialized panel %s\n", ud->panel->desc); + + return 0; +} + +/* + * ubicom32lcd_probe + */ +static int ubicom32lcd_probe(struct platform_device *pdev) +{ + const struct ubicom32lcd_platform_data *pdata = pdev->dev.platform_data; + struct ubicom32lcd_data *ud; + int retval; + + /* + * Allocate our private data + */ + ud = kzalloc(sizeof(struct ubicom32lcd_data), GFP_KERNEL); + if (!ud) { + return -ENOMEM; + } + + if (pdata) { + ud->pin_cs = pdata->pin_cs; + ud->pin_rd = pdata->pin_rd; + ud->pin_wr = pdata->pin_wr; + ud->pin_rs = pdata->pin_rs; + ud->pin_reset = pdata->pin_reset; + ud->port_data = pdata->port_data; + ud->data_shift = pdata->data_shift; + } else { + /* + * Defaults + */ + ud->pin_cs = GPIO_RD_4; + ud->pin_rd = GPIO_RD_5; + ud->pin_rs = GPIO_RD_3; + ud->pin_wr = GPIO_RD_2; + ud->pin_reset = GPIO_RD_7; + ud->port_data = (struct ubicom32_io_port *)RI; + ud->data_shift = 0; + } + + /* + * Initialize the display + */ + retval = ubicom32lcd_panel_init(ud); + if (retval) { + kfree(ud); + return retval; + } + + printk(KERN_INFO DRIVER_NAME ": LCD initialized\n"); + + return 0; +} + +/* + * ubicom32lcd_remove + */ +static int __exit ubicom32lcd_remove(struct platform_device *pdev) +{ + struct ubicom32lcd_data *ud = platform_get_drvdata(pdev); + + kfree(ud); + + return 0; +} + +static struct platform_driver ubicom32lcd_driver = { + .probe = ubicom32lcd_probe, + .remove = ubicom32lcd_remove, + + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + + .remove = __exit_p(ubicom32lcd_remove), +}; + +static struct platform_device *ubicom32lcd_device; + +/* + * ubicom32lcd_init + */ +static int __init ubicom32lcd_init(void) +{ + int res; + + res = platform_driver_register(&ubicom32lcd_driver); + if (res == 0) { + ubicom32lcd_device = platform_device_alloc(DRIVER_NAME, 0); + if (ubicom32lcd_device) { + res = platform_device_add(ubicom32lcd_device); + } else { + res = -ENOMEM; + } + if (res) { + platform_device_put(ubicom32lcd_device); + platform_driver_unregister(&ubicom32lcd_driver); + } + } + return res; +} +module_init(ubicom32lcd_init); + +/* + * ubicom32lcd_exit + */ +static void __exit ubicom32lcd_exit(void) +{ + platform_device_unregister(ubicom32lcd_device); + platform_driver_unregister(&ubicom32lcd_driver); +} +module_exit(ubicom32lcd_exit); + +MODULE_AUTHOR("Patrick Tjin <@ubicom.com>"); +MODULE_DESCRIPTION("Ubicom32 LCD driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3