1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2023 Microchip Technology Inc. and its subsidiaries 4 * 5 * Author: Manikandan Muralidharan <manikandan.m@microchip.com> 6 * Author: Dharma Balasubiramani <dharma.b@microchip.com> 7 * 8 */ 9 10 #include <linux/clk.h> 11 #include <linux/component.h> 12 #include <linux/delay.h> 13 #include <linux/jiffies.h> 14 #include <linux/media-bus-format.h> 15 #include <linux/mfd/syscon.h> 16 #include <linux/of_graph.h> 17 #include <linux/pinctrl/devinfo.h> 18 #include <linux/phy/phy.h> 19 #include <linux/platform_device.h> 20 #include <linux/pm_runtime.h> 21 #include <linux/regmap.h> 22 #include <linux/reset.h> 23 24 #include <drm/drm_atomic_helper.h> 25 #include <drm/drm_bridge.h> 26 #include <drm/drm_of.h> 27 #include <drm/drm_print.h> 28 #include <drm/drm_probe_helper.h> 29 #include <drm/drm_simple_kms_helper.h> 30 31 #define LVDS_POLL_TIMEOUT_MS 1000 32 33 /* LVDSC register offsets */ 34 #define LVDSC_CR 0x00 35 #define LVDSC_CFGR 0x04 36 #define LVDSC_SR 0x0C 37 #define LVDSC_WPMR 0xE4 38 39 /* Bitfields in LVDSC_CR (Control Register) */ 40 #define LVDSC_CR_SER_EN BIT(0) 41 42 /* Bitfields in LVDSC_CFGR (Configuration Register) */ 43 #define LVDSC_CFGR_PIXSIZE_24BITS 0 44 #define LVDSC_CFGR_PIXSIZE_18BITS BIT(0) 45 #define LVDSC_CFGR_DEN_POL_HIGH 0 46 #define LVDSC_CFGR_DC_UNBALANCED 0 47 #define LVDSC_CFGR_MAPPING_JEIDA BIT(6) 48 #define LVDSC_CFGR_MAPPING_VESA 0 49 50 /*Bitfields in LVDSC_SR */ 51 #define LVDSC_SR_CS BIT(0) 52 53 /* Bitfields in LVDSC_WPMR (Write Protection Mode Register) */ 54 #define LVDSC_WPMR_WPKEY_MASK GENMASK(31, 8) 55 #define LVDSC_WPMR_WPKEY_PSSWD 0x4C5644 56 57 struct mchp_lvds { 58 struct device *dev; 59 void __iomem *regs; 60 struct clk *pclk; 61 struct drm_bridge bridge; 62 struct drm_bridge *panel_bridge; 63 }; 64 65 static inline struct mchp_lvds *bridge_to_lvds(struct drm_bridge *bridge) 66 { 67 return container_of(bridge, struct mchp_lvds, bridge); 68 } 69 70 static inline u32 lvds_readl(struct mchp_lvds *lvds, u32 offset) 71 { 72 return readl_relaxed(lvds->regs + offset); 73 } 74 75 static inline void lvds_writel(struct mchp_lvds *lvds, u32 offset, u32 val) 76 { 77 writel_relaxed(val, lvds->regs + offset); 78 } 79 80 static void lvds_serialiser_on(struct mchp_lvds *lvds, u32 bus_format) 81 { 82 unsigned long timeout = jiffies + msecs_to_jiffies(LVDS_POLL_TIMEOUT_MS); 83 u8 map, pix_size; 84 85 /* The LVDSC registers can only be written if WPEN is cleared */ 86 lvds_writel(lvds, LVDSC_WPMR, (LVDSC_WPMR_WPKEY_PSSWD & 87 LVDSC_WPMR_WPKEY_MASK)); 88 89 /* Wait for the status of configuration registers to be changed */ 90 while (lvds_readl(lvds, LVDSC_SR) & LVDSC_SR_CS) { 91 if (time_after(jiffies, timeout)) { 92 dev_err(lvds->dev, "%s: timeout error\n", __func__); 93 return; 94 } 95 usleep_range(1000, 2000); 96 } 97 98 switch (bus_format) { 99 case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: 100 map = LVDSC_CFGR_MAPPING_JEIDA; 101 pix_size = LVDSC_CFGR_PIXSIZE_18BITS; 102 break; 103 case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: 104 map = LVDSC_CFGR_MAPPING_VESA; 105 pix_size = LVDSC_CFGR_PIXSIZE_24BITS; 106 break; 107 default: 108 map = LVDSC_CFGR_MAPPING_JEIDA; 109 pix_size = LVDSC_CFGR_PIXSIZE_24BITS; 110 break; 111 } 112 113 /* Configure the LVDSC */ 114 lvds_writel(lvds, LVDSC_CFGR, map | LVDSC_CFGR_DC_UNBALANCED | 115 LVDSC_CFGR_DEN_POL_HIGH | pix_size); 116 117 /* Enable the LVDS serializer */ 118 lvds_writel(lvds, LVDSC_CR, LVDSC_CR_SER_EN); 119 } 120 121 static int mchp_lvds_attach(struct drm_bridge *bridge, 122 struct drm_encoder *encoder, 123 enum drm_bridge_attach_flags flags) 124 { 125 struct mchp_lvds *lvds = bridge_to_lvds(bridge); 126 127 return drm_bridge_attach(encoder, lvds->panel_bridge, 128 bridge, flags); 129 } 130 131 static void mchp_lvds_atomic_enable(struct drm_bridge *bridge, 132 struct drm_atomic_commit *state) 133 { 134 struct mchp_lvds *lvds = bridge_to_lvds(bridge); 135 struct drm_connector *connector; 136 int ret; 137 138 ret = clk_prepare_enable(lvds->pclk); 139 if (ret < 0) { 140 dev_err(lvds->dev, "failed to enable lvds pclk %d\n", ret); 141 return; 142 } 143 144 ret = pm_runtime_get_sync(lvds->dev); 145 if (ret < 0) { 146 dev_err(lvds->dev, "failed to get pm runtime: %d\n", ret); 147 return; 148 } 149 150 /* default to jeida-24 */ 151 u32 bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA; 152 153 connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); 154 if (connector && connector->display_info.num_bus_formats) 155 bus_format = connector->display_info.bus_formats[0]; 156 157 lvds_serialiser_on(lvds, bus_format); 158 } 159 160 static void mchp_lvds_atomic_disable(struct drm_bridge *bridge, 161 struct drm_atomic_commit *state) 162 { 163 struct mchp_lvds *lvds = bridge_to_lvds(bridge); 164 165 pm_runtime_put(lvds->dev); 166 clk_disable_unprepare(lvds->pclk); 167 } 168 169 static const struct drm_bridge_funcs mchp_lvds_bridge_funcs = { 170 .attach = mchp_lvds_attach, 171 .atomic_enable = mchp_lvds_atomic_enable, 172 .atomic_disable = mchp_lvds_atomic_disable, 173 }; 174 175 static int mchp_lvds_probe(struct platform_device *pdev) 176 { 177 struct device *dev = &pdev->dev; 178 struct mchp_lvds *lvds; 179 int ret; 180 181 if (!dev->of_node) 182 return -ENODEV; 183 184 lvds = devm_drm_bridge_alloc(&pdev->dev, struct mchp_lvds, bridge, 185 &mchp_lvds_bridge_funcs); 186 if (IS_ERR(lvds)) 187 return PTR_ERR(lvds); 188 189 lvds->dev = dev; 190 191 lvds->regs = devm_platform_ioremap_resource(pdev, 0); 192 if (IS_ERR(lvds->regs)) 193 return PTR_ERR(lvds->regs); 194 195 lvds->pclk = devm_clk_get(lvds->dev, "pclk"); 196 if (IS_ERR(lvds->pclk)) 197 return dev_err_probe(lvds->dev, PTR_ERR(lvds->pclk), 198 "could not get pclk_lvds\n"); 199 200 lvds->panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0); 201 202 if (IS_ERR(lvds->panel_bridge)) 203 return PTR_ERR(lvds->panel_bridge); 204 205 lvds->bridge.of_node = dev->of_node; 206 lvds->bridge.type = DRM_MODE_CONNECTOR_LVDS; 207 208 dev_set_drvdata(dev, lvds); 209 ret = devm_pm_runtime_enable(dev); 210 if (ret < 0) { 211 dev_err(lvds->dev, "failed to enable pm runtime: %d\n", ret); 212 return ret; 213 } 214 215 drm_bridge_add(&lvds->bridge); 216 217 return 0; 218 } 219 220 static const struct of_device_id mchp_lvds_dt_ids[] = { 221 { 222 .compatible = "microchip,sam9x75-lvds", 223 }, 224 {}, 225 }; 226 MODULE_DEVICE_TABLE(of, mchp_lvds_dt_ids); 227 228 static struct platform_driver mchp_lvds_driver = { 229 .probe = mchp_lvds_probe, 230 .driver = { 231 .name = "microchip-lvds", 232 .of_match_table = mchp_lvds_dt_ids, 233 }, 234 }; 235 module_platform_driver(mchp_lvds_driver); 236 237 MODULE_AUTHOR("Manikandan Muralidharan <manikandan.m@microchip.com>"); 238 MODULE_AUTHOR("Dharma Balasubiramani <dharma.b@microchip.com>"); 239 MODULE_DESCRIPTION("Low Voltage Differential Signaling Controller Driver"); 240 MODULE_LICENSE("GPL"); 241