.. | .. |
---|
5 | 5 | |
---|
6 | 6 | #include <linux/component.h> |
---|
7 | 7 | #include <linux/module.h> |
---|
| 8 | +#include <linux/of_device.h> |
---|
8 | 9 | #include <linux/platform_device.h> |
---|
9 | 10 | |
---|
10 | | -#include <drm/drm_of.h> |
---|
11 | | -#include <drm/drmP.h> |
---|
12 | 11 | #include <drm/drm_crtc_helper.h> |
---|
| 12 | +#include <drm/drm_of.h> |
---|
| 13 | +#include <drm/drm_simple_kms_helper.h> |
---|
13 | 14 | |
---|
14 | 15 | #include "sun8i_dw_hdmi.h" |
---|
15 | 16 | #include "sun8i_tcon_top.h" |
---|
.. | .. |
---|
28 | 29 | .mode_set = sun8i_dw_hdmi_encoder_mode_set, |
---|
29 | 30 | }; |
---|
30 | 31 | |
---|
31 | | -static const struct drm_encoder_funcs sun8i_dw_hdmi_encoder_funcs = { |
---|
32 | | - .destroy = drm_encoder_cleanup, |
---|
33 | | -}; |
---|
34 | | - |
---|
35 | 32 | static enum drm_mode_status |
---|
36 | | -sun8i_dw_hdmi_mode_valid(struct drm_connector *connector, |
---|
37 | | - const struct drm_display_mode *mode) |
---|
| 33 | +sun8i_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) |
---|
38 | 36 | { |
---|
39 | 37 | if (mode->clock > 297000) |
---|
| 38 | + return MODE_CLOCK_HIGH; |
---|
| 39 | + |
---|
| 40 | + return MODE_OK; |
---|
| 41 | +} |
---|
| 42 | + |
---|
| 43 | +static enum drm_mode_status |
---|
| 44 | +sun8i_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) |
---|
40 | 53 | return MODE_CLOCK_HIGH; |
---|
41 | 54 | |
---|
42 | 55 | return MODE_OK; |
---|
.. | .. |
---|
80 | 93 | return crtcs; |
---|
81 | 94 | } |
---|
82 | 95 | |
---|
| 96 | +static int sun8i_dw_hdmi_find_connector_pdev(struct device *dev, |
---|
| 97 | + struct platform_device **pdev_out) |
---|
| 98 | +{ |
---|
| 99 | + struct platform_device *pdev; |
---|
| 100 | + struct device_node *remote; |
---|
| 101 | + |
---|
| 102 | + remote = of_graph_get_remote_node(dev->of_node, 1, -1); |
---|
| 103 | + if (!remote) |
---|
| 104 | + return -ENODEV; |
---|
| 105 | + |
---|
| 106 | + if (!of_device_is_compatible(remote, "hdmi-connector")) { |
---|
| 107 | + of_node_put(remote); |
---|
| 108 | + return -ENODEV; |
---|
| 109 | + } |
---|
| 110 | + |
---|
| 111 | + pdev = of_find_device_by_node(remote); |
---|
| 112 | + of_node_put(remote); |
---|
| 113 | + if (!pdev) |
---|
| 114 | + return -ENODEV; |
---|
| 115 | + |
---|
| 116 | + *pdev_out = pdev; |
---|
| 117 | + return 0; |
---|
| 118 | +} |
---|
| 119 | + |
---|
83 | 120 | static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master, |
---|
84 | 121 | void *data) |
---|
85 | 122 | { |
---|
86 | | - struct platform_device *pdev = to_platform_device(dev); |
---|
| 123 | + struct platform_device *pdev = to_platform_device(dev), *connector_pdev; |
---|
87 | 124 | struct dw_hdmi_plat_data *plat_data; |
---|
88 | 125 | struct drm_device *drm = data; |
---|
89 | 126 | struct device_node *phy_node; |
---|
.. | .. |
---|
101 | 138 | plat_data = &hdmi->plat_data; |
---|
102 | 139 | hdmi->dev = &pdev->dev; |
---|
103 | 140 | encoder = &hdmi->encoder; |
---|
| 141 | + |
---|
| 142 | + hdmi->quirks = of_device_get_match_data(dev); |
---|
104 | 143 | |
---|
105 | 144 | encoder->possible_crtcs = |
---|
106 | 145 | sun8i_dw_hdmi_find_possible_crtcs(drm, dev->of_node); |
---|
.. | .. |
---|
125 | 164 | return PTR_ERR(hdmi->clk_tmds); |
---|
126 | 165 | } |
---|
127 | 166 | |
---|
| 167 | + hdmi->regulator = devm_regulator_get(dev, "hvcc"); |
---|
| 168 | + if (IS_ERR(hdmi->regulator)) { |
---|
| 169 | + dev_err(dev, "Couldn't get regulator\n"); |
---|
| 170 | + return PTR_ERR(hdmi->regulator); |
---|
| 171 | + } |
---|
| 172 | + |
---|
| 173 | + ret = sun8i_dw_hdmi_find_connector_pdev(dev, &connector_pdev); |
---|
| 174 | + if (!ret) { |
---|
| 175 | + hdmi->ddc_en = gpiod_get_optional(&connector_pdev->dev, |
---|
| 176 | + "ddc-en", GPIOD_OUT_HIGH); |
---|
| 177 | + platform_device_put(connector_pdev); |
---|
| 178 | + |
---|
| 179 | + if (IS_ERR(hdmi->ddc_en)) { |
---|
| 180 | + dev_err(dev, "Couldn't get ddc-en gpio\n"); |
---|
| 181 | + return PTR_ERR(hdmi->ddc_en); |
---|
| 182 | + } |
---|
| 183 | + } |
---|
| 184 | + |
---|
| 185 | + ret = regulator_enable(hdmi->regulator); |
---|
| 186 | + if (ret) { |
---|
| 187 | + dev_err(dev, "Failed to enable regulator\n"); |
---|
| 188 | + goto err_unref_ddc_en; |
---|
| 189 | + } |
---|
| 190 | + |
---|
| 191 | + gpiod_set_value(hdmi->ddc_en, 1); |
---|
| 192 | + |
---|
128 | 193 | ret = reset_control_deassert(hdmi->rst_ctrl); |
---|
129 | 194 | if (ret) { |
---|
130 | 195 | dev_err(dev, "Could not deassert ctrl reset control\n"); |
---|
131 | | - return ret; |
---|
| 196 | + goto err_disable_ddc_en; |
---|
132 | 197 | } |
---|
133 | 198 | |
---|
134 | 199 | ret = clk_prepare_enable(hdmi->clk_tmds); |
---|
.. | .. |
---|
144 | 209 | goto err_disable_clk_tmds; |
---|
145 | 210 | } |
---|
146 | 211 | |
---|
147 | | - ret = sun8i_hdmi_phy_probe(hdmi, phy_node); |
---|
| 212 | + ret = sun8i_hdmi_phy_get(hdmi, phy_node); |
---|
148 | 213 | of_node_put(phy_node); |
---|
149 | 214 | if (ret) { |
---|
150 | 215 | dev_err(dev, "Couldn't get the HDMI PHY\n"); |
---|
151 | 216 | goto err_disable_clk_tmds; |
---|
152 | 217 | } |
---|
153 | 218 | |
---|
| 219 | + ret = sun8i_hdmi_phy_init(hdmi->phy); |
---|
| 220 | + if (ret) |
---|
| 221 | + goto err_disable_clk_tmds; |
---|
| 222 | + |
---|
154 | 223 | drm_encoder_helper_add(encoder, &sun8i_dw_hdmi_encoder_helper_funcs); |
---|
155 | | - drm_encoder_init(drm, encoder, &sun8i_dw_hdmi_encoder_funcs, |
---|
156 | | - DRM_MODE_ENCODER_TMDS, NULL); |
---|
| 224 | + drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); |
---|
157 | 225 | |
---|
158 | | - sun8i_hdmi_phy_init(hdmi->phy); |
---|
159 | | - |
---|
160 | | - plat_data->mode_valid = &sun8i_dw_hdmi_mode_valid; |
---|
161 | | - plat_data->phy_ops = sun8i_hdmi_phy_get_ops(); |
---|
162 | | - plat_data->phy_name = "sun8i_dw_hdmi_phy"; |
---|
163 | | - plat_data->phy_data = hdmi->phy; |
---|
| 226 | + plat_data->mode_valid = hdmi->quirks->mode_valid; |
---|
| 227 | + plat_data->use_drm_infoframe = hdmi->quirks->use_drm_infoframe; |
---|
| 228 | + sun8i_hdmi_phy_set_ops(hdmi->phy, plat_data); |
---|
164 | 229 | |
---|
165 | 230 | platform_set_drvdata(pdev, hdmi); |
---|
166 | 231 | |
---|
.. | .. |
---|
179 | 244 | |
---|
180 | 245 | cleanup_encoder: |
---|
181 | 246 | drm_encoder_cleanup(encoder); |
---|
182 | | - sun8i_hdmi_phy_remove(hdmi); |
---|
183 | 247 | err_disable_clk_tmds: |
---|
184 | 248 | clk_disable_unprepare(hdmi->clk_tmds); |
---|
185 | 249 | err_assert_ctrl_reset: |
---|
186 | 250 | reset_control_assert(hdmi->rst_ctrl); |
---|
| 251 | +err_disable_ddc_en: |
---|
| 252 | + gpiod_set_value(hdmi->ddc_en, 0); |
---|
| 253 | + regulator_disable(hdmi->regulator); |
---|
| 254 | +err_unref_ddc_en: |
---|
| 255 | + if (hdmi->ddc_en) |
---|
| 256 | + gpiod_put(hdmi->ddc_en); |
---|
187 | 257 | |
---|
188 | 258 | return ret; |
---|
189 | 259 | } |
---|
.. | .. |
---|
194 | 264 | struct sun8i_dw_hdmi *hdmi = dev_get_drvdata(dev); |
---|
195 | 265 | |
---|
196 | 266 | dw_hdmi_unbind(hdmi->hdmi); |
---|
197 | | - sun8i_hdmi_phy_remove(hdmi); |
---|
| 267 | + sun8i_hdmi_phy_deinit(hdmi->phy); |
---|
198 | 268 | clk_disable_unprepare(hdmi->clk_tmds); |
---|
199 | 269 | reset_control_assert(hdmi->rst_ctrl); |
---|
| 270 | + gpiod_set_value(hdmi->ddc_en, 0); |
---|
| 271 | + regulator_disable(hdmi->regulator); |
---|
| 272 | + |
---|
| 273 | + if (hdmi->ddc_en) |
---|
| 274 | + gpiod_put(hdmi->ddc_en); |
---|
200 | 275 | } |
---|
201 | 276 | |
---|
202 | 277 | static const struct component_ops sun8i_dw_hdmi_ops = { |
---|
.. | .. |
---|
216 | 291 | return 0; |
---|
217 | 292 | } |
---|
218 | 293 | |
---|
| 294 | +static const struct sun8i_dw_hdmi_quirks sun8i_a83t_quirks = { |
---|
| 295 | + .mode_valid = sun8i_dw_hdmi_mode_valid_a83t, |
---|
| 296 | +}; |
---|
| 297 | + |
---|
| 298 | +static const struct sun8i_dw_hdmi_quirks sun50i_h6_quirks = { |
---|
| 299 | + .mode_valid = sun8i_dw_hdmi_mode_valid_h6, |
---|
| 300 | + .use_drm_infoframe = true, |
---|
| 301 | +}; |
---|
| 302 | + |
---|
219 | 303 | static const struct of_device_id sun8i_dw_hdmi_dt_ids[] = { |
---|
220 | | - { .compatible = "allwinner,sun8i-a83t-dw-hdmi" }, |
---|
| 304 | + { |
---|
| 305 | + .compatible = "allwinner,sun8i-a83t-dw-hdmi", |
---|
| 306 | + .data = &sun8i_a83t_quirks, |
---|
| 307 | + }, |
---|
| 308 | + { |
---|
| 309 | + .compatible = "allwinner,sun50i-h6-dw-hdmi", |
---|
| 310 | + .data = &sun50i_h6_quirks, |
---|
| 311 | + }, |
---|
221 | 312 | { /* sentinel */ }, |
---|
222 | 313 | }; |
---|
223 | 314 | MODULE_DEVICE_TABLE(of, sun8i_dw_hdmi_dt_ids); |
---|
.. | .. |
---|
230 | 321 | .of_match_table = sun8i_dw_hdmi_dt_ids, |
---|
231 | 322 | }, |
---|
232 | 323 | }; |
---|
233 | | -module_platform_driver(sun8i_dw_hdmi_pltfm_driver); |
---|
| 324 | + |
---|
| 325 | +static int __init sun8i_dw_hdmi_init(void) |
---|
| 326 | +{ |
---|
| 327 | + int ret; |
---|
| 328 | + |
---|
| 329 | + ret = platform_driver_register(&sun8i_dw_hdmi_pltfm_driver); |
---|
| 330 | + if (ret) |
---|
| 331 | + return ret; |
---|
| 332 | + |
---|
| 333 | + ret = platform_driver_register(&sun8i_hdmi_phy_driver); |
---|
| 334 | + if (ret) { |
---|
| 335 | + platform_driver_unregister(&sun8i_dw_hdmi_pltfm_driver); |
---|
| 336 | + return ret; |
---|
| 337 | + } |
---|
| 338 | + |
---|
| 339 | + return ret; |
---|
| 340 | +} |
---|
| 341 | + |
---|
| 342 | +static void __exit sun8i_dw_hdmi_exit(void) |
---|
| 343 | +{ |
---|
| 344 | + platform_driver_unregister(&sun8i_dw_hdmi_pltfm_driver); |
---|
| 345 | + platform_driver_unregister(&sun8i_hdmi_phy_driver); |
---|
| 346 | +} |
---|
| 347 | + |
---|
| 348 | +module_init(sun8i_dw_hdmi_init); |
---|
| 349 | +module_exit(sun8i_dw_hdmi_exit); |
---|
234 | 350 | |
---|
235 | 351 | MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@siol.net>"); |
---|
236 | 352 | MODULE_DESCRIPTION("Allwinner DW HDMI bridge"); |
---|