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