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 
3000440bcdSDave Stevenson struct attiny_lcd {
3100440bcdSDave Stevenson 	/* lock to serialise overall accesses to the Atmel */
3200440bcdSDave Stevenson 	struct mutex	lock;
3300440bcdSDave Stevenson 	struct regmap	*regmap;
3400440bcdSDave Stevenson };
3500440bcdSDave Stevenson 
3638573472SMarek Vasut static const struct regmap_config attiny_regmap_config = {
3738573472SMarek Vasut 	.reg_bits = 8,
3838573472SMarek Vasut 	.val_bits = 8,
3900440bcdSDave 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 {
4600440bcdSDave Stevenson 	struct mutex *lock = rdev_get_drvdata(rdev);
4738573472SMarek Vasut 	unsigned int data;
485665eee7SDave Stevenson 	int ret, i;
4938573472SMarek Vasut 
5000440bcdSDave Stevenson 	mutex_lock(lock);
5100440bcdSDave 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 
7600440bcdSDave Stevenson 	mutex_unlock(lock);
7700440bcdSDave Stevenson 
7838573472SMarek Vasut 	return 0;
7938573472SMarek Vasut }
8038573472SMarek Vasut 
8138573472SMarek Vasut static int attiny_lcd_power_disable(struct regulator_dev *rdev)
8238573472SMarek Vasut {
8300440bcdSDave Stevenson 	struct mutex *lock = rdev_get_drvdata(rdev);
8400440bcdSDave Stevenson 
8500440bcdSDave Stevenson 	mutex_lock(lock);
8600440bcdSDave Stevenson 
8738573472SMarek Vasut 	regmap_write(rdev->regmap, REG_PWM, 0);
8838573472SMarek Vasut 	regmap_write(rdev->regmap, REG_POWERON, 0);
895665eee7SDave Stevenson 	msleep(30);
9000440bcdSDave Stevenson 
9100440bcdSDave Stevenson 	mutex_unlock(lock);
9200440bcdSDave Stevenson 
9338573472SMarek Vasut 	return 0;
9438573472SMarek Vasut }
9538573472SMarek Vasut 
9638573472SMarek Vasut static int attiny_lcd_power_is_enabled(struct regulator_dev *rdev)
9738573472SMarek Vasut {
9800440bcdSDave Stevenson 	struct mutex *lock = rdev_get_drvdata(rdev);
9938573472SMarek Vasut 	unsigned int data;
1005665eee7SDave Stevenson 	int ret, i;
10138573472SMarek Vasut 
10200440bcdSDave Stevenson 	mutex_lock(lock);
10300440bcdSDave 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 	}
11000440bcdSDave Stevenson 	if (ret < 0) {
11100440bcdSDave Stevenson 		mutex_unlock(lock);
11238573472SMarek Vasut 		return ret;
11300440bcdSDave Stevenson 	}
11438573472SMarek Vasut 
11500440bcdSDave Stevenson 	if (!(data & BIT(0))) {
11600440bcdSDave Stevenson 		mutex_unlock(lock);
11738573472SMarek Vasut 		return 0;
11800440bcdSDave 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 
12700440bcdSDave Stevenson 	mutex_unlock(lock);
12800440bcdSDave 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 {
15600440bcdSDave Stevenson 	struct attiny_lcd *state = bl_get_data(bl);
15700440bcdSDave Stevenson 	struct regmap *regmap = state->regmap;
15838573472SMarek Vasut 	int brightness = bl->props.brightness;
1595665eee7SDave Stevenson 	int ret, i;
16038573472SMarek Vasut 
16100440bcdSDave Stevenson 	mutex_lock(&state->lock);
16200440bcdSDave 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 
17300440bcdSDave Stevenson 	mutex_unlock(&state->lock);
17400440bcdSDave Stevenson 
1755665eee7SDave Stevenson 	return ret;
17638573472SMarek Vasut }
17738573472SMarek Vasut 
17838573472SMarek Vasut static int attiny_get_brightness(struct backlight_device *bl)
17938573472SMarek Vasut {
18000440bcdSDave Stevenson 	struct attiny_lcd *state = bl_get_data(bl);
18100440bcdSDave Stevenson 	struct regmap *regmap = state->regmap;
1825665eee7SDave Stevenson 	int ret, brightness, i;
18338573472SMarek Vasut 
18400440bcdSDave Stevenson 	mutex_lock(&state->lock);
18500440bcdSDave 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 
19200440bcdSDave Stevenson 	mutex_unlock(&state->lock);
19300440bcdSDave 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;
21500440bcdSDave Stevenson 	struct attiny_lcd *state;
21638573472SMarek Vasut 	struct regmap *regmap;
21738573472SMarek Vasut 	unsigned int data;
21838573472SMarek Vasut 	int ret;
21938573472SMarek Vasut 
22000440bcdSDave Stevenson 	state = devm_kzalloc(&i2c->dev, sizeof(*state), GFP_KERNEL);
22100440bcdSDave Stevenson 	if (!state)
22200440bcdSDave Stevenson 		return -ENOMEM;
22300440bcdSDave Stevenson 
22400440bcdSDave Stevenson 	mutex_init(&state->lock);
22500440bcdSDave Stevenson 	i2c_set_clientdata(i2c, state);
22600440bcdSDave 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);
23200440bcdSDave 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);
23800440bcdSDave 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);
24700440bcdSDave Stevenson 		ret = -ENODEV;
24800440bcdSDave Stevenson 		goto error;
24938573472SMarek Vasut 	}
25038573472SMarek Vasut 
25138573472SMarek Vasut 	regmap_write(regmap, REG_POWERON, 0);
2525665eee7SDave Stevenson 	msleep(30);
253*89339a2aSDave Stevenson 	regmap_write(regmap, REG_PWM, 0);
25438573472SMarek Vasut 
25538573472SMarek Vasut 	config.dev = &i2c->dev;
25638573472SMarek Vasut 	config.regmap = regmap;
25738573472SMarek Vasut 	config.of_node = i2c->dev.of_node;
25838573472SMarek Vasut 	config.init_data = &attiny_regulator_default;
25900440bcdSDave Stevenson 	config.driver_data = &state->lock;
26038573472SMarek Vasut 
26138573472SMarek Vasut 	rdev = devm_regulator_register(&i2c->dev, &attiny_regulator, &config);
26238573472SMarek Vasut 	if (IS_ERR(rdev)) {
26338573472SMarek Vasut 		dev_err(&i2c->dev, "Failed to register ATTINY regulator\n");
26400440bcdSDave Stevenson 		ret = PTR_ERR(rdev);
26500440bcdSDave Stevenson 		goto error;
26638573472SMarek Vasut 	}
26738573472SMarek Vasut 
26838573472SMarek Vasut 	props.type = BACKLIGHT_RAW;
26938573472SMarek Vasut 	props.max_brightness = 0xff;
27000440bcdSDave Stevenson 
27100440bcdSDave Stevenson 	state->regmap = regmap;
27200440bcdSDave Stevenson 
2737291e7d6SDave Stevenson 	bl = devm_backlight_device_register(&i2c->dev, dev_name(&i2c->dev),
27400440bcdSDave Stevenson 					    &i2c->dev, state, &attiny_bl,
27538573472SMarek Vasut 					    &props);
27600440bcdSDave Stevenson 	if (IS_ERR(bl)) {
27700440bcdSDave Stevenson 		ret = PTR_ERR(bl);
27800440bcdSDave Stevenson 		goto error;
27900440bcdSDave Stevenson 	}
28038573472SMarek Vasut 
28138573472SMarek Vasut 	bl->props.brightness = 0xff;
28238573472SMarek Vasut 
28338573472SMarek Vasut 	return 0;
28400440bcdSDave Stevenson 
28500440bcdSDave Stevenson error:
28600440bcdSDave Stevenson 	mutex_destroy(&state->lock);
28700440bcdSDave Stevenson 
28800440bcdSDave Stevenson 	return ret;
28900440bcdSDave Stevenson }
29000440bcdSDave Stevenson 
29100440bcdSDave Stevenson static int attiny_i2c_remove(struct i2c_client *client)
29200440bcdSDave Stevenson {
29300440bcdSDave Stevenson 	struct attiny_lcd *state = i2c_get_clientdata(client);
29400440bcdSDave Stevenson 
29500440bcdSDave Stevenson 	mutex_destroy(&state->lock);
29600440bcdSDave Stevenson 
29700440bcdSDave Stevenson 	return 0;
29838573472SMarek Vasut }
29938573472SMarek Vasut 
30038573472SMarek Vasut static const struct of_device_id attiny_dt_ids[] = {
30138573472SMarek Vasut 	{ .compatible = "raspberrypi,7inch-touchscreen-panel-regulator" },
30238573472SMarek Vasut 	{},
30338573472SMarek Vasut };
30438573472SMarek Vasut MODULE_DEVICE_TABLE(of, attiny_dt_ids);
30538573472SMarek Vasut 
30638573472SMarek Vasut static struct i2c_driver attiny_regulator_driver = {
30738573472SMarek Vasut 	.driver = {
30838573472SMarek Vasut 		.name = "rpi_touchscreen_attiny",
30938573472SMarek Vasut 		.of_match_table = of_match_ptr(attiny_dt_ids),
31038573472SMarek Vasut 	},
31138573472SMarek Vasut 	.probe = attiny_i2c_probe,
31200440bcdSDave Stevenson 	.remove	= attiny_i2c_remove,
31338573472SMarek Vasut };
31438573472SMarek Vasut 
31538573472SMarek Vasut module_i2c_driver(attiny_regulator_driver);
31638573472SMarek Vasut 
31738573472SMarek Vasut MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
31838573472SMarek Vasut MODULE_DESCRIPTION("Regulator device driver for Raspberry Pi 7-inch touchscreen");
31938573472SMarek Vasut MODULE_LICENSE("GPL v2");
320