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