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