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

wm831x_bl.c (5335B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Backlight driver for Wolfson Microelectronics WM831x PMICs
      4 *
      5 * Copyright 2009 Wolfson Microelectonics plc
      6 */
      7
      8#include <linux/kernel.h>
      9#include <linux/init.h>
     10#include <linux/platform_device.h>
     11#include <linux/module.h>
     12#include <linux/fb.h>
     13#include <linux/backlight.h>
     14#include <linux/slab.h>
     15
     16#include <linux/mfd/wm831x/core.h>
     17#include <linux/mfd/wm831x/pdata.h>
     18#include <linux/mfd/wm831x/regulator.h>
     19
     20struct wm831x_backlight_data {
     21	struct wm831x *wm831x;
     22	int isink_reg;
     23	int current_brightness;
     24};
     25
     26static int wm831x_backlight_set(struct backlight_device *bl, int brightness)
     27{
     28	struct wm831x_backlight_data *data = bl_get_data(bl);
     29	struct wm831x *wm831x = data->wm831x;
     30	int power_up = !data->current_brightness && brightness;
     31	int power_down = data->current_brightness && !brightness;
     32	int ret;
     33
     34	if (power_up) {
     35		/* Enable the ISINK */
     36		ret = wm831x_set_bits(wm831x, data->isink_reg,
     37				      WM831X_CS1_ENA, WM831X_CS1_ENA);
     38		if (ret < 0)
     39			goto err;
     40
     41		/* Enable the DC-DC */
     42		ret = wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE,
     43				      WM831X_DC4_ENA, WM831X_DC4_ENA);
     44		if (ret < 0)
     45			goto err;
     46	}
     47
     48	if (power_down) {
     49		/* DCDC first */
     50		ret = wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE,
     51				      WM831X_DC4_ENA, 0);
     52		if (ret < 0)
     53			goto err;
     54
     55		/* ISINK */
     56		ret = wm831x_set_bits(wm831x, data->isink_reg,
     57				      WM831X_CS1_DRIVE | WM831X_CS1_ENA, 0);
     58		if (ret < 0)
     59			goto err;
     60	}
     61
     62	/* Set the new brightness */
     63	ret = wm831x_set_bits(wm831x, data->isink_reg,
     64			      WM831X_CS1_ISEL_MASK, brightness);
     65	if (ret < 0)
     66		goto err;
     67
     68	if (power_up) {
     69		/* Drive current through the ISINK */
     70		ret = wm831x_set_bits(wm831x, data->isink_reg,
     71				      WM831X_CS1_DRIVE, WM831X_CS1_DRIVE);
     72		if (ret < 0)
     73			return ret;
     74	}
     75
     76	data->current_brightness = brightness;
     77
     78	return 0;
     79
     80err:
     81	/* If we were in the middle of a power transition always shut down
     82	 * for safety.
     83	 */
     84	if (power_up || power_down) {
     85		wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, WM831X_DC4_ENA, 0);
     86		wm831x_set_bits(wm831x, data->isink_reg, WM831X_CS1_ENA, 0);
     87	}
     88
     89	return ret;
     90}
     91
     92static int wm831x_backlight_update_status(struct backlight_device *bl)
     93{
     94	return wm831x_backlight_set(bl, backlight_get_brightness(bl));
     95}
     96
     97static int wm831x_backlight_get_brightness(struct backlight_device *bl)
     98{
     99	struct wm831x_backlight_data *data = bl_get_data(bl);
    100
    101	return data->current_brightness;
    102}
    103
    104static const struct backlight_ops wm831x_backlight_ops = {
    105	.options = BL_CORE_SUSPENDRESUME,
    106	.update_status	= wm831x_backlight_update_status,
    107	.get_brightness	= wm831x_backlight_get_brightness,
    108};
    109
    110static int wm831x_backlight_probe(struct platform_device *pdev)
    111{
    112	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
    113	struct wm831x_pdata *wm831x_pdata = dev_get_platdata(pdev->dev.parent);
    114	struct wm831x_backlight_pdata *pdata;
    115	struct wm831x_backlight_data *data;
    116	struct backlight_device *bl;
    117	struct backlight_properties props;
    118	int ret, i, max_isel, isink_reg, dcdc_cfg;
    119
    120	/* We need platform data */
    121	if (wm831x_pdata)
    122		pdata = wm831x_pdata->backlight;
    123	else
    124		pdata = NULL;
    125
    126	if (!pdata) {
    127		dev_err(&pdev->dev, "No platform data supplied\n");
    128		return -EINVAL;
    129	}
    130
    131	/* Figure out the maximum current we can use */
    132	for (i = 0; i < WM831X_ISINK_MAX_ISEL; i++) {
    133		if (wm831x_isinkv_values[i] > pdata->max_uA)
    134			break;
    135	}
    136
    137	if (i == 0) {
    138		dev_err(&pdev->dev, "Invalid max_uA: %duA\n", pdata->max_uA);
    139		return -EINVAL;
    140	}
    141	max_isel = i - 1;
    142
    143	if (pdata->max_uA != wm831x_isinkv_values[max_isel])
    144		dev_warn(&pdev->dev,
    145			 "Maximum current is %duA not %duA as requested\n",
    146			 wm831x_isinkv_values[max_isel], pdata->max_uA);
    147
    148	switch (pdata->isink) {
    149	case 1:
    150		isink_reg = WM831X_CURRENT_SINK_1;
    151		dcdc_cfg = 0;
    152		break;
    153	case 2:
    154		isink_reg = WM831X_CURRENT_SINK_2;
    155		dcdc_cfg = WM831X_DC4_FBSRC;
    156		break;
    157	default:
    158		dev_err(&pdev->dev, "Invalid ISINK %d\n", pdata->isink);
    159		return -EINVAL;
    160	}
    161
    162	/* Configure the ISINK to use for feedback */
    163	ret = wm831x_reg_unlock(wm831x);
    164	if (ret < 0)
    165		return ret;
    166
    167	ret = wm831x_set_bits(wm831x, WM831X_DC4_CONTROL, WM831X_DC4_FBSRC,
    168			      dcdc_cfg);
    169
    170	wm831x_reg_lock(wm831x);
    171	if (ret < 0)
    172		return ret;
    173
    174	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
    175	if (data == NULL)
    176		return -ENOMEM;
    177
    178	data->wm831x = wm831x;
    179	data->current_brightness = 0;
    180	data->isink_reg = isink_reg;
    181
    182	memset(&props, 0, sizeof(props));
    183	props.type = BACKLIGHT_RAW;
    184	props.max_brightness = max_isel;
    185	bl = devm_backlight_device_register(&pdev->dev, "wm831x", &pdev->dev,
    186					data, &wm831x_backlight_ops, &props);
    187	if (IS_ERR(bl)) {
    188		dev_err(&pdev->dev, "failed to register backlight\n");
    189		return PTR_ERR(bl);
    190	}
    191
    192	bl->props.brightness = max_isel;
    193
    194	platform_set_drvdata(pdev, bl);
    195
    196	/* Disable the DCDC if it was started so we can bootstrap */
    197	wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, WM831X_DC4_ENA, 0);
    198
    199	backlight_update_status(bl);
    200
    201	return 0;
    202}
    203
    204static struct platform_driver wm831x_backlight_driver = {
    205	.driver		= {
    206		.name	= "wm831x-backlight",
    207	},
    208	.probe		= wm831x_backlight_probe,
    209};
    210
    211module_platform_driver(wm831x_backlight_driver);
    212
    213MODULE_DESCRIPTION("Backlight Driver for WM831x PMICs");
    214MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com");
    215MODULE_LICENSE("GPL");
    216MODULE_ALIAS("platform:wm831x-backlight");