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_SPWG: /* 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_RGB888_1X7X4_SPWG: /* vesa-24 */ 54 format = LVDS_8BIT_MODE_FORMAT_1; 55 break; 56 default: 57 format = LVDS_8BIT_MODE_FORMAT_3; 58 break; 59 } 60 61 value = LVDS_CON_CHA0TTL_DISABLE | LVDS_CON_CHA1TTL_DISABLE | 62 LVDS_CON_CHA0_POWER_UP | LVDS_CON_CBG_POWER_UP | 63 LVDS_CON_PLL_POWER_UP | LVDS_CON_SELECT(format); 64 65 if (priv->dual_channel) 66 value |= LVDS_CON_CHA1_POWER_UP | LVDS_DCLK_INV | 67 LVDS_CON_CHASEL_DOUBLE_CHANNEL; 68 else 69 value |= LVDS_CON_CHA1_POWER_DOWN | 70 LVDS_CON_CHASEL_SINGLE_CHANNEL; 71 72 lvds_write(priv, RK618_LVDS_CON, value); 73 } 74 75 static void rk618_lvds_bridge_disable(struct rockchip_bridge *bridge) 76 { 77 struct rk618_lvds_priv *priv = dev_get_priv(bridge->dev); 78 79 lvds_write(priv, RK618_LVDS_CON, 80 LVDS_CON_CHA0_POWER_DOWN | LVDS_CON_CHA1_POWER_DOWN | 81 LVDS_CON_CBG_POWER_DOWN | LVDS_CON_PLL_POWER_DOWN); 82 } 83 84 static const struct rockchip_bridge_funcs rk618_lvds_bridge_funcs = { 85 .enable = rk618_lvds_bridge_enable, 86 .disable = rk618_lvds_bridge_disable, 87 }; 88 89 static int rk618_lvds_probe(struct udevice *dev) 90 { 91 struct rk618_lvds_priv *priv = dev_get_priv(dev); 92 struct rockchip_bridge *bridge = 93 (struct rockchip_bridge *)dev_get_driver_data(dev); 94 int ret; 95 96 priv->dev = dev; 97 priv->parent = dev_get_priv(dev->parent); 98 priv->dual_channel = dev_read_bool(dev, "dual-channel"); 99 100 ret = device_probe(dev->parent); 101 if (ret) 102 return ret; 103 104 bridge->dev = dev; 105 106 return 0; 107 } 108 109 static struct rockchip_bridge rk618_lvds_driver_data = { 110 .funcs = &rk618_lvds_bridge_funcs, 111 }; 112 113 static const struct udevice_id rk618_lvds_ids[] = { 114 { 115 .compatible = "rockchip,rk618-lvds", 116 .data = (ulong)&rk618_lvds_driver_data, 117 }, 118 { } 119 }; 120 121 U_BOOT_DRIVER(rk618_lvds) = { 122 .name = "rk618_lvds", 123 .id = UCLASS_VIDEO_BRIDGE, 124 .of_match = rk618_lvds_ids, 125 .probe = rk618_lvds_probe, 126 .priv_auto_alloc_size = sizeof(struct rk618_lvds_priv), 127 }; 128