aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRob Clark <robdclark@gmail.com>2014-08-01 13:08:11 -0400
committerRob Clark <robdclark@gmail.com>2014-09-10 11:19:07 -0400
commit3e87599b68e7929a84a32ab65ad17b79a3f271f6 (patch)
tree073b98e5d2e097be7016f467be3eaaf3c08f2a18
parentd65bd0e431156f156f43946b6efb524694afb685 (diff)
drm/msm/mdp4: add LVDS panel support
LVDS panel support uses the LCDC (parallel) encoder. Unlike with HDMI, there is not a separate LVDS block, so no need to split things into a bridge+connector. Nor is there is anything re-used with mdp5. Note that there can be some regulators shared between HDMI and LVDS (in particular, on apq8064, ext_3v3p), so we should not use the _exclusive() variants of devm_regulator_get(). The drm_panel framework is used for panel-specific driver. Signed-off-by: Rob Clark <robdclark@gmail.com>
-rw-r--r--drivers/gpu/drm/msm/Kconfig1
-rw-r--r--drivers/gpu/drm/msm/Makefile3
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.c4
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c88
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h18
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c506
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c151
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_pll.c172
8 files changed, 933 insertions, 10 deletions
diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index c99c50de3226..9d907c526c94 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -4,6 +4,7 @@ config DRM_MSM
4 depends on DRM 4 depends on DRM
5 depends on ARCH_QCOM || (ARM && COMPILE_TEST) 5 depends on ARCH_QCOM || (ARM && COMPILE_TEST)
6 select DRM_KMS_HELPER 6 select DRM_KMS_HELPER
7 select DRM_PANEL
7 select SHMEM 8 select SHMEM
8 select TMPFS 9 select TMPFS
9 default y 10 default y
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 93ca49c8df44..48f796056887 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -18,6 +18,8 @@ msm-y := \
18 mdp/mdp_kms.o \ 18 mdp/mdp_kms.o \
19 mdp/mdp4/mdp4_crtc.o \ 19 mdp/mdp4/mdp4_crtc.o \
20 mdp/mdp4/mdp4_dtv_encoder.o \ 20 mdp/mdp4/mdp4_dtv_encoder.o \
21 mdp/mdp4/mdp4_lcdc_encoder.o \
22 mdp/mdp4/mdp4_lvds_connector.o \
21 mdp/mdp4/mdp4_irq.o \ 23 mdp/mdp4/mdp4_irq.o \
22 mdp/mdp4/mdp4_kms.o \ 24 mdp/mdp4/mdp4_kms.o \
23 mdp/mdp4/mdp4_plane.o \ 25 mdp/mdp4/mdp4_plane.o \
@@ -39,5 +41,6 @@ msm-y := \
39 msm_ringbuffer.o 41 msm_ringbuffer.o
40 42
41msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o 43msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o
44msm-$(CONFIG_COMMON_CLK) += mdp/mdp4/mdp4_lvds_pll.o
42 45
43obj-$(CONFIG_DRM_MSM) += msm.o 46obj-$(CONFIG_DRM_MSM) += msm.o
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c
index a125a7e32742..2e0eac7cdf89 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.c
@@ -123,7 +123,7 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
123 for (i = 0; i < config->hpd_reg_cnt; i++) { 123 for (i = 0; i < config->hpd_reg_cnt; i++) {
124 struct regulator *reg; 124 struct regulator *reg;
125 125
126 reg = devm_regulator_get_exclusive(&pdev->dev, 126 reg = devm_regulator_get(&pdev->dev,
127 config->hpd_reg_names[i]); 127 config->hpd_reg_names[i]);
128 if (IS_ERR(reg)) { 128 if (IS_ERR(reg)) {
129 ret = PTR_ERR(reg); 129 ret = PTR_ERR(reg);
@@ -139,7 +139,7 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
139 for (i = 0; i < config->pwr_reg_cnt; i++) { 139 for (i = 0; i < config->pwr_reg_cnt; i++) {
140 struct regulator *reg; 140 struct regulator *reg;
141 141
142 reg = devm_regulator_get_exclusive(&pdev->dev, 142 reg = devm_regulator_get(&pdev->dev,
143 config->pwr_reg_names[i]); 143 config->pwr_reg_names[i]);
144 if (IS_ERR(reg)) { 144 if (IS_ERR(reg)) {
145 ret = PTR_ERR(reg); 145 ret = PTR_ERR(reg);
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
index af69079082f7..79d804e61cc4 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
@@ -197,6 +197,28 @@ int mdp4_enable(struct mdp4_kms *mdp4_kms)
197 return 0; 197 return 0;
198} 198}
199 199
200#ifdef CONFIG_OF
201static struct drm_panel *detect_panel(struct drm_device *dev, const char *name)
202{
203 struct device_node *n;
204 struct drm_panel *panel = NULL;
205
206 n = of_parse_phandle(dev->dev->of_node, name, 0);
207 if (n) {
208 panel = of_drm_find_panel(n);
209 if (!panel)
210 panel = ERR_PTR(-EPROBE_DEFER);
211 }
212
213 return panel;
214}
215#else
216static struct drm_panel *detect_panel(struct drm_device *dev, const char *name)
217{
218 // ??? maybe use a module param to specify which panel is attached?
219}
220#endif
221
200static int modeset_init(struct mdp4_kms *mdp4_kms) 222static int modeset_init(struct mdp4_kms *mdp4_kms)
201{ 223{
202 struct drm_device *dev = mdp4_kms->dev; 224 struct drm_device *dev = mdp4_kms->dev;
@@ -204,14 +226,11 @@ static int modeset_init(struct mdp4_kms *mdp4_kms)
204 struct drm_plane *plane; 226 struct drm_plane *plane;
205 struct drm_crtc *crtc; 227 struct drm_crtc *crtc;
206 struct drm_encoder *encoder; 228 struct drm_encoder *encoder;
229 struct drm_connector *connector;
230 struct drm_panel *panel;
207 struct hdmi *hdmi; 231 struct hdmi *hdmi;
208 int ret; 232 int ret;
209 233
210 /*
211 * NOTE: this is a bit simplistic until we add support
212 * for more than just RGB1->DMA_E->DTV->HDMI
213 */
214
215 /* construct non-private planes: */ 234 /* construct non-private planes: */
216 plane = mdp4_plane_init(dev, VG1, false); 235 plane = mdp4_plane_init(dev, VG1, false);
217 if (IS_ERR(plane)) { 236 if (IS_ERR(plane)) {
@@ -229,7 +248,57 @@ static int modeset_init(struct mdp4_kms *mdp4_kms)
229 } 248 }
230 priv->planes[priv->num_planes++] = plane; 249 priv->planes[priv->num_planes++] = plane;
231 250
232 /* the CRTCs get constructed with a private plane: */ 251 /*
252 * Setup the LCDC/LVDS path: RGB2 -> DMA_P -> LCDC -> LVDS:
253 */
254
255 panel = detect_panel(dev, "qcom,lvds-panel");
256 if (IS_ERR(panel)) {
257 ret = PTR_ERR(panel);
258 dev_err(dev->dev, "failed to detect LVDS panel: %d\n", ret);
259 goto fail;
260 }
261
262 plane = mdp4_plane_init(dev, RGB2, true);
263 if (IS_ERR(plane)) {
264 dev_err(dev->dev, "failed to construct plane for RGB2\n");
265 ret = PTR_ERR(plane);
266 goto fail;
267 }
268
269 crtc = mdp4_crtc_init(dev, plane, priv->num_crtcs, 0, DMA_P);
270 if (IS_ERR(crtc)) {
271 dev_err(dev->dev, "failed to construct crtc for DMA_P\n");
272 ret = PTR_ERR(crtc);
273 goto fail;
274 }
275
276 encoder = mdp4_lcdc_encoder_init(dev, panel);
277 if (IS_ERR(encoder)) {
278 dev_err(dev->dev, "failed to construct LCDC encoder\n");
279 ret = PTR_ERR(encoder);
280 goto fail;
281 }
282
283 /* LCDC can be hooked to DMA_P: */
284 encoder->possible_crtcs = 1 << priv->num_crtcs;
285
286 priv->crtcs[priv->num_crtcs++] = crtc;
287 priv->encoders[priv->num_encoders++] = encoder;
288
289 connector = mdp4_lvds_connector_init(dev, panel, encoder);
290 if (IS_ERR(connector)) {
291 ret = PTR_ERR(connector);
292 dev_err(dev->dev, "failed to initialize LVDS connector: %d\n", ret);
293 goto fail;
294 }
295
296 priv->connectors[priv->num_connectors++] = connector;
297
298 /*
299 * Setup DTV/HDMI path: RGB1 -> DMA_E -> DTV -> HDMI:
300 */
301
233 plane = mdp4_plane_init(dev, RGB1, true); 302 plane = mdp4_plane_init(dev, RGB1, true);
234 if (IS_ERR(plane)) { 303 if (IS_ERR(plane)) {
235 dev_err(dev->dev, "failed to construct plane for RGB1\n"); 304 dev_err(dev->dev, "failed to construct plane for RGB1\n");
@@ -243,7 +312,6 @@ static int modeset_init(struct mdp4_kms *mdp4_kms)
243 ret = PTR_ERR(crtc); 312 ret = PTR_ERR(crtc);
244 goto fail; 313 goto fail;
245 } 314 }
246 priv->crtcs[priv->num_crtcs++] = crtc;
247 315
248 encoder = mdp4_dtv_encoder_init(dev); 316 encoder = mdp4_dtv_encoder_init(dev);
249 if (IS_ERR(encoder)) { 317 if (IS_ERR(encoder)) {
@@ -251,7 +319,11 @@ static int modeset_init(struct mdp4_kms *mdp4_kms)
251 ret = PTR_ERR(encoder); 319 ret = PTR_ERR(encoder);
252 goto fail; 320 goto fail;
253 } 321 }
254 encoder->possible_crtcs = 0x1; /* DTV can be hooked to DMA_E */ 322
323 /* DTV can be hooked to DMA_E: */
324 encoder->possible_crtcs = 1 << priv->num_crtcs;
325
326 priv->crtcs[priv->num_crtcs++] = crtc;
255 priv->encoders[priv->num_encoders++] = encoder; 327 priv->encoders[priv->num_encoders++] = encoder;
256 328
257 hdmi = hdmi_init(dev, encoder); 329 hdmi = hdmi_init(dev, encoder);
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
index e74146fe2ae6..9ff6e7ccfe90 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
@@ -23,6 +23,8 @@
23#include "mdp/mdp_kms.h" 23#include "mdp/mdp_kms.h"
24#include "mdp4.xml.h" 24#include "mdp4.xml.h"
25 25
26#include "drm_panel.h"
27
26struct mdp4_kms { 28struct mdp4_kms {
27 struct mdp_kms base; 29 struct mdp_kms base;
28 30
@@ -217,6 +219,22 @@ struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,
217long mdp4_dtv_round_pixclk(struct drm_encoder *encoder, unsigned long rate); 219long mdp4_dtv_round_pixclk(struct drm_encoder *encoder, unsigned long rate);
218struct drm_encoder *mdp4_dtv_encoder_init(struct drm_device *dev); 220struct drm_encoder *mdp4_dtv_encoder_init(struct drm_device *dev);
219 221
222long mdp4_lcdc_round_pixclk(struct drm_encoder *encoder, unsigned long rate);
223struct drm_encoder *mdp4_lcdc_encoder_init(struct drm_device *dev,
224 struct drm_panel *panel);
225
226struct drm_connector *mdp4_lvds_connector_init(struct drm_device *dev,
227 struct drm_panel *panel, struct drm_encoder *encoder);
228
229#ifdef CONFIG_COMMON_CLK
230struct clk *mpd4_lvds_pll_init(struct drm_device *dev);
231#else
232static inline struct clk *mpd4_lvds_pll_init(struct drm_device *dev)
233{
234 return ERR_PTR(-ENODEV);
235}
236#endif
237
220#ifdef CONFIG_MSM_BUS_SCALING 238#ifdef CONFIG_MSM_BUS_SCALING
221static inline int match_dev_name(struct device *dev, void *data) 239static inline int match_dev_name(struct device *dev, void *data)
222{ 240{
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c
new file mode 100644
index 000000000000..41f6436754fc
--- /dev/null
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c
@@ -0,0 +1,506 @@
1/*
2 * Copyright (C) 2014 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 * Author: Vinay Simha <vinaysimha@inforcecomputing.com>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 as published by
8 * the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "mdp4_kms.h"
20
21#include "drm_crtc.h"
22#include "drm_crtc_helper.h"
23
24struct mdp4_lcdc_encoder {
25 struct drm_encoder base;
26 struct drm_panel *panel;
27 struct clk *lcdc_clk;
28 unsigned long int pixclock;
29 struct regulator *regs[3];
30 bool enabled;
31 uint32_t bsc;
32};
33#define to_mdp4_lcdc_encoder(x) container_of(x, struct mdp4_lcdc_encoder, base)
34
35static struct mdp4_kms *get_kms(struct drm_encoder *encoder)
36{
37 struct msm_drm_private *priv = encoder->dev->dev_private;
38 return to_mdp4_kms(to_mdp_kms(priv->kms));
39}
40
41#ifdef CONFIG_MSM_BUS_SCALING
42#include <mach/board.h>
43static void bs_init(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder)
44{
45 struct drm_device *dev = mdp4_lcdc_encoder->base.dev;
46 struct lcdc_platform_data *lcdc_pdata = mdp4_find_pdata("lvds.0");
47
48 if (!lcdc_pdata) {
49 dev_err(dev->dev, "could not find lvds pdata\n");
50 return;
51 }
52
53 if (lcdc_pdata->bus_scale_table) {
54 mdp4_lcdc_encoder->bsc = msm_bus_scale_register_client(
55 lcdc_pdata->bus_scale_table);
56 DBG("lvds : bus scale client: %08x", mdp4_lcdc_encoder->bsc);
57 }
58}
59
60static void bs_fini(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder)
61{
62 if (mdp4_lcdc_encoder->bsc) {
63 msm_bus_scale_unregister_client(mdp4_lcdc_encoder->bsc);
64 mdp4_lcdc_encoder->bsc = 0;
65 }
66}
67
68static void bs_set(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder, int idx)
69{
70 if (mdp4_lcdc_encoder->bsc) {
71 DBG("set bus scaling: %d", idx);
72 msm_bus_scale_client_update_request(mdp4_lcdc_encoder->bsc, idx);
73 }
74}
75#else
76static void bs_init(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder) {}
77static void bs_fini(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder) {}
78static void bs_set(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder, int idx) {}
79#endif
80
81static void mdp4_lcdc_encoder_destroy(struct drm_encoder *encoder)
82{
83 struct mdp4_lcdc_encoder *mdp4_lcdc_encoder =
84 to_mdp4_lcdc_encoder(encoder);
85 bs_fini(mdp4_lcdc_encoder);
86 drm_encoder_cleanup(encoder);
87 kfree(mdp4_lcdc_encoder);
88}
89
90static const struct drm_encoder_funcs mdp4_lcdc_encoder_funcs = {
91 .destroy = mdp4_lcdc_encoder_destroy,
92};
93
94/* this should probably be a helper: */
95struct drm_connector *get_connector(struct drm_encoder *encoder)
96{
97 struct drm_device *dev = encoder->dev;
98 struct drm_connector *connector;
99
100 list_for_each_entry(connector, &dev->mode_config.connector_list, head)
101 if (connector->encoder == encoder)
102 return connector;
103
104 return NULL;
105}
106
107static void setup_phy(struct drm_encoder *encoder)
108{
109 struct drm_device *dev = encoder->dev;
110 struct drm_connector *connector = get_connector(encoder);
111 struct mdp4_kms *mdp4_kms = get_kms(encoder);
112 uint32_t lvds_intf = 0, lvds_phy_cfg0 = 0;
113 int bpp, nchan, swap;
114
115 if (!connector)
116 return;
117
118 bpp = 3 * connector->display_info.bpc;
119
120 if (!bpp)
121 bpp = 18;
122
123 /* TODO, these should come from panel somehow: */
124 nchan = 1;
125 swap = 0;
126
127 switch (bpp) {
128 case 24:
129 mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(0),
130 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x08) |
131 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x05) |
132 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x04) |
133 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x03));
134 mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(0),
135 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x02) |
136 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x01) |
137 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x00));
138 mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(1),
139 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x11) |
140 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x10) |
141 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x0d) |
142 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x0c));
143 mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(1),
144 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x0b) |
145 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x0a) |
146 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x09));
147 mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(2),
148 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x1a) |
149 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x19) |
150 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x18) |
151 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x15));
152 mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(2),
153 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x14) |
154 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x13) |
155 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x12));
156 mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(3),
157 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x1b) |
158 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x17) |
159 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x16) |
160 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x0f));
161 mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(3),
162 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x0e) |
163 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x07) |
164 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x06));
165 if (nchan == 2) {
166 lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE3_EN |
167 MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE2_EN |
168 MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE1_EN |
169 MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE0_EN |
170 MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE3_EN |
171 MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN |
172 MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN |
173 MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN;
174 } else {
175 lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE3_EN |
176 MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN |
177 MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN |
178 MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN;
179 }
180 break;
181
182 case 18:
183 mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(0),
184 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x0a) |
185 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x07) |
186 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x06) |
187 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x05));
188 mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(0),
189 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x04) |
190 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x03) |
191 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x02));
192 mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(1),
193 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x13) |
194 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x12) |
195 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x0f) |
196 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x0e));
197 mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(1),
198 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x0d) |
199 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x0c) |
200 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x0b));
201 mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(2),
202 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x1a) |
203 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x19) |
204 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x18) |
205 MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x17));
206 mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(2),
207 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x16) |
208 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x15) |
209 MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x14));
210 if (nchan == 2) {
211 lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE2_EN |
212 MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE1_EN |
213 MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE0_EN |
214 MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN |
215 MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN |
216 MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN;
217 } else {
218 lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN |
219 MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN |
220 MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN;
221 }
222 lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_RGB_OUT;
223 break;
224
225 default:
226 dev_err(dev->dev, "unknown bpp: %d\n", bpp);
227 return;
228 }
229
230 switch (nchan) {
231 case 1:
232 lvds_phy_cfg0 = MDP4_LVDS_PHY_CFG0_CHANNEL0;
233 lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH1_CLK_LANE_EN |
234 MDP4_LCDC_LVDS_INTF_CTL_MODE_SEL;
235 break;
236 case 2:
237 lvds_phy_cfg0 = MDP4_LVDS_PHY_CFG0_CHANNEL0 |
238 MDP4_LVDS_PHY_CFG0_CHANNEL1;
239 lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH2_CLK_LANE_EN |
240 MDP4_LCDC_LVDS_INTF_CTL_CH1_CLK_LANE_EN;
241 break;
242 default:
243 dev_err(dev->dev, "unknown # of channels: %d\n", nchan);
244 return;
245 }
246
247 if (swap)
248 lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH_SWAP;
249
250 lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_ENABLE;
251
252 mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG0, lvds_phy_cfg0);
253 mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_INTF_CTL, lvds_intf);
254 mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG2, 0x30);
255
256 mb();
257 udelay(1);
258 lvds_phy_cfg0 |= MDP4_LVDS_PHY_CFG0_SERIALIZATION_ENBLE;
259 mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG0, lvds_phy_cfg0);
260}
261
262static void mdp4_lcdc_encoder_dpms(struct drm_encoder *encoder, int mode)
263{
264 struct drm_device *dev = encoder->dev;
265 struct mdp4_lcdc_encoder *mdp4_lcdc_encoder =
266 to_mdp4_lcdc_encoder(encoder);
267 struct mdp4_kms *mdp4_kms = get_kms(encoder);
268 struct drm_panel *panel = mdp4_lcdc_encoder->panel;
269 bool enabled = (mode == DRM_MODE_DPMS_ON);
270 int i, ret;
271
272 DBG("mode=%d", mode);
273
274 if (enabled == mdp4_lcdc_encoder->enabled)
275 return;
276
277 if (enabled) {
278 unsigned long pc = mdp4_lcdc_encoder->pixclock;
279 int ret;
280
281 bs_set(mdp4_lcdc_encoder, 1);
282
283 for (i = 0; i < ARRAY_SIZE(mdp4_lcdc_encoder->regs); i++) {
284 ret = regulator_enable(mdp4_lcdc_encoder->regs[i]);
285 if (ret)
286 dev_err(dev->dev, "failed to enable regulator: %d\n", ret);
287 }
288
289 DBG("setting lcdc_clk=%lu", pc);
290 ret = clk_set_rate(mdp4_lcdc_encoder->lcdc_clk, pc);
291 if (ret)
292 dev_err(dev->dev, "failed to configure lcdc_clk: %d\n", ret);
293 ret = clk_prepare_enable(mdp4_lcdc_encoder->lcdc_clk);
294 if (ret)
295 dev_err(dev->dev, "failed to enable lcdc_clk: %d\n", ret);
296
297 if (panel)
298 drm_panel_enable(panel);
299
300 setup_phy(encoder);
301
302 mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 1);
303 } else {
304 mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 0);
305
306 if (panel)
307 drm_panel_disable(panel);
308
309 /*
310 * Wait for a vsync so we know the ENABLE=0 latched before
311 * the (connector) source of the vsync's gets disabled,
312 * otherwise we end up in a funny state if we re-enable
313 * before the disable latches, which results that some of
314 * the settings changes for the new modeset (like new
315 * scanout buffer) don't latch properly..
316 */
317 mdp_irq_wait(&mdp4_kms->base, MDP4_IRQ_PRIMARY_VSYNC);
318
319 clk_disable_unprepare(mdp4_lcdc_encoder->lcdc_clk);
320
321 for (i = 0; i < ARRAY_SIZE(mdp4_lcdc_encoder->regs); i++) {
322 ret = regulator_disable(mdp4_lcdc_encoder->regs[i]);
323 if (ret)
324 dev_err(dev->dev, "failed to disable regulator: %d\n", ret);
325 }
326
327 bs_set(mdp4_lcdc_encoder, 0);
328 }
329
330 mdp4_lcdc_encoder->enabled = enabled;
331}
332
333static bool mdp4_lcdc_encoder_mode_fixup(struct drm_encoder *encoder,
334 const struct drm_display_mode *mode,
335 struct drm_display_mode *adjusted_mode)
336{
337 return true;
338}
339
340static void mdp4_lcdc_encoder_mode_set(struct drm_encoder *encoder,
341 struct drm_display_mode *mode,
342 struct drm_display_mode *adjusted_mode)
343{
344 struct mdp4_lcdc_encoder *mdp4_lcdc_encoder =
345 to_mdp4_lcdc_encoder(encoder);
346 struct mdp4_kms *mdp4_kms = get_kms(encoder);
347 uint32_t lcdc_hsync_skew, vsync_period, vsync_len, ctrl_pol;
348 uint32_t display_v_start, display_v_end;
349 uint32_t hsync_start_x, hsync_end_x;
350
351 mode = adjusted_mode;
352
353 DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
354 mode->base.id, mode->name,
355 mode->vrefresh, mode->clock,
356 mode->hdisplay, mode->hsync_start,
357 mode->hsync_end, mode->htotal,
358 mode->vdisplay, mode->vsync_start,
359 mode->vsync_end, mode->vtotal,
360 mode->type, mode->flags);
361
362 mdp4_lcdc_encoder->pixclock = mode->clock * 1000;
363
364 DBG("pixclock=%lu", mdp4_lcdc_encoder->pixclock);
365
366 ctrl_pol = 0;
367 if (mode->flags & DRM_MODE_FLAG_NHSYNC)
368 ctrl_pol |= MDP4_LCDC_CTRL_POLARITY_HSYNC_LOW;
369 if (mode->flags & DRM_MODE_FLAG_NVSYNC)
370 ctrl_pol |= MDP4_LCDC_CTRL_POLARITY_VSYNC_LOW;
371 /* probably need to get DATA_EN polarity from panel.. */
372
373 lcdc_hsync_skew = 0; /* get this from panel? */
374
375 hsync_start_x = (mode->htotal - mode->hsync_start);
376 hsync_end_x = mode->htotal - (mode->hsync_start - mode->hdisplay) - 1;
377
378 vsync_period = mode->vtotal * mode->htotal;
379 vsync_len = (mode->vsync_end - mode->vsync_start) * mode->htotal;
380 display_v_start = (mode->vtotal - mode->vsync_start) * mode->htotal + lcdc_hsync_skew;
381 display_v_end = vsync_period - ((mode->vsync_start - mode->vdisplay) * mode->htotal) + lcdc_hsync_skew - 1;
382
383 mdp4_write(mdp4_kms, REG_MDP4_LCDC_HSYNC_CTRL,
384 MDP4_LCDC_HSYNC_CTRL_PULSEW(mode->hsync_end - mode->hsync_start) |
385 MDP4_LCDC_HSYNC_CTRL_PERIOD(mode->htotal));
386 mdp4_write(mdp4_kms, REG_MDP4_LCDC_VSYNC_PERIOD, vsync_period);
387 mdp4_write(mdp4_kms, REG_MDP4_LCDC_VSYNC_LEN, vsync_len);
388 mdp4_write(mdp4_kms, REG_MDP4_LCDC_DISPLAY_HCTRL,
389 MDP4_LCDC_DISPLAY_HCTRL_START(hsync_start_x) |
390 MDP4_LCDC_DISPLAY_HCTRL_END(hsync_end_x));
391 mdp4_write(mdp4_kms, REG_MDP4_LCDC_DISPLAY_VSTART, display_v_start);
392 mdp4_write(mdp4_kms, REG_MDP4_LCDC_DISPLAY_VEND, display_v_end);
393 mdp4_write(mdp4_kms, REG_MDP4_LCDC_BORDER_CLR, 0);
394 mdp4_write(mdp4_kms, REG_MDP4_LCDC_UNDERFLOW_CLR,
395 MDP4_LCDC_UNDERFLOW_CLR_ENABLE_RECOVERY |
396 MDP4_LCDC_UNDERFLOW_CLR_COLOR(0xff));
397 mdp4_write(mdp4_kms, REG_MDP4_LCDC_HSYNC_SKEW, lcdc_hsync_skew);
398 mdp4_write(mdp4_kms, REG_MDP4_LCDC_CTRL_POLARITY, ctrl_pol);
399 mdp4_write(mdp4_kms, REG_MDP4_LCDC_ACTIVE_HCTL,
400 MDP4_LCDC_ACTIVE_HCTL_START(0) |
401 MDP4_LCDC_ACTIVE_HCTL_END(0));
402 mdp4_write(mdp4_kms, REG_MDP4_LCDC_ACTIVE_VSTART, 0);
403 mdp4_write(mdp4_kms, REG_MDP4_LCDC_ACTIVE_VEND, 0);
404}
405
406static void mdp4_lcdc_encoder_prepare(struct drm_encoder *encoder)
407{
408 mdp4_lcdc_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
409}
410
411static void mdp4_lcdc_encoder_commit(struct drm_encoder *encoder)
412{
413 /* TODO: hard-coded for 18bpp: */
414 mdp4_crtc_set_config(encoder->crtc,
415 MDP4_DMA_CONFIG_R_BPC(BPC6) |
416 MDP4_DMA_CONFIG_G_BPC(BPC6) |
417 MDP4_DMA_CONFIG_B_BPC(BPC6) |
418 MDP4_DMA_CONFIG_PACK_ALIGN_MSB |
419 MDP4_DMA_CONFIG_PACK(0x21) |
420 MDP4_DMA_CONFIG_DEFLKR_EN |
421 MDP4_DMA_CONFIG_DITHER_EN);
422 mdp4_crtc_set_intf(encoder->crtc, INTF_LCDC_DTV, 0);
423 mdp4_lcdc_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
424}
425
426static const struct drm_encoder_helper_funcs mdp4_lcdc_encoder_helper_funcs = {
427 .dpms = mdp4_lcdc_encoder_dpms,
428 .mode_fixup = mdp4_lcdc_encoder_mode_fixup,
429 .mode_set = mdp4_lcdc_encoder_mode_set,
430 .prepare = mdp4_lcdc_encoder_prepare,
431 .commit = mdp4_lcdc_encoder_commit,
432};
433
434long mdp4_lcdc_round_pixclk(struct drm_encoder *encoder, unsigned long rate)
435{
436 struct mdp4_lcdc_encoder *mdp4_lcdc_encoder =
437 to_mdp4_lcdc_encoder(encoder);
438 return clk_round_rate(mdp4_lcdc_encoder->lcdc_clk, rate);
439}
440
441/* initialize encoder */
442struct drm_encoder *mdp4_lcdc_encoder_init(struct drm_device *dev,
443 struct drm_panel *panel)
444{
445 struct drm_encoder *encoder = NULL;
446 struct mdp4_lcdc_encoder *mdp4_lcdc_encoder;
447 struct regulator *reg;
448 int ret;
449
450 mdp4_lcdc_encoder = kzalloc(sizeof(*mdp4_lcdc_encoder), GFP_KERNEL);
451 if (!mdp4_lcdc_encoder) {
452 ret = -ENOMEM;
453 goto fail;
454 }
455
456 mdp4_lcdc_encoder->panel = panel;
457
458 encoder = &mdp4_lcdc_encoder->base;
459
460 drm_encoder_init(dev, encoder, &mdp4_lcdc_encoder_funcs,
461 DRM_MODE_ENCODER_LVDS);
462 drm_encoder_helper_add(encoder, &mdp4_lcdc_encoder_helper_funcs);
463
464 /* TODO: do we need different pll in other cases? */
465 mdp4_lcdc_encoder->lcdc_clk = mpd4_lvds_pll_init(dev);
466 if (IS_ERR(mdp4_lcdc_encoder->lcdc_clk)) {
467 dev_err(dev->dev, "failed to get lvds_clk\n");
468 ret = PTR_ERR(mdp4_lcdc_encoder->lcdc_clk);
469 goto fail;
470 }
471
472 /* TODO: different regulators in other cases? */
473 reg = devm_regulator_get(dev->dev, "lvds-vccs-3p3v");
474 if (IS_ERR(reg)) {
475 ret = PTR_ERR(reg);
476 dev_err(dev->dev, "failed to get lvds-vccs-3p3v: %d\n", ret);
477 goto fail;
478 }
479 mdp4_lcdc_encoder->regs[0] = reg;
480
481 reg = devm_regulator_get(dev->dev, "lvds-pll-vdda");
482 if (IS_ERR(reg)) {
483 ret = PTR_ERR(reg);
484 dev_err(dev->dev, "failed to get lvds-pll-vdda: %d\n", ret);
485 goto fail;
486 }
487 mdp4_lcdc_encoder->regs[1] = reg;
488
489 reg = devm_regulator_get(dev->dev, "lvds-vdda");
490 if (IS_ERR(reg)) {
491 ret = PTR_ERR(reg);
492 dev_err(dev->dev, "failed to get lvds-vdda: %d\n", ret);
493 goto fail;
494 }
495 mdp4_lcdc_encoder->regs[2] = reg;
496
497 bs_init(mdp4_lcdc_encoder);
498
499 return encoder;
500
501fail:
502 if (encoder)
503 mdp4_lcdc_encoder_destroy(encoder);
504
505 return ERR_PTR(ret);
506}
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c
new file mode 100644
index 000000000000..310034688c15
--- /dev/null
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c
@@ -0,0 +1,151 @@
1/*
2 * Copyright (C) 2014 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 * Author: Vinay Simha <vinaysimha@inforcecomputing.com>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 as published by
8 * the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <linux/gpio.h>
20
21#include "mdp4_kms.h"
22
23struct mdp4_lvds_connector {
24 struct drm_connector base;
25 struct drm_encoder *encoder;
26 struct drm_panel *panel;
27};
28#define to_mdp4_lvds_connector(x) container_of(x, struct mdp4_lvds_connector, base)
29
30static enum drm_connector_status mdp4_lvds_connector_detect(
31 struct drm_connector *connector, bool force)
32{
33 struct mdp4_lvds_connector *mdp4_lvds_connector =
34 to_mdp4_lvds_connector(connector);
35
36 return mdp4_lvds_connector->panel ?
37 connector_status_connected :
38 connector_status_disconnected;
39}
40
41static void mdp4_lvds_connector_destroy(struct drm_connector *connector)
42{
43 struct mdp4_lvds_connector *mdp4_lvds_connector =
44 to_mdp4_lvds_connector(connector);
45 struct drm_panel *panel = mdp4_lvds_connector->panel;
46
47 if (panel)
48 drm_panel_detach(panel);
49
50 drm_connector_unregister(connector);
51 drm_connector_cleanup(connector);
52
53 kfree(mdp4_lvds_connector);
54}
55
56static int mdp4_lvds_connector_get_modes(struct drm_connector *connector)
57{
58 struct mdp4_lvds_connector *mdp4_lvds_connector =
59 to_mdp4_lvds_connector(connector);
60 struct drm_panel *panel = mdp4_lvds_connector->panel;
61 int ret = 0;
62
63 if (panel)
64 ret = panel->funcs->get_modes(panel);
65
66 return ret;
67}
68
69static int mdp4_lvds_connector_mode_valid(struct drm_connector *connector,
70 struct drm_display_mode *mode)
71{
72 struct mdp4_lvds_connector *mdp4_lvds_connector =
73 to_mdp4_lvds_connector(connector);
74 struct drm_encoder *encoder = mdp4_lvds_connector->encoder;
75 long actual, requested;
76
77 requested = 1000 * mode->clock;
78 actual = mdp4_lcdc_round_pixclk(encoder, requested);
79
80 DBG("requested=%ld, actual=%ld", requested, actual);
81
82 if (actual != requested)
83 return MODE_CLOCK_RANGE;
84
85 return MODE_OK;
86}
87
88static struct drm_encoder *
89mdp4_lvds_connector_best_encoder(struct drm_connector *connector)
90{
91 struct mdp4_lvds_connector *mdp4_lvds_connector =
92 to_mdp4_lvds_connector(connector);
93 return mdp4_lvds_connector->encoder;
94}
95
96static const struct drm_connector_funcs mdp4_lvds_connector_funcs = {
97 .dpms = drm_helper_connector_dpms,
98 .detect = mdp4_lvds_connector_detect,
99 .fill_modes = drm_helper_probe_single_connector_modes,
100 .destroy = mdp4_lvds_connector_destroy,
101};
102
103static const struct drm_connector_helper_funcs mdp4_lvds_connector_helper_funcs = {
104 .get_modes = mdp4_lvds_connector_get_modes,
105 .mode_valid = mdp4_lvds_connector_mode_valid,
106 .best_encoder = mdp4_lvds_connector_best_encoder,
107};
108
109/* initialize connector */
110struct drm_connector *mdp4_lvds_connector_init(struct drm_device *dev,
111 struct drm_panel *panel, struct drm_encoder *encoder)
112{
113 struct drm_connector *connector = NULL;
114 struct mdp4_lvds_connector *mdp4_lvds_connector;
115 int ret;
116
117 mdp4_lvds_connector = kzalloc(sizeof(*mdp4_lvds_connector), GFP_KERNEL);
118 if (!mdp4_lvds_connector) {
119 ret = -ENOMEM;
120 goto fail;
121 }
122
123 mdp4_lvds_connector->encoder = encoder;
124 mdp4_lvds_connector->panel = panel;
125
126 connector = &mdp4_lvds_connector->base;
127
128 drm_connector_init(dev, connector, &mdp4_lvds_connector_funcs,
129 DRM_MODE_CONNECTOR_LVDS);
130 drm_connector_helper_add(connector, &mdp4_lvds_connector_helper_funcs);
131
132 connector->polled = 0;
133
134 connector->interlace_allowed = 0;
135 connector->doublescan_allowed = 0;
136
137 drm_connector_register(connector);
138
139 drm_mode_connector_attach_encoder(connector, encoder);
140
141 if (panel)
142 drm_panel_attach(panel, connector);
143
144 return connector;
145
146fail:
147 if (connector)
148 mdp4_lvds_connector_destroy(connector);
149
150 return ERR_PTR(ret);
151}
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_pll.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_pll.c
new file mode 100644
index 000000000000..ce4245971673
--- /dev/null
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_pll.c
@@ -0,0 +1,172 @@
1/*
2 * Copyright (C) 2014 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include <linux/clk.h>
19#include <linux/clk-provider.h>
20
21#include "mdp4_kms.h"
22
23struct mdp4_lvds_pll {
24 struct clk_hw pll_hw;
25 struct drm_device *dev;
26 unsigned long pixclk;
27};
28#define to_mdp4_lvds_pll(x) container_of(x, struct mdp4_lvds_pll, pll_hw)
29
30static struct mdp4_kms *get_kms(struct mdp4_lvds_pll *lvds_pll)
31{
32 struct msm_drm_private *priv = lvds_pll->dev->dev_private;
33 return to_mdp4_kms(to_mdp_kms(priv->kms));
34}
35
36struct pll_rate {
37 unsigned long rate;
38 struct {
39 uint32_t val;
40 uint32_t reg;
41 } conf[32];
42};
43
44/* NOTE: keep sorted highest freq to lowest: */
45static const struct pll_rate freqtbl[] = {
46 { 72000000, {
47 { 0x8f, REG_MDP4_LVDS_PHY_PLL_CTRL_1 },
48 { 0x30, REG_MDP4_LVDS_PHY_PLL_CTRL_2 },
49 { 0xc6, REG_MDP4_LVDS_PHY_PLL_CTRL_3 },
50 { 0x10, REG_MDP4_LVDS_PHY_PLL_CTRL_5 },
51 { 0x07, REG_MDP4_LVDS_PHY_PLL_CTRL_6 },
52 { 0x62, REG_MDP4_LVDS_PHY_PLL_CTRL_7 },
53 { 0x41, REG_MDP4_LVDS_PHY_PLL_CTRL_8 },
54 { 0x0d, REG_MDP4_LVDS_PHY_PLL_CTRL_9 },
55 { 0, 0 } }
56 },
57};
58
59static const struct pll_rate *find_rate(unsigned long rate)
60{
61 int i;
62 for (i = 1; i < ARRAY_SIZE(freqtbl); i++)
63 if (rate > freqtbl[i].rate)
64 return &freqtbl[i-1];
65 return &freqtbl[i-1];
66}
67
68static int mpd4_lvds_pll_enable(struct clk_hw *hw)
69{
70 struct mdp4_lvds_pll *lvds_pll = to_mdp4_lvds_pll(hw);
71 struct mdp4_kms *mdp4_kms = get_kms(lvds_pll);
72 const struct pll_rate *pll_rate = find_rate(lvds_pll->pixclk);
73 int i;
74
75 DBG("pixclk=%lu (%lu)", lvds_pll->pixclk, pll_rate->rate);
76
77 if (WARN_ON(!pll_rate))
78 return -EINVAL;
79
80 mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_PHY_RESET, 0x33);
81
82 for (i = 0; pll_rate->conf[i].reg; i++)
83 mdp4_write(mdp4_kms, pll_rate->conf[i].reg, pll_rate->conf[i].val);
84
85 mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_PLL_CTRL_0, 0x01);
86
87 /* Wait until LVDS PLL is locked and ready */
88 while (!mdp4_read(mdp4_kms, REG_MDP4_LVDS_PHY_PLL_LOCKED))
89 cpu_relax();
90
91 return 0;
92}
93
94static void mpd4_lvds_pll_disable(struct clk_hw *hw)
95{
96 struct mdp4_lvds_pll *lvds_pll = to_mdp4_lvds_pll(hw);
97 struct mdp4_kms *mdp4_kms = get_kms(lvds_pll);
98
99 DBG("");
100
101 mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG0, 0x0);
102 mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_PLL_CTRL_0, 0x0);
103}
104
105static unsigned long mpd4_lvds_pll_recalc_rate(struct clk_hw *hw,
106 unsigned long parent_rate)
107{
108 struct mdp4_lvds_pll *lvds_pll = to_mdp4_lvds_pll(hw);
109 return lvds_pll->pixclk;
110}
111
112static long mpd4_lvds_pll_round_rate(struct clk_hw *hw, unsigned long rate,
113 unsigned long *parent_rate)
114{
115 const struct pll_rate *pll_rate = find_rate(rate);
116 return pll_rate->rate;
117}
118
119static int mpd4_lvds_pll_set_rate(struct clk_hw *hw, unsigned long rate,
120 unsigned long parent_rate)
121{
122 struct mdp4_lvds_pll *lvds_pll = to_mdp4_lvds_pll(hw);
123 lvds_pll->pixclk = rate;
124 return 0;
125}
126
127
128static const struct clk_ops mpd4_lvds_pll_ops = {
129 .enable = mpd4_lvds_pll_enable,
130 .disable = mpd4_lvds_pll_disable,
131 .recalc_rate = mpd4_lvds_pll_recalc_rate,
132 .round_rate = mpd4_lvds_pll_round_rate,
133 .set_rate = mpd4_lvds_pll_set_rate,
134};
135
136static const char *mpd4_lvds_pll_parents[] = {
137 "pxo",
138};
139
140static struct clk_init_data pll_init = {
141 .name = "mpd4_lvds_pll",
142 .ops = &mpd4_lvds_pll_ops,
143 .parent_names = mpd4_lvds_pll_parents,
144 .num_parents = ARRAY_SIZE(mpd4_lvds_pll_parents),
145};
146
147struct clk *mpd4_lvds_pll_init(struct drm_device *dev)
148{
149 struct mdp4_lvds_pll *lvds_pll;
150 struct clk *clk;
151 int ret;
152
153 lvds_pll = devm_kzalloc(dev->dev, sizeof(*lvds_pll), GFP_KERNEL);
154 if (!lvds_pll) {
155 ret = -ENOMEM;
156 goto fail;
157 }
158
159 lvds_pll->dev = dev;
160
161 lvds_pll->pll_hw.init = &pll_init;
162 clk = devm_clk_register(dev->dev, &lvds_pll->pll_hw);
163 if (IS_ERR(clk)) {
164 ret = PTR_ERR(clk);
165 goto fail;
166 }
167
168 return clk;
169
170fail:
171 return ERR_PTR(ret);
172}