cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

pwm_bl.c (17897B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Simple PWM based backlight control, board code has to setup
      4 * 1) pin configuration so PWM waveforms can output
      5 * 2) platform_data being correctly configured
      6 */
      7
      8#include <linux/delay.h>
      9#include <linux/gpio/consumer.h>
     10#include <linux/module.h>
     11#include <linux/kernel.h>
     12#include <linux/init.h>
     13#include <linux/platform_device.h>
     14#include <linux/fb.h>
     15#include <linux/backlight.h>
     16#include <linux/err.h>
     17#include <linux/pwm.h>
     18#include <linux/pwm_backlight.h>
     19#include <linux/regulator/consumer.h>
     20#include <linux/slab.h>
     21
     22struct pwm_bl_data {
     23	struct pwm_device	*pwm;
     24	struct device		*dev;
     25	unsigned int		lth_brightness;
     26	unsigned int		*levels;
     27	bool			enabled;
     28	struct regulator	*power_supply;
     29	struct gpio_desc	*enable_gpio;
     30	unsigned int		scale;
     31	bool			legacy;
     32	unsigned int		post_pwm_on_delay;
     33	unsigned int		pwm_off_delay;
     34	int			(*notify)(struct device *,
     35					  int brightness);
     36	void			(*notify_after)(struct device *,
     37					int brightness);
     38	int			(*check_fb)(struct device *, struct fb_info *);
     39	void			(*exit)(struct device *);
     40};
     41
     42static void pwm_backlight_power_on(struct pwm_bl_data *pb)
     43{
     44	struct pwm_state state;
     45	int err;
     46
     47	pwm_get_state(pb->pwm, &state);
     48	if (pb->enabled)
     49		return;
     50
     51	err = regulator_enable(pb->power_supply);
     52	if (err < 0)
     53		dev_err(pb->dev, "failed to enable power supply\n");
     54
     55	state.enabled = true;
     56	pwm_apply_state(pb->pwm, &state);
     57
     58	if (pb->post_pwm_on_delay)
     59		msleep(pb->post_pwm_on_delay);
     60
     61	if (pb->enable_gpio)
     62		gpiod_set_value_cansleep(pb->enable_gpio, 1);
     63
     64	pb->enabled = true;
     65}
     66
     67static void pwm_backlight_power_off(struct pwm_bl_data *pb)
     68{
     69	struct pwm_state state;
     70
     71	pwm_get_state(pb->pwm, &state);
     72	if (!pb->enabled)
     73		return;
     74
     75	if (pb->enable_gpio)
     76		gpiod_set_value_cansleep(pb->enable_gpio, 0);
     77
     78	if (pb->pwm_off_delay)
     79		msleep(pb->pwm_off_delay);
     80
     81	state.enabled = false;
     82	state.duty_cycle = 0;
     83	pwm_apply_state(pb->pwm, &state);
     84
     85	regulator_disable(pb->power_supply);
     86	pb->enabled = false;
     87}
     88
     89static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness)
     90{
     91	unsigned int lth = pb->lth_brightness;
     92	struct pwm_state state;
     93	u64 duty_cycle;
     94
     95	pwm_get_state(pb->pwm, &state);
     96
     97	if (pb->levels)
     98		duty_cycle = pb->levels[brightness];
     99	else
    100		duty_cycle = brightness;
    101
    102	duty_cycle *= state.period - lth;
    103	do_div(duty_cycle, pb->scale);
    104
    105	return duty_cycle + lth;
    106}
    107
    108static int pwm_backlight_update_status(struct backlight_device *bl)
    109{
    110	struct pwm_bl_data *pb = bl_get_data(bl);
    111	int brightness = backlight_get_brightness(bl);
    112	struct pwm_state state;
    113
    114	if (pb->notify)
    115		brightness = pb->notify(pb->dev, brightness);
    116
    117	if (brightness > 0) {
    118		pwm_get_state(pb->pwm, &state);
    119		state.duty_cycle = compute_duty_cycle(pb, brightness);
    120		pwm_apply_state(pb->pwm, &state);
    121		pwm_backlight_power_on(pb);
    122	} else {
    123		pwm_backlight_power_off(pb);
    124	}
    125
    126	if (pb->notify_after)
    127		pb->notify_after(pb->dev, brightness);
    128
    129	return 0;
    130}
    131
    132static int pwm_backlight_check_fb(struct backlight_device *bl,
    133				  struct fb_info *info)
    134{
    135	struct pwm_bl_data *pb = bl_get_data(bl);
    136
    137	return !pb->check_fb || pb->check_fb(pb->dev, info);
    138}
    139
    140static const struct backlight_ops pwm_backlight_ops = {
    141	.update_status	= pwm_backlight_update_status,
    142	.check_fb	= pwm_backlight_check_fb,
    143};
    144
    145#ifdef CONFIG_OF
    146#define PWM_LUMINANCE_SHIFT	16
    147#define PWM_LUMINANCE_SCALE	(1 << PWM_LUMINANCE_SHIFT) /* luminance scale */
    148
    149/*
    150 * CIE lightness to PWM conversion.
    151 *
    152 * The CIE 1931 lightness formula is what actually describes how we perceive
    153 * light:
    154 *          Y = (L* / 903.3)           if L* ≤ 8
    155 *          Y = ((L* + 16) / 116)^3    if L* > 8
    156 *
    157 * Where Y is the luminance, the amount of light coming out of the screen, and
    158 * is a number between 0.0 and 1.0; and L* is the lightness, how bright a human
    159 * perceives the screen to be, and is a number between 0 and 100.
    160 *
    161 * The following function does the fixed point maths needed to implement the
    162 * above formula.
    163 */
    164static u64 cie1931(unsigned int lightness)
    165{
    166	u64 retval;
    167
    168	/*
    169	 * @lightness is given as a number between 0 and 1, expressed
    170	 * as a fixed-point number in scale
    171	 * PWM_LUMINANCE_SCALE. Convert to a percentage, still
    172	 * expressed as a fixed-point number, so the above formulas
    173	 * can be applied.
    174	 */
    175	lightness *= 100;
    176	if (lightness <= (8 * PWM_LUMINANCE_SCALE)) {
    177		retval = DIV_ROUND_CLOSEST(lightness * 10, 9033);
    178	} else {
    179		retval = (lightness + (16 * PWM_LUMINANCE_SCALE)) / 116;
    180		retval *= retval * retval;
    181		retval += 1ULL << (2*PWM_LUMINANCE_SHIFT - 1);
    182		retval >>= 2*PWM_LUMINANCE_SHIFT;
    183	}
    184
    185	return retval;
    186}
    187
    188/*
    189 * Create a default correction table for PWM values to create linear brightness
    190 * for LED based backlights using the CIE1931 algorithm.
    191 */
    192static
    193int pwm_backlight_brightness_default(struct device *dev,
    194				     struct platform_pwm_backlight_data *data,
    195				     unsigned int period)
    196{
    197	unsigned int i;
    198	u64 retval;
    199
    200	/*
    201	 * Once we have 4096 levels there's little point going much higher...
    202	 * neither interactive sliders nor animation benefits from having
    203	 * more values in the table.
    204	 */
    205	data->max_brightness =
    206		min((int)DIV_ROUND_UP(period, fls(period)), 4096);
    207
    208	data->levels = devm_kcalloc(dev, data->max_brightness,
    209				    sizeof(*data->levels), GFP_KERNEL);
    210	if (!data->levels)
    211		return -ENOMEM;
    212
    213	/* Fill the table using the cie1931 algorithm */
    214	for (i = 0; i < data->max_brightness; i++) {
    215		retval = cie1931((i * PWM_LUMINANCE_SCALE) /
    216				 data->max_brightness) * period;
    217		retval = DIV_ROUND_CLOSEST_ULL(retval, PWM_LUMINANCE_SCALE);
    218		if (retval > UINT_MAX)
    219			return -EINVAL;
    220		data->levels[i] = (unsigned int)retval;
    221	}
    222
    223	data->dft_brightness = data->max_brightness / 2;
    224	data->max_brightness--;
    225
    226	return 0;
    227}
    228
    229static int pwm_backlight_parse_dt(struct device *dev,
    230				  struct platform_pwm_backlight_data *data)
    231{
    232	struct device_node *node = dev->of_node;
    233	unsigned int num_levels;
    234	unsigned int num_steps = 0;
    235	struct property *prop;
    236	unsigned int *table;
    237	int length;
    238	u32 value;
    239	int ret;
    240
    241	if (!node)
    242		return -ENODEV;
    243
    244	memset(data, 0, sizeof(*data));
    245
    246	/*
    247	 * These values are optional and set as 0 by default, the out values
    248	 * are modified only if a valid u32 value can be decoded.
    249	 */
    250	of_property_read_u32(node, "post-pwm-on-delay-ms",
    251			     &data->post_pwm_on_delay);
    252	of_property_read_u32(node, "pwm-off-delay-ms", &data->pwm_off_delay);
    253
    254	/*
    255	 * Determine the number of brightness levels, if this property is not
    256	 * set a default table of brightness levels will be used.
    257	 */
    258	prop = of_find_property(node, "brightness-levels", &length);
    259	if (!prop)
    260		return 0;
    261
    262	num_levels = length / sizeof(u32);
    263
    264	/* read brightness levels from DT property */
    265	if (num_levels > 0) {
    266		data->levels = devm_kcalloc(dev, num_levels,
    267					    sizeof(*data->levels), GFP_KERNEL);
    268		if (!data->levels)
    269			return -ENOMEM;
    270
    271		ret = of_property_read_u32_array(node, "brightness-levels",
    272						 data->levels,
    273						 num_levels);
    274		if (ret < 0)
    275			return ret;
    276
    277		ret = of_property_read_u32(node, "default-brightness-level",
    278					   &value);
    279		if (ret < 0)
    280			return ret;
    281
    282		data->dft_brightness = value;
    283
    284		/*
    285		 * This property is optional, if is set enables linear
    286		 * interpolation between each of the values of brightness levels
    287		 * and creates a new pre-computed table.
    288		 */
    289		of_property_read_u32(node, "num-interpolated-steps",
    290				     &num_steps);
    291
    292		/*
    293		 * Make sure that there is at least two entries in the
    294		 * brightness-levels table, otherwise we can't interpolate
    295		 * between two points.
    296		 */
    297		if (num_steps) {
    298			unsigned int num_input_levels = num_levels;
    299			unsigned int i;
    300			u32 x1, x2, x, dx;
    301			u32 y1, y2;
    302			s64 dy;
    303
    304			if (num_input_levels < 2) {
    305				dev_err(dev, "can't interpolate\n");
    306				return -EINVAL;
    307			}
    308
    309			/*
    310			 * Recalculate the number of brightness levels, now
    311			 * taking in consideration the number of interpolated
    312			 * steps between two levels.
    313			 */
    314			num_levels = (num_input_levels - 1) * num_steps + 1;
    315			dev_dbg(dev, "new number of brightness levels: %d\n",
    316				num_levels);
    317
    318			/*
    319			 * Create a new table of brightness levels with all the
    320			 * interpolated steps.
    321			 */
    322			table = devm_kcalloc(dev, num_levels, sizeof(*table),
    323					     GFP_KERNEL);
    324			if (!table)
    325				return -ENOMEM;
    326			/*
    327			 * Fill the interpolated table[x] = y
    328			 * by draw lines between each (x1, y1) to (x2, y2).
    329			 */
    330			dx = num_steps;
    331			for (i = 0; i < num_input_levels - 1; i++) {
    332				x1 = i * dx;
    333				x2 = x1 + dx;
    334				y1 = data->levels[i];
    335				y2 = data->levels[i + 1];
    336				dy = (s64)y2 - y1;
    337
    338				for (x = x1; x < x2; x++) {
    339					table[x] = y1 +
    340						div_s64(dy * (x - x1), dx);
    341				}
    342			}
    343			/* Fill in the last point, since no line starts here. */
    344			table[x2] = y2;
    345
    346			/*
    347			 * As we use interpolation lets remove current
    348			 * brightness levels table and replace for the
    349			 * new interpolated table.
    350			 */
    351			devm_kfree(dev, data->levels);
    352			data->levels = table;
    353		}
    354
    355		data->max_brightness = num_levels - 1;
    356	}
    357
    358	return 0;
    359}
    360
    361static const struct of_device_id pwm_backlight_of_match[] = {
    362	{ .compatible = "pwm-backlight" },
    363	{ }
    364};
    365
    366MODULE_DEVICE_TABLE(of, pwm_backlight_of_match);
    367#else
    368static int pwm_backlight_parse_dt(struct device *dev,
    369				  struct platform_pwm_backlight_data *data)
    370{
    371	return -ENODEV;
    372}
    373
    374static
    375int pwm_backlight_brightness_default(struct device *dev,
    376				     struct platform_pwm_backlight_data *data,
    377				     unsigned int period)
    378{
    379	return -ENODEV;
    380}
    381#endif
    382
    383static bool pwm_backlight_is_linear(struct platform_pwm_backlight_data *data)
    384{
    385	unsigned int nlevels = data->max_brightness + 1;
    386	unsigned int min_val = data->levels[0];
    387	unsigned int max_val = data->levels[nlevels - 1];
    388	/*
    389	 * Multiplying by 128 means that even in pathological cases such
    390	 * as (max_val - min_val) == nlevels the error at max_val is less
    391	 * than 1%.
    392	 */
    393	unsigned int slope = (128 * (max_val - min_val)) / nlevels;
    394	unsigned int margin = (max_val - min_val) / 20; /* 5% */
    395	int i;
    396
    397	for (i = 1; i < nlevels; i++) {
    398		unsigned int linear_value = min_val + ((i * slope) / 128);
    399		unsigned int delta = abs(linear_value - data->levels[i]);
    400
    401		if (delta > margin)
    402			return false;
    403	}
    404
    405	return true;
    406}
    407
    408static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb)
    409{
    410	struct device_node *node = pb->dev->of_node;
    411	bool active = true;
    412
    413	/*
    414	 * If the enable GPIO is present, observable (either as input
    415	 * or output) and off then the backlight is not currently active.
    416	 * */
    417	if (pb->enable_gpio && gpiod_get_value_cansleep(pb->enable_gpio) == 0)
    418		active = false;
    419
    420	if (!regulator_is_enabled(pb->power_supply))
    421		active = false;
    422
    423	if (!pwm_is_enabled(pb->pwm))
    424		active = false;
    425
    426	/*
    427	 * Synchronize the enable_gpio with the observed state of the
    428	 * hardware.
    429	 */
    430	if (pb->enable_gpio)
    431		gpiod_direction_output(pb->enable_gpio, active);
    432
    433	/*
    434	 * Do not change pb->enabled here! pb->enabled essentially
    435	 * tells us if we own one of the regulator's use counts and
    436	 * right now we do not.
    437	 */
    438
    439	/* Not booted with device tree or no phandle link to the node */
    440	if (!node || !node->phandle)
    441		return FB_BLANK_UNBLANK;
    442
    443	/*
    444	 * If the driver is probed from the device tree and there is a
    445	 * phandle link pointing to the backlight node, it is safe to
    446	 * assume that another driver will enable the backlight at the
    447	 * appropriate time. Therefore, if it is disabled, keep it so.
    448	 */
    449	return active ? FB_BLANK_UNBLANK: FB_BLANK_POWERDOWN;
    450}
    451
    452static int pwm_backlight_probe(struct platform_device *pdev)
    453{
    454	struct platform_pwm_backlight_data *data = dev_get_platdata(&pdev->dev);
    455	struct platform_pwm_backlight_data defdata;
    456	struct backlight_properties props;
    457	struct backlight_device *bl;
    458	struct device_node *node = pdev->dev.of_node;
    459	struct pwm_bl_data *pb;
    460	struct pwm_state state;
    461	unsigned int i;
    462	int ret;
    463
    464	if (!data) {
    465		ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
    466		if (ret < 0) {
    467			dev_err(&pdev->dev, "failed to find platform data\n");
    468			return ret;
    469		}
    470
    471		data = &defdata;
    472	}
    473
    474	if (data->init) {
    475		ret = data->init(&pdev->dev);
    476		if (ret < 0)
    477			return ret;
    478	}
    479
    480	pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL);
    481	if (!pb) {
    482		ret = -ENOMEM;
    483		goto err_alloc;
    484	}
    485
    486	pb->notify = data->notify;
    487	pb->notify_after = data->notify_after;
    488	pb->check_fb = data->check_fb;
    489	pb->exit = data->exit;
    490	pb->dev = &pdev->dev;
    491	pb->enabled = false;
    492	pb->post_pwm_on_delay = data->post_pwm_on_delay;
    493	pb->pwm_off_delay = data->pwm_off_delay;
    494
    495	pb->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable",
    496						  GPIOD_ASIS);
    497	if (IS_ERR(pb->enable_gpio)) {
    498		ret = PTR_ERR(pb->enable_gpio);
    499		goto err_alloc;
    500	}
    501
    502	pb->power_supply = devm_regulator_get(&pdev->dev, "power");
    503	if (IS_ERR(pb->power_supply)) {
    504		ret = PTR_ERR(pb->power_supply);
    505		goto err_alloc;
    506	}
    507
    508	pb->pwm = devm_pwm_get(&pdev->dev, NULL);
    509	if (IS_ERR(pb->pwm) && PTR_ERR(pb->pwm) != -EPROBE_DEFER && !node) {
    510		dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");
    511		pb->legacy = true;
    512		pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
    513	}
    514
    515	if (IS_ERR(pb->pwm)) {
    516		ret = PTR_ERR(pb->pwm);
    517		if (ret != -EPROBE_DEFER)
    518			dev_err(&pdev->dev, "unable to request PWM\n");
    519		goto err_alloc;
    520	}
    521
    522	dev_dbg(&pdev->dev, "got pwm for backlight\n");
    523
    524	/* Sync up PWM state. */
    525	pwm_init_state(pb->pwm, &state);
    526
    527	/*
    528	 * The DT case will set the pwm_period_ns field to 0 and store the
    529	 * period, parsed from the DT, in the PWM device. For the non-DT case,
    530	 * set the period from platform data if it has not already been set
    531	 * via the PWM lookup table.
    532	 */
    533	if (!state.period && (data->pwm_period_ns > 0))
    534		state.period = data->pwm_period_ns;
    535
    536	ret = pwm_apply_state(pb->pwm, &state);
    537	if (ret) {
    538		dev_err(&pdev->dev, "failed to apply initial PWM state: %d\n",
    539			ret);
    540		goto err_alloc;
    541	}
    542
    543	memset(&props, 0, sizeof(struct backlight_properties));
    544
    545	if (data->levels) {
    546		pb->levels = data->levels;
    547
    548		/*
    549		 * For the DT case, only when brightness levels is defined
    550		 * data->levels is filled. For the non-DT case, data->levels
    551		 * can come from platform data, however is not usual.
    552		 */
    553		for (i = 0; i <= data->max_brightness; i++)
    554			if (data->levels[i] > pb->scale)
    555				pb->scale = data->levels[i];
    556
    557		if (pwm_backlight_is_linear(data))
    558			props.scale = BACKLIGHT_SCALE_LINEAR;
    559		else
    560			props.scale = BACKLIGHT_SCALE_NON_LINEAR;
    561	} else if (!data->max_brightness) {
    562		/*
    563		 * If no brightness levels are provided and max_brightness is
    564		 * not set, use the default brightness table. For the DT case,
    565		 * max_brightness is set to 0 when brightness levels is not
    566		 * specified. For the non-DT case, max_brightness is usually
    567		 * set to some value.
    568		 */
    569
    570		/* Get the PWM period (in nanoseconds) */
    571		pwm_get_state(pb->pwm, &state);
    572
    573		ret = pwm_backlight_brightness_default(&pdev->dev, data,
    574						       state.period);
    575		if (ret < 0) {
    576			dev_err(&pdev->dev,
    577				"failed to setup default brightness table\n");
    578			goto err_alloc;
    579		}
    580
    581		for (i = 0; i <= data->max_brightness; i++) {
    582			if (data->levels[i] > pb->scale)
    583				pb->scale = data->levels[i];
    584
    585			pb->levels = data->levels;
    586		}
    587
    588		props.scale = BACKLIGHT_SCALE_NON_LINEAR;
    589	} else {
    590		/*
    591		 * That only happens for the non-DT case, where platform data
    592		 * sets the max_brightness value.
    593		 */
    594		pb->scale = data->max_brightness;
    595	}
    596
    597	pb->lth_brightness = data->lth_brightness * (div_u64(state.period,
    598				pb->scale));
    599
    600	props.type = BACKLIGHT_RAW;
    601	props.max_brightness = data->max_brightness;
    602	bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb,
    603				       &pwm_backlight_ops, &props);
    604	if (IS_ERR(bl)) {
    605		dev_err(&pdev->dev, "failed to register backlight\n");
    606		ret = PTR_ERR(bl);
    607		if (pb->legacy)
    608			pwm_free(pb->pwm);
    609		goto err_alloc;
    610	}
    611
    612	if (data->dft_brightness > data->max_brightness) {
    613		dev_warn(&pdev->dev,
    614			 "invalid default brightness level: %u, using %u\n",
    615			 data->dft_brightness, data->max_brightness);
    616		data->dft_brightness = data->max_brightness;
    617	}
    618
    619	bl->props.brightness = data->dft_brightness;
    620	bl->props.power = pwm_backlight_initial_power_state(pb);
    621	backlight_update_status(bl);
    622
    623	platform_set_drvdata(pdev, bl);
    624	return 0;
    625
    626err_alloc:
    627	if (data->exit)
    628		data->exit(&pdev->dev);
    629	return ret;
    630}
    631
    632static int pwm_backlight_remove(struct platform_device *pdev)
    633{
    634	struct backlight_device *bl = platform_get_drvdata(pdev);
    635	struct pwm_bl_data *pb = bl_get_data(bl);
    636
    637	backlight_device_unregister(bl);
    638	pwm_backlight_power_off(pb);
    639
    640	if (pb->exit)
    641		pb->exit(&pdev->dev);
    642	if (pb->legacy)
    643		pwm_free(pb->pwm);
    644
    645	return 0;
    646}
    647
    648static void pwm_backlight_shutdown(struct platform_device *pdev)
    649{
    650	struct backlight_device *bl = platform_get_drvdata(pdev);
    651	struct pwm_bl_data *pb = bl_get_data(bl);
    652
    653	pwm_backlight_power_off(pb);
    654}
    655
    656#ifdef CONFIG_PM_SLEEP
    657static int pwm_backlight_suspend(struct device *dev)
    658{
    659	struct backlight_device *bl = dev_get_drvdata(dev);
    660	struct pwm_bl_data *pb = bl_get_data(bl);
    661
    662	if (pb->notify)
    663		pb->notify(pb->dev, 0);
    664
    665	pwm_backlight_power_off(pb);
    666
    667	if (pb->notify_after)
    668		pb->notify_after(pb->dev, 0);
    669
    670	return 0;
    671}
    672
    673static int pwm_backlight_resume(struct device *dev)
    674{
    675	struct backlight_device *bl = dev_get_drvdata(dev);
    676
    677	backlight_update_status(bl);
    678
    679	return 0;
    680}
    681#endif
    682
    683static const struct dev_pm_ops pwm_backlight_pm_ops = {
    684#ifdef CONFIG_PM_SLEEP
    685	.suspend = pwm_backlight_suspend,
    686	.resume = pwm_backlight_resume,
    687	.poweroff = pwm_backlight_suspend,
    688	.restore = pwm_backlight_resume,
    689#endif
    690};
    691
    692static struct platform_driver pwm_backlight_driver = {
    693	.driver		= {
    694		.name		= "pwm-backlight",
    695		.pm		= &pwm_backlight_pm_ops,
    696		.of_match_table	= of_match_ptr(pwm_backlight_of_match),
    697	},
    698	.probe		= pwm_backlight_probe,
    699	.remove		= pwm_backlight_remove,
    700	.shutdown	= pwm_backlight_shutdown,
    701};
    702
    703module_platform_driver(pwm_backlight_driver);
    704
    705MODULE_DESCRIPTION("PWM based Backlight Driver");
    706MODULE_LICENSE("GPL v2");
    707MODULE_ALIAS("platform:pwm-backlight");