OpenWrt – Blame information for rev 2
?pathlinks?
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1 | office | 1 | From 8741ead92bc93e66740237e51b88b8690ebcbba3 Mon Sep 17 00:00:00 2001 |
| 2 | From: Lars-Peter Clausen <lars@metafoo.de> |
||
| 3 | Date: Sun, 1 Aug 2010 21:19:40 +0200 |
||
| 4 | Subject: [PATCH 6/7] Add ili8960 lcd driver |
||
| 5 | |||
| 6 | Includes the following changes from the jz-3.5 branch: |
||
| 7 | - Use module_spi_driver |
||
| 8 | - Use devm_kzalloc |
||
| 9 | - Use kstrtoul |
||
| 10 | |||
| 11 | Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> |
||
| 12 | --- |
||
| 13 | drivers/video/backlight/Kconfig | 7 + |
||
| 14 | drivers/video/backlight/Makefile | 1 + |
||
| 15 | drivers/video/backlight/ili8960.c | 262 +++++++++++++++++++++++++++++++++++++ |
||
| 16 | 3 files changed, 270 insertions(+) |
||
| 17 | create mode 100644 drivers/video/backlight/ili8960.c |
||
| 18 | |||
| 19 | --- a/drivers/video/backlight/Kconfig |
||
| 20 | +++ b/drivers/video/backlight/Kconfig |
||
| 21 | @@ -59,6 +59,13 @@ config LCD_LTV350QV |
||
| 22 | |||
| 23 | The LTV350QV panel is present on all ATSTK1000 boards. |
||
| 24 | |||
| 25 | +config LCD_ILI8960 |
||
| 26 | + tristate "Ilitek ili8960 LCD driver" |
||
| 27 | + depends on LCD_CLASS_DEVICE && SPI |
||
| 28 | + default n |
||
| 29 | + help |
||
| 30 | + Driver for the Ilitek ili8960 LCD controller chip. |
||
| 31 | + |
||
| 32 | config LCD_ILI922X |
||
| 33 | tristate "ILI Technology ILI9221/ILI9222 support" |
||
| 34 | depends on SPI |
||
| 35 | --- a/drivers/video/backlight/Makefile |
||
| 36 | +++ b/drivers/video/backlight/Makefile |
||
| 37 | @@ -5,6 +5,7 @@ obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o |
||
| 38 | obj-$(CONFIG_LCD_CORGI) += corgi_lcd.o |
||
| 39 | obj-$(CONFIG_LCD_HP700) += jornada720_lcd.o |
||
| 40 | obj-$(CONFIG_LCD_HX8357) += hx8357.o |
||
| 41 | +obj-$(CONFIG_LCD_ILI8960) += ili8960.o |
||
| 42 | obj-$(CONFIG_LCD_ILI922X) += ili922x.o |
||
| 43 | obj-$(CONFIG_LCD_ILI9320) += ili9320.o |
||
| 44 | obj-$(CONFIG_LCD_L4F00242T03) += l4f00242t03.o |
||
| 45 | --- /dev/null |
||
| 46 | +++ b/drivers/video/backlight/ili8960.c |
||
| 47 | @@ -0,0 +1,262 @@ |
||
| 48 | +/* |
||
| 49 | + * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> |
||
| 50 | + * Driver for Ilitek ili8960 LCD |
||
| 51 | + * |
||
| 52 | + * This program is free software; you can redistribute it and/or modify it |
||
| 53 | + * under the terms of the GNU General Public License as published by the |
||
| 54 | + * Free Software Foundation; either version 2 of the License, or (at your |
||
| 55 | + * option) any later version. |
||
| 56 | + * |
||
| 57 | + * You should have received a copy of the GNU General Public License along |
||
| 58 | + * with this program; if not, write to the Free Software Foundation, Inc., |
||
| 59 | + * 675 Mass Ave, Cambridge, MA 02139, USA. |
||
| 60 | + * |
||
| 61 | + */ |
||
| 62 | + |
||
| 63 | +#include <linux/module.h> |
||
| 64 | +#include <linux/spi/spi.h> |
||
| 65 | +#include <linux/lcd.h> |
||
| 66 | +#include <linux/delay.h> |
||
| 67 | + |
||
| 68 | +struct ili8960 { |
||
| 69 | + struct spi_device *spi; |
||
| 70 | + struct lcd_device *lcd; |
||
| 71 | + bool enabled; |
||
| 72 | + unsigned int brightness; |
||
| 73 | +}; |
||
| 74 | + |
||
| 75 | +#define ILI8960_REG_BRIGHTNESS 0x03 |
||
| 76 | +#define ILI8960_REG_POWER 0x05 |
||
| 77 | +#define ILI8960_REG_CONTRAST 0x0d |
||
| 78 | + |
||
| 79 | +static int ili8960_write_reg(struct spi_device *spi, uint8_t reg, |
||
| 80 | + uint8_t data) |
||
| 81 | +{ |
||
| 82 | + uint8_t buf[2]; |
||
| 83 | + buf[0] = ((reg & 0x40) << 1) | (reg & 0x3f); |
||
| 84 | + buf[1] = data; |
||
| 85 | + |
||
| 86 | + return spi_write(spi, buf, sizeof(buf)); |
||
| 87 | +} |
||
| 88 | + |
||
| 89 | +static int ili8960_programm_power(struct spi_device *spi, bool enabled) |
||
| 90 | +{ |
||
| 91 | + int ret; |
||
| 92 | + |
||
| 93 | + if (enabled) |
||
| 94 | + mdelay(20); |
||
| 95 | + |
||
| 96 | + ret = ili8960_write_reg(spi, ILI8960_REG_POWER, enabled ? 0xc7 : 0xc6); |
||
| 97 | + |
||
| 98 | + if (!enabled) |
||
| 99 | + mdelay(20); |
||
| 100 | + |
||
| 101 | + return ret; |
||
| 102 | +} |
||
| 103 | + |
||
| 104 | +static int ili8960_set_power(struct lcd_device *lcd, int power) |
||
| 105 | +{ |
||
| 106 | + struct ili8960 *ili8960 = lcd_get_data(lcd); |
||
| 107 | + |
||
| 108 | + switch (power) { |
||
| 109 | + case FB_BLANK_UNBLANK: |
||
| 110 | + ili8960->enabled = true; |
||
| 111 | + break; |
||
| 112 | + default: |
||
| 113 | + return 0; |
||
| 114 | + } |
||
| 115 | + |
||
| 116 | + return ili8960_programm_power(ili8960->spi, ili8960->enabled); |
||
| 117 | +} |
||
| 118 | + |
||
| 119 | +static int ili8960_early_set_power(struct lcd_device *lcd, int power) |
||
| 120 | +{ |
||
| 121 | + struct ili8960 *ili8960 = lcd_get_data(lcd); |
||
| 122 | + |
||
| 123 | + switch (power) { |
||
| 124 | + case FB_BLANK_UNBLANK: |
||
| 125 | + return 0; |
||
| 126 | + default: |
||
| 127 | + ili8960->enabled = false; |
||
| 128 | + break; |
||
| 129 | + } |
||
| 130 | + |
||
| 131 | + return ili8960_programm_power(ili8960->spi, ili8960->enabled); |
||
| 132 | +} |
||
| 133 | + |
||
| 134 | +static int ili8960_get_power(struct lcd_device *lcd) |
||
| 135 | +{ |
||
| 136 | + struct ili8960 *ili8960 = lcd_get_data(lcd); |
||
| 137 | + return ili8960->enabled ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; |
||
| 138 | +} |
||
| 139 | + |
||
| 140 | +static int ili8960_set_contrast(struct lcd_device *lcd, int contrast) |
||
| 141 | +{ |
||
| 142 | + struct ili8960 *ili8960 = lcd_get_data(lcd); |
||
| 143 | + |
||
| 144 | + return ili8960_write_reg(ili8960->spi, ILI8960_REG_CONTRAST, contrast); |
||
| 145 | +} |
||
| 146 | + |
||
| 147 | +static int ili8960_set_mode(struct lcd_device *lcd, struct fb_videomode *mode) |
||
| 148 | +{ |
||
| 149 | + if (mode->xres != 320 && mode->yres != 240) |
||
| 150 | + return -EINVAL; |
||
| 151 | + |
||
| 152 | + return 0; |
||
| 153 | +} |
||
| 154 | + |
||
| 155 | +static int ili8960_set_brightness(struct ili8960 *ili8960, int brightness) |
||
| 156 | +{ |
||
| 157 | + int ret; |
||
| 158 | + |
||
| 159 | + ret = ili8960_write_reg(ili8960->spi, ILI8960_REG_BRIGHTNESS, brightness); |
||
| 160 | + |
||
| 161 | + if (ret == 0) |
||
| 162 | + ili8960->brightness = brightness; |
||
| 163 | + |
||
| 164 | + return ret; |
||
| 165 | +} |
||
| 166 | + |
||
| 167 | +static ssize_t ili8960_show_brightness(struct device *dev, |
||
| 168 | + struct device_attribute *attr, char *buf) |
||
| 169 | +{ |
||
| 170 | + struct lcd_device *ld = to_lcd_device(dev); |
||
| 171 | + struct ili8960 *ili8960 = lcd_get_data(ld); |
||
| 172 | + |
||
| 173 | + return sprintf(buf, "%u\n", ili8960->brightness); |
||
| 174 | +} |
||
| 175 | + |
||
| 176 | +static ssize_t ili8960_store_brightness(struct device *dev, |
||
| 177 | + struct device_attribute *attr, const char *buf, size_t count) |
||
| 178 | +{ |
||
| 179 | + struct lcd_device *ld = to_lcd_device(dev); |
||
| 180 | + struct ili8960 *ili8960 = lcd_get_data(ld); |
||
| 181 | + unsigned long brightness; |
||
| 182 | + int ret; |
||
| 183 | + |
||
| 184 | + ret = kstrtoul(buf, 0, &brightness); |
||
| 185 | + if (ret) |
||
| 186 | + return ret; |
||
| 187 | + |
||
| 188 | + if (brightness > 255) |
||
| 189 | + return -EINVAL; |
||
| 190 | + |
||
| 191 | + ili8960_set_brightness(ili8960, brightness); |
||
| 192 | + |
||
| 193 | + return count; |
||
| 194 | +} |
||
| 195 | + |
||
| 196 | + |
||
| 197 | +static DEVICE_ATTR(brightness, 0644, ili8960_show_brightness, |
||
| 198 | + ili8960_store_brightness); |
||
| 199 | + |
||
| 200 | +static struct lcd_ops ili8960_lcd_ops = { |
||
| 201 | + .set_power = ili8960_set_power, |
||
| 202 | + .early_set_power = ili8960_early_set_power, |
||
| 203 | + .get_power = ili8960_get_power, |
||
| 204 | + .set_contrast = ili8960_set_contrast, |
||
| 205 | + .set_mode = ili8960_set_mode, |
||
| 206 | +}; |
||
| 207 | + |
||
| 208 | +static int ili8960_probe(struct spi_device *spi) |
||
| 209 | +{ |
||
| 210 | + int ret; |
||
| 211 | + struct ili8960 *ili8960; |
||
| 212 | + |
||
| 213 | + ili8960 = devm_kzalloc(&spi->dev, sizeof(*ili8960), GFP_KERNEL); |
||
| 214 | + if (!ili8960) |
||
| 215 | + return -ENOMEM; |
||
| 216 | + |
||
| 217 | + spi->bits_per_word = 8; |
||
| 218 | + spi->mode = SPI_MODE_3; |
||
| 219 | + |
||
| 220 | + ret = spi_setup(spi); |
||
| 221 | + if (ret) { |
||
| 222 | + dev_err(&spi->dev, "Failed to setup spi\n"); |
||
| 223 | + return ret; |
||
| 224 | + } |
||
| 225 | + |
||
| 226 | + ili8960->spi = spi; |
||
| 227 | + |
||
| 228 | + ili8960->lcd = lcd_device_register("ili8960-lcd", &spi->dev, ili8960, |
||
| 229 | + &ili8960_lcd_ops); |
||
| 230 | + |
||
| 231 | + if (IS_ERR(ili8960->lcd)) { |
||
| 232 | + ret = PTR_ERR(ili8960->lcd); |
||
| 233 | + dev_err(&spi->dev, "Failed to register lcd device: %d\n", ret); |
||
| 234 | + return ret; |
||
| 235 | + } |
||
| 236 | + |
||
| 237 | + ili8960->lcd->props.max_contrast = 255; |
||
| 238 | + |
||
| 239 | + ret = device_create_file(&ili8960->lcd->dev, &dev_attr_brightness); |
||
| 240 | + if (ret) |
||
| 241 | + goto err_unregister_lcd; |
||
| 242 | + |
||
| 243 | + ili8960_programm_power(ili8960->spi, true); |
||
| 244 | + ili8960->enabled = true; |
||
| 245 | + |
||
| 246 | + spi_set_drvdata(spi, ili8960); |
||
| 247 | + |
||
| 248 | + ili8960_write_reg(spi, 0x13, 0x01); |
||
| 249 | + |
||
| 250 | + return 0; |
||
| 251 | +err_unregister_lcd: |
||
| 252 | + lcd_device_unregister(ili8960->lcd); |
||
| 253 | + return ret; |
||
| 254 | +} |
||
| 255 | + |
||
| 256 | +static int ili8960_remove(struct spi_device *spi) |
||
| 257 | +{ |
||
| 258 | + struct ili8960 *ili8960 = spi_get_drvdata(spi); |
||
| 259 | + |
||
| 260 | + device_remove_file(&ili8960->lcd->dev, &dev_attr_brightness); |
||
| 261 | + lcd_device_unregister(ili8960->lcd); |
||
| 262 | + |
||
| 263 | + spi_set_drvdata(spi, NULL); |
||
| 264 | + return 0; |
||
| 265 | +} |
||
| 266 | + |
||
| 267 | +#ifdef CONFIG_PM |
||
| 268 | + |
||
| 269 | +static int ili8960_suspend(struct spi_device *spi, pm_message_t state) |
||
| 270 | +{ |
||
| 271 | + struct ili8960 *ili8960 = spi_get_drvdata(spi); |
||
| 272 | + |
||
| 273 | + if (ili8960->enabled) |
||
| 274 | + ili8960_programm_power(ili8960->spi, false); |
||
| 275 | + |
||
| 276 | + return 0; |
||
| 277 | +} |
||
| 278 | + |
||
| 279 | +static int ili8960_resume(struct spi_device *spi) |
||
| 280 | +{ |
||
| 281 | + struct ili8960 *ili8960 = spi_get_drvdata(spi); |
||
| 282 | + |
||
| 283 | + if (ili8960->enabled) |
||
| 284 | + ili8960_programm_power(ili8960->spi, true); |
||
| 285 | + |
||
| 286 | + return 0; |
||
| 287 | +} |
||
| 288 | + |
||
| 289 | +#else |
||
| 290 | +#define ili8960_suspend NULL |
||
| 291 | +#define ili8960_resume NULL |
||
| 292 | +#endif |
||
| 293 | + |
||
| 294 | +static struct spi_driver ili8960_driver = { |
||
| 295 | + .driver = { |
||
| 296 | + .name = "ili8960", |
||
| 297 | + .owner = THIS_MODULE, |
||
| 298 | + }, |
||
| 299 | + .probe = ili8960_probe, |
||
| 300 | + .remove = ili8960_remove, |
||
| 301 | + .suspend = ili8960_suspend, |
||
| 302 | + .resume = ili8960_resume, |
||
| 303 | +}; |
||
| 304 | +module_spi_driver(ili8960_driver); |
||
| 305 | + |
||
| 306 | +MODULE_AUTHOR("Lars-Peter Clausen"); |
||
| 307 | +MODULE_LICENSE("GPL"); |
||
| 308 | +MODULE_DESCRIPTION("LCD driver for Ilitek ili8960"); |
||
| 309 | +MODULE_ALIAS("spi:ili8960"); |