1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * (C) Copyright 2008-2018 Fuzhou Rockchip Electronics Co., Ltd 4 */ 5 6 #include <common.h> 7 #include <dm.h> 8 #include <dm/device-internal.h> 9 #include <errno.h> 10 #include <i2c.h> 11 #include <video_bridge.h> 12 #include <linux/media-bus-format.h> 13 14 #include "rockchip_display.h" 15 #include "rockchip_bridge.h" 16 #include "rockchip_panel.h" 17 #include "rockchip_connector.h" 18 19 #include "rk618.h" 20 21 enum { 22 LVDS_8BIT_MODE_FORMAT_1, 23 LVDS_8BIT_MODE_FORMAT_2, 24 LVDS_8BIT_MODE_FORMAT_3, 25 LVDS_6BIT_MODE, 26 }; 27 28 struct rk618_lvds_priv { 29 struct udevice *dev; 30 struct rk618 *parent; 31 bool dual_channel; 32 }; 33 34 static int lvds_write(struct rk618_lvds_priv *priv, u16 reg, u32 val) 35 { 36 return rk618_i2c_write(priv->parent, reg, val); 37 } 38 39 static void rk618_lvds_bridge_enable(struct rockchip_bridge *bridge) 40 { 41 struct rk618_lvds_priv *priv = dev_get_priv(bridge->dev); 42 struct rockchip_panel *panel = bridge->conn->panel; 43 u32 value, format; 44 45 rk618_frc_dclk_invert(priv->parent); 46 47 switch (panel->bus_format) { 48 case MEDIA_BUS_FMT_RGB666_1X7X3_JEIDA: /* jeida-18 */ 49 format = LVDS_6BIT_MODE; 50 break; 51 case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: /* jeida-24 */ 52 format = LVDS_8BIT_MODE_FORMAT_2; 53 break; 54 case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: /* vesa-18 */ 55 format = LVDS_8BIT_MODE_FORMAT_3; 56 break; 57 case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: /* vesa-24 */ 58 default: 59 format = LVDS_8BIT_MODE_FORMAT_1; 60 break; 61 } 62 63 value = LVDS_CON_CHA0TTL_DISABLE | LVDS_CON_CHA1TTL_DISABLE | 64 LVDS_CON_CHA0_POWER_UP | LVDS_CON_CBG_POWER_UP | 65 LVDS_CON_PLL_POWER_UP | LVDS_CON_SELECT(format); 66 67 if (priv->dual_channel) 68 value |= LVDS_CON_CHA1_POWER_UP | LVDS_DCLK_INV | 69 LVDS_CON_CHASEL_DOUBLE_CHANNEL; 70 else 71 value |= LVDS_CON_CHA1_POWER_DOWN | 72 LVDS_CON_CHASEL_SINGLE_CHANNEL; 73 74 lvds_write(priv, RK618_LVDS_CON, value); 75 } 76 77 static void rk618_lvds_bridge_disable(struct rockchip_bridge *bridge) 78 { 79 struct rk618_lvds_priv *priv = dev_get_priv(bridge->dev); 80 81 lvds_write(priv, RK618_LVDS_CON, 82 LVDS_CON_CHA0_POWER_DOWN | LVDS_CON_CHA1_POWER_DOWN | 83 LVDS_CON_CBG_POWER_DOWN | LVDS_CON_PLL_POWER_DOWN); 84 } 85 86 static const struct rockchip_bridge_funcs rk618_lvds_bridge_funcs = { 87 .enable = rk618_lvds_bridge_enable, 88 .disable = rk618_lvds_bridge_disable, 89 }; 90 91 static int rk618_lvds_probe(struct udevice *dev) 92 { 93 struct rk618_lvds_priv *priv = dev_get_priv(dev); 94 struct rockchip_bridge *bridge = 95 (struct rockchip_bridge *)dev_get_driver_data(dev); 96 int ret; 97 98 priv->dev = dev; 99 priv->parent = dev_get_priv(dev->parent); 100 priv->dual_channel = dev_read_bool(dev, "dual-channel"); 101 102 ret = device_probe(dev->parent); 103 if (ret) 104 return ret; 105 106 bridge->dev = dev; 107 108 return 0; 109 } 110 111 static struct rockchip_bridge rk618_lvds_driver_data = { 112 .funcs = &rk618_lvds_bridge_funcs, 113 }; 114 115 static const struct udevice_id rk618_lvds_ids[] = { 116 { 117 .compatible = "rockchip,rk618-lvds", 118 .data = (ulong)&rk618_lvds_driver_data, 119 }, 120 { } 121 }; 122 123 U_BOOT_DRIVER(rk618_lvds) = { 124 .name = "rk618_lvds", 125 .id = UCLASS_VIDEO_BRIDGE, 126 .of_match = rk618_lvds_ids, 127 .probe = rk618_lvds_probe, 128 .priv_auto_alloc_size = sizeof(struct rk618_lvds_priv), 129 }; 130