Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

Rob Clark 3e87599b d65bd0e4

+933 -10
+1
drivers/gpu/drm/msm/Kconfig
··· 4 4 depends on DRM 5 5 depends on ARCH_QCOM || (ARM && COMPILE_TEST) 6 6 select DRM_KMS_HELPER 7 + select DRM_PANEL 7 8 select SHMEM 8 9 select TMPFS 9 10 default y
+3
drivers/gpu/drm/msm/Makefile
··· 18 18 mdp/mdp_kms.o \ 19 19 mdp/mdp4/mdp4_crtc.o \ 20 20 mdp/mdp4/mdp4_dtv_encoder.o \ 21 + mdp/mdp4/mdp4_lcdc_encoder.o \ 22 + mdp/mdp4/mdp4_lvds_connector.o \ 21 23 mdp/mdp4/mdp4_irq.o \ 22 24 mdp/mdp4/mdp4_kms.o \ 23 25 mdp/mdp4/mdp4_plane.o \ ··· 41 39 msm_ringbuffer.o 42 40 43 41 msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o 42 + msm-$(CONFIG_COMMON_CLK) += mdp/mdp4/mdp4_lvds_pll.o 44 43 45 44 obj-$(CONFIG_DRM_MSM) += msm.o
+2 -2
drivers/gpu/drm/msm/hdmi/hdmi.c
··· 123 123 for (i = 0; i < config->hpd_reg_cnt; i++) { 124 124 struct regulator *reg; 125 125 126 - reg = devm_regulator_get_exclusive(&pdev->dev, 126 + reg = devm_regulator_get(&pdev->dev, 127 127 config->hpd_reg_names[i]); 128 128 if (IS_ERR(reg)) { 129 129 ret = PTR_ERR(reg); ··· 139 139 for (i = 0; i < config->pwr_reg_cnt; i++) { 140 140 struct regulator *reg; 141 141 142 - reg = devm_regulator_get_exclusive(&pdev->dev, 142 + reg = devm_regulator_get(&pdev->dev, 143 143 config->pwr_reg_names[i]); 144 144 if (IS_ERR(reg)) { 145 145 ret = PTR_ERR(reg);
+80 -8
drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
··· 197 197 return 0; 198 198 } 199 199 200 + #ifdef CONFIG_OF 201 + static 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 216 + static 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 + 200 222 static int modeset_init(struct mdp4_kms *mdp4_kms) 201 223 { 202 224 struct drm_device *dev = mdp4_kms->dev; ··· 226 204 struct drm_plane *plane; 227 205 struct drm_crtc *crtc; 228 206 struct drm_encoder *encoder; 207 + struct drm_connector *connector; 208 + struct drm_panel *panel; 229 209 struct hdmi *hdmi; 230 210 int ret; 231 - 232 - /* 233 - * NOTE: this is a bit simplistic until we add support 234 - * for more than just RGB1->DMA_E->DTV->HDMI 235 - */ 236 211 237 212 /* construct non-private planes: */ 238 213 plane = mdp4_plane_init(dev, VG1, false); ··· 248 229 } 249 230 priv->planes[priv->num_planes++] = plane; 250 231 251 - /* the CRTCs get constructed with a private plane: */ 232 + /* 233 + * Setup the LCDC/LVDS path: RGB2 -> DMA_P -> LCDC -> LVDS: 234 + */ 235 + 236 + panel = detect_panel(dev, "qcom,lvds-panel"); 237 + if (IS_ERR(panel)) { 238 + ret = PTR_ERR(panel); 239 + dev_err(dev->dev, "failed to detect LVDS panel: %d\n", ret); 240 + goto fail; 241 + } 242 + 243 + plane = mdp4_plane_init(dev, RGB2, true); 244 + if (IS_ERR(plane)) { 245 + dev_err(dev->dev, "failed to construct plane for RGB2\n"); 246 + ret = PTR_ERR(plane); 247 + goto fail; 248 + } 249 + 250 + crtc = mdp4_crtc_init(dev, plane, priv->num_crtcs, 0, DMA_P); 251 + if (IS_ERR(crtc)) { 252 + dev_err(dev->dev, "failed to construct crtc for DMA_P\n"); 253 + ret = PTR_ERR(crtc); 254 + goto fail; 255 + } 256 + 257 + encoder = mdp4_lcdc_encoder_init(dev, panel); 258 + if (IS_ERR(encoder)) { 259 + dev_err(dev->dev, "failed to construct LCDC encoder\n"); 260 + ret = PTR_ERR(encoder); 261 + goto fail; 262 + } 263 + 264 + /* LCDC can be hooked to DMA_P: */ 265 + encoder->possible_crtcs = 1 << priv->num_crtcs; 266 + 267 + priv->crtcs[priv->num_crtcs++] = crtc; 268 + priv->encoders[priv->num_encoders++] = encoder; 269 + 270 + connector = mdp4_lvds_connector_init(dev, panel, encoder); 271 + if (IS_ERR(connector)) { 272 + ret = PTR_ERR(connector); 273 + dev_err(dev->dev, "failed to initialize LVDS connector: %d\n", ret); 274 + goto fail; 275 + } 276 + 277 + priv->connectors[priv->num_connectors++] = connector; 278 + 279 + /* 280 + * Setup DTV/HDMI path: RGB1 -> DMA_E -> DTV -> HDMI: 281 + */ 282 + 252 283 plane = mdp4_plane_init(dev, RGB1, true); 253 284 if (IS_ERR(plane)) { 254 285 dev_err(dev->dev, "failed to construct plane for RGB1\n"); ··· 312 243 ret = PTR_ERR(crtc); 313 244 goto fail; 314 245 } 315 - priv->crtcs[priv->num_crtcs++] = crtc; 316 246 317 247 encoder = mdp4_dtv_encoder_init(dev); 318 248 if (IS_ERR(encoder)) { ··· 319 251 ret = PTR_ERR(encoder); 320 252 goto fail; 321 253 } 322 - encoder->possible_crtcs = 0x1; /* DTV can be hooked to DMA_E */ 254 + 255 + /* DTV can be hooked to DMA_E: */ 256 + encoder->possible_crtcs = 1 << priv->num_crtcs; 257 + 258 + priv->crtcs[priv->num_crtcs++] = crtc; 323 259 priv->encoders[priv->num_encoders++] = encoder; 324 260 325 261 hdmi = hdmi_init(dev, encoder);
+18
drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
··· 23 23 #include "mdp/mdp_kms.h" 24 24 #include "mdp4.xml.h" 25 25 26 + #include "drm_panel.h" 27 + 26 28 struct mdp4_kms { 27 29 struct mdp_kms base; 28 30 ··· 218 216 219 217 long mdp4_dtv_round_pixclk(struct drm_encoder *encoder, unsigned long rate); 220 218 struct drm_encoder *mdp4_dtv_encoder_init(struct drm_device *dev); 219 + 220 + long mdp4_lcdc_round_pixclk(struct drm_encoder *encoder, unsigned long rate); 221 + struct drm_encoder *mdp4_lcdc_encoder_init(struct drm_device *dev, 222 + struct drm_panel *panel); 223 + 224 + struct drm_connector *mdp4_lvds_connector_init(struct drm_device *dev, 225 + struct drm_panel *panel, struct drm_encoder *encoder); 226 + 227 + #ifdef CONFIG_COMMON_CLK 228 + struct clk *mpd4_lvds_pll_init(struct drm_device *dev); 229 + #else 230 + static inline struct clk *mpd4_lvds_pll_init(struct drm_device *dev) 231 + { 232 + return ERR_PTR(-ENODEV); 233 + } 234 + #endif 221 235 222 236 #ifdef CONFIG_MSM_BUS_SCALING 223 237 static inline int match_dev_name(struct device *dev, void *data)
+506
drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c
··· 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 + 24 + struct 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 + 35 + static 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> 43 + static 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 + 60 + static 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 + 68 + static 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 76 + static void bs_init(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder) {} 77 + static void bs_fini(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder) {} 78 + static void bs_set(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder, int idx) {} 79 + #endif 80 + 81 + static 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 + 90 + static const struct drm_encoder_funcs mdp4_lcdc_encoder_funcs = { 91 + .destroy = mdp4_lcdc_encoder_destroy, 92 + }; 93 + 94 + /* this should probably be a helper: */ 95 + struct 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 + 107 + static 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 + 262 + static 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 + 333 + static 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 + 340 + static 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 + 406 + static void mdp4_lcdc_encoder_prepare(struct drm_encoder *encoder) 407 + { 408 + mdp4_lcdc_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); 409 + } 410 + 411 + static 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 + 426 + static 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 + 434 + long 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 */ 442 + struct 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 + 501 + fail: 502 + if (encoder) 503 + mdp4_lcdc_encoder_destroy(encoder); 504 + 505 + return ERR_PTR(ret); 506 + }
+151
drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c
··· 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 + 23 + struct 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 + 30 + static 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 + 41 + static 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 + 56 + static 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 + 69 + static 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 + 88 + static struct drm_encoder * 89 + mdp4_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 + 96 + static 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 + 103 + static 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 */ 110 + struct 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 + 146 + fail: 147 + if (connector) 148 + mdp4_lvds_connector_destroy(connector); 149 + 150 + return ERR_PTR(ret); 151 + }
+172
drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_pll.c
··· 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 + 23 + struct 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 + 30 + static 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 + 36 + struct 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: */ 45 + static 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 + 59 + static 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 + 68 + static 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 + 94 + static 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 + 105 + static 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 + 112 + static 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 + 119 + static 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 + 128 + static 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 + 136 + static const char *mpd4_lvds_pll_parents[] = { 137 + "pxo", 138 + }; 139 + 140 + static 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 + 147 + struct 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 + 170 + fail: 171 + return ERR_PTR(ret); 172 + }