|
@@ -1,7 +1,7 @@
|
|
|
/*
|
|
|
* Generic DSI Command Mode panel driver
|
|
|
*
|
|
|
- * Copyright (C) 2013 Texas Instruments
|
|
|
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
|
|
|
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
|
|
|
*
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
@@ -22,9 +22,10 @@
|
|
|
#include <linux/slab.h>
|
|
|
#include <linux/workqueue.h>
|
|
|
#include <linux/of_device.h>
|
|
|
-#include <linux/of_gpio.h>
|
|
|
+#include <linux/regulator/consumer.h>
|
|
|
|
|
|
#include <video/mipi_display.h>
|
|
|
+#include <video/of_display_timing.h>
|
|
|
|
|
|
#include "../dss/omapdss.h"
|
|
|
|
|
@@ -49,6 +50,7 @@ struct panel_drv_data {
|
|
|
struct mutex lock;
|
|
|
|
|
|
struct backlight_device *bldev;
|
|
|
+ struct backlight_device *extbldev;
|
|
|
|
|
|
unsigned long hw_guard_end; /* next value of jiffies when we can
|
|
|
* issue the next sleep in/out command
|
|
@@ -56,11 +58,17 @@ struct panel_drv_data {
|
|
|
unsigned long hw_guard_wait; /* max guard time in jiffies */
|
|
|
|
|
|
/* panel HW configuration from DT or platform data */
|
|
|
- int reset_gpio;
|
|
|
- int ext_te_gpio;
|
|
|
+ struct gpio_desc *reset_gpio;
|
|
|
+ struct gpio_desc *ext_te_gpio;
|
|
|
+
|
|
|
+ struct regulator *vpnl;
|
|
|
+ struct regulator *vddi;
|
|
|
|
|
|
bool use_dsi_backlight;
|
|
|
|
|
|
+ int width_mm;
|
|
|
+ int height_mm;
|
|
|
+
|
|
|
struct omap_dsi_pin_config pin_config;
|
|
|
|
|
|
/* runtime variables */
|
|
@@ -92,6 +100,30 @@ static int dsicm_panel_reset(struct panel_drv_data *ddata);
|
|
|
|
|
|
static void dsicm_ulps_work(struct work_struct *work);
|
|
|
|
|
|
+static void dsicm_bl_power(struct panel_drv_data *ddata, bool enable)
|
|
|
+{
|
|
|
+ struct backlight_device *backlight;
|
|
|
+
|
|
|
+ if (ddata->bldev)
|
|
|
+ backlight = ddata->bldev;
|
|
|
+ else if (ddata->extbldev)
|
|
|
+ backlight = ddata->extbldev;
|
|
|
+ else
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (enable) {
|
|
|
+ backlight->props.fb_blank = FB_BLANK_UNBLANK;
|
|
|
+ backlight->props.state = ~(BL_CORE_FBBLANK | BL_CORE_SUSPENDED);
|
|
|
+ backlight->props.power = FB_BLANK_UNBLANK;
|
|
|
+ } else {
|
|
|
+ backlight->props.fb_blank = FB_BLANK_NORMAL;
|
|
|
+ backlight->props.power = FB_BLANK_POWERDOWN;
|
|
|
+ backlight->props.state |= BL_CORE_FBBLANK | BL_CORE_SUSPENDED;
|
|
|
+ }
|
|
|
+
|
|
|
+ backlight_update_status(backlight);
|
|
|
+}
|
|
|
+
|
|
|
static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec)
|
|
|
{
|
|
|
ddata->hw_guard_wait = msecs_to_jiffies(guard_msec);
|
|
@@ -255,8 +287,8 @@ static int dsicm_enter_ulps(struct panel_drv_data *ddata)
|
|
|
if (r)
|
|
|
goto err;
|
|
|
|
|
|
- if (gpio_is_valid(ddata->ext_te_gpio))
|
|
|
- disable_irq(gpio_to_irq(ddata->ext_te_gpio));
|
|
|
+ if (ddata->ext_te_gpio)
|
|
|
+ disable_irq(gpiod_to_irq(ddata->ext_te_gpio));
|
|
|
|
|
|
in->ops.dsi->disable(in, false, true);
|
|
|
|
|
@@ -297,8 +329,8 @@ static int dsicm_exit_ulps(struct panel_drv_data *ddata)
|
|
|
goto err2;
|
|
|
}
|
|
|
|
|
|
- if (gpio_is_valid(ddata->ext_te_gpio))
|
|
|
- enable_irq(gpio_to_irq(ddata->ext_te_gpio));
|
|
|
+ if (ddata->ext_te_gpio)
|
|
|
+ enable_irq(gpiod_to_irq(ddata->ext_te_gpio));
|
|
|
|
|
|
dsicm_queue_ulps_work(ddata);
|
|
|
|
|
@@ -311,8 +343,8 @@ err2:
|
|
|
|
|
|
r = dsicm_panel_reset(ddata);
|
|
|
if (!r) {
|
|
|
- if (gpio_is_valid(ddata->ext_te_gpio))
|
|
|
- enable_irq(gpio_to_irq(ddata->ext_te_gpio));
|
|
|
+ if (ddata->ext_te_gpio)
|
|
|
+ enable_irq(gpiod_to_irq(ddata->ext_te_gpio));
|
|
|
ddata->ulps_enabled = false;
|
|
|
}
|
|
|
err1:
|
|
@@ -335,7 +367,7 @@ static int dsicm_bl_update_status(struct backlight_device *dev)
|
|
|
{
|
|
|
struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
|
|
|
struct omap_dss_device *in = ddata->in;
|
|
|
- int r;
|
|
|
+ int r = 0;
|
|
|
int level;
|
|
|
|
|
|
if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
|
|
@@ -356,8 +388,6 @@ static int dsicm_bl_update_status(struct backlight_device *dev)
|
|
|
r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, level);
|
|
|
|
|
|
in->ops.dsi->bus_unlock(in);
|
|
|
- } else {
|
|
|
- r = 0;
|
|
|
}
|
|
|
|
|
|
mutex_unlock(&ddata->lock);
|
|
@@ -560,16 +590,13 @@ static const struct attribute_group dsicm_attr_group = {
|
|
|
|
|
|
static void dsicm_hw_reset(struct panel_drv_data *ddata)
|
|
|
{
|
|
|
- if (!gpio_is_valid(ddata->reset_gpio))
|
|
|
- return;
|
|
|
-
|
|
|
- gpio_set_value(ddata->reset_gpio, 1);
|
|
|
+ gpiod_set_value(ddata->reset_gpio, 1);
|
|
|
udelay(10);
|
|
|
/* reset the panel */
|
|
|
- gpio_set_value(ddata->reset_gpio, 0);
|
|
|
+ gpiod_set_value(ddata->reset_gpio, 0);
|
|
|
/* assert reset */
|
|
|
udelay(10);
|
|
|
- gpio_set_value(ddata->reset_gpio, 1);
|
|
|
+ gpiod_set_value(ddata->reset_gpio, 1);
|
|
|
/* wait after releasing reset */
|
|
|
usleep_range(5000, 10000);
|
|
|
}
|
|
@@ -589,25 +616,43 @@ static int dsicm_power_on(struct panel_drv_data *ddata)
|
|
|
.lp_clk_max = 10000000,
|
|
|
};
|
|
|
|
|
|
+ if (ddata->vpnl) {
|
|
|
+ r = regulator_enable(ddata->vpnl);
|
|
|
+ if (r) {
|
|
|
+ dev_err(&ddata->pdev->dev,
|
|
|
+ "failed to enable VPNL: %d\n", r);
|
|
|
+ return r;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ddata->vddi) {
|
|
|
+ r = regulator_enable(ddata->vddi);
|
|
|
+ if (r) {
|
|
|
+ dev_err(&ddata->pdev->dev,
|
|
|
+ "failed to enable VDDI: %d\n", r);
|
|
|
+ goto err_vpnl;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if (ddata->pin_config.num_pins > 0) {
|
|
|
r = in->ops.dsi->configure_pins(in, &ddata->pin_config);
|
|
|
if (r) {
|
|
|
dev_err(&ddata->pdev->dev,
|
|
|
"failed to configure DSI pins\n");
|
|
|
- goto err0;
|
|
|
+ goto err_vddi;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
r = in->ops.dsi->set_config(in, &dsi_config);
|
|
|
if (r) {
|
|
|
dev_err(&ddata->pdev->dev, "failed to configure DSI\n");
|
|
|
- goto err0;
|
|
|
+ goto err_vddi;
|
|
|
}
|
|
|
|
|
|
r = in->ops.dsi->enable(in);
|
|
|
if (r) {
|
|
|
dev_err(&ddata->pdev->dev, "failed to enable DSI\n");
|
|
|
- goto err0;
|
|
|
+ goto err_vddi;
|
|
|
}
|
|
|
|
|
|
dsicm_hw_reset(ddata);
|
|
@@ -665,7 +710,13 @@ err:
|
|
|
dsicm_hw_reset(ddata);
|
|
|
|
|
|
in->ops.dsi->disable(in, true, false);
|
|
|
-err0:
|
|
|
+err_vddi:
|
|
|
+ if (ddata->vddi)
|
|
|
+ regulator_disable(ddata->vddi);
|
|
|
+err_vpnl:
|
|
|
+ if (ddata->vpnl)
|
|
|
+ regulator_disable(ddata->vpnl);
|
|
|
+
|
|
|
return r;
|
|
|
}
|
|
|
|
|
@@ -688,6 +739,11 @@ static void dsicm_power_off(struct panel_drv_data *ddata)
|
|
|
|
|
|
in->ops.dsi->disable(in, true, false);
|
|
|
|
|
|
+ if (ddata->vddi)
|
|
|
+ regulator_disable(ddata->vddi);
|
|
|
+ if (ddata->vpnl)
|
|
|
+ regulator_disable(ddata->vpnl);
|
|
|
+
|
|
|
ddata->enabled = 0;
|
|
|
}
|
|
|
|
|
@@ -782,6 +838,8 @@ static int dsicm_enable(struct omap_dss_device *dssdev)
|
|
|
|
|
|
mutex_unlock(&ddata->lock);
|
|
|
|
|
|
+ dsicm_bl_power(ddata, true);
|
|
|
+
|
|
|
return 0;
|
|
|
err:
|
|
|
dev_dbg(&ddata->pdev->dev, "enable failed\n");
|
|
@@ -797,6 +855,8 @@ static void dsicm_disable(struct omap_dss_device *dssdev)
|
|
|
|
|
|
dev_dbg(&ddata->pdev->dev, "disable\n");
|
|
|
|
|
|
+ dsicm_bl_power(ddata, false);
|
|
|
+
|
|
|
mutex_lock(&ddata->lock);
|
|
|
|
|
|
dsicm_cancel_ulps_work(ddata);
|
|
@@ -890,7 +950,7 @@ static int dsicm_update(struct omap_dss_device *dssdev,
|
|
|
if (r)
|
|
|
goto err;
|
|
|
|
|
|
- if (ddata->te_enabled && gpio_is_valid(ddata->ext_te_gpio)) {
|
|
|
+ if (ddata->te_enabled && ddata->ext_te_gpio) {
|
|
|
schedule_delayed_work(&ddata->te_timeout_work,
|
|
|
msecs_to_jiffies(250));
|
|
|
atomic_set(&ddata->do_update, 1);
|
|
@@ -937,7 +997,7 @@ static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable)
|
|
|
else
|
|
|
r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_TEAR_OFF);
|
|
|
|
|
|
- if (!gpio_is_valid(ddata->ext_te_gpio))
|
|
|
+ if (!ddata->ext_te_gpio)
|
|
|
in->ops.dsi->enable_te(in, enable);
|
|
|
|
|
|
/* possible panel bug */
|
|
@@ -1099,6 +1159,45 @@ static void dsicm_ulps_work(struct work_struct *work)
|
|
|
mutex_unlock(&ddata->lock);
|
|
|
}
|
|
|
|
|
|
+static void dsicm_get_timings(struct omap_dss_device *dssdev,
|
|
|
+ struct videomode *vm)
|
|
|
+{
|
|
|
+ struct panel_drv_data *ddata = to_panel_data(dssdev);
|
|
|
+
|
|
|
+ *vm = ddata->vm;
|
|
|
+}
|
|
|
+
|
|
|
+static int dsicm_check_timings(struct omap_dss_device *dssdev,
|
|
|
+ struct videomode *vm)
|
|
|
+{
|
|
|
+ struct panel_drv_data *ddata = to_panel_data(dssdev);
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (vm->hactive != ddata->vm.hactive)
|
|
|
+ ret = -EINVAL;
|
|
|
+
|
|
|
+ if (vm->vactive != ddata->vm.vactive)
|
|
|
+ ret = -EINVAL;
|
|
|
+
|
|
|
+ if (ret) {
|
|
|
+ dev_warn(dssdev->dev, "wrong resolution: %d x %d",
|
|
|
+ vm->hactive, vm->vactive);
|
|
|
+ dev_warn(dssdev->dev, "panel resolution: %d x %d",
|
|
|
+ ddata->vm.hactive, ddata->vm.vactive);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void dsicm_get_size(struct omap_dss_device *dssdev,
|
|
|
+ unsigned int *width, unsigned int *height)
|
|
|
+{
|
|
|
+ struct panel_drv_data *ddata = to_panel_data(dssdev);
|
|
|
+
|
|
|
+ *width = ddata->width_mm;
|
|
|
+ *height = ddata->height_mm;
|
|
|
+}
|
|
|
+
|
|
|
static struct omap_dss_driver dsicm_ops = {
|
|
|
.connect = dsicm_connect,
|
|
|
.disconnect = dsicm_disconnect,
|
|
@@ -1109,6 +1208,10 @@ static struct omap_dss_driver dsicm_ops = {
|
|
|
.update = dsicm_update,
|
|
|
.sync = dsicm_sync,
|
|
|
|
|
|
+ .get_timings = dsicm_get_timings,
|
|
|
+ .check_timings = dsicm_check_timings,
|
|
|
+ .get_size = dsicm_get_size,
|
|
|
+
|
|
|
.enable_te = dsicm_enable_te,
|
|
|
.get_te = dsicm_get_te,
|
|
|
|
|
@@ -1118,41 +1221,87 @@ static struct omap_dss_driver dsicm_ops = {
|
|
|
static int dsicm_probe_of(struct platform_device *pdev)
|
|
|
{
|
|
|
struct device_node *node = pdev->dev.of_node;
|
|
|
+ struct device_node *backlight;
|
|
|
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
|
|
struct omap_dss_device *in;
|
|
|
- int gpio;
|
|
|
+ struct display_timing timing;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ ddata->reset_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW);
|
|
|
+ if (IS_ERR(ddata->reset_gpio)) {
|
|
|
+ err = PTR_ERR(ddata->reset_gpio);
|
|
|
+ dev_err(&pdev->dev, "reset gpio request failed: %d", err);
|
|
|
+ return err;
|
|
|
+ }
|
|
|
|
|
|
- gpio = of_get_named_gpio(node, "reset-gpios", 0);
|
|
|
- if (!gpio_is_valid(gpio)) {
|
|
|
- dev_err(&pdev->dev, "failed to parse reset gpio\n");
|
|
|
- return gpio;
|
|
|
+ ddata->ext_te_gpio = devm_gpiod_get_optional(&pdev->dev, "te",
|
|
|
+ GPIOD_IN);
|
|
|
+ if (IS_ERR(ddata->ext_te_gpio)) {
|
|
|
+ err = PTR_ERR(ddata->ext_te_gpio);
|
|
|
+ dev_err(&pdev->dev, "TE gpio request failed: %d", err);
|
|
|
+ return err;
|
|
|
}
|
|
|
- ddata->reset_gpio = gpio;
|
|
|
|
|
|
- gpio = of_get_named_gpio(node, "te-gpios", 0);
|
|
|
- if (gpio_is_valid(gpio) || gpio == -ENOENT) {
|
|
|
- ddata->ext_te_gpio = gpio;
|
|
|
+ err = of_get_display_timing(node, "panel-timing", &timing);
|
|
|
+ if (!err) {
|
|
|
+ videomode_from_timing(&timing, &ddata->vm);
|
|
|
+ if (!ddata->vm.pixelclock)
|
|
|
+ ddata->vm.pixelclock =
|
|
|
+ ddata->vm.hactive * ddata->vm.vactive * 60;
|
|
|
} else {
|
|
|
- dev_err(&pdev->dev, "failed to parse TE gpio\n");
|
|
|
- return gpio;
|
|
|
+ dev_warn(&pdev->dev,
|
|
|
+ "failed to get video timing, using defaults\n");
|
|
|
}
|
|
|
|
|
|
+ ddata->width_mm = 0;
|
|
|
+ of_property_read_u32(node, "width-mm", &ddata->width_mm);
|
|
|
+
|
|
|
+ ddata->height_mm = 0;
|
|
|
+ of_property_read_u32(node, "height-mm", &ddata->height_mm);
|
|
|
+
|
|
|
in = omapdss_of_find_source_for_first_ep(node);
|
|
|
if (IS_ERR(in)) {
|
|
|
dev_err(&pdev->dev, "failed to find video source\n");
|
|
|
return PTR_ERR(in);
|
|
|
}
|
|
|
|
|
|
+ ddata->vpnl = devm_regulator_get_optional(&pdev->dev, "vpnl");
|
|
|
+ if (IS_ERR(ddata->vpnl)) {
|
|
|
+ err = PTR_ERR(ddata->vpnl);
|
|
|
+ if (err == -EPROBE_DEFER)
|
|
|
+ return err;
|
|
|
+ ddata->vpnl = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ ddata->vddi = devm_regulator_get_optional(&pdev->dev, "vddi");
|
|
|
+ if (IS_ERR(ddata->vddi)) {
|
|
|
+ err = PTR_ERR(ddata->vddi);
|
|
|
+ if (err == -EPROBE_DEFER)
|
|
|
+ return err;
|
|
|
+ ddata->vddi = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
ddata->in = in;
|
|
|
|
|
|
- /* TODO: ulps, backlight */
|
|
|
+ backlight = of_parse_phandle(node, "backlight", 0);
|
|
|
+ if (backlight) {
|
|
|
+ ddata->extbldev = of_find_backlight_by_node(backlight);
|
|
|
+ of_node_put(backlight);
|
|
|
+
|
|
|
+ if (!ddata->extbldev)
|
|
|
+ return -EPROBE_DEFER;
|
|
|
+ } else {
|
|
|
+ /* assume native backlight support */
|
|
|
+ ddata->use_dsi_backlight = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* TODO: ulps */
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
static int dsicm_probe(struct platform_device *pdev)
|
|
|
{
|
|
|
- struct backlight_properties props;
|
|
|
struct panel_drv_data *ddata;
|
|
|
struct backlight_device *bldev = NULL;
|
|
|
struct device *dev = &pdev->dev;
|
|
@@ -1171,14 +1320,14 @@ static int dsicm_probe(struct platform_device *pdev)
|
|
|
if (!pdev->dev.of_node)
|
|
|
return -ENODEV;
|
|
|
|
|
|
- r = dsicm_probe_of(pdev);
|
|
|
- if (r)
|
|
|
- return r;
|
|
|
-
|
|
|
ddata->vm.hactive = 864;
|
|
|
ddata->vm.vactive = 480;
|
|
|
ddata->vm.pixelclock = 864 * 480 * 60;
|
|
|
|
|
|
+ r = dsicm_probe_of(pdev);
|
|
|
+ if (r)
|
|
|
+ return r;
|
|
|
+
|
|
|
dssdev = &ddata->dssdev;
|
|
|
dssdev->dev = dev;
|
|
|
dssdev->driver = &dsicm_ops;
|
|
@@ -1200,31 +1349,15 @@ static int dsicm_probe(struct platform_device *pdev)
|
|
|
|
|
|
atomic_set(&ddata->do_update, 0);
|
|
|
|
|
|
- if (gpio_is_valid(ddata->reset_gpio)) {
|
|
|
- r = devm_gpio_request_one(dev, ddata->reset_gpio,
|
|
|
- GPIOF_OUT_INIT_LOW, "taal rst");
|
|
|
- if (r) {
|
|
|
- dev_err(dev, "failed to request reset gpio\n");
|
|
|
- return r;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (gpio_is_valid(ddata->ext_te_gpio)) {
|
|
|
- r = devm_gpio_request_one(dev, ddata->ext_te_gpio,
|
|
|
- GPIOF_IN, "taal irq");
|
|
|
- if (r) {
|
|
|
- dev_err(dev, "GPIO request failed\n");
|
|
|
- return r;
|
|
|
- }
|
|
|
-
|
|
|
- r = devm_request_irq(dev, gpio_to_irq(ddata->ext_te_gpio),
|
|
|
+ if (ddata->ext_te_gpio) {
|
|
|
+ r = devm_request_irq(dev, gpiod_to_irq(ddata->ext_te_gpio),
|
|
|
dsicm_te_isr,
|
|
|
IRQF_TRIGGER_RISING,
|
|
|
"taal vsync", ddata);
|
|
|
|
|
|
if (r) {
|
|
|
dev_err(dev, "IRQ request failed\n");
|
|
|
- return r;
|
|
|
+ goto err_reg;
|
|
|
}
|
|
|
|
|
|
INIT_DEFERRABLE_WORK(&ddata->te_timeout_work,
|
|
@@ -1234,48 +1367,43 @@ static int dsicm_probe(struct platform_device *pdev)
|
|
|
}
|
|
|
|
|
|
ddata->workqueue = create_singlethread_workqueue("dsicm_wq");
|
|
|
- if (ddata->workqueue == NULL) {
|
|
|
- dev_err(dev, "can't create workqueue\n");
|
|
|
- return -ENOMEM;
|
|
|
+ if (!ddata->workqueue) {
|
|
|
+ r = -ENOMEM;
|
|
|
+ goto err_reg;
|
|
|
}
|
|
|
INIT_DELAYED_WORK(&ddata->ulps_work, dsicm_ulps_work);
|
|
|
|
|
|
dsicm_hw_reset(ddata);
|
|
|
|
|
|
if (ddata->use_dsi_backlight) {
|
|
|
- memset(&props, 0, sizeof(props));
|
|
|
+ struct backlight_properties props = { 0 };
|
|
|
props.max_brightness = 255;
|
|
|
-
|
|
|
props.type = BACKLIGHT_RAW;
|
|
|
- bldev = backlight_device_register(dev_name(dev),
|
|
|
- dev, ddata, &dsicm_bl_ops, &props);
|
|
|
+
|
|
|
+ bldev = devm_backlight_device_register(dev, dev_name(dev),
|
|
|
+ dev, ddata, &dsicm_bl_ops, &props);
|
|
|
if (IS_ERR(bldev)) {
|
|
|
r = PTR_ERR(bldev);
|
|
|
goto err_bl;
|
|
|
}
|
|
|
|
|
|
ddata->bldev = bldev;
|
|
|
-
|
|
|
- bldev->props.fb_blank = FB_BLANK_UNBLANK;
|
|
|
- bldev->props.power = FB_BLANK_UNBLANK;
|
|
|
- bldev->props.brightness = 255;
|
|
|
-
|
|
|
- dsicm_bl_update_status(bldev);
|
|
|
}
|
|
|
|
|
|
r = sysfs_create_group(&dev->kobj, &dsicm_attr_group);
|
|
|
if (r) {
|
|
|
dev_err(dev, "failed to create sysfs files\n");
|
|
|
- goto err_sysfs_create;
|
|
|
+ goto err_bl;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
-err_sysfs_create:
|
|
|
- backlight_device_unregister(bldev);
|
|
|
err_bl:
|
|
|
destroy_workqueue(ddata->workqueue);
|
|
|
err_reg:
|
|
|
+ if (ddata->extbldev)
|
|
|
+ put_device(&ddata->extbldev->dev);
|
|
|
+
|
|
|
return r;
|
|
|
}
|
|
|
|
|
@@ -1283,7 +1411,6 @@ static int __exit dsicm_remove(struct platform_device *pdev)
|
|
|
{
|
|
|
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
|
|
struct omap_dss_device *dssdev = &ddata->dssdev;
|
|
|
- struct backlight_device *bldev;
|
|
|
|
|
|
dev_dbg(&pdev->dev, "remove\n");
|
|
|
|
|
@@ -1294,12 +1421,8 @@ static int __exit dsicm_remove(struct platform_device *pdev)
|
|
|
|
|
|
sysfs_remove_group(&pdev->dev.kobj, &dsicm_attr_group);
|
|
|
|
|
|
- bldev = ddata->bldev;
|
|
|
- if (bldev != NULL) {
|
|
|
- bldev->props.power = FB_BLANK_POWERDOWN;
|
|
|
- dsicm_bl_update_status(bldev);
|
|
|
- backlight_device_unregister(bldev);
|
|
|
- }
|
|
|
+ if (ddata->extbldev)
|
|
|
+ put_device(&ddata->extbldev->dev);
|
|
|
|
|
|
omap_dss_put_device(ddata->in);
|
|
|
|