138573472SMarek Vasut // SPDX-License-Identifier: GPL-2.0 238573472SMarek Vasut /* 338573472SMarek Vasut * Copyright (C) 2020 Marek Vasut <marex@denx.de> 438573472SMarek Vasut * 538573472SMarek Vasut * Based on rpi_touchscreen.c by Eric Anholt <eric@anholt.net> 638573472SMarek Vasut */ 738573472SMarek Vasut 838573472SMarek Vasut #include <linux/backlight.h> 938573472SMarek Vasut #include <linux/err.h> 1038573472SMarek Vasut #include <linux/gpio.h> 1138573472SMarek Vasut #include <linux/i2c.h> 1238573472SMarek Vasut #include <linux/init.h> 1338573472SMarek Vasut #include <linux/interrupt.h> 1438573472SMarek Vasut #include <linux/module.h> 1538573472SMarek Vasut #include <linux/regmap.h> 1638573472SMarek Vasut #include <linux/regulator/driver.h> 1738573472SMarek Vasut #include <linux/regulator/machine.h> 1838573472SMarek Vasut #include <linux/regulator/of_regulator.h> 1938573472SMarek Vasut #include <linux/slab.h> 2038573472SMarek Vasut 2138573472SMarek Vasut /* I2C registers of the Atmel microcontroller. */ 2238573472SMarek Vasut #define REG_ID 0x80 2338573472SMarek Vasut #define REG_PORTA 0x81 2438573472SMarek Vasut #define REG_PORTA_HF BIT(2) 2538573472SMarek Vasut #define REG_PORTA_VF BIT(3) 2638573472SMarek Vasut #define REG_PORTB 0x82 2738573472SMarek Vasut #define REG_POWERON 0x85 2838573472SMarek Vasut #define REG_PWM 0x86 2938573472SMarek Vasut 30*00440bcdSDave Stevenson struct attiny_lcd { 31*00440bcdSDave Stevenson /* lock to serialise overall accesses to the Atmel */ 32*00440bcdSDave Stevenson struct mutex lock; 33*00440bcdSDave Stevenson struct regmap *regmap; 34*00440bcdSDave Stevenson }; 35*00440bcdSDave Stevenson 3638573472SMarek Vasut static const struct regmap_config attiny_regmap_config = { 3738573472SMarek Vasut .reg_bits = 8, 3838573472SMarek Vasut .val_bits = 8, 39*00440bcdSDave Stevenson .disable_locking = 1, 4038573472SMarek Vasut .max_register = REG_PWM, 4138573472SMarek Vasut .cache_type = REGCACHE_NONE, 4238573472SMarek Vasut }; 4338573472SMarek Vasut 4438573472SMarek Vasut static int attiny_lcd_power_enable(struct regulator_dev *rdev) 4538573472SMarek Vasut { 46*00440bcdSDave Stevenson struct mutex *lock = rdev_get_drvdata(rdev); 4738573472SMarek Vasut unsigned int data; 485665eee7SDave Stevenson int ret, i; 4938573472SMarek Vasut 50*00440bcdSDave Stevenson mutex_lock(lock); 51*00440bcdSDave Stevenson 5238573472SMarek Vasut regmap_write(rdev->regmap, REG_POWERON, 1); 535665eee7SDave Stevenson msleep(80); 545665eee7SDave Stevenson 5538573472SMarek Vasut /* Wait for nPWRDWN to go low to indicate poweron is done. */ 565665eee7SDave Stevenson for (i = 0; i < 20; i++) { 575665eee7SDave Stevenson ret = regmap_read(rdev->regmap, REG_PORTB, &data); 585665eee7SDave Stevenson if (!ret) { 595665eee7SDave Stevenson if (data & BIT(0)) 605665eee7SDave Stevenson break; 615665eee7SDave Stevenson } 625665eee7SDave Stevenson usleep_range(10000, 12000); 635665eee7SDave Stevenson } 645665eee7SDave Stevenson usleep_range(10000, 12000); 655665eee7SDave Stevenson 665665eee7SDave Stevenson if (ret) 675665eee7SDave Stevenson pr_err("%s: regmap_read_poll_timeout failed %d\n", __func__, ret); 6838573472SMarek Vasut 6938573472SMarek Vasut /* Default to the same orientation as the closed source 7038573472SMarek Vasut * firmware used for the panel. Runtime rotation 7138573472SMarek Vasut * configuration will be supported using VC4's plane 7238573472SMarek Vasut * orientation bits. 7338573472SMarek Vasut */ 7438573472SMarek Vasut regmap_write(rdev->regmap, REG_PORTA, BIT(2)); 7538573472SMarek Vasut 76*00440bcdSDave Stevenson mutex_unlock(lock); 77*00440bcdSDave Stevenson 7838573472SMarek Vasut return 0; 7938573472SMarek Vasut } 8038573472SMarek Vasut 8138573472SMarek Vasut static int attiny_lcd_power_disable(struct regulator_dev *rdev) 8238573472SMarek Vasut { 83*00440bcdSDave Stevenson struct mutex *lock = rdev_get_drvdata(rdev); 84*00440bcdSDave Stevenson 85*00440bcdSDave Stevenson mutex_lock(lock); 86*00440bcdSDave Stevenson 8738573472SMarek Vasut regmap_write(rdev->regmap, REG_PWM, 0); 8838573472SMarek Vasut regmap_write(rdev->regmap, REG_POWERON, 0); 895665eee7SDave Stevenson msleep(30); 90*00440bcdSDave Stevenson 91*00440bcdSDave Stevenson mutex_unlock(lock); 92*00440bcdSDave Stevenson 9338573472SMarek Vasut return 0; 9438573472SMarek Vasut } 9538573472SMarek Vasut 9638573472SMarek Vasut static int attiny_lcd_power_is_enabled(struct regulator_dev *rdev) 9738573472SMarek Vasut { 98*00440bcdSDave Stevenson struct mutex *lock = rdev_get_drvdata(rdev); 9938573472SMarek Vasut unsigned int data; 1005665eee7SDave Stevenson int ret, i; 10138573472SMarek Vasut 102*00440bcdSDave Stevenson mutex_lock(lock); 103*00440bcdSDave Stevenson 1045665eee7SDave Stevenson for (i = 0; i < 10; i++) { 10538573472SMarek Vasut ret = regmap_read(rdev->regmap, REG_POWERON, &data); 1065665eee7SDave Stevenson if (!ret) 1075665eee7SDave Stevenson break; 1085665eee7SDave Stevenson usleep_range(10000, 12000); 1095665eee7SDave Stevenson } 110*00440bcdSDave Stevenson if (ret < 0) { 111*00440bcdSDave Stevenson mutex_unlock(lock); 11238573472SMarek Vasut return ret; 113*00440bcdSDave Stevenson } 11438573472SMarek Vasut 115*00440bcdSDave Stevenson if (!(data & BIT(0))) { 116*00440bcdSDave Stevenson mutex_unlock(lock); 11738573472SMarek Vasut return 0; 118*00440bcdSDave Stevenson } 11938573472SMarek Vasut 1205665eee7SDave Stevenson for (i = 0; i < 10; i++) { 12138573472SMarek Vasut ret = regmap_read(rdev->regmap, REG_PORTB, &data); 1225665eee7SDave Stevenson if (!ret) 1235665eee7SDave Stevenson break; 1245665eee7SDave Stevenson usleep_range(10000, 12000); 1255665eee7SDave Stevenson } 1265665eee7SDave Stevenson 127*00440bcdSDave Stevenson mutex_unlock(lock); 128*00440bcdSDave Stevenson 12938573472SMarek Vasut if (ret < 0) 13038573472SMarek Vasut return ret; 13138573472SMarek Vasut 13238573472SMarek Vasut return data & BIT(0); 13338573472SMarek Vasut } 13438573472SMarek Vasut 13538573472SMarek Vasut static const struct regulator_init_data attiny_regulator_default = { 13638573472SMarek Vasut .constraints = { 13738573472SMarek Vasut .valid_ops_mask = REGULATOR_CHANGE_STATUS, 13838573472SMarek Vasut }, 13938573472SMarek Vasut }; 14038573472SMarek Vasut 14138573472SMarek Vasut static const struct regulator_ops attiny_regulator_ops = { 14238573472SMarek Vasut .enable = attiny_lcd_power_enable, 14338573472SMarek Vasut .disable = attiny_lcd_power_disable, 14438573472SMarek Vasut .is_enabled = attiny_lcd_power_is_enabled, 14538573472SMarek Vasut }; 14638573472SMarek Vasut 14738573472SMarek Vasut static const struct regulator_desc attiny_regulator = { 14838573472SMarek Vasut .name = "tc358762-power", 14938573472SMarek Vasut .ops = &attiny_regulator_ops, 15038573472SMarek Vasut .type = REGULATOR_VOLTAGE, 15138573472SMarek Vasut .owner = THIS_MODULE, 15238573472SMarek Vasut }; 15338573472SMarek Vasut 15438573472SMarek Vasut static int attiny_update_status(struct backlight_device *bl) 15538573472SMarek Vasut { 156*00440bcdSDave Stevenson struct attiny_lcd *state = bl_get_data(bl); 157*00440bcdSDave Stevenson struct regmap *regmap = state->regmap; 15838573472SMarek Vasut int brightness = bl->props.brightness; 1595665eee7SDave Stevenson int ret, i; 16038573472SMarek Vasut 161*00440bcdSDave Stevenson mutex_lock(&state->lock); 162*00440bcdSDave Stevenson 16338573472SMarek Vasut if (bl->props.power != FB_BLANK_UNBLANK || 16438573472SMarek Vasut bl->props.fb_blank != FB_BLANK_UNBLANK) 16538573472SMarek Vasut brightness = 0; 16638573472SMarek Vasut 1675665eee7SDave Stevenson for (i = 0; i < 10; i++) { 1685665eee7SDave Stevenson ret = regmap_write(regmap, REG_PWM, brightness); 1695665eee7SDave Stevenson if (!ret) 1705665eee7SDave Stevenson break; 1715665eee7SDave Stevenson } 1725665eee7SDave Stevenson 173*00440bcdSDave Stevenson mutex_unlock(&state->lock); 174*00440bcdSDave Stevenson 1755665eee7SDave Stevenson return ret; 17638573472SMarek Vasut } 17738573472SMarek Vasut 17838573472SMarek Vasut static int attiny_get_brightness(struct backlight_device *bl) 17938573472SMarek Vasut { 180*00440bcdSDave Stevenson struct attiny_lcd *state = bl_get_data(bl); 181*00440bcdSDave Stevenson struct regmap *regmap = state->regmap; 1825665eee7SDave Stevenson int ret, brightness, i; 18338573472SMarek Vasut 184*00440bcdSDave Stevenson mutex_lock(&state->lock); 185*00440bcdSDave Stevenson 1865665eee7SDave Stevenson for (i = 0; i < 10; i++) { 18738573472SMarek Vasut ret = regmap_read(regmap, REG_PWM, &brightness); 1885665eee7SDave Stevenson if (!ret) 1895665eee7SDave Stevenson break; 1905665eee7SDave Stevenson } 1915665eee7SDave Stevenson 192*00440bcdSDave Stevenson mutex_unlock(&state->lock); 193*00440bcdSDave Stevenson 19438573472SMarek Vasut if (ret) 19538573472SMarek Vasut return ret; 19638573472SMarek Vasut 19738573472SMarek Vasut return brightness; 19838573472SMarek Vasut } 19938573472SMarek Vasut 20038573472SMarek Vasut static const struct backlight_ops attiny_bl = { 20138573472SMarek Vasut .update_status = attiny_update_status, 20238573472SMarek Vasut .get_brightness = attiny_get_brightness, 20338573472SMarek Vasut }; 20438573472SMarek Vasut 20538573472SMarek Vasut /* 20638573472SMarek Vasut * I2C driver interface functions 20738573472SMarek Vasut */ 20838573472SMarek Vasut static int attiny_i2c_probe(struct i2c_client *i2c, 20938573472SMarek Vasut const struct i2c_device_id *id) 21038573472SMarek Vasut { 21138573472SMarek Vasut struct backlight_properties props = { }; 21238573472SMarek Vasut struct regulator_config config = { }; 21338573472SMarek Vasut struct backlight_device *bl; 21438573472SMarek Vasut struct regulator_dev *rdev; 215*00440bcdSDave Stevenson struct attiny_lcd *state; 21638573472SMarek Vasut struct regmap *regmap; 21738573472SMarek Vasut unsigned int data; 21838573472SMarek Vasut int ret; 21938573472SMarek Vasut 220*00440bcdSDave Stevenson state = devm_kzalloc(&i2c->dev, sizeof(*state), GFP_KERNEL); 221*00440bcdSDave Stevenson if (!state) 222*00440bcdSDave Stevenson return -ENOMEM; 223*00440bcdSDave Stevenson 224*00440bcdSDave Stevenson mutex_init(&state->lock); 225*00440bcdSDave Stevenson i2c_set_clientdata(i2c, state); 226*00440bcdSDave Stevenson 22738573472SMarek Vasut regmap = devm_regmap_init_i2c(i2c, &attiny_regmap_config); 22838573472SMarek Vasut if (IS_ERR(regmap)) { 22938573472SMarek Vasut ret = PTR_ERR(regmap); 23038573472SMarek Vasut dev_err(&i2c->dev, "Failed to allocate register map: %d\n", 23138573472SMarek Vasut ret); 232*00440bcdSDave Stevenson goto error; 23338573472SMarek Vasut } 23438573472SMarek Vasut 23538573472SMarek Vasut ret = regmap_read(regmap, REG_ID, &data); 23638573472SMarek Vasut if (ret < 0) { 23738573472SMarek Vasut dev_err(&i2c->dev, "Failed to read REG_ID reg: %d\n", ret); 238*00440bcdSDave Stevenson goto error; 23938573472SMarek Vasut } 24038573472SMarek Vasut 24138573472SMarek Vasut switch (data) { 24238573472SMarek Vasut case 0xde: /* ver 1 */ 24338573472SMarek Vasut case 0xc3: /* ver 2 */ 24438573472SMarek Vasut break; 24538573472SMarek Vasut default: 24638573472SMarek Vasut dev_err(&i2c->dev, "Unknown Atmel firmware revision: 0x%02x\n", data); 247*00440bcdSDave Stevenson ret = -ENODEV; 248*00440bcdSDave Stevenson goto error; 24938573472SMarek Vasut } 25038573472SMarek Vasut 25138573472SMarek Vasut regmap_write(regmap, REG_POWERON, 0); 2525665eee7SDave Stevenson msleep(30); 25338573472SMarek Vasut 25438573472SMarek Vasut config.dev = &i2c->dev; 25538573472SMarek Vasut config.regmap = regmap; 25638573472SMarek Vasut config.of_node = i2c->dev.of_node; 25738573472SMarek Vasut config.init_data = &attiny_regulator_default; 258*00440bcdSDave Stevenson config.driver_data = &state->lock; 25938573472SMarek Vasut 26038573472SMarek Vasut rdev = devm_regulator_register(&i2c->dev, &attiny_regulator, &config); 26138573472SMarek Vasut if (IS_ERR(rdev)) { 26238573472SMarek Vasut dev_err(&i2c->dev, "Failed to register ATTINY regulator\n"); 263*00440bcdSDave Stevenson ret = PTR_ERR(rdev); 264*00440bcdSDave Stevenson goto error; 26538573472SMarek Vasut } 26638573472SMarek Vasut 26738573472SMarek Vasut props.type = BACKLIGHT_RAW; 26838573472SMarek Vasut props.max_brightness = 0xff; 269*00440bcdSDave Stevenson 270*00440bcdSDave Stevenson state->regmap = regmap; 271*00440bcdSDave Stevenson 2727291e7d6SDave Stevenson bl = devm_backlight_device_register(&i2c->dev, dev_name(&i2c->dev), 273*00440bcdSDave Stevenson &i2c->dev, state, &attiny_bl, 27438573472SMarek Vasut &props); 275*00440bcdSDave Stevenson if (IS_ERR(bl)) { 276*00440bcdSDave Stevenson ret = PTR_ERR(bl); 277*00440bcdSDave Stevenson goto error; 278*00440bcdSDave Stevenson } 27938573472SMarek Vasut 28038573472SMarek Vasut bl->props.brightness = 0xff; 28138573472SMarek Vasut 28238573472SMarek Vasut return 0; 283*00440bcdSDave Stevenson 284*00440bcdSDave Stevenson error: 285*00440bcdSDave Stevenson mutex_destroy(&state->lock); 286*00440bcdSDave Stevenson 287*00440bcdSDave Stevenson return ret; 288*00440bcdSDave Stevenson } 289*00440bcdSDave Stevenson 290*00440bcdSDave Stevenson static int attiny_i2c_remove(struct i2c_client *client) 291*00440bcdSDave Stevenson { 292*00440bcdSDave Stevenson struct attiny_lcd *state = i2c_get_clientdata(client); 293*00440bcdSDave Stevenson 294*00440bcdSDave Stevenson mutex_destroy(&state->lock); 295*00440bcdSDave Stevenson 296*00440bcdSDave Stevenson return 0; 29738573472SMarek Vasut } 29838573472SMarek Vasut 29938573472SMarek Vasut static const struct of_device_id attiny_dt_ids[] = { 30038573472SMarek Vasut { .compatible = "raspberrypi,7inch-touchscreen-panel-regulator" }, 30138573472SMarek Vasut {}, 30238573472SMarek Vasut }; 30338573472SMarek Vasut MODULE_DEVICE_TABLE(of, attiny_dt_ids); 30438573472SMarek Vasut 30538573472SMarek Vasut static struct i2c_driver attiny_regulator_driver = { 30638573472SMarek Vasut .driver = { 30738573472SMarek Vasut .name = "rpi_touchscreen_attiny", 30838573472SMarek Vasut .of_match_table = of_match_ptr(attiny_dt_ids), 30938573472SMarek Vasut }, 31038573472SMarek Vasut .probe = attiny_i2c_probe, 311*00440bcdSDave Stevenson .remove = attiny_i2c_remove, 31238573472SMarek Vasut }; 31338573472SMarek Vasut 31438573472SMarek Vasut module_i2c_driver(attiny_regulator_driver); 31538573472SMarek Vasut 31638573472SMarek Vasut MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); 31738573472SMarek Vasut MODULE_DESCRIPTION("Regulator device driver for Raspberry Pi 7-inch touchscreen"); 31838573472SMarek Vasut MODULE_LICENSE("GPL v2"); 319