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