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

Merge tag 'sunxi-drm-for-4.9' of https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux into drm-next

Allwinner DRM changes for 4.9

This tag adds the support of a new SoC to sun4i-drm (the Allwinner A33),
and the usual few fixes and enhancements

* tag 'sunxi-drm-for-4.9' of https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux:
drm/sun4i: add missing header dependencies
drm/sun4i: Add a DRC driver
drm/sun4i: backend: Handle the SAT
drm/sun4i: support A33 tcon
drm/sun4i: support TCONs without channel 1
drm/sun4i: Clear encoder->bridge if a bridge is not found
drm/sun4i: rgb: add missing calls to drm_panel_{prepare,unprepare}
drm/sun4i: Remove redundant dev_err call in sun4i_tcon_init_regmap()
drm/sun4i: Add bridge support
drm/sun4i: Move panel retrieval in RGB connector
drm/sun4i: Store TCON's device structure pointer

+365 -51
+41 -2
Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
··· 26 26 The TCON acts as a timing controller for RGB, LVDS and TV interfaces. 27 27 28 28 Required properties: 29 - - compatible: value should be "allwinner,sun5i-a13-tcon". 29 + - compatible: value must be either: 30 + * allwinner,sun5i-a13-tcon 31 + * allwinner,sun8i-a33-tcon 30 32 - reg: base address and size of memory-mapped region 31 33 - interrupts: interrupt associated to this IP 32 34 - clocks: phandles to the clocks feeding the TCON. Three are needed: 33 35 - 'ahb': the interface clocks 34 36 - 'tcon-ch0': The clock driving the TCON channel 0 35 - - 'tcon-ch1': The clock driving the TCON channel 1 36 37 - resets: phandles to the reset controllers driving the encoder 37 38 - "lcd": the reset line for the TCON channel 0 38 39 ··· 50 49 second the block connected to the TCON channel 1 (usually the TV 51 50 encoder) 52 51 52 + On the A13, there is one more clock required: 53 + - 'tcon-ch1': The clock driving the TCON channel 1 54 + 55 + DRC 56 + --- 57 + 58 + The DRC (Dynamic Range Controller), found in the latest Allwinner SoCs 59 + (A31, A23, A33), allows to dynamically adjust pixel 60 + brightness/contrast based on histogram measurements for LCD content 61 + adaptive backlight control. 62 + 63 + 64 + Required properties: 65 + - compatible: value must be one of: 66 + * allwinner,sun8i-a33-drc 67 + - reg: base address and size of the memory-mapped region. 68 + - interrupts: interrupt associated to this IP 69 + - clocks: phandles to the clocks feeding the DRC 70 + * ahb: the DRC interface clock 71 + * mod: the DRC module clock 72 + * ram: the DRC DRAM clock 73 + - clock-names: the clock names mentioned above 74 + - resets: phandles to the reset line driving the DRC 75 + 76 + - ports: A ports node with endpoint definitions as defined in 77 + Documentation/devicetree/bindings/media/video-interfaces.txt. The 78 + first port should be the input endpoints, the second one the outputs 53 79 54 80 Display Engine Backend 55 81 ---------------------- ··· 87 59 Required properties: 88 60 - compatible: value must be one of: 89 61 * allwinner,sun5i-a13-display-backend 62 + * allwinner,sun8i-a33-display-backend 90 63 - reg: base address and size of the memory-mapped region. 91 64 - clocks: phandles to the clocks feeding the frontend and backend 92 65 * ahb: the backend interface clock ··· 100 71 Documentation/devicetree/bindings/media/video-interfaces.txt. The 101 72 first port should be the input endpoints, the second one the output 102 73 74 + On the A33, some additional properties are required: 75 + - reg needs to have an additional region corresponding to the SAT 76 + - reg-names need to be set, with "be" and "sat" 77 + - clocks and clock-names need to have a phandle to the SAT bus 78 + clocks, whose name will be "sat" 79 + - resets and reset-names need to have a phandle to the SAT bus 80 + resets, whose name will be "sat" 81 + 103 82 Display Engine Frontend 104 83 ----------------------- 105 84 ··· 117 80 Required properties: 118 81 - compatible: value must be one of: 119 82 * allwinner,sun5i-a13-display-frontend 83 + * allwinner,sun8i-a33-display-frontend 120 84 - reg: base address and size of the memory-mapped region. 121 85 - interrupts: interrupt associated to this IP 122 86 - clocks: phandles to the clocks feeding the frontend and backend ··· 142 104 Required properties: 143 105 - compatible: value must be one of: 144 106 * allwinner,sun5i-a13-display-engine 107 + * allwinner,sun8i-a33-display-engine 145 108 146 109 - allwinner,pipelines: list of phandle to the display engine 147 110 frontends available.
+1 -1
drivers/gpu/drm/sun4i/Makefile
··· 9 9 10 10 obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o sun4i-tcon.o 11 11 obj-$(CONFIG_DRM_SUN4I) += sun4i_backend.o 12 - 12 + obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o 13 13 obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o
+61
drivers/gpu/drm/sun4i/sun4i_backend.c
··· 217 217 } 218 218 EXPORT_SYMBOL(sun4i_backend_update_layer_buffer); 219 219 220 + static int sun4i_backend_init_sat(struct device *dev) { 221 + struct sun4i_backend *backend = dev_get_drvdata(dev); 222 + int ret; 223 + 224 + backend->sat_reset = devm_reset_control_get(dev, "sat"); 225 + if (IS_ERR(backend->sat_reset)) { 226 + dev_err(dev, "Couldn't get the SAT reset line\n"); 227 + return PTR_ERR(backend->sat_reset); 228 + } 229 + 230 + ret = reset_control_deassert(backend->sat_reset); 231 + if (ret) { 232 + dev_err(dev, "Couldn't deassert the SAT reset line\n"); 233 + return ret; 234 + } 235 + 236 + backend->sat_clk = devm_clk_get(dev, "sat"); 237 + if (IS_ERR(backend->sat_clk)) { 238 + dev_err(dev, "Couldn't get our SAT clock\n"); 239 + ret = PTR_ERR(backend->sat_clk); 240 + goto err_assert_reset; 241 + } 242 + 243 + ret = clk_prepare_enable(backend->sat_clk); 244 + if (ret) { 245 + dev_err(dev, "Couldn't enable the SAT clock\n"); 246 + return ret; 247 + } 248 + 249 + return 0; 250 + 251 + err_assert_reset: 252 + reset_control_assert(backend->sat_reset); 253 + return ret; 254 + } 255 + 256 + static int sun4i_backend_free_sat(struct device *dev) { 257 + struct sun4i_backend *backend = dev_get_drvdata(dev); 258 + 259 + clk_disable_unprepare(backend->sat_clk); 260 + reset_control_assert(backend->sat_reset); 261 + 262 + return 0; 263 + } 264 + 220 265 static struct regmap_config sun4i_backend_regmap_config = { 221 266 .reg_bits = 32, 222 267 .val_bits = 32, ··· 336 291 } 337 292 clk_prepare_enable(backend->ram_clk); 338 293 294 + if (of_device_is_compatible(dev->of_node, 295 + "allwinner,sun8i-a33-display-backend")) { 296 + ret = sun4i_backend_init_sat(dev); 297 + if (ret) { 298 + dev_err(dev, "Couldn't init SAT resources\n"); 299 + goto err_disable_ram_clk; 300 + } 301 + } 302 + 339 303 /* Reset the registers */ 340 304 for (i = 0x800; i < 0x1000; i += 4) 341 305 regmap_write(backend->regs, i, 0); ··· 360 306 361 307 return 0; 362 308 309 + err_disable_ram_clk: 310 + clk_disable_unprepare(backend->ram_clk); 363 311 err_disable_mod_clk: 364 312 clk_disable_unprepare(backend->mod_clk); 365 313 err_disable_bus_clk: ··· 375 319 void *data) 376 320 { 377 321 struct sun4i_backend *backend = dev_get_drvdata(dev); 322 + 323 + if (of_device_is_compatible(dev->of_node, 324 + "allwinner,sun8i-a33-display-backend")) 325 + sun4i_backend_free_sat(dev); 378 326 379 327 clk_disable_unprepare(backend->ram_clk); 380 328 clk_disable_unprepare(backend->mod_clk); ··· 405 345 406 346 static const struct of_device_id sun4i_backend_of_table[] = { 407 347 { .compatible = "allwinner,sun5i-a13-display-backend" }, 348 + { .compatible = "allwinner,sun8i-a33-display-backend" }, 408 349 { } 409 350 }; 410 351 MODULE_DEVICE_TABLE(of, sun4i_backend_of_table);
+3
drivers/gpu/drm/sun4i/sun4i_backend.h
··· 146 146 struct clk *bus_clk; 147 147 struct clk *mod_clk; 148 148 struct clk *ram_clk; 149 + 150 + struct clk *sat_clk; 151 + struct reset_control *sat_reset; 149 152 }; 150 153 151 154 void sun4i_backend_apply_color_correction(struct sun4i_backend *backend);
+1
drivers/gpu/drm/sun4i/sun4i_dotclock.c
··· 14 14 #include <linux/regmap.h> 15 15 16 16 #include "sun4i_tcon.h" 17 + #include "sun4i_dotclock.h" 17 18 18 19 struct sun4i_dclk { 19 20 struct clk_hw hw;
+7 -5
drivers/gpu/drm/sun4i/sun4i_drv.c
··· 200 200 201 201 static bool sun4i_drv_node_is_frontend(struct device_node *node) 202 202 { 203 - return of_device_is_compatible(node, 204 - "allwinner,sun5i-a13-display-frontend"); 203 + return of_device_is_compatible(node, "allwinner,sun5i-a13-display-frontend") || 204 + of_device_is_compatible(node, "allwinner,sun8i-a33-display-frontend"); 205 205 } 206 206 207 207 static bool sun4i_drv_node_is_tcon(struct device_node *node) 208 208 { 209 - return of_device_is_compatible(node, "allwinner,sun5i-a13-tcon"); 209 + return of_device_is_compatible(node, "allwinner,sun5i-a13-tcon") || 210 + of_device_is_compatible(node, "allwinner,sun8i-a33-tcon"); 210 211 } 211 212 212 213 static int compare_of(struct device *dev, void *data) ··· 259 258 } 260 259 261 260 /* 262 - * If the node is our TCON, the first port is used for our 263 - * panel, and will not be part of the 261 + * If the node is our TCON, the first port is used for 262 + * panel or bridges, and will not be part of the 264 263 * component framework. 265 264 */ 266 265 if (sun4i_drv_node_is_tcon(node)) { ··· 322 321 323 322 static const struct of_device_id sun4i_drv_of_table[] = { 324 323 { .compatible = "allwinner,sun5i-a13-display-engine" }, 324 + { .compatible = "allwinner,sun8i-a33-display-engine" }, 325 325 { } 326 326 }; 327 327 MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
+1
drivers/gpu/drm/sun4i/sun4i_framebuffer.c
··· 15 15 #include <drm/drmP.h> 16 16 17 17 #include "sun4i_drv.h" 18 + #include "sun4i_framebuffer.h" 18 19 19 20 static void sun4i_de_output_poll_changed(struct drm_device *drm) 20 21 {
+55 -16
drivers/gpu/drm/sun4i/sun4i_rgb.c
··· 19 19 20 20 #include "sun4i_drv.h" 21 21 #include "sun4i_tcon.h" 22 + #include "sun4i_rgb.h" 22 23 23 24 struct sun4i_rgb { 24 25 struct drm_connector connector; ··· 152 151 153 152 DRM_DEBUG_DRIVER("Enabling RGB output\n"); 154 153 155 - drm_panel_enable(tcon->panel); 154 + if (!IS_ERR(tcon->panel)) { 155 + drm_panel_prepare(tcon->panel); 156 + drm_panel_enable(tcon->panel); 157 + } 158 + 159 + /* encoder->bridge can be NULL; drm_bridge_enable checks for it */ 160 + drm_bridge_enable(encoder->bridge); 161 + 156 162 sun4i_tcon_channel_enable(tcon, 0); 157 163 } 158 164 ··· 172 164 DRM_DEBUG_DRIVER("Disabling RGB output\n"); 173 165 174 166 sun4i_tcon_channel_disable(tcon, 0); 175 - drm_panel_disable(tcon->panel); 167 + 168 + /* encoder->bridge can be NULL; drm_bridge_disable checks for it */ 169 + drm_bridge_disable(encoder->bridge); 170 + 171 + if (!IS_ERR(tcon->panel)) { 172 + drm_panel_disable(tcon->panel); 173 + drm_panel_unprepare(tcon->panel); 174 + } 176 175 } 177 176 178 177 static void sun4i_rgb_encoder_mode_set(struct drm_encoder *encoder, ··· 218 203 { 219 204 struct sun4i_drv *drv = drm->dev_private; 220 205 struct sun4i_tcon *tcon = drv->tcon; 206 + struct drm_encoder *encoder; 221 207 struct sun4i_rgb *rgb; 222 208 int ret; 223 - 224 - /* If we don't have a panel, there's no point in going on */ 225 - if (IS_ERR(tcon->panel)) 226 - return -ENODEV; 227 209 228 210 rgb = devm_kzalloc(drm->dev, sizeof(*rgb), GFP_KERNEL); 229 211 if (!rgb) 230 212 return -ENOMEM; 231 213 rgb->drv = drv; 214 + encoder = &rgb->encoder; 215 + 216 + tcon->panel = sun4i_tcon_find_panel(tcon->dev->of_node); 217 + encoder->bridge = sun4i_tcon_find_bridge(tcon->dev->of_node); 218 + if (IS_ERR(tcon->panel) && IS_ERR(encoder->bridge)) { 219 + dev_info(drm->dev, "No panel or bridge found... RGB output disabled\n"); 220 + return 0; 221 + } 232 222 233 223 drm_encoder_helper_add(&rgb->encoder, 234 224 &sun4i_rgb_enc_helper_funcs); ··· 250 230 /* The RGB encoder can only work with the TCON channel 0 */ 251 231 rgb->encoder.possible_crtcs = BIT(0); 252 232 253 - drm_connector_helper_add(&rgb->connector, 254 - &sun4i_rgb_con_helper_funcs); 255 - ret = drm_connector_init(drm, &rgb->connector, 256 - &sun4i_rgb_con_funcs, 257 - DRM_MODE_CONNECTOR_Unknown); 258 - if (ret) { 259 - dev_err(drm->dev, "Couldn't initialise the rgb connector\n"); 260 - goto err_cleanup_connector; 233 + if (!IS_ERR(tcon->panel)) { 234 + drm_connector_helper_add(&rgb->connector, 235 + &sun4i_rgb_con_helper_funcs); 236 + ret = drm_connector_init(drm, &rgb->connector, 237 + &sun4i_rgb_con_funcs, 238 + DRM_MODE_CONNECTOR_Unknown); 239 + if (ret) { 240 + dev_err(drm->dev, "Couldn't initialise the rgb connector\n"); 241 + goto err_cleanup_connector; 242 + } 243 + 244 + drm_mode_connector_attach_encoder(&rgb->connector, 245 + &rgb->encoder); 246 + 247 + ret = drm_panel_attach(tcon->panel, &rgb->connector); 248 + if (ret) { 249 + dev_err(drm->dev, "Couldn't attach our panel\n"); 250 + goto err_cleanup_connector; 251 + } 261 252 } 262 253 263 - drm_mode_connector_attach_encoder(&rgb->connector, &rgb->encoder); 254 + if (!IS_ERR(encoder->bridge)) { 255 + encoder->bridge->encoder = &rgb->encoder; 264 256 265 - drm_panel_attach(tcon->panel, &rgb->connector); 257 + ret = drm_bridge_attach(drm, encoder->bridge); 258 + if (ret) { 259 + dev_err(drm->dev, "Couldn't attach our bridge\n"); 260 + goto err_cleanup_connector; 261 + } 262 + } else { 263 + encoder->bridge = NULL; 264 + } 266 265 267 266 return 0; 268 267
+71 -27
drivers/gpu/drm/sun4i/sun4i_tcon.c
··· 59 59 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, 60 60 SUN4I_TCON0_CTL_TCON_ENABLE, 0); 61 61 clk_disable_unprepare(tcon->dclk); 62 - } else if (channel == 1) { 63 - regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, 64 - SUN4I_TCON1_CTL_TCON_ENABLE, 0); 65 - clk_disable_unprepare(tcon->sclk1); 62 + return; 66 63 } 64 + 65 + WARN_ON(!tcon->has_channel_1); 66 + regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, 67 + SUN4I_TCON1_CTL_TCON_ENABLE, 0); 68 + clk_disable_unprepare(tcon->sclk1); 67 69 } 68 70 EXPORT_SYMBOL(sun4i_tcon_channel_disable); 69 71 ··· 77 75 SUN4I_TCON0_CTL_TCON_ENABLE, 78 76 SUN4I_TCON0_CTL_TCON_ENABLE); 79 77 clk_prepare_enable(tcon->dclk); 80 - } else if (channel == 1) { 81 - regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, 82 - SUN4I_TCON1_CTL_TCON_ENABLE, 83 - SUN4I_TCON1_CTL_TCON_ENABLE); 84 - clk_prepare_enable(tcon->sclk1); 78 + return; 85 79 } 80 + 81 + WARN_ON(!tcon->has_channel_1); 82 + regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, 83 + SUN4I_TCON1_CTL_TCON_ENABLE, 84 + SUN4I_TCON1_CTL_TCON_ENABLE); 85 + clk_prepare_enable(tcon->sclk1); 86 86 } 87 87 EXPORT_SYMBOL(sun4i_tcon_channel_enable); 88 88 ··· 201 197 unsigned int bp, hsync, vsync; 202 198 u8 clk_delay; 203 199 u32 val; 200 + 201 + WARN_ON(!tcon->has_channel_1); 204 202 205 203 /* Adjust clock delay */ 206 204 clk_delay = sun4i_tcon_get_clk_delay(mode, 1); ··· 327 321 return PTR_ERR(tcon->sclk0); 328 322 } 329 323 330 - tcon->sclk1 = devm_clk_get(dev, "tcon-ch1"); 331 - if (IS_ERR(tcon->sclk1)) { 332 - dev_err(dev, "Couldn't get the TCON channel 1 clock\n"); 333 - return PTR_ERR(tcon->sclk1); 324 + if (tcon->has_channel_1) { 325 + tcon->sclk1 = devm_clk_get(dev, "tcon-ch1"); 326 + if (IS_ERR(tcon->sclk1)) { 327 + dev_err(dev, "Couldn't get the TCON channel 1 clock\n"); 328 + return PTR_ERR(tcon->sclk1); 329 + } 334 330 } 335 331 336 332 return sun4i_dclk_create(dev, tcon); ··· 382 374 383 375 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 384 376 regs = devm_ioremap_resource(dev, res); 385 - if (IS_ERR(regs)) { 386 - dev_err(dev, "Couldn't map the TCON registers\n"); 377 + if (IS_ERR(regs)) 387 378 return PTR_ERR(regs); 388 - } 389 379 390 380 tcon->regs = devm_regmap_init_mmio(dev, regs, 391 381 &sun4i_tcon_regmap_config); ··· 404 398 return 0; 405 399 } 406 400 407 - static struct drm_panel *sun4i_tcon_find_panel(struct device_node *node) 401 + struct drm_panel *sun4i_tcon_find_panel(struct device_node *node) 408 402 { 409 403 struct device_node *port, *remote, *child; 410 404 struct device_node *end_node = NULL; ··· 438 432 return of_drm_find_panel(remote) ?: ERR_PTR(-EPROBE_DEFER); 439 433 } 440 434 435 + struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node) 436 + { 437 + struct device_node *port, *remote, *child; 438 + struct device_node *end_node = NULL; 439 + 440 + /* Inputs are listed first, then outputs */ 441 + port = of_graph_get_port_by_id(node, 1); 442 + 443 + /* 444 + * Our first output is the RGB interface where the panel will 445 + * be connected. 446 + */ 447 + for_each_child_of_node(port, child) { 448 + u32 reg; 449 + 450 + of_property_read_u32(child, "reg", &reg); 451 + if (reg == 0) 452 + end_node = child; 453 + } 454 + 455 + if (!end_node) { 456 + DRM_DEBUG_DRIVER("Missing bridge endpoint\n"); 457 + return ERR_PTR(-ENODEV); 458 + } 459 + 460 + remote = of_graph_get_remote_port_parent(end_node); 461 + if (!remote) { 462 + DRM_DEBUG_DRIVER("Enable to parse remote node\n"); 463 + return ERR_PTR(-EINVAL); 464 + } 465 + 466 + return of_drm_find_bridge(remote) ?: ERR_PTR(-EPROBE_DEFER); 467 + } 468 + 441 469 static int sun4i_tcon_bind(struct device *dev, struct device *master, 442 470 void *data) 443 471 { ··· 486 446 dev_set_drvdata(dev, tcon); 487 447 drv->tcon = tcon; 488 448 tcon->drm = drm; 449 + tcon->dev = dev; 489 450 490 - if (of_device_is_compatible(dev->of_node, "allwinner,sun5i-a13-tcon")) 451 + if (of_device_is_compatible(dev->of_node, "allwinner,sun5i-a13-tcon")) { 491 452 tcon->has_mux = true; 453 + tcon->has_channel_1 = true; 454 + } else { 455 + tcon->has_mux = false; 456 + tcon->has_channel_1 = false; 457 + } 492 458 493 459 tcon->lcd_rst = devm_reset_control_get(dev, "lcd"); 494 460 if (IS_ERR(tcon->lcd_rst)) { ··· 530 484 goto err_free_clocks; 531 485 } 532 486 533 - tcon->panel = sun4i_tcon_find_panel(dev->of_node); 534 - if (IS_ERR(tcon->panel)) { 535 - dev_info(dev, "No panel found... RGB output disabled\n"); 536 - return 0; 537 - } 538 - 539 487 ret = sun4i_rgb_init(drm); 540 488 if (ret < 0) 541 489 goto err_free_clocks; ··· 559 519 static int sun4i_tcon_probe(struct platform_device *pdev) 560 520 { 561 521 struct device_node *node = pdev->dev.of_node; 522 + struct drm_bridge *bridge; 562 523 struct drm_panel *panel; 563 524 564 525 /* 565 - * The panel is not ready. 526 + * Neither the bridge or the panel is ready. 566 527 * Defer the probe. 567 528 */ 568 529 panel = sun4i_tcon_find_panel(node); 530 + bridge = sun4i_tcon_find_bridge(node); 569 531 570 532 /* 571 533 * If we don't have a panel endpoint, just go on 572 534 */ 573 - if (PTR_ERR(panel) == -EPROBE_DEFER) { 574 - DRM_DEBUG_DRIVER("Still waiting for our panel. Deferring...\n"); 535 + if ((PTR_ERR(panel) == -EPROBE_DEFER) && 536 + (PTR_ERR(bridge) == -EPROBE_DEFER)) { 537 + DRM_DEBUG_DRIVER("Still waiting for our panel/bridge. Deferring...\n"); 575 538 return -EPROBE_DEFER; 576 539 } 577 540 ··· 590 547 591 548 static const struct of_device_id sun4i_tcon_of_table[] = { 592 549 { .compatible = "allwinner,sun5i-a13-tcon" }, 550 + { .compatible = "allwinner,sun8i-a33-tcon" }, 593 551 { } 594 552 }; 595 553 MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table);
+6
drivers/gpu/drm/sun4i/sun4i_tcon.h
··· 143 143 #define SUN4I_TCON_MAX_CHANNELS 2 144 144 145 145 struct sun4i_tcon { 146 + struct device *dev; 146 147 struct drm_device *drm; 147 148 struct regmap *regs; 148 149 ··· 164 163 bool has_mux; 165 164 166 165 struct drm_panel *panel; 166 + 167 + bool has_channel_1; 167 168 }; 169 + 170 + struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node); 171 + struct drm_panel *sun4i_tcon_find_panel(struct device_node *node); 168 172 169 173 /* Global Control */ 170 174 void sun4i_tcon_disable(struct sun4i_tcon *tcon);
+118
drivers/gpu/drm/sun4i/sun6i_drc.c
··· 1 + /* 2 + * Copyright (C) 2016 Free Electrons 3 + * 4 + * Maxime Ripard <maxime.ripard@free-electrons.com> 5 + * 6 + * This program is free software; you can redistribute it and/or 7 + * modify it under the terms of the GNU General Public License as 8 + * published by the Free Software Foundation; either version 2 of 9 + * the License, or (at your option) any later version. 10 + */ 11 + 12 + #include <linux/clk.h> 13 + #include <linux/component.h> 14 + #include <linux/module.h> 15 + #include <linux/platform_device.h> 16 + #include <linux/regmap.h> 17 + #include <linux/reset.h> 18 + 19 + struct sun6i_drc { 20 + struct clk *bus_clk; 21 + struct clk *mod_clk; 22 + struct reset_control *reset; 23 + }; 24 + 25 + static int sun6i_drc_bind(struct device *dev, struct device *master, 26 + void *data) 27 + { 28 + struct sun6i_drc *drc; 29 + int ret; 30 + 31 + drc = devm_kzalloc(dev, sizeof(*drc), GFP_KERNEL); 32 + if (!drc) 33 + return -ENOMEM; 34 + dev_set_drvdata(dev, drc); 35 + 36 + drc->reset = devm_reset_control_get(dev, NULL); 37 + if (IS_ERR(drc->reset)) { 38 + dev_err(dev, "Couldn't get our reset line\n"); 39 + return PTR_ERR(drc->reset); 40 + } 41 + 42 + ret = reset_control_deassert(drc->reset); 43 + if (ret) { 44 + dev_err(dev, "Couldn't deassert our reset line\n"); 45 + return ret; 46 + } 47 + 48 + drc->bus_clk = devm_clk_get(dev, "ahb"); 49 + if (IS_ERR(drc->bus_clk)) { 50 + dev_err(dev, "Couldn't get our bus clock\n"); 51 + ret = PTR_ERR(drc->bus_clk); 52 + goto err_assert_reset; 53 + } 54 + clk_prepare_enable(drc->bus_clk); 55 + 56 + drc->mod_clk = devm_clk_get(dev, "mod"); 57 + if (IS_ERR(drc->mod_clk)) { 58 + dev_err(dev, "Couldn't get our mod clock\n"); 59 + ret = PTR_ERR(drc->mod_clk); 60 + goto err_disable_bus_clk; 61 + } 62 + clk_prepare_enable(drc->mod_clk); 63 + 64 + return 0; 65 + 66 + err_disable_bus_clk: 67 + clk_disable_unprepare(drc->bus_clk); 68 + err_assert_reset: 69 + reset_control_assert(drc->reset); 70 + return ret; 71 + } 72 + 73 + static void sun6i_drc_unbind(struct device *dev, struct device *master, 74 + void *data) 75 + { 76 + struct sun6i_drc *drc = dev_get_drvdata(dev); 77 + 78 + clk_disable_unprepare(drc->mod_clk); 79 + clk_disable_unprepare(drc->bus_clk); 80 + reset_control_assert(drc->reset); 81 + } 82 + 83 + static struct component_ops sun6i_drc_ops = { 84 + .bind = sun6i_drc_bind, 85 + .unbind = sun6i_drc_unbind, 86 + }; 87 + 88 + static int sun6i_drc_probe(struct platform_device *pdev) 89 + { 90 + return component_add(&pdev->dev, &sun6i_drc_ops); 91 + } 92 + 93 + static int sun6i_drc_remove(struct platform_device *pdev) 94 + { 95 + component_del(&pdev->dev, &sun6i_drc_ops); 96 + 97 + return 0; 98 + } 99 + 100 + static const struct of_device_id sun6i_drc_of_table[] = { 101 + { .compatible = "allwinner,sun8i-a33-drc" }, 102 + { } 103 + }; 104 + MODULE_DEVICE_TABLE(of, sun6i_drc_of_table); 105 + 106 + static struct platform_driver sun6i_drc_platform_driver = { 107 + .probe = sun6i_drc_probe, 108 + .remove = sun6i_drc_remove, 109 + .driver = { 110 + .name = "sun6i-drc", 111 + .of_match_table = sun6i_drc_of_table, 112 + }, 113 + }; 114 + module_platform_driver(sun6i_drc_platform_driver); 115 + 116 + MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); 117 + MODULE_DESCRIPTION("Allwinner A31 Dynamic Range Control (DRC) Driver"); 118 + MODULE_LICENSE("GPL");