Skip to contentSkip to conversation

backlight: enable backlight in 88pm860x

PATCH New Haojian Zhuang 3 participants 5 messages Dec 9, 2009 – Jan 12, 2010

lore
Share & tools
lore
#1 lore raw

PATCH v2 6/9 backlight: enable backlight in 88pm860x

Hi Haojian, Richard,

Quoted 6 lines · Haojian Zhuang

From 8d2bc9826f758f113fde6f3fd01723111fbf2a91 Mon Sep 17 00:00:00 2001
From: Haojian Zhuang <[email protected]>
Date: Mon, 9 Nov 2009 12:41:07 -0500
Subject: [PATCH] backlight: enable backlight in 88pm860x

At most, three backlight device can be supported in 88pm860x driver.

I applied this patch and the LED one to my for-next branch. Richard, whenever you have time for that, could you please quickly check if they oook ok to you ? They're 2.6.34 material, so no rush here.

Cheers,
Samuel.

Quoted 345 lines
---
 drivers/video/backlight/88pm860x_bl.c |  304 +++++++++++++++++++++++++++++++++
 drivers/video/backlight/Kconfig       |    6 +
 drivers/video/backlight/Makefile      |    1 +
 3 files changed, 311 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/backlight/88pm860x_bl.c
drivers/video/backlight/88pm860x_bl.c new file +304 -0
new file mode 100644
index 0000000..b8f705c
@@ -0,0 +1,304 @@
+/*
+ * Backlight driver for Marvell Semiconductor 88PM8606
+ *
+ * Copyright (C) 2009 Marvell International Ltd.
+ *	Haojian Zhuang <[email protected]>
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/fb.h>
+#include <linux/i2c.h>
+#include <linux/backlight.h>
+#include <linux/mfd/88pm860x.h>
+
+#define MAX_BRIGHTNESS		(0xFF)
+#define MIN_BRIGHTNESS		(0)
+
+#define CURRENT_MASK		(0x1F << 1)
+
+struct pm860x_backlight_data {
+	struct pm860x_chip *chip;
+	struct i2c_client *i2c;
+	int	current_brightness;
+	int	port;
+	int	pwm;
+	int	iset;
+};
+
+static inline int wled_a(int port)
+{
+	int ret;
+
+	ret = ((port - PM8606_BACKLIGHT1) << 1) + 2;
+	return ret;
+}
+
+static inline int wled_b(int port)
+{
+	int ret;
+
+	ret = ((port - PM8606_BACKLIGHT1) << 1) + 3;
+	return ret;
+}
+
+/* WLED2 & WLED3 share the same IDC */
+static inline int wled_idc(int port)
+{
+	int ret;
+
+	switch (port) {
+	case PM8606_BACKLIGHT1:
+	case PM8606_BACKLIGHT2:
+		ret = ((port - PM8606_BACKLIGHT1) << 1) + 3;
+		break;
+	case PM8606_BACKLIGHT3:
+	default:
+		ret = ((port - PM8606_BACKLIGHT2) << 1) + 3;
+		break;
+	}
+	return ret;
+}
+
+static int pm860x_backlight_set(struct backlight_device *bl, int brightness)
+{
+	struct pm860x_backlight_data *data = bl_get_data(bl);
+	struct pm860x_chip *chip = data->chip;
+	unsigned char value;
+	int ret;
+
+	if (brightness > MAX_BRIGHTNESS)
+		value = MAX_BRIGHTNESS;
+	else
+		value = brightness;
+
+	ret = pm860x_reg_write(data->i2c, wled_a(data->port), value);
+	if (ret < 0)
+		goto out;
+
+	if ((data->current_brightness == 0) && brightness) {
+		if (data->iset) {
+			ret = pm860x_set_bits(data->i2c, wled_idc(data->port),
+					      CURRENT_MASK, data->iset);
+			if (ret < 0)
+				goto out;
+		}
+		if (data->pwm) {
+			ret = pm860x_set_bits(data->i2c, PM8606_PWM,
+					      PM8606_PWM_FREQ_MASK, data->pwm);
+			if (ret < 0)
+				goto out;
+		}
+		if (brightness == MAX_BRIGHTNESS) {
+			/* set WLED_ON bit as 100% */
+			ret = pm860x_set_bits(data->i2c, wled_b(data->port),
+					      PM8606_WLED_ON, PM8606_WLED_ON);
+		}
+	} else {
+		if (brightness == MAX_BRIGHTNESS) {
+			/* set WLED_ON bit as 100% */
+			ret = pm860x_set_bits(data->i2c, wled_b(data->port),
+					      PM8606_WLED_ON, PM8606_WLED_ON);
+		} else {
+			/* clear WLED_ON bit since it's not 100% */
+			ret = pm860x_set_bits(data->i2c, wled_b(data->port),
+					      PM8606_WLED_ON, 0);
+		}
+	}
+	if (ret < 0)
+		goto out;
+
+	dev_dbg(chip->dev, "set brightness %d\n", value);
+	data->current_brightness = value;
+	return 0;
+out:
+	dev_dbg(chip->dev, "set brightness %d failure with return "
+		"value:%d\n", value, ret);
+	return ret;
+}
+
+static int pm860x_backlight_update_status(struct backlight_device *bl)
+{
+	int brightness = bl->props.brightness;
+
+	if (bl->props.power != FB_BLANK_UNBLANK)
+		brightness = 0;
+
+	if (bl->props.fb_blank != FB_BLANK_UNBLANK)
+		brightness = 0;
+
+	if (bl->props.state & BL_CORE_SUSPENDED)
+		brightness = 0;
+
+	return pm860x_backlight_set(bl, brightness);
+}
+
+static int pm860x_backlight_get_brightness(struct backlight_device *bl)
+{
+	struct pm860x_backlight_data *data = bl_get_data(bl);
+	struct pm860x_chip *chip = data->chip;
+	int ret;
+
+	ret = pm860x_reg_read(data->i2c, wled_a(data->port));
+	if (ret < 0)
+		goto out;
+	data->current_brightness = ret;
+	dev_dbg(chip->dev, "get brightness %d\n", data->current_brightness);
+	return data->current_brightness;
+out:
+	return -EINVAL;
+}
+
+static struct backlight_ops pm860x_backlight_ops = {
+	.options	= BL_CORE_SUSPENDRESUME,
+	.update_status	= pm860x_backlight_update_status,
+	.get_brightness	= pm860x_backlight_get_brightness,
+};
+
+static int __check_device(struct pm860x_backlight_pdata *pdata, char *name)
+{
+	struct pm860x_backlight_pdata *p = pdata;
+	int ret = -EINVAL;
+
+	while (p && p->id) {
+		if ((p->id != PM8606_ID_BACKLIGHT) || (p->flags < 0))
+			break;
+
+		if (!strncmp(name, pm860x_backlight_name[p->flags],
+			MFD_NAME_SIZE)) {
+			ret = (int)p->flags;
+			break;
+		}
+		p++;
+	}
+	return ret;
+}
+
+static int pm860x_backlight_probe(struct platform_device *pdev)
+{
+	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+	struct pm860x_platform_data *pm860x_pdata;
+	struct pm860x_backlight_pdata *pdata = NULL;
+	struct pm860x_backlight_data *data;
+	struct backlight_device *bl;
+	struct resource *res;
+	unsigned char value;
+	char name[MFD_NAME_SIZE];
+	int ret;
+
+	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "No I/O resource!\n");
+		return -EINVAL;
+	}
+
+	if (pdev->dev.parent->platform_data) {
+		pm860x_pdata = pdev->dev.parent->platform_data;
+		pdata = pm860x_pdata->backlight;
+	}
+	if (pdata == NULL) {
+		dev_err(&pdev->dev, "platform data isn't assigned to "
+			"backlight\n");
+		return -EINVAL;
+	}
+
+	data = kzalloc(sizeof(struct pm860x_backlight_data), GFP_KERNEL);
+	if (data == NULL)
+		return -ENOMEM;
+	strncpy(name, res->name, MFD_NAME_SIZE);
+	data->chip = chip;
+	data->i2c = (chip->id == CHIP_PM8606) ? chip->client	\
+			: chip->companion;
+	data->current_brightness = MAX_BRIGHTNESS;
+	data->pwm = pdata->pwm;
+	data->iset = pdata->iset;
+	data->port = __check_device(pdata, name);
+	if (data->port < 0) {
+		dev_err(&pdev->dev, "wrong platform data is assigned");
+		return -EINVAL;
+	}
+
+	bl = backlight_device_register(name, &pdev->dev, data,
+					&pm860x_backlight_ops);
+	if (IS_ERR(bl)) {
+		dev_err(&pdev->dev, "failed to register backlight\n");
+		kfree(data);
+		return PTR_ERR(bl);
+	}
+	bl->props.max_brightness = MAX_BRIGHTNESS;
+	bl->props.brightness = MAX_BRIGHTNESS;
+
+	platform_set_drvdata(pdev, bl);
+
+	/* Enable reference VSYS */
+	ret = pm860x_reg_read(data->i2c, PM8606_VSYS);
+	if (ret < 0)
+		goto out;
+	if ((ret & PM8606_VSYS_EN) == 0) {
+		value = ret | PM8606_VSYS_EN;
+		ret = pm860x_reg_write(data->i2c, PM8606_VSYS, value);
+		if (ret < 0)
+			goto out;
+	}
+	/* Enable reference OSC */
+	ret = pm860x_reg_read(data->i2c, PM8606_MISC);
+	if (ret < 0)
+		goto out;
+	if ((ret & PM8606_MISC_OSC_EN) == 0) {
+		value = ret | PM8606_MISC_OSC_EN;
+		ret = pm860x_reg_write(data->i2c, PM8606_MISC, value);
+		if (ret < 0)
+			goto out;
+	}
+	/* read current backlight */
+	ret = pm860x_backlight_get_brightness(bl);
+	if (ret < 0)
+		goto out;
+
+	backlight_update_status(bl);
+	return 0;
+out:
+	kfree(data);
+	return ret;
+}
+
+static int pm860x_backlight_remove(struct platform_device *pdev)
+{
+	struct backlight_device *bl = platform_get_drvdata(pdev);
+	struct pm860x_backlight_data *data = bl_get_data(bl);
+
+	backlight_device_unregister(bl);
+	kfree(data);
+	return 0;
+}
+
+static struct platform_driver pm860x_backlight_driver = {
+	.driver		= {
+		.name	= "88pm860x-backlight",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= pm860x_backlight_probe,
+	.remove		= pm860x_backlight_remove,
+};
+
+static int __init pm860x_backlight_init(void)
+{
+	return platform_driver_register(&pm860x_backlight_driver);
+}
+module_init(pm860x_backlight_init);
+
+static void __exit pm860x_backlight_exit(void)
+{
+	platform_driver_unregister(&pm860x_backlight_driver);
+}
+module_exit(pm860x_backlight_exit);
+
+MODULE_DESCRIPTION("Backlight Driver for Marvell Semiconductor 88PM8606");
+MODULE_AUTHOR("Haojian Zhuang <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:88pm860x-backlight");
drivers/video/backlight/Kconfig +6 -0
index 09bfa96..96a6b30 100644
@@ -262,3 +262,9 @@ config BACKLIGHT_ADP5520
 	  To compile this driver as a module, choose M here: the module will
 	  be called adp5520_bl.
 
+config BACKLIGHT_88PM860X
+	tristate "Backlight Driver for 88PM8606 using WLED"
+	depends on BACKLIGHT_CLASS_DEVICE && MFD_88PM860X
+	help
+	  Say Y to enable the backlight driver for Marvell 88PM8606.
+
drivers/video/backlight/Makefile +1 -0
index 9a40554..802d467 100644
@@ -28,4 +28,5 @@ obj-$(CONFIG_BACKLIGHT_SAHARA)	+= kb3886_bl.o
 obj-$(CONFIG_BACKLIGHT_WM831X)	+= wm831x_bl.o
 obj-$(CONFIG_BACKLIGHT_ADX)    += adx_bl.o
 obj-$(CONFIG_BACKLIGHT_ADP5520)	+= adp5520_bl.o
+obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o
 
Signature · 1 line
1.5.6.5
Signature · 2 lines
Intel Open Source Technology Centre
http://oss.intel.com/
#2 lore raw

PATCH v2 6/9 backlight: enable backlight in 88pm860x

Quoted 8 lines · Samuel Ortiz

Hi Haojian, Richard,

Quoted 5 lines · Haojian Zhuang

From 8d2bc9826f758f113fde6f3fd01723111fbf2a91 Mon Sep 17 00:00:00 2001
From: Haojian Zhuang <[email protected]>
Date: Mon, 9 Nov 2009 12:41:07 -0500
Subject: [PATCH] backlight: enable backlight in 88pm860x

Quoted 4 lines
Quoted 1 line

At most, three backlight device can be supported in 88pm860x driver.

I applied this patch and the LED one to my for-next branch. Richard, whenever you have time for that, could you please quickly check if they oook ok to you ? They're 2.6.34 material, so no rush here.

I had a look through, they look basically ok.

In the LED patch I'm not sure I like the SET_BRIGHTNESS and SET_BLINK sharing of a workqueue though. Its not going to crash, I can just see values potentially getting lost as the code has a race condition. It would be easier if it just compared led->current_brightness to led->brightness acting if needed and something similar for LED blinking.

Cheers,

Richard

Signature · 2 lines
Richard Purdie
Intel Open Source Technology Centre
#3 lore raw

PATCH v2 6/9 backlight: enable backlight in 88pm860x

Quoted 22 lines · Richard Purdie
Quoted 8 lines · Samuel Ortiz

Hi Haojian, Richard,

Quoted 5 lines · Haojian Zhuang

From 8d2bc9826f758f113fde6f3fd01723111fbf2a91 Mon Sep 17 00:00:00 2001
From: Haojian Zhuang <[email protected]>
Date: Mon, 9 Nov 2009 12:41:07 -0500
Subject: [PATCH] backlight: enable backlight in 88pm860x

Quoted 4 lines
Quoted 1 line

At most, three backlight device can be supported in 88pm860x driver.

I applied this patch and the LED one to my for-next branch. Richard, whenever you have time for that, could you please quickly check if they oook ok to you ? They're 2.6.34 material, so no rush here.

I had a look through, they look basically ok.

In the LED patch I'm not sure I like the SET_BRIGHTNESS and SET_BLINK sharing of a workqueue though. Its not going to crash, I can just see values potentially getting lost as the code has a race condition. It would be easier if it just compared led->current_brightness to led->brightness acting if needed and something similar for LED blinking.

Quoted 4 lines

Cheers,

Richard

Excuse me that there's a mutex lock in __led_set(). Both SET_BRIGHTNESS & SET_BLINK calls __led_set(). There shouldn't be race condition on set led. What's your opinion?

Best Regards
Haojian

#4 lore raw

PATCH v2 6/9 backlight: enable backlight in 88pm860x

Quoted 10 lines · Haojian Zhuang

On Fri, Jan 8, 2010 at 7:27 AM, Richard Purdie <[email protected]> > In the LED patch I'm not sure I like the SET_BRIGHTNESS and SET_BLINK

Quoted 4 lines

sharing of a workqueue though. Its not going to crash, I can just see values potentially getting lost as the code has a race condition. It would be easier if it just compared led->current_brightness to led->brightness acting if needed and something similar for LED blinking.

Excuse me that there's a mutex lock in __led_set(). Both SET_BRIGHTNESS & SET_BLINK calls __led_set(). There shouldn't be race condition on set led. What's your opinion?

There is a race condition since if you get a blink and brightness set call at around the same time, only one of them will end up being applied. Its not going to crash but its also less than ideal.

Cheers,

Richard