brintos

brintos / linux-shallow public Read only

0
0
Text · 6.7 KiB · 0006ea5 Raw
282 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Copyright (C) 2011-2013 Freescale Semiconductor, Inc.3 *4 * derived from imx-hdmi.c(renamed to bridge/dw_hdmi.c now)5 */6 7#include <linux/component.h>8#include <linux/mfd/syscon.h>9#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>10#include <linux/module.h>11#include <linux/platform_device.h>12#include <linux/regmap.h>13 14#include <video/imx-ipu-v3.h>15 16#include <drm/bridge/dw_hdmi.h>17#include <drm/drm_atomic_helper.h>18#include <drm/drm_bridge.h>19#include <drm/drm_edid.h>20#include <drm/drm_encoder.h>21#include <drm/drm_managed.h>22#include <drm/drm_of.h>23#include <drm/drm_simple_kms_helper.h>24 25#include "imx-drm.h"26 27struct imx_hdmi;28 29struct imx_hdmi_encoder {30	struct drm_encoder encoder;31	struct imx_hdmi *hdmi;32};33 34struct imx_hdmi {35	struct device *dev;36	struct drm_bridge *bridge;37	struct dw_hdmi *hdmi;38	struct regmap *regmap;39};40 41static inline struct imx_hdmi *enc_to_imx_hdmi(struct drm_encoder *e)42{43	return container_of(e, struct imx_hdmi_encoder, encoder)->hdmi;44}45 46static const struct dw_hdmi_mpll_config imx_mpll_cfg[] = {47	{48		45250000, {49			{ 0x01e0, 0x0000 },50			{ 0x21e1, 0x0000 },51			{ 0x41e2, 0x0000 }52		},53	}, {54		92500000, {55			{ 0x0140, 0x0005 },56			{ 0x2141, 0x0005 },57			{ 0x4142, 0x0005 },58	},59	}, {60		148500000, {61			{ 0x00a0, 0x000a },62			{ 0x20a1, 0x000a },63			{ 0x40a2, 0x000a },64		},65	}, {66		216000000, {67			{ 0x00a0, 0x000a },68			{ 0x2001, 0x000f },69			{ 0x4002, 0x000f },70		},71	}, {72		~0UL, {73			{ 0x0000, 0x0000 },74			{ 0x0000, 0x0000 },75			{ 0x0000, 0x0000 },76		},77	}78};79 80static const struct dw_hdmi_curr_ctrl imx_cur_ctr[] = {81	/*      pixelclk     bpp8    bpp10   bpp12 */82	{83		54000000, { 0x091c, 0x091c, 0x06dc },84	}, {85		58400000, { 0x091c, 0x06dc, 0x06dc },86	}, {87		72000000, { 0x06dc, 0x06dc, 0x091c },88	}, {89		74250000, { 0x06dc, 0x0b5c, 0x091c },90	}, {91		118800000, { 0x091c, 0x091c, 0x06dc },92	}, {93		216000000, { 0x06dc, 0x0b5c, 0x091c },94	}, {95		~0UL, { 0x0000, 0x0000, 0x0000 },96	},97};98 99/*100 * Resistance term 133Ohm Cfg101 * PREEMP config 0.00102 * TX/CK level 10103 */104static const struct dw_hdmi_phy_config imx_phy_config[] = {105	/*pixelclk   symbol   term   vlev */106	{ 216000000, 0x800d, 0x0005, 0x01ad},107	{ ~0UL,      0x0000, 0x0000, 0x0000}108};109 110static void dw_hdmi_imx_encoder_enable(struct drm_encoder *encoder)111{112	struct imx_hdmi *hdmi = enc_to_imx_hdmi(encoder);113	int mux = drm_of_encoder_active_port_id(hdmi->dev->of_node, encoder);114 115	regmap_update_bits(hdmi->regmap, IOMUXC_GPR3,116			   IMX6Q_GPR3_HDMI_MUX_CTL_MASK,117			   mux << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT);118}119 120static int dw_hdmi_imx_atomic_check(struct drm_encoder *encoder,121				    struct drm_crtc_state *crtc_state,122				    struct drm_connector_state *conn_state)123{124	struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);125 126	imx_crtc_state->bus_format = MEDIA_BUS_FMT_RGB888_1X24;127	imx_crtc_state->di_hsync_pin = 2;128	imx_crtc_state->di_vsync_pin = 3;129 130	return 0;131}132 133static const struct drm_encoder_helper_funcs dw_hdmi_imx_encoder_helper_funcs = {134	.enable     = dw_hdmi_imx_encoder_enable,135	.atomic_check = dw_hdmi_imx_atomic_check,136};137 138static enum drm_mode_status139imx6q_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data,140		      const struct drm_display_info *info,141		      const struct drm_display_mode *mode)142{143	if (mode->clock < 13500)144		return MODE_CLOCK_LOW;145	/* FIXME: Hardware is capable of 266MHz, but setup data is missing. */146	if (mode->clock > 216000)147		return MODE_CLOCK_HIGH;148 149	return MODE_OK;150}151 152static enum drm_mode_status153imx6dl_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data,154		       const struct drm_display_info *info,155		       const struct drm_display_mode *mode)156{157	if (mode->clock < 13500)158		return MODE_CLOCK_LOW;159	/* FIXME: Hardware is capable of 270MHz, but setup data is missing. */160	if (mode->clock > 216000)161		return MODE_CLOCK_HIGH;162 163	return MODE_OK;164}165 166static struct dw_hdmi_plat_data imx6q_hdmi_drv_data = {167	.mpll_cfg   = imx_mpll_cfg,168	.cur_ctr    = imx_cur_ctr,169	.phy_config = imx_phy_config,170	.mode_valid = imx6q_hdmi_mode_valid,171};172 173static struct dw_hdmi_plat_data imx6dl_hdmi_drv_data = {174	.mpll_cfg = imx_mpll_cfg,175	.cur_ctr  = imx_cur_ctr,176	.phy_config = imx_phy_config,177	.mode_valid = imx6dl_hdmi_mode_valid,178};179 180static const struct of_device_id dw_hdmi_imx_dt_ids[] = {181	{ .compatible = "fsl,imx6q-hdmi",182	  .data = &imx6q_hdmi_drv_data183	}, {184	  .compatible = "fsl,imx6dl-hdmi",185	  .data = &imx6dl_hdmi_drv_data186	},187	{},188};189MODULE_DEVICE_TABLE(of, dw_hdmi_imx_dt_ids);190 191static int dw_hdmi_imx_bind(struct device *dev, struct device *master,192			    void *data)193{194	struct drm_device *drm = data;195	struct imx_hdmi_encoder *hdmi_encoder;196	struct drm_encoder *encoder;197	int ret;198 199	hdmi_encoder = drmm_simple_encoder_alloc(drm, struct imx_hdmi_encoder,200						 encoder, DRM_MODE_ENCODER_TMDS);201	if (IS_ERR(hdmi_encoder))202		return PTR_ERR(hdmi_encoder);203 204	hdmi_encoder->hdmi = dev_get_drvdata(dev);205	encoder = &hdmi_encoder->encoder;206 207	ret = imx_drm_encoder_parse_of(drm, encoder, dev->of_node);208	if (ret)209		return ret;210 211	drm_encoder_helper_add(encoder, &dw_hdmi_imx_encoder_helper_funcs);212 213	return drm_bridge_attach(encoder, hdmi_encoder->hdmi->bridge, NULL, 0);214}215 216static const struct component_ops dw_hdmi_imx_ops = {217	.bind	= dw_hdmi_imx_bind,218};219 220static int dw_hdmi_imx_probe(struct platform_device *pdev)221{222	struct device_node *np = pdev->dev.of_node;223	const struct of_device_id *match = of_match_node(dw_hdmi_imx_dt_ids, np);224	struct imx_hdmi *hdmi;225	int ret;226 227	hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);228	if (!hdmi)229		return -ENOMEM;230 231	platform_set_drvdata(pdev, hdmi);232	hdmi->dev = &pdev->dev;233 234	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");235	if (IS_ERR(hdmi->regmap)) {236		dev_err(hdmi->dev, "Unable to get gpr\n");237		return PTR_ERR(hdmi->regmap);238	}239 240	hdmi->hdmi = dw_hdmi_probe(pdev, match->data);241	if (IS_ERR(hdmi->hdmi))242		return PTR_ERR(hdmi->hdmi);243 244	hdmi->bridge = of_drm_find_bridge(np);245	if (!hdmi->bridge) {246		dev_err(hdmi->dev, "Unable to find bridge\n");247		dw_hdmi_remove(hdmi->hdmi);248		return -ENODEV;249	}250 251	ret = component_add(&pdev->dev, &dw_hdmi_imx_ops);252	if (ret)253		dw_hdmi_remove(hdmi->hdmi);254 255	return ret;256}257 258static void dw_hdmi_imx_remove(struct platform_device *pdev)259{260	struct imx_hdmi *hdmi = platform_get_drvdata(pdev);261 262	component_del(&pdev->dev, &dw_hdmi_imx_ops);263	dw_hdmi_remove(hdmi->hdmi);264}265 266static struct platform_driver dw_hdmi_imx_platform_driver = {267	.probe  = dw_hdmi_imx_probe,268	.remove_new = dw_hdmi_imx_remove,269	.driver = {270		.name = "dwhdmi-imx",271		.of_match_table = dw_hdmi_imx_dt_ids,272	},273};274 275module_platform_driver(dw_hdmi_imx_platform_driver);276 277MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");278MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");279MODULE_DESCRIPTION("IMX6 Specific DW-HDMI Driver Extension");280MODULE_LICENSE("GPL");281MODULE_ALIAS("platform:dwhdmi-imx");282