summaryrefslogtreecommitdiffstats
path: root/drivers/video
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-10-05 12:01:30 +0900
committerLinus Torvalds <torvalds@linux-foundation.org>2012-10-05 12:01:30 +0900
commit578f1ef91aa92beb571bfb9af8f4d18f405f3b9e (patch)
tree8ff59e772d09180b7e7f952a8c90a1bcf25e1d19 /drivers/video
parentecefbd94b834fa32559d854646d777c56749ef1c (diff)
parent74d8378159de16a0a1d1975d4778120d263d6000 (diff)
Merge tag 'mfd-3.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6
Pull MFD changes from Samuel Ortiz: "MFD bits for the 3.7 merge window. As usual we have a few new drivers: - TI LP8788 - TI OMAP USB TLL - Maxim MAX8907 - SMSC ECE1099 - Dialog Semiconductor DA9055 - A simpler syscon driver that allow us to get rid of the anatop one. Drivers are also gradually getting Device Tree and IRQ domain support. The following drivers got DT support: - palmas, 88pm860x, tc3589x and twl4030-audio And those ones now use the IRQ domain APIs: - 88pm860x, tc3589x, db8500_prcmu Also some other interesting changes: - Intel's ICH LPC now supports Lynx Point - TI's twl4030-audio added a GPO child - tps6527 enabled its backlight subdevice - The twl6030 pwm driver moved to the new PWM subsystem And finally a bunch of cleanup and casual fixes for mc13xxx, 88pm860x, palmas, ab8500, wm8994, wm5110, max8907 and the tps65xxx family." Fix up various annoying conflicts: the DT and IRQ domain support came in twice and was already in 3.6. And then it was apparently rebased. Guys, DON'T REBASE! * tag 'mfd-3.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (89 commits) ARM: dts: Enable 88pm860x pmic mfd: 88pm860x: Move gpadc init into touch mfd: 88pm860x: Device tree support mfd: 88pm860x: Use irqdomain mfd: smsc: Add support for smsc gpio io/keypad driver backlight: tps65217_bl: Add missing platform_set_drvdata in tps65217_bl_probe mfd: DA9055 core driver mfd: tps65910: Add alarm interrupt of TPS65910 RTC to mfd device list mfd: wm5110: Add register patches for revision B mfd: wm5110: Disable control interface error report for WM5110 rev B mfd: max8907: Remove regulator-compatible from DT docs backlight: Add TPS65217 WLED driver mfd: Add backlight as subdevice to the tps65217 mfd: Provide the PRCMU with its own IRQ domain mfd: Fix max8907 sparse warning mfd: Add lp8788 mfd driver mfd: dbx500: Provide a more accurate smp_twd clock mfd: rc5t583: Fix warning messages regulator: palmas: Add DT support mfd: palmas: Change regulator defns to better suite DT ...
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/backlight/88pm860x_bl.c146
-rw-r--r--drivers/video/backlight/Kconfig7
-rw-r--r--drivers/video/backlight/Makefile1
-rw-r--r--drivers/video/backlight/max8925_bl.c79
-rw-r--r--drivers/video/backlight/tps65217_bl.c342
5 files changed, 470 insertions, 105 deletions
diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c
index f49181c7311..b7ec34c57f4 100644
--- a/drivers/video/backlight/88pm860x_bl.c
+++ b/drivers/video/backlight/88pm860x_bl.c
@@ -11,6 +11,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/fb.h>
@@ -31,57 +32,26 @@ struct pm860x_backlight_data {
int port;
int pwm;
int iset;
+ int reg_duty_cycle;
+ int reg_always_on;
+ int reg_current;
};
-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 backlight_power_set(struct pm860x_chip *chip, int port,
int on)
{
int ret = -EINVAL;
switch (port) {
- case PM8606_BACKLIGHT1:
+ case 0:
ret = on ? pm8606_osc_enable(chip, WLED1_DUTY) :
pm8606_osc_disable(chip, WLED1_DUTY);
break;
- case PM8606_BACKLIGHT2:
+ case 1:
ret = on ? pm8606_osc_enable(chip, WLED2_DUTY) :
pm8606_osc_disable(chip, WLED2_DUTY);
break;
- case PM8606_BACKLIGHT3:
+ case 2:
ret = on ? pm8606_osc_enable(chip, WLED3_DUTY) :
pm8606_osc_disable(chip, WLED3_DUTY);
break;
@@ -104,13 +74,13 @@ static int pm860x_backlight_set(struct backlight_device *bl, int brightness)
if (brightness)
backlight_power_set(chip, data->port, 1);
- ret = pm860x_reg_write(data->i2c, wled_a(data->port), value);
+ ret = pm860x_reg_write(data->i2c, data->reg_duty_cycle, 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),
+ ret = pm860x_set_bits(data->i2c, data->reg_current,
CURRENT_BITMASK, data->iset);
if (ret < 0)
goto out;
@@ -123,17 +93,17 @@ static int pm860x_backlight_set(struct backlight_device *bl, int brightness)
}
if (brightness == MAX_BRIGHTNESS) {
/* set WLED_ON bit as 100% */
- ret = pm860x_set_bits(data->i2c, wled_b(data->port),
+ ret = pm860x_set_bits(data->i2c, data->reg_always_on,
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),
+ ret = pm860x_set_bits(data->i2c, data->reg_always_on,
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),
+ ret = pm860x_set_bits(data->i2c, data->reg_always_on,
PM8606_WLED_ON, 0);
}
}
@@ -174,7 +144,7 @@ static int pm860x_backlight_get_brightness(struct backlight_device *bl)
struct pm860x_chip *chip = data->chip;
int ret;
- ret = pm860x_reg_read(data->i2c, wled_a(data->port));
+ ret = pm860x_reg_read(data->i2c, data->reg_duty_cycle);
if (ret < 0)
goto out;
data->current_brightness = ret;
@@ -190,45 +160,85 @@ static const struct backlight_ops pm860x_backlight_ops = {
.get_brightness = pm860x_backlight_get_brightness,
};
+#ifdef CONFIG_OF
+static int pm860x_backlight_dt_init(struct platform_device *pdev,
+ struct pm860x_backlight_data *data,
+ char *name)
+{
+ struct device_node *nproot = pdev->dev.parent->of_node, *np;
+ int iset = 0;
+ if (!nproot)
+ return -ENODEV;
+ nproot = of_find_node_by_name(nproot, "backlights");
+ if (!nproot) {
+ dev_err(&pdev->dev, "failed to find backlights node\n");
+ return -ENODEV;
+ }
+ for_each_child_of_node(nproot, np) {
+ if (!of_node_cmp(np->name, name)) {
+ of_property_read_u32(np, "marvell,88pm860x-iset",
+ &iset);
+ data->iset = PM8606_WLED_CURRENT(iset);
+ of_property_read_u32(np, "marvell,88pm860x-pwm",
+ &data->pwm);
+ break;
+ }
+ }
+ return 0;
+}
+#else
+#define pm860x_backlight_dt_init(x, y, z) (-1)
+#endif
+
static int pm860x_backlight_probe(struct platform_device *pdev)
{
struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
- struct pm860x_backlight_pdata *pdata = NULL;
+ struct pm860x_backlight_pdata *pdata = pdev->dev.platform_data;
struct pm860x_backlight_data *data;
struct backlight_device *bl;
struct resource *res;
struct backlight_properties props;
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;
- }
-
- pdata = pdev->dev.platform_data;
- if (pdata == NULL) {
- dev_err(&pdev->dev, "platform data isn't assigned to "
- "backlight\n");
- return -EINVAL;
- }
+ int ret = 0;
data = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_backlight_data),
GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
- strncpy(name, res->name, MFD_NAME_SIZE);
+ res = platform_get_resource_byname(pdev, IORESOURCE_REG, "duty cycle");
+ if (!res) {
+ dev_err(&pdev->dev, "No REG resource for duty cycle\n");
+ ret = -ENXIO;
+ goto out;
+ }
+ data->reg_duty_cycle = res->start;
+ res = platform_get_resource_byname(pdev, IORESOURCE_REG, "always on");
+ if (!res) {
+ dev_err(&pdev->dev, "No REG resorce for always on\n");
+ ret = -ENXIO;
+ goto out;
+ }
+ data->reg_always_on = res->start;
+ res = platform_get_resource_byname(pdev, IORESOURCE_REG, "current");
+ if (!res) {
+ dev_err(&pdev->dev, "No REG resource for current\n");
+ ret = -ENXIO;
+ goto out;
+ }
+ data->reg_current = res->start;
+
+ memset(name, 0, MFD_NAME_SIZE);
+ sprintf(name, "backlight-%d", pdev->id);
+ data->port = pdev->id;
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 = pdata->flags;
- if (data->port < 0) {
- dev_err(&pdev->dev, "wrong platform data is assigned");
- return -EINVAL;
+ if (pm860x_backlight_dt_init(pdev, data, name)) {
+ if (pdata) {
+ data->pwm = pdata->pwm;
+ data->iset = pdata->iset;
+ }
}
memset(&props, 0, sizeof(struct backlight_properties));
@@ -247,12 +257,14 @@ static int pm860x_backlight_probe(struct platform_device *pdev)
/* read current backlight */
ret = pm860x_backlight_get_brightness(bl);
if (ret < 0)
- goto out;
+ goto out_brt;
backlight_update_status(bl);
return 0;
-out:
+out_brt:
backlight_device_unregister(bl);
+out:
+ devm_kfree(&pdev->dev, data);
return ret;
}
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index cf282763a8d..63cee2e9d62 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -373,6 +373,13 @@ config BACKLIGHT_PANDORA
If you have a Pandora console, say Y to enable the
backlight driver.
+config BACKLIGHT_TPS65217
+ tristate "TPS65217 Backlight"
+ depends on BACKLIGHT_CLASS_DEVICE && MFD_TPS65217
+ help
+ If you have a Texas Instruments TPS65217 say Y to enable the
+ backlight driver.
+
endif # BACKLIGHT_CLASS_DEVICE
endif # BACKLIGHT_LCD_SUPPORT
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index a2ac9cfbaf6..00223a62ec1 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -43,3 +43,4 @@ obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o
obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o
obj-$(CONFIG_BACKLIGHT_AAT2870) += aat2870_bl.o
obj-$(CONFIG_BACKLIGHT_OT200) += ot200_bl.o
+obj-$(CONFIG_BACKLIGHT_TPS65217) += tps65217_bl.o
diff --git a/drivers/video/backlight/max8925_bl.c b/drivers/video/backlight/max8925_bl.c
index e833ac72e06..f72ba54f364 100644
--- a/drivers/video/backlight/max8925_bl.c
+++ b/drivers/video/backlight/max8925_bl.c
@@ -27,7 +27,9 @@
struct max8925_backlight_data {
struct max8925_chip *chip;
- int current_brightness;
+ int current_brightness;
+ int reg_mode_cntl;
+ int reg_cntl;
};
static int max8925_backlight_set(struct backlight_device *bl, int brightness)
@@ -42,16 +44,16 @@ static int max8925_backlight_set(struct backlight_device *bl, int brightness)
else
value = brightness;
- ret = max8925_reg_write(chip->i2c, MAX8925_WLED_CNTL, value);
+ ret = max8925_reg_write(chip->i2c, data->reg_cntl, value);
if (ret < 0)
goto out;
if (!data->current_brightness && brightness)
/* enable WLED output */
- ret = max8925_set_bits(chip->i2c, MAX8925_WLED_MODE_CNTL, 1, 1);
+ ret = max8925_set_bits(chip->i2c, data->reg_mode_cntl, 1, 1);
else if (!brightness)
/* disable WLED output */
- ret = max8925_set_bits(chip->i2c, MAX8925_WLED_MODE_CNTL, 1, 0);
+ ret = max8925_set_bits(chip->i2c, data->reg_mode_cntl, 1, 0);
if (ret < 0)
goto out;
dev_dbg(chip->dev, "set brightness %d\n", value);
@@ -85,7 +87,7 @@ static int max8925_backlight_get_brightness(struct backlight_device *bl)
struct max8925_chip *chip = data->chip;
int ret;
- ret = max8925_reg_read(chip->i2c, MAX8925_WLED_CNTL);
+ ret = max8925_reg_read(chip->i2c, data->reg_cntl);
if (ret < 0)
return -EINVAL;
data->current_brightness = ret;
@@ -102,69 +104,70 @@ static const struct backlight_ops max8925_backlight_ops = {
static int __devinit max8925_backlight_probe(struct platform_device *pdev)
{
struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
- struct max8925_platform_data *max8925_pdata;
- struct max8925_backlight_pdata *pdata = NULL;
+ struct max8925_backlight_pdata *pdata = pdev->dev.platform_data;
struct max8925_backlight_data *data;
struct backlight_device *bl;
struct backlight_properties props;
struct resource *res;
- char name[MAX8925_NAME_SIZE];
unsigned char value;
- 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) {
- max8925_pdata = pdev->dev.parent->platform_data;
- pdata = max8925_pdata->backlight;
- }
-
- if (!pdata) {
- dev_err(&pdev->dev, "platform data isn't assigned to "
- "backlight\n");
- return -EINVAL;
- }
+ int ret = 0;
data = devm_kzalloc(&pdev->dev, sizeof(struct max8925_backlight_data),
GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
- strncpy(name, res->name, MAX8925_NAME_SIZE);
+
+ res = platform_get_resource(pdev, IORESOURCE_REG, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "No REG resource for mode control!\n");
+ ret = -ENXIO;
+ goto out;
+ }
+ data->reg_mode_cntl = res->start;
+ res = platform_get_resource(pdev, IORESOURCE_REG, 1);
+ if (!res) {
+ dev_err(&pdev->dev, "No REG resource for control!\n");
+ ret = -ENXIO;
+ goto out;
+ }
+ data->reg_cntl = res->start;
+
data->chip = chip;
data->current_brightness = 0;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
props.max_brightness = MAX_BRIGHTNESS;
- bl = backlight_device_register(name, &pdev->dev, data,
+ bl = backlight_device_register("max8925-backlight", &pdev->dev, data,
&max8925_backlight_ops, &props);
if (IS_ERR(bl)) {
dev_err(&pdev->dev, "failed to register backlight\n");
- return PTR_ERR(bl);
+ ret = PTR_ERR(bl);
+ goto out;
}
bl->props.brightness = MAX_BRIGHTNESS;
platform_set_drvdata(pdev, bl);
value = 0;
- if (pdata->lxw_scl)
- value |= (1 << 7);
- if (pdata->lxw_freq)
- value |= (LWX_FREQ(pdata->lxw_freq) << 4);
- if (pdata->dual_string)
- value |= (1 << 1);
- ret = max8925_set_bits(chip->i2c, MAX8925_WLED_MODE_CNTL, 0xfe, value);
+ if (pdata) {
+ if (pdata->lxw_scl)
+ value |= (1 << 7);
+ if (pdata->lxw_freq)
+ value |= (LWX_FREQ(pdata->lxw_freq) << 4);
+ if (pdata->dual_string)
+ value |= (1 << 1);
+ }
+ ret = max8925_set_bits(chip->i2c, data->reg_mode_cntl, 0xfe, value);
if (ret < 0)
- goto out;
+ goto out_brt;
backlight_update_status(bl);
return 0;
-out:
+out_brt:
backlight_device_unregister(bl);
+out:
+ devm_kfree(&pdev->dev, data);
return ret;
}
diff --git a/drivers/video/backlight/tps65217_bl.c b/drivers/video/backlight/tps65217_bl.c
new file mode 100644
index 00000000000..70881633b45
--- /dev/null
+++ b/drivers/video/backlight/tps65217_bl.c
@@ -0,0 +1,342 @@
+/*
+ * tps65217_bl.c
+ *
+ * TPS65217 backlight driver
+ *
+ * Copyright (C) 2012 Matthias Kaehlcke
+ * Author: Matthias Kaehlcke <matthias@kaehlcke.net>
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/backlight.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/mfd/tps65217.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct tps65217_bl {
+ struct tps65217 *tps;
+ struct device *dev;
+ struct backlight_device *bl;
+ bool is_enabled;
+};
+
+static int tps65217_bl_enable(struct tps65217_bl *tps65217_bl)
+{
+ int rc;
+
+ rc = tps65217_set_bits(tps65217_bl->tps, TPS65217_REG_WLEDCTRL1,
+ TPS65217_WLEDCTRL1_ISINK_ENABLE,
+ TPS65217_WLEDCTRL1_ISINK_ENABLE, TPS65217_PROTECT_NONE);
+ if (rc) {
+ dev_err(tps65217_bl->dev,
+ "failed to enable backlight: %d\n", rc);
+ return rc;
+ }
+
+ tps65217_bl->is_enabled = true;
+
+ dev_dbg(tps65217_bl->dev, "backlight enabled\n");
+
+ return 0;
+}
+
+static int tps65217_bl_disable(struct tps65217_bl *tps65217_bl)
+{
+ int rc;
+
+ rc = tps65217_clear_bits(tps65217_bl->tps,
+ TPS65217_REG_WLEDCTRL1,
+ TPS65217_WLEDCTRL1_ISINK_ENABLE,
+ TPS65217_PROTECT_NONE);
+ if (rc) {
+ dev_err(tps65217_bl->dev,
+ "failed to disable backlight: %d\n", rc);
+ return rc;
+ }
+
+ tps65217_bl->is_enabled = false;
+
+ dev_dbg(tps65217_bl->dev, "backlight disabled\n");
+
+ return 0;
+}
+
+static int tps65217_bl_update_status(struct backlight_device *bl)
+{
+ struct tps65217_bl *tps65217_bl = bl_get_data(bl);
+ int rc;
+ int brightness = bl->props.brightness;
+
+ if (bl->props.state & BL_CORE_SUSPENDED)
+ brightness = 0;
+
+ if ((bl->props.power != FB_BLANK_UNBLANK) ||
+ (bl->props.fb_blank != FB_BLANK_UNBLANK))
+ /* framebuffer in low power mode or blanking active */
+ brightness = 0;
+
+ if (brightness > 0) {
+ rc = tps65217_reg_write(tps65217_bl->tps,
+ TPS65217_REG_WLEDCTRL2,
+ brightness - 1,
+ TPS65217_PROTECT_NONE);
+ if (rc) {
+ dev_err(tps65217_bl->dev,
+ "failed to set brightness level: %d\n", rc);
+ return rc;
+ }
+
+ dev_dbg(tps65217_bl->dev, "brightness set to %d\n", brightness);
+
+ if (!tps65217_bl->is_enabled)
+ rc = tps65217_bl_enable(tps65217_bl);
+ } else {
+ rc = tps65217_bl_disable(tps65217_bl);
+ }
+
+ return rc;
+}
+
+static int tps65217_bl_get_brightness(struct backlight_device *bl)
+{
+ return bl->props.brightness;
+}
+
+static const struct backlight_ops tps65217_bl_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = tps65217_bl_update_status,
+ .get_brightness = tps65217_bl_get_brightness
+};
+
+static int tps65217_bl_hw_init(struct tps65217_bl *tps65217_bl,
+ struct tps65217_bl_pdata *pdata)
+{
+ int rc;
+
+ rc = tps65217_bl_disable(tps65217_bl);
+ if (rc)
+ return rc;
+
+ switch (pdata->isel) {
+ case TPS65217_BL_ISET1:
+ /* select ISET_1 current level */
+ rc = tps65217_clear_bits(tps65217_bl->tps,
+ TPS65217_REG_WLEDCTRL1,
+ TPS65217_WLEDCTRL1_ISEL,
+ TPS65217_PROTECT_NONE);
+ if (rc) {
+ dev_err(tps65217_bl->dev,
+ "failed to select ISET1 current level: %d)\n",
+ rc);
+ return rc;
+ }
+
+ dev_dbg(tps65217_bl->dev, "selected ISET1 current level\n");
+
+ break;
+
+ case TPS65217_BL_ISET2:
+ /* select ISET2 current level */
+ rc = tps65217_set_bits(tps65217_bl->tps, TPS65217_REG_WLEDCTRL1,
+ TPS65217_WLEDCTRL1_ISEL,
+ TPS65217_WLEDCTRL1_ISEL, TPS65217_PROTECT_NONE);
+ if (rc) {
+ dev_err(tps65217_bl->dev,
+ "failed to select ISET2 current level: %d\n",
+ rc);
+ return rc;
+ }
+
+ dev_dbg(tps65217_bl->dev, "selected ISET2 current level\n");
+
+ break;
+
+ default:
+ dev_err(tps65217_bl->dev,
+ "invalid value for current level: %d\n", pdata->isel);
+ return -EINVAL;
+ }
+
+ /* set PWM frequency */
+ rc = tps65217_set_bits(tps65217_bl->tps,
+ TPS65217_REG_WLEDCTRL1,
+ TPS65217_WLEDCTRL1_FDIM_MASK,
+ pdata->fdim,
+ TPS65217_PROTECT_NONE);
+ if (rc) {
+ dev_err(tps65217_bl->dev,
+ "failed to select PWM dimming frequency: %d\n",
+ rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static struct tps65217_bl_pdata *
+tps65217_bl_parse_dt(struct platform_device *pdev)
+{
+ struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
+ struct device_node *node = of_node_get(tps->dev->of_node);
+ struct tps65217_bl_pdata *pdata, *err;
+ u32 val;
+
+ node = of_find_node_by_name(node, "backlight");
+ if (!node)
+ return ERR_PTR(-ENODEV);
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&pdev->dev, "failed to allocate platform data\n");
+ err = ERR_PTR(-ENOMEM);
+ goto err;
+ }
+
+ pdata->isel = TPS65217_BL_ISET1;
+ if (!of_property_read_u32(node, "isel", &val)) {
+ if (val < TPS65217_BL_ISET1 ||
+ val > TPS65217_BL_ISET2) {
+ dev_err(&pdev->dev,
+ "invalid 'isel' value in the device tree\n");
+ err = ERR_PTR(-EINVAL);
+ goto err;
+ }
+
+ pdata->isel = val;
+ }
+
+ pdata->fdim = TPS65217_BL_FDIM_200HZ;
+ if (!of_property_read_u32(node, "fdim", &val)) {
+ switch (val) {
+ case 100:
+ pdata->fdim = TPS65217_BL_FDIM_100HZ;
+ break;
+
+ case 200:
+ pdata->fdim = TPS65217_BL_FDIM_200HZ;
+ break;
+
+ case 500:
+ pdata->fdim = TPS65217_BL_FDIM_500HZ;
+ break;
+
+ case 1000:
+ pdata->fdim = TPS65217_BL_FDIM_1000HZ;
+ break;
+
+ default:
+ dev_err(&pdev->dev,
+ "invalid 'fdim' value in the device tree\n");
+ err = ERR_PTR(-EINVAL);
+ goto err;
+ }
+ }
+
+ of_node_put(node);
+
+ return pdata;
+
+err:
+ of_node_put(node);
+
+ return err;
+}
+#else
+static struct tps65217_bl_pdata *
+tps65217_bl_parse_dt(struct platform_device *pdev)
+{
+ return NULL;
+}
+#endif
+
+static int tps65217_bl_probe(struct platform_device *pdev)
+{
+ int rc;
+ struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
+ struct tps65217_bl *tps65217_bl;
+ struct tps65217_bl_pdata *pdata;
+ struct backlight_properties bl_props;
+
+ if (tps->dev->of_node) {
+ pdata = tps65217_bl_parse_dt(pdev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ } else {
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "no platform data provided\n");
+ return -EINVAL;
+ }
+
+ pdata = pdev->dev.platform_data;
+ }
+
+ tps65217_bl = devm_kzalloc(&pdev->dev, sizeof(*tps65217_bl),
+ GFP_KERNEL);
+ if (tps65217_bl == NULL) {
+ dev_err(&pdev->dev, "allocation of struct tps65217_bl failed\n");
+ return -ENOMEM;
+ }
+
+ tps65217_bl->tps = tps;
+ tps65217_bl->dev = &pdev->dev;
+ tps65217_bl->is_enabled = false;
+
+ rc = tps65217_bl_hw_init(tps65217_bl, pdata);
+ if (rc)
+ return rc;
+
+ memset(&bl_props, 0, sizeof(struct backlight_properties));
+ bl_props.type = BACKLIGHT_RAW;
+ bl_props.max_brightness = 100;
+
+ tps65217_bl->bl = backlight_device_register(pdev->name,
+ tps65217_bl->dev, tps65217_bl,
+ &tps65217_bl_ops, &bl_props);
+ if (IS_ERR(tps65217_bl->bl)) {
+ dev_err(tps65217_bl->dev,
+ "registration of backlight device failed: %d\n", rc);
+ return PTR_ERR(tps65217_bl->bl);
+ }
+
+ tps65217_bl->bl->props.brightness = 0;
+ platform_set_drvdata(pdev, tps65217_bl);
+
+ return 0;
+}
+
+static int tps65217_bl_remove(struct platform_device *pdev)
+{
+ struct tps65217_bl *tps65217_bl = platform_get_drvdata(pdev);
+
+ backlight_device_unregister(tps65217_bl->bl);
+
+ return 0;
+}
+
+static struct platform_driver tps65217_bl_driver = {
+ .probe = tps65217_bl_probe,
+ .remove = tps65217_bl_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tps65217-bl",
+ },
+};
+
+module_platform_driver(tps65217_bl_driver);
+
+MODULE_DESCRIPTION("TPS65217 Backlight driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Matthias Kaehlcke <matthias@kaehlcke.net>");