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 ...
This commit is contained in:
		
						commit
						cf481068cd
					
				| @ -7,6 +7,7 @@ | |||||||
| #include "sti_awg_utils.h" | #include "sti_awg_utils.h" | ||||||
| 
 | 
 | ||||||
| #define AWG_OPCODE_OFFSET 10 | #define AWG_OPCODE_OFFSET 10 | ||||||
|  | #define AWG_MAX_ARG       0x3ff | ||||||
| 
 | 
 | ||||||
| enum opcode { | enum opcode { | ||||||
| 	SET, | 	SET, | ||||||
| @ -34,6 +35,8 @@ static int awg_generate_instr(enum opcode opcode, | |||||||
| 	/* skip, repeat and replay arg should not exceed 1023.
 | 	/* skip, repeat and replay arg should not exceed 1023.
 | ||||||
| 	 * If user wants to exceed this value, the instruction should be | 	 * If user wants to exceed this value, the instruction should be | ||||||
| 	 * duplicate and arg should be adjust for each duplicated instruction. | 	 * duplicate and arg should be adjust for each duplicated instruction. | ||||||
|  | 	 * | ||||||
|  | 	 * mux_sel is used in case of SAV/EAV synchronization. | ||||||
| 	 */ | 	 */ | ||||||
| 
 | 
 | ||||||
| 	while (arg_tmp > 0) { | 	while (arg_tmp > 0) { | ||||||
| @ -65,7 +68,7 @@ static int awg_generate_instr(enum opcode opcode, | |||||||
| 
 | 
 | ||||||
| 			mux = 0; | 			mux = 0; | ||||||
| 			data_enable = 0; | 			data_enable = 0; | ||||||
| 			arg &= (0x3ff); | 			arg &= AWG_MAX_ARG; | ||||||
| 			break; | 			break; | ||||||
| 		case REPEAT: | 		case REPEAT: | ||||||
| 		case REPLAY: | 		case REPLAY: | ||||||
| @ -76,13 +79,13 @@ static int awg_generate_instr(enum opcode opcode, | |||||||
| 
 | 
 | ||||||
| 			mux = 0; | 			mux = 0; | ||||||
| 			data_enable = 0; | 			data_enable = 0; | ||||||
| 			arg &= (0x3ff); | 			arg &= AWG_MAX_ARG; | ||||||
| 			break; | 			break; | ||||||
| 		case JUMP: | 		case JUMP: | ||||||
| 			mux = 0; | 			mux = 0; | ||||||
| 			data_enable = 0; | 			data_enable = 0; | ||||||
| 			arg |= 0x40; /* for jump instruction 7th bit is 1 */ | 			arg |= 0x40; /* for jump instruction 7th bit is 1 */ | ||||||
| 			arg &= 0x3ff; | 			arg &= AWG_MAX_ARG; | ||||||
| 			break; | 			break; | ||||||
| 		case STOP: | 		case STOP: | ||||||
| 			arg = 0; | 			arg = 0; | ||||||
| @ -110,68 +113,75 @@ static int awg_generate_instr(enum opcode opcode, | |||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int sti_awg_generate_code_data_enable_mode( | static int awg_generate_line_signal( | ||||||
| 		struct awg_code_generation_params *fwparams, | 		struct awg_code_generation_params *fwparams, | ||||||
| 		struct awg_timing *timing) | 		struct awg_timing *timing) | ||||||
| { | { | ||||||
| 	long int val; | 	long int val; | ||||||
| 	long int data_en; | 	int ret = 0; | ||||||
|  | 
 | ||||||
|  | 	if (timing->trailing_pixels > 0) { | ||||||
|  | 		/* skip trailing pixel */ | ||||||
|  | 		val = timing->blanking_level; | ||||||
|  | 		ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams); | ||||||
|  | 
 | ||||||
|  | 		val = timing->trailing_pixels - 1; | ||||||
|  | 		ret |= awg_generate_instr(SKIP, val, 0, 0, fwparams); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* set DE signal high */ | ||||||
|  | 	val = timing->blanking_level; | ||||||
|  | 	ret |= awg_generate_instr((timing->trailing_pixels > 0) ? SET : RPLSET, | ||||||
|  | 			val, 0, 1, fwparams); | ||||||
|  | 
 | ||||||
|  | 	if (timing->blanking_pixels > 0) { | ||||||
|  | 		/* skip the number of active pixel */ | ||||||
|  | 		val = timing->active_pixels - 1; | ||||||
|  | 		ret |= awg_generate_instr(SKIP, val, 0, 1, fwparams); | ||||||
|  | 
 | ||||||
|  | 		/* set DE signal low */ | ||||||
|  | 		val = timing->blanking_level; | ||||||
|  | 		ret |= awg_generate_instr(SET, val, 0, 0, fwparams); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int sti_awg_generate_code_data_enable_mode( | ||||||
|  | 		struct awg_code_generation_params *fwparams, | ||||||
|  | 		struct awg_timing *timing) | ||||||
|  | { | ||||||
|  | 	long int val, tmp_val; | ||||||
| 	int ret = 0; | 	int ret = 0; | ||||||
| 
 | 
 | ||||||
| 	if (timing->trailing_lines > 0) { | 	if (timing->trailing_lines > 0) { | ||||||
| 		/* skip trailing lines */ | 		/* skip trailing lines */ | ||||||
| 		val = timing->blanking_level; | 		val = timing->blanking_level; | ||||||
| 		data_en = 0; | 		ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams); | ||||||
| 		ret |= awg_generate_instr(RPLSET, val, 0, data_en, fwparams); |  | ||||||
| 
 | 
 | ||||||
| 		val = timing->trailing_lines - 1; | 		val = timing->trailing_lines - 1; | ||||||
| 		data_en = 0; | 		ret |= awg_generate_instr(REPLAY, val, 0, 0, fwparams); | ||||||
| 		ret |= awg_generate_instr(REPLAY, val, 0, data_en, fwparams); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (timing->trailing_pixels > 0) { | 	tmp_val = timing->active_lines - 1; | ||||||
| 		/* skip trailing pixel */ |  | ||||||
| 		val = timing->blanking_level; |  | ||||||
| 		data_en = 0; |  | ||||||
| 		ret |= awg_generate_instr(RPLSET, val, 0, data_en, fwparams); |  | ||||||
| 
 |  | ||||||
| 		val = timing->trailing_pixels - 1; |  | ||||||
| 		data_en = 0; |  | ||||||
| 		ret |= awg_generate_instr(SKIP, val, 0, data_en, fwparams); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/* set DE signal high */ |  | ||||||
| 	val = timing->blanking_level; |  | ||||||
| 	data_en = 1; |  | ||||||
| 	ret |= awg_generate_instr((timing->trailing_pixels > 0) ? SET : RPLSET, |  | ||||||
| 			val, 0, data_en, fwparams); |  | ||||||
| 
 |  | ||||||
| 	if (timing->blanking_pixels > 0) { |  | ||||||
| 		/* skip the number of active pixel */ |  | ||||||
| 		val = timing->active_pixels - 1; |  | ||||||
| 		data_en = 1; |  | ||||||
| 		ret |= awg_generate_instr(SKIP, val, 0, data_en, fwparams); |  | ||||||
| 
 |  | ||||||
| 		/* set DE signal low */ |  | ||||||
| 		val = timing->blanking_level; |  | ||||||
| 		data_en = 0; |  | ||||||
| 		ret |= awg_generate_instr(SET, val, 0, data_en, fwparams); |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
|  | 	while (tmp_val > 0) { | ||||||
|  | 		/* generate DE signal for each line */ | ||||||
|  | 		ret |= awg_generate_line_signal(fwparams, timing); | ||||||
| 		/* replay the sequence as many active lines defined */ | 		/* replay the sequence as many active lines defined */ | ||||||
| 	val = timing->active_lines - 1; | 		ret |= awg_generate_instr(REPLAY, | ||||||
| 	data_en = 0; | 					  min_t(int, AWG_MAX_ARG, tmp_val), | ||||||
| 	ret |= awg_generate_instr(REPLAY, val, 0, data_en, fwparams); | 					  0, 0, fwparams); | ||||||
|  | 		tmp_val -= AWG_MAX_ARG; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	if (timing->blanking_lines > 0) { | 	if (timing->blanking_lines > 0) { | ||||||
| 		/* skip blanking lines */ | 		/* skip blanking lines */ | ||||||
| 		val = timing->blanking_level; | 		val = timing->blanking_level; | ||||||
| 		data_en = 0; | 		ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams); | ||||||
| 		ret |= awg_generate_instr(RPLSET, val, 0, data_en, fwparams); |  | ||||||
| 
 | 
 | ||||||
| 		val = timing->blanking_lines - 1; | 		val = timing->blanking_lines - 1; | ||||||
| 		data_en = 0; | 		ret |= awg_generate_instr(REPLAY, val, 0, 0, fwparams); | ||||||
| 		ret |= awg_generate_instr(REPLAY, val, 0, data_en, fwparams); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return ret; | 	return ret; | ||||||
|  | |||||||
| @ -75,13 +75,13 @@ static int sti_compositor_bind(struct device *dev, | |||||||
| 		switch (desc[i].type) { | 		switch (desc[i].type) { | ||||||
| 		case STI_VID_SUBDEV: | 		case STI_VID_SUBDEV: | ||||||
| 			compo->vid[vid_id++] = | 			compo->vid[vid_id++] = | ||||||
| 			    sti_vid_create(compo->dev, desc[i].id, | 			    sti_vid_create(compo->dev, drm_dev, desc[i].id, | ||||||
| 					   compo->regs + desc[i].offset); | 					   compo->regs + desc[i].offset); | ||||||
| 			break; | 			break; | ||||||
| 		case STI_MIXER_MAIN_SUBDEV: | 		case STI_MIXER_MAIN_SUBDEV: | ||||||
| 		case STI_MIXER_AUX_SUBDEV: | 		case STI_MIXER_AUX_SUBDEV: | ||||||
| 			compo->mixer[mixer_id++] = | 			compo->mixer[mixer_id++] = | ||||||
| 			    sti_mixer_create(compo->dev, desc[i].id, | 			    sti_mixer_create(compo->dev, drm_dev, desc[i].id, | ||||||
| 					     compo->regs + desc[i].offset); | 					     compo->regs + desc[i].offset); | ||||||
| 			break; | 			break; | ||||||
| 		case STI_GPD_SUBDEV: | 		case STI_GPD_SUBDEV: | ||||||
|  | |||||||
| @ -51,6 +51,15 @@ static void sti_crtc_disabling(struct drm_crtc *crtc) | |||||||
| 	mixer->status = STI_MIXER_DISABLING; | 	mixer->status = STI_MIXER_DISABLING; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static bool sti_crtc_mode_fixup(struct drm_crtc *crtc, | ||||||
|  | 				const struct drm_display_mode *mode, | ||||||
|  | 				struct drm_display_mode *adjusted_mode) | ||||||
|  | { | ||||||
|  | 	/* accept the provided drm_display_mode, do not fix it up */ | ||||||
|  | 	drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int | static int | ||||||
| sti_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode) | sti_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode) | ||||||
| { | { | ||||||
|  | |||||||
| @ -5,12 +5,10 @@ | |||||||
|  *          for STMicroelectronics. |  *          for STMicroelectronics. | ||||||
|  * License terms:  GNU General Public License (GPL), version 2 |  * License terms:  GNU General Public License (GPL), version 2 | ||||||
|  */ |  */ | ||||||
| #include <drm/drmP.h> |  | ||||||
| 
 | 
 | ||||||
| #include <drm/drm_atomic_helper.h> | #include <drm/drm_atomic.h> | ||||||
| #include <drm/drm_fb_cma_helper.h> | #include <drm/drm_fb_cma_helper.h> | ||||||
| #include <drm/drm_gem_cma_helper.h> | #include <drm/drm_gem_cma_helper.h> | ||||||
| #include <drm/drm_plane_helper.h> |  | ||||||
| 
 | 
 | ||||||
| #include "sti_compositor.h" | #include "sti_compositor.h" | ||||||
| #include "sti_cursor.h" | #include "sti_cursor.h" | ||||||
| @ -74,6 +72,82 @@ static const uint32_t cursor_supported_formats[] = { | |||||||
| 
 | 
 | ||||||
| #define to_sti_cursor(x) container_of(x, struct sti_cursor, plane) | #define to_sti_cursor(x) container_of(x, struct sti_cursor, plane) | ||||||
| 
 | 
 | ||||||
|  | #define DBGFS_DUMP(reg) seq_printf(s, "\n  %-25s 0x%08X", #reg, \ | ||||||
|  | 				   readl(cursor->regs + reg)) | ||||||
|  | 
 | ||||||
|  | static void cursor_dbg_vpo(struct seq_file *s, u32 val) | ||||||
|  | { | ||||||
|  | 	seq_printf(s, "\txdo:%4d\tydo:%4d", val & 0x0FFF, (val >> 16) & 0x0FFF); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void cursor_dbg_size(struct seq_file *s, u32 val) | ||||||
|  | { | ||||||
|  | 	seq_printf(s, "\t%d x %d", val & 0x07FF, (val >> 16) & 0x07FF); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void cursor_dbg_pml(struct seq_file *s, | ||||||
|  | 			   struct sti_cursor *cursor, u32 val) | ||||||
|  | { | ||||||
|  | 	if (cursor->pixmap.paddr == val) | ||||||
|  | 		seq_printf(s, "\tVirt @: %p", cursor->pixmap.base); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void cursor_dbg_cml(struct seq_file *s, | ||||||
|  | 			   struct sti_cursor *cursor, u32 val) | ||||||
|  | { | ||||||
|  | 	if (cursor->clut_paddr == val) | ||||||
|  | 		seq_printf(s, "\tVirt @: %p", cursor->clut); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int cursor_dbg_show(struct seq_file *s, void *data) | ||||||
|  | { | ||||||
|  | 	struct drm_info_node *node = s->private; | ||||||
|  | 	struct sti_cursor *cursor = (struct sti_cursor *)node->info_ent->data; | ||||||
|  | 	struct drm_device *dev = node->minor->dev; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = mutex_lock_interruptible(&dev->struct_mutex); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	seq_printf(s, "%s: (vaddr = 0x%p)", | ||||||
|  | 		   sti_plane_to_str(&cursor->plane), cursor->regs); | ||||||
|  | 
 | ||||||
|  | 	DBGFS_DUMP(CUR_CTL); | ||||||
|  | 	DBGFS_DUMP(CUR_VPO); | ||||||
|  | 	cursor_dbg_vpo(s, readl(cursor->regs + CUR_VPO)); | ||||||
|  | 	DBGFS_DUMP(CUR_PML); | ||||||
|  | 	cursor_dbg_pml(s, cursor, readl(cursor->regs + CUR_PML)); | ||||||
|  | 	DBGFS_DUMP(CUR_PMP); | ||||||
|  | 	DBGFS_DUMP(CUR_SIZE); | ||||||
|  | 	cursor_dbg_size(s, readl(cursor->regs + CUR_SIZE)); | ||||||
|  | 	DBGFS_DUMP(CUR_CML); | ||||||
|  | 	cursor_dbg_cml(s, cursor, readl(cursor->regs + CUR_CML)); | ||||||
|  | 	DBGFS_DUMP(CUR_AWS); | ||||||
|  | 	DBGFS_DUMP(CUR_AWE); | ||||||
|  | 	seq_puts(s, "\n"); | ||||||
|  | 
 | ||||||
|  | 	mutex_unlock(&dev->struct_mutex); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct drm_info_list cursor_debugfs_files[] = { | ||||||
|  | 	{ "cursor", cursor_dbg_show, 0, NULL }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int cursor_debugfs_init(struct sti_cursor *cursor, | ||||||
|  | 			       struct drm_minor *minor) | ||||||
|  | { | ||||||
|  | 	unsigned int i; | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(cursor_debugfs_files); i++) | ||||||
|  | 		cursor_debugfs_files[i].data = cursor; | ||||||
|  | 
 | ||||||
|  | 	return drm_debugfs_create_files(cursor_debugfs_files, | ||||||
|  | 					ARRAY_SIZE(cursor_debugfs_files), | ||||||
|  | 					minor->debugfs_root, minor); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void sti_cursor_argb8888_to_clut8(struct sti_cursor *cursor, u32 *src) | static void sti_cursor_argb8888_to_clut8(struct sti_cursor *cursor, u32 *src) | ||||||
| { | { | ||||||
| 	u8  *dst = cursor->pixmap.base; | 	u8  *dst = cursor->pixmap.base; | ||||||
| @ -110,35 +184,31 @@ static void sti_cursor_init(struct sti_cursor *cursor) | |||||||
| 						  (b * 5); | 						  (b * 5); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void sti_cursor_atomic_update(struct drm_plane *drm_plane, | static int sti_cursor_atomic_check(struct drm_plane *drm_plane, | ||||||
| 				     struct drm_plane_state *oldstate) | 				   struct drm_plane_state *state) | ||||||
| { | { | ||||||
| 	struct drm_plane_state *state = drm_plane->state; |  | ||||||
| 	struct sti_plane *plane = to_sti_plane(drm_plane); | 	struct sti_plane *plane = to_sti_plane(drm_plane); | ||||||
| 	struct sti_cursor *cursor = to_sti_cursor(plane); | 	struct sti_cursor *cursor = to_sti_cursor(plane); | ||||||
| 	struct drm_crtc *crtc = state->crtc; | 	struct drm_crtc *crtc = state->crtc; | ||||||
| 	struct sti_mixer *mixer = to_sti_mixer(crtc); |  | ||||||
| 	struct drm_framebuffer *fb = state->fb; | 	struct drm_framebuffer *fb = state->fb; | ||||||
| 	struct drm_display_mode *mode = &crtc->mode; | 	struct drm_crtc_state *crtc_state; | ||||||
| 	int dst_x = state->crtc_x; | 	struct drm_display_mode *mode; | ||||||
| 	int dst_y = state->crtc_y; | 	int dst_x, dst_y, dst_w, dst_h; | ||||||
| 	int dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); | 	int src_w, src_h; | ||||||
| 	int dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); | 
 | ||||||
|  | 	/* no need for further checks if the plane is being disabled */ | ||||||
|  | 	if (!crtc || !fb) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	crtc_state = drm_atomic_get_crtc_state(state->state, crtc); | ||||||
|  | 	mode = &crtc_state->mode; | ||||||
|  | 	dst_x = state->crtc_x; | ||||||
|  | 	dst_y = state->crtc_y; | ||||||
|  | 	dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); | ||||||
|  | 	dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); | ||||||
| 	/* src_x are in 16.16 format */ | 	/* src_x are in 16.16 format */ | ||||||
| 	int src_w = state->src_w >> 16; | 	src_w = state->src_w >> 16; | ||||||
| 	int src_h = state->src_h >> 16; | 	src_h = state->src_h >> 16; | ||||||
| 	bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false; |  | ||||||
| 	struct drm_gem_cma_object *cma_obj; |  | ||||||
| 	u32 y, x; |  | ||||||
| 	u32 val; |  | ||||||
| 
 |  | ||||||
| 	DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n", |  | ||||||
| 		      crtc->base.id, sti_mixer_to_str(mixer), |  | ||||||
| 		      drm_plane->base.id, sti_plane_to_str(plane)); |  | ||||||
| 	DRM_DEBUG_KMS("(%dx%d)@(%d,%d)\n", dst_w, dst_h, dst_x, dst_y); |  | ||||||
| 
 |  | ||||||
| 	dev_dbg(cursor->dev, "%s %s\n", __func__, |  | ||||||
| 		sti_plane_to_str(plane)); |  | ||||||
| 
 | 
 | ||||||
| 	if (src_w < STI_CURS_MIN_SIZE || | 	if (src_w < STI_CURS_MIN_SIZE || | ||||||
| 	    src_h < STI_CURS_MIN_SIZE || | 	    src_h < STI_CURS_MIN_SIZE || | ||||||
| @ -146,7 +216,7 @@ static void sti_cursor_atomic_update(struct drm_plane *drm_plane, | |||||||
| 	    src_h > STI_CURS_MAX_SIZE) { | 	    src_h > STI_CURS_MAX_SIZE) { | ||||||
| 		DRM_ERROR("Invalid cursor size (%dx%d)\n", | 		DRM_ERROR("Invalid cursor size (%dx%d)\n", | ||||||
| 				src_w, src_h); | 				src_w, src_h); | ||||||
| 		return; | 		return -EINVAL; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* If the cursor size has changed, re-allocated the pixmap */ | 	/* 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, | |||||||
| 							GFP_KERNEL | GFP_DMA); | 							GFP_KERNEL | GFP_DMA); | ||||||
| 		if (!cursor->pixmap.base) { | 		if (!cursor->pixmap.base) { | ||||||
| 			DRM_ERROR("Failed to allocate memory for pixmap\n"); | 			DRM_ERROR("Failed to allocate memory for pixmap\n"); | ||||||
| 			return; | 			return -EINVAL; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cma_obj = drm_fb_cma_get_gem_obj(fb, 0); | 	if (!drm_fb_cma_get_gem_obj(fb, 0)) { | ||||||
| 	if (!cma_obj) { |  | ||||||
| 		DRM_ERROR("Can't get CMA GEM object for fb\n"); | 		DRM_ERROR("Can't get CMA GEM object for fb\n"); | ||||||
| 		return; | 		return -EINVAL; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n", | ||||||
|  | 		      crtc->base.id, sti_mixer_to_str(to_sti_mixer(crtc)), | ||||||
|  | 		      drm_plane->base.id, sti_plane_to_str(plane)); | ||||||
|  | 	DRM_DEBUG_KMS("(%dx%d)@(%d,%d)\n", dst_w, dst_h, dst_x, dst_y); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void sti_cursor_atomic_update(struct drm_plane *drm_plane, | ||||||
|  | 				     struct drm_plane_state *oldstate) | ||||||
|  | { | ||||||
|  | 	struct drm_plane_state *state = drm_plane->state; | ||||||
|  | 	struct sti_plane *plane = to_sti_plane(drm_plane); | ||||||
|  | 	struct sti_cursor *cursor = to_sti_cursor(plane); | ||||||
|  | 	struct drm_crtc *crtc = state->crtc; | ||||||
|  | 	struct drm_framebuffer *fb = state->fb; | ||||||
|  | 	struct drm_display_mode *mode; | ||||||
|  | 	int dst_x, dst_y; | ||||||
|  | 	struct drm_gem_cma_object *cma_obj; | ||||||
|  | 	u32 y, x; | ||||||
|  | 	u32 val; | ||||||
|  | 
 | ||||||
|  | 	if (!crtc || !fb) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	mode = &crtc->mode; | ||||||
|  | 	dst_x = state->crtc_x; | ||||||
|  | 	dst_y = state->crtc_y; | ||||||
|  | 
 | ||||||
|  | 	cma_obj = drm_fb_cma_get_gem_obj(fb, 0); | ||||||
|  | 
 | ||||||
| 	/* Convert ARGB8888 to CLUT8 */ | 	/* Convert ARGB8888 to CLUT8 */ | ||||||
| 	sti_cursor_argb8888_to_clut8(cursor, (u32 *)cma_obj->vaddr); | 	sti_cursor_argb8888_to_clut8(cursor, (u32 *)cma_obj->vaddr); | ||||||
| 
 | 
 | ||||||
| @ -193,21 +293,21 @@ static void sti_cursor_atomic_update(struct drm_plane *drm_plane, | |||||||
| 	val = y << 16 | x; | 	val = y << 16 | x; | ||||||
| 	writel(val, cursor->regs + CUR_AWE); | 	writel(val, cursor->regs + CUR_AWE); | ||||||
| 
 | 
 | ||||||
| 	if (first_prepare) { |  | ||||||
| 		/* Set and fetch CLUT */ |  | ||||||
| 		writel(cursor->clut_paddr, cursor->regs + CUR_CML); |  | ||||||
| 		writel(CUR_CTL_CLUT_UPDATE, cursor->regs + CUR_CTL); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/* Set memory location, size, and position */ | 	/* Set memory location, size, and position */ | ||||||
| 	writel(cursor->pixmap.paddr, cursor->regs + CUR_PML); | 	writel(cursor->pixmap.paddr, cursor->regs + CUR_PML); | ||||||
| 	writel(cursor->width, cursor->regs + CUR_PMP); | 	writel(cursor->width, cursor->regs + CUR_PMP); | ||||||
| 	writel(cursor->height << 16 | cursor->width, cursor->regs + CUR_SIZE); | 	writel(cursor->height << 16 | cursor->width, cursor->regs + CUR_SIZE); | ||||||
| 
 | 
 | ||||||
| 	y = sti_vtg_get_line_number(*mode, dst_y); | 	y = sti_vtg_get_line_number(*mode, dst_y); | ||||||
| 	x = sti_vtg_get_pixel_number(*mode, dst_y); | 	x = sti_vtg_get_pixel_number(*mode, dst_x); | ||||||
| 	writel((y << 16) | x, cursor->regs + CUR_VPO); | 	writel((y << 16) | x, cursor->regs + CUR_VPO); | ||||||
| 
 | 
 | ||||||
|  | 	/* Set and fetch CLUT */ | ||||||
|  | 	writel(cursor->clut_paddr, cursor->regs + CUR_CML); | ||||||
|  | 	writel(CUR_CTL_CLUT_UPDATE, cursor->regs + CUR_CTL); | ||||||
|  | 
 | ||||||
|  | 	sti_plane_update_fps(plane, true, false); | ||||||
|  | 
 | ||||||
| 	plane->status = STI_PLANE_UPDATED; | 	plane->status = STI_PLANE_UPDATED; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -215,7 +315,6 @@ static void sti_cursor_atomic_disable(struct drm_plane *drm_plane, | |||||||
| 				      struct drm_plane_state *oldstate) | 				      struct drm_plane_state *oldstate) | ||||||
| { | { | ||||||
| 	struct sti_plane *plane = to_sti_plane(drm_plane); | 	struct sti_plane *plane = to_sti_plane(drm_plane); | ||||||
| 	struct sti_mixer *mixer = to_sti_mixer(drm_plane->crtc); |  | ||||||
| 
 | 
 | ||||||
| 	if (!drm_plane->crtc) { | 	if (!drm_plane->crtc) { | ||||||
| 		DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", | 		DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", | ||||||
| @ -224,13 +323,15 @@ static void sti_cursor_atomic_disable(struct drm_plane *drm_plane, | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n", | 	DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n", | ||||||
| 			 drm_plane->crtc->base.id, sti_mixer_to_str(mixer), | 			 drm_plane->crtc->base.id, | ||||||
|  | 			 sti_mixer_to_str(to_sti_mixer(drm_plane->crtc)), | ||||||
| 			 drm_plane->base.id, sti_plane_to_str(plane)); | 			 drm_plane->base.id, sti_plane_to_str(plane)); | ||||||
| 
 | 
 | ||||||
| 	plane->status = STI_PLANE_DISABLING; | 	plane->status = STI_PLANE_DISABLING; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static const struct drm_plane_helper_funcs sti_cursor_helpers_funcs = { | static const struct drm_plane_helper_funcs sti_cursor_helpers_funcs = { | ||||||
|  | 	.atomic_check = sti_cursor_atomic_check, | ||||||
| 	.atomic_update = sti_cursor_atomic_update, | 	.atomic_update = sti_cursor_atomic_update, | ||||||
| 	.atomic_disable = sti_cursor_atomic_disable, | 	.atomic_disable = sti_cursor_atomic_disable, | ||||||
| }; | }; | ||||||
| @ -283,6 +384,9 @@ struct drm_plane *sti_cursor_create(struct drm_device *drm_dev, | |||||||
| 
 | 
 | ||||||
| 	sti_plane_init_property(&cursor->plane, DRM_PLANE_TYPE_CURSOR); | 	sti_plane_init_property(&cursor->plane, DRM_PLANE_TYPE_CURSOR); | ||||||
| 
 | 
 | ||||||
|  | 	if (cursor_debugfs_init(cursor, drm_dev->primary)) | ||||||
|  | 		DRM_ERROR("CURSOR debugfs setup failed\n"); | ||||||
|  | 
 | ||||||
| 	return &cursor->plane.drm_plane; | 	return &cursor->plane.drm_plane; | ||||||
| 
 | 
 | ||||||
| err_plane: | err_plane: | ||||||
|  | |||||||
| @ -20,6 +20,7 @@ | |||||||
| 
 | 
 | ||||||
| #include "sti_crtc.h" | #include "sti_crtc.h" | ||||||
| #include "sti_drv.h" | #include "sti_drv.h" | ||||||
|  | #include "sti_plane.h" | ||||||
| 
 | 
 | ||||||
| #define DRIVER_NAME	"sti" | #define DRIVER_NAME	"sti" | ||||||
| #define DRIVER_DESC	"STMicroelectronics SoC DRM" | #define DRIVER_DESC	"STMicroelectronics SoC DRM" | ||||||
| @ -30,6 +31,130 @@ | |||||||
| #define STI_MAX_FB_HEIGHT	4096 | #define STI_MAX_FB_HEIGHT	4096 | ||||||
| #define STI_MAX_FB_WIDTH	4096 | #define STI_MAX_FB_WIDTH	4096 | ||||||
| 
 | 
 | ||||||
|  | static int sti_drm_fps_get(void *data, u64 *val) | ||||||
|  | { | ||||||
|  | 	struct drm_device *drm_dev = data; | ||||||
|  | 	struct drm_plane *p; | ||||||
|  | 	unsigned int i = 0; | ||||||
|  | 
 | ||||||
|  | 	*val = 0; | ||||||
|  | 	list_for_each_entry(p, &drm_dev->mode_config.plane_list, head) { | ||||||
|  | 		struct sti_plane *plane = to_sti_plane(p); | ||||||
|  | 
 | ||||||
|  | 		*val |= plane->fps_info.output << i; | ||||||
|  | 		i++; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int sti_drm_fps_set(void *data, u64 val) | ||||||
|  | { | ||||||
|  | 	struct drm_device *drm_dev = data; | ||||||
|  | 	struct drm_plane *p; | ||||||
|  | 	unsigned int i = 0; | ||||||
|  | 
 | ||||||
|  | 	list_for_each_entry(p, &drm_dev->mode_config.plane_list, head) { | ||||||
|  | 		struct sti_plane *plane = to_sti_plane(p); | ||||||
|  | 
 | ||||||
|  | 		plane->fps_info.output = (val >> i) & 1; | ||||||
|  | 		i++; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DEFINE_SIMPLE_ATTRIBUTE(sti_drm_fps_fops, | ||||||
|  | 			sti_drm_fps_get, sti_drm_fps_set, "%llu\n"); | ||||||
|  | 
 | ||||||
|  | static int sti_drm_fps_dbg_show(struct seq_file *s, void *data) | ||||||
|  | { | ||||||
|  | 	struct drm_info_node *node = s->private; | ||||||
|  | 	struct drm_device *dev = node->minor->dev; | ||||||
|  | 	struct drm_plane *p; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = mutex_lock_interruptible(&dev->struct_mutex); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	list_for_each_entry(p, &dev->mode_config.plane_list, head) { | ||||||
|  | 		struct sti_plane *plane = to_sti_plane(p); | ||||||
|  | 
 | ||||||
|  | 		seq_printf(s, "%s%s\n", | ||||||
|  | 			   plane->fps_info.fps_str, | ||||||
|  | 			   plane->fps_info.fips_str); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	mutex_unlock(&dev->struct_mutex); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct drm_info_list sti_drm_dbg_list[] = { | ||||||
|  | 	{"fps_get", sti_drm_fps_dbg_show, 0}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int sti_drm_debugfs_create(struct dentry *root, | ||||||
|  | 				  struct drm_minor *minor, | ||||||
|  | 				  const char *name, | ||||||
|  | 				  const struct file_operations *fops) | ||||||
|  | { | ||||||
|  | 	struct drm_device *dev = minor->dev; | ||||||
|  | 	struct drm_info_node *node; | ||||||
|  | 	struct dentry *ent; | ||||||
|  | 
 | ||||||
|  | 	ent = debugfs_create_file(name, S_IRUGO | S_IWUSR, root, dev, fops); | ||||||
|  | 	if (IS_ERR(ent)) | ||||||
|  | 		return PTR_ERR(ent); | ||||||
|  | 
 | ||||||
|  | 	node = kmalloc(sizeof(*node), GFP_KERNEL); | ||||||
|  | 	if (!node) { | ||||||
|  | 		debugfs_remove(ent); | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	node->minor = minor; | ||||||
|  | 	node->dent = ent; | ||||||
|  | 	node->info_ent = (void *)fops; | ||||||
|  | 
 | ||||||
|  | 	mutex_lock(&minor->debugfs_lock); | ||||||
|  | 	list_add(&node->list, &minor->debugfs_list); | ||||||
|  | 	mutex_unlock(&minor->debugfs_lock); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int sti_drm_dbg_init(struct drm_minor *minor) | ||||||
|  | { | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = drm_debugfs_create_files(sti_drm_dbg_list, | ||||||
|  | 				       ARRAY_SIZE(sti_drm_dbg_list), | ||||||
|  | 				       minor->debugfs_root, minor); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
|  | 
 | ||||||
|  | 	ret = sti_drm_debugfs_create(minor->debugfs_root, minor, "fps_show", | ||||||
|  | 				     &sti_drm_fps_fops); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
|  | 
 | ||||||
|  | 	DRM_INFO("%s: debugfs installed\n", DRIVER_NAME); | ||||||
|  | 	return 0; | ||||||
|  | err: | ||||||
|  | 	DRM_ERROR("%s: cannot install debugfs\n", DRIVER_NAME); | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void sti_drm_dbg_cleanup(struct drm_minor *minor) | ||||||
|  | { | ||||||
|  | 	drm_debugfs_remove_files(sti_drm_dbg_list, | ||||||
|  | 				 ARRAY_SIZE(sti_drm_dbg_list), minor); | ||||||
|  | 
 | ||||||
|  | 	drm_debugfs_remove_files((struct drm_info_list *)&sti_drm_fps_fops, | ||||||
|  | 				 1, minor); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void sti_atomic_schedule(struct sti_private *private, | static void sti_atomic_schedule(struct sti_private *private, | ||||||
| 				struct drm_atomic_state *state) | 				struct drm_atomic_state *state) | ||||||
| { | { | ||||||
| @ -181,18 +306,9 @@ static const struct file_operations sti_driver_fops = { | |||||||
| 	.release = drm_release, | 	.release = drm_release, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static struct dma_buf *sti_gem_prime_export(struct drm_device *dev, |  | ||||||
| 					    struct drm_gem_object *obj, |  | ||||||
| 					    int flags) |  | ||||||
| { |  | ||||||
| 	/* we want to be able to write in mmapped buffer */ |  | ||||||
| 	flags |= O_RDWR; |  | ||||||
| 	return drm_gem_prime_export(dev, obj, flags); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static struct drm_driver sti_driver = { | static struct drm_driver sti_driver = { | ||||||
| 	.driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET | | 	.driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET | | ||||||
| 	    DRIVER_GEM | DRIVER_PRIME, | 	    DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, | ||||||
| 	.load = sti_load, | 	.load = sti_load, | ||||||
| 	.gem_free_object = drm_gem_cma_free_object, | 	.gem_free_object = drm_gem_cma_free_object, | ||||||
| 	.gem_vm_ops = &drm_gem_cma_vm_ops, | 	.gem_vm_ops = &drm_gem_cma_vm_ops, | ||||||
| @ -207,7 +323,7 @@ static struct drm_driver sti_driver = { | |||||||
| 
 | 
 | ||||||
| 	.prime_handle_to_fd = drm_gem_prime_handle_to_fd, | 	.prime_handle_to_fd = drm_gem_prime_handle_to_fd, | ||||||
| 	.prime_fd_to_handle = drm_gem_prime_fd_to_handle, | 	.prime_fd_to_handle = drm_gem_prime_fd_to_handle, | ||||||
| 	.gem_prime_export = sti_gem_prime_export, | 	.gem_prime_export = drm_gem_prime_export, | ||||||
| 	.gem_prime_import = drm_gem_prime_import, | 	.gem_prime_import = drm_gem_prime_import, | ||||||
| 	.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, | 	.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, | ||||||
| 	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, | 	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, | ||||||
| @ -215,6 +331,9 @@ static struct drm_driver sti_driver = { | |||||||
| 	.gem_prime_vunmap = drm_gem_cma_prime_vunmap, | 	.gem_prime_vunmap = drm_gem_cma_prime_vunmap, | ||||||
| 	.gem_prime_mmap = drm_gem_cma_prime_mmap, | 	.gem_prime_mmap = drm_gem_cma_prime_mmap, | ||||||
| 
 | 
 | ||||||
|  | 	.debugfs_init = sti_drm_dbg_init, | ||||||
|  | 	.debugfs_cleanup = sti_drm_dbg_cleanup, | ||||||
|  | 
 | ||||||
| 	.name = DRIVER_NAME, | 	.name = DRIVER_NAME, | ||||||
| 	.desc = DRIVER_DESC, | 	.desc = DRIVER_DESC, | ||||||
| 	.date = DRIVER_DATE, | 	.date = DRIVER_DATE, | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ | |||||||
| 
 | 
 | ||||||
| #include <linux/clk.h> | #include <linux/clk.h> | ||||||
| #include <linux/component.h> | #include <linux/component.h> | ||||||
|  | #include <linux/debugfs.h> | ||||||
| #include <linux/module.h> | #include <linux/module.h> | ||||||
| #include <linux/of_gpio.h> | #include <linux/of_gpio.h> | ||||||
| #include <linux/platform_device.h> | #include <linux/platform_device.h> | ||||||
| @ -156,6 +157,69 @@ static void dvo_awg_configure(struct sti_dvo *dvo, u32 *awg_ram_code, int nb) | |||||||
| 	writel(DVO_AWG_CTRL_EN, dvo->regs + DVO_AWG_DIGSYNC_CTRL); | 	writel(DVO_AWG_CTRL_EN, dvo->regs + DVO_AWG_DIGSYNC_CTRL); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #define DBGFS_DUMP(reg) seq_printf(s, "\n  %-25s 0x%08X", #reg, \ | ||||||
|  | 				   readl(dvo->regs + reg)) | ||||||
|  | 
 | ||||||
|  | static void dvo_dbg_awg_microcode(struct seq_file *s, void __iomem *reg) | ||||||
|  | { | ||||||
|  | 	unsigned int i; | ||||||
|  | 
 | ||||||
|  | 	seq_puts(s, "\n\n"); | ||||||
|  | 	seq_puts(s, "  DVO AWG microcode:"); | ||||||
|  | 	for (i = 0; i < AWG_MAX_INST; i++) { | ||||||
|  | 		if (i % 8 == 0) | ||||||
|  | 			seq_printf(s, "\n  %04X:", i); | ||||||
|  | 		seq_printf(s, " %04X", readl(reg + i * 4)); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int dvo_dbg_show(struct seq_file *s, void *data) | ||||||
|  | { | ||||||
|  | 	struct drm_info_node *node = s->private; | ||||||
|  | 	struct sti_dvo *dvo = (struct sti_dvo *)node->info_ent->data; | ||||||
|  | 	struct drm_device *dev = node->minor->dev; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = mutex_lock_interruptible(&dev->struct_mutex); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	seq_printf(s, "DVO: (vaddr = 0x%p)", dvo->regs); | ||||||
|  | 	DBGFS_DUMP(DVO_AWG_DIGSYNC_CTRL); | ||||||
|  | 	DBGFS_DUMP(DVO_DOF_CFG); | ||||||
|  | 	DBGFS_DUMP(DVO_LUT_PROG_LOW); | ||||||
|  | 	DBGFS_DUMP(DVO_LUT_PROG_MID); | ||||||
|  | 	DBGFS_DUMP(DVO_LUT_PROG_HIGH); | ||||||
|  | 	dvo_dbg_awg_microcode(s, dvo->regs + DVO_DIGSYNC_INSTR_I); | ||||||
|  | 	seq_puts(s, "\n"); | ||||||
|  | 
 | ||||||
|  | 	mutex_unlock(&dev->struct_mutex); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct drm_info_list dvo_debugfs_files[] = { | ||||||
|  | 	{ "dvo", dvo_dbg_show, 0, NULL }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void dvo_debugfs_exit(struct sti_dvo *dvo, struct drm_minor *minor) | ||||||
|  | { | ||||||
|  | 	drm_debugfs_remove_files(dvo_debugfs_files, | ||||||
|  | 				 ARRAY_SIZE(dvo_debugfs_files), | ||||||
|  | 				 minor); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int dvo_debugfs_init(struct sti_dvo *dvo, struct drm_minor *minor) | ||||||
|  | { | ||||||
|  | 	unsigned int i; | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(dvo_debugfs_files); i++) | ||||||
|  | 		dvo_debugfs_files[i].data = dvo; | ||||||
|  | 
 | ||||||
|  | 	return drm_debugfs_create_files(dvo_debugfs_files, | ||||||
|  | 					ARRAY_SIZE(dvo_debugfs_files), | ||||||
|  | 					minor->debugfs_root, minor); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void sti_dvo_disable(struct drm_bridge *bridge) | static void sti_dvo_disable(struct drm_bridge *bridge) | ||||||
| { | { | ||||||
| 	struct sti_dvo *dvo = bridge->driver_private; | 	struct sti_dvo *dvo = bridge->driver_private; | ||||||
| @ -345,11 +409,13 @@ sti_dvo_connector_detect(struct drm_connector *connector, bool force) | |||||||
| 
 | 
 | ||||||
| 	DRM_DEBUG_DRIVER("\n"); | 	DRM_DEBUG_DRIVER("\n"); | ||||||
| 
 | 
 | ||||||
| 	if (!dvo->panel) | 	if (!dvo->panel) { | ||||||
| 		dvo->panel = of_drm_find_panel(dvo->panel_node); | 		dvo->panel = of_drm_find_panel(dvo->panel_node); | ||||||
|  | 		if (dvo->panel) | ||||||
|  | 			drm_panel_attach(dvo->panel, connector); | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	if (dvo->panel) | 	if (dvo->panel) | ||||||
| 		if (!drm_panel_attach(dvo->panel, connector)) |  | ||||||
| 		return connector_status_connected; | 		return connector_status_connected; | ||||||
| 
 | 
 | ||||||
| 	return connector_status_disconnected; | 	return connector_status_disconnected; | ||||||
| @ -453,6 +519,9 @@ static int sti_dvo_bind(struct device *dev, struct device *master, void *data) | |||||||
| 		goto err_sysfs; | 		goto err_sysfs; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if (dvo_debugfs_init(dvo, drm_dev->primary)) | ||||||
|  | 		DRM_ERROR("DVO debugfs setup failed\n"); | ||||||
|  | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | 
 | ||||||
| err_sysfs: | err_sysfs: | ||||||
| @ -467,6 +536,9 @@ static void sti_dvo_unbind(struct device *dev, | |||||||
| 			   struct device *master, void *data) | 			   struct device *master, void *data) | ||||||
| { | { | ||||||
| 	struct sti_dvo *dvo = dev_get_drvdata(dev); | 	struct sti_dvo *dvo = dev_get_drvdata(dev); | ||||||
|  | 	struct drm_device *drm_dev = data; | ||||||
|  | 
 | ||||||
|  | 	dvo_debugfs_exit(dvo, drm_dev->primary); | ||||||
| 
 | 
 | ||||||
| 	drm_bridge_remove(dvo->bridge); | 	drm_bridge_remove(dvo->bridge); | ||||||
| } | } | ||||||
|  | |||||||
| @ -6,9 +6,7 @@ | |||||||
|  * License terms:  GNU General Public License (GPL), version 2 |  * License terms:  GNU General Public License (GPL), version 2 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include <linux/clk.h> | #include <drm/drm_atomic.h> | ||||||
| #include <linux/dma-mapping.h> |  | ||||||
| 
 |  | ||||||
| #include <drm/drm_fb_cma_helper.h> | #include <drm/drm_fb_cma_helper.h> | ||||||
| #include <drm/drm_gem_cma_helper.h> | #include <drm/drm_gem_cma_helper.h> | ||||||
| 
 | 
 | ||||||
| @ -32,10 +30,23 @@ | |||||||
| #define GDP_ABGR8888    (GDP_ARGB8888 | BIGNOTLITTLE | ALPHASWITCH) | #define GDP_ABGR8888    (GDP_ARGB8888 | BIGNOTLITTLE | ALPHASWITCH) | ||||||
| #define GDP_ARGB1555    0x06 | #define GDP_ARGB1555    0x06 | ||||||
| #define GDP_ARGB4444    0x07 | #define GDP_ARGB4444    0x07 | ||||||
| #define GDP_CLUT8       0x0B | 
 | ||||||
| #define GDP_YCBR888     0x10 | #define GDP2STR(fmt) { GDP_ ## fmt, #fmt } | ||||||
| #define GDP_YCBR422R    0x12 | 
 | ||||||
| #define GDP_AYCBR8888   0x15 | static struct gdp_format_to_str { | ||||||
|  | 	int format; | ||||||
|  | 	char name[20]; | ||||||
|  | } gdp_format_to_str[] = { | ||||||
|  | 		GDP2STR(RGB565), | ||||||
|  | 		GDP2STR(RGB888), | ||||||
|  | 		GDP2STR(RGB888_32), | ||||||
|  | 		GDP2STR(XBGR8888), | ||||||
|  | 		GDP2STR(ARGB8565), | ||||||
|  | 		GDP2STR(ARGB8888), | ||||||
|  | 		GDP2STR(ABGR8888), | ||||||
|  | 		GDP2STR(ARGB1555), | ||||||
|  | 		GDP2STR(ARGB4444) | ||||||
|  | 		}; | ||||||
| 
 | 
 | ||||||
| #define GAM_GDP_CTL_OFFSET      0x00 | #define GAM_GDP_CTL_OFFSET      0x00 | ||||||
| #define GAM_GDP_AGC_OFFSET      0x04 | #define GAM_GDP_AGC_OFFSET      0x04 | ||||||
| @ -97,6 +108,7 @@ struct sti_gdp_node_list { | |||||||
|  * @vtg_field_nb:       callback for VTG FIELD (top or bottom) notification |  * @vtg_field_nb:       callback for VTG FIELD (top or bottom) notification | ||||||
|  * @is_curr_top:        true if the current node processed is the top field |  * @is_curr_top:        true if the current node processed is the top field | ||||||
|  * @node_list:          array of node list |  * @node_list:          array of node list | ||||||
|  |  * @vtg:                registered vtg | ||||||
|  */ |  */ | ||||||
| struct sti_gdp { | struct sti_gdp { | ||||||
| 	struct sti_plane plane; | 	struct sti_plane plane; | ||||||
| @ -108,6 +120,7 @@ struct sti_gdp { | |||||||
| 	struct notifier_block vtg_field_nb; | 	struct notifier_block vtg_field_nb; | ||||||
| 	bool is_curr_top; | 	bool is_curr_top; | ||||||
| 	struct sti_gdp_node_list node_list[GDP_NODE_NB_BANK]; | 	struct sti_gdp_node_list node_list[GDP_NODE_NB_BANK]; | ||||||
|  | 	struct sti_vtg *vtg; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #define to_sti_gdp(x) container_of(x, struct sti_gdp, plane) | #define to_sti_gdp(x) container_of(x, struct sti_gdp, plane) | ||||||
| @ -121,12 +134,224 @@ static const uint32_t gdp_supported_formats[] = { | |||||||
| 	DRM_FORMAT_ARGB1555, | 	DRM_FORMAT_ARGB1555, | ||||||
| 	DRM_FORMAT_RGB565, | 	DRM_FORMAT_RGB565, | ||||||
| 	DRM_FORMAT_RGB888, | 	DRM_FORMAT_RGB888, | ||||||
| 	DRM_FORMAT_AYUV, |  | ||||||
| 	DRM_FORMAT_YUV444, |  | ||||||
| 	DRM_FORMAT_VYUY, |  | ||||||
| 	DRM_FORMAT_C8, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | #define DBGFS_DUMP(reg) seq_printf(s, "\n  %-25s 0x%08X", #reg, \ | ||||||
|  | 				   readl(gdp->regs + reg ## _OFFSET)) | ||||||
|  | 
 | ||||||
|  | static void gdp_dbg_ctl(struct seq_file *s, int val) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 
 | ||||||
|  | 	seq_puts(s, "\tColor:"); | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(gdp_format_to_str); i++) { | ||||||
|  | 		if (gdp_format_to_str[i].format == (val & 0x1F)) { | ||||||
|  | 			seq_printf(s, gdp_format_to_str[i].name); | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if (i == ARRAY_SIZE(gdp_format_to_str)) | ||||||
|  | 		seq_puts(s, "<UNKNOWN>"); | ||||||
|  | 
 | ||||||
|  | 	seq_printf(s, "\tWaitNextVsync:%d", val & WAIT_NEXT_VSYNC ? 1 : 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gdp_dbg_vpo(struct seq_file *s, int val) | ||||||
|  | { | ||||||
|  | 	seq_printf(s, "\txdo:%4d\tydo:%4d", val & 0xFFFF, (val >> 16) & 0xFFFF); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gdp_dbg_vps(struct seq_file *s, int val) | ||||||
|  | { | ||||||
|  | 	seq_printf(s, "\txds:%4d\tyds:%4d", val & 0xFFFF, (val >> 16) & 0xFFFF); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gdp_dbg_size(struct seq_file *s, int val) | ||||||
|  | { | ||||||
|  | 	seq_printf(s, "\t%d x %d", val & 0xFFFF, (val >> 16) & 0xFFFF); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gdp_dbg_nvn(struct seq_file *s, struct sti_gdp *gdp, int val) | ||||||
|  | { | ||||||
|  | 	void *base = NULL; | ||||||
|  | 	unsigned int i; | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < GDP_NODE_NB_BANK; i++) { | ||||||
|  | 		if (gdp->node_list[i].top_field_paddr == val) { | ||||||
|  | 			base = gdp->node_list[i].top_field; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		if (gdp->node_list[i].btm_field_paddr == val) { | ||||||
|  | 			base = gdp->node_list[i].btm_field; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (base) | ||||||
|  | 		seq_printf(s, "\tVirt @: %p", base); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gdp_dbg_ppt(struct seq_file *s, int val) | ||||||
|  | { | ||||||
|  | 	if (val & GAM_GDP_PPT_IGNORE) | ||||||
|  | 		seq_puts(s, "\tNot displayed on mixer!"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gdp_dbg_mst(struct seq_file *s, int val) | ||||||
|  | { | ||||||
|  | 	if (val & 1) | ||||||
|  | 		seq_puts(s, "\tBUFFER UNDERFLOW!"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int gdp_dbg_show(struct seq_file *s, void *data) | ||||||
|  | { | ||||||
|  | 	struct drm_info_node *node = s->private; | ||||||
|  | 	struct sti_gdp *gdp = (struct sti_gdp *)node->info_ent->data; | ||||||
|  | 	struct drm_device *dev = node->minor->dev; | ||||||
|  | 	struct drm_plane *drm_plane = &gdp->plane.drm_plane; | ||||||
|  | 	struct drm_crtc *crtc = drm_plane->crtc; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = mutex_lock_interruptible(&dev->struct_mutex); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	seq_printf(s, "%s: (vaddr = 0x%p)", | ||||||
|  | 		   sti_plane_to_str(&gdp->plane), gdp->regs); | ||||||
|  | 
 | ||||||
|  | 	DBGFS_DUMP(GAM_GDP_CTL); | ||||||
|  | 	gdp_dbg_ctl(s, readl(gdp->regs + GAM_GDP_CTL_OFFSET)); | ||||||
|  | 	DBGFS_DUMP(GAM_GDP_AGC); | ||||||
|  | 	DBGFS_DUMP(GAM_GDP_VPO); | ||||||
|  | 	gdp_dbg_vpo(s, readl(gdp->regs + GAM_GDP_VPO_OFFSET)); | ||||||
|  | 	DBGFS_DUMP(GAM_GDP_VPS); | ||||||
|  | 	gdp_dbg_vps(s, readl(gdp->regs + GAM_GDP_VPS_OFFSET)); | ||||||
|  | 	DBGFS_DUMP(GAM_GDP_PML); | ||||||
|  | 	DBGFS_DUMP(GAM_GDP_PMP); | ||||||
|  | 	DBGFS_DUMP(GAM_GDP_SIZE); | ||||||
|  | 	gdp_dbg_size(s, readl(gdp->regs + GAM_GDP_SIZE_OFFSET)); | ||||||
|  | 	DBGFS_DUMP(GAM_GDP_NVN); | ||||||
|  | 	gdp_dbg_nvn(s, gdp, readl(gdp->regs + GAM_GDP_NVN_OFFSET)); | ||||||
|  | 	DBGFS_DUMP(GAM_GDP_KEY1); | ||||||
|  | 	DBGFS_DUMP(GAM_GDP_KEY2); | ||||||
|  | 	DBGFS_DUMP(GAM_GDP_PPT); | ||||||
|  | 	gdp_dbg_ppt(s, readl(gdp->regs + GAM_GDP_PPT_OFFSET)); | ||||||
|  | 	DBGFS_DUMP(GAM_GDP_CML); | ||||||
|  | 	DBGFS_DUMP(GAM_GDP_MST); | ||||||
|  | 	gdp_dbg_mst(s, readl(gdp->regs + GAM_GDP_MST_OFFSET)); | ||||||
|  | 
 | ||||||
|  | 	seq_puts(s, "\n\n"); | ||||||
|  | 	if (!crtc) | ||||||
|  | 		seq_puts(s, "  Not connected to any DRM CRTC\n"); | ||||||
|  | 	else | ||||||
|  | 		seq_printf(s, "  Connected to DRM CRTC #%d (%s)\n", | ||||||
|  | 			   crtc->base.id, sti_mixer_to_str(to_sti_mixer(crtc))); | ||||||
|  | 
 | ||||||
|  | 	mutex_unlock(&dev->struct_mutex); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gdp_node_dump_node(struct seq_file *s, struct sti_gdp_node *node) | ||||||
|  | { | ||||||
|  | 	seq_printf(s, "\t@:0x%p", node); | ||||||
|  | 	seq_printf(s, "\n\tCTL  0x%08X", node->gam_gdp_ctl); | ||||||
|  | 	gdp_dbg_ctl(s, node->gam_gdp_ctl); | ||||||
|  | 	seq_printf(s, "\n\tAGC  0x%08X", node->gam_gdp_agc); | ||||||
|  | 	seq_printf(s, "\n\tVPO  0x%08X", node->gam_gdp_vpo); | ||||||
|  | 	gdp_dbg_vpo(s, node->gam_gdp_vpo); | ||||||
|  | 	seq_printf(s, "\n\tVPS  0x%08X", node->gam_gdp_vps); | ||||||
|  | 	gdp_dbg_vps(s, node->gam_gdp_vps); | ||||||
|  | 	seq_printf(s, "\n\tPML  0x%08X", node->gam_gdp_pml); | ||||||
|  | 	seq_printf(s, "\n\tPMP  0x%08X", node->gam_gdp_pmp); | ||||||
|  | 	seq_printf(s, "\n\tSIZE 0x%08X", node->gam_gdp_size); | ||||||
|  | 	gdp_dbg_size(s, node->gam_gdp_size); | ||||||
|  | 	seq_printf(s, "\n\tNVN  0x%08X", node->gam_gdp_nvn); | ||||||
|  | 	seq_printf(s, "\n\tKEY1 0x%08X", node->gam_gdp_key1); | ||||||
|  | 	seq_printf(s, "\n\tKEY2 0x%08X", node->gam_gdp_key2); | ||||||
|  | 	seq_printf(s, "\n\tPPT  0x%08X", node->gam_gdp_ppt); | ||||||
|  | 	gdp_dbg_ppt(s, node->gam_gdp_ppt); | ||||||
|  | 	seq_printf(s, "\n\tCML  0x%08X", node->gam_gdp_cml); | ||||||
|  | 	seq_puts(s, "\n"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int gdp_node_dbg_show(struct seq_file *s, void *arg) | ||||||
|  | { | ||||||
|  | 	struct drm_info_node *node = s->private; | ||||||
|  | 	struct sti_gdp *gdp = (struct sti_gdp *)node->info_ent->data; | ||||||
|  | 	struct drm_device *dev = node->minor->dev; | ||||||
|  | 	unsigned int b; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = mutex_lock_interruptible(&dev->struct_mutex); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	for (b = 0; b < GDP_NODE_NB_BANK; b++) { | ||||||
|  | 		seq_printf(s, "\n%s[%d].top", sti_plane_to_str(&gdp->plane), b); | ||||||
|  | 		gdp_node_dump_node(s, gdp->node_list[b].top_field); | ||||||
|  | 		seq_printf(s, "\n%s[%d].btm", sti_plane_to_str(&gdp->plane), b); | ||||||
|  | 		gdp_node_dump_node(s, gdp->node_list[b].btm_field); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	mutex_unlock(&dev->struct_mutex); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct drm_info_list gdp0_debugfs_files[] = { | ||||||
|  | 	{ "gdp0", gdp_dbg_show, 0, NULL }, | ||||||
|  | 	{ "gdp0_node", gdp_node_dbg_show, 0, NULL }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static struct drm_info_list gdp1_debugfs_files[] = { | ||||||
|  | 	{ "gdp1", gdp_dbg_show, 0, NULL }, | ||||||
|  | 	{ "gdp1_node", gdp_node_dbg_show, 0, NULL }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static struct drm_info_list gdp2_debugfs_files[] = { | ||||||
|  | 	{ "gdp2", gdp_dbg_show, 0, NULL }, | ||||||
|  | 	{ "gdp2_node", gdp_node_dbg_show, 0, NULL }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static struct drm_info_list gdp3_debugfs_files[] = { | ||||||
|  | 	{ "gdp3", gdp_dbg_show, 0, NULL }, | ||||||
|  | 	{ "gdp3_node", gdp_node_dbg_show, 0, NULL }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int gdp_debugfs_init(struct sti_gdp *gdp, struct drm_minor *minor) | ||||||
|  | { | ||||||
|  | 	unsigned int i; | ||||||
|  | 	struct drm_info_list *gdp_debugfs_files; | ||||||
|  | 	int nb_files; | ||||||
|  | 
 | ||||||
|  | 	switch (gdp->plane.desc) { | ||||||
|  | 	case STI_GDP_0: | ||||||
|  | 		gdp_debugfs_files = gdp0_debugfs_files; | ||||||
|  | 		nb_files = ARRAY_SIZE(gdp0_debugfs_files); | ||||||
|  | 		break; | ||||||
|  | 	case STI_GDP_1: | ||||||
|  | 		gdp_debugfs_files = gdp1_debugfs_files; | ||||||
|  | 		nb_files = ARRAY_SIZE(gdp1_debugfs_files); | ||||||
|  | 		break; | ||||||
|  | 	case STI_GDP_2: | ||||||
|  | 		gdp_debugfs_files = gdp2_debugfs_files; | ||||||
|  | 		nb_files = ARRAY_SIZE(gdp2_debugfs_files); | ||||||
|  | 		break; | ||||||
|  | 	case STI_GDP_3: | ||||||
|  | 		gdp_debugfs_files = gdp3_debugfs_files; | ||||||
|  | 		nb_files = ARRAY_SIZE(gdp3_debugfs_files); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < nb_files; i++) | ||||||
|  | 		gdp_debugfs_files[i].data = gdp; | ||||||
|  | 
 | ||||||
|  | 	return drm_debugfs_create_files(gdp_debugfs_files, | ||||||
|  | 					nb_files, | ||||||
|  | 					minor->debugfs_root, minor); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int sti_gdp_fourcc2format(int fourcc) | static int sti_gdp_fourcc2format(int fourcc) | ||||||
| { | { | ||||||
| 	switch (fourcc) { | 	switch (fourcc) { | ||||||
| @ -146,14 +371,6 @@ static int sti_gdp_fourcc2format(int fourcc) | |||||||
| 		return GDP_RGB565; | 		return GDP_RGB565; | ||||||
| 	case DRM_FORMAT_RGB888: | 	case DRM_FORMAT_RGB888: | ||||||
| 		return GDP_RGB888; | 		return GDP_RGB888; | ||||||
| 	case DRM_FORMAT_AYUV: |  | ||||||
| 		return GDP_AYCBR8888; |  | ||||||
| 	case DRM_FORMAT_YUV444: |  | ||||||
| 		return GDP_YCBR888; |  | ||||||
| 	case DRM_FORMAT_VYUY: |  | ||||||
| 		return GDP_YCBR422R; |  | ||||||
| 	case DRM_FORMAT_C8: |  | ||||||
| 		return GDP_CLUT8; |  | ||||||
| 	} | 	} | ||||||
| 	return -1; | 	return -1; | ||||||
| } | } | ||||||
| @ -163,7 +380,6 @@ static int sti_gdp_get_alpharange(int format) | |||||||
| 	switch (format) { | 	switch (format) { | ||||||
| 	case GDP_ARGB8565: | 	case GDP_ARGB8565: | ||||||
| 	case GDP_ARGB8888: | 	case GDP_ARGB8888: | ||||||
| 	case GDP_AYCBR8888: |  | ||||||
| 	case GDP_ABGR8888: | 	case GDP_ABGR8888: | ||||||
| 		return GAM_GDP_ALPHARANGE_255; | 		return GAM_GDP_ALPHARANGE_255; | ||||||
| 	} | 	} | ||||||
| @ -240,9 +456,6 @@ end: | |||||||
|  */ |  */ | ||||||
| static void sti_gdp_disable(struct sti_gdp *gdp) | static void sti_gdp_disable(struct sti_gdp *gdp) | ||||||
| { | { | ||||||
| 	struct drm_plane *drm_plane = &gdp->plane.drm_plane; |  | ||||||
| 	struct sti_mixer *mixer = to_sti_mixer(drm_plane->crtc); |  | ||||||
| 	struct sti_compositor *compo = dev_get_drvdata(gdp->dev); |  | ||||||
| 	unsigned int i; | 	unsigned int i; | ||||||
| 
 | 
 | ||||||
| 	DRM_DEBUG_DRIVER("%s\n", sti_plane_to_str(&gdp->plane)); | 	DRM_DEBUG_DRIVER("%s\n", sti_plane_to_str(&gdp->plane)); | ||||||
| @ -253,8 +466,7 @@ static void sti_gdp_disable(struct sti_gdp *gdp) | |||||||
| 		gdp->node_list[i].btm_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE; | 		gdp->node_list[i].btm_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (sti_vtg_unregister_client(mixer->id == STI_MIXER_MAIN ? | 	if (sti_vtg_unregister_client(gdp->vtg, &gdp->vtg_field_nb)) | ||||||
| 			compo->vtg_main : compo->vtg_aux, &gdp->vtg_field_nb)) |  | ||||||
| 		DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n"); | 		DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n"); | ||||||
| 
 | 
 | ||||||
| 	if (gdp->clk_pix) | 	if (gdp->clk_pix) | ||||||
| @ -380,37 +592,54 @@ static void sti_gdp_init(struct sti_gdp *gdp) | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void sti_gdp_atomic_update(struct drm_plane *drm_plane, | /**
 | ||||||
| 				  struct drm_plane_state *oldstate) |  * sti_gdp_get_dst | ||||||
|  |  * @dev: device | ||||||
|  |  * @dst: requested destination size | ||||||
|  |  * @src: source size | ||||||
|  |  * | ||||||
|  |  * Return the cropped / clamped destination size | ||||||
|  |  * | ||||||
|  |  * RETURNS: | ||||||
|  |  * cropped / clamped destination size | ||||||
|  |  */ | ||||||
|  | static int sti_gdp_get_dst(struct device *dev, int dst, int src) | ||||||
|  | { | ||||||
|  | 	if (dst == src) | ||||||
|  | 		return dst; | ||||||
|  | 
 | ||||||
|  | 	if (dst < src) { | ||||||
|  | 		dev_dbg(dev, "WARNING: GDP scale not supported, will crop\n"); | ||||||
|  | 		return dst; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	dev_dbg(dev, "WARNING: GDP scale not supported, will clamp\n"); | ||||||
|  | 	return src; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int sti_gdp_atomic_check(struct drm_plane *drm_plane, | ||||||
|  | 				struct drm_plane_state *state) | ||||||
| { | { | ||||||
| 	struct drm_plane_state *state = drm_plane->state; |  | ||||||
| 	struct sti_plane *plane = to_sti_plane(drm_plane); | 	struct sti_plane *plane = to_sti_plane(drm_plane); | ||||||
| 	struct sti_gdp *gdp = to_sti_gdp(plane); | 	struct sti_gdp *gdp = to_sti_gdp(plane); | ||||||
| 	struct drm_crtc *crtc = state->crtc; | 	struct drm_crtc *crtc = state->crtc; | ||||||
| 	struct sti_compositor *compo = dev_get_drvdata(gdp->dev); | 	struct sti_compositor *compo = dev_get_drvdata(gdp->dev); | ||||||
| 	struct drm_framebuffer *fb =  state->fb; | 	struct drm_framebuffer *fb =  state->fb; | ||||||
| 	bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false; | 	bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false; | ||||||
|  | 	struct drm_crtc_state *crtc_state; | ||||||
| 	struct sti_mixer *mixer; | 	struct sti_mixer *mixer; | ||||||
| 	struct drm_display_mode *mode; | 	struct drm_display_mode *mode; | ||||||
| 	int dst_x, dst_y, dst_w, dst_h; | 	int dst_x, dst_y, dst_w, dst_h; | ||||||
| 	int src_x, src_y, src_w, src_h; | 	int src_x, src_y, src_w, src_h; | ||||||
| 	struct drm_gem_cma_object *cma_obj; |  | ||||||
| 	struct sti_gdp_node_list *list; |  | ||||||
| 	struct sti_gdp_node_list *curr_list; |  | ||||||
| 	struct sti_gdp_node *top_field, *btm_field; |  | ||||||
| 	u32 dma_updated_top; |  | ||||||
| 	u32 dma_updated_btm; |  | ||||||
| 	int format; | 	int format; | ||||||
| 	unsigned int depth, bpp; |  | ||||||
| 	u32 ydo, xdo, yds, xds; |  | ||||||
| 	int res; |  | ||||||
| 
 | 
 | ||||||
| 	/* Manage the case where crtc is null (disabled) */ | 	/* no need for further checks if the plane is being disabled */ | ||||||
| 	if (!crtc) | 	if (!crtc || !fb) | ||||||
| 		return; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	mixer = to_sti_mixer(crtc); | 	mixer = to_sti_mixer(crtc); | ||||||
| 	mode = &crtc->mode; | 	crtc_state = drm_atomic_get_crtc_state(state->state, crtc); | ||||||
|  | 	mode = &crtc_state->mode; | ||||||
| 	dst_x = state->crtc_x; | 	dst_x = state->crtc_x; | ||||||
| 	dst_y = state->crtc_y; | 	dst_y = state->crtc_y; | ||||||
| 	dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); | 	dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); | ||||||
| @ -418,92 +647,41 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane, | |||||||
| 	/* src_x are in 16.16 format */ | 	/* src_x are in 16.16 format */ | ||||||
| 	src_x = state->src_x >> 16; | 	src_x = state->src_x >> 16; | ||||||
| 	src_y = state->src_y >> 16; | 	src_y = state->src_y >> 16; | ||||||
| 	src_w = state->src_w >> 16; | 	src_w = clamp_val(state->src_w >> 16, 0, GAM_GDP_SIZE_MAX); | ||||||
| 	src_h = state->src_h >> 16; | 	src_h = clamp_val(state->src_h >> 16, 0, GAM_GDP_SIZE_MAX); | ||||||
| 
 | 
 | ||||||
| 	DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n", |  | ||||||
| 		      crtc->base.id, sti_mixer_to_str(mixer), |  | ||||||
| 		      drm_plane->base.id, sti_plane_to_str(plane)); |  | ||||||
| 	DRM_DEBUG_KMS("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n", |  | ||||||
| 		      sti_plane_to_str(plane), |  | ||||||
| 		      dst_w, dst_h, dst_x, dst_y, |  | ||||||
| 		      src_w, src_h, src_x, src_y); |  | ||||||
| 
 |  | ||||||
| 	list = sti_gdp_get_free_nodes(gdp); |  | ||||||
| 	top_field = list->top_field; |  | ||||||
| 	btm_field = list->btm_field; |  | ||||||
| 
 |  | ||||||
| 	dev_dbg(gdp->dev, "%s %s top_node:0x%p btm_node:0x%p\n", __func__, |  | ||||||
| 		sti_plane_to_str(plane), top_field, btm_field); |  | ||||||
| 
 |  | ||||||
| 	/* build the top field */ |  | ||||||
| 	top_field->gam_gdp_agc = GAM_GDP_AGC_FULL_RANGE; |  | ||||||
| 	top_field->gam_gdp_ctl = WAIT_NEXT_VSYNC; |  | ||||||
| 	format = sti_gdp_fourcc2format(fb->pixel_format); | 	format = sti_gdp_fourcc2format(fb->pixel_format); | ||||||
| 	if (format == -1) { | 	if (format == -1) { | ||||||
| 		DRM_ERROR("Format not supported by GDP %.4s\n", | 		DRM_ERROR("Format not supported by GDP %.4s\n", | ||||||
| 			  (char *)&fb->pixel_format); | 			  (char *)&fb->pixel_format); | ||||||
| 		return; | 		return -EINVAL; | ||||||
| 	} | 	} | ||||||
| 	top_field->gam_gdp_ctl |= format; |  | ||||||
| 	top_field->gam_gdp_ctl |= sti_gdp_get_alpharange(format); |  | ||||||
| 	top_field->gam_gdp_ppt &= ~GAM_GDP_PPT_IGNORE; |  | ||||||
| 
 | 
 | ||||||
| 	cma_obj = drm_fb_cma_get_gem_obj(fb, 0); | 	if (!drm_fb_cma_get_gem_obj(fb, 0)) { | ||||||
| 	if (!cma_obj) { |  | ||||||
| 		DRM_ERROR("Can't get CMA GEM object for fb\n"); | 		DRM_ERROR("Can't get CMA GEM object for fb\n"); | ||||||
| 		return; | 		return -EINVAL; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id, |  | ||||||
| 			 (char *)&fb->pixel_format, |  | ||||||
| 			 (unsigned long)cma_obj->paddr); |  | ||||||
| 
 |  | ||||||
| 	/* pixel memory location */ |  | ||||||
| 	drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp); |  | ||||||
| 	top_field->gam_gdp_pml = (u32)cma_obj->paddr + fb->offsets[0]; |  | ||||||
| 	top_field->gam_gdp_pml += src_x * (bpp >> 3); |  | ||||||
| 	top_field->gam_gdp_pml += src_y * fb->pitches[0]; |  | ||||||
| 
 |  | ||||||
| 	/* input parameters */ |  | ||||||
| 	top_field->gam_gdp_pmp = fb->pitches[0]; |  | ||||||
| 	top_field->gam_gdp_size = clamp_val(src_h, 0, GAM_GDP_SIZE_MAX) << 16 | |  | ||||||
| 				  clamp_val(src_w, 0, GAM_GDP_SIZE_MAX); |  | ||||||
| 
 |  | ||||||
| 	/* output parameters */ |  | ||||||
| 	ydo = sti_vtg_get_line_number(*mode, dst_y); |  | ||||||
| 	yds = sti_vtg_get_line_number(*mode, dst_y + dst_h - 1); |  | ||||||
| 	xdo = sti_vtg_get_pixel_number(*mode, dst_x); |  | ||||||
| 	xds = sti_vtg_get_pixel_number(*mode, dst_x + dst_w - 1); |  | ||||||
| 	top_field->gam_gdp_vpo = (ydo << 16) | xdo; |  | ||||||
| 	top_field->gam_gdp_vps = (yds << 16) | xds; |  | ||||||
| 
 |  | ||||||
| 	/* Same content and chained together */ |  | ||||||
| 	memcpy(btm_field, top_field, sizeof(*btm_field)); |  | ||||||
| 	top_field->gam_gdp_nvn = list->btm_field_paddr; |  | ||||||
| 	btm_field->gam_gdp_nvn = list->top_field_paddr; |  | ||||||
| 
 |  | ||||||
| 	/* Interlaced mode */ |  | ||||||
| 	if (mode->flags & DRM_MODE_FLAG_INTERLACE) |  | ||||||
| 		btm_field->gam_gdp_pml = top_field->gam_gdp_pml + |  | ||||||
| 					 fb->pitches[0]; |  | ||||||
| 
 |  | ||||||
| 	if (first_prepare) { | 	if (first_prepare) { | ||||||
| 		/* Register gdp callback */ | 		/* Register gdp callback */ | ||||||
| 		if (sti_vtg_register_client(mixer->id == STI_MIXER_MAIN ? | 		gdp->vtg = mixer->id == STI_MIXER_MAIN ? | ||||||
| 				compo->vtg_main : compo->vtg_aux, | 					compo->vtg_main : compo->vtg_aux; | ||||||
|  | 		if (sti_vtg_register_client(gdp->vtg, | ||||||
| 					    &gdp->vtg_field_nb, crtc)) { | 					    &gdp->vtg_field_nb, crtc)) { | ||||||
| 			DRM_ERROR("Cannot register VTG notifier\n"); | 			DRM_ERROR("Cannot register VTG notifier\n"); | ||||||
| 			return; | 			return -EINVAL; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		/* Set and enable gdp clock */ | 		/* Set and enable gdp clock */ | ||||||
| 		if (gdp->clk_pix) { | 		if (gdp->clk_pix) { | ||||||
| 			struct clk *clkp; | 			struct clk *clkp; | ||||||
| 			int rate = mode->clock * 1000; | 			int rate = mode->clock * 1000; | ||||||
|  | 			int res; | ||||||
| 
 | 
 | ||||||
| 			/* According to the mixer used, the gdp pixel clock
 | 			/*
 | ||||||
| 			 * should have a different parent clock. */ | 			 * According to the mixer used, the gdp pixel clock | ||||||
|  | 			 * should have a different parent clock. | ||||||
|  | 			 */ | ||||||
| 			if (mixer->id == STI_MIXER_MAIN) | 			if (mixer->id == STI_MIXER_MAIN) | ||||||
| 				clkp = gdp->clk_main_parent; | 				clkp = gdp->clk_main_parent; | ||||||
| 			else | 			else | ||||||
| @ -516,15 +694,113 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane, | |||||||
| 			if (res < 0) { | 			if (res < 0) { | ||||||
| 				DRM_ERROR("Cannot set rate (%dHz) for gdp\n", | 				DRM_ERROR("Cannot set rate (%dHz) for gdp\n", | ||||||
| 					  rate); | 					  rate); | ||||||
| 				return; | 				return -EINVAL; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if (clk_prepare_enable(gdp->clk_pix)) { | 			if (clk_prepare_enable(gdp->clk_pix)) { | ||||||
| 				DRM_ERROR("Failed to prepare/enable gdp\n"); | 				DRM_ERROR("Failed to prepare/enable gdp\n"); | ||||||
|  | 				return -EINVAL; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n", | ||||||
|  | 		      crtc->base.id, sti_mixer_to_str(mixer), | ||||||
|  | 		      drm_plane->base.id, sti_plane_to_str(plane)); | ||||||
|  | 	DRM_DEBUG_KMS("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n", | ||||||
|  | 		      sti_plane_to_str(plane), | ||||||
|  | 		      dst_w, dst_h, dst_x, dst_y, | ||||||
|  | 		      src_w, src_h, src_x, src_y); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void sti_gdp_atomic_update(struct drm_plane *drm_plane, | ||||||
|  | 				  struct drm_plane_state *oldstate) | ||||||
|  | { | ||||||
|  | 	struct drm_plane_state *state = drm_plane->state; | ||||||
|  | 	struct sti_plane *plane = to_sti_plane(drm_plane); | ||||||
|  | 	struct sti_gdp *gdp = to_sti_gdp(plane); | ||||||
|  | 	struct drm_crtc *crtc = state->crtc; | ||||||
|  | 	struct drm_framebuffer *fb =  state->fb; | ||||||
|  | 	struct drm_display_mode *mode; | ||||||
|  | 	int dst_x, dst_y, dst_w, dst_h; | ||||||
|  | 	int src_x, src_y, src_w, src_h; | ||||||
|  | 	struct drm_gem_cma_object *cma_obj; | ||||||
|  | 	struct sti_gdp_node_list *list; | ||||||
|  | 	struct sti_gdp_node_list *curr_list; | ||||||
|  | 	struct sti_gdp_node *top_field, *btm_field; | ||||||
|  | 	u32 dma_updated_top; | ||||||
|  | 	u32 dma_updated_btm; | ||||||
|  | 	int format; | ||||||
|  | 	unsigned int depth, bpp; | ||||||
|  | 	u32 ydo, xdo, yds, xds; | ||||||
|  | 
 | ||||||
|  | 	if (!crtc || !fb) | ||||||
| 		return; | 		return; | ||||||
| 			} | 
 | ||||||
| 		} | 	mode = &crtc->mode; | ||||||
| 	} | 	dst_x = state->crtc_x; | ||||||
|  | 	dst_y = state->crtc_y; | ||||||
|  | 	dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); | ||||||
|  | 	dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); | ||||||
|  | 	/* src_x are in 16.16 format */ | ||||||
|  | 	src_x = state->src_x >> 16; | ||||||
|  | 	src_y = state->src_y >> 16; | ||||||
|  | 	src_w = clamp_val(state->src_w >> 16, 0, GAM_GDP_SIZE_MAX); | ||||||
|  | 	src_h = clamp_val(state->src_h >> 16, 0, GAM_GDP_SIZE_MAX); | ||||||
|  | 
 | ||||||
|  | 	list = sti_gdp_get_free_nodes(gdp); | ||||||
|  | 	top_field = list->top_field; | ||||||
|  | 	btm_field = list->btm_field; | ||||||
|  | 
 | ||||||
|  | 	dev_dbg(gdp->dev, "%s %s top_node:0x%p btm_node:0x%p\n", __func__, | ||||||
|  | 		sti_plane_to_str(plane), top_field, btm_field); | ||||||
|  | 
 | ||||||
|  | 	/* build the top field */ | ||||||
|  | 	top_field->gam_gdp_agc = GAM_GDP_AGC_FULL_RANGE; | ||||||
|  | 	top_field->gam_gdp_ctl = WAIT_NEXT_VSYNC; | ||||||
|  | 	format = sti_gdp_fourcc2format(fb->pixel_format); | ||||||
|  | 	top_field->gam_gdp_ctl |= format; | ||||||
|  | 	top_field->gam_gdp_ctl |= sti_gdp_get_alpharange(format); | ||||||
|  | 	top_field->gam_gdp_ppt &= ~GAM_GDP_PPT_IGNORE; | ||||||
|  | 
 | ||||||
|  | 	cma_obj = drm_fb_cma_get_gem_obj(fb, 0); | ||||||
|  | 
 | ||||||
|  | 	DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id, | ||||||
|  | 			 (char *)&fb->pixel_format, | ||||||
|  | 			 (unsigned long)cma_obj->paddr); | ||||||
|  | 
 | ||||||
|  | 	/* pixel memory location */ | ||||||
|  | 	drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp); | ||||||
|  | 	top_field->gam_gdp_pml = (u32)cma_obj->paddr + fb->offsets[0]; | ||||||
|  | 	top_field->gam_gdp_pml += src_x * (bpp >> 3); | ||||||
|  | 	top_field->gam_gdp_pml += src_y * fb->pitches[0]; | ||||||
|  | 
 | ||||||
|  | 	/* output parameters (clamped / cropped) */ | ||||||
|  | 	dst_w = sti_gdp_get_dst(gdp->dev, dst_w, src_w); | ||||||
|  | 	dst_h = sti_gdp_get_dst(gdp->dev, dst_h, src_h); | ||||||
|  | 	ydo = sti_vtg_get_line_number(*mode, dst_y); | ||||||
|  | 	yds = sti_vtg_get_line_number(*mode, dst_y + dst_h - 1); | ||||||
|  | 	xdo = sti_vtg_get_pixel_number(*mode, dst_x); | ||||||
|  | 	xds = sti_vtg_get_pixel_number(*mode, dst_x + dst_w - 1); | ||||||
|  | 	top_field->gam_gdp_vpo = (ydo << 16) | xdo; | ||||||
|  | 	top_field->gam_gdp_vps = (yds << 16) | xds; | ||||||
|  | 
 | ||||||
|  | 	/* input parameters */ | ||||||
|  | 	src_w = dst_w; | ||||||
|  | 	top_field->gam_gdp_pmp = fb->pitches[0]; | ||||||
|  | 	top_field->gam_gdp_size = src_h << 16 | src_w; | ||||||
|  | 
 | ||||||
|  | 	/* Same content and chained together */ | ||||||
|  | 	memcpy(btm_field, top_field, sizeof(*btm_field)); | ||||||
|  | 	top_field->gam_gdp_nvn = list->btm_field_paddr; | ||||||
|  | 	btm_field->gam_gdp_nvn = list->top_field_paddr; | ||||||
|  | 
 | ||||||
|  | 	/* Interlaced mode */ | ||||||
|  | 	if (mode->flags & DRM_MODE_FLAG_INTERLACE) | ||||||
|  | 		btm_field->gam_gdp_pml = top_field->gam_gdp_pml + | ||||||
|  | 					 fb->pitches[0]; | ||||||
| 
 | 
 | ||||||
| 	/* Update the NVN field of the 'right' field of the current GDP node
 | 	/* Update the NVN field of the 'right' field of the current GDP node
 | ||||||
| 	 * (being used by the HW) with the address of the updated ('free') top | 	 * (being used by the HW) with the address of the updated ('free') top | ||||||
| @ -574,6 +850,8 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane, | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| end: | end: | ||||||
|  | 	sti_plane_update_fps(plane, true, false); | ||||||
|  | 
 | ||||||
| 	plane->status = STI_PLANE_UPDATED; | 	plane->status = STI_PLANE_UPDATED; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -581,7 +859,6 @@ static void sti_gdp_atomic_disable(struct drm_plane *drm_plane, | |||||||
| 				   struct drm_plane_state *oldstate) | 				   struct drm_plane_state *oldstate) | ||||||
| { | { | ||||||
| 	struct sti_plane *plane = to_sti_plane(drm_plane); | 	struct sti_plane *plane = to_sti_plane(drm_plane); | ||||||
| 	struct sti_mixer *mixer = to_sti_mixer(drm_plane->crtc); |  | ||||||
| 
 | 
 | ||||||
| 	if (!drm_plane->crtc) { | 	if (!drm_plane->crtc) { | ||||||
| 		DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", | 		DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", | ||||||
| @ -590,13 +867,15 @@ static void sti_gdp_atomic_disable(struct drm_plane *drm_plane, | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n", | 	DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n", | ||||||
| 			 drm_plane->crtc->base.id, sti_mixer_to_str(mixer), | 			 drm_plane->crtc->base.id, | ||||||
|  | 			 sti_mixer_to_str(to_sti_mixer(drm_plane->crtc)), | ||||||
| 			 drm_plane->base.id, sti_plane_to_str(plane)); | 			 drm_plane->base.id, sti_plane_to_str(plane)); | ||||||
| 
 | 
 | ||||||
| 	plane->status = STI_PLANE_DISABLING; | 	plane->status = STI_PLANE_DISABLING; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static const struct drm_plane_helper_funcs sti_gdp_helpers_funcs = { | static const struct drm_plane_helper_funcs sti_gdp_helpers_funcs = { | ||||||
|  | 	.atomic_check = sti_gdp_atomic_check, | ||||||
| 	.atomic_update = sti_gdp_atomic_update, | 	.atomic_update = sti_gdp_atomic_update, | ||||||
| 	.atomic_disable = sti_gdp_atomic_disable, | 	.atomic_disable = sti_gdp_atomic_disable, | ||||||
| }; | }; | ||||||
| @ -640,6 +919,9 @@ struct drm_plane *sti_gdp_create(struct drm_device *drm_dev, | |||||||
| 
 | 
 | ||||||
| 	sti_plane_init_property(&gdp->plane, type); | 	sti_plane_init_property(&gdp->plane, type); | ||||||
| 
 | 
 | ||||||
|  | 	if (gdp_debugfs_init(gdp, drm_dev->primary)) | ||||||
|  | 		DRM_ERROR("GDP debugfs setup failed\n"); | ||||||
|  | 
 | ||||||
| 	return &gdp->plane.drm_plane; | 	return &gdp->plane.drm_plane; | ||||||
| 
 | 
 | ||||||
| err: | err: | ||||||
|  | |||||||
| @ -326,6 +326,103 @@ static void hda_enable_hd_dacs(struct sti_hda *hda, bool enable) | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #define DBGFS_DUMP(reg) seq_printf(s, "\n  %-25s 0x%08X", #reg, \ | ||||||
|  | 				   readl(hda->regs + reg)) | ||||||
|  | 
 | ||||||
|  | static void hda_dbg_cfg(struct seq_file *s, int val) | ||||||
|  | { | ||||||
|  | 	seq_puts(s, "\tAWG "); | ||||||
|  | 	seq_puts(s, val & CFG_AWG_ASYNC_EN ? "enabled" : "disabled"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void hda_dbg_awg_microcode(struct seq_file *s, void __iomem *reg) | ||||||
|  | { | ||||||
|  | 	unsigned int i; | ||||||
|  | 
 | ||||||
|  | 	seq_puts(s, "\n\n"); | ||||||
|  | 	seq_puts(s, "  HDA AWG microcode:"); | ||||||
|  | 	for (i = 0; i < AWG_MAX_INST; i++) { | ||||||
|  | 		if (i % 8 == 0) | ||||||
|  | 			seq_printf(s, "\n  %04X:", i); | ||||||
|  | 		seq_printf(s, " %04X", readl(reg + i * 4)); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void hda_dbg_video_dacs_ctrl(struct seq_file *s, void __iomem *reg) | ||||||
|  | { | ||||||
|  | 	u32 val = readl(reg); | ||||||
|  | 	u32 mask; | ||||||
|  | 
 | ||||||
|  | 	switch ((u32)reg & VIDEO_DACS_CONTROL_MASK) { | ||||||
|  | 	case VIDEO_DACS_CONTROL_SYSCFG2535: | ||||||
|  | 		mask = DAC_CFG_HD_OFF_MASK; | ||||||
|  | 		break; | ||||||
|  | 	case VIDEO_DACS_CONTROL_SYSCFG5072: | ||||||
|  | 		mask = DAC_CFG_HD_HZUVW_OFF_MASK; | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		DRM_DEBUG_DRIVER("Warning: DACS ctrl register not supported!"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	seq_puts(s, "\n"); | ||||||
|  | 	seq_printf(s, "\n  %-25s 0x%08X", "VIDEO_DACS_CONTROL", val); | ||||||
|  | 	seq_puts(s, "\tHD DACs "); | ||||||
|  | 	seq_puts(s, val & mask ? "disabled" : "enabled"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int hda_dbg_show(struct seq_file *s, void *data) | ||||||
|  | { | ||||||
|  | 	struct drm_info_node *node = s->private; | ||||||
|  | 	struct sti_hda *hda = (struct sti_hda *)node->info_ent->data; | ||||||
|  | 	struct drm_device *dev = node->minor->dev; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = mutex_lock_interruptible(&dev->struct_mutex); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	seq_printf(s, "HD Analog: (vaddr = 0x%p)", hda->regs); | ||||||
|  | 	DBGFS_DUMP(HDA_ANA_CFG); | ||||||
|  | 	hda_dbg_cfg(s, readl(hda->regs + HDA_ANA_CFG)); | ||||||
|  | 	DBGFS_DUMP(HDA_ANA_SCALE_CTRL_Y); | ||||||
|  | 	DBGFS_DUMP(HDA_ANA_SCALE_CTRL_CB); | ||||||
|  | 	DBGFS_DUMP(HDA_ANA_SCALE_CTRL_CR); | ||||||
|  | 	DBGFS_DUMP(HDA_ANA_ANC_CTRL); | ||||||
|  | 	DBGFS_DUMP(HDA_ANA_SRC_Y_CFG); | ||||||
|  | 	DBGFS_DUMP(HDA_ANA_SRC_C_CFG); | ||||||
|  | 	hda_dbg_awg_microcode(s, hda->regs + HDA_SYNC_AWGI); | ||||||
|  | 	if (hda->video_dacs_ctrl) | ||||||
|  | 		hda_dbg_video_dacs_ctrl(s, hda->video_dacs_ctrl); | ||||||
|  | 	seq_puts(s, "\n"); | ||||||
|  | 
 | ||||||
|  | 	mutex_unlock(&dev->struct_mutex); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct drm_info_list hda_debugfs_files[] = { | ||||||
|  | 	{ "hda", hda_dbg_show, 0, NULL }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void hda_debugfs_exit(struct sti_hda *hda, struct drm_minor *minor) | ||||||
|  | { | ||||||
|  | 	drm_debugfs_remove_files(hda_debugfs_files, | ||||||
|  | 				 ARRAY_SIZE(hda_debugfs_files), | ||||||
|  | 				 minor); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int hda_debugfs_init(struct sti_hda *hda, struct drm_minor *minor) | ||||||
|  | { | ||||||
|  | 	unsigned int i; | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(hda_debugfs_files); i++) | ||||||
|  | 		hda_debugfs_files[i].data = hda; | ||||||
|  | 
 | ||||||
|  | 	return drm_debugfs_create_files(hda_debugfs_files, | ||||||
|  | 					ARRAY_SIZE(hda_debugfs_files), | ||||||
|  | 					minor->debugfs_root, minor); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Configure AWG, writing instructions |  * Configure AWG, writing instructions | ||||||
|  * |  * | ||||||
| @ -685,6 +782,12 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data) | |||||||
| 		goto err_sysfs; | 		goto err_sysfs; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/* force to disable hd dacs at startup */ | ||||||
|  | 	hda_enable_hd_dacs(hda, false); | ||||||
|  | 
 | ||||||
|  | 	if (hda_debugfs_init(hda, drm_dev->primary)) | ||||||
|  | 		DRM_ERROR("HDA debugfs setup failed\n"); | ||||||
|  | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | 
 | ||||||
| err_sysfs: | err_sysfs: | ||||||
| @ -697,7 +800,10 @@ err_connector: | |||||||
| static void sti_hda_unbind(struct device *dev, | static void sti_hda_unbind(struct device *dev, | ||||||
| 		struct device *master, void *data) | 		struct device *master, void *data) | ||||||
| { | { | ||||||
| 	/* do nothing */ | 	struct sti_hda *hda = dev_get_drvdata(dev); | ||||||
|  | 	struct drm_device *drm_dev = data; | ||||||
|  | 
 | ||||||
|  | 	hda_debugfs_exit(hda, drm_dev->primary); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static const struct component_ops sti_hda_ops = { | static const struct component_ops sti_hda_ops = { | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ | |||||||
| 
 | 
 | ||||||
| #include <linux/clk.h> | #include <linux/clk.h> | ||||||
| #include <linux/component.h> | #include <linux/component.h> | ||||||
|  | #include <linux/debugfs.h> | ||||||
| #include <linux/hdmi.h> | #include <linux/hdmi.h> | ||||||
| #include <linux/module.h> | #include <linux/module.h> | ||||||
| #include <linux/of_gpio.h> | #include <linux/of_gpio.h> | ||||||
| @ -51,9 +52,18 @@ | |||||||
| #define HDMI_SW_DI_2_PKT_WORD4          0x0614 | #define HDMI_SW_DI_2_PKT_WORD4          0x0614 | ||||||
| #define HDMI_SW_DI_2_PKT_WORD5          0x0618 | #define HDMI_SW_DI_2_PKT_WORD5          0x0618 | ||||||
| #define HDMI_SW_DI_2_PKT_WORD6          0x061C | #define HDMI_SW_DI_2_PKT_WORD6          0x061C | ||||||
|  | #define HDMI_SW_DI_3_HEAD_WORD          0x0620 | ||||||
|  | #define HDMI_SW_DI_3_PKT_WORD0          0x0624 | ||||||
|  | #define HDMI_SW_DI_3_PKT_WORD1          0x0628 | ||||||
|  | #define HDMI_SW_DI_3_PKT_WORD2          0x062C | ||||||
|  | #define HDMI_SW_DI_3_PKT_WORD3          0x0630 | ||||||
|  | #define HDMI_SW_DI_3_PKT_WORD4          0x0634 | ||||||
|  | #define HDMI_SW_DI_3_PKT_WORD5          0x0638 | ||||||
|  | #define HDMI_SW_DI_3_PKT_WORD6          0x063C | ||||||
| 
 | 
 | ||||||
| #define HDMI_IFRAME_SLOT_AVI            1 | #define HDMI_IFRAME_SLOT_AVI            1 | ||||||
| #define HDMI_IFRAME_SLOT_AUDIO          2 | #define HDMI_IFRAME_SLOT_AUDIO          2 | ||||||
|  | #define HDMI_IFRAME_SLOT_VENDOR         3 | ||||||
| 
 | 
 | ||||||
| #define  XCAT(prefix, x, suffix)        prefix ## x ## suffix | #define  XCAT(prefix, x, suffix)        prefix ## x ## suffix | ||||||
| #define  HDMI_SW_DI_N_HEAD_WORD(x)      XCAT(HDMI_SW_DI_, x, _HEAD_WORD) | #define  HDMI_SW_DI_N_HEAD_WORD(x)      XCAT(HDMI_SW_DI_, x, _HEAD_WORD) | ||||||
| @ -65,6 +75,8 @@ | |||||||
| #define  HDMI_SW_DI_N_PKT_WORD5(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD5) | #define  HDMI_SW_DI_N_PKT_WORD5(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD5) | ||||||
| #define  HDMI_SW_DI_N_PKT_WORD6(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD6) | #define  HDMI_SW_DI_N_PKT_WORD6(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD6) | ||||||
| 
 | 
 | ||||||
|  | #define HDMI_SW_DI_MAX_WORD             7 | ||||||
|  | 
 | ||||||
| #define HDMI_IFRAME_DISABLED            0x0 | #define HDMI_IFRAME_DISABLED            0x0 | ||||||
| #define HDMI_IFRAME_SINGLE_SHOT         0x1 | #define HDMI_IFRAME_SINGLE_SHOT         0x1 | ||||||
| #define HDMI_IFRAME_FIELD               0x2 | #define HDMI_IFRAME_FIELD               0x2 | ||||||
| @ -117,6 +129,8 @@ struct sti_hdmi_connector { | |||||||
| 	struct drm_connector drm_connector; | 	struct drm_connector drm_connector; | ||||||
| 	struct drm_encoder *encoder; | 	struct drm_encoder *encoder; | ||||||
| 	struct sti_hdmi *hdmi; | 	struct sti_hdmi *hdmi; | ||||||
|  | 	struct drm_property *colorspace_property; | ||||||
|  | 	struct drm_property *hdmi_mode_property; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #define to_sti_hdmi_connector(x) \ | #define to_sti_hdmi_connector(x) \ | ||||||
| @ -217,8 +231,10 @@ static void hdmi_config(struct sti_hdmi *hdmi) | |||||||
| 	/* Clear overrun and underrun fifo */ | 	/* Clear overrun and underrun fifo */ | ||||||
| 	conf = HDMI_CFG_FIFO_OVERRUN_CLR | HDMI_CFG_FIFO_UNDERRUN_CLR; | 	conf = HDMI_CFG_FIFO_OVERRUN_CLR | HDMI_CFG_FIFO_UNDERRUN_CLR; | ||||||
| 
 | 
 | ||||||
| 	/* Enable HDMI mode not DVI */ | 	/* Select encryption type and the framing mode */ | ||||||
| 	conf |= HDMI_CFG_HDMI_NOT_DVI | HDMI_CFG_ESS_NOT_OESS; | 	conf |= HDMI_CFG_ESS_NOT_OESS; | ||||||
|  | 	if (hdmi->hdmi_mode == HDMI_MODE_HDMI) | ||||||
|  | 		conf |= HDMI_CFG_HDMI_NOT_DVI; | ||||||
| 
 | 
 | ||||||
| 	/* Enable sink term detection */ | 	/* Enable sink term detection */ | ||||||
| 	conf |= HDMI_CFG_SINK_TERM_DET_EN; | 	conf |= HDMI_CFG_SINK_TERM_DET_EN; | ||||||
| @ -241,6 +257,47 @@ static void hdmi_config(struct sti_hdmi *hdmi) | |||||||
| 	hdmi_write(hdmi, conf, HDMI_CFG); | 	hdmi_write(hdmi, conf, HDMI_CFG); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * Helper to reset info frame | ||||||
|  |  * | ||||||
|  |  * @hdmi: pointer on the hdmi internal structure | ||||||
|  |  * @slot: infoframe to reset | ||||||
|  |  */ | ||||||
|  | static void hdmi_infoframe_reset(struct sti_hdmi *hdmi, | ||||||
|  | 				 u32 slot) | ||||||
|  | { | ||||||
|  | 	u32 val, i; | ||||||
|  | 	u32 head_offset, pack_offset; | ||||||
|  | 
 | ||||||
|  | 	switch (slot) { | ||||||
|  | 	case HDMI_IFRAME_SLOT_AVI: | ||||||
|  | 		head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI); | ||||||
|  | 		pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI); | ||||||
|  | 		break; | ||||||
|  | 	case HDMI_IFRAME_SLOT_AUDIO: | ||||||
|  | 		head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AUDIO); | ||||||
|  | 		pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AUDIO); | ||||||
|  | 		break; | ||||||
|  | 	case HDMI_IFRAME_SLOT_VENDOR: | ||||||
|  | 		head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_VENDOR); | ||||||
|  | 		pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_VENDOR); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		DRM_ERROR("unsupported infoframe slot: %#x\n", slot); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* Disable transmission for the selected slot */ | ||||||
|  | 	val = hdmi_read(hdmi, HDMI_SW_DI_CFG); | ||||||
|  | 	val &= ~HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, slot); | ||||||
|  | 	hdmi_write(hdmi, val, HDMI_SW_DI_CFG); | ||||||
|  | 
 | ||||||
|  | 	/* Reset info frame registers */ | ||||||
|  | 	hdmi_write(hdmi, 0x0, head_offset); | ||||||
|  | 	for (i = 0; i < HDMI_SW_DI_MAX_WORD; i += sizeof(u32)) | ||||||
|  | 		hdmi_write(hdmi, 0x0, pack_offset + i); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Helper to concatenate infoframe in 32 bits word |  * Helper to concatenate infoframe in 32 bits word | ||||||
|  * |  * | ||||||
| @ -266,12 +323,13 @@ static inline unsigned int hdmi_infoframe_subpack(const u8 *ptr, size_t size) | |||||||
|  * @data: infoframe to write |  * @data: infoframe to write | ||||||
|  * @size: size to write |  * @size: size to write | ||||||
|  */ |  */ | ||||||
| static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, const u8 *data) | static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, | ||||||
|  | 					  const u8 *data, | ||||||
|  | 					  size_t size) | ||||||
| { | { | ||||||
| 	const u8 *ptr = data; | 	const u8 *ptr = data; | ||||||
| 	u32 val, slot, mode, i; | 	u32 val, slot, mode, i; | ||||||
| 	u32 head_offset, pack_offset; | 	u32 head_offset, pack_offset; | ||||||
| 	size_t size; |  | ||||||
| 
 | 
 | ||||||
| 	switch (*ptr) { | 	switch (*ptr) { | ||||||
| 	case HDMI_INFOFRAME_TYPE_AVI: | 	case HDMI_INFOFRAME_TYPE_AVI: | ||||||
| @ -279,17 +337,19 @@ static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, const u8 *data) | |||||||
| 		mode = HDMI_IFRAME_FIELD; | 		mode = HDMI_IFRAME_FIELD; | ||||||
| 		head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI); | 		head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI); | ||||||
| 		pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI); | 		pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI); | ||||||
| 		size = HDMI_AVI_INFOFRAME_SIZE; |  | ||||||
| 		break; | 		break; | ||||||
| 
 |  | ||||||
| 	case HDMI_INFOFRAME_TYPE_AUDIO: | 	case HDMI_INFOFRAME_TYPE_AUDIO: | ||||||
| 		slot = HDMI_IFRAME_SLOT_AUDIO; | 		slot = HDMI_IFRAME_SLOT_AUDIO; | ||||||
| 		mode = HDMI_IFRAME_FRAME; | 		mode = HDMI_IFRAME_FRAME; | ||||||
| 		head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AUDIO); | 		head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AUDIO); | ||||||
| 		pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AUDIO); | 		pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AUDIO); | ||||||
| 		size = HDMI_AUDIO_INFOFRAME_SIZE; |  | ||||||
| 		break; | 		break; | ||||||
| 
 | 	case HDMI_INFOFRAME_TYPE_VENDOR: | ||||||
|  | 		slot = HDMI_IFRAME_SLOT_VENDOR; | ||||||
|  | 		mode = HDMI_IFRAME_FRAME; | ||||||
|  | 		head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_VENDOR); | ||||||
|  | 		pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_VENDOR); | ||||||
|  | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		DRM_ERROR("unsupported infoframe type: %#x\n", *ptr); | 		DRM_ERROR("unsupported infoframe type: %#x\n", *ptr); | ||||||
| 		return; | 		return; | ||||||
| @ -308,8 +368,9 @@ static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, const u8 *data) | |||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Each subpack contains 4 bytes | 	 * Each subpack contains 4 bytes | ||||||
| 	 * The First Bytes of the first subpacket must contain the checksum | 	 * The First Bytes of the first subpacket must contain the checksum | ||||||
| 	 * Packet size in increase by one. | 	 * Packet size is increase by one. | ||||||
| 	 */ | 	 */ | ||||||
|  | 	size = size - HDMI_INFOFRAME_HEADER_SIZE + 1; | ||||||
| 	for (i = 0; i < size; i += sizeof(u32)) { | 	for (i = 0; i < size; i += sizeof(u32)) { | ||||||
| 		size_t num; | 		size_t num; | ||||||
| 
 | 
 | ||||||
| @ -321,7 +382,7 @@ static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, const u8 *data) | |||||||
| 
 | 
 | ||||||
| 	/* Enable transmission slot for updated infoframe */ | 	/* Enable transmission slot for updated infoframe */ | ||||||
| 	val = hdmi_read(hdmi, HDMI_SW_DI_CFG); | 	val = hdmi_read(hdmi, HDMI_SW_DI_CFG); | ||||||
| 	val |= HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_FIELD, slot); | 	val |= HDMI_IFRAME_CFG_DI_N(mode, slot); | ||||||
| 	hdmi_write(hdmi, val, HDMI_SW_DI_CFG); | 	hdmi_write(hdmi, val, HDMI_SW_DI_CFG); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -352,7 +413,7 @@ static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi) | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* fixed infoframe configuration not linked to the mode */ | 	/* fixed infoframe configuration not linked to the mode */ | ||||||
| 	infoframe.colorspace = HDMI_COLORSPACE_RGB; | 	infoframe.colorspace = hdmi->colorspace; | ||||||
| 	infoframe.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT; | 	infoframe.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT; | ||||||
| 	infoframe.colorimetry = HDMI_COLORIMETRY_NONE; | 	infoframe.colorimetry = HDMI_COLORIMETRY_NONE; | ||||||
| 
 | 
 | ||||||
| @ -362,7 +423,7 @@ static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi) | |||||||
| 		return ret; | 		return ret; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	hdmi_infoframe_write_infopack(hdmi, buffer); | 	hdmi_infoframe_write_infopack(hdmi, buffer, ret); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| @ -398,7 +459,49 @@ static int hdmi_audio_infoframe_config(struct sti_hdmi *hdmi) | |||||||
| 		return ret; | 		return ret; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	hdmi_infoframe_write_infopack(hdmi, buffer); | 	hdmi_infoframe_write_infopack(hdmi, buffer, ret); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Prepare and configure the VS infoframe | ||||||
|  |  * | ||||||
|  |  * Vendor Specific infoframe are transmitted once per frame and | ||||||
|  |  * contains vendor specific information. | ||||||
|  |  * | ||||||
|  |  * @hdmi: pointer on the hdmi internal structure | ||||||
|  |  * | ||||||
|  |  * Return negative value if error occurs | ||||||
|  |  */ | ||||||
|  | #define HDMI_VENDOR_INFOFRAME_MAX_SIZE 6 | ||||||
|  | static int hdmi_vendor_infoframe_config(struct sti_hdmi *hdmi) | ||||||
|  | { | ||||||
|  | 	struct drm_display_mode *mode = &hdmi->mode; | ||||||
|  | 	struct hdmi_vendor_infoframe infoframe; | ||||||
|  | 	u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_VENDOR_INFOFRAME_MAX_SIZE]; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	DRM_DEBUG_DRIVER("\n"); | ||||||
|  | 
 | ||||||
|  | 	ret = drm_hdmi_vendor_infoframe_from_display_mode(&infoframe, mode); | ||||||
|  | 	if (ret < 0) { | ||||||
|  | 		/*
 | ||||||
|  | 		 * Going into that statement does not means vendor infoframe | ||||||
|  | 		 * fails. It just informed us that vendor infoframe is not | ||||||
|  | 		 * needed for the selected mode. Only  4k or stereoscopic 3D | ||||||
|  | 		 * mode requires vendor infoframe. So just simply return 0. | ||||||
|  | 		 */ | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ret = hdmi_vendor_infoframe_pack(&infoframe, buffer, sizeof(buffer)); | ||||||
|  | 	if (ret < 0) { | ||||||
|  | 		DRM_ERROR("failed to pack VS infoframe: %d\n", ret); | ||||||
|  | 		return ret; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	hdmi_infoframe_write_infopack(hdmi, buffer, ret); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| @ -448,6 +551,172 @@ static void hdmi_swreset(struct sti_hdmi *hdmi) | |||||||
| 	clk_disable_unprepare(hdmi->clk_audio); | 	clk_disable_unprepare(hdmi->clk_audio); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #define DBGFS_PRINT_STR(str1, str2) seq_printf(s, "%-24s %s\n", str1, str2) | ||||||
|  | #define DBGFS_PRINT_INT(str1, int2) seq_printf(s, "%-24s %d\n", str1, int2) | ||||||
|  | #define DBGFS_DUMP(str, reg) seq_printf(s, "%s  %-25s 0x%08X", str, #reg, \ | ||||||
|  | 					hdmi_read(hdmi, reg)) | ||||||
|  | #define DBGFS_DUMP_DI(reg, slot) DBGFS_DUMP("\n", reg(slot)) | ||||||
|  | 
 | ||||||
|  | static void hdmi_dbg_cfg(struct seq_file *s, int val) | ||||||
|  | { | ||||||
|  | 	int tmp; | ||||||
|  | 
 | ||||||
|  | 	seq_puts(s, "\t"); | ||||||
|  | 	tmp = val & HDMI_CFG_HDMI_NOT_DVI; | ||||||
|  | 	DBGFS_PRINT_STR("mode:", tmp ? "HDMI" : "DVI"); | ||||||
|  | 	seq_puts(s, "\t\t\t\t\t"); | ||||||
|  | 	tmp = val & HDMI_CFG_HDCP_EN; | ||||||
|  | 	DBGFS_PRINT_STR("HDCP:", tmp ? "enable" : "disable"); | ||||||
|  | 	seq_puts(s, "\t\t\t\t\t"); | ||||||
|  | 	tmp = val & HDMI_CFG_ESS_NOT_OESS; | ||||||
|  | 	DBGFS_PRINT_STR("HDCP mode:", tmp ? "ESS enable" : "OESS enable"); | ||||||
|  | 	seq_puts(s, "\t\t\t\t\t"); | ||||||
|  | 	tmp = val & HDMI_CFG_SINK_TERM_DET_EN; | ||||||
|  | 	DBGFS_PRINT_STR("Sink term detection:", tmp ? "enable" : "disable"); | ||||||
|  | 	seq_puts(s, "\t\t\t\t\t"); | ||||||
|  | 	tmp = val & HDMI_CFG_H_SYNC_POL_NEG; | ||||||
|  | 	DBGFS_PRINT_STR("Hsync polarity:", tmp ? "inverted" : "normal"); | ||||||
|  | 	seq_puts(s, "\t\t\t\t\t"); | ||||||
|  | 	tmp = val & HDMI_CFG_V_SYNC_POL_NEG; | ||||||
|  | 	DBGFS_PRINT_STR("Vsync polarity:", tmp ? "inverted" : "normal"); | ||||||
|  | 	seq_puts(s, "\t\t\t\t\t"); | ||||||
|  | 	tmp = val & HDMI_CFG_422_EN; | ||||||
|  | 	DBGFS_PRINT_STR("YUV422 format:", tmp ? "enable" : "disable"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void hdmi_dbg_sta(struct seq_file *s, int val) | ||||||
|  | { | ||||||
|  | 	int tmp; | ||||||
|  | 
 | ||||||
|  | 	seq_puts(s, "\t"); | ||||||
|  | 	tmp = (val & HDMI_STA_DLL_LCK); | ||||||
|  | 	DBGFS_PRINT_STR("pll:", tmp ? "locked" : "not locked"); | ||||||
|  | 	seq_puts(s, "\t\t\t\t\t"); | ||||||
|  | 	tmp = (val & HDMI_STA_HOT_PLUG); | ||||||
|  | 	DBGFS_PRINT_STR("hdmi cable:", tmp ? "connected" : "not connected"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void hdmi_dbg_sw_di_cfg(struct seq_file *s, int val) | ||||||
|  | { | ||||||
|  | 	int tmp; | ||||||
|  | 	char *const en_di[] = {"no transmission", | ||||||
|  | 			       "single transmission", | ||||||
|  | 			       "once every field", | ||||||
|  | 			       "once every frame"}; | ||||||
|  | 
 | ||||||
|  | 	seq_puts(s, "\t"); | ||||||
|  | 	tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 1)); | ||||||
|  | 	DBGFS_PRINT_STR("Data island 1:", en_di[tmp]); | ||||||
|  | 	seq_puts(s, "\t\t\t\t\t"); | ||||||
|  | 	tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 2)) >> 4; | ||||||
|  | 	DBGFS_PRINT_STR("Data island 2:", en_di[tmp]); | ||||||
|  | 	seq_puts(s, "\t\t\t\t\t"); | ||||||
|  | 	tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 3)) >> 8; | ||||||
|  | 	DBGFS_PRINT_STR("Data island 3:", en_di[tmp]); | ||||||
|  | 	seq_puts(s, "\t\t\t\t\t"); | ||||||
|  | 	tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 4)) >> 12; | ||||||
|  | 	DBGFS_PRINT_STR("Data island 4:", en_di[tmp]); | ||||||
|  | 	seq_puts(s, "\t\t\t\t\t"); | ||||||
|  | 	tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 5)) >> 16; | ||||||
|  | 	DBGFS_PRINT_STR("Data island 5:", en_di[tmp]); | ||||||
|  | 	seq_puts(s, "\t\t\t\t\t"); | ||||||
|  | 	tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 6)) >> 20; | ||||||
|  | 	DBGFS_PRINT_STR("Data island 6:", en_di[tmp]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int hdmi_dbg_show(struct seq_file *s, void *data) | ||||||
|  | { | ||||||
|  | 	struct drm_info_node *node = s->private; | ||||||
|  | 	struct sti_hdmi *hdmi = (struct sti_hdmi *)node->info_ent->data; | ||||||
|  | 	struct drm_device *dev = node->minor->dev; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = mutex_lock_interruptible(&dev->struct_mutex); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	seq_printf(s, "HDMI: (vaddr = 0x%p)", hdmi->regs); | ||||||
|  | 	DBGFS_DUMP("\n", HDMI_CFG); | ||||||
|  | 	hdmi_dbg_cfg(s, hdmi_read(hdmi, HDMI_CFG)); | ||||||
|  | 	DBGFS_DUMP("", HDMI_INT_EN); | ||||||
|  | 	DBGFS_DUMP("\n", HDMI_STA); | ||||||
|  | 	hdmi_dbg_sta(s, hdmi_read(hdmi, HDMI_STA)); | ||||||
|  | 	DBGFS_DUMP("", HDMI_ACTIVE_VID_XMIN); | ||||||
|  | 	seq_puts(s, "\t"); | ||||||
|  | 	DBGFS_PRINT_INT("Xmin:", hdmi_read(hdmi, HDMI_ACTIVE_VID_XMIN)); | ||||||
|  | 	DBGFS_DUMP("", HDMI_ACTIVE_VID_XMAX); | ||||||
|  | 	seq_puts(s, "\t"); | ||||||
|  | 	DBGFS_PRINT_INT("Xmax:", hdmi_read(hdmi, HDMI_ACTIVE_VID_XMAX)); | ||||||
|  | 	DBGFS_DUMP("", HDMI_ACTIVE_VID_YMIN); | ||||||
|  | 	seq_puts(s, "\t"); | ||||||
|  | 	DBGFS_PRINT_INT("Ymin:", hdmi_read(hdmi, HDMI_ACTIVE_VID_YMIN)); | ||||||
|  | 	DBGFS_DUMP("", HDMI_ACTIVE_VID_YMAX); | ||||||
|  | 	seq_puts(s, "\t"); | ||||||
|  | 	DBGFS_PRINT_INT("Ymax:", hdmi_read(hdmi, HDMI_ACTIVE_VID_YMAX)); | ||||||
|  | 	DBGFS_DUMP("", HDMI_SW_DI_CFG); | ||||||
|  | 	hdmi_dbg_sw_di_cfg(s, hdmi_read(hdmi, HDMI_SW_DI_CFG)); | ||||||
|  | 
 | ||||||
|  | 	seq_printf(s, "\n AVI Infoframe (Data Island slot N=%d):", | ||||||
|  | 		   HDMI_IFRAME_SLOT_AVI); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_AVI); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_AVI); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD1, HDMI_IFRAME_SLOT_AVI); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD2, HDMI_IFRAME_SLOT_AVI); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD3, HDMI_IFRAME_SLOT_AVI); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_AVI); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_AVI); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_AVI); | ||||||
|  | 	seq_puts(s, "\n"); | ||||||
|  | 	seq_printf(s, "\n AUDIO Infoframe (Data Island slot N=%d):", | ||||||
|  | 		   HDMI_IFRAME_SLOT_AUDIO); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_AUDIO); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_AUDIO); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD1, HDMI_IFRAME_SLOT_AUDIO); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD2, HDMI_IFRAME_SLOT_AUDIO); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD3, HDMI_IFRAME_SLOT_AUDIO); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_AUDIO); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_AUDIO); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_AUDIO); | ||||||
|  | 	seq_puts(s, "\n"); | ||||||
|  | 	seq_printf(s, "\n VENDOR SPECIFIC Infoframe (Data Island slot N=%d):", | ||||||
|  | 		   HDMI_IFRAME_SLOT_VENDOR); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_VENDOR); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_VENDOR); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD1, HDMI_IFRAME_SLOT_VENDOR); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD2, HDMI_IFRAME_SLOT_VENDOR); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD3, HDMI_IFRAME_SLOT_VENDOR); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_VENDOR); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_VENDOR); | ||||||
|  | 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_VENDOR); | ||||||
|  | 	seq_puts(s, "\n"); | ||||||
|  | 
 | ||||||
|  | 	mutex_unlock(&dev->struct_mutex); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct drm_info_list hdmi_debugfs_files[] = { | ||||||
|  | 	{ "hdmi", hdmi_dbg_show, 0, NULL }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void hdmi_debugfs_exit(struct sti_hdmi *hdmi, struct drm_minor *minor) | ||||||
|  | { | ||||||
|  | 	drm_debugfs_remove_files(hdmi_debugfs_files, | ||||||
|  | 				 ARRAY_SIZE(hdmi_debugfs_files), | ||||||
|  | 				 minor); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int hdmi_debugfs_init(struct sti_hdmi *hdmi, struct drm_minor *minor) | ||||||
|  | { | ||||||
|  | 	unsigned int i; | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(hdmi_debugfs_files); i++) | ||||||
|  | 		hdmi_debugfs_files[i].data = hdmi; | ||||||
|  | 
 | ||||||
|  | 	return drm_debugfs_create_files(hdmi_debugfs_files, | ||||||
|  | 					ARRAY_SIZE(hdmi_debugfs_files), | ||||||
|  | 					minor->debugfs_root, minor); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void sti_hdmi_disable(struct drm_bridge *bridge) | static void sti_hdmi_disable(struct drm_bridge *bridge) | ||||||
| { | { | ||||||
| 	struct sti_hdmi *hdmi = bridge->driver_private; | 	struct sti_hdmi *hdmi = bridge->driver_private; | ||||||
| @ -468,6 +737,11 @@ static void sti_hdmi_disable(struct drm_bridge *bridge) | |||||||
| 	/* Stop the phy */ | 	/* Stop the phy */ | ||||||
| 	hdmi->phy_ops->stop(hdmi); | 	hdmi->phy_ops->stop(hdmi); | ||||||
| 
 | 
 | ||||||
|  | 	/* Reset info frame transmission */ | ||||||
|  | 	hdmi_infoframe_reset(hdmi, HDMI_IFRAME_SLOT_AVI); | ||||||
|  | 	hdmi_infoframe_reset(hdmi, HDMI_IFRAME_SLOT_AUDIO); | ||||||
|  | 	hdmi_infoframe_reset(hdmi, HDMI_IFRAME_SLOT_VENDOR); | ||||||
|  | 
 | ||||||
| 	/* Set the default channel data to be a dark red */ | 	/* Set the default channel data to be a dark red */ | ||||||
| 	hdmi_write(hdmi, 0x0000, HDMI_DFLT_CHL0_DAT); | 	hdmi_write(hdmi, 0x0000, HDMI_DFLT_CHL0_DAT); | ||||||
| 	hdmi_write(hdmi, 0x0000, HDMI_DFLT_CHL1_DAT); | 	hdmi_write(hdmi, 0x0000, HDMI_DFLT_CHL1_DAT); | ||||||
| @ -523,6 +797,10 @@ static void sti_hdmi_pre_enable(struct drm_bridge *bridge) | |||||||
| 	if (hdmi_audio_infoframe_config(hdmi)) | 	if (hdmi_audio_infoframe_config(hdmi)) | ||||||
| 		DRM_ERROR("Unable to configure AUDIO infoframe\n"); | 		DRM_ERROR("Unable to configure AUDIO infoframe\n"); | ||||||
| 
 | 
 | ||||||
|  | 	/* Program VS infoframe */ | ||||||
|  | 	if (hdmi_vendor_infoframe_config(hdmi)) | ||||||
|  | 		DRM_ERROR("Unable to configure VS infoframe\n"); | ||||||
|  | 
 | ||||||
| 	/* Sw reset */ | 	/* Sw reset */ | ||||||
| 	hdmi_swreset(hdmi); | 	hdmi_swreset(hdmi); | ||||||
| } | } | ||||||
| @ -664,12 +942,97 @@ static void sti_hdmi_connector_destroy(struct drm_connector *connector) | |||||||
| 	kfree(hdmi_connector); | 	kfree(hdmi_connector); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void sti_hdmi_connector_init_property(struct drm_device *drm_dev, | ||||||
|  | 					     struct drm_connector *connector) | ||||||
|  | { | ||||||
|  | 	struct sti_hdmi_connector *hdmi_connector | ||||||
|  | 		= to_sti_hdmi_connector(connector); | ||||||
|  | 	struct sti_hdmi *hdmi = hdmi_connector->hdmi; | ||||||
|  | 	struct drm_property *prop; | ||||||
|  | 
 | ||||||
|  | 	/* colorspace property */ | ||||||
|  | 	hdmi->colorspace = DEFAULT_COLORSPACE_MODE; | ||||||
|  | 	prop = drm_property_create_enum(drm_dev, 0, "colorspace", | ||||||
|  | 					colorspace_mode_names, | ||||||
|  | 					ARRAY_SIZE(colorspace_mode_names)); | ||||||
|  | 	if (!prop) { | ||||||
|  | 		DRM_ERROR("fails to create colorspace property\n"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	hdmi_connector->colorspace_property = prop; | ||||||
|  | 	drm_object_attach_property(&connector->base, prop, hdmi->colorspace); | ||||||
|  | 
 | ||||||
|  | 	/* hdmi_mode property */ | ||||||
|  | 	hdmi->hdmi_mode = DEFAULT_HDMI_MODE; | ||||||
|  | 	prop = drm_property_create_enum(drm_dev, 0, "hdmi_mode", | ||||||
|  | 					hdmi_mode_names, | ||||||
|  | 					ARRAY_SIZE(hdmi_mode_names)); | ||||||
|  | 	if (!prop) { | ||||||
|  | 		DRM_ERROR("fails to create colorspace property\n"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	hdmi_connector->hdmi_mode_property = prop; | ||||||
|  | 	drm_object_attach_property(&connector->base, prop, hdmi->hdmi_mode); | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int | ||||||
|  | sti_hdmi_connector_set_property(struct drm_connector *connector, | ||||||
|  | 				struct drm_connector_state *state, | ||||||
|  | 				struct drm_property *property, | ||||||
|  | 				uint64_t val) | ||||||
|  | { | ||||||
|  | 	struct sti_hdmi_connector *hdmi_connector | ||||||
|  | 		= to_sti_hdmi_connector(connector); | ||||||
|  | 	struct sti_hdmi *hdmi = hdmi_connector->hdmi; | ||||||
|  | 
 | ||||||
|  | 	if (property == hdmi_connector->colorspace_property) { | ||||||
|  | 		hdmi->colorspace = val; | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (property == hdmi_connector->hdmi_mode_property) { | ||||||
|  | 		hdmi->hdmi_mode = val; | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	DRM_ERROR("failed to set hdmi connector property\n"); | ||||||
|  | 	return -EINVAL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int | ||||||
|  | sti_hdmi_connector_get_property(struct drm_connector *connector, | ||||||
|  | 				const struct drm_connector_state *state, | ||||||
|  | 				struct drm_property *property, | ||||||
|  | 				uint64_t *val) | ||||||
|  | { | ||||||
|  | 	struct sti_hdmi_connector *hdmi_connector | ||||||
|  | 		= to_sti_hdmi_connector(connector); | ||||||
|  | 	struct sti_hdmi *hdmi = hdmi_connector->hdmi; | ||||||
|  | 
 | ||||||
|  | 	if (property == hdmi_connector->colorspace_property) { | ||||||
|  | 		*val = hdmi->colorspace; | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (property == hdmi_connector->hdmi_mode_property) { | ||||||
|  | 		*val = hdmi->hdmi_mode; | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	DRM_ERROR("failed to get hdmi connector property\n"); | ||||||
|  | 	return -EINVAL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static const struct drm_connector_funcs sti_hdmi_connector_funcs = { | static const struct drm_connector_funcs sti_hdmi_connector_funcs = { | ||||||
| 	.dpms = drm_atomic_helper_connector_dpms, | 	.dpms = drm_atomic_helper_connector_dpms, | ||||||
| 	.fill_modes = drm_helper_probe_single_connector_modes, | 	.fill_modes = drm_helper_probe_single_connector_modes, | ||||||
| 	.detect = sti_hdmi_connector_detect, | 	.detect = sti_hdmi_connector_detect, | ||||||
| 	.destroy = sti_hdmi_connector_destroy, | 	.destroy = sti_hdmi_connector_destroy, | ||||||
| 	.reset = drm_atomic_helper_connector_reset, | 	.reset = drm_atomic_helper_connector_reset, | ||||||
|  | 	.set_property = drm_atomic_helper_connector_set_property, | ||||||
|  | 	.atomic_set_property = sti_hdmi_connector_set_property, | ||||||
|  | 	.atomic_get_property = sti_hdmi_connector_get_property, | ||||||
| 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | ||||||
| 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | ||||||
| }; | }; | ||||||
| @ -729,6 +1092,9 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) | |||||||
| 	drm_connector_helper_add(drm_connector, | 	drm_connector_helper_add(drm_connector, | ||||||
| 			&sti_hdmi_connector_helper_funcs); | 			&sti_hdmi_connector_helper_funcs); | ||||||
| 
 | 
 | ||||||
|  | 	/* initialise property */ | ||||||
|  | 	sti_hdmi_connector_init_property(drm_dev, drm_connector); | ||||||
|  | 
 | ||||||
| 	err = drm_connector_register(drm_connector); | 	err = drm_connector_register(drm_connector); | ||||||
| 	if (err) | 	if (err) | ||||||
| 		goto err_connector; | 		goto err_connector; | ||||||
| @ -742,6 +1108,9 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) | |||||||
| 	/* Enable default interrupts */ | 	/* Enable default interrupts */ | ||||||
| 	hdmi_write(hdmi, HDMI_DEFAULT_INT, HDMI_INT_EN); | 	hdmi_write(hdmi, HDMI_DEFAULT_INT, HDMI_INT_EN); | ||||||
| 
 | 
 | ||||||
|  | 	if (hdmi_debugfs_init(hdmi, drm_dev->primary)) | ||||||
|  | 		DRM_ERROR("HDMI debugfs setup failed\n"); | ||||||
|  | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | 
 | ||||||
| err_sysfs: | err_sysfs: | ||||||
| @ -755,7 +1124,10 @@ err_connector: | |||||||
| static void sti_hdmi_unbind(struct device *dev, | static void sti_hdmi_unbind(struct device *dev, | ||||||
| 		struct device *master, void *data) | 		struct device *master, void *data) | ||||||
| { | { | ||||||
| 	/* do nothing */ | 	struct sti_hdmi *hdmi = dev_get_drvdata(dev); | ||||||
|  | 	struct drm_device *drm_dev = data; | ||||||
|  | 
 | ||||||
|  | 	hdmi_debugfs_exit(hdmi, drm_dev->primary); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static const struct component_ops sti_hdmi_ops = { | static const struct component_ops sti_hdmi_ops = { | ||||||
|  | |||||||
| @ -7,15 +7,14 @@ | |||||||
| #ifndef _STI_HDMI_H_ | #ifndef _STI_HDMI_H_ | ||||||
| #define _STI_HDMI_H_ | #define _STI_HDMI_H_ | ||||||
| 
 | 
 | ||||||
|  | #include <linux/hdmi.h> | ||||||
| #include <linux/platform_device.h> | #include <linux/platform_device.h> | ||||||
| 
 | 
 | ||||||
| #include <drm/drmP.h> | #include <drm/drmP.h> | ||||||
| 
 | 
 | ||||||
| #define HDMI_STA           0x0010 | #define HDMI_STA           0x0010 | ||||||
| #define HDMI_STA_DLL_LCK   BIT(5) | #define HDMI_STA_DLL_LCK   BIT(5) | ||||||
| 
 | #define HDMI_STA_HOT_PLUG  BIT(4) | ||||||
| #define HDMI_STA_HOT_PLUG_SHIFT 4 |  | ||||||
| #define HDMI_STA_HOT_PLUG	(1 << HDMI_STA_HOT_PLUG_SHIFT) |  | ||||||
| 
 | 
 | ||||||
| struct sti_hdmi; | struct sti_hdmi; | ||||||
| 
 | 
 | ||||||
| @ -24,6 +23,27 @@ struct hdmi_phy_ops { | |||||||
| 	void (*stop)(struct sti_hdmi *hdmi); | 	void (*stop)(struct sti_hdmi *hdmi); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /* values for the framing mode property */ | ||||||
|  | enum sti_hdmi_modes { | ||||||
|  | 	HDMI_MODE_HDMI, | ||||||
|  | 	HDMI_MODE_DVI, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const struct drm_prop_enum_list hdmi_mode_names[] = { | ||||||
|  | 	{ HDMI_MODE_HDMI, "hdmi" }, | ||||||
|  | 	{ HDMI_MODE_DVI, "dvi" }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define DEFAULT_HDMI_MODE HDMI_MODE_HDMI | ||||||
|  | 
 | ||||||
|  | static const struct drm_prop_enum_list colorspace_mode_names[] = { | ||||||
|  | 	{ HDMI_COLORSPACE_RGB, "rgb" }, | ||||||
|  | 	{ HDMI_COLORSPACE_YUV422, "yuv422" }, | ||||||
|  | 	{ HDMI_COLORSPACE_YUV444, "yuv444" }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define DEFAULT_COLORSPACE_MODE HDMI_COLORSPACE_RGB | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * STI hdmi structure |  * STI hdmi structure | ||||||
|  * |  * | ||||||
| @ -44,6 +64,9 @@ struct hdmi_phy_ops { | |||||||
|  * @wait_event: wait event |  * @wait_event: wait event | ||||||
|  * @event_received: wait event status |  * @event_received: wait event status | ||||||
|  * @reset: reset control of the hdmi phy |  * @reset: reset control of the hdmi phy | ||||||
|  |  * @ddc_adapt: i2c ddc adapter | ||||||
|  |  * @colorspace: current colorspace selected | ||||||
|  |  * @hdmi_mode: select framing for HDMI or DVI | ||||||
|  */ |  */ | ||||||
| struct sti_hdmi { | struct sti_hdmi { | ||||||
| 	struct device dev; | 	struct device dev; | ||||||
| @ -64,6 +87,8 @@ struct sti_hdmi { | |||||||
| 	bool event_received; | 	bool event_received; | ||||||
| 	struct reset_control *reset; | 	struct reset_control *reset; | ||||||
| 	struct i2c_adapter *ddc_adapt; | 	struct i2c_adapter *ddc_adapt; | ||||||
|  | 	enum hdmi_colorspace colorspace; | ||||||
|  | 	enum sti_hdmi_modes hdmi_mode; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| u32 hdmi_read(struct sti_hdmi *hdmi, int offset); | u32 hdmi_read(struct sti_hdmi *hdmi, int offset); | ||||||
|  | |||||||
| @ -4,14 +4,11 @@ | |||||||
|  * License terms:  GNU General Public License (GPL), version 2 |  * License terms:  GNU General Public License (GPL), version 2 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include <linux/clk.h> |  | ||||||
| #include <linux/component.h> | #include <linux/component.h> | ||||||
| #include <linux/firmware.h> | #include <linux/firmware.h> | ||||||
| #include <linux/module.h> |  | ||||||
| #include <linux/platform_device.h> |  | ||||||
| #include <linux/reset.h> | #include <linux/reset.h> | ||||||
| 
 | 
 | ||||||
| #include <drm/drmP.h> | #include <drm/drm_atomic.h> | ||||||
| #include <drm/drm_fb_cma_helper.h> | #include <drm/drm_fb_cma_helper.h> | ||||||
| #include <drm/drm_gem_cma_helper.h> | #include <drm/drm_gem_cma_helper.h> | ||||||
| 
 | 
 | ||||||
| @ -329,8 +326,6 @@ struct sti_hqvdp_cmd { | |||||||
|  * @reset:             reset control |  * @reset:             reset control | ||||||
|  * @vtg_nb:            notifier to handle VTG Vsync |  * @vtg_nb:            notifier to handle VTG Vsync | ||||||
|  * @btm_field_pending: is there any bottom field (interlaced frame) to display |  * @btm_field_pending: is there any bottom field (interlaced frame) to display | ||||||
|  * @curr_field_count:  number of field updates |  | ||||||
|  * @last_field_count:  number of field updates since last fps measure |  | ||||||
|  * @hqvdp_cmd:         buffer of commands |  * @hqvdp_cmd:         buffer of commands | ||||||
|  * @hqvdp_cmd_paddr:   physical address of hqvdp_cmd |  * @hqvdp_cmd_paddr:   physical address of hqvdp_cmd | ||||||
|  * @vtg:               vtg for main data path |  * @vtg:               vtg for main data path | ||||||
| @ -346,10 +341,8 @@ struct sti_hqvdp { | |||||||
| 	struct reset_control *reset; | 	struct reset_control *reset; | ||||||
| 	struct notifier_block vtg_nb; | 	struct notifier_block vtg_nb; | ||||||
| 	bool btm_field_pending; | 	bool btm_field_pending; | ||||||
| 	unsigned int curr_field_count; |  | ||||||
| 	unsigned int last_field_count; |  | ||||||
| 	void *hqvdp_cmd; | 	void *hqvdp_cmd; | ||||||
| 	dma_addr_t hqvdp_cmd_paddr; | 	u32 hqvdp_cmd_paddr; | ||||||
| 	struct sti_vtg *vtg; | 	struct sti_vtg *vtg; | ||||||
| 	bool xp70_initialized; | 	bool xp70_initialized; | ||||||
| }; | }; | ||||||
| @ -372,8 +365,8 @@ static const uint32_t hqvdp_supported_formats[] = { | |||||||
|  */ |  */ | ||||||
| static int sti_hqvdp_get_free_cmd(struct sti_hqvdp *hqvdp) | static int sti_hqvdp_get_free_cmd(struct sti_hqvdp *hqvdp) | ||||||
| { | { | ||||||
| 	int curr_cmd, next_cmd; | 	u32 curr_cmd, next_cmd; | ||||||
| 	dma_addr_t cmd = hqvdp->hqvdp_cmd_paddr; | 	u32 cmd = hqvdp->hqvdp_cmd_paddr; | ||||||
| 	int i; | 	int i; | ||||||
| 
 | 
 | ||||||
| 	curr_cmd = readl(hqvdp->regs + HQVDP_MBX_CURRENT_CMD); | 	curr_cmd = readl(hqvdp->regs + HQVDP_MBX_CURRENT_CMD); | ||||||
| @ -400,8 +393,8 @@ static int sti_hqvdp_get_free_cmd(struct sti_hqvdp *hqvdp) | |||||||
|  */ |  */ | ||||||
| static int sti_hqvdp_get_curr_cmd(struct sti_hqvdp *hqvdp) | static int sti_hqvdp_get_curr_cmd(struct sti_hqvdp *hqvdp) | ||||||
| { | { | ||||||
| 	int curr_cmd; | 	u32 curr_cmd; | ||||||
| 	dma_addr_t cmd = hqvdp->hqvdp_cmd_paddr; | 	u32 cmd = hqvdp->hqvdp_cmd_paddr; | ||||||
| 	unsigned int i; | 	unsigned int i; | ||||||
| 
 | 
 | ||||||
| 	curr_cmd = readl(hqvdp->regs + HQVDP_MBX_CURRENT_CMD); | 	curr_cmd = readl(hqvdp->regs + HQVDP_MBX_CURRENT_CMD); | ||||||
| @ -416,6 +409,246 @@ static int sti_hqvdp_get_curr_cmd(struct sti_hqvdp *hqvdp) | |||||||
| 	return -1; | 	return -1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * sti_hqvdp_get_next_cmd | ||||||
|  |  * @hqvdp: hqvdp structure | ||||||
|  |  * | ||||||
|  |  * Look for the next hqvdp_cmd that will be used by the FW. | ||||||
|  |  * | ||||||
|  |  * RETURNS: | ||||||
|  |  *  the offset of the next command that will be used. | ||||||
|  |  * -1 in error cases | ||||||
|  |  */ | ||||||
|  | static int sti_hqvdp_get_next_cmd(struct sti_hqvdp *hqvdp) | ||||||
|  | { | ||||||
|  | 	int next_cmd; | ||||||
|  | 	dma_addr_t cmd = hqvdp->hqvdp_cmd_paddr; | ||||||
|  | 	unsigned int i; | ||||||
|  | 
 | ||||||
|  | 	next_cmd = readl(hqvdp->regs + HQVDP_MBX_NEXT_CMD); | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < NB_VDP_CMD; i++) { | ||||||
|  | 		if (cmd == next_cmd) | ||||||
|  | 			return i * sizeof(struct sti_hqvdp_cmd); | ||||||
|  | 
 | ||||||
|  | 		cmd += sizeof(struct sti_hqvdp_cmd); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #define DBGFS_DUMP(reg) seq_printf(s, "\n  %-25s 0x%08X", #reg, \ | ||||||
|  | 				   readl(hqvdp->regs + reg)) | ||||||
|  | 
 | ||||||
|  | static const char *hqvdp_dbg_get_lut(u32 *coef) | ||||||
|  | { | ||||||
|  | 	if (!memcmp(coef, coef_lut_a_legacy, 16)) | ||||||
|  | 		return "LUT A"; | ||||||
|  | 	if (!memcmp(coef, coef_lut_b, 16)) | ||||||
|  | 		return "LUT B"; | ||||||
|  | 	if (!memcmp(coef, coef_lut_c_y_legacy, 16)) | ||||||
|  | 		return "LUT C Y"; | ||||||
|  | 	if (!memcmp(coef, coef_lut_c_c_legacy, 16)) | ||||||
|  | 		return "LUT C C"; | ||||||
|  | 	if (!memcmp(coef, coef_lut_d_y_legacy, 16)) | ||||||
|  | 		return "LUT D Y"; | ||||||
|  | 	if (!memcmp(coef, coef_lut_d_c_legacy, 16)) | ||||||
|  | 		return "LUT D C"; | ||||||
|  | 	if (!memcmp(coef, coef_lut_e_y_legacy, 16)) | ||||||
|  | 		return "LUT E Y"; | ||||||
|  | 	if (!memcmp(coef, coef_lut_e_c_legacy, 16)) | ||||||
|  | 		return "LUT E C"; | ||||||
|  | 	if (!memcmp(coef, coef_lut_f_y_legacy, 16)) | ||||||
|  | 		return "LUT F Y"; | ||||||
|  | 	if (!memcmp(coef, coef_lut_f_c_legacy, 16)) | ||||||
|  | 		return "LUT F C"; | ||||||
|  | 	return "<UNKNOWN>"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void hqvdp_dbg_dump_cmd(struct seq_file *s, struct sti_hqvdp_cmd *c) | ||||||
|  | { | ||||||
|  | 	int src_w, src_h, dst_w, dst_h; | ||||||
|  | 
 | ||||||
|  | 	seq_puts(s, "\n\tTOP:"); | ||||||
|  | 	seq_printf(s, "\n\t %-20s 0x%08X", "Config", c->top.config); | ||||||
|  | 	switch (c->top.config) { | ||||||
|  | 	case TOP_CONFIG_PROGRESSIVE: | ||||||
|  | 		seq_puts(s, "\tProgressive"); | ||||||
|  | 		break; | ||||||
|  | 	case TOP_CONFIG_INTER_TOP: | ||||||
|  | 		seq_puts(s, "\tInterlaced, top field"); | ||||||
|  | 		break; | ||||||
|  | 	case TOP_CONFIG_INTER_BTM: | ||||||
|  | 		seq_puts(s, "\tInterlaced, bottom field"); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		seq_puts(s, "\t<UNKNOWN>"); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	seq_printf(s, "\n\t %-20s 0x%08X", "MemFormat", c->top.mem_format); | ||||||
|  | 	seq_printf(s, "\n\t %-20s 0x%08X", "CurrentY", c->top.current_luma); | ||||||
|  | 	seq_printf(s, "\n\t %-20s 0x%08X", "CurrentC", c->top.current_chroma); | ||||||
|  | 	seq_printf(s, "\n\t %-20s 0x%08X", "YSrcPitch", c->top.luma_src_pitch); | ||||||
|  | 	seq_printf(s, "\n\t %-20s 0x%08X", "CSrcPitch", | ||||||
|  | 		   c->top.chroma_src_pitch); | ||||||
|  | 	seq_printf(s, "\n\t %-20s 0x%08X", "InputFrameSize", | ||||||
|  | 		   c->top.input_frame_size); | ||||||
|  | 	seq_printf(s, "\t%dx%d", | ||||||
|  | 		   c->top.input_frame_size & 0x0000FFFF, | ||||||
|  | 		   c->top.input_frame_size >> 16); | ||||||
|  | 	seq_printf(s, "\n\t %-20s 0x%08X", "InputViewportSize", | ||||||
|  | 		   c->top.input_viewport_size); | ||||||
|  | 	src_w = c->top.input_viewport_size & 0x0000FFFF; | ||||||
|  | 	src_h = c->top.input_viewport_size >> 16; | ||||||
|  | 	seq_printf(s, "\t%dx%d", src_w, src_h); | ||||||
|  | 
 | ||||||
|  | 	seq_puts(s, "\n\tHVSRC:"); | ||||||
|  | 	seq_printf(s, "\n\t %-20s 0x%08X", "OutputPictureSize", | ||||||
|  | 		   c->hvsrc.output_picture_size); | ||||||
|  | 	dst_w = c->hvsrc.output_picture_size & 0x0000FFFF; | ||||||
|  | 	dst_h = c->hvsrc.output_picture_size >> 16; | ||||||
|  | 	seq_printf(s, "\t%dx%d", dst_w, dst_h); | ||||||
|  | 	seq_printf(s, "\n\t %-20s 0x%08X", "ParamCtrl", c->hvsrc.param_ctrl); | ||||||
|  | 
 | ||||||
|  | 	seq_printf(s, "\n\t %-20s %s", "yh_coef", | ||||||
|  | 		   hqvdp_dbg_get_lut(c->hvsrc.yh_coef)); | ||||||
|  | 	seq_printf(s, "\n\t %-20s %s", "ch_coef", | ||||||
|  | 		   hqvdp_dbg_get_lut(c->hvsrc.ch_coef)); | ||||||
|  | 	seq_printf(s, "\n\t %-20s %s", "yv_coef", | ||||||
|  | 		   hqvdp_dbg_get_lut(c->hvsrc.yv_coef)); | ||||||
|  | 	seq_printf(s, "\n\t %-20s %s", "cv_coef", | ||||||
|  | 		   hqvdp_dbg_get_lut(c->hvsrc.cv_coef)); | ||||||
|  | 
 | ||||||
|  | 	seq_printf(s, "\n\t %-20s", "ScaleH"); | ||||||
|  | 	if (dst_w > src_w) | ||||||
|  | 		seq_printf(s, " %d/1", dst_w / src_w); | ||||||
|  | 	else | ||||||
|  | 		seq_printf(s, " 1/%d", src_w / dst_w); | ||||||
|  | 
 | ||||||
|  | 	seq_printf(s, "\n\t %-20s", "tScaleV"); | ||||||
|  | 	if (dst_h > src_h) | ||||||
|  | 		seq_printf(s, " %d/1", dst_h / src_h); | ||||||
|  | 	else | ||||||
|  | 		seq_printf(s, " 1/%d", src_h / dst_h); | ||||||
|  | 
 | ||||||
|  | 	seq_puts(s, "\n\tCSDI:"); | ||||||
|  | 	seq_printf(s, "\n\t %-20s 0x%08X\t", "Config", c->csdi.config); | ||||||
|  | 	switch (c->csdi.config) { | ||||||
|  | 	case CSDI_CONFIG_PROG: | ||||||
|  | 		seq_puts(s, "Bypass"); | ||||||
|  | 		break; | ||||||
|  | 	case CSDI_CONFIG_INTER_DIR: | ||||||
|  | 		seq_puts(s, "Deinterlace, directional"); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		seq_puts(s, "<UNKNOWN>"); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	seq_printf(s, "\n\t %-20s 0x%08X", "Config2", c->csdi.config2); | ||||||
|  | 	seq_printf(s, "\n\t %-20s 0x%08X", "DcdiConfig", c->csdi.dcdi_config); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int hqvdp_dbg_show(struct seq_file *s, void *data) | ||||||
|  | { | ||||||
|  | 	struct drm_info_node *node = s->private; | ||||||
|  | 	struct sti_hqvdp *hqvdp = (struct sti_hqvdp *)node->info_ent->data; | ||||||
|  | 	struct drm_device *dev = node->minor->dev; | ||||||
|  | 	int cmd, cmd_offset, infoxp70; | ||||||
|  | 	void *virt; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = mutex_lock_interruptible(&dev->struct_mutex); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	seq_printf(s, "%s: (vaddr = 0x%p)", | ||||||
|  | 		   sti_plane_to_str(&hqvdp->plane), hqvdp->regs); | ||||||
|  | 
 | ||||||
|  | 	DBGFS_DUMP(HQVDP_MBX_IRQ_TO_XP70); | ||||||
|  | 	DBGFS_DUMP(HQVDP_MBX_INFO_HOST); | ||||||
|  | 	DBGFS_DUMP(HQVDP_MBX_IRQ_TO_HOST); | ||||||
|  | 	DBGFS_DUMP(HQVDP_MBX_INFO_XP70); | ||||||
|  | 	infoxp70 = readl(hqvdp->regs + HQVDP_MBX_INFO_XP70); | ||||||
|  | 	seq_puts(s, "\tFirmware state: "); | ||||||
|  | 	if (infoxp70 & INFO_XP70_FW_READY) | ||||||
|  | 		seq_puts(s, "idle and ready"); | ||||||
|  | 	else if (infoxp70 & INFO_XP70_FW_PROCESSING) | ||||||
|  | 		seq_puts(s, "processing a picture"); | ||||||
|  | 	else if (infoxp70 & INFO_XP70_FW_INITQUEUES) | ||||||
|  | 		seq_puts(s, "programming queues"); | ||||||
|  | 	else | ||||||
|  | 		seq_puts(s, "NOT READY"); | ||||||
|  | 
 | ||||||
|  | 	DBGFS_DUMP(HQVDP_MBX_SW_RESET_CTRL); | ||||||
|  | 	DBGFS_DUMP(HQVDP_MBX_STARTUP_CTRL1); | ||||||
|  | 	if (readl(hqvdp->regs + HQVDP_MBX_STARTUP_CTRL1) | ||||||
|  | 					& STARTUP_CTRL1_RST_DONE) | ||||||
|  | 		seq_puts(s, "\tReset is done"); | ||||||
|  | 	else | ||||||
|  | 		seq_puts(s, "\tReset is NOT done"); | ||||||
|  | 	DBGFS_DUMP(HQVDP_MBX_STARTUP_CTRL2); | ||||||
|  | 	if (readl(hqvdp->regs + HQVDP_MBX_STARTUP_CTRL2) | ||||||
|  | 					& STARTUP_CTRL2_FETCH_EN) | ||||||
|  | 		seq_puts(s, "\tFetch is enabled"); | ||||||
|  | 	else | ||||||
|  | 		seq_puts(s, "\tFetch is NOT enabled"); | ||||||
|  | 	DBGFS_DUMP(HQVDP_MBX_GP_STATUS); | ||||||
|  | 	DBGFS_DUMP(HQVDP_MBX_NEXT_CMD); | ||||||
|  | 	DBGFS_DUMP(HQVDP_MBX_CURRENT_CMD); | ||||||
|  | 	DBGFS_DUMP(HQVDP_MBX_SOFT_VSYNC); | ||||||
|  | 	if (!(readl(hqvdp->regs + HQVDP_MBX_SOFT_VSYNC) & 3)) | ||||||
|  | 		seq_puts(s, "\tHW Vsync"); | ||||||
|  | 	else | ||||||
|  | 		seq_puts(s, "\tSW Vsync ?!?!"); | ||||||
|  | 
 | ||||||
|  | 	/* Last command */ | ||||||
|  | 	cmd = readl(hqvdp->regs + HQVDP_MBX_CURRENT_CMD); | ||||||
|  | 	cmd_offset = sti_hqvdp_get_curr_cmd(hqvdp); | ||||||
|  | 	if (cmd_offset == -1) { | ||||||
|  | 		seq_puts(s, "\n\n  Last command: unknown"); | ||||||
|  | 	} else { | ||||||
|  | 		virt = hqvdp->hqvdp_cmd + cmd_offset; | ||||||
|  | 		seq_printf(s, "\n\n  Last command: address @ 0x%x (0x%p)", | ||||||
|  | 			   cmd, virt); | ||||||
|  | 		hqvdp_dbg_dump_cmd(s, (struct sti_hqvdp_cmd *)virt); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* Next command */ | ||||||
|  | 	cmd = readl(hqvdp->regs + HQVDP_MBX_NEXT_CMD); | ||||||
|  | 	cmd_offset = sti_hqvdp_get_next_cmd(hqvdp); | ||||||
|  | 	if (cmd_offset == -1) { | ||||||
|  | 		seq_puts(s, "\n\n  Next command: unknown"); | ||||||
|  | 	} else { | ||||||
|  | 		virt = hqvdp->hqvdp_cmd + cmd_offset; | ||||||
|  | 		seq_printf(s, "\n\n  Next command address: @ 0x%x (0x%p)", | ||||||
|  | 			   cmd, virt); | ||||||
|  | 		hqvdp_dbg_dump_cmd(s, (struct sti_hqvdp_cmd *)virt); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	seq_puts(s, "\n"); | ||||||
|  | 
 | ||||||
|  | 	mutex_unlock(&dev->struct_mutex); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct drm_info_list hqvdp_debugfs_files[] = { | ||||||
|  | 	{ "hqvdp", hqvdp_dbg_show, 0, NULL }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int hqvdp_debugfs_init(struct sti_hqvdp *hqvdp, struct drm_minor *minor) | ||||||
|  | { | ||||||
|  | 	unsigned int i; | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(hqvdp_debugfs_files); i++) | ||||||
|  | 		hqvdp_debugfs_files[i].data = hqvdp; | ||||||
|  | 
 | ||||||
|  | 	return drm_debugfs_create_files(hqvdp_debugfs_files, | ||||||
|  | 					ARRAY_SIZE(hqvdp_debugfs_files), | ||||||
|  | 					minor->debugfs_root, minor); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * sti_hqvdp_update_hvsrc |  * sti_hqvdp_update_hvsrc | ||||||
|  * @orient: horizontal or vertical |  * @orient: horizontal or vertical | ||||||
| @ -580,7 +813,7 @@ int sti_hqvdp_vtg_cb(struct notifier_block *nb, unsigned long evt, void *data) | |||||||
| 		btm_cmd_offset = sti_hqvdp_get_free_cmd(hqvdp); | 		btm_cmd_offset = sti_hqvdp_get_free_cmd(hqvdp); | ||||||
| 		top_cmd_offest = sti_hqvdp_get_curr_cmd(hqvdp); | 		top_cmd_offest = sti_hqvdp_get_curr_cmd(hqvdp); | ||||||
| 		if ((btm_cmd_offset == -1) || (top_cmd_offest == -1)) { | 		if ((btm_cmd_offset == -1) || (top_cmd_offest == -1)) { | ||||||
| 			DRM_ERROR("Cannot get cmds, skip btm field\n"); | 			DRM_DEBUG_DRIVER("Warning: no cmd, will skip field\n"); | ||||||
| 			return -EBUSY; | 			return -EBUSY; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| @ -599,11 +832,12 @@ int sti_hqvdp_vtg_cb(struct notifier_block *nb, unsigned long evt, void *data) | |||||||
| 		writel(hqvdp->hqvdp_cmd_paddr + btm_cmd_offset, | 		writel(hqvdp->hqvdp_cmd_paddr + btm_cmd_offset, | ||||||
| 				hqvdp->regs + HQVDP_MBX_NEXT_CMD); | 				hqvdp->regs + HQVDP_MBX_NEXT_CMD); | ||||||
| 
 | 
 | ||||||
| 		hqvdp->curr_field_count++; |  | ||||||
| 		hqvdp->btm_field_pending = false; | 		hqvdp->btm_field_pending = false; | ||||||
| 
 | 
 | ||||||
| 		dev_dbg(hqvdp->dev, "%s Posted command:0x%x\n", | 		dev_dbg(hqvdp->dev, "%s Posted command:0x%x\n", | ||||||
| 				__func__, hqvdp->hqvdp_cmd_paddr); | 				__func__, hqvdp->hqvdp_cmd_paddr); | ||||||
|  | 
 | ||||||
|  | 		sti_plane_update_fps(&hqvdp->plane, false, true); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| @ -612,19 +846,21 @@ int sti_hqvdp_vtg_cb(struct notifier_block *nb, unsigned long evt, void *data) | |||||||
| static void sti_hqvdp_init(struct sti_hqvdp *hqvdp) | static void sti_hqvdp_init(struct sti_hqvdp *hqvdp) | ||||||
| { | { | ||||||
| 	int size; | 	int size; | ||||||
|  | 	dma_addr_t dma_addr; | ||||||
| 
 | 
 | ||||||
| 	hqvdp->vtg_nb.notifier_call = sti_hqvdp_vtg_cb; | 	hqvdp->vtg_nb.notifier_call = sti_hqvdp_vtg_cb; | ||||||
| 
 | 
 | ||||||
| 	/* Allocate memory for the VDP commands */ | 	/* Allocate memory for the VDP commands */ | ||||||
| 	size = NB_VDP_CMD * sizeof(struct sti_hqvdp_cmd); | 	size = NB_VDP_CMD * sizeof(struct sti_hqvdp_cmd); | ||||||
| 	hqvdp->hqvdp_cmd = dma_alloc_writecombine(hqvdp->dev, size, | 	hqvdp->hqvdp_cmd = dma_alloc_writecombine(hqvdp->dev, size, | ||||||
| 					 &hqvdp->hqvdp_cmd_paddr, | 					 &dma_addr, | ||||||
| 					 GFP_KERNEL | GFP_DMA); | 					 GFP_KERNEL | GFP_DMA); | ||||||
| 	if (!hqvdp->hqvdp_cmd) { | 	if (!hqvdp->hqvdp_cmd) { | ||||||
| 		DRM_ERROR("Failed to allocate memory for VDP cmd\n"); | 		DRM_ERROR("Failed to allocate memory for VDP cmd\n"); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	hqvdp->hqvdp_cmd_paddr = (u32)dma_addr; | ||||||
| 	memset(hqvdp->hqvdp_cmd, 0, size); | 	memset(hqvdp->hqvdp_cmd, 0, size); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -670,7 +906,7 @@ static void sti_hqvdp_start_xp70(struct sti_hqvdp *hqvdp) | |||||||
| 	DRM_DEBUG_DRIVER("\n"); | 	DRM_DEBUG_DRIVER("\n"); | ||||||
| 
 | 
 | ||||||
| 	if (hqvdp->xp70_initialized) { | 	if (hqvdp->xp70_initialized) { | ||||||
| 		DRM_INFO("HQVDP XP70 already initialized\n"); | 		DRM_DEBUG_DRIVER("HQVDP XP70 already initialized\n"); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -775,6 +1011,94 @@ out: | |||||||
| 	release_firmware(firmware); | 	release_firmware(firmware); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int sti_hqvdp_atomic_check(struct drm_plane *drm_plane, | ||||||
|  | 				  struct drm_plane_state *state) | ||||||
|  | { | ||||||
|  | 	struct sti_plane *plane = to_sti_plane(drm_plane); | ||||||
|  | 	struct sti_hqvdp *hqvdp = to_sti_hqvdp(plane); | ||||||
|  | 	struct drm_crtc *crtc = state->crtc; | ||||||
|  | 	struct drm_framebuffer *fb = state->fb; | ||||||
|  | 	bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false; | ||||||
|  | 	struct drm_crtc_state *crtc_state; | ||||||
|  | 	struct drm_display_mode *mode; | ||||||
|  | 	int dst_x, dst_y, dst_w, dst_h; | ||||||
|  | 	int src_x, src_y, src_w, src_h; | ||||||
|  | 
 | ||||||
|  | 	/* no need for further checks if the plane is being disabled */ | ||||||
|  | 	if (!crtc || !fb) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	crtc_state = drm_atomic_get_crtc_state(state->state, crtc); | ||||||
|  | 	mode = &crtc_state->mode; | ||||||
|  | 	dst_x = state->crtc_x; | ||||||
|  | 	dst_y = state->crtc_y; | ||||||
|  | 	dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); | ||||||
|  | 	dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); | ||||||
|  | 	/* src_x are in 16.16 format */ | ||||||
|  | 	src_x = state->src_x >> 16; | ||||||
|  | 	src_y = state->src_y >> 16; | ||||||
|  | 	src_w = state->src_w >> 16; | ||||||
|  | 	src_h = state->src_h >> 16; | ||||||
|  | 
 | ||||||
|  | 	if (!sti_hqvdp_check_hw_scaling(hqvdp, mode, | ||||||
|  | 					src_w, src_h, | ||||||
|  | 					dst_w, dst_h)) { | ||||||
|  | 		DRM_ERROR("Scaling beyond HW capabilities\n"); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!drm_fb_cma_get_gem_obj(fb, 0)) { | ||||||
|  | 		DRM_ERROR("Can't get CMA GEM object for fb\n"); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Input / output size | ||||||
|  | 	 * Align to upper even value | ||||||
|  | 	 */ | ||||||
|  | 	dst_w = ALIGN(dst_w, 2); | ||||||
|  | 	dst_h = ALIGN(dst_h, 2); | ||||||
|  | 
 | ||||||
|  | 	if ((src_w > MAX_WIDTH) || (src_w < MIN_WIDTH) || | ||||||
|  | 	    (src_h > MAX_HEIGHT) || (src_h < MIN_HEIGHT) || | ||||||
|  | 	    (dst_w > MAX_WIDTH) || (dst_w < MIN_WIDTH) || | ||||||
|  | 	    (dst_h > MAX_HEIGHT) || (dst_h < MIN_HEIGHT)) { | ||||||
|  | 		DRM_ERROR("Invalid in/out size %dx%d -> %dx%d\n", | ||||||
|  | 			  src_w, src_h, | ||||||
|  | 			  dst_w, dst_h); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (first_prepare) { | ||||||
|  | 		/* Start HQVDP XP70 coprocessor */ | ||||||
|  | 		sti_hqvdp_start_xp70(hqvdp); | ||||||
|  | 
 | ||||||
|  | 		/* Prevent VTG shutdown */ | ||||||
|  | 		if (clk_prepare_enable(hqvdp->clk_pix_main)) { | ||||||
|  | 			DRM_ERROR("Failed to prepare/enable pix main clk\n"); | ||||||
|  | 			return -EINVAL; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/* Register VTG Vsync callback to handle bottom fields */ | ||||||
|  | 		if (sti_vtg_register_client(hqvdp->vtg, | ||||||
|  | 					    &hqvdp->vtg_nb, | ||||||
|  | 					    crtc)) { | ||||||
|  | 			DRM_ERROR("Cannot register VTG notifier\n"); | ||||||
|  | 			return -EINVAL; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n", | ||||||
|  | 		      crtc->base.id, sti_mixer_to_str(to_sti_mixer(crtc)), | ||||||
|  | 		      drm_plane->base.id, sti_plane_to_str(plane)); | ||||||
|  | 	DRM_DEBUG_KMS("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n", | ||||||
|  | 		      sti_plane_to_str(plane), | ||||||
|  | 		      dst_w, dst_h, dst_x, dst_y, | ||||||
|  | 		      src_w, src_h, src_x, src_y); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane, | static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane, | ||||||
| 				    struct drm_plane_state *oldstate) | 				    struct drm_plane_state *oldstate) | ||||||
| { | { | ||||||
| @ -782,46 +1106,36 @@ static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane, | |||||||
| 	struct sti_plane *plane = to_sti_plane(drm_plane); | 	struct sti_plane *plane = to_sti_plane(drm_plane); | ||||||
| 	struct sti_hqvdp *hqvdp = to_sti_hqvdp(plane); | 	struct sti_hqvdp *hqvdp = to_sti_hqvdp(plane); | ||||||
| 	struct drm_crtc *crtc = state->crtc; | 	struct drm_crtc *crtc = state->crtc; | ||||||
| 	struct sti_mixer *mixer = to_sti_mixer(crtc); |  | ||||||
| 	struct drm_framebuffer *fb = state->fb; | 	struct drm_framebuffer *fb = state->fb; | ||||||
| 	struct drm_display_mode *mode = &crtc->mode; | 	struct drm_display_mode *mode; | ||||||
| 	int dst_x = state->crtc_x; | 	int dst_x, dst_y, dst_w, dst_h; | ||||||
| 	int dst_y = state->crtc_y; | 	int src_x, src_y, src_w, src_h; | ||||||
| 	int dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); |  | ||||||
| 	int dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); |  | ||||||
| 	/* src_x are in 16.16 format */ |  | ||||||
| 	int src_x = state->src_x >> 16; |  | ||||||
| 	int src_y = state->src_y >> 16; |  | ||||||
| 	int src_w = state->src_w >> 16; |  | ||||||
| 	int src_h = state->src_h >> 16; |  | ||||||
| 	bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false; |  | ||||||
| 	struct drm_gem_cma_object *cma_obj; | 	struct drm_gem_cma_object *cma_obj; | ||||||
| 	struct sti_hqvdp_cmd *cmd; | 	struct sti_hqvdp_cmd *cmd; | ||||||
| 	int scale_h, scale_v; | 	int scale_h, scale_v; | ||||||
| 	int cmd_offset; | 	int cmd_offset; | ||||||
| 
 | 
 | ||||||
| 	DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n", | 	if (!crtc || !fb) | ||||||
| 		      crtc->base.id, sti_mixer_to_str(mixer), | 		return; | ||||||
| 		      drm_plane->base.id, sti_plane_to_str(plane)); | 
 | ||||||
| 	DRM_DEBUG_KMS("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n", | 	mode = &crtc->mode; | ||||||
| 		      sti_plane_to_str(plane), | 	dst_x = state->crtc_x; | ||||||
| 		      dst_w, dst_h, dst_x, dst_y, | 	dst_y = state->crtc_y; | ||||||
| 		      src_w, src_h, src_x, src_y); | 	dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); | ||||||
|  | 	dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); | ||||||
|  | 	/* src_x are in 16.16 format */ | ||||||
|  | 	src_x = state->src_x >> 16; | ||||||
|  | 	src_y = state->src_y >> 16; | ||||||
|  | 	src_w = state->src_w >> 16; | ||||||
|  | 	src_h = state->src_h >> 16; | ||||||
| 
 | 
 | ||||||
| 	cmd_offset = sti_hqvdp_get_free_cmd(hqvdp); | 	cmd_offset = sti_hqvdp_get_free_cmd(hqvdp); | ||||||
| 	if (cmd_offset == -1) { | 	if (cmd_offset == -1) { | ||||||
| 		DRM_ERROR("No available hqvdp_cmd now\n"); | 		DRM_DEBUG_DRIVER("Warning: no cmd, will skip frame\n"); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 	cmd = hqvdp->hqvdp_cmd + cmd_offset; | 	cmd = hqvdp->hqvdp_cmd + cmd_offset; | ||||||
| 
 | 
 | ||||||
| 	if (!sti_hqvdp_check_hw_scaling(hqvdp, mode, |  | ||||||
| 					src_w, src_h, |  | ||||||
| 					dst_w, dst_h)) { |  | ||||||
| 		DRM_ERROR("Scaling beyond HW capabilities\n"); |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/* Static parameters, defaulting to progressive mode */ | 	/* Static parameters, defaulting to progressive mode */ | ||||||
| 	cmd->top.config = TOP_CONFIG_PROGRESSIVE; | 	cmd->top.config = TOP_CONFIG_PROGRESSIVE; | ||||||
| 	cmd->top.mem_format = TOP_MEM_FORMAT_DFLT; | 	cmd->top.mem_format = TOP_MEM_FORMAT_DFLT; | ||||||
| @ -836,10 +1150,6 @@ static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane, | |||||||
| 	cmd->iqi.pxf_conf = IQI_PXF_CONF_DFLT; | 	cmd->iqi.pxf_conf = IQI_PXF_CONF_DFLT; | ||||||
| 
 | 
 | ||||||
| 	cma_obj = drm_fb_cma_get_gem_obj(fb, 0); | 	cma_obj = drm_fb_cma_get_gem_obj(fb, 0); | ||||||
| 	if (!cma_obj) { |  | ||||||
| 		DRM_ERROR("Can't get CMA GEM object for fb\n"); |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id, | 	DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id, | ||||||
| 			 (char *)&fb->pixel_format, | 			 (char *)&fb->pixel_format, | ||||||
| @ -860,16 +1170,6 @@ static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane, | |||||||
| 	dst_w = ALIGN(dst_w, 2); | 	dst_w = ALIGN(dst_w, 2); | ||||||
| 	dst_h = ALIGN(dst_h, 2); | 	dst_h = ALIGN(dst_h, 2); | ||||||
| 
 | 
 | ||||||
| 	if ((src_w > MAX_WIDTH) || (src_w < MIN_WIDTH) || |  | ||||||
| 	    (src_h > MAX_HEIGHT) || (src_h < MIN_HEIGHT) || |  | ||||||
| 	    (dst_w > MAX_WIDTH) || (dst_w < MIN_WIDTH) || |  | ||||||
| 	    (dst_h > MAX_HEIGHT) || (dst_h < MIN_HEIGHT)) { |  | ||||||
| 		DRM_ERROR("Invalid in/out size %dx%d -> %dx%d\n", |  | ||||||
| 			  src_w, src_h, |  | ||||||
| 			  dst_w, dst_h); |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	cmd->top.input_viewport_size = src_h << 16 | src_w; | 	cmd->top.input_viewport_size = src_h << 16 | src_w; | ||||||
| 	cmd->top.input_frame_size = src_h << 16 | src_w; | 	cmd->top.input_frame_size = src_h << 16 | src_w; | ||||||
| 	cmd->hvsrc.output_picture_size = dst_h << 16 | dst_w; | 	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, | |||||||
| 	scale_v = SCALE_FACTOR * dst_h / src_h; | 	scale_v = SCALE_FACTOR * dst_h / src_h; | ||||||
| 	sti_hqvdp_update_hvsrc(HVSRC_VERT, scale_v, &cmd->hvsrc); | 	sti_hqvdp_update_hvsrc(HVSRC_VERT, scale_v, &cmd->hvsrc); | ||||||
| 
 | 
 | ||||||
| 	if (first_prepare) { |  | ||||||
| 		/* Start HQVDP XP70 coprocessor */ |  | ||||||
| 		sti_hqvdp_start_xp70(hqvdp); |  | ||||||
| 
 |  | ||||||
| 		/* Prevent VTG shutdown */ |  | ||||||
| 		if (clk_prepare_enable(hqvdp->clk_pix_main)) { |  | ||||||
| 			DRM_ERROR("Failed to prepare/enable pix main clk\n"); |  | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		/* Register VTG Vsync callback to handle bottom fields */ |  | ||||||
| 		if (sti_vtg_register_client(hqvdp->vtg, |  | ||||||
| 					    &hqvdp->vtg_nb, |  | ||||||
| 					    crtc)) { |  | ||||||
| 			DRM_ERROR("Cannot register VTG notifier\n"); |  | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	writel(hqvdp->hqvdp_cmd_paddr + cmd_offset, | 	writel(hqvdp->hqvdp_cmd_paddr + cmd_offset, | ||||||
| 	       hqvdp->regs + HQVDP_MBX_NEXT_CMD); | 	       hqvdp->regs + HQVDP_MBX_NEXT_CMD); | ||||||
| 
 | 
 | ||||||
| 	hqvdp->curr_field_count++; |  | ||||||
| 
 |  | ||||||
| 	/* Interlaced : get ready to display the bottom field at next Vsync */ | 	/* Interlaced : get ready to display the bottom field at next Vsync */ | ||||||
| 	if (fb->flags & DRM_MODE_FB_INTERLACED) | 	if (fb->flags & DRM_MODE_FB_INTERLACED) | ||||||
| 		hqvdp->btm_field_pending = true; | 		hqvdp->btm_field_pending = true; | ||||||
| @ -931,6 +1210,8 @@ static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane, | |||||||
| 	dev_dbg(hqvdp->dev, "%s Posted command:0x%x\n", | 	dev_dbg(hqvdp->dev, "%s Posted command:0x%x\n", | ||||||
| 		__func__, hqvdp->hqvdp_cmd_paddr + cmd_offset); | 		__func__, hqvdp->hqvdp_cmd_paddr + cmd_offset); | ||||||
| 
 | 
 | ||||||
|  | 	sti_plane_update_fps(plane, true, true); | ||||||
|  | 
 | ||||||
| 	plane->status = STI_PLANE_UPDATED; | 	plane->status = STI_PLANE_UPDATED; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -938,7 +1219,6 @@ static void sti_hqvdp_atomic_disable(struct drm_plane *drm_plane, | |||||||
| 				     struct drm_plane_state *oldstate) | 				     struct drm_plane_state *oldstate) | ||||||
| { | { | ||||||
| 	struct sti_plane *plane = to_sti_plane(drm_plane); | 	struct sti_plane *plane = to_sti_plane(drm_plane); | ||||||
| 	struct sti_mixer *mixer = to_sti_mixer(drm_plane->crtc); |  | ||||||
| 
 | 
 | ||||||
| 	if (!drm_plane->crtc) { | 	if (!drm_plane->crtc) { | ||||||
| 		DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", | 		DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", | ||||||
| @ -947,13 +1227,15 @@ static void sti_hqvdp_atomic_disable(struct drm_plane *drm_plane, | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n", | 	DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n", | ||||||
| 			 drm_plane->crtc->base.id, sti_mixer_to_str(mixer), | 			 drm_plane->crtc->base.id, | ||||||
|  | 			 sti_mixer_to_str(to_sti_mixer(drm_plane->crtc)), | ||||||
| 			 drm_plane->base.id, sti_plane_to_str(plane)); | 			 drm_plane->base.id, sti_plane_to_str(plane)); | ||||||
| 
 | 
 | ||||||
| 	plane->status = STI_PLANE_DISABLING; | 	plane->status = STI_PLANE_DISABLING; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static const struct drm_plane_helper_funcs sti_hqvdp_helpers_funcs = { | static const struct drm_plane_helper_funcs sti_hqvdp_helpers_funcs = { | ||||||
|  | 	.atomic_check = sti_hqvdp_atomic_check, | ||||||
| 	.atomic_update = sti_hqvdp_atomic_update, | 	.atomic_update = sti_hqvdp_atomic_update, | ||||||
| 	.atomic_disable = sti_hqvdp_atomic_disable, | 	.atomic_disable = sti_hqvdp_atomic_disable, | ||||||
| }; | }; | ||||||
| @ -983,6 +1265,9 @@ static struct drm_plane *sti_hqvdp_create(struct drm_device *drm_dev, | |||||||
| 
 | 
 | ||||||
| 	sti_plane_init_property(&hqvdp->plane, DRM_PLANE_TYPE_OVERLAY); | 	sti_plane_init_property(&hqvdp->plane, DRM_PLANE_TYPE_OVERLAY); | ||||||
| 
 | 
 | ||||||
|  | 	if (hqvdp_debugfs_init(hqvdp, drm_dev->primary)) | ||||||
|  | 		DRM_ERROR("HQVDP debugfs setup failed\n"); | ||||||
|  | 
 | ||||||
| 	return &hqvdp->plane.drm_plane; | 	return &hqvdp->plane.drm_plane; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -75,6 +75,145 @@ static inline void sti_mixer_reg_write(struct sti_mixer *mixer, | |||||||
| 	writel(val, mixer->regs + reg_id); | 	writel(val, mixer->regs + reg_id); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #define DBGFS_DUMP(reg) seq_printf(s, "\n  %-25s 0x%08X", #reg, \ | ||||||
|  | 				   sti_mixer_reg_read(mixer, reg)) | ||||||
|  | 
 | ||||||
|  | static void mixer_dbg_ctl(struct seq_file *s, int val) | ||||||
|  | { | ||||||
|  | 	unsigned int i; | ||||||
|  | 	int count = 0; | ||||||
|  | 	char *const disp_layer[] = {"BKG", "VID0", "VID1", "GDP0", | ||||||
|  | 				    "GDP1", "GDP2", "GDP3"}; | ||||||
|  | 
 | ||||||
|  | 	seq_puts(s, "\tEnabled: "); | ||||||
|  | 	for (i = 0; i < 7; i++) { | ||||||
|  | 		if (val & 1) { | ||||||
|  | 			seq_printf(s, "%s ", disp_layer[i]); | ||||||
|  | 			count++; | ||||||
|  | 		} | ||||||
|  | 		val = val >> 1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	val = val >> 2; | ||||||
|  | 	if (val & 1) { | ||||||
|  | 		seq_puts(s, "CURS "); | ||||||
|  | 		count++; | ||||||
|  | 	} | ||||||
|  | 	if (!count) | ||||||
|  | 		seq_puts(s, "Nothing"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void mixer_dbg_crb(struct seq_file *s, int val) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 
 | ||||||
|  | 	seq_puts(s, "\tDepth: "); | ||||||
|  | 	for (i = 0; i < GAM_MIXER_NB_DEPTH_LEVEL; i++) { | ||||||
|  | 		switch (val & GAM_DEPTH_MASK_ID) { | ||||||
|  | 		case GAM_DEPTH_VID0_ID: | ||||||
|  | 			seq_puts(s, "VID0"); | ||||||
|  | 			break; | ||||||
|  | 		case GAM_DEPTH_VID1_ID: | ||||||
|  | 			seq_puts(s, "VID1"); | ||||||
|  | 			break; | ||||||
|  | 		case GAM_DEPTH_GDP0_ID: | ||||||
|  | 			seq_puts(s, "GDP0"); | ||||||
|  | 			break; | ||||||
|  | 		case GAM_DEPTH_GDP1_ID: | ||||||
|  | 			seq_puts(s, "GDP1"); | ||||||
|  | 			break; | ||||||
|  | 		case GAM_DEPTH_GDP2_ID: | ||||||
|  | 			seq_puts(s, "GDP2"); | ||||||
|  | 			break; | ||||||
|  | 		case GAM_DEPTH_GDP3_ID: | ||||||
|  | 			seq_puts(s, "GDP3"); | ||||||
|  | 			break; | ||||||
|  | 		default: | ||||||
|  | 			seq_puts(s, "---"); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (i < GAM_MIXER_NB_DEPTH_LEVEL - 1) | ||||||
|  | 			seq_puts(s, " < "); | ||||||
|  | 		val = val >> 3; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void mixer_dbg_mxn(struct seq_file *s, void *addr) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 
 | ||||||
|  | 	for (i = 1; i < 8; i++) | ||||||
|  | 		seq_printf(s, "-0x%08X", (int)readl(addr + i * 4)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mixer_dbg_show(struct seq_file *s, void *arg) | ||||||
|  | { | ||||||
|  | 	struct drm_info_node *node = s->private; | ||||||
|  | 	struct sti_mixer *mixer = (struct sti_mixer *)node->info_ent->data; | ||||||
|  | 	struct drm_device *dev = node->minor->dev; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = mutex_lock_interruptible(&dev->struct_mutex); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	seq_printf(s, "%s: (vaddr = 0x%p)", | ||||||
|  | 		   sti_mixer_to_str(mixer), mixer->regs); | ||||||
|  | 
 | ||||||
|  | 	DBGFS_DUMP(GAM_MIXER_CTL); | ||||||
|  | 	mixer_dbg_ctl(s, sti_mixer_reg_read(mixer, GAM_MIXER_CTL)); | ||||||
|  | 	DBGFS_DUMP(GAM_MIXER_BKC); | ||||||
|  | 	DBGFS_DUMP(GAM_MIXER_BCO); | ||||||
|  | 	DBGFS_DUMP(GAM_MIXER_BCS); | ||||||
|  | 	DBGFS_DUMP(GAM_MIXER_AVO); | ||||||
|  | 	DBGFS_DUMP(GAM_MIXER_AVS); | ||||||
|  | 	DBGFS_DUMP(GAM_MIXER_CRB); | ||||||
|  | 	mixer_dbg_crb(s, sti_mixer_reg_read(mixer, GAM_MIXER_CRB)); | ||||||
|  | 	DBGFS_DUMP(GAM_MIXER_ACT); | ||||||
|  | 	DBGFS_DUMP(GAM_MIXER_MBP); | ||||||
|  | 	DBGFS_DUMP(GAM_MIXER_MX0); | ||||||
|  | 	mixer_dbg_mxn(s, mixer->regs + GAM_MIXER_MX0); | ||||||
|  | 	seq_puts(s, "\n"); | ||||||
|  | 
 | ||||||
|  | 	mutex_unlock(&dev->struct_mutex); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct drm_info_list mixer0_debugfs_files[] = { | ||||||
|  | 	{ "mixer_main", mixer_dbg_show, 0, NULL }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static struct drm_info_list mixer1_debugfs_files[] = { | ||||||
|  | 	{ "mixer_aux", mixer_dbg_show, 0, NULL }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int mixer_debugfs_init(struct sti_mixer *mixer, struct drm_minor *minor) | ||||||
|  | { | ||||||
|  | 	unsigned int i; | ||||||
|  | 	struct drm_info_list *mixer_debugfs_files; | ||||||
|  | 	int nb_files; | ||||||
|  | 
 | ||||||
|  | 	switch (mixer->id) { | ||||||
|  | 	case STI_MIXER_MAIN: | ||||||
|  | 		mixer_debugfs_files = mixer0_debugfs_files; | ||||||
|  | 		nb_files = ARRAY_SIZE(mixer0_debugfs_files); | ||||||
|  | 		break; | ||||||
|  | 	case STI_MIXER_AUX: | ||||||
|  | 		mixer_debugfs_files = mixer1_debugfs_files; | ||||||
|  | 		nb_files = ARRAY_SIZE(mixer1_debugfs_files); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < nb_files; i++) | ||||||
|  | 		mixer_debugfs_files[i].data = mixer; | ||||||
|  | 
 | ||||||
|  | 	return drm_debugfs_create_files(mixer_debugfs_files, | ||||||
|  | 					nb_files, | ||||||
|  | 					minor->debugfs_root, minor); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void sti_mixer_set_background_status(struct sti_mixer *mixer, bool enable) | void sti_mixer_set_background_status(struct sti_mixer *mixer, bool enable) | ||||||
| { | { | ||||||
| 	u32 val = sti_mixer_reg_read(mixer, GAM_MIXER_CTL); | 	u32 val = sti_mixer_reg_read(mixer, GAM_MIXER_CTL); | ||||||
| @ -237,7 +376,9 @@ void sti_mixer_set_matrix(struct sti_mixer *mixer) | |||||||
| 				    mixerColorSpaceMatIdentity[i]); | 				    mixerColorSpaceMatIdentity[i]); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct sti_mixer *sti_mixer_create(struct device *dev, int id, | struct sti_mixer *sti_mixer_create(struct device *dev, | ||||||
|  | 				   struct drm_device *drm_dev, | ||||||
|  | 				   int id, | ||||||
| 				   void __iomem *baseaddr) | 				   void __iomem *baseaddr) | ||||||
| { | { | ||||||
| 	struct sti_mixer *mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL); | 	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, | |||||||
| 	DRM_DEBUG_DRIVER("%s created. Regs=%p\n", | 	DRM_DEBUG_DRIVER("%s created. Regs=%p\n", | ||||||
| 			 sti_mixer_to_str(mixer), mixer->regs); | 			 sti_mixer_to_str(mixer), mixer->regs); | ||||||
| 
 | 
 | ||||||
|  | 	if (mixer_debugfs_init(mixer, drm_dev->primary)) | ||||||
|  | 		DRM_ERROR("MIXER debugfs setup failed\n"); | ||||||
|  | 
 | ||||||
| 	return mixer; | 	return mixer; | ||||||
| } | } | ||||||
|  | |||||||
| @ -42,7 +42,9 @@ struct sti_mixer { | |||||||
| 
 | 
 | ||||||
| const char *sti_mixer_to_str(struct sti_mixer *mixer); | const char *sti_mixer_to_str(struct sti_mixer *mixer); | ||||||
| 
 | 
 | ||||||
| struct sti_mixer *sti_mixer_create(struct device *dev, int id, | struct sti_mixer *sti_mixer_create(struct device *dev, | ||||||
|  | 				   struct drm_device *drm_dev, | ||||||
|  | 				   int id, | ||||||
| 				   void __iomem *baseaddr); | 				   void __iomem *baseaddr); | ||||||
| 
 | 
 | ||||||
| int sti_mixer_set_plane_status(struct sti_mixer *mixer, | int sti_mixer_set_plane_status(struct sti_mixer *mixer, | ||||||
|  | |||||||
| @ -43,6 +43,69 @@ const char *sti_plane_to_str(struct sti_plane *plane) | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #define STI_FPS_INTERVAL_MS     3000 | ||||||
|  | 
 | ||||||
|  | static int sti_plane_timespec_ms_diff(struct timespec lhs, struct timespec rhs) | ||||||
|  | { | ||||||
|  | 	struct timespec tmp_ts = timespec_sub(lhs, rhs); | ||||||
|  | 	u64 tmp_ns = (u64)timespec_to_ns(&tmp_ts); | ||||||
|  | 
 | ||||||
|  | 	do_div(tmp_ns, NSEC_PER_MSEC); | ||||||
|  | 
 | ||||||
|  | 	return (u32)tmp_ns; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void sti_plane_update_fps(struct sti_plane *plane, | ||||||
|  | 			  bool new_frame, | ||||||
|  | 			  bool new_field) | ||||||
|  | { | ||||||
|  | 	struct timespec now; | ||||||
|  | 	struct sti_fps_info *fps; | ||||||
|  | 	int fpks, fipks, ms_since_last, num_frames, num_fields; | ||||||
|  | 
 | ||||||
|  | 	getrawmonotonic(&now); | ||||||
|  | 
 | ||||||
|  | 	/* Compute number of frame updates */ | ||||||
|  | 	fps = &plane->fps_info; | ||||||
|  | 
 | ||||||
|  | 	if (new_field) | ||||||
|  | 		fps->curr_field_counter++; | ||||||
|  | 
 | ||||||
|  | 	/* do not perform fps calcul if new_frame is false */ | ||||||
|  | 	if (!new_frame) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	fps->curr_frame_counter++; | ||||||
|  | 	ms_since_last = sti_plane_timespec_ms_diff(now, fps->last_timestamp); | ||||||
|  | 	num_frames = fps->curr_frame_counter - fps->last_frame_counter; | ||||||
|  | 
 | ||||||
|  | 	if (num_frames <= 0  || ms_since_last < STI_FPS_INTERVAL_MS) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	fps->last_timestamp = now; | ||||||
|  | 	fps->last_frame_counter = fps->curr_frame_counter; | ||||||
|  | 	fpks = (num_frames * 1000000) / ms_since_last; | ||||||
|  | 	snprintf(plane->fps_info.fps_str, FPS_LENGTH, "%-6s @ %d.%.3d fps", | ||||||
|  | 		 sti_plane_to_str(plane), fpks / 1000, fpks % 1000); | ||||||
|  | 
 | ||||||
|  | 	if (fps->curr_field_counter) { | ||||||
|  | 		/* Compute number of field updates */ | ||||||
|  | 		num_fields = fps->curr_field_counter - fps->last_field_counter; | ||||||
|  | 		fps->last_field_counter = fps->curr_field_counter; | ||||||
|  | 		fipks = (num_fields * 1000000) / ms_since_last; | ||||||
|  | 		snprintf(plane->fps_info.fips_str, | ||||||
|  | 			 FPS_LENGTH, " - %d.%.3d field/sec", | ||||||
|  | 			 fipks / 1000, fipks % 1000); | ||||||
|  | 	} else { | ||||||
|  | 		plane->fps_info.fips_str[0] = '\0'; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (fps->output) | ||||||
|  | 		DRM_INFO("%s%s\n", | ||||||
|  | 			 plane->fps_info.fps_str, | ||||||
|  | 			 plane->fps_info.fips_str); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void sti_plane_destroy(struct drm_plane *drm_plane) | static void sti_plane_destroy(struct drm_plane *drm_plane) | ||||||
| { | { | ||||||
| 	DRM_DEBUG_DRIVER("\n"); | 	DRM_DEBUG_DRIVER("\n"); | ||||||
|  | |||||||
| @ -50,6 +50,18 @@ enum sti_plane_status { | |||||||
| 	STI_PLANE_DISABLED, | 	STI_PLANE_DISABLED, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | #define FPS_LENGTH 64 | ||||||
|  | struct sti_fps_info { | ||||||
|  | 	bool output; | ||||||
|  | 	unsigned int curr_frame_counter; | ||||||
|  | 	unsigned int last_frame_counter; | ||||||
|  | 	unsigned int curr_field_counter; | ||||||
|  | 	unsigned int last_field_counter; | ||||||
|  | 	struct timespec last_timestamp; | ||||||
|  | 	char fps_str[FPS_LENGTH]; | ||||||
|  | 	char fips_str[FPS_LENGTH]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * STI plane structure |  * STI plane structure | ||||||
|  * |  * | ||||||
| @ -57,15 +69,20 @@ enum sti_plane_status { | |||||||
|  * @desc:               plane type & id |  * @desc:               plane type & id | ||||||
|  * @status:             to know the status of the plane |  * @status:             to know the status of the plane | ||||||
|  * @zorder:             plane z-order |  * @zorder:             plane z-order | ||||||
|  |  * @fps_info:           frame per second info | ||||||
|  */ |  */ | ||||||
| struct sti_plane { | struct sti_plane { | ||||||
| 	struct drm_plane drm_plane; | 	struct drm_plane drm_plane; | ||||||
| 	enum sti_plane_desc desc; | 	enum sti_plane_desc desc; | ||||||
| 	enum sti_plane_status status; | 	enum sti_plane_status status; | ||||||
| 	int zorder; | 	int zorder; | ||||||
|  | 	struct sti_fps_info fps_info; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const char *sti_plane_to_str(struct sti_plane *plane); | const char *sti_plane_to_str(struct sti_plane *plane); | ||||||
|  | void sti_plane_update_fps(struct sti_plane *plane, | ||||||
|  | 			  bool new_frame, | ||||||
|  | 			  bool new_field); | ||||||
| void sti_plane_init_property(struct sti_plane *plane, | void sti_plane_init_property(struct sti_plane *plane, | ||||||
| 			     enum drm_plane_type type); | 			     enum drm_plane_type type); | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -17,6 +17,7 @@ | |||||||
| #include <drm/drm_crtc_helper.h> | #include <drm/drm_crtc_helper.h> | ||||||
| 
 | 
 | ||||||
| #include "sti_crtc.h" | #include "sti_crtc.h" | ||||||
|  | #include "sti_vtg.h" | ||||||
| 
 | 
 | ||||||
| /* glue registers */ | /* glue registers */ | ||||||
| #define TVO_CSC_MAIN_M0                  0x000 | #define TVO_CSC_MAIN_M0                  0x000 | ||||||
| @ -85,19 +86,7 @@ | |||||||
| #define TVO_VIP_SEL_INPUT_BYPASSED       1 | #define TVO_VIP_SEL_INPUT_BYPASSED       1 | ||||||
| 
 | 
 | ||||||
| #define TVO_SYNC_MAIN_VTG_SET_REF        0x00 | #define TVO_SYNC_MAIN_VTG_SET_REF        0x00 | ||||||
| #define TVO_SYNC_MAIN_VTG_SET_1          0x01 |  | ||||||
| #define TVO_SYNC_MAIN_VTG_SET_2          0x02 |  | ||||||
| #define TVO_SYNC_MAIN_VTG_SET_3          0x03 |  | ||||||
| #define TVO_SYNC_MAIN_VTG_SET_4          0x04 |  | ||||||
| #define TVO_SYNC_MAIN_VTG_SET_5          0x05 |  | ||||||
| #define TVO_SYNC_MAIN_VTG_SET_6          0x06 |  | ||||||
| #define TVO_SYNC_AUX_VTG_SET_REF         0x10 | #define TVO_SYNC_AUX_VTG_SET_REF         0x10 | ||||||
| #define TVO_SYNC_AUX_VTG_SET_1           0x11 |  | ||||||
| #define TVO_SYNC_AUX_VTG_SET_2           0x12 |  | ||||||
| #define TVO_SYNC_AUX_VTG_SET_3           0x13 |  | ||||||
| #define TVO_SYNC_AUX_VTG_SET_4           0x14 |  | ||||||
| #define TVO_SYNC_AUX_VTG_SET_5           0x15 |  | ||||||
| #define TVO_SYNC_AUX_VTG_SET_6           0x16 |  | ||||||
| 
 | 
 | ||||||
| #define TVO_SYNC_HD_DCS_SHIFT            8 | #define TVO_SYNC_HD_DCS_SHIFT            8 | ||||||
| 
 | 
 | ||||||
| @ -106,6 +95,8 @@ | |||||||
| 
 | 
 | ||||||
| #define ENCODER_CRTC_MASK                (BIT(0) | BIT(1)) | #define ENCODER_CRTC_MASK                (BIT(0) | BIT(1)) | ||||||
| 
 | 
 | ||||||
|  | #define TVO_MIN_HD_HEIGHT                720 | ||||||
|  | 
 | ||||||
| /* enum listing the supported output data format */ | /* enum listing the supported output data format */ | ||||||
| enum sti_tvout_video_out_type { | enum sti_tvout_video_out_type { | ||||||
| 	STI_TVOUT_VIDEO_OUT_RGB, | 	STI_TVOUT_VIDEO_OUT_RGB, | ||||||
| @ -268,6 +259,31 @@ static void tvout_vip_set_in_vid_fmt(struct sti_tvout *tvout, | |||||||
| 	tvout_write(tvout, val, reg); | 	tvout_write(tvout, val, reg); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set preformatter matrix | ||||||
|  |  * | ||||||
|  |  * @tvout: tvout structure | ||||||
|  |  * @mode: display mode structure | ||||||
|  |  */ | ||||||
|  | static void tvout_preformatter_set_matrix(struct sti_tvout *tvout, | ||||||
|  | 					  struct drm_display_mode *mode) | ||||||
|  | { | ||||||
|  | 	unsigned int i; | ||||||
|  | 	const u32 *pf_matrix; | ||||||
|  | 
 | ||||||
|  | 	if (mode->vdisplay >= TVO_MIN_HD_HEIGHT) | ||||||
|  | 		pf_matrix = rgb_to_ycbcr_709; | ||||||
|  | 	else | ||||||
|  | 		pf_matrix = rgb_to_ycbcr_601; | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < 8; i++) { | ||||||
|  | 		tvout_write(tvout, *(pf_matrix + i), | ||||||
|  | 			    TVO_CSC_MAIN_M0 + (i * 4)); | ||||||
|  | 		tvout_write(tvout, *(pf_matrix + i), | ||||||
|  | 			    TVO_CSC_AUX_M0 + (i * 4)); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Start VIP block for DVO output |  * Start VIP block for DVO output | ||||||
|  * |  * | ||||||
| @ -280,24 +296,26 @@ static void tvout_dvo_start(struct sti_tvout *tvout, bool main_path) | |||||||
| 	struct device_node *node = tvout->dev->of_node; | 	struct device_node *node = tvout->dev->of_node; | ||||||
| 	bool sel_input_logic_inverted = false; | 	bool sel_input_logic_inverted = false; | ||||||
| 	u32 tvo_in_vid_format; | 	u32 tvo_in_vid_format; | ||||||
| 	int val; | 	int val, tmp; | ||||||
| 
 | 
 | ||||||
| 	dev_dbg(tvout->dev, "%s\n", __func__); | 	dev_dbg(tvout->dev, "%s\n", __func__); | ||||||
| 
 | 
 | ||||||
| 	if (main_path) { | 	if (main_path) { | ||||||
| 		DRM_DEBUG_DRIVER("main vip for DVO\n"); | 		DRM_DEBUG_DRIVER("main vip for DVO\n"); | ||||||
| 		/* Select the input sync for dvo = VTG set 4 */ | 		/* Select the input sync for dvo */ | ||||||
| 		val  = TVO_SYNC_MAIN_VTG_SET_4 << TVO_SYNC_DVO_PAD_VSYNC_SHIFT; | 		tmp = TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_DVO; | ||||||
| 		val |= TVO_SYNC_MAIN_VTG_SET_4 << TVO_SYNC_DVO_PAD_HSYNC_SHIFT; | 		val  = tmp << TVO_SYNC_DVO_PAD_VSYNC_SHIFT; | ||||||
| 		val |= TVO_SYNC_MAIN_VTG_SET_4; | 		val |= tmp << TVO_SYNC_DVO_PAD_HSYNC_SHIFT; | ||||||
|  | 		val |= tmp; | ||||||
| 		tvout_write(tvout, val, TVO_DVO_SYNC_SEL); | 		tvout_write(tvout, val, TVO_DVO_SYNC_SEL); | ||||||
| 		tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; | 		tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; | ||||||
| 	} else { | 	} else { | ||||||
| 		DRM_DEBUG_DRIVER("aux vip for DVO\n"); | 		DRM_DEBUG_DRIVER("aux vip for DVO\n"); | ||||||
| 		/* Select the input sync for dvo = VTG set 4 */ | 		/* Select the input sync for dvo */ | ||||||
| 		val  = TVO_SYNC_AUX_VTG_SET_4 << TVO_SYNC_DVO_PAD_VSYNC_SHIFT; | 		tmp = TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_DVO; | ||||||
| 		val |= TVO_SYNC_AUX_VTG_SET_4 << TVO_SYNC_DVO_PAD_HSYNC_SHIFT; | 		val  = tmp << TVO_SYNC_DVO_PAD_VSYNC_SHIFT; | ||||||
| 		val |= TVO_SYNC_AUX_VTG_SET_4; | 		val |= tmp << TVO_SYNC_DVO_PAD_HSYNC_SHIFT; | ||||||
|  | 		val |= tmp; | ||||||
| 		tvout_write(tvout, val, TVO_DVO_SYNC_SEL); | 		tvout_write(tvout, val, TVO_DVO_SYNC_SEL); | ||||||
| 		tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; | 		tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; | ||||||
| 	} | 	} | ||||||
| @ -308,9 +326,8 @@ static void tvout_dvo_start(struct sti_tvout *tvout, bool main_path) | |||||||
| 				  TVO_VIP_REORDER_Y_G_SEL, | 				  TVO_VIP_REORDER_Y_G_SEL, | ||||||
| 				  TVO_VIP_REORDER_CB_B_SEL); | 				  TVO_VIP_REORDER_CB_B_SEL); | ||||||
| 
 | 
 | ||||||
| 	/* Set clipping mode (Limited range RGB/Y) */ | 	/* Set clipping mode */ | ||||||
| 	tvout_vip_set_clip_mode(tvout, TVO_VIP_DVO, | 	tvout_vip_set_clip_mode(tvout, TVO_VIP_DVO, TVO_VIP_CLIP_DISABLED); | ||||||
| 				TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y); |  | ||||||
| 
 | 
 | ||||||
| 	/* Set round mode (rounded to 8-bit per component) */ | 	/* Set round mode (rounded to 8-bit per component) */ | ||||||
| 	tvout_vip_set_rnd(tvout, TVO_VIP_DVO, TVO_VIP_RND_8BIT_ROUNDED); | 	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) | |||||||
| 
 | 
 | ||||||
| 	if (main_path) { | 	if (main_path) { | ||||||
| 		DRM_DEBUG_DRIVER("main vip for hdmi\n"); | 		DRM_DEBUG_DRIVER("main vip for hdmi\n"); | ||||||
| 		/* select the input sync for hdmi = VTG set 1 */ | 		/* select the input sync for hdmi */ | ||||||
| 		tvout_write(tvout, TVO_SYNC_MAIN_VTG_SET_1, TVO_HDMI_SYNC_SEL); | 		tvout_write(tvout, | ||||||
|  | 			    TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDMI, | ||||||
|  | 			    TVO_HDMI_SYNC_SEL); | ||||||
| 		tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; | 		tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; | ||||||
| 	} else { | 	} else { | ||||||
| 		DRM_DEBUG_DRIVER("aux vip for hdmi\n"); | 		DRM_DEBUG_DRIVER("aux vip for hdmi\n"); | ||||||
| 		/* select the input sync for hdmi = VTG set 1 */ | 		/* select the input sync for hdmi */ | ||||||
| 		tvout_write(tvout, TVO_SYNC_AUX_VTG_SET_1, TVO_HDMI_SYNC_SEL); | 		tvout_write(tvout, | ||||||
|  | 			    TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDMI, | ||||||
|  | 			    TVO_HDMI_SYNC_SEL); | ||||||
| 		tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; | 		tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -361,9 +382,8 @@ static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path) | |||||||
| 				  TVO_VIP_REORDER_Y_G_SEL, | 				  TVO_VIP_REORDER_Y_G_SEL, | ||||||
| 				  TVO_VIP_REORDER_CB_B_SEL); | 				  TVO_VIP_REORDER_CB_B_SEL); | ||||||
| 
 | 
 | ||||||
| 	/* set clipping mode (Limited range RGB/Y) */ | 	/* set clipping mode */ | ||||||
| 	tvout_vip_set_clip_mode(tvout, TVO_VIP_HDMI, | 	tvout_vip_set_clip_mode(tvout, TVO_VIP_HDMI, TVO_VIP_CLIP_DISABLED); | ||||||
| 			TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y); |  | ||||||
| 
 | 
 | ||||||
| 	/* set round mode (rounded to 8-bit per component) */ | 	/* set round mode (rounded to 8-bit per component) */ | ||||||
| 	tvout_vip_set_rnd(tvout, TVO_VIP_HDMI, TVO_VIP_RND_8BIT_ROUNDED); | 	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) | |||||||
| 	dev_dbg(tvout->dev, "%s\n", __func__); | 	dev_dbg(tvout->dev, "%s\n", __func__); | ||||||
| 
 | 
 | ||||||
| 	if (main_path) { | 	if (main_path) { | ||||||
| 		val = TVO_SYNC_MAIN_VTG_SET_2 << TVO_SYNC_HD_DCS_SHIFT; | 		DRM_DEBUG_DRIVER("main vip for HDF\n"); | ||||||
| 		val |= TVO_SYNC_MAIN_VTG_SET_3; | 		/* Select the input sync for HD analog and HD DCS */ | ||||||
|  | 		val  = TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDDCS; | ||||||
|  | 		val  = val << TVO_SYNC_HD_DCS_SHIFT; | ||||||
|  | 		val |= TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDF; | ||||||
| 		tvout_write(tvout, val, TVO_HD_SYNC_SEL); | 		tvout_write(tvout, val, TVO_HD_SYNC_SEL); | ||||||
| 		tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; | 		tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; | ||||||
| 	} else { | 	} else { | ||||||
| 		val = TVO_SYNC_AUX_VTG_SET_2 << TVO_SYNC_HD_DCS_SHIFT; | 		DRM_DEBUG_DRIVER("aux vip for HDF\n"); | ||||||
| 		val |= TVO_SYNC_AUX_VTG_SET_3; | 		/* Select the input sync for HD analog and HD DCS */ | ||||||
|  | 		val  = TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDDCS; | ||||||
|  | 		val  = val << TVO_SYNC_HD_DCS_SHIFT; | ||||||
|  | 		val |= TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDF; | ||||||
| 		tvout_write(tvout, val, TVO_HD_SYNC_SEL); | 		tvout_write(tvout, val, TVO_HD_SYNC_SEL); | ||||||
| 		tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; | 		tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; | ||||||
| 	} | 	} | ||||||
| @ -414,8 +440,8 @@ static void tvout_hda_start(struct sti_tvout *tvout, bool main_path) | |||||||
| 				  TVO_VIP_REORDER_Y_G_SEL, | 				  TVO_VIP_REORDER_Y_G_SEL, | ||||||
| 				  TVO_VIP_REORDER_CB_B_SEL); | 				  TVO_VIP_REORDER_CB_B_SEL); | ||||||
| 
 | 
 | ||||||
| 	/* set clipping mode (EAV/SAV clipping) */ | 	/* set clipping mode */ | ||||||
| 	tvout_vip_set_clip_mode(tvout, TVO_VIP_HDF, TVO_VIP_CLIP_EAV_SAV); | 	tvout_vip_set_clip_mode(tvout, TVO_VIP_HDF, TVO_VIP_CLIP_DISABLED); | ||||||
| 
 | 
 | ||||||
| 	/* set round mode (rounded to 10-bit per component) */ | 	/* set round mode (rounded to 10-bit per component) */ | ||||||
| 	tvout_vip_set_rnd(tvout, TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED); | 	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) | |||||||
| 	tvout_write(tvout, 0, TVO_HD_DAC_CFG_OFF); | 	tvout_write(tvout, 0, TVO_HD_DAC_CFG_OFF); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #define DBGFS_DUMP(reg) seq_printf(s, "\n  %-25s 0x%08X", #reg, \ | ||||||
|  | 				   readl(tvout->regs + reg)) | ||||||
|  | 
 | ||||||
|  | static void tvout_dbg_vip(struct seq_file *s, int val) | ||||||
|  | { | ||||||
|  | 	int r, g, b, tmp, mask; | ||||||
|  | 	char *const reorder[] = {"Y_G", "Cb_B", "Cr_R"}; | ||||||
|  | 	char *const clipping[] = {"No", "EAV/SAV", "Limited range RGB/Y", | ||||||
|  | 				  "Limited range Cb/Cr", "decided by register"}; | ||||||
|  | 	char *const round[] = {"8-bit", "10-bit", "12-bit"}; | ||||||
|  | 	char *const input_sel[] = {"Main (color matrix enabled)", | ||||||
|  | 				   "Main (color matrix by-passed)", | ||||||
|  | 				   "", "", "", "", "", "", | ||||||
|  | 				   "Aux (color matrix enabled)", | ||||||
|  | 				   "Aux (color matrix by-passed)", | ||||||
|  | 				   "", "", "", "", "", "Force value"}; | ||||||
|  | 
 | ||||||
|  | 	seq_puts(s, "\t"); | ||||||
|  | 	mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT; | ||||||
|  | 	r = (val & mask) >> TVO_VIP_REORDER_R_SHIFT; | ||||||
|  | 	mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT; | ||||||
|  | 	g = (val & mask) >> TVO_VIP_REORDER_G_SHIFT; | ||||||
|  | 	mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_B_SHIFT; | ||||||
|  | 	b = (val & mask) >> TVO_VIP_REORDER_B_SHIFT; | ||||||
|  | 	seq_printf(s, "%-24s %s->%s %s->%s %s->%s\n", "Reorder:", | ||||||
|  | 		   reorder[r], reorder[TVO_VIP_REORDER_CR_R_SEL], | ||||||
|  | 		   reorder[g], reorder[TVO_VIP_REORDER_Y_G_SEL], | ||||||
|  | 		   reorder[b], reorder[TVO_VIP_REORDER_CB_B_SEL]); | ||||||
|  | 	seq_puts(s, "\t\t\t\t\t"); | ||||||
|  | 	mask = TVO_VIP_CLIP_MASK << TVO_VIP_CLIP_SHIFT; | ||||||
|  | 	tmp = (val & mask) >> TVO_VIP_CLIP_SHIFT; | ||||||
|  | 	seq_printf(s, "%-24s %s\n", "Clipping:", clipping[tmp]); | ||||||
|  | 	seq_puts(s, "\t\t\t\t\t"); | ||||||
|  | 	mask = TVO_VIP_RND_MASK << TVO_VIP_RND_SHIFT; | ||||||
|  | 	tmp = (val & mask) >> TVO_VIP_RND_SHIFT; | ||||||
|  | 	seq_printf(s, "%-24s input data rounded to %s per component\n", | ||||||
|  | 		   "Round:", round[tmp]); | ||||||
|  | 	seq_puts(s, "\t\t\t\t\t"); | ||||||
|  | 	tmp = (val & TVO_VIP_SEL_INPUT_MASK); | ||||||
|  | 	seq_printf(s, "%-24s %s", "Input selection:", input_sel[tmp]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void tvout_dbg_hd_dac_cfg(struct seq_file *s, int val) | ||||||
|  | { | ||||||
|  | 	seq_printf(s, "\t%-24s %s", "HD DAC:", | ||||||
|  | 		   val & 1 ? "disabled" : "enabled"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int tvout_dbg_show(struct seq_file *s, void *data) | ||||||
|  | { | ||||||
|  | 	struct drm_info_node *node = s->private; | ||||||
|  | 	struct sti_tvout *tvout = (struct sti_tvout *)node->info_ent->data; | ||||||
|  | 	struct drm_device *dev = node->minor->dev; | ||||||
|  | 	struct drm_crtc *crtc; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = mutex_lock_interruptible(&dev->struct_mutex); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	seq_printf(s, "TVOUT: (vaddr = 0x%p)", tvout->regs); | ||||||
|  | 
 | ||||||
|  | 	seq_puts(s, "\n\n  HDMI encoder: "); | ||||||
|  | 	crtc = tvout->hdmi->crtc; | ||||||
|  | 	if (crtc) { | ||||||
|  | 		seq_printf(s, "connected to %s path", | ||||||
|  | 			   sti_crtc_is_main(crtc) ? "main" : "aux"); | ||||||
|  | 		DBGFS_DUMP(TVO_HDMI_SYNC_SEL); | ||||||
|  | 		DBGFS_DUMP(TVO_VIP_HDMI); | ||||||
|  | 		tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_HDMI)); | ||||||
|  | 	} else { | ||||||
|  | 		seq_puts(s, "disabled"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	seq_puts(s, "\n\n  DVO encoder: "); | ||||||
|  | 	crtc = tvout->dvo->crtc; | ||||||
|  | 	if (crtc) { | ||||||
|  | 		seq_printf(s, "connected to %s path", | ||||||
|  | 			   sti_crtc_is_main(crtc) ? "main" : "aux"); | ||||||
|  | 		DBGFS_DUMP(TVO_DVO_SYNC_SEL); | ||||||
|  | 		DBGFS_DUMP(TVO_DVO_CONFIG); | ||||||
|  | 		DBGFS_DUMP(TVO_VIP_DVO); | ||||||
|  | 		tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_DVO)); | ||||||
|  | 	} else { | ||||||
|  | 		seq_puts(s, "disabled"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	seq_puts(s, "\n\n  HDA encoder: "); | ||||||
|  | 	crtc = tvout->hda->crtc; | ||||||
|  | 	if (crtc) { | ||||||
|  | 		seq_printf(s, "connected to %s path", | ||||||
|  | 			   sti_crtc_is_main(crtc) ? "main" : "aux"); | ||||||
|  | 		DBGFS_DUMP(TVO_HD_SYNC_SEL); | ||||||
|  | 		DBGFS_DUMP(TVO_HD_DAC_CFG_OFF); | ||||||
|  | 		tvout_dbg_hd_dac_cfg(s, | ||||||
|  | 				     readl(tvout->regs + TVO_HD_DAC_CFG_OFF)); | ||||||
|  | 		DBGFS_DUMP(TVO_VIP_HDF); | ||||||
|  | 		tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_HDF)); | ||||||
|  | 	} else { | ||||||
|  | 		seq_puts(s, "disabled"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	seq_puts(s, "\n\n  main path configuration"); | ||||||
|  | 	DBGFS_DUMP(TVO_CSC_MAIN_M0); | ||||||
|  | 	DBGFS_DUMP(TVO_CSC_MAIN_M1); | ||||||
|  | 	DBGFS_DUMP(TVO_CSC_MAIN_M2); | ||||||
|  | 	DBGFS_DUMP(TVO_CSC_MAIN_M3); | ||||||
|  | 	DBGFS_DUMP(TVO_CSC_MAIN_M4); | ||||||
|  | 	DBGFS_DUMP(TVO_CSC_MAIN_M5); | ||||||
|  | 	DBGFS_DUMP(TVO_CSC_MAIN_M6); | ||||||
|  | 	DBGFS_DUMP(TVO_CSC_MAIN_M7); | ||||||
|  | 	DBGFS_DUMP(TVO_MAIN_IN_VID_FORMAT); | ||||||
|  | 
 | ||||||
|  | 	seq_puts(s, "\n\n  auxiliary path configuration"); | ||||||
|  | 	DBGFS_DUMP(TVO_CSC_AUX_M0); | ||||||
|  | 	DBGFS_DUMP(TVO_CSC_AUX_M2); | ||||||
|  | 	DBGFS_DUMP(TVO_CSC_AUX_M3); | ||||||
|  | 	DBGFS_DUMP(TVO_CSC_AUX_M4); | ||||||
|  | 	DBGFS_DUMP(TVO_CSC_AUX_M5); | ||||||
|  | 	DBGFS_DUMP(TVO_CSC_AUX_M6); | ||||||
|  | 	DBGFS_DUMP(TVO_CSC_AUX_M7); | ||||||
|  | 	DBGFS_DUMP(TVO_AUX_IN_VID_FORMAT); | ||||||
|  | 	seq_puts(s, "\n"); | ||||||
|  | 
 | ||||||
|  | 	mutex_unlock(&dev->struct_mutex); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct drm_info_list tvout_debugfs_files[] = { | ||||||
|  | 	{ "tvout", tvout_dbg_show, 0, NULL }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void tvout_debugfs_exit(struct sti_tvout *tvout, struct drm_minor *minor) | ||||||
|  | { | ||||||
|  | 	drm_debugfs_remove_files(tvout_debugfs_files, | ||||||
|  | 				 ARRAY_SIZE(tvout_debugfs_files), | ||||||
|  | 				 minor); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int tvout_debugfs_init(struct sti_tvout *tvout, struct drm_minor *minor) | ||||||
|  | { | ||||||
|  | 	unsigned int i; | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(tvout_debugfs_files); i++) | ||||||
|  | 		tvout_debugfs_files[i].data = tvout; | ||||||
|  | 
 | ||||||
|  | 	return drm_debugfs_create_files(tvout_debugfs_files, | ||||||
|  | 					ARRAY_SIZE(tvout_debugfs_files), | ||||||
|  | 					minor->debugfs_root, minor); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void sti_tvout_encoder_dpms(struct drm_encoder *encoder, int mode) | static void sti_tvout_encoder_dpms(struct drm_encoder *encoder, int mode) | ||||||
| { | { | ||||||
| } | } | ||||||
| @ -446,10 +623,6 @@ static void sti_tvout_encoder_mode_set(struct drm_encoder *encoder, | |||||||
| { | { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void sti_tvout_encoder_prepare(struct drm_encoder *encoder) |  | ||||||
| { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void sti_tvout_encoder_destroy(struct drm_encoder *encoder) | static void sti_tvout_encoder_destroy(struct drm_encoder *encoder) | ||||||
| { | { | ||||||
| 	struct sti_tvout_encoder *sti_encoder = to_sti_tvout_encoder(encoder); | 	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 = { | |||||||
| 	.destroy = sti_tvout_encoder_destroy, | 	.destroy = sti_tvout_encoder_destroy, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void sti_dvo_encoder_commit(struct drm_encoder *encoder) | static void sti_dvo_encoder_enable(struct drm_encoder *encoder) | ||||||
| { | { | ||||||
| 	struct sti_tvout *tvout = to_sti_tvout(encoder); | 	struct sti_tvout *tvout = to_sti_tvout(encoder); | ||||||
| 
 | 
 | ||||||
|  | 	tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode); | ||||||
|  | 
 | ||||||
| 	tvout_dvo_start(tvout, sti_crtc_is_main(encoder->crtc)); | 	tvout_dvo_start(tvout, sti_crtc_is_main(encoder->crtc)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -480,8 +655,7 @@ static void sti_dvo_encoder_disable(struct drm_encoder *encoder) | |||||||
| static const struct drm_encoder_helper_funcs sti_dvo_encoder_helper_funcs = { | static const struct drm_encoder_helper_funcs sti_dvo_encoder_helper_funcs = { | ||||||
| 	.dpms = sti_tvout_encoder_dpms, | 	.dpms = sti_tvout_encoder_dpms, | ||||||
| 	.mode_set = sti_tvout_encoder_mode_set, | 	.mode_set = sti_tvout_encoder_mode_set, | ||||||
| 	.prepare = sti_tvout_encoder_prepare, | 	.enable = sti_dvo_encoder_enable, | ||||||
| 	.commit = sti_dvo_encoder_commit, |  | ||||||
| 	.disable = sti_dvo_encoder_disable, | 	.disable = sti_dvo_encoder_disable, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -512,10 +686,12 @@ sti_tvout_create_dvo_encoder(struct drm_device *dev, | |||||||
| 	return drm_encoder; | 	return drm_encoder; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void sti_hda_encoder_commit(struct drm_encoder *encoder) | static void sti_hda_encoder_enable(struct drm_encoder *encoder) | ||||||
| { | { | ||||||
| 	struct sti_tvout *tvout = to_sti_tvout(encoder); | 	struct sti_tvout *tvout = to_sti_tvout(encoder); | ||||||
| 
 | 
 | ||||||
|  | 	tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode); | ||||||
|  | 
 | ||||||
| 	tvout_hda_start(tvout, sti_crtc_is_main(encoder->crtc)); | 	tvout_hda_start(tvout, sti_crtc_is_main(encoder->crtc)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -533,8 +709,7 @@ static void sti_hda_encoder_disable(struct drm_encoder *encoder) | |||||||
| static const struct drm_encoder_helper_funcs sti_hda_encoder_helper_funcs = { | static const struct drm_encoder_helper_funcs sti_hda_encoder_helper_funcs = { | ||||||
| 	.dpms = sti_tvout_encoder_dpms, | 	.dpms = sti_tvout_encoder_dpms, | ||||||
| 	.mode_set = sti_tvout_encoder_mode_set, | 	.mode_set = sti_tvout_encoder_mode_set, | ||||||
| 	.prepare = sti_tvout_encoder_prepare, | 	.commit = sti_hda_encoder_enable, | ||||||
| 	.commit = sti_hda_encoder_commit, |  | ||||||
| 	.disable = sti_hda_encoder_disable, | 	.disable = sti_hda_encoder_disable, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -563,10 +738,12 @@ static struct drm_encoder *sti_tvout_create_hda_encoder(struct drm_device *dev, | |||||||
| 	return drm_encoder; | 	return drm_encoder; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void sti_hdmi_encoder_commit(struct drm_encoder *encoder) | static void sti_hdmi_encoder_enable(struct drm_encoder *encoder) | ||||||
| { | { | ||||||
| 	struct sti_tvout *tvout = to_sti_tvout(encoder); | 	struct sti_tvout *tvout = to_sti_tvout(encoder); | ||||||
| 
 | 
 | ||||||
|  | 	tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode); | ||||||
|  | 
 | ||||||
| 	tvout_hdmi_start(tvout, sti_crtc_is_main(encoder->crtc)); | 	tvout_hdmi_start(tvout, sti_crtc_is_main(encoder->crtc)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -581,8 +758,7 @@ static void sti_hdmi_encoder_disable(struct drm_encoder *encoder) | |||||||
| static const struct drm_encoder_helper_funcs sti_hdmi_encoder_helper_funcs = { | static const struct drm_encoder_helper_funcs sti_hdmi_encoder_helper_funcs = { | ||||||
| 	.dpms = sti_tvout_encoder_dpms, | 	.dpms = sti_tvout_encoder_dpms, | ||||||
| 	.mode_set = sti_tvout_encoder_mode_set, | 	.mode_set = sti_tvout_encoder_mode_set, | ||||||
| 	.prepare = sti_tvout_encoder_prepare, | 	.commit = sti_hdmi_encoder_enable, | ||||||
| 	.commit = sti_hdmi_encoder_commit, |  | ||||||
| 	.disable = sti_hdmi_encoder_disable, | 	.disable = sti_hdmi_encoder_disable, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -628,26 +804,24 @@ static void sti_tvout_destroy_encoders(struct sti_tvout *tvout) | |||||||
| 	if (tvout->hda) | 	if (tvout->hda) | ||||||
| 		drm_encoder_cleanup(tvout->hda); | 		drm_encoder_cleanup(tvout->hda); | ||||||
| 	tvout->hda = NULL; | 	tvout->hda = NULL; | ||||||
|  | 
 | ||||||
|  | 	if (tvout->dvo) | ||||||
|  | 		drm_encoder_cleanup(tvout->dvo); | ||||||
|  | 	tvout->dvo = NULL; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int sti_tvout_bind(struct device *dev, struct device *master, void *data) | static int sti_tvout_bind(struct device *dev, struct device *master, void *data) | ||||||
| { | { | ||||||
| 	struct sti_tvout *tvout = dev_get_drvdata(dev); | 	struct sti_tvout *tvout = dev_get_drvdata(dev); | ||||||
| 	struct drm_device *drm_dev = data; | 	struct drm_device *drm_dev = data; | ||||||
| 	unsigned int i; |  | ||||||
| 
 | 
 | ||||||
| 	tvout->drm_dev = drm_dev; | 	tvout->drm_dev = drm_dev; | ||||||
| 
 | 
 | ||||||
| 	/* set preformatter matrix */ |  | ||||||
| 	for (i = 0; i < 8; i++) { |  | ||||||
| 		tvout_write(tvout, rgb_to_ycbcr_601[i], |  | ||||||
| 			TVO_CSC_MAIN_M0 + (i * 4)); |  | ||||||
| 		tvout_write(tvout, rgb_to_ycbcr_601[i], |  | ||||||
| 			TVO_CSC_AUX_M0 + (i * 4)); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	sti_tvout_create_encoders(drm_dev, tvout); | 	sti_tvout_create_encoders(drm_dev, tvout); | ||||||
| 
 | 
 | ||||||
|  | 	if (tvout_debugfs_init(tvout, drm_dev->primary)) | ||||||
|  | 		DRM_ERROR("TVOUT debugfs setup failed\n"); | ||||||
|  | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -655,8 +829,11 @@ static void sti_tvout_unbind(struct device *dev, struct device *master, | |||||||
| 	void *data) | 	void *data) | ||||||
| { | { | ||||||
| 	struct sti_tvout *tvout = dev_get_drvdata(dev); | 	struct sti_tvout *tvout = dev_get_drvdata(dev); | ||||||
|  | 	struct drm_device *drm_dev = data; | ||||||
| 
 | 
 | ||||||
| 	sti_tvout_destroy_encoders(tvout); | 	sti_tvout_destroy_encoders(tvout); | ||||||
|  | 
 | ||||||
|  | 	tvout_debugfs_exit(tvout, drm_dev->primary); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static const struct component_ops sti_tvout_ops = { | static const struct component_ops sti_tvout_ops = { | ||||||
|  | |||||||
| @ -42,6 +42,104 @@ | |||||||
| #define VID_MPR1_BT709          0x0AC50000 | #define VID_MPR1_BT709          0x0AC50000 | ||||||
| #define VID_MPR2_BT709          0x07150545 | #define VID_MPR2_BT709          0x07150545 | ||||||
| #define VID_MPR3_BT709          0x00000AE8 | #define VID_MPR3_BT709          0x00000AE8 | ||||||
|  | /* YCbCr to RGB BT709:
 | ||||||
|  |  * R = Y+1.3711Cr | ||||||
|  |  * G = Y-0.6992Cr-0.3359Cb | ||||||
|  |  * B = Y+1.7344Cb | ||||||
|  |  */ | ||||||
|  | #define VID_MPR0_BT601          0x0A800000 | ||||||
|  | #define VID_MPR1_BT601          0x0AAF0000 | ||||||
|  | #define VID_MPR2_BT601          0x094E0754 | ||||||
|  | #define VID_MPR3_BT601          0x00000ADD | ||||||
|  | 
 | ||||||
|  | #define VID_MIN_HD_HEIGHT       720 | ||||||
|  | 
 | ||||||
|  | #define DBGFS_DUMP(reg) seq_printf(s, "\n  %-25s 0x%08X", #reg, \ | ||||||
|  | 				   readl(vid->regs + reg)) | ||||||
|  | 
 | ||||||
|  | static void vid_dbg_ctl(struct seq_file *s, int val) | ||||||
|  | { | ||||||
|  | 	val = val >> 30; | ||||||
|  | 	seq_puts(s, "\t"); | ||||||
|  | 
 | ||||||
|  | 	if (!(val & 1)) | ||||||
|  | 		seq_puts(s, "NOT "); | ||||||
|  | 	seq_puts(s, "ignored on main mixer - "); | ||||||
|  | 
 | ||||||
|  | 	if (!(val & 2)) | ||||||
|  | 		seq_puts(s, "NOT "); | ||||||
|  | 	seq_puts(s, "ignored on aux mixer"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void vid_dbg_vpo(struct seq_file *s, int val) | ||||||
|  | { | ||||||
|  | 	seq_printf(s, "\txdo:%4d\tydo:%4d", val & 0x0FFF, (val >> 16) & 0x0FFF); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void vid_dbg_vps(struct seq_file *s, int val) | ||||||
|  | { | ||||||
|  | 	seq_printf(s, "\txds:%4d\tyds:%4d", val & 0x0FFF, (val >> 16) & 0x0FFF); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void vid_dbg_mst(struct seq_file *s, int val) | ||||||
|  | { | ||||||
|  | 	if (val & 1) | ||||||
|  | 		seq_puts(s, "\tBUFFER UNDERFLOW!"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int vid_dbg_show(struct seq_file *s, void *arg) | ||||||
|  | { | ||||||
|  | 	struct drm_info_node *node = s->private; | ||||||
|  | 	struct sti_vid *vid = (struct sti_vid *)node->info_ent->data; | ||||||
|  | 	struct drm_device *dev = node->minor->dev; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = mutex_lock_interruptible(&dev->struct_mutex); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	seq_printf(s, "VID: (vaddr= 0x%p)", vid->regs); | ||||||
|  | 
 | ||||||
|  | 	DBGFS_DUMP(VID_CTL); | ||||||
|  | 	vid_dbg_ctl(s, readl(vid->regs + VID_CTL)); | ||||||
|  | 	DBGFS_DUMP(VID_ALP); | ||||||
|  | 	DBGFS_DUMP(VID_CLF); | ||||||
|  | 	DBGFS_DUMP(VID_VPO); | ||||||
|  | 	vid_dbg_vpo(s, readl(vid->regs + VID_VPO)); | ||||||
|  | 	DBGFS_DUMP(VID_VPS); | ||||||
|  | 	vid_dbg_vps(s, readl(vid->regs + VID_VPS)); | ||||||
|  | 	DBGFS_DUMP(VID_KEY1); | ||||||
|  | 	DBGFS_DUMP(VID_KEY2); | ||||||
|  | 	DBGFS_DUMP(VID_MPR0); | ||||||
|  | 	DBGFS_DUMP(VID_MPR1); | ||||||
|  | 	DBGFS_DUMP(VID_MPR2); | ||||||
|  | 	DBGFS_DUMP(VID_MPR3); | ||||||
|  | 	DBGFS_DUMP(VID_MST); | ||||||
|  | 	vid_dbg_mst(s, readl(vid->regs + VID_MST)); | ||||||
|  | 	DBGFS_DUMP(VID_BC); | ||||||
|  | 	DBGFS_DUMP(VID_TINT); | ||||||
|  | 	DBGFS_DUMP(VID_CSAT); | ||||||
|  | 	seq_puts(s, "\n"); | ||||||
|  | 
 | ||||||
|  | 	mutex_unlock(&dev->struct_mutex); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct drm_info_list vid_debugfs_files[] = { | ||||||
|  | 	{ "vid", vid_dbg_show, 0, NULL }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int vid_debugfs_init(struct sti_vid *vid, struct drm_minor *minor) | ||||||
|  | { | ||||||
|  | 	unsigned int i; | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(vid_debugfs_files); i++) | ||||||
|  | 		vid_debugfs_files[i].data = vid; | ||||||
|  | 
 | ||||||
|  | 	return drm_debugfs_create_files(vid_debugfs_files, | ||||||
|  | 					ARRAY_SIZE(vid_debugfs_files), | ||||||
|  | 					minor->debugfs_root, minor); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| void sti_vid_commit(struct sti_vid *vid, | void sti_vid_commit(struct sti_vid *vid, | ||||||
| 		    struct drm_plane_state *state) | 		    struct drm_plane_state *state) | ||||||
| @ -52,6 +150,7 @@ void sti_vid_commit(struct sti_vid *vid, | |||||||
| 	int dst_y = state->crtc_y; | 	int dst_y = state->crtc_y; | ||||||
| 	int dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); | 	int dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); | ||||||
| 	int dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); | 	int dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); | ||||||
|  | 	int src_h = state->src_h >> 16; | ||||||
| 	u32 val, ydo, xdo, yds, xds; | 	u32 val, ydo, xdo, yds, xds; | ||||||
| 
 | 
 | ||||||
| 	/* Input / output size
 | 	/* Input / output size
 | ||||||
| @ -71,6 +170,19 @@ void sti_vid_commit(struct sti_vid *vid, | |||||||
| 
 | 
 | ||||||
| 	writel((ydo << 16) | xdo, vid->regs + VID_VPO); | 	writel((ydo << 16) | xdo, vid->regs + VID_VPO); | ||||||
| 	writel((yds << 16) | xds, vid->regs + VID_VPS); | 	writel((yds << 16) | xds, vid->regs + VID_VPS); | ||||||
|  | 
 | ||||||
|  | 	/* Color conversion parameters */ | ||||||
|  | 	if (src_h >= VID_MIN_HD_HEIGHT) { | ||||||
|  | 		writel(VID_MPR0_BT709, vid->regs + VID_MPR0); | ||||||
|  | 		writel(VID_MPR1_BT709, vid->regs + VID_MPR1); | ||||||
|  | 		writel(VID_MPR2_BT709, vid->regs + VID_MPR2); | ||||||
|  | 		writel(VID_MPR3_BT709, vid->regs + VID_MPR3); | ||||||
|  | 	} else { | ||||||
|  | 		writel(VID_MPR0_BT601, vid->regs + VID_MPR0); | ||||||
|  | 		writel(VID_MPR1_BT601, vid->regs + VID_MPR1); | ||||||
|  | 		writel(VID_MPR2_BT601, vid->regs + VID_MPR2); | ||||||
|  | 		writel(VID_MPR3_BT601, vid->regs + VID_MPR3); | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void sti_vid_disable(struct sti_vid *vid) | void sti_vid_disable(struct sti_vid *vid) | ||||||
| @ -91,20 +203,14 @@ static void sti_vid_init(struct sti_vid *vid) | |||||||
| 	/* Opaque */ | 	/* Opaque */ | ||||||
| 	writel(VID_ALP_OPAQUE, vid->regs + VID_ALP); | 	writel(VID_ALP_OPAQUE, vid->regs + VID_ALP); | ||||||
| 
 | 
 | ||||||
| 	/* Color conversion parameters */ |  | ||||||
| 	writel(VID_MPR0_BT709, vid->regs + VID_MPR0); |  | ||||||
| 	writel(VID_MPR1_BT709, vid->regs + VID_MPR1); |  | ||||||
| 	writel(VID_MPR2_BT709, vid->regs + VID_MPR2); |  | ||||||
| 	writel(VID_MPR3_BT709, vid->regs + VID_MPR3); |  | ||||||
| 
 |  | ||||||
| 	/* Brightness, contrast, tint, saturation */ | 	/* Brightness, contrast, tint, saturation */ | ||||||
| 	writel(VID_BC_DFLT, vid->regs + VID_BC); | 	writel(VID_BC_DFLT, vid->regs + VID_BC); | ||||||
| 	writel(VID_TINT_DFLT, vid->regs + VID_TINT); | 	writel(VID_TINT_DFLT, vid->regs + VID_TINT); | ||||||
| 	writel(VID_CSAT_DFLT, vid->regs + VID_CSAT); | 	writel(VID_CSAT_DFLT, vid->regs + VID_CSAT); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct sti_vid *sti_vid_create(struct device *dev, int id, | struct sti_vid *sti_vid_create(struct device *dev, struct drm_device *drm_dev, | ||||||
| 			       void __iomem *baseaddr) | 			       int id, void __iomem *baseaddr) | ||||||
| { | { | ||||||
| 	struct sti_vid *vid; | 	struct sti_vid *vid; | ||||||
| 
 | 
 | ||||||
| @ -120,5 +226,8 @@ struct sti_vid *sti_vid_create(struct device *dev, int id, | |||||||
| 
 | 
 | ||||||
| 	sti_vid_init(vid); | 	sti_vid_init(vid); | ||||||
| 
 | 
 | ||||||
|  | 	if (vid_debugfs_init(vid, drm_dev->primary)) | ||||||
|  | 		DRM_ERROR("VID debugfs setup failed\n"); | ||||||
|  | 
 | ||||||
| 	return vid; | 	return vid; | ||||||
| } | } | ||||||
|  | |||||||
| @ -23,7 +23,7 @@ struct sti_vid { | |||||||
| void sti_vid_commit(struct sti_vid *vid, | void sti_vid_commit(struct sti_vid *vid, | ||||||
| 		    struct drm_plane_state *state); | 		    struct drm_plane_state *state); | ||||||
| void sti_vid_disable(struct sti_vid *vid); | void sti_vid_disable(struct sti_vid *vid); | ||||||
| struct sti_vid *sti_vid_create(struct device *dev, int id, | struct sti_vid *sti_vid_create(struct device *dev, struct drm_device *drm_dev, | ||||||
| 			       void __iomem *baseaddr); | 			       int id, void __iomem *baseaddr); | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -15,8 +15,8 @@ | |||||||
| 
 | 
 | ||||||
| #include "sti_vtg.h" | #include "sti_vtg.h" | ||||||
| 
 | 
 | ||||||
| #define VTG_TYPE_MASTER         0 | #define VTG_MODE_MASTER         0 | ||||||
| #define VTG_TYPE_SLAVE_BY_EXT0  1 | #define VTG_MODE_SLAVE_BY_EXT0  1 | ||||||
| 
 | 
 | ||||||
| /* registers offset */ | /* registers offset */ | ||||||
| #define VTG_MODE            0x0000 | #define VTG_MODE            0x0000 | ||||||
| @ -64,6 +64,9 @@ | |||||||
| /* Delay introduced by the HDMI in nb of pixel */ | /* Delay introduced by the HDMI in nb of pixel */ | ||||||
| #define HDMI_DELAY          (5) | #define HDMI_DELAY          (5) | ||||||
| 
 | 
 | ||||||
|  | /* Delay introduced by the DVO in nb of pixel */ | ||||||
|  | #define DVO_DELAY           (2) | ||||||
|  | 
 | ||||||
| /* delay introduced by the Arbitrary Waveform Generator in nb of pixels */ | /* delay introduced by the Arbitrary Waveform Generator in nb of pixels */ | ||||||
| #define AWG_DELAY_HD        (-9) | #define AWG_DELAY_HD        (-9) | ||||||
| #define AWG_DELAY_ED        (-8) | #define AWG_DELAY_ED        (-8) | ||||||
| @ -71,13 +74,61 @@ | |||||||
| 
 | 
 | ||||||
| LIST_HEAD(vtg_lookup); | LIST_HEAD(vtg_lookup); | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * STI VTG register offset structure | ||||||
|  |  * | ||||||
|  |  *@h_hd:     stores the VTG_H_HD_x     register offset | ||||||
|  |  *@top_v_vd: stores the VTG_TOP_V_VD_x register offset | ||||||
|  |  *@bot_v_vd: stores the VTG_BOT_V_VD_x register offset | ||||||
|  |  *@top_v_hd: stores the VTG_TOP_V_HD_x register offset | ||||||
|  |  *@bot_v_hd: stores the VTG_BOT_V_HD_x register offset | ||||||
|  |  */ | ||||||
|  | struct sti_vtg_regs_offs { | ||||||
|  | 	u32 h_hd; | ||||||
|  | 	u32 top_v_vd; | ||||||
|  | 	u32 bot_v_vd; | ||||||
|  | 	u32 top_v_hd; | ||||||
|  | 	u32 bot_v_hd; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define VTG_MAX_SYNC_OUTPUT 4 | ||||||
|  | static const struct sti_vtg_regs_offs vtg_regs_offs[VTG_MAX_SYNC_OUTPUT] = { | ||||||
|  | 	{ VTG_H_HD_1, | ||||||
|  | 	  VTG_TOP_V_VD_1, VTG_BOT_V_VD_1, VTG_TOP_V_HD_1, VTG_BOT_V_HD_1 }, | ||||||
|  | 	{ VTG_H_HD_2, | ||||||
|  | 	  VTG_TOP_V_VD_2, VTG_BOT_V_VD_2, VTG_TOP_V_HD_2, VTG_BOT_V_HD_2 }, | ||||||
|  | 	{ VTG_H_HD_3, | ||||||
|  | 	  VTG_TOP_V_VD_3, VTG_BOT_V_VD_3, VTG_TOP_V_HD_3, VTG_BOT_V_HD_3 }, | ||||||
|  | 	{ VTG_H_HD_4, | ||||||
|  | 	  VTG_TOP_V_VD_4, VTG_BOT_V_VD_4, VTG_TOP_V_HD_4, VTG_BOT_V_HD_4 } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * STI VTG synchronisation parameters structure | ||||||
|  |  * | ||||||
|  |  *@hsync: sample number falling and rising edge | ||||||
|  |  *@vsync_line_top: vertical top field line number falling and rising edge | ||||||
|  |  *@vsync_line_bot: vertical bottom field line number falling and rising edge | ||||||
|  |  *@vsync_off_top: vertical top field sample number rising and falling edge | ||||||
|  |  *@vsync_off_bot: vertical bottom field sample number rising and falling edge | ||||||
|  |  */ | ||||||
|  | struct sti_vtg_sync_params { | ||||||
|  | 	u32 hsync; | ||||||
|  | 	u32 vsync_line_top; | ||||||
|  | 	u32 vsync_line_bot; | ||||||
|  | 	u32 vsync_off_top; | ||||||
|  | 	u32 vsync_off_bot; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * STI VTG structure |  * STI VTG structure | ||||||
|  * |  * | ||||||
|  * @dev: pointer to device driver |  * @dev: pointer to device driver | ||||||
|  * @data: data associated to the device |  * @np: device node | ||||||
|  |  * @regs: register mapping | ||||||
|  |  * @sync_params: synchronisation parameters used to generate timings | ||||||
|  * @irq: VTG irq |  * @irq: VTG irq | ||||||
|  * @type: VTG type (main or aux) |  * @irq_status: store the IRQ status value | ||||||
|  * @notifier_list: notifier callback |  * @notifier_list: notifier callback | ||||||
|  * @crtc: the CRTC for vblank event |  * @crtc: the CRTC for vblank event | ||||||
|  * @slave: slave vtg |  * @slave: slave vtg | ||||||
| @ -87,6 +138,7 @@ struct sti_vtg { | |||||||
| 	struct device *dev; | 	struct device *dev; | ||||||
| 	struct device_node *np; | 	struct device_node *np; | ||||||
| 	void __iomem *regs; | 	void __iomem *regs; | ||||||
|  | 	struct sti_vtg_sync_params sync_params[VTG_MAX_SYNC_OUTPUT]; | ||||||
| 	int irq; | 	int irq; | ||||||
| 	u32 irq_status; | 	u32 irq_status; | ||||||
| 	struct raw_notifier_head notifier_list; | 	struct raw_notifier_head notifier_list; | ||||||
| @ -146,13 +198,69 @@ static void vtg_set_output_window(void __iomem *regs, | |||||||
| 	writel(video_bottom_field_stop, regs + VTG_VID_BFS); | 	writel(video_bottom_field_stop, regs + VTG_VID_BFS); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void vtg_set_mode(struct sti_vtg *vtg, | static void vtg_set_hsync_vsync_pos(struct sti_vtg_sync_params *sync, | ||||||
| 			 int type, const struct drm_display_mode *mode) | 				    int delay, | ||||||
|  | 				    const struct drm_display_mode *mode) | ||||||
| { | { | ||||||
| 	u32 tmp; | 	long clocksperline, start, stop; | ||||||
|  | 	u32 risesync_top, fallsync_top; | ||||||
|  | 	u32 risesync_offs_top, fallsync_offs_top; | ||||||
|  | 
 | ||||||
|  | 	clocksperline = mode->htotal; | ||||||
|  | 
 | ||||||
|  | 	/* Get the hsync position */ | ||||||
|  | 	start = 0; | ||||||
|  | 	stop = mode->hsync_end - mode->hsync_start; | ||||||
|  | 
 | ||||||
|  | 	start += delay; | ||||||
|  | 	stop  += delay; | ||||||
|  | 
 | ||||||
|  | 	if (start < 0) | ||||||
|  | 		start += clocksperline; | ||||||
|  | 	else if (start >= clocksperline) | ||||||
|  | 		start -= clocksperline; | ||||||
|  | 
 | ||||||
|  | 	if (stop < 0) | ||||||
|  | 		stop += clocksperline; | ||||||
|  | 	else if (stop >= clocksperline) | ||||||
|  | 		stop -= clocksperline; | ||||||
|  | 
 | ||||||
|  | 	sync->hsync = (stop << 16) | start; | ||||||
|  | 
 | ||||||
|  | 	/* Get the vsync position */ | ||||||
|  | 	if (delay >= 0) { | ||||||
|  | 		risesync_top = 1; | ||||||
|  | 		fallsync_top = risesync_top; | ||||||
|  | 		fallsync_top += mode->vsync_end - mode->vsync_start; | ||||||
|  | 
 | ||||||
|  | 		fallsync_offs_top = (u32)delay; | ||||||
|  | 		risesync_offs_top = (u32)delay; | ||||||
|  | 	} else { | ||||||
|  | 		risesync_top = mode->vtotal; | ||||||
|  | 		fallsync_top = mode->vsync_end - mode->vsync_start; | ||||||
|  | 
 | ||||||
|  | 		fallsync_offs_top = clocksperline + delay; | ||||||
|  | 		risesync_offs_top = clocksperline + delay; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	sync->vsync_line_top = (fallsync_top << 16) | risesync_top; | ||||||
|  | 	sync->vsync_off_top = (fallsync_offs_top << 16) | risesync_offs_top; | ||||||
|  | 
 | ||||||
|  | 	/* Only progressive supported for now */ | ||||||
|  | 	sync->vsync_line_bot = sync->vsync_line_top; | ||||||
|  | 	sync->vsync_off_bot = sync->vsync_off_top; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void vtg_set_mode(struct sti_vtg *vtg, | ||||||
|  | 			 int type, | ||||||
|  | 			 struct sti_vtg_sync_params *sync, | ||||||
|  | 			 const struct drm_display_mode *mode) | ||||||
|  | { | ||||||
|  | 	unsigned int i; | ||||||
| 
 | 
 | ||||||
| 	if (vtg->slave) | 	if (vtg->slave) | ||||||
| 		vtg_set_mode(vtg->slave, VTG_TYPE_SLAVE_BY_EXT0, mode); | 		vtg_set_mode(vtg->slave, VTG_MODE_SLAVE_BY_EXT0, | ||||||
|  | 			     vtg->sync_params, mode); | ||||||
| 
 | 
 | ||||||
| 	/* Set the number of clock cycles per line */ | 	/* Set the number of clock cycles per line */ | ||||||
| 	writel(mode->htotal, vtg->regs + VTG_CLKLN); | 	writel(mode->htotal, vtg->regs + VTG_CLKLN); | ||||||
| @ -163,57 +271,31 @@ static void vtg_set_mode(struct sti_vtg *vtg, | |||||||
| 	/* Program output window */ | 	/* Program output window */ | ||||||
| 	vtg_set_output_window(vtg->regs, mode); | 	vtg_set_output_window(vtg->regs, mode); | ||||||
| 
 | 
 | ||||||
| 	/* prepare VTG set 1 for HDMI */ | 	/* Set hsync and vsync position for HDMI */ | ||||||
| 	tmp = (mode->hsync_end - mode->hsync_start + HDMI_DELAY) << 16; | 	vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDMI - 1], HDMI_DELAY, mode); | ||||||
| 	tmp |= HDMI_DELAY; |  | ||||||
| 	writel(tmp, vtg->regs + VTG_H_HD_1); |  | ||||||
| 
 | 
 | ||||||
| 	tmp = (mode->vsync_end - mode->vsync_start + 1) << 16; | 	/* Set hsync and vsync position for HD DCS */ | ||||||
| 	tmp |= 1; | 	vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDDCS - 1], 0, mode); | ||||||
| 	writel(tmp, vtg->regs + VTG_TOP_V_VD_1); |  | ||||||
| 	writel(tmp, vtg->regs + VTG_BOT_V_VD_1); |  | ||||||
| 
 | 
 | ||||||
| 	tmp = HDMI_DELAY << 16; | 	/* Set hsync and vsync position for HDF */ | ||||||
| 	tmp |= HDMI_DELAY; | 	vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDF - 1], AWG_DELAY_HD, mode); | ||||||
| 	writel(tmp, vtg->regs + VTG_TOP_V_HD_1); |  | ||||||
| 	writel(tmp, vtg->regs + VTG_BOT_V_HD_1); |  | ||||||
| 
 | 
 | ||||||
| 	/* prepare VTG set 2 for for HD DCS */ | 	/* Set hsync and vsync position for DVO */ | ||||||
| 	tmp = (mode->hsync_end - mode->hsync_start) << 16; | 	vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_DVO - 1], DVO_DELAY, mode); | ||||||
| 	writel(tmp, vtg->regs + VTG_H_HD_2); |  | ||||||
| 
 | 
 | ||||||
| 	tmp = (mode->vsync_end - mode->vsync_start + 1) << 16; | 	/* Progam the syncs outputs */ | ||||||
| 	tmp |= 1; | 	for (i = 0; i < VTG_MAX_SYNC_OUTPUT ; i++) { | ||||||
| 	writel(tmp, vtg->regs + VTG_TOP_V_VD_2); | 		writel(sync[i].hsync, | ||||||
| 	writel(tmp, vtg->regs + VTG_BOT_V_VD_2); | 		       vtg->regs + vtg_regs_offs[i].h_hd); | ||||||
| 	writel(0, vtg->regs + VTG_TOP_V_HD_2); | 		writel(sync[i].vsync_line_top, | ||||||
| 	writel(0, vtg->regs + VTG_BOT_V_HD_2); | 		       vtg->regs + vtg_regs_offs[i].top_v_vd); | ||||||
| 
 | 		writel(sync[i].vsync_line_bot, | ||||||
| 	/* prepare VTG set 3 for HD Analog in HD mode */ | 		       vtg->regs + vtg_regs_offs[i].bot_v_vd); | ||||||
| 	tmp = (mode->hsync_end - mode->hsync_start + AWG_DELAY_HD) << 16; | 		writel(sync[i].vsync_off_top, | ||||||
| 	tmp |= mode->htotal + AWG_DELAY_HD; | 		       vtg->regs + vtg_regs_offs[i].top_v_hd); | ||||||
| 	writel(tmp, vtg->regs + VTG_H_HD_3); | 		writel(sync[i].vsync_off_bot, | ||||||
| 
 | 		       vtg->regs + vtg_regs_offs[i].bot_v_hd); | ||||||
| 	tmp = (mode->vsync_end - mode->vsync_start) << 16; | 	} | ||||||
| 	tmp |= mode->vtotal; |  | ||||||
| 	writel(tmp, vtg->regs + VTG_TOP_V_VD_3); |  | ||||||
| 	writel(tmp, vtg->regs + VTG_BOT_V_VD_3); |  | ||||||
| 
 |  | ||||||
| 	tmp = (mode->htotal + AWG_DELAY_HD) << 16; |  | ||||||
| 	tmp |= mode->htotal + AWG_DELAY_HD; |  | ||||||
| 	writel(tmp, vtg->regs + VTG_TOP_V_HD_3); |  | ||||||
| 	writel(tmp, vtg->regs + VTG_BOT_V_HD_3); |  | ||||||
| 
 |  | ||||||
| 	/* Prepare VTG set 4 for DVO */ |  | ||||||
| 	tmp = (mode->hsync_end - mode->hsync_start) << 16; |  | ||||||
| 	writel(tmp, vtg->regs + VTG_H_HD_4); |  | ||||||
| 
 |  | ||||||
| 	tmp = (mode->vsync_end - mode->vsync_start + 1) << 16; |  | ||||||
| 	tmp |= 1; |  | ||||||
| 	writel(tmp, vtg->regs + VTG_TOP_V_VD_4); |  | ||||||
| 	writel(tmp, vtg->regs + VTG_BOT_V_VD_4); |  | ||||||
| 	writel(0, vtg->regs + VTG_TOP_V_HD_4); |  | ||||||
| 	writel(0, vtg->regs + VTG_BOT_V_HD_4); |  | ||||||
| 
 | 
 | ||||||
| 	/* mode */ | 	/* mode */ | ||||||
| 	writel(type, vtg->regs + VTG_MODE); | 	writel(type, vtg->regs + VTG_MODE); | ||||||
| @ -231,7 +313,7 @@ void sti_vtg_set_config(struct sti_vtg *vtg, | |||||||
| 		const struct drm_display_mode *mode) | 		const struct drm_display_mode *mode) | ||||||
| { | { | ||||||
| 	/* write configuration */ | 	/* write configuration */ | ||||||
| 	vtg_set_mode(vtg, VTG_TYPE_MASTER, mode); | 	vtg_set_mode(vtg, VTG_MODE_MASTER, vtg->sync_params, mode); | ||||||
| 
 | 
 | ||||||
| 	vtg_reset(vtg); | 	vtg_reset(vtg); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -10,6 +10,11 @@ | |||||||
| #define VTG_TOP_FIELD_EVENT     1 | #define VTG_TOP_FIELD_EVENT     1 | ||||||
| #define VTG_BOTTOM_FIELD_EVENT  2 | #define VTG_BOTTOM_FIELD_EVENT  2 | ||||||
| 
 | 
 | ||||||
|  | #define VTG_SYNC_ID_HDMI        1 | ||||||
|  | #define VTG_SYNC_ID_HDDCS       2 | ||||||
|  | #define VTG_SYNC_ID_HDF         3 | ||||||
|  | #define VTG_SYNC_ID_DVO         4 | ||||||
|  | 
 | ||||||
| struct sti_vtg; | struct sti_vtg; | ||||||
| struct drm_display_mode; | struct drm_display_mode; | ||||||
| struct notifier_block; | struct notifier_block; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user