Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | // SPDX-License-Identifier: GPL-2.0+ /* * R-Car Gen3 HDMI PHY * * Copyright (C) 2016 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) */ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <drm/bridge/dw_hdmi.h> #include <drm/drm_modes.h> #define RCAR_HDMI_PHY_OPMODE_PLLCFG 0x06 /* Mode of operation and PLL dividers */ #define RCAR_HDMI_PHY_PLLCURRGMPCTRL 0x10 /* PLL current and Gmp (conductance) */ #define RCAR_HDMI_PHY_PLLDIVCTRL 0x11 /* PLL dividers */ struct rcar_hdmi_phy_params { unsigned long mpixelclock; u16 opmode_div; /* Mode of operation and PLL dividers */ u16 curr_gmp; /* PLL current and Gmp (conductance) */ u16 div; /* PLL dividers */ }; static const struct rcar_hdmi_phy_params rcar_hdmi_phy_params[] = { { 35500000, 0x0003, 0x0344, 0x0328 }, { 44900000, 0x0003, 0x0285, 0x0128 }, { 71000000, 0x0002, 0x1184, 0x0314 }, { 90000000, 0x0002, 0x1144, 0x0114 }, { 140250000, 0x0001, 0x20c4, 0x030a }, { 182750000, 0x0001, 0x2084, 0x010a }, { 281250000, 0x0000, 0x0084, 0x0305 }, { 297000000, 0x0000, 0x0084, 0x0105 }, { ~0UL, 0x0000, 0x0000, 0x0000 }, }; static enum drm_mode_status rcar_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data, const struct drm_display_info *info, const struct drm_display_mode *mode) { /* * The maximum supported clock frequency is 297 MHz, as shown in the PHY * parameters table. */ if (mode->clock > 297000) return MODE_CLOCK_HIGH; return MODE_OK; } static int rcar_hdmi_phy_configure(struct dw_hdmi *hdmi, void *data, unsigned long mpixelclock) { const struct rcar_hdmi_phy_params *params = rcar_hdmi_phy_params; for (; params->mpixelclock != ~0UL; ++params) { if (mpixelclock <= params->mpixelclock) break; } if (params->mpixelclock == ~0UL) return -EINVAL; dw_hdmi_phy_i2c_write(hdmi, params->opmode_div, RCAR_HDMI_PHY_OPMODE_PLLCFG); dw_hdmi_phy_i2c_write(hdmi, params->curr_gmp, RCAR_HDMI_PHY_PLLCURRGMPCTRL); dw_hdmi_phy_i2c_write(hdmi, params->div, RCAR_HDMI_PHY_PLLDIVCTRL); return 0; } static const struct dw_hdmi_plat_data rcar_dw_hdmi_plat_data = { .output_port = 1, .mode_valid = rcar_hdmi_mode_valid, .configure_phy = rcar_hdmi_phy_configure, }; static int rcar_dw_hdmi_probe(struct platform_device *pdev) { struct dw_hdmi *hdmi; hdmi = dw_hdmi_probe(pdev, &rcar_dw_hdmi_plat_data); if (IS_ERR(hdmi)) return PTR_ERR(hdmi); platform_set_drvdata(pdev, hdmi); return 0; } static int rcar_dw_hdmi_remove(struct platform_device *pdev) { struct dw_hdmi *hdmi = platform_get_drvdata(pdev); dw_hdmi_remove(hdmi); return 0; } static const struct of_device_id rcar_dw_hdmi_of_table[] = { { .compatible = "renesas,rcar-gen3-hdmi" }, { /* Sentinel */ }, }; MODULE_DEVICE_TABLE(of, rcar_dw_hdmi_of_table); static struct platform_driver rcar_dw_hdmi_platform_driver = { .probe = rcar_dw_hdmi_probe, .remove = rcar_dw_hdmi_remove, .driver = { .name = "rcar-dw-hdmi", .of_match_table = rcar_dw_hdmi_of_table, }, }; module_platform_driver(rcar_dw_hdmi_platform_driver); MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); MODULE_DESCRIPTION("Renesas R-Car Gen3 HDMI Encoder Driver"); MODULE_LICENSE("GPL"); |