cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sun8i_dw_hdmi.c (7586B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Copyright (c) 2018 Jernej Skrabec <jernej.skrabec@siol.net>
      4 */
      5
      6#include <linux/component.h>
      7#include <linux/module.h>
      8#include <linux/of_device.h>
      9#include <linux/platform_device.h>
     10
     11#include <drm/drm_crtc_helper.h>
     12#include <drm/drm_of.h>
     13#include <drm/drm_simple_kms_helper.h>
     14
     15#include "sun8i_dw_hdmi.h"
     16#include "sun8i_tcon_top.h"
     17
     18static void sun8i_dw_hdmi_encoder_mode_set(struct drm_encoder *encoder,
     19					   struct drm_display_mode *mode,
     20					   struct drm_display_mode *adj_mode)
     21{
     22	struct sun8i_dw_hdmi *hdmi = encoder_to_sun8i_dw_hdmi(encoder);
     23
     24	clk_set_rate(hdmi->clk_tmds, mode->crtc_clock * 1000);
     25}
     26
     27static const struct drm_encoder_helper_funcs
     28sun8i_dw_hdmi_encoder_helper_funcs = {
     29	.mode_set = sun8i_dw_hdmi_encoder_mode_set,
     30};
     31
     32static enum drm_mode_status
     33sun8i_dw_hdmi_mode_valid_a83t(struct dw_hdmi *hdmi, void *data,
     34			      const struct drm_display_info *info,
     35			      const struct drm_display_mode *mode)
     36{
     37	if (mode->clock > 297000)
     38		return MODE_CLOCK_HIGH;
     39
     40	return MODE_OK;
     41}
     42
     43static enum drm_mode_status
     44sun8i_dw_hdmi_mode_valid_h6(struct dw_hdmi *hdmi, void *data,
     45			    const struct drm_display_info *info,
     46			    const struct drm_display_mode *mode)
     47{
     48	/*
     49	 * Controller support maximum of 594 MHz, which correlates to
     50	 * 4K@60Hz 4:4:4 or RGB.
     51	 */
     52	if (mode->clock > 594000)
     53		return MODE_CLOCK_HIGH;
     54
     55	return MODE_OK;
     56}
     57
     58static bool sun8i_dw_hdmi_node_is_tcon_top(struct device_node *node)
     59{
     60	return IS_ENABLED(CONFIG_DRM_SUN8I_TCON_TOP) &&
     61		!!of_match_node(sun8i_tcon_top_of_table, node);
     62}
     63
     64static u32 sun8i_dw_hdmi_find_possible_crtcs(struct drm_device *drm,
     65					     struct device_node *node)
     66{
     67	struct device_node *port, *ep, *remote, *remote_port;
     68	u32 crtcs = 0;
     69
     70	remote = of_graph_get_remote_node(node, 0, -1);
     71	if (!remote)
     72		return 0;
     73
     74	if (sun8i_dw_hdmi_node_is_tcon_top(remote)) {
     75		port = of_graph_get_port_by_id(remote, 4);
     76		if (!port)
     77			goto crtcs_exit;
     78
     79		for_each_child_of_node(port, ep) {
     80			remote_port = of_graph_get_remote_port(ep);
     81			if (remote_port) {
     82				crtcs |= drm_of_crtc_port_mask(drm, remote_port);
     83				of_node_put(remote_port);
     84			}
     85		}
     86	} else {
     87		crtcs = drm_of_find_possible_crtcs(drm, node);
     88	}
     89
     90crtcs_exit:
     91	of_node_put(remote);
     92
     93	return crtcs;
     94}
     95
     96static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
     97			      void *data)
     98{
     99	struct platform_device *pdev = to_platform_device(dev);
    100	struct dw_hdmi_plat_data *plat_data;
    101	struct drm_device *drm = data;
    102	struct device_node *phy_node;
    103	struct drm_encoder *encoder;
    104	struct sun8i_dw_hdmi *hdmi;
    105	int ret;
    106
    107	if (!pdev->dev.of_node)
    108		return -ENODEV;
    109
    110	hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
    111	if (!hdmi)
    112		return -ENOMEM;
    113
    114	plat_data = &hdmi->plat_data;
    115	hdmi->dev = &pdev->dev;
    116	encoder = &hdmi->encoder;
    117
    118	hdmi->quirks = of_device_get_match_data(dev);
    119
    120	encoder->possible_crtcs =
    121		sun8i_dw_hdmi_find_possible_crtcs(drm, dev->of_node);
    122	/*
    123	 * If we failed to find the CRTC(s) which this encoder is
    124	 * supposed to be connected to, it's because the CRTC has
    125	 * not been registered yet.  Defer probing, and hope that
    126	 * the required CRTC is added later.
    127	 */
    128	if (encoder->possible_crtcs == 0)
    129		return -EPROBE_DEFER;
    130
    131	hdmi->rst_ctrl = devm_reset_control_get(dev, "ctrl");
    132	if (IS_ERR(hdmi->rst_ctrl))
    133		return dev_err_probe(dev, PTR_ERR(hdmi->rst_ctrl),
    134				     "Could not get ctrl reset control\n");
    135
    136	hdmi->clk_tmds = devm_clk_get(dev, "tmds");
    137	if (IS_ERR(hdmi->clk_tmds))
    138		return dev_err_probe(dev, PTR_ERR(hdmi->clk_tmds),
    139				     "Couldn't get the tmds clock\n");
    140
    141	hdmi->regulator = devm_regulator_get(dev, "hvcc");
    142	if (IS_ERR(hdmi->regulator))
    143		return dev_err_probe(dev, PTR_ERR(hdmi->regulator),
    144				     "Couldn't get regulator\n");
    145
    146	ret = regulator_enable(hdmi->regulator);
    147	if (ret) {
    148		dev_err(dev, "Failed to enable regulator\n");
    149		return ret;
    150	}
    151
    152	ret = reset_control_deassert(hdmi->rst_ctrl);
    153	if (ret) {
    154		dev_err(dev, "Could not deassert ctrl reset control\n");
    155		goto err_disable_regulator;
    156	}
    157
    158	ret = clk_prepare_enable(hdmi->clk_tmds);
    159	if (ret) {
    160		dev_err(dev, "Could not enable tmds clock\n");
    161		goto err_assert_ctrl_reset;
    162	}
    163
    164	phy_node = of_parse_phandle(dev->of_node, "phys", 0);
    165	if (!phy_node) {
    166		dev_err(dev, "Can't found PHY phandle\n");
    167		ret = -EINVAL;
    168		goto err_disable_clk_tmds;
    169	}
    170
    171	ret = sun8i_hdmi_phy_get(hdmi, phy_node);
    172	of_node_put(phy_node);
    173	if (ret) {
    174		dev_err(dev, "Couldn't get the HDMI PHY\n");
    175		goto err_disable_clk_tmds;
    176	}
    177
    178	ret = sun8i_hdmi_phy_init(hdmi->phy);
    179	if (ret)
    180		goto err_disable_clk_tmds;
    181
    182	drm_encoder_helper_add(encoder, &sun8i_dw_hdmi_encoder_helper_funcs);
    183	drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
    184
    185	plat_data->mode_valid = hdmi->quirks->mode_valid;
    186	plat_data->use_drm_infoframe = hdmi->quirks->use_drm_infoframe;
    187	sun8i_hdmi_phy_set_ops(hdmi->phy, plat_data);
    188
    189	platform_set_drvdata(pdev, hdmi);
    190
    191	hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
    192
    193	/*
    194	 * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(),
    195	 * which would have called the encoder cleanup.  Do it manually.
    196	 */
    197	if (IS_ERR(hdmi->hdmi)) {
    198		ret = PTR_ERR(hdmi->hdmi);
    199		goto cleanup_encoder;
    200	}
    201
    202	return 0;
    203
    204cleanup_encoder:
    205	drm_encoder_cleanup(encoder);
    206err_disable_clk_tmds:
    207	clk_disable_unprepare(hdmi->clk_tmds);
    208err_assert_ctrl_reset:
    209	reset_control_assert(hdmi->rst_ctrl);
    210err_disable_regulator:
    211	regulator_disable(hdmi->regulator);
    212
    213	return ret;
    214}
    215
    216static void sun8i_dw_hdmi_unbind(struct device *dev, struct device *master,
    217				 void *data)
    218{
    219	struct sun8i_dw_hdmi *hdmi = dev_get_drvdata(dev);
    220
    221	dw_hdmi_unbind(hdmi->hdmi);
    222	sun8i_hdmi_phy_deinit(hdmi->phy);
    223	clk_disable_unprepare(hdmi->clk_tmds);
    224	reset_control_assert(hdmi->rst_ctrl);
    225	regulator_disable(hdmi->regulator);
    226}
    227
    228static const struct component_ops sun8i_dw_hdmi_ops = {
    229	.bind	= sun8i_dw_hdmi_bind,
    230	.unbind	= sun8i_dw_hdmi_unbind,
    231};
    232
    233static int sun8i_dw_hdmi_probe(struct platform_device *pdev)
    234{
    235	return component_add(&pdev->dev, &sun8i_dw_hdmi_ops);
    236}
    237
    238static int sun8i_dw_hdmi_remove(struct platform_device *pdev)
    239{
    240	component_del(&pdev->dev, &sun8i_dw_hdmi_ops);
    241
    242	return 0;
    243}
    244
    245static const struct sun8i_dw_hdmi_quirks sun8i_a83t_quirks = {
    246	.mode_valid = sun8i_dw_hdmi_mode_valid_a83t,
    247};
    248
    249static const struct sun8i_dw_hdmi_quirks sun50i_h6_quirks = {
    250	.mode_valid = sun8i_dw_hdmi_mode_valid_h6,
    251	.use_drm_infoframe = true,
    252};
    253
    254static const struct of_device_id sun8i_dw_hdmi_dt_ids[] = {
    255	{
    256		.compatible = "allwinner,sun8i-a83t-dw-hdmi",
    257		.data = &sun8i_a83t_quirks,
    258	},
    259	{
    260		.compatible = "allwinner,sun50i-h6-dw-hdmi",
    261		.data = &sun50i_h6_quirks,
    262	},
    263	{ /* sentinel */ },
    264};
    265MODULE_DEVICE_TABLE(of, sun8i_dw_hdmi_dt_ids);
    266
    267static struct platform_driver sun8i_dw_hdmi_pltfm_driver = {
    268	.probe  = sun8i_dw_hdmi_probe,
    269	.remove = sun8i_dw_hdmi_remove,
    270	.driver = {
    271		.name = "sun8i-dw-hdmi",
    272		.of_match_table = sun8i_dw_hdmi_dt_ids,
    273	},
    274};
    275
    276static int __init sun8i_dw_hdmi_init(void)
    277{
    278	int ret;
    279
    280	ret = platform_driver_register(&sun8i_dw_hdmi_pltfm_driver);
    281	if (ret)
    282		return ret;
    283
    284	ret = platform_driver_register(&sun8i_hdmi_phy_driver);
    285	if (ret) {
    286		platform_driver_unregister(&sun8i_dw_hdmi_pltfm_driver);
    287		return ret;
    288	}
    289
    290	return ret;
    291}
    292
    293static void __exit sun8i_dw_hdmi_exit(void)
    294{
    295	platform_driver_unregister(&sun8i_dw_hdmi_pltfm_driver);
    296	platform_driver_unregister(&sun8i_hdmi_phy_driver);
    297}
    298
    299module_init(sun8i_dw_hdmi_init);
    300module_exit(sun8i_dw_hdmi_exit);
    301
    302MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@siol.net>");
    303MODULE_DESCRIPTION("Allwinner DW HDMI bridge");
    304MODULE_LICENSE("GPL");