xref: /openbmc/u-boot/drivers/video/bridge/ps862x.c (revision c74dda8b)
1 /*
2  * Copyright (C) 2015 Google, Inc
3  * Written by Simon Glass <sjg@chromium.org>
4  *
5  * SPDX-License-Identifier:	GPL-2.0+
6  */
7 
8 #include <common.h>
9 #include <dm.h>
10 #include <errno.h>
11 #include <i2c.h>
12 #include <video_bridge.h>
13 #include <power/regulator.h>
14 
15 DECLARE_GLOBAL_DATA_PTR;
16 
17 /*
18  * Initialisation of the chip is a process of writing certain values into
19  * certain registers over i2c bus. The chip in fact responds to a range of
20  * addresses on the i2c bus, so for each written value three parameters are
21  * required: i2c address, register address and the actual value.
22  *
23  * The base address is derived from the device tree, but oddly the chip
24  * responds on several addresses with different register sets for each.
25  */
26 
27 /**
28  * ps8622_write() Write a PS8622 eDP bridge i2c register
29  *
30  * @param dev		I2C device
31  * @param addr_off	offset from the i2c base address for ps8622
32  * @param reg_addr	register address to write
33  * @param value		value to be written
34  * @return 0 on success, non-0 on failure
35  */
36 static int ps8622_write(struct udevice *dev, unsigned addr_off,
37 			unsigned char reg_addr, unsigned char value)
38 {
39 	struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
40 	uint8_t buf[2];
41 	struct i2c_msg msg;
42 	int ret;
43 
44 	msg.addr = chip->chip_addr + addr_off;
45 	msg.flags = 0;
46 	buf[0] = reg_addr;
47 	buf[1] = value;
48 	msg.buf = buf;
49 	msg.len = 2;
50 	ret = dm_i2c_xfer(dev, &msg, 1);
51 	if (ret) {
52 		debug("%s: write failed, reg=%#x, value=%#x, ret=%d\n",
53 		      __func__, reg_addr, value, ret);
54 		return ret;
55 	}
56 
57 	return 0;
58 }
59 
60 static int ps8622_set_backlight(struct udevice *dev, int percent)
61 {
62 	int level = percent * 255 / 100;
63 
64 	debug("%s: level=%d\n", __func__, level);
65 	return ps8622_write(dev, 0x01, 0xa7, level);
66 }
67 
68 static int ps8622_attach(struct udevice *dev)
69 {
70 	const uint8_t *params;
71 	struct udevice *reg;
72 	int ret, i, len;
73 
74 	debug("%s: %s\n", __func__, dev->name);
75 	/* set the LDO providing the 1.2V rail to the Parade bridge */
76 	ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
77 					   "power-supply", &reg);
78 	if (!ret) {
79 		ret = regulator_autoset(reg);
80 	} else if (ret != -ENOENT) {
81 		debug("%s: Failed to enable power: ret=%d\n", __func__, ret);
82 		return ret;
83 	}
84 
85 	ret = video_bridge_set_active(dev, true);
86 	if (ret)
87 		return ret;
88 
89 	params = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "parade,regs",
90 			     &len);
91 	if (!params || len % 3) {
92 		debug("%s: missing/invalid params=%p, len=%x\n", __func__,
93 		      params, len);
94 		return -EINVAL;
95 	}
96 
97 	/* need to wait 20ms after power on before doing I2C writes */
98 	mdelay(20);
99 	for (i = 0; i < len; i += 3) {
100 		ret = ps8622_write(dev, params[i + 0], params[i + 1],
101 				   params[i + 2]);
102 		if (ret)
103 			return ret;
104 	}
105 
106 	return 0;
107 }
108 
109 static int ps8622_probe(struct udevice *dev)
110 {
111 	debug("%s\n", __func__);
112 	if (device_get_uclass_id(dev->parent) != UCLASS_I2C)
113 		return -EPROTONOSUPPORT;
114 
115 	return 0;
116 }
117 
118 struct video_bridge_ops ps8622_ops = {
119 	.attach = ps8622_attach,
120 	.set_backlight = ps8622_set_backlight,
121 };
122 
123 static const struct udevice_id ps8622_ids[] = {
124 	{ .compatible = "parade,ps8622", },
125 	{ .compatible = "parade,ps8625", },
126 	{ }
127 };
128 
129 U_BOOT_DRIVER(parade_ps8622) = {
130 	.name	= "parade_ps8622",
131 	.id	= UCLASS_VIDEO_BRIDGE,
132 	.of_match = ps8622_ids,
133 	.probe	= ps8622_probe,
134 	.ops	= &ps8622_ops,
135 };
136