* [PATCH] backlight: add ams369fg06 amoled driver
@ 2011-06-28 7:42 Jingoo Han
2011-06-28 23:20 ` Andrew Morton
0 siblings, 1 reply; 2+ messages in thread
From: Jingoo Han @ 2011-06-28 7:42 UTC (permalink / raw)
To: Andrew Morton, LKML; +Cc: Richard Purdie, Jingoo Han
This patch adds ams369fg06 amoled panel driver. The ams369fg06
amoled panel (480 x 800) driver uses 3-wired SPI inteface.
The brightness can be controlled by gamma setting of amoled panel.
Signed-off-by: Jingoo Han <jg1.han@samsung.com>
---
drivers/video/backlight/Kconfig | 8 +
drivers/video/backlight/Makefile | 1 +
drivers/video/backlight/ams369fg06.c | 593 ++++++++++++++++++++++++++++
drivers/video/backlight/ams369fg06_gamma.h | 61 +++
4 files changed, 663 insertions(+), 0 deletions(-)
create mode 100644 drivers/video/backlight/ams369fg06.c
create mode 100644 drivers/video/backlight/ams369fg06_gamma.h
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 2d93c8d..1e54b8b 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -117,6 +117,14 @@ config LCD_LD9040
If you have an LD9040 Panel, say Y to enable its
control driver.
+config LCD_AMS369FG06
+ tristate "AMS369FG06 AMOLED LCD Driver"
+ depends on SPI && BACKLIGHT_CLASS_DEVICE
+ default n
+ help
+ If you have an AMS369FG06 AMOLED Panel, say Y to enable its
+ LCD control driver.
+
endif # LCD_CLASS_DEVICE
#
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index ee72adb..bf1dd92 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_LCD_TDO24M) += tdo24m.o
obj-$(CONFIG_LCD_TOSA) += tosa_lcd.o
obj-$(CONFIG_LCD_S6E63M0) += s6e63m0.o
obj-$(CONFIG_LCD_LD9040) += ld9040.o
+obj-$(CONFIG_LCD_AMS369FG06) += ams369fg06.o
obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o
diff --git a/drivers/video/backlight/ams369fg06.c b/drivers/video/backlight/ams369fg06.c
new file mode 100644
index 0000000..7aa47e1
--- /dev/null
+++ b/drivers/video/backlight/ams369fg06.c
@@ -0,0 +1,593 @@
+/*
+ * ams369fg06 AMOLED LCD panel driver.
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Author: Jingoo Han <jg1.han@samsung.com>
+ *
+ * Derived from drivers/video/s6e63m0.c
+ *
+ * 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.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/wait.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/lcd.h>
+#include <linux/backlight.h>
+
+#include "ams369fg06_gamma.h"
+
+#define SLEEPMSEC 0x1000
+#define ENDDEF 0x2000
+#define DEFMASK 0xFF00
+#define COMMAND_ONLY 0xFE
+#define DATA_ONLY 0xFF
+
+#define MIN_BRIGHTNESS 0
+#define MAX_BRIGHTNESS 255
+#define DEFAULT_BRIGHTNESS 150
+
+#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL)
+
+struct ams369fg06 {
+ struct device *dev;
+ struct spi_device *spi;
+ unsigned int power;
+ struct lcd_device *ld;
+ struct backlight_device *bd;
+ struct lcd_platform_data *lcd_pd;
+};
+
+const unsigned short SEQ_DISPLAY_ON[] = {
+ 0x14, 0x03,
+ ENDDEF, 0x0000
+};
+
+const unsigned short SEQ_DISPLAY_OFF[] = {
+ 0x14, 0x00,
+ ENDDEF, 0x0000
+};
+
+const unsigned short SEQ_STAND_BY_ON[] = {
+ 0x1D, 0xA1,
+ SLEEPMSEC, 200,
+ ENDDEF, 0x0000
+};
+
+const unsigned short SEQ_STAND_BY_OFF[] = {
+ 0x1D, 0xA0,
+ SLEEPMSEC, 250,
+ ENDDEF, 0x0000
+};
+
+const unsigned short SEQ_SETTING[] = {
+ 0x31, 0x08,
+ 0x32, 0x14,
+ 0x30, 0x02,
+ 0x27, 0x01,
+ 0x12, 0x08,
+ 0x13, 0x08,
+ 0x15, 0x00,
+ 0x16, 0x00,
+
+ 0xef, 0xd0,
+ DATA_ONLY, 0xe8,
+
+ 0x39, 0x44,
+ 0x40, 0x00,
+ 0x41, 0x3f,
+ 0x42, 0x2a,
+ 0x43, 0x27,
+ 0x44, 0x27,
+ 0x45, 0x1f,
+ 0x46, 0x44,
+ 0x50, 0x00,
+ 0x51, 0x00,
+ 0x52, 0x17,
+ 0x53, 0x24,
+ 0x54, 0x26,
+ 0x55, 0x1f,
+ 0x56, 0x43,
+ 0x60, 0x00,
+ 0x61, 0x3f,
+ 0x62, 0x2a,
+ 0x63, 0x25,
+ 0x64, 0x24,
+ 0x65, 0x1b,
+ 0x66, 0x5c,
+
+ 0x17, 0x22,
+ 0x18, 0x33,
+ 0x19, 0x03,
+ 0x1a, 0x01,
+ 0x22, 0xa4,
+ 0x23, 0x00,
+ 0x26, 0xa0,
+
+ 0x1d, 0xa0,
+ SLEEPMSEC, 300,
+
+ 0x14, 0x03,
+
+ ENDDEF, 0x0000
+};
+
+static int ams369fg06_spi_write_byte(struct ams369fg06 *lcd, int addr, int data)
+{
+ u16 buf[1];
+ struct spi_message msg;
+
+ struct spi_transfer xfer = {
+ .len = 2,
+ .tx_buf = buf,
+ };
+
+ buf[0] = (addr << 8) | data;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ return spi_sync(lcd->spi, &msg);
+}
+
+static int ams369fg06_spi_write(struct ams369fg06 *lcd, unsigned char address,
+ unsigned char command)
+{
+ int ret = 0;
+
+ if (address != DATA_ONLY)
+ ret = ams369fg06_spi_write_byte(lcd, 0x70, address);
+ if (command != COMMAND_ONLY)
+ ret = ams369fg06_spi_write_byte(lcd, 0x72, command);
+
+ return ret;
+}
+
+static int ams369fg06_panel_send_sequence(struct ams369fg06 *lcd,
+ const unsigned short *wbuf)
+{
+ int ret = 0, i = 0;
+
+ while ((wbuf[i] & DEFMASK) != ENDDEF) {
+ if ((wbuf[i] & DEFMASK) != SLEEPMSEC) {
+ ret = ams369fg06_spi_write(lcd, wbuf[i], wbuf[i+1]);
+ if (ret)
+ break;
+ } else
+ udelay(wbuf[i+1]*1000);
+ i += 2;
+ }
+
+ return ret;
+}
+
+static int _ams369fg06_gamma_ctl(struct ams369fg06 *lcd,
+ const unsigned int *gamma)
+{
+ unsigned int i = 0;
+ int ret = 0;
+
+ for (i = 0 ; i < GAMMA_TABLE_COUNT / 3; i++) {
+ ret = ams369fg06_spi_write(lcd, 0x40 + i, gamma[i]);
+ ret = ams369fg06_spi_write(lcd, 0x50 + i, gamma[i+7*1]);
+ ret = ams369fg06_spi_write(lcd, 0x60 + i, gamma[i+7*2]);
+ if (ret) {
+ dev_err(lcd->dev, "failed to set gamma table.\n");
+ goto gamma_err;
+ }
+ }
+
+gamma_err:
+ return ret;
+}
+
+static int ams369fg06_gamma_ctl(struct ams369fg06 *lcd, int brightness)
+{
+ int ret = 0;
+ int gamma = 0;
+
+ if ((brightness >= 0) && (brightness <= 50))
+ gamma = 0;
+ else if ((brightness > 50) && (brightness <= 100))
+ gamma = 1;
+ else if ((brightness > 100) && (brightness <= 150))
+ gamma = 2;
+ else if ((brightness > 150) && (brightness <= 200))
+ gamma = 3;
+ else if ((brightness > 200) && (brightness <= 255))
+ gamma = 4;
+
+ ret = _ams369fg06_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
+
+ return ret;
+}
+
+static int ams369fg06_ldi_init(struct ams369fg06 *lcd)
+{
+ int ret, i;
+ const unsigned short *init_seq[] = {
+ SEQ_SETTING,
+ SEQ_STAND_BY_OFF,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
+ ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int ams369fg06_ldi_enable(struct ams369fg06 *lcd)
+{
+ int ret, i;
+ const unsigned short *init_seq[] = {
+ SEQ_STAND_BY_OFF,
+ SEQ_DISPLAY_ON,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
+ ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int ams369fg06_ldi_disable(struct ams369fg06 *lcd)
+{
+ int ret, i;
+
+ const unsigned short *init_seq[] = {
+ SEQ_DISPLAY_OFF,
+ SEQ_STAND_BY_ON,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
+ ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int ams369fg06_power_on(struct ams369fg06 *lcd)
+{
+ int ret = 0;
+ struct lcd_platform_data *pd = NULL;
+ struct backlight_device *bd = NULL;
+
+ pd = lcd->lcd_pd;
+ if (!pd) {
+ dev_err(lcd->dev, "platform data is NULL.\n");
+ return -EFAULT;
+ }
+
+ bd = lcd->bd;
+ if (!bd) {
+ dev_err(lcd->dev, "backlight device is NULL.\n");
+ return -EFAULT;
+ }
+
+ if (!pd->power_on) {
+ dev_err(lcd->dev, "power_on is NULL.\n");
+ return -EFAULT;
+ } else {
+ pd->power_on(lcd->ld, 1);
+ mdelay(pd->power_on_delay);
+ }
+
+ if (!pd->reset) {
+ dev_err(lcd->dev, "reset is NULL.\n");
+ return -EFAULT;
+ } else {
+ pd->reset(lcd->ld);
+ mdelay(pd->reset_delay);
+ }
+
+ ret = ams369fg06_ldi_init(lcd);
+ if (ret) {
+ dev_err(lcd->dev, "failed to initialize ldi.\n");
+ return ret;
+ }
+
+ ret = ams369fg06_ldi_enable(lcd);
+ if (ret) {
+ dev_err(lcd->dev, "failed to enable ldi.\n");
+ return ret;
+ }
+
+ /* set brightness to current value after power on or resume. */
+ ret = ams369fg06_gamma_ctl(lcd, bd->props.brightness);
+ if (ret) {
+ dev_err(lcd->dev, "lcd gamma setting failed.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ams369fg06_power_off(struct ams369fg06 *lcd)
+{
+ int ret = 0;
+ struct lcd_platform_data *pd = NULL;
+
+ pd = lcd->lcd_pd;
+ if (!pd) {
+ dev_err(lcd->dev, "platform data is NULL\n");
+ return -EFAULT;
+ }
+
+ ret = ams369fg06_ldi_disable(lcd);
+ if (ret) {
+ dev_err(lcd->dev, "lcd setting failed.\n");
+ return -EIO;
+ }
+
+ mdelay(pd->power_off_delay);
+
+ if (!pd->power_on) {
+ dev_err(lcd->dev, "power_on is NULL.\n");
+ return -EFAULT;
+ } else
+ pd->power_on(lcd->ld, 0);
+
+ return 0;
+}
+
+static int ams369fg06_power(struct ams369fg06 *lcd, int power)
+{
+ int ret = 0;
+
+ if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
+ ret = ams369fg06_power_on(lcd);
+ else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
+ ret = ams369fg06_power_off(lcd);
+
+ if (!ret)
+ lcd->power = power;
+
+ return ret;
+}
+
+static int ams369fg06_get_power(struct lcd_device *ld)
+{
+ struct ams369fg06 *lcd = lcd_get_data(ld);
+
+ return lcd->power;
+}
+
+static int ams369fg06_set_power(struct lcd_device *ld, int power)
+{
+ struct ams369fg06 *lcd = lcd_get_data(ld);
+
+ if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
+ power != FB_BLANK_NORMAL) {
+ dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
+ return -EINVAL;
+ }
+
+ return ams369fg06_power(lcd, power);
+}
+
+static int ams369fg06_get_brightness(struct backlight_device *bd)
+{
+ return bd->props.brightness;
+}
+
+static int ams369fg06_set_brightness(struct backlight_device *bd)
+{
+ int ret = 0;
+ int brightness = bd->props.brightness;
+ struct ams369fg06 *lcd = dev_get_drvdata(&bd->dev);
+
+ if (brightness < MIN_BRIGHTNESS ||
+ brightness > bd->props.max_brightness) {
+ dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
+ MIN_BRIGHTNESS, MAX_BRIGHTNESS);
+ return -EINVAL;
+ }
+
+ ret = ams369fg06_gamma_ctl(lcd, bd->props.brightness);
+ if (ret) {
+ dev_err(&bd->dev, "lcd brightness setting failed.\n");
+ return -EIO;
+ }
+
+ return ret;
+}
+
+static struct lcd_ops ams369fg06_lcd_ops = {
+ .get_power = ams369fg06_get_power,
+ .set_power = ams369fg06_set_power,
+};
+
+static const struct backlight_ops ams369fg06_backlight_ops = {
+ .get_brightness = ams369fg06_get_brightness,
+ .update_status = ams369fg06_set_brightness,
+};
+
+static int __init ams369fg06_probe(struct spi_device *spi)
+{
+ int ret = 0;
+ struct ams369fg06 *lcd = NULL;
+ struct lcd_device *ld = NULL;
+ struct backlight_device *bd = NULL;
+
+ lcd = kzalloc(sizeof(struct ams369fg06), GFP_KERNEL);
+ if (!lcd)
+ return -ENOMEM;
+
+ /* ams369fg06 lcd panel uses 3-wire 16bits SPI Mode. */
+ spi->bits_per_word = 16;
+
+ ret = spi_setup(spi);
+ if (ret < 0) {
+ dev_err(&spi->dev, "spi setup failed.\n");
+ goto out_free_lcd;
+ }
+
+ lcd->spi = spi;
+ lcd->dev = &spi->dev;
+
+ lcd->lcd_pd = (struct lcd_platform_data *)spi->dev.platform_data;
+ if (!lcd->lcd_pd) {
+ dev_err(&spi->dev, "platform data is NULL\n");
+ goto out_free_lcd;
+ }
+
+ ld = lcd_device_register("ams369fg06", &spi->dev, lcd,
+ &ams369fg06_lcd_ops);
+ if (IS_ERR(ld)) {
+ ret = PTR_ERR(ld);
+ goto out_free_lcd;
+ }
+
+ lcd->ld = ld;
+
+ bd = backlight_device_register("ams369fg06-bl", &spi->dev, lcd,
+ &ams369fg06_backlight_ops, NULL);
+ if (IS_ERR(bd)) {
+ ret = PTR_ERR(bd);
+ goto out_lcd_unregister;
+ }
+
+ bd->props.max_brightness = MAX_BRIGHTNESS;
+ bd->props.brightness = DEFAULT_BRIGHTNESS;
+ bd->props.type = BACKLIGHT_RAW;
+ lcd->bd = bd;
+
+ if (!lcd->lcd_pd->lcd_enabled) {
+ /*
+ * if lcd panel was off from bootloader then
+ * current lcd status is powerdown and then
+ * it enables lcd panel.
+ */
+ lcd->power = FB_BLANK_POWERDOWN;
+
+ ams369fg06_power(lcd, FB_BLANK_UNBLANK);
+ } else
+ lcd->power = FB_BLANK_UNBLANK;
+
+ dev_set_drvdata(&spi->dev, lcd);
+
+ dev_info(&spi->dev, "ams369fg06 panel driver has been probed.\n");
+
+ return 0;
+
+out_lcd_unregister:
+ lcd_device_unregister(ld);
+out_free_lcd:
+ kfree(lcd);
+ return ret;
+}
+
+static int __devexit ams369fg06_remove(struct spi_device *spi)
+{
+ struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
+
+ ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
+ lcd_device_unregister(lcd->ld);
+ kfree(lcd);
+
+ return 0;
+}
+
+#if defined(CONFIG_PM)
+unsigned int before_power;
+
+static int ams369fg06_suspend(struct spi_device *spi, pm_message_t mesg)
+{
+ int ret = 0;
+ struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
+
+ dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
+
+ before_power = lcd->power;
+
+ /*
+ * when lcd panel is suspend, lcd panel becomes off
+ * regardless of status.
+ */
+ ret = ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
+
+ return ret;
+}
+
+static int ams369fg06_resume(struct spi_device *spi)
+{
+ int ret = 0;
+ struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
+
+ /*
+ * after suspended, if lcd panel status is FB_BLANK_UNBLANK
+ * (at that time, before_power is FB_BLANK_UNBLANK) then
+ * it changes that status to FB_BLANK_POWERDOWN to get lcd on.
+ */
+ if (before_power == FB_BLANK_UNBLANK)
+ lcd->power = FB_BLANK_POWERDOWN;
+
+ dev_dbg(&spi->dev, "before_power = %d\n", before_power);
+
+ ret = ams369fg06_power(lcd, before_power);
+
+ return ret;
+}
+#else
+#define ams369fg06_suspend NULL
+#define ams369fg06_resume NULL
+#endif
+
+void ams369fg06_shutdown(struct spi_device *spi)
+{
+ struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
+
+ ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
+}
+
+static struct spi_driver ams369fg06_driver = {
+ .driver = {
+ .name = "ams369fg06",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = ams369fg06_probe,
+ .remove = __devexit_p(ams369fg06_remove),
+ .shutdown = ams369fg06_shutdown,
+ .suspend = ams369fg06_suspend,
+ .resume = ams369fg06_resume,
+};
+
+static int __init ams369fg06_init(void)
+{
+ return spi_register_driver(&ams369fg06_driver);
+}
+
+static void __exit ams369fg06_exit(void)
+{
+ spi_unregister_driver(&ams369fg06_driver);
+}
+
+module_init(ams369fg06_init);
+module_exit(ams369fg06_exit);
+
+MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
+MODULE_DESCRIPTION("ams369fg06 LCD Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/ams369fg06_gamma.h b/drivers/video/backlight/ams369fg06_gamma.h
new file mode 100644
index 0000000..6ecea88
--- /dev/null
+++ b/drivers/video/backlight/ams369fg06_gamma.h
@@ -0,0 +1,61 @@
+/*
+ * Gamma level definitions.
+ *
+ * Copyright (c) 2011 Samsung Electronics
+ * Jingoo Han <jg1.han@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef _AMS369FG06_BRIGHTNESS_H
+#define _AMS369FG06_BRIGHTNESS_H
+
+#define MAX_GAMMA_LEVEL 5
+#define GAMMA_TABLE_COUNT 21
+
+/* gamma value: 2.2 */
+static const unsigned int ams369fg06_22_250[] = {
+ 0x00, 0x3f, 0x2a, 0x27, 0x27, 0x1f, 0x44,
+ 0x00, 0x00, 0x17, 0x24, 0x26, 0x1f, 0x43,
+ 0x00, 0x3f, 0x2a, 0x25, 0x24, 0x1b, 0x5c,
+};
+
+static const unsigned int ams369fg06_22_200[] = {
+ 0x00, 0x3f, 0x28, 0x29, 0x27, 0x21, 0x3e,
+ 0x00, 0x00, 0x10, 0x25, 0x27, 0x20, 0x3d,
+ 0x00, 0x3f, 0x28, 0x27, 0x25, 0x1d, 0x53,
+};
+
+static const unsigned int ams369fg06_22_150[] = {
+ 0x00, 0x3f, 0x2d, 0x29, 0x28, 0x23, 0x37,
+ 0x00, 0x00, 0x0b, 0x25, 0x28, 0x22, 0x36,
+ 0x00, 0x3f, 0x2b, 0x28, 0x26, 0x1f, 0x4a,
+};
+
+static const unsigned int ams369fg06_22_100[] = {
+ 0x00, 0x3f, 0x30, 0x2a, 0x2b, 0x24, 0x2f,
+ 0x00, 0x00, 0x00, 0x25, 0x29, 0x24, 0x2e,
+ 0x00, 0x3f, 0x2f, 0x29, 0x29, 0x21, 0x3f,
+};
+
+static const unsigned int ams369fg06_22_50[] = {
+ 0x00, 0x3f, 0x3c, 0x2c, 0x2d, 0x27, 0x24,
+ 0x00, 0x00, 0x00, 0x22, 0x2a, 0x27, 0x23,
+ 0x00, 0x3f, 0x3b, 0x2c, 0x2b, 0x24, 0x31,
+};
+
+struct ams369fg06_gamma {
+ unsigned int *gamma_22_table[MAX_GAMMA_LEVEL];
+};
+
+struct ams369fg06_gamma gamma_table = {
+ .gamma_22_table[0] = (unsigned int *)&ams369fg06_22_50,
+ .gamma_22_table[1] = (unsigned int *)&ams369fg06_22_100,
+ .gamma_22_table[2] = (unsigned int *)&ams369fg06_22_150,
+ .gamma_22_table[3] = (unsigned int *)&ams369fg06_22_200,
+ .gamma_22_table[4] = (unsigned int *)&ams369fg06_22_250,
+};
+
+#endif
--
1.7.1
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH] backlight: add ams369fg06 amoled driver
2011-06-28 7:42 [PATCH] backlight: add ams369fg06 amoled driver Jingoo Han
@ 2011-06-28 23:20 ` Andrew Morton
0 siblings, 0 replies; 2+ messages in thread
From: Andrew Morton @ 2011-06-28 23:20 UTC (permalink / raw)
To: Jingoo Han; +Cc: LKML, Richard Purdie
On Tue, 28 Jun 2011 16:42:57 +0900
Jingoo Han <jg1.han@samsung.com> wrote:
> This patch adds ams369fg06 amoled panel driver. The ams369fg06
> amoled panel (480 x 800) driver uses 3-wired SPI inteface.
> The brightness can be controlled by gamma setting of amoled panel.
>
>
> ...
>
> +
> +#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL)
This could have been implemented as a regular lower-case C function.
They're better than macros.
> +struct ams369fg06 {
> + struct device *dev;
> + struct spi_device *spi;
> + unsigned int power;
> + struct lcd_device *ld;
> + struct backlight_device *bd;
> + struct lcd_platform_data *lcd_pd;
> +};
> +
> +const unsigned short SEQ_DISPLAY_ON[] = {
> + 0x14, 0x03,
> + ENDDEF, 0x0000
> +};
> +
> +const unsigned short SEQ_DISPLAY_OFF[] = {
> + 0x14, 0x00,
> + ENDDEF, 0x0000
> +};
> +
> +const unsigned short SEQ_STAND_BY_ON[] = {
> + 0x1D, 0xA1,
> + SLEEPMSEC, 200,
> + ENDDEF, 0x0000
> +};
> +
> +const unsigned short SEQ_STAND_BY_OFF[] = {
> + 0x1D, 0xA0,
> + SLEEPMSEC, 250,
> + ENDDEF, 0x0000
> +};
> +
> +const unsigned short SEQ_SETTING[] = {
The patch adds lots of globally-scoped symbols, such as the above.
There are others, too. Please go through it and make as many things
static as possible.
It's odd that these symbols are all-uppercase. The reader will expect
them to be cpp macros, but they aren't. I suggest they be made
lower-case.
>
> ...
>
> +static int ams369fg06_panel_send_sequence(struct ams369fg06 *lcd,
> + const unsigned short *wbuf)
> +{
> + int ret = 0, i = 0;
> +
> + while ((wbuf[i] & DEFMASK) != ENDDEF) {
> + if ((wbuf[i] & DEFMASK) != SLEEPMSEC) {
> + ret = ams369fg06_spi_write(lcd, wbuf[i], wbuf[i+1]);
> + if (ret)
> + break;
> + } else
> + udelay(wbuf[i+1]*1000);
mdelay()
> + i += 2;
> + }
> +
> + return ret;
> +}
> +
>
> ...
>
> +static int ams369fg06_gamma_ctl(struct ams369fg06 *lcd, int brightness)
> +{
> + int ret = 0;
> + int gamma = 0;
> +
> + if ((brightness >= 0) && (brightness <= 50))
> + gamma = 0;
> + else if ((brightness > 50) && (brightness <= 100))
> + gamma = 1;
> + else if ((brightness > 100) && (brightness <= 150))
> + gamma = 2;
> + else if ((brightness > 150) && (brightness <= 200))
> + gamma = 3;
> + else if ((brightness > 200) && (brightness <= 255))
> + gamma = 4;
gcc has a
switch (foo) {
case 0..50:
case 51..100:
feature. But I wouldn't really suggest that you use it ;)
> + ret = _ams369fg06_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
> +
> + return ret;
> +}
> +
> +static int ams369fg06_ldi_init(struct ams369fg06 *lcd)
> +{
> + int ret, i;
> + const unsigned short *init_seq[] = {
> + SEQ_SETTING,
> + SEQ_STAND_BY_OFF,
> + };
Please consider making things like this static also. Probably the
compiler is already doing this internally. If it isn't doing this then
the array gets needlessly assembled on the stack each time the function
is called!
> + for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
> + ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
> + if (ret)
> + break;
> + }
> +
> + return ret;
> +}
> +
>
> ...
>
> +static int __init ams369fg06_probe(struct spi_device *spi)
> +{
> + int ret = 0;
> + struct ams369fg06 *lcd = NULL;
> + struct lcd_device *ld = NULL;
> + struct backlight_device *bd = NULL;
> +
> + lcd = kzalloc(sizeof(struct ams369fg06), GFP_KERNEL);
> + if (!lcd)
> + return -ENOMEM;
> +
> + /* ams369fg06 lcd panel uses 3-wire 16bits SPI Mode. */
> + spi->bits_per_word = 16;
> +
> + ret = spi_setup(spi);
> + if (ret < 0) {
> + dev_err(&spi->dev, "spi setup failed.\n");
> + goto out_free_lcd;
> + }
> +
> + lcd->spi = spi;
> + lcd->dev = &spi->dev;
> +
> + lcd->lcd_pd = (struct lcd_platform_data *)spi->dev.platform_data;
This is an unneeed and undesirable cast of void*.
> + if (!lcd->lcd_pd) {
> + dev_err(&spi->dev, "platform data is NULL\n");
> + goto out_free_lcd;
> + }
> +
> + ld = lcd_device_register("ams369fg06", &spi->dev, lcd,
> + &ams369fg06_lcd_ops);
> + if (IS_ERR(ld)) {
> + ret = PTR_ERR(ld);
> + goto out_free_lcd;
> + }
> +
> + lcd->ld = ld;
> +
> + bd = backlight_device_register("ams369fg06-bl", &spi->dev, lcd,
> + &ams369fg06_backlight_ops, NULL);
> + if (IS_ERR(bd)) {
> + ret = PTR_ERR(bd);
> + goto out_lcd_unregister;
> + }
> +
> + bd->props.max_brightness = MAX_BRIGHTNESS;
> + bd->props.brightness = DEFAULT_BRIGHTNESS;
> + bd->props.type = BACKLIGHT_RAW;
> + lcd->bd = bd;
> +
> + if (!lcd->lcd_pd->lcd_enabled) {
> + /*
> + * if lcd panel was off from bootloader then
> + * current lcd status is powerdown and then
> + * it enables lcd panel.
> + */
> + lcd->power = FB_BLANK_POWERDOWN;
> +
> + ams369fg06_power(lcd, FB_BLANK_UNBLANK);
> + } else
> + lcd->power = FB_BLANK_UNBLANK;
> +
> + dev_set_drvdata(&spi->dev, lcd);
> +
> + dev_info(&spi->dev, "ams369fg06 panel driver has been probed.\n");
> +
> + return 0;
> +
> +out_lcd_unregister:
> + lcd_device_unregister(ld);
> +out_free_lcd:
> + kfree(lcd);
> + return ret;
> +}
> +
>
> ...
>
> --- /dev/null
> +++ b/drivers/video/backlight/ams369fg06_gamma.h
> @@ -0,0 +1,61 @@
> +/*
> + * Gamma level definitions.
> + *
> + * Copyright (c) 2011 Samsung Electronics
> + * Jingoo Han <jg1.han@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +*/
> +
> +#ifndef _AMS369FG06_BRIGHTNESS_H
> +#define _AMS369FG06_BRIGHTNESS_H
> +
> +#define MAX_GAMMA_LEVEL 5
> +#define GAMMA_TABLE_COUNT 21
> +
> +/* gamma value: 2.2 */
> +static const unsigned int ams369fg06_22_250[] = {
> + 0x00, 0x3f, 0x2a, 0x27, 0x27, 0x1f, 0x44,
> + 0x00, 0x00, 0x17, 0x24, 0x26, 0x1f, 0x43,
> + 0x00, 0x3f, 0x2a, 0x25, 0x24, 0x1b, 0x5c,
> +};
> +
> +static const unsigned int ams369fg06_22_200[] = {
> + 0x00, 0x3f, 0x28, 0x29, 0x27, 0x21, 0x3e,
> + 0x00, 0x00, 0x10, 0x25, 0x27, 0x20, 0x3d,
> + 0x00, 0x3f, 0x28, 0x27, 0x25, 0x1d, 0x53,
> +};
> +
> +static const unsigned int ams369fg06_22_150[] = {
> + 0x00, 0x3f, 0x2d, 0x29, 0x28, 0x23, 0x37,
> + 0x00, 0x00, 0x0b, 0x25, 0x28, 0x22, 0x36,
> + 0x00, 0x3f, 0x2b, 0x28, 0x26, 0x1f, 0x4a,
> +};
> +
> +static const unsigned int ams369fg06_22_100[] = {
> + 0x00, 0x3f, 0x30, 0x2a, 0x2b, 0x24, 0x2f,
> + 0x00, 0x00, 0x00, 0x25, 0x29, 0x24, 0x2e,
> + 0x00, 0x3f, 0x2f, 0x29, 0x29, 0x21, 0x3f,
> +};
> +
> +static const unsigned int ams369fg06_22_50[] = {
> + 0x00, 0x3f, 0x3c, 0x2c, 0x2d, 0x27, 0x24,
> + 0x00, 0x00, 0x00, 0x22, 0x2a, 0x27, 0x23,
> + 0x00, 0x3f, 0x3b, 0x2c, 0x2b, 0x24, 0x31,
> +};
> +
> +struct ams369fg06_gamma {
> + unsigned int *gamma_22_table[MAX_GAMMA_LEVEL];
> +};
> +
> +struct ams369fg06_gamma gamma_table = {
> + .gamma_22_table[0] = (unsigned int *)&ams369fg06_22_50,
> + .gamma_22_table[1] = (unsigned int *)&ams369fg06_22_100,
> + .gamma_22_table[2] = (unsigned int *)&ams369fg06_22_150,
> + .gamma_22_table[3] = (unsigned int *)&ams369fg06_22_200,
> + .gamma_22_table[4] = (unsigned int *)&ams369fg06_22_250,
> +};
This is a bad idea, IMO. If this header file is included in more than
one .c file then each .c file gets its own copy of the data! Unless
the compiler and linker are being very clever, whcih they might be
nowadays.
But we shouldn't depend on that, and it makes no sense to put this info
in a header file, because that header file will never be used from more
than one .c file.
IOW, this stuff should be moved into the .c file.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2011-06-28 23:21 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-06-28 7:42 [PATCH] backlight: add ams369fg06 amoled driver Jingoo Han
2011-06-28 23:20 ` Andrew Morton
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox