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

pandora_bl.c (4294B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Backlight driver for Pandora handheld.
      4 * Pandora uses TWL4030 PWM0 -> TPS61161 combo for control backlight.
      5 * Based on pwm_bl.c
      6 *
      7 * Copyright 2009,2012 Gražvydas Ignotas <notasas@gmail.com>
      8 */
      9
     10#include <linux/module.h>
     11#include <linux/kernel.h>
     12#include <linux/platform_device.h>
     13#include <linux/delay.h>
     14#include <linux/fb.h>
     15#include <linux/backlight.h>
     16#include <linux/mfd/twl.h>
     17#include <linux/err.h>
     18
     19#define TWL_PWM0_ON		0x00
     20#define TWL_PWM0_OFF		0x01
     21
     22#define TWL_INTBR_GPBR1		0x0c
     23#define TWL_INTBR_PMBR1		0x0d
     24
     25#define TWL_PMBR1_PWM0_MUXMASK	0x0c
     26#define TWL_PMBR1_PWM0		0x04
     27#define PWM0_CLK_ENABLE		BIT(0)
     28#define PWM0_ENABLE		BIT(2)
     29
     30/* range accepted by hardware */
     31#define MIN_VALUE 9
     32#define MAX_VALUE 63
     33#define MAX_USER_VALUE (MAX_VALUE - MIN_VALUE)
     34
     35struct pandora_private {
     36	unsigned old_state;
     37#define PANDORABL_WAS_OFF 1
     38};
     39
     40static int pandora_backlight_update_status(struct backlight_device *bl)
     41{
     42	int brightness = bl->props.brightness;
     43	struct pandora_private *priv = bl_get_data(bl);
     44	u8 r;
     45
     46	if (bl->props.power != FB_BLANK_UNBLANK)
     47		brightness = 0;
     48	if (bl->props.state & BL_CORE_FBBLANK)
     49		brightness = 0;
     50	if (bl->props.state & BL_CORE_SUSPENDED)
     51		brightness = 0;
     52
     53	if ((unsigned int)brightness > MAX_USER_VALUE)
     54		brightness = MAX_USER_VALUE;
     55
     56	if (brightness == 0) {
     57		if (priv->old_state == PANDORABL_WAS_OFF)
     58			goto done;
     59
     60		/* first disable PWM0 output, then clock */
     61		twl_i2c_read_u8(TWL4030_MODULE_INTBR, &r, TWL_INTBR_GPBR1);
     62		r &= ~PWM0_ENABLE;
     63		twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1);
     64		r &= ~PWM0_CLK_ENABLE;
     65		twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1);
     66
     67		goto done;
     68	}
     69
     70	if (priv->old_state == PANDORABL_WAS_OFF) {
     71		/*
     72		 * set PWM duty cycle to max. TPS61161 seems to use this
     73		 * to calibrate it's PWM sensitivity when it starts.
     74		 */
     75		twl_i2c_write_u8(TWL_MODULE_PWM, MAX_VALUE, TWL_PWM0_OFF);
     76
     77		/* first enable clock, then PWM0 out */
     78		twl_i2c_read_u8(TWL4030_MODULE_INTBR, &r, TWL_INTBR_GPBR1);
     79		r &= ~PWM0_ENABLE;
     80		r |= PWM0_CLK_ENABLE;
     81		twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1);
     82		r |= PWM0_ENABLE;
     83		twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1);
     84
     85		/*
     86		 * TI made it very easy to enable digital control, so easy that
     87		 * it often triggers unintentionally and disabes PWM control,
     88		 * so wait until 1 wire mode detection window ends.
     89		 */
     90		usleep_range(2000, 10000);
     91	}
     92
     93	twl_i2c_write_u8(TWL_MODULE_PWM, MIN_VALUE + brightness, TWL_PWM0_OFF);
     94
     95done:
     96	if (brightness != 0)
     97		priv->old_state = 0;
     98	else
     99		priv->old_state = PANDORABL_WAS_OFF;
    100
    101	return 0;
    102}
    103
    104static const struct backlight_ops pandora_backlight_ops = {
    105	.options	= BL_CORE_SUSPENDRESUME,
    106	.update_status	= pandora_backlight_update_status,
    107};
    108
    109static int pandora_backlight_probe(struct platform_device *pdev)
    110{
    111	struct backlight_properties props;
    112	struct backlight_device *bl;
    113	struct pandora_private *priv;
    114	u8 r;
    115
    116	priv = devm_kmalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
    117	if (!priv) {
    118		dev_err(&pdev->dev, "failed to allocate driver private data\n");
    119		return -ENOMEM;
    120	}
    121
    122	memset(&props, 0, sizeof(props));
    123	props.max_brightness = MAX_USER_VALUE;
    124	props.type = BACKLIGHT_RAW;
    125	bl = devm_backlight_device_register(&pdev->dev, pdev->name, &pdev->dev,
    126					priv, &pandora_backlight_ops, &props);
    127	if (IS_ERR(bl)) {
    128		dev_err(&pdev->dev, "failed to register backlight\n");
    129		return PTR_ERR(bl);
    130	}
    131
    132	platform_set_drvdata(pdev, bl);
    133
    134	/* 64 cycle period, ON position 0 */
    135	twl_i2c_write_u8(TWL_MODULE_PWM, 0x80, TWL_PWM0_ON);
    136
    137	priv->old_state = PANDORABL_WAS_OFF;
    138	bl->props.brightness = MAX_USER_VALUE;
    139	backlight_update_status(bl);
    140
    141	/* enable PWM function in pin mux */
    142	twl_i2c_read_u8(TWL4030_MODULE_INTBR, &r, TWL_INTBR_PMBR1);
    143	r &= ~TWL_PMBR1_PWM0_MUXMASK;
    144	r |= TWL_PMBR1_PWM0;
    145	twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_PMBR1);
    146
    147	return 0;
    148}
    149
    150static struct platform_driver pandora_backlight_driver = {
    151	.driver		= {
    152		.name	= "pandora-backlight",
    153	},
    154	.probe		= pandora_backlight_probe,
    155};
    156
    157module_platform_driver(pandora_backlight_driver);
    158
    159MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>");
    160MODULE_DESCRIPTION("Pandora Backlight Driver");
    161MODULE_LICENSE("GPL");
    162MODULE_ALIAS("platform:pandora-backlight");