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