aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2014-11-28 10:50:59 -0500
committerThierry Reding <treding@nvidia.com>2015-01-27 04:14:47 -0500
commit596827196f5fad8ac77201058978128f6c06f234 (patch)
tree768475f0926f81354cb8142edf85b4369a466b6e
parent3b0e58554873d1034beef737f15c7aa46492ff98 (diff)
drm/tegra: hdmi: Demidlayer
Implement encoder and connector within the HDMI driver itself using the Tegra output helpers rather than using the Tegra output as midlayer. By doing so one level of indirection is removed and output drivers become more flexible while keeping the majority of the advantages provided by the common output helpers. Signed-off-by: Thierry Reding <treding@nvidia.com>
-rw-r--r--drivers/gpu/drm/tegra/drm.h1
-rw-r--r--drivers/gpu/drm/tegra/hdmi.c280
-rw-r--r--drivers/gpu/drm/tegra/output.c5
3 files changed, 147 insertions, 139 deletions
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index dbc1f83327ea..95b6aebfaf00 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -193,7 +193,6 @@ struct tegra_output_ops {
193}; 193};
194 194
195enum tegra_output_type { 195enum tegra_output_type {
196 TEGRA_OUTPUT_HDMI,
197 TEGRA_OUTPUT_DSI, 196 TEGRA_OUTPUT_DSI,
198 TEGRA_OUTPUT_EDP, 197 TEGRA_OUTPUT_EDP,
199}; 198};
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index d4c635148cc7..056bb2c1c426 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -9,10 +9,14 @@
9 9
10#include <linux/clk.h> 10#include <linux/clk.h>
11#include <linux/debugfs.h> 11#include <linux/debugfs.h>
12#include <linux/gpio.h>
12#include <linux/hdmi.h> 13#include <linux/hdmi.h>
13#include <linux/regulator/consumer.h> 14#include <linux/regulator/consumer.h>
14#include <linux/reset.h> 15#include <linux/reset.h>
15 16
17#include <drm/drm_crtc.h>
18#include <drm/drm_crtc_helper.h>
19
16#include "hdmi.h" 20#include "hdmi.h"
17#include "drm.h" 21#include "drm.h"
18#include "dc.h" 22#include "dc.h"
@@ -40,7 +44,6 @@ struct tegra_hdmi {
40 struct host1x_client client; 44 struct host1x_client client;
41 struct tegra_output output; 45 struct tegra_output output;
42 struct device *dev; 46 struct device *dev;
43 bool enabled;
44 47
45 struct regulator *hdmi; 48 struct regulator *hdmi;
46 struct regulator *pll; 49 struct regulator *pll;
@@ -768,53 +771,107 @@ static bool tegra_output_is_hdmi(struct tegra_output *output)
768 return drm_detect_hdmi_monitor(edid); 771 return drm_detect_hdmi_monitor(edid);
769} 772}
770 773
771static int tegra_output_hdmi_enable(struct tegra_output *output) 774static void tegra_hdmi_connector_dpms(struct drm_connector *connector,
775 int mode)
772{ 776{
773 unsigned int h_sync_width, h_front_porch, h_back_porch, i, rekey; 777}
774 struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); 778
775 struct drm_display_mode *mode = &dc->base.mode; 779static const struct drm_connector_funcs tegra_hdmi_connector_funcs = {
780 .dpms = tegra_hdmi_connector_dpms,
781 .detect = tegra_output_connector_detect,
782 .fill_modes = drm_helper_probe_single_connector_modes,
783 .destroy = tegra_output_connector_destroy,
784};
785
786static enum drm_mode_status
787tegra_hdmi_connector_mode_valid(struct drm_connector *connector,
788 struct drm_display_mode *mode)
789{
790 struct tegra_output *output = connector_to_output(connector);
776 struct tegra_hdmi *hdmi = to_hdmi(output); 791 struct tegra_hdmi *hdmi = to_hdmi(output);
777 struct device_node *node = hdmi->dev->of_node; 792 unsigned long pclk = mode->clock * 1000;
778 unsigned int pulse_start, div82, pclk; 793 enum drm_mode_status status = MODE_OK;
779 int retries = 1000; 794 struct clk *parent;
780 u32 value; 795 long err;
781 int err;
782 796
783 if (hdmi->enabled) 797 parent = clk_get_parent(hdmi->clk_parent);
784 return 0;
785 798
786 hdmi->dvi = !tegra_output_is_hdmi(output); 799 err = clk_round_rate(parent, pclk * 4);
800 if (err <= 0)
801 status = MODE_NOCLOCK;
787 802
788 pclk = mode->clock * 1000; 803 return status;
789 h_sync_width = mode->hsync_end - mode->hsync_start; 804}
790 h_back_porch = mode->htotal - mode->hsync_end;
791 h_front_porch = mode->hsync_start - mode->hdisplay;
792 805
793 err = regulator_enable(hdmi->pll); 806static const struct drm_connector_helper_funcs
807tegra_hdmi_connector_helper_funcs = {
808 .get_modes = tegra_output_connector_get_modes,
809 .mode_valid = tegra_hdmi_connector_mode_valid,
810 .best_encoder = tegra_output_connector_best_encoder,
811};
812
813static const struct drm_encoder_funcs tegra_hdmi_encoder_funcs = {
814 .destroy = tegra_output_encoder_destroy,
815};
816
817static void tegra_hdmi_encoder_dpms(struct drm_encoder *encoder, int mode)
818{
819}
820
821static bool tegra_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
822 const struct drm_display_mode *mode,
823 struct drm_display_mode *adjusted)
824{
825 struct tegra_output *output = encoder_to_output(encoder);
826 struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
827 struct tegra_hdmi *hdmi = to_hdmi(output);
828 unsigned long pclk = mode->clock * 1000;
829 int err;
830
831 err = tegra_dc_setup_clock(dc, hdmi->clk_parent, pclk, 0);
794 if (err < 0) { 832 if (err < 0) {
795 dev_err(hdmi->dev, "failed to enable PLL regulator: %d\n", err); 833 dev_err(output->dev, "failed to setup DC clock: %d\n", err);
796 return err; 834 return false;
797 } 835 }
798 836
799 err = regulator_enable(hdmi->vdd); 837 err = clk_set_rate(hdmi->clk_parent, pclk);
800 if (err < 0) { 838 if (err < 0) {
801 dev_err(hdmi->dev, "failed to enable VDD regulator: %d\n", err); 839 dev_err(output->dev, "failed to set clock rate to %lu Hz\n",
802 return err; 840 pclk);
841 return false;
803 } 842 }
804 843
805 err = clk_set_rate(hdmi->clk, pclk); 844 return true;
806 if (err < 0) 845}
807 return err;
808 846
809 err = clk_prepare_enable(hdmi->clk); 847static void tegra_hdmi_encoder_prepare(struct drm_encoder *encoder)
810 if (err < 0) { 848{
811 dev_err(hdmi->dev, "failed to enable clock: %d\n", err); 849}
812 return err;
813 }
814 850
815 reset_control_assert(hdmi->rst); 851static void tegra_hdmi_encoder_commit(struct drm_encoder *encoder)
816 usleep_range(1000, 2000); 852{
817 reset_control_deassert(hdmi->rst); 853}
854
855static void tegra_hdmi_encoder_mode_set(struct drm_encoder *encoder,
856 struct drm_display_mode *mode,
857 struct drm_display_mode *adjusted)
858{
859 unsigned int h_sync_width, h_front_porch, h_back_porch, i, rekey;
860 struct tegra_output *output = encoder_to_output(encoder);
861 struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
862 struct device_node *node = output->dev->of_node;
863 struct tegra_hdmi *hdmi = to_hdmi(output);
864 unsigned int pulse_start, div82, pclk;
865 int retries = 1000;
866 u32 value;
867 int err;
868
869 hdmi->dvi = !tegra_output_is_hdmi(output);
870
871 pclk = mode->clock * 1000;
872 h_sync_width = mode->hsync_end - mode->hsync_start;
873 h_back_porch = mode->htotal - mode->hsync_end;
874 h_front_porch = mode->hsync_start - mode->hdisplay;
818 875
819 /* power up sequence */ 876 /* power up sequence */
820 value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_PLL0); 877 value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_PLL0);
@@ -1000,104 +1057,33 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
1000 tegra_dc_commit(dc); 1057 tegra_dc_commit(dc);
1001 1058
1002 /* TODO: add HDCP support */ 1059 /* TODO: add HDCP support */
1003
1004 hdmi->enabled = true;
1005
1006 return 0;
1007} 1060}
1008 1061
1009static int tegra_output_hdmi_disable(struct tegra_output *output) 1062static void tegra_hdmi_encoder_disable(struct drm_encoder *encoder)
1010{ 1063{
1011 struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); 1064 struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
1012 struct tegra_hdmi *hdmi = to_hdmi(output);
1013 u32 value; 1065 u32 value;
1014 1066
1015 if (!hdmi->enabled)
1016 return 0;
1017
1018 /* 1067 /*
1019 * The following accesses registers of the display controller, so make 1068 * The following accesses registers of the display controller, so make
1020 * sure it's only executed when the output is attached to one. 1069 * sure it's only executed when the output is attached to one.
1021 */ 1070 */
1022 if (dc) { 1071 if (dc) {
1023 /*
1024 * XXX: We can't do this here because it causes HDMI to go
1025 * into an erroneous state with the result that HDMI won't
1026 * properly work once disabled. See also a similar symptom
1027 * for the SOR output.
1028 */
1029 /*
1030 value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
1031 value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
1032 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
1033 tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
1034 */
1035
1036 value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); 1072 value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
1037 value &= ~HDMI_ENABLE; 1073 value &= ~HDMI_ENABLE;
1038 tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); 1074 tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
1039 1075
1040 tegra_dc_commit(dc); 1076 tegra_dc_commit(dc);
1041 } 1077 }
1042
1043 clk_disable_unprepare(hdmi->clk);
1044 reset_control_assert(hdmi->rst);
1045 regulator_disable(hdmi->vdd);
1046 regulator_disable(hdmi->pll);
1047
1048 hdmi->enabled = false;
1049
1050 return 0;
1051}
1052
1053static int tegra_output_hdmi_setup_clock(struct tegra_output *output,
1054 struct clk *clk, unsigned long pclk,
1055 unsigned int *div)
1056{
1057 struct tegra_hdmi *hdmi = to_hdmi(output);
1058 int err;
1059
1060 err = clk_set_parent(clk, hdmi->clk_parent);
1061 if (err < 0) {
1062 dev_err(output->dev, "failed to set parent: %d\n", err);
1063 return err;
1064 }
1065
1066 err = clk_set_rate(hdmi->clk_parent, pclk);
1067 if (err < 0)
1068 dev_err(output->dev, "failed to set clock rate to %lu Hz\n",
1069 pclk);
1070
1071 *div = 0;
1072
1073 return 0;
1074}
1075
1076static int tegra_output_hdmi_check_mode(struct tegra_output *output,
1077 struct drm_display_mode *mode,
1078 enum drm_mode_status *status)
1079{
1080 struct tegra_hdmi *hdmi = to_hdmi(output);
1081 unsigned long pclk = mode->clock * 1000;
1082 struct clk *parent;
1083 long err;
1084
1085 parent = clk_get_parent(hdmi->clk_parent);
1086
1087 err = clk_round_rate(parent, pclk * 4);
1088 if (err <= 0)
1089 *status = MODE_NOCLOCK;
1090 else
1091 *status = MODE_OK;
1092
1093 return 0;
1094} 1078}
1095 1079
1096static const struct tegra_output_ops hdmi_ops = { 1080static const struct drm_encoder_helper_funcs tegra_hdmi_encoder_helper_funcs = {
1097 .enable = tegra_output_hdmi_enable, 1081 .dpms = tegra_hdmi_encoder_dpms,
1098 .disable = tegra_output_hdmi_disable, 1082 .mode_fixup = tegra_hdmi_encoder_mode_fixup,
1099 .setup_clock = tegra_output_hdmi_setup_clock, 1083 .prepare = tegra_hdmi_encoder_prepare,
1100 .check_mode = tegra_output_hdmi_check_mode, 1084 .commit = tegra_hdmi_encoder_commit,
1085 .mode_set = tegra_hdmi_encoder_mode_set,
1086 .disable = tegra_hdmi_encoder_disable,
1101}; 1087};
1102 1088
1103static int tegra_hdmi_show_regs(struct seq_file *s, void *data) 1089static int tegra_hdmi_show_regs(struct seq_file *s, void *data)
@@ -1345,15 +1331,28 @@ static int tegra_hdmi_init(struct host1x_client *client)
1345 struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); 1331 struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
1346 int err; 1332 int err;
1347 1333
1348 hdmi->output.type = TEGRA_OUTPUT_HDMI;
1349 hdmi->output.dev = client->dev; 1334 hdmi->output.dev = client->dev;
1350 hdmi->output.ops = &hdmi_ops;
1351 1335
1352 err = tegra_output_init(drm, &hdmi->output); 1336 drm_connector_init(drm, &hdmi->output.connector,
1353 if (err < 0) { 1337 &tegra_hdmi_connector_funcs,
1354 dev_err(client->dev, "output setup failed: %d\n", err); 1338 DRM_MODE_CONNECTOR_HDMIA);
1355 return err; 1339 drm_connector_helper_add(&hdmi->output.connector,
1356 } 1340 &tegra_hdmi_connector_helper_funcs);
1341 hdmi->output.connector.dpms = DRM_MODE_DPMS_OFF;
1342
1343 drm_encoder_init(drm, &hdmi->output.encoder, &tegra_hdmi_encoder_funcs,
1344 DRM_MODE_ENCODER_TMDS);
1345 drm_encoder_helper_add(&hdmi->output.encoder,
1346 &tegra_hdmi_encoder_helper_funcs);
1347
1348 drm_mode_connector_attach_encoder(&hdmi->output.connector,
1349 &hdmi->output.encoder);
1350 drm_connector_register(&hdmi->output.connector);
1351
1352 hdmi->output.encoder.possible_crtcs = 0x3;
1353
1354 if (gpio_is_valid(hdmi->output.hpd_gpio))
1355 enable_irq(hdmi->output.hpd_irq);
1357 1356
1358 if (IS_ENABLED(CONFIG_DEBUG_FS)) { 1357 if (IS_ENABLED(CONFIG_DEBUG_FS)) {
1359 err = tegra_hdmi_debugfs_init(hdmi, drm->primary); 1358 err = tegra_hdmi_debugfs_init(hdmi, drm->primary);
@@ -1368,6 +1367,26 @@ static int tegra_hdmi_init(struct host1x_client *client)
1368 return err; 1367 return err;
1369 } 1368 }
1370 1369
1370 err = regulator_enable(hdmi->pll);
1371 if (err < 0) {
1372 dev_err(hdmi->dev, "failed to enable PLL regulator: %d\n", err);
1373 return err;
1374 }
1375
1376 err = regulator_enable(hdmi->vdd);
1377 if (err < 0) {
1378 dev_err(hdmi->dev, "failed to enable VDD regulator: %d\n", err);
1379 return err;
1380 }
1381
1382 err = clk_prepare_enable(hdmi->clk);
1383 if (err < 0) {
1384 dev_err(hdmi->dev, "failed to enable clock: %d\n", err);
1385 return err;
1386 }
1387
1388 reset_control_deassert(hdmi->rst);
1389
1371 return 0; 1390 return 0;
1372} 1391}
1373 1392
@@ -1376,6 +1395,13 @@ static int tegra_hdmi_exit(struct host1x_client *client)
1376 struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); 1395 struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
1377 int err; 1396 int err;
1378 1397
1398 tegra_output_exit(&hdmi->output);
1399
1400 clk_disable_unprepare(hdmi->clk);
1401 reset_control_assert(hdmi->rst);
1402
1403 regulator_disable(hdmi->vdd);
1404 regulator_disable(hdmi->pll);
1379 regulator_disable(hdmi->hdmi); 1405 regulator_disable(hdmi->hdmi);
1380 1406
1381 if (IS_ENABLED(CONFIG_DEBUG_FS)) { 1407 if (IS_ENABLED(CONFIG_DEBUG_FS)) {
@@ -1385,18 +1411,6 @@ static int tegra_hdmi_exit(struct host1x_client *client)
1385 err); 1411 err);
1386 } 1412 }
1387 1413
1388 err = tegra_output_disable(&hdmi->output);
1389 if (err < 0) {
1390 dev_err(client->dev, "output failed to disable: %d\n", err);
1391 return err;
1392 }
1393
1394 err = tegra_output_exit(&hdmi->output);
1395 if (err < 0) {
1396 dev_err(client->dev, "output cleanup failed: %d\n", err);
1397 return err;
1398 }
1399
1400 return 0; 1414 return 0;
1401} 1415}
1402 1416
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index f01ddfaa4c99..c7ebe8ea3868 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -274,11 +274,6 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
274 int connector, encoder; 274 int connector, encoder;
275 275
276 switch (output->type) { 276 switch (output->type) {
277 case TEGRA_OUTPUT_HDMI:
278 connector = DRM_MODE_CONNECTOR_HDMIA;
279 encoder = DRM_MODE_ENCODER_TMDS;
280 break;
281
282 case TEGRA_OUTPUT_DSI: 277 case TEGRA_OUTPUT_DSI:
283 connector = DRM_MODE_CONNECTOR_DSI; 278 connector = DRM_MODE_CONNECTOR_DSI;
284 encoder = DRM_MODE_ENCODER_DSI; 279 encoder = DRM_MODE_ENCODER_DSI;