| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0+ |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * i.MX drm driver - LVDS display bridge |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2012 Sascha Hauer, Pengutronix |
|---|
| 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 |
|---|
| 8 | | - * as published by the Free Software Foundation; either version 2 |
|---|
| 9 | | - * of the License, or (at your option) any later version. |
|---|
| 10 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 11 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 12 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 13 | | - * GNU General Public License for more details. |
|---|
| 14 | 6 | */ |
|---|
| 15 | 7 | |
|---|
| 16 | | -#include <linux/module.h> |
|---|
| 17 | 8 | #include <linux/clk.h> |
|---|
| 18 | 9 | #include <linux/component.h> |
|---|
| 19 | | -#include <drm/drmP.h> |
|---|
| 20 | | -#include <drm/drm_atomic.h> |
|---|
| 21 | | -#include <drm/drm_atomic_helper.h> |
|---|
| 22 | | -#include <drm/drm_fb_helper.h> |
|---|
| 23 | | -#include <drm/drm_crtc_helper.h> |
|---|
| 24 | | -#include <drm/drm_of.h> |
|---|
| 25 | | -#include <drm/drm_panel.h> |
|---|
| 26 | 10 | #include <linux/mfd/syscon.h> |
|---|
| 27 | 11 | #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> |
|---|
| 12 | +#include <linux/module.h> |
|---|
| 28 | 13 | #include <linux/of_device.h> |
|---|
| 29 | 14 | #include <linux/of_graph.h> |
|---|
| 30 | | -#include <video/of_display_timing.h> |
|---|
| 31 | | -#include <video/of_videomode.h> |
|---|
| 32 | 15 | #include <linux/regmap.h> |
|---|
| 33 | 16 | #include <linux/videodev2.h> |
|---|
| 17 | + |
|---|
| 18 | +#include <video/of_display_timing.h> |
|---|
| 19 | +#include <video/of_videomode.h> |
|---|
| 20 | + |
|---|
| 21 | +#include <drm/drm_atomic.h> |
|---|
| 22 | +#include <drm/drm_atomic_helper.h> |
|---|
| 23 | +#include <drm/drm_bridge.h> |
|---|
| 24 | +#include <drm/drm_fb_helper.h> |
|---|
| 25 | +#include <drm/drm_of.h> |
|---|
| 26 | +#include <drm/drm_panel.h> |
|---|
| 27 | +#include <drm/drm_print.h> |
|---|
| 28 | +#include <drm/drm_probe_helper.h> |
|---|
| 29 | +#include <drm/drm_simple_kms_helper.h> |
|---|
| 34 | 30 | |
|---|
| 35 | 31 | #include "imx-drm.h" |
|---|
| 36 | 32 | |
|---|
| .. | .. |
|---|
| 66 | 62 | struct i2c_adapter *ddc; |
|---|
| 67 | 63 | int chno; |
|---|
| 68 | 64 | void *edid; |
|---|
| 69 | | - int edid_len; |
|---|
| 70 | 65 | struct drm_display_mode mode; |
|---|
| 71 | 66 | int mode_valid; |
|---|
| 72 | 67 | u32 bus_format; |
|---|
| .. | .. |
|---|
| 130 | 125 | static int imx_ldb_connector_get_modes(struct drm_connector *connector) |
|---|
| 131 | 126 | { |
|---|
| 132 | 127 | struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector); |
|---|
| 133 | | - int num_modes = 0; |
|---|
| 128 | + int num_modes; |
|---|
| 134 | 129 | |
|---|
| 135 | | - if (imx_ldb_ch->panel && imx_ldb_ch->panel->funcs && |
|---|
| 136 | | - imx_ldb_ch->panel->funcs->get_modes) { |
|---|
| 137 | | - num_modes = imx_ldb_ch->panel->funcs->get_modes(imx_ldb_ch->panel); |
|---|
| 138 | | - if (num_modes > 0) |
|---|
| 139 | | - return num_modes; |
|---|
| 140 | | - } |
|---|
| 130 | + num_modes = drm_panel_get_modes(imx_ldb_ch->panel, connector); |
|---|
| 131 | + if (num_modes > 0) |
|---|
| 132 | + return num_modes; |
|---|
| 141 | 133 | |
|---|
| 142 | 134 | if (!imx_ldb_ch->edid && imx_ldb_ch->ddc) |
|---|
| 143 | 135 | imx_ldb_ch->edid = drm_get_edid(connector, imx_ldb_ch->ddc); |
|---|
| .. | .. |
|---|
| 161 | 153 | } |
|---|
| 162 | 154 | |
|---|
| 163 | 155 | return num_modes; |
|---|
| 164 | | -} |
|---|
| 165 | | - |
|---|
| 166 | | -static struct drm_encoder *imx_ldb_connector_best_encoder( |
|---|
| 167 | | - struct drm_connector *connector) |
|---|
| 168 | | -{ |
|---|
| 169 | | - struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector); |
|---|
| 170 | | - |
|---|
| 171 | | - return &imx_ldb_ch->encoder; |
|---|
| 172 | 156 | } |
|---|
| 173 | 157 | |
|---|
| 174 | 158 | static void imx_ldb_set_clock(struct imx_ldb *ldb, int mux, int chno, |
|---|
| .. | .. |
|---|
| 409 | 393 | |
|---|
| 410 | 394 | static const struct drm_connector_helper_funcs imx_ldb_connector_helper_funcs = { |
|---|
| 411 | 395 | .get_modes = imx_ldb_connector_get_modes, |
|---|
| 412 | | - .best_encoder = imx_ldb_connector_best_encoder, |
|---|
| 413 | | -}; |
|---|
| 414 | | - |
|---|
| 415 | | -static const struct drm_encoder_funcs imx_ldb_encoder_funcs = { |
|---|
| 416 | | - .destroy = imx_drm_encoder_destroy, |
|---|
| 417 | 396 | }; |
|---|
| 418 | 397 | |
|---|
| 419 | 398 | static const struct drm_encoder_helper_funcs imx_ldb_encoder_helper_funcs = { |
|---|
| .. | .. |
|---|
| 460 | 439 | } |
|---|
| 461 | 440 | |
|---|
| 462 | 441 | drm_encoder_helper_add(encoder, &imx_ldb_encoder_helper_funcs); |
|---|
| 463 | | - drm_encoder_init(drm, encoder, &imx_ldb_encoder_funcs, |
|---|
| 464 | | - DRM_MODE_ENCODER_LVDS, NULL); |
|---|
| 442 | + drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_LVDS); |
|---|
| 465 | 443 | |
|---|
| 466 | 444 | if (imx_ldb_ch->bridge) { |
|---|
| 467 | 445 | ret = drm_bridge_attach(&imx_ldb_ch->encoder, |
|---|
| 468 | | - imx_ldb_ch->bridge, NULL); |
|---|
| 446 | + imx_ldb_ch->bridge, NULL, 0); |
|---|
| 469 | 447 | if (ret) { |
|---|
| 470 | 448 | DRM_ERROR("Failed to initialize bridge with drm\n"); |
|---|
| 471 | 449 | return ret; |
|---|
| .. | .. |
|---|
| 479 | 457 | */ |
|---|
| 480 | 458 | drm_connector_helper_add(&imx_ldb_ch->connector, |
|---|
| 481 | 459 | &imx_ldb_connector_helper_funcs); |
|---|
| 482 | | - drm_connector_init(drm, &imx_ldb_ch->connector, |
|---|
| 483 | | - &imx_ldb_connector_funcs, |
|---|
| 484 | | - DRM_MODE_CONNECTOR_LVDS); |
|---|
| 460 | + drm_connector_init_with_ddc(drm, &imx_ldb_ch->connector, |
|---|
| 461 | + &imx_ldb_connector_funcs, |
|---|
| 462 | + DRM_MODE_CONNECTOR_LVDS, |
|---|
| 463 | + imx_ldb_ch->ddc); |
|---|
| 485 | 464 | drm_connector_attach_encoder(&imx_ldb_ch->connector, encoder); |
|---|
| 486 | | - } |
|---|
| 487 | | - |
|---|
| 488 | | - if (imx_ldb_ch->panel) { |
|---|
| 489 | | - ret = drm_panel_attach(imx_ldb_ch->panel, |
|---|
| 490 | | - &imx_ldb_ch->connector); |
|---|
| 491 | | - if (ret) |
|---|
| 492 | | - return ret; |
|---|
| 493 | 465 | } |
|---|
| 494 | 466 | |
|---|
| 495 | 467 | return 0; |
|---|
| 496 | 468 | } |
|---|
| 497 | | - |
|---|
| 498 | | -enum { |
|---|
| 499 | | - LVDS_BIT_MAP_SPWG, |
|---|
| 500 | | - LVDS_BIT_MAP_JEIDA |
|---|
| 501 | | -}; |
|---|
| 502 | 469 | |
|---|
| 503 | 470 | struct imx_ldb_bit_mapping { |
|---|
| 504 | 471 | u32 bus_format; |
|---|
| .. | .. |
|---|
| 578 | 545 | } |
|---|
| 579 | 546 | |
|---|
| 580 | 547 | if (!channel->ddc) { |
|---|
| 548 | + int edid_len; |
|---|
| 549 | + |
|---|
| 581 | 550 | /* if no DDC available, fallback to hardcoded EDID */ |
|---|
| 582 | 551 | dev_dbg(dev, "no ddc available\n"); |
|---|
| 583 | 552 | |
|---|
| 584 | | - edidp = of_get_property(child, "edid", |
|---|
| 585 | | - &channel->edid_len); |
|---|
| 553 | + edidp = of_get_property(child, "edid", &edid_len); |
|---|
| 586 | 554 | if (edidp) { |
|---|
| 587 | | - channel->edid = kmemdup(edidp, |
|---|
| 588 | | - channel->edid_len, |
|---|
| 589 | | - GFP_KERNEL); |
|---|
| 555 | + channel->edid = kmemdup(edidp, edid_len, GFP_KERNEL); |
|---|
| 556 | + if (!channel->edid) |
|---|
| 557 | + return -ENOMEM; |
|---|
| 590 | 558 | } else if (!channel->panel) { |
|---|
| 591 | 559 | /* fallback to display-timings node */ |
|---|
| 592 | 560 | ret = of_get_drm_display_mode(child, |
|---|
| .. | .. |
|---|
| 612 | 580 | int ret; |
|---|
| 613 | 581 | int i; |
|---|
| 614 | 582 | |
|---|
| 615 | | - imx_ldb = devm_kzalloc(dev, sizeof(*imx_ldb), GFP_KERNEL); |
|---|
| 616 | | - if (!imx_ldb) |
|---|
| 617 | | - return -ENOMEM; |
|---|
| 583 | + imx_ldb = dev_get_drvdata(dev); |
|---|
| 584 | + memset(imx_ldb, 0, sizeof(*imx_ldb)); |
|---|
| 618 | 585 | |
|---|
| 619 | 586 | imx_ldb->regmap = syscon_regmap_lookup_by_phandle(np, "gpr"); |
|---|
| 620 | 587 | if (IS_ERR(imx_ldb->regmap)) { |
|---|
| .. | .. |
|---|
| 722 | 689 | } |
|---|
| 723 | 690 | } |
|---|
| 724 | 691 | |
|---|
| 725 | | - dev_set_drvdata(dev, imx_ldb); |
|---|
| 726 | | - |
|---|
| 727 | 692 | return 0; |
|---|
| 728 | 693 | |
|---|
| 729 | 694 | free_child: |
|---|
| .. | .. |
|---|
| 740 | 705 | for (i = 0; i < 2; i++) { |
|---|
| 741 | 706 | struct imx_ldb_channel *channel = &imx_ldb->channel[i]; |
|---|
| 742 | 707 | |
|---|
| 743 | | - if (channel->panel) |
|---|
| 744 | | - drm_panel_detach(channel->panel); |
|---|
| 745 | | - |
|---|
| 746 | 708 | kfree(channel->edid); |
|---|
| 747 | 709 | i2c_put_adapter(channel->ddc); |
|---|
| 748 | 710 | } |
|---|
| .. | .. |
|---|
| 755 | 717 | |
|---|
| 756 | 718 | static int imx_ldb_probe(struct platform_device *pdev) |
|---|
| 757 | 719 | { |
|---|
| 720 | + struct imx_ldb *imx_ldb; |
|---|
| 721 | + |
|---|
| 722 | + imx_ldb = devm_kzalloc(&pdev->dev, sizeof(*imx_ldb), GFP_KERNEL); |
|---|
| 723 | + if (!imx_ldb) |
|---|
| 724 | + return -ENOMEM; |
|---|
| 725 | + |
|---|
| 726 | + platform_set_drvdata(pdev, imx_ldb); |
|---|
| 727 | + |
|---|
| 758 | 728 | return component_add(&pdev->dev, &imx_ldb_ops); |
|---|
| 759 | 729 | } |
|---|
| 760 | 730 | |
|---|