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 */ 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 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 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 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