Blame view
drivers/regulator/rpi-panel-attiny-regulator.c
5.01 KB
385734722 regulator: rpi-pa... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2020 Marek Vasut <marex@denx.de> * * Based on rpi_touchscreen.c by Eric Anholt <eric@anholt.net> */ #include <linux/backlight.h> #include <linux/err.h> #include <linux/gpio.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/module.h> #include <linux/regmap.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/regulator/of_regulator.h> #include <linux/slab.h> /* I2C registers of the Atmel microcontroller. */ #define REG_ID 0x80 #define REG_PORTA 0x81 #define REG_PORTA_HF BIT(2) #define REG_PORTA_VF BIT(3) #define REG_PORTB 0x82 #define REG_POWERON 0x85 #define REG_PWM 0x86 static const struct regmap_config attiny_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = REG_PWM, .cache_type = REGCACHE_NONE, }; static int attiny_lcd_power_enable(struct regulator_dev *rdev) { unsigned int data; regmap_write(rdev->regmap, REG_POWERON, 1); /* Wait for nPWRDWN to go low to indicate poweron is done. */ regmap_read_poll_timeout(rdev->regmap, REG_PORTB, data, data & BIT(0), 10, 1000000); /* Default to the same orientation as the closed source * firmware used for the panel. Runtime rotation * configuration will be supported using VC4's plane * orientation bits. */ regmap_write(rdev->regmap, REG_PORTA, BIT(2)); return 0; } static int attiny_lcd_power_disable(struct regulator_dev *rdev) { regmap_write(rdev->regmap, REG_PWM, 0); regmap_write(rdev->regmap, REG_POWERON, 0); udelay(1); return 0; } static int attiny_lcd_power_is_enabled(struct regulator_dev *rdev) { unsigned int data; int ret; ret = regmap_read(rdev->regmap, REG_POWERON, &data); if (ret < 0) return ret; if (!(data & BIT(0))) return 0; ret = regmap_read(rdev->regmap, REG_PORTB, &data); if (ret < 0) return ret; return data & BIT(0); } static const struct regulator_init_data attiny_regulator_default = { .constraints = { .valid_ops_mask = REGULATOR_CHANGE_STATUS, }, }; static const struct regulator_ops attiny_regulator_ops = { .enable = attiny_lcd_power_enable, .disable = attiny_lcd_power_disable, .is_enabled = attiny_lcd_power_is_enabled, }; static const struct regulator_desc attiny_regulator = { .name = "tc358762-power", .ops = &attiny_regulator_ops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }; static int attiny_update_status(struct backlight_device *bl) { struct regmap *regmap = bl_get_data(bl); int brightness = bl->props.brightness; if (bl->props.power != FB_BLANK_UNBLANK || bl->props.fb_blank != FB_BLANK_UNBLANK) brightness = 0; return regmap_write(regmap, REG_PWM, brightness); } static int attiny_get_brightness(struct backlight_device *bl) { struct regmap *regmap = bl_get_data(bl); int ret, brightness; ret = regmap_read(regmap, REG_PWM, &brightness); if (ret) return ret; return brightness; } static const struct backlight_ops attiny_bl = { .update_status = attiny_update_status, .get_brightness = attiny_get_brightness, }; /* * I2C driver interface functions */ static int attiny_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct backlight_properties props = { }; struct regulator_config config = { }; struct backlight_device *bl; struct regulator_dev *rdev; struct regmap *regmap; unsigned int data; int ret; regmap = devm_regmap_init_i2c(i2c, &attiny_regmap_config); if (IS_ERR(regmap)) { ret = PTR_ERR(regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d ", ret); return ret; } ret = regmap_read(regmap, REG_ID, &data); if (ret < 0) { dev_err(&i2c->dev, "Failed to read REG_ID reg: %d ", ret); return ret; } switch (data) { case 0xde: /* ver 1 */ case 0xc3: /* ver 2 */ break; default: dev_err(&i2c->dev, "Unknown Atmel firmware revision: 0x%02x ", data); return -ENODEV; } regmap_write(regmap, REG_POWERON, 0); mdelay(1); config.dev = &i2c->dev; config.regmap = regmap; config.of_node = i2c->dev.of_node; config.init_data = &attiny_regulator_default; rdev = devm_regulator_register(&i2c->dev, &attiny_regulator, &config); if (IS_ERR(rdev)) { dev_err(&i2c->dev, "Failed to register ATTINY regulator "); return PTR_ERR(rdev); } props.type = BACKLIGHT_RAW; props.max_brightness = 0xff; bl = devm_backlight_device_register(&i2c->dev, "7inch-touchscreen-panel-bl", &i2c->dev, regmap, &attiny_bl, &props); if (IS_ERR(bl)) return PTR_ERR(bl); bl->props.brightness = 0xff; return 0; } static const struct of_device_id attiny_dt_ids[] = { { .compatible = "raspberrypi,7inch-touchscreen-panel-regulator" }, {}, }; MODULE_DEVICE_TABLE(of, attiny_dt_ids); static struct i2c_driver attiny_regulator_driver = { .driver = { .name = "rpi_touchscreen_attiny", .of_match_table = of_match_ptr(attiny_dt_ids), }, .probe = attiny_i2c_probe, }; module_i2c_driver(attiny_regulator_driver); MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); MODULE_DESCRIPTION("Regulator device driver for Raspberry Pi 7-inch touchscreen"); MODULE_LICENSE("GPL v2"); |