Merge tag 'drm-misc-next-2019-10-09-2' of git://anongit.freedesktop.org/drm/drm-misc...
[linux-2.6-microblaze.git] / drivers / gpu / drm / rockchip / rockchip_lvds.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
4  * Author:
5  *      Mark Yao <mark.yao@rock-chips.com>
6  *      Sandy Huang <hjc@rock-chips.com>
7  */
8
9 #include <linux/clk.h>
10 #include <linux/component.h>
11 #include <linux/mfd/syscon.h>
12 #include <linux/of_graph.h>
13 #include <linux/pinctrl/devinfo.h>
14 #include <linux/platform_device.h>
15 #include <linux/pm_runtime.h>
16 #include <linux/regmap.h>
17 #include <linux/reset.h>
18 #include <drm/drm_atomic_helper.h>
19 #include <drm/drm_bridge.h>
20
21 #include <drm/drm_dp_helper.h>
22 #include <drm/drm_of.h>
23 #include <drm/drm_panel.h>
24 #include <drm/drm_probe_helper.h>
25
26 #include "rockchip_drm_drv.h"
27 #include "rockchip_drm_vop.h"
28 #include "rockchip_lvds.h"
29
30 #define DISPLAY_OUTPUT_RGB              0
31 #define DISPLAY_OUTPUT_LVDS             1
32 #define DISPLAY_OUTPUT_DUAL_LVDS        2
33
34 #define connector_to_lvds(c) \
35                 container_of(c, struct rockchip_lvds, connector)
36
37 #define encoder_to_lvds(c) \
38                 container_of(c, struct rockchip_lvds, encoder)
39
40 /**
41  * rockchip_lvds_soc_data - rockchip lvds Soc private data
42  * @ch1_offset: lvds channel 1 registe offset
43  * grf_soc_con6: general registe offset for LVDS contrl
44  * grf_soc_con7: general registe offset for LVDS contrl
45  * has_vop_sel: to indicate whether need to choose from different VOP.
46  */
47 struct rockchip_lvds_soc_data {
48         u32 ch1_offset;
49         int grf_soc_con6;
50         int grf_soc_con7;
51         bool has_vop_sel;
52 };
53
54 struct rockchip_lvds {
55         struct device *dev;
56         void __iomem *regs;
57         struct regmap *grf;
58         struct clk *pclk;
59         const struct rockchip_lvds_soc_data *soc_data;
60         int output; /* rgb lvds or dual lvds output */
61         int format; /* vesa or jeida format */
62         struct drm_device *drm_dev;
63         struct drm_panel *panel;
64         struct drm_bridge *bridge;
65         struct drm_connector connector;
66         struct drm_encoder encoder;
67         struct dev_pin_info *pins;
68 };
69
70 static inline void lvds_writel(struct rockchip_lvds *lvds, u32 offset, u32 val)
71 {
72         writel_relaxed(val, lvds->regs + offset);
73         if (lvds->output == DISPLAY_OUTPUT_LVDS)
74                 return;
75         writel_relaxed(val, lvds->regs + offset + lvds->soc_data->ch1_offset);
76 }
77
78 static inline int lvds_name_to_format(const char *s)
79 {
80         if (strncmp(s, "jeida-18", 8) == 0)
81                 return LVDS_JEIDA_18;
82         else if (strncmp(s, "jeida-24", 8) == 0)
83                 return LVDS_JEIDA_24;
84         else if (strncmp(s, "vesa-24", 7) == 0)
85                 return LVDS_VESA_24;
86
87         return -EINVAL;
88 }
89
90 static inline int lvds_name_to_output(const char *s)
91 {
92         if (strncmp(s, "rgb", 3) == 0)
93                 return DISPLAY_OUTPUT_RGB;
94         else if (strncmp(s, "lvds", 4) == 0)
95                 return DISPLAY_OUTPUT_LVDS;
96         else if (strncmp(s, "duallvds", 8) == 0)
97                 return DISPLAY_OUTPUT_DUAL_LVDS;
98
99         return -EINVAL;
100 }
101
102 static int rockchip_lvds_poweron(struct rockchip_lvds *lvds)
103 {
104         int ret;
105         u32 val;
106
107         ret = clk_enable(lvds->pclk);
108         if (ret < 0) {
109                 DRM_DEV_ERROR(lvds->dev, "failed to enable lvds pclk %d\n", ret);
110                 return ret;
111         }
112         ret = pm_runtime_get_sync(lvds->dev);
113         if (ret < 0) {
114                 DRM_DEV_ERROR(lvds->dev, "failed to get pm runtime: %d\n", ret);
115                 clk_disable(lvds->pclk);
116                 return ret;
117         }
118         val = RK3288_LVDS_CH0_REG0_LANE4_EN | RK3288_LVDS_CH0_REG0_LANE3_EN |
119                 RK3288_LVDS_CH0_REG0_LANE2_EN | RK3288_LVDS_CH0_REG0_LANE1_EN |
120                 RK3288_LVDS_CH0_REG0_LANE0_EN;
121         if (lvds->output == DISPLAY_OUTPUT_RGB) {
122                 val |= RK3288_LVDS_CH0_REG0_TTL_EN |
123                         RK3288_LVDS_CH0_REG0_LANECK_EN;
124                 lvds_writel(lvds, RK3288_LVDS_CH0_REG0, val);
125                 lvds_writel(lvds, RK3288_LVDS_CH0_REG2,
126                             RK3288_LVDS_PLL_FBDIV_REG2(0x46));
127                 lvds_writel(lvds, RK3288_LVDS_CH0_REG4,
128                             RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE |
129                             RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE |
130                             RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE |
131                             RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE |
132                             RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE |
133                             RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE);
134                 lvds_writel(lvds, RK3288_LVDS_CH0_REG5,
135                             RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA |
136                             RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA |
137                             RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA |
138                             RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA |
139                             RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA |
140                             RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA);
141         } else {
142                 val |= RK3288_LVDS_CH0_REG0_LVDS_EN |
143                             RK3288_LVDS_CH0_REG0_LANECK_EN;
144                 lvds_writel(lvds, RK3288_LVDS_CH0_REG0, val);
145                 lvds_writel(lvds, RK3288_LVDS_CH0_REG1,
146                             RK3288_LVDS_CH0_REG1_LANECK_BIAS |
147                             RK3288_LVDS_CH0_REG1_LANE4_BIAS |
148                             RK3288_LVDS_CH0_REG1_LANE3_BIAS |
149                             RK3288_LVDS_CH0_REG1_LANE2_BIAS |
150                             RK3288_LVDS_CH0_REG1_LANE1_BIAS |
151                             RK3288_LVDS_CH0_REG1_LANE0_BIAS);
152                 lvds_writel(lvds, RK3288_LVDS_CH0_REG2,
153                             RK3288_LVDS_CH0_REG2_RESERVE_ON |
154                             RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE |
155                             RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE |
156                             RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE |
157                             RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE |
158                             RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE |
159                             RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE |
160                             RK3288_LVDS_PLL_FBDIV_REG2(0x46));
161                 lvds_writel(lvds, RK3288_LVDS_CH0_REG4, 0x00);
162                 lvds_writel(lvds, RK3288_LVDS_CH0_REG5, 0x00);
163         }
164         lvds_writel(lvds, RK3288_LVDS_CH0_REG3, RK3288_LVDS_PLL_FBDIV_REG3(0x46));
165         lvds_writel(lvds, RK3288_LVDS_CH0_REGD, RK3288_LVDS_PLL_PREDIV_REGD(0x0a));
166         lvds_writel(lvds, RK3288_LVDS_CH0_REG20, RK3288_LVDS_CH0_REG20_LSB);
167
168         lvds_writel(lvds, RK3288_LVDS_CFG_REGC, RK3288_LVDS_CFG_REGC_PLL_ENABLE);
169         lvds_writel(lvds, RK3288_LVDS_CFG_REG21, RK3288_LVDS_CFG_REG21_TX_ENABLE);
170
171         return 0;
172 }
173
174 static void rockchip_lvds_poweroff(struct rockchip_lvds *lvds)
175 {
176         int ret;
177         u32 val;
178
179         lvds_writel(lvds, RK3288_LVDS_CFG_REG21, RK3288_LVDS_CFG_REG21_TX_ENABLE);
180         lvds_writel(lvds, RK3288_LVDS_CFG_REGC, RK3288_LVDS_CFG_REGC_PLL_ENABLE);
181         val = LVDS_DUAL | LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN | LVDS_PWRDN;
182         val |= val << 16;
183         ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, val);
184         if (ret != 0)
185                 DRM_DEV_ERROR(lvds->dev, "Could not write to GRF: %d\n", ret);
186
187         pm_runtime_put(lvds->dev);
188         clk_disable(lvds->pclk);
189 }
190
191 static const struct drm_connector_funcs rockchip_lvds_connector_funcs = {
192         .fill_modes = drm_helper_probe_single_connector_modes,
193         .destroy = drm_connector_cleanup,
194         .reset = drm_atomic_helper_connector_reset,
195         .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
196         .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
197 };
198
199 static int rockchip_lvds_connector_get_modes(struct drm_connector *connector)
200 {
201         struct rockchip_lvds *lvds = connector_to_lvds(connector);
202         struct drm_panel *panel = lvds->panel;
203
204         return drm_panel_get_modes(panel);
205 }
206
207 static const
208 struct drm_connector_helper_funcs rockchip_lvds_connector_helper_funcs = {
209         .get_modes = rockchip_lvds_connector_get_modes,
210 };
211
212 static void rockchip_lvds_grf_config(struct drm_encoder *encoder,
213                                      struct drm_display_mode *mode)
214 {
215         struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
216         u8 pin_hsync = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 1 : 0;
217         u8 pin_dclk = (mode->flags & DRM_MODE_FLAG_PCSYNC) ? 1 : 0;
218         u32 val;
219         int ret;
220
221         /* iomux to LCD data/sync mode */
222         if (lvds->output == DISPLAY_OUTPUT_RGB)
223                 if (lvds->pins && !IS_ERR(lvds->pins->default_state))
224                         pinctrl_select_state(lvds->pins->p,
225                                              lvds->pins->default_state);
226         val = lvds->format | LVDS_CH0_EN;
227         if (lvds->output == DISPLAY_OUTPUT_RGB)
228                 val |= LVDS_TTL_EN | LVDS_CH1_EN;
229         else if (lvds->output == DISPLAY_OUTPUT_DUAL_LVDS)
230                 val |= LVDS_DUAL | LVDS_CH1_EN;
231
232         if ((mode->htotal - mode->hsync_start) & 0x01)
233                 val |= LVDS_START_PHASE_RST_1;
234
235         val |= (pin_dclk << 8) | (pin_hsync << 9);
236         val |= (0xffff << 16);
237         ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, val);
238         if (ret != 0) {
239                 DRM_DEV_ERROR(lvds->dev, "Could not write to GRF: %d\n", ret);
240                 return;
241         }
242 }
243
244 static int rockchip_lvds_set_vop_source(struct rockchip_lvds *lvds,
245                                         struct drm_encoder *encoder)
246 {
247         u32 val;
248         int ret;
249
250         if (!lvds->soc_data->has_vop_sel)
251                 return 0;
252
253         ret = drm_of_encoder_active_endpoint_id(lvds->dev->of_node, encoder);
254         if (ret < 0)
255                 return ret;
256
257         val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16;
258         if (ret)
259                 val |= RK3288_LVDS_SOC_CON6_SEL_VOP_LIT;
260
261         ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con6, val);
262         if (ret < 0)
263                 return ret;
264
265         return 0;
266 }
267
268 static int
269 rockchip_lvds_encoder_atomic_check(struct drm_encoder *encoder,
270                                    struct drm_crtc_state *crtc_state,
271                                    struct drm_connector_state *conn_state)
272 {
273         struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
274
275         s->output_mode = ROCKCHIP_OUT_MODE_P888;
276         s->output_type = DRM_MODE_CONNECTOR_LVDS;
277
278         return 0;
279 }
280
281 static void rockchip_lvds_encoder_enable(struct drm_encoder *encoder)
282 {
283         struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
284         struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
285         int ret;
286
287         drm_panel_prepare(lvds->panel);
288         ret = rockchip_lvds_poweron(lvds);
289         if (ret < 0) {
290                 DRM_DEV_ERROR(lvds->dev, "failed to power on lvds: %d\n", ret);
291                 drm_panel_unprepare(lvds->panel);
292         }
293         rockchip_lvds_grf_config(encoder, mode);
294         rockchip_lvds_set_vop_source(lvds, encoder);
295         drm_panel_enable(lvds->panel);
296 }
297
298 static void rockchip_lvds_encoder_disable(struct drm_encoder *encoder)
299 {
300         struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
301
302         drm_panel_disable(lvds->panel);
303         rockchip_lvds_poweroff(lvds);
304         drm_panel_unprepare(lvds->panel);
305 }
306
307 static const
308 struct drm_encoder_helper_funcs rockchip_lvds_encoder_helper_funcs = {
309         .enable = rockchip_lvds_encoder_enable,
310         .disable = rockchip_lvds_encoder_disable,
311         .atomic_check = rockchip_lvds_encoder_atomic_check,
312 };
313
314 static const struct drm_encoder_funcs rockchip_lvds_encoder_funcs = {
315         .destroy = drm_encoder_cleanup,
316 };
317
318 static const struct rockchip_lvds_soc_data rk3288_lvds_data = {
319         .ch1_offset = 0x100,
320         .grf_soc_con6 = 0x025c,
321         .grf_soc_con7 = 0x0260,
322         .has_vop_sel = true,
323 };
324
325 static const struct of_device_id rockchip_lvds_dt_ids[] = {
326         {
327                 .compatible = "rockchip,rk3288-lvds",
328                 .data = &rk3288_lvds_data
329         },
330         {}
331 };
332 MODULE_DEVICE_TABLE(of, rockchip_lvds_dt_ids);
333
334 static int rockchip_lvds_bind(struct device *dev, struct device *master,
335                               void *data)
336 {
337         struct rockchip_lvds *lvds = dev_get_drvdata(dev);
338         struct drm_device *drm_dev = data;
339         struct drm_encoder *encoder;
340         struct drm_connector *connector;
341         struct device_node *remote = NULL;
342         struct device_node  *port, *endpoint;
343         int ret = 0, child_count = 0;
344         const char *name;
345         u32 endpoint_id;
346
347         lvds->drm_dev = drm_dev;
348         port = of_graph_get_port_by_id(dev->of_node, 1);
349         if (!port) {
350                 DRM_DEV_ERROR(dev,
351                               "can't found port point, please init lvds panel port!\n");
352                 return -EINVAL;
353         }
354         for_each_child_of_node(port, endpoint) {
355                 child_count++;
356                 of_property_read_u32(endpoint, "reg", &endpoint_id);
357                 ret = drm_of_find_panel_or_bridge(dev->of_node, 1, endpoint_id,
358                                                   &lvds->panel, &lvds->bridge);
359                 if (!ret) {
360                         of_node_put(endpoint);
361                         break;
362                 }
363         }
364         if (!child_count) {
365                 DRM_DEV_ERROR(dev, "lvds port does not have any children\n");
366                 ret = -EINVAL;
367                 goto err_put_port;
368         } else if (ret) {
369                 DRM_DEV_ERROR(dev, "failed to find panel and bridge node\n");
370                 ret = -EPROBE_DEFER;
371                 goto err_put_port;
372         }
373         if (lvds->panel)
374                 remote = lvds->panel->dev->of_node;
375         else
376                 remote = lvds->bridge->of_node;
377         if (of_property_read_string(dev->of_node, "rockchip,output", &name))
378                 /* default set it as output rgb */
379                 lvds->output = DISPLAY_OUTPUT_RGB;
380         else
381                 lvds->output = lvds_name_to_output(name);
382
383         if (lvds->output < 0) {
384                 DRM_DEV_ERROR(dev, "invalid output type [%s]\n", name);
385                 ret = lvds->output;
386                 goto err_put_remote;
387         }
388
389         if (of_property_read_string(remote, "data-mapping", &name))
390                 /* default set it as format vesa 18 */
391                 lvds->format = LVDS_VESA_18;
392         else
393                 lvds->format = lvds_name_to_format(name);
394
395         if (lvds->format < 0) {
396                 DRM_DEV_ERROR(dev, "invalid data-mapping format [%s]\n", name);
397                 ret = lvds->format;
398                 goto err_put_remote;
399         }
400
401         encoder = &lvds->encoder;
402         encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
403                                                              dev->of_node);
404
405         ret = drm_encoder_init(drm_dev, encoder, &rockchip_lvds_encoder_funcs,
406                                DRM_MODE_ENCODER_LVDS, NULL);
407         if (ret < 0) {
408                 DRM_DEV_ERROR(drm_dev->dev,
409                               "failed to initialize encoder: %d\n", ret);
410                 goto err_put_remote;
411         }
412
413         drm_encoder_helper_add(encoder, &rockchip_lvds_encoder_helper_funcs);
414
415         if (lvds->panel) {
416                 connector = &lvds->connector;
417                 connector->dpms = DRM_MODE_DPMS_OFF;
418                 ret = drm_connector_init(drm_dev, connector,
419                                          &rockchip_lvds_connector_funcs,
420                                          DRM_MODE_CONNECTOR_LVDS);
421                 if (ret < 0) {
422                         DRM_DEV_ERROR(drm_dev->dev,
423                                       "failed to initialize connector: %d\n", ret);
424                         goto err_free_encoder;
425                 }
426
427                 drm_connector_helper_add(connector,
428                                          &rockchip_lvds_connector_helper_funcs);
429
430                 ret = drm_connector_attach_encoder(connector, encoder);
431                 if (ret < 0) {
432                         DRM_DEV_ERROR(drm_dev->dev,
433                                       "failed to attach encoder: %d\n", ret);
434                         goto err_free_connector;
435                 }
436
437                 ret = drm_panel_attach(lvds->panel, connector);
438                 if (ret < 0) {
439                         DRM_DEV_ERROR(drm_dev->dev,
440                                       "failed to attach panel: %d\n", ret);
441                         goto err_free_connector;
442                 }
443         } else {
444                 ret = drm_bridge_attach(encoder, lvds->bridge, NULL);
445                 if (ret) {
446                         DRM_DEV_ERROR(drm_dev->dev,
447                                       "failed to attach bridge: %d\n", ret);
448                         goto err_free_encoder;
449                 }
450         }
451
452         pm_runtime_enable(dev);
453         of_node_put(remote);
454         of_node_put(port);
455
456         return 0;
457
458 err_free_connector:
459         drm_connector_cleanup(connector);
460 err_free_encoder:
461         drm_encoder_cleanup(encoder);
462 err_put_remote:
463         of_node_put(remote);
464 err_put_port:
465         of_node_put(port);
466
467         return ret;
468 }
469
470 static void rockchip_lvds_unbind(struct device *dev, struct device *master,
471                                 void *data)
472 {
473         struct rockchip_lvds *lvds = dev_get_drvdata(dev);
474
475         rockchip_lvds_encoder_disable(&lvds->encoder);
476         if (lvds->panel)
477                 drm_panel_detach(lvds->panel);
478         pm_runtime_disable(dev);
479         drm_connector_cleanup(&lvds->connector);
480         drm_encoder_cleanup(&lvds->encoder);
481 }
482
483 static const struct component_ops rockchip_lvds_component_ops = {
484         .bind = rockchip_lvds_bind,
485         .unbind = rockchip_lvds_unbind,
486 };
487
488 static int rockchip_lvds_probe(struct platform_device *pdev)
489 {
490         struct device *dev = &pdev->dev;
491         struct rockchip_lvds *lvds;
492         const struct of_device_id *match;
493         struct resource *res;
494         int ret;
495
496         if (!dev->of_node)
497                 return -ENODEV;
498
499         lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
500         if (!lvds)
501                 return -ENOMEM;
502
503         lvds->dev = dev;
504         match = of_match_node(rockchip_lvds_dt_ids, dev->of_node);
505         if (!match)
506                 return -ENODEV;
507         lvds->soc_data = match->data;
508
509         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
510         lvds->regs = devm_ioremap_resource(&pdev->dev, res);
511         if (IS_ERR(lvds->regs))
512                 return PTR_ERR(lvds->regs);
513
514         lvds->pclk = devm_clk_get(&pdev->dev, "pclk_lvds");
515         if (IS_ERR(lvds->pclk)) {
516                 DRM_DEV_ERROR(dev, "could not get pclk_lvds\n");
517                 return PTR_ERR(lvds->pclk);
518         }
519
520         lvds->pins = devm_kzalloc(lvds->dev, sizeof(*lvds->pins),
521                                   GFP_KERNEL);
522         if (!lvds->pins)
523                 return -ENOMEM;
524
525         lvds->pins->p = devm_pinctrl_get(lvds->dev);
526         if (IS_ERR(lvds->pins->p)) {
527                 DRM_DEV_ERROR(dev, "no pinctrl handle\n");
528                 devm_kfree(lvds->dev, lvds->pins);
529                 lvds->pins = NULL;
530         } else {
531                 lvds->pins->default_state =
532                         pinctrl_lookup_state(lvds->pins->p, "lcdc");
533                 if (IS_ERR(lvds->pins->default_state)) {
534                         DRM_DEV_ERROR(dev, "no default pinctrl state\n");
535                         devm_kfree(lvds->dev, lvds->pins);
536                         lvds->pins = NULL;
537                 }
538         }
539
540         lvds->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
541                                                     "rockchip,grf");
542         if (IS_ERR(lvds->grf)) {
543                 DRM_DEV_ERROR(dev, "missing rockchip,grf property\n");
544                 return PTR_ERR(lvds->grf);
545         }
546
547         dev_set_drvdata(dev, lvds);
548
549         ret = clk_prepare(lvds->pclk);
550         if (ret < 0) {
551                 DRM_DEV_ERROR(dev, "failed to prepare pclk_lvds\n");
552                 return ret;
553         }
554         ret = component_add(&pdev->dev, &rockchip_lvds_component_ops);
555         if (ret < 0) {
556                 DRM_DEV_ERROR(dev, "failed to add component\n");
557                 clk_unprepare(lvds->pclk);
558         }
559
560         return ret;
561 }
562
563 static int rockchip_lvds_remove(struct platform_device *pdev)
564 {
565         struct rockchip_lvds *lvds = dev_get_drvdata(&pdev->dev);
566
567         component_del(&pdev->dev, &rockchip_lvds_component_ops);
568         clk_unprepare(lvds->pclk);
569
570         return 0;
571 }
572
573 struct platform_driver rockchip_lvds_driver = {
574         .probe = rockchip_lvds_probe,
575         .remove = rockchip_lvds_remove,
576         .driver = {
577                    .name = "rockchip-lvds",
578                    .of_match_table = of_match_ptr(rockchip_lvds_dt_ids),
579         },
580 };