diff options
author | Dave Airlie <airlied@redhat.com> | 2016-03-16 18:27:51 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2016-03-16 18:27:51 -0400 |
commit | cf481068cdd430a22425d7712c8deeb25efdedc1 (patch) | |
tree | 7ddf5043cb06c12327ae8b5529356ff45e83e5e8 | |
parent | 9f443bf53b5699835e0132d62d1e6c99a1eaeee8 (diff) | |
parent | 52807ae90e76b69fd01688d6031190271536d29f (diff) |
Merge branch '2016-02-26-st-drm-next' of http://git.linaro.org/people/benjamin.gaignard/kernel into drm-next
Here are sti patches for drm-next.
It brings:
- The support of the atomic_check for the planes and minor fixes for
planes
- The support of the vendor specific infoframe for HDMI and the
support of 2 HDMI properties related to the connector
- The support of the DVO solving panel detection issue and timing issue.
- The support of debugfs for connectors, encoders, crtcs and planes.
* '2016-02-26-st-drm-next' of http://git.linaro.org/people/benjamin.gaignard/kernel: (36 commits)
drm/sti: use u32 to store DMA addresses
drm: sti: remove sti_gem_prime_export hack
drm/sti: add debugfs fps_show/fps_get mechanism for planes
drm/sti: add debugfs entries for TVOUT encoders
drm/sti: add debugfs entries for MIXER crtc
drm/sti: add debugfs entries for VID plane
drm/sti: add debugfs entries for HQVDP plane
drm/sti: add debugfs entries for GDP planes
drm/sti: add debugfs entries for CURSOR plane
drm/sti: add debugfs entries for HDA connector
drm/sti: add debugfs entries for DVO connector
drm/sti: add debugfs entries for HDMI connector
drm/sti: add hdmi_mode property for HDMI connector
drm/sti: add colorspace property to the HDMI connector
drm/sti: add HDMI vendor specific infoframe
drm/sti: reset infoframe transmission when HDMI is stopped
drm/sti: HDMI infoframe transmission mode not take into account
drm/sti: reset HD DACS when HDA connector is created
drm/sti: fix dvo data_enable signal
drm/sti: adjust delay for DVO
...
-rw-r--r-- | drivers/gpu/drm/sti/sti_awg_utils.c | 78 | ||||
-rw-r--r-- | drivers/gpu/drm/sti/sti_compositor.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/sti/sti_crtc.c | 9 | ||||
-rw-r--r-- | drivers/gpu/drm/sti/sti_cursor.c | 184 | ||||
-rw-r--r-- | drivers/gpu/drm/sti/sti_drv.c | 141 | ||||
-rw-r--r-- | drivers/gpu/drm/sti/sti_dvo.c | 78 | ||||
-rw-r--r-- | drivers/gpu/drm/sti/sti_gdp.c | 476 | ||||
-rw-r--r-- | drivers/gpu/drm/sti/sti_hda.c | 108 | ||||
-rw-r--r-- | drivers/gpu/drm/sti/sti_hdmi.c | 400 | ||||
-rw-r--r-- | drivers/gpu/drm/sti/sti_hdmi.h | 31 | ||||
-rw-r--r-- | drivers/gpu/drm/sti/sti_hqvdp.c | 447 | ||||
-rw-r--r-- | drivers/gpu/drm/sti/sti_mixer.c | 146 | ||||
-rw-r--r-- | drivers/gpu/drm/sti/sti_mixer.h | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/sti/sti_plane.c | 63 | ||||
-rw-r--r-- | drivers/gpu/drm/sti/sti_plane.h | 17 | ||||
-rw-r--r-- | drivers/gpu/drm/sti/sti_tvout.c | 295 | ||||
-rw-r--r-- | drivers/gpu/drm/sti/sti_vid.c | 125 | ||||
-rw-r--r-- | drivers/gpu/drm/sti/sti_vid.h | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/sti/sti_vtg.c | 200 | ||||
-rw-r--r-- | drivers/gpu/drm/sti/sti_vtg.h | 5 |
20 files changed, 2399 insertions, 416 deletions
diff --git a/drivers/gpu/drm/sti/sti_awg_utils.c b/drivers/gpu/drm/sti/sti_awg_utils.c index 00d0698be9d3..a516eb869f6f 100644 --- a/drivers/gpu/drm/sti/sti_awg_utils.c +++ b/drivers/gpu/drm/sti/sti_awg_utils.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include "sti_awg_utils.h" | 7 | #include "sti_awg_utils.h" |
8 | 8 | ||
9 | #define AWG_OPCODE_OFFSET 10 | 9 | #define AWG_OPCODE_OFFSET 10 |
10 | #define AWG_MAX_ARG 0x3ff | ||
10 | 11 | ||
11 | enum opcode { | 12 | enum opcode { |
12 | SET, | 13 | SET, |
@@ -34,6 +35,8 @@ static int awg_generate_instr(enum opcode opcode, | |||
34 | /* skip, repeat and replay arg should not exceed 1023. | 35 | /* skip, repeat and replay arg should not exceed 1023. |
35 | * If user wants to exceed this value, the instruction should be | 36 | * If user wants to exceed this value, the instruction should be |
36 | * duplicate and arg should be adjust for each duplicated instruction. | 37 | * duplicate and arg should be adjust for each duplicated instruction. |
38 | * | ||
39 | * mux_sel is used in case of SAV/EAV synchronization. | ||
37 | */ | 40 | */ |
38 | 41 | ||
39 | while (arg_tmp > 0) { | 42 | while (arg_tmp > 0) { |
@@ -65,7 +68,7 @@ static int awg_generate_instr(enum opcode opcode, | |||
65 | 68 | ||
66 | mux = 0; | 69 | mux = 0; |
67 | data_enable = 0; | 70 | data_enable = 0; |
68 | arg &= (0x3ff); | 71 | arg &= AWG_MAX_ARG; |
69 | break; | 72 | break; |
70 | case REPEAT: | 73 | case REPEAT: |
71 | case REPLAY: | 74 | case REPLAY: |
@@ -76,13 +79,13 @@ static int awg_generate_instr(enum opcode opcode, | |||
76 | 79 | ||
77 | mux = 0; | 80 | mux = 0; |
78 | data_enable = 0; | 81 | data_enable = 0; |
79 | arg &= (0x3ff); | 82 | arg &= AWG_MAX_ARG; |
80 | break; | 83 | break; |
81 | case JUMP: | 84 | case JUMP: |
82 | mux = 0; | 85 | mux = 0; |
83 | data_enable = 0; | 86 | data_enable = 0; |
84 | arg |= 0x40; /* for jump instruction 7th bit is 1 */ | 87 | arg |= 0x40; /* for jump instruction 7th bit is 1 */ |
85 | arg &= 0x3ff; | 88 | arg &= AWG_MAX_ARG; |
86 | break; | 89 | break; |
87 | case STOP: | 90 | case STOP: |
88 | arg = 0; | 91 | arg = 0; |
@@ -110,68 +113,75 @@ static int awg_generate_instr(enum opcode opcode, | |||
110 | return 0; | 113 | return 0; |
111 | } | 114 | } |
112 | 115 | ||
113 | int sti_awg_generate_code_data_enable_mode( | 116 | static int awg_generate_line_signal( |
114 | struct awg_code_generation_params *fwparams, | 117 | struct awg_code_generation_params *fwparams, |
115 | struct awg_timing *timing) | 118 | struct awg_timing *timing) |
116 | { | 119 | { |
117 | long int val; | 120 | long int val; |
118 | long int data_en; | ||
119 | int ret = 0; | 121 | int ret = 0; |
120 | 122 | ||
121 | if (timing->trailing_lines > 0) { | ||
122 | /* skip trailing lines */ | ||
123 | val = timing->blanking_level; | ||
124 | data_en = 0; | ||
125 | ret |= awg_generate_instr(RPLSET, val, 0, data_en, fwparams); | ||
126 | |||
127 | val = timing->trailing_lines - 1; | ||
128 | data_en = 0; | ||
129 | ret |= awg_generate_instr(REPLAY, val, 0, data_en, fwparams); | ||
130 | } | ||
131 | |||
132 | if (timing->trailing_pixels > 0) { | 123 | if (timing->trailing_pixels > 0) { |
133 | /* skip trailing pixel */ | 124 | /* skip trailing pixel */ |
134 | val = timing->blanking_level; | 125 | val = timing->blanking_level; |
135 | data_en = 0; | 126 | ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams); |
136 | ret |= awg_generate_instr(RPLSET, val, 0, data_en, fwparams); | ||
137 | 127 | ||
138 | val = timing->trailing_pixels - 1; | 128 | val = timing->trailing_pixels - 1; |
139 | data_en = 0; | 129 | ret |= awg_generate_instr(SKIP, val, 0, 0, fwparams); |
140 | ret |= awg_generate_instr(SKIP, val, 0, data_en, fwparams); | ||
141 | } | 130 | } |
142 | 131 | ||
143 | /* set DE signal high */ | 132 | /* set DE signal high */ |
144 | val = timing->blanking_level; | 133 | val = timing->blanking_level; |
145 | data_en = 1; | ||
146 | ret |= awg_generate_instr((timing->trailing_pixels > 0) ? SET : RPLSET, | 134 | ret |= awg_generate_instr((timing->trailing_pixels > 0) ? SET : RPLSET, |
147 | val, 0, data_en, fwparams); | 135 | val, 0, 1, fwparams); |
148 | 136 | ||
149 | if (timing->blanking_pixels > 0) { | 137 | if (timing->blanking_pixels > 0) { |
150 | /* skip the number of active pixel */ | 138 | /* skip the number of active pixel */ |
151 | val = timing->active_pixels - 1; | 139 | val = timing->active_pixels - 1; |
152 | data_en = 1; | 140 | ret |= awg_generate_instr(SKIP, val, 0, 1, fwparams); |
153 | ret |= awg_generate_instr(SKIP, val, 0, data_en, fwparams); | ||
154 | 141 | ||
155 | /* set DE signal low */ | 142 | /* set DE signal low */ |
156 | val = timing->blanking_level; | 143 | val = timing->blanking_level; |
157 | data_en = 0; | 144 | ret |= awg_generate_instr(SET, val, 0, 0, fwparams); |
158 | ret |= awg_generate_instr(SET, val, 0, data_en, fwparams); | 145 | } |
146 | |||
147 | return ret; | ||
148 | } | ||
149 | |||
150 | int sti_awg_generate_code_data_enable_mode( | ||
151 | struct awg_code_generation_params *fwparams, | ||
152 | struct awg_timing *timing) | ||
153 | { | ||
154 | long int val, tmp_val; | ||
155 | int ret = 0; | ||
156 | |||
157 | if (timing->trailing_lines > 0) { | ||
158 | /* skip trailing lines */ | ||
159 | val = timing->blanking_level; | ||
160 | ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams); | ||
161 | |||
162 | val = timing->trailing_lines - 1; | ||
163 | ret |= awg_generate_instr(REPLAY, val, 0, 0, fwparams); | ||
159 | } | 164 | } |
160 | 165 | ||
161 | /* replay the sequence as many active lines defined */ | 166 | tmp_val = timing->active_lines - 1; |
162 | val = timing->active_lines - 1; | 167 | |
163 | data_en = 0; | 168 | while (tmp_val > 0) { |
164 | ret |= awg_generate_instr(REPLAY, val, 0, data_en, fwparams); | 169 | /* generate DE signal for each line */ |
170 | ret |= awg_generate_line_signal(fwparams, timing); | ||
171 | /* replay the sequence as many active lines defined */ | ||
172 | ret |= awg_generate_instr(REPLAY, | ||
173 | min_t(int, AWG_MAX_ARG, tmp_val), | ||
174 | 0, 0, fwparams); | ||
175 | tmp_val -= AWG_MAX_ARG; | ||
176 | } | ||
165 | 177 | ||
166 | if (timing->blanking_lines > 0) { | 178 | if (timing->blanking_lines > 0) { |
167 | /* skip blanking lines */ | 179 | /* skip blanking lines */ |
168 | val = timing->blanking_level; | 180 | val = timing->blanking_level; |
169 | data_en = 0; | 181 | ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams); |
170 | ret |= awg_generate_instr(RPLSET, val, 0, data_en, fwparams); | ||
171 | 182 | ||
172 | val = timing->blanking_lines - 1; | 183 | val = timing->blanking_lines - 1; |
173 | data_en = 0; | 184 | ret |= awg_generate_instr(REPLAY, val, 0, 0, fwparams); |
174 | ret |= awg_generate_instr(REPLAY, val, 0, data_en, fwparams); | ||
175 | } | 185 | } |
176 | 186 | ||
177 | return ret; | 187 | return ret; |
diff --git a/drivers/gpu/drm/sti/sti_compositor.c b/drivers/gpu/drm/sti/sti_compositor.c index afed2171beb9..3d2fa3ab33df 100644 --- a/drivers/gpu/drm/sti/sti_compositor.c +++ b/drivers/gpu/drm/sti/sti_compositor.c | |||
@@ -75,13 +75,13 @@ static int sti_compositor_bind(struct device *dev, | |||
75 | switch (desc[i].type) { | 75 | switch (desc[i].type) { |
76 | case STI_VID_SUBDEV: | 76 | case STI_VID_SUBDEV: |
77 | compo->vid[vid_id++] = | 77 | compo->vid[vid_id++] = |
78 | sti_vid_create(compo->dev, desc[i].id, | 78 | sti_vid_create(compo->dev, drm_dev, desc[i].id, |
79 | compo->regs + desc[i].offset); | 79 | compo->regs + desc[i].offset); |
80 | break; | 80 | break; |
81 | case STI_MIXER_MAIN_SUBDEV: | 81 | case STI_MIXER_MAIN_SUBDEV: |
82 | case STI_MIXER_AUX_SUBDEV: | 82 | case STI_MIXER_AUX_SUBDEV: |
83 | compo->mixer[mixer_id++] = | 83 | compo->mixer[mixer_id++] = |
84 | sti_mixer_create(compo->dev, desc[i].id, | 84 | sti_mixer_create(compo->dev, drm_dev, desc[i].id, |
85 | compo->regs + desc[i].offset); | 85 | compo->regs + desc[i].offset); |
86 | break; | 86 | break; |
87 | case STI_GPD_SUBDEV: | 87 | case STI_GPD_SUBDEV: |
diff --git a/drivers/gpu/drm/sti/sti_crtc.c b/drivers/gpu/drm/sti/sti_crtc.c index e04deedabd4a..fa47f63b5316 100644 --- a/drivers/gpu/drm/sti/sti_crtc.c +++ b/drivers/gpu/drm/sti/sti_crtc.c | |||
@@ -51,6 +51,15 @@ static void sti_crtc_disabling(struct drm_crtc *crtc) | |||
51 | mixer->status = STI_MIXER_DISABLING; | 51 | mixer->status = STI_MIXER_DISABLING; |
52 | } | 52 | } |
53 | 53 | ||
54 | static bool sti_crtc_mode_fixup(struct drm_crtc *crtc, | ||
55 | const struct drm_display_mode *mode, | ||
56 | struct drm_display_mode *adjusted_mode) | ||
57 | { | ||
58 | /* accept the provided drm_display_mode, do not fix it up */ | ||
59 | drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); | ||
60 | return true; | ||
61 | } | ||
62 | |||
54 | static int | 63 | static int |
55 | sti_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode) | 64 | sti_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode) |
56 | { | 65 | { |
diff --git a/drivers/gpu/drm/sti/sti_cursor.c b/drivers/gpu/drm/sti/sti_cursor.c index 807863106b8d..82b5711fefef 100644 --- a/drivers/gpu/drm/sti/sti_cursor.c +++ b/drivers/gpu/drm/sti/sti_cursor.c | |||
@@ -5,12 +5,10 @@ | |||
5 | * for STMicroelectronics. | 5 | * for STMicroelectronics. |
6 | * License terms: GNU General Public License (GPL), version 2 | 6 | * License terms: GNU General Public License (GPL), version 2 |
7 | */ | 7 | */ |
8 | #include <drm/drmP.h> | ||
9 | 8 | ||
10 | #include <drm/drm_atomic_helper.h> | 9 | #include <drm/drm_atomic.h> |
11 | #include <drm/drm_fb_cma_helper.h> | 10 | #include <drm/drm_fb_cma_helper.h> |
12 | #include <drm/drm_gem_cma_helper.h> | 11 | #include <drm/drm_gem_cma_helper.h> |
13 | #include <drm/drm_plane_helper.h> | ||
14 | 12 | ||
15 | #include "sti_compositor.h" | 13 | #include "sti_compositor.h" |
16 | #include "sti_cursor.h" | 14 | #include "sti_cursor.h" |
@@ -74,6 +72,82 @@ static const uint32_t cursor_supported_formats[] = { | |||
74 | 72 | ||
75 | #define to_sti_cursor(x) container_of(x, struct sti_cursor, plane) | 73 | #define to_sti_cursor(x) container_of(x, struct sti_cursor, plane) |
76 | 74 | ||
75 | #define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \ | ||
76 | readl(cursor->regs + reg)) | ||
77 | |||
78 | static void cursor_dbg_vpo(struct seq_file *s, u32 val) | ||
79 | { | ||
80 | seq_printf(s, "\txdo:%4d\tydo:%4d", val & 0x0FFF, (val >> 16) & 0x0FFF); | ||
81 | } | ||
82 | |||
83 | static void cursor_dbg_size(struct seq_file *s, u32 val) | ||
84 | { | ||
85 | seq_printf(s, "\t%d x %d", val & 0x07FF, (val >> 16) & 0x07FF); | ||
86 | } | ||
87 | |||
88 | static void cursor_dbg_pml(struct seq_file *s, | ||
89 | struct sti_cursor *cursor, u32 val) | ||
90 | { | ||
91 | if (cursor->pixmap.paddr == val) | ||
92 | seq_printf(s, "\tVirt @: %p", cursor->pixmap.base); | ||
93 | } | ||
94 | |||
95 | static void cursor_dbg_cml(struct seq_file *s, | ||
96 | struct sti_cursor *cursor, u32 val) | ||
97 | { | ||
98 | if (cursor->clut_paddr == val) | ||
99 | seq_printf(s, "\tVirt @: %p", cursor->clut); | ||
100 | } | ||
101 | |||
102 | static int cursor_dbg_show(struct seq_file *s, void *data) | ||
103 | { | ||
104 | struct drm_info_node *node = s->private; | ||
105 | struct sti_cursor *cursor = (struct sti_cursor *)node->info_ent->data; | ||
106 | struct drm_device *dev = node->minor->dev; | ||
107 | int ret; | ||
108 | |||
109 | ret = mutex_lock_interruptible(&dev->struct_mutex); | ||
110 | if (ret) | ||
111 | return ret; | ||
112 | |||
113 | seq_printf(s, "%s: (vaddr = 0x%p)", | ||
114 | sti_plane_to_str(&cursor->plane), cursor->regs); | ||
115 | |||
116 | DBGFS_DUMP(CUR_CTL); | ||
117 | DBGFS_DUMP(CUR_VPO); | ||
118 | cursor_dbg_vpo(s, readl(cursor->regs + CUR_VPO)); | ||
119 | DBGFS_DUMP(CUR_PML); | ||
120 | cursor_dbg_pml(s, cursor, readl(cursor->regs + CUR_PML)); | ||
121 | DBGFS_DUMP(CUR_PMP); | ||
122 | DBGFS_DUMP(CUR_SIZE); | ||
123 | cursor_dbg_size(s, readl(cursor->regs + CUR_SIZE)); | ||
124 | DBGFS_DUMP(CUR_CML); | ||
125 | cursor_dbg_cml(s, cursor, readl(cursor->regs + CUR_CML)); | ||
126 | DBGFS_DUMP(CUR_AWS); | ||
127 | DBGFS_DUMP(CUR_AWE); | ||
128 | seq_puts(s, "\n"); | ||
129 | |||
130 | mutex_unlock(&dev->struct_mutex); | ||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | static struct drm_info_list cursor_debugfs_files[] = { | ||
135 | { "cursor", cursor_dbg_show, 0, NULL }, | ||
136 | }; | ||
137 | |||
138 | static int cursor_debugfs_init(struct sti_cursor *cursor, | ||
139 | struct drm_minor *minor) | ||
140 | { | ||
141 | unsigned int i; | ||
142 | |||
143 | for (i = 0; i < ARRAY_SIZE(cursor_debugfs_files); i++) | ||
144 | cursor_debugfs_files[i].data = cursor; | ||
145 | |||
146 | return drm_debugfs_create_files(cursor_debugfs_files, | ||
147 | ARRAY_SIZE(cursor_debugfs_files), | ||
148 | minor->debugfs_root, minor); | ||
149 | } | ||
150 | |||
77 | static void sti_cursor_argb8888_to_clut8(struct sti_cursor *cursor, u32 *src) | 151 | static void sti_cursor_argb8888_to_clut8(struct sti_cursor *cursor, u32 *src) |
78 | { | 152 | { |
79 | u8 *dst = cursor->pixmap.base; | 153 | u8 *dst = cursor->pixmap.base; |
@@ -110,35 +184,31 @@ static void sti_cursor_init(struct sti_cursor *cursor) | |||
110 | (b * 5); | 184 | (b * 5); |
111 | } | 185 | } |
112 | 186 | ||
113 | static void sti_cursor_atomic_update(struct drm_plane *drm_plane, | 187 | static int sti_cursor_atomic_check(struct drm_plane *drm_plane, |
114 | struct drm_plane_state *oldstate) | 188 | struct drm_plane_state *state) |
115 | { | 189 | { |
116 | struct drm_plane_state *state = drm_plane->state; | ||
117 | struct sti_plane *plane = to_sti_plane(drm_plane); | 190 | struct sti_plane *plane = to_sti_plane(drm_plane); |
118 | struct sti_cursor *cursor = to_sti_cursor(plane); | 191 | struct sti_cursor *cursor = to_sti_cursor(plane); |
119 | struct drm_crtc *crtc = state->crtc; | 192 | struct drm_crtc *crtc = state->crtc; |
120 | struct sti_mixer *mixer = to_sti_mixer(crtc); | ||
121 | struct drm_framebuffer *fb = state->fb; | 193 | struct drm_framebuffer *fb = state->fb; |
122 | struct drm_display_mode *mode = &crtc->mode; | 194 | struct drm_crtc_state *crtc_state; |
123 | int dst_x = state->crtc_x; | 195 | struct drm_display_mode *mode; |
124 | int dst_y = state->crtc_y; | 196 | int dst_x, dst_y, dst_w, dst_h; |
125 | int dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); | 197 | int src_w, src_h; |
126 | int dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); | 198 | |
199 | /* no need for further checks if the plane is being disabled */ | ||
200 | if (!crtc || !fb) | ||
201 | return 0; | ||
202 | |||
203 | crtc_state = drm_atomic_get_crtc_state(state->state, crtc); | ||
204 | mode = &crtc_state->mode; | ||
205 | dst_x = state->crtc_x; | ||
206 | dst_y = state->crtc_y; | ||
207 | dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); | ||
208 | dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); | ||
127 | /* src_x are in 16.16 format */ | 209 | /* src_x are in 16.16 format */ |
128 | int src_w = state->src_w >> 16; | 210 | src_w = state->src_w >> 16; |
129 | int src_h = state->src_h >> 16; | 211 | src_h = state->src_h >> 16; |
130 | bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false; | ||
131 | struct drm_gem_cma_object *cma_obj; | ||
132 | u32 y, x; | ||
133 | u32 val; | ||
134 | |||
135 | DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n", | ||
136 | crtc->base.id, sti_mixer_to_str(mixer), | ||
137 | drm_plane->base.id, sti_plane_to_str(plane)); | ||
138 | DRM_DEBUG_KMS("(%dx%d)@(%d,%d)\n", dst_w, dst_h, dst_x, dst_y); | ||
139 | |||
140 | dev_dbg(cursor->dev, "%s %s\n", __func__, | ||
141 | sti_plane_to_str(plane)); | ||
142 | 212 | ||
143 | if (src_w < STI_CURS_MIN_SIZE || | 213 | if (src_w < STI_CURS_MIN_SIZE || |
144 | src_h < STI_CURS_MIN_SIZE || | 214 | src_h < STI_CURS_MIN_SIZE || |
@@ -146,7 +216,7 @@ static void sti_cursor_atomic_update(struct drm_plane *drm_plane, | |||
146 | src_h > STI_CURS_MAX_SIZE) { | 216 | src_h > STI_CURS_MAX_SIZE) { |
147 | DRM_ERROR("Invalid cursor size (%dx%d)\n", | 217 | DRM_ERROR("Invalid cursor size (%dx%d)\n", |
148 | src_w, src_h); | 218 | src_w, src_h); |
149 | return; | 219 | return -EINVAL; |
150 | } | 220 | } |
151 | 221 | ||
152 | /* If the cursor size has changed, re-allocated the pixmap */ | 222 | /* If the cursor size has changed, re-allocated the pixmap */ |
@@ -170,16 +240,46 @@ static void sti_cursor_atomic_update(struct drm_plane *drm_plane, | |||
170 | GFP_KERNEL | GFP_DMA); | 240 | GFP_KERNEL | GFP_DMA); |
171 | if (!cursor->pixmap.base) { | 241 | if (!cursor->pixmap.base) { |
172 | DRM_ERROR("Failed to allocate memory for pixmap\n"); | 242 | DRM_ERROR("Failed to allocate memory for pixmap\n"); |
173 | return; | 243 | return -EINVAL; |
174 | } | 244 | } |
175 | } | 245 | } |
176 | 246 | ||
177 | cma_obj = drm_fb_cma_get_gem_obj(fb, 0); | 247 | if (!drm_fb_cma_get_gem_obj(fb, 0)) { |
178 | if (!cma_obj) { | ||
179 | DRM_ERROR("Can't get CMA GEM object for fb\n"); | 248 | DRM_ERROR("Can't get CMA GEM object for fb\n"); |
180 | return; | 249 | return -EINVAL; |
181 | } | 250 | } |
182 | 251 | ||
252 | DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n", | ||
253 | crtc->base.id, sti_mixer_to_str(to_sti_mixer(crtc)), | ||
254 | drm_plane->base.id, sti_plane_to_str(plane)); | ||
255 | DRM_DEBUG_KMS("(%dx%d)@(%d,%d)\n", dst_w, dst_h, dst_x, dst_y); | ||
256 | |||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static void sti_cursor_atomic_update(struct drm_plane *drm_plane, | ||
261 | struct drm_plane_state *oldstate) | ||
262 | { | ||
263 | struct drm_plane_state *state = drm_plane->state; | ||
264 | struct sti_plane *plane = to_sti_plane(drm_plane); | ||
265 | struct sti_cursor *cursor = to_sti_cursor(plane); | ||
266 | struct drm_crtc *crtc = state->crtc; | ||
267 | struct drm_framebuffer *fb = state->fb; | ||
268 | struct drm_display_mode *mode; | ||
269 | int dst_x, dst_y; | ||
270 | struct drm_gem_cma_object *cma_obj; | ||
271 | u32 y, x; | ||
272 | u32 val; | ||
273 | |||
274 | if (!crtc || !fb) | ||
275 | return; | ||
276 | |||
277 | mode = &crtc->mode; | ||
278 | dst_x = state->crtc_x; | ||
279 | dst_y = state->crtc_y; | ||
280 | |||
281 | cma_obj = drm_fb_cma_get_gem_obj(fb, 0); | ||
282 | |||
183 | /* Convert ARGB8888 to CLUT8 */ | 283 | /* Convert ARGB8888 to CLUT8 */ |
184 | sti_cursor_argb8888_to_clut8(cursor, (u32 *)cma_obj->vaddr); | 284 | sti_cursor_argb8888_to_clut8(cursor, (u32 *)cma_obj->vaddr); |
185 | 285 | ||
@@ -193,21 +293,21 @@ static void sti_cursor_atomic_update(struct drm_plane *drm_plane, | |||
193 | val = y << 16 | x; | 293 | val = y << 16 | x; |
194 | writel(val, cursor->regs + CUR_AWE); | 294 | writel(val, cursor->regs + CUR_AWE); |
195 | 295 | ||
196 | if (first_prepare) { | ||
197 | /* Set and fetch CLUT */ | ||
198 | writel(cursor->clut_paddr, cursor->regs + CUR_CML); | ||
199 | writel(CUR_CTL_CLUT_UPDATE, cursor->regs + CUR_CTL); | ||
200 | } | ||
201 | |||
202 | /* Set memory location, size, and position */ | 296 | /* Set memory location, size, and position */ |
203 | writel(cursor->pixmap.paddr, cursor->regs + CUR_PML); | 297 | writel(cursor->pixmap.paddr, cursor->regs + CUR_PML); |
204 | writel(cursor->width, cursor->regs + CUR_PMP); | 298 | writel(cursor->width, cursor->regs + CUR_PMP); |
205 | writel(cursor->height << 16 | cursor->width, cursor->regs + CUR_SIZE); | 299 | writel(cursor->height << 16 | cursor->width, cursor->regs + CUR_SIZE); |
206 | 300 | ||
207 | y = sti_vtg_get_line_number(*mode, dst_y); | 301 | y = sti_vtg_get_line_number(*mode, dst_y); |
208 | x = sti_vtg_get_pixel_number(*mode, dst_y); | 302 | x = sti_vtg_get_pixel_number(*mode, dst_x); |
209 | writel((y << 16) | x, cursor->regs + CUR_VPO); | 303 | writel((y << 16) | x, cursor->regs + CUR_VPO); |
210 | 304 | ||
305 | /* Set and fetch CLUT */ | ||
306 | writel(cursor->clut_paddr, cursor->regs + CUR_CML); | ||
307 | writel(CUR_CTL_CLUT_UPDATE, cursor->regs + CUR_CTL); | ||
308 | |||
309 | sti_plane_update_fps(plane, true, false); | ||
310 | |||
211 | plane->status = STI_PLANE_UPDATED; | 311 | plane->status = STI_PLANE_UPDATED; |
212 | } | 312 | } |
213 | 313 | ||
@@ -215,7 +315,6 @@ static void sti_cursor_atomic_disable(struct drm_plane *drm_plane, | |||
215 | struct drm_plane_state *oldstate) | 315 | struct drm_plane_state *oldstate) |
216 | { | 316 | { |
217 | struct sti_plane *plane = to_sti_plane(drm_plane); | 317 | struct sti_plane *plane = to_sti_plane(drm_plane); |
218 | struct sti_mixer *mixer = to_sti_mixer(drm_plane->crtc); | ||
219 | 318 | ||
220 | if (!drm_plane->crtc) { | 319 | if (!drm_plane->crtc) { |
221 | DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", | 320 | DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", |
@@ -224,13 +323,15 @@ static void sti_cursor_atomic_disable(struct drm_plane *drm_plane, | |||
224 | } | 323 | } |
225 | 324 | ||
226 | DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n", | 325 | DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n", |
227 | drm_plane->crtc->base.id, sti_mixer_to_str(mixer), | 326 | drm_plane->crtc->base.id, |
327 | sti_mixer_to_str(to_sti_mixer(drm_plane->crtc)), | ||
228 | drm_plane->base.id, sti_plane_to_str(plane)); | 328 | drm_plane->base.id, sti_plane_to_str(plane)); |
229 | 329 | ||
230 | plane->status = STI_PLANE_DISABLING; | 330 | plane->status = STI_PLANE_DISABLING; |
231 | } | 331 | } |
232 | 332 | ||
233 | static const struct drm_plane_helper_funcs sti_cursor_helpers_funcs = { | 333 | static const struct drm_plane_helper_funcs sti_cursor_helpers_funcs = { |
334 | .atomic_check = sti_cursor_atomic_check, | ||
234 | .atomic_update = sti_cursor_atomic_update, | 335 | .atomic_update = sti_cursor_atomic_update, |
235 | .atomic_disable = sti_cursor_atomic_disable, | 336 | .atomic_disable = sti_cursor_atomic_disable, |
236 | }; | 337 | }; |
@@ -283,6 +384,9 @@ struct drm_plane *sti_cursor_create(struct drm_device *drm_dev, | |||
283 | 384 | ||
284 | sti_plane_init_property(&cursor->plane, DRM_PLANE_TYPE_CURSOR); | 385 | sti_plane_init_property(&cursor->plane, DRM_PLANE_TYPE_CURSOR); |
285 | 386 | ||
387 | if (cursor_debugfs_init(cursor, drm_dev->primary)) | ||
388 | DRM_ERROR("CURSOR debugfs setup failed\n"); | ||
389 | |||
286 | return &cursor->plane.drm_plane; | 390 | return &cursor->plane.drm_plane; |
287 | 391 | ||
288 | err_plane: | 392 | err_plane: |
diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index 506b5626f3ed..6bd6abaa5a70 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c | |||
@@ -20,6 +20,7 @@ | |||
20 | 20 | ||
21 | #include "sti_crtc.h" | 21 | #include "sti_crtc.h" |
22 | #include "sti_drv.h" | 22 | #include "sti_drv.h" |
23 | #include "sti_plane.h" | ||
23 | 24 | ||
24 | #define DRIVER_NAME "sti" | 25 | #define DRIVER_NAME "sti" |
25 | #define DRIVER_DESC "STMicroelectronics SoC DRM" | 26 | #define DRIVER_DESC "STMicroelectronics SoC DRM" |
@@ -30,6 +31,130 @@ | |||
30 | #define STI_MAX_FB_HEIGHT 4096 | 31 | #define STI_MAX_FB_HEIGHT 4096 |
31 | #define STI_MAX_FB_WIDTH 4096 | 32 | #define STI_MAX_FB_WIDTH 4096 |
32 | 33 | ||
34 | static int sti_drm_fps_get(void *data, u64 *val) | ||
35 | { | ||
36 | struct drm_device *drm_dev = data; | ||
37 | struct drm_plane *p; | ||
38 | unsigned int i = 0; | ||
39 | |||
40 | *val = 0; | ||
41 | list_for_each_entry(p, &drm_dev->mode_config.plane_list, head) { | ||
42 | struct sti_plane *plane = to_sti_plane(p); | ||
43 | |||
44 | *val |= plane->fps_info.output << i; | ||
45 | i++; | ||
46 | } | ||
47 | |||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | static int sti_drm_fps_set(void *data, u64 val) | ||
52 | { | ||
53 | struct drm_device *drm_dev = data; | ||
54 | struct drm_plane *p; | ||
55 | unsigned int i = 0; | ||
56 | |||
57 | list_for_each_entry(p, &drm_dev->mode_config.plane_list, head) { | ||
58 | struct sti_plane *plane = to_sti_plane(p); | ||
59 | |||
60 | plane->fps_info.output = (val >> i) & 1; | ||
61 | i++; | ||
62 | } | ||
63 | |||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | DEFINE_SIMPLE_ATTRIBUTE(sti_drm_fps_fops, | ||
68 | sti_drm_fps_get, sti_drm_fps_set, "%llu\n"); | ||
69 | |||
70 | static int sti_drm_fps_dbg_show(struct seq_file *s, void *data) | ||
71 | { | ||
72 | struct drm_info_node *node = s->private; | ||
73 | struct drm_device *dev = node->minor->dev; | ||
74 | struct drm_plane *p; | ||
75 | int ret; | ||
76 | |||
77 | ret = mutex_lock_interruptible(&dev->struct_mutex); | ||
78 | if (ret) | ||
79 | return ret; | ||
80 | |||
81 | list_for_each_entry(p, &dev->mode_config.plane_list, head) { | ||
82 | struct sti_plane *plane = to_sti_plane(p); | ||
83 | |||
84 | seq_printf(s, "%s%s\n", | ||
85 | plane->fps_info.fps_str, | ||
86 | plane->fps_info.fips_str); | ||
87 | } | ||
88 | |||
89 | mutex_unlock(&dev->struct_mutex); | ||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | static struct drm_info_list sti_drm_dbg_list[] = { | ||
94 | {"fps_get", sti_drm_fps_dbg_show, 0}, | ||
95 | }; | ||
96 | |||
97 | static int sti_drm_debugfs_create(struct dentry *root, | ||
98 | struct drm_minor *minor, | ||
99 | const char *name, | ||
100 | const struct file_operations *fops) | ||
101 | { | ||
102 | struct drm_device *dev = minor->dev; | ||
103 | struct drm_info_node *node; | ||
104 | struct dentry *ent; | ||
105 | |||
106 | ent = debugfs_create_file(name, S_IRUGO | S_IWUSR, root, dev, fops); | ||
107 | if (IS_ERR(ent)) | ||
108 | return PTR_ERR(ent); | ||
109 | |||
110 | node = kmalloc(sizeof(*node), GFP_KERNEL); | ||
111 | if (!node) { | ||
112 | debugfs_remove(ent); | ||
113 | return -ENOMEM; | ||
114 | } | ||
115 | |||
116 | node->minor = minor; | ||
117 | node->dent = ent; | ||
118 | node->info_ent = (void *)fops; | ||
119 | |||
120 | mutex_lock(&minor->debugfs_lock); | ||
121 | list_add(&node->list, &minor->debugfs_list); | ||
122 | mutex_unlock(&minor->debugfs_lock); | ||
123 | |||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | static int sti_drm_dbg_init(struct drm_minor *minor) | ||
128 | { | ||
129 | int ret; | ||
130 | |||
131 | ret = drm_debugfs_create_files(sti_drm_dbg_list, | ||
132 | ARRAY_SIZE(sti_drm_dbg_list), | ||
133 | minor->debugfs_root, minor); | ||
134 | if (ret) | ||
135 | goto err; | ||
136 | |||
137 | ret = sti_drm_debugfs_create(minor->debugfs_root, minor, "fps_show", | ||
138 | &sti_drm_fps_fops); | ||
139 | if (ret) | ||
140 | goto err; | ||
141 | |||
142 | DRM_INFO("%s: debugfs installed\n", DRIVER_NAME); | ||
143 | return 0; | ||
144 | err: | ||
145 | DRM_ERROR("%s: cannot install debugfs\n", DRIVER_NAME); | ||
146 | return ret; | ||
147 | } | ||
148 | |||
149 | void sti_drm_dbg_cleanup(struct drm_minor *minor) | ||
150 | { | ||
151 | drm_debugfs_remove_files(sti_drm_dbg_list, | ||
152 | ARRAY_SIZE(sti_drm_dbg_list), minor); | ||
153 | |||
154 | drm_debugfs_remove_files((struct drm_info_list *)&sti_drm_fps_fops, | ||
155 | 1, minor); | ||
156 | } | ||
157 | |||
33 | static void sti_atomic_schedule(struct sti_private *private, | 158 | static void sti_atomic_schedule(struct sti_private *private, |
34 | struct drm_atomic_state *state) | 159 | struct drm_atomic_state *state) |
35 | { | 160 | { |
@@ -181,18 +306,9 @@ static const struct file_operations sti_driver_fops = { | |||
181 | .release = drm_release, | 306 | .release = drm_release, |
182 | }; | 307 | }; |
183 | 308 | ||
184 | static struct dma_buf *sti_gem_prime_export(struct drm_device *dev, | ||
185 | struct drm_gem_object *obj, | ||
186 | int flags) | ||
187 | { | ||
188 | /* we want to be able to write in mmapped buffer */ | ||
189 | flags |= O_RDWR; | ||
190 | return drm_gem_prime_export(dev, obj, flags); | ||
191 | } | ||
192 | |||
193 | static struct drm_driver sti_driver = { | 309 | static struct drm_driver sti_driver = { |
194 | .driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET | | 310 | .driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET | |
195 | DRIVER_GEM | DRIVER_PRIME, | 311 | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, |
196 | .load = sti_load, | 312 | .load = sti_load, |
197 | .gem_free_object = drm_gem_cma_free_object, | 313 | .gem_free_object = drm_gem_cma_free_object, |
198 | .gem_vm_ops = &drm_gem_cma_vm_ops, | 314 | .gem_vm_ops = &drm_gem_cma_vm_ops, |
@@ -207,7 +323,7 @@ static struct drm_driver sti_driver = { | |||
207 | 323 | ||
208 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, | 324 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, |
209 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | 325 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, |
210 | .gem_prime_export = sti_gem_prime_export, | 326 | .gem_prime_export = drm_gem_prime_export, |
211 | .gem_prime_import = drm_gem_prime_import, | 327 | .gem_prime_import = drm_gem_prime_import, |
212 | .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, | 328 | .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, |
213 | .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, | 329 | .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, |
@@ -215,6 +331,9 @@ static struct drm_driver sti_driver = { | |||
215 | .gem_prime_vunmap = drm_gem_cma_prime_vunmap, | 331 | .gem_prime_vunmap = drm_gem_cma_prime_vunmap, |
216 | .gem_prime_mmap = drm_gem_cma_prime_mmap, | 332 | .gem_prime_mmap = drm_gem_cma_prime_mmap, |
217 | 333 | ||
334 | .debugfs_init = sti_drm_dbg_init, | ||
335 | .debugfs_cleanup = sti_drm_dbg_cleanup, | ||
336 | |||
218 | .name = DRIVER_NAME, | 337 | .name = DRIVER_NAME, |
219 | .desc = DRIVER_DESC, | 338 | .desc = DRIVER_DESC, |
220 | .date = DRIVER_DATE, | 339 | .date = DRIVER_DATE, |
diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c index 45cbe2bf7dd6..25f76632002c 100644 --- a/drivers/gpu/drm/sti/sti_dvo.c +++ b/drivers/gpu/drm/sti/sti_dvo.c | |||
@@ -6,6 +6,7 @@ | |||
6 | 6 | ||
7 | #include <linux/clk.h> | 7 | #include <linux/clk.h> |
8 | #include <linux/component.h> | 8 | #include <linux/component.h> |
9 | #include <linux/debugfs.h> | ||
9 | #include <linux/module.h> | 10 | #include <linux/module.h> |
10 | #include <linux/of_gpio.h> | 11 | #include <linux/of_gpio.h> |
11 | #include <linux/platform_device.h> | 12 | #include <linux/platform_device.h> |
@@ -156,6 +157,69 @@ static void dvo_awg_configure(struct sti_dvo *dvo, u32 *awg_ram_code, int nb) | |||
156 | writel(DVO_AWG_CTRL_EN, dvo->regs + DVO_AWG_DIGSYNC_CTRL); | 157 | writel(DVO_AWG_CTRL_EN, dvo->regs + DVO_AWG_DIGSYNC_CTRL); |
157 | } | 158 | } |
158 | 159 | ||
160 | #define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \ | ||
161 | readl(dvo->regs + reg)) | ||
162 | |||
163 | static void dvo_dbg_awg_microcode(struct seq_file *s, void __iomem *reg) | ||
164 | { | ||
165 | unsigned int i; | ||
166 | |||
167 | seq_puts(s, "\n\n"); | ||
168 | seq_puts(s, " DVO AWG microcode:"); | ||
169 | for (i = 0; i < AWG_MAX_INST; i++) { | ||
170 | if (i % 8 == 0) | ||
171 | seq_printf(s, "\n %04X:", i); | ||
172 | seq_printf(s, " %04X", readl(reg + i * 4)); | ||
173 | } | ||
174 | } | ||
175 | |||
176 | static int dvo_dbg_show(struct seq_file *s, void *data) | ||
177 | { | ||
178 | struct drm_info_node *node = s->private; | ||
179 | struct sti_dvo *dvo = (struct sti_dvo *)node->info_ent->data; | ||
180 | struct drm_device *dev = node->minor->dev; | ||
181 | int ret; | ||
182 | |||
183 | ret = mutex_lock_interruptible(&dev->struct_mutex); | ||
184 | if (ret) | ||
185 | return ret; | ||
186 | |||
187 | seq_printf(s, "DVO: (vaddr = 0x%p)", dvo->regs); | ||
188 | DBGFS_DUMP(DVO_AWG_DIGSYNC_CTRL); | ||
189 | DBGFS_DUMP(DVO_DOF_CFG); | ||
190 | DBGFS_DUMP(DVO_LUT_PROG_LOW); | ||
191 | DBGFS_DUMP(DVO_LUT_PROG_MID); | ||
192 | DBGFS_DUMP(DVO_LUT_PROG_HIGH); | ||
193 | dvo_dbg_awg_microcode(s, dvo->regs + DVO_DIGSYNC_INSTR_I); | ||
194 | seq_puts(s, "\n"); | ||
195 | |||
196 | mutex_unlock(&dev->struct_mutex); | ||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | static struct drm_info_list dvo_debugfs_files[] = { | ||
201 | { "dvo", dvo_dbg_show, 0, NULL }, | ||
202 | }; | ||
203 | |||
204 | static void dvo_debugfs_exit(struct sti_dvo *dvo, struct drm_minor *minor) | ||
205 | { | ||
206 | drm_debugfs_remove_files(dvo_debugfs_files, | ||
207 | ARRAY_SIZE(dvo_debugfs_files), | ||
208 | minor); | ||
209 | } | ||
210 | |||
211 | static int dvo_debugfs_init(struct sti_dvo *dvo, struct drm_minor *minor) | ||
212 | { | ||
213 | unsigned int i; | ||
214 | |||
215 | for (i = 0; i < ARRAY_SIZE(dvo_debugfs_files); i++) | ||
216 | dvo_debugfs_files[i].data = dvo; | ||
217 | |||
218 | return drm_debugfs_create_files(dvo_debugfs_files, | ||
219 | ARRAY_SIZE(dvo_debugfs_files), | ||
220 | minor->debugfs_root, minor); | ||
221 | } | ||
222 | |||
159 | static void sti_dvo_disable(struct drm_bridge *bridge) | 223 | static void sti_dvo_disable(struct drm_bridge *bridge) |
160 | { | 224 | { |
161 | struct sti_dvo *dvo = bridge->driver_private; | 225 | struct sti_dvo *dvo = bridge->driver_private; |
@@ -345,12 +409,14 @@ sti_dvo_connector_detect(struct drm_connector *connector, bool force) | |||
345 | 409 | ||
346 | DRM_DEBUG_DRIVER("\n"); | 410 | DRM_DEBUG_DRIVER("\n"); |
347 | 411 | ||
348 | if (!dvo->panel) | 412 | if (!dvo->panel) { |
349 | dvo->panel = of_drm_find_panel(dvo->panel_node); | 413 | dvo->panel = of_drm_find_panel(dvo->panel_node); |
414 | if (dvo->panel) | ||
415 | drm_panel_attach(dvo->panel, connector); | ||
416 | } | ||
350 | 417 | ||
351 | if (dvo->panel) | 418 | if (dvo->panel) |
352 | if (!drm_panel_attach(dvo->panel, connector)) | 419 | return connector_status_connected; |
353 | return connector_status_connected; | ||
354 | 420 | ||
355 | return connector_status_disconnected; | 421 | return connector_status_disconnected; |
356 | } | 422 | } |
@@ -453,6 +519,9 @@ static int sti_dvo_bind(struct device *dev, struct device *master, void *data) | |||
453 | goto err_sysfs; | 519 | goto err_sysfs; |
454 | } | 520 | } |
455 | 521 | ||
522 | if (dvo_debugfs_init(dvo, drm_dev->primary)) | ||
523 | DRM_ERROR("DVO debugfs setup failed\n"); | ||
524 | |||
456 | return 0; | 525 | return 0; |
457 | 526 | ||
458 | err_sysfs: | 527 | err_sysfs: |
@@ -467,6 +536,9 @@ static void sti_dvo_unbind(struct device *dev, | |||
467 | struct device *master, void *data) | 536 | struct device *master, void *data) |
468 | { | 537 | { |
469 | struct sti_dvo *dvo = dev_get_drvdata(dev); | 538 | struct sti_dvo *dvo = dev_get_drvdata(dev); |
539 | struct drm_device *drm_dev = data; | ||
540 | |||
541 | dvo_debugfs_exit(dvo, drm_dev->primary); | ||
470 | 542 | ||
471 | drm_bridge_remove(dvo->bridge); | 543 | drm_bridge_remove(dvo->bridge); |
472 | } | 544 | } |
diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c index f9a1d92c9d95..67f606a41c3f 100644 --- a/drivers/gpu/drm/sti/sti_gdp.c +++ b/drivers/gpu/drm/sti/sti_gdp.c | |||
@@ -6,9 +6,7 @@ | |||
6 | * License terms: GNU General Public License (GPL), version 2 | 6 | * License terms: GNU General Public License (GPL), version 2 |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <linux/clk.h> | 9 | #include <drm/drm_atomic.h> |
10 | #include <linux/dma-mapping.h> | ||
11 | |||
12 | #include <drm/drm_fb_cma_helper.h> | 10 | #include <drm/drm_fb_cma_helper.h> |
13 | #include <drm/drm_gem_cma_helper.h> | 11 | #include <drm/drm_gem_cma_helper.h> |
14 | 12 | ||
@@ -32,10 +30,23 @@ | |||
32 | #define GDP_ABGR8888 (GDP_ARGB8888 | BIGNOTLITTLE | ALPHASWITCH) | 30 | #define GDP_ABGR8888 (GDP_ARGB8888 | BIGNOTLITTLE | ALPHASWITCH) |
33 | #define GDP_ARGB1555 0x06 | 31 | #define GDP_ARGB1555 0x06 |
34 | #define GDP_ARGB4444 0x07 | 32 | #define GDP_ARGB4444 0x07 |
35 | #define GDP_CLUT8 0x0B | 33 | |
36 | #define GDP_YCBR888 0x10 | 34 | #define GDP2STR(fmt) { GDP_ ## fmt, #fmt } |
37 | #define GDP_YCBR422R 0x12 | 35 | |
38 | #define GDP_AYCBR8888 0x15 | 36 | static struct gdp_format_to_str { |
37 | int format; | ||
38 | char name[20]; | ||
39 | } gdp_format_to_str[] = { | ||
40 | GDP2STR(RGB565), | ||
41 | GDP2STR(RGB888), | ||
42 | GDP2STR(RGB888_32), | ||
43 | GDP2STR(XBGR8888), | ||
44 | GDP2STR(ARGB8565), | ||
45 | GDP2STR(ARGB8888), | ||
46 | GDP2STR(ABGR8888), | ||
47 | GDP2STR(ARGB1555), | ||
48 | GDP2STR(ARGB4444) | ||
49 | }; | ||
39 | 50 | ||
40 | #define GAM_GDP_CTL_OFFSET 0x00 | 51 | #define GAM_GDP_CTL_OFFSET 0x00 |
41 | #define GAM_GDP_AGC_OFFSET 0x04 | 52 | #define GAM_GDP_AGC_OFFSET 0x04 |
@@ -97,6 +108,7 @@ struct sti_gdp_node_list { | |||
97 | * @vtg_field_nb: callback for VTG FIELD (top or bottom) notification | 108 | * @vtg_field_nb: callback for VTG FIELD (top or bottom) notification |
98 | * @is_curr_top: true if the current node processed is the top field | 109 | * @is_curr_top: true if the current node processed is the top field |
99 | * @node_list: array of node list | 110 | * @node_list: array of node list |
111 | * @vtg: registered vtg | ||
100 | */ | 112 | */ |
101 | struct sti_gdp { | 113 | struct sti_gdp { |
102 | struct sti_plane plane; | 114 | struct sti_plane plane; |
@@ -108,6 +120,7 @@ struct sti_gdp { | |||
108 | struct notifier_block vtg_field_nb; | 120 | struct notifier_block vtg_field_nb; |
109 | bool is_curr_top; | 121 | bool is_curr_top; |
110 | struct sti_gdp_node_list node_list[GDP_NODE_NB_BANK]; | 122 | struct sti_gdp_node_list node_list[GDP_NODE_NB_BANK]; |
123 | struct sti_vtg *vtg; | ||
111 | }; | 124 | }; |
112 | 125 | ||
113 | #define to_sti_gdp(x) container_of(x, struct sti_gdp, plane) | 126 | #define to_sti_gdp(x) container_of(x, struct sti_gdp, plane) |
@@ -121,12 +134,224 @@ static const uint32_t gdp_supported_formats[] = { | |||
121 | DRM_FORMAT_ARGB1555, | 134 | DRM_FORMAT_ARGB1555, |
122 | DRM_FORMAT_RGB565, | 135 | DRM_FORMAT_RGB565, |
123 | DRM_FORMAT_RGB888, | 136 | DRM_FORMAT_RGB888, |
124 | DRM_FORMAT_AYUV, | ||
125 | DRM_FORMAT_YUV444, | ||
126 | DRM_FORMAT_VYUY, | ||
127 | DRM_FORMAT_C8, | ||
128 | }; | 137 | }; |
129 | 138 | ||
139 | #define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \ | ||
140 | readl(gdp->regs + reg ## _OFFSET)) | ||
141 | |||
142 | static void gdp_dbg_ctl(struct seq_file *s, int val) | ||
143 | { | ||
144 | int i; | ||
145 | |||
146 | seq_puts(s, "\tColor:"); | ||
147 | for (i = 0; i < ARRAY_SIZE(gdp_format_to_str); i++) { | ||
148 | if (gdp_format_to_str[i].format == (val & 0x1F)) { | ||
149 | seq_printf(s, gdp_format_to_str[i].name); | ||
150 | break; | ||
151 | } | ||
152 | } | ||
153 | if (i == ARRAY_SIZE(gdp_format_to_str)) | ||
154 | seq_puts(s, "<UNKNOWN>"); | ||
155 | |||
156 | seq_printf(s, "\tWaitNextVsync:%d", val & WAIT_NEXT_VSYNC ? 1 : 0); | ||
157 | } | ||
158 | |||
159 | static void gdp_dbg_vpo(struct seq_file *s, int val) | ||
160 | { | ||
161 | seq_printf(s, "\txdo:%4d\tydo:%4d", val & 0xFFFF, (val >> 16) & 0xFFFF); | ||
162 | } | ||
163 | |||
164 | static void gdp_dbg_vps(struct seq_file *s, int val) | ||
165 | { | ||
166 | seq_printf(s, "\txds:%4d\tyds:%4d", val & 0xFFFF, (val >> 16) & 0xFFFF); | ||
167 | } | ||
168 | |||
169 | static void gdp_dbg_size(struct seq_file *s, int val) | ||
170 | { | ||
171 | seq_printf(s, "\t%d x %d", val & 0xFFFF, (val >> 16) & 0xFFFF); | ||
172 | } | ||
173 | |||
174 | static void gdp_dbg_nvn(struct seq_file *s, struct sti_gdp *gdp, int val) | ||
175 | { | ||
176 | void *base = NULL; | ||
177 | unsigned int i; | ||
178 | |||
179 | for (i = 0; i < GDP_NODE_NB_BANK; i++) { | ||
180 | if (gdp->node_list[i].top_field_paddr == val) { | ||
181 | base = gdp->node_list[i].top_field; | ||
182 | break; | ||
183 | } | ||
184 | if (gdp->node_list[i].btm_field_paddr == val) { | ||
185 | base = gdp->node_list[i].btm_field; | ||
186 | break; | ||
187 | } | ||
188 | } | ||
189 | |||
190 | if (base) | ||
191 | seq_printf(s, "\tVirt @: %p", base); | ||
192 | } | ||
193 | |||
194 | static void gdp_dbg_ppt(struct seq_file *s, int val) | ||
195 | { | ||
196 | if (val & GAM_GDP_PPT_IGNORE) | ||
197 | seq_puts(s, "\tNot displayed on mixer!"); | ||
198 | } | ||
199 | |||
200 | static void gdp_dbg_mst(struct seq_file *s, int val) | ||
201 | { | ||
202 | if (val & 1) | ||
203 | seq_puts(s, "\tBUFFER UNDERFLOW!"); | ||
204 | } | ||
205 | |||
206 | static int gdp_dbg_show(struct seq_file *s, void *data) | ||
207 | { | ||
208 | struct drm_info_node *node = s->private; | ||
209 | struct sti_gdp *gdp = (struct sti_gdp *)node->info_ent->data; | ||
210 | struct drm_device *dev = node->minor->dev; | ||
211 | struct drm_plane *drm_plane = &gdp->plane.drm_plane; | ||
212 | struct drm_crtc *crtc = drm_plane->crtc; | ||
213 | int ret; | ||
214 | |||
215 | ret = mutex_lock_interruptible(&dev->struct_mutex); | ||
216 | if (ret) | ||
217 | return ret; | ||
218 | |||
219 | seq_printf(s, "%s: (vaddr = 0x%p)", | ||
220 | sti_plane_to_str(&gdp->plane), gdp->regs); | ||
221 | |||
222 | DBGFS_DUMP(GAM_GDP_CTL); | ||
223 | gdp_dbg_ctl(s, readl(gdp->regs + GAM_GDP_CTL_OFFSET)); | ||
224 | DBGFS_DUMP(GAM_GDP_AGC); | ||
225 | DBGFS_DUMP(GAM_GDP_VPO); | ||
226 | gdp_dbg_vpo(s, readl(gdp->regs + GAM_GDP_VPO_OFFSET)); | ||
227 | DBGFS_DUMP(GAM_GDP_VPS); | ||
228 | gdp_dbg_vps(s, readl(gdp->regs + GAM_GDP_VPS_OFFSET)); | ||
229 | DBGFS_DUMP(GAM_GDP_PML); | ||
230 | DBGFS_DUMP(GAM_GDP_PMP); | ||
231 | DBGFS_DUMP(GAM_GDP_SIZE); | ||
232 | gdp_dbg_size(s, readl(gdp->regs + GAM_GDP_SIZE_OFFSET)); | ||
233 | DBGFS_DUMP(GAM_GDP_NVN); | ||
234 | gdp_dbg_nvn(s, gdp, readl(gdp->regs + GAM_GDP_NVN_OFFSET)); | ||
235 | DBGFS_DUMP(GAM_GDP_KEY1); | ||
236 | DBGFS_DUMP(GAM_GDP_KEY2); | ||
237 | DBGFS_DUMP(GAM_GDP_PPT); | ||
238 | gdp_dbg_ppt(s, readl(gdp->regs + GAM_GDP_PPT_OFFSET)); | ||
239 | DBGFS_DUMP(GAM_GDP_CML); | ||
240 | DBGFS_DUMP(GAM_GDP_MST); | ||
241 | gdp_dbg_mst(s, readl(gdp->regs + GAM_GDP_MST_OFFSET)); | ||
242 | |||
243 | seq_puts(s, "\n\n"); | ||
244 | if (!crtc) | ||
245 | seq_puts(s, " Not connected to any DRM CRTC\n"); | ||
246 | else | ||
247 | seq_printf(s, " Connected to DRM CRTC #%d (%s)\n", | ||
248 | crtc->base.id, sti_mixer_to_str(to_sti_mixer(crtc))); | ||
249 | |||
250 | mutex_unlock(&dev->struct_mutex); | ||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | static void gdp_node_dump_node(struct seq_file *s, struct sti_gdp_node *node) | ||
255 | { | ||
256 | seq_printf(s, "\t@:0x%p", node); | ||
257 | seq_printf(s, "\n\tCTL 0x%08X", node->gam_gdp_ctl); | ||
258 | gdp_dbg_ctl(s, node->gam_gdp_ctl); | ||
259 | seq_printf(s, "\n\tAGC 0x%08X", node->gam_gdp_agc); | ||
260 | seq_printf(s, "\n\tVPO 0x%08X", node->gam_gdp_vpo); | ||
261 | gdp_dbg_vpo(s, node->gam_gdp_vpo); | ||
262 | seq_printf(s, "\n\tVPS 0x%08X", node->gam_gdp_vps); | ||
263 | gdp_dbg_vps(s, node->gam_gdp_vps); | ||
264 | seq_printf(s, "\n\tPML 0x%08X", node->gam_gdp_pml); | ||
265 | seq_printf(s, "\n\tPMP 0x%08X", node->gam_gdp_pmp); | ||
266 | seq_printf(s, "\n\tSIZE 0x%08X", node->gam_gdp_size); | ||
267 | gdp_dbg_size(s, node->gam_gdp_size); | ||
268 | seq_printf(s, "\n\tNVN 0x%08X", node->gam_gdp_nvn); | ||
269 | seq_printf(s, "\n\tKEY1 0x%08X", node->gam_gdp_key1); | ||
270 | seq_printf(s, "\n\tKEY2 0x%08X", node->gam_gdp_key2); | ||
271 | seq_printf(s, "\n\tPPT 0x%08X", node->gam_gdp_ppt); | ||
272 | gdp_dbg_ppt(s, node->gam_gdp_ppt); | ||
273 | seq_printf(s, "\n\tCML 0x%08X", node->gam_gdp_cml); | ||
274 | seq_puts(s, "\n"); | ||
275 | } | ||
276 | |||
277 | static int gdp_node_dbg_show(struct seq_file *s, void *arg) | ||
278 | { | ||
279 | struct drm_info_node *node = s->private; | ||
280 | struct sti_gdp *gdp = (struct sti_gdp *)node->info_ent->data; | ||
281 | struct drm_device *dev = node->minor->dev; | ||
282 | unsigned int b; | ||
283 | int ret; | ||
284 | |||
285 | ret = mutex_lock_interruptible(&dev->struct_mutex); | ||
286 | if (ret) | ||
287 | return ret; | ||
288 | |||
289 | for (b = 0; b < GDP_NODE_NB_BANK; b++) { | ||
290 | seq_printf(s, "\n%s[%d].top", sti_plane_to_str(&gdp->plane), b); | ||
291 | gdp_node_dump_node(s, gdp->node_list[b].top_field); | ||
292 | seq_printf(s, "\n%s[%d].btm", sti_plane_to_str(&gdp->plane), b); | ||
293 | gdp_node_dump_node(s, gdp->node_list[b].btm_field); | ||
294 | } | ||
295 | |||
296 | mutex_unlock(&dev->struct_mutex); | ||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | static struct drm_info_list gdp0_debugfs_files[] = { | ||
301 | { "gdp0", gdp_dbg_show, 0, NULL }, | ||
302 | { "gdp0_node", gdp_node_dbg_show, 0, NULL }, | ||
303 | }; | ||
304 | |||
305 | static struct drm_info_list gdp1_debugfs_files[] = { | ||
306 | { "gdp1", gdp_dbg_show, 0, NULL }, | ||
307 | { "gdp1_node", gdp_node_dbg_show, 0, NULL }, | ||
308 | }; | ||
309 | |||
310 | static struct drm_info_list gdp2_debugfs_files[] = { | ||
311 | { "gdp2", gdp_dbg_show, 0, NULL }, | ||
312 | { "gdp2_node", gdp_node_dbg_show, 0, NULL }, | ||
313 | }; | ||
314 | |||
315 | static struct drm_info_list gdp3_debugfs_files[] = { | ||
316 | { "gdp3", gdp_dbg_show, 0, NULL }, | ||
317 | { "gdp3_node", gdp_node_dbg_show, 0, NULL }, | ||
318 | }; | ||
319 | |||
320 | static int gdp_debugfs_init(struct sti_gdp *gdp, struct drm_minor *minor) | ||
321 | { | ||
322 | unsigned int i; | ||
323 | struct drm_info_list *gdp_debugfs_files; | ||
324 | int nb_files; | ||
325 | |||
326 | switch (gdp->plane.desc) { | ||
327 | case STI_GDP_0: | ||
328 | gdp_debugfs_files = gdp0_debugfs_files; | ||
329 | nb_files = ARRAY_SIZE(gdp0_debugfs_files); | ||
330 | break; | ||
331 | case STI_GDP_1: | ||
332 | gdp_debugfs_files = gdp1_debugfs_files; | ||
333 | nb_files = ARRAY_SIZE(gdp1_debugfs_files); | ||
334 | break; | ||
335 | case STI_GDP_2: | ||
336 | gdp_debugfs_files = gdp2_debugfs_files; | ||
337 | nb_files = ARRAY_SIZE(gdp2_debugfs_files); | ||
338 | break; | ||
339 | case STI_GDP_3: | ||
340 | gdp_debugfs_files = gdp3_debugfs_files; | ||
341 | nb_files = ARRAY_SIZE(gdp3_debugfs_files); | ||
342 | break; | ||
343 | default: | ||
344 | return -EINVAL; | ||
345 | } | ||
346 | |||
347 | for (i = 0; i < nb_files; i++) | ||
348 | gdp_debugfs_files[i].data = gdp; | ||
349 | |||
350 | return drm_debugfs_create_files(gdp_debugfs_files, | ||
351 | nb_files, | ||
352 | minor->debugfs_root, minor); | ||
353 | } | ||
354 | |||
130 | static int sti_gdp_fourcc2format(int fourcc) | 355 | static int sti_gdp_fourcc2format(int fourcc) |
131 | { | 356 | { |
132 | switch (fourcc) { | 357 | switch (fourcc) { |
@@ -146,14 +371,6 @@ static int sti_gdp_fourcc2format(int fourcc) | |||
146 | return GDP_RGB565; | 371 | return GDP_RGB565; |
147 | case DRM_FORMAT_RGB888: | 372 | case DRM_FORMAT_RGB888: |
148 | return GDP_RGB888; | 373 | return GDP_RGB888; |
149 | case DRM_FORMAT_AYUV: | ||
150 | return GDP_AYCBR8888; | ||
151 | case DRM_FORMAT_YUV444: | ||
152 | return GDP_YCBR888; | ||
153 | case DRM_FORMAT_VYUY: | ||
154 | return GDP_YCBR422R; | ||
155 | case DRM_FORMAT_C8: | ||
156 | return GDP_CLUT8; | ||
157 | } | 374 | } |
158 | return -1; | 375 | return -1; |
159 | } | 376 | } |
@@ -163,7 +380,6 @@ static int sti_gdp_get_alpharange(int format) | |||
163 | switch (format) { | 380 | switch (format) { |
164 | case GDP_ARGB8565: | 381 | case GDP_ARGB8565: |
165 | case GDP_ARGB8888: | 382 | case GDP_ARGB8888: |
166 | case GDP_AYCBR8888: | ||
167 | case GDP_ABGR8888: | 383 | case GDP_ABGR8888: |
168 | return GAM_GDP_ALPHARANGE_255; | 384 | return GAM_GDP_ALPHARANGE_255; |
169 | } | 385 | } |
@@ -240,9 +456,6 @@ end: | |||
240 | */ | 456 | */ |
241 | static void sti_gdp_disable(struct sti_gdp *gdp) | 457 | static void sti_gdp_disable(struct sti_gdp *gdp) |
242 | { | 458 | { |
243 | struct drm_plane *drm_plane = &gdp->plane.drm_plane; | ||
244 | struct sti_mixer *mixer = to_sti_mixer(drm_plane->crtc); | ||
245 | struct sti_compositor *compo = dev_get_drvdata(gdp->dev); | ||
246 | unsigned int i; | 459 | unsigned int i; |
247 | 460 | ||
248 | DRM_DEBUG_DRIVER("%s\n", sti_plane_to_str(&gdp->plane)); | 461 | DRM_DEBUG_DRIVER("%s\n", sti_plane_to_str(&gdp->plane)); |
@@ -253,8 +466,7 @@ static void sti_gdp_disable(struct sti_gdp *gdp) | |||
253 | gdp->node_list[i].btm_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE; | 466 | gdp->node_list[i].btm_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE; |
254 | } | 467 | } |
255 | 468 | ||
256 | if (sti_vtg_unregister_client(mixer->id == STI_MIXER_MAIN ? | 469 | if (sti_vtg_unregister_client(gdp->vtg, &gdp->vtg_field_nb)) |
257 | compo->vtg_main : compo->vtg_aux, &gdp->vtg_field_nb)) | ||
258 | DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n"); | 470 | DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n"); |
259 | 471 | ||
260 | if (gdp->clk_pix) | 472 | if (gdp->clk_pix) |
@@ -380,20 +592,140 @@ static void sti_gdp_init(struct sti_gdp *gdp) | |||
380 | } | 592 | } |
381 | } | 593 | } |
382 | 594 | ||
383 | static void sti_gdp_atomic_update(struct drm_plane *drm_plane, | 595 | /** |
384 | struct drm_plane_state *oldstate) | 596 | * sti_gdp_get_dst |
597 | * @dev: device | ||
598 | * @dst: requested destination size | ||
599 | * @src: source size | ||
600 | * | ||
601 | * Return the cropped / clamped destination size | ||
602 | * | ||
603 | * RETURNS: | ||
604 | * cropped / clamped destination size | ||
605 | */ | ||
606 | static int sti_gdp_get_dst(struct device *dev, int dst, int src) | ||
607 | { | ||
608 | if (dst == src) | ||
609 | return dst; | ||
610 | |||
611 | if (dst < src) { | ||
612 | dev_dbg(dev, "WARNING: GDP scale not supported, will crop\n"); | ||
613 | return dst; | ||
614 | } | ||
615 | |||
616 | dev_dbg(dev, "WARNING: GDP scale not supported, will clamp\n"); | ||
617 | return src; | ||
618 | } | ||
619 | |||
620 | static int sti_gdp_atomic_check(struct drm_plane *drm_plane, | ||
621 | struct drm_plane_state *state) | ||
385 | { | 622 | { |
386 | struct drm_plane_state *state = drm_plane->state; | ||
387 | struct sti_plane *plane = to_sti_plane(drm_plane); | 623 | struct sti_plane *plane = to_sti_plane(drm_plane); |
388 | struct sti_gdp *gdp = to_sti_gdp(plane); | 624 | struct sti_gdp *gdp = to_sti_gdp(plane); |
389 | struct drm_crtc *crtc = state->crtc; | 625 | struct drm_crtc *crtc = state->crtc; |
390 | struct sti_compositor *compo = dev_get_drvdata(gdp->dev); | 626 | struct sti_compositor *compo = dev_get_drvdata(gdp->dev); |
391 | struct drm_framebuffer *fb = state->fb; | 627 | struct drm_framebuffer *fb = state->fb; |
392 | bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false; | 628 | bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false; |
629 | struct drm_crtc_state *crtc_state; | ||
393 | struct sti_mixer *mixer; | 630 | struct sti_mixer *mixer; |
394 | struct drm_display_mode *mode; | 631 | struct drm_display_mode *mode; |
395 | int dst_x, dst_y, dst_w, dst_h; | 632 | int dst_x, dst_y, dst_w, dst_h; |
396 | int src_x, src_y, src_w, src_h; | 633 | int src_x, src_y, src_w, src_h; |
634 | int format; | ||
635 | |||
636 | /* no need for further checks if the plane is being disabled */ | ||
637 | if (!crtc || !fb) | ||
638 | return 0; | ||
639 | |||
640 | mixer = to_sti_mixer(crtc); | ||
641 | crtc_state = drm_atomic_get_crtc_state(state->state, crtc); | ||
642 | mode = &crtc_state->mode; | ||
643 | dst_x = state->crtc_x; | ||
644 | dst_y = state->crtc_y; | ||
645 | dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); | ||
646 | dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); | ||
647 | /* src_x are in 16.16 format */ | ||
648 | src_x = state->src_x >> 16; | ||
649 | src_y = state->src_y >> 16; | ||
650 | src_w = clamp_val(state->src_w >> 16, 0, GAM_GDP_SIZE_MAX); | ||
651 | src_h = clamp_val(state->src_h >> 16, 0, GAM_GDP_SIZE_MAX); | ||
652 | |||
653 | format = sti_gdp_fourcc2format(fb->pixel_format); | ||
654 | if (format == -1) { | ||
655 | DRM_ERROR("Format not supported by GDP %.4s\n", | ||
656 | (char *)&fb->pixel_format); | ||
657 | return -EINVAL; | ||
658 | } | ||
659 | |||
660 | if (!drm_fb_cma_get_gem_obj(fb, 0)) { | ||
661 | DRM_ERROR("Can't get CMA GEM object for fb\n"); | ||
662 | return -EINVAL; | ||
663 | } | ||
664 | |||
665 | if (first_prepare) { | ||
666 | /* Register gdp callback */ | ||
667 | gdp->vtg = mixer->id == STI_MIXER_MAIN ? | ||
668 | compo->vtg_main : compo->vtg_aux; | ||
669 | if (sti_vtg_register_client(gdp->vtg, | ||
670 | &gdp->vtg_field_nb, crtc)) { | ||
671 | DRM_ERROR("Cannot register VTG notifier\n"); | ||
672 | return -EINVAL; | ||
673 | } | ||
674 | |||
675 | /* Set and enable gdp clock */ | ||
676 | if (gdp->clk_pix) { | ||
677 | struct clk *clkp; | ||
678 | int rate = mode->clock * 1000; | ||
679 | int res; | ||
680 | |||
681 | /* | ||
682 | * According to the mixer used, the gdp pixel clock | ||
683 | * should have a different parent clock. | ||
684 | */ | ||
685 | if (mixer->id == STI_MIXER_MAIN) | ||
686 | clkp = gdp->clk_main_parent; | ||
687 | else | ||
688 | clkp = gdp->clk_aux_parent; | ||
689 | |||
690 | if (clkp) | ||
691 | clk_set_parent(gdp->clk_pix, clkp); | ||
692 | |||
693 | res = clk_set_rate(gdp->clk_pix, rate); | ||
694 | if (res < 0) { | ||
695 | DRM_ERROR("Cannot set rate (%dHz) for gdp\n", | ||
696 | rate); | ||
697 | return -EINVAL; | ||
698 | } | ||
699 | |||
700 | if (clk_prepare_enable(gdp->clk_pix)) { | ||
701 | DRM_ERROR("Failed to prepare/enable gdp\n"); | ||
702 | return -EINVAL; | ||
703 | } | ||
704 | } | ||
705 | } | ||
706 | |||
707 | DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n", | ||
708 | crtc->base.id, sti_mixer_to_str(mixer), | ||
709 | drm_plane->base.id, sti_plane_to_str(plane)); | ||
710 | DRM_DEBUG_KMS("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n", | ||
711 | sti_plane_to_str(plane), | ||
712 | dst_w, dst_h, dst_x, dst_y, | ||
713 | src_w, src_h, src_x, src_y); | ||
714 | |||
715 | return 0; | ||
716 | } | ||
717 | |||
718 | static void sti_gdp_atomic_update(struct drm_plane *drm_plane, | ||
719 | struct drm_plane_state *oldstate) | ||
720 | { | ||
721 | struct drm_plane_state *state = drm_plane->state; | ||
722 | struct sti_plane *plane = to_sti_plane(drm_plane); | ||
723 | struct sti_gdp *gdp = to_sti_gdp(plane); | ||
724 | struct drm_crtc *crtc = state->crtc; | ||
725 | struct drm_framebuffer *fb = state->fb; | ||
726 | struct drm_display_mode *mode; | ||
727 | int dst_x, dst_y, dst_w, dst_h; | ||
728 | int src_x, src_y, src_w, src_h; | ||
397 | struct drm_gem_cma_object *cma_obj; | 729 | struct drm_gem_cma_object *cma_obj; |
398 | struct sti_gdp_node_list *list; | 730 | struct sti_gdp_node_list *list; |
399 | struct sti_gdp_node_list *curr_list; | 731 | struct sti_gdp_node_list *curr_list; |
@@ -403,13 +735,10 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane, | |||
403 | int format; | 735 | int format; |
404 | unsigned int depth, bpp; | 736 | unsigned int depth, bpp; |
405 | u32 ydo, xdo, yds, xds; | 737 | u32 ydo, xdo, yds, xds; |
406 | int res; | ||
407 | 738 | ||
408 | /* Manage the case where crtc is null (disabled) */ | 739 | if (!crtc || !fb) |
409 | if (!crtc) | ||
410 | return; | 740 | return; |
411 | 741 | ||
412 | mixer = to_sti_mixer(crtc); | ||
413 | mode = &crtc->mode; | 742 | mode = &crtc->mode; |
414 | dst_x = state->crtc_x; | 743 | dst_x = state->crtc_x; |
415 | dst_y = state->crtc_y; | 744 | dst_y = state->crtc_y; |
@@ -418,16 +747,8 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane, | |||
418 | /* src_x are in 16.16 format */ | 747 | /* src_x are in 16.16 format */ |
419 | src_x = state->src_x >> 16; | 748 | src_x = state->src_x >> 16; |
420 | src_y = state->src_y >> 16; | 749 | src_y = state->src_y >> 16; |
421 | src_w = state->src_w >> 16; | 750 | src_w = clamp_val(state->src_w >> 16, 0, GAM_GDP_SIZE_MAX); |
422 | src_h = state->src_h >> 16; | 751 | src_h = clamp_val(state->src_h >> 16, 0, GAM_GDP_SIZE_MAX); |
423 | |||
424 | DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n", | ||
425 | crtc->base.id, sti_mixer_to_str(mixer), | ||
426 | drm_plane->base.id, sti_plane_to_str(plane)); | ||
427 | DRM_DEBUG_KMS("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n", | ||
428 | sti_plane_to_str(plane), | ||
429 | dst_w, dst_h, dst_x, dst_y, | ||
430 | src_w, src_h, src_x, src_y); | ||
431 | 752 | ||
432 | list = sti_gdp_get_free_nodes(gdp); | 753 | list = sti_gdp_get_free_nodes(gdp); |
433 | top_field = list->top_field; | 754 | top_field = list->top_field; |
@@ -440,20 +761,11 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane, | |||
440 | top_field->gam_gdp_agc = GAM_GDP_AGC_FULL_RANGE; | 761 | top_field->gam_gdp_agc = GAM_GDP_AGC_FULL_RANGE; |
441 | top_field->gam_gdp_ctl = WAIT_NEXT_VSYNC; | 762 | top_field->gam_gdp_ctl = WAIT_NEXT_VSYNC; |
442 | format = sti_gdp_fourcc2format(fb->pixel_format); | 763 | format = sti_gdp_fourcc2format(fb->pixel_format); |
443 | if (format == -1) { | ||
444 | DRM_ERROR("Format not supported by GDP %.4s\n", | ||
445 | (char *)&fb->pixel_format); | ||
446 | return; | ||
447 | } | ||
448 | top_field->gam_gdp_ctl |= format; | 764 | top_field->gam_gdp_ctl |= format; |
449 | top_field->gam_gdp_ctl |= sti_gdp_get_alpharange(format); | 765 | top_field->gam_gdp_ctl |= sti_gdp_get_alpharange(format); |
450 | top_field->gam_gdp_ppt &= ~GAM_GDP_PPT_IGNORE; | 766 | top_field->gam_gdp_ppt &= ~GAM_GDP_PPT_IGNORE; |
451 | 767 | ||
452 | cma_obj = drm_fb_cma_get_gem_obj(fb, 0); | 768 | cma_obj = drm_fb_cma_get_gem_obj(fb, 0); |
453 | if (!cma_obj) { | ||
454 | DRM_ERROR("Can't get CMA GEM object for fb\n"); | ||
455 | return; | ||
456 | } | ||
457 | 769 | ||
458 | DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id, | 770 | DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id, |
459 | (char *)&fb->pixel_format, | 771 | (char *)&fb->pixel_format, |
@@ -465,12 +777,9 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane, | |||
465 | top_field->gam_gdp_pml += src_x * (bpp >> 3); | 777 | top_field->gam_gdp_pml += src_x * (bpp >> 3); |
466 | top_field->gam_gdp_pml += src_y * fb->pitches[0]; | 778 | top_field->gam_gdp_pml += src_y * fb->pitches[0]; |
467 | 779 | ||
468 | /* input parameters */ | 780 | /* output parameters (clamped / cropped) */ |
469 | top_field->gam_gdp_pmp = fb->pitches[0]; | 781 | dst_w = sti_gdp_get_dst(gdp->dev, dst_w, src_w); |
470 | top_field->gam_gdp_size = clamp_val(src_h, 0, GAM_GDP_SIZE_MAX) << 16 | | 782 | dst_h = sti_gdp_get_dst(gdp->dev, dst_h, src_h); |
471 | clamp_val(src_w, 0, GAM_GDP_SIZE_MAX); | ||
472 | |||
473 | /* output parameters */ | ||
474 | ydo = sti_vtg_get_line_number(*mode, dst_y); | 783 | ydo = sti_vtg_get_line_number(*mode, dst_y); |
475 | yds = sti_vtg_get_line_number(*mode, dst_y + dst_h - 1); | 784 | yds = sti_vtg_get_line_number(*mode, dst_y + dst_h - 1); |
476 | xdo = sti_vtg_get_pixel_number(*mode, dst_x); | 785 | xdo = sti_vtg_get_pixel_number(*mode, dst_x); |
@@ -478,6 +787,11 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane, | |||
478 | top_field->gam_gdp_vpo = (ydo << 16) | xdo; | 787 | top_field->gam_gdp_vpo = (ydo << 16) | xdo; |
479 | top_field->gam_gdp_vps = (yds << 16) | xds; | 788 | top_field->gam_gdp_vps = (yds << 16) | xds; |
480 | 789 | ||
790 | /* input parameters */ | ||
791 | src_w = dst_w; | ||
792 | top_field->gam_gdp_pmp = fb->pitches[0]; | ||
793 | top_field->gam_gdp_size = src_h << 16 | src_w; | ||
794 | |||
481 | /* Same content and chained together */ | 795 | /* Same content and chained together */ |
482 | memcpy(btm_field, top_field, sizeof(*btm_field)); | 796 | memcpy(btm_field, top_field, sizeof(*btm_field)); |
483 | top_field->gam_gdp_nvn = list->btm_field_paddr; | 797 | top_field->gam_gdp_nvn = list->btm_field_paddr; |
@@ -488,44 +802,6 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane, | |||
488 | btm_field->gam_gdp_pml = top_field->gam_gdp_pml + | 802 | btm_field->gam_gdp_pml = top_field->gam_gdp_pml + |
489 | fb->pitches[0]; | 803 | fb->pitches[0]; |
490 | 804 | ||
491 | if (first_prepare) { | ||
492 | /* Register gdp callback */ | ||
493 | if (sti_vtg_register_client(mixer->id == STI_MIXER_MAIN ? | ||
494 | compo->vtg_main : compo->vtg_aux, | ||
495 | &gdp->vtg_field_nb, crtc)) { | ||
496 | DRM_ERROR("Cannot register VTG notifier\n"); | ||
497 | return; | ||
498 | } | ||
499 | |||
500 | /* Set and enable gdp clock */ | ||
501 | if (gdp->clk_pix) { | ||
502 | struct clk *clkp; | ||
503 | int rate = mode->clock * 1000; | ||
504 | |||
505 | /* According to the mixer used, the gdp pixel clock | ||
506 | * should have a different parent clock. */ | ||
507 | if (mixer->id == STI_MIXER_MAIN) | ||
508 | clkp = gdp->clk_main_parent; | ||
509 | else | ||
510 | clkp = gdp->clk_aux_parent; | ||
511 | |||
512 | if (clkp) | ||
513 | clk_set_parent(gdp->clk_pix, clkp); | ||
514 | |||
515 | res = clk_set_rate(gdp->clk_pix, rate); | ||
516 | if (res < 0) { | ||
517 | DRM_ERROR("Cannot set rate (%dHz) for gdp\n", | ||
518 | rate); | ||
519 | return; | ||
520 | } | ||
521 | |||
522 | if (clk_prepare_enable(gdp->clk_pix)) { | ||
523 | DRM_ERROR("Failed to prepare/enable gdp\n"); | ||
524 | return; | ||
525 | } | ||
526 | } | ||
527 | } | ||
528 | |||
529 | /* Update the NVN field of the 'right' field of the current GDP node | 805 | /* Update the NVN field of the 'right' field of the current GDP node |
530 | * (being used by the HW) with the address of the updated ('free') top | 806 | * (being used by the HW) with the address of the updated ('free') top |
531 | * field GDP node. | 807 | * field GDP node. |
@@ -574,6 +850,8 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane, | |||
574 | } | 850 | } |
575 | 851 | ||
576 | end: | 852 | end: |
853 | sti_plane_update_fps(plane, true, false); | ||
854 | |||
577 | plane->status = STI_PLANE_UPDATED; | 855 | plane->status = STI_PLANE_UPDATED; |
578 | } | 856 | } |
579 | 857 | ||
@@ -581,7 +859,6 @@ static void sti_gdp_atomic_disable(struct drm_plane *drm_plane, | |||
581 | struct drm_plane_state *oldstate) | 859 | struct drm_plane_state *oldstate) |
582 | { | 860 | { |
583 | struct sti_plane *plane = to_sti_plane(drm_plane); | 861 | struct sti_plane *plane = to_sti_plane(drm_plane); |
584 | struct sti_mixer *mixer = to_sti_mixer(drm_plane->crtc); | ||
585 | 862 | ||
586 | if (!drm_plane->crtc) { | 863 | if (!drm_plane->crtc) { |
587 | DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", | 864 | DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", |
@@ -590,13 +867,15 @@ static void sti_gdp_atomic_disable(struct drm_plane *drm_plane, | |||
590 | } | 867 | } |
591 | 868 | ||
592 | DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n", | 869 | DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n", |
593 | drm_plane->crtc->base.id, sti_mixer_to_str(mixer), | 870 | drm_plane->crtc->base.id, |
871 | sti_mixer_to_str(to_sti_mixer(drm_plane->crtc)), | ||
594 | drm_plane->base.id, sti_plane_to_str(plane)); | 872 | drm_plane->base.id, sti_plane_to_str(plane)); |
595 | 873 | ||
596 | plane->status = STI_PLANE_DISABLING; | 874 | plane->status = STI_PLANE_DISABLING; |
597 | } | 875 | } |
598 | 876 | ||
599 | static const struct drm_plane_helper_funcs sti_gdp_helpers_funcs = { | 877 | static const struct drm_plane_helper_funcs sti_gdp_helpers_funcs = { |
878 | .atomic_check = sti_gdp_atomic_check, | ||
600 | .atomic_update = sti_gdp_atomic_update, | 879 | .atomic_update = sti_gdp_atomic_update, |
601 | .atomic_disable = sti_gdp_atomic_disable, | 880 | .atomic_disable = sti_gdp_atomic_disable, |
602 | }; | 881 | }; |
@@ -640,6 +919,9 @@ struct drm_plane *sti_gdp_create(struct drm_device *drm_dev, | |||
640 | 919 | ||
641 | sti_plane_init_property(&gdp->plane, type); | 920 | sti_plane_init_property(&gdp->plane, type); |
642 | 921 | ||
922 | if (gdp_debugfs_init(gdp, drm_dev->primary)) | ||
923 | DRM_ERROR("GDP debugfs setup failed\n"); | ||
924 | |||
643 | return &gdp->plane.drm_plane; | 925 | return &gdp->plane.drm_plane; |
644 | 926 | ||
645 | err: | 927 | err: |
diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c index 49cce833f2c8..ec0d017eaf1a 100644 --- a/drivers/gpu/drm/sti/sti_hda.c +++ b/drivers/gpu/drm/sti/sti_hda.c | |||
@@ -326,6 +326,103 @@ static void hda_enable_hd_dacs(struct sti_hda *hda, bool enable) | |||
326 | } | 326 | } |
327 | } | 327 | } |
328 | 328 | ||
329 | #define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \ | ||
330 | readl(hda->regs + reg)) | ||
331 | |||
332 | static void hda_dbg_cfg(struct seq_file *s, int val) | ||
333 | { | ||
334 | seq_puts(s, "\tAWG "); | ||
335 | seq_puts(s, val & CFG_AWG_ASYNC_EN ? "enabled" : "disabled"); | ||
336 | } | ||
337 | |||
338 | static void hda_dbg_awg_microcode(struct seq_file *s, void __iomem *reg) | ||
339 | { | ||
340 | unsigned int i; | ||
341 | |||
342 | seq_puts(s, "\n\n"); | ||
343 | seq_puts(s, " HDA AWG microcode:"); | ||
344 | for (i = 0; i < AWG_MAX_INST; i++) { | ||
345 | if (i % 8 == 0) | ||
346 | seq_printf(s, "\n %04X:", i); | ||
347 | seq_printf(s, " %04X", readl(reg + i * 4)); | ||
348 | } | ||
349 | } | ||
350 | |||
351 | static void hda_dbg_video_dacs_ctrl(struct seq_file *s, void __iomem *reg) | ||
352 | { | ||
353 | u32 val = readl(reg); | ||
354 | u32 mask; | ||
355 | |||
356 | switch ((u32)reg & VIDEO_DACS_CONTROL_MASK) { | ||
357 | case VIDEO_DACS_CONTROL_SYSCFG2535: | ||
358 | mask = DAC_CFG_HD_OFF_MASK; | ||
359 | break; | ||
360 | case VIDEO_DACS_CONTROL_SYSCFG5072: | ||
361 | mask = DAC_CFG_HD_HZUVW_OFF_MASK; | ||
362 | break; | ||
363 | default: | ||
364 | DRM_DEBUG_DRIVER("Warning: DACS ctrl register not supported!"); | ||
365 | return; | ||
366 | } | ||
367 | |||
368 | seq_puts(s, "\n"); | ||
369 | seq_printf(s, "\n %-25s 0x%08X", "VIDEO_DACS_CONTROL", val); | ||
370 | seq_puts(s, "\tHD DACs "); | ||
371 | seq_puts(s, val & mask ? "disabled" : "enabled"); | ||
372 | } | ||
373 | |||
374 | static int hda_dbg_show(struct seq_file *s, void *data) | ||
375 | { | ||
376 | struct drm_info_node *node = s->private; | ||
377 | struct sti_hda *hda = (struct sti_hda *)node->info_ent->data; | ||
378 | struct drm_device *dev = node->minor->dev; | ||
379 | int ret; | ||
380 | |||
381 | ret = mutex_lock_interruptible(&dev->struct_mutex); | ||
382 | if (ret) | ||
383 | return ret; | ||
384 | |||
385 | seq_printf(s, "HD Analog: (vaddr = 0x%p)", hda->regs); | ||
386 | DBGFS_DUMP(HDA_ANA_CFG); | ||
387 | hda_dbg_cfg(s, readl(hda->regs + HDA_ANA_CFG)); | ||
388 | DBGFS_DUMP(HDA_ANA_SCALE_CTRL_Y); | ||
389 | DBGFS_DUMP(HDA_ANA_SCALE_CTRL_CB); | ||
390 | DBGFS_DUMP(HDA_ANA_SCALE_CTRL_CR); | ||
391 | DBGFS_DUMP(HDA_ANA_ANC_CTRL); | ||
392 | DBGFS_DUMP(HDA_ANA_SRC_Y_CFG); | ||
393 | DBGFS_DUMP(HDA_ANA_SRC_C_CFG); | ||
394 | hda_dbg_awg_microcode(s, hda->regs + HDA_SYNC_AWGI); | ||
395 | if (hda->video_dacs_ctrl) | ||
396 | hda_dbg_video_dacs_ctrl(s, hda->video_dacs_ctrl); | ||
397 | seq_puts(s, "\n"); | ||
398 | |||
399 | mutex_unlock(&dev->struct_mutex); | ||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | static struct drm_info_list hda_debugfs_files[] = { | ||
404 | { "hda", hda_dbg_show, 0, NULL }, | ||
405 | }; | ||
406 | |||
407 | static void hda_debugfs_exit(struct sti_hda *hda, struct drm_minor *minor) | ||
408 | { | ||
409 | drm_debugfs_remove_files(hda_debugfs_files, | ||
410 | ARRAY_SIZE(hda_debugfs_files), | ||
411 | minor); | ||
412 | } | ||
413 | |||
414 | static int hda_debugfs_init(struct sti_hda *hda, struct drm_minor *minor) | ||
415 | { | ||
416 | unsigned int i; | ||
417 | |||
418 | for (i = 0; i < ARRAY_SIZE(hda_debugfs_files); i++) | ||
419 | hda_debugfs_files[i].data = hda; | ||
420 | |||
421 | return drm_debugfs_create_files(hda_debugfs_files, | ||
422 | ARRAY_SIZE(hda_debugfs_files), | ||
423 | minor->debugfs_root, minor); | ||
424 | } | ||
425 | |||
329 | /** | 426 | /** |
330 | * Configure AWG, writing instructions | 427 | * Configure AWG, writing instructions |
331 | * | 428 | * |
@@ -685,6 +782,12 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data) | |||
685 | goto err_sysfs; | 782 | goto err_sysfs; |
686 | } | 783 | } |
687 | 784 | ||
785 | /* force to disable hd dacs at startup */ | ||
786 | hda_enable_hd_dacs(hda, false); | ||
787 | |||
788 | if (hda_debugfs_init(hda, drm_dev->primary)) | ||
789 | DRM_ERROR("HDA debugfs setup failed\n"); | ||
790 | |||
688 | return 0; | 791 | return 0; |
689 | 792 | ||
690 | err_sysfs: | 793 | err_sysfs: |
@@ -697,7 +800,10 @@ err_connector: | |||
697 | static void sti_hda_unbind(struct device *dev, | 800 | static void sti_hda_unbind(struct device *dev, |
698 | struct device *master, void *data) | 801 | struct device *master, void *data) |
699 | { | 802 | { |
700 | /* do nothing */ | 803 | struct sti_hda *hda = dev_get_drvdata(dev); |
804 | struct drm_device *drm_dev = data; | ||
805 | |||
806 | hda_debugfs_exit(hda, drm_dev->primary); | ||
701 | } | 807 | } |
702 | 808 | ||
703 | static const struct component_ops sti_hda_ops = { | 809 | static const struct component_ops sti_hda_ops = { |
diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c index cd501563c0cc..6ef0715bd5b9 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.c +++ b/drivers/gpu/drm/sti/sti_hdmi.c | |||
@@ -6,6 +6,7 @@ | |||
6 | 6 | ||
7 | #include <linux/clk.h> | 7 | #include <linux/clk.h> |
8 | #include <linux/component.h> | 8 | #include <linux/component.h> |
9 | #include <linux/debugfs.h> | ||
9 | #include <linux/hdmi.h> | 10 | #include <linux/hdmi.h> |
10 | #include <linux/module.h> | 11 | #include <linux/module.h> |
11 | #include <linux/of_gpio.h> | 12 | #include <linux/of_gpio.h> |
@@ -51,9 +52,18 @@ | |||
51 | #define HDMI_SW_DI_2_PKT_WORD4 0x0614 | 52 | #define HDMI_SW_DI_2_PKT_WORD4 0x0614 |
52 | #define HDMI_SW_DI_2_PKT_WORD5 0x0618 | 53 | #define HDMI_SW_DI_2_PKT_WORD5 0x0618 |
53 | #define HDMI_SW_DI_2_PKT_WORD6 0x061C | 54 | #define HDMI_SW_DI_2_PKT_WORD6 0x061C |
55 | #define HDMI_SW_DI_3_HEAD_WORD 0x0620 | ||
56 | #define HDMI_SW_DI_3_PKT_WORD0 0x0624 | ||
57 | #define HDMI_SW_DI_3_PKT_WORD1 0x0628 | ||
58 | #define HDMI_SW_DI_3_PKT_WORD2 0x062C | ||
59 | #define HDMI_SW_DI_3_PKT_WORD3 0x0630 | ||
60 | #define HDMI_SW_DI_3_PKT_WORD4 0x0634 | ||
61 | #define HDMI_SW_DI_3_PKT_WORD5 0x0638 | ||
62 | #define HDMI_SW_DI_3_PKT_WORD6 0x063C | ||
54 | 63 | ||
55 | #define HDMI_IFRAME_SLOT_AVI 1 | 64 | #define HDMI_IFRAME_SLOT_AVI 1 |
56 | #define HDMI_IFRAME_SLOT_AUDIO 2 | 65 | #define HDMI_IFRAME_SLOT_AUDIO 2 |
66 | #define HDMI_IFRAME_SLOT_VENDOR 3 | ||
57 | 67 | ||
58 | #define XCAT(prefix, x, suffix) prefix ## x ## suffix | 68 | #define XCAT(prefix, x, suffix) prefix ## x ## suffix |
59 | #define HDMI_SW_DI_N_HEAD_WORD(x) XCAT(HDMI_SW_DI_, x, _HEAD_WORD) | 69 | #define HDMI_SW_DI_N_HEAD_WORD(x) XCAT(HDMI_SW_DI_, x, _HEAD_WORD) |
@@ -65,6 +75,8 @@ | |||
65 | #define HDMI_SW_DI_N_PKT_WORD5(x) XCAT(HDMI_SW_DI_, x, _PKT_WORD5) | 75 | #define HDMI_SW_DI_N_PKT_WORD5(x) XCAT(HDMI_SW_DI_, x, _PKT_WORD5) |
66 | #define HDMI_SW_DI_N_PKT_WORD6(x) XCAT(HDMI_SW_DI_, x, _PKT_WORD6) | 76 | #define HDMI_SW_DI_N_PKT_WORD6(x) XCAT(HDMI_SW_DI_, x, _PKT_WORD6) |
67 | 77 | ||
78 | #define HDMI_SW_DI_MAX_WORD 7 | ||
79 | |||
68 | #define HDMI_IFRAME_DISABLED 0x0 | 80 | #define HDMI_IFRAME_DISABLED 0x0 |
69 | #define HDMI_IFRAME_SINGLE_SHOT 0x1 | 81 | #define HDMI_IFRAME_SINGLE_SHOT 0x1 |
70 | #define HDMI_IFRAME_FIELD 0x2 | 82 | #define HDMI_IFRAME_FIELD 0x2 |
@@ -117,6 +129,8 @@ struct sti_hdmi_connector { | |||
117 | struct drm_connector drm_connector; | 129 | struct drm_connector drm_connector; |
118 | struct drm_encoder *encoder; | 130 | struct drm_encoder *encoder; |
119 | struct sti_hdmi *hdmi; | 131 | struct sti_hdmi *hdmi; |
132 | struct drm_property *colorspace_property; | ||
133 | struct drm_property *hdmi_mode_property; | ||
120 | }; | 134 | }; |
121 | 135 | ||
122 | #define to_sti_hdmi_connector(x) \ | 136 | #define to_sti_hdmi_connector(x) \ |
@@ -217,8 +231,10 @@ static void hdmi_config(struct sti_hdmi *hdmi) | |||
217 | /* Clear overrun and underrun fifo */ | 231 | /* Clear overrun and underrun fifo */ |
218 | conf = HDMI_CFG_FIFO_OVERRUN_CLR | HDMI_CFG_FIFO_UNDERRUN_CLR; | 232 | conf = HDMI_CFG_FIFO_OVERRUN_CLR | HDMI_CFG_FIFO_UNDERRUN_CLR; |
219 | 233 | ||
220 | /* Enable HDMI mode not DVI */ | 234 | /* Select encryption type and the framing mode */ |
221 | conf |= HDMI_CFG_HDMI_NOT_DVI | HDMI_CFG_ESS_NOT_OESS; | 235 | conf |= HDMI_CFG_ESS_NOT_OESS; |
236 | if (hdmi->hdmi_mode == HDMI_MODE_HDMI) | ||
237 | conf |= HDMI_CFG_HDMI_NOT_DVI; | ||
222 | 238 | ||
223 | /* Enable sink term detection */ | 239 | /* Enable sink term detection */ |
224 | conf |= HDMI_CFG_SINK_TERM_DET_EN; | 240 | conf |= HDMI_CFG_SINK_TERM_DET_EN; |
@@ -241,6 +257,47 @@ static void hdmi_config(struct sti_hdmi *hdmi) | |||
241 | hdmi_write(hdmi, conf, HDMI_CFG); | 257 | hdmi_write(hdmi, conf, HDMI_CFG); |
242 | } | 258 | } |
243 | 259 | ||
260 | /* | ||
261 | * Helper to reset info frame | ||
262 | * | ||
263 | * @hdmi: pointer on the hdmi internal structure | ||
264 | * @slot: infoframe to reset | ||
265 | */ | ||
266 | static void hdmi_infoframe_reset(struct sti_hdmi *hdmi, | ||
267 | u32 slot) | ||
268 | { | ||
269 | u32 val, i; | ||
270 | u32 head_offset, pack_offset; | ||
271 | |||
272 | switch (slot) { | ||
273 | case HDMI_IFRAME_SLOT_AVI: | ||
274 | head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI); | ||
275 | pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI); | ||
276 | break; | ||
277 | case HDMI_IFRAME_SLOT_AUDIO: | ||
278 | head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AUDIO); | ||
279 | pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AUDIO); | ||
280 | break; | ||
281 | case HDMI_IFRAME_SLOT_VENDOR: | ||
282 | head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_VENDOR); | ||
283 | pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_VENDOR); | ||
284 | break; | ||
285 | default: | ||
286 | DRM_ERROR("unsupported infoframe slot: %#x\n", slot); | ||
287 | return; | ||
288 | } | ||
289 | |||
290 | /* Disable transmission for the selected slot */ | ||
291 | val = hdmi_read(hdmi, HDMI_SW_DI_CFG); | ||
292 | val &= ~HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, slot); | ||
293 | hdmi_write(hdmi, val, HDMI_SW_DI_CFG); | ||
294 | |||
295 | /* Reset info frame registers */ | ||
296 | hdmi_write(hdmi, 0x0, head_offset); | ||
297 | for (i = 0; i < HDMI_SW_DI_MAX_WORD; i += sizeof(u32)) | ||
298 | hdmi_write(hdmi, 0x0, pack_offset + i); | ||
299 | } | ||
300 | |||
244 | /** | 301 | /** |
245 | * Helper to concatenate infoframe in 32 bits word | 302 | * Helper to concatenate infoframe in 32 bits word |
246 | * | 303 | * |
@@ -266,12 +323,13 @@ static inline unsigned int hdmi_infoframe_subpack(const u8 *ptr, size_t size) | |||
266 | * @data: infoframe to write | 323 | * @data: infoframe to write |
267 | * @size: size to write | 324 | * @size: size to write |
268 | */ | 325 | */ |
269 | static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, const u8 *data) | 326 | static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, |
327 | const u8 *data, | ||
328 | size_t size) | ||
270 | { | 329 | { |
271 | const u8 *ptr = data; | 330 | const u8 *ptr = data; |
272 | u32 val, slot, mode, i; | 331 | u32 val, slot, mode, i; |
273 | u32 head_offset, pack_offset; | 332 | u32 head_offset, pack_offset; |
274 | size_t size; | ||
275 | 333 | ||
276 | switch (*ptr) { | 334 | switch (*ptr) { |
277 | case HDMI_INFOFRAME_TYPE_AVI: | 335 | case HDMI_INFOFRAME_TYPE_AVI: |
@@ -279,17 +337,19 @@ static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, const u8 *data) | |||
279 | mode = HDMI_IFRAME_FIELD; | 337 | mode = HDMI_IFRAME_FIELD; |
280 | head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI); | 338 | head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI); |
281 | pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI); | 339 | pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI); |
282 | size = HDMI_AVI_INFOFRAME_SIZE; | ||
283 | break; | 340 | break; |
284 | |||
285 | case HDMI_INFOFRAME_TYPE_AUDIO: | 341 | case HDMI_INFOFRAME_TYPE_AUDIO: |
286 | slot = HDMI_IFRAME_SLOT_AUDIO; | 342 | slot = HDMI_IFRAME_SLOT_AUDIO; |
287 | mode = HDMI_IFRAME_FRAME; | 343 | mode = HDMI_IFRAME_FRAME; |
288 | head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AUDIO); | 344 | head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AUDIO); |
289 | pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AUDIO); | 345 | pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AUDIO); |
290 | size = HDMI_AUDIO_INFOFRAME_SIZE; | ||
291 | break; | 346 | break; |
292 | 347 | case HDMI_INFOFRAME_TYPE_VENDOR: | |
348 | slot = HDMI_IFRAME_SLOT_VENDOR; | ||
349 | mode = HDMI_IFRAME_FRAME; | ||
350 | head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_VENDOR); | ||
351 | pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_VENDOR); | ||
352 | break; | ||
293 | default: | 353 | default: |
294 | DRM_ERROR("unsupported infoframe type: %#x\n", *ptr); | 354 | DRM_ERROR("unsupported infoframe type: %#x\n", *ptr); |
295 | return; | 355 | return; |
@@ -308,8 +368,9 @@ static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, const u8 *data) | |||
308 | /* | 368 | /* |
309 | * Each subpack contains 4 bytes | 369 | * Each subpack contains 4 bytes |
310 | * The First Bytes of the first subpacket must contain the checksum | 370 | * The First Bytes of the first subpacket must contain the checksum |
311 | * Packet size in increase by one. | 371 | * Packet size is increase by one. |
312 | */ | 372 | */ |
373 | size = size - HDMI_INFOFRAME_HEADER_SIZE + 1; | ||
313 | for (i = 0; i < size; i += sizeof(u32)) { | 374 | for (i = 0; i < size; i += sizeof(u32)) { |
314 | size_t num; | 375 | size_t num; |
315 | 376 | ||
@@ -321,7 +382,7 @@ static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, const u8 *data) | |||
321 | 382 | ||
322 | /* Enable transmission slot for updated infoframe */ | 383 | /* Enable transmission slot for updated infoframe */ |
323 | val = hdmi_read(hdmi, HDMI_SW_DI_CFG); | 384 | val = hdmi_read(hdmi, HDMI_SW_DI_CFG); |
324 | val |= HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_FIELD, slot); | 385 | val |= HDMI_IFRAME_CFG_DI_N(mode, slot); |
325 | hdmi_write(hdmi, val, HDMI_SW_DI_CFG); | 386 | hdmi_write(hdmi, val, HDMI_SW_DI_CFG); |
326 | } | 387 | } |
327 | 388 | ||
@@ -352,7 +413,7 @@ static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi) | |||
352 | } | 413 | } |
353 | 414 | ||
354 | /* fixed infoframe configuration not linked to the mode */ | 415 | /* fixed infoframe configuration not linked to the mode */ |
355 | infoframe.colorspace = HDMI_COLORSPACE_RGB; | 416 | infoframe.colorspace = hdmi->colorspace; |
356 | infoframe.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT; | 417 | infoframe.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT; |
357 | infoframe.colorimetry = HDMI_COLORIMETRY_NONE; | 418 | infoframe.colorimetry = HDMI_COLORIMETRY_NONE; |
358 | 419 | ||
@@ -362,7 +423,7 @@ static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi) | |||
362 | return ret; | 423 | return ret; |
363 | } | 424 | } |
364 | 425 | ||
365 | hdmi_infoframe_write_infopack(hdmi, buffer); | 426 | hdmi_infoframe_write_infopack(hdmi, buffer, ret); |
366 | 427 | ||
367 | return 0; | 428 | return 0; |
368 | } | 429 | } |
@@ -398,7 +459,49 @@ static int hdmi_audio_infoframe_config(struct sti_hdmi *hdmi) | |||
398 | return ret; | 459 | return ret; |
399 | } | 460 | } |
400 | 461 | ||
401 | hdmi_infoframe_write_infopack(hdmi, buffer); | 462 | hdmi_infoframe_write_infopack(hdmi, buffer, ret); |
463 | |||
464 | return 0; | ||
465 | } | ||
466 | |||
467 | /* | ||
468 | * Prepare and configure the VS infoframe | ||
469 | * | ||
470 | * Vendor Specific infoframe are transmitted once per frame and | ||
471 | * contains vendor specific information. | ||
472 | * | ||
473 | * @hdmi: pointer on the hdmi internal structure | ||
474 | * | ||
475 | * Return negative value if error occurs | ||
476 | */ | ||
477 | #define HDMI_VENDOR_INFOFRAME_MAX_SIZE 6 | ||
478 | static int hdmi_vendor_infoframe_config(struct sti_hdmi *hdmi) | ||
479 | { | ||
480 | struct drm_display_mode *mode = &hdmi->mode; | ||
481 | struct hdmi_vendor_infoframe infoframe; | ||
482 | u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_VENDOR_INFOFRAME_MAX_SIZE]; | ||
483 | int ret; | ||
484 | |||
485 | DRM_DEBUG_DRIVER("\n"); | ||
486 | |||
487 | ret = drm_hdmi_vendor_infoframe_from_display_mode(&infoframe, mode); | ||
488 | if (ret < 0) { | ||
489 | /* | ||
490 | * Going into that statement does not means vendor infoframe | ||
491 | * fails. It just informed us that vendor infoframe is not | ||
492 | * needed for the selected mode. Only 4k or stereoscopic 3D | ||
493 | * mode requires vendor infoframe. So just simply return 0. | ||
494 | */ | ||
495 | return 0; | ||
496 | } | ||
497 | |||
498 | ret = hdmi_vendor_infoframe_pack(&infoframe, buffer, sizeof(buffer)); | ||
499 | if (ret < 0) { | ||
500 | DRM_ERROR("failed to pack VS infoframe: %d\n", ret); | ||
501 | return ret; | ||
502 | } | ||
503 | |||
504 | hdmi_infoframe_write_infopack(hdmi, buffer, ret); | ||
402 | 505 | ||
403 | return 0; | 506 | return 0; |
404 | } | 507 | } |
@@ -448,6 +551,172 @@ static void hdmi_swreset(struct sti_hdmi *hdmi) | |||
448 | clk_disable_unprepare(hdmi->clk_audio); | 551 | clk_disable_unprepare(hdmi->clk_audio); |
449 | } | 552 | } |
450 | 553 | ||
554 | #define DBGFS_PRINT_STR(str1, str2) seq_printf(s, "%-24s %s\n", str1, str2) | ||
555 | #define DBGFS_PRINT_INT(str1, int2) seq_printf(s, "%-24s %d\n", str1, int2) | ||
556 | #define DBGFS_DUMP(str, reg) seq_printf(s, "%s %-25s 0x%08X", str, #reg, \ | ||
557 | hdmi_read(hdmi, reg)) | ||
558 | #define DBGFS_DUMP_DI(reg, slot) DBGFS_DUMP("\n", reg(slot)) | ||
559 | |||
560 | static void hdmi_dbg_cfg(struct seq_file *s, int val) | ||
561 | { | ||
562 | int tmp; | ||
563 | |||
564 | seq_puts(s, "\t"); | ||
565 | tmp = val & HDMI_CFG_HDMI_NOT_DVI; | ||
566 | DBGFS_PRINT_STR("mode:", tmp ? "HDMI" : "DVI"); | ||
567 | seq_puts(s, "\t\t\t\t\t"); | ||
568 | tmp = val & HDMI_CFG_HDCP_EN; | ||
569 | DBGFS_PRINT_STR("HDCP:", tmp ? "enable" : "disable"); | ||
570 | seq_puts(s, "\t\t\t\t\t"); | ||
571 | tmp = val & HDMI_CFG_ESS_NOT_OESS; | ||
572 | DBGFS_PRINT_STR("HDCP mode:", tmp ? "ESS enable" : "OESS enable"); | ||
573 | seq_puts(s, "\t\t\t\t\t"); | ||
574 | tmp = val & HDMI_CFG_SINK_TERM_DET_EN; | ||
575 | DBGFS_PRINT_STR("Sink term detection:", tmp ? "enable" : "disable"); | ||
576 | seq_puts(s, "\t\t\t\t\t"); | ||
577 | tmp = val & HDMI_CFG_H_SYNC_POL_NEG; | ||
578 | DBGFS_PRINT_STR("Hsync polarity:", tmp ? "inverted" : "normal"); | ||
579 | seq_puts(s, "\t\t\t\t\t"); | ||
580 | tmp = val & HDMI_CFG_V_SYNC_POL_NEG; | ||
581 | DBGFS_PRINT_STR("Vsync polarity:", tmp ? "inverted" : "normal"); | ||
582 | seq_puts(s, "\t\t\t\t\t"); | ||
583 | tmp = val & HDMI_CFG_422_EN; | ||
584 | DBGFS_PRINT_STR("YUV422 format:", tmp ? "enable" : "disable"); | ||
585 | } | ||
586 | |||
587 | static void hdmi_dbg_sta(struct seq_file *s, int val) | ||
588 | { | ||
589 | int tmp; | ||
590 | |||
591 | seq_puts(s, "\t"); | ||
592 | tmp = (val & HDMI_STA_DLL_LCK); | ||
593 | DBGFS_PRINT_STR("pll:", tmp ? "locked" : "not locked"); | ||
594 | seq_puts(s, "\t\t\t\t\t"); | ||
595 | tmp = (val & HDMI_STA_HOT_PLUG); | ||
596 | DBGFS_PRINT_STR("hdmi cable:", tmp ? "connected" : "not connected"); | ||
597 | } | ||
598 | |||
599 | static void hdmi_dbg_sw_di_cfg(struct seq_file *s, int val) | ||
600 | { | ||
601 | int tmp; | ||
602 | char *const en_di[] = {"no transmission", | ||
603 | "single transmission", | ||
604 | "once every field", | ||
605 | "once every frame"}; | ||
606 | |||
607 | seq_puts(s, "\t"); | ||
608 | tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 1)); | ||
609 | DBGFS_PRINT_STR("Data island 1:", en_di[tmp]); | ||
610 | seq_puts(s, "\t\t\t\t\t"); | ||
611 | tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 2)) >> 4; | ||
612 | DBGFS_PRINT_STR("Data island 2:", en_di[tmp]); | ||
613 | seq_puts(s, "\t\t\t\t\t"); | ||
614 | tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 3)) >> 8; | ||
615 | DBGFS_PRINT_STR("Data island 3:", en_di[tmp]); | ||
616 | seq_puts(s, "\t\t\t\t\t"); | ||
617 | tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 4)) >> 12; | ||
618 | DBGFS_PRINT_STR("Data island 4:", en_di[tmp]); | ||
619 | seq_puts(s, "\t\t\t\t\t"); | ||
620 | tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 5)) >> 16; | ||
621 | DBGFS_PRINT_STR("Data island 5:", en_di[tmp]); | ||
622 | seq_puts(s, "\t\t\t\t\t"); | ||
623 | tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 6)) >> 20; | ||
624 | DBGFS_PRINT_STR("Data island 6:", en_di[tmp]); | ||
625 | } | ||
626 | |||
627 | static int hdmi_dbg_show(struct seq_file *s, void *data) | ||
628 | { | ||
629 | struct drm_info_node *node = s->private; | ||
630 | struct sti_hdmi *hdmi = (struct sti_hdmi *)node->info_ent->data; | ||
631 | struct drm_device *dev = node->minor->dev; | ||
632 | int ret; | ||
633 | |||
634 | ret = mutex_lock_interruptible(&dev->struct_mutex); | ||
635 | if (ret) | ||
636 | return ret; | ||
637 | |||
638 | seq_printf(s, "HDMI: (vaddr = 0x%p)", hdmi->regs); | ||
639 | DBGFS_DUMP("\n", HDMI_CFG); | ||
640 | hdmi_dbg_cfg(s, hdmi_read(hdmi, HDMI_CFG)); | ||
641 | DBGFS_DUMP("", HDMI_INT_EN); | ||
642 | DBGFS_DUMP("\n", HDMI_STA); | ||
643 | hdmi_dbg_sta(s, hdmi_read(hdmi, HDMI_STA)); | ||
644 | DBGFS_DUMP("", HDMI_ACTIVE_VID_XMIN); | ||
645 | seq_puts(s, "\t"); | ||
646 | DBGFS_PRINT_INT("Xmin:", hdmi_read(hdmi, HDMI_ACTIVE_VID_XMIN)); | ||
647 | DBGFS_DUMP("", HDMI_ACTIVE_VID_XMAX); | ||
648 | seq_puts(s, "\t"); | ||
649 | DBGFS_PRINT_INT("Xmax:", hdmi_read(hdmi, HDMI_ACTIVE_VID_XMAX)); | ||
650 | DBGFS_DUMP("", HDMI_ACTIVE_VID_YMIN); | ||
651 | seq_puts(s, "\t"); | ||
652 | DBGFS_PRINT_INT("Ymin:", hdmi_read(hdmi, HDMI_ACTIVE_VID_YMIN)); | ||
653 | DBGFS_DUMP("", HDMI_ACTIVE_VID_YMAX); | ||
654 | seq_puts(s, "\t"); | ||
655 | DBGFS_PRINT_INT("Ymax:", hdmi_read(hdmi, HDMI_ACTIVE_VID_YMAX)); | ||
656 | DBGFS_DUMP("", HDMI_SW_DI_CFG); | ||
657 | hdmi_dbg_sw_di_cfg(s, hdmi_read(hdmi, HDMI_SW_DI_CFG)); | ||
658 | |||
659 | seq_printf(s, "\n AVI Infoframe (Data Island slot N=%d):", | ||
660 | HDMI_IFRAME_SLOT_AVI); | ||
661 | DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_AVI); | ||
662 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_AVI); | ||
663 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD1, HDMI_IFRAME_SLOT_AVI); | ||
664 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD2, HDMI_IFRAME_SLOT_AVI); | ||
665 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD3, HDMI_IFRAME_SLOT_AVI); | ||
666 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_AVI); | ||
667 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_AVI); | ||
668 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_AVI); | ||
669 | seq_puts(s, "\n"); | ||
670 | seq_printf(s, "\n AUDIO Infoframe (Data Island slot N=%d):", | ||
671 | HDMI_IFRAME_SLOT_AUDIO); | ||
672 | DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_AUDIO); | ||
673 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_AUDIO); | ||
674 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD1, HDMI_IFRAME_SLOT_AUDIO); | ||
675 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD2, HDMI_IFRAME_SLOT_AUDIO); | ||
676 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD3, HDMI_IFRAME_SLOT_AUDIO); | ||
677 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_AUDIO); | ||
678 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_AUDIO); | ||
679 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_AUDIO); | ||
680 | seq_puts(s, "\n"); | ||
681 | seq_printf(s, "\n VENDOR SPECIFIC Infoframe (Data Island slot N=%d):", | ||
682 | HDMI_IFRAME_SLOT_VENDOR); | ||
683 | DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_VENDOR); | ||
684 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_VENDOR); | ||
685 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD1, HDMI_IFRAME_SLOT_VENDOR); | ||
686 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD2, HDMI_IFRAME_SLOT_VENDOR); | ||
687 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD3, HDMI_IFRAME_SLOT_VENDOR); | ||
688 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_VENDOR); | ||
689 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_VENDOR); | ||
690 | DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_VENDOR); | ||
691 | seq_puts(s, "\n"); | ||
692 | |||
693 | mutex_unlock(&dev->struct_mutex); | ||
694 | return 0; | ||
695 | } | ||
696 | |||
697 | static struct drm_info_list hdmi_debugfs_files[] = { | ||
698 | { "hdmi", hdmi_dbg_show, 0, NULL }, | ||
699 | }; | ||
700 | |||
701 | static void hdmi_debugfs_exit(struct sti_hdmi *hdmi, struct drm_minor *minor) | ||
702 | { | ||
703 | drm_debugfs_remove_files(hdmi_debugfs_files, | ||
704 | ARRAY_SIZE(hdmi_debugfs_files), | ||
705 | minor); | ||
706 | } | ||
707 | |||
708 | static int hdmi_debugfs_init(struct sti_hdmi *hdmi, struct drm_minor *minor) | ||
709 | { | ||
710 | unsigned int i; | ||
711 | |||
712 | for (i = 0; i < ARRAY_SIZE(hdmi_debugfs_files); i++) | ||
713 | hdmi_debugfs_files[i].data = hdmi; | ||
714 | |||
715 | return drm_debugfs_create_files(hdmi_debugfs_files, | ||
716 | ARRAY_SIZE(hdmi_debugfs_files), | ||
717 | minor->debugfs_root, minor); | ||
718 | } | ||
719 | |||
451 | static void sti_hdmi_disable(struct drm_bridge *bridge) | 720 | static void sti_hdmi_disable(struct drm_bridge *bridge) |
452 | { | 721 | { |
453 | struct sti_hdmi *hdmi = bridge->driver_private; | 722 | struct sti_hdmi *hdmi = bridge->driver_private; |
@@ -468,6 +737,11 @@ static void sti_hdmi_disable(struct drm_bridge *bridge) | |||
468 | /* Stop the phy */ | 737 | /* Stop the phy */ |
469 | hdmi->phy_ops->stop(hdmi); | 738 | hdmi->phy_ops->stop(hdmi); |
470 | 739 | ||
740 | /* Reset info frame transmission */ | ||
741 | hdmi_infoframe_reset(hdmi, HDMI_IFRAME_SLOT_AVI); | ||
742 | hdmi_infoframe_reset(hdmi, HDMI_IFRAME_SLOT_AUDIO); | ||
743 | hdmi_infoframe_reset(hdmi, HDMI_IFRAME_SLOT_VENDOR); | ||
744 | |||
471 | /* Set the default channel data to be a dark red */ | 745 | /* Set the default channel data to be a dark red */ |
472 | hdmi_write(hdmi, 0x0000, HDMI_DFLT_CHL0_DAT); | 746 | hdmi_write(hdmi, 0x0000, HDMI_DFLT_CHL0_DAT); |
473 | hdmi_write(hdmi, 0x0000, HDMI_DFLT_CHL1_DAT); | 747 | hdmi_write(hdmi, 0x0000, HDMI_DFLT_CHL1_DAT); |
@@ -523,6 +797,10 @@ static void sti_hdmi_pre_enable(struct drm_bridge *bridge) | |||
523 | if (hdmi_audio_infoframe_config(hdmi)) | 797 | if (hdmi_audio_infoframe_config(hdmi)) |
524 | DRM_ERROR("Unable to configure AUDIO infoframe\n"); | 798 | DRM_ERROR("Unable to configure AUDIO infoframe\n"); |
525 | 799 | ||
800 | /* Program VS infoframe */ | ||
801 | if (hdmi_vendor_infoframe_config(hdmi)) | ||
802 | DRM_ERROR("Unable to configure VS infoframe\n"); | ||
803 | |||
526 | /* Sw reset */ | 804 | /* Sw reset */ |
527 | hdmi_swreset(hdmi); | 805 | hdmi_swreset(hdmi); |
528 | } | 806 | } |
@@ -664,12 +942,97 @@ static void sti_hdmi_connector_destroy(struct drm_connector *connector) | |||
664 | kfree(hdmi_connector); | 942 | kfree(hdmi_connector); |
665 | } | 943 | } |
666 | 944 | ||
945 | static void sti_hdmi_connector_init_property(struct drm_device *drm_dev, | ||
946 | struct drm_connector *connector) | ||
947 | { | ||
948 | struct sti_hdmi_connector *hdmi_connector | ||
949 | = to_sti_hdmi_connector(connector); | ||
950 | struct sti_hdmi *hdmi = hdmi_connector->hdmi; | ||
951 | struct drm_property *prop; | ||
952 | |||
953 | /* colorspace property */ | ||
954 | hdmi->colorspace = DEFAULT_COLORSPACE_MODE; | ||
955 | prop = drm_property_create_enum(drm_dev, 0, "colorspace", | ||
956 | colorspace_mode_names, | ||
957 | ARRAY_SIZE(colorspace_mode_names)); | ||
958 | if (!prop) { | ||
959 | DRM_ERROR("fails to create colorspace property\n"); | ||
960 | return; | ||
961 | } | ||
962 | hdmi_connector->colorspace_property = prop; | ||
963 | drm_object_attach_property(&connector->base, prop, hdmi->colorspace); | ||
964 | |||
965 | /* hdmi_mode property */ | ||
966 | hdmi->hdmi_mode = DEFAULT_HDMI_MODE; | ||
967 | prop = drm_property_create_enum(drm_dev, 0, "hdmi_mode", | ||
968 | hdmi_mode_names, | ||
969 | ARRAY_SIZE(hdmi_mode_names)); | ||
970 | if (!prop) { | ||
971 | DRM_ERROR("fails to create colorspace property\n"); | ||
972 | return; | ||
973 | } | ||
974 | hdmi_connector->hdmi_mode_property = prop; | ||
975 | drm_object_attach_property(&connector->base, prop, hdmi->hdmi_mode); | ||
976 | |||
977 | } | ||
978 | |||
979 | static int | ||
980 | sti_hdmi_connector_set_property(struct drm_connector *connector, | ||
981 | struct drm_connector_state *state, | ||
982 | struct drm_property *property, | ||
983 | uint64_t val) | ||
984 | { | ||
985 | struct sti_hdmi_connector *hdmi_connector | ||
986 | = to_sti_hdmi_connector(connector); | ||
987 | struct sti_hdmi *hdmi = hdmi_connector->hdmi; | ||
988 | |||
989 | if (property == hdmi_connector->colorspace_property) { | ||
990 | hdmi->colorspace = val; | ||
991 | return 0; | ||
992 | } | ||
993 | |||
994 | if (property == hdmi_connector->hdmi_mode_property) { | ||
995 | hdmi->hdmi_mode = val; | ||
996 | return 0; | ||
997 | } | ||
998 | |||
999 | DRM_ERROR("failed to set hdmi connector property\n"); | ||
1000 | return -EINVAL; | ||
1001 | } | ||
1002 | |||
1003 | static int | ||
1004 | sti_hdmi_connector_get_property(struct drm_connector *connector, | ||
1005 | const struct drm_connector_state *state, | ||
1006 | struct drm_property *property, | ||
1007 | uint64_t *val) | ||
1008 | { | ||
1009 | struct sti_hdmi_connector *hdmi_connector | ||
1010 | = to_sti_hdmi_connector(connector); | ||
1011 | struct sti_hdmi *hdmi = hdmi_connector->hdmi; | ||
1012 | |||
1013 | if (property == hdmi_connector->colorspace_property) { | ||
1014 | *val = hdmi->colorspace; | ||
1015 | return 0; | ||
1016 | } | ||
1017 | |||
1018 | if (property == hdmi_connector->hdmi_mode_property) { | ||
1019 | *val = hdmi->hdmi_mode; | ||
1020 | return 0; | ||
1021 | } | ||
1022 | |||
1023 | DRM_ERROR("failed to get hdmi connector property\n"); | ||
1024 | return -EINVAL; | ||
1025 | } | ||
1026 | |||
667 | static const struct drm_connector_funcs sti_hdmi_connector_funcs = { | 1027 | static const struct drm_connector_funcs sti_hdmi_connector_funcs = { |
668 | .dpms = drm_atomic_helper_connector_dpms, | 1028 | .dpms = drm_atomic_helper_connector_dpms, |
669 | .fill_modes = drm_helper_probe_single_connector_modes, | 1029 | .fill_modes = drm_helper_probe_single_connector_modes, |
670 | .detect = sti_hdmi_connector_detect, | 1030 | .detect = sti_hdmi_connector_detect, |
671 | .destroy = sti_hdmi_connector_destroy, | 1031 | .destroy = sti_hdmi_connector_destroy, |
672 | .reset = drm_atomic_helper_connector_reset, | 1032 | .reset = drm_atomic_helper_connector_reset, |
1033 | .set_property = drm_atomic_helper_connector_set_property, | ||
1034 | .atomic_set_property = sti_hdmi_connector_set_property, | ||
1035 | .atomic_get_property = sti_hdmi_connector_get_property, | ||
673 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | 1036 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, |
674 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | 1037 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, |
675 | }; | 1038 | }; |
@@ -729,6 +1092,9 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) | |||
729 | drm_connector_helper_add(drm_connector, | 1092 | drm_connector_helper_add(drm_connector, |
730 | &sti_hdmi_connector_helper_funcs); | 1093 | &sti_hdmi_connector_helper_funcs); |
731 | 1094 | ||
1095 | /* initialise property */ | ||
1096 | sti_hdmi_connector_init_property(drm_dev, drm_connector); | ||
1097 | |||
732 | err = drm_connector_register(drm_connector); | 1098 | err = drm_connector_register(drm_connector); |
733 | if (err) | 1099 | if (err) |
734 | goto err_connector; | 1100 | goto err_connector; |
@@ -742,6 +1108,9 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) | |||
742 | /* Enable default interrupts */ | 1108 | /* Enable default interrupts */ |
743 | hdmi_write(hdmi, HDMI_DEFAULT_INT, HDMI_INT_EN); | 1109 | hdmi_write(hdmi, HDMI_DEFAULT_INT, HDMI_INT_EN); |
744 | 1110 | ||
1111 | if (hdmi_debugfs_init(hdmi, drm_dev->primary)) | ||
1112 | DRM_ERROR("HDMI debugfs setup failed\n"); | ||
1113 | |||
745 | return 0; | 1114 | return 0; |
746 | 1115 | ||
747 | err_sysfs: | 1116 | err_sysfs: |
@@ -755,7 +1124,10 @@ err_connector: | |||
755 | static void sti_hdmi_unbind(struct device *dev, | 1124 | static void sti_hdmi_unbind(struct device *dev, |
756 | struct device *master, void *data) | 1125 | struct device *master, void *data) |
757 | { | 1126 | { |
758 | /* do nothing */ | 1127 | struct sti_hdmi *hdmi = dev_get_drvdata(dev); |
1128 | struct drm_device *drm_dev = data; | ||
1129 | |||
1130 | hdmi_debugfs_exit(hdmi, drm_dev->primary); | ||
759 | } | 1131 | } |
760 | 1132 | ||
761 | static const struct component_ops sti_hdmi_ops = { | 1133 | static const struct component_ops sti_hdmi_ops = { |
diff --git a/drivers/gpu/drm/sti/sti_hdmi.h b/drivers/gpu/drm/sti/sti_hdmi.h index 3d22390e1f3b..ef3a94583bbd 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.h +++ b/drivers/gpu/drm/sti/sti_hdmi.h | |||
@@ -7,15 +7,14 @@ | |||
7 | #ifndef _STI_HDMI_H_ | 7 | #ifndef _STI_HDMI_H_ |
8 | #define _STI_HDMI_H_ | 8 | #define _STI_HDMI_H_ |
9 | 9 | ||
10 | #include <linux/hdmi.h> | ||
10 | #include <linux/platform_device.h> | 11 | #include <linux/platform_device.h> |
11 | 12 | ||
12 | #include <drm/drmP.h> | 13 | #include <drm/drmP.h> |
13 | 14 | ||
14 | #define HDMI_STA 0x0010 | 15 | #define HDMI_STA 0x0010 |
15 | #define HDMI_STA_DLL_LCK BIT(5) | 16 | #define HDMI_STA_DLL_LCK BIT(5) |
16 | 17 | #define HDMI_STA_HOT_PLUG BIT(4) | |
17 | #define HDMI_STA_HOT_PLUG_SHIFT 4 | ||
18 | #define HDMI_STA_HOT_PLUG (1 << HDMI_STA_HOT_PLUG_SHIFT) | ||
19 | 18 | ||
20 | struct sti_hdmi; | 19 | struct sti_hdmi; |
21 | 20 | ||
@@ -24,6 +23,27 @@ struct hdmi_phy_ops { | |||
24 | void (*stop)(struct sti_hdmi *hdmi); | 23 | void (*stop)(struct sti_hdmi *hdmi); |
25 | }; | 24 | }; |
26 | 25 | ||
26 | /* values for the framing mode property */ | ||
27 | enum sti_hdmi_modes { | ||
28 | HDMI_MODE_HDMI, | ||
29 | HDMI_MODE_DVI, | ||
30 | }; | ||
31 | |||
32 | static const struct drm_prop_enum_list hdmi_mode_names[] = { | ||
33 | { HDMI_MODE_HDMI, "hdmi" }, | ||
34 | { HDMI_MODE_DVI, "dvi" }, | ||
35 | }; | ||
36 | |||
37 | #define DEFAULT_HDMI_MODE HDMI_MODE_HDMI | ||
38 | |||
39 | static const struct drm_prop_enum_list colorspace_mode_names[] = { | ||
40 | { HDMI_COLORSPACE_RGB, "rgb" }, | ||
41 | { HDMI_COLORSPACE_YUV422, "yuv422" }, | ||
42 | { HDMI_COLORSPACE_YUV444, "yuv444" }, | ||
43 | }; | ||
44 | |||
45 | #define DEFAULT_COLORSPACE_MODE HDMI_COLORSPACE_RGB | ||
46 | |||
27 | /** | 47 | /** |
28 | * STI hdmi structure | 48 | * STI hdmi structure |
29 | * | 49 | * |
@@ -44,6 +64,9 @@ struct hdmi_phy_ops { | |||
44 | * @wait_event: wait event | 64 | * @wait_event: wait event |
45 | * @event_received: wait event status | 65 | * @event_received: wait event status |
46 | * @reset: reset control of the hdmi phy | 66 | * @reset: reset control of the hdmi phy |
67 | * @ddc_adapt: i2c ddc adapter | ||
68 | * @colorspace: current colorspace selected | ||
69 | * @hdmi_mode: select framing for HDMI or DVI | ||
47 | */ | 70 | */ |
48 | struct sti_hdmi { | 71 | struct sti_hdmi { |
49 | struct device dev; | 72 | struct device dev; |
@@ -64,6 +87,8 @@ struct sti_hdmi { | |||
64 | bool event_received; | 87 | bool event_received; |
65 | struct reset_control *reset; | 88 | struct reset_control *reset; |
66 | struct i2c_adapter *ddc_adapt; | 89 | struct i2c_adapter *ddc_adapt; |
90 | enum hdmi_colorspace colorspace; | ||
91 | enum sti_hdmi_modes hdmi_mode; | ||
67 | }; | 92 | }; |
68 | 93 | ||
69 | u32 hdmi_read(struct sti_hdmi *hdmi, int offset); | 94 | u32 hdmi_read(struct sti_hdmi *hdmi, int offset); |
diff --git a/drivers/gpu/drm/sti/sti_hqvdp.c b/drivers/gpu/drm/sti/sti_hqvdp.c index 43861b52261d..d7c1f427811d 100644 --- a/drivers/gpu/drm/sti/sti_hqvdp.c +++ b/drivers/gpu/drm/sti/sti_hqvdp.c | |||
@@ -4,14 +4,11 @@ | |||
4 | * License terms: GNU General Public License (GPL), version 2 | 4 | * License terms: GNU General Public License (GPL), version 2 |
5 | */ | 5 | */ |
6 | 6 | ||
7 | #include <linux/clk.h> | ||
8 | #include <linux/component.h> | 7 | #include <linux/component.h> |
9 | #include <linux/firmware.h> | 8 | #include <linux/firmware.h> |
10 | #include <linux/module.h> | ||
11 | #include <linux/platform_device.h> | ||
12 | #include <linux/reset.h> | 9 | #include <linux/reset.h> |
13 | 10 | ||
14 | #include <drm/drmP.h> | 11 | #include <drm/drm_atomic.h> |
15 | #include <drm/drm_fb_cma_helper.h> | 12 | #include <drm/drm_fb_cma_helper.h> |
16 | #include <drm/drm_gem_cma_helper.h> | 13 | #include <drm/drm_gem_cma_helper.h> |
17 | 14 | ||
@@ -329,8 +326,6 @@ struct sti_hqvdp_cmd { | |||
329 | * @reset: reset control | 326 | * @reset: reset control |
330 | * @vtg_nb: notifier to handle VTG Vsync | 327 | * @vtg_nb: notifier to handle VTG Vsync |
331 | * @btm_field_pending: is there any bottom field (interlaced frame) to display | 328 | * @btm_field_pending: is there any bottom field (interlaced frame) to display |
332 | * @curr_field_count: number of field updates | ||
333 | * @last_field_count: number of field updates since last fps measure | ||
334 | * @hqvdp_cmd: buffer of commands | 329 | * @hqvdp_cmd: buffer of commands |
335 | * @hqvdp_cmd_paddr: physical address of hqvdp_cmd | 330 | * @hqvdp_cmd_paddr: physical address of hqvdp_cmd |
336 | * @vtg: vtg for main data path | 331 | * @vtg: vtg for main data path |
@@ -346,10 +341,8 @@ struct sti_hqvdp { | |||
346 | struct reset_control *reset; | 341 | struct reset_control *reset; |
347 | struct notifier_block vtg_nb; | 342 | struct notifier_block vtg_nb; |
348 | bool btm_field_pending; | 343 | bool btm_field_pending; |
349 | unsigned int curr_field_count; | ||
350 | unsigned int last_field_count; | ||
351 | void *hqvdp_cmd; | 344 | void *hqvdp_cmd; |
352 | dma_addr_t hqvdp_cmd_paddr; | 345 | u32 hqvdp_cmd_paddr; |
353 | struct sti_vtg *vtg; | 346 | struct sti_vtg *vtg; |
354 | bool xp70_initialized; | 347 | bool xp70_initialized; |
355 | }; | 348 | }; |
@@ -372,8 +365,8 @@ static const uint32_t hqvdp_supported_formats[] = { | |||
372 | */ | 365 | */ |
373 | static int sti_hqvdp_get_free_cmd(struct sti_hqvdp *hqvdp) | 366 | static int sti_hqvdp_get_free_cmd(struct sti_hqvdp *hqvdp) |
374 | { | 367 | { |
375 | int curr_cmd, next_cmd; | 368 | u32 curr_cmd, next_cmd; |
376 | dma_addr_t cmd = hqvdp->hqvdp_cmd_paddr; | 369 | u32 cmd = hqvdp->hqvdp_cmd_paddr; |
377 | int i; | 370 | int i; |
378 | 371 | ||
379 | curr_cmd = readl(hqvdp->regs + HQVDP_MBX_CURRENT_CMD); | 372 | curr_cmd = readl(hqvdp->regs + HQVDP_MBX_CURRENT_CMD); |
@@ -400,8 +393,8 @@ static int sti_hqvdp_get_free_cmd(struct sti_hqvdp *hqvdp) | |||
400 | */ | 393 | */ |
401 | static int sti_hqvdp_get_curr_cmd(struct sti_hqvdp *hqvdp) | 394 | static int sti_hqvdp_get_curr_cmd(struct sti_hqvdp *hqvdp) |
402 | { | 395 | { |
403 | int curr_cmd; | 396 | u32 curr_cmd; |
404 | dma_addr_t cmd = hqvdp->hqvdp_cmd_paddr; | 397 | u32 cmd = hqvdp->hqvdp_cmd_paddr; |
405 | unsigned int i; | 398 | unsigned int i; |
406 | 399 | ||
407 | curr_cmd = readl(hqvdp->regs + HQVDP_MBX_CURRENT_CMD); | 400 | curr_cmd = readl(hqvdp->regs + HQVDP_MBX_CURRENT_CMD); |
@@ -417,6 +410,246 @@ static int sti_hqvdp_get_curr_cmd(struct sti_hqvdp *hqvdp) | |||
417 | } | 410 | } |
418 | 411 | ||
419 | /** | 412 | /** |
413 | * sti_hqvdp_get_next_cmd | ||
414 | * @hqvdp: hqvdp structure | ||
415 | * | ||
416 | * Look for the next hqvdp_cmd that will be used by the FW. | ||
417 | * | ||
418 | * RETURNS: | ||
419 | * the offset of the next command that will be used. | ||
420 | * -1 in error cases | ||
421 | */ | ||
422 | static int sti_hqvdp_get_next_cmd(struct sti_hqvdp *hqvdp) | ||
423 | { | ||
424 | int next_cmd; | ||
425 | dma_addr_t cmd = hqvdp->hqvdp_cmd_paddr; | ||
426 | unsigned int i; | ||
427 | |||
428 | next_cmd = readl(hqvdp->regs + HQVDP_MBX_NEXT_CMD); | ||
429 | |||
430 | for (i = 0; i < NB_VDP_CMD; i++) { | ||
431 | if (cmd == next_cmd) | ||
432 | return i * sizeof(struct sti_hqvdp_cmd); | ||
433 | |||
434 | cmd += sizeof(struct sti_hqvdp_cmd); | ||
435 | } | ||
436 | |||
437 | return -1; | ||
438 | } | ||
439 | |||
440 | #define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \ | ||
441 | readl(hqvdp->regs + reg)) | ||
442 | |||
443 | static const char *hqvdp_dbg_get_lut(u32 *coef) | ||
444 | { | ||
445 | if (!memcmp(coef, coef_lut_a_legacy, 16)) | ||
446 | return "LUT A"; | ||
447 | if (!memcmp(coef, coef_lut_b, 16)) | ||
448 | return "LUT B"; | ||
449 | if (!memcmp(coef, coef_lut_c_y_legacy, 16)) | ||
450 | return "LUT C Y"; | ||
451 | if (!memcmp(coef, coef_lut_c_c_legacy, 16)) | ||
452 | return "LUT C C"; | ||
453 | if (!memcmp(coef, coef_lut_d_y_legacy, 16)) | ||
454 | return "LUT D Y"; | ||
455 | if (!memcmp(coef, coef_lut_d_c_legacy, 16)) | ||
456 | return "LUT D C"; | ||
457 | if (!memcmp(coef, coef_lut_e_y_legacy, 16)) | ||
458 | return "LUT E Y"; | ||
459 | if (!memcmp(coef, coef_lut_e_c_legacy, 16)) | ||
460 | return "LUT E C"; | ||
461 | if (!memcmp(coef, coef_lut_f_y_legacy, 16)) | ||
462 | return "LUT F Y"; | ||
463 | if (!memcmp(coef, coef_lut_f_c_legacy, 16)) | ||
464 | return "LUT F C"; | ||
465 | return "<UNKNOWN>"; | ||
466 | } | ||
467 | |||
468 | static void hqvdp_dbg_dump_cmd(struct seq_file *s, struct sti_hqvdp_cmd *c) | ||
469 | { | ||
470 | int src_w, src_h, dst_w, dst_h; | ||
471 | |||
472 | seq_puts(s, "\n\tTOP:"); | ||
473 | seq_printf(s, "\n\t %-20s 0x%08X", "Config", c->top.config); | ||
474 | switch (c->top.config) { | ||
475 | case TOP_CONFIG_PROGRESSIVE: | ||
476 | seq_puts(s, "\tProgressive"); | ||
477 | break; | ||
478 | case TOP_CONFIG_INTER_TOP: | ||
479 | seq_puts(s, "\tInterlaced, top field"); | ||
480 | break; | ||
481 | case TOP_CONFIG_INTER_BTM: | ||
482 | seq_puts(s, "\tInterlaced, bottom field"); | ||
483 | break; | ||
484 | default: | ||
485 | seq_puts(s, "\t<UNKNOWN>"); | ||
486 | break; | ||
487 | } | ||
488 | |||
489 | seq_printf(s, "\n\t %-20s 0x%08X", "MemFormat", c->top.mem_format); | ||
490 | seq_printf(s, "\n\t %-20s 0x%08X", "CurrentY", c->top.current_luma); | ||
491 | seq_printf(s, "\n\t %-20s 0x%08X", "CurrentC", c->top.current_chroma); | ||
492 | seq_printf(s, "\n\t %-20s 0x%08X", "YSrcPitch", c->top.luma_src_pitch); | ||
493 | seq_printf(s, "\n\t %-20s 0x%08X", "CSrcPitch", | ||
494 | c->top.chroma_src_pitch); | ||
495 | seq_printf(s, "\n\t %-20s 0x%08X", "InputFrameSize", | ||
496 | c->top.input_frame_size); | ||
497 | seq_printf(s, "\t%dx%d", | ||
498 | c->top.input_frame_size & 0x0000FFFF, | ||
499 | c->top.input_frame_size >> 16); | ||
500 | seq_printf(s, "\n\t %-20s 0x%08X", "InputViewportSize", | ||
501 | c->top.input_viewport_size); | ||
502 | src_w = c->top.input_viewport_size & 0x0000FFFF; | ||
503 | src_h = c->top.input_viewport_size >> 16; | ||
504 | seq_printf(s, "\t%dx%d", src_w, src_h); | ||
505 | |||
506 | seq_puts(s, "\n\tHVSRC:"); | ||
507 | seq_printf(s, "\n\t %-20s 0x%08X", "OutputPictureSize", | ||
508 | c->hvsrc.output_picture_size); | ||
509 | dst_w = c->hvsrc.output_picture_size & 0x0000FFFF; | ||
510 | dst_h = c->hvsrc.output_picture_size >> 16; | ||
511 | seq_printf(s, "\t%dx%d", dst_w, dst_h); | ||
512 | seq_printf(s, "\n\t %-20s 0x%08X", "ParamCtrl", c->hvsrc.param_ctrl); | ||
513 | |||
514 | seq_printf(s, "\n\t %-20s %s", "yh_coef", | ||
515 | hqvdp_dbg_get_lut(c->hvsrc.yh_coef)); | ||
516 | seq_printf(s, "\n\t %-20s %s", "ch_coef", | ||
517 | hqvdp_dbg_get_lut(c->hvsrc.ch_coef)); | ||
518 | seq_printf(s, "\n\t %-20s %s", "yv_coef", | ||
519 | hqvdp_dbg_get_lut(c->hvsrc.yv_coef)); | ||
520 | seq_printf(s, "\n\t %-20s %s", "cv_coef", | ||
521 | hqvdp_dbg_get_lut(c->hvsrc.cv_coef)); | ||
522 | |||
523 | seq_printf(s, "\n\t %-20s", "ScaleH"); | ||
524 | if (dst_w > src_w) | ||
525 | seq_printf(s, " %d/1", dst_w / src_w); | ||
526 | else | ||
527 | seq_printf(s, " 1/%d", src_w / dst_w); | ||
528 | |||
529 | seq_printf(s, "\n\t %-20s", "tScaleV"); | ||
530 | if (dst_h > src_h) | ||
531 | seq_printf(s, " %d/1", dst_h / src_h); | ||
532 | else | ||
533 | seq_printf(s, " 1/%d", src_h / dst_h); | ||
534 | |||
535 | seq_puts(s, "\n\tCSDI:"); | ||
536 | seq_printf(s, "\n\t %-20s 0x%08X\t", "Config", c->csdi.config); | ||
537 | switch (c->csdi.config) { | ||
538 | case CSDI_CONFIG_PROG: | ||
539 | seq_puts(s, "Bypass"); | ||
540 | break; | ||
541 | case CSDI_CONFIG_INTER_DIR: | ||
542 | seq_puts(s, "Deinterlace, directional"); | ||
543 | break; | ||
544 | default: | ||
545 | seq_puts(s, "<UNKNOWN>"); | ||
546 | break; | ||
547 | } | ||
548 | |||
549 | seq_printf(s, "\n\t %-20s 0x%08X", "Config2", c->csdi.config2); | ||
550 | seq_printf(s, "\n\t %-20s 0x%08X", "DcdiConfig", c->csdi.dcdi_config); | ||
551 | } | ||
552 | |||
553 | static int hqvdp_dbg_show(struct seq_file *s, void *data) | ||
554 | { | ||
555 | struct drm_info_node *node = s->private; | ||
556 | struct sti_hqvdp *hqvdp = (struct sti_hqvdp *)node->info_ent->data; | ||
557 | struct drm_device *dev = node->minor->dev; | ||
558 | int cmd, cmd_offset, infoxp70; | ||
559 | void *virt; | ||
560 | int ret; | ||
561 | |||
562 | ret = mutex_lock_interruptible(&dev->struct_mutex); | ||
563 | if (ret) | ||
564 | return ret; | ||
565 | |||
566 | seq_printf(s, "%s: (vaddr = 0x%p)", | ||
567 | sti_plane_to_str(&hqvdp->plane), hqvdp->regs); | ||
568 | |||
569 | DBGFS_DUMP(HQVDP_MBX_IRQ_TO_XP70); | ||
570 | DBGFS_DUMP(HQVDP_MBX_INFO_HOST); | ||
571 | DBGFS_DUMP(HQVDP_MBX_IRQ_TO_HOST); | ||
572 | DBGFS_DUMP(HQVDP_MBX_INFO_XP70); | ||
573 | infoxp70 = readl(hqvdp->regs + HQVDP_MBX_INFO_XP70); | ||
574 | seq_puts(s, "\tFirmware state: "); | ||
575 | if (infoxp70 & INFO_XP70_FW_READY) | ||
576 | seq_puts(s, "idle and ready"); | ||
577 | else if (infoxp70 & INFO_XP70_FW_PROCESSING) | ||
578 | seq_puts(s, "processing a picture"); | ||
579 | else if (infoxp70 & INFO_XP70_FW_INITQUEUES) | ||
580 | seq_puts(s, "programming queues"); | ||
581 | else | ||
582 | seq_puts(s, "NOT READY"); | ||
583 | |||
584 | DBGFS_DUMP(HQVDP_MBX_SW_RESET_CTRL); | ||
585 | DBGFS_DUMP(HQVDP_MBX_STARTUP_CTRL1); | ||
586 | if (readl(hqvdp->regs + HQVDP_MBX_STARTUP_CTRL1) | ||
587 | & STARTUP_CTRL1_RST_DONE) | ||
588 | seq_puts(s, "\tReset is done"); | ||
589 | else | ||
590 | seq_puts(s, "\tReset is NOT done"); | ||
591 | DBGFS_DUMP(HQVDP_MBX_STARTUP_CTRL2); | ||
592 | if (readl(hqvdp->regs + HQVDP_MBX_STARTUP_CTRL2) | ||
593 | & STARTUP_CTRL2_FETCH_EN) | ||
594 | seq_puts(s, "\tFetch is enabled"); | ||
595 | else | ||
596 | seq_puts(s, "\tFetch is NOT enabled"); | ||
597 | DBGFS_DUMP(HQVDP_MBX_GP_STATUS); | ||
598 | DBGFS_DUMP(HQVDP_MBX_NEXT_CMD); | ||
599 | DBGFS_DUMP(HQVDP_MBX_CURRENT_CMD); | ||
600 | DBGFS_DUMP(HQVDP_MBX_SOFT_VSYNC); | ||
601 | if (!(readl(hqvdp->regs + HQVDP_MBX_SOFT_VSYNC) & 3)) | ||
602 | seq_puts(s, "\tHW Vsync"); | ||
603 | else | ||
604 | seq_puts(s, "\tSW Vsync ?!?!"); | ||
605 | |||
606 | /* Last command */ | ||
607 | cmd = readl(hqvdp->regs + HQVDP_MBX_CURRENT_CMD); | ||
608 | cmd_offset = sti_hqvdp_get_curr_cmd(hqvdp); | ||
609 | if (cmd_offset == -1) { | ||
610 | seq_puts(s, "\n\n Last command: unknown"); | ||
611 | } else { | ||
612 | virt = hqvdp->hqvdp_cmd + cmd_offset; | ||
613 | seq_printf(s, "\n\n Last command: address @ 0x%x (0x%p)", | ||
614 | cmd, virt); | ||
615 | hqvdp_dbg_dump_cmd(s, (struct sti_hqvdp_cmd *)virt); | ||
616 | } | ||
617 | |||
618 | /* Next command */ | ||
619 | cmd = readl(hqvdp->regs + HQVDP_MBX_NEXT_CMD); | ||
620 | cmd_offset = sti_hqvdp_get_next_cmd(hqvdp); | ||
621 | if (cmd_offset == -1) { | ||
622 | seq_puts(s, "\n\n Next command: unknown"); | ||
623 | } else { | ||
624 | virt = hqvdp->hqvdp_cmd + cmd_offset; | ||
625 | seq_printf(s, "\n\n Next command address: @ 0x%x (0x%p)", | ||
626 | cmd, virt); | ||
627 | hqvdp_dbg_dump_cmd(s, (struct sti_hqvdp_cmd *)virt); | ||
628 | } | ||
629 | |||
630 | seq_puts(s, "\n"); | ||
631 | |||
632 | mutex_unlock(&dev->struct_mutex); | ||
633 | return 0; | ||
634 | } | ||
635 | |||
636 | static struct drm_info_list hqvdp_debugfs_files[] = { | ||
637 | { "hqvdp", hqvdp_dbg_show, 0, NULL }, | ||
638 | }; | ||
639 | |||
640 | static int hqvdp_debugfs_init(struct sti_hqvdp *hqvdp, struct drm_minor *minor) | ||
641 | { | ||
642 | unsigned int i; | ||
643 | |||
644 | for (i = 0; i < ARRAY_SIZE(hqvdp_debugfs_files); i++) | ||
645 | hqvdp_debugfs_files[i].data = hqvdp; | ||
646 | |||
647 | return drm_debugfs_create_files(hqvdp_debugfs_files, | ||
648 | ARRAY_SIZE(hqvdp_debugfs_files), | ||
649 | minor->debugfs_root, minor); | ||
650 | } | ||
651 | |||
652 | /** | ||
420 | * sti_hqvdp_update_hvsrc | 653 | * sti_hqvdp_update_hvsrc |
421 | * @orient: horizontal or vertical | 654 | * @orient: horizontal or vertical |
422 | * @scale: scaling/zoom factor | 655 | * @scale: scaling/zoom factor |
@@ -580,7 +813,7 @@ int sti_hqvdp_vtg_cb(struct notifier_block *nb, unsigned long evt, void *data) | |||
580 | btm_cmd_offset = sti_hqvdp_get_free_cmd(hqvdp); | 813 | btm_cmd_offset = sti_hqvdp_get_free_cmd(hqvdp); |
581 | top_cmd_offest = sti_hqvdp_get_curr_cmd(hqvdp); | 814 | top_cmd_offest = sti_hqvdp_get_curr_cmd(hqvdp); |
582 | if ((btm_cmd_offset == -1) || (top_cmd_offest == -1)) { | 815 | if ((btm_cmd_offset == -1) || (top_cmd_offest == -1)) { |
583 | DRM_ERROR("Cannot get cmds, skip btm field\n"); | 816 | DRM_DEBUG_DRIVER("Warning: no cmd, will skip field\n"); |
584 | return -EBUSY; | 817 | return -EBUSY; |
585 | } | 818 | } |
586 | 819 | ||
@@ -599,11 +832,12 @@ int sti_hqvdp_vtg_cb(struct notifier_block *nb, unsigned long evt, void *data) | |||
599 | writel(hqvdp->hqvdp_cmd_paddr + btm_cmd_offset, | 832 | writel(hqvdp->hqvdp_cmd_paddr + btm_cmd_offset, |
600 | hqvdp->regs + HQVDP_MBX_NEXT_CMD); | 833 | hqvdp->regs + HQVDP_MBX_NEXT_CMD); |
601 | 834 | ||
602 | hqvdp->curr_field_count++; | ||
603 | hqvdp->btm_field_pending = false; | 835 | hqvdp->btm_field_pending = false; |
604 | 836 | ||
605 | dev_dbg(hqvdp->dev, "%s Posted command:0x%x\n", | 837 | dev_dbg(hqvdp->dev, "%s Posted command:0x%x\n", |
606 | __func__, hqvdp->hqvdp_cmd_paddr); | 838 | __func__, hqvdp->hqvdp_cmd_paddr); |
839 | |||
840 | sti_plane_update_fps(&hqvdp->plane, false, true); | ||
607 | } | 841 | } |
608 | 842 | ||
609 | return 0; | 843 | return 0; |
@@ -612,19 +846,21 @@ int sti_hqvdp_vtg_cb(struct notifier_block *nb, unsigned long evt, void *data) | |||
612 | static void sti_hqvdp_init(struct sti_hqvdp *hqvdp) | 846 | static void sti_hqvdp_init(struct sti_hqvdp *hqvdp) |
613 | { | 847 | { |
614 | int size; | 848 | int size; |
849 | dma_addr_t dma_addr; | ||
615 | 850 | ||
616 | hqvdp->vtg_nb.notifier_call = sti_hqvdp_vtg_cb; | 851 | hqvdp->vtg_nb.notifier_call = sti_hqvdp_vtg_cb; |
617 | 852 | ||
618 | /* Allocate memory for the VDP commands */ | 853 | /* Allocate memory for the VDP commands */ |
619 | size = NB_VDP_CMD * sizeof(struct sti_hqvdp_cmd); | 854 | size = NB_VDP_CMD * sizeof(struct sti_hqvdp_cmd); |
620 | hqvdp->hqvdp_cmd = dma_alloc_writecombine(hqvdp->dev, size, | 855 | hqvdp->hqvdp_cmd = dma_alloc_writecombine(hqvdp->dev, size, |
621 | &hqvdp->hqvdp_cmd_paddr, | 856 | &dma_addr, |
622 | GFP_KERNEL | GFP_DMA); | 857 | GFP_KERNEL | GFP_DMA); |
623 | if (!hqvdp->hqvdp_cmd) { | 858 | if (!hqvdp->hqvdp_cmd) { |
624 | DRM_ERROR("Failed to allocate memory for VDP cmd\n"); | 859 | DRM_ERROR("Failed to allocate memory for VDP cmd\n"); |
625 | return; | 860 | return; |
626 | } | 861 | } |
627 | 862 | ||
863 | hqvdp->hqvdp_cmd_paddr = (u32)dma_addr; | ||
628 | memset(hqvdp->hqvdp_cmd, 0, size); | 864 | memset(hqvdp->hqvdp_cmd, 0, size); |
629 | } | 865 | } |
630 | 866 | ||
@@ -670,7 +906,7 @@ static void sti_hqvdp_start_xp70(struct sti_hqvdp *hqvdp) | |||
670 | DRM_DEBUG_DRIVER("\n"); | 906 | DRM_DEBUG_DRIVER("\n"); |
671 | 907 | ||
672 | if (hqvdp->xp70_initialized) { | 908 | if (hqvdp->xp70_initialized) { |
673 | DRM_INFO("HQVDP XP70 already initialized\n"); | 909 | DRM_DEBUG_DRIVER("HQVDP XP70 already initialized\n"); |
674 | return; | 910 | return; |
675 | } | 911 | } |
676 | 912 | ||
@@ -775,53 +1011,131 @@ out: | |||
775 | release_firmware(firmware); | 1011 | release_firmware(firmware); |
776 | } | 1012 | } |
777 | 1013 | ||
778 | static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane, | 1014 | static int sti_hqvdp_atomic_check(struct drm_plane *drm_plane, |
779 | struct drm_plane_state *oldstate) | 1015 | struct drm_plane_state *state) |
780 | { | 1016 | { |
781 | struct drm_plane_state *state = drm_plane->state; | ||
782 | struct sti_plane *plane = to_sti_plane(drm_plane); | 1017 | struct sti_plane *plane = to_sti_plane(drm_plane); |
783 | struct sti_hqvdp *hqvdp = to_sti_hqvdp(plane); | 1018 | struct sti_hqvdp *hqvdp = to_sti_hqvdp(plane); |
784 | struct drm_crtc *crtc = state->crtc; | 1019 | struct drm_crtc *crtc = state->crtc; |
785 | struct sti_mixer *mixer = to_sti_mixer(crtc); | ||
786 | struct drm_framebuffer *fb = state->fb; | 1020 | struct drm_framebuffer *fb = state->fb; |
787 | struct drm_display_mode *mode = &crtc->mode; | ||
788 | int dst_x = state->crtc_x; | ||
789 | int dst_y = state->crtc_y; | ||
790 | int dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); | ||
791 | int dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); | ||
792 | /* src_x are in 16.16 format */ | ||
793 | int src_x = state->src_x >> 16; | ||
794 | int src_y = state->src_y >> 16; | ||
795 | int src_w = state->src_w >> 16; | ||
796 | int src_h = state->src_h >> 16; | ||
797 | bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false; | 1021 | bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false; |
798 | struct drm_gem_cma_object *cma_obj; | 1022 | struct drm_crtc_state *crtc_state; |
799 | struct sti_hqvdp_cmd *cmd; | 1023 | struct drm_display_mode *mode; |
800 | int scale_h, scale_v; | 1024 | int dst_x, dst_y, dst_w, dst_h; |
801 | int cmd_offset; | 1025 | int src_x, src_y, src_w, src_h; |
1026 | |||
1027 | /* no need for further checks if the plane is being disabled */ | ||
1028 | if (!crtc || !fb) | ||
1029 | return 0; | ||
1030 | |||
1031 | crtc_state = drm_atomic_get_crtc_state(state->state, crtc); | ||
1032 | mode = &crtc_state->mode; | ||
1033 | dst_x = state->crtc_x; | ||
1034 | dst_y = state->crtc_y; | ||
1035 | dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); | ||
1036 | dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); | ||
1037 | /* src_x are in 16.16 format */ | ||
1038 | src_x = state->src_x >> 16; | ||
1039 | src_y = state->src_y >> 16; | ||
1040 | src_w = state->src_w >> 16; | ||
1041 | src_h = state->src_h >> 16; | ||
1042 | |||
1043 | if (!sti_hqvdp_check_hw_scaling(hqvdp, mode, | ||
1044 | src_w, src_h, | ||
1045 | dst_w, dst_h)) { | ||
1046 | DRM_ERROR("Scaling beyond HW capabilities\n"); | ||
1047 | return -EINVAL; | ||
1048 | } | ||
1049 | |||
1050 | if (!drm_fb_cma_get_gem_obj(fb, 0)) { | ||
1051 | DRM_ERROR("Can't get CMA GEM object for fb\n"); | ||
1052 | return -EINVAL; | ||
1053 | } | ||
1054 | |||
1055 | /* | ||
1056 | * Input / output size | ||
1057 | * Align to upper even value | ||
1058 | */ | ||
1059 | dst_w = ALIGN(dst_w, 2); | ||
1060 | dst_h = ALIGN(dst_h, 2); | ||
1061 | |||
1062 | if ((src_w > MAX_WIDTH) || (src_w < MIN_WIDTH) || | ||
1063 | (src_h > MAX_HEIGHT) || (src_h < MIN_HEIGHT) || | ||
1064 | (dst_w > MAX_WIDTH) || (dst_w < MIN_WIDTH) || | ||
1065 | (dst_h > MAX_HEIGHT) || (dst_h < MIN_HEIGHT)) { | ||
1066 | DRM_ERROR("Invalid in/out size %dx%d -> %dx%d\n", | ||
1067 | src_w, src_h, | ||
1068 | dst_w, dst_h); | ||
1069 | return -EINVAL; | ||
1070 | } | ||
1071 | |||
1072 | if (first_prepare) { | ||
1073 | /* Start HQVDP XP70 coprocessor */ | ||
1074 | sti_hqvdp_start_xp70(hqvdp); | ||
1075 | |||
1076 | /* Prevent VTG shutdown */ | ||
1077 | if (clk_prepare_enable(hqvdp->clk_pix_main)) { | ||
1078 | DRM_ERROR("Failed to prepare/enable pix main clk\n"); | ||
1079 | return -EINVAL; | ||
1080 | } | ||
1081 | |||
1082 | /* Register VTG Vsync callback to handle bottom fields */ | ||
1083 | if (sti_vtg_register_client(hqvdp->vtg, | ||
1084 | &hqvdp->vtg_nb, | ||
1085 | crtc)) { | ||
1086 | DRM_ERROR("Cannot register VTG notifier\n"); | ||
1087 | return -EINVAL; | ||
1088 | } | ||
1089 | } | ||
802 | 1090 | ||
803 | DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n", | 1091 | DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n", |
804 | crtc->base.id, sti_mixer_to_str(mixer), | 1092 | crtc->base.id, sti_mixer_to_str(to_sti_mixer(crtc)), |
805 | drm_plane->base.id, sti_plane_to_str(plane)); | 1093 | drm_plane->base.id, sti_plane_to_str(plane)); |
806 | DRM_DEBUG_KMS("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n", | 1094 | DRM_DEBUG_KMS("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n", |
807 | sti_plane_to_str(plane), | 1095 | sti_plane_to_str(plane), |
808 | dst_w, dst_h, dst_x, dst_y, | 1096 | dst_w, dst_h, dst_x, dst_y, |
809 | src_w, src_h, src_x, src_y); | 1097 | src_w, src_h, src_x, src_y); |
810 | 1098 | ||
1099 | return 0; | ||
1100 | } | ||
1101 | |||
1102 | static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane, | ||
1103 | struct drm_plane_state *oldstate) | ||
1104 | { | ||
1105 | struct drm_plane_state *state = drm_plane->state; | ||
1106 | struct sti_plane *plane = to_sti_plane(drm_plane); | ||
1107 | struct sti_hqvdp *hqvdp = to_sti_hqvdp(plane); | ||
1108 | struct drm_crtc *crtc = state->crtc; | ||
1109 | struct drm_framebuffer *fb = state->fb; | ||
1110 | struct drm_display_mode *mode; | ||
1111 | int dst_x, dst_y, dst_w, dst_h; | ||
1112 | int src_x, src_y, src_w, src_h; | ||
1113 | struct drm_gem_cma_object *cma_obj; | ||
1114 | struct sti_hqvdp_cmd *cmd; | ||
1115 | int scale_h, scale_v; | ||
1116 | int cmd_offset; | ||
1117 | |||
1118 | if (!crtc || !fb) | ||
1119 | return; | ||
1120 | |||
1121 | mode = &crtc->mode; | ||
1122 | dst_x = state->crtc_x; | ||
1123 | dst_y = state->crtc_y; | ||
1124 | dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); | ||
1125 | dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); | ||
1126 | /* src_x are in 16.16 format */ | ||
1127 | src_x = state->src_x >> 16; | ||
1128 | src_y = state->src_y >> 16; | ||
1129 | src_w = state->src_w >> 16; | ||
1130 | src_h = state->src_h >> 16; | ||
1131 | |||
811 | cmd_offset = sti_hqvdp_get_free_cmd(hqvdp); | 1132 | cmd_offset = sti_hqvdp_get_free_cmd(hqvdp); |
812 | if (cmd_offset == -1) { | 1133 | if (cmd_offset == -1) { |
813 | DRM_ERROR("No available hqvdp_cmd now\n"); | 1134 | DRM_DEBUG_DRIVER("Warning: no cmd, will skip frame\n"); |
814 | return; | 1135 | return; |
815 | } | 1136 | } |
816 | cmd = hqvdp->hqvdp_cmd + cmd_offset; | 1137 | cmd = hqvdp->hqvdp_cmd + cmd_offset; |
817 | 1138 | ||
818 | if (!sti_hqvdp_check_hw_scaling(hqvdp, mode, | ||
819 | src_w, src_h, | ||
820 | dst_w, dst_h)) { | ||
821 | DRM_ERROR("Scaling beyond HW capabilities\n"); | ||
822 | return; | ||
823 | } | ||
824 | |||
825 | /* Static parameters, defaulting to progressive mode */ | 1139 | /* Static parameters, defaulting to progressive mode */ |
826 | cmd->top.config = TOP_CONFIG_PROGRESSIVE; | 1140 | cmd->top.config = TOP_CONFIG_PROGRESSIVE; |
827 | cmd->top.mem_format = TOP_MEM_FORMAT_DFLT; | 1141 | cmd->top.mem_format = TOP_MEM_FORMAT_DFLT; |
@@ -836,10 +1150,6 @@ static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane, | |||
836 | cmd->iqi.pxf_conf = IQI_PXF_CONF_DFLT; | 1150 | cmd->iqi.pxf_conf = IQI_PXF_CONF_DFLT; |
837 | 1151 | ||
838 | cma_obj = drm_fb_cma_get_gem_obj(fb, 0); | 1152 | cma_obj = drm_fb_cma_get_gem_obj(fb, 0); |
839 | if (!cma_obj) { | ||
840 | DRM_ERROR("Can't get CMA GEM object for fb\n"); | ||
841 | return; | ||
842 | } | ||
843 | 1153 | ||
844 | DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id, | 1154 | DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id, |
845 | (char *)&fb->pixel_format, | 1155 | (char *)&fb->pixel_format, |
@@ -860,16 +1170,6 @@ static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane, | |||
860 | dst_w = ALIGN(dst_w, 2); | 1170 | dst_w = ALIGN(dst_w, 2); |
861 | dst_h = ALIGN(dst_h, 2); | 1171 | dst_h = ALIGN(dst_h, 2); |
862 | 1172 | ||
863 | if ((src_w > MAX_WIDTH) || (src_w < MIN_WIDTH) || | ||
864 | (src_h > MAX_HEIGHT) || (src_h < MIN_HEIGHT) || | ||
865 | (dst_w > MAX_WIDTH) || (dst_w < MIN_WIDTH) || | ||
866 | (dst_h > MAX_HEIGHT) || (dst_h < MIN_HEIGHT)) { | ||
867 | DRM_ERROR("Invalid in/out size %dx%d -> %dx%d\n", | ||
868 | src_w, src_h, | ||
869 | dst_w, dst_h); | ||
870 | return; | ||
871 | } | ||
872 | |||
873 | cmd->top.input_viewport_size = src_h << 16 | src_w; | 1173 | cmd->top.input_viewport_size = src_h << 16 | src_w; |
874 | cmd->top.input_frame_size = src_h << 16 | src_w; | 1174 | cmd->top.input_frame_size = src_h << 16 | src_w; |
875 | cmd->hvsrc.output_picture_size = dst_h << 16 | dst_w; | 1175 | cmd->hvsrc.output_picture_size = dst_h << 16 | dst_w; |
@@ -900,30 +1200,9 @@ static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane, | |||
900 | scale_v = SCALE_FACTOR * dst_h / src_h; | 1200 | scale_v = SCALE_FACTOR * dst_h / src_h; |
901 | sti_hqvdp_update_hvsrc(HVSRC_VERT, scale_v, &cmd->hvsrc); | 1201 | sti_hqvdp_update_hvsrc(HVSRC_VERT, scale_v, &cmd->hvsrc); |
902 | 1202 | ||
903 | if (first_prepare) { | ||
904 | /* Start HQVDP XP70 coprocessor */ | ||
905 | sti_hqvdp_start_xp70(hqvdp); | ||
906 | |||
907 | /* Prevent VTG shutdown */ | ||
908 | if (clk_prepare_enable(hqvdp->clk_pix_main)) { | ||
909 | DRM_ERROR("Failed to prepare/enable pix main clk\n"); | ||
910 | return; | ||
911 | } | ||
912 | |||
913 | /* Register VTG Vsync callback to handle bottom fields */ | ||
914 | if (sti_vtg_register_client(hqvdp->vtg, | ||
915 | &hqvdp->vtg_nb, | ||
916 | crtc)) { | ||
917 | DRM_ERROR("Cannot register VTG notifier\n"); | ||
918 | return; | ||
919 | } | ||
920 | } | ||
921 | |||
922 | writel(hqvdp->hqvdp_cmd_paddr + cmd_offset, | 1203 | writel(hqvdp->hqvdp_cmd_paddr + cmd_offset, |
923 | hqvdp->regs + HQVDP_MBX_NEXT_CMD); | 1204 | hqvdp->regs + HQVDP_MBX_NEXT_CMD); |
924 | 1205 | ||
925 | hqvdp->curr_field_count++; | ||
926 | |||
927 | /* Interlaced : get ready to display the bottom field at next Vsync */ | 1206 | /* Interlaced : get ready to display the bottom field at next Vsync */ |
928 | if (fb->flags & DRM_MODE_FB_INTERLACED) | 1207 | if (fb->flags & DRM_MODE_FB_INTERLACED) |
929 | hqvdp->btm_field_pending = true; | 1208 | hqvdp->btm_field_pending = true; |
@@ -931,6 +1210,8 @@ static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane, | |||
931 | dev_dbg(hqvdp->dev, "%s Posted command:0x%x\n", | 1210 | dev_dbg(hqvdp->dev, "%s Posted command:0x%x\n", |
932 | __func__, hqvdp->hqvdp_cmd_paddr + cmd_offset); | 1211 | __func__, hqvdp->hqvdp_cmd_paddr + cmd_offset); |
933 | 1212 | ||
1213 | sti_plane_update_fps(plane, true, true); | ||
1214 | |||
934 | plane->status = STI_PLANE_UPDATED; | 1215 | plane->status = STI_PLANE_UPDATED; |
935 | } | 1216 | } |
936 | 1217 | ||
@@ -938,7 +1219,6 @@ static void sti_hqvdp_atomic_disable(struct drm_plane *drm_plane, | |||
938 | struct drm_plane_state *oldstate) | 1219 | struct drm_plane_state *oldstate) |
939 | { | 1220 | { |
940 | struct sti_plane *plane = to_sti_plane(drm_plane); | 1221 | struct sti_plane *plane = to_sti_plane(drm_plane); |
941 | struct sti_mixer *mixer = to_sti_mixer(drm_plane->crtc); | ||
942 | 1222 | ||
943 | if (!drm_plane->crtc) { | 1223 | if (!drm_plane->crtc) { |
944 | DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", | 1224 | DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", |
@@ -947,13 +1227,15 @@ static void sti_hqvdp_atomic_disable(struct drm_plane *drm_plane, | |||
947 | } | 1227 | } |
948 | 1228 | ||
949 | DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n", | 1229 | DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n", |
950 | drm_plane->crtc->base.id, sti_mixer_to_str(mixer), | 1230 | drm_plane->crtc->base.id, |
1231 | sti_mixer_to_str(to_sti_mixer(drm_plane->crtc)), | ||
951 | drm_plane->base.id, sti_plane_to_str(plane)); | 1232 | drm_plane->base.id, sti_plane_to_str(plane)); |
952 | 1233 | ||
953 | plane->status = STI_PLANE_DISABLING; | 1234 | plane->status = STI_PLANE_DISABLING; |
954 | } | 1235 | } |
955 | 1236 | ||
956 | static const struct drm_plane_helper_funcs sti_hqvdp_helpers_funcs = { | 1237 | static const struct drm_plane_helper_funcs sti_hqvdp_helpers_funcs = { |
1238 | .atomic_check = sti_hqvdp_atomic_check, | ||
957 | .atomic_update = sti_hqvdp_atomic_update, | 1239 | .atomic_update = sti_hqvdp_atomic_update, |
958 | .atomic_disable = sti_hqvdp_atomic_disable, | 1240 | .atomic_disable = sti_hqvdp_atomic_disable, |
959 | }; | 1241 | }; |
@@ -983,6 +1265,9 @@ static struct drm_plane *sti_hqvdp_create(struct drm_device *drm_dev, | |||
983 | 1265 | ||
984 | sti_plane_init_property(&hqvdp->plane, DRM_PLANE_TYPE_OVERLAY); | 1266 | sti_plane_init_property(&hqvdp->plane, DRM_PLANE_TYPE_OVERLAY); |
985 | 1267 | ||
1268 | if (hqvdp_debugfs_init(hqvdp, drm_dev->primary)) | ||
1269 | DRM_ERROR("HQVDP debugfs setup failed\n"); | ||
1270 | |||
986 | return &hqvdp->plane.drm_plane; | 1271 | return &hqvdp->plane.drm_plane; |
987 | } | 1272 | } |
988 | 1273 | ||
diff --git a/drivers/gpu/drm/sti/sti_mixer.c b/drivers/gpu/drm/sti/sti_mixer.c index 49db835dce03..e7425c38fc93 100644 --- a/drivers/gpu/drm/sti/sti_mixer.c +++ b/drivers/gpu/drm/sti/sti_mixer.c | |||
@@ -75,6 +75,145 @@ static inline void sti_mixer_reg_write(struct sti_mixer *mixer, | |||
75 | writel(val, mixer->regs + reg_id); | 75 | writel(val, mixer->regs + reg_id); |
76 | } | 76 | } |
77 | 77 | ||
78 | #define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \ | ||
79 | sti_mixer_reg_read(mixer, reg)) | ||
80 | |||
81 | static void mixer_dbg_ctl(struct seq_file *s, int val) | ||
82 | { | ||
83 | unsigned int i; | ||
84 | int count = 0; | ||
85 | char *const disp_layer[] = {"BKG", "VID0", "VID1", "GDP0", | ||
86 | "GDP1", "GDP2", "GDP3"}; | ||
87 | |||
88 | seq_puts(s, "\tEnabled: "); | ||
89 | for (i = 0; i < 7; i++) { | ||
90 | if (val & 1) { | ||
91 | seq_printf(s, "%s ", disp_layer[i]); | ||
92 | count++; | ||
93 | } | ||
94 | val = val >> 1; | ||
95 | } | ||
96 | |||
97 | val = val >> 2; | ||
98 | if (val & 1) { | ||
99 | seq_puts(s, "CURS "); | ||
100 | count++; | ||
101 | } | ||
102 | if (!count) | ||
103 | seq_puts(s, "Nothing"); | ||
104 | } | ||
105 | |||
106 | static void mixer_dbg_crb(struct seq_file *s, int val) | ||
107 | { | ||
108 | int i; | ||
109 | |||
110 | seq_puts(s, "\tDepth: "); | ||
111 | for (i = 0; i < GAM_MIXER_NB_DEPTH_LEVEL; i++) { | ||
112 | switch (val & GAM_DEPTH_MASK_ID) { | ||
113 | case GAM_DEPTH_VID0_ID: | ||
114 | seq_puts(s, "VID0"); | ||
115 | break; | ||
116 | case GAM_DEPTH_VID1_ID: | ||
117 | seq_puts(s, "VID1"); | ||
118 | break; | ||
119 | case GAM_DEPTH_GDP0_ID: | ||
120 | seq_puts(s, "GDP0"); | ||
121 | break; | ||
122 | case GAM_DEPTH_GDP1_ID: | ||
123 | seq_puts(s, "GDP1"); | ||
124 | break; | ||
125 | case GAM_DEPTH_GDP2_ID: | ||
126 | seq_puts(s, "GDP2"); | ||
127 | break; | ||
128 | case GAM_DEPTH_GDP3_ID: | ||
129 | seq_puts(s, "GDP3"); | ||
130 | break; | ||
131 | default: | ||
132 | seq_puts(s, "---"); | ||
133 | } | ||
134 | |||
135 | if (i < GAM_MIXER_NB_DEPTH_LEVEL - 1) | ||
136 | seq_puts(s, " < "); | ||
137 | val = val >> 3; | ||
138 | } | ||
139 | } | ||
140 | |||
141 | static void mixer_dbg_mxn(struct seq_file *s, void *addr) | ||
142 | { | ||
143 | int i; | ||
144 | |||
145 | for (i = 1; i < 8; i++) | ||
146 | seq_printf(s, "-0x%08X", (int)readl(addr + i * 4)); | ||
147 | } | ||
148 | |||
149 | static int mixer_dbg_show(struct seq_file *s, void *arg) | ||
150 | { | ||
151 | struct drm_info_node *node = s->private; | ||
152 | struct sti_mixer *mixer = (struct sti_mixer *)node->info_ent->data; | ||
153 | struct drm_device *dev = node->minor->dev; | ||
154 | int ret; | ||
155 | |||
156 | ret = mutex_lock_interruptible(&dev->struct_mutex); | ||
157 | if (ret) | ||
158 | return ret; | ||
159 | |||
160 | seq_printf(s, "%s: (vaddr = 0x%p)", | ||
161 | sti_mixer_to_str(mixer), mixer->regs); | ||
162 | |||
163 | DBGFS_DUMP(GAM_MIXER_CTL); | ||
164 | mixer_dbg_ctl(s, sti_mixer_reg_read(mixer, GAM_MIXER_CTL)); | ||
165 | DBGFS_DUMP(GAM_MIXER_BKC); | ||
166 | DBGFS_DUMP(GAM_MIXER_BCO); | ||
167 | DBGFS_DUMP(GAM_MIXER_BCS); | ||
168 | DBGFS_DUMP(GAM_MIXER_AVO); | ||
169 | DBGFS_DUMP(GAM_MIXER_AVS); | ||
170 | DBGFS_DUMP(GAM_MIXER_CRB); | ||
171 | mixer_dbg_crb(s, sti_mixer_reg_read(mixer, GAM_MIXER_CRB)); | ||
172 | DBGFS_DUMP(GAM_MIXER_ACT); | ||
173 | DBGFS_DUMP(GAM_MIXER_MBP); | ||
174 | DBGFS_DUMP(GAM_MIXER_MX0); | ||
175 | mixer_dbg_mxn(s, mixer->regs + GAM_MIXER_MX0); | ||
176 | seq_puts(s, "\n"); | ||
177 | |||
178 | mutex_unlock(&dev->struct_mutex); | ||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | static struct drm_info_list mixer0_debugfs_files[] = { | ||
183 | { "mixer_main", mixer_dbg_show, 0, NULL }, | ||
184 | }; | ||
185 | |||
186 | static struct drm_info_list mixer1_debugfs_files[] = { | ||
187 | { "mixer_aux", mixer_dbg_show, 0, NULL }, | ||
188 | }; | ||
189 | |||
190 | static int mixer_debugfs_init(struct sti_mixer *mixer, struct drm_minor *minor) | ||
191 | { | ||
192 | unsigned int i; | ||
193 | struct drm_info_list *mixer_debugfs_files; | ||
194 | int nb_files; | ||
195 | |||
196 | switch (mixer->id) { | ||
197 | case STI_MIXER_MAIN: | ||
198 | mixer_debugfs_files = mixer0_debugfs_files; | ||
199 | nb_files = ARRAY_SIZE(mixer0_debugfs_files); | ||
200 | break; | ||
201 | case STI_MIXER_AUX: | ||
202 | mixer_debugfs_files = mixer1_debugfs_files; | ||
203 | nb_files = ARRAY_SIZE(mixer1_debugfs_files); | ||
204 | break; | ||
205 | default: | ||
206 | return -EINVAL; | ||
207 | } | ||
208 | |||
209 | for (i = 0; i < nb_files; i++) | ||
210 | mixer_debugfs_files[i].data = mixer; | ||
211 | |||
212 | return drm_debugfs_create_files(mixer_debugfs_files, | ||
213 | nb_files, | ||
214 | minor->debugfs_root, minor); | ||
215 | } | ||
216 | |||
78 | void sti_mixer_set_background_status(struct sti_mixer *mixer, bool enable) | 217 | void sti_mixer_set_background_status(struct sti_mixer *mixer, bool enable) |
79 | { | 218 | { |
80 | u32 val = sti_mixer_reg_read(mixer, GAM_MIXER_CTL); | 219 | u32 val = sti_mixer_reg_read(mixer, GAM_MIXER_CTL); |
@@ -237,7 +376,9 @@ void sti_mixer_set_matrix(struct sti_mixer *mixer) | |||
237 | mixerColorSpaceMatIdentity[i]); | 376 | mixerColorSpaceMatIdentity[i]); |
238 | } | 377 | } |
239 | 378 | ||
240 | struct sti_mixer *sti_mixer_create(struct device *dev, int id, | 379 | struct sti_mixer *sti_mixer_create(struct device *dev, |
380 | struct drm_device *drm_dev, | ||
381 | int id, | ||
241 | void __iomem *baseaddr) | 382 | void __iomem *baseaddr) |
242 | { | 383 | { |
243 | struct sti_mixer *mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL); | 384 | struct sti_mixer *mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL); |
@@ -258,5 +399,8 @@ struct sti_mixer *sti_mixer_create(struct device *dev, int id, | |||
258 | DRM_DEBUG_DRIVER("%s created. Regs=%p\n", | 399 | DRM_DEBUG_DRIVER("%s created. Regs=%p\n", |
259 | sti_mixer_to_str(mixer), mixer->regs); | 400 | sti_mixer_to_str(mixer), mixer->regs); |
260 | 401 | ||
402 | if (mixer_debugfs_init(mixer, drm_dev->primary)) | ||
403 | DRM_ERROR("MIXER debugfs setup failed\n"); | ||
404 | |||
261 | return mixer; | 405 | return mixer; |
262 | } | 406 | } |
diff --git a/drivers/gpu/drm/sti/sti_mixer.h b/drivers/gpu/drm/sti/sti_mixer.h index efb1a9a5ba86..6f35fc086873 100644 --- a/drivers/gpu/drm/sti/sti_mixer.h +++ b/drivers/gpu/drm/sti/sti_mixer.h | |||
@@ -42,7 +42,9 @@ struct sti_mixer { | |||
42 | 42 | ||
43 | const char *sti_mixer_to_str(struct sti_mixer *mixer); | 43 | const char *sti_mixer_to_str(struct sti_mixer *mixer); |
44 | 44 | ||
45 | struct sti_mixer *sti_mixer_create(struct device *dev, int id, | 45 | struct sti_mixer *sti_mixer_create(struct device *dev, |
46 | struct drm_device *drm_dev, | ||
47 | int id, | ||
46 | void __iomem *baseaddr); | 48 | void __iomem *baseaddr); |
47 | 49 | ||
48 | int sti_mixer_set_plane_status(struct sti_mixer *mixer, | 50 | int sti_mixer_set_plane_status(struct sti_mixer *mixer, |
diff --git a/drivers/gpu/drm/sti/sti_plane.c b/drivers/gpu/drm/sti/sti_plane.c index 2e5c751910c5..f10c98d3f012 100644 --- a/drivers/gpu/drm/sti/sti_plane.c +++ b/drivers/gpu/drm/sti/sti_plane.c | |||
@@ -43,6 +43,69 @@ const char *sti_plane_to_str(struct sti_plane *plane) | |||
43 | } | 43 | } |
44 | } | 44 | } |
45 | 45 | ||
46 | #define STI_FPS_INTERVAL_MS 3000 | ||
47 | |||
48 | static int sti_plane_timespec_ms_diff(struct timespec lhs, struct timespec rhs) | ||
49 | { | ||
50 | struct timespec tmp_ts = timespec_sub(lhs, rhs); | ||
51 | u64 tmp_ns = (u64)timespec_to_ns(&tmp_ts); | ||
52 | |||
53 | do_div(tmp_ns, NSEC_PER_MSEC); | ||
54 | |||
55 | return (u32)tmp_ns; | ||
56 | } | ||
57 | |||
58 | void sti_plane_update_fps(struct sti_plane *plane, | ||
59 | bool new_frame, | ||
60 | bool new_field) | ||
61 | { | ||
62 | struct timespec now; | ||
63 | struct sti_fps_info *fps; | ||
64 | int fpks, fipks, ms_since_last, num_frames, num_fields; | ||
65 | |||
66 | getrawmonotonic(&now); | ||
67 | |||
68 | /* Compute number of frame updates */ | ||
69 | fps = &plane->fps_info; | ||
70 | |||
71 | if (new_field) | ||
72 | fps->curr_field_counter++; | ||
73 | |||
74 | /* do not perform fps calcul if new_frame is false */ | ||
75 | if (!new_frame) | ||
76 | return; | ||
77 | |||
78 | fps->curr_frame_counter++; | ||
79 | ms_since_last = sti_plane_timespec_ms_diff(now, fps->last_timestamp); | ||
80 | num_frames = fps->curr_frame_counter - fps->last_frame_counter; | ||
81 | |||
82 | if (num_frames <= 0 || ms_since_last < STI_FPS_INTERVAL_MS) | ||
83 | return; | ||
84 | |||
85 | fps->last_timestamp = now; | ||
86 | fps->last_frame_counter = fps->curr_frame_counter; | ||
87 | fpks = (num_frames * 1000000) / ms_since_last; | ||
88 | snprintf(plane->fps_info.fps_str, FPS_LENGTH, "%-6s @ %d.%.3d fps", | ||
89 | sti_plane_to_str(plane), fpks / 1000, fpks % 1000); | ||
90 | |||
91 | if (fps->curr_field_counter) { | ||
92 | /* Compute number of field updates */ | ||
93 | num_fields = fps->curr_field_counter - fps->last_field_counter; | ||
94 | fps->last_field_counter = fps->curr_field_counter; | ||
95 | fipks = (num_fields * 1000000) / ms_since_last; | ||
96 | snprintf(plane->fps_info.fips_str, | ||
97 | FPS_LENGTH, " - %d.%.3d field/sec", | ||
98 | fipks / 1000, fipks % 1000); | ||
99 | } else { | ||
100 | plane->fps_info.fips_str[0] = '\0'; | ||
101 | } | ||
102 | |||
103 | if (fps->output) | ||
104 | DRM_INFO("%s%s\n", | ||
105 | plane->fps_info.fps_str, | ||
106 | plane->fps_info.fips_str); | ||
107 | } | ||
108 | |||
46 | static void sti_plane_destroy(struct drm_plane *drm_plane) | 109 | static void sti_plane_destroy(struct drm_plane *drm_plane) |
47 | { | 110 | { |
48 | DRM_DEBUG_DRIVER("\n"); | 111 | DRM_DEBUG_DRIVER("\n"); |
diff --git a/drivers/gpu/drm/sti/sti_plane.h b/drivers/gpu/drm/sti/sti_plane.h index 86f1e6fc81b9..c50a3b9f5d37 100644 --- a/drivers/gpu/drm/sti/sti_plane.h +++ b/drivers/gpu/drm/sti/sti_plane.h | |||
@@ -50,6 +50,18 @@ enum sti_plane_status { | |||
50 | STI_PLANE_DISABLED, | 50 | STI_PLANE_DISABLED, |
51 | }; | 51 | }; |
52 | 52 | ||
53 | #define FPS_LENGTH 64 | ||
54 | struct sti_fps_info { | ||
55 | bool output; | ||
56 | unsigned int curr_frame_counter; | ||
57 | unsigned int last_frame_counter; | ||
58 | unsigned int curr_field_counter; | ||
59 | unsigned int last_field_counter; | ||
60 | struct timespec last_timestamp; | ||
61 | char fps_str[FPS_LENGTH]; | ||
62 | char fips_str[FPS_LENGTH]; | ||
63 | }; | ||
64 | |||
53 | /** | 65 | /** |
54 | * STI plane structure | 66 | * STI plane structure |
55 | * | 67 | * |
@@ -57,15 +69,20 @@ enum sti_plane_status { | |||
57 | * @desc: plane type & id | 69 | * @desc: plane type & id |
58 | * @status: to know the status of the plane | 70 | * @status: to know the status of the plane |
59 | * @zorder: plane z-order | 71 | * @zorder: plane z-order |
72 | * @fps_info: frame per second info | ||
60 | */ | 73 | */ |
61 | struct sti_plane { | 74 | struct sti_plane { |
62 | struct drm_plane drm_plane; | 75 | struct drm_plane drm_plane; |
63 | enum sti_plane_desc desc; | 76 | enum sti_plane_desc desc; |
64 | enum sti_plane_status status; | 77 | enum sti_plane_status status; |
65 | int zorder; | 78 | int zorder; |
79 | struct sti_fps_info fps_info; | ||
66 | }; | 80 | }; |
67 | 81 | ||
68 | const char *sti_plane_to_str(struct sti_plane *plane); | 82 | const char *sti_plane_to_str(struct sti_plane *plane); |
83 | void sti_plane_update_fps(struct sti_plane *plane, | ||
84 | bool new_frame, | ||
85 | bool new_field); | ||
69 | void sti_plane_init_property(struct sti_plane *plane, | 86 | void sti_plane_init_property(struct sti_plane *plane, |
70 | enum drm_plane_type type); | 87 | enum drm_plane_type type); |
71 | #endif | 88 | #endif |
diff --git a/drivers/gpu/drm/sti/sti_tvout.c b/drivers/gpu/drm/sti/sti_tvout.c index 24a3735b88fd..2c99016443e5 100644 --- a/drivers/gpu/drm/sti/sti_tvout.c +++ b/drivers/gpu/drm/sti/sti_tvout.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <drm/drm_crtc_helper.h> | 17 | #include <drm/drm_crtc_helper.h> |
18 | 18 | ||
19 | #include "sti_crtc.h" | 19 | #include "sti_crtc.h" |
20 | #include "sti_vtg.h" | ||
20 | 21 | ||
21 | /* glue registers */ | 22 | /* glue registers */ |
22 | #define TVO_CSC_MAIN_M0 0x000 | 23 | #define TVO_CSC_MAIN_M0 0x000 |
@@ -85,19 +86,7 @@ | |||
85 | #define TVO_VIP_SEL_INPUT_BYPASSED 1 | 86 | #define TVO_VIP_SEL_INPUT_BYPASSED 1 |
86 | 87 | ||
87 | #define TVO_SYNC_MAIN_VTG_SET_REF 0x00 | 88 | #define TVO_SYNC_MAIN_VTG_SET_REF 0x00 |
88 | #define TVO_SYNC_MAIN_VTG_SET_1 0x01 | ||
89 | #define TVO_SYNC_MAIN_VTG_SET_2 0x02 | ||
90 | #define TVO_SYNC_MAIN_VTG_SET_3 0x03 | ||
91 | #define TVO_SYNC_MAIN_VTG_SET_4 0x04 | ||
92 | #define TVO_SYNC_MAIN_VTG_SET_5 0x05 | ||
93 | #define TVO_SYNC_MAIN_VTG_SET_6 0x06 | ||
94 | #define TVO_SYNC_AUX_VTG_SET_REF 0x10 | 89 | #define TVO_SYNC_AUX_VTG_SET_REF 0x10 |
95 | #define TVO_SYNC_AUX_VTG_SET_1 0x11 | ||
96 | #define TVO_SYNC_AUX_VTG_SET_2 0x12 | ||
97 | #define TVO_SYNC_AUX_VTG_SET_3 0x13 | ||
98 | #define TVO_SYNC_AUX_VTG_SET_4 0x14 | ||
99 | #define TVO_SYNC_AUX_VTG_SET_5 0x15 | ||
100 | #define TVO_SYNC_AUX_VTG_SET_6 0x16 | ||
101 | 90 | ||
102 | #define TVO_SYNC_HD_DCS_SHIFT 8 | 91 | #define TVO_SYNC_HD_DCS_SHIFT 8 |
103 | 92 | ||
@@ -106,6 +95,8 @@ | |||
106 | 95 | ||
107 | #define ENCODER_CRTC_MASK (BIT(0) | BIT(1)) | 96 | #define ENCODER_CRTC_MASK (BIT(0) | BIT(1)) |
108 | 97 | ||
98 | #define TVO_MIN_HD_HEIGHT 720 | ||
99 | |||
109 | /* enum listing the supported output data format */ | 100 | /* enum listing the supported output data format */ |
110 | enum sti_tvout_video_out_type { | 101 | enum sti_tvout_video_out_type { |
111 | STI_TVOUT_VIDEO_OUT_RGB, | 102 | STI_TVOUT_VIDEO_OUT_RGB, |
@@ -269,6 +260,31 @@ static void tvout_vip_set_in_vid_fmt(struct sti_tvout *tvout, | |||
269 | } | 260 | } |
270 | 261 | ||
271 | /** | 262 | /** |
263 | * Set preformatter matrix | ||
264 | * | ||
265 | * @tvout: tvout structure | ||
266 | * @mode: display mode structure | ||
267 | */ | ||
268 | static void tvout_preformatter_set_matrix(struct sti_tvout *tvout, | ||
269 | struct drm_display_mode *mode) | ||
270 | { | ||
271 | unsigned int i; | ||
272 | const u32 *pf_matrix; | ||
273 | |||
274 | if (mode->vdisplay >= TVO_MIN_HD_HEIGHT) | ||
275 | pf_matrix = rgb_to_ycbcr_709; | ||
276 | else | ||
277 | pf_matrix = rgb_to_ycbcr_601; | ||
278 | |||
279 | for (i = 0; i < 8; i++) { | ||
280 | tvout_write(tvout, *(pf_matrix + i), | ||
281 | TVO_CSC_MAIN_M0 + (i * 4)); | ||
282 | tvout_write(tvout, *(pf_matrix + i), | ||
283 | TVO_CSC_AUX_M0 + (i * 4)); | ||
284 | } | ||
285 | } | ||
286 | |||
287 | /** | ||
272 | * Start VIP block for DVO output | 288 | * Start VIP block for DVO output |
273 | * | 289 | * |
274 | * @tvout: pointer on tvout structure | 290 | * @tvout: pointer on tvout structure |
@@ -280,24 +296,26 @@ static void tvout_dvo_start(struct sti_tvout *tvout, bool main_path) | |||
280 | struct device_node *node = tvout->dev->of_node; | 296 | struct device_node *node = tvout->dev->of_node; |
281 | bool sel_input_logic_inverted = false; | 297 | bool sel_input_logic_inverted = false; |
282 | u32 tvo_in_vid_format; | 298 | u32 tvo_in_vid_format; |
283 | int val; | 299 | int val, tmp; |
284 | 300 | ||
285 | dev_dbg(tvout->dev, "%s\n", __func__); | 301 | dev_dbg(tvout->dev, "%s\n", __func__); |
286 | 302 | ||
287 | if (main_path) { | 303 | if (main_path) { |
288 | DRM_DEBUG_DRIVER("main vip for DVO\n"); | 304 | DRM_DEBUG_DRIVER("main vip for DVO\n"); |
289 | /* Select the input sync for dvo = VTG set 4 */ | 305 | /* Select the input sync for dvo */ |
290 | val = TVO_SYNC_MAIN_VTG_SET_4 << TVO_SYNC_DVO_PAD_VSYNC_SHIFT; | 306 | tmp = TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_DVO; |
291 | val |= TVO_SYNC_MAIN_VTG_SET_4 << TVO_SYNC_DVO_PAD_HSYNC_SHIFT; | 307 | val = tmp << TVO_SYNC_DVO_PAD_VSYNC_SHIFT; |
292 | val |= TVO_SYNC_MAIN_VTG_SET_4; | 308 | val |= tmp << TVO_SYNC_DVO_PAD_HSYNC_SHIFT; |
309 | val |= tmp; | ||
293 | tvout_write(tvout, val, TVO_DVO_SYNC_SEL); | 310 | tvout_write(tvout, val, TVO_DVO_SYNC_SEL); |
294 | tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; | 311 | tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; |
295 | } else { | 312 | } else { |
296 | DRM_DEBUG_DRIVER("aux vip for DVO\n"); | 313 | DRM_DEBUG_DRIVER("aux vip for DVO\n"); |
297 | /* Select the input sync for dvo = VTG set 4 */ | 314 | /* Select the input sync for dvo */ |
298 | val = TVO_SYNC_AUX_VTG_SET_4 << TVO_SYNC_DVO_PAD_VSYNC_SHIFT; | 315 | tmp = TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_DVO; |
299 | val |= TVO_SYNC_AUX_VTG_SET_4 << TVO_SYNC_DVO_PAD_HSYNC_SHIFT; | 316 | val = tmp << TVO_SYNC_DVO_PAD_VSYNC_SHIFT; |
300 | val |= TVO_SYNC_AUX_VTG_SET_4; | 317 | val |= tmp << TVO_SYNC_DVO_PAD_HSYNC_SHIFT; |
318 | val |= tmp; | ||
301 | tvout_write(tvout, val, TVO_DVO_SYNC_SEL); | 319 | tvout_write(tvout, val, TVO_DVO_SYNC_SEL); |
302 | tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; | 320 | tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; |
303 | } | 321 | } |
@@ -308,9 +326,8 @@ static void tvout_dvo_start(struct sti_tvout *tvout, bool main_path) | |||
308 | TVO_VIP_REORDER_Y_G_SEL, | 326 | TVO_VIP_REORDER_Y_G_SEL, |
309 | TVO_VIP_REORDER_CB_B_SEL); | 327 | TVO_VIP_REORDER_CB_B_SEL); |
310 | 328 | ||
311 | /* Set clipping mode (Limited range RGB/Y) */ | 329 | /* Set clipping mode */ |
312 | tvout_vip_set_clip_mode(tvout, TVO_VIP_DVO, | 330 | tvout_vip_set_clip_mode(tvout, TVO_VIP_DVO, TVO_VIP_CLIP_DISABLED); |
313 | TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y); | ||
314 | 331 | ||
315 | /* Set round mode (rounded to 8-bit per component) */ | 332 | /* Set round mode (rounded to 8-bit per component) */ |
316 | tvout_vip_set_rnd(tvout, TVO_VIP_DVO, TVO_VIP_RND_8BIT_ROUNDED); | 333 | tvout_vip_set_rnd(tvout, TVO_VIP_DVO, TVO_VIP_RND_8BIT_ROUNDED); |
@@ -345,13 +362,17 @@ static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path) | |||
345 | 362 | ||
346 | if (main_path) { | 363 | if (main_path) { |
347 | DRM_DEBUG_DRIVER("main vip for hdmi\n"); | 364 | DRM_DEBUG_DRIVER("main vip for hdmi\n"); |
348 | /* select the input sync for hdmi = VTG set 1 */ | 365 | /* select the input sync for hdmi */ |
349 | tvout_write(tvout, TVO_SYNC_MAIN_VTG_SET_1, TVO_HDMI_SYNC_SEL); | 366 | tvout_write(tvout, |
367 | TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDMI, | ||
368 | TVO_HDMI_SYNC_SEL); | ||
350 | tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; | 369 | tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; |
351 | } else { | 370 | } else { |
352 | DRM_DEBUG_DRIVER("aux vip for hdmi\n"); | 371 | DRM_DEBUG_DRIVER("aux vip for hdmi\n"); |
353 | /* select the input sync for hdmi = VTG set 1 */ | 372 | /* select the input sync for hdmi */ |
354 | tvout_write(tvout, TVO_SYNC_AUX_VTG_SET_1, TVO_HDMI_SYNC_SEL); | 373 | tvout_write(tvout, |
374 | TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDMI, | ||
375 | TVO_HDMI_SYNC_SEL); | ||
355 | tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; | 376 | tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; |
356 | } | 377 | } |
357 | 378 | ||
@@ -361,9 +382,8 @@ static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path) | |||
361 | TVO_VIP_REORDER_Y_G_SEL, | 382 | TVO_VIP_REORDER_Y_G_SEL, |
362 | TVO_VIP_REORDER_CB_B_SEL); | 383 | TVO_VIP_REORDER_CB_B_SEL); |
363 | 384 | ||
364 | /* set clipping mode (Limited range RGB/Y) */ | 385 | /* set clipping mode */ |
365 | tvout_vip_set_clip_mode(tvout, TVO_VIP_HDMI, | 386 | tvout_vip_set_clip_mode(tvout, TVO_VIP_HDMI, TVO_VIP_CLIP_DISABLED); |
366 | TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y); | ||
367 | 387 | ||
368 | /* set round mode (rounded to 8-bit per component) */ | 388 | /* set round mode (rounded to 8-bit per component) */ |
369 | tvout_vip_set_rnd(tvout, TVO_VIP_HDMI, TVO_VIP_RND_8BIT_ROUNDED); | 389 | tvout_vip_set_rnd(tvout, TVO_VIP_HDMI, TVO_VIP_RND_8BIT_ROUNDED); |
@@ -397,13 +417,19 @@ static void tvout_hda_start(struct sti_tvout *tvout, bool main_path) | |||
397 | dev_dbg(tvout->dev, "%s\n", __func__); | 417 | dev_dbg(tvout->dev, "%s\n", __func__); |
398 | 418 | ||
399 | if (main_path) { | 419 | if (main_path) { |
400 | val = TVO_SYNC_MAIN_VTG_SET_2 << TVO_SYNC_HD_DCS_SHIFT; | 420 | DRM_DEBUG_DRIVER("main vip for HDF\n"); |
401 | val |= TVO_SYNC_MAIN_VTG_SET_3; | 421 | /* Select the input sync for HD analog and HD DCS */ |
422 | val = TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDDCS; | ||
423 | val = val << TVO_SYNC_HD_DCS_SHIFT; | ||
424 | val |= TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDF; | ||
402 | tvout_write(tvout, val, TVO_HD_SYNC_SEL); | 425 | tvout_write(tvout, val, TVO_HD_SYNC_SEL); |
403 | tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; | 426 | tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; |
404 | } else { | 427 | } else { |
405 | val = TVO_SYNC_AUX_VTG_SET_2 << TVO_SYNC_HD_DCS_SHIFT; | 428 | DRM_DEBUG_DRIVER("aux vip for HDF\n"); |
406 | val |= TVO_SYNC_AUX_VTG_SET_3; | 429 | /* Select the input sync for HD analog and HD DCS */ |
430 | val = TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDDCS; | ||
431 | val = val << TVO_SYNC_HD_DCS_SHIFT; | ||
432 | val |= TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDF; | ||
407 | tvout_write(tvout, val, TVO_HD_SYNC_SEL); | 433 | tvout_write(tvout, val, TVO_HD_SYNC_SEL); |
408 | tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; | 434 | tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; |
409 | } | 435 | } |
@@ -414,8 +440,8 @@ static void tvout_hda_start(struct sti_tvout *tvout, bool main_path) | |||
414 | TVO_VIP_REORDER_Y_G_SEL, | 440 | TVO_VIP_REORDER_Y_G_SEL, |
415 | TVO_VIP_REORDER_CB_B_SEL); | 441 | TVO_VIP_REORDER_CB_B_SEL); |
416 | 442 | ||
417 | /* set clipping mode (EAV/SAV clipping) */ | 443 | /* set clipping mode */ |
418 | tvout_vip_set_clip_mode(tvout, TVO_VIP_HDF, TVO_VIP_CLIP_EAV_SAV); | 444 | tvout_vip_set_clip_mode(tvout, TVO_VIP_HDF, TVO_VIP_CLIP_DISABLED); |
419 | 445 | ||
420 | /* set round mode (rounded to 10-bit per component) */ | 446 | /* set round mode (rounded to 10-bit per component) */ |
421 | tvout_vip_set_rnd(tvout, TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED); | 447 | tvout_vip_set_rnd(tvout, TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED); |
@@ -436,6 +462,157 @@ static void tvout_hda_start(struct sti_tvout *tvout, bool main_path) | |||
436 | tvout_write(tvout, 0, TVO_HD_DAC_CFG_OFF); | 462 | tvout_write(tvout, 0, TVO_HD_DAC_CFG_OFF); |
437 | } | 463 | } |
438 | 464 | ||
465 | #define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \ | ||
466 | readl(tvout->regs + reg)) | ||
467 | |||
468 | static void tvout_dbg_vip(struct seq_file *s, int val) | ||
469 | { | ||
470 | int r, g, b, tmp, mask; | ||
471 | char *const reorder[] = {"Y_G", "Cb_B", "Cr_R"}; | ||
472 | char *const clipping[] = {"No", "EAV/SAV", "Limited range RGB/Y", | ||
473 | "Limited range Cb/Cr", "decided by register"}; | ||
474 | char *const round[] = {"8-bit", "10-bit", "12-bit"}; | ||
475 | char *const input_sel[] = {"Main (color matrix enabled)", | ||
476 | "Main (color matrix by-passed)", | ||
477 | "", "", "", "", "", "", | ||
478 | "Aux (color matrix enabled)", | ||
479 | "Aux (color matrix by-passed)", | ||
480 | "", "", "", "", "", "Force value"}; | ||
481 | |||
482 | seq_puts(s, "\t"); | ||
483 | mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT; | ||
484 | r = (val & mask) >> TVO_VIP_REORDER_R_SHIFT; | ||
485 | mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT; | ||
486 | g = (val & mask) >> TVO_VIP_REORDER_G_SHIFT; | ||
487 | mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_B_SHIFT; | ||
488 | b = (val & mask) >> TVO_VIP_REORDER_B_SHIFT; | ||
489 | seq_printf(s, "%-24s %s->%s %s->%s %s->%s\n", "Reorder:", | ||
490 | reorder[r], reorder[TVO_VIP_REORDER_CR_R_SEL], | ||
491 | reorder[g], reorder[TVO_VIP_REORDER_Y_G_SEL], | ||
492 | reorder[b], reorder[TVO_VIP_REORDER_CB_B_SEL]); | ||
493 | seq_puts(s, "\t\t\t\t\t"); | ||
494 | mask = TVO_VIP_CLIP_MASK << TVO_VIP_CLIP_SHIFT; | ||
495 | tmp = (val & mask) >> TVO_VIP_CLIP_SHIFT; | ||
496 | seq_printf(s, "%-24s %s\n", "Clipping:", clipping[tmp]); | ||
497 | seq_puts(s, "\t\t\t\t\t"); | ||
498 | mask = TVO_VIP_RND_MASK << TVO_VIP_RND_SHIFT; | ||
499 | tmp = (val & mask) >> TVO_VIP_RND_SHIFT; | ||
500 | seq_printf(s, "%-24s input data rounded to %s per component\n", | ||
501 | "Round:", round[tmp]); | ||
502 | seq_puts(s, "\t\t\t\t\t"); | ||
503 | tmp = (val & TVO_VIP_SEL_INPUT_MASK); | ||
504 | seq_printf(s, "%-24s %s", "Input selection:", input_sel[tmp]); | ||
505 | } | ||
506 | |||
507 | static void tvout_dbg_hd_dac_cfg(struct seq_file *s, int val) | ||
508 | { | ||
509 | seq_printf(s, "\t%-24s %s", "HD DAC:", | ||
510 | val & 1 ? "disabled" : "enabled"); | ||
511 | } | ||
512 | |||
513 | static int tvout_dbg_show(struct seq_file *s, void *data) | ||
514 | { | ||
515 | struct drm_info_node *node = s->private; | ||
516 | struct sti_tvout *tvout = (struct sti_tvout *)node->info_ent->data; | ||
517 | struct drm_device *dev = node->minor->dev; | ||
518 | struct drm_crtc *crtc; | ||
519 | int ret; | ||
520 | |||
521 | ret = mutex_lock_interruptible(&dev->struct_mutex); | ||
522 | if (ret) | ||
523 | return ret; | ||
524 | |||
525 | seq_printf(s, "TVOUT: (vaddr = 0x%p)", tvout->regs); | ||
526 | |||
527 | seq_puts(s, "\n\n HDMI encoder: "); | ||
528 | crtc = tvout->hdmi->crtc; | ||
529 | if (crtc) { | ||
530 | seq_printf(s, "connected to %s path", | ||
531 | sti_crtc_is_main(crtc) ? "main" : "aux"); | ||
532 | DBGFS_DUMP(TVO_HDMI_SYNC_SEL); | ||
533 | DBGFS_DUMP(TVO_VIP_HDMI); | ||
534 | tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_HDMI)); | ||
535 | } else { | ||
536 | seq_puts(s, "disabled"); | ||
537 | } | ||
538 | |||
539 | seq_puts(s, "\n\n DVO encoder: "); | ||
540 | crtc = tvout->dvo->crtc; | ||
541 | if (crtc) { | ||
542 | seq_printf(s, "connected to %s path", | ||
543 | sti_crtc_is_main(crtc) ? "main" : "aux"); | ||
544 | DBGFS_DUMP(TVO_DVO_SYNC_SEL); | ||
545 | DBGFS_DUMP(TVO_DVO_CONFIG); | ||
546 | DBGFS_DUMP(TVO_VIP_DVO); | ||
547 | tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_DVO)); | ||
548 | } else { | ||
549 | seq_puts(s, "disabled"); | ||
550 | } | ||
551 | |||
552 | seq_puts(s, "\n\n HDA encoder: "); | ||
553 | crtc = tvout->hda->crtc; | ||
554 | if (crtc) { | ||
555 | seq_printf(s, "connected to %s path", | ||
556 | sti_crtc_is_main(crtc) ? "main" : "aux"); | ||
557 | DBGFS_DUMP(TVO_HD_SYNC_SEL); | ||
558 | DBGFS_DUMP(TVO_HD_DAC_CFG_OFF); | ||
559 | tvout_dbg_hd_dac_cfg(s, | ||
560 | readl(tvout->regs + TVO_HD_DAC_CFG_OFF)); | ||
561 | DBGFS_DUMP(TVO_VIP_HDF); | ||
562 | tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_HDF)); | ||
563 | } else { | ||
564 | seq_puts(s, "disabled"); | ||
565 | } | ||
566 | |||
567 | seq_puts(s, "\n\n main path configuration"); | ||
568 | DBGFS_DUMP(TVO_CSC_MAIN_M0); | ||
569 | DBGFS_DUMP(TVO_CSC_MAIN_M1); | ||
570 | DBGFS_DUMP(TVO_CSC_MAIN_M2); | ||
571 | DBGFS_DUMP(TVO_CSC_MAIN_M3); | ||
572 | DBGFS_DUMP(TVO_CSC_MAIN_M4); | ||
573 | DBGFS_DUMP(TVO_CSC_MAIN_M5); | ||
574 | DBGFS_DUMP(TVO_CSC_MAIN_M6); | ||
575 | DBGFS_DUMP(TVO_CSC_MAIN_M7); | ||
576 | DBGFS_DUMP(TVO_MAIN_IN_VID_FORMAT); | ||
577 | |||
578 | seq_puts(s, "\n\n auxiliary path configuration"); | ||
579 | DBGFS_DUMP(TVO_CSC_AUX_M0); | ||
580 | DBGFS_DUMP(TVO_CSC_AUX_M2); | ||
581 | DBGFS_DUMP(TVO_CSC_AUX_M3); | ||
582 | DBGFS_DUMP(TVO_CSC_AUX_M4); | ||
583 | DBGFS_DUMP(TVO_CSC_AUX_M5); | ||
584 | DBGFS_DUMP(TVO_CSC_AUX_M6); | ||
585 | DBGFS_DUMP(TVO_CSC_AUX_M7); | ||
586 | DBGFS_DUMP(TVO_AUX_IN_VID_FORMAT); | ||
587 | seq_puts(s, "\n"); | ||
588 | |||
589 | mutex_unlock(&dev->struct_mutex); | ||
590 | return 0; | ||
591 | } | ||
592 | |||
593 | static struct drm_info_list tvout_debugfs_files[] = { | ||
594 | { "tvout", tvout_dbg_show, 0, NULL }, | ||
595 | }; | ||
596 | |||
597 | static void tvout_debugfs_exit(struct sti_tvout *tvout, struct drm_minor *minor) | ||
598 | { | ||
599 | drm_debugfs_remove_files(tvout_debugfs_files, | ||
600 | ARRAY_SIZE(tvout_debugfs_files), | ||
601 | minor); | ||
602 | } | ||
603 | |||
604 | static int tvout_debugfs_init(struct sti_tvout *tvout, struct drm_minor *minor) | ||
605 | { | ||
606 | unsigned int i; | ||
607 | |||
608 | for (i = 0; i < ARRAY_SIZE(tvout_debugfs_files); i++) | ||
609 | tvout_debugfs_files[i].data = tvout; | ||
610 | |||
611 | return drm_debugfs_create_files(tvout_debugfs_files, | ||
612 | ARRAY_SIZE(tvout_debugfs_files), | ||
613 | minor->debugfs_root, minor); | ||
614 | } | ||
615 | |||
439 | static void sti_tvout_encoder_dpms(struct drm_encoder *encoder, int mode) | 616 | static void sti_tvout_encoder_dpms(struct drm_encoder *encoder, int mode) |
440 | { | 617 | { |
441 | } | 618 | } |
@@ -446,10 +623,6 @@ static void sti_tvout_encoder_mode_set(struct drm_encoder *encoder, | |||
446 | { | 623 | { |
447 | } | 624 | } |
448 | 625 | ||
449 | static void sti_tvout_encoder_prepare(struct drm_encoder *encoder) | ||
450 | { | ||
451 | } | ||
452 | |||
453 | static void sti_tvout_encoder_destroy(struct drm_encoder *encoder) | 626 | static void sti_tvout_encoder_destroy(struct drm_encoder *encoder) |
454 | { | 627 | { |
455 | struct sti_tvout_encoder *sti_encoder = to_sti_tvout_encoder(encoder); | 628 | struct sti_tvout_encoder *sti_encoder = to_sti_tvout_encoder(encoder); |
@@ -462,10 +635,12 @@ static const struct drm_encoder_funcs sti_tvout_encoder_funcs = { | |||
462 | .destroy = sti_tvout_encoder_destroy, | 635 | .destroy = sti_tvout_encoder_destroy, |
463 | }; | 636 | }; |
464 | 637 | ||
465 | static void sti_dvo_encoder_commit(struct drm_encoder *encoder) | 638 | static void sti_dvo_encoder_enable(struct drm_encoder *encoder) |
466 | { | 639 | { |
467 | struct sti_tvout *tvout = to_sti_tvout(encoder); | 640 | struct sti_tvout *tvout = to_sti_tvout(encoder); |
468 | 641 | ||
642 | tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode); | ||
643 | |||
469 | tvout_dvo_start(tvout, sti_crtc_is_main(encoder->crtc)); | 644 | tvout_dvo_start(tvout, sti_crtc_is_main(encoder->crtc)); |
470 | } | 645 | } |
471 | 646 | ||
@@ -480,8 +655,7 @@ static void sti_dvo_encoder_disable(struct drm_encoder *encoder) | |||
480 | static const struct drm_encoder_helper_funcs sti_dvo_encoder_helper_funcs = { | 655 | static const struct drm_encoder_helper_funcs sti_dvo_encoder_helper_funcs = { |
481 | .dpms = sti_tvout_encoder_dpms, | 656 | .dpms = sti_tvout_encoder_dpms, |
482 | .mode_set = sti_tvout_encoder_mode_set, | 657 | .mode_set = sti_tvout_encoder_mode_set, |
483 | .prepare = sti_tvout_encoder_prepare, | 658 | .enable = sti_dvo_encoder_enable, |
484 | .commit = sti_dvo_encoder_commit, | ||
485 | .disable = sti_dvo_encoder_disable, | 659 | .disable = sti_dvo_encoder_disable, |
486 | }; | 660 | }; |
487 | 661 | ||
@@ -512,10 +686,12 @@ sti_tvout_create_dvo_encoder(struct drm_device *dev, | |||
512 | return drm_encoder; | 686 | return drm_encoder; |
513 | } | 687 | } |
514 | 688 | ||
515 | static void sti_hda_encoder_commit(struct drm_encoder *encoder) | 689 | static void sti_hda_encoder_enable(struct drm_encoder *encoder) |
516 | { | 690 | { |
517 | struct sti_tvout *tvout = to_sti_tvout(encoder); | 691 | struct sti_tvout *tvout = to_sti_tvout(encoder); |
518 | 692 | ||
693 | tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode); | ||
694 | |||
519 | tvout_hda_start(tvout, sti_crtc_is_main(encoder->crtc)); | 695 | tvout_hda_start(tvout, sti_crtc_is_main(encoder->crtc)); |
520 | } | 696 | } |
521 | 697 | ||
@@ -533,8 +709,7 @@ static void sti_hda_encoder_disable(struct drm_encoder *encoder) | |||
533 | static const struct drm_encoder_helper_funcs sti_hda_encoder_helper_funcs = { | 709 | static const struct drm_encoder_helper_funcs sti_hda_encoder_helper_funcs = { |
534 | .dpms = sti_tvout_encoder_dpms, | 710 | .dpms = sti_tvout_encoder_dpms, |
535 | .mode_set = sti_tvout_encoder_mode_set, | 711 | .mode_set = sti_tvout_encoder_mode_set, |
536 | .prepare = sti_tvout_encoder_prepare, | 712 | .commit = sti_hda_encoder_enable, |
537 | .commit = sti_hda_encoder_commit, | ||
538 | .disable = sti_hda_encoder_disable, | 713 | .disable = sti_hda_encoder_disable, |
539 | }; | 714 | }; |
540 | 715 | ||
@@ -563,10 +738,12 @@ static struct drm_encoder *sti_tvout_create_hda_encoder(struct drm_device *dev, | |||
563 | return drm_encoder; | 738 | return drm_encoder; |
564 | } | 739 | } |
565 | 740 | ||
566 | static void sti_hdmi_encoder_commit(struct drm_encoder *encoder) | 741 | static void sti_hdmi_encoder_enable(struct drm_encoder *encoder) |
567 | { | 742 | { |
568 | struct sti_tvout *tvout = to_sti_tvout(encoder); | 743 | struct sti_tvout *tvout = to_sti_tvout(encoder); |
569 | 744 | ||
745 | tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode); | ||
746 | |||
570 | tvout_hdmi_start(tvout, sti_crtc_is_main(encoder->crtc)); | 747 | tvout_hdmi_start(tvout, sti_crtc_is_main(encoder->crtc)); |
571 | } | 748 | } |
572 | 749 | ||
@@ -581,8 +758,7 @@ static void sti_hdmi_encoder_disable(struct drm_encoder *encoder) | |||
581 | static const struct drm_encoder_helper_funcs sti_hdmi_encoder_helper_funcs = { | 758 | static const struct drm_encoder_helper_funcs sti_hdmi_encoder_helper_funcs = { |
582 | .dpms = sti_tvout_encoder_dpms, | 759 | .dpms = sti_tvout_encoder_dpms, |
583 | .mode_set = sti_tvout_encoder_mode_set, | 760 | .mode_set = sti_tvout_encoder_mode_set, |
584 | .prepare = sti_tvout_encoder_prepare, | 761 | .commit = sti_hdmi_encoder_enable, |
585 | .commit = sti_hdmi_encoder_commit, | ||
586 | .disable = sti_hdmi_encoder_disable, | 762 | .disable = sti_hdmi_encoder_disable, |
587 | }; | 763 | }; |
588 | 764 | ||
@@ -628,26 +804,24 @@ static void sti_tvout_destroy_encoders(struct sti_tvout *tvout) | |||
628 | if (tvout->hda) | 804 | if (tvout->hda) |
629 | drm_encoder_cleanup(tvout->hda); | 805 | drm_encoder_cleanup(tvout->hda); |
630 | tvout->hda = NULL; | 806 | tvout->hda = NULL; |
807 | |||
808 | if (tvout->dvo) | ||
809 | drm_encoder_cleanup(tvout->dvo); | ||
810 | tvout->dvo = NULL; | ||
631 | } | 811 | } |
632 | 812 | ||
633 | static int sti_tvout_bind(struct device *dev, struct device *master, void *data) | 813 | static int sti_tvout_bind(struct device *dev, struct device *master, void *data) |
634 | { | 814 | { |
635 | struct sti_tvout *tvout = dev_get_drvdata(dev); | 815 | struct sti_tvout *tvout = dev_get_drvdata(dev); |
636 | struct drm_device *drm_dev = data; | 816 | struct drm_device *drm_dev = data; |
637 | unsigned int i; | ||
638 | 817 | ||
639 | tvout->drm_dev = drm_dev; | 818 | tvout->drm_dev = drm_dev; |
640 | 819 | ||
641 | /* set preformatter matrix */ | ||
642 | for (i = 0; i < 8; i++) { | ||
643 | tvout_write(tvout, rgb_to_ycbcr_601[i], | ||
644 | TVO_CSC_MAIN_M0 + (i * 4)); | ||
645 | tvout_write(tvout, rgb_to_ycbcr_601[i], | ||
646 | TVO_CSC_AUX_M0 + (i * 4)); | ||
647 | } | ||
648 | |||
649 | sti_tvout_create_encoders(drm_dev, tvout); | 820 | sti_tvout_create_encoders(drm_dev, tvout); |
650 | 821 | ||
822 | if (tvout_debugfs_init(tvout, drm_dev->primary)) | ||
823 | DRM_ERROR("TVOUT debugfs setup failed\n"); | ||
824 | |||
651 | return 0; | 825 | return 0; |
652 | } | 826 | } |
653 | 827 | ||
@@ -655,8 +829,11 @@ static void sti_tvout_unbind(struct device *dev, struct device *master, | |||
655 | void *data) | 829 | void *data) |
656 | { | 830 | { |
657 | struct sti_tvout *tvout = dev_get_drvdata(dev); | 831 | struct sti_tvout *tvout = dev_get_drvdata(dev); |
832 | struct drm_device *drm_dev = data; | ||
658 | 833 | ||
659 | sti_tvout_destroy_encoders(tvout); | 834 | sti_tvout_destroy_encoders(tvout); |
835 | |||
836 | tvout_debugfs_exit(tvout, drm_dev->primary); | ||
660 | } | 837 | } |
661 | 838 | ||
662 | static const struct component_ops sti_tvout_ops = { | 839 | static const struct component_ops sti_tvout_ops = { |
diff --git a/drivers/gpu/drm/sti/sti_vid.c b/drivers/gpu/drm/sti/sti_vid.c index a8254cc362a1..5a2c5dc3687b 100644 --- a/drivers/gpu/drm/sti/sti_vid.c +++ b/drivers/gpu/drm/sti/sti_vid.c | |||
@@ -42,6 +42,104 @@ | |||
42 | #define VID_MPR1_BT709 0x0AC50000 | 42 | #define VID_MPR1_BT709 0x0AC50000 |
43 | #define VID_MPR2_BT709 0x07150545 | 43 | #define VID_MPR2_BT709 0x07150545 |
44 | #define VID_MPR3_BT709 0x00000AE8 | 44 | #define VID_MPR3_BT709 0x00000AE8 |
45 | /* YCbCr to RGB BT709: | ||
46 | * R = Y+1.3711Cr | ||
47 | * G = Y-0.6992Cr-0.3359Cb | ||
48 | * B = Y+1.7344Cb | ||
49 | */ | ||
50 | #define VID_MPR0_BT601 0x0A800000 | ||
51 | #define VID_MPR1_BT601 0x0AAF0000 | ||
52 | #define VID_MPR2_BT601 0x094E0754 | ||
53 | #define VID_MPR3_BT601 0x00000ADD | ||
54 | |||
55 | #define VID_MIN_HD_HEIGHT 720 | ||
56 | |||
57 | #define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \ | ||
58 | readl(vid->regs + reg)) | ||
59 | |||
60 | static void vid_dbg_ctl(struct seq_file *s, int val) | ||
61 | { | ||
62 | val = val >> 30; | ||
63 | seq_puts(s, "\t"); | ||
64 | |||
65 | if (!(val & 1)) | ||
66 | seq_puts(s, "NOT "); | ||
67 | seq_puts(s, "ignored on main mixer - "); | ||
68 | |||
69 | if (!(val & 2)) | ||
70 | seq_puts(s, "NOT "); | ||
71 | seq_puts(s, "ignored on aux mixer"); | ||
72 | } | ||
73 | |||
74 | static void vid_dbg_vpo(struct seq_file *s, int val) | ||
75 | { | ||
76 | seq_printf(s, "\txdo:%4d\tydo:%4d", val & 0x0FFF, (val >> 16) & 0x0FFF); | ||
77 | } | ||
78 | |||
79 | static void vid_dbg_vps(struct seq_file *s, int val) | ||
80 | { | ||
81 | seq_printf(s, "\txds:%4d\tyds:%4d", val & 0x0FFF, (val >> 16) & 0x0FFF); | ||
82 | } | ||
83 | |||
84 | static void vid_dbg_mst(struct seq_file *s, int val) | ||
85 | { | ||
86 | if (val & 1) | ||
87 | seq_puts(s, "\tBUFFER UNDERFLOW!"); | ||
88 | } | ||
89 | |||
90 | static int vid_dbg_show(struct seq_file *s, void *arg) | ||
91 | { | ||
92 | struct drm_info_node *node = s->private; | ||
93 | struct sti_vid *vid = (struct sti_vid *)node->info_ent->data; | ||
94 | struct drm_device *dev = node->minor->dev; | ||
95 | int ret; | ||
96 | |||
97 | ret = mutex_lock_interruptible(&dev->struct_mutex); | ||
98 | if (ret) | ||
99 | return ret; | ||
100 | |||
101 | seq_printf(s, "VID: (vaddr= 0x%p)", vid->regs); | ||
102 | |||
103 | DBGFS_DUMP(VID_CTL); | ||
104 | vid_dbg_ctl(s, readl(vid->regs + VID_CTL)); | ||
105 | DBGFS_DUMP(VID_ALP); | ||
106 | DBGFS_DUMP(VID_CLF); | ||
107 | DBGFS_DUMP(VID_VPO); | ||
108 | vid_dbg_vpo(s, readl(vid->regs + VID_VPO)); | ||
109 | DBGFS_DUMP(VID_VPS); | ||
110 | vid_dbg_vps(s, readl(vid->regs + VID_VPS)); | ||
111 | DBGFS_DUMP(VID_KEY1); | ||
112 | DBGFS_DUMP(VID_KEY2); | ||
113 | DBGFS_DUMP(VID_MPR0); | ||
114 | DBGFS_DUMP(VID_MPR1); | ||
115 | DBGFS_DUMP(VID_MPR2); | ||
116 | DBGFS_DUMP(VID_MPR3); | ||
117 | DBGFS_DUMP(VID_MST); | ||
118 | vid_dbg_mst(s, readl(vid->regs + VID_MST)); | ||
119 | DBGFS_DUMP(VID_BC); | ||
120 | DBGFS_DUMP(VID_TINT); | ||
121 | DBGFS_DUMP(VID_CSAT); | ||
122 | seq_puts(s, "\n"); | ||
123 | |||
124 | mutex_unlock(&dev->struct_mutex); | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static struct drm_info_list vid_debugfs_files[] = { | ||
129 | { "vid", vid_dbg_show, 0, NULL }, | ||
130 | }; | ||
131 | |||
132 | static int vid_debugfs_init(struct sti_vid *vid, struct drm_minor *minor) | ||
133 | { | ||
134 | unsigned int i; | ||
135 | |||
136 | for (i = 0; i < ARRAY_SIZE(vid_debugfs_files); i++) | ||
137 | vid_debugfs_files[i].data = vid; | ||
138 | |||
139 | return drm_debugfs_create_files(vid_debugfs_files, | ||
140 | ARRAY_SIZE(vid_debugfs_files), | ||
141 | minor->debugfs_root, minor); | ||
142 | } | ||
45 | 143 | ||
46 | void sti_vid_commit(struct sti_vid *vid, | 144 | void sti_vid_commit(struct sti_vid *vid, |
47 | struct drm_plane_state *state) | 145 | struct drm_plane_state *state) |
@@ -52,6 +150,7 @@ void sti_vid_commit(struct sti_vid *vid, | |||
52 | int dst_y = state->crtc_y; | 150 | int dst_y = state->crtc_y; |
53 | int dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); | 151 | int dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); |
54 | int dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); | 152 | int dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); |
153 | int src_h = state->src_h >> 16; | ||
55 | u32 val, ydo, xdo, yds, xds; | 154 | u32 val, ydo, xdo, yds, xds; |
56 | 155 | ||
57 | /* Input / output size | 156 | /* Input / output size |
@@ -71,6 +170,19 @@ void sti_vid_commit(struct sti_vid *vid, | |||
71 | 170 | ||
72 | writel((ydo << 16) | xdo, vid->regs + VID_VPO); | 171 | writel((ydo << 16) | xdo, vid->regs + VID_VPO); |
73 | writel((yds << 16) | xds, vid->regs + VID_VPS); | 172 | writel((yds << 16) | xds, vid->regs + VID_VPS); |
173 | |||
174 | /* Color conversion parameters */ | ||
175 | if (src_h >= VID_MIN_HD_HEIGHT) { | ||
176 | writel(VID_MPR0_BT709, vid->regs + VID_MPR0); | ||
177 | writel(VID_MPR1_BT709, vid->regs + VID_MPR1); | ||
178 | writel(VID_MPR2_BT709, vid->regs + VID_MPR2); | ||
179 | writel(VID_MPR3_BT709, vid->regs + VID_MPR3); | ||
180 | } else { | ||
181 | writel(VID_MPR0_BT601, vid->regs + VID_MPR0); | ||
182 | writel(VID_MPR1_BT601, vid->regs + VID_MPR1); | ||
183 | writel(VID_MPR2_BT601, vid->regs + VID_MPR2); | ||
184 | writel(VID_MPR3_BT601, vid->regs + VID_MPR3); | ||
185 | } | ||
74 | } | 186 | } |
75 | 187 | ||
76 | void sti_vid_disable(struct sti_vid *vid) | 188 | void sti_vid_disable(struct sti_vid *vid) |
@@ -91,20 +203,14 @@ static void sti_vid_init(struct sti_vid *vid) | |||
91 | /* Opaque */ | 203 | /* Opaque */ |
92 | writel(VID_ALP_OPAQUE, vid->regs + VID_ALP); | 204 | writel(VID_ALP_OPAQUE, vid->regs + VID_ALP); |
93 | 205 | ||
94 | /* Color conversion parameters */ | ||
95 | writel(VID_MPR0_BT709, vid->regs + VID_MPR0); | ||
96 | writel(VID_MPR1_BT709, vid->regs + VID_MPR1); | ||
97 | writel(VID_MPR2_BT709, vid->regs + VID_MPR2); | ||
98 | writel(VID_MPR3_BT709, vid->regs + VID_MPR3); | ||
99 | |||
100 | /* Brightness, contrast, tint, saturation */ | 206 | /* Brightness, contrast, tint, saturation */ |
101 | writel(VID_BC_DFLT, vid->regs + VID_BC); | 207 | writel(VID_BC_DFLT, vid->regs + VID_BC); |
102 | writel(VID_TINT_DFLT, vid->regs + VID_TINT); | 208 | writel(VID_TINT_DFLT, vid->regs + VID_TINT); |
103 | writel(VID_CSAT_DFLT, vid->regs + VID_CSAT); | 209 | writel(VID_CSAT_DFLT, vid->regs + VID_CSAT); |
104 | } | 210 | } |
105 | 211 | ||
106 | struct sti_vid *sti_vid_create(struct device *dev, int id, | 212 | struct sti_vid *sti_vid_create(struct device *dev, struct drm_device *drm_dev, |
107 | void __iomem *baseaddr) | 213 | int id, void __iomem *baseaddr) |
108 | { | 214 | { |
109 | struct sti_vid *vid; | 215 | struct sti_vid *vid; |
110 | 216 | ||
@@ -120,5 +226,8 @@ struct sti_vid *sti_vid_create(struct device *dev, int id, | |||
120 | 226 | ||
121 | sti_vid_init(vid); | 227 | sti_vid_init(vid); |
122 | 228 | ||
229 | if (vid_debugfs_init(vid, drm_dev->primary)) | ||
230 | DRM_ERROR("VID debugfs setup failed\n"); | ||
231 | |||
123 | return vid; | 232 | return vid; |
124 | } | 233 | } |
diff --git a/drivers/gpu/drm/sti/sti_vid.h b/drivers/gpu/drm/sti/sti_vid.h index 5dea4791f1d6..6c842344f3d8 100644 --- a/drivers/gpu/drm/sti/sti_vid.h +++ b/drivers/gpu/drm/sti/sti_vid.h | |||
@@ -23,7 +23,7 @@ struct sti_vid { | |||
23 | void sti_vid_commit(struct sti_vid *vid, | 23 | void sti_vid_commit(struct sti_vid *vid, |
24 | struct drm_plane_state *state); | 24 | struct drm_plane_state *state); |
25 | void sti_vid_disable(struct sti_vid *vid); | 25 | void sti_vid_disable(struct sti_vid *vid); |
26 | struct sti_vid *sti_vid_create(struct device *dev, int id, | 26 | struct sti_vid *sti_vid_create(struct device *dev, struct drm_device *drm_dev, |
27 | void __iomem *baseaddr); | 27 | int id, void __iomem *baseaddr); |
28 | 28 | ||
29 | #endif | 29 | #endif |
diff --git a/drivers/gpu/drm/sti/sti_vtg.c b/drivers/gpu/drm/sti/sti_vtg.c index d56630c60039..32c7986b63ab 100644 --- a/drivers/gpu/drm/sti/sti_vtg.c +++ b/drivers/gpu/drm/sti/sti_vtg.c | |||
@@ -15,8 +15,8 @@ | |||
15 | 15 | ||
16 | #include "sti_vtg.h" | 16 | #include "sti_vtg.h" |
17 | 17 | ||
18 | #define VTG_TYPE_MASTER 0 | 18 | #define VTG_MODE_MASTER 0 |
19 | #define VTG_TYPE_SLAVE_BY_EXT0 1 | 19 | #define VTG_MODE_SLAVE_BY_EXT0 1 |
20 | 20 | ||
21 | /* registers offset */ | 21 | /* registers offset */ |
22 | #define VTG_MODE 0x0000 | 22 | #define VTG_MODE 0x0000 |
@@ -64,6 +64,9 @@ | |||
64 | /* Delay introduced by the HDMI in nb of pixel */ | 64 | /* Delay introduced by the HDMI in nb of pixel */ |
65 | #define HDMI_DELAY (5) | 65 | #define HDMI_DELAY (5) |
66 | 66 | ||
67 | /* Delay introduced by the DVO in nb of pixel */ | ||
68 | #define DVO_DELAY (2) | ||
69 | |||
67 | /* delay introduced by the Arbitrary Waveform Generator in nb of pixels */ | 70 | /* delay introduced by the Arbitrary Waveform Generator in nb of pixels */ |
68 | #define AWG_DELAY_HD (-9) | 71 | #define AWG_DELAY_HD (-9) |
69 | #define AWG_DELAY_ED (-8) | 72 | #define AWG_DELAY_ED (-8) |
@@ -71,13 +74,61 @@ | |||
71 | 74 | ||
72 | LIST_HEAD(vtg_lookup); | 75 | LIST_HEAD(vtg_lookup); |
73 | 76 | ||
77 | /* | ||
78 | * STI VTG register offset structure | ||
79 | * | ||
80 | *@h_hd: stores the VTG_H_HD_x register offset | ||
81 | *@top_v_vd: stores the VTG_TOP_V_VD_x register offset | ||
82 | *@bot_v_vd: stores the VTG_BOT_V_VD_x register offset | ||
83 | *@top_v_hd: stores the VTG_TOP_V_HD_x register offset | ||
84 | *@bot_v_hd: stores the VTG_BOT_V_HD_x register offset | ||
85 | */ | ||
86 | struct sti_vtg_regs_offs { | ||
87 | u32 h_hd; | ||
88 | u32 top_v_vd; | ||
89 | u32 bot_v_vd; | ||
90 | u32 top_v_hd; | ||
91 | u32 bot_v_hd; | ||
92 | }; | ||
93 | |||
94 | #define VTG_MAX_SYNC_OUTPUT 4 | ||
95 | static const struct sti_vtg_regs_offs vtg_regs_offs[VTG_MAX_SYNC_OUTPUT] = { | ||
96 | { VTG_H_HD_1, | ||
97 | VTG_TOP_V_VD_1, VTG_BOT_V_VD_1, VTG_TOP_V_HD_1, VTG_BOT_V_HD_1 }, | ||
98 | { VTG_H_HD_2, | ||
99 | VTG_TOP_V_VD_2, VTG_BOT_V_VD_2, VTG_TOP_V_HD_2, VTG_BOT_V_HD_2 }, | ||
100 | { VTG_H_HD_3, | ||
101 | VTG_TOP_V_VD_3, VTG_BOT_V_VD_3, VTG_TOP_V_HD_3, VTG_BOT_V_HD_3 }, | ||
102 | { VTG_H_HD_4, | ||
103 | VTG_TOP_V_VD_4, VTG_BOT_V_VD_4, VTG_TOP_V_HD_4, VTG_BOT_V_HD_4 } | ||
104 | }; | ||
105 | |||
106 | /* | ||
107 | * STI VTG synchronisation parameters structure | ||
108 | * | ||
109 | *@hsync: sample number falling and rising edge | ||
110 | *@vsync_line_top: vertical top field line number falling and rising edge | ||
111 | *@vsync_line_bot: vertical bottom field line number falling and rising edge | ||
112 | *@vsync_off_top: vertical top field sample number rising and falling edge | ||
113 | *@vsync_off_bot: vertical bottom field sample number rising and falling edge | ||
114 | */ | ||
115 | struct sti_vtg_sync_params { | ||
116 | u32 hsync; | ||
117 | u32 vsync_line_top; | ||
118 | u32 vsync_line_bot; | ||
119 | u32 vsync_off_top; | ||
120 | u32 vsync_off_bot; | ||
121 | }; | ||
122 | |||
74 | /** | 123 | /** |
75 | * STI VTG structure | 124 | * STI VTG structure |
76 | * | 125 | * |
77 | * @dev: pointer to device driver | 126 | * @dev: pointer to device driver |
78 | * @data: data associated to the device | 127 | * @np: device node |
128 | * @regs: register mapping | ||
129 | * @sync_params: synchronisation parameters used to generate timings | ||
79 | * @irq: VTG irq | 130 | * @irq: VTG irq |
80 | * @type: VTG type (main or aux) | 131 | * @irq_status: store the IRQ status value |
81 | * @notifier_list: notifier callback | 132 | * @notifier_list: notifier callback |
82 | * @crtc: the CRTC for vblank event | 133 | * @crtc: the CRTC for vblank event |
83 | * @slave: slave vtg | 134 | * @slave: slave vtg |
@@ -87,6 +138,7 @@ struct sti_vtg { | |||
87 | struct device *dev; | 138 | struct device *dev; |
88 | struct device_node *np; | 139 | struct device_node *np; |
89 | void __iomem *regs; | 140 | void __iomem *regs; |
141 | struct sti_vtg_sync_params sync_params[VTG_MAX_SYNC_OUTPUT]; | ||
90 | int irq; | 142 | int irq; |
91 | u32 irq_status; | 143 | u32 irq_status; |
92 | struct raw_notifier_head notifier_list; | 144 | struct raw_notifier_head notifier_list; |
@@ -146,13 +198,69 @@ static void vtg_set_output_window(void __iomem *regs, | |||
146 | writel(video_bottom_field_stop, regs + VTG_VID_BFS); | 198 | writel(video_bottom_field_stop, regs + VTG_VID_BFS); |
147 | } | 199 | } |
148 | 200 | ||
201 | static void vtg_set_hsync_vsync_pos(struct sti_vtg_sync_params *sync, | ||
202 | int delay, | ||
203 | const struct drm_display_mode *mode) | ||
204 | { | ||
205 | long clocksperline, start, stop; | ||
206 | u32 risesync_top, fallsync_top; | ||
207 | u32 risesync_offs_top, fallsync_offs_top; | ||
208 | |||
209 | clocksperline = mode->htotal; | ||
210 | |||
211 | /* Get the hsync position */ | ||
212 | start = 0; | ||
213 | stop = mode->hsync_end - mode->hsync_start; | ||
214 | |||
215 | start += delay; | ||
216 | stop += delay; | ||
217 | |||
218 | if (start < 0) | ||
219 | start += clocksperline; | ||
220 | else if (start >= clocksperline) | ||
221 | start -= clocksperline; | ||
222 | |||
223 | if (stop < 0) | ||
224 | stop += clocksperline; | ||
225 | else if (stop >= clocksperline) | ||
226 | stop -= clocksperline; | ||
227 | |||
228 | sync->hsync = (stop << 16) | start; | ||
229 | |||
230 | /* Get the vsync position */ | ||
231 | if (delay >= 0) { | ||
232 | risesync_top = 1; | ||
233 | fallsync_top = risesync_top; | ||
234 | fallsync_top += mode->vsync_end - mode->vsync_start; | ||
235 | |||
236 | fallsync_offs_top = (u32)delay; | ||
237 | risesync_offs_top = (u32)delay; | ||
238 | } else { | ||
239 | risesync_top = mode->vtotal; | ||
240 | fallsync_top = mode->vsync_end - mode->vsync_start; | ||
241 | |||
242 | fallsync_offs_top = clocksperline + delay; | ||
243 | risesync_offs_top = clocksperline + delay; | ||
244 | } | ||
245 | |||
246 | sync->vsync_line_top = (fallsync_top << 16) | risesync_top; | ||
247 | sync->vsync_off_top = (fallsync_offs_top << 16) | risesync_offs_top; | ||
248 | |||
249 | /* Only progressive supported for now */ | ||
250 | sync->vsync_line_bot = sync->vsync_line_top; | ||
251 | sync->vsync_off_bot = sync->vsync_off_top; | ||
252 | } | ||
253 | |||
149 | static void vtg_set_mode(struct sti_vtg *vtg, | 254 | static void vtg_set_mode(struct sti_vtg *vtg, |
150 | int type, const struct drm_display_mode *mode) | 255 | int type, |
256 | struct sti_vtg_sync_params *sync, | ||
257 | const struct drm_display_mode *mode) | ||
151 | { | 258 | { |
152 | u32 tmp; | 259 | unsigned int i; |
153 | 260 | ||
154 | if (vtg->slave) | 261 | if (vtg->slave) |
155 | vtg_set_mode(vtg->slave, VTG_TYPE_SLAVE_BY_EXT0, mode); | 262 | vtg_set_mode(vtg->slave, VTG_MODE_SLAVE_BY_EXT0, |
263 | vtg->sync_params, mode); | ||
156 | 264 | ||
157 | /* Set the number of clock cycles per line */ | 265 | /* Set the number of clock cycles per line */ |
158 | writel(mode->htotal, vtg->regs + VTG_CLKLN); | 266 | writel(mode->htotal, vtg->regs + VTG_CLKLN); |
@@ -163,57 +271,31 @@ static void vtg_set_mode(struct sti_vtg *vtg, | |||
163 | /* Program output window */ | 271 | /* Program output window */ |
164 | vtg_set_output_window(vtg->regs, mode); | 272 | vtg_set_output_window(vtg->regs, mode); |
165 | 273 | ||
166 | /* prepare VTG set 1 for HDMI */ | 274 | /* Set hsync and vsync position for HDMI */ |
167 | tmp = (mode->hsync_end - mode->hsync_start + HDMI_DELAY) << 16; | 275 | vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDMI - 1], HDMI_DELAY, mode); |
168 | tmp |= HDMI_DELAY; | 276 | |
169 | writel(tmp, vtg->regs + VTG_H_HD_1); | 277 | /* Set hsync and vsync position for HD DCS */ |
170 | 278 | vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDDCS - 1], 0, mode); | |
171 | tmp = (mode->vsync_end - mode->vsync_start + 1) << 16; | 279 | |
172 | tmp |= 1; | 280 | /* Set hsync and vsync position for HDF */ |
173 | writel(tmp, vtg->regs + VTG_TOP_V_VD_1); | 281 | vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDF - 1], AWG_DELAY_HD, mode); |
174 | writel(tmp, vtg->regs + VTG_BOT_V_VD_1); | 282 | |
175 | 283 | /* Set hsync and vsync position for DVO */ | |
176 | tmp = HDMI_DELAY << 16; | 284 | vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_DVO - 1], DVO_DELAY, mode); |
177 | tmp |= HDMI_DELAY; | 285 | |
178 | writel(tmp, vtg->regs + VTG_TOP_V_HD_1); | 286 | /* Progam the syncs outputs */ |
179 | writel(tmp, vtg->regs + VTG_BOT_V_HD_1); | 287 | for (i = 0; i < VTG_MAX_SYNC_OUTPUT ; i++) { |
180 | 288 | writel(sync[i].hsync, | |
181 | /* prepare VTG set 2 for for HD DCS */ | 289 | vtg->regs + vtg_regs_offs[i].h_hd); |
182 | tmp = (mode->hsync_end - mode->hsync_start) << 16; | 290 | writel(sync[i].vsync_line_top, |
183 | writel(tmp, vtg->regs + VTG_H_HD_2); | 291 | vtg->regs + vtg_regs_offs[i].top_v_vd); |
184 | 292 | writel(sync[i].vsync_line_bot, | |
185 | tmp = (mode->vsync_end - mode->vsync_start + 1) << 16; | 293 | vtg->regs + vtg_regs_offs[i].bot_v_vd); |
186 | tmp |= 1; | 294 | writel(sync[i].vsync_off_top, |
187 | writel(tmp, vtg->regs + VTG_TOP_V_VD_2); | 295 | vtg->regs + vtg_regs_offs[i].top_v_hd); |
188 | writel(tmp, vtg->regs + VTG_BOT_V_VD_2); | 296 | writel(sync[i].vsync_off_bot, |
189 | writel(0, vtg->regs + VTG_TOP_V_HD_2); | 297 | vtg->regs + vtg_regs_offs[i].bot_v_hd); |
190 | writel(0, vtg->regs + VTG_BOT_V_HD_2); | 298 | } |
191 | |||
192 | /* prepare VTG set 3 for HD Analog in HD mode */ | ||
193 | tmp = (mode->hsync_end - mode->hsync_start + AWG_DELAY_HD) << 16; | ||
194 | tmp |= mode->htotal + AWG_DELAY_HD; | ||
195 | writel(tmp, vtg->regs + VTG_H_HD_3); | ||
196 | |||
197 | tmp = (mode->vsync_end - mode->vsync_start) << 16; | ||
198 | tmp |= mode->vtotal; | ||
199 | writel(tmp, vtg->regs + VTG_TOP_V_VD_3); | ||
200 | writel(tmp, vtg->regs + VTG_BOT_V_VD_3); | ||
201 | |||
202 | tmp = (mode->htotal + AWG_DELAY_HD) << 16; | ||
203 | tmp |= mode->htotal + AWG_DELAY_HD; | ||
204 | writel(tmp, vtg->regs + VTG_TOP_V_HD_3); | ||
205 | writel(tmp, vtg->regs + VTG_BOT_V_HD_3); | ||
206 | |||
207 | /* Prepare VTG set 4 for DVO */ | ||
208 | tmp = (mode->hsync_end - mode->hsync_start) << 16; | ||
209 | writel(tmp, vtg->regs + VTG_H_HD_4); | ||
210 | |||
211 | tmp = (mode->vsync_end - mode->vsync_start + 1) << 16; | ||
212 | tmp |= 1; | ||
213 | writel(tmp, vtg->regs + VTG_TOP_V_VD_4); | ||
214 | writel(tmp, vtg->regs + VTG_BOT_V_VD_4); | ||
215 | writel(0, vtg->regs + VTG_TOP_V_HD_4); | ||
216 | writel(0, vtg->regs + VTG_BOT_V_HD_4); | ||
217 | 299 | ||
218 | /* mode */ | 300 | /* mode */ |
219 | writel(type, vtg->regs + VTG_MODE); | 301 | writel(type, vtg->regs + VTG_MODE); |
@@ -231,7 +313,7 @@ void sti_vtg_set_config(struct sti_vtg *vtg, | |||
231 | const struct drm_display_mode *mode) | 313 | const struct drm_display_mode *mode) |
232 | { | 314 | { |
233 | /* write configuration */ | 315 | /* write configuration */ |
234 | vtg_set_mode(vtg, VTG_TYPE_MASTER, mode); | 316 | vtg_set_mode(vtg, VTG_MODE_MASTER, vtg->sync_params, mode); |
235 | 317 | ||
236 | vtg_reset(vtg); | 318 | vtg_reset(vtg); |
237 | 319 | ||
diff --git a/drivers/gpu/drm/sti/sti_vtg.h b/drivers/gpu/drm/sti/sti_vtg.h index cd2439f89d05..f1dcdf9c2342 100644 --- a/drivers/gpu/drm/sti/sti_vtg.h +++ b/drivers/gpu/drm/sti/sti_vtg.h | |||
@@ -10,6 +10,11 @@ | |||
10 | #define VTG_TOP_FIELD_EVENT 1 | 10 | #define VTG_TOP_FIELD_EVENT 1 |
11 | #define VTG_BOTTOM_FIELD_EVENT 2 | 11 | #define VTG_BOTTOM_FIELD_EVENT 2 |
12 | 12 | ||
13 | #define VTG_SYNC_ID_HDMI 1 | ||
14 | #define VTG_SYNC_ID_HDDCS 2 | ||
15 | #define VTG_SYNC_ID_HDF 3 | ||
16 | #define VTG_SYNC_ID_DVO 4 | ||
17 | |||
13 | struct sti_vtg; | 18 | struct sti_vtg; |
14 | struct drm_display_mode; | 19 | struct drm_display_mode; |
15 | struct notifier_block; | 20 | struct notifier_block; |