From ecb7e16bf187bc369cf6a5cd108582c01329980d Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Mon, 1 Dec 2014 15:40:09 -0800 Subject: drm: add helper to get crtc timings (v5) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to get hdisplay and vdisplay in a few places so create a helper to make our job easier. Note that drm_crtc_check_viewport() and intel_modeset_pipe_config() were previously making adjustments for doublescan modes and vscan > 1 modes, which was incorrect. Using our new helper fixes this mistake. v2 (by Matt): Use new stereo doubling function (suggested by Ville) v3 (by Matt): - Add missing kerneldoc (Daniel) - Use drm_mode_copy() (Jani) v4 (by Matt): - Drop stereo doubling function again; add 'stereo only' flag to drm_mode_set_crtcinfo() instead (Ville) v5 (by Matt): - Note behavioral change in drm_crtc_check_viewport() and intel_modeset_pipe_config(). (Ander) - Describe new adjustment flags in drm_mode_set_crtcinfo()'s kerneldoc. (Ander) Cc: dri-devel@lists.freedesktop.org Suggested-by: Ville Syrjälä Signed-off-by: Gustavo Padovan Signed-off-by: Matt Roper Acked-by: Dave Airlie Reviewed-by: Ander Conselvan de Oliveira Signed-off-by: Daniel Vetter --- include/drm/drm_crtc.h | 2 ++ include/drm/drm_modes.h | 3 +++ 2 files changed, 5 insertions(+) (limited to 'include') diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index dd2c16e43333..969da0f1ded8 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1161,6 +1161,8 @@ extern int drm_plane_init(struct drm_device *dev, extern void drm_plane_cleanup(struct drm_plane *plane); extern unsigned int drm_plane_index(struct drm_plane *plane); extern void drm_plane_force_disable(struct drm_plane *plane); +extern void drm_crtc_get_hv_timing(const struct drm_display_mode *mode, + int *hdisplay, int *vdisplay); extern int drm_crtc_check_viewport(const struct drm_crtc *crtc, int x, int y, const struct drm_display_mode *mode, diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h index 91d0582f924e..8f17811d27f5 100644 --- a/include/drm/drm_modes.h +++ b/include/drm/drm_modes.h @@ -90,6 +90,9 @@ enum drm_mode_status { #define CRTC_INTERLACE_HALVE_V (1 << 0) /* halve V values for interlacing */ #define CRTC_STEREO_DOUBLE (1 << 1) /* adjust timings for stereo modes */ +#define CRTC_NO_DBLSCAN (1 << 2) /* don't adjust doublescan */ +#define CRTC_NO_VSCAN (1 << 3) /* don't adjust doublescan */ +#define CRTC_STEREO_DOUBLE_ONLY (CRTC_NO_DBLSCAN | CRTC_NO_VSCAN) #define DRM_MODE_FLAG_3D_MAX DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF -- cgit v1.2.3 From ae28290be3871969fc3f64b480d42575a16ae990 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 10 Dec 2014 12:17:41 -0500 Subject: drm: bit of spell-check / editorializing. Signed-off-by: Rob Clark Signed-off-by: Daniel Vetter --- include/uapi/drm/drm_mode.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 86574b0005ff..aae71cb32123 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -338,7 +338,7 @@ struct drm_mode_fb_cmd2 { /* * In case of planar formats, this ioctl allows up to 4 - * buffer objects with offets and pitches per plane. + * buffer objects with offsets and pitches per plane. * The pitch and offset order is dictated by the fourcc, * e.g. NV12 (http://fourcc.org/yuv.php#NV12) is described as: * @@ -346,9 +346,9 @@ struct drm_mode_fb_cmd2 { * followed by an interleaved U/V plane containing * 8 bit 2x2 subsampled colour difference samples. * - * So it would consist of Y as offset[0] and UV as - * offeset[1]. Note that offset[0] will generally - * be 0. + * So it would consist of Y as offsets[0] and UV as + * offsets[1]. Note that offsets[0] will generally + * be 0 (but this is not required). */ __u32 handles[4]; __u32 pitches[4]; /* pitch for each plane */ -- cgit v1.2.3 From 05acaec334fcc1132d1e48c5042e044651e0b75b Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 17 Dec 2014 13:56:22 +0200 Subject: drm: Reorganize probed mode validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make drm_mode_validate_size() and drm_mode_validate_flag() deal with a single mode instead of having each iterate through the mode list. The hope is that in the future we might be able to share various mode validation functions between modeset and get_modes. Signed-off-by: Ville Syrjälä Reviewed-by: Alex Deucher Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_modes.c | 24 +++++++++++------------ drivers/gpu/drm/drm_probe_helper.c | 40 +++++++++++++++++--------------------- include/drm/drm_modes.h | 5 ++--- 3 files changed, 32 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 6d8b941c8200..19188667f928 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -907,8 +907,7 @@ EXPORT_SYMBOL(drm_mode_equal_no_clocks_no_stereo); /** * drm_mode_validate_size - make sure modes adhere to size constraints - * @dev: DRM device - * @mode_list: list of modes to check + * @mode: mode to check * @maxX: maximum width * @maxY: maximum height * @@ -916,20 +915,21 @@ EXPORT_SYMBOL(drm_mode_equal_no_clocks_no_stereo); * limitations of the DRM device/connector. If a mode is too big its status * member is updated with the appropriate validation failure code. The list * itself is not changed. + * + * Returns: + * The mode status */ -void drm_mode_validate_size(struct drm_device *dev, - struct list_head *mode_list, - int maxX, int maxY) +enum drm_mode_status +drm_mode_validate_size(const struct drm_display_mode *mode, + int maxX, int maxY) { - struct drm_display_mode *mode; + if (maxX > 0 && mode->hdisplay > maxX) + return MODE_VIRTUAL_X; - list_for_each_entry(mode, mode_list, head) { - if (maxX > 0 && mode->hdisplay > maxX) - mode->status = MODE_VIRTUAL_X; + if (maxY > 0 && mode->vdisplay > maxY) + return MODE_VIRTUAL_Y; - if (maxY > 0 && mode->vdisplay > maxY) - mode->status = MODE_VIRTUAL_Y; - } + return MODE_OK; } EXPORT_SYMBOL(drm_mode_validate_size); diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 7483a47de8e4..2161d8ed7c65 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -58,28 +58,23 @@ static bool drm_kms_helper_poll = true; module_param_named(poll, drm_kms_helper_poll, bool, 0600); -static void drm_mode_validate_flag(struct drm_connector *connector, - int flags) +static enum drm_mode_status +drm_mode_validate_flag(const struct drm_display_mode *mode, + int flags) { - struct drm_display_mode *mode; + if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && + !(flags & DRM_MODE_FLAG_INTERLACE)) + return MODE_NO_INTERLACE; - if (flags == (DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_INTERLACE | - DRM_MODE_FLAG_3D_MASK)) - return; + if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) && + !(flags & DRM_MODE_FLAG_DBLSCAN)) + return MODE_NO_DBLESCAN; - list_for_each_entry(mode, &connector->modes, head) { - if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && - !(flags & DRM_MODE_FLAG_INTERLACE)) - mode->status = MODE_NO_INTERLACE; - if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) && - !(flags & DRM_MODE_FLAG_DBLSCAN)) - mode->status = MODE_NO_DBLESCAN; - if ((mode->flags & DRM_MODE_FLAG_3D_MASK) && - !(flags & DRM_MODE_FLAG_3D_MASK)) - mode->status = MODE_NO_STEREO; - } + if ((mode->flags & DRM_MODE_FLAG_3D_MASK) && + !(flags & DRM_MODE_FLAG_3D_MASK)) + return MODE_NO_STEREO; - return; + return MODE_OK; } static int drm_helper_probe_add_cmdline_mode(struct drm_connector *connector) @@ -164,18 +159,19 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect drm_mode_connector_list_update(connector, merge_type_bits); - if (maxX && maxY) - drm_mode_validate_size(dev, &connector->modes, maxX, maxY); - if (connector->interlace_allowed) mode_flags |= DRM_MODE_FLAG_INTERLACE; if (connector->doublescan_allowed) mode_flags |= DRM_MODE_FLAG_DBLSCAN; if (connector->stereo_allowed) mode_flags |= DRM_MODE_FLAG_3D_MASK; - drm_mode_validate_flag(connector, mode_flags); list_for_each_entry(mode, &connector->modes, head) { + mode->status = drm_mode_validate_size(mode, maxX, maxY); + + if (mode->status == MODE_OK) + mode->status = drm_mode_validate_flag(mode, mode_flags); + if (mode->status == MODE_OK && connector_funcs->mode_valid) mode->status = connector_funcs->mode_valid(connector, mode); diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h index 91d0582f924e..6c531140d458 100644 --- a/include/drm/drm_modes.h +++ b/include/drm/drm_modes.h @@ -217,9 +217,8 @@ bool drm_mode_equal_no_clocks_no_stereo(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2); /* for use by the crtc helper probe functions */ -void drm_mode_validate_size(struct drm_device *dev, - struct list_head *mode_list, - int maxX, int maxY); +enum drm_mode_status drm_mode_validate_size(const struct drm_display_mode *mode, + int maxX, int maxY); void drm_mode_prune_invalid(struct drm_device *dev, struct list_head *mode_list, bool verbose); void drm_mode_sort(struct list_head *mode_list); -- cgit v1.2.3 From abc0b1447d4974963548777a5ba4a4457c82c426 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 17 Dec 2014 13:56:23 +0200 Subject: drm: Perform basic sanity checks on probed modes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure the timings of probed modes at least pass some very basic sanity checks. The checks include: - clock,hdisplay,vdisplay are non zero - sync pulse fits within the blanking period - htotal,vtotal are big enough I have not checked all the drivers to see if the modes the generate might violate these constraints. I'm hoping not, because that would mean either abandoning the idea of doing this from the core code, or fixing the drivers. I'm not entirely sure about limiting the sync pulse to the blanking period. Intel hardware doesn't support such things, but some other hardware might. However at least HDMI doesn't allow having sync pulse edges within the active period, so I'm thinking the check is probably OK to have in the common code. Signed-off-by: Ville Syrjälä Reviewed-by: Alex Deucher Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_modes.c | 32 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_probe_helper.c | 5 ++++- include/drm/drm_modes.h | 1 + 3 files changed, 37 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 19188667f928..11cc4deca55b 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -905,6 +905,38 @@ bool drm_mode_equal_no_clocks_no_stereo(const struct drm_display_mode *mode1, } EXPORT_SYMBOL(drm_mode_equal_no_clocks_no_stereo); +/** + * drm_mode_validate_basic - make sure the mode is somewhat sane + * @mode: mode to check + * + * Check that the mode timings are at least somewhat reasonable. + * Any hardware specific limits are left up for each driver to check. + * + * Returns: + * The mode status + */ +enum drm_mode_status +drm_mode_validate_basic(const struct drm_display_mode *mode) +{ + if (mode->clock == 0) + return MODE_CLOCK_LOW; + + if (mode->hdisplay == 0 || + mode->hsync_start < mode->hdisplay || + mode->hsync_end < mode->hsync_start || + mode->htotal < mode->hsync_end) + return MODE_H_ILLEGAL; + + if (mode->vdisplay == 0 || + mode->vsync_start < mode->vdisplay || + mode->vsync_end < mode->vsync_start || + mode->vtotal < mode->vsync_end) + return MODE_V_ILLEGAL; + + return MODE_OK; +} +EXPORT_SYMBOL(drm_mode_validate_basic); + /** * drm_mode_validate_size - make sure modes adhere to size constraints * @mode: mode to check diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 2161d8ed7c65..2fbdcca7ca9a 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -167,7 +167,10 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect mode_flags |= DRM_MODE_FLAG_3D_MASK; list_for_each_entry(mode, &connector->modes, head) { - mode->status = drm_mode_validate_size(mode, maxX, maxY); + mode->status = drm_mode_validate_basic(mode); + + if (mode->status == MODE_OK) + mode->status = drm_mode_validate_size(mode, maxX, maxY); if (mode->status == MODE_OK) mode->status = drm_mode_validate_flag(mode, mode_flags); diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h index 6c531140d458..a36a5bfce2f5 100644 --- a/include/drm/drm_modes.h +++ b/include/drm/drm_modes.h @@ -217,6 +217,7 @@ bool drm_mode_equal_no_clocks_no_stereo(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2); /* for use by the crtc helper probe functions */ +enum drm_mode_status drm_mode_validate_basic(const struct drm_display_mode *mode); enum drm_mode_status drm_mode_validate_size(const struct drm_display_mode *mode, int maxX, int maxY); void drm_mode_prune_invalid(struct drm_device *dev, -- cgit v1.2.3 From 261ea74f3689a997502f1264494f1749951a05a8 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 17 Dec 2014 16:41:40 +0100 Subject: drm: Remove stale comment The struct drm_connector_funcs kerneldoc refers to a part of struct drm_crtc_funcs that no longer exists. Signed-off-by: Thierry Reding Signed-off-by: Daniel Vetter --- include/drm/drm_crtc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index dd2c16e43333..eb32b09b3bab 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -455,7 +455,7 @@ struct drm_connector_state { /** * struct drm_connector_funcs - control connectors on a given device - * @dpms: set power state (see drm_crtc_funcs above) + * @dpms: set power state * @save: save connector state * @restore: restore connector state * @reset: reset connector after state has been invalidated (e.g. resume) -- cgit v1.2.3 From d9b13620fa09d2652008f96e083592c772532fd1 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 26 Nov 2014 16:57:41 +0100 Subject: drm/atomic-helper: Export both plane and modeset check helpers The default call sequence for these two parts won't fit for all drivers. So export the two pieces and explain with a bit of kerneldoc when each should be called. v2: Squash in fixup from Rob to actually add the newly exported functions to headers Cc: Rob Clark Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic_helper.c | 65 ++++++++++++++++++++++++++++++++----- include/drm/drm_atomic_helper.h | 4 +++ include/drm/drm_crtc.h | 8 +++++ 3 files changed, 69 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index bbdbe4721573..0cd71dac394e 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -330,7 +330,29 @@ mode_fixup(struct drm_atomic_state *state) return 0; } -static int +/** + * drm_atomic_helper_check - validate state object for modeset changes + * @dev: DRM device + * @state: the driver state object + * + * Check the state object to see if the requested state is physically possible. + * This does all the crtc and connector related computations for an atomic + * update. It computes and updates crtc_state->mode_changed, adds any additional + * connectors needed for full modesets and calls down into ->mode_fixup + * functions of the driver backend. + * + * IMPORTANT: + * + * Drivers which update ->mode_changed (e.g. in their ->atomic_check hooks if a + * plane update can't be done without a full modeset) _must_ call this function + * afterwards after that change. It is permitted to call this function multiple + * times for the same update, e.g. when the ->atomic_check functions depend upon + * the adjusted dotclock for fifo space allocation and watermark computation. + * + * RETURNS + * Zero for success or -errno + */ +int drm_atomic_helper_check_modeset(struct drm_device *dev, struct drm_atomic_state *state) { @@ -406,23 +428,23 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, return mode_fixup(state); } +EXPORT_SYMBOL(drm_atomic_helper_check_modeset); /** - * drm_atomic_helper_check - validate state object + * drm_atomic_helper_check - validate state object for modeset changes * @dev: DRM device * @state: the driver state object * * Check the state object to see if the requested state is physically possible. - * Only crtcs and planes have check callbacks, so for any additional (global) - * checking that a driver needs it can simply wrap that around this function. - * Drivers without such needs can directly use this as their ->atomic_check() - * callback. + * This does all the plane update related checks using by calling into the + * ->atomic_check hooks provided by the driver. * * RETURNS * Zero for success or -errno */ -int drm_atomic_helper_check(struct drm_device *dev, - struct drm_atomic_state *state) +int +drm_atomic_helper_check_planes(struct drm_device *dev, + struct drm_atomic_state *state) { int nplanes = dev->mode_config.num_total_plane; int ncrtcs = dev->mode_config.num_crtc; @@ -471,6 +493,33 @@ int drm_atomic_helper_check(struct drm_device *dev, } } + return ret; +} +EXPORT_SYMBOL(drm_atomic_helper_check_planes); + +/** + * drm_atomic_helper_check - validate state object + * @dev: DRM device + * @state: the driver state object + * + * Check the state object to see if the requested state is physically possible. + * Only crtcs and planes have check callbacks, so for any additional (global) + * checking that a driver needs it can simply wrap that around this function. + * Drivers without such needs can directly use this as their ->atomic_check() + * callback. + * + * RETURNS + * Zero for success or -errno + */ +int drm_atomic_helper_check(struct drm_device *dev, + struct drm_atomic_state *state) +{ + int ret; + + ret = drm_atomic_helper_check_planes(dev, state); + if (ret) + return ret; + ret = drm_atomic_helper_check_modeset(dev, state); if (ret) return ret; diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index f956b413311e..2095917ff8c7 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -30,6 +30,10 @@ #include +int drm_atomic_helper_check_modeset(struct drm_device *dev, + struct drm_atomic_state *state); +int drm_atomic_helper_check_planes(struct drm_device *dev, + struct drm_atomic_state *state); int drm_atomic_helper_check(struct drm_device *dev, struct drm_atomic_state *state); int drm_atomic_helper_commit(struct drm_device *dev, diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index b86329813ad3..4ee78212f8bf 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -238,6 +238,7 @@ struct drm_atomic_state; /** * struct drm_crtc_state - mutable CRTC state * @enable: whether the CRTC should be enabled, gates all other state + * @active: whether the CRTC is actively displaying (used for DPMS) * @mode_changed: for use by helpers and drivers when computing state updates * @plane_mask: bitmask of (1 << drm_plane_index(plane)) of attached planes * @last_vblank_count: for helpers and drivers to capture the vblank of the @@ -248,9 +249,16 @@ struct drm_atomic_state; * @event: optional pointer to a DRM event to signal upon completion of the * state update * @state: backpointer to global drm_atomic_state + * + * Note that the distinction between @enable and @active is rather subtile: + * Flipping @active while @enable is set without changing anything else may + * never return in a failure from the ->atomic_check callback. Userspace assumes + * that a DPMS On will always succeed. In other words: @enable controls resource + * assignment, @active controls the actual hardware state. */ struct drm_crtc_state { bool enable; + bool active; /* computed state bits used by helpers and drivers */ bool planes_changed : 1; -- cgit v1.2.3 From 07cc0ef67fa873c8d21e0b626d57753bfd190095 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 27 Nov 2014 15:49:39 +0100 Subject: drm/atomic: Introduce state->obj backpointers Useful since this way we can pass around just the state objects and will get ther real object, too. Specifically this allows us to again simplify the parameters for set_crtc_for_plane. v2: msm already has it's own specific plane_reset hook, don't forget that one! v3: Fixup kerneldoc, reported by 0-day builder. Cc: Rob Clark Reviewed-by: Rob Clark (v2) Tested-by: Rob Clark (v2) Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic.c | 13 ++++--------- drivers/gpu/drm/drm_atomic_helper.c | 19 ++++++++++++++----- drivers/gpu/drm/drm_crtc_helper.c | 2 ++ drivers/gpu/drm/drm_plane_helper.c | 2 ++ drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 1 + include/drm/drm_atomic.h | 4 ++-- include/drm/drm_crtc.h | 9 +++++++++ 7 files changed, 34 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index ff5f034cc405..cbd5e7240b6b 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -344,8 +344,7 @@ EXPORT_SYMBOL(drm_atomic_get_connector_state); /** * drm_atomic_set_crtc_for_plane - set crtc for plane - * @state: the incoming atomic state - * @plane: the plane whose incoming state to update + * @plane_state: the plane whose incoming state to update * @crtc: crtc to use for the plane * * Changing the assigned crtc for a plane requires us to grab the lock and state @@ -358,16 +357,12 @@ EXPORT_SYMBOL(drm_atomic_get_connector_state); * sequence must be restarted. All other errors are fatal. */ int -drm_atomic_set_crtc_for_plane(struct drm_atomic_state *state, - struct drm_plane *plane, struct drm_crtc *crtc) +drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, + struct drm_crtc *crtc) { - struct drm_plane_state *plane_state = - drm_atomic_get_plane_state(state, plane); + struct drm_plane *plane = plane_state->plane; struct drm_crtc_state *crtc_state; - if (WARN_ON(IS_ERR(plane_state))) - return PTR_ERR(plane_state); - if (plane_state->crtc) { crtc_state = drm_atomic_get_crtc_state(plane_state->state, plane_state->crtc); diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index eb380ee2dc58..379d37acd5b5 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1277,7 +1277,7 @@ retry: goto fail; } - ret = drm_atomic_set_crtc_for_plane(state, plane, crtc); + ret = drm_atomic_set_crtc_for_plane(plane_state, crtc); if (ret != 0) goto fail; drm_atomic_set_fb_for_plane(plane_state, fb); @@ -1356,7 +1356,7 @@ retry: goto fail; } - ret = drm_atomic_set_crtc_for_plane(state, plane, NULL); + ret = drm_atomic_set_crtc_for_plane(plane_state, NULL); if (ret != 0) goto fail; drm_atomic_set_fb_for_plane(plane_state, NULL); @@ -1519,7 +1519,7 @@ retry: crtc_state->enable = false; - ret = drm_atomic_set_crtc_for_plane(state, crtc->primary, NULL); + ret = drm_atomic_set_crtc_for_plane(primary_state, NULL); if (ret != 0) goto fail; @@ -1534,7 +1534,7 @@ retry: crtc_state->enable = true; drm_mode_copy(&crtc_state->mode, set->mode); - ret = drm_atomic_set_crtc_for_plane(state, crtc->primary, crtc); + ret = drm_atomic_set_crtc_for_plane(primary_state, crtc); if (ret != 0) goto fail; drm_atomic_set_fb_for_plane(primary_state, set->fb); @@ -1806,7 +1806,7 @@ retry: goto fail; } - ret = drm_atomic_set_crtc_for_plane(state, plane, crtc); + ret = drm_atomic_set_crtc_for_plane(plane_state, crtc); if (ret != 0) goto fail; drm_atomic_set_fb_for_plane(plane_state, fb); @@ -1869,6 +1869,9 @@ void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc) { kfree(crtc->state); crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL); + + if (crtc->state) + crtc->state->crtc = crtc; } EXPORT_SYMBOL(drm_atomic_helper_crtc_reset); @@ -1928,6 +1931,9 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane) kfree(plane->state); plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL); + + if (plane->state) + plane->state->plane = plane; } EXPORT_SYMBOL(drm_atomic_helper_plane_reset); @@ -1985,6 +1991,9 @@ void drm_atomic_helper_connector_reset(struct drm_connector *connector) { kfree(connector->state); connector->state = kzalloc(sizeof(*connector->state), GFP_KERNEL); + + if (connector->state) + connector->state->connector = connector; } EXPORT_SYMBOL(drm_atomic_helper_connector_reset); diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index d552708409de..b1979e7bdc88 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -946,6 +946,7 @@ int drm_helper_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mod crtc_state = kzalloc(sizeof(*crtc_state), GFP_KERNEL); if (!crtc_state) return -ENOMEM; + crtc_state->crtc = crtc; crtc_state->enable = true; crtc_state->planes_changed = true; @@ -1005,6 +1006,7 @@ int drm_helper_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, plane_state = kzalloc(sizeof(*plane_state), GFP_KERNEL); if (!plane_state) return -ENOMEM; + plane_state->plane = plane; plane_state->crtc = crtc; drm_atomic_set_fb_for_plane(plane_state, crtc->primary->fb); diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index 18a1ac6ac22f..ae61fb21ea86 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -517,6 +517,7 @@ int drm_plane_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, plane_state = kzalloc(sizeof(*plane_state), GFP_KERNEL); if (!plane_state) return -ENOMEM; + plane_state->plane = plane; plane_state->crtc = crtc; drm_atomic_set_fb_for_plane(plane_state, fb); @@ -563,6 +564,7 @@ int drm_plane_helper_disable(struct drm_plane *plane) plane_state = kzalloc(sizeof(*plane_state), GFP_KERNEL); if (!plane_state) return -ENOMEM; + plane_state->plane = plane; plane_state->crtc = NULL; drm_atomic_set_fb_for_plane(plane_state, NULL); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 26e5fdea6594..fc76f630e5b1 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -113,6 +113,7 @@ static void mdp5_plane_reset(struct drm_plane *plane) } else { mdp5_state->zpos = 1 + drm_plane_index(plane); } + mdp5_state->base.plane = plane; plane->state = &mdp5_state->base; } diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index ad2229574dd9..e224ccfa11ca 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -46,8 +46,8 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state, struct drm_connector *connector); int __must_check -drm_atomic_set_crtc_for_plane(struct drm_atomic_state *state, - struct drm_plane *plane, struct drm_crtc *crtc); +drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, + struct drm_crtc *crtc); void drm_atomic_set_fb_for_plane(struct drm_plane_state *plane_state, struct drm_framebuffer *fb); int __must_check diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 4ee78212f8bf..fd8139ca629a 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -237,6 +237,7 @@ struct drm_atomic_state; /** * struct drm_crtc_state - mutable CRTC state + * @crtc: backpointer to the CRTC * @enable: whether the CRTC should be enabled, gates all other state * @active: whether the CRTC is actively displaying (used for DPMS) * @mode_changed: for use by helpers and drivers when computing state updates @@ -257,6 +258,8 @@ struct drm_atomic_state; * assignment, @active controls the actual hardware state. */ struct drm_crtc_state { + struct drm_crtc *crtc; + bool enable; bool active; @@ -457,11 +460,14 @@ struct drm_crtc { /** * struct drm_connector_state - mutable connector state + * @connector: backpointer to the connector * @crtc: CRTC to connect connector to, NULL if disabled * @best_encoder: can be used by helpers and drivers to select the encoder * @state: backpointer to global drm_atomic_state */ struct drm_connector_state { + struct drm_connector *connector; + struct drm_crtc *crtc; /* do not write directly, use drm_atomic_set_crtc_for_connector() */ struct drm_encoder *best_encoder; @@ -701,6 +707,7 @@ struct drm_connector { /** * struct drm_plane_state - mutable plane state + * @plane: backpointer to the plane * @crtc: currently bound CRTC, NULL if disabled * @fb: currently bound framebuffer * @fence: optional fence to wait for before scanning out @fb @@ -717,6 +724,8 @@ struct drm_connector { * @state: backpointer to global drm_atomic_state */ struct drm_plane_state { + struct drm_plane *plane; + struct drm_crtc *crtc; /* do not write directly, use drm_atomic_set_crtc_for_plane() */ struct drm_framebuffer *fb; /* do not write directly, use drm_atomic_set_fb_for_plane() */ struct fence *fence; -- cgit v1.2.3 From b17cd757a3f61e4519b70b4673f0467ec0153a10 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 16 Dec 2014 18:05:30 -0500 Subject: drm: store property instead of id in obj attachment Keep property pointer, instead of id, in per mode-object attachments. This will simplify things in later patches. Signed-off-by: Rob Clark Reviewed-by: Sean Paul Reviewed-by: Daniel Vetter Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 17 ++++++++--------- include/drm/drm_crtc.h | 7 ++++++- 2 files changed, 14 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 5ee4b8806aca..2780a088051e 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2105,12 +2105,11 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, prop_ptr = (uint32_t __user *)(unsigned long)(out_resp->props_ptr); prop_values = (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr); for (i = 0; i < connector->properties.count; i++) { - if (put_user(connector->properties.ids[i], - prop_ptr + copied)) { + struct drm_property *prop = connector->properties.properties[i]; + if (put_user(prop->base.id, prop_ptr + copied)) { ret = -EFAULT; goto out; } - if (put_user(connector->properties.values[i], prop_values + copied)) { ret = -EFAULT; @@ -3822,7 +3821,7 @@ void drm_object_attach_property(struct drm_mode_object *obj, return; } - obj->properties->ids[count] = property->base.id; + obj->properties->properties[count] = property; obj->properties->values[count] = init_val; obj->properties->count++; } @@ -3847,7 +3846,7 @@ int drm_object_property_set_value(struct drm_mode_object *obj, int i; for (i = 0; i < obj->properties->count; i++) { - if (obj->properties->ids[i] == property->base.id) { + if (obj->properties->properties[i] == property) { obj->properties->values[i] = val; return 0; } @@ -3877,7 +3876,7 @@ int drm_object_property_get_value(struct drm_mode_object *obj, int i; for (i = 0; i < obj->properties->count; i++) { - if (obj->properties->ids[i] == property->base.id) { + if (obj->properties->properties[i] == property) { *val = obj->properties->values[i]; return 0; } @@ -4413,8 +4412,8 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, prop_values_ptr = (uint64_t __user *)(unsigned long) (arg->prop_values_ptr); for (i = 0; i < props_count; i++) { - if (put_user(obj->properties->ids[i], - props_ptr + copied)) { + struct drm_property *prop = obj->properties->properties[i]; + if (put_user(prop->base.id, props_ptr + copied)) { ret = -EFAULT; goto out; } @@ -4472,7 +4471,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, goto out; for (i = 0; i < arg_obj->properties->count; i++) - if (arg_obj->properties->ids[i] == arg->prop_id) + if (arg_obj->properties->properties[i]->base.id == arg->prop_id) break; if (i == arg_obj->properties->count) diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index fd8139ca629a..265f90afcac4 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -64,7 +64,12 @@ struct drm_mode_object { #define DRM_OBJECT_MAX_PROPERTY 24 struct drm_object_properties { int count; - uint32_t ids[DRM_OBJECT_MAX_PROPERTY]; + /* NOTE: if we ever start dynamically destroying properties (ie. + * not at drm_mode_config_cleanup() time), then we'd have to do + * a better job of detaching property from mode objects to avoid + * dangling property pointers: + */ + struct drm_property *properties[DRM_OBJECT_MAX_PROPERTY]; uint64_t values[DRM_OBJECT_MAX_PROPERTY]; }; -- cgit v1.2.3 From 22b8b13b6f436ffbb6e540f5f8039b1084a72794 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 16 Dec 2014 18:05:31 -0500 Subject: drm: get rid of direct property value access For atomic drivers, we won't use the values array but instead shunt things off to obj->atomic_get_property(). So to simplify things make all read/write of properties values go through the accessors. Signed-off-by: Rob Clark Reviewed-by: Sean Paul Reviewed-by: Daniel Vetter Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 19 +++++++++++++++---- include/drm/drm_crtc.h | 3 +++ 2 files changed, 18 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 2780a088051e..481bb2598b62 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2106,12 +2106,17 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, prop_values = (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr); for (i = 0; i < connector->properties.count; i++) { struct drm_property *prop = connector->properties.properties[i]; + uint64_t val; + + ret = drm_object_property_get_value(&connector->base, prop, &val); + if (ret) + goto out; + if (put_user(prop->base.id, prop_ptr + copied)) { ret = -EFAULT; goto out; } - if (put_user(connector->properties.values[i], - prop_values + copied)) { + if (put_user(val, prop_values + copied)) { ret = -EFAULT; goto out; } @@ -4413,12 +4418,18 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, (arg->prop_values_ptr); for (i = 0; i < props_count; i++) { struct drm_property *prop = obj->properties->properties[i]; + uint64_t val; + + ret = drm_object_property_get_value(obj, prop, &val); + if (ret) + goto out; + if (put_user(prop->base.id, props_ptr + copied)) { ret = -EFAULT; goto out; } - if (put_user(obj->properties->values[i], - prop_values_ptr + copied)) { + + if (put_user(val, prop_values_ptr + copied)) { ret = -EFAULT; goto out; } diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 265f90afcac4..f7c0b7bb9d5f 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -70,6 +70,9 @@ struct drm_object_properties { * dangling property pointers: */ struct drm_property *properties[DRM_OBJECT_MAX_PROPERTY]; + /* do not read/write values directly, but use drm_object_property_get_value() + * and drm_object_property_set_value(): + */ uint64_t values[DRM_OBJECT_MAX_PROPERTY]; }; -- cgit v1.2.3 From b46004b70367974d5318caeabfd435017adf9e2a Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 17 Dec 2014 16:41:41 +0100 Subject: drm: Move IRQ related fields to proper section The .irq and .irq_enabled fields are part of the VBLANK interrupt handling infrastructure, so move them to the appropriate section within the structure. Signed-off-by: Thierry Reding Reviewed-by: Alex Deucher Signed-off-by: Daniel Vetter --- include/drm/drmP.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 8ba35c622e22..7cd1c681a792 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -744,8 +744,6 @@ struct drm_device { /** \name Context support */ /*@{ */ - bool irq_enabled; /**< True if irq handler is enabled */ - int irq; __volatile__ long context_flag; /**< Context swapping flag */ int last_context; /**< Last current context */ @@ -753,6 +751,8 @@ struct drm_device { /** \name VBLANK IRQ support */ /*@{ */ + bool irq_enabled; + int irq; /* * At load time, disabling the vblank interrupt won't be allowed since -- cgit v1.2.3 From 6d11a2f00724d52b988b5fedc6e6a050e1a46389 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 17 Dec 2014 16:41:42 +0100 Subject: drm: Make drm_crtc_helper.h standalone includible The file refers to a bunch of structure declared in drm_crtc.h, so include it to make sure the drm_crtc_helper.h header can be included standalone. Signed-off-by: Thierry Reding Signed-off-by: Daniel Vetter --- include/drm/drm_crtc_helper.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index 7adbb65ea8ae..8608897ace10 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -39,6 +39,8 @@ #include +#include + enum mode_set_atomic { LEAVE_ATOMIC_MODE_SET, ENTER_ATOMIC_MODE_SET, -- cgit v1.2.3 From 7552e7dd9527c41f891c87854418896eaf309c20 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 17 Dec 2014 16:41:43 +0100 Subject: drm: Include drm_crtc_helper.h in DocBook There is already a section that describes the helpers implemented by this module. Add the kerneldoc-generated structure descriptions to this section. While at it, add missing kerneldoc for the structures to avoid warnings when generating the documentation. Signed-off-by: Thierry Reding Reviewed-by: Alex Deucher Signed-off-by: Daniel Vetter --- Documentation/DocBook/drm.tmpl | 1 + include/drm/drm_crtc_helper.h | 30 +++++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index d45b2676fbb7..7af413f6aa6f 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -2362,6 +2362,7 @@ void intel_crt_init(struct drm_device *dev) Modeset Helper Functions Reference +!Iinclude/drm/drm_crtc_helper.h !Edrivers/gpu/drm/drm_crtc_helper.c !Pdrivers/gpu/drm/drm_crtc_helper.c overview diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index 8608897ace10..e76828d81a8b 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -47,9 +47,20 @@ enum mode_set_atomic { }; /** - * drm_crtc_helper_funcs - helper operations for CRTCs - * @mode_fixup: try to fixup proposed mode for this connector + * struct drm_crtc_helper_funcs - helper operations for CRTCs + * @dpms: set power state + * @prepare: prepare the CRTC, called before @mode_set + * @commit: commit changes to CRTC, called after @mode_set + * @mode_fixup: try to fixup proposed mode for this CRTC * @mode_set: set this mode + * @mode_set_nofb: set mode only (no scanout buffer attached) + * @mode_set_base: update the scanout buffer + * @mode_set_base_atomic: non-blocking mode set (used for kgdb support) + * @load_lut: load color palette + * @disable: disable CRTC when no longer in use + * @atomic_check: check for validity of an atomic state + * @atomic_begin: begin atomic update + * @atomic_flush: flush atomic update * * The helper operations are called by the mid-layer CRTC helper. */ @@ -93,9 +104,17 @@ struct drm_crtc_helper_funcs { }; /** - * drm_encoder_helper_funcs - helper operations for encoders + * struct drm_encoder_helper_funcs - helper operations for encoders + * @dpms: set power state + * @save: save connector state + * @restore: restore connector state * @mode_fixup: try to fixup proposed mode for this connector + * @prepare: part of the disable sequence, called before the CRTC modeset + * @commit: called after the CRTC modeset * @mode_set: set this mode + * @get_crtc: return CRTC that the encoder is currently attached to + * @detect: connection status detection + * @disable: disable encoder when not in use (overrides DPMS off) * * The helper operations are called by the mid-layer CRTC helper. */ @@ -121,9 +140,10 @@ struct drm_encoder_helper_funcs { }; /** - * drm_connector_helper_funcs - helper operations for connectors + * struct drm_connector_helper_funcs - helper operations for connectors * @get_modes: get mode list for this connector - * @mode_valid (optional): is this mode valid on the given connector? + * @mode_valid: is this mode valid on the given connector? (optional) + * @best_encoder: return the preferred encoder for this connector * * The helper operations are called by the mid-layer CRTC helper. */ -- cgit v1.2.3 From 40ecc694e114a06b9ed77e3e94641b0f5490693c Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 18 Dec 2014 16:01:46 -0500 Subject: drm: add atomic_set_property wrappers As we add properties for all the standard plane/crtc/connector attributes (in preperation for the atomic ioctl), we are going to want to handle core state in core (rather than per driver). Intercepting the core properties will be easier if the atomic_set_property vfuncs are not called directly, but instead have a mandatory wrapper function (which will later serve as the point to intercept core properties). v2: more verbose comments and copypasta comment fix Signed-off-by: Rob Clark Reviewed-by: Sean Paul Reviewed-by: Daniel Vetter Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic.c | 90 +++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_atomic_helper.c | 12 ++--- include/drm/drm_atomic.h | 9 ++++ include/drm/drm_crtc.h | 3 ++ 4 files changed, 108 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index cbd5e7240b6b..6f729d1bdea2 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -216,6 +216,32 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state, } EXPORT_SYMBOL(drm_atomic_get_crtc_state); +/** + * drm_atomic_crtc_set_property - set property on CRTC + * @crtc: the drm CRTC to set a property on + * @state: the state object to update with the new property value + * @property: the property to set + * @val: the new property value + * + * Use this instead of calling crtc->atomic_set_property directly. + * This function handles generic/core properties and calls out to + * driver's ->atomic_set_property() for driver properties. To ensure + * consistent behavior you must call this function rather than the + * driver hook directly. + * + * RETURNS: + * Zero on success, error code on failure + */ +int drm_atomic_crtc_set_property(struct drm_crtc *crtc, + struct drm_crtc_state *state, struct drm_property *property, + uint64_t val) +{ + if (crtc->funcs->atomic_set_property) + return crtc->funcs->atomic_set_property(crtc, state, property, val); + return -EINVAL; +} +EXPORT_SYMBOL(drm_atomic_crtc_set_property); + /** * drm_atomic_get_plane_state - get plane state * @state: global atomic state object @@ -271,6 +297,32 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state, } EXPORT_SYMBOL(drm_atomic_get_plane_state); +/** + * drm_atomic_plane_set_property - set property on plane + * @plane: the drm plane to set a property on + * @state: the state object to update with the new property value + * @property: the property to set + * @val: the new property value + * + * Use this instead of calling plane->atomic_set_property directly. + * This function handles generic/core properties and calls out to + * driver's ->atomic_set_property() for driver properties. To ensure + * consistent behavior you must call this function rather than the + * driver hook directly. + * + * RETURNS: + * Zero on success, error code on failure + */ +int drm_atomic_plane_set_property(struct drm_plane *plane, + struct drm_plane_state *state, struct drm_property *property, + uint64_t val) +{ + if (plane->funcs->atomic_set_property) + return plane->funcs->atomic_set_property(plane, state, property, val); + return -EINVAL; +} +EXPORT_SYMBOL(drm_atomic_plane_set_property); + /** * drm_atomic_get_connector_state - get connector state * @state: global atomic state object @@ -342,6 +394,44 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state, } EXPORT_SYMBOL(drm_atomic_get_connector_state); +/** + * drm_atomic_connector_set_property - set property on connector. + * @connector: the drm connector to set a property on + * @state: the state object to update with the new property value + * @property: the property to set + * @val: the new property value + * + * Use this instead of calling connector->atomic_set_property directly. + * This function handles generic/core properties and calls out to + * driver's ->atomic_set_property() for driver properties. To ensure + * consistent behavior you must call this function rather than the + * driver hook directly. + * + * RETURNS: + * Zero on success, error code on failure + */ +int drm_atomic_connector_set_property(struct drm_connector *connector, + struct drm_connector_state *state, struct drm_property *property, + uint64_t val) +{ + struct drm_device *dev = connector->dev; + struct drm_mode_config *config = &dev->mode_config; + + if (property == config->dpms_property) { + /* setting DPMS property requires special handling, which + * is done in legacy setprop path for us. Disallow (for + * now?) atomic writes to DPMS property: + */ + return -EINVAL; + } else if (connector->funcs->atomic_set_property) { + return connector->funcs->atomic_set_property(connector, + state, property, val); + } else { + return -EINVAL; + } +} +EXPORT_SYMBOL(drm_atomic_connector_set_property); + /** * drm_atomic_set_crtc_for_plane - set crtc for plane * @plane_state: the plane whose incoming state to update diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 379d37acd5b5..57e5540259cc 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1613,8 +1613,8 @@ retry: goto fail; } - ret = crtc->funcs->atomic_set_property(crtc, crtc_state, - property, val); + ret = drm_atomic_crtc_set_property(crtc, crtc_state, + property, val); if (ret) goto fail; @@ -1672,8 +1672,8 @@ retry: goto fail; } - ret = plane->funcs->atomic_set_property(plane, plane_state, - property, val); + ret = drm_atomic_plane_set_property(plane, plane_state, + property, val); if (ret) goto fail; @@ -1731,8 +1731,8 @@ retry: goto fail; } - ret = connector->funcs->atomic_set_property(connector, connector_state, - property, val); + ret = drm_atomic_connector_set_property(connector, connector_state, + property, val); if (ret) goto fail; diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index e224ccfa11ca..51168a8b723a 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -38,12 +38,21 @@ void drm_atomic_state_free(struct drm_atomic_state *state); struct drm_crtc_state * __must_check drm_atomic_get_crtc_state(struct drm_atomic_state *state, struct drm_crtc *crtc); +int drm_atomic_crtc_set_property(struct drm_crtc *crtc, + struct drm_crtc_state *state, struct drm_property *property, + uint64_t val); struct drm_plane_state * __must_check drm_atomic_get_plane_state(struct drm_atomic_state *state, struct drm_plane *plane); +int drm_atomic_plane_set_property(struct drm_plane *plane, + struct drm_plane_state *state, struct drm_property *property, + uint64_t val); struct drm_connector_state * __must_check drm_atomic_get_connector_state(struct drm_atomic_state *state, struct drm_connector *connector); +int drm_atomic_connector_set_property(struct drm_connector *connector, + struct drm_connector_state *state, struct drm_property *property, + uint64_t val); int __must_check drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index f7c0b7bb9d5f..7f158963ef87 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -311,6 +311,7 @@ struct drm_crtc_state { * @atomic_duplicate_state: duplicate the atomic state for this CRTC * @atomic_destroy_state: destroy an atomic state for this CRTC * @atomic_set_property: set a property on an atomic state for this CRTC + * (do not call directly, use drm_atomic_crtc_set_property()) * * The drm_crtc_funcs structure is the central CRTC management structure * in the DRM. Each CRTC controls one or more connectors (note that the name @@ -497,6 +498,7 @@ struct drm_connector_state { * @atomic_duplicate_state: duplicate the atomic state for this connector * @atomic_destroy_state: destroy an atomic state for this connector * @atomic_set_property: set a property on an atomic state for this connector + * (do not call directly, use drm_atomic_connector_set_property()) * * Each CRTC may have one or more connectors attached to it. The functions * below allow the core DRM code to control connectors, enumerate available modes, @@ -760,6 +762,7 @@ struct drm_plane_state { * @atomic_duplicate_state: duplicate the atomic state for this plane * @atomic_destroy_state: destroy an atomic state for this plane * @atomic_set_property: set a property on an atomic state for this plane + * (do not call directly, use drm_atomic_plane_set_property()) */ struct drm_plane_funcs { int (*update_plane)(struct drm_plane *plane, -- cgit v1.2.3 From ac9c925616028f1f03f631f229bca49b3a92ce9a Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 18 Dec 2014 16:01:47 -0500 Subject: drm: add atomic_get_property Since we won't be using the obj->properties->values[] array to shadow property values for atomic drivers, we are going to need a vfunc for getting prop values. Add that along w/ mandatory wrapper fxns. v2: more comments and copypasta comment typo fix Signed-off-by: Rob Clark Reviewed-by: Sean Paul Reviewed-by: Daniel Vetter Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic.c | 88 ++++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_atomic.h | 9 +++++ include/drm/drm_crtc.h | 18 +++++++++ 3 files changed, 115 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 6f729d1bdea2..9c4e149a61e2 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -242,6 +242,32 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc, } EXPORT_SYMBOL(drm_atomic_crtc_set_property); +/** + * drm_atomic_crtc_get_property - get property on CRTC + * @crtc: the drm CRTC to get a property on + * @state: the state object with the property value to read + * @property: the property to get + * @val: the property value (returned by reference) + * + * Use this instead of calling crtc->atomic_get_property directly. + * This function handles generic/core properties and calls out to + * driver's ->atomic_get_property() for driver properties. To ensure + * consistent behavior you must call this function rather than the + * driver hook directly. + * + * RETURNS: + * Zero on success, error code on failure + */ +int drm_atomic_crtc_get_property(struct drm_crtc *crtc, + const struct drm_crtc_state *state, + struct drm_property *property, uint64_t *val) +{ + if (crtc->funcs->atomic_get_property) + return crtc->funcs->atomic_get_property(crtc, state, property, val); + return -EINVAL; +} +EXPORT_SYMBOL(drm_atomic_crtc_get_property); + /** * drm_atomic_get_plane_state - get plane state * @state: global atomic state object @@ -323,6 +349,32 @@ int drm_atomic_plane_set_property(struct drm_plane *plane, } EXPORT_SYMBOL(drm_atomic_plane_set_property); +/** + * drm_atomic_plane_get_property - get property on plane + * @plane: the drm plane to get a property on + * @state: the state object with the property value to read + * @property: the property to get + * @val: the property value (returned by reference) + * + * Use this instead of calling plane->atomic_get_property directly. + * This function handles generic/core properties and calls out to + * driver's ->atomic_get_property() for driver properties. To ensure + * consistent behavior you must call this function rather than the + * driver hook directly. + * + * RETURNS: + * Zero on success, error code on failure + */ +int drm_atomic_plane_get_property(struct drm_plane *plane, + const struct drm_plane_state *state, + struct drm_property *property, uint64_t *val) +{ + if (plane->funcs->atomic_get_property) + return plane->funcs->atomic_get_property(plane, state, property, val); + return -EINVAL; +} +EXPORT_SYMBOL(drm_atomic_plane_get_property); + /** * drm_atomic_get_connector_state - get connector state * @state: global atomic state object @@ -432,6 +484,42 @@ int drm_atomic_connector_set_property(struct drm_connector *connector, } EXPORT_SYMBOL(drm_atomic_connector_set_property); +/** + * drm_atomic_connector_get_property - get property on connector + * @connector: the drm connector to get a property on + * @state: the state object with the property value to read + * @property: the property to get + * @val: the property value (returned by reference) + * + * Use this instead of calling connector->atomic_get_property directly. + * This function handles generic/core properties and calls out to + * driver's ->atomic_get_property() for driver properties. To ensure + * consistent behavior you must call this function rather than the + * driver hook directly. + * + * RETURNS: + * Zero on success, error code on failure + */ +int drm_atomic_connector_get_property(struct drm_connector *connector, + const struct drm_connector_state *state, + struct drm_property *property, uint64_t *val) +{ + struct drm_device *dev = connector->dev; + struct drm_mode_config *config = &dev->mode_config; + + if (property == config->dpms_property) { + *val = connector->dpms; + } else if (connector->funcs->atomic_get_property) { + return connector->funcs->atomic_get_property(connector, + state, property, val); + } else { + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_connector_get_property); + /** * drm_atomic_set_crtc_for_plane - set crtc for plane * @plane_state: the plane whose incoming state to update diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 51168a8b723a..d41233ccbc9e 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -41,18 +41,27 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state, int drm_atomic_crtc_set_property(struct drm_crtc *crtc, struct drm_crtc_state *state, struct drm_property *property, uint64_t val); +int drm_atomic_crtc_get_property(struct drm_crtc *crtc, + const struct drm_crtc_state *state, + struct drm_property *property, uint64_t *val); struct drm_plane_state * __must_check drm_atomic_get_plane_state(struct drm_atomic_state *state, struct drm_plane *plane); int drm_atomic_plane_set_property(struct drm_plane *plane, struct drm_plane_state *state, struct drm_property *property, uint64_t val); +int drm_atomic_plane_get_property(struct drm_plane *plane, + const struct drm_plane_state *state, + struct drm_property *property, uint64_t *val); struct drm_connector_state * __must_check drm_atomic_get_connector_state(struct drm_atomic_state *state, struct drm_connector *connector); int drm_atomic_connector_set_property(struct drm_connector *connector, struct drm_connector_state *state, struct drm_property *property, uint64_t val); +int drm_atomic_connector_get_property(struct drm_connector *connector, + const struct drm_connector_state *state, + struct drm_property *property, uint64_t *val); int __must_check drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 7f158963ef87..e1f34694fcff 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -312,6 +312,8 @@ struct drm_crtc_state { * @atomic_destroy_state: destroy an atomic state for this CRTC * @atomic_set_property: set a property on an atomic state for this CRTC * (do not call directly, use drm_atomic_crtc_set_property()) + * @atomic_get_property: get a property on an atomic state for this CRTC + * (do not call directly, use drm_atomic_crtc_get_property()) * * The drm_crtc_funcs structure is the central CRTC management structure * in the DRM. Each CRTC controls one or more connectors (note that the name @@ -371,6 +373,10 @@ struct drm_crtc_funcs { struct drm_crtc_state *state, struct drm_property *property, uint64_t val); + int (*atomic_get_property)(struct drm_crtc *crtc, + const struct drm_crtc_state *state, + struct drm_property *property, + uint64_t *val); }; /** @@ -499,6 +505,8 @@ struct drm_connector_state { * @atomic_destroy_state: destroy an atomic state for this connector * @atomic_set_property: set a property on an atomic state for this connector * (do not call directly, use drm_atomic_connector_set_property()) + * @atomic_get_property: get a property on an atomic state for this connector + * (do not call directly, use drm_atomic_connector_get_property()) * * Each CRTC may have one or more connectors attached to it. The functions * below allow the core DRM code to control connectors, enumerate available modes, @@ -532,6 +540,10 @@ struct drm_connector_funcs { struct drm_connector_state *state, struct drm_property *property, uint64_t val); + int (*atomic_get_property)(struct drm_connector *connector, + const struct drm_connector_state *state, + struct drm_property *property, + uint64_t *val); }; /** @@ -763,6 +775,8 @@ struct drm_plane_state { * @atomic_destroy_state: destroy an atomic state for this plane * @atomic_set_property: set a property on an atomic state for this plane * (do not call directly, use drm_atomic_plane_set_property()) + * @atomic_get_property: get a property on an atomic state for this plane + * (do not call directly, use drm_atomic_plane_get_property()) */ struct drm_plane_funcs { int (*update_plane)(struct drm_plane *plane, @@ -786,6 +800,10 @@ struct drm_plane_funcs { struct drm_plane_state *state, struct drm_property *property, uint64_t val); + int (*atomic_get_property)(struct drm_plane *plane, + const struct drm_plane_state *state, + struct drm_property *property, + uint64_t *val); }; enum drm_plane_type { -- cgit v1.2.3 From 88a48e297b3a3bac6022c03babfb038f1a886cea Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 18 Dec 2014 16:01:50 -0500 Subject: drm: add atomic properties Once a driver is using atomic helpers for modeset, the next step is to switch over to atomic properties. To do this, make sure that any modeset objects have their ->atomic_{get,set}_property() vfuncs suitably populated if they have custom properties (you did already remember to plug in atomic-helper func for the legacy ->set_property() vfuncs, right?), and then set DRIVER_ATOMIC bit in driver_features flag. A new cap is introduced, DRM_CLIENT_CAP_ATOMIC, for the purposes of shielding legacy userspace from atomic properties. Mostly for the benefit of legacy DDX drivers that do silly things like getting/setting each property at startup (since some of the new atomic properties will be able to trigger modeset). Signed-off-by: Rob Clark [danvet: Squash in fixup patch to check for DRM_MODE_PROP_ATOMIC instaed of the CAP define when filtering properties. Reported by Tvrtko Uruslin, acked by Rob.] Signed-off-by: Daniel Vetter --- Documentation/DocBook/drm.tmpl | 8 ++++++++ drivers/gpu/drm/drm_atomic.c | 45 ++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_crtc.c | 31 ++++++++++++++++++++++------- drivers/gpu/drm/drm_drv.c | 4 ++++ drivers/gpu/drm/drm_ioctl.c | 10 ++++++++++ include/drm/drmP.h | 4 ++++ include/drm/drm_atomic.h | 3 +++ include/drm/drm_crtc.h | 2 +- include/uapi/drm/drm.h | 7 +++++++ include/uapi/drm/drm_mode.h | 7 +++++++ 10 files changed, 113 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 4b592ffbafee..7fa4f9873cf7 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -239,6 +239,14 @@ Driver supports dedicated render nodes. + + DRIVER_ATOMIC + + Driver supports atomic properties. In this case the driver + must implement appropriate obj->atomic_get_property() vfuncs + for any modeset objects with driver specific properties. + + diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 9c4e149a61e2..ce3c681d60f8 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -520,6 +520,51 @@ int drm_atomic_connector_get_property(struct drm_connector *connector, } EXPORT_SYMBOL(drm_atomic_connector_get_property); +/** + * drm_atomic_get_property - helper to read atomic property + * @obj: drm mode object whose property to read + * @property: the property to read + * @val: the read value, returned by reference + * + * RETURNS: + * Zero on success, error code on failure + */ +int drm_atomic_get_property(struct drm_mode_object *obj, + struct drm_property *property, uint64_t *val) +{ + struct drm_device *dev = property->dev; + int ret; + + switch (obj->type) { + case DRM_MODE_OBJECT_CONNECTOR: { + struct drm_connector *connector = obj_to_connector(obj); + WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); + ret = drm_atomic_connector_get_property(connector, + connector->state, property, val); + break; + } + case DRM_MODE_OBJECT_CRTC: { + struct drm_crtc *crtc = obj_to_crtc(obj); + WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); + ret = drm_atomic_crtc_get_property(crtc, + crtc->state, property, val); + break; + } + case DRM_MODE_OBJECT_PLANE: { + struct drm_plane *plane = obj_to_plane(obj); + WARN_ON(!drm_modeset_is_locked(&plane->mutex)); + ret = drm_atomic_plane_get_property(plane, + plane->state, property, val); + break; + } + default: + ret = -EINVAL; + break; + } + + return ret; +} + /** * drm_atomic_set_crtc_for_plane - set crtc for plane * @plane_state: the plane whose incoming state to update diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f5f34d0d7c20..3bac877228fa 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -38,6 +38,7 @@ #include #include #include +#include #include "drm_crtc_internal.h" #include "drm_internal.h" @@ -1992,19 +1993,25 @@ static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *conne } /* helper for getconnector and getproperties ioctls */ -static int get_properties(struct drm_mode_object *obj, +static int get_properties(struct drm_mode_object *obj, bool atomic, uint32_t __user *prop_ptr, uint64_t __user *prop_values, uint32_t *arg_count_props) { - int props_count = obj->properties->count; - int i, ret, copied = 0; + int props_count; + int i, ret, copied; + + props_count = obj->properties->count; + if (!atomic) + props_count -= obj->properties->atomic_count; if ((*arg_count_props >= props_count) && props_count) { - copied = 0; - for (i = 0; i < props_count; i++) { + for (i = 0, copied = 0; copied < props_count; i++) { struct drm_property *prop = obj->properties->properties[i]; uint64_t val; + if ((prop->flags & DRM_MODE_PROP_ATOMIC) && !atomic) + continue; + ret = drm_object_property_get_value(obj, prop, &val); if (ret) return ret; @@ -2118,7 +2125,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, } out_resp->count_modes = mode_count; - ret = get_properties(&connector->base, + ret = get_properties(&connector->base, file_priv->atomic, (uint32_t __user *)(unsigned long)(out_resp->props_ptr), (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr), &out_resp->count_props); @@ -3832,6 +3839,8 @@ void drm_object_attach_property(struct drm_mode_object *obj, obj->properties->properties[count] = property; obj->properties->values[count] = init_val; obj->properties->count++; + if (property->flags & DRM_MODE_PROP_ATOMIC) + obj->properties->atomic_count++; } EXPORT_SYMBOL(drm_object_attach_property); @@ -3883,6 +3892,14 @@ int drm_object_property_get_value(struct drm_mode_object *obj, { int i; + /* read-only properties bypass atomic mechanism and still store + * their value in obj->properties->values[].. mostly to avoid + * having to deal w/ EDID and similar props in atomic paths: + */ + if (drm_core_check_feature(property->dev, DRIVER_ATOMIC) && + !(property->flags & DRM_MODE_PROP_IMMUTABLE)) + return drm_atomic_get_property(obj, property, val); + for (i = 0; i < obj->properties->count; i++) { if (obj->properties->properties[i] == property) { *val = obj->properties->values[i]; @@ -4413,7 +4430,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, goto out; } - ret = get_properties(obj, + ret = get_properties(obj, file_priv->atomic, (uint32_t __user *)(unsigned long)(arg->props_ptr), (uint64_t __user *)(unsigned long)(arg->prop_values_ptr), &arg->count_props); diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 4f41377b0b80..d51213464672 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -40,15 +40,19 @@ unsigned int drm_debug = 0; /* 1 to enable debug output */ EXPORT_SYMBOL(drm_debug); +bool drm_atomic = 0; + MODULE_AUTHOR(CORE_AUTHOR); MODULE_DESCRIPTION(CORE_DESC); MODULE_LICENSE("GPL and additional rights"); MODULE_PARM_DESC(debug, "Enable debug output"); +MODULE_PARM_DESC(atomic, "Enable experimental atomic KMS API"); MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs] (0: never disable, <0: disable immediately)"); MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]"); MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps"); module_param_named(debug, drm_debug, int, 0600); +module_param_named_unsafe(atomic, drm_atomic, bool, 0600); static DEFINE_SPINLOCK(drm_minor_lock); static struct idr drm_minors_idr; diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 00587a1e3c83..adc822312f6c 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -345,6 +345,16 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv) return -EINVAL; file_priv->universal_planes = req->value; break; + case DRM_CLIENT_CAP_ATOMIC: + /* for now, hide behind experimental drm.atomic moduleparam */ + if (!drm_atomic) + return -EINVAL; + if (!drm_core_check_feature(dev, DRIVER_ATOMIC)) + return -EINVAL; + if (req->value > 1) + return -EINVAL; + file_priv->atomic = req->value; + break; default: return -EINVAL; } diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 8ba35c622e22..0f7115e988a0 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -143,6 +143,7 @@ void drm_err(const char *format, ...); #define DRIVER_MODESET 0x2000 #define DRIVER_PRIME 0x4000 #define DRIVER_RENDER 0x8000 +#define DRIVER_ATOMIC 0x10000 /***********************************************************************/ /** \name Macros to make printk easier */ @@ -283,6 +284,8 @@ struct drm_file { * in the plane list */ unsigned universal_planes:1; + /* true if client understands atomic properties */ + unsigned atomic:1; struct pid *pid; kuid_t uid; @@ -950,6 +953,7 @@ extern void drm_master_put(struct drm_master **master); extern void drm_put_dev(struct drm_device *dev); extern void drm_unplug_dev(struct drm_device *dev); extern unsigned int drm_debug; +extern bool drm_atomic; /* Debugfs support */ #if defined(CONFIG_DEBUG_FS) diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index d41233ccbc9e..231fb485abb3 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -63,6 +63,9 @@ int drm_atomic_connector_get_property(struct drm_connector *connector, const struct drm_connector_state *state, struct drm_property *property, uint64_t *val); +int drm_atomic_get_property(struct drm_mode_object *obj, + struct drm_property *property, uint64_t *val); + int __must_check drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, struct drm_crtc *crtc); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index e1f34694fcff..b5ab673add29 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -63,7 +63,7 @@ struct drm_mode_object { #define DRM_OBJECT_MAX_PROPERTY 24 struct drm_object_properties { - int count; + int count, atomic_count; /* NOTE: if we ever start dynamically destroying properties (ie. * not at drm_mode_config_cleanup() time), then we'd have to do * a better job of detaching property from mode objects to avoid diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index b0b855613641..f7b2baf7ecb2 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -654,6 +654,13 @@ struct drm_get_cap { */ #define DRM_CLIENT_CAP_UNIVERSAL_PLANES 2 +/** + * DRM_CLIENT_CAP_ATOMIC + * + * If set to 1, the DRM core will expose atomic properties to userspace + */ +#define DRM_CLIENT_CAP_ATOMIC 3 + /** DRM_IOCTL_SET_CLIENT_CAP ioctl argument type */ struct drm_set_client_cap { __u64 capability; diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index aae71cb32123..b8f9c0f2e7fe 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -272,6 +272,13 @@ struct drm_mode_get_connector { #define DRM_MODE_PROP_OBJECT DRM_MODE_PROP_TYPE(1) #define DRM_MODE_PROP_SIGNED_RANGE DRM_MODE_PROP_TYPE(2) +/* the PROP_ATOMIC flag is used to hide properties from userspace that + * is not aware of atomic properties. This is mostly to work around + * older userspace (DDX drivers) that read/write each prop they find, + * witout being aware that this could be triggering a lengthy modeset. + */ +#define DRM_MODE_PROP_ATOMIC 0x80000000 + struct drm_mode_property_enum { __u64 value; char name[DRM_PROP_NAME_LEN]; -- cgit v1.2.3 From 6b4959f43a04e12d39c5700607727f2cbcfeac31 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 18 Dec 2014 16:01:53 -0500 Subject: drm/atomic: atomic plane properties Expose the core plane state as properties, so they can be updated via atomic ioctl. v2: atomic property flag Signed-off-by: Rob Clark Reviewed-by: Sean Paul Reviewed-by: Daniel Vetter Signed-off-by: Daniel Vetter --- Documentation/DocBook/drm.tmpl | 74 ++++++++++++++++++++++++++++- drivers/gpu/drm/drm_atomic.c | 69 +++++++++++++++++++++++++-- drivers/gpu/drm/drm_crtc.c | 103 +++++++++++++++++++++++++++++++++-------- include/drm/drm_crtc.h | 10 ++++ 4 files changed, 230 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 7fa4f9873cf7..8d8dc7124bb5 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -2572,7 +2572,7 @@ void intel_crt_init(struct drm_device *dev) Description/Restrictions - DRM + DRM Generic “EDID” BLOB | IMMUTABLE @@ -2602,7 +2602,7 @@ void intel_crt_init(struct drm_device *dev) Contains tiling information for a connector. - Plane + Plane “type” ENUM | IMMUTABLE { "Overlay", "Primary", "Cursor" } @@ -2610,6 +2610,76 @@ void intel_crt_init(struct drm_device *dev) Plane type + “SRC_X” + RANGE + Min=0, Max=UINT_MAX + Plane + Scanout source x coordinate in 16.16 fixed point (atomic) + + + “SRC_Y” + RANGE + Min=0, Max=UINT_MAX + Plane + Scanout source y coordinate in 16.16 fixed point (atomic) + + + “SRC_W” + RANGE + Min=0, Max=UINT_MAX + Plane + Scanout source width in 16.16 fixed point (atomic) + + + “SRC_H” + RANGE + Min=0, Max=UINT_MAX + Plane + Scanout source height in 16.16 fixed point (atomic) + + + “CRTC_X” + SIGNED_RANGE + Min=INT_MIN, Max=INT_MAX + Plane + Scanout CRTC (destination) x coordinate (atomic) + + + “CRTC_Y” + SIGNED_RANGE + Min=INT_MIN, Max=INT_MAX + Plane + Scanout CRTC (destination) y coordinate (atomic) + + + “CRTC_W” + RANGE + Min=0, Max=UINT_MAX + Plane + Scanout CRTC (destination) width (atomic) + + + “CRTC_H” + RANGE + Min=0, Max=UINT_MAX + Plane + Scanout CRTC (destination) height (atomic) + + + “FB_ID” + OBJECT + DRM_MODE_OBJECT_FB + Plane + Scanout framebuffer (atomic) + + + “CRTC_ID” + OBJECT + DRM_MODE_OBJECT_CRTC + Plane + CRTC that plane is attached to (atomic) + + DVI-I “subconnector” ENUM diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 1c472b1baeb8..131d47f6f7a2 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -366,9 +366,41 @@ int drm_atomic_plane_set_property(struct drm_plane *plane, struct drm_plane_state *state, struct drm_property *property, uint64_t val) { - if (plane->funcs->atomic_set_property) - return plane->funcs->atomic_set_property(plane, state, property, val); - return -EINVAL; + struct drm_device *dev = plane->dev; + struct drm_mode_config *config = &dev->mode_config; + + if (property == config->prop_fb_id) { + struct drm_framebuffer *fb = drm_framebuffer_lookup(dev, val); + drm_atomic_set_fb_for_plane(state, fb); + if (fb) + drm_framebuffer_unreference(fb); + } else if (property == config->prop_crtc_id) { + struct drm_crtc *crtc = drm_crtc_find(dev, val); + return drm_atomic_set_crtc_for_plane(state, crtc); + } else if (property == config->prop_crtc_x) { + state->crtc_x = U642I64(val); + } else if (property == config->prop_crtc_y) { + state->crtc_y = U642I64(val); + } else if (property == config->prop_crtc_w) { + state->crtc_w = val; + } else if (property == config->prop_crtc_h) { + state->crtc_h = val; + } else if (property == config->prop_src_x) { + state->src_x = val; + } else if (property == config->prop_src_y) { + state->src_y = val; + } else if (property == config->prop_src_w) { + state->src_w = val; + } else if (property == config->prop_src_h) { + state->src_h = val; + } else if (plane->funcs->atomic_set_property) { + return plane->funcs->atomic_set_property(plane, state, + property, val); + } else { + return -EINVAL; + } + + return 0; } EXPORT_SYMBOL(drm_atomic_plane_set_property); @@ -392,9 +424,36 @@ int drm_atomic_plane_get_property(struct drm_plane *plane, const struct drm_plane_state *state, struct drm_property *property, uint64_t *val) { - if (plane->funcs->atomic_get_property) + struct drm_device *dev = plane->dev; + struct drm_mode_config *config = &dev->mode_config; + + if (property == config->prop_fb_id) { + *val = (state->fb) ? state->fb->base.id : 0; + } else if (property == config->prop_crtc_id) { + *val = (state->crtc) ? state->crtc->base.id : 0; + } else if (property == config->prop_crtc_x) { + *val = I642U64(state->crtc_x); + } else if (property == config->prop_crtc_y) { + *val = I642U64(state->crtc_y); + } else if (property == config->prop_crtc_w) { + *val = state->crtc_w; + } else if (property == config->prop_crtc_h) { + *val = state->crtc_h; + } else if (property == config->prop_src_x) { + *val = state->src_x; + } else if (property == config->prop_src_y) { + *val = state->src_y; + } else if (property == config->prop_src_w) { + *val = state->src_w; + } else if (property == config->prop_src_h) { + *val = state->src_h; + } else if (plane->funcs->atomic_get_property) { return plane->funcs->atomic_get_property(plane, state, property, val); - return -EINVAL; + } else { + return -EINVAL; + } + + return 0; } EXPORT_SYMBOL(drm_atomic_plane_get_property); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f33863a40d42..46fa0945b53e 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1169,6 +1169,7 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, const uint32_t *formats, uint32_t format_count, enum drm_plane_type type) { + struct drm_mode_config *config = &dev->mode_config; int ret; ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); @@ -1193,15 +1194,28 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, plane->possible_crtcs = possible_crtcs; plane->type = type; - list_add_tail(&plane->head, &dev->mode_config.plane_list); - dev->mode_config.num_total_plane++; + list_add_tail(&plane->head, &config->plane_list); + config->num_total_plane++; if (plane->type == DRM_PLANE_TYPE_OVERLAY) - dev->mode_config.num_overlay_plane++; + config->num_overlay_plane++; drm_object_attach_property(&plane->base, - dev->mode_config.plane_type_property, + config->plane_type_property, plane->type); + if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { + drm_object_attach_property(&plane->base, config->prop_fb_id, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_id, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_x, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_y, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_w, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_h, 0); + drm_object_attach_property(&plane->base, config->prop_src_x, 0); + drm_object_attach_property(&plane->base, config->prop_src_y, 0); + drm_object_attach_property(&plane->base, config->prop_src_w, 0); + drm_object_attach_property(&plane->base, config->prop_src_h, 0); + } + return 0; } EXPORT_SYMBOL(drm_universal_plane_init); @@ -1323,7 +1337,7 @@ void drm_plane_force_disable(struct drm_plane *plane) } EXPORT_SYMBOL(drm_plane_force_disable); -static int drm_mode_create_standard_connector_properties(struct drm_device *dev) +static int drm_mode_create_standard_properties(struct drm_device *dev) { struct drm_property *prop; @@ -1360,20 +1374,72 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev) return -ENOMEM; dev->mode_config.tile_property = prop; - return 0; -} - -static int drm_mode_create_standard_plane_properties(struct drm_device *dev) -{ - struct drm_property *type; - - /* - * Standard properties (apply to all planes) - */ - type = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, + prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, "type", drm_plane_type_enum_list, ARRAY_SIZE(drm_plane_type_enum_list)); - dev->mode_config.plane_type_property = type; + if (!prop) + return -ENOMEM; + dev->mode_config.plane_type_property = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "SRC_X", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_x = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "SRC_Y", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_y = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "SRC_W", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_w = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "SRC_H", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_h = prop; + + prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC, + "CRTC_X", INT_MIN, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_x = prop; + + prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC, + "CRTC_Y", INT_MIN, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_y = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "CRTC_W", 0, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_w = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "CRTC_H", 0, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_h = prop; + + prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC, + "FB_ID", DRM_MODE_OBJECT_FB); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_fb_id = prop; + + prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC, + "CRTC_ID", DRM_MODE_OBJECT_CRTC); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_id = prop; return 0; } @@ -5264,8 +5330,7 @@ void drm_mode_config_init(struct drm_device *dev) idr_init(&dev->mode_config.tile_idr); drm_modeset_lock_all(dev); - drm_mode_create_standard_connector_properties(dev); - drm_mode_create_standard_plane_properties(dev); + drm_mode_create_standard_properties(dev); drm_modeset_unlock_all(dev); /* Just to be sure */ diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index b5ab673add29..fc4767fa723b 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1099,6 +1099,16 @@ struct drm_mode_config { struct drm_property *tile_property; struct drm_property *plane_type_property; struct drm_property *rotation_property; + struct drm_property *prop_src_x; + struct drm_property *prop_src_y; + struct drm_property *prop_src_w; + struct drm_property *prop_src_h; + struct drm_property *prop_crtc_x; + struct drm_property *prop_crtc_y; + struct drm_property *prop_crtc_w; + struct drm_property *prop_crtc_h; + struct drm_property *prop_fb_id; + struct drm_property *prop_crtc_id; /* DVI-I properties */ struct drm_property *dvi_i_subconnector_property; -- cgit v1.2.3 From d34f20d6e2f21bd3531b969dc40913181a8ae31a Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 18 Dec 2014 16:01:56 -0500 Subject: drm: Atomic modeset ioctl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The atomic modeset ioctl can be used to push any number of new values for object properties. The driver can then check the full device configuration as single unit, and try to apply the changes atomically. The ioctl simply takes a list of object IDs and property IDs and their values. Originally based on a patch from Ville Syrjälä, although it has mutated (mutilated?) enough since then that you probably shouldn't blame it on him ;-) The atomic support is hidden behind the DRM_CLIENT_CAP_ATOMIC cap (to protect legacy userspace) and drm.atomic module param (for now). v2: Check for file_priv->atomic to make sure we only allow userspace in-the-know to use atomic. Signed-off-by: Rob Clark Reviewed-by: Sean Paul Reviewed-by: Daniel Vetter Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic.c | 331 +++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_crtc.c | 4 +- drivers/gpu/drm/drm_ioctl.c | 1 + include/drm/drm_crtc.h | 10 +- include/uapi/drm/drm.h | 1 + include/uapi/drm/drm_mode.h | 23 +++ 6 files changed, 366 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 57cc68177f09..67f64bd1058f 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -56,6 +56,11 @@ drm_atomic_state_alloc(struct drm_device *dev) if (!state) return NULL; + /* TODO legacy paths should maybe do a better job about + * setting this appropriately? + */ + state->allow_modeset = true; + state->num_connector = ACCESS_ONCE(dev->mode_config.num_connector); state->crtcs = kcalloc(dev->mode_config.num_crtc, @@ -1003,6 +1008,22 @@ int drm_atomic_check_only(struct drm_atomic_state *state) if (config->funcs->atomic_check) ret = config->funcs->atomic_check(state->dev, state); + if (!state->allow_modeset) { + for (i = 0; i < ncrtcs; i++) { + struct drm_crtc *crtc = state->crtcs[i]; + struct drm_crtc_state *crtc_state = state->crtc_states[i]; + + if (!crtc) + continue; + + if (crtc_state->mode_changed) { + DRM_DEBUG_KMS("[CRTC:%d] requires full modeset\n", + crtc->base.id); + return -EINVAL; + } + } + } + return ret; } EXPORT_SYMBOL(drm_atomic_check_only); @@ -1068,3 +1089,313 @@ int drm_atomic_async_commit(struct drm_atomic_state *state) return config->funcs->atomic_commit(state->dev, state, true); } EXPORT_SYMBOL(drm_atomic_async_commit); + +/* + * The big monstor ioctl + */ + +static struct drm_pending_vblank_event *create_vblank_event( + struct drm_device *dev, struct drm_file *file_priv, uint64_t user_data) +{ + struct drm_pending_vblank_event *e = NULL; + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + if (file_priv->event_space < sizeof e->event) { + spin_unlock_irqrestore(&dev->event_lock, flags); + goto out; + } + file_priv->event_space -= sizeof e->event; + spin_unlock_irqrestore(&dev->event_lock, flags); + + e = kzalloc(sizeof *e, GFP_KERNEL); + if (e == NULL) { + spin_lock_irqsave(&dev->event_lock, flags); + file_priv->event_space += sizeof e->event; + spin_unlock_irqrestore(&dev->event_lock, flags); + goto out; + } + + e->event.base.type = DRM_EVENT_FLIP_COMPLETE; + e->event.base.length = sizeof e->event; + e->event.user_data = user_data; + e->base.event = &e->event.base; + e->base.file_priv = file_priv; + e->base.destroy = (void (*) (struct drm_pending_event *)) kfree; + +out: + return e; +} + +static void destroy_vblank_event(struct drm_device *dev, + struct drm_file *file_priv, struct drm_pending_vblank_event *e) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + file_priv->event_space += sizeof e->event; + spin_unlock_irqrestore(&dev->event_lock, flags); + kfree(e); +} + +static int atomic_set_prop(struct drm_atomic_state *state, + struct drm_mode_object *obj, struct drm_property *prop, + uint64_t prop_value) +{ + struct drm_mode_object *ref; + int ret; + + if (!drm_property_change_valid_get(prop, prop_value, &ref)) + return -EINVAL; + + switch (obj->type) { + case DRM_MODE_OBJECT_CONNECTOR: { + struct drm_connector *connector = obj_to_connector(obj); + struct drm_connector_state *connector_state; + + connector_state = drm_atomic_get_connector_state(state, connector); + if (IS_ERR(connector_state)) { + ret = PTR_ERR(connector_state); + break; + } + + ret = drm_atomic_connector_set_property(connector, + connector_state, prop, prop_value); + break; + } + case DRM_MODE_OBJECT_CRTC: { + struct drm_crtc *crtc = obj_to_crtc(obj); + struct drm_crtc_state *crtc_state; + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + break; + } + + ret = drm_atomic_crtc_set_property(crtc, + crtc_state, prop, prop_value); + break; + } + case DRM_MODE_OBJECT_PLANE: { + struct drm_plane *plane = obj_to_plane(obj); + struct drm_plane_state *plane_state; + + plane_state = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_state)) { + ret = PTR_ERR(plane_state); + break; + } + + ret = drm_atomic_plane_set_property(plane, + plane_state, prop, prop_value); + break; + } + default: + ret = -EINVAL; + break; + } + + drm_property_change_valid_put(prop, ref); + return ret; +} + +int drm_mode_atomic_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_atomic *arg = data; + uint32_t __user *objs_ptr = (uint32_t __user *)(unsigned long)(arg->objs_ptr); + uint32_t __user *count_props_ptr = (uint32_t __user *)(unsigned long)(arg->count_props_ptr); + uint32_t __user *props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr); + uint64_t __user *prop_values_ptr = (uint64_t __user *)(unsigned long)(arg->prop_values_ptr); + unsigned int copied_objs, copied_props; + struct drm_atomic_state *state; + struct drm_modeset_acquire_ctx ctx; + struct drm_plane *plane; + unsigned plane_mask = 0; + int ret = 0; + unsigned int i, j; + + /* disallow for drivers not supporting atomic: */ + if (!drm_core_check_feature(dev, DRIVER_ATOMIC)) + return -EINVAL; + + /* disallow for userspace that has not enabled atomic cap (even + * though this may be a bit overkill, since legacy userspace + * wouldn't know how to call this ioctl) + */ + if (!file_priv->atomic) + return -EINVAL; + + if (arg->flags & ~DRM_MODE_ATOMIC_FLAGS) + return -EINVAL; + + if (arg->reserved) + return -EINVAL; + + if ((arg->flags & DRM_MODE_PAGE_FLIP_ASYNC) && + !dev->mode_config.async_page_flip) + return -EINVAL; + + /* can't test and expect an event at the same time. */ + if ((arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) && + (arg->flags & DRM_MODE_PAGE_FLIP_EVENT)) + return -EINVAL; + + drm_modeset_acquire_init(&ctx, 0); + + state = drm_atomic_state_alloc(dev); + if (!state) + return -ENOMEM; + + state->acquire_ctx = &ctx; + state->allow_modeset = !!(arg->flags & DRM_MODE_ATOMIC_ALLOW_MODESET); + +retry: + copied_objs = 0; + copied_props = 0; + + for (i = 0; i < arg->count_objs; i++) { + uint32_t obj_id, count_props; + struct drm_mode_object *obj; + + if (get_user(obj_id, objs_ptr + copied_objs)) { + ret = -EFAULT; + goto fail; + } + + obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_ANY); + if (!obj || !obj->properties) { + ret = -ENOENT; + goto fail; + } + + if (obj->type == DRM_MODE_OBJECT_PLANE) { + plane = obj_to_plane(obj); + plane_mask |= (1 << drm_plane_index(plane)); + plane->old_fb = plane->fb; + } + + if (get_user(count_props, count_props_ptr + copied_objs)) { + ret = -EFAULT; + goto fail; + } + + copied_objs++; + + for (j = 0; j < count_props; j++) { + uint32_t prop_id; + uint64_t prop_value; + struct drm_property *prop; + + if (get_user(prop_id, props_ptr + copied_props)) { + ret = -EFAULT; + goto fail; + } + + prop = drm_property_find(dev, prop_id); + if (!prop) { + ret = -ENOENT; + goto fail; + } + + if (get_user(prop_value, prop_values_ptr + copied_props)) { + ret = -EFAULT; + goto fail; + } + + ret = atomic_set_prop(state, obj, prop, prop_value); + if (ret) + goto fail; + + copied_props++; + } + } + + if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) { + int ncrtcs = dev->mode_config.num_crtc; + + for (i = 0; i < ncrtcs; i++) { + struct drm_crtc_state *crtc_state = state->crtc_states[i]; + struct drm_pending_vblank_event *e; + + if (!crtc_state) + continue; + + e = create_vblank_event(dev, file_priv, arg->user_data); + if (!e) { + ret = -ENOMEM; + goto fail; + } + + crtc_state->event = e; + } + } + + if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) { + ret = drm_atomic_check_only(state); + /* _check_only() does not free state, unlike _commit() */ + drm_atomic_state_free(state); + } else if (arg->flags & DRM_MODE_ATOMIC_NONBLOCK) { + ret = drm_atomic_async_commit(state); + } else { + ret = drm_atomic_commit(state); + } + + /* if succeeded, fixup legacy plane crtc/fb ptrs before dropping + * locks (ie. while it is still safe to deref plane->state). We + * need to do this here because the driver entry points cannot + * distinguish between legacy and atomic ioctls. + */ + drm_for_each_plane_mask(plane, dev, plane_mask) { + if (ret == 0) { + struct drm_framebuffer *new_fb = plane->state->fb; + if (new_fb) + drm_framebuffer_reference(new_fb); + plane->fb = new_fb; + plane->crtc = plane->state->crtc; + } else { + plane->old_fb = NULL; + } + if (plane->old_fb) { + drm_framebuffer_unreference(plane->old_fb); + plane->old_fb = NULL; + } + } + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + + return ret; + +fail: + if (ret == -EDEADLK) + goto backoff; + + if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) { + int ncrtcs = dev->mode_config.num_crtc; + + for (i = 0; i < ncrtcs; i++) { + struct drm_crtc_state *crtc_state = state->crtc_states[i]; + + if (!crtc_state) + continue; + + destroy_vblank_event(dev, file_priv, crtc_state->event); + crtc_state->event = NULL; + } + } + + drm_atomic_state_free(state); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + + return ret; + +backoff: + drm_atomic_state_clear(state); + drm_modeset_backoff(&ctx); + + goto retry; +} diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 3cb1fa09ac9e..20596423d52d 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -4303,7 +4303,7 @@ EXPORT_SYMBOL(drm_mode_connector_update_edid_property); * object to which the property is attached has a chance to take it's own * reference). */ -static bool drm_property_change_valid_get(struct drm_property *property, +bool drm_property_change_valid_get(struct drm_property *property, uint64_t value, struct drm_mode_object **ref) { int i; @@ -4365,7 +4365,7 @@ static bool drm_property_change_valid_get(struct drm_property *property, return false; } -static void drm_property_change_valid_put(struct drm_property *property, +void drm_property_change_valid_put(struct drm_property *property, struct drm_mode_object *ref) { if (!ref) diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index adc822312f6c..a28c0abd1a38 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -630,6 +630,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATOMIC, drm_mode_atomic_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), }; #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index fc4767fa723b..1dcfb685d15f 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -902,7 +902,7 @@ struct drm_bridge { /** * struct struct drm_atomic_state - the global state object for atomic updates * @dev: parent DRM device - * @flags: state flags like async update + * @allow_modeset: allow full modeset * @planes: pointer to array of plane pointers * @plane_states: pointer to array of plane states pointers * @crtcs: pointer to array of CRTC pointers @@ -914,7 +914,7 @@ struct drm_bridge { */ struct drm_atomic_state { struct drm_device *dev; - uint32_t flags; + bool allow_modeset : 1; struct drm_plane **planes; struct drm_plane_state **plane_states; struct drm_crtc **crtcs; @@ -1346,6 +1346,10 @@ extern int drm_mode_create_scaling_mode_property(struct drm_device *dev); extern int drm_mode_create_aspect_ratio_property(struct drm_device *dev); extern int drm_mode_create_dirty_info_property(struct drm_device *dev); extern int drm_mode_create_suggested_offset_properties(struct drm_device *dev); +extern bool drm_property_change_valid_get(struct drm_property *property, + uint64_t value, struct drm_mode_object **ref); +extern void drm_property_change_valid_put(struct drm_property *property, + struct drm_mode_object *ref); extern int drm_mode_connector_attach_encoder(struct drm_connector *connector, struct drm_encoder *encoder); @@ -1437,6 +1441,8 @@ extern int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, extern int drm_mode_plane_set_obj_prop(struct drm_plane *plane, struct drm_property *property, uint64_t value); +extern int drm_mode_atomic_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); extern void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, int *bpp); diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index f7b2baf7ecb2..01b2d6d0e355 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -784,6 +784,7 @@ struct drm_prime_handle { #define DRM_IOCTL_MODE_OBJ_GETPROPERTIES DRM_IOWR(0xB9, struct drm_mode_obj_get_properties) #define DRM_IOCTL_MODE_OBJ_SETPROPERTY DRM_IOWR(0xBA, struct drm_mode_obj_set_property) #define DRM_IOCTL_MODE_CURSOR2 DRM_IOWR(0xBB, struct drm_mode_cursor2) +#define DRM_IOCTL_MODE_ATOMIC DRM_IOWR(0xBC, struct drm_mode_atomic) /** * Device specific ioctls should only be in their respective headers diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index b8f9c0f2e7fe..ca788e01dab2 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -526,4 +526,27 @@ struct drm_mode_destroy_dumb { uint32_t handle; }; +/* page-flip flags are valid, plus: */ +#define DRM_MODE_ATOMIC_TEST_ONLY 0x0100 +#define DRM_MODE_ATOMIC_NONBLOCK 0x0200 +#define DRM_MODE_ATOMIC_ALLOW_MODESET 0x0400 + +#define DRM_MODE_ATOMIC_FLAGS (\ + DRM_MODE_PAGE_FLIP_EVENT |\ + DRM_MODE_PAGE_FLIP_ASYNC |\ + DRM_MODE_ATOMIC_TEST_ONLY |\ + DRM_MODE_ATOMIC_NONBLOCK |\ + DRM_MODE_ATOMIC_ALLOW_MODESET) + +struct drm_mode_atomic { + __u32 flags; + __u32 count_objs; + __u64 objs_ptr; + __u64 count_props_ptr; + __u64 props_ptr; + __u64 prop_values_ptr; + __u64 reserved; + __u64 user_data; +}; + #endif -- cgit v1.2.3 From a97df1ccd3c30f16385696964767adf854878021 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 18 Dec 2014 22:49:02 +0100 Subject: drm/atomic: Hide drm.ko internal interfaces This is just a bit fallout from patch polishing and moving the get_prop logic fully into the core: - Drop EXPORT_SYMBOL and make the helpers static. - Drop kerneldoc since not used by drivers. - Move the cross-file function declarations only used by drm.ko internally to an internal header. v2: keep the gist of the comments, requested by Rob. Cc: Rob Clark Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic.c | 54 +++++-------------------------------- drivers/gpu/drm/drm_crtc_internal.h | 6 +++++ drivers/gpu/drm/drm_ioctl.c | 1 + include/drm/drm_atomic.h | 12 --------- 4 files changed, 14 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 67f64bd1058f..1e38dfc8e462 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -247,21 +247,11 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc, } EXPORT_SYMBOL(drm_atomic_crtc_set_property); -/** - * drm_atomic_crtc_get_property - get property on CRTC - * @crtc: the drm CRTC to get a property on - * @state: the state object with the property value to read - * @property: the property to get - * @val: the property value (returned by reference) - * - * Use this instead of calling crtc->atomic_get_property directly. +/* * This function handles generic/core properties and calls out to * driver's ->atomic_get_property() for driver properties. To ensure * consistent behavior you must call this function rather than the * driver hook directly. - * - * RETURNS: - * Zero on success, error code on failure */ int drm_atomic_crtc_get_property(struct drm_crtc *crtc, const struct drm_crtc_state *state, @@ -271,7 +261,6 @@ int drm_atomic_crtc_get_property(struct drm_crtc *crtc, return crtc->funcs->atomic_get_property(crtc, state, property, val); return -EINVAL; } -EXPORT_SYMBOL(drm_atomic_crtc_get_property); /** * drm_atomic_crtc_check - check crtc state @@ -409,23 +398,14 @@ int drm_atomic_plane_set_property(struct drm_plane *plane, } EXPORT_SYMBOL(drm_atomic_plane_set_property); -/** - * drm_atomic_plane_get_property - get property on plane - * @plane: the drm plane to get a property on - * @state: the state object with the property value to read - * @property: the property to get - * @val: the property value (returned by reference) - * - * Use this instead of calling plane->atomic_get_property directly. +/* * This function handles generic/core properties and calls out to * driver's ->atomic_get_property() for driver properties. To ensure * consistent behavior you must call this function rather than the * driver hook directly. - * - * RETURNS: - * Zero on success, error code on failure */ -int drm_atomic_plane_get_property(struct drm_plane *plane, +static int +drm_atomic_plane_get_property(struct drm_plane *plane, const struct drm_plane_state *state, struct drm_property *property, uint64_t *val) { @@ -460,7 +440,6 @@ int drm_atomic_plane_get_property(struct drm_plane *plane, return 0; } -EXPORT_SYMBOL(drm_atomic_plane_get_property); /** * drm_atomic_plane_check - check plane state @@ -650,23 +629,14 @@ int drm_atomic_connector_set_property(struct drm_connector *connector, } EXPORT_SYMBOL(drm_atomic_connector_set_property); -/** - * drm_atomic_connector_get_property - get property on connector - * @connector: the drm connector to get a property on - * @state: the state object with the property value to read - * @property: the property to get - * @val: the property value (returned by reference) - * - * Use this instead of calling connector->atomic_get_property directly. +/* * This function handles generic/core properties and calls out to * driver's ->atomic_get_property() for driver properties. To ensure * consistent behavior you must call this function rather than the * driver hook directly. - * - * RETURNS: - * Zero on success, error code on failure */ -int drm_atomic_connector_get_property(struct drm_connector *connector, +static int +drm_atomic_connector_get_property(struct drm_connector *connector, const struct drm_connector_state *state, struct drm_property *property, uint64_t *val) { @@ -686,17 +656,7 @@ int drm_atomic_connector_get_property(struct drm_connector *connector, return 0; } -EXPORT_SYMBOL(drm_atomic_connector_get_property); -/** - * drm_atomic_get_property - helper to read atomic property - * @obj: drm mode object whose property to read - * @property: the property to read - * @val: the read value, returned by reference - * - * RETURNS: - * Zero on success, error code on failure - */ int drm_atomic_get_property(struct drm_mode_object *obj, struct drm_property *property, uint64_t *val) { diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index a2945ee6d675..247dc8b62564 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -36,3 +36,9 @@ int drm_mode_object_get(struct drm_device *dev, void drm_mode_object_put(struct drm_device *dev, struct drm_mode_object *object); +/* drm_atomic.c */ +int drm_atomic_get_property(struct drm_mode_object *obj, + struct drm_property *property, uint64_t *val); +int drm_mode_atomic_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); + diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index a28c0abd1a38..5cb405812245 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -32,6 +32,7 @@ #include #include "drm_legacy.h" #include "drm_internal.h" +#include "drm_crtc_internal.h" #include #include diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 231fb485abb3..51168a8b723a 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -41,30 +41,18 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state, int drm_atomic_crtc_set_property(struct drm_crtc *crtc, struct drm_crtc_state *state, struct drm_property *property, uint64_t val); -int drm_atomic_crtc_get_property(struct drm_crtc *crtc, - const struct drm_crtc_state *state, - struct drm_property *property, uint64_t *val); struct drm_plane_state * __must_check drm_atomic_get_plane_state(struct drm_atomic_state *state, struct drm_plane *plane); int drm_atomic_plane_set_property(struct drm_plane *plane, struct drm_plane_state *state, struct drm_property *property, uint64_t val); -int drm_atomic_plane_get_property(struct drm_plane *plane, - const struct drm_plane_state *state, - struct drm_property *property, uint64_t *val); struct drm_connector_state * __must_check drm_atomic_get_connector_state(struct drm_atomic_state *state, struct drm_connector *connector); int drm_atomic_connector_set_property(struct drm_connector *connector, struct drm_connector_state *state, struct drm_property *property, uint64_t val); -int drm_atomic_connector_get_property(struct drm_connector *connector, - const struct drm_connector_state *state, - struct drm_property *property, uint64_t *val); - -int drm_atomic_get_property(struct drm_mode_object *obj, - struct drm_property *property, uint64_t *val); int __must_check drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, -- cgit v1.2.3 From 1816f92363036600f2387bb8273b1e5e1f5b304e Mon Sep 17 00:00:00 2001 From: Akash Goel Date: Fri, 2 Jan 2015 16:29:30 +0530 Subject: drm/i915: Support creation of unbound wc user mappings for objects This patch provides support to create write-combining virtual mappings of GEM object. It intends to provide the same funtionality of 'mmap_gtt' interface without the constraints and contention of a limited aperture space, but requires clients handles the linear to tile conversion on their own. This is for improving the CPU write operation performance, as with such mapping, writes and reads are almost 50% faster than with mmap_gtt. Similar to the GTT mmapping, unlike the regular CPU mmapping, it avoids the cache flush after update from CPU side, when object is passed onto GPU. This type of mapping is specially useful in case of sub-region update, i.e. when only a portion of the object is to be updated. Using a CPU mmap in such cases would normally incur a clflush of the whole object, and using a GTT mmapping would likely require eviction of an active object or fence and thus stall. The write-combining CPU mmap avoids both. To ensure the cache coherency, before using this mapping, the GTT domain has been reused here. This provides the required cache flush if the object is in CPU domain or synchronization against the concurrent rendering. Although the access through an uncached mmap should automatically invalidate the cache lines, this may not be true for non-temporal write instructions and also not all pages of the object may be updated at any given point of time through this mapping. Having a call to get_pages in set_to_gtt_domain function, as added in the earlier patch 'drm/i915: Broaden application of set-domain(GTT)', would guarantee the clflush and so there will be no cachelines holding the data for the object before it is accessed through this map. The drm_i915_gem_mmap structure (for the DRM_I915_GEM_MMAP_IOCTL) has been extended with a new flags field (defaulting to 0 for existent users). In order for userspace to detect the extended ioctl, a new parameter I915_PARAM_MMAP_VERSION has been added for versioning the ioctl interface. v2: Fix error handling, invalid flag detection, renaming (ickle) v3: Rebase to latest drm-intel-nightly codebase The new mmapping is exercised by igt/gem_mmap_wc, igt/gem_concurrent_blit and igt/gem_gtt_speed. Change-Id: Ie883942f9e689525f72fe9a8d3780c3a9faa769a Signed-off-by: Akash Goel Signed-off-by: Chris Wilson Cc: Daniel Vetter Reviewed-by: Tvrtko Ursulin Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 3 +++ drivers/gpu/drm/i915/i915_gem.c | 19 +++++++++++++++++++ include/uapi/drm/i915_drm.h | 9 +++++++++ 3 files changed, 31 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 52730ed86385..7fad6b8ac63a 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -143,6 +143,9 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_HAS_COHERENT_PHYS_GTT: value = 1; break; + case I915_PARAM_MMAP_VERSION: + value = 1; + break; default: DRM_DEBUG("Unknown parameter %d\n", param->param); return -EINVAL; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index a2c64a6f665c..9cd457ae4427 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1534,6 +1534,12 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, struct drm_gem_object *obj; unsigned long addr; + if (args->flags & ~(I915_MMAP_WC)) + return -EINVAL; + + if (args->flags & I915_MMAP_WC && !cpu_has_pat) + return -ENODEV; + obj = drm_gem_object_lookup(dev, file, args->handle); if (obj == NULL) return -ENOENT; @@ -1549,6 +1555,19 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, addr = vm_mmap(obj->filp, 0, args->size, PROT_READ | PROT_WRITE, MAP_SHARED, args->offset); + if (args->flags & I915_MMAP_WC) { + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + + down_write(&mm->mmap_sem); + vma = find_vma(mm, addr); + if (vma) + vma->vm_page_prot = + pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); + else + addr = -ENOMEM; + up_write(&mm->mmap_sem); + } drm_gem_object_unreference_unlocked(obj); if (IS_ERR((void *)addr)) return addr; diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 250262265ee3..c155a0347949 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -341,6 +341,7 @@ typedef struct drm_i915_irq_wait { #define I915_PARAM_HAS_WT 27 #define I915_PARAM_CMD_PARSER_VERSION 28 #define I915_PARAM_HAS_COHERENT_PHYS_GTT 29 +#define I915_PARAM_MMAP_VERSION 30 typedef struct drm_i915_getparam { int param; @@ -488,6 +489,14 @@ struct drm_i915_gem_mmap { * This is a fixed-size type for 32/64 compatibility. */ __u64 addr_ptr; + + /** + * Flags for extended behaviour. + * + * Added in version 2. + */ + __u64 flags; +#define I915_MMAP_WC 0x1 }; struct drm_i915_gem_mmap_gtt { -- cgit v1.2.3 From c9dc0f35986c0e2fc81e0b71ddc7e3adad733829 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 24 Dec 2014 08:13:40 -0800 Subject: drm/i915: Add ioctl to set per-context parameters Sometimes we wish to tweak how an individual context behaves. Since we always create a context for every filp, this means that individual processes can fine tune their behaviour even if they do not explicitly create a context. The first example parameter here is to enable multi-process GPU testing, but the interface should be able to cope with passing arbitrarily complex parameters. Signed-off-by: Chris Wilson Signed-off-by: Rodrigo Vivi Reviewed-by: Mika Kuoppala Testcase: igt/gem_reset_stats/ban-period-* Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 2 + drivers/gpu/drm/i915/i915_drv.h | 4 ++ drivers/gpu/drm/i915/i915_gem_context.c | 69 +++++++++++++++++++++++++++++++++ include/uapi/drm/i915_drm.h | 12 ++++++ 4 files changed, 87 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 7fad6b8ac63a..8cbff3010e1c 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1066,6 +1066,8 @@ const struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF_DRV(I915_REG_READ, i915_reg_read_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(I915_GET_RESET_STATS, i915_get_reset_stats_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(I915_GEM_USERPTR, i915_gem_userptr_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_GETPARAM, i915_gem_context_getparam_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_SETPARAM, i915_gem_context_setparam_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), }; int i915_max_ioctl = ARRAY_SIZE(i915_ioctls); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ee841349407e..71016f5192c5 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2898,6 +2898,10 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file); int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file); +int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); /* i915_gem_evict.c */ int __must_check i915_gem_evict_something(struct drm_device *dev, diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 1d10c21f979b..b775ed4a189c 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -762,3 +762,72 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, DRM_DEBUG_DRIVER("HW context %d destroyed\n", args->ctx_id); return 0; } + +int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_i915_file_private *file_priv = file->driver_priv; + struct drm_i915_gem_context_param *args = data; + struct intel_context *ctx; + int ret; + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; + + ctx = i915_gem_context_get(file_priv, args->ctx_id); + if (IS_ERR(ctx)) { + mutex_unlock(&dev->struct_mutex); + return PTR_ERR(ctx); + } + + args->size = 0; + switch (args->param) { + case I915_CONTEXT_PARAM_BAN_PERIOD: + args->value = ctx->hang_stats.ban_period_seconds; + break; + default: + ret = -EINVAL; + break; + } + mutex_unlock(&dev->struct_mutex); + + return ret; +} + +int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_i915_file_private *file_priv = file->driver_priv; + struct drm_i915_gem_context_param *args = data; + struct intel_context *ctx; + int ret; + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; + + ctx = i915_gem_context_get(file_priv, args->ctx_id); + if (IS_ERR(ctx)) { + mutex_unlock(&dev->struct_mutex); + return PTR_ERR(ctx); + } + + switch (args->param) { + case I915_CONTEXT_PARAM_BAN_PERIOD: + if (args->size) + ret = -EINVAL; + else if (args->value < ctx->hang_stats.ban_period_seconds && + !capable(CAP_SYS_ADMIN)) + ret = -EPERM; + else + ctx->hang_stats.ban_period_seconds = args->value; + break; + default: + ret = -EINVAL; + break; + } + mutex_unlock(&dev->struct_mutex); + + return ret; +} diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index c155a0347949..2e559f6e699e 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -224,6 +224,8 @@ typedef struct _drm_i915_sarea { #define DRM_I915_REG_READ 0x31 #define DRM_I915_GET_RESET_STATS 0x32 #define DRM_I915_GEM_USERPTR 0x33 +#define DRM_I915_GEM_CONTEXT_GETPARAM 0x34 +#define DRM_I915_GEM_CONTEXT_SETPARAM 0x35 #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) @@ -275,6 +277,8 @@ typedef struct _drm_i915_sarea { #define DRM_IOCTL_I915_REG_READ DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_REG_READ, struct drm_i915_reg_read) #define DRM_IOCTL_I915_GET_RESET_STATS DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GET_RESET_STATS, struct drm_i915_reset_stats) #define DRM_IOCTL_I915_GEM_USERPTR DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_USERPTR, struct drm_i915_gem_userptr) +#define DRM_IOCTL_I915_GEM_CONTEXT_GETPARAM DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_GETPARAM, struct drm_i915_gem_context_param) +#define DRM_IOCTL_I915_GEM_CONTEXT_SETPARAM DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_SETPARAM, struct drm_i915_gem_context_param) /* Allow drivers to submit batchbuffers directly to hardware, relying * on the security mechanisms provided by hardware. @@ -1082,4 +1086,12 @@ struct drm_i915_gem_userptr { __u32 handle; }; +struct drm_i915_gem_context_param { + __u32 ctx_id; + __u32 size; + __u64 param; +#define I915_CONTEXT_PARAM_BAN_PERIOD 0x1 + __u64 value; +}; + #endif /* _UAPI_I915_DRM_H_ */ -- cgit v1.2.3 From b21f4b658df885068c65852ef3d9d2f4f2821b03 Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Fri, 5 Dec 2014 14:26:31 +0800 Subject: drm: imx: imx-hdmi: move imx-hdmi to bridge/dw_hdmi the original imx hdmi driver is under drm/imx/, which depends on imx-drm, so move the imx hdmi driver out to drm/bridge and rename it to dw_hdmi Signed-off-by: Andy Yan Tested-by: Russell King Acked-by: Russell King Signed-off-by: Philipp Zabel --- drivers/gpu/drm/bridge/Kconfig | 5 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/dw_hdmi.c | 1645 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/bridge/dw_hdmi.h | 1033 +++++++++++++++++++++ drivers/gpu/drm/imx/Kconfig | 1 + drivers/gpu/drm/imx/Makefile | 2 +- drivers/gpu/drm/imx/dw_hdmi-imx.c | 258 ++++++ drivers/gpu/drm/imx/imx-hdmi.c | 1643 --------------------------------- drivers/gpu/drm/imx/imx-hdmi.h | 1076 ---------------------- drivers/gpu/drm/imx/imx-hdmi_pltfm.c | 260 ------ include/drm/bridge/dw_hdmi.h | 60 ++ 11 files changed, 3004 insertions(+), 2980 deletions(-) create mode 100644 drivers/gpu/drm/bridge/dw_hdmi.c create mode 100644 drivers/gpu/drm/bridge/dw_hdmi.h create mode 100644 drivers/gpu/drm/imx/dw_hdmi-imx.c delete mode 100644 drivers/gpu/drm/imx/imx-hdmi.c delete mode 100644 drivers/gpu/drm/imx/imx-hdmi.h delete mode 100644 drivers/gpu/drm/imx/imx-hdmi_pltfm.c create mode 100644 include/drm/bridge/dw_hdmi.h (limited to 'include') diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 884923f982d9..b70f3c8d4e8a 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -3,3 +3,8 @@ config DRM_PTN3460 depends on DRM select DRM_KMS_HELPER ---help--- + +config DRM_DW_HDMI + tristate + depends on DRM + select DRM_KMS_HELPER diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index b4733e1fbd2e..d8a8cfd12fbb 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -1,3 +1,4 @@ ccflags-y := -Iinclude/drm obj-$(CONFIG_DRM_PTN3460) += ptn3460.o +obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c new file mode 100644 index 000000000000..ee2cbee8bf69 --- /dev/null +++ b/drivers/gpu/drm/bridge/dw_hdmi.c @@ -0,0 +1,1645 @@ +/* + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Designware High-Definition Multimedia Interface (HDMI) driver + * + * Copyright (C) 2010, Guennadi Liakhovetski + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "dw_hdmi.h" + +#define HDMI_EDID_LEN 512 + +#define RGB 0 +#define YCBCR444 1 +#define YCBCR422_16BITS 2 +#define YCBCR422_8BITS 3 +#define XVYCC444 4 + +enum hdmi_datamap { + RGB444_8B = 0x01, + RGB444_10B = 0x03, + RGB444_12B = 0x05, + RGB444_16B = 0x07, + YCbCr444_8B = 0x09, + YCbCr444_10B = 0x0B, + YCbCr444_12B = 0x0D, + YCbCr444_16B = 0x0F, + YCbCr422_8B = 0x16, + YCbCr422_10B = 0x14, + YCbCr422_12B = 0x12, +}; + +static const u16 csc_coeff_default[3][4] = { + { 0x2000, 0x0000, 0x0000, 0x0000 }, + { 0x0000, 0x2000, 0x0000, 0x0000 }, + { 0x0000, 0x0000, 0x2000, 0x0000 } +}; + +static const u16 csc_coeff_rgb_out_eitu601[3][4] = { + { 0x2000, 0x6926, 0x74fd, 0x010e }, + { 0x2000, 0x2cdd, 0x0000, 0x7e9a }, + { 0x2000, 0x0000, 0x38b4, 0x7e3b } +}; + +static const u16 csc_coeff_rgb_out_eitu709[3][4] = { + { 0x2000, 0x7106, 0x7a02, 0x00a7 }, + { 0x2000, 0x3264, 0x0000, 0x7e6d }, + { 0x2000, 0x0000, 0x3b61, 0x7e25 } +}; + +static const u16 csc_coeff_rgb_in_eitu601[3][4] = { + { 0x2591, 0x1322, 0x074b, 0x0000 }, + { 0x6535, 0x2000, 0x7acc, 0x0200 }, + { 0x6acd, 0x7534, 0x2000, 0x0200 } +}; + +static const u16 csc_coeff_rgb_in_eitu709[3][4] = { + { 0x2dc5, 0x0d9b, 0x049e, 0x0000 }, + { 0x62f0, 0x2000, 0x7d11, 0x0200 }, + { 0x6756, 0x78ab, 0x2000, 0x0200 } +}; + +struct hdmi_vmode { + bool mdvi; + bool mhsyncpolarity; + bool mvsyncpolarity; + bool minterlaced; + bool mdataenablepolarity; + + unsigned int mpixelclock; + unsigned int mpixelrepetitioninput; + unsigned int mpixelrepetitionoutput; +}; + +struct hdmi_data_info { + unsigned int enc_in_format; + unsigned int enc_out_format; + unsigned int enc_color_depth; + unsigned int colorimetry; + unsigned int pix_repet_factor; + unsigned int hdcp_enable; + struct hdmi_vmode video_mode; +}; + +struct dw_hdmi { + struct drm_connector connector; + struct drm_encoder *encoder; + struct drm_bridge *bridge; + + enum dw_hdmi_devtype dev_type; + struct device *dev; + struct clk *isfr_clk; + struct clk *iahb_clk; + + struct hdmi_data_info hdmi_data; + const struct dw_hdmi_plat_data *plat_data; + + int vic; + + u8 edid[HDMI_EDID_LEN]; + bool cable_plugin; + + bool phy_enabled; + struct drm_display_mode previous_mode; + + struct regmap *regmap; + struct i2c_adapter *ddc; + void __iomem *regs; + + unsigned int sample_rate; + int ratio; +}; + +static inline void hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset) +{ + writeb(val, hdmi->regs + offset); +} + +static inline u8 hdmi_readb(struct dw_hdmi *hdmi, int offset) +{ + return readb(hdmi->regs + offset); +} + +static void hdmi_modb(struct dw_hdmi *hdmi, u8 data, u8 mask, unsigned reg) +{ + u8 val = hdmi_readb(hdmi, reg) & ~mask; + + val |= data & mask; + hdmi_writeb(hdmi, val, reg); +} + +static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg, + u8 shift, u8 mask) +{ + hdmi_modb(hdmi, data << shift, mask, reg); +} + +static void hdmi_set_clock_regenerator_n(struct dw_hdmi *hdmi, + unsigned int value) +{ + hdmi_writeb(hdmi, value & 0xff, HDMI_AUD_N1); + hdmi_writeb(hdmi, (value >> 8) & 0xff, HDMI_AUD_N2); + hdmi_writeb(hdmi, (value >> 16) & 0x0f, HDMI_AUD_N3); + + /* nshift factor = 0 */ + hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3); +} + +static void hdmi_regenerate_cts(struct dw_hdmi *hdmi, unsigned int cts) +{ + /* Must be set/cleared first */ + hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); + + hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1); + hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2); + hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) | + HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); +} + +static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk, + unsigned int ratio) +{ + unsigned int n = (128 * freq) / 1000; + + switch (freq) { + case 32000: + if (pixel_clk == 25170000) + n = (ratio == 150) ? 9152 : 4576; + else if (pixel_clk == 27020000) + n = (ratio == 150) ? 8192 : 4096; + else if (pixel_clk == 74170000 || pixel_clk == 148350000) + n = 11648; + else + n = 4096; + break; + + case 44100: + if (pixel_clk == 25170000) + n = 7007; + else if (pixel_clk == 74170000) + n = 17836; + else if (pixel_clk == 148350000) + n = (ratio == 150) ? 17836 : 8918; + else + n = 6272; + break; + + case 48000: + if (pixel_clk == 25170000) + n = (ratio == 150) ? 9152 : 6864; + else if (pixel_clk == 27020000) + n = (ratio == 150) ? 8192 : 6144; + else if (pixel_clk == 74170000) + n = 11648; + else if (pixel_clk == 148350000) + n = (ratio == 150) ? 11648 : 5824; + else + n = 6144; + break; + + case 88200: + n = hdmi_compute_n(44100, pixel_clk, ratio) * 2; + break; + + case 96000: + n = hdmi_compute_n(48000, pixel_clk, ratio) * 2; + break; + + case 176400: + n = hdmi_compute_n(44100, pixel_clk, ratio) * 4; + break; + + case 192000: + n = hdmi_compute_n(48000, pixel_clk, ratio) * 4; + break; + + default: + break; + } + + return n; +} + +static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk, + unsigned int ratio) +{ + unsigned int cts = 0; + + pr_debug("%s: freq: %d pixel_clk: %ld ratio: %d\n", __func__, freq, + pixel_clk, ratio); + + switch (freq) { + case 32000: + if (pixel_clk == 297000000) { + cts = 222750; + break; + } + case 48000: + case 96000: + case 192000: + switch (pixel_clk) { + case 25200000: + case 27000000: + case 54000000: + case 74250000: + case 148500000: + cts = pixel_clk / 1000; + break; + case 297000000: + cts = 247500; + break; + /* + * All other TMDS clocks are not supported by + * DWC_hdmi_tx. The TMDS clocks divided or + * multiplied by 1,001 coefficients are not + * supported. + */ + default: + break; + } + break; + case 44100: + case 88200: + case 176400: + switch (pixel_clk) { + case 25200000: + cts = 28000; + break; + case 27000000: + cts = 30000; + break; + case 54000000: + cts = 60000; + break; + case 74250000: + cts = 82500; + break; + case 148500000: + cts = 165000; + break; + case 297000000: + cts = 247500; + break; + default: + break; + } + break; + default: + break; + } + if (ratio == 100) + return cts; + return (cts * ratio) / 100; +} + +static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi, + unsigned long pixel_clk) +{ + unsigned int clk_n, clk_cts; + + clk_n = hdmi_compute_n(hdmi->sample_rate, pixel_clk, + hdmi->ratio); + clk_cts = hdmi_compute_cts(hdmi->sample_rate, pixel_clk, + hdmi->ratio); + + if (!clk_cts) { + dev_dbg(hdmi->dev, "%s: pixel clock not supported: %lu\n", + __func__, pixel_clk); + return; + } + + dev_dbg(hdmi->dev, "%s: samplerate=%d ratio=%d pixelclk=%lu N=%d cts=%d\n", + __func__, hdmi->sample_rate, hdmi->ratio, + pixel_clk, clk_n, clk_cts); + + hdmi_set_clock_regenerator_n(hdmi, clk_n); + hdmi_regenerate_cts(hdmi, clk_cts); +} + +static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi) +{ + hdmi_set_clk_regenerator(hdmi, 74250000); +} + +static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi) +{ + hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock); +} + +/* + * this submodule is responsible for the video data synchronization. + * for example, for RGB 4:4:4 input, the data map is defined as + * pin{47~40} <==> R[7:0] + * pin{31~24} <==> G[7:0] + * pin{15~8} <==> B[7:0] + */ +static void hdmi_video_sample(struct dw_hdmi *hdmi) +{ + int color_format = 0; + u8 val; + + if (hdmi->hdmi_data.enc_in_format == RGB) { + if (hdmi->hdmi_data.enc_color_depth == 8) + color_format = 0x01; + else if (hdmi->hdmi_data.enc_color_depth == 10) + color_format = 0x03; + else if (hdmi->hdmi_data.enc_color_depth == 12) + color_format = 0x05; + else if (hdmi->hdmi_data.enc_color_depth == 16) + color_format = 0x07; + else + return; + } else if (hdmi->hdmi_data.enc_in_format == YCBCR444) { + if (hdmi->hdmi_data.enc_color_depth == 8) + color_format = 0x09; + else if (hdmi->hdmi_data.enc_color_depth == 10) + color_format = 0x0B; + else if (hdmi->hdmi_data.enc_color_depth == 12) + color_format = 0x0D; + else if (hdmi->hdmi_data.enc_color_depth == 16) + color_format = 0x0F; + else + return; + } else if (hdmi->hdmi_data.enc_in_format == YCBCR422_8BITS) { + if (hdmi->hdmi_data.enc_color_depth == 8) + color_format = 0x16; + else if (hdmi->hdmi_data.enc_color_depth == 10) + color_format = 0x14; + else if (hdmi->hdmi_data.enc_color_depth == 12) + color_format = 0x12; + else + return; + } + + val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE | + ((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) & + HDMI_TX_INVID0_VIDEO_MAPPING_MASK); + hdmi_writeb(hdmi, val, HDMI_TX_INVID0); + + /* Enable TX stuffing: When DE is inactive, fix the output data to 0 */ + val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE | + HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE | + HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE; + hdmi_writeb(hdmi, val, HDMI_TX_INSTUFFING); + hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA0); + hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA1); + hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA0); + hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA1); + hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA0); + hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA1); +} + +static int is_color_space_conversion(struct dw_hdmi *hdmi) +{ + return hdmi->hdmi_data.enc_in_format != hdmi->hdmi_data.enc_out_format; +} + +static int is_color_space_decimation(struct dw_hdmi *hdmi) +{ + if (hdmi->hdmi_data.enc_out_format != YCBCR422_8BITS) + return 0; + if (hdmi->hdmi_data.enc_in_format == RGB || + hdmi->hdmi_data.enc_in_format == YCBCR444) + return 1; + return 0; +} + +static int is_color_space_interpolation(struct dw_hdmi *hdmi) +{ + if (hdmi->hdmi_data.enc_in_format != YCBCR422_8BITS) + return 0; + if (hdmi->hdmi_data.enc_out_format == RGB || + hdmi->hdmi_data.enc_out_format == YCBCR444) + return 1; + return 0; +} + +static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi) +{ + const u16 (*csc_coeff)[3][4] = &csc_coeff_default; + unsigned i; + u32 csc_scale = 1; + + if (is_color_space_conversion(hdmi)) { + if (hdmi->hdmi_data.enc_out_format == RGB) { + if (hdmi->hdmi_data.colorimetry == + HDMI_COLORIMETRY_ITU_601) + csc_coeff = &csc_coeff_rgb_out_eitu601; + else + csc_coeff = &csc_coeff_rgb_out_eitu709; + } else if (hdmi->hdmi_data.enc_in_format == RGB) { + if (hdmi->hdmi_data.colorimetry == + HDMI_COLORIMETRY_ITU_601) + csc_coeff = &csc_coeff_rgb_in_eitu601; + else + csc_coeff = &csc_coeff_rgb_in_eitu709; + csc_scale = 0; + } + } + + /* The CSC registers are sequential, alternating MSB then LSB */ + for (i = 0; i < ARRAY_SIZE(csc_coeff_default[0]); i++) { + u16 coeff_a = (*csc_coeff)[0][i]; + u16 coeff_b = (*csc_coeff)[1][i]; + u16 coeff_c = (*csc_coeff)[2][i]; + + hdmi_writeb(hdmi, coeff_a & 0xff, HDMI_CSC_COEF_A1_LSB + i * 2); + hdmi_writeb(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2); + hdmi_writeb(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2); + hdmi_writeb(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2); + hdmi_writeb(hdmi, coeff_c & 0xff, HDMI_CSC_COEF_C1_LSB + i * 2); + hdmi_writeb(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2); + } + + hdmi_modb(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK, + HDMI_CSC_SCALE); +} + +static void hdmi_video_csc(struct dw_hdmi *hdmi) +{ + int color_depth = 0; + int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE; + int decimation = 0; + + /* YCC422 interpolation to 444 mode */ + if (is_color_space_interpolation(hdmi)) + interpolation = HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1; + else if (is_color_space_decimation(hdmi)) + decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3; + + if (hdmi->hdmi_data.enc_color_depth == 8) + color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP; + else if (hdmi->hdmi_data.enc_color_depth == 10) + color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP; + else if (hdmi->hdmi_data.enc_color_depth == 12) + color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP; + else if (hdmi->hdmi_data.enc_color_depth == 16) + color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP; + else + return; + + /* Configure the CSC registers */ + hdmi_writeb(hdmi, interpolation | decimation, HDMI_CSC_CFG); + hdmi_modb(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK, + HDMI_CSC_SCALE); + + dw_hdmi_update_csc_coeffs(hdmi); +} + +/* + * HDMI video packetizer is used to packetize the data. + * for example, if input is YCC422 mode or repeater is used, + * data should be repacked this module can be bypassed. + */ +static void hdmi_video_packetize(struct dw_hdmi *hdmi) +{ + unsigned int color_depth = 0; + unsigned int remap_size = HDMI_VP_REMAP_YCC422_16bit; + unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP; + struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data; + u8 val, vp_conf; + + if (hdmi_data->enc_out_format == RGB || + hdmi_data->enc_out_format == YCBCR444) { + if (!hdmi_data->enc_color_depth) { + output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS; + } else if (hdmi_data->enc_color_depth == 8) { + color_depth = 4; + output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS; + } else if (hdmi_data->enc_color_depth == 10) { + color_depth = 5; + } else if (hdmi_data->enc_color_depth == 12) { + color_depth = 6; + } else if (hdmi_data->enc_color_depth == 16) { + color_depth = 7; + } else { + return; + } + } else if (hdmi_data->enc_out_format == YCBCR422_8BITS) { + if (!hdmi_data->enc_color_depth || + hdmi_data->enc_color_depth == 8) + remap_size = HDMI_VP_REMAP_YCC422_16bit; + else if (hdmi_data->enc_color_depth == 10) + remap_size = HDMI_VP_REMAP_YCC422_20bit; + else if (hdmi_data->enc_color_depth == 12) + remap_size = HDMI_VP_REMAP_YCC422_24bit; + else + return; + output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422; + } else { + return; + } + + /* set the packetizer registers */ + val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) & + HDMI_VP_PR_CD_COLOR_DEPTH_MASK) | + ((hdmi_data->pix_repet_factor << + HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET) & + HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK); + hdmi_writeb(hdmi, val, HDMI_VP_PR_CD); + + hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE, + HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF); + + /* Data from pixel repeater block */ + if (hdmi_data->pix_repet_factor > 1) { + vp_conf = HDMI_VP_CONF_PR_EN_ENABLE | + HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER; + } else { /* data from packetizer block */ + vp_conf = HDMI_VP_CONF_PR_EN_DISABLE | + HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER; + } + + hdmi_modb(hdmi, vp_conf, + HDMI_VP_CONF_PR_EN_MASK | + HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF); + + hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET, + HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF); + + hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP); + + if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) { + vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE | + HDMI_VP_CONF_PP_EN_ENABLE | + HDMI_VP_CONF_YCC422_EN_DISABLE; + } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422) { + vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE | + HDMI_VP_CONF_PP_EN_DISABLE | + HDMI_VP_CONF_YCC422_EN_ENABLE; + } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS) { + vp_conf = HDMI_VP_CONF_BYPASS_EN_ENABLE | + HDMI_VP_CONF_PP_EN_DISABLE | + HDMI_VP_CONF_YCC422_EN_DISABLE; + } else { + return; + } + + hdmi_modb(hdmi, vp_conf, + HDMI_VP_CONF_BYPASS_EN_MASK | HDMI_VP_CONF_PP_EN_ENMASK | + HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF); + + hdmi_modb(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE | + HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE, + HDMI_VP_STUFF_PP_STUFFING_MASK | + HDMI_VP_STUFF_YCC422_STUFFING_MASK, HDMI_VP_STUFF); + + hdmi_modb(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK, + HDMI_VP_CONF); +} + +static inline void hdmi_phy_test_clear(struct dw_hdmi *hdmi, + unsigned char bit) +{ + hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET, + HDMI_PHY_TST0_TSTCLR_MASK, HDMI_PHY_TST0); +} + +static inline void hdmi_phy_test_enable(struct dw_hdmi *hdmi, + unsigned char bit) +{ + hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET, + HDMI_PHY_TST0_TSTEN_MASK, HDMI_PHY_TST0); +} + +static inline void hdmi_phy_test_clock(struct dw_hdmi *hdmi, + unsigned char bit) +{ + hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET, + HDMI_PHY_TST0_TSTCLK_MASK, HDMI_PHY_TST0); +} + +static inline void hdmi_phy_test_din(struct dw_hdmi *hdmi, + unsigned char bit) +{ + hdmi_writeb(hdmi, bit, HDMI_PHY_TST1); +} + +static inline void hdmi_phy_test_dout(struct dw_hdmi *hdmi, + unsigned char bit) +{ + hdmi_writeb(hdmi, bit, HDMI_PHY_TST2); +} + +static bool hdmi_phy_wait_i2c_done(struct dw_hdmi *hdmi, int msec) +{ + while ((hdmi_readb(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3) == 0) { + if (msec-- == 0) + return false; + udelay(1000); + } + return true; +} + +static void __hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, + unsigned char addr) +{ + hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0); + hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR); + hdmi_writeb(hdmi, (unsigned char)(data >> 8), + HDMI_PHY_I2CM_DATAO_1_ADDR); + hdmi_writeb(hdmi, (unsigned char)(data >> 0), + HDMI_PHY_I2CM_DATAO_0_ADDR); + hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE, + HDMI_PHY_I2CM_OPERATION_ADDR); + hdmi_phy_wait_i2c_done(hdmi, 1000); +} + +static int hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, + unsigned char addr) +{ + __hdmi_phy_i2c_write(hdmi, data, addr); + return 0; +} + +static void dw_hdmi_phy_enable_power(struct dw_hdmi *hdmi, u8 enable) +{ + hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_PDZ_OFFSET, + HDMI_PHY_CONF0_PDZ_MASK); +} + +static void dw_hdmi_phy_enable_tmds(struct dw_hdmi *hdmi, u8 enable) +{ + hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_ENTMDS_OFFSET, + HDMI_PHY_CONF0_ENTMDS_MASK); +} + +static void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable) +{ + hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET, + HDMI_PHY_CONF0_GEN2_PDDQ_MASK); +} + +static void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable) +{ + hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET, + HDMI_PHY_CONF0_GEN2_TXPWRON_MASK); +} + +static void dw_hdmi_phy_sel_data_en_pol(struct dw_hdmi *hdmi, u8 enable) +{ + hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_SELDATAENPOL_OFFSET, + HDMI_PHY_CONF0_SELDATAENPOL_MASK); +} + +static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable) +{ + hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_SELDIPIF_OFFSET, + HDMI_PHY_CONF0_SELDIPIF_MASK); +} + +static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep, + unsigned char res, int cscon) +{ + unsigned res_idx, i; + u8 val, msec; + const struct dw_hdmi_mpll_config *mpll_config = + hdmi->plat_data->mpll_cfg; + const struct dw_hdmi_curr_ctrl *curr_ctrl = hdmi->plat_data->cur_ctr; + const struct dw_hdmi_sym_term *sym_term = hdmi->plat_data->sym_term; + + if (prep) + return -EINVAL; + + switch (res) { + case 0: /* color resolution 0 is 8 bit colour depth */ + case 8: + res_idx = DW_HDMI_RES_8; + break; + case 10: + res_idx = DW_HDMI_RES_10; + break; + case 12: + res_idx = DW_HDMI_RES_12; + break; + default: + return -EINVAL; + } + + /* Enable csc path */ + if (cscon) + val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH; + else + val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS; + + hdmi_writeb(hdmi, val, HDMI_MC_FLOWCTRL); + + /* gen2 tx power off */ + dw_hdmi_phy_gen2_txpwron(hdmi, 0); + + /* gen2 pddq */ + dw_hdmi_phy_gen2_pddq(hdmi, 1); + + /* PHY reset */ + hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ); + hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ); + + hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST); + + hdmi_phy_test_clear(hdmi, 1); + hdmi_writeb(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2, + HDMI_PHY_I2CM_SLAVE_ADDR); + hdmi_phy_test_clear(hdmi, 0); + + /* PLL/MPLL Cfg - always match on final entry */ + for (i = 0; mpll_config[i].mpixelclock != (~0UL); i++) + if (hdmi->hdmi_data.video_mode.mpixelclock <= + mpll_config[i].mpixelclock) + break; + + hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].cpce, 0x06); + hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].gmp, 0x15); + + for (i = 0; curr_ctrl[i].mpixelclock != (~0UL); i++) + if (hdmi->hdmi_data.video_mode.mpixelclock <= + curr_ctrl[i].mpixelclock) + break; + + if (curr_ctrl[i].mpixelclock == (~0UL)) { + dev_err(hdmi->dev, "Pixel clock %d - unsupported by HDMI\n", + hdmi->hdmi_data.video_mode.mpixelclock); + return -EINVAL; + } + + /* CURRCTRL */ + hdmi_phy_i2c_write(hdmi, curr_ctrl[i].curr[res_idx], 0x10); + + hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); /* PLLPHBYCTRL */ + hdmi_phy_i2c_write(hdmi, 0x0006, 0x17); + + for (i = 0; sym_term[i].mpixelclock != (~0UL); i++) + if (hdmi->hdmi_data.video_mode.mpixelclock <= + sym_term[i].mpixelclock) + break; + + /* RESISTANCE TERM 133Ohm Cfg */ + hdmi_phy_i2c_write(hdmi, sym_term[i].term, 0x19); /* TXTERM */ + /* PREEMP Cgf 0.00 */ + hdmi_phy_i2c_write(hdmi, sym_term[i].sym_ctr, 0x09); /* CKSYMTXCTRL */ + + /* TX/CK LVL 10 */ + hdmi_phy_i2c_write(hdmi, 0x01ad, 0x0E); /* VLEVCTRL */ + /* REMOVE CLK TERM */ + hdmi_phy_i2c_write(hdmi, 0x8000, 0x05); /* CKCALCTRL */ + + dw_hdmi_phy_enable_power(hdmi, 1); + + /* toggle TMDS enable */ + dw_hdmi_phy_enable_tmds(hdmi, 0); + dw_hdmi_phy_enable_tmds(hdmi, 1); + + /* gen2 tx power on */ + dw_hdmi_phy_gen2_txpwron(hdmi, 1); + dw_hdmi_phy_gen2_pddq(hdmi, 0); + + /*Wait for PHY PLL lock */ + msec = 5; + do { + val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK; + if (!val) + break; + + if (msec == 0) { + dev_err(hdmi->dev, "PHY PLL not locked\n"); + return -ETIMEDOUT; + } + + udelay(1000); + msec--; + } while (1); + + return 0; +} + +static int dw_hdmi_phy_init(struct dw_hdmi *hdmi) +{ + int i, ret; + bool cscon = false; + + /*check csc whether needed activated in HDMI mode */ + cscon = (is_color_space_conversion(hdmi) && + !hdmi->hdmi_data.video_mode.mdvi); + + /* HDMI Phy spec says to do the phy initialization sequence twice */ + for (i = 0; i < 2; i++) { + dw_hdmi_phy_sel_data_en_pol(hdmi, 1); + dw_hdmi_phy_sel_interface_control(hdmi, 0); + dw_hdmi_phy_enable_tmds(hdmi, 0); + dw_hdmi_phy_enable_power(hdmi, 0); + + /* Enable CSC */ + ret = hdmi_phy_configure(hdmi, 0, 8, cscon); + if (ret) + return ret; + } + + hdmi->phy_enabled = true; + return 0; +} + +static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi) +{ + u8 de; + + if (hdmi->hdmi_data.video_mode.mdataenablepolarity) + de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH; + else + de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW; + + /* disable rx detect */ + hdmi_modb(hdmi, HDMI_A_HDCPCFG0_RXDETECT_DISABLE, + HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0); + + hdmi_modb(hdmi, de, HDMI_A_VIDPOLCFG_DATAENPOL_MASK, HDMI_A_VIDPOLCFG); + + hdmi_modb(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE, + HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1); +} + +static void hdmi_config_AVI(struct dw_hdmi *hdmi) +{ + u8 val, pix_fmt, under_scan; + u8 act_ratio, coded_ratio, colorimetry, ext_colorimetry; + bool aspect_16_9; + + aspect_16_9 = false; /* FIXME */ + + /* AVI Data Byte 1 */ + if (hdmi->hdmi_data.enc_out_format == YCBCR444) + pix_fmt = HDMI_FC_AVICONF0_PIX_FMT_YCBCR444; + else if (hdmi->hdmi_data.enc_out_format == YCBCR422_8BITS) + pix_fmt = HDMI_FC_AVICONF0_PIX_FMT_YCBCR422; + else + pix_fmt = HDMI_FC_AVICONF0_PIX_FMT_RGB; + + under_scan = HDMI_FC_AVICONF0_SCAN_INFO_NODATA; + + /* + * Active format identification data is present in the AVI InfoFrame. + * Under scan info, no bar data + */ + val = pix_fmt | under_scan | + HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT | + HDMI_FC_AVICONF0_BAR_DATA_NO_DATA; + + hdmi_writeb(hdmi, val, HDMI_FC_AVICONF0); + + /* AVI Data Byte 2 -Set the Aspect Ratio */ + if (aspect_16_9) { + act_ratio = HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_16_9; + coded_ratio = HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_16_9; + } else { + act_ratio = HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_4_3; + coded_ratio = HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_4_3; + } + + /* Set up colorimetry */ + if (hdmi->hdmi_data.enc_out_format == XVYCC444) { + colorimetry = HDMI_FC_AVICONF1_COLORIMETRY_EXTENDED_INFO; + if (hdmi->hdmi_data.colorimetry == HDMI_COLORIMETRY_ITU_601) + ext_colorimetry = + HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601; + else /*hdmi->hdmi_data.colorimetry == HDMI_COLORIMETRY_ITU_709*/ + ext_colorimetry = + HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC709; + } else if (hdmi->hdmi_data.enc_out_format != RGB) { + if (hdmi->hdmi_data.colorimetry == HDMI_COLORIMETRY_ITU_601) + colorimetry = HDMI_FC_AVICONF1_COLORIMETRY_SMPTE; + else /*hdmi->hdmi_data.colorimetry == HDMI_COLORIMETRY_ITU_709*/ + colorimetry = HDMI_FC_AVICONF1_COLORIMETRY_ITUR; + ext_colorimetry = HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601; + } else { /* Carries no data */ + colorimetry = HDMI_FC_AVICONF1_COLORIMETRY_NO_DATA; + ext_colorimetry = HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601; + } + + val = colorimetry | coded_ratio | act_ratio; + hdmi_writeb(hdmi, val, HDMI_FC_AVICONF1); + + /* AVI Data Byte 3 */ + val = HDMI_FC_AVICONF2_IT_CONTENT_NO_DATA | ext_colorimetry | + HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT | + HDMI_FC_AVICONF2_SCALING_NONE; + hdmi_writeb(hdmi, val, HDMI_FC_AVICONF2); + + /* AVI Data Byte 4 */ + hdmi_writeb(hdmi, hdmi->vic, HDMI_FC_AVIVID); + + /* AVI Data Byte 5- set up input and output pixel repetition */ + val = (((hdmi->hdmi_data.video_mode.mpixelrepetitioninput + 1) << + HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET) & + HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK) | + ((hdmi->hdmi_data.video_mode.mpixelrepetitionoutput << + HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET) & + HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK); + hdmi_writeb(hdmi, val, HDMI_FC_PRCONF); + + /* IT Content and quantization range = don't care */ + val = HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GRAPHICS | + HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED; + hdmi_writeb(hdmi, val, HDMI_FC_AVICONF3); + + /* AVI Data Bytes 6-13 */ + hdmi_writeb(hdmi, 0, HDMI_FC_AVIETB0); + hdmi_writeb(hdmi, 0, HDMI_FC_AVIETB1); + hdmi_writeb(hdmi, 0, HDMI_FC_AVISBB0); + hdmi_writeb(hdmi, 0, HDMI_FC_AVISBB1); + hdmi_writeb(hdmi, 0, HDMI_FC_AVIELB0); + hdmi_writeb(hdmi, 0, HDMI_FC_AVIELB1); + hdmi_writeb(hdmi, 0, HDMI_FC_AVISRB0); + hdmi_writeb(hdmi, 0, HDMI_FC_AVISRB1); +} + +static void hdmi_av_composer(struct dw_hdmi *hdmi, + const struct drm_display_mode *mode) +{ + u8 inv_val; + struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode; + int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len; + + vmode->mhsyncpolarity = !!(mode->flags & DRM_MODE_FLAG_PHSYNC); + vmode->mvsyncpolarity = !!(mode->flags & DRM_MODE_FLAG_PVSYNC); + vmode->minterlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); + vmode->mpixelclock = mode->clock * 1000; + + dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock); + + /* Set up HDMI_FC_INVIDCONF */ + inv_val = (hdmi->hdmi_data.hdcp_enable ? + HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE : + HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE); + + inv_val |= (vmode->mvsyncpolarity ? + HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH : + HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW); + + inv_val |= (vmode->mhsyncpolarity ? + HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH : + HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW); + + inv_val |= (vmode->mdataenablepolarity ? + HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH : + HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW); + + if (hdmi->vic == 39) + inv_val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH; + else + inv_val |= (vmode->minterlaced ? + HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH : + HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW); + + inv_val |= (vmode->minterlaced ? + HDMI_FC_INVIDCONF_IN_I_P_INTERLACED : + HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE); + + inv_val |= (vmode->mdvi ? + HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE : + HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE); + + hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF); + + /* Set up horizontal active pixel width */ + hdmi_writeb(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1); + hdmi_writeb(hdmi, mode->hdisplay, HDMI_FC_INHACTV0); + + /* Set up vertical active lines */ + hdmi_writeb(hdmi, mode->vdisplay >> 8, HDMI_FC_INVACTV1); + hdmi_writeb(hdmi, mode->vdisplay, HDMI_FC_INVACTV0); + + /* Set up horizontal blanking pixel region width */ + hblank = mode->htotal - mode->hdisplay; + hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1); + hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0); + + /* Set up vertical blanking pixel region width */ + vblank = mode->vtotal - mode->vdisplay; + hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK); + + /* Set up HSYNC active edge delay width (in pixel clks) */ + h_de_hs = mode->hsync_start - mode->hdisplay; + hdmi_writeb(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1); + hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0); + + /* Set up VSYNC active edge delay (in lines) */ + v_de_vs = mode->vsync_start - mode->vdisplay; + hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY); + + /* Set up HSYNC active pulse width (in pixel clks) */ + hsync_len = mode->hsync_end - mode->hsync_start; + hdmi_writeb(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1); + hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0); + + /* Set up VSYNC active edge delay (in lines) */ + vsync_len = mode->vsync_end - mode->vsync_start; + hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH); +} + +static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi) +{ + if (!hdmi->phy_enabled) + return; + + dw_hdmi_phy_enable_tmds(hdmi, 0); + dw_hdmi_phy_enable_power(hdmi, 0); + + hdmi->phy_enabled = false; +} + +/* HDMI Initialization Step B.4 */ +static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi) +{ + u8 clkdis; + + /* control period minimum duration */ + hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR); + hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR); + hdmi_writeb(hdmi, 1, HDMI_FC_EXCTRLSPAC); + + /* Set to fill TMDS data channels */ + hdmi_writeb(hdmi, 0x0B, HDMI_FC_CH0PREAM); + hdmi_writeb(hdmi, 0x16, HDMI_FC_CH1PREAM); + hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM); + + /* Enable pixel clock and tmds data path */ + clkdis = 0x7F; + clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE; + hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); + + clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE; + hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); + + /* Enable csc path */ + if (is_color_space_conversion(hdmi)) { + clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE; + hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); + } +} + +static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi) +{ + hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); +} + +/* Workaround to clear the overflow condition */ +static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi) +{ + int count; + u8 val; + + /* TMDS software reset */ + hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ); + + val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF); + if (hdmi->dev_type == IMX6DL_HDMI) { + hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF); + return; + } + + for (count = 0; count < 4; count++) + hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF); +} + +static void hdmi_enable_overflow_interrupts(struct dw_hdmi *hdmi) +{ + hdmi_writeb(hdmi, 0, HDMI_FC_MASK2); + hdmi_writeb(hdmi, 0, HDMI_IH_MUTE_FC_STAT2); +} + +static void hdmi_disable_overflow_interrupts(struct dw_hdmi *hdmi) +{ + hdmi_writeb(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK, + HDMI_IH_MUTE_FC_STAT2); +} + +static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode) +{ + int ret; + + hdmi_disable_overflow_interrupts(hdmi); + + hdmi->vic = drm_match_cea_mode(mode); + + if (!hdmi->vic) { + dev_dbg(hdmi->dev, "Non-CEA mode used in HDMI\n"); + hdmi->hdmi_data.video_mode.mdvi = true; + } else { + dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic); + hdmi->hdmi_data.video_mode.mdvi = false; + } + + if ((hdmi->vic == 6) || (hdmi->vic == 7) || + (hdmi->vic == 21) || (hdmi->vic == 22) || + (hdmi->vic == 2) || (hdmi->vic == 3) || + (hdmi->vic == 17) || (hdmi->vic == 18)) + hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601; + else + hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709; + + if ((hdmi->vic == 10) || (hdmi->vic == 11) || + (hdmi->vic == 12) || (hdmi->vic == 13) || + (hdmi->vic == 14) || (hdmi->vic == 15) || + (hdmi->vic == 25) || (hdmi->vic == 26) || + (hdmi->vic == 27) || (hdmi->vic == 28) || + (hdmi->vic == 29) || (hdmi->vic == 30) || + (hdmi->vic == 35) || (hdmi->vic == 36) || + (hdmi->vic == 37) || (hdmi->vic == 38)) + hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1; + else + hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0; + + hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0; + + /* TODO: Get input format from IPU (via FB driver interface) */ + hdmi->hdmi_data.enc_in_format = RGB; + + hdmi->hdmi_data.enc_out_format = RGB; + + hdmi->hdmi_data.enc_color_depth = 8; + hdmi->hdmi_data.pix_repet_factor = 0; + hdmi->hdmi_data.hdcp_enable = 0; + hdmi->hdmi_data.video_mode.mdataenablepolarity = true; + + /* HDMI Initialization Step B.1 */ + hdmi_av_composer(hdmi, mode); + + /* HDMI Initializateion Step B.2 */ + ret = dw_hdmi_phy_init(hdmi); + if (ret) + return ret; + + /* HDMI Initialization Step B.3 */ + dw_hdmi_enable_video_path(hdmi); + + /* not for DVI mode */ + if (hdmi->hdmi_data.video_mode.mdvi) { + dev_dbg(hdmi->dev, "%s DVI mode\n", __func__); + } else { + dev_dbg(hdmi->dev, "%s CEA mode\n", __func__); + + /* HDMI Initialization Step E - Configure audio */ + hdmi_clk_regenerator_update_pixel_clock(hdmi); + hdmi_enable_audio_clk(hdmi); + + /* HDMI Initialization Step F - Configure AVI InfoFrame */ + hdmi_config_AVI(hdmi); + } + + hdmi_video_packetize(hdmi); + hdmi_video_csc(hdmi); + hdmi_video_sample(hdmi); + hdmi_tx_hdcp_config(hdmi); + + dw_hdmi_clear_overflow(hdmi); + if (hdmi->cable_plugin && !hdmi->hdmi_data.video_mode.mdvi) + hdmi_enable_overflow_interrupts(hdmi); + + return 0; +} + +/* Wait until we are registered to enable interrupts */ +static int dw_hdmi_fb_registered(struct dw_hdmi *hdmi) +{ + hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL, + HDMI_PHY_I2CM_INT_ADDR); + + hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL | + HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL, + HDMI_PHY_I2CM_CTLINT_ADDR); + + /* enable cable hot plug irq */ + hdmi_writeb(hdmi, (u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0); + + /* Clear Hotplug interrupts */ + hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); + + return 0; +} + +static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi) +{ + u8 ih_mute; + + /* + * Boot up defaults are: + * HDMI_IH_MUTE = 0x03 (disabled) + * HDMI_IH_MUTE_* = 0x00 (enabled) + * + * Disable top level interrupt bits in HDMI block + */ + ih_mute = hdmi_readb(hdmi, HDMI_IH_MUTE) | + HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT | + HDMI_IH_MUTE_MUTE_ALL_INTERRUPT; + + hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE); + + /* by default mask all interrupts */ + hdmi_writeb(hdmi, 0xff, HDMI_VP_MASK); + hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK0); + hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK1); + hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK2); + hdmi_writeb(hdmi, 0xff, HDMI_PHY_MASK0); + hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_INT_ADDR); + hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_CTLINT_ADDR); + hdmi_writeb(hdmi, 0xff, HDMI_AUD_INT); + hdmi_writeb(hdmi, 0xff, HDMI_AUD_SPDIFINT); + hdmi_writeb(hdmi, 0xff, HDMI_AUD_HBR_MASK); + hdmi_writeb(hdmi, 0xff, HDMI_GP_MASK); + hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK); + hdmi_writeb(hdmi, 0xff, HDMI_CEC_MASK); + hdmi_writeb(hdmi, 0xff, HDMI_I2CM_INT); + hdmi_writeb(hdmi, 0xff, HDMI_I2CM_CTLINT); + + /* Disable interrupts in the IH_MUTE_* registers */ + hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT0); + hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT1); + hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT2); + hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AS_STAT0); + hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_PHY_STAT0); + hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CM_STAT0); + hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_CEC_STAT0); + hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_VP_STAT0); + hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CMPHY_STAT0); + hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0); + + /* Enable top level interrupt bits in HDMI block */ + ih_mute &= ~(HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT | + HDMI_IH_MUTE_MUTE_ALL_INTERRUPT); + hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE); +} + +static void dw_hdmi_poweron(struct dw_hdmi *hdmi) +{ + dw_hdmi_setup(hdmi, &hdmi->previous_mode); +} + +static void dw_hdmi_poweroff(struct dw_hdmi *hdmi) +{ + dw_hdmi_phy_disable(hdmi); +} + +static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct dw_hdmi *hdmi = bridge->driver_private; + + dw_hdmi_setup(hdmi, mode); + + /* Store the display mode for plugin/DKMS poweron events */ + memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); +} + +static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void dw_hdmi_bridge_disable(struct drm_bridge *bridge) +{ + struct dw_hdmi *hdmi = bridge->driver_private; + + dw_hdmi_poweroff(hdmi); +} + +static void dw_hdmi_bridge_enable(struct drm_bridge *bridge) +{ + struct dw_hdmi *hdmi = bridge->driver_private; + + dw_hdmi_poweron(hdmi); +} + +static void dw_hdmi_bridge_destroy(struct drm_bridge *bridge) +{ + drm_bridge_cleanup(bridge); + kfree(bridge); +} + +static void dw_hdmi_bridge_nop(struct drm_bridge *bridge) +{ + /* do nothing */ +} + +static enum drm_connector_status +dw_hdmi_connector_detect(struct drm_connector *connector, bool force) +{ + struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, + connector); + + return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ? + connector_status_connected : connector_status_disconnected; +} + +static int dw_hdmi_connector_get_modes(struct drm_connector *connector) +{ + struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, + connector); + struct edid *edid; + int ret; + + if (!hdmi->ddc) + return 0; + + edid = drm_get_edid(connector, hdmi->ddc); + if (edid) { + dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n", + edid->width_cm, edid->height_cm); + + drm_mode_connector_update_edid_property(connector, edid); + ret = drm_add_edid_modes(connector, edid); + kfree(edid); + } else { + dev_dbg(hdmi->dev, "failed to get edid\n"); + } + + return 0; +} + +static struct drm_encoder *dw_hdmi_connector_best_encoder(struct drm_connector + *connector) +{ + struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, + connector); + + return hdmi->encoder; +} + +static void dw_hdmi_connector_destroy(struct drm_connector *connector) +{ + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + +static struct drm_connector_funcs dw_hdmi_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = dw_hdmi_connector_detect, + .destroy = dw_hdmi_connector_destroy, +}; + +static struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = { + .get_modes = dw_hdmi_connector_get_modes, + .best_encoder = dw_hdmi_connector_best_encoder, +}; + +struct drm_bridge_funcs dw_hdmi_bridge_funcs = { + .enable = dw_hdmi_bridge_enable, + .disable = dw_hdmi_bridge_disable, + .pre_enable = dw_hdmi_bridge_nop, + .post_disable = dw_hdmi_bridge_nop, + .mode_set = dw_hdmi_bridge_mode_set, + .mode_fixup = dw_hdmi_bridge_mode_fixup, + .destroy = dw_hdmi_bridge_destroy, +}; + +static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id) +{ + struct dw_hdmi *hdmi = dev_id; + u8 intr_stat; + + intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0); + if (intr_stat) + hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); + + return intr_stat ? IRQ_WAKE_THREAD : IRQ_NONE; +} + +static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) +{ + struct dw_hdmi *hdmi = dev_id; + u8 intr_stat; + u8 phy_int_pol; + + intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0); + + phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0); + + if (intr_stat & HDMI_IH_PHY_STAT0_HPD) { + if (phy_int_pol & HDMI_PHY_HPD) { + dev_dbg(hdmi->dev, "EVENT=plugin\n"); + + hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0); + + dw_hdmi_poweron(hdmi); + } else { + dev_dbg(hdmi->dev, "EVENT=plugout\n"); + + hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD, + HDMI_PHY_POL0); + + dw_hdmi_poweroff(hdmi); + } + drm_helper_hpd_irq_event(hdmi->connector.dev); + } + + hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0); + hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); + + return IRQ_HANDLED; +} + +static int dw_hdmi_register(struct drm_device *drm, struct dw_hdmi *hdmi) +{ + struct drm_encoder *encoder = hdmi->encoder; + struct drm_bridge *bridge; + int ret; + + bridge = devm_kzalloc(drm->dev, sizeof(*bridge), GFP_KERNEL); + if (!bridge) { + DRM_ERROR("Failed to allocate drm bridge\n"); + return -ENOMEM; + } + + hdmi->bridge = bridge; + bridge->driver_private = hdmi; + + ret = drm_bridge_init(drm, bridge, &dw_hdmi_bridge_funcs); + if (ret) { + DRM_ERROR("Failed to initialize bridge with drm\n"); + return -EINVAL; + } + + encoder->bridge = bridge; + hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD; + + drm_connector_helper_add(&hdmi->connector, + &dw_hdmi_connector_helper_funcs); + drm_connector_init(drm, &hdmi->connector, &dw_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA); + + hdmi->connector.encoder = encoder; + + drm_mode_connector_attach_encoder(&hdmi->connector, encoder); + + return 0; +} + +int dw_hdmi_bind(struct device *dev, struct device *master, + void *data, struct drm_encoder *encoder, + struct resource *iores, int irq, + const struct dw_hdmi_plat_data *plat_data) +{ + struct drm_device *drm = data; + struct device_node *np = dev->of_node; + struct device_node *ddc_node; + struct dw_hdmi *hdmi; + int ret; + + hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); + if (!hdmi) + return -ENOMEM; + + hdmi->plat_data = plat_data; + hdmi->dev = dev; + hdmi->dev_type = plat_data->dev_type; + hdmi->sample_rate = 48000; + hdmi->ratio = 100; + hdmi->encoder = encoder; + + ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); + if (ddc_node) { + hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node); + of_node_put(ddc_node); + if (!hdmi->ddc) { + dev_dbg(hdmi->dev, "failed to read ddc node\n"); + return -EPROBE_DEFER; + } + + } else { + dev_dbg(hdmi->dev, "no ddc property found\n"); + } + + ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq, + dw_hdmi_irq, IRQF_SHARED, + dev_name(dev), hdmi); + if (ret) + return ret; + + hdmi->regs = devm_ioremap_resource(dev, iores); + if (IS_ERR(hdmi->regs)) + return PTR_ERR(hdmi->regs); + + hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr"); + if (IS_ERR(hdmi->isfr_clk)) { + ret = PTR_ERR(hdmi->isfr_clk); + dev_err(hdmi->dev, "Unable to get HDMI isfr clk: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(hdmi->isfr_clk); + if (ret) { + dev_err(hdmi->dev, "Cannot enable HDMI isfr clock: %d\n", ret); + return ret; + } + + hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb"); + if (IS_ERR(hdmi->iahb_clk)) { + ret = PTR_ERR(hdmi->iahb_clk); + dev_err(hdmi->dev, "Unable to get HDMI iahb clk: %d\n", ret); + goto err_isfr; + } + + ret = clk_prepare_enable(hdmi->iahb_clk); + if (ret) { + dev_err(hdmi->dev, "Cannot enable HDMI iahb clock: %d\n", ret); + goto err_isfr; + } + + /* Product and revision IDs */ + dev_info(dev, + "Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n", + hdmi_readb(hdmi, HDMI_DESIGN_ID), + hdmi_readb(hdmi, HDMI_REVISION_ID), + hdmi_readb(hdmi, HDMI_PRODUCT_ID0), + hdmi_readb(hdmi, HDMI_PRODUCT_ID1)); + + initialize_hdmi_ih_mutes(hdmi); + + /* + * To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator + * N and cts values before enabling phy + */ + hdmi_init_clk_regenerator(hdmi); + + /* + * Configure registers related to HDMI interrupt + * generation before registering IRQ. + */ + hdmi_writeb(hdmi, HDMI_PHY_HPD, HDMI_PHY_POL0); + + /* Clear Hotplug interrupts */ + hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); + + ret = dw_hdmi_fb_registered(hdmi); + if (ret) + goto err_iahb; + + ret = dw_hdmi_register(drm, hdmi); + if (ret) + goto err_iahb; + + /* Unmute interrupts */ + hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); + + dev_set_drvdata(dev, hdmi); + + return 0; + +err_iahb: + clk_disable_unprepare(hdmi->iahb_clk); +err_isfr: + clk_disable_unprepare(hdmi->isfr_clk); + + return ret; +} +EXPORT_SYMBOL_GPL(dw_hdmi_bind); + +void dw_hdmi_unbind(struct device *dev, struct device *master, void *data) +{ + struct dw_hdmi *hdmi = dev_get_drvdata(dev); + + /* Disable all interrupts */ + hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); + + hdmi->connector.funcs->destroy(&hdmi->connector); + hdmi->encoder->funcs->destroy(hdmi->encoder); + + clk_disable_unprepare(hdmi->iahb_clk); + clk_disable_unprepare(hdmi->isfr_clk); + i2c_put_adapter(hdmi->ddc); +} +EXPORT_SYMBOL_GPL(dw_hdmi_unbind); + +MODULE_AUTHOR("Sascha Hauer "); +MODULE_AUTHOR("Andy Yan "); +MODULE_AUTHOR("Yakir Yang "); +MODULE_DESCRIPTION("DW HDMI transmitter driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:dw-hdmi"); diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h new file mode 100644 index 000000000000..baa7849851d4 --- /dev/null +++ b/drivers/gpu/drm/bridge/dw_hdmi.h @@ -0,0 +1,1033 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __IMX_HDMI_H__ +#define __IMX_HDMI_H__ + +/* Identification Registers */ +#define HDMI_DESIGN_ID 0x0000 +#define HDMI_REVISION_ID 0x0001 +#define HDMI_PRODUCT_ID0 0x0002 +#define HDMI_PRODUCT_ID1 0x0003 +#define HDMI_CONFIG0_ID 0x0004 +#define HDMI_CONFIG1_ID 0x0005 +#define HDMI_CONFIG2_ID 0x0006 +#define HDMI_CONFIG3_ID 0x0007 + +/* Interrupt Registers */ +#define HDMI_IH_FC_STAT0 0x0100 +#define HDMI_IH_FC_STAT1 0x0101 +#define HDMI_IH_FC_STAT2 0x0102 +#define HDMI_IH_AS_STAT0 0x0103 +#define HDMI_IH_PHY_STAT0 0x0104 +#define HDMI_IH_I2CM_STAT0 0x0105 +#define HDMI_IH_CEC_STAT0 0x0106 +#define HDMI_IH_VP_STAT0 0x0107 +#define HDMI_IH_I2CMPHY_STAT0 0x0108 +#define HDMI_IH_AHBDMAAUD_STAT0 0x0109 + +#define HDMI_IH_MUTE_FC_STAT0 0x0180 +#define HDMI_IH_MUTE_FC_STAT1 0x0181 +#define HDMI_IH_MUTE_FC_STAT2 0x0182 +#define HDMI_IH_MUTE_AS_STAT0 0x0183 +#define HDMI_IH_MUTE_PHY_STAT0 0x0184 +#define HDMI_IH_MUTE_I2CM_STAT0 0x0185 +#define HDMI_IH_MUTE_CEC_STAT0 0x0186 +#define HDMI_IH_MUTE_VP_STAT0 0x0187 +#define HDMI_IH_MUTE_I2CMPHY_STAT0 0x0188 +#define HDMI_IH_MUTE_AHBDMAAUD_STAT0 0x0189 +#define HDMI_IH_MUTE 0x01FF + +/* Video Sample Registers */ +#define HDMI_TX_INVID0 0x0200 +#define HDMI_TX_INSTUFFING 0x0201 +#define HDMI_TX_GYDATA0 0x0202 +#define HDMI_TX_GYDATA1 0x0203 +#define HDMI_TX_RCRDATA0 0x0204 +#define HDMI_TX_RCRDATA1 0x0205 +#define HDMI_TX_BCBDATA0 0x0206 +#define HDMI_TX_BCBDATA1 0x0207 + +/* Video Packetizer Registers */ +#define HDMI_VP_STATUS 0x0800 +#define HDMI_VP_PR_CD 0x0801 +#define HDMI_VP_STUFF 0x0802 +#define HDMI_VP_REMAP 0x0803 +#define HDMI_VP_CONF 0x0804 +#define HDMI_VP_STAT 0x0805 +#define HDMI_VP_INT 0x0806 +#define HDMI_VP_MASK 0x0807 +#define HDMI_VP_POL 0x0808 + +/* Frame Composer Registers */ +#define HDMI_FC_INVIDCONF 0x1000 +#define HDMI_FC_INHACTV0 0x1001 +#define HDMI_FC_INHACTV1 0x1002 +#define HDMI_FC_INHBLANK0 0x1003 +#define HDMI_FC_INHBLANK1 0x1004 +#define HDMI_FC_INVACTV0 0x1005 +#define HDMI_FC_INVACTV1 0x1006 +#define HDMI_FC_INVBLANK 0x1007 +#define HDMI_FC_HSYNCINDELAY0 0x1008 +#define HDMI_FC_HSYNCINDELAY1 0x1009 +#define HDMI_FC_HSYNCINWIDTH0 0x100A +#define HDMI_FC_HSYNCINWIDTH1 0x100B +#define HDMI_FC_VSYNCINDELAY 0x100C +#define HDMI_FC_VSYNCINWIDTH 0x100D +#define HDMI_FC_INFREQ0 0x100E +#define HDMI_FC_INFREQ1 0x100F +#define HDMI_FC_INFREQ2 0x1010 +#define HDMI_FC_CTRLDUR 0x1011 +#define HDMI_FC_EXCTRLDUR 0x1012 +#define HDMI_FC_EXCTRLSPAC 0x1013 +#define HDMI_FC_CH0PREAM 0x1014 +#define HDMI_FC_CH1PREAM 0x1015 +#define HDMI_FC_CH2PREAM 0x1016 +#define HDMI_FC_AVICONF3 0x1017 +#define HDMI_FC_GCP 0x1018 +#define HDMI_FC_AVICONF0 0x1019 +#define HDMI_FC_AVICONF1 0x101A +#define HDMI_FC_AVICONF2 0x101B +#define HDMI_FC_AVIVID 0x101C +#define HDMI_FC_AVIETB0 0x101D +#define HDMI_FC_AVIETB1 0x101E +#define HDMI_FC_AVISBB0 0x101F +#define HDMI_FC_AVISBB1 0x1020 +#define HDMI_FC_AVIELB0 0x1021 +#define HDMI_FC_AVIELB1 0x1022 +#define HDMI_FC_AVISRB0 0x1023 +#define HDMI_FC_AVISRB1 0x1024 +#define HDMI_FC_AUDICONF0 0x1025 +#define HDMI_FC_AUDICONF1 0x1026 +#define HDMI_FC_AUDICONF2 0x1027 +#define HDMI_FC_AUDICONF3 0x1028 +#define HDMI_FC_VSDIEEEID0 0x1029 +#define HDMI_FC_VSDSIZE 0x102A +#define HDMI_FC_VSDIEEEID1 0x1030 +#define HDMI_FC_VSDIEEEID2 0x1031 +#define HDMI_FC_VSDPAYLOAD0 0x1032 +#define HDMI_FC_VSDPAYLOAD1 0x1033 +#define HDMI_FC_VSDPAYLOAD2 0x1034 +#define HDMI_FC_VSDPAYLOAD3 0x1035 +#define HDMI_FC_VSDPAYLOAD4 0x1036 +#define HDMI_FC_VSDPAYLOAD5 0x1037 +#define HDMI_FC_VSDPAYLOAD6 0x1038 +#define HDMI_FC_VSDPAYLOAD7 0x1039 +#define HDMI_FC_VSDPAYLOAD8 0x103A +#define HDMI_FC_VSDPAYLOAD9 0x103B +#define HDMI_FC_VSDPAYLOAD10 0x103C +#define HDMI_FC_VSDPAYLOAD11 0x103D +#define HDMI_FC_VSDPAYLOAD12 0x103E +#define HDMI_FC_VSDPAYLOAD13 0x103F +#define HDMI_FC_VSDPAYLOAD14 0x1040 +#define HDMI_FC_VSDPAYLOAD15 0x1041 +#define HDMI_FC_VSDPAYLOAD16 0x1042 +#define HDMI_FC_VSDPAYLOAD17 0x1043 +#define HDMI_FC_VSDPAYLOAD18 0x1044 +#define HDMI_FC_VSDPAYLOAD19 0x1045 +#define HDMI_FC_VSDPAYLOAD20 0x1046 +#define HDMI_FC_VSDPAYLOAD21 0x1047 +#define HDMI_FC_VSDPAYLOAD22 0x1048 +#define HDMI_FC_VSDPAYLOAD23 0x1049 +#define HDMI_FC_SPDVENDORNAME0 0x104A +#define HDMI_FC_SPDVENDORNAME1 0x104B +#define HDMI_FC_SPDVENDORNAME2 0x104C +#define HDMI_FC_SPDVENDORNAME3 0x104D +#define HDMI_FC_SPDVENDORNAME4 0x104E +#define HDMI_FC_SPDVENDORNAME5 0x104F +#define HDMI_FC_SPDVENDORNAME6 0x1050 +#define HDMI_FC_SPDVENDORNAME7 0x1051 +#define HDMI_FC_SDPPRODUCTNAME0 0x1052 +#define HDMI_FC_SDPPRODUCTNAME1 0x1053 +#define HDMI_FC_SDPPRODUCTNAME2 0x1054 +#define HDMI_FC_SDPPRODUCTNAME3 0x1055 +#define HDMI_FC_SDPPRODUCTNAME4 0x1056 +#define HDMI_FC_SDPPRODUCTNAME5 0x1057 +#define HDMI_FC_SDPPRODUCTNAME6 0x1058 +#define HDMI_FC_SDPPRODUCTNAME7 0x1059 +#define HDMI_FC_SDPPRODUCTNAME8 0x105A +#define HDMI_FC_SDPPRODUCTNAME9 0x105B +#define HDMI_FC_SDPPRODUCTNAME10 0x105C +#define HDMI_FC_SDPPRODUCTNAME11 0x105D +#define HDMI_FC_SDPPRODUCTNAME12 0x105E +#define HDMI_FC_SDPPRODUCTNAME13 0x105F +#define HDMI_FC_SDPPRODUCTNAME14 0x1060 +#define HDMI_FC_SPDPRODUCTNAME15 0x1061 +#define HDMI_FC_SPDDEVICEINF 0x1062 +#define HDMI_FC_AUDSCONF 0x1063 +#define HDMI_FC_AUDSSTAT 0x1064 +#define HDMI_FC_DATACH0FILL 0x1070 +#define HDMI_FC_DATACH1FILL 0x1071 +#define HDMI_FC_DATACH2FILL 0x1072 +#define HDMI_FC_CTRLQHIGH 0x1073 +#define HDMI_FC_CTRLQLOW 0x1074 +#define HDMI_FC_ACP0 0x1075 +#define HDMI_FC_ACP28 0x1076 +#define HDMI_FC_ACP27 0x1077 +#define HDMI_FC_ACP26 0x1078 +#define HDMI_FC_ACP25 0x1079 +#define HDMI_FC_ACP24 0x107A +#define HDMI_FC_ACP23 0x107B +#define HDMI_FC_ACP22 0x107C +#define HDMI_FC_ACP21 0x107D +#define HDMI_FC_ACP20 0x107E +#define HDMI_FC_ACP19 0x107F +#define HDMI_FC_ACP18 0x1080 +#define HDMI_FC_ACP17 0x1081 +#define HDMI_FC_ACP16 0x1082 +#define HDMI_FC_ACP15 0x1083 +#define HDMI_FC_ACP14 0x1084 +#define HDMI_FC_ACP13 0x1085 +#define HDMI_FC_ACP12 0x1086 +#define HDMI_FC_ACP11 0x1087 +#define HDMI_FC_ACP10 0x1088 +#define HDMI_FC_ACP9 0x1089 +#define HDMI_FC_ACP8 0x108A +#define HDMI_FC_ACP7 0x108B +#define HDMI_FC_ACP6 0x108C +#define HDMI_FC_ACP5 0x108D +#define HDMI_FC_ACP4 0x108E +#define HDMI_FC_ACP3 0x108F +#define HDMI_FC_ACP2 0x1090 +#define HDMI_FC_ACP1 0x1091 +#define HDMI_FC_ISCR1_0 0x1092 +#define HDMI_FC_ISCR1_16 0x1093 +#define HDMI_FC_ISCR1_15 0x1094 +#define HDMI_FC_ISCR1_14 0x1095 +#define HDMI_FC_ISCR1_13 0x1096 +#define HDMI_FC_ISCR1_12 0x1097 +#define HDMI_FC_ISCR1_11 0x1098 +#define HDMI_FC_ISCR1_10 0x1099 +#define HDMI_FC_ISCR1_9 0x109A +#define HDMI_FC_ISCR1_8 0x109B +#define HDMI_FC_ISCR1_7 0x109C +#define HDMI_FC_ISCR1_6 0x109D +#define HDMI_FC_ISCR1_5 0x109E +#define HDMI_FC_ISCR1_4 0x109F +#define HDMI_FC_ISCR1_3 0x10A0 +#define HDMI_FC_ISCR1_2 0x10A1 +#define HDMI_FC_ISCR1_1 0x10A2 +#define HDMI_FC_ISCR2_15 0x10A3 +#define HDMI_FC_ISCR2_14 0x10A4 +#define HDMI_FC_ISCR2_13 0x10A5 +#define HDMI_FC_ISCR2_12 0x10A6 +#define HDMI_FC_ISCR2_11 0x10A7 +#define HDMI_FC_ISCR2_10 0x10A8 +#define HDMI_FC_ISCR2_9 0x10A9 +#define HDMI_FC_ISCR2_8 0x10AA +#define HDMI_FC_ISCR2_7 0x10AB +#define HDMI_FC_ISCR2_6 0x10AC +#define HDMI_FC_ISCR2_5 0x10AD +#define HDMI_FC_ISCR2_4 0x10AE +#define HDMI_FC_ISCR2_3 0x10AF +#define HDMI_FC_ISCR2_2 0x10B0 +#define HDMI_FC_ISCR2_1 0x10B1 +#define HDMI_FC_ISCR2_0 0x10B2 +#define HDMI_FC_DATAUTO0 0x10B3 +#define HDMI_FC_DATAUTO1 0x10B4 +#define HDMI_FC_DATAUTO2 0x10B5 +#define HDMI_FC_DATMAN 0x10B6 +#define HDMI_FC_DATAUTO3 0x10B7 +#define HDMI_FC_RDRB0 0x10B8 +#define HDMI_FC_RDRB1 0x10B9 +#define HDMI_FC_RDRB2 0x10BA +#define HDMI_FC_RDRB3 0x10BB +#define HDMI_FC_RDRB4 0x10BC +#define HDMI_FC_RDRB5 0x10BD +#define HDMI_FC_RDRB6 0x10BE +#define HDMI_FC_RDRB7 0x10BF +#define HDMI_FC_STAT0 0x10D0 +#define HDMI_FC_INT0 0x10D1 +#define HDMI_FC_MASK0 0x10D2 +#define HDMI_FC_POL0 0x10D3 +#define HDMI_FC_STAT1 0x10D4 +#define HDMI_FC_INT1 0x10D5 +#define HDMI_FC_MASK1 0x10D6 +#define HDMI_FC_POL1 0x10D7 +#define HDMI_FC_STAT2 0x10D8 +#define HDMI_FC_INT2 0x10D9 +#define HDMI_FC_MASK2 0x10DA +#define HDMI_FC_POL2 0x10DB +#define HDMI_FC_PRCONF 0x10E0 + +#define HDMI_FC_GMD_STAT 0x1100 +#define HDMI_FC_GMD_EN 0x1101 +#define HDMI_FC_GMD_UP 0x1102 +#define HDMI_FC_GMD_CONF 0x1103 +#define HDMI_FC_GMD_HB 0x1104 +#define HDMI_FC_GMD_PB0 0x1105 +#define HDMI_FC_GMD_PB1 0x1106 +#define HDMI_FC_GMD_PB2 0x1107 +#define HDMI_FC_GMD_PB3 0x1108 +#define HDMI_FC_GMD_PB4 0x1109 +#define HDMI_FC_GMD_PB5 0x110A +#define HDMI_FC_GMD_PB6 0x110B +#define HDMI_FC_GMD_PB7 0x110C +#define HDMI_FC_GMD_PB8 0x110D +#define HDMI_FC_GMD_PB9 0x110E +#define HDMI_FC_GMD_PB10 0x110F +#define HDMI_FC_GMD_PB11 0x1110 +#define HDMI_FC_GMD_PB12 0x1111 +#define HDMI_FC_GMD_PB13 0x1112 +#define HDMI_FC_GMD_PB14 0x1113 +#define HDMI_FC_GMD_PB15 0x1114 +#define HDMI_FC_GMD_PB16 0x1115 +#define HDMI_FC_GMD_PB17 0x1116 +#define HDMI_FC_GMD_PB18 0x1117 +#define HDMI_FC_GMD_PB19 0x1118 +#define HDMI_FC_GMD_PB20 0x1119 +#define HDMI_FC_GMD_PB21 0x111A +#define HDMI_FC_GMD_PB22 0x111B +#define HDMI_FC_GMD_PB23 0x111C +#define HDMI_FC_GMD_PB24 0x111D +#define HDMI_FC_GMD_PB25 0x111E +#define HDMI_FC_GMD_PB26 0x111F +#define HDMI_FC_GMD_PB27 0x1120 + +#define HDMI_FC_DBGFORCE 0x1200 +#define HDMI_FC_DBGAUD0CH0 0x1201 +#define HDMI_FC_DBGAUD1CH0 0x1202 +#define HDMI_FC_DBGAUD2CH0 0x1203 +#define HDMI_FC_DBGAUD0CH1 0x1204 +#define HDMI_FC_DBGAUD1CH1 0x1205 +#define HDMI_FC_DBGAUD2CH1 0x1206 +#define HDMI_FC_DBGAUD0CH2 0x1207 +#define HDMI_FC_DBGAUD1CH2 0x1208 +#define HDMI_FC_DBGAUD2CH2 0x1209 +#define HDMI_FC_DBGAUD0CH3 0x120A +#define HDMI_FC_DBGAUD1CH3 0x120B +#define HDMI_FC_DBGAUD2CH3 0x120C +#define HDMI_FC_DBGAUD0CH4 0x120D +#define HDMI_FC_DBGAUD1CH4 0x120E +#define HDMI_FC_DBGAUD2CH4 0x120F +#define HDMI_FC_DBGAUD0CH5 0x1210 +#define HDMI_FC_DBGAUD1CH5 0x1211 +#define HDMI_FC_DBGAUD2CH5 0x1212 +#define HDMI_FC_DBGAUD0CH6 0x1213 +#define HDMI_FC_DBGAUD1CH6 0x1214 +#define HDMI_FC_DBGAUD2CH6 0x1215 +#define HDMI_FC_DBGAUD0CH7 0x1216 +#define HDMI_FC_DBGAUD1CH7 0x1217 +#define HDMI_FC_DBGAUD2CH7 0x1218 +#define HDMI_FC_DBGTMDS0 0x1219 +#define HDMI_FC_DBGTMDS1 0x121A +#define HDMI_FC_DBGTMDS2 0x121B + +/* HDMI Source PHY Registers */ +#define HDMI_PHY_CONF0 0x3000 +#define HDMI_PHY_TST0 0x3001 +#define HDMI_PHY_TST1 0x3002 +#define HDMI_PHY_TST2 0x3003 +#define HDMI_PHY_STAT0 0x3004 +#define HDMI_PHY_INT0 0x3005 +#define HDMI_PHY_MASK0 0x3006 +#define HDMI_PHY_POL0 0x3007 + +/* HDMI Master PHY Registers */ +#define HDMI_PHY_I2CM_SLAVE_ADDR 0x3020 +#define HDMI_PHY_I2CM_ADDRESS_ADDR 0x3021 +#define HDMI_PHY_I2CM_DATAO_1_ADDR 0x3022 +#define HDMI_PHY_I2CM_DATAO_0_ADDR 0x3023 +#define HDMI_PHY_I2CM_DATAI_1_ADDR 0x3024 +#define HDMI_PHY_I2CM_DATAI_0_ADDR 0x3025 +#define HDMI_PHY_I2CM_OPERATION_ADDR 0x3026 +#define HDMI_PHY_I2CM_INT_ADDR 0x3027 +#define HDMI_PHY_I2CM_CTLINT_ADDR 0x3028 +#define HDMI_PHY_I2CM_DIV_ADDR 0x3029 +#define HDMI_PHY_I2CM_SOFTRSTZ_ADDR 0x302a +#define HDMI_PHY_I2CM_SS_SCL_HCNT_1_ADDR 0x302b +#define HDMI_PHY_I2CM_SS_SCL_HCNT_0_ADDR 0x302c +#define HDMI_PHY_I2CM_SS_SCL_LCNT_1_ADDR 0x302d +#define HDMI_PHY_I2CM_SS_SCL_LCNT_0_ADDR 0x302e +#define HDMI_PHY_I2CM_FS_SCL_HCNT_1_ADDR 0x302f +#define HDMI_PHY_I2CM_FS_SCL_HCNT_0_ADDR 0x3030 +#define HDMI_PHY_I2CM_FS_SCL_LCNT_1_ADDR 0x3031 +#define HDMI_PHY_I2CM_FS_SCL_LCNT_0_ADDR 0x3032 + +/* Audio Sampler Registers */ +#define HDMI_AUD_CONF0 0x3100 +#define HDMI_AUD_CONF1 0x3101 +#define HDMI_AUD_INT 0x3102 +#define HDMI_AUD_CONF2 0x3103 +#define HDMI_AUD_N1 0x3200 +#define HDMI_AUD_N2 0x3201 +#define HDMI_AUD_N3 0x3202 +#define HDMI_AUD_CTS1 0x3203 +#define HDMI_AUD_CTS2 0x3204 +#define HDMI_AUD_CTS3 0x3205 +#define HDMI_AUD_INPUTCLKFS 0x3206 +#define HDMI_AUD_SPDIFINT 0x3302 +#define HDMI_AUD_CONF0_HBR 0x3400 +#define HDMI_AUD_HBR_STATUS 0x3401 +#define HDMI_AUD_HBR_INT 0x3402 +#define HDMI_AUD_HBR_POL 0x3403 +#define HDMI_AUD_HBR_MASK 0x3404 + +/* + * Generic Parallel Audio Interface Registers + * Not used as GPAUD interface is not enabled in hw + */ +#define HDMI_GP_CONF0 0x3500 +#define HDMI_GP_CONF1 0x3501 +#define HDMI_GP_CONF2 0x3502 +#define HDMI_GP_STAT 0x3503 +#define HDMI_GP_INT 0x3504 +#define HDMI_GP_MASK 0x3505 +#define HDMI_GP_POL 0x3506 + +/* Audio DMA Registers */ +#define HDMI_AHB_DMA_CONF0 0x3600 +#define HDMI_AHB_DMA_START 0x3601 +#define HDMI_AHB_DMA_STOP 0x3602 +#define HDMI_AHB_DMA_THRSLD 0x3603 +#define HDMI_AHB_DMA_STRADDR0 0x3604 +#define HDMI_AHB_DMA_STRADDR1 0x3605 +#define HDMI_AHB_DMA_STRADDR2 0x3606 +#define HDMI_AHB_DMA_STRADDR3 0x3607 +#define HDMI_AHB_DMA_STPADDR0 0x3608 +#define HDMI_AHB_DMA_STPADDR1 0x3609 +#define HDMI_AHB_DMA_STPADDR2 0x360a +#define HDMI_AHB_DMA_STPADDR3 0x360b +#define HDMI_AHB_DMA_BSTADDR0 0x360c +#define HDMI_AHB_DMA_BSTADDR1 0x360d +#define HDMI_AHB_DMA_BSTADDR2 0x360e +#define HDMI_AHB_DMA_BSTADDR3 0x360f +#define HDMI_AHB_DMA_MBLENGTH0 0x3610 +#define HDMI_AHB_DMA_MBLENGTH1 0x3611 +#define HDMI_AHB_DMA_STAT 0x3612 +#define HDMI_AHB_DMA_INT 0x3613 +#define HDMI_AHB_DMA_MASK 0x3614 +#define HDMI_AHB_DMA_POL 0x3615 +#define HDMI_AHB_DMA_CONF1 0x3616 +#define HDMI_AHB_DMA_BUFFSTAT 0x3617 +#define HDMI_AHB_DMA_BUFFINT 0x3618 +#define HDMI_AHB_DMA_BUFFMASK 0x3619 +#define HDMI_AHB_DMA_BUFFPOL 0x361a + +/* Main Controller Registers */ +#define HDMI_MC_SFRDIV 0x4000 +#define HDMI_MC_CLKDIS 0x4001 +#define HDMI_MC_SWRSTZ 0x4002 +#define HDMI_MC_OPCTRL 0x4003 +#define HDMI_MC_FLOWCTRL 0x4004 +#define HDMI_MC_PHYRSTZ 0x4005 +#define HDMI_MC_LOCKONCLOCK 0x4006 +#define HDMI_MC_HEACPHY_RST 0x4007 + +/* Color Space Converter Registers */ +#define HDMI_CSC_CFG 0x4100 +#define HDMI_CSC_SCALE 0x4101 +#define HDMI_CSC_COEF_A1_MSB 0x4102 +#define HDMI_CSC_COEF_A1_LSB 0x4103 +#define HDMI_CSC_COEF_A2_MSB 0x4104 +#define HDMI_CSC_COEF_A2_LSB 0x4105 +#define HDMI_CSC_COEF_A3_MSB 0x4106 +#define HDMI_CSC_COEF_A3_LSB 0x4107 +#define HDMI_CSC_COEF_A4_MSB 0x4108 +#define HDMI_CSC_COEF_A4_LSB 0x4109 +#define HDMI_CSC_COEF_B1_MSB 0x410A +#define HDMI_CSC_COEF_B1_LSB 0x410B +#define HDMI_CSC_COEF_B2_MSB 0x410C +#define HDMI_CSC_COEF_B2_LSB 0x410D +#define HDMI_CSC_COEF_B3_MSB 0x410E +#define HDMI_CSC_COEF_B3_LSB 0x410F +#define HDMI_CSC_COEF_B4_MSB 0x4110 +#define HDMI_CSC_COEF_B4_LSB 0x4111 +#define HDMI_CSC_COEF_C1_MSB 0x4112 +#define HDMI_CSC_COEF_C1_LSB 0x4113 +#define HDMI_CSC_COEF_C2_MSB 0x4114 +#define HDMI_CSC_COEF_C2_LSB 0x4115 +#define HDMI_CSC_COEF_C3_MSB 0x4116 +#define HDMI_CSC_COEF_C3_LSB 0x4117 +#define HDMI_CSC_COEF_C4_MSB 0x4118 +#define HDMI_CSC_COEF_C4_LSB 0x4119 + +/* HDCP Encryption Engine Registers */ +#define HDMI_A_HDCPCFG0 0x5000 +#define HDMI_A_HDCPCFG1 0x5001 +#define HDMI_A_HDCPOBS0 0x5002 +#define HDMI_A_HDCPOBS1 0x5003 +#define HDMI_A_HDCPOBS2 0x5004 +#define HDMI_A_HDCPOBS3 0x5005 +#define HDMI_A_APIINTCLR 0x5006 +#define HDMI_A_APIINTSTAT 0x5007 +#define HDMI_A_APIINTMSK 0x5008 +#define HDMI_A_VIDPOLCFG 0x5009 +#define HDMI_A_OESSWCFG 0x500A +#define HDMI_A_TIMER1SETUP0 0x500B +#define HDMI_A_TIMER1SETUP1 0x500C +#define HDMI_A_TIMER2SETUP0 0x500D +#define HDMI_A_TIMER2SETUP1 0x500E +#define HDMI_A_100MSCFG 0x500F +#define HDMI_A_2SCFG0 0x5010 +#define HDMI_A_2SCFG1 0x5011 +#define HDMI_A_5SCFG0 0x5012 +#define HDMI_A_5SCFG1 0x5013 +#define HDMI_A_SRMVERLSB 0x5014 +#define HDMI_A_SRMVERMSB 0x5015 +#define HDMI_A_SRMCTRL 0x5016 +#define HDMI_A_SFRSETUP 0x5017 +#define HDMI_A_I2CHSETUP 0x5018 +#define HDMI_A_INTSETUP 0x5019 +#define HDMI_A_PRESETUP 0x501A +#define HDMI_A_SRM_BASE 0x5020 + +/* CEC Engine Registers */ +#define HDMI_CEC_CTRL 0x7D00 +#define HDMI_CEC_STAT 0x7D01 +#define HDMI_CEC_MASK 0x7D02 +#define HDMI_CEC_POLARITY 0x7D03 +#define HDMI_CEC_INT 0x7D04 +#define HDMI_CEC_ADDR_L 0x7D05 +#define HDMI_CEC_ADDR_H 0x7D06 +#define HDMI_CEC_TX_CNT 0x7D07 +#define HDMI_CEC_RX_CNT 0x7D08 +#define HDMI_CEC_TX_DATA0 0x7D10 +#define HDMI_CEC_TX_DATA1 0x7D11 +#define HDMI_CEC_TX_DATA2 0x7D12 +#define HDMI_CEC_TX_DATA3 0x7D13 +#define HDMI_CEC_TX_DATA4 0x7D14 +#define HDMI_CEC_TX_DATA5 0x7D15 +#define HDMI_CEC_TX_DATA6 0x7D16 +#define HDMI_CEC_TX_DATA7 0x7D17 +#define HDMI_CEC_TX_DATA8 0x7D18 +#define HDMI_CEC_TX_DATA9 0x7D19 +#define HDMI_CEC_TX_DATA10 0x7D1a +#define HDMI_CEC_TX_DATA11 0x7D1b +#define HDMI_CEC_TX_DATA12 0x7D1c +#define HDMI_CEC_TX_DATA13 0x7D1d +#define HDMI_CEC_TX_DATA14 0x7D1e +#define HDMI_CEC_TX_DATA15 0x7D1f +#define HDMI_CEC_RX_DATA0 0x7D20 +#define HDMI_CEC_RX_DATA1 0x7D21 +#define HDMI_CEC_RX_DATA2 0x7D22 +#define HDMI_CEC_RX_DATA3 0x7D23 +#define HDMI_CEC_RX_DATA4 0x7D24 +#define HDMI_CEC_RX_DATA5 0x7D25 +#define HDMI_CEC_RX_DATA6 0x7D26 +#define HDMI_CEC_RX_DATA7 0x7D27 +#define HDMI_CEC_RX_DATA8 0x7D28 +#define HDMI_CEC_RX_DATA9 0x7D29 +#define HDMI_CEC_RX_DATA10 0x7D2a +#define HDMI_CEC_RX_DATA11 0x7D2b +#define HDMI_CEC_RX_DATA12 0x7D2c +#define HDMI_CEC_RX_DATA13 0x7D2d +#define HDMI_CEC_RX_DATA14 0x7D2e +#define HDMI_CEC_RX_DATA15 0x7D2f +#define HDMI_CEC_LOCK 0x7D30 +#define HDMI_CEC_WKUPCTRL 0x7D31 + +/* I2C Master Registers (E-DDC) */ +#define HDMI_I2CM_SLAVE 0x7E00 +#define HDMI_I2CMESS 0x7E01 +#define HDMI_I2CM_DATAO 0x7E02 +#define HDMI_I2CM_DATAI 0x7E03 +#define HDMI_I2CM_OPERATION 0x7E04 +#define HDMI_I2CM_INT 0x7E05 +#define HDMI_I2CM_CTLINT 0x7E06 +#define HDMI_I2CM_DIV 0x7E07 +#define HDMI_I2CM_SEGADDR 0x7E08 +#define HDMI_I2CM_SOFTRSTZ 0x7E09 +#define HDMI_I2CM_SEGPTR 0x7E0A +#define HDMI_I2CM_SS_SCL_HCNT_1_ADDR 0x7E0B +#define HDMI_I2CM_SS_SCL_HCNT_0_ADDR 0x7E0C +#define HDMI_I2CM_SS_SCL_LCNT_1_ADDR 0x7E0D +#define HDMI_I2CM_SS_SCL_LCNT_0_ADDR 0x7E0E +#define HDMI_I2CM_FS_SCL_HCNT_1_ADDR 0x7E0F +#define HDMI_I2CM_FS_SCL_HCNT_0_ADDR 0x7E10 +#define HDMI_I2CM_FS_SCL_LCNT_1_ADDR 0x7E11 +#define HDMI_I2CM_FS_SCL_LCNT_0_ADDR 0x7E12 + +enum { +/* IH_FC_INT2 field values */ + HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03, + HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02, + HDMI_IH_FC_INT2_HIGH_PRIORITY_OVERFLOW = 0x01, + +/* IH_FC_STAT2 field values */ + HDMI_IH_FC_STAT2_OVERFLOW_MASK = 0x03, + HDMI_IH_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02, + HDMI_IH_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01, + +/* IH_PHY_STAT0 field values */ + HDMI_IH_PHY_STAT0_RX_SENSE3 = 0x20, + HDMI_IH_PHY_STAT0_RX_SENSE2 = 0x10, + HDMI_IH_PHY_STAT0_RX_SENSE1 = 0x8, + HDMI_IH_PHY_STAT0_RX_SENSE0 = 0x4, + HDMI_IH_PHY_STAT0_TX_PHY_LOCK = 0x2, + HDMI_IH_PHY_STAT0_HPD = 0x1, + +/* IH_MUTE_I2CMPHY_STAT0 field values */ + HDMI_IH_MUTE_I2CMPHY_STAT0_I2CMPHYDONE = 0x2, + HDMI_IH_MUTE_I2CMPHY_STAT0_I2CMPHYERROR = 0x1, + +/* IH_AHBDMAAUD_STAT0 field values */ + HDMI_IH_AHBDMAAUD_STAT0_ERROR = 0x20, + HDMI_IH_AHBDMAAUD_STAT0_LOST = 0x10, + HDMI_IH_AHBDMAAUD_STAT0_RETRY = 0x08, + HDMI_IH_AHBDMAAUD_STAT0_DONE = 0x04, + HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = 0x02, + HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = 0x01, + +/* IH_MUTE_FC_STAT2 field values */ + HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK = 0x03, + HDMI_IH_MUTE_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02, + HDMI_IH_MUTE_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01, + +/* IH_MUTE_AHBDMAAUD_STAT0 field values */ + HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = 0x20, + HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = 0x10, + HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = 0x08, + HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = 0x04, + HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = 0x02, + HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = 0x01, + +/* IH_MUTE field values */ + HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT = 0x2, + HDMI_IH_MUTE_MUTE_ALL_INTERRUPT = 0x1, + +/* TX_INVID0 field values */ + HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_MASK = 0x80, + HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_ENABLE = 0x80, + HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE = 0x00, + HDMI_TX_INVID0_VIDEO_MAPPING_MASK = 0x1F, + HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET = 0, + +/* TX_INSTUFFING field values */ + HDMI_TX_INSTUFFING_BDBDATA_STUFFING_MASK = 0x4, + HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE = 0x4, + HDMI_TX_INSTUFFING_BDBDATA_STUFFING_DISABLE = 0x0, + HDMI_TX_INSTUFFING_RCRDATA_STUFFING_MASK = 0x2, + HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE = 0x2, + HDMI_TX_INSTUFFING_RCRDATA_STUFFING_DISABLE = 0x0, + HDMI_TX_INSTUFFING_GYDATA_STUFFING_MASK = 0x1, + HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE = 0x1, + HDMI_TX_INSTUFFING_GYDATA_STUFFING_DISABLE = 0x0, + +/* VP_PR_CD field values */ + HDMI_VP_PR_CD_COLOR_DEPTH_MASK = 0xF0, + HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET = 4, + HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK = 0x0F, + HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET = 0, + +/* VP_STUFF field values */ + HDMI_VP_STUFF_IDEFAULT_PHASE_MASK = 0x20, + HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET = 5, + HDMI_VP_STUFF_IFIX_PP_TO_LAST_MASK = 0x10, + HDMI_VP_STUFF_IFIX_PP_TO_LAST_OFFSET = 4, + HDMI_VP_STUFF_ICX_GOTO_P0_ST_MASK = 0x8, + HDMI_VP_STUFF_ICX_GOTO_P0_ST_OFFSET = 3, + HDMI_VP_STUFF_YCC422_STUFFING_MASK = 0x4, + HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE = 0x4, + HDMI_VP_STUFF_YCC422_STUFFING_DIRECT_MODE = 0x0, + HDMI_VP_STUFF_PP_STUFFING_MASK = 0x2, + HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE = 0x2, + HDMI_VP_STUFF_PP_STUFFING_DIRECT_MODE = 0x0, + HDMI_VP_STUFF_PR_STUFFING_MASK = 0x1, + HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE = 0x1, + HDMI_VP_STUFF_PR_STUFFING_DIRECT_MODE = 0x0, + +/* VP_CONF field values */ + HDMI_VP_CONF_BYPASS_EN_MASK = 0x40, + HDMI_VP_CONF_BYPASS_EN_ENABLE = 0x40, + HDMI_VP_CONF_BYPASS_EN_DISABLE = 0x00, + HDMI_VP_CONF_PP_EN_ENMASK = 0x20, + HDMI_VP_CONF_PP_EN_ENABLE = 0x20, + HDMI_VP_CONF_PP_EN_DISABLE = 0x00, + HDMI_VP_CONF_PR_EN_MASK = 0x10, + HDMI_VP_CONF_PR_EN_ENABLE = 0x10, + HDMI_VP_CONF_PR_EN_DISABLE = 0x00, + HDMI_VP_CONF_YCC422_EN_MASK = 0x8, + HDMI_VP_CONF_YCC422_EN_ENABLE = 0x8, + HDMI_VP_CONF_YCC422_EN_DISABLE = 0x0, + HDMI_VP_CONF_BYPASS_SELECT_MASK = 0x4, + HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER = 0x4, + HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER = 0x0, + HDMI_VP_CONF_OUTPUT_SELECTOR_MASK = 0x3, + HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS = 0x3, + HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422 = 0x1, + HDMI_VP_CONF_OUTPUT_SELECTOR_PP = 0x0, + +/* VP_REMAP field values */ + HDMI_VP_REMAP_MASK = 0x3, + HDMI_VP_REMAP_YCC422_24bit = 0x2, + HDMI_VP_REMAP_YCC422_20bit = 0x1, + HDMI_VP_REMAP_YCC422_16bit = 0x0, + +/* FC_INVIDCONF field values */ + HDMI_FC_INVIDCONF_HDCP_KEEPOUT_MASK = 0x80, + HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE = 0x80, + HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE = 0x00, + HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_MASK = 0x40, + HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH = 0x40, + HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW = 0x00, + HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_MASK = 0x20, + HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH = 0x20, + HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW = 0x00, + HDMI_FC_INVIDCONF_DE_IN_POLARITY_MASK = 0x10, + HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH = 0x10, + HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW = 0x00, + HDMI_FC_INVIDCONF_DVI_MODEZ_MASK = 0x8, + HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE = 0x8, + HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE = 0x0, + HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_MASK = 0x2, + HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH = 0x2, + HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW = 0x0, + HDMI_FC_INVIDCONF_IN_I_P_MASK = 0x1, + HDMI_FC_INVIDCONF_IN_I_P_INTERLACED = 0x1, + HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE = 0x0, + +/* FC_AUDICONF0 field values */ + HDMI_FC_AUDICONF0_CC_OFFSET = 4, + HDMI_FC_AUDICONF0_CC_MASK = 0x70, + HDMI_FC_AUDICONF0_CT_OFFSET = 0, + HDMI_FC_AUDICONF0_CT_MASK = 0xF, + +/* FC_AUDICONF1 field values */ + HDMI_FC_AUDICONF1_SS_OFFSET = 3, + HDMI_FC_AUDICONF1_SS_MASK = 0x18, + HDMI_FC_AUDICONF1_SF_OFFSET = 0, + HDMI_FC_AUDICONF1_SF_MASK = 0x7, + +/* FC_AUDICONF3 field values */ + HDMI_FC_AUDICONF3_LFEPBL_OFFSET = 5, + HDMI_FC_AUDICONF3_LFEPBL_MASK = 0x60, + HDMI_FC_AUDICONF3_DM_INH_OFFSET = 4, + HDMI_FC_AUDICONF3_DM_INH_MASK = 0x10, + HDMI_FC_AUDICONF3_LSV_OFFSET = 0, + HDMI_FC_AUDICONF3_LSV_MASK = 0xF, + +/* FC_AUDSCHNLS0 field values */ + HDMI_FC_AUDSCHNLS0_CGMSA_OFFSET = 4, + HDMI_FC_AUDSCHNLS0_CGMSA_MASK = 0x30, + HDMI_FC_AUDSCHNLS0_COPYRIGHT_OFFSET = 0, + HDMI_FC_AUDSCHNLS0_COPYRIGHT_MASK = 0x01, + +/* FC_AUDSCHNLS3-6 field values */ + HDMI_FC_AUDSCHNLS3_OIEC_CH0_OFFSET = 0, + HDMI_FC_AUDSCHNLS3_OIEC_CH0_MASK = 0x0f, + HDMI_FC_AUDSCHNLS3_OIEC_CH1_OFFSET = 4, + HDMI_FC_AUDSCHNLS3_OIEC_CH1_MASK = 0xf0, + HDMI_FC_AUDSCHNLS4_OIEC_CH2_OFFSET = 0, + HDMI_FC_AUDSCHNLS4_OIEC_CH2_MASK = 0x0f, + HDMI_FC_AUDSCHNLS4_OIEC_CH3_OFFSET = 4, + HDMI_FC_AUDSCHNLS4_OIEC_CH3_MASK = 0xf0, + + HDMI_FC_AUDSCHNLS5_OIEC_CH0_OFFSET = 0, + HDMI_FC_AUDSCHNLS5_OIEC_CH0_MASK = 0x0f, + HDMI_FC_AUDSCHNLS5_OIEC_CH1_OFFSET = 4, + HDMI_FC_AUDSCHNLS5_OIEC_CH1_MASK = 0xf0, + HDMI_FC_AUDSCHNLS6_OIEC_CH2_OFFSET = 0, + HDMI_FC_AUDSCHNLS6_OIEC_CH2_MASK = 0x0f, + HDMI_FC_AUDSCHNLS6_OIEC_CH3_OFFSET = 4, + HDMI_FC_AUDSCHNLS6_OIEC_CH3_MASK = 0xf0, + +/* HDMI_FC_AUDSCHNLS7 field values */ + HDMI_FC_AUDSCHNLS7_ACCURACY_OFFSET = 4, + HDMI_FC_AUDSCHNLS7_ACCURACY_MASK = 0x30, + +/* HDMI_FC_AUDSCHNLS8 field values */ + HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_MASK = 0xf0, + HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_OFFSET = 4, + HDMI_FC_AUDSCHNLS8_WORDLEGNTH_MASK = 0x0f, + HDMI_FC_AUDSCHNLS8_WORDLEGNTH_OFFSET = 0, + +/* FC_AUDSCONF field values */ + HDMI_FC_AUDSCONF_AUD_PACKET_SAMPFIT_MASK = 0xF0, + HDMI_FC_AUDSCONF_AUD_PACKET_SAMPFIT_OFFSET = 4, + HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_MASK = 0x1, + HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_OFFSET = 0, + HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT1 = 0x1, + HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT0 = 0x0, + +/* FC_STAT2 field values */ + HDMI_FC_STAT2_OVERFLOW_MASK = 0x03, + HDMI_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02, + HDMI_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01, + +/* FC_INT2 field values */ + HDMI_FC_INT2_OVERFLOW_MASK = 0x03, + HDMI_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02, + HDMI_FC_INT2_HIGH_PRIORITY_OVERFLOW = 0x01, + +/* FC_MASK2 field values */ + HDMI_FC_MASK2_OVERFLOW_MASK = 0x03, + HDMI_FC_MASK2_LOW_PRIORITY_OVERFLOW = 0x02, + HDMI_FC_MASK2_HIGH_PRIORITY_OVERFLOW = 0x01, + +/* FC_PRCONF field values */ + HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK = 0xF0, + HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET = 4, + HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK = 0x0F, + HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET = 0, + +/* FC_AVICONF0-FC_AVICONF3 field values */ + HDMI_FC_AVICONF0_PIX_FMT_MASK = 0x03, + HDMI_FC_AVICONF0_PIX_FMT_RGB = 0x00, + HDMI_FC_AVICONF0_PIX_FMT_YCBCR422 = 0x01, + HDMI_FC_AVICONF0_PIX_FMT_YCBCR444 = 0x02, + HDMI_FC_AVICONF0_ACTIVE_FMT_MASK = 0x40, + HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT = 0x40, + HDMI_FC_AVICONF0_ACTIVE_FMT_NO_INFO = 0x00, + HDMI_FC_AVICONF0_BAR_DATA_MASK = 0x0C, + HDMI_FC_AVICONF0_BAR_DATA_NO_DATA = 0x00, + HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR = 0x04, + HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR = 0x08, + HDMI_FC_AVICONF0_BAR_DATA_VERT_HORIZ_BAR = 0x0C, + HDMI_FC_AVICONF0_SCAN_INFO_MASK = 0x30, + HDMI_FC_AVICONF0_SCAN_INFO_OVERSCAN = 0x10, + HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN = 0x20, + HDMI_FC_AVICONF0_SCAN_INFO_NODATA = 0x00, + + HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_MASK = 0x0F, + HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_USE_CODED = 0x08, + HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_4_3 = 0x09, + HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_16_9 = 0x0A, + HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_14_9 = 0x0B, + HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_MASK = 0x30, + HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_NO_DATA = 0x00, + HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_4_3 = 0x10, + HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_16_9 = 0x20, + HDMI_FC_AVICONF1_COLORIMETRY_MASK = 0xC0, + HDMI_FC_AVICONF1_COLORIMETRY_NO_DATA = 0x00, + HDMI_FC_AVICONF1_COLORIMETRY_SMPTE = 0x40, + HDMI_FC_AVICONF1_COLORIMETRY_ITUR = 0x80, + HDMI_FC_AVICONF1_COLORIMETRY_EXTENDED_INFO = 0xC0, + + HDMI_FC_AVICONF2_SCALING_MASK = 0x03, + HDMI_FC_AVICONF2_SCALING_NONE = 0x00, + HDMI_FC_AVICONF2_SCALING_HORIZ = 0x01, + HDMI_FC_AVICONF2_SCALING_VERT = 0x02, + HDMI_FC_AVICONF2_SCALING_HORIZ_VERT = 0x03, + HDMI_FC_AVICONF2_RGB_QUANT_MASK = 0x0C, + HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT = 0x00, + HDMI_FC_AVICONF2_RGB_QUANT_LIMITED_RANGE = 0x04, + HDMI_FC_AVICONF2_RGB_QUANT_FULL_RANGE = 0x08, + HDMI_FC_AVICONF2_EXT_COLORIMETRY_MASK = 0x70, + HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601 = 0x00, + HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC709 = 0x10, + HDMI_FC_AVICONF2_EXT_COLORIMETRY_SYCC601 = 0x20, + HDMI_FC_AVICONF2_EXT_COLORIMETRY_ADOBE_YCC601 = 0x30, + HDMI_FC_AVICONF2_EXT_COLORIMETRY_ADOBE_RGB = 0x40, + HDMI_FC_AVICONF2_IT_CONTENT_MASK = 0x80, + HDMI_FC_AVICONF2_IT_CONTENT_NO_DATA = 0x00, + HDMI_FC_AVICONF2_IT_CONTENT_VALID = 0x80, + + HDMI_FC_AVICONF3_IT_CONTENT_TYPE_MASK = 0x03, + HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GRAPHICS = 0x00, + HDMI_FC_AVICONF3_IT_CONTENT_TYPE_PHOTO = 0x01, + HDMI_FC_AVICONF3_IT_CONTENT_TYPE_CINEMA = 0x02, + HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GAME = 0x03, + HDMI_FC_AVICONF3_QUANT_RANGE_MASK = 0x0C, + HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED = 0x00, + HDMI_FC_AVICONF3_QUANT_RANGE_FULL = 0x04, + +/* FC_DBGFORCE field values */ + HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10, + HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1, + +/* PHY_CONF0 field values */ + HDMI_PHY_CONF0_PDZ_MASK = 0x80, + HDMI_PHY_CONF0_PDZ_OFFSET = 7, + HDMI_PHY_CONF0_ENTMDS_MASK = 0x40, + HDMI_PHY_CONF0_ENTMDS_OFFSET = 6, + HDMI_PHY_CONF0_SPARECTRL = 0x20, + HDMI_PHY_CONF0_GEN2_PDDQ_MASK = 0x10, + HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET = 4, + HDMI_PHY_CONF0_GEN2_TXPWRON_MASK = 0x8, + HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET = 3, + HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_MASK = 0x4, + HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_OFFSET = 2, + HDMI_PHY_CONF0_SELDATAENPOL_MASK = 0x2, + HDMI_PHY_CONF0_SELDATAENPOL_OFFSET = 1, + HDMI_PHY_CONF0_SELDIPIF_MASK = 0x1, + HDMI_PHY_CONF0_SELDIPIF_OFFSET = 0, + +/* PHY_TST0 field values */ + HDMI_PHY_TST0_TSTCLR_MASK = 0x20, + HDMI_PHY_TST0_TSTCLR_OFFSET = 5, + HDMI_PHY_TST0_TSTEN_MASK = 0x10, + HDMI_PHY_TST0_TSTEN_OFFSET = 4, + HDMI_PHY_TST0_TSTCLK_MASK = 0x1, + HDMI_PHY_TST0_TSTCLK_OFFSET = 0, + +/* PHY_STAT0 field values */ + HDMI_PHY_RX_SENSE3 = 0x80, + HDMI_PHY_RX_SENSE2 = 0x40, + HDMI_PHY_RX_SENSE1 = 0x20, + HDMI_PHY_RX_SENSE0 = 0x10, + HDMI_PHY_HPD = 0x02, + HDMI_PHY_TX_PHY_LOCK = 0x01, + +/* PHY_I2CM_SLAVE_ADDR field values */ + HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 = 0x69, + HDMI_PHY_I2CM_SLAVE_ADDR_HEAC_PHY = 0x49, + +/* PHY_I2CM_OPERATION_ADDR field values */ + HDMI_PHY_I2CM_OPERATION_ADDR_WRITE = 0x10, + HDMI_PHY_I2CM_OPERATION_ADDR_READ = 0x1, + +/* HDMI_PHY_I2CM_INT_ADDR */ + HDMI_PHY_I2CM_INT_ADDR_DONE_POL = 0x08, + HDMI_PHY_I2CM_INT_ADDR_DONE_MASK = 0x04, + +/* HDMI_PHY_I2CM_CTLINT_ADDR */ + HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL = 0x80, + HDMI_PHY_I2CM_CTLINT_ADDR_NAC_MASK = 0x40, + HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL = 0x08, + HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_MASK = 0x04, + +/* AUD_CTS3 field values */ + HDMI_AUD_CTS3_N_SHIFT_OFFSET = 5, + HDMI_AUD_CTS3_N_SHIFT_MASK = 0xe0, + HDMI_AUD_CTS3_N_SHIFT_1 = 0, + HDMI_AUD_CTS3_N_SHIFT_16 = 0x20, + HDMI_AUD_CTS3_N_SHIFT_32 = 0x40, + HDMI_AUD_CTS3_N_SHIFT_64 = 0x60, + HDMI_AUD_CTS3_N_SHIFT_128 = 0x80, + HDMI_AUD_CTS3_N_SHIFT_256 = 0xa0, + /* note that the CTS3 MANUAL bit has been removed + from our part. Can't set it, will read as 0. */ + HDMI_AUD_CTS3_CTS_MANUAL = 0x10, + HDMI_AUD_CTS3_AUDCTS19_16_MASK = 0x0f, + +/* AHB_DMA_CONF0 field values */ + HDMI_AHB_DMA_CONF0_SW_FIFO_RST_OFFSET = 7, + HDMI_AHB_DMA_CONF0_SW_FIFO_RST_MASK = 0x80, + HDMI_AHB_DMA_CONF0_HBR = 0x10, + HDMI_AHB_DMA_CONF0_EN_HLOCK_OFFSET = 3, + HDMI_AHB_DMA_CONF0_EN_HLOCK_MASK = 0x08, + HDMI_AHB_DMA_CONF0_INCR_TYPE_OFFSET = 1, + HDMI_AHB_DMA_CONF0_INCR_TYPE_MASK = 0x06, + HDMI_AHB_DMA_CONF0_INCR4 = 0x0, + HDMI_AHB_DMA_CONF0_INCR8 = 0x2, + HDMI_AHB_DMA_CONF0_INCR16 = 0x4, + HDMI_AHB_DMA_CONF0_BURST_MODE = 0x1, + +/* HDMI_AHB_DMA_START field values */ + HDMI_AHB_DMA_START_START_OFFSET = 0, + HDMI_AHB_DMA_START_START_MASK = 0x01, + +/* HDMI_AHB_DMA_STOP field values */ + HDMI_AHB_DMA_STOP_STOP_OFFSET = 0, + HDMI_AHB_DMA_STOP_STOP_MASK = 0x01, + +/* AHB_DMA_STAT, AHB_DMA_INT, AHB_DMA_MASK, AHB_DMA_POL field values */ + HDMI_AHB_DMA_DONE = 0x80, + HDMI_AHB_DMA_RETRY_SPLIT = 0x40, + HDMI_AHB_DMA_LOSTOWNERSHIP = 0x20, + HDMI_AHB_DMA_ERROR = 0x10, + HDMI_AHB_DMA_FIFO_THREMPTY = 0x04, + HDMI_AHB_DMA_FIFO_FULL = 0x02, + HDMI_AHB_DMA_FIFO_EMPTY = 0x01, + +/* AHB_DMA_BUFFSTAT, AHB_DMA_BUFFINT,AHB_DMA_BUFFMASK,AHB_DMA_BUFFPOL values */ + HDMI_AHB_DMA_BUFFSTAT_FULL = 0x02, + HDMI_AHB_DMA_BUFFSTAT_EMPTY = 0x01, + +/* MC_CLKDIS field values */ + HDMI_MC_CLKDIS_HDCPCLK_DISABLE = 0x40, + HDMI_MC_CLKDIS_CECCLK_DISABLE = 0x20, + HDMI_MC_CLKDIS_CSCCLK_DISABLE = 0x10, + HDMI_MC_CLKDIS_AUDCLK_DISABLE = 0x8, + HDMI_MC_CLKDIS_PREPCLK_DISABLE = 0x4, + HDMI_MC_CLKDIS_TMDSCLK_DISABLE = 0x2, + HDMI_MC_CLKDIS_PIXELCLK_DISABLE = 0x1, + +/* MC_SWRSTZ field values */ + HDMI_MC_SWRSTZ_TMDSSWRST_REQ = 0x02, + +/* MC_FLOWCTRL field values */ + HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_MASK = 0x1, + HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH = 0x1, + HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS = 0x0, + +/* MC_PHYRSTZ field values */ + HDMI_MC_PHYRSTZ_ASSERT = 0x0, + HDMI_MC_PHYRSTZ_DEASSERT = 0x1, + +/* MC_HEACPHY_RST field values */ + HDMI_MC_HEACPHY_RST_ASSERT = 0x1, + HDMI_MC_HEACPHY_RST_DEASSERT = 0x0, + +/* CSC_CFG field values */ + HDMI_CSC_CFG_INTMODE_MASK = 0x30, + HDMI_CSC_CFG_INTMODE_OFFSET = 4, + HDMI_CSC_CFG_INTMODE_DISABLE = 0x00, + HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1 = 0x10, + HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA2 = 0x20, + HDMI_CSC_CFG_DECMODE_MASK = 0x3, + HDMI_CSC_CFG_DECMODE_OFFSET = 0, + HDMI_CSC_CFG_DECMODE_DISABLE = 0x0, + HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA1 = 0x1, + HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA2 = 0x2, + HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3 = 0x3, + +/* CSC_SCALE field values */ + HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK = 0xF0, + HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP = 0x00, + HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP = 0x50, + HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP = 0x60, + HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP = 0x70, + HDMI_CSC_SCALE_CSCSCALE_MASK = 0x03, + +/* A_HDCPCFG0 field values */ + HDMI_A_HDCPCFG0_ELVENA_MASK = 0x80, + HDMI_A_HDCPCFG0_ELVENA_ENABLE = 0x80, + HDMI_A_HDCPCFG0_ELVENA_DISABLE = 0x00, + HDMI_A_HDCPCFG0_I2CFASTMODE_MASK = 0x40, + HDMI_A_HDCPCFG0_I2CFASTMODE_ENABLE = 0x40, + HDMI_A_HDCPCFG0_I2CFASTMODE_DISABLE = 0x00, + HDMI_A_HDCPCFG0_BYPENCRYPTION_MASK = 0x20, + HDMI_A_HDCPCFG0_BYPENCRYPTION_ENABLE = 0x20, + HDMI_A_HDCPCFG0_BYPENCRYPTION_DISABLE = 0x00, + HDMI_A_HDCPCFG0_SYNCRICHECK_MASK = 0x10, + HDMI_A_HDCPCFG0_SYNCRICHECK_ENABLE = 0x10, + HDMI_A_HDCPCFG0_SYNCRICHECK_DISABLE = 0x00, + HDMI_A_HDCPCFG0_AVMUTE_MASK = 0x8, + HDMI_A_HDCPCFG0_AVMUTE_ENABLE = 0x8, + HDMI_A_HDCPCFG0_AVMUTE_DISABLE = 0x0, + HDMI_A_HDCPCFG0_RXDETECT_MASK = 0x4, + HDMI_A_HDCPCFG0_RXDETECT_ENABLE = 0x4, + HDMI_A_HDCPCFG0_RXDETECT_DISABLE = 0x0, + HDMI_A_HDCPCFG0_EN11FEATURE_MASK = 0x2, + HDMI_A_HDCPCFG0_EN11FEATURE_ENABLE = 0x2, + HDMI_A_HDCPCFG0_EN11FEATURE_DISABLE = 0x0, + HDMI_A_HDCPCFG0_HDMIDVI_MASK = 0x1, + HDMI_A_HDCPCFG0_HDMIDVI_HDMI = 0x1, + HDMI_A_HDCPCFG0_HDMIDVI_DVI = 0x0, + +/* A_HDCPCFG1 field values */ + HDMI_A_HDCPCFG1_DISSHA1CHECK_MASK = 0x8, + HDMI_A_HDCPCFG1_DISSHA1CHECK_DISABLE = 0x8, + HDMI_A_HDCPCFG1_DISSHA1CHECK_ENABLE = 0x0, + HDMI_A_HDCPCFG1_PH2UPSHFTENC_MASK = 0x4, + HDMI_A_HDCPCFG1_PH2UPSHFTENC_ENABLE = 0x4, + HDMI_A_HDCPCFG1_PH2UPSHFTENC_DISABLE = 0x0, + HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK = 0x2, + HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE = 0x2, + HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_ENABLE = 0x0, + HDMI_A_HDCPCFG1_SWRESET_MASK = 0x1, + HDMI_A_HDCPCFG1_SWRESET_ASSERT = 0x0, + +/* A_VIDPOLCFG field values */ + HDMI_A_VIDPOLCFG_UNENCRYPTCONF_MASK = 0x60, + HDMI_A_VIDPOLCFG_UNENCRYPTCONF_OFFSET = 5, + HDMI_A_VIDPOLCFG_DATAENPOL_MASK = 0x10, + HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH = 0x10, + HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW = 0x0, + HDMI_A_VIDPOLCFG_VSYNCPOL_MASK = 0x8, + HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_HIGH = 0x8, + HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_LOW = 0x0, + HDMI_A_VIDPOLCFG_HSYNCPOL_MASK = 0x2, + HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH = 0x2, + HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW = 0x0, +}; + +#endif /* __IMX_HDMI_H__ */ diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig index ab31848e92cf..5d5e4092d40a 100644 --- a/drivers/gpu/drm/imx/Kconfig +++ b/drivers/gpu/drm/imx/Kconfig @@ -49,6 +49,7 @@ config DRM_IMX_IPUV3 config DRM_IMX_HDMI tristate "Freescale i.MX DRM HDMI" + select DRM_DW_HDMI depends on DRM_IMX help Choose this if you want to use HDMI on i.MX6. diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile index 63cf56ad5c61..f3ecd8903d97 100644 --- a/drivers/gpu/drm/imx/Makefile +++ b/drivers/gpu/drm/imx/Makefile @@ -9,4 +9,4 @@ obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o imx-ipuv3-crtc-objs := ipuv3-crtc.o ipuv3-plane.o obj-$(CONFIG_DRM_IMX_IPUV3) += imx-ipuv3-crtc.o -obj-$(CONFIG_DRM_IMX_HDMI) += imx-hdmi.o imx-hdmi_pltfm.o +obj-$(CONFIG_DRM_IMX_HDMI) += dw_hdmi-imx.o diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/dw_hdmi-imx.c new file mode 100644 index 000000000000..121d30ca2d44 --- /dev/null +++ b/drivers/gpu/drm/imx/dw_hdmi-imx.c @@ -0,0 +1,258 @@ +/* Copyright (C) 2011-2013 Freescale Semiconductor, Inc. + * + * derived from imx-hdmi.c(renamed to bridge/dw_hdmi.c now) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include