diff options
author | Dave Airlie <airlied@redhat.com> | 2016-12-12 21:05:12 -0500 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2016-12-12 21:05:12 -0500 |
commit | 2601a15d5d9b7f262e94b88784b1e1cf28ec020d (patch) | |
tree | 52a5434ad72100759552f18c706dc44598fa6dbc | |
parent | 25dfd7cfefeea5bddd48da5763ecbe686579c922 (diff) | |
parent | c167df443b4a8d97d25a8e69bd9f490a1e3fe646 (diff) |
Merge tag 'drm-vc4-next-2016-12-09' of https://github.com/anholt/linux into drm-next
This pull request brings in VEC (TV-out) support for vc4, along with a
pageflipping race fix.
* tag 'drm-vc4-next-2016-12-09' of https://github.com/anholt/linux:
drm/vc4: Don't use drm_put_dev
drm/vc4: Document VEC DT binding
drm/vc4: Add support for the VEC (Video Encoder) IP
drm: Add TV connector states to drm_connector_state
drm: Turn DRM_MODE_SUBCONNECTOR_xx definitions into an enum
drm/vc4: Fix ->clock_select setting for the VEC encoder
drm/vc4: Fix race between page flip completion event and clean-up
-rw-r--r-- | Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt | 14 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_atomic.c | 50 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_crtc.c | 46 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_debugfs.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_drv.c | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_drv.h | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_kms.c | 33 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_regs.h | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_vec.c | 657 | ||||
-rw-r--r-- | include/drm/drm_connector.h | 32 | ||||
-rw-r--r-- | include/uapi/drm/drm_mode.h | 18 |
12 files changed, 834 insertions, 33 deletions
diff --git a/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt b/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt index a5ea451e67fc..e2768703ac2b 100644 --- a/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt +++ b/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt | |||
@@ -43,6 +43,13 @@ Required properties for DPI: | |||
43 | - port: Port node with a single endpoint connecting to the panel | 43 | - port: Port node with a single endpoint connecting to the panel |
44 | device, as defined in [1] | 44 | device, as defined in [1] |
45 | 45 | ||
46 | Required properties for VEC: | ||
47 | - compatible: Should be "brcm,bcm2835-vec" | ||
48 | - reg: Physical base address and length of the registers | ||
49 | - clocks: The core clock the unit runs on | ||
50 | - interrupts: The interrupt number | ||
51 | See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt | ||
52 | |||
46 | Required properties for V3D: | 53 | Required properties for V3D: |
47 | - compatible: Should be "brcm,bcm2835-v3d" | 54 | - compatible: Should be "brcm,bcm2835-v3d" |
48 | - reg: Physical base address and length of the V3D's registers | 55 | - reg: Physical base address and length of the V3D's registers |
@@ -92,6 +99,13 @@ dpi: dpi@7e208000 { | |||
92 | }; | 99 | }; |
93 | }; | 100 | }; |
94 | 101 | ||
102 | vec: vec@7e806000 { | ||
103 | compatible = "brcm,bcm2835-vec"; | ||
104 | reg = <0x7e806000 0x1000>; | ||
105 | clocks = <&clocks BCM2835_CLOCK_VEC>; | ||
106 | interrupts = <2 27>; | ||
107 | }; | ||
108 | |||
95 | v3d: v3d@7ec00000 { | 109 | v3d: v3d@7ec00000 { |
96 | compatible = "brcm,bcm2835-v3d"; | 110 | compatible = "brcm,bcm2835-v3d"; |
97 | reg = <0x7ec00000 0x1000>; | 111 | reg = <0x7ec00000 0x1000>; |
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 72fa5b20baf1..60697482b94c 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c | |||
@@ -1087,12 +1087,38 @@ int drm_atomic_connector_set_property(struct drm_connector *connector, | |||
1087 | * now?) atomic writes to DPMS property: | 1087 | * now?) atomic writes to DPMS property: |
1088 | */ | 1088 | */ |
1089 | return -EINVAL; | 1089 | return -EINVAL; |
1090 | } else if (property == config->tv_select_subconnector_property) { | ||
1091 | state->tv.subconnector = val; | ||
1092 | } else if (property == config->tv_left_margin_property) { | ||
1093 | state->tv.margins.left = val; | ||
1094 | } else if (property == config->tv_right_margin_property) { | ||
1095 | state->tv.margins.right = val; | ||
1096 | } else if (property == config->tv_top_margin_property) { | ||
1097 | state->tv.margins.top = val; | ||
1098 | } else if (property == config->tv_bottom_margin_property) { | ||
1099 | state->tv.margins.bottom = val; | ||
1100 | } else if (property == config->tv_mode_property) { | ||
1101 | state->tv.mode = val; | ||
1102 | } else if (property == config->tv_brightness_property) { | ||
1103 | state->tv.brightness = val; | ||
1104 | } else if (property == config->tv_contrast_property) { | ||
1105 | state->tv.contrast = val; | ||
1106 | } else if (property == config->tv_flicker_reduction_property) { | ||
1107 | state->tv.flicker_reduction = val; | ||
1108 | } else if (property == config->tv_overscan_property) { | ||
1109 | state->tv.overscan = val; | ||
1110 | } else if (property == config->tv_saturation_property) { | ||
1111 | state->tv.saturation = val; | ||
1112 | } else if (property == config->tv_hue_property) { | ||
1113 | state->tv.hue = val; | ||
1090 | } else if (connector->funcs->atomic_set_property) { | 1114 | } else if (connector->funcs->atomic_set_property) { |
1091 | return connector->funcs->atomic_set_property(connector, | 1115 | return connector->funcs->atomic_set_property(connector, |
1092 | state, property, val); | 1116 | state, property, val); |
1093 | } else { | 1117 | } else { |
1094 | return -EINVAL; | 1118 | return -EINVAL; |
1095 | } | 1119 | } |
1120 | |||
1121 | return 0; | ||
1096 | } | 1122 | } |
1097 | EXPORT_SYMBOL(drm_atomic_connector_set_property); | 1123 | EXPORT_SYMBOL(drm_atomic_connector_set_property); |
1098 | 1124 | ||
@@ -1135,6 +1161,30 @@ drm_atomic_connector_get_property(struct drm_connector *connector, | |||
1135 | *val = (state->crtc) ? state->crtc->base.id : 0; | 1161 | *val = (state->crtc) ? state->crtc->base.id : 0; |
1136 | } else if (property == config->dpms_property) { | 1162 | } else if (property == config->dpms_property) { |
1137 | *val = connector->dpms; | 1163 | *val = connector->dpms; |
1164 | } else if (property == config->tv_select_subconnector_property) { | ||
1165 | *val = state->tv.subconnector; | ||
1166 | } else if (property == config->tv_left_margin_property) { | ||
1167 | *val = state->tv.margins.left; | ||
1168 | } else if (property == config->tv_right_margin_property) { | ||
1169 | *val = state->tv.margins.right; | ||
1170 | } else if (property == config->tv_top_margin_property) { | ||
1171 | *val = state->tv.margins.top; | ||
1172 | } else if (property == config->tv_bottom_margin_property) { | ||
1173 | *val = state->tv.margins.bottom; | ||
1174 | } else if (property == config->tv_mode_property) { | ||
1175 | *val = state->tv.mode; | ||
1176 | } else if (property == config->tv_brightness_property) { | ||
1177 | *val = state->tv.brightness; | ||
1178 | } else if (property == config->tv_contrast_property) { | ||
1179 | *val = state->tv.contrast; | ||
1180 | } else if (property == config->tv_flicker_reduction_property) { | ||
1181 | *val = state->tv.flicker_reduction; | ||
1182 | } else if (property == config->tv_overscan_property) { | ||
1183 | *val = state->tv.overscan; | ||
1184 | } else if (property == config->tv_saturation_property) { | ||
1185 | *val = state->tv.saturation; | ||
1186 | } else if (property == config->tv_hue_property) { | ||
1187 | *val = state->tv.hue; | ||
1138 | } else if (connector->funcs->atomic_get_property) { | 1188 | } else if (connector->funcs->atomic_get_property) { |
1139 | return connector->funcs->atomic_get_property(connector, | 1189 | return connector->funcs->atomic_get_property(connector, |
1140 | state, property, val); | 1190 | state, property, val); |
diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile index fb77db755e0a..7757f69a8a77 100644 --- a/drivers/gpu/drm/vc4/Makefile +++ b/drivers/gpu/drm/vc4/Makefile | |||
@@ -11,6 +11,7 @@ vc4-y := \ | |||
11 | vc4_kms.o \ | 11 | vc4_kms.o \ |
12 | vc4_gem.o \ | 12 | vc4_gem.o \ |
13 | vc4_hdmi.o \ | 13 | vc4_hdmi.o \ |
14 | vc4_vec.o \ | ||
14 | vc4_hvs.o \ | 15 | vc4_hvs.o \ |
15 | vc4_irq.o \ | 16 | vc4_irq.o \ |
16 | vc4_plane.o \ | 17 | vc4_plane.o \ |
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 7f08d681a74b..a0fd3e66bc4b 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c | |||
@@ -83,8 +83,7 @@ struct vc4_crtc_data { | |||
83 | /* Which channel of the HVS this pixelvalve sources from. */ | 83 | /* Which channel of the HVS this pixelvalve sources from. */ |
84 | int hvs_channel; | 84 | int hvs_channel; |
85 | 85 | ||
86 | enum vc4_encoder_type encoder0_type; | 86 | enum vc4_encoder_type encoder_types[4]; |
87 | enum vc4_encoder_type encoder1_type; | ||
88 | }; | 87 | }; |
89 | 88 | ||
90 | #define CRTC_WRITE(offset, val) writel(val, vc4_crtc->regs + (offset)) | 89 | #define CRTC_WRITE(offset, val) writel(val, vc4_crtc->regs + (offset)) |
@@ -669,6 +668,14 @@ void vc4_disable_vblank(struct drm_device *dev, unsigned int crtc_id) | |||
669 | CRTC_WRITE(PV_INTEN, 0); | 668 | CRTC_WRITE(PV_INTEN, 0); |
670 | } | 669 | } |
671 | 670 | ||
671 | /* Must be called with the event lock held */ | ||
672 | bool vc4_event_pending(struct drm_crtc *crtc) | ||
673 | { | ||
674 | struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); | ||
675 | |||
676 | return !!vc4_crtc->event; | ||
677 | } | ||
678 | |||
672 | static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc) | 679 | static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc) |
673 | { | 680 | { |
674 | struct drm_crtc *crtc = &vc4_crtc->base; | 681 | struct drm_crtc *crtc = &vc4_crtc->base; |
@@ -859,20 +866,26 @@ static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = { | |||
859 | 866 | ||
860 | static const struct vc4_crtc_data pv0_data = { | 867 | static const struct vc4_crtc_data pv0_data = { |
861 | .hvs_channel = 0, | 868 | .hvs_channel = 0, |
862 | .encoder0_type = VC4_ENCODER_TYPE_DSI0, | 869 | .encoder_types = { |
863 | .encoder1_type = VC4_ENCODER_TYPE_DPI, | 870 | [PV_CONTROL_CLK_SELECT_DSI] = VC4_ENCODER_TYPE_DSI0, |
871 | [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_DPI, | ||
872 | }, | ||
864 | }; | 873 | }; |
865 | 874 | ||
866 | static const struct vc4_crtc_data pv1_data = { | 875 | static const struct vc4_crtc_data pv1_data = { |
867 | .hvs_channel = 2, | 876 | .hvs_channel = 2, |
868 | .encoder0_type = VC4_ENCODER_TYPE_DSI1, | 877 | .encoder_types = { |
869 | .encoder1_type = VC4_ENCODER_TYPE_SMI, | 878 | [PV_CONTROL_CLK_SELECT_DSI] = VC4_ENCODER_TYPE_DSI1, |
879 | [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_SMI, | ||
880 | }, | ||
870 | }; | 881 | }; |
871 | 882 | ||
872 | static const struct vc4_crtc_data pv2_data = { | 883 | static const struct vc4_crtc_data pv2_data = { |
873 | .hvs_channel = 1, | 884 | .hvs_channel = 1, |
874 | .encoder0_type = VC4_ENCODER_TYPE_VEC, | 885 | .encoder_types = { |
875 | .encoder1_type = VC4_ENCODER_TYPE_HDMI, | 886 | [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_HDMI, |
887 | [PV_CONTROL_CLK_SELECT_VEC] = VC4_ENCODER_TYPE_VEC, | ||
888 | }, | ||
876 | }; | 889 | }; |
877 | 890 | ||
878 | static const struct of_device_id vc4_crtc_dt_match[] = { | 891 | static const struct of_device_id vc4_crtc_dt_match[] = { |
@@ -886,17 +899,20 @@ static void vc4_set_crtc_possible_masks(struct drm_device *drm, | |||
886 | struct drm_crtc *crtc) | 899 | struct drm_crtc *crtc) |
887 | { | 900 | { |
888 | struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); | 901 | struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); |
902 | const struct vc4_crtc_data *crtc_data = vc4_crtc->data; | ||
903 | const enum vc4_encoder_type *encoder_types = crtc_data->encoder_types; | ||
889 | struct drm_encoder *encoder; | 904 | struct drm_encoder *encoder; |
890 | 905 | ||
891 | drm_for_each_encoder(encoder, drm) { | 906 | drm_for_each_encoder(encoder, drm) { |
892 | struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); | 907 | struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); |
893 | 908 | int i; | |
894 | if (vc4_encoder->type == vc4_crtc->data->encoder0_type) { | 909 | |
895 | vc4_encoder->clock_select = 0; | 910 | for (i = 0; i < ARRAY_SIZE(crtc_data->encoder_types); i++) { |
896 | encoder->possible_crtcs |= drm_crtc_mask(crtc); | 911 | if (vc4_encoder->type == encoder_types[i]) { |
897 | } else if (vc4_encoder->type == vc4_crtc->data->encoder1_type) { | 912 | vc4_encoder->clock_select = i; |
898 | vc4_encoder->clock_select = 1; | 913 | encoder->possible_crtcs |= drm_crtc_mask(crtc); |
899 | encoder->possible_crtcs |= drm_crtc_mask(crtc); | 914 | break; |
915 | } | ||
900 | } | 916 | } |
901 | } | 917 | } |
902 | } | 918 | } |
diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c index 245115d49c46..caf817bac885 100644 --- a/drivers/gpu/drm/vc4/vc4_debugfs.c +++ b/drivers/gpu/drm/vc4/vc4_debugfs.c | |||
@@ -19,6 +19,7 @@ static const struct drm_info_list vc4_debugfs_list[] = { | |||
19 | {"bo_stats", vc4_bo_stats_debugfs, 0}, | 19 | {"bo_stats", vc4_bo_stats_debugfs, 0}, |
20 | {"dpi_regs", vc4_dpi_debugfs_regs, 0}, | 20 | {"dpi_regs", vc4_dpi_debugfs_regs, 0}, |
21 | {"hdmi_regs", vc4_hdmi_debugfs_regs, 0}, | 21 | {"hdmi_regs", vc4_hdmi_debugfs_regs, 0}, |
22 | {"vec_regs", vc4_vec_debugfs_regs, 0}, | ||
22 | {"hvs_regs", vc4_hvs_debugfs_regs, 0}, | 23 | {"hvs_regs", vc4_hvs_debugfs_regs, 0}, |
23 | {"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0}, | 24 | {"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0}, |
24 | {"crtc1_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)1}, | 25 | {"crtc1_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)1}, |
diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 1dab9e5b3689..ac09ca7ff430 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c | |||
@@ -277,12 +277,14 @@ static void vc4_drm_unbind(struct device *dev) | |||
277 | struct drm_device *drm = platform_get_drvdata(pdev); | 277 | struct drm_device *drm = platform_get_drvdata(pdev); |
278 | struct vc4_dev *vc4 = to_vc4_dev(drm); | 278 | struct vc4_dev *vc4 = to_vc4_dev(drm); |
279 | 279 | ||
280 | drm_dev_unregister(drm); | ||
281 | |||
280 | if (vc4->fbdev) | 282 | if (vc4->fbdev) |
281 | drm_fbdev_cma_fini(vc4->fbdev); | 283 | drm_fbdev_cma_fini(vc4->fbdev); |
282 | 284 | ||
283 | drm_mode_config_cleanup(drm); | 285 | drm_mode_config_cleanup(drm); |
284 | 286 | ||
285 | drm_put_dev(drm); | 287 | drm_dev_unref(drm); |
286 | } | 288 | } |
287 | 289 | ||
288 | static const struct component_master_ops vc4_drm_ops = { | 290 | static const struct component_master_ops vc4_drm_ops = { |
@@ -292,6 +294,7 @@ static const struct component_master_ops vc4_drm_ops = { | |||
292 | 294 | ||
293 | static struct platform_driver *const component_drivers[] = { | 295 | static struct platform_driver *const component_drivers[] = { |
294 | &vc4_hdmi_driver, | 296 | &vc4_hdmi_driver, |
297 | &vc4_vec_driver, | ||
295 | &vc4_dpi_driver, | 298 | &vc4_dpi_driver, |
296 | &vc4_hvs_driver, | 299 | &vc4_hvs_driver, |
297 | &vc4_crtc_driver, | 300 | &vc4_crtc_driver, |
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index fef172804345..b5c4bb14d0d1 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h | |||
@@ -17,6 +17,7 @@ struct vc4_dev { | |||
17 | struct vc4_crtc *crtc[3]; | 17 | struct vc4_crtc *crtc[3]; |
18 | struct vc4_v3d *v3d; | 18 | struct vc4_v3d *v3d; |
19 | struct vc4_dpi *dpi; | 19 | struct vc4_dpi *dpi; |
20 | struct vc4_vec *vec; | ||
20 | 21 | ||
21 | struct drm_fbdev_cma *fbdev; | 22 | struct drm_fbdev_cma *fbdev; |
22 | 23 | ||
@@ -194,6 +195,7 @@ to_vc4_plane(struct drm_plane *plane) | |||
194 | } | 195 | } |
195 | 196 | ||
196 | enum vc4_encoder_type { | 197 | enum vc4_encoder_type { |
198 | VC4_ENCODER_TYPE_NONE, | ||
197 | VC4_ENCODER_TYPE_HDMI, | 199 | VC4_ENCODER_TYPE_HDMI, |
198 | VC4_ENCODER_TYPE_VEC, | 200 | VC4_ENCODER_TYPE_VEC, |
199 | VC4_ENCODER_TYPE_DSI0, | 201 | VC4_ENCODER_TYPE_DSI0, |
@@ -442,6 +444,7 @@ int vc4_bo_stats_debugfs(struct seq_file *m, void *arg); | |||
442 | extern struct platform_driver vc4_crtc_driver; | 444 | extern struct platform_driver vc4_crtc_driver; |
443 | int vc4_enable_vblank(struct drm_device *dev, unsigned int crtc_id); | 445 | int vc4_enable_vblank(struct drm_device *dev, unsigned int crtc_id); |
444 | void vc4_disable_vblank(struct drm_device *dev, unsigned int crtc_id); | 446 | void vc4_disable_vblank(struct drm_device *dev, unsigned int crtc_id); |
447 | bool vc4_event_pending(struct drm_crtc *crtc); | ||
445 | int vc4_crtc_debugfs_regs(struct seq_file *m, void *arg); | 448 | int vc4_crtc_debugfs_regs(struct seq_file *m, void *arg); |
446 | int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id, | 449 | int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id, |
447 | unsigned int flags, int *vpos, int *hpos, | 450 | unsigned int flags, int *vpos, int *hpos, |
@@ -485,6 +488,10 @@ int vc4_queue_seqno_cb(struct drm_device *dev, | |||
485 | extern struct platform_driver vc4_hdmi_driver; | 488 | extern struct platform_driver vc4_hdmi_driver; |
486 | int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused); | 489 | int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused); |
487 | 490 | ||
491 | /* vc4_hdmi.c */ | ||
492 | extern struct platform_driver vc4_vec_driver; | ||
493 | int vc4_vec_debugfs_regs(struct seq_file *m, void *unused); | ||
494 | |||
488 | /* vc4_irq.c */ | 495 | /* vc4_irq.c */ |
489 | irqreturn_t vc4_irq(int irq, void *arg); | 496 | irqreturn_t vc4_irq(int irq, void *arg); |
490 | void vc4_irq_preinstall(struct drm_device *dev); | 497 | void vc4_irq_preinstall(struct drm_device *dev); |
diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index f31f72af8551..be8dd8262f27 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c | |||
@@ -119,17 +119,34 @@ static int vc4_atomic_commit(struct drm_device *dev, | |||
119 | 119 | ||
120 | /* Make sure that any outstanding modesets have finished. */ | 120 | /* Make sure that any outstanding modesets have finished. */ |
121 | if (nonblock) { | 121 | if (nonblock) { |
122 | ret = down_trylock(&vc4->async_modeset); | 122 | struct drm_crtc *crtc; |
123 | if (ret) { | 123 | struct drm_crtc_state *crtc_state; |
124 | unsigned long flags; | ||
125 | bool busy = false; | ||
126 | |||
127 | /* | ||
128 | * If there's an undispatched event to send then we're | ||
129 | * obviously still busy. If there isn't, then we can | ||
130 | * unconditionally wait for the semaphore because it | ||
131 | * shouldn't be contended (for long). | ||
132 | * | ||
133 | * This is to prevent a race where queuing a new flip | ||
134 | * from userspace immediately on receipt of an event | ||
135 | * beats our clean-up and returns EBUSY. | ||
136 | */ | ||
137 | spin_lock_irqsave(&dev->event_lock, flags); | ||
138 | for_each_crtc_in_state(state, crtc, crtc_state, i) | ||
139 | busy |= vc4_event_pending(crtc); | ||
140 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
141 | if (busy) { | ||
124 | kfree(c); | 142 | kfree(c); |
125 | return -EBUSY; | 143 | return -EBUSY; |
126 | } | 144 | } |
127 | } else { | 145 | } |
128 | ret = down_interruptible(&vc4->async_modeset); | 146 | ret = down_interruptible(&vc4->async_modeset); |
129 | if (ret) { | 147 | if (ret) { |
130 | kfree(c); | 148 | kfree(c); |
131 | return ret; | 149 | return ret; |
132 | } | ||
133 | } | 150 | } |
134 | 151 | ||
135 | ret = drm_atomic_helper_prepare_planes(dev, state); | 152 | ret = drm_atomic_helper_prepare_planes(dev, state); |
diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index 1aa44c2db556..39f6886b2410 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h | |||
@@ -177,8 +177,9 @@ | |||
177 | # define PV_CONTROL_WAIT_HSTART BIT(12) | 177 | # define PV_CONTROL_WAIT_HSTART BIT(12) |
178 | # define PV_CONTROL_PIXEL_REP_MASK VC4_MASK(5, 4) | 178 | # define PV_CONTROL_PIXEL_REP_MASK VC4_MASK(5, 4) |
179 | # define PV_CONTROL_PIXEL_REP_SHIFT 4 | 179 | # define PV_CONTROL_PIXEL_REP_SHIFT 4 |
180 | # define PV_CONTROL_CLK_SELECT_DSI_VEC 0 | 180 | # define PV_CONTROL_CLK_SELECT_DSI 0 |
181 | # define PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI 1 | 181 | # define PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI 1 |
182 | # define PV_CONTROL_CLK_SELECT_VEC 2 | ||
182 | # define PV_CONTROL_CLK_SELECT_MASK VC4_MASK(3, 2) | 183 | # define PV_CONTROL_CLK_SELECT_MASK VC4_MASK(3, 2) |
183 | # define PV_CONTROL_CLK_SELECT_SHIFT 2 | 184 | # define PV_CONTROL_CLK_SELECT_SHIFT 2 |
184 | # define PV_CONTROL_FIFO_CLR BIT(1) | 185 | # define PV_CONTROL_FIFO_CLR BIT(1) |
diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c new file mode 100644 index 000000000000..32bb8ef985fb --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_vec.c | |||
@@ -0,0 +1,657 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 Broadcom | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of the GNU General Public License version 2 as published by | ||
6 | * the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License along with | ||
14 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | */ | ||
16 | |||
17 | /** | ||
18 | * DOC: VC4 SDTV module | ||
19 | */ | ||
20 | |||
21 | #include <drm/drm_atomic_helper.h> | ||
22 | #include <drm/drm_crtc_helper.h> | ||
23 | #include <drm/drm_edid.h> | ||
24 | #include <drm/drm_panel.h> | ||
25 | #include <linux/clk.h> | ||
26 | #include <linux/component.h> | ||
27 | #include <linux/of_graph.h> | ||
28 | #include <linux/of_platform.h> | ||
29 | #include <linux/pm_runtime.h> | ||
30 | |||
31 | #include "vc4_drv.h" | ||
32 | #include "vc4_regs.h" | ||
33 | |||
34 | /* WSE Registers */ | ||
35 | #define VEC_WSE_RESET 0xc0 | ||
36 | |||
37 | #define VEC_WSE_CONTROL 0xc4 | ||
38 | #define VEC_WSE_WSS_ENABLE BIT(7) | ||
39 | |||
40 | #define VEC_WSE_WSS_DATA 0xc8 | ||
41 | #define VEC_WSE_VPS_DATA1 0xcc | ||
42 | #define VEC_WSE_VPS_CONTROL 0xd0 | ||
43 | |||
44 | /* VEC Registers */ | ||
45 | #define VEC_REVID 0x100 | ||
46 | |||
47 | #define VEC_CONFIG0 0x104 | ||
48 | #define VEC_CONFIG0_YDEL_MASK GENMASK(28, 26) | ||
49 | #define VEC_CONFIG0_YDEL(x) ((x) << 26) | ||
50 | #define VEC_CONFIG0_CDEL_MASK GENMASK(25, 24) | ||
51 | #define VEC_CONFIG0_CDEL(x) ((x) << 24) | ||
52 | #define VEC_CONFIG0_PBPR_FIL BIT(18) | ||
53 | #define VEC_CONFIG0_CHROMA_GAIN_MASK GENMASK(17, 16) | ||
54 | #define VEC_CONFIG0_CHROMA_GAIN_UNITY (0 << 16) | ||
55 | #define VEC_CONFIG0_CHROMA_GAIN_1_32 (1 << 16) | ||
56 | #define VEC_CONFIG0_CHROMA_GAIN_1_16 (2 << 16) | ||
57 | #define VEC_CONFIG0_CHROMA_GAIN_1_8 (3 << 16) | ||
58 | #define VEC_CONFIG0_CBURST_GAIN_MASK GENMASK(14, 13) | ||
59 | #define VEC_CONFIG0_CBURST_GAIN_UNITY (0 << 13) | ||
60 | #define VEC_CONFIG0_CBURST_GAIN_1_128 (1 << 13) | ||
61 | #define VEC_CONFIG0_CBURST_GAIN_1_64 (2 << 13) | ||
62 | #define VEC_CONFIG0_CBURST_GAIN_1_32 (3 << 13) | ||
63 | #define VEC_CONFIG0_CHRBW1 BIT(11) | ||
64 | #define VEC_CONFIG0_CHRBW0 BIT(10) | ||
65 | #define VEC_CONFIG0_SYNCDIS BIT(9) | ||
66 | #define VEC_CONFIG0_BURDIS BIT(8) | ||
67 | #define VEC_CONFIG0_CHRDIS BIT(7) | ||
68 | #define VEC_CONFIG0_PDEN BIT(6) | ||
69 | #define VEC_CONFIG0_YCDELAY BIT(4) | ||
70 | #define VEC_CONFIG0_RAMPEN BIT(2) | ||
71 | #define VEC_CONFIG0_YCDIS BIT(2) | ||
72 | #define VEC_CONFIG0_STD_MASK GENMASK(1, 0) | ||
73 | #define VEC_CONFIG0_NTSC_STD 0 | ||
74 | #define VEC_CONFIG0_PAL_BDGHI_STD 1 | ||
75 | #define VEC_CONFIG0_PAL_N_STD 3 | ||
76 | |||
77 | #define VEC_SCHPH 0x108 | ||
78 | #define VEC_SOFT_RESET 0x10c | ||
79 | #define VEC_CLMP0_START 0x144 | ||
80 | #define VEC_CLMP0_END 0x148 | ||
81 | #define VEC_FREQ3_2 0x180 | ||
82 | #define VEC_FREQ1_0 0x184 | ||
83 | |||
84 | #define VEC_CONFIG1 0x188 | ||
85 | #define VEC_CONFIG_VEC_RESYNC_OFF BIT(18) | ||
86 | #define VEC_CONFIG_RGB219 BIT(17) | ||
87 | #define VEC_CONFIG_CBAR_EN BIT(16) | ||
88 | #define VEC_CONFIG_TC_OBB BIT(15) | ||
89 | #define VEC_CONFIG1_OUTPUT_MODE_MASK GENMASK(12, 10) | ||
90 | #define VEC_CONFIG1_C_Y_CVBS (0 << 10) | ||
91 | #define VEC_CONFIG1_CVBS_Y_C (1 << 10) | ||
92 | #define VEC_CONFIG1_PR_Y_PB (2 << 10) | ||
93 | #define VEC_CONFIG1_RGB (4 << 10) | ||
94 | #define VEC_CONFIG1_Y_C_CVBS (5 << 10) | ||
95 | #define VEC_CONFIG1_C_CVBS_Y (6 << 10) | ||
96 | #define VEC_CONFIG1_C_CVBS_CVBS (7 << 10) | ||
97 | #define VEC_CONFIG1_DIS_CHR BIT(9) | ||
98 | #define VEC_CONFIG1_DIS_LUMA BIT(8) | ||
99 | #define VEC_CONFIG1_YCBCR_IN BIT(6) | ||
100 | #define VEC_CONFIG1_DITHER_TYPE_LFSR 0 | ||
101 | #define VEC_CONFIG1_DITHER_TYPE_COUNTER BIT(5) | ||
102 | #define VEC_CONFIG1_DITHER_EN BIT(4) | ||
103 | #define VEC_CONFIG1_CYDELAY BIT(3) | ||
104 | #define VEC_CONFIG1_LUMADIS BIT(2) | ||
105 | #define VEC_CONFIG1_COMPDIS BIT(1) | ||
106 | #define VEC_CONFIG1_CUSTOM_FREQ BIT(0) | ||
107 | |||
108 | #define VEC_CONFIG2 0x18c | ||
109 | #define VEC_CONFIG2_PROG_SCAN BIT(15) | ||
110 | #define VEC_CONFIG2_SYNC_ADJ_MASK GENMASK(14, 12) | ||
111 | #define VEC_CONFIG2_SYNC_ADJ(x) (((x) / 2) << 12) | ||
112 | #define VEC_CONFIG2_PBPR_EN BIT(10) | ||
113 | #define VEC_CONFIG2_UV_DIG_DIS BIT(6) | ||
114 | #define VEC_CONFIG2_RGB_DIG_DIS BIT(5) | ||
115 | #define VEC_CONFIG2_TMUX_MASK GENMASK(3, 2) | ||
116 | #define VEC_CONFIG2_TMUX_DRIVE0 (0 << 2) | ||
117 | #define VEC_CONFIG2_TMUX_RG_COMP (1 << 2) | ||
118 | #define VEC_CONFIG2_TMUX_UV_YC (2 << 2) | ||
119 | #define VEC_CONFIG2_TMUX_SYNC_YC (3 << 2) | ||
120 | |||
121 | #define VEC_INTERRUPT_CONTROL 0x190 | ||
122 | #define VEC_INTERRUPT_STATUS 0x194 | ||
123 | #define VEC_FCW_SECAM_B 0x198 | ||
124 | #define VEC_SECAM_GAIN_VAL 0x19c | ||
125 | |||
126 | #define VEC_CONFIG3 0x1a0 | ||
127 | #define VEC_CONFIG3_HORIZ_LEN_STD (0 << 0) | ||
128 | #define VEC_CONFIG3_HORIZ_LEN_MPEG1_SIF (1 << 0) | ||
129 | #define VEC_CONFIG3_SHAPE_NON_LINEAR BIT(1) | ||
130 | |||
131 | #define VEC_STATUS0 0x200 | ||
132 | #define VEC_MASK0 0x204 | ||
133 | |||
134 | #define VEC_CFG 0x208 | ||
135 | #define VEC_CFG_SG_MODE_MASK GENMASK(6, 5) | ||
136 | #define VEC_CFG_SG_MODE(x) ((x) << 5) | ||
137 | #define VEC_CFG_SG_EN BIT(4) | ||
138 | #define VEC_CFG_VEC_EN BIT(3) | ||
139 | #define VEC_CFG_MB_EN BIT(2) | ||
140 | #define VEC_CFG_ENABLE BIT(1) | ||
141 | #define VEC_CFG_TB_EN BIT(0) | ||
142 | |||
143 | #define VEC_DAC_TEST 0x20c | ||
144 | |||
145 | #define VEC_DAC_CONFIG 0x210 | ||
146 | #define VEC_DAC_CONFIG_LDO_BIAS_CTRL(x) ((x) << 24) | ||
147 | #define VEC_DAC_CONFIG_DRIVER_CTRL(x) ((x) << 16) | ||
148 | #define VEC_DAC_CONFIG_DAC_CTRL(x) (x) | ||
149 | |||
150 | #define VEC_DAC_MISC 0x214 | ||
151 | #define VEC_DAC_MISC_VCD_CTRL_MASK GENMASK(31, 16) | ||
152 | #define VEC_DAC_MISC_VCD_CTRL(x) ((x) << 16) | ||
153 | #define VEC_DAC_MISC_VID_ACT BIT(8) | ||
154 | #define VEC_DAC_MISC_VCD_PWRDN BIT(6) | ||
155 | #define VEC_DAC_MISC_BIAS_PWRDN BIT(5) | ||
156 | #define VEC_DAC_MISC_DAC_PWRDN BIT(2) | ||
157 | #define VEC_DAC_MISC_LDO_PWRDN BIT(1) | ||
158 | #define VEC_DAC_MISC_DAC_RST_N BIT(0) | ||
159 | |||
160 | |||
161 | /* General VEC hardware state. */ | ||
162 | struct vc4_vec { | ||
163 | struct platform_device *pdev; | ||
164 | |||
165 | struct drm_encoder *encoder; | ||
166 | struct drm_connector *connector; | ||
167 | |||
168 | void __iomem *regs; | ||
169 | |||
170 | struct clk *clock; | ||
171 | |||
172 | const struct vc4_vec_tv_mode *tv_mode; | ||
173 | }; | ||
174 | |||
175 | #define VEC_READ(offset) readl(vec->regs + (offset)) | ||
176 | #define VEC_WRITE(offset, val) writel(val, vec->regs + (offset)) | ||
177 | |||
178 | /* VC4 VEC encoder KMS struct */ | ||
179 | struct vc4_vec_encoder { | ||
180 | struct vc4_encoder base; | ||
181 | struct vc4_vec *vec; | ||
182 | }; | ||
183 | |||
184 | static inline struct vc4_vec_encoder * | ||
185 | to_vc4_vec_encoder(struct drm_encoder *encoder) | ||
186 | { | ||
187 | return container_of(encoder, struct vc4_vec_encoder, base.base); | ||
188 | } | ||
189 | |||
190 | /* VC4 VEC connector KMS struct */ | ||
191 | struct vc4_vec_connector { | ||
192 | struct drm_connector base; | ||
193 | struct vc4_vec *vec; | ||
194 | |||
195 | /* Since the connector is attached to just the one encoder, | ||
196 | * this is the reference to it so we can do the best_encoder() | ||
197 | * hook. | ||
198 | */ | ||
199 | struct drm_encoder *encoder; | ||
200 | }; | ||
201 | |||
202 | static inline struct vc4_vec_connector * | ||
203 | to_vc4_vec_connector(struct drm_connector *connector) | ||
204 | { | ||
205 | return container_of(connector, struct vc4_vec_connector, base); | ||
206 | } | ||
207 | |||
208 | enum vc4_vec_tv_mode_id { | ||
209 | VC4_VEC_TV_MODE_NTSC, | ||
210 | VC4_VEC_TV_MODE_NTSC_J, | ||
211 | VC4_VEC_TV_MODE_PAL, | ||
212 | VC4_VEC_TV_MODE_PAL_M, | ||
213 | }; | ||
214 | |||
215 | struct vc4_vec_tv_mode { | ||
216 | const struct drm_display_mode *mode; | ||
217 | void (*mode_set)(struct vc4_vec *vec); | ||
218 | }; | ||
219 | |||
220 | #define VEC_REG(reg) { reg, #reg } | ||
221 | static const struct { | ||
222 | u32 reg; | ||
223 | const char *name; | ||
224 | } vec_regs[] = { | ||
225 | VEC_REG(VEC_WSE_CONTROL), | ||
226 | VEC_REG(VEC_WSE_WSS_DATA), | ||
227 | VEC_REG(VEC_WSE_VPS_DATA1), | ||
228 | VEC_REG(VEC_WSE_VPS_CONTROL), | ||
229 | VEC_REG(VEC_REVID), | ||
230 | VEC_REG(VEC_CONFIG0), | ||
231 | VEC_REG(VEC_SCHPH), | ||
232 | VEC_REG(VEC_CLMP0_START), | ||
233 | VEC_REG(VEC_CLMP0_END), | ||
234 | VEC_REG(VEC_FREQ3_2), | ||
235 | VEC_REG(VEC_FREQ1_0), | ||
236 | VEC_REG(VEC_CONFIG1), | ||
237 | VEC_REG(VEC_CONFIG2), | ||
238 | VEC_REG(VEC_INTERRUPT_CONTROL), | ||
239 | VEC_REG(VEC_INTERRUPT_STATUS), | ||
240 | VEC_REG(VEC_FCW_SECAM_B), | ||
241 | VEC_REG(VEC_SECAM_GAIN_VAL), | ||
242 | VEC_REG(VEC_CONFIG3), | ||
243 | VEC_REG(VEC_STATUS0), | ||
244 | VEC_REG(VEC_MASK0), | ||
245 | VEC_REG(VEC_CFG), | ||
246 | VEC_REG(VEC_DAC_TEST), | ||
247 | VEC_REG(VEC_DAC_CONFIG), | ||
248 | VEC_REG(VEC_DAC_MISC), | ||
249 | }; | ||
250 | |||
251 | #ifdef CONFIG_DEBUG_FS | ||
252 | int vc4_vec_debugfs_regs(struct seq_file *m, void *unused) | ||
253 | { | ||
254 | struct drm_info_node *node = (struct drm_info_node *)m->private; | ||
255 | struct drm_device *dev = node->minor->dev; | ||
256 | struct vc4_dev *vc4 = to_vc4_dev(dev); | ||
257 | struct vc4_vec *vec = vc4->vec; | ||
258 | int i; | ||
259 | |||
260 | if (!vec) | ||
261 | return 0; | ||
262 | |||
263 | for (i = 0; i < ARRAY_SIZE(vec_regs); i++) { | ||
264 | seq_printf(m, "%s (0x%04x): 0x%08x\n", | ||
265 | vec_regs[i].name, vec_regs[i].reg, | ||
266 | VEC_READ(vec_regs[i].reg)); | ||
267 | } | ||
268 | |||
269 | return 0; | ||
270 | } | ||
271 | #endif | ||
272 | |||
273 | static void vc4_vec_ntsc_mode_set(struct vc4_vec *vec) | ||
274 | { | ||
275 | VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN); | ||
276 | VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS); | ||
277 | } | ||
278 | |||
279 | static void vc4_vec_ntsc_j_mode_set(struct vc4_vec *vec) | ||
280 | { | ||
281 | VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_NTSC_STD); | ||
282 | VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS); | ||
283 | } | ||
284 | |||
285 | static const struct drm_display_mode ntsc_mode = { | ||
286 | DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 13500, | ||
287 | 720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0, | ||
288 | 480, 480 + 3, 480 + 3 + 3, 480 + 3 + 3 + 16, 0, | ||
289 | DRM_MODE_FLAG_INTERLACE) | ||
290 | }; | ||
291 | |||
292 | static void vc4_vec_pal_mode_set(struct vc4_vec *vec) | ||
293 | { | ||
294 | VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_PAL_BDGHI_STD); | ||
295 | VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS); | ||
296 | } | ||
297 | |||
298 | static void vc4_vec_pal_m_mode_set(struct vc4_vec *vec) | ||
299 | { | ||
300 | VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_PAL_BDGHI_STD); | ||
301 | VEC_WRITE(VEC_CONFIG1, | ||
302 | VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ); | ||
303 | VEC_WRITE(VEC_FREQ3_2, 0x223b); | ||
304 | VEC_WRITE(VEC_FREQ1_0, 0x61d1); | ||
305 | } | ||
306 | |||
307 | static const struct drm_display_mode pal_mode = { | ||
308 | DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 13500, | ||
309 | 720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0, | ||
310 | 576, 576 + 2, 576 + 2 + 3, 576 + 2 + 3 + 20, 0, | ||
311 | DRM_MODE_FLAG_INTERLACE) | ||
312 | }; | ||
313 | |||
314 | static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = { | ||
315 | [VC4_VEC_TV_MODE_NTSC] = { | ||
316 | .mode = &ntsc_mode, | ||
317 | .mode_set = vc4_vec_ntsc_mode_set, | ||
318 | }, | ||
319 | [VC4_VEC_TV_MODE_NTSC_J] = { | ||
320 | .mode = &ntsc_mode, | ||
321 | .mode_set = vc4_vec_ntsc_j_mode_set, | ||
322 | }, | ||
323 | [VC4_VEC_TV_MODE_PAL] = { | ||
324 | .mode = &pal_mode, | ||
325 | .mode_set = vc4_vec_pal_mode_set, | ||
326 | }, | ||
327 | [VC4_VEC_TV_MODE_PAL_M] = { | ||
328 | .mode = &pal_mode, | ||
329 | .mode_set = vc4_vec_pal_m_mode_set, | ||
330 | }, | ||
331 | }; | ||
332 | |||
333 | static enum drm_connector_status | ||
334 | vc4_vec_connector_detect(struct drm_connector *connector, bool force) | ||
335 | { | ||
336 | return connector_status_unknown; | ||
337 | } | ||
338 | |||
339 | static void vc4_vec_connector_destroy(struct drm_connector *connector) | ||
340 | { | ||
341 | drm_connector_unregister(connector); | ||
342 | drm_connector_cleanup(connector); | ||
343 | } | ||
344 | |||
345 | static int vc4_vec_connector_get_modes(struct drm_connector *connector) | ||
346 | { | ||
347 | struct drm_connector_state *state = connector->state; | ||
348 | struct drm_display_mode *mode; | ||
349 | |||
350 | mode = drm_mode_duplicate(connector->dev, | ||
351 | vc4_vec_tv_modes[state->tv.mode].mode); | ||
352 | if (!mode) { | ||
353 | DRM_ERROR("Failed to create a new display mode\n"); | ||
354 | return -ENOMEM; | ||
355 | } | ||
356 | |||
357 | drm_mode_probed_add(connector, mode); | ||
358 | |||
359 | return 1; | ||
360 | } | ||
361 | |||
362 | static const struct drm_connector_funcs vc4_vec_connector_funcs = { | ||
363 | .dpms = drm_atomic_helper_connector_dpms, | ||
364 | .detect = vc4_vec_connector_detect, | ||
365 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
366 | .set_property = drm_atomic_helper_connector_set_property, | ||
367 | .destroy = vc4_vec_connector_destroy, | ||
368 | .reset = drm_atomic_helper_connector_reset, | ||
369 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | ||
370 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | ||
371 | }; | ||
372 | |||
373 | static const struct drm_connector_helper_funcs vc4_vec_connector_helper_funcs = { | ||
374 | .get_modes = vc4_vec_connector_get_modes, | ||
375 | }; | ||
376 | |||
377 | static struct drm_connector *vc4_vec_connector_init(struct drm_device *dev, | ||
378 | struct vc4_vec *vec) | ||
379 | { | ||
380 | struct drm_connector *connector = NULL; | ||
381 | struct vc4_vec_connector *vec_connector; | ||
382 | |||
383 | vec_connector = devm_kzalloc(dev->dev, sizeof(*vec_connector), | ||
384 | GFP_KERNEL); | ||
385 | if (!vec_connector) | ||
386 | return ERR_PTR(-ENOMEM); | ||
387 | |||
388 | connector = &vec_connector->base; | ||
389 | connector->interlace_allowed = true; | ||
390 | |||
391 | vec_connector->encoder = vec->encoder; | ||
392 | vec_connector->vec = vec; | ||
393 | |||
394 | drm_connector_init(dev, connector, &vc4_vec_connector_funcs, | ||
395 | DRM_MODE_CONNECTOR_Composite); | ||
396 | drm_connector_helper_add(connector, &vc4_vec_connector_helper_funcs); | ||
397 | |||
398 | drm_object_attach_property(&connector->base, | ||
399 | dev->mode_config.tv_mode_property, | ||
400 | VC4_VEC_TV_MODE_NTSC); | ||
401 | vec->tv_mode = &vc4_vec_tv_modes[VC4_VEC_TV_MODE_NTSC]; | ||
402 | |||
403 | drm_mode_connector_attach_encoder(connector, vec->encoder); | ||
404 | |||
405 | return connector; | ||
406 | } | ||
407 | |||
408 | static const struct drm_encoder_funcs vc4_vec_encoder_funcs = { | ||
409 | .destroy = drm_encoder_cleanup, | ||
410 | }; | ||
411 | |||
412 | static void vc4_vec_encoder_disable(struct drm_encoder *encoder) | ||
413 | { | ||
414 | struct vc4_vec_encoder *vc4_vec_encoder = to_vc4_vec_encoder(encoder); | ||
415 | struct vc4_vec *vec = vc4_vec_encoder->vec; | ||
416 | int ret; | ||
417 | |||
418 | VEC_WRITE(VEC_CFG, 0); | ||
419 | VEC_WRITE(VEC_DAC_MISC, | ||
420 | VEC_DAC_MISC_VCD_PWRDN | | ||
421 | VEC_DAC_MISC_BIAS_PWRDN | | ||
422 | VEC_DAC_MISC_DAC_PWRDN | | ||
423 | VEC_DAC_MISC_LDO_PWRDN); | ||
424 | |||
425 | clk_disable_unprepare(vec->clock); | ||
426 | |||
427 | ret = pm_runtime_put(&vec->pdev->dev); | ||
428 | if (ret < 0) { | ||
429 | DRM_ERROR("Failed to release power domain: %d\n", ret); | ||
430 | return; | ||
431 | } | ||
432 | } | ||
433 | |||
434 | static void vc4_vec_encoder_enable(struct drm_encoder *encoder) | ||
435 | { | ||
436 | struct vc4_vec_encoder *vc4_vec_encoder = to_vc4_vec_encoder(encoder); | ||
437 | struct vc4_vec *vec = vc4_vec_encoder->vec; | ||
438 | int ret; | ||
439 | |||
440 | ret = pm_runtime_get_sync(&vec->pdev->dev); | ||
441 | if (ret < 0) { | ||
442 | DRM_ERROR("Failed to retain power domain: %d\n", ret); | ||
443 | return; | ||
444 | } | ||
445 | |||
446 | /* | ||
447 | * We need to set the clock rate each time we enable the encoder | ||
448 | * because there's a chance we share the same parent with the HDMI | ||
449 | * clock, and both drivers are requesting different rates. | ||
450 | * The good news is, these 2 encoders cannot be enabled at the same | ||
451 | * time, thus preventing incompatible rate requests. | ||
452 | */ | ||
453 | ret = clk_set_rate(vec->clock, 108000000); | ||
454 | if (ret) { | ||
455 | DRM_ERROR("Failed to set clock rate: %d\n", ret); | ||
456 | return; | ||
457 | } | ||
458 | |||
459 | ret = clk_prepare_enable(vec->clock); | ||
460 | if (ret) { | ||
461 | DRM_ERROR("Failed to turn on core clock: %d\n", ret); | ||
462 | return; | ||
463 | } | ||
464 | |||
465 | /* Reset the different blocks */ | ||
466 | VEC_WRITE(VEC_WSE_RESET, 1); | ||
467 | VEC_WRITE(VEC_SOFT_RESET, 1); | ||
468 | |||
469 | /* Disable the CGSM-A and WSE blocks */ | ||
470 | VEC_WRITE(VEC_WSE_CONTROL, 0); | ||
471 | |||
472 | /* Write config common to all modes. */ | ||
473 | |||
474 | /* | ||
475 | * Color subcarrier phase: phase = 360 * SCHPH / 256. | ||
476 | * 0x28 <=> 39.375 deg. | ||
477 | */ | ||
478 | VEC_WRITE(VEC_SCHPH, 0x28); | ||
479 | |||
480 | /* | ||
481 | * Reset to default values. | ||
482 | */ | ||
483 | VEC_WRITE(VEC_CLMP0_START, 0xac); | ||
484 | VEC_WRITE(VEC_CLMP0_END, 0xec); | ||
485 | VEC_WRITE(VEC_CONFIG2, | ||
486 | VEC_CONFIG2_UV_DIG_DIS | VEC_CONFIG2_RGB_DIG_DIS); | ||
487 | VEC_WRITE(VEC_CONFIG3, VEC_CONFIG3_HORIZ_LEN_STD); | ||
488 | VEC_WRITE(VEC_DAC_CONFIG, | ||
489 | VEC_DAC_CONFIG_DAC_CTRL(0xc) | | ||
490 | VEC_DAC_CONFIG_DRIVER_CTRL(0xc) | | ||
491 | VEC_DAC_CONFIG_LDO_BIAS_CTRL(0x46)); | ||
492 | |||
493 | /* Mask all interrupts. */ | ||
494 | VEC_WRITE(VEC_MASK0, 0); | ||
495 | |||
496 | vec->tv_mode->mode_set(vec); | ||
497 | |||
498 | VEC_WRITE(VEC_DAC_MISC, | ||
499 | VEC_DAC_MISC_VID_ACT | VEC_DAC_MISC_DAC_RST_N); | ||
500 | VEC_WRITE(VEC_CFG, VEC_CFG_VEC_EN); | ||
501 | } | ||
502 | |||
503 | |||
504 | static bool vc4_vec_encoder_mode_fixup(struct drm_encoder *encoder, | ||
505 | const struct drm_display_mode *mode, | ||
506 | struct drm_display_mode *adjusted_mode) | ||
507 | { | ||
508 | return true; | ||
509 | } | ||
510 | |||
511 | static void vc4_vec_encoder_atomic_mode_set(struct drm_encoder *encoder, | ||
512 | struct drm_crtc_state *crtc_state, | ||
513 | struct drm_connector_state *conn_state) | ||
514 | { | ||
515 | struct vc4_vec_encoder *vc4_vec_encoder = to_vc4_vec_encoder(encoder); | ||
516 | struct vc4_vec *vec = vc4_vec_encoder->vec; | ||
517 | |||
518 | vec->tv_mode = &vc4_vec_tv_modes[conn_state->tv.mode]; | ||
519 | } | ||
520 | |||
521 | static int vc4_vec_encoder_atomic_check(struct drm_encoder *encoder, | ||
522 | struct drm_crtc_state *crtc_state, | ||
523 | struct drm_connector_state *conn_state) | ||
524 | { | ||
525 | const struct vc4_vec_tv_mode *vec_mode; | ||
526 | |||
527 | vec_mode = &vc4_vec_tv_modes[conn_state->tv.mode]; | ||
528 | |||
529 | if (conn_state->crtc && | ||
530 | !drm_mode_equal(vec_mode->mode, &crtc_state->adjusted_mode)) | ||
531 | return -EINVAL; | ||
532 | |||
533 | return 0; | ||
534 | } | ||
535 | |||
536 | static const struct drm_encoder_helper_funcs vc4_vec_encoder_helper_funcs = { | ||
537 | .disable = vc4_vec_encoder_disable, | ||
538 | .enable = vc4_vec_encoder_enable, | ||
539 | .mode_fixup = vc4_vec_encoder_mode_fixup, | ||
540 | .atomic_check = vc4_vec_encoder_atomic_check, | ||
541 | .atomic_mode_set = vc4_vec_encoder_atomic_mode_set, | ||
542 | }; | ||
543 | |||
544 | static const struct of_device_id vc4_vec_dt_match[] = { | ||
545 | { .compatible = "brcm,bcm2835-vec", .data = NULL }, | ||
546 | { /* sentinel */ }, | ||
547 | }; | ||
548 | |||
549 | static const char * const tv_mode_names[] = { | ||
550 | [VC4_VEC_TV_MODE_NTSC] = "NTSC", | ||
551 | [VC4_VEC_TV_MODE_NTSC_J] = "NTSC-J", | ||
552 | [VC4_VEC_TV_MODE_PAL] = "PAL", | ||
553 | [VC4_VEC_TV_MODE_PAL_M] = "PAL-M", | ||
554 | }; | ||
555 | |||
556 | static int vc4_vec_bind(struct device *dev, struct device *master, void *data) | ||
557 | { | ||
558 | struct platform_device *pdev = to_platform_device(dev); | ||
559 | struct drm_device *drm = dev_get_drvdata(master); | ||
560 | struct vc4_dev *vc4 = to_vc4_dev(drm); | ||
561 | struct vc4_vec *vec; | ||
562 | struct vc4_vec_encoder *vc4_vec_encoder; | ||
563 | int ret; | ||
564 | |||
565 | ret = drm_mode_create_tv_properties(drm, ARRAY_SIZE(tv_mode_names), | ||
566 | tv_mode_names); | ||
567 | if (ret) | ||
568 | return ret; | ||
569 | |||
570 | vec = devm_kzalloc(dev, sizeof(*vec), GFP_KERNEL); | ||
571 | if (!vec) | ||
572 | return -ENOMEM; | ||
573 | |||
574 | vc4_vec_encoder = devm_kzalloc(dev, sizeof(*vc4_vec_encoder), | ||
575 | GFP_KERNEL); | ||
576 | if (!vc4_vec_encoder) | ||
577 | return -ENOMEM; | ||
578 | vc4_vec_encoder->base.type = VC4_ENCODER_TYPE_VEC; | ||
579 | vc4_vec_encoder->vec = vec; | ||
580 | vec->encoder = &vc4_vec_encoder->base.base; | ||
581 | |||
582 | vec->pdev = pdev; | ||
583 | vec->regs = vc4_ioremap_regs(pdev, 0); | ||
584 | if (IS_ERR(vec->regs)) | ||
585 | return PTR_ERR(vec->regs); | ||
586 | |||
587 | vec->clock = devm_clk_get(dev, NULL); | ||
588 | if (IS_ERR(vec->clock)) { | ||
589 | ret = PTR_ERR(vec->clock); | ||
590 | if (ret != -EPROBE_DEFER) | ||
591 | DRM_ERROR("Failed to get clock: %d\n", ret); | ||
592 | return ret; | ||
593 | } | ||
594 | |||
595 | pm_runtime_enable(dev); | ||
596 | |||
597 | drm_encoder_init(drm, vec->encoder, &vc4_vec_encoder_funcs, | ||
598 | DRM_MODE_ENCODER_TVDAC, NULL); | ||
599 | drm_encoder_helper_add(vec->encoder, &vc4_vec_encoder_helper_funcs); | ||
600 | |||
601 | vec->connector = vc4_vec_connector_init(drm, vec); | ||
602 | if (IS_ERR(vec->connector)) { | ||
603 | ret = PTR_ERR(vec->connector); | ||
604 | goto err_destroy_encoder; | ||
605 | } | ||
606 | |||
607 | dev_set_drvdata(dev, vec); | ||
608 | |||
609 | vc4->vec = vec; | ||
610 | |||
611 | return 0; | ||
612 | |||
613 | err_destroy_encoder: | ||
614 | drm_encoder_cleanup(vec->encoder); | ||
615 | pm_runtime_disable(dev); | ||
616 | |||
617 | return ret; | ||
618 | } | ||
619 | |||
620 | static void vc4_vec_unbind(struct device *dev, struct device *master, | ||
621 | void *data) | ||
622 | { | ||
623 | struct drm_device *drm = dev_get_drvdata(master); | ||
624 | struct vc4_dev *vc4 = to_vc4_dev(drm); | ||
625 | struct vc4_vec *vec = dev_get_drvdata(dev); | ||
626 | |||
627 | vc4_vec_connector_destroy(vec->connector); | ||
628 | drm_encoder_cleanup(vec->encoder); | ||
629 | pm_runtime_disable(dev); | ||
630 | |||
631 | vc4->vec = NULL; | ||
632 | } | ||
633 | |||
634 | static const struct component_ops vc4_vec_ops = { | ||
635 | .bind = vc4_vec_bind, | ||
636 | .unbind = vc4_vec_unbind, | ||
637 | }; | ||
638 | |||
639 | static int vc4_vec_dev_probe(struct platform_device *pdev) | ||
640 | { | ||
641 | return component_add(&pdev->dev, &vc4_vec_ops); | ||
642 | } | ||
643 | |||
644 | static int vc4_vec_dev_remove(struct platform_device *pdev) | ||
645 | { | ||
646 | component_del(&pdev->dev, &vc4_vec_ops); | ||
647 | return 0; | ||
648 | } | ||
649 | |||
650 | struct platform_driver vc4_vec_driver = { | ||
651 | .probe = vc4_vec_dev_probe, | ||
652 | .remove = vc4_vec_dev_remove, | ||
653 | .driver = { | ||
654 | .name = "vc4_vec", | ||
655 | .of_match_table = vc4_vec_dt_match, | ||
656 | }, | ||
657 | }; | ||
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 1218a0c002c0..a9b95246e26e 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h | |||
@@ -195,10 +195,40 @@ int drm_display_info_set_bus_formats(struct drm_display_info *info, | |||
195 | unsigned int num_formats); | 195 | unsigned int num_formats); |
196 | 196 | ||
197 | /** | 197 | /** |
198 | * struct drm_tv_connector_state - TV connector related states | ||
199 | * @subconnector: selected subconnector | ||
200 | * @margins: left/right/top/bottom margins | ||
201 | * @mode: TV mode | ||
202 | * @brightness: brightness in percent | ||
203 | * @contrast: contrast in percent | ||
204 | * @flicker_reduction: flicker reduction in percent | ||
205 | * @overscan: overscan in percent | ||
206 | * @saturation: saturation in percent | ||
207 | * @hue: hue in percent | ||
208 | */ | ||
209 | struct drm_tv_connector_state { | ||
210 | enum drm_mode_subconnector subconnector; | ||
211 | struct { | ||
212 | unsigned int left; | ||
213 | unsigned int right; | ||
214 | unsigned int top; | ||
215 | unsigned int bottom; | ||
216 | } margins; | ||
217 | unsigned int mode; | ||
218 | unsigned int brightness; | ||
219 | unsigned int contrast; | ||
220 | unsigned int flicker_reduction; | ||
221 | unsigned int overscan; | ||
222 | unsigned int saturation; | ||
223 | unsigned int hue; | ||
224 | }; | ||
225 | |||
226 | /** | ||
198 | * struct drm_connector_state - mutable connector state | 227 | * struct drm_connector_state - mutable connector state |
199 | * @connector: backpointer to the connector | 228 | * @connector: backpointer to the connector |
200 | * @best_encoder: can be used by helpers and drivers to select the encoder | 229 | * @best_encoder: can be used by helpers and drivers to select the encoder |
201 | * @state: backpointer to global drm_atomic_state | 230 | * @state: backpointer to global drm_atomic_state |
231 | * @tv: TV connector state | ||
202 | */ | 232 | */ |
203 | struct drm_connector_state { | 233 | struct drm_connector_state { |
204 | struct drm_connector *connector; | 234 | struct drm_connector *connector; |
@@ -214,6 +244,8 @@ struct drm_connector_state { | |||
214 | struct drm_encoder *best_encoder; | 244 | struct drm_encoder *best_encoder; |
215 | 245 | ||
216 | struct drm_atomic_state *state; | 246 | struct drm_atomic_state *state; |
247 | |||
248 | struct drm_tv_connector_state tv; | ||
217 | }; | 249 | }; |
218 | 250 | ||
219 | /** | 251 | /** |
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 728790b92354..ce7efe2e8a5e 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h | |||
@@ -236,14 +236,16 @@ struct drm_mode_get_encoder { | |||
236 | 236 | ||
237 | /* This is for connectors with multiple signal types. */ | 237 | /* This is for connectors with multiple signal types. */ |
238 | /* Try to match DRM_MODE_CONNECTOR_X as closely as possible. */ | 238 | /* Try to match DRM_MODE_CONNECTOR_X as closely as possible. */ |
239 | #define DRM_MODE_SUBCONNECTOR_Automatic 0 | 239 | enum drm_mode_subconnector { |
240 | #define DRM_MODE_SUBCONNECTOR_Unknown 0 | 240 | DRM_MODE_SUBCONNECTOR_Automatic = 0, |
241 | #define DRM_MODE_SUBCONNECTOR_DVID 3 | 241 | DRM_MODE_SUBCONNECTOR_Unknown = 0, |
242 | #define DRM_MODE_SUBCONNECTOR_DVIA 4 | 242 | DRM_MODE_SUBCONNECTOR_DVID = 3, |
243 | #define DRM_MODE_SUBCONNECTOR_Composite 5 | 243 | DRM_MODE_SUBCONNECTOR_DVIA = 4, |
244 | #define DRM_MODE_SUBCONNECTOR_SVIDEO 6 | 244 | DRM_MODE_SUBCONNECTOR_Composite = 5, |
245 | #define DRM_MODE_SUBCONNECTOR_Component 8 | 245 | DRM_MODE_SUBCONNECTOR_SVIDEO = 6, |
246 | #define DRM_MODE_SUBCONNECTOR_SCART 9 | 246 | DRM_MODE_SUBCONNECTOR_Component = 8, |
247 | DRM_MODE_SUBCONNECTOR_SCART = 9, | ||
248 | }; | ||
247 | 249 | ||
248 | #define DRM_MODE_CONNECTOR_Unknown 0 | 250 | #define DRM_MODE_CONNECTOR_Unknown 0 |
249 | #define DRM_MODE_CONNECTOR_VGA 1 | 251 | #define DRM_MODE_CONNECTOR_VGA 1 |