blob: 46aacca2b7dc8e610373874363a83ccbe9cd11e3 [file] [log] [blame]
Simon Glassbcd5dff2015-07-02 18:16:09 -06001/*
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
15DECLARE_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 */
36static 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
60static 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
68static 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
Simon Glasse160f7d2017-01-17 16:52:55 -070089 params = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "parade,regs",
90 &len);
Simon Glassbcd5dff2015-07-02 18:16:09 -060091 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
109static 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
118struct video_bridge_ops ps8622_ops = {
119 .attach = ps8622_attach,
120 .set_backlight = ps8622_set_backlight,
121};
122
123static const struct udevice_id ps8622_ids[] = {
124 { .compatible = "parade,ps8622", },
125 { .compatible = "parade,ps8625", },
126 { }
127};
128
129U_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};