1 // SPDX-License-Identifier: GPL-2.0+ 2 /* Copyright (c) 2018 Jernej Skrabec <jernej.skrabec@siol.net> */ 3 4 5 #include <linux/bitfield.h> 6 #include <linux/component.h> 7 #include <linux/device.h> 8 #include <linux/io.h> 9 #include <linux/module.h> 10 #include <linux/of.h> 11 #include <linux/of_graph.h> 12 #include <linux/platform_device.h> 13 14 #include <dt-bindings/clock/sun8i-tcon-top.h> 15 16 #include "sun8i_tcon_top.h" 17 18 struct sun8i_tcon_top_quirks { 19 bool has_tcon_tv1; 20 bool has_dsi; 21 }; 22 23 static bool sun8i_tcon_top_node_is_tcon_top(struct device_node *node) 24 { 25 return !!of_match_node(sun8i_tcon_top_of_table, node); 26 } 27 28 static unsigned int sun8i_tcon_top_get_tcon_map(struct device_node *node) 29 { 30 static const u32 out_ports[] = { 31 TCON_TOP_MIXER0_OUT_PORT, 32 TCON_TOP_MIXER1_OUT_PORT, 33 }; 34 unsigned int i, map = 0; 35 36 for (i = 0; i < ARRAY_SIZE(out_ports); i++) { 37 struct device_node *port; 38 39 port = of_graph_get_port_by_id(node, out_ports[i]); 40 if (!port) 41 continue; 42 43 for_each_of_graph_port_endpoint(port, ep) { 44 struct of_endpoint endpoint; 45 46 if (of_graph_parse_endpoint(ep, &endpoint)) 47 continue; 48 49 if (endpoint.id < TCON_TOP_PORT_TCON_NUM) 50 map |= BIT(endpoint.id); 51 } 52 53 of_node_put(port); 54 } 55 56 return map; 57 } 58 59 static unsigned int sun8i_tcon_top_park_index(struct sun8i_tcon_top *tcon_top, 60 int tcon) 61 { 62 unsigned int candidates; 63 64 candidates = tcon_top->tcon_map & ~BIT(tcon); 65 if (!candidates) 66 candidates = GENMASK(TCON_TOP_PORT_TCON_NUM - 1, 0) & ~BIT(tcon); 67 68 return ffs(candidates) - 1; 69 } 70 71 int sun8i_tcon_top_set_hdmi_src(struct device *dev, int tcon) 72 { 73 struct sun8i_tcon_top *tcon_top = dev_get_drvdata(dev); 74 unsigned long flags; 75 u32 val; 76 77 if (!sun8i_tcon_top_node_is_tcon_top(dev->of_node)) { 78 dev_err(dev, "Device is not TCON TOP!\n"); 79 return -EINVAL; 80 } 81 82 if (tcon < 2 || tcon > 3) { 83 dev_err(dev, "TCON index must be 2 or 3!\n"); 84 return -EINVAL; 85 } 86 87 spin_lock_irqsave(&tcon_top->reg_lock, flags); 88 89 val = readl(tcon_top->regs + TCON_TOP_GATE_SRC_REG); 90 val &= ~TCON_TOP_HDMI_SRC_MSK; 91 val |= FIELD_PREP(TCON_TOP_HDMI_SRC_MSK, tcon - 1); 92 writel(val, tcon_top->regs + TCON_TOP_GATE_SRC_REG); 93 94 spin_unlock_irqrestore(&tcon_top->reg_lock, flags); 95 96 return 0; 97 } 98 EXPORT_SYMBOL(sun8i_tcon_top_set_hdmi_src); 99 100 int sun8i_tcon_top_de_config(struct device *dev, int mixer, int tcon) 101 { 102 struct sun8i_tcon_top *tcon_top = dev_get_drvdata(dev); 103 u32 mixer_msk, other_msk; 104 unsigned long flags; 105 u32 reg; 106 107 if (!sun8i_tcon_top_node_is_tcon_top(dev->of_node)) { 108 dev_err(dev, "Device is not TCON TOP!\n"); 109 return -EINVAL; 110 } 111 112 if (mixer > 1) { 113 dev_err(dev, "Mixer index is too high!\n"); 114 return -EINVAL; 115 } 116 117 if (tcon < 0 || tcon >= TCON_TOP_PORT_TCON_NUM) { 118 dev_err(dev, "TCON index is invalid!\n"); 119 return -EINVAL; 120 } 121 122 mixer_msk = mixer ? TCON_TOP_PORT_DE1_MSK : TCON_TOP_PORT_DE0_MSK; 123 other_msk = mixer ? TCON_TOP_PORT_DE0_MSK : TCON_TOP_PORT_DE1_MSK; 124 125 spin_lock_irqsave(&tcon_top->reg_lock, flags); 126 127 reg = readl(tcon_top->regs + TCON_TOP_PORT_SEL_REG); 128 129 reg &= ~mixer_msk; 130 reg |= field_prep(mixer_msk, tcon); 131 132 if (field_get(other_msk, reg) == tcon) { 133 reg &= ~other_msk; 134 reg |= field_prep(other_msk, 135 sun8i_tcon_top_park_index(tcon_top, tcon)); 136 } 137 138 writel(reg, tcon_top->regs + TCON_TOP_PORT_SEL_REG); 139 140 spin_unlock_irqrestore(&tcon_top->reg_lock, flags); 141 142 return 0; 143 } 144 EXPORT_SYMBOL(sun8i_tcon_top_de_config); 145 146 147 static struct clk_hw *sun8i_tcon_top_register_gate(struct device *dev, 148 const char *parent, 149 void __iomem *regs, 150 spinlock_t *lock, 151 u8 bit, int name_index) 152 { 153 const char *clk_name, *parent_name; 154 int ret, index; 155 156 index = of_property_match_string(dev->of_node, "clock-names", parent); 157 if (index < 0) 158 return ERR_PTR(index); 159 160 parent_name = of_clk_get_parent_name(dev->of_node, index); 161 162 ret = of_property_read_string_index(dev->of_node, 163 "clock-output-names", name_index, 164 &clk_name); 165 if (ret) 166 return ERR_PTR(ret); 167 168 return clk_hw_register_gate(dev, clk_name, parent_name, 169 CLK_SET_RATE_PARENT, 170 regs + TCON_TOP_GATE_SRC_REG, 171 bit, 0, lock); 172 }; 173 174 static int sun8i_tcon_top_bind(struct device *dev, struct device *master, 175 void *data) 176 { 177 struct platform_device *pdev = to_platform_device(dev); 178 struct clk_hw_onecell_data *clk_data; 179 struct sun8i_tcon_top *tcon_top; 180 const struct sun8i_tcon_top_quirks *quirks; 181 void __iomem *regs; 182 int ret, i; 183 184 quirks = of_device_get_match_data(&pdev->dev); 185 186 tcon_top = devm_kzalloc(dev, sizeof(*tcon_top), GFP_KERNEL); 187 if (!tcon_top) 188 return -ENOMEM; 189 190 clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, CLK_NUM), 191 GFP_KERNEL); 192 if (!clk_data) 193 return -ENOMEM; 194 clk_data->num = CLK_NUM; 195 tcon_top->clk_data = clk_data; 196 tcon_top->tcon_map = sun8i_tcon_top_get_tcon_map(dev->of_node); 197 198 spin_lock_init(&tcon_top->reg_lock); 199 200 tcon_top->rst = devm_reset_control_get(dev, NULL); 201 if (IS_ERR(tcon_top->rst)) { 202 dev_err(dev, "Couldn't get our reset line\n"); 203 return PTR_ERR(tcon_top->rst); 204 } 205 206 tcon_top->bus = devm_clk_get(dev, "bus"); 207 if (IS_ERR(tcon_top->bus)) { 208 dev_err(dev, "Couldn't get the bus clock\n"); 209 return PTR_ERR(tcon_top->bus); 210 } 211 212 regs = devm_platform_ioremap_resource(pdev, 0); 213 tcon_top->regs = regs; 214 if (IS_ERR(regs)) 215 return PTR_ERR(regs); 216 217 ret = reset_control_deassert(tcon_top->rst); 218 if (ret) { 219 dev_err(dev, "Could not deassert ctrl reset control\n"); 220 return ret; 221 } 222 223 ret = clk_prepare_enable(tcon_top->bus); 224 if (ret) { 225 dev_err(dev, "Could not enable bus clock\n"); 226 goto err_assert_reset; 227 } 228 229 writel(0, regs + TCON_TOP_PORT_SEL_REG); 230 writel(0, regs + TCON_TOP_GATE_SRC_REG); 231 232 /* 233 * TCON TOP has two muxes, which select parent clock for each TCON TV 234 * channel clock. Parent could be either TCON TV or TVE clock. For now 235 * we leave this fixed to TCON TV, since TVE driver for R40 is not yet 236 * implemented. Once it is, graph needs to be traversed to determine 237 * if TVE is active on each TCON TV. If it is, mux should be switched 238 * to TVE clock parent. 239 */ 240 i = 0; 241 clk_data->hws[CLK_TCON_TOP_TV0] = 242 sun8i_tcon_top_register_gate(dev, "tcon-tv0", regs, 243 &tcon_top->reg_lock, 244 TCON_TOP_TCON_TV0_GATE, i++); 245 246 if (quirks->has_tcon_tv1) 247 clk_data->hws[CLK_TCON_TOP_TV1] = 248 sun8i_tcon_top_register_gate(dev, "tcon-tv1", regs, 249 &tcon_top->reg_lock, 250 TCON_TOP_TCON_TV1_GATE, i++); 251 252 if (quirks->has_dsi) 253 clk_data->hws[CLK_TCON_TOP_DSI] = 254 sun8i_tcon_top_register_gate(dev, "dsi", regs, 255 &tcon_top->reg_lock, 256 TCON_TOP_TCON_DSI_GATE, i++); 257 258 for (i = 0; i < CLK_NUM; i++) 259 if (IS_ERR(clk_data->hws[i])) { 260 ret = PTR_ERR(clk_data->hws[i]); 261 goto err_unregister_gates; 262 } 263 264 ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, 265 clk_data); 266 if (ret) 267 goto err_unregister_gates; 268 269 dev_set_drvdata(dev, tcon_top); 270 271 return 0; 272 273 err_unregister_gates: 274 for (i = 0; i < CLK_NUM; i++) 275 if (!IS_ERR_OR_NULL(clk_data->hws[i])) 276 clk_hw_unregister_gate(clk_data->hws[i]); 277 clk_disable_unprepare(tcon_top->bus); 278 err_assert_reset: 279 reset_control_assert(tcon_top->rst); 280 281 return ret; 282 } 283 284 static void sun8i_tcon_top_unbind(struct device *dev, struct device *master, 285 void *data) 286 { 287 struct sun8i_tcon_top *tcon_top = dev_get_drvdata(dev); 288 struct clk_hw_onecell_data *clk_data = tcon_top->clk_data; 289 int i; 290 291 of_clk_del_provider(dev->of_node); 292 for (i = 0; i < CLK_NUM; i++) 293 if (clk_data->hws[i]) 294 clk_hw_unregister_gate(clk_data->hws[i]); 295 296 clk_disable_unprepare(tcon_top->bus); 297 reset_control_assert(tcon_top->rst); 298 } 299 300 static const struct component_ops sun8i_tcon_top_ops = { 301 .bind = sun8i_tcon_top_bind, 302 .unbind = sun8i_tcon_top_unbind, 303 }; 304 305 static int sun8i_tcon_top_probe(struct platform_device *pdev) 306 { 307 return component_add(&pdev->dev, &sun8i_tcon_top_ops); 308 } 309 310 static void sun8i_tcon_top_remove(struct platform_device *pdev) 311 { 312 component_del(&pdev->dev, &sun8i_tcon_top_ops); 313 } 314 315 static const struct sun8i_tcon_top_quirks sun8i_r40_tcon_top_quirks = { 316 .has_tcon_tv1 = true, 317 .has_dsi = true, 318 }; 319 320 static const struct sun8i_tcon_top_quirks sun20i_d1_tcon_top_quirks = { 321 .has_dsi = true, 322 }; 323 324 static const struct sun8i_tcon_top_quirks sun50i_h6_tcon_top_quirks = { 325 /* Nothing special */ 326 }; 327 328 /* sun4i_drv uses this list to check if a device node is a TCON TOP */ 329 const struct of_device_id sun8i_tcon_top_of_table[] = { 330 { 331 .compatible = "allwinner,sun8i-r40-tcon-top", 332 .data = &sun8i_r40_tcon_top_quirks 333 }, 334 { 335 .compatible = "allwinner,sun20i-d1-tcon-top", 336 .data = &sun20i_d1_tcon_top_quirks 337 }, 338 { 339 .compatible = "allwinner,sun50i-h6-tcon-top", 340 .data = &sun50i_h6_tcon_top_quirks 341 }, 342 { /* sentinel */ } 343 }; 344 MODULE_DEVICE_TABLE(of, sun8i_tcon_top_of_table); 345 EXPORT_SYMBOL(sun8i_tcon_top_of_table); 346 347 static struct platform_driver sun8i_tcon_top_platform_driver = { 348 .probe = sun8i_tcon_top_probe, 349 .remove = sun8i_tcon_top_remove, 350 .driver = { 351 .name = "sun8i-tcon-top", 352 .of_match_table = sun8i_tcon_top_of_table, 353 }, 354 }; 355 module_platform_driver(sun8i_tcon_top_platform_driver); 356 357 MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@siol.net>"); 358 MODULE_DESCRIPTION("Allwinner R40 TCON TOP driver"); 359 MODULE_LICENSE("GPL"); 360