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