Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Simple PWM based backlight control, board code has to setup
0004  * 1) pin configuration so PWM waveforms can output
0005  * 2) platform_data being correctly configured
0006  */
0007 
0008 #include <linux/delay.h>
0009 #include <linux/gpio/consumer.h>
0010 #include <linux/module.h>
0011 #include <linux/kernel.h>
0012 #include <linux/init.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/fb.h>
0015 #include <linux/backlight.h>
0016 #include <linux/err.h>
0017 #include <linux/pwm.h>
0018 #include <linux/pwm_backlight.h>
0019 #include <linux/regulator/consumer.h>
0020 #include <linux/slab.h>
0021 
0022 struct pwm_bl_data {
0023     struct pwm_device   *pwm;
0024     struct device       *dev;
0025     unsigned int        lth_brightness;
0026     unsigned int        *levels;
0027     bool            enabled;
0028     struct regulator    *power_supply;
0029     struct gpio_desc    *enable_gpio;
0030     unsigned int        scale;
0031     bool            legacy;
0032     unsigned int        post_pwm_on_delay;
0033     unsigned int        pwm_off_delay;
0034     int         (*notify)(struct device *,
0035                       int brightness);
0036     void            (*notify_after)(struct device *,
0037                     int brightness);
0038     int         (*check_fb)(struct device *, struct fb_info *);
0039     void            (*exit)(struct device *);
0040 };
0041 
0042 static void pwm_backlight_power_on(struct pwm_bl_data *pb)
0043 {
0044     struct pwm_state state;
0045     int err;
0046 
0047     pwm_get_state(pb->pwm, &state);
0048     if (pb->enabled)
0049         return;
0050 
0051     err = regulator_enable(pb->power_supply);
0052     if (err < 0)
0053         dev_err(pb->dev, "failed to enable power supply\n");
0054 
0055     state.enabled = true;
0056     pwm_apply_state(pb->pwm, &state);
0057 
0058     if (pb->post_pwm_on_delay)
0059         msleep(pb->post_pwm_on_delay);
0060 
0061     if (pb->enable_gpio)
0062         gpiod_set_value_cansleep(pb->enable_gpio, 1);
0063 
0064     pb->enabled = true;
0065 }
0066 
0067 static void pwm_backlight_power_off(struct pwm_bl_data *pb)
0068 {
0069     struct pwm_state state;
0070 
0071     pwm_get_state(pb->pwm, &state);
0072     if (!pb->enabled)
0073         return;
0074 
0075     if (pb->enable_gpio)
0076         gpiod_set_value_cansleep(pb->enable_gpio, 0);
0077 
0078     if (pb->pwm_off_delay)
0079         msleep(pb->pwm_off_delay);
0080 
0081     state.enabled = false;
0082     state.duty_cycle = 0;
0083     pwm_apply_state(pb->pwm, &state);
0084 
0085     regulator_disable(pb->power_supply);
0086     pb->enabled = false;
0087 }
0088 
0089 static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness)
0090 {
0091     unsigned int lth = pb->lth_brightness;
0092     struct pwm_state state;
0093     u64 duty_cycle;
0094 
0095     pwm_get_state(pb->pwm, &state);
0096 
0097     if (pb->levels)
0098         duty_cycle = pb->levels[brightness];
0099     else
0100         duty_cycle = brightness;
0101 
0102     duty_cycle *= state.period - lth;
0103     do_div(duty_cycle, pb->scale);
0104 
0105     return duty_cycle + lth;
0106 }
0107 
0108 static int pwm_backlight_update_status(struct backlight_device *bl)
0109 {
0110     struct pwm_bl_data *pb = bl_get_data(bl);
0111     int brightness = backlight_get_brightness(bl);
0112     struct pwm_state state;
0113 
0114     if (pb->notify)
0115         brightness = pb->notify(pb->dev, brightness);
0116 
0117     if (brightness > 0) {
0118         pwm_get_state(pb->pwm, &state);
0119         state.duty_cycle = compute_duty_cycle(pb, brightness);
0120         pwm_apply_state(pb->pwm, &state);
0121         pwm_backlight_power_on(pb);
0122     } else {
0123         pwm_backlight_power_off(pb);
0124     }
0125 
0126     if (pb->notify_after)
0127         pb->notify_after(pb->dev, brightness);
0128 
0129     return 0;
0130 }
0131 
0132 static int pwm_backlight_check_fb(struct backlight_device *bl,
0133                   struct fb_info *info)
0134 {
0135     struct pwm_bl_data *pb = bl_get_data(bl);
0136 
0137     return !pb->check_fb || pb->check_fb(pb->dev, info);
0138 }
0139 
0140 static const struct backlight_ops pwm_backlight_ops = {
0141     .update_status  = pwm_backlight_update_status,
0142     .check_fb   = pwm_backlight_check_fb,
0143 };
0144 
0145 #ifdef CONFIG_OF
0146 #define PWM_LUMINANCE_SHIFT 16
0147 #define PWM_LUMINANCE_SCALE (1 << PWM_LUMINANCE_SHIFT) /* luminance scale */
0148 
0149 /*
0150  * CIE lightness to PWM conversion.
0151  *
0152  * The CIE 1931 lightness formula is what actually describes how we perceive
0153  * light:
0154  *          Y = (L* / 903.3)           if L* ≤ 8
0155  *          Y = ((L* + 16) / 116)^3    if L* > 8
0156  *
0157  * Where Y is the luminance, the amount of light coming out of the screen, and
0158  * is a number between 0.0 and 1.0; and L* is the lightness, how bright a human
0159  * perceives the screen to be, and is a number between 0 and 100.
0160  *
0161  * The following function does the fixed point maths needed to implement the
0162  * above formula.
0163  */
0164 static u64 cie1931(unsigned int lightness)
0165 {
0166     u64 retval;
0167 
0168     /*
0169      * @lightness is given as a number between 0 and 1, expressed
0170      * as a fixed-point number in scale
0171      * PWM_LUMINANCE_SCALE. Convert to a percentage, still
0172      * expressed as a fixed-point number, so the above formulas
0173      * can be applied.
0174      */
0175     lightness *= 100;
0176     if (lightness <= (8 * PWM_LUMINANCE_SCALE)) {
0177         retval = DIV_ROUND_CLOSEST(lightness * 10, 9033);
0178     } else {
0179         retval = (lightness + (16 * PWM_LUMINANCE_SCALE)) / 116;
0180         retval *= retval * retval;
0181         retval += 1ULL << (2*PWM_LUMINANCE_SHIFT - 1);
0182         retval >>= 2*PWM_LUMINANCE_SHIFT;
0183     }
0184 
0185     return retval;
0186 }
0187 
0188 /*
0189  * Create a default correction table for PWM values to create linear brightness
0190  * for LED based backlights using the CIE1931 algorithm.
0191  */
0192 static
0193 int pwm_backlight_brightness_default(struct device *dev,
0194                      struct platform_pwm_backlight_data *data,
0195                      unsigned int period)
0196 {
0197     unsigned int i;
0198     u64 retval;
0199 
0200     /*
0201      * Once we have 4096 levels there's little point going much higher...
0202      * neither interactive sliders nor animation benefits from having
0203      * more values in the table.
0204      */
0205     data->max_brightness =
0206         min((int)DIV_ROUND_UP(period, fls(period)), 4096);
0207 
0208     data->levels = devm_kcalloc(dev, data->max_brightness,
0209                     sizeof(*data->levels), GFP_KERNEL);
0210     if (!data->levels)
0211         return -ENOMEM;
0212 
0213     /* Fill the table using the cie1931 algorithm */
0214     for (i = 0; i < data->max_brightness; i++) {
0215         retval = cie1931((i * PWM_LUMINANCE_SCALE) /
0216                  data->max_brightness) * period;
0217         retval = DIV_ROUND_CLOSEST_ULL(retval, PWM_LUMINANCE_SCALE);
0218         if (retval > UINT_MAX)
0219             return -EINVAL;
0220         data->levels[i] = (unsigned int)retval;
0221     }
0222 
0223     data->dft_brightness = data->max_brightness / 2;
0224     data->max_brightness--;
0225 
0226     return 0;
0227 }
0228 
0229 static int pwm_backlight_parse_dt(struct device *dev,
0230                   struct platform_pwm_backlight_data *data)
0231 {
0232     struct device_node *node = dev->of_node;
0233     unsigned int num_levels;
0234     unsigned int num_steps = 0;
0235     struct property *prop;
0236     unsigned int *table;
0237     int length;
0238     u32 value;
0239     int ret;
0240 
0241     if (!node)
0242         return -ENODEV;
0243 
0244     memset(data, 0, sizeof(*data));
0245 
0246     /*
0247      * These values are optional and set as 0 by default, the out values
0248      * are modified only if a valid u32 value can be decoded.
0249      */
0250     of_property_read_u32(node, "post-pwm-on-delay-ms",
0251                  &data->post_pwm_on_delay);
0252     of_property_read_u32(node, "pwm-off-delay-ms", &data->pwm_off_delay);
0253 
0254     /*
0255      * Determine the number of brightness levels, if this property is not
0256      * set a default table of brightness levels will be used.
0257      */
0258     prop = of_find_property(node, "brightness-levels", &length);
0259     if (!prop)
0260         return 0;
0261 
0262     num_levels = length / sizeof(u32);
0263 
0264     /* read brightness levels from DT property */
0265     if (num_levels > 0) {
0266         data->levels = devm_kcalloc(dev, num_levels,
0267                         sizeof(*data->levels), GFP_KERNEL);
0268         if (!data->levels)
0269             return -ENOMEM;
0270 
0271         ret = of_property_read_u32_array(node, "brightness-levels",
0272                          data->levels,
0273                          num_levels);
0274         if (ret < 0)
0275             return ret;
0276 
0277         ret = of_property_read_u32(node, "default-brightness-level",
0278                        &value);
0279         if (ret < 0)
0280             return ret;
0281 
0282         data->dft_brightness = value;
0283 
0284         /*
0285          * This property is optional, if is set enables linear
0286          * interpolation between each of the values of brightness levels
0287          * and creates a new pre-computed table.
0288          */
0289         of_property_read_u32(node, "num-interpolated-steps",
0290                      &num_steps);
0291 
0292         /*
0293          * Make sure that there is at least two entries in the
0294          * brightness-levels table, otherwise we can't interpolate
0295          * between two points.
0296          */
0297         if (num_steps) {
0298             unsigned int num_input_levels = num_levels;
0299             unsigned int i;
0300             u32 x1, x2, x, dx;
0301             u32 y1, y2;
0302             s64 dy;
0303 
0304             if (num_input_levels < 2) {
0305                 dev_err(dev, "can't interpolate\n");
0306                 return -EINVAL;
0307             }
0308 
0309             /*
0310              * Recalculate the number of brightness levels, now
0311              * taking in consideration the number of interpolated
0312              * steps between two levels.
0313              */
0314             num_levels = (num_input_levels - 1) * num_steps + 1;
0315             dev_dbg(dev, "new number of brightness levels: %d\n",
0316                 num_levels);
0317 
0318             /*
0319              * Create a new table of brightness levels with all the
0320              * interpolated steps.
0321              */
0322             table = devm_kcalloc(dev, num_levels, sizeof(*table),
0323                          GFP_KERNEL);
0324             if (!table)
0325                 return -ENOMEM;
0326             /*
0327              * Fill the interpolated table[x] = y
0328              * by draw lines between each (x1, y1) to (x2, y2).
0329              */
0330             dx = num_steps;
0331             for (i = 0; i < num_input_levels - 1; i++) {
0332                 x1 = i * dx;
0333                 x2 = x1 + dx;
0334                 y1 = data->levels[i];
0335                 y2 = data->levels[i + 1];
0336                 dy = (s64)y2 - y1;
0337 
0338                 for (x = x1; x < x2; x++) {
0339                     table[x] = y1 +
0340                         div_s64(dy * (x - x1), dx);
0341                 }
0342             }
0343             /* Fill in the last point, since no line starts here. */
0344             table[x2] = y2;
0345 
0346             /*
0347              * As we use interpolation lets remove current
0348              * brightness levels table and replace for the
0349              * new interpolated table.
0350              */
0351             devm_kfree(dev, data->levels);
0352             data->levels = table;
0353         }
0354 
0355         data->max_brightness = num_levels - 1;
0356     }
0357 
0358     return 0;
0359 }
0360 
0361 static const struct of_device_id pwm_backlight_of_match[] = {
0362     { .compatible = "pwm-backlight" },
0363     { }
0364 };
0365 
0366 MODULE_DEVICE_TABLE(of, pwm_backlight_of_match);
0367 #else
0368 static int pwm_backlight_parse_dt(struct device *dev,
0369                   struct platform_pwm_backlight_data *data)
0370 {
0371     return -ENODEV;
0372 }
0373 
0374 static
0375 int pwm_backlight_brightness_default(struct device *dev,
0376                      struct platform_pwm_backlight_data *data,
0377                      unsigned int period)
0378 {
0379     return -ENODEV;
0380 }
0381 #endif
0382 
0383 static bool pwm_backlight_is_linear(struct platform_pwm_backlight_data *data)
0384 {
0385     unsigned int nlevels = data->max_brightness + 1;
0386     unsigned int min_val = data->levels[0];
0387     unsigned int max_val = data->levels[nlevels - 1];
0388     /*
0389      * Multiplying by 128 means that even in pathological cases such
0390      * as (max_val - min_val) == nlevels the error at max_val is less
0391      * than 1%.
0392      */
0393     unsigned int slope = (128 * (max_val - min_val)) / nlevels;
0394     unsigned int margin = (max_val - min_val) / 20; /* 5% */
0395     int i;
0396 
0397     for (i = 1; i < nlevels; i++) {
0398         unsigned int linear_value = min_val + ((i * slope) / 128);
0399         unsigned int delta = abs(linear_value - data->levels[i]);
0400 
0401         if (delta > margin)
0402             return false;
0403     }
0404 
0405     return true;
0406 }
0407 
0408 static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb)
0409 {
0410     struct device_node *node = pb->dev->of_node;
0411     bool active = true;
0412 
0413     /*
0414      * If the enable GPIO is present, observable (either as input
0415      * or output) and off then the backlight is not currently active.
0416      * */
0417     if (pb->enable_gpio && gpiod_get_value_cansleep(pb->enable_gpio) == 0)
0418         active = false;
0419 
0420     if (!regulator_is_enabled(pb->power_supply))
0421         active = false;
0422 
0423     if (!pwm_is_enabled(pb->pwm))
0424         active = false;
0425 
0426     /*
0427      * Synchronize the enable_gpio with the observed state of the
0428      * hardware.
0429      */
0430     if (pb->enable_gpio)
0431         gpiod_direction_output(pb->enable_gpio, active);
0432 
0433     /*
0434      * Do not change pb->enabled here! pb->enabled essentially
0435      * tells us if we own one of the regulator's use counts and
0436      * right now we do not.
0437      */
0438 
0439     /* Not booted with device tree or no phandle link to the node */
0440     if (!node || !node->phandle)
0441         return FB_BLANK_UNBLANK;
0442 
0443     /*
0444      * If the driver is probed from the device tree and there is a
0445      * phandle link pointing to the backlight node, it is safe to
0446      * assume that another driver will enable the backlight at the
0447      * appropriate time. Therefore, if it is disabled, keep it so.
0448      */
0449     return active ? FB_BLANK_UNBLANK: FB_BLANK_POWERDOWN;
0450 }
0451 
0452 static int pwm_backlight_probe(struct platform_device *pdev)
0453 {
0454     struct platform_pwm_backlight_data *data = dev_get_platdata(&pdev->dev);
0455     struct platform_pwm_backlight_data defdata;
0456     struct backlight_properties props;
0457     struct backlight_device *bl;
0458     struct device_node *node = pdev->dev.of_node;
0459     struct pwm_bl_data *pb;
0460     struct pwm_state state;
0461     unsigned int i;
0462     int ret;
0463 
0464     if (!data) {
0465         ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
0466         if (ret < 0) {
0467             dev_err(&pdev->dev, "failed to find platform data\n");
0468             return ret;
0469         }
0470 
0471         data = &defdata;
0472     }
0473 
0474     if (data->init) {
0475         ret = data->init(&pdev->dev);
0476         if (ret < 0)
0477             return ret;
0478     }
0479 
0480     pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL);
0481     if (!pb) {
0482         ret = -ENOMEM;
0483         goto err_alloc;
0484     }
0485 
0486     pb->notify = data->notify;
0487     pb->notify_after = data->notify_after;
0488     pb->check_fb = data->check_fb;
0489     pb->exit = data->exit;
0490     pb->dev = &pdev->dev;
0491     pb->enabled = false;
0492     pb->post_pwm_on_delay = data->post_pwm_on_delay;
0493     pb->pwm_off_delay = data->pwm_off_delay;
0494 
0495     pb->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable",
0496                           GPIOD_ASIS);
0497     if (IS_ERR(pb->enable_gpio)) {
0498         ret = PTR_ERR(pb->enable_gpio);
0499         goto err_alloc;
0500     }
0501 
0502     pb->power_supply = devm_regulator_get(&pdev->dev, "power");
0503     if (IS_ERR(pb->power_supply)) {
0504         ret = PTR_ERR(pb->power_supply);
0505         goto err_alloc;
0506     }
0507 
0508     pb->pwm = devm_pwm_get(&pdev->dev, NULL);
0509     if (IS_ERR(pb->pwm) && PTR_ERR(pb->pwm) != -EPROBE_DEFER && !node) {
0510         dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");
0511         pb->legacy = true;
0512         pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
0513     }
0514 
0515     if (IS_ERR(pb->pwm)) {
0516         ret = PTR_ERR(pb->pwm);
0517         if (ret != -EPROBE_DEFER)
0518             dev_err(&pdev->dev, "unable to request PWM\n");
0519         goto err_alloc;
0520     }
0521 
0522     dev_dbg(&pdev->dev, "got pwm for backlight\n");
0523 
0524     /* Sync up PWM state. */
0525     pwm_init_state(pb->pwm, &state);
0526 
0527     /*
0528      * The DT case will set the pwm_period_ns field to 0 and store the
0529      * period, parsed from the DT, in the PWM device. For the non-DT case,
0530      * set the period from platform data if it has not already been set
0531      * via the PWM lookup table.
0532      */
0533     if (!state.period && (data->pwm_period_ns > 0))
0534         state.period = data->pwm_period_ns;
0535 
0536     ret = pwm_apply_state(pb->pwm, &state);
0537     if (ret) {
0538         dev_err(&pdev->dev, "failed to apply initial PWM state: %d\n",
0539             ret);
0540         goto err_alloc;
0541     }
0542 
0543     memset(&props, 0, sizeof(struct backlight_properties));
0544 
0545     if (data->levels) {
0546         pb->levels = data->levels;
0547 
0548         /*
0549          * For the DT case, only when brightness levels is defined
0550          * data->levels is filled. For the non-DT case, data->levels
0551          * can come from platform data, however is not usual.
0552          */
0553         for (i = 0; i <= data->max_brightness; i++)
0554             if (data->levels[i] > pb->scale)
0555                 pb->scale = data->levels[i];
0556 
0557         if (pwm_backlight_is_linear(data))
0558             props.scale = BACKLIGHT_SCALE_LINEAR;
0559         else
0560             props.scale = BACKLIGHT_SCALE_NON_LINEAR;
0561     } else if (!data->max_brightness) {
0562         /*
0563          * If no brightness levels are provided and max_brightness is
0564          * not set, use the default brightness table. For the DT case,
0565          * max_brightness is set to 0 when brightness levels is not
0566          * specified. For the non-DT case, max_brightness is usually
0567          * set to some value.
0568          */
0569 
0570         /* Get the PWM period (in nanoseconds) */
0571         pwm_get_state(pb->pwm, &state);
0572 
0573         ret = pwm_backlight_brightness_default(&pdev->dev, data,
0574                                state.period);
0575         if (ret < 0) {
0576             dev_err(&pdev->dev,
0577                 "failed to setup default brightness table\n");
0578             goto err_alloc;
0579         }
0580 
0581         for (i = 0; i <= data->max_brightness; i++) {
0582             if (data->levels[i] > pb->scale)
0583                 pb->scale = data->levels[i];
0584 
0585             pb->levels = data->levels;
0586         }
0587 
0588         props.scale = BACKLIGHT_SCALE_NON_LINEAR;
0589     } else {
0590         /*
0591          * That only happens for the non-DT case, where platform data
0592          * sets the max_brightness value.
0593          */
0594         pb->scale = data->max_brightness;
0595     }
0596 
0597     pb->lth_brightness = data->lth_brightness * (div_u64(state.period,
0598                 pb->scale));
0599 
0600     props.type = BACKLIGHT_RAW;
0601     props.max_brightness = data->max_brightness;
0602     bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb,
0603                        &pwm_backlight_ops, &props);
0604     if (IS_ERR(bl)) {
0605         dev_err(&pdev->dev, "failed to register backlight\n");
0606         ret = PTR_ERR(bl);
0607         if (pb->legacy)
0608             pwm_free(pb->pwm);
0609         goto err_alloc;
0610     }
0611 
0612     if (data->dft_brightness > data->max_brightness) {
0613         dev_warn(&pdev->dev,
0614              "invalid default brightness level: %u, using %u\n",
0615              data->dft_brightness, data->max_brightness);
0616         data->dft_brightness = data->max_brightness;
0617     }
0618 
0619     bl->props.brightness = data->dft_brightness;
0620     bl->props.power = pwm_backlight_initial_power_state(pb);
0621     backlight_update_status(bl);
0622 
0623     platform_set_drvdata(pdev, bl);
0624     return 0;
0625 
0626 err_alloc:
0627     if (data->exit)
0628         data->exit(&pdev->dev);
0629     return ret;
0630 }
0631 
0632 static int pwm_backlight_remove(struct platform_device *pdev)
0633 {
0634     struct backlight_device *bl = platform_get_drvdata(pdev);
0635     struct pwm_bl_data *pb = bl_get_data(bl);
0636 
0637     backlight_device_unregister(bl);
0638     pwm_backlight_power_off(pb);
0639 
0640     if (pb->exit)
0641         pb->exit(&pdev->dev);
0642     if (pb->legacy)
0643         pwm_free(pb->pwm);
0644 
0645     return 0;
0646 }
0647 
0648 static void pwm_backlight_shutdown(struct platform_device *pdev)
0649 {
0650     struct backlight_device *bl = platform_get_drvdata(pdev);
0651     struct pwm_bl_data *pb = bl_get_data(bl);
0652 
0653     pwm_backlight_power_off(pb);
0654 }
0655 
0656 #ifdef CONFIG_PM_SLEEP
0657 static int pwm_backlight_suspend(struct device *dev)
0658 {
0659     struct backlight_device *bl = dev_get_drvdata(dev);
0660     struct pwm_bl_data *pb = bl_get_data(bl);
0661 
0662     if (pb->notify)
0663         pb->notify(pb->dev, 0);
0664 
0665     pwm_backlight_power_off(pb);
0666 
0667     if (pb->notify_after)
0668         pb->notify_after(pb->dev, 0);
0669 
0670     return 0;
0671 }
0672 
0673 static int pwm_backlight_resume(struct device *dev)
0674 {
0675     struct backlight_device *bl = dev_get_drvdata(dev);
0676 
0677     backlight_update_status(bl);
0678 
0679     return 0;
0680 }
0681 #endif
0682 
0683 static const struct dev_pm_ops pwm_backlight_pm_ops = {
0684 #ifdef CONFIG_PM_SLEEP
0685     .suspend = pwm_backlight_suspend,
0686     .resume = pwm_backlight_resume,
0687     .poweroff = pwm_backlight_suspend,
0688     .restore = pwm_backlight_resume,
0689 #endif
0690 };
0691 
0692 static struct platform_driver pwm_backlight_driver = {
0693     .driver     = {
0694         .name       = "pwm-backlight",
0695         .pm     = &pwm_backlight_pm_ops,
0696         .of_match_table = of_match_ptr(pwm_backlight_of_match),
0697     },
0698     .probe      = pwm_backlight_probe,
0699     .remove     = pwm_backlight_remove,
0700     .shutdown   = pwm_backlight_shutdown,
0701 };
0702 
0703 module_platform_driver(pwm_backlight_driver);
0704 
0705 MODULE_DESCRIPTION("PWM based Backlight Driver");
0706 MODULE_LICENSE("GPL v2");
0707 MODULE_ALIAS("platform:pwm-backlight");