Hello,
I use a LCD display that has a defined powerup sequence (switch on VCC and backlight vcc - wait 5ms send LVDS data - wait 300 ms - switch on backlight)
Right now I use kernel 3.0.43 and added a function to set the gpio pins inside the drivers/video/mxc/ldb.c which handles my extended fsl_mxc_ldb_platform_data.
We port our system to kernel 3.10.53 and use the device tree for board configuration.
Is there an option in device tree files to handle powerup sequences for display ?
Kind regards
Wolfgang
Solved! Go to Solution.
I fixed my issue by extending the ldb driver located in driver/video/mxc/ldb.c ,with parameters display-powerup@0 and display-powerdown@0.
Here the part of the devicetree used to configure the display
&ldb {
status = "okay";
lvds-channel@0 {
fsl,data-mapping = "spwg";
fsl,data-width = <18>;
crtc = "ipu1-di0";
primary;
status = "okay";
//display powerup sequence
display-powerup@0{
pre_gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>,
<&gpio7 12 GPIO_ACTIVE_LOW>;
pre_values = <1 1>;
pre_delays-us = <0 40000>;
init_delays-us = <200000>;
post_gpios = <&gpio2 15 GPIO_ACTIVE_LOW>;
post_values = <1>;
post_delays-us = <0>;
};
display-powerdown@0{
pre_gpios = <&gpio2 15 GPIO_ACTIVE_LOW>;
pre_values = <0>;
pre_delays-us = <0>;
init_delays-us = <0>;
post_gpios = <&gpio7 12 GPIO_ACTIVE_LOW>,
<&gpio1 14 GPIO_ACTIVE_HIGH>;
//post_values = <0 0>;
post_values = <0 0>;
post_delays-us = <5000 0>;
};
display-timings {
native-mode = <&timing0>;
timing0: 800x600_60Hz {
// 800x600 60Hz
clock-frequency = <40000000>; //pixclock [pico seconds] -> MHz = 1/(pixclock*10^-12)
hactive = <800>;
vactive = <600>;
hfront-porch = <88>; // (Blank Horizontal Active Display Term) / 2
hback-porch = <72>; // (Blank Horizontal Active Display Term) / 2
hsync-len = <64>; // (Total Horizontal Active Display Term) / DCLK [MHz]
vback-porch = <15>; // (Blank Vertical Active Display Term) / 2
vfront-porch = <4>; // (Blank Vertical Active Display Term) / 2
vsync-len = <6>; // (Total Vertical Active Display Term) / DCLK [MHz]
hsync-active = <2>; //ignored
vsync-active = <2>; //ignored
enable-count = <26>;
};
};
};
};
Kind regards
Wolfgang
Hi, Wolfgang Netbal
is it a same problem between us. look at this thread:where to add lvds power sequence in linux-4.1.15
Do you mean use the GPIO pin output high or low to control the powerup sequence?
May be you can read the following for reference:
Re: usbh1 only works when booted plugged in. (mainline linux 3.17)
Re: spi more than 4 chipselects on imx6
Hope this information can help you.
Thank you for your fast reply jimmy
I added the power setting to my ldb device but the gpio pins stay low.
How does the startup delay work ?
Starts the time when the parsing of the devicetree was started or when ldb will be parsed or when the first delay finished the second one will be started ?
My main problem is that my display doesn't show anything
I tried the hints from this thread LVDS and PWM settings on iMX6 but my display stays black.
I set the gpios by hardware to the values I need so the wrong gpio settings are not the reason for my black screen.
&ldb {
startup-delay-us = <2>;
gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>;
enable-active-high;
startup-delay-us = <5000>;
gpios = <&gpio7 12 GPIO_ACTIVE_LOW>;
startup-delay-us = <10000>;
lvds-channel@0 {
crtc = "ipu1-di0";
status = "okay";
display-timings {
native-mode = <&timing0>;
timing0: 1024x768_60Hz {
// 1024x768 60Hz
clock-frequency = <64900000>;
hactive = <1024>;
vactive = <768>;
hfront-porch = <108 160 220>; // (Blank Horizontal Active Display Term) / 2
hback-porch = <108 160 220>; // (Blank Horizontal Active Display Term) / 2
hsync-len = <21>; // (Total Horizontal Active Display Term) / DCLK [MHz]
vback-porch = <3 19 40>; // (Blank Vertical Active Display Term) / 2
vfront-porch = <3 19 40>; // (Blank Vertical Active Display Term) / 2
vsync-len = <12>; // (Total Vertical Active Display Term) / DCLK [MHz]
hsync-active = <2>; //ignored
vsync-active = <2>; //ignored
};
};
};
};
Thank you
Wolfgang
I think my problem is that the ipu is not activated,
because I have a working boardfile for kernel 3.0.43 and I read out the registers to see whats the different.
I am able to read register IPU_CONF at address 0x260_0000 where value 0x660 is set,
but when I try to read this on kernel 3.10.53 with my device tree the kernel stops working and I have to hard reset the device.
Here my whole device tree for display control
I added the powersupply part because this fix the issue in this thread LVDS and PWM settings on iMX6
&cpu0 {
pu-supply = <®_pu>; /* use pu_dummy if VDDSOC share with VDDPU */
};
&gpc {
pu-supply = <®_pu>; /* ldo-bypass:use pu_dummy if VDDSOC share with VDDPU */
};
&gpu {
pu-supply = <®_pu>; /* ldo-bypass:use pu_dummy if VDDSOC share with VDDPU */
};
&vpu {
pu-supply = <®_pu>; /* ldo-bypass:use pu_dummy if VDDSOC share with VDDPU */
};
/*
* Backlight
*/
&bl {
// startup-delay-us = <300000>;
// gpio = <&gpio2 15 GPIO_ACTIVE_HIGH>;
// enable-active-high;
// startup-delay-us = <700000>;
pwms = <&pwm1 0 5000000>;
status = "okay";
};
&pwm1 {
status = "okay";
};
/*
* LCD
*/
/*
&mxcfb1 {
compatible = "fsl,mxc_sdc_fb";
disp_dev = "ldb";
mode_str ="SIGMATEK-XGA";
status = "okay";
};
&ldb {
ipu_id = <0>;
disp_id = <0>;
ext_ref = <1>;
mode = "sin0";
sec_ipu_id = <1>;
sec_disp_id = <0>;
status = "okay";
};
If anyone has a hint this would be great.
Kind regards
Wolfgang
I fixed my issue by extending the ldb driver located in driver/video/mxc/ldb.c ,with parameters display-powerup@0 and display-powerdown@0.
Here the part of the devicetree used to configure the display
&ldb {
status = "okay";
lvds-channel@0 {
fsl,data-mapping = "spwg";
fsl,data-width = <18>;
crtc = "ipu1-di0";
primary;
status = "okay";
//display powerup sequence
display-powerup@0{
pre_gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>,
<&gpio7 12 GPIO_ACTIVE_LOW>;
pre_values = <1 1>;
pre_delays-us = <0 40000>;
init_delays-us = <200000>;
post_gpios = <&gpio2 15 GPIO_ACTIVE_LOW>;
post_values = <1>;
post_delays-us = <0>;
};
display-powerdown@0{
pre_gpios = <&gpio2 15 GPIO_ACTIVE_LOW>;
pre_values = <0>;
pre_delays-us = <0>;
init_delays-us = <0>;
post_gpios = <&gpio7 12 GPIO_ACTIVE_LOW>,
<&gpio1 14 GPIO_ACTIVE_HIGH>;
//post_values = <0 0>;
post_values = <0 0>;
post_delays-us = <5000 0>;
};
display-timings {
native-mode = <&timing0>;
timing0: 800x600_60Hz {
// 800x600 60Hz
clock-frequency = <40000000>; //pixclock [pico seconds] -> MHz = 1/(pixclock*10^-12)
hactive = <800>;
vactive = <600>;
hfront-porch = <88>; // (Blank Horizontal Active Display Term) / 2
hback-porch = <72>; // (Blank Horizontal Active Display Term) / 2
hsync-len = <64>; // (Total Horizontal Active Display Term) / DCLK [MHz]
vback-porch = <15>; // (Blank Vertical Active Display Term) / 2
vfront-porch = <4>; // (Blank Vertical Active Display Term) / 2
vsync-len = <6>; // (Total Vertical Active Display Term) / DCLK [MHz]
hsync-active = <2>; //ignored
vsync-active = <2>; //ignored
enable-count = <26>;
};
};
};
};
Kind regards
Wolfgang
Hi Wolfgang,
I also use a LCD display and have to comply with powerup sequence. I have some questions to your code.
1. Did you sent the fix to freescale? Is your code elsewhere available?
2. Why do you have 3 gpos? First gpo to enable VCC, LVDS data can be enabled by register write and a second gpo to enable backlight. Or do you enable LVDS data via a separate gpo?
3. Is gpo for backlight-pwm not the gpo to enalbe backlight?
And what I do not understand. Everyone has to comply with the display timings. Is there no official way to fulfill the powerup sequence?
Kind regards
Mario
Dear Mario,
1. I didn't send my patch to freescale because I thought it will only be a quick fix and there is a better/official way to handle this.
I had to extend the ldb driver in the linux kernel and disable some warnings in the kernel, because access GPIOs directly will cause a warning in kernel 3.10.53.
2. Our display uses 3 GPIOs (VCC, VCC_ENABLE and BACKLIGHT_ENABLE)
3. In our case the backlight-pwm has an extry pin on the display and you can switch on/off the backlight using the BACKLIGHT_ENABLE pin.
If you find a better way to handle this please let me know.
Kind regards
Wolfgang
I attached you my patch, for kernel 3.10.53.
--- a/drivers/video/mxc/ldb.c
+++ b/drivers/video/mxc/ldb.c
@@ -29,8 +29,12 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/types.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/of_gpio.h>
#include <video/of_videomode.h>
#include <video/videomode.h>
+#include <dt-bindings/gpio/gpio.h>
#include "mxc_dispdrv.h"
#define DRIVER_NAME "ldb"
@@ -57,6 +61,47 @@
#define INVALID_BUS_REG (~0UL)
+
+#define PWRUPSEQ_INIT 0
+#define PWRUPSEQ_PRE_FINISHED 1
+#define PWRUPSEQ_POST_FINISHED 2
+/**
+ * struct ldb_power_timing - config structure
+ * @gpios: Array containing the gpios needed to control
+ * the powersequence
+ * @nr_gpios: Number of gpios
+ * @startup_delay_times Array of delays between the configured gpios
+ * @nr_delays Number of delays, should be the same like nr_gpios if not the delay is 0
+ *
+ * This structure contains powersequence configuration
+ * information that must be passed by platform code to the
+ * ldb driver, if a display is used that needs a defined powersequence.
+ */
+struct ldb_gpio_timing{
+ struct gpio *gpios;
+ int nr_gpios;
+ unsigned *values;
+ int nr_values;
+ unsigned *delay_times;
+ int nr_delays;
+};
+
+/**
+ * struct ldb_power_timing - config structure
+ * @pre_timing: powersequence running befor lvds data will be initialised
+ * @post_timing: powersequence running after lvds data was initialised
+ * @lvds_delay_us: delay waiting befor LVDS data will be initialised
+ *
+ * This structure contains powersequence configuration
+ * information that must be passed by platform code to the
+ * ldb driver, if a display is used that needs a defined powersequence.
+ */
+struct ldb_power_timing{
+ struct ldb_gpio_timing pre_timing;
+ struct ldb_gpio_timing post_timing;
+ int lvds_delay_us;
+};
+
struct crtc_mux {
enum crtc crtc;
u32 val;
@@ -76,7 +121,12 @@ struct ldb_info {
bool ext_bgref_cap;
int ctrl_reg;
int bus_mux_num;
+ int PowerState;
const struct bus_mux *buses;
+ struct fb_info *fbi;
+ struct ldb_power_timing disp_powerup_timing;
+ struct ldb_power_timing disp_powerdown_timing;
+
};
struct ldb_data;
@@ -285,7 +335,7 @@ static const struct of_device_id ldb_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, ldb_dt_ids);
-static int ldb_init(struct mxc_dispdrv_handle *mddh,
+static int ldb_init_internal(struct mxc_dispdrv_handle *mddh,
struct mxc_dispdrv_setting *setting)
{
struct ldb_data *ldb = mxc_dispdrv_getdata(mddh);
@@ -324,6 +374,90 @@ static int ldb_init(struct mxc_dispdrv_handle *mddh,
return 0;
}
+static inline void ldb_delay(int delay_us) {
+ if (delay_us >= 1000) {
+ mdelay(delay_us / 1000);
+ udelay(delay_us % 1000);
+ } else {
+ udelay(delay_us);
+ }
+}
+
+static void ldb_init_gpio(const struct ldb_gpio_timing *gpio_timing)
+{
+ int i;
+ char label[15];
+ for(i = 0; i < gpio_timing->nr_gpios; ++i) {
+ if(gpio_is_output(gpio_timing->gpios[i].gpio) == 0)
+ {
+ memset(label, 0, sizeof(label));
+ sprintf(label, "%d", gpio_timing->gpios[i].gpio);
+ gpio_request(gpio_timing->gpios[i].gpio, label);
+
+ if(i < gpio_timing->nr_values) {
+ int value = gpio_timing->values[i];
+ //Invert value if pin is ACTIVE_LOW
+ if(gpio_timing->gpios[i].flags & GPIO_ACTIVE_LOW) {
+ value = ((~gpio_timing->values[i]) & 0x01);
+ }
+ //Initvalue is set the inverse of value
+ gpio_direction_output(gpio_timing->gpios[i].gpio, ~value);
+ } else {
+ gpio_direction_output(gpio_timing->gpios[i].gpio, 0);
+ }
+ }
+ }
+}
+
+static void ldb_gpio_timing(const struct ldb_gpio_timing *gpio_timing)
+{
+ int i;
+ for(i = 0; i < gpio_timing->nr_gpios; ++i) {
+ if(i < gpio_timing->nr_values) {
+ int value = gpio_timing->values[i];
+ //Invert value if pin is ACTIVE_LOW
+ if(gpio_timing->gpios[i].flags & GPIO_ACTIVE_LOW) {
+ value = ((~gpio_timing->values[i]) & 0x01);
+ }
+ gpio_set_value(gpio_timing->gpios[i].gpio, value);
+ } else {
+ gpio_set_value(gpio_timing->gpios[i].gpio, 0);
+ }
+
+ if(i < gpio_timing->nr_delays
+ && gpio_timing->delay_times[i] > 0) {
+ ldb_delay(gpio_timing->delay_times[i]);
+ }
+ }
+}
+
+static int ldb_init(struct mxc_dispdrv_handle *mddh,
+ struct mxc_dispdrv_setting *setting)
+{
+ struct ldb_data *ldb = mxc_dispdrv_getdata(mddh);
+ struct device *dev = ldb->dev;
+ const struct of_device_id *of_id = of_match_device(ldb_dt_ids, dev);
+ struct ldb_info *ldb_info = (struct ldb_info *)of_id->data;
+ int ret;
+
+ //Init GPIOs
+ ldb_init_gpio(&ldb_info->disp_powerup_timing.pre_timing);
+ ldb_init_gpio(&ldb_info->disp_powerup_timing.post_timing);
+ ldb_init_gpio(&ldb_info->disp_powerdown_timing.pre_timing);
+ ldb_init_gpio(&ldb_info->disp_powerdown_timing.post_timing);
+
+ //Set gpios needed befor LVDS init
+ ldb_gpio_timing(&ldb_info->disp_powerup_timing.pre_timing);
+
+ //init lvds data, the lvds lines will be activated by ldb_enable()
+ ret = ldb_init_internal(mddh, setting);
+
+ //Set PowerState to PWRUPSEQ_PRE_FINISHED because ldb_enable switches the post_powerup gpios
+ ldb_info->PowerState = PWRUPSEQ_PRE_FINISHED;
+
+ return ret;
+}
+
static int get_di_clk_id(struct ldb_chan chan, int *id)
{
struct ldb_data *ldb = chan.ldb;
@@ -484,6 +618,8 @@ static int ldb_enable(struct mxc_dispdrv_handle *mddh,
struct ldb_data *ldb = mxc_dispdrv_getdata(mddh);
struct ldb_chan chan;
struct device *dev = ldb->dev;
+ const struct of_device_id *of_id = of_match_device(ldb_dt_ids, dev);
+ struct ldb_info *ldb_info = (struct ldb_info *)of_id->data;
struct bus_mux bus_mux;
int ret = 0, id = 0, chno, other_chno;
@@ -523,6 +659,19 @@ static int ldb_enable(struct mxc_dispdrv_handle *mddh,
}
regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ctrl);
+
+ if(ldb_info->PowerState == PWRUPSEQ_PRE_FINISHED) {
+ ldb_info->fbi = fbi;
+
+ //Wait after LVDS init
+ ldb_delay(ldb_info->disp_powerup_timing.lvds_delay_us);
+
+ //Set gpios needed after LVDS init
+ ldb_gpio_timing(&ldb_info->disp_powerup_timing.post_timing);
+
+ ldb_info->PowerState = PWRUPSEQ_POST_FINISHED;
+ }
+
return 0;
}
@@ -549,9 +698,34 @@ static void ldb_disable(struct mxc_dispdrv_handle *mddh,
return;
}
+static void ldb_deinit(struct mxc_dispdrv_handle *mddh)
+{
+ struct ldb_data *ldb = mxc_dispdrv_getdata(mddh);
+ struct device *dev = ldb->dev;
+ const struct of_device_id *of_id = of_match_device(ldb_dt_ids, dev);
+ struct ldb_info *ldb_info = (struct ldb_info *)of_id->data;
+ int ret;
+ ret = 0;
+
+ //Set gpios needed befor disable LVDS data
+ ldb_gpio_timing(&ldb_info->disp_powerdown_timing.pre_timing);
+
+ //deactivate lvds data
+ if(mddh != NULL && ldb_info->fbi != NULL) {
+ ldb_disable(mddh, ldb_info->fbi);
+ }
+
+ //Set gpios needed after LVDS disabled
+ ldb_gpio_timing(&ldb_info->disp_powerdown_timing.post_timing);
+
+ //Set powerup sequence t
+ ldb_info->PowerState = PWRUPSEQ_INIT;
+}
+
static struct mxc_dispdrv_driver ldb_drv = {
.name = DRIVER_NAME,
.init = ldb_init,
+ .deinit = ldb_deinit,
.setup = ldb_setup,
.enable = ldb_enable,
.disable = ldb_disable
@@ -663,7 +837,7 @@ static bool is_valid_crtc(struct ldb_data *ldb, enum crtc crtc,
return false;
}
-static int ldb_probe(struct platform_device *pdev)
+static int ldb_probe_internal(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct of_device_id *of_id =
@@ -864,12 +1038,157 @@ static int ldb_probe(struct platform_device *pdev)
static int ldb_remove(struct platform_device *pdev)
{
struct ldb_data *ldb = dev_get_drvdata(&pdev->dev);
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *of_id = of_match_device(ldb_dt_ids, dev);
+ const struct ldb_info *ldb_info = (const struct ldb_info *)of_id->data;
mxc_dispdrv_puthandle(ldb->mddh);
mxc_dispdrv_unregister(ldb->mddh);
+
+ //Cleanup memory
+ kfree(ldb_info->disp_powerup_timing.pre_timing.gpios);
+ kfree(ldb_info->disp_powerup_timing.pre_timing.delay_times);
+ kfree(ldb_info->disp_powerup_timing.pre_timing.values);
+ kfree(ldb_info->disp_powerup_timing.post_timing.gpios);
+ kfree(ldb_info->disp_powerup_timing.post_timing.delay_times);
+ kfree(ldb_info->disp_powerup_timing.post_timing.values);
+ kfree(ldb_info->disp_powerdown_timing.pre_timing.gpios);
+ kfree(ldb_info->disp_powerdown_timing.pre_timing.delay_times);
+ kfree(ldb_info->disp_powerdown_timing.pre_timing.values);
+ kfree(ldb_info->disp_powerdown_timing.post_timing.gpios);
+ kfree(ldb_info->disp_powerdown_timing.post_timing.delay_times);
+ kfree(ldb_info->disp_powerdown_timing.post_timing.values);
return 0;
}
+static int ldb_parse_powertiming(struct platform_device *pdev, struct device_node *np, struct ldb_power_timing* ldb_power_timing)
+{
+ int gpio, length, i;
+ enum of_gpio_flags flags;
+ struct property *prop;
+
+ //Parse pre_timing
+ //Parse gpios
+ ldb_power_timing->pre_timing.nr_gpios = of_gpio_named_count(np, "pre_gpios");
+ ldb_power_timing->pre_timing.gpios = devm_kzalloc(&pdev->dev,
+ sizeof(struct gpio) * ldb_power_timing->pre_timing.nr_gpios,
+ GFP_KERNEL);
+ if (!ldb_power_timing->pre_timing.gpios)
+ return -ENOMEM;
+
+ for (i = 0; i < ldb_power_timing->pre_timing.nr_gpios; i++) {
+ gpio = of_get_named_gpio_flags(np, "pre_gpios", i, &flags);
+ if (gpio < 0)
+ break;
+ ldb_power_timing->pre_timing.gpios[i].gpio = gpio;
+ ldb_power_timing->pre_timing.gpios[i].flags = flags;
+ }
+
+ //Parse values
+ prop = of_find_property(np, "pre_values", &length);
+ if (prop) {
+ ldb_power_timing->pre_timing.nr_values = length / sizeof(u32);
+
+ ldb_power_timing->pre_timing.values = devm_kzalloc(&pdev->dev,
+ sizeof(u32) * ldb_power_timing->pre_timing.nr_values,
+ GFP_KERNEL);
+ if (!ldb_power_timing->pre_timing.values)
+ return -ENOMEM;
+
+ of_property_read_u32_array(np, "pre_values", ldb_power_timing->pre_timing.values, ldb_power_timing->pre_timing.nr_values);
+ }
+
+ //Parse delay times
+ prop = of_find_property(np, "pre_delays-us", &length);
+ if (prop) {
+ ldb_power_timing->pre_timing.nr_delays = length / sizeof(u32);
+
+ ldb_power_timing->pre_timing.delay_times = devm_kzalloc(&pdev->dev,
+ sizeof(u32) * ldb_power_timing->pre_timing.nr_delays,
+ GFP_KERNEL);
+ if (!ldb_power_timing->pre_timing.delay_times)
+ return -ENOMEM;
+
+ of_property_read_u32_array(np, "pre_delays-us", ldb_power_timing->pre_timing.delay_times, ldb_power_timing->pre_timing.nr_delays);
+ }
+
+ //Parse LVDS delay
+ of_property_read_u32(np, "init_delays-us", &ldb_power_timing->lvds_delay_us);
+
+ //Parse post_timing
+ //Parse gpios
+ ldb_power_timing->post_timing.nr_gpios = of_gpio_named_count(np, "post_gpios");
+ ldb_power_timing->post_timing.gpios = devm_kzalloc(&pdev->dev,
+ sizeof(struct gpio) * ldb_power_timing->post_timing.nr_gpios,
+ GFP_KERNEL);
+ if (!ldb_power_timing->post_timing.gpios)
+ return -ENOMEM;
+
+ for (i = 0; i < ldb_power_timing->post_timing.nr_gpios; i++) {
+ gpio = of_get_named_gpio_flags(np, "post_gpios", i, &flags);
+ if (gpio < 0)
+ break;
+ ldb_power_timing->post_timing.gpios[i].gpio = gpio;
+ ldb_power_timing->post_timing.gpios[i].flags = flags;
+ }
+
+ //Parse values
+ prop = of_find_property(np, "post_values", &length);
+ if (prop) {
+ ldb_power_timing->post_timing.nr_values = length / sizeof(u32);
+
+ ldb_power_timing->post_timing.values = devm_kzalloc(&pdev->dev,
+ sizeof(u32) * ldb_power_timing->post_timing.nr_values,
+ GFP_KERNEL);
+ if (!ldb_power_timing->post_timing.values)
+ return -ENOMEM;
+
+ of_property_read_u32_array(np, "post_values", ldb_power_timing->post_timing.values, ldb_power_timing->post_timing.nr_values);
+ }
+ //Parse delay times
+ prop = of_find_property(np, "post_delays-us", &length);
+ if (prop) {
+ ldb_power_timing->post_timing.nr_delays = length / sizeof(u32);
+
+ ldb_power_timing->post_timing.delay_times = devm_kzalloc(&pdev->dev,
+ sizeof(u32) * ldb_power_timing->post_timing.nr_delays,
+ GFP_KERNEL);
+ if (!ldb_power_timing->post_timing.delay_times)
+ return -ENOMEM;
+
+ of_property_read_u32_array(np, "post_delays-us", ldb_power_timing->post_timing.delay_times, ldb_power_timing->post_timing.nr_delays);
+ }
+
+ return 0;
+}
+
+static int ldb_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *of_id = of_match_device(ldb_dt_ids, dev);
+ struct ldb_info *ldb_info = (struct ldb_info *)of_id->data;
+ struct device_node *np = dev->of_node, *child, *powerseq;
+ int ret;
+ ret = 0;
+
+ //Get display power sequence
+ for_each_child_of_node(np, child) {
+ powerseq = of_get_child_by_name(child, "display-powerup");
+ if(powerseq != NULL) {
+ ret = ldb_parse_powertiming(pdev, powerseq, &ldb_info->disp_powerup_timing);
+ }
+
+ powerseq = of_get_child_by_name(child, "display-powerdown");
+ if(powerseq != NULL) {
+ ret = ldb_parse_powertiming(pdev, powerseq, &ldb_info->disp_powerdown_timing);
+ }
+ }
+
+ ret = ldb_probe_internal(pdev);
+
+ return ret;
+}
+
static struct platform_driver ldb_driver = {
.driver = {
.name = DRIVER_NAME,
--- a/drivers/video/mxc/mxc_dispdrv.c
+++ b/drivers/video/mxc/mxc_dispdrv.c
@@ -50,6 +50,8 @@ struct mxc_dispdrv_entry {
struct list_head list;
};
+static struct mxc_dispdrv_entry *g_dispdrv_handle = NULL;
+
struct mxc_dispdrv_handle *mxc_dispdrv_register(struct mxc_dispdrv_driver *drv)
{
struct mxc_dispdrv_entry *new;
@@ -77,6 +79,9 @@ int mxc_dispdrv_unregister(struct mxc_dispdrv_handle *handle)
if (entry) {
mutex_lock(&dispdrv_lock);
+ if(entry->drv->deinit) {
+ entry->drv->deinit(handle);
+ }
list_del(&entry->list);
mutex_unlock(&dispdrv_lock);
kfree(entry);
@@ -86,6 +91,15 @@ int mxc_dispdrv_unregister(struct mxc_dispdrv_handle *handle)
}
EXPORT_SYMBOL_GPL(mxc_dispdrv_unregister);
+int mxc_dispdrv_powerfail(void)
+{
+ int ret = 0;
+ ret = mxc_dispdrv_unregister((struct mxc_dispdrv_handle *)g_dispdrv_handle);
+ g_dispdrv_handle = NULL;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mxc_dispdrv_powerfail);
+
struct mxc_dispdrv_handle *mxc_dispdrv_gethandle(char *name,
struct mxc_dispdrv_setting *setting)
{
@@ -99,6 +113,7 @@ struct mxc_dispdrv_handle *mxc_dispdrv_gethandle(char *name,
entry, setting);
if (ret >= 0) {
entry->active = true;
+ g_dispdrv_handle = entry;
found = 1;
break;
}
--- a/drivers/video/mxc/mxc_dispdrv.h
+++ b/drivers/video/mxc/mxc_dispdrv.h
@@ -44,6 +44,7 @@ struct mxc_dispdrv_driver {
struct mxc_dispdrv_handle *mxc_dispdrv_register(struct mxc_dispdrv_driver *drv);
int mxc_dispdrv_unregister(struct mxc_dispdrv_handle *handle);
+int mxc_dispdrv_powerfail(void);
struct mxc_dispdrv_handle *mxc_dispdrv_gethandle(char *name,
struct mxc_dispdrv_setting *setting);
void mxc_dispdrv_puthandle(struct mxc_dispdrv_handle *handle);
Hi wolnet
I am interested in this patch.
I am applying your patch.
I get the following error during compilation.
drivers/video/mxc/ldb.c: In function ‘ldb_init_gpio’:
drivers/video/mxc/ldb.c:390:3: error: implicit declaration of function ‘gpio_is_output’ [-Werror=implicit-function-declaration]
cc1: some warnings being treated as errors
scripts/Makefile.build:308: recipe for target 'drivers/video/mxc/ldb.o' failed
make[3]: *** [drivers/video/mxc/ldb.o] Error 1
I could not find gpio_is_output().
Can you share something about gpio_is_output()?
I'm sorry I do not have enough English. :)
Thank you.
Kind regards
Kwon