From 015b85a067bd9949756e49b01b70c820d31316d7 Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Tue, 18 Sep 2012 10:58:47 -0400 Subject: drm: Export drm_probe_ddc() Tested-by: Takashi Iwai Signed-off-by: Adam Jackson Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- include/drm/drm_crtc.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index eb91d520ce0b..07e2956d9644 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -861,6 +861,7 @@ extern char *drm_get_tv_subconnector_name(int val); extern char *drm_get_tv_select_name(int val); extern void drm_fb_release(struct drm_file *file_priv); extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group); +extern bool drm_probe_ddc(struct i2c_adapter *adapter); extern struct edid *drm_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter); extern int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid); -- cgit v1.2.3-59-g8ed1b From e89861dfa3ed0d8e9d184becf25aebea14e98372 Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Tue, 18 Sep 2012 10:58:48 -0400 Subject: drm/dp: Update DPCD defines Sources: DP, eDP, and DP interop specs, and a VESA slideshow about DP 1.2 for the MST bits. Tested-by: Takashi Iwai Signed-off-by: Adam Jackson Acked-by: Dave Airlie Reviewed-by: Paulo Zanoni Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- include/drm/drm_dp_helper.h | 60 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 1744b18c06b3..f9888c3cb955 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -69,16 +69,30 @@ /* 10b = TMDS or HDMI */ /* 11b = Other */ # define DP_FORMAT_CONVERSION (1 << 3) +# define DP_DETAILED_CAP_INFO_AVAILABLE (1 << 4) #define DP_MAIN_LINK_CHANNEL_CODING 0x006 #define DP_DOWN_STREAM_PORT_COUNT 0x007 -#define DP_PORT_COUNT_MASK 0x0f -#define DP_OUI_SUPPORT (1 << 7) +# define DP_PORT_COUNT_MASK 0x0f +# define DP_MSA_TIMING_PAR_IGNORED (1 << 6) +# define DP_OUI_SUPPORT (1 << 7) + +#define DP_I2C_SPEED_CAP 0x00c +# define DP_I2C_SPEED_1K 0x01 +# define DP_I2C_SPEED_5K 0x02 +# define DP_I2C_SPEED_10K 0x04 +# define DP_I2C_SPEED_100K 0x08 +# define DP_I2C_SPEED_400K 0x10 +# define DP_I2C_SPEED_1M 0x20 #define DP_EDP_CONFIGURATION_CAP 0x00d #define DP_TRAINING_AUX_RD_INTERVAL 0x00e +/* Multiple stream transport */ +#define DP_MSTM_CAP 0x021 +# define DP_MST_CAP (1 << 0) + #define DP_PSR_SUPPORT 0x070 # define DP_PSR_IS_SUPPORTED 1 #define DP_PSR_CAPS 0x071 @@ -93,6 +107,31 @@ # define DP_PSR_SETUP_TIME_MASK (7 << 1) # define DP_PSR_SETUP_TIME_SHIFT 1 +/* + * 0x80-0x8f describe downstream port capabilities, but there are two layouts + * based on whether DP_DETAILED_CAP_INFO_AVAILABLE was set. If it was not, + * each port's descriptor is one byte wide. If it was set, each port's is + * four bytes wide, starting with the one byte from the base info. As of + * DP interop v1.1a only VGA defines additional detail. + */ + +/* offset 0 */ +#define DP_DOWNSTREAM_PORT_0 0x80 +# define DP_DS_PORT_TYPE_MASK (7 << 0) +# define DP_DS_PORT_TYPE_DP 0 +# define DP_DS_PORT_TYPE_VGA 1 +# define DP_DS_PORT_TYPE_DVI 2 +# define DP_DS_PORT_TYPE_HDMI 3 +# define DP_DS_PORT_TYPE_NON_EDID 4 +# define DP_DS_PORT_HPD (1 << 3) +/* offset 1 for VGA is maximum megapixels per second / 8 */ +/* offset 2 */ +# define DP_DS_VGA_MAX_BPC_MASK (3 << 0) +# define DP_DS_VGA_8BPC 0 +# define DP_DS_VGA_10BPC 1 +# define DP_DS_VGA_12BPC 2 +# define DP_DS_VGA_16BPC 3 + /* link configuration */ #define DP_LINK_BW_SET 0x100 # define DP_LINK_BW_1_62 0x06 @@ -148,24 +187,37 @@ #define DP_DOWNSPREAD_CTRL 0x107 # define DP_SPREAD_AMP_0_5 (1 << 4) +# define DP_MSA_TIMING_PAR_IGNORE_EN (1 << 7) #define DP_MAIN_LINK_CHANNEL_CODING_SET 0x108 # define DP_SET_ANSI_8B10B (1 << 0) +#define DP_I2C_SPEED_CONTROL_STATUS 0x109 +/* bitmask as for DP_I2C_SPEED_CAP */ + +#define DP_EDP_CONFIGURATION_SET 0x10a + +#define DP_MSTM_CTRL 0x111 +# define DP_MST_EN (1 << 0) +# define DP_UP_REQ_EN (1 << 1) +# define DP_UPSTREAM_IS_SRC (1 << 2) + #define DP_PSR_EN_CFG 0x170 # define DP_PSR_ENABLE (1 << 0) # define DP_PSR_MAIN_LINK_ACTIVE (1 << 1) # define DP_PSR_CRC_VERIFICATION (1 << 2) # define DP_PSR_FRAME_CAPTURE (1 << 3) +#define DP_SINK_COUNT 0x200 +# define DP_SINK_COUNT_MASK (31 << 0) +# define DP_SINK_CP_READY (1 << 6) + #define DP_DEVICE_SERVICE_IRQ_VECTOR 0x201 # define DP_REMOTE_CONTROL_COMMAND_PENDING (1 << 0) # define DP_AUTOMATED_TEST_REQUEST (1 << 1) # define DP_CP_IRQ (1 << 2) # define DP_SINK_SPECIFIC_IRQ (1 << 6) -#define DP_EDP_CONFIGURATION_SET 0x10a - #define DP_LANE0_1_STATUS 0x202 #define DP_LANE2_3_STATUS 0x203 # define DP_LANE_CR_DONE (1 << 0) -- cgit v1.2.3-59-g8ed1b From a477f4fcbdd48837610974b0d025af1d46051a57 Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Thu, 20 Sep 2012 16:42:44 -0400 Subject: drm/dp: Document DP spec versions for various DPCD registers Note with a comment anything newer than DP 1.1a. Obviously this needs some work still... Signed-off-by: Adam Jackson Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- include/drm/drm_dp_helper.h | 52 ++++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index f9888c3cb955..38ffcb4332aa 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -26,7 +26,19 @@ #include #include -/* From the VESA DisplayPort spec */ +/* + * Unless otherwise noted, all values are from the DP 1.1a spec. Note that + * DP and DPCD versions are independent. Differences from 1.0 are not noted, + * 1.0 devices basically don't exist in the wild. + * + * Abbreviations, in chronological order: + * + * eDP: Embedded DisplayPort version 1 + * DPI: DisplayPort Interoperability Guideline v1.1a + * 1.2: DisplayPort 1.2 + * + * 1.2 formally includes both eDP and DPI definitions. + */ #define AUX_NATIVE_WRITE 0x8 #define AUX_NATIVE_READ 0x9 @@ -53,7 +65,7 @@ #define DP_MAX_LANE_COUNT 0x002 # define DP_MAX_LANE_COUNT_MASK 0x1f -# define DP_TPS3_SUPPORTED (1 << 6) +# define DP_TPS3_SUPPORTED (1 << 6) /* 1.2 */ # define DP_ENHANCED_FRAME_CAP (1 << 7) #define DP_MAX_DOWNSPREAD 0x003 @@ -69,16 +81,16 @@ /* 10b = TMDS or HDMI */ /* 11b = Other */ # define DP_FORMAT_CONVERSION (1 << 3) -# define DP_DETAILED_CAP_INFO_AVAILABLE (1 << 4) +# define DP_DETAILED_CAP_INFO_AVAILABLE (1 << 4) /* DPI */ #define DP_MAIN_LINK_CHANNEL_CODING 0x006 #define DP_DOWN_STREAM_PORT_COUNT 0x007 # define DP_PORT_COUNT_MASK 0x0f -# define DP_MSA_TIMING_PAR_IGNORED (1 << 6) +# define DP_MSA_TIMING_PAR_IGNORED (1 << 6) /* eDP */ # define DP_OUI_SUPPORT (1 << 7) -#define DP_I2C_SPEED_CAP 0x00c +#define DP_I2C_SPEED_CAP 0x00c /* DPI */ # define DP_I2C_SPEED_1K 0x01 # define DP_I2C_SPEED_5K 0x02 # define DP_I2C_SPEED_10K 0x04 @@ -86,16 +98,16 @@ # define DP_I2C_SPEED_400K 0x10 # define DP_I2C_SPEED_1M 0x20 -#define DP_EDP_CONFIGURATION_CAP 0x00d -#define DP_TRAINING_AUX_RD_INTERVAL 0x00e +#define DP_EDP_CONFIGURATION_CAP 0x00d /* XXX 1.2? */ +#define DP_TRAINING_AUX_RD_INTERVAL 0x00e /* XXX 1.2? */ /* Multiple stream transport */ -#define DP_MSTM_CAP 0x021 +#define DP_MSTM_CAP 0x021 /* 1.2 */ # define DP_MST_CAP (1 << 0) -#define DP_PSR_SUPPORT 0x070 +#define DP_PSR_SUPPORT 0x070 /* XXX 1.2? */ # define DP_PSR_IS_SUPPORTED 1 -#define DP_PSR_CAPS 0x071 +#define DP_PSR_CAPS 0x071 /* XXX 1.2? */ # define DP_PSR_NO_TRAIN_ON_EXIT 1 # define DP_PSR_SETUP_TIME_330 (0 << 1) # define DP_PSR_SETUP_TIME_275 (1 << 1) @@ -136,7 +148,7 @@ #define DP_LINK_BW_SET 0x100 # define DP_LINK_BW_1_62 0x06 # define DP_LINK_BW_2_7 0x0a -# define DP_LINK_BW_5_4 0x14 +# define DP_LINK_BW_5_4 0x14 /* 1.2 */ #define DP_LANE_COUNT_SET 0x101 # define DP_LANE_COUNT_MASK 0x0f @@ -146,7 +158,7 @@ # define DP_TRAINING_PATTERN_DISABLE 0 # define DP_TRAINING_PATTERN_1 1 # define DP_TRAINING_PATTERN_2 2 -# define DP_TRAINING_PATTERN_3 3 +# define DP_TRAINING_PATTERN_3 3 /* 1.2 */ # define DP_TRAINING_PATTERN_MASK 0x3 # define DP_LINK_QUAL_PATTERN_DISABLE (0 << 2) @@ -187,22 +199,22 @@ #define DP_DOWNSPREAD_CTRL 0x107 # define DP_SPREAD_AMP_0_5 (1 << 4) -# define DP_MSA_TIMING_PAR_IGNORE_EN (1 << 7) +# define DP_MSA_TIMING_PAR_IGNORE_EN (1 << 7) /* eDP */ #define DP_MAIN_LINK_CHANNEL_CODING_SET 0x108 # define DP_SET_ANSI_8B10B (1 << 0) -#define DP_I2C_SPEED_CONTROL_STATUS 0x109 +#define DP_I2C_SPEED_CONTROL_STATUS 0x109 /* DPI */ /* bitmask as for DP_I2C_SPEED_CAP */ -#define DP_EDP_CONFIGURATION_SET 0x10a +#define DP_EDP_CONFIGURATION_SET 0x10a /* XXX 1.2? */ -#define DP_MSTM_CTRL 0x111 +#define DP_MSTM_CTRL 0x111 /* 1.2 */ # define DP_MST_EN (1 << 0) # define DP_UP_REQ_EN (1 << 1) # define DP_UPSTREAM_IS_SRC (1 << 2) -#define DP_PSR_EN_CFG 0x170 +#define DP_PSR_EN_CFG 0x170 /* XXX 1.2? */ # define DP_PSR_ENABLE (1 << 0) # define DP_PSR_MAIN_LINK_ACTIVE (1 << 1) # define DP_PSR_CRC_VERIFICATION (1 << 2) @@ -277,14 +289,14 @@ # define DP_SET_POWER_D0 0x1 # define DP_SET_POWER_D3 0x2 -#define DP_PSR_ERROR_STATUS 0x2006 +#define DP_PSR_ERROR_STATUS 0x2006 /* XXX 1.2? */ # define DP_PSR_LINK_CRC_ERROR (1 << 0) # define DP_PSR_RFB_STORAGE_ERROR (1 << 1) -#define DP_PSR_ESI 0x2007 +#define DP_PSR_ESI 0x2007 /* XXX 1.2? */ # define DP_PSR_CAPS_CHANGE (1 << 0) -#define DP_PSR_STATUS 0x2008 +#define DP_PSR_STATUS 0x2008 /* XXX 1.2? */ # define DP_PSR_SINK_INACTIVE 0 # define DP_PSR_SINK_ACTIVE_SRC_SYNCED 1 # define DP_PSR_SINK_ACTIVE_RFB 2 -- cgit v1.2.3-59-g8ed1b From da131a46268bf2a67e7b7fa137a90a1279866367 Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Thu, 20 Sep 2012 16:42:45 -0400 Subject: drm/dp: Make sink count DP 1.2 aware Signed-off-by: Adam Jackson Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 9 ++++----- include/drm/drm_dp_helper.h | 3 ++- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 4f2a38181491..c63f54e84847 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2101,13 +2101,12 @@ intel_dp_detect_dpcd(struct intel_dp *intel_dp) /* If we're HPD-aware, SINK_COUNT changes dynamically */ hpd = !!(intel_dp->downstream_ports[0] & DP_DS_PORT_HPD); if (hpd) { - uint8_t sink_count; + uint8_t reg; if (!intel_dp_aux_native_read_retry(intel_dp, DP_SINK_COUNT, - &sink_count, 1)) + ®, 1)) return connector_status_unknown; - sink_count &= DP_SINK_COUNT_MASK; - return sink_count ? connector_status_connected - : connector_status_disconnected; + return DP_GET_SINK_COUNT(reg) ? connector_status_connected + : connector_status_disconnected; } /* If no HPD, poke DDC gently */ diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 38ffcb4332aa..fe061489f91f 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -221,7 +221,8 @@ # define DP_PSR_FRAME_CAPTURE (1 << 3) #define DP_SINK_COUNT 0x200 -# define DP_SINK_COUNT_MASK (31 << 0) +/* prior to 1.2 bit 7 was reserved mbz */ +# define DP_GET_SINK_COUNT(x) ((((x) & 0x80) >> 1) | ((x) & 0x3f)) # define DP_SINK_CP_READY (1 << 6) #define DP_DEVICE_SERVICE_IRQ_VECTOR 0x201 -- cgit v1.2.3-59-g8ed1b From d7d4eeddb8f72342f70621c4b3cb718af9361712 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 17 Oct 2012 12:09:54 +0100 Subject: drm/i915: Allow DRM_ROOT_ONLY|DRM_MASTER to submit privileged batchbuffers With the introduction of per-process GTT space, the hardware designers thought it wise to also limit the ability to write to MMIO space to only a "secure" batch buffer. The ability to rewrite registers is the only way to program the hardware to perform certain operations like scanline waits (required for tear-free windowed updates). So we either have a choice of adding an interface to perform those synchronized updates inside the kernel, or we permit certain processes the ability to write to the "safe" registers from within its command stream. This patch exposes the ability to submit a SECURE batch buffer to DRM_ROOT_ONLY|DRM_MASTER processes. v2: Haswell split up bit8 into a ppgtt bit (still bit8) and a security bit (bit 13, accidentally not set). Also add a comment explaining why secure batches need a global gtt binding. Signed-off-by: Chris Wilson (v1) [danvet: added hsw fixup.] Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 3 ++ drivers/gpu/drm/i915/i915_gem_execbuffer.c | 25 ++++++++++++++-- drivers/gpu/drm/i915/i915_reg.h | 7 +++-- drivers/gpu/drm/i915/i915_trace.h | 10 ++++--- drivers/gpu/drm/i915/intel_ringbuffer.c | 48 ++++++++++++++++++++++++------ drivers/gpu/drm/i915/intel_ringbuffer.h | 4 ++- include/drm/i915_drm.h | 6 ++++ 7 files changed, 84 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 491394fd94cd..14271aab72bb 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1015,6 +1015,9 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_HAS_PRIME_VMAP_FLUSH: value = 1; break; + case I915_PARAM_HAS_SECURE_BATCHES: + value = capable(CAP_SYS_ADMIN); + break; default: DRM_DEBUG_DRIVER("Unknown parameter %d\n", param->param); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 6a2f3e50c714..afbc9240a992 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -801,6 +801,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, u32 exec_start, exec_len; u32 seqno; u32 mask; + u32 flags; int ret, mode, i; if (!i915_gem_check_execbuffer(args)) { @@ -812,6 +813,14 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, if (ret) return ret; + flags = 0; + if (args->flags & I915_EXEC_SECURE) { + if (!file->is_master || !capable(CAP_SYS_ADMIN)) + return -EPERM; + + flags |= I915_DISPATCH_SECURE; + } + switch (args->flags & I915_EXEC_RING_MASK) { case I915_EXEC_DEFAULT: case I915_EXEC_RENDER: @@ -984,6 +993,13 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, } batch_obj->base.pending_read_domains |= I915_GEM_DOMAIN_COMMAND; + /* snb/ivb/vlv conflate the "batch in ppgtt" bit with the "non-secure + * batch" bit. Hence we need to pin secure batches into the global gtt. + * hsw should have this fixed, but let's be paranoid and do it + * unconditionally for now. */ + if (flags & I915_DISPATCH_SECURE && !batch_obj->has_global_gtt_mapping) + i915_gem_gtt_bind_object(batch_obj, batch_obj->cache_level); + ret = i915_gem_execbuffer_move_to_gpu(ring, &objects); if (ret) goto err; @@ -1029,7 +1045,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, goto err; } - trace_i915_gem_ring_dispatch(ring, seqno); + trace_i915_gem_ring_dispatch(ring, seqno, flags); exec_start = batch_obj->gtt_offset + args->batch_start_offset; exec_len = args->batch_len; @@ -1041,12 +1057,15 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, goto err; ret = ring->dispatch_execbuffer(ring, - exec_start, exec_len); + exec_start, exec_len, + flags); if (ret) goto err; } } else { - ret = ring->dispatch_execbuffer(ring, exec_start, exec_len); + ret = ring->dispatch_execbuffer(ring, + exec_start, exec_len, + flags); if (ret) goto err; } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index d7f516c4855a..455beb4f690f 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -244,8 +244,11 @@ #define MI_INVALIDATE_TLB (1<<18) #define MI_INVALIDATE_BSD (1<<7) #define MI_BATCH_BUFFER MI_INSTR(0x30, 1) -#define MI_BATCH_NON_SECURE (1) -#define MI_BATCH_NON_SECURE_I965 (1<<8) +#define MI_BATCH_NON_SECURE (1) +/* for snb/ivb/vlv this also means "batch in ppgtt" when ppgtt is enabled. */ +#define MI_BATCH_NON_SECURE_I965 (1<<8) +#define MI_BATCH_PPGTT_HSW (1<<8) +#define MI_BATCH_NON_SECURE_HSW (1<<13) #define MI_BATCH_BUFFER_START MI_INSTR(0x31, 0) #define MI_BATCH_GTT (2<<6) /* aliased with (1<<7) on gen4 */ #define MI_SEMAPHORE_MBOX MI_INSTR(0x16, 1) /* gen6+ */ diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index 8134421b89a6..3db4a6817713 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -229,24 +229,26 @@ TRACE_EVENT(i915_gem_evict_everything, ); TRACE_EVENT(i915_gem_ring_dispatch, - TP_PROTO(struct intel_ring_buffer *ring, u32 seqno), - TP_ARGS(ring, seqno), + TP_PROTO(struct intel_ring_buffer *ring, u32 seqno, u32 flags), + TP_ARGS(ring, seqno, flags), TP_STRUCT__entry( __field(u32, dev) __field(u32, ring) __field(u32, seqno) + __field(u32, flags) ), TP_fast_assign( __entry->dev = ring->dev->primary->index; __entry->ring = ring->id; __entry->seqno = seqno; + __entry->flags = flags; i915_trace_irq_get(ring, seqno); ), - TP_printk("dev=%u, ring=%u, seqno=%u", - __entry->dev, __entry->ring, __entry->seqno) + TP_printk("dev=%u, ring=%u, seqno=%u, flags=%x", + __entry->dev, __entry->ring, __entry->seqno, __entry->flags) ); TRACE_EVENT(i915_gem_ring_flush, diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 984a0c5fbf5d..6c6f95a534b1 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -965,7 +965,9 @@ gen6_ring_put_irq(struct intel_ring_buffer *ring) } static int -i965_dispatch_execbuffer(struct intel_ring_buffer *ring, u32 offset, u32 length) +i965_dispatch_execbuffer(struct intel_ring_buffer *ring, + u32 offset, u32 length, + unsigned flags) { int ret; @@ -976,7 +978,7 @@ i965_dispatch_execbuffer(struct intel_ring_buffer *ring, u32 offset, u32 length) intel_ring_emit(ring, MI_BATCH_BUFFER_START | MI_BATCH_GTT | - MI_BATCH_NON_SECURE_I965); + (flags & I915_DISPATCH_SECURE ? 0 : MI_BATCH_NON_SECURE_I965)); intel_ring_emit(ring, offset); intel_ring_advance(ring); @@ -985,7 +987,8 @@ i965_dispatch_execbuffer(struct intel_ring_buffer *ring, u32 offset, u32 length) static int i830_dispatch_execbuffer(struct intel_ring_buffer *ring, - u32 offset, u32 len) + u32 offset, u32 len, + unsigned flags) { int ret; @@ -994,7 +997,7 @@ i830_dispatch_execbuffer(struct intel_ring_buffer *ring, return ret; intel_ring_emit(ring, MI_BATCH_BUFFER); - intel_ring_emit(ring, offset | MI_BATCH_NON_SECURE); + intel_ring_emit(ring, offset | (flags & I915_DISPATCH_SECURE ? 0 : MI_BATCH_NON_SECURE)); intel_ring_emit(ring, offset + len - 8); intel_ring_emit(ring, 0); intel_ring_advance(ring); @@ -1004,7 +1007,8 @@ i830_dispatch_execbuffer(struct intel_ring_buffer *ring, static int i915_dispatch_execbuffer(struct intel_ring_buffer *ring, - u32 offset, u32 len) + u32 offset, u32 len, + unsigned flags) { int ret; @@ -1013,7 +1017,7 @@ i915_dispatch_execbuffer(struct intel_ring_buffer *ring, return ret; intel_ring_emit(ring, MI_BATCH_BUFFER_START | MI_BATCH_GTT); - intel_ring_emit(ring, offset | MI_BATCH_NON_SECURE); + intel_ring_emit(ring, offset | (flags & I915_DISPATCH_SECURE ? 0 : MI_BATCH_NON_SECURE)); intel_ring_advance(ring); return 0; @@ -1402,9 +1406,31 @@ static int gen6_ring_flush(struct intel_ring_buffer *ring, return 0; } +static int +hsw_ring_dispatch_execbuffer(struct intel_ring_buffer *ring, + u32 offset, u32 len, + unsigned flags) +{ + int ret; + + ret = intel_ring_begin(ring, 2); + if (ret) + return ret; + + intel_ring_emit(ring, + MI_BATCH_BUFFER_START | MI_BATCH_PPGTT_HSW | + (flags & I915_DISPATCH_SECURE ? 0 : MI_BATCH_NON_SECURE_HSW)); + /* bit0-7 is the length on GEN6+ */ + intel_ring_emit(ring, offset); + intel_ring_advance(ring); + + return 0; +} + static int gen6_ring_dispatch_execbuffer(struct intel_ring_buffer *ring, - u32 offset, u32 len) + u32 offset, u32 len, + unsigned flags) { int ret; @@ -1412,7 +1438,9 @@ gen6_ring_dispatch_execbuffer(struct intel_ring_buffer *ring, if (ret) return ret; - intel_ring_emit(ring, MI_BATCH_BUFFER_START | MI_BATCH_NON_SECURE_I965); + intel_ring_emit(ring, + MI_BATCH_BUFFER_START | + (flags & I915_DISPATCH_SECURE ? 0 : MI_BATCH_NON_SECURE_I965)); /* bit0-7 is the length on GEN6+ */ intel_ring_emit(ring, offset); intel_ring_advance(ring); @@ -1491,7 +1519,9 @@ int intel_init_render_ring_buffer(struct drm_device *dev) ring->irq_enable_mask = I915_USER_INTERRUPT; } ring->write_tail = ring_write_tail; - if (INTEL_INFO(dev)->gen >= 6) + if (IS_HASWELL(dev)) + ring->dispatch_execbuffer = hsw_ring_dispatch_execbuffer; + else if (INTEL_INFO(dev)->gen >= 6) ring->dispatch_execbuffer = gen6_ring_dispatch_execbuffer; else if (INTEL_INFO(dev)->gen >= 4) ring->dispatch_execbuffer = i965_dispatch_execbuffer; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 2ea7a311a1f0..3745d1dc1fa1 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -81,7 +81,9 @@ struct intel_ring_buffer { u32 (*get_seqno)(struct intel_ring_buffer *ring, bool lazy_coherency); int (*dispatch_execbuffer)(struct intel_ring_buffer *ring, - u32 offset, u32 length); + u32 offset, u32 length, + unsigned flags); +#define I915_DISPATCH_SECURE 0x1 void (*cleanup)(struct intel_ring_buffer *ring); int (*sync_to)(struct intel_ring_buffer *ring, struct intel_ring_buffer *to, diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index c8833009f37b..0e6e135f5397 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -314,6 +314,7 @@ typedef struct drm_i915_irq_wait { #define I915_PARAM_HAS_SEMAPHORES 20 #define I915_PARAM_HAS_PRIME_VMAP_FLUSH 21 #define I915_PARAM_RSVD_FOR_FUTURE_USE 22 +#define I915_PARAM_HAS_SECURE_BATCHES 23 typedef struct drm_i915_getparam { int param; @@ -679,6 +680,11 @@ struct drm_i915_gem_execbuffer2 { /** Resets the SO write offset registers for transform feedback on gen7. */ #define I915_EXEC_GEN7_SOL_RESET (1<<8) +/** Request a privileged ("secure") batch buffer. Note only available for + * DRM_ROOT_ONLY | DRM_MASTER processes. + */ +#define I915_EXEC_SECURE (1<<9) + #define I915_EXEC_CONTEXT_ID_MASK (0xffffffff) #define i915_execbuffer2_set_context_id(eb2, context) \ (eb2).rsvd1 = context & I915_EXEC_CONTEXT_ID_MASK -- cgit v1.2.3-59-g8ed1b From 1ffdff134eb2d943bde3e4901ac48a9656a7e7a5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 18 Oct 2012 10:15:24 +0200 Subject: drm: dp helper: extract drm_dp_channel_eq_ok radeon and intel use the exact same definition. Reviewed-by: Alex Deucher Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_dp_helper.c | 50 ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_dp.c | 35 ++----------------------- drivers/gpu/drm/radeon/atombios_dp.c | 24 ++--------------- include/drm/drm_dp_helper.h | 5 ++++ 4 files changed, 59 insertions(+), 55 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index bb4eaf60117f..1378b789bd10 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -205,3 +205,53 @@ i2c_dp_aux_add_bus(struct i2c_adapter *adapter) return error; } EXPORT_SYMBOL(i2c_dp_aux_add_bus); + +/* Helpers for DP link training */ +static u8 dp_link_status(u8 link_status[DP_LINK_STATUS_SIZE], int r) +{ + return link_status[r - DP_LANE0_1_STATUS]; +} + +static u8 dp_get_lane_status(u8 link_status[DP_LINK_STATUS_SIZE], + int lane) +{ + int i = DP_LANE0_1_STATUS + (lane >> 1); + int s = (lane & 1) * 4; + u8 l = dp_link_status(link_status, i); + return (l >> s) & 0xf; +} + +bool drm_dp_channel_eq_ok(u8 link_status[DP_LINK_STATUS_SIZE], + int lane_count) +{ + u8 lane_align; + u8 lane_status; + int lane; + + lane_align = dp_link_status(link_status, + DP_LANE_ALIGN_STATUS_UPDATED); + if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) + return false; + for (lane = 0; lane < lane_count; lane++) { + lane_status = dp_get_lane_status(link_status, lane); + if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS) + return false; + } + return true; +} +EXPORT_SYMBOL(drm_dp_channel_eq_ok); + +bool drm_dp_clock_recovery_ok(u8 link_status[DP_LINK_STATUS_SIZE], + int lane_count) +{ + int lane; + u8 lane_status; + + for (lane = 0; lane < lane_count; lane++) { + lane_status = dp_get_lane_status(link_status, lane); + if ((lane_status & DP_LANE_CR_DONE) == 0) + return false; + } + return true; +} +EXPORT_SYMBOL(drm_dp_clock_recovery_ok); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 38305c93754c..f69044b7f008 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -37,7 +37,6 @@ #include "i915_drv.h" #define DP_RECEIVER_CAP_SIZE 0xf -#define DP_LINK_STATUS_SIZE 6 #define DP_LINK_CHECK_TIMEOUT (10 * 1000) /** @@ -1436,13 +1435,6 @@ intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_ DP_LINK_STATUS_SIZE); } -static uint8_t -intel_dp_link_status(uint8_t link_status[DP_LINK_STATUS_SIZE], - int r) -{ - return link_status[r - DP_LANE0_1_STATUS]; -} - static uint8_t intel_get_adjust_request_voltage(uint8_t adjust_request[2], int lane) @@ -1728,29 +1720,6 @@ intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count return true; } -/* Check to see if channel eq is done on all channels */ -#define CHANNEL_EQ_BITS (DP_LANE_CR_DONE|\ - DP_LANE_CHANNEL_EQ_DONE|\ - DP_LANE_SYMBOL_LOCKED) -static bool -intel_channel_eq_ok(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]) -{ - uint8_t lane_align; - uint8_t lane_status; - int lane; - - lane_align = intel_dp_link_status(link_status, - DP_LANE_ALIGN_STATUS_UPDATED); - if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) - return false; - for (lane = 0; lane < intel_dp->lane_count; lane++) { - lane_status = intel_get_lane_status(link_status, lane); - if ((lane_status & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS) - return false; - } - return true; -} - static bool intel_dp_set_link_train(struct intel_dp *intel_dp, uint32_t dp_reg_value, @@ -2004,7 +1973,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) continue; } - if (intel_channel_eq_ok(intel_dp, link_status)) { + if (drm_dp_channel_eq_ok(link_status, intel_dp->lane_count)) { channel_eq = true; break; } @@ -2223,7 +2192,7 @@ intel_dp_check_link_status(struct intel_dp *intel_dp) DRM_DEBUG_DRIVER("CP or sink specific irq unhandled\n"); } - if (!intel_channel_eq_ok(intel_dp, link_status)) { + if (!drm_dp_channel_eq_ok(link_status, intel_dp->lane_count)) { DRM_DEBUG_KMS("%s: channel EQ not ok, retraining\n", drm_get_encoder_name(&intel_dp->base.base)); intel_dp_start_link_train(intel_dp); diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index d5699fe4f1e8..3f46bb1bb987 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -34,7 +34,6 @@ /* move these to drm_dp_helper.c/h */ #define DP_LINK_CONFIGURATION_SIZE 9 -#define DP_LINK_STATUS_SIZE 6 #define DP_DPCD_SIZE 8 static char *voltage_names[] = { @@ -318,25 +317,6 @@ static bool dp_clock_recovery_ok(u8 link_status[DP_LINK_STATUS_SIZE], return true; } -static bool dp_channel_eq_ok(u8 link_status[DP_LINK_STATUS_SIZE], - int lane_count) -{ - u8 lane_align; - u8 lane_status; - int lane; - - lane_align = dp_link_status(link_status, - DP_LANE_ALIGN_STATUS_UPDATED); - if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) - return false; - for (lane = 0; lane < lane_count; lane++) { - lane_status = dp_get_lane_status(link_status, lane); - if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS) - return false; - } - return true; -} - static u8 dp_get_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE], int lane) @@ -664,7 +644,7 @@ bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector) if (!radeon_dp_get_link_status(radeon_connector, link_status)) return false; - if (dp_channel_eq_ok(link_status, dig->dp_lane_count)) + if (drm_dp_channel_eq_ok(link_status, dig->dp_lane_count)) return false; return true; } @@ -896,7 +876,7 @@ static int radeon_dp_link_train_ce(struct radeon_dp_link_train_info *dp_info) break; } - if (dp_channel_eq_ok(dp_info->link_status, dp_info->dp_lane_count)) { + if (drm_dp_channel_eq_ok(dp_info->link_status, dp_info->dp_lane_count)) { channel_eq = true; break; } diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index fe061489f91f..9e1042073f67 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -322,4 +322,9 @@ struct i2c_algo_dp_aux_data { int i2c_dp_aux_add_bus(struct i2c_adapter *adapter); + +#define DP_LINK_STATUS_SIZE 6 +bool drm_dp_channel_eq_ok(u8 link_status[DP_LINK_STATUS_SIZE], + int lane_count); + #endif /* _DRM_DP_HELPER_H_ */ -- cgit v1.2.3-59-g8ed1b From 01916270b840f7f37b7daab936add1747d6afbbf Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 18 Oct 2012 10:15:25 +0200 Subject: drm: dp helper: extract drm_dp_clock_recovery_ok radeon and intel use the exact same definition. Reviewed-by: Alex Deucher Acked-by: Dave Airlie v2: Kill 2 more helpers in intel_dp.c that I've missed. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 29 ++--------------------------- drivers/gpu/drm/radeon/atombios_dp.c | 25 +------------------------ include/drm/drm_dp_helper.h | 2 ++ 3 files changed, 5 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index f69044b7f008..401db1d42da1 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1695,31 +1695,6 @@ intel_dp_signal_levels_hsw(uint8_t train_set) } } -static uint8_t -intel_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE], - int lane) -{ - int s = (lane & 1) * 4; - uint8_t l = link_status[lane>>1]; - - return (l >> s) & 0xf; -} - -/* Check for clock recovery is done on all channels */ -static bool -intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count) -{ - int lane; - uint8_t lane_status; - - for (lane = 0; lane < lane_count; lane++) { - lane_status = intel_get_lane_status(link_status, lane); - if ((lane_status & DP_LANE_CR_DONE) == 0) - return false; - } - return true; -} - static bool intel_dp_set_link_train(struct intel_dp *intel_dp, uint32_t dp_reg_value, @@ -1885,7 +1860,7 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) break; } - if (intel_clock_recovery_ok(link_status, intel_dp->lane_count)) { + if (drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count)) { DRM_DEBUG_KMS("clock recovery OK\n"); clock_recovery = true; break; @@ -1967,7 +1942,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) break; /* Make sure clock is still ok */ - if (!intel_clock_recovery_ok(link_status, intel_dp->lane_count)) { + if (!drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count)) { intel_dp_start_link_train(intel_dp); cr_tries++; continue; diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 3f46bb1bb987..65f0c6049472 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -294,29 +294,6 @@ static u8 dp_link_status(u8 link_status[DP_LINK_STATUS_SIZE], int r) return link_status[r - DP_LANE0_1_STATUS]; } -static u8 dp_get_lane_status(u8 link_status[DP_LINK_STATUS_SIZE], - int lane) -{ - int i = DP_LANE0_1_STATUS + (lane >> 1); - int s = (lane & 1) * 4; - u8 l = dp_link_status(link_status, i); - return (l >> s) & 0xf; -} - -static bool dp_clock_recovery_ok(u8 link_status[DP_LINK_STATUS_SIZE], - int lane_count) -{ - int lane; - u8 lane_status; - - for (lane = 0; lane < lane_count; lane++) { - lane_status = dp_get_lane_status(link_status, lane); - if ((lane_status & DP_LANE_CR_DONE) == 0) - return false; - } - return true; -} - static u8 dp_get_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE], int lane) @@ -811,7 +788,7 @@ static int radeon_dp_link_train_cr(struct radeon_dp_link_train_info *dp_info) break; } - if (dp_clock_recovery_ok(dp_info->link_status, dp_info->dp_lane_count)) { + if (drm_dp_clock_recovery_ok(dp_info->link_status, dp_info->dp_lane_count)) { clock_recovery = true; break; } diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 9e1042073f67..89e92c95cf86 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -326,5 +326,7 @@ i2c_dp_aux_add_bus(struct i2c_adapter *adapter); #define DP_LINK_STATUS_SIZE 6 bool drm_dp_channel_eq_ok(u8 link_status[DP_LINK_STATUS_SIZE], int lane_count); +bool drm_dp_clock_recovery_ok(u8 link_status[DP_LINK_STATUS_SIZE], + int lane_count); #endif /* _DRM_DP_HELPER_H_ */ -- cgit v1.2.3-59-g8ed1b From 0f037bdee1a12947a0c55b21a05f57793332bc07 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 18 Oct 2012 10:15:27 +0200 Subject: drm: extract helpers to compute new training values from sink request Safe for the minor difference that the intel versions get an offset into the link_status as an argument, both are the same again. Reviewed-by: Alex Deucher Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_dp_helper.c | 27 +++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_dp.c | 30 ++---------------------------- drivers/gpu/drm/radeon/atombios_dp.c | 34 ++-------------------------------- include/drm/drm_dp_helper.h | 4 ++++ 4 files changed, 35 insertions(+), 60 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 1378b789bd10..9461e2f27316 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -255,3 +255,30 @@ bool drm_dp_clock_recovery_ok(u8 link_status[DP_LINK_STATUS_SIZE], return true; } EXPORT_SYMBOL(drm_dp_clock_recovery_ok); + +u8 drm_dp_get_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE], + int lane) +{ + int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); + int s = ((lane & 1) ? + DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : + DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); + u8 l = dp_link_status(link_status, i); + + return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; +} +EXPORT_SYMBOL(drm_dp_get_adjust_request_voltage); + +u8 drm_dp_get_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE], + int lane) +{ + int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); + int s = ((lane & 1) ? + DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : + DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); + u8 l = dp_link_status(link_status, i); + + return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; +} +EXPORT_SYMBOL(drm_dp_get_adjust_request_pre_emphasis); + diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 401db1d42da1..016febc9b55e 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1435,31 +1435,6 @@ intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_ DP_LINK_STATUS_SIZE); } -static uint8_t -intel_get_adjust_request_voltage(uint8_t adjust_request[2], - int lane) -{ - int s = ((lane & 1) ? - DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : - DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); - uint8_t l = adjust_request[lane>>1]; - - return ((l >> s) & 3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; -} - -static uint8_t -intel_get_adjust_request_pre_emphasis(uint8_t adjust_request[2], - int lane) -{ - int s = ((lane & 1) ? - DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : - DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); - uint8_t l = adjust_request[lane>>1]; - - return ((l >> s) & 3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; -} - - #if 0 static char *voltage_names[] = { "0.4V", "0.6V", "0.8V", "1.2V" @@ -1538,13 +1513,12 @@ intel_get_adjust_train(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_ST uint8_t v = 0; uint8_t p = 0; int lane; - uint8_t *adjust_request = link_status + (DP_ADJUST_REQUEST_LANE0_1 - DP_LANE0_1_STATUS); uint8_t voltage_max; uint8_t preemph_max; for (lane = 0; lane < intel_dp->lane_count; lane++) { - uint8_t this_v = intel_get_adjust_request_voltage(adjust_request, lane); - uint8_t this_p = intel_get_adjust_request_pre_emphasis(adjust_request, lane); + uint8_t this_v = drm_dp_get_adjust_request_voltage(link_status, lane); + uint8_t this_p = drm_dp_get_adjust_request_pre_emphasis(link_status, lane); if (this_v > v) v = this_v; diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 65f0c6049472..5ad8bfacf728 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -289,36 +289,6 @@ int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, /***** general DP utility functions *****/ -static u8 dp_link_status(u8 link_status[DP_LINK_STATUS_SIZE], int r) -{ - return link_status[r - DP_LANE0_1_STATUS]; -} - -static u8 dp_get_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE], - int lane) - -{ - int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); - int s = ((lane & 1) ? - DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : - DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); - u8 l = dp_link_status(link_status, i); - - return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; -} - -static u8 dp_get_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE], - int lane) -{ - int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); - int s = ((lane & 1) ? - DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : - DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); - u8 l = dp_link_status(link_status, i); - - return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; -} - #define DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_1200 #define DP_PRE_EMPHASIS_MAX DP_TRAIN_PRE_EMPHASIS_9_5 @@ -331,8 +301,8 @@ static void dp_get_adjust_train(u8 link_status[DP_LINK_STATUS_SIZE], int lane; for (lane = 0; lane < lane_count; lane++) { - u8 this_v = dp_get_adjust_request_voltage(link_status, lane); - u8 this_p = dp_get_adjust_request_pre_emphasis(link_status, lane); + u8 this_v = drm_dp_get_adjust_request_voltage(link_status, lane); + u8 this_p = drm_dp_get_adjust_request_pre_emphasis(link_status, lane); DRM_DEBUG_KMS("requested signal parameters: lane %d voltage %s pre_emph %s\n", lane, diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 89e92c95cf86..57e6dbd0580c 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -328,5 +328,9 @@ bool drm_dp_channel_eq_ok(u8 link_status[DP_LINK_STATUS_SIZE], int lane_count); bool drm_dp_clock_recovery_ok(u8 link_status[DP_LINK_STATUS_SIZE], int lane_count); +u8 drm_dp_get_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE], + int lane); +u8 drm_dp_get_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE], + int lane); #endif /* _DRM_DP_HELPER_H_ */ -- cgit v1.2.3-59-g8ed1b From 1a644cd47ca0c40a9210db170bd0630031c3a60b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 18 Oct 2012 15:32:40 +0200 Subject: drm: extract dp link train delay functions from radeon This requires a few changes since that dpcd value is above the range currently cached by radeon. I've check the dp specs, and above 0xf there's a big gap and nothing that looks like we should cache it while a given device is plugged in. It's also the same value that i915.ko uses. Hence extend the various dpcd arrays in the radeon driver, use proper symbolic constants where applicable (one place overallocated the dpcd array to 25 bytes). Then also drop the rd_interval cache - radeon_dp_link_train_init re-reads the dpcd block, so the values we'll consume in train_cr and train_ce will always be fresh. To avoid needless diff-churn, #define the old size of dpcd as the new one and keep it around. v2: Alex Deucher noticed one place where I've forgotten to replace 8 with DP_RECEIVER_CAP_SIZE. Reviewed-by: Alex Deucher Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_dp_helper.c | 15 +++++++++++++++ drivers/gpu/drm/i915/intel_dp.c | 1 - drivers/gpu/drm/radeon/atombios_dp.c | 27 ++++++++++----------------- drivers/gpu/drm/radeon/radeon_mode.h | 2 +- include/drm/drm_dp_helper.h | 5 +++++ 5 files changed, 31 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 9461e2f27316..7ecaa11f35f6 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -282,3 +282,18 @@ u8 drm_dp_get_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE], } EXPORT_SYMBOL(drm_dp_get_adjust_request_pre_emphasis); +void drm_dp_link_train_clock_recovery_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]) { + if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0) + udelay(100); + else + mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4); +} +EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay); + +void drm_dp_link_train_channel_eq_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]) { + if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0) + udelay(400); + else + mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4); +} +EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 016febc9b55e..cd23ffadfda1 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -36,7 +36,6 @@ #include #include "i915_drv.h" -#define DP_RECEIVER_CAP_SIZE 0xf #define DP_LINK_CHECK_TIMEOUT (10 * 1000) /** diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 5ad8bfacf728..5e23ab27ae46 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -34,7 +34,7 @@ /* move these to drm_dp_helper.c/h */ #define DP_LINK_CONFIGURATION_SIZE 9 -#define DP_DPCD_SIZE 8 +#define DP_DPCD_SIZE DP_RECEIVER_CAP_SIZE static char *voltage_names[] = { "0.4V", "0.6V", "0.8V", "1.2V" @@ -478,14 +478,15 @@ static void radeon_dp_probe_oui(struct radeon_connector *radeon_connector) bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector) { struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; - u8 msg[25]; + u8 msg[DP_DPCD_SIZE]; int ret, i; - ret = radeon_dp_aux_native_read(radeon_connector, DP_DPCD_REV, msg, 8, 0); + ret = radeon_dp_aux_native_read(radeon_connector, DP_DPCD_REV, msg, + DP_DPCD_SIZE, 0); if (ret > 0) { - memcpy(dig_connector->dpcd, msg, 8); + memcpy(dig_connector->dpcd, msg, DP_DPCD_SIZE); DRM_DEBUG_KMS("DPCD: "); - for (i = 0; i < 8; i++) + for (i = 0; i < DP_DPCD_SIZE; i++) DRM_DEBUG_KMS("%02x ", msg[i]); DRM_DEBUG_KMS("\n"); @@ -604,9 +605,8 @@ struct radeon_dp_link_train_info { int enc_id; int dp_clock; int dp_lane_count; - int rd_interval; bool tp3_supported; - u8 dpcd[8]; + u8 dpcd[DP_RECEIVER_CAP_SIZE]; u8 train_set[4]; u8 link_status[DP_LINK_STATUS_SIZE]; u8 tries; @@ -748,10 +748,7 @@ static int radeon_dp_link_train_cr(struct radeon_dp_link_train_info *dp_info) dp_info->tries = 0; voltage = 0xff; while (1) { - if (dp_info->rd_interval == 0) - udelay(100); - else - mdelay(dp_info->rd_interval * 4); + drm_dp_link_train_clock_recovery_delay(dp_info->dpcd); if (!radeon_dp_get_link_status(dp_info->radeon_connector, dp_info->link_status)) { DRM_ERROR("displayport link status failed\n"); @@ -813,10 +810,7 @@ static int radeon_dp_link_train_ce(struct radeon_dp_link_train_info *dp_info) dp_info->tries = 0; channel_eq = false; while (1) { - if (dp_info->rd_interval == 0) - udelay(400); - else - mdelay(dp_info->rd_interval * 4); + drm_dp_link_train_channel_eq_delay(dp_info->dpcd); if (!radeon_dp_get_link_status(dp_info->radeon_connector, dp_info->link_status)) { DRM_ERROR("displayport link status failed\n"); @@ -901,14 +895,13 @@ void radeon_dp_link_train(struct drm_encoder *encoder, else dp_info.enc_id |= ATOM_DP_CONFIG_LINK_A; - dp_info.rd_interval = radeon_read_dpcd_reg(radeon_connector, DP_TRAINING_AUX_RD_INTERVAL); tmp = radeon_read_dpcd_reg(radeon_connector, DP_MAX_LANE_COUNT); if (ASIC_IS_DCE5(rdev) && (tmp & DP_TPS3_SUPPORTED)) dp_info.tp3_supported = true; else dp_info.tp3_supported = false; - memcpy(dp_info.dpcd, dig_connector->dpcd, 8); + memcpy(dp_info.dpcd, dig_connector->dpcd, DP_RECEIVER_CAP_SIZE); dp_info.rdev = rdev; dp_info.encoder = encoder; dp_info.connector = connector; diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 92c5f473cf08..d818b503b42f 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -427,7 +427,7 @@ struct radeon_connector_atom_dig { uint32_t igp_lane_info; /* displayport */ struct radeon_i2c_chan *dp_i2c_bus; - u8 dpcd[8]; + u8 dpcd[DP_RECEIVER_CAP_SIZE]; u8 dp_sink_type; int dp_clock; int dp_lane_count; diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 57e6dbd0580c..60bd8d3ae6eb 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -25,6 +25,7 @@ #include #include +#include /* * Unless otherwise noted, all values are from the DP 1.1a spec. Note that @@ -333,4 +334,8 @@ u8 drm_dp_get_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE], u8 drm_dp_get_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE], int lane); +#define DP_RECEIVER_CAP_SIZE 0xf +void drm_dp_link_train_clock_recovery_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]); +void drm_dp_link_train_channel_eq_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]); + #endif /* _DRM_DP_HELPER_H_ */ -- cgit v1.2.3-59-g8ed1b From 3b5c662e8f536ca47396116de82f08d771727076 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 18 Oct 2012 10:15:31 +0200 Subject: drm: extract dp link bw helpers Reviewed-by: Alex Deucher Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_dp_helper.c | 28 ++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_dp.c | 5 +---- drivers/gpu/drm/radeon/atombios_dp.c | 32 +++----------------------------- include/drm/drm_dp_helper.h | 8 ++++++++ 4 files changed, 40 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 7ecaa11f35f6..3c4cccd0d753 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -297,3 +297,31 @@ void drm_dp_link_train_channel_eq_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]) { mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4); } EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay); + +u8 drm_dp_link_rate_to_bw_code(int link_rate) +{ + switch (link_rate) { + case 162000: + default: + return DP_LINK_BW_1_62; + case 270000: + return DP_LINK_BW_2_7; + case 540000: + return DP_LINK_BW_5_4; + } +} +EXPORT_SYMBOL(drm_dp_link_rate_to_bw_code); + +int drm_dp_bw_code_to_link_rate(u8 link_bw) +{ + switch (link_bw) { + case DP_LINK_BW_1_62: + default: + return 162000; + case DP_LINK_BW_2_7: + return 270000; + case DP_LINK_BW_5_4: + return 540000; + } +} +EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 3cb180e38ca1..f7b7bfc455e2 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -108,10 +108,7 @@ intel_edp_link_config(struct intel_encoder *intel_encoder, struct intel_dp *intel_dp = container_of(intel_encoder, struct intel_dp, base); *lane_num = intel_dp->lane_count; - if (intel_dp->link_bw == DP_LINK_BW_1_62) - *link_bw = 162000; - else if (intel_dp->link_bw == DP_LINK_BW_2_7) - *link_bw = 270000; + *link_bw = drm_dp_bw_code_to_link_rate(intel_dp->link_bw); } int diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 5e23ab27ae46..093e17d07574 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -347,37 +347,11 @@ static int dp_get_max_dp_pix_clock(int link_rate, return (link_rate * lane_num * 8) / bpp; } -static int dp_get_max_link_rate(u8 dpcd[DP_DPCD_SIZE]) -{ - switch (dpcd[DP_MAX_LINK_RATE]) { - case DP_LINK_BW_1_62: - default: - return 162000; - case DP_LINK_BW_2_7: - return 270000; - case DP_LINK_BW_5_4: - return 540000; - } -} - static u8 dp_get_max_lane_number(u8 dpcd[DP_DPCD_SIZE]) { return dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; } -static u8 dp_get_dp_link_rate_coded(int link_rate) -{ - switch (link_rate) { - case 162000: - default: - return DP_LINK_BW_1_62; - case 270000: - return DP_LINK_BW_2_7; - case 540000: - return DP_LINK_BW_5_4; - } -} - /***** radeon specific DP functions *****/ /* First get the min lane# when low rate is used according to pixel clock @@ -389,7 +363,7 @@ static int radeon_dp_get_dp_lane_number(struct drm_connector *connector, int pix_clock) { int bpp = convert_bpc_to_bpp(radeon_get_monitor_bpc(connector)); - int max_link_rate = dp_get_max_link_rate(dpcd); + int max_link_rate = drm_dp_max_link_rate(dpcd); int max_lane_num = dp_get_max_lane_number(dpcd); int lane_num; int max_dp_pix_clock; @@ -427,7 +401,7 @@ static int radeon_dp_get_dp_link_clock(struct drm_connector *connector, return 540000; } - return dp_get_max_link_rate(dpcd); + return drm_dp_max_link_rate(dpcd); } static u8 radeon_dp_encoder_service(struct radeon_device *rdev, @@ -692,7 +666,7 @@ static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info) radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LANE_COUNT_SET, tmp); /* set the link rate on the sink */ - tmp = dp_get_dp_link_rate_coded(dp_info->dp_clock); + tmp = drm_dp_link_rate_to_bw_code(dp_info->dp_clock); radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LINK_BW_SET, tmp); /* start training on the source */ diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 60bd8d3ae6eb..455f8e05ca3f 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -338,4 +338,12 @@ u8 drm_dp_get_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE], void drm_dp_link_train_clock_recovery_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]); void drm_dp_link_train_channel_eq_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]); +u8 drm_dp_link_rate_to_bw_code(int link_rate); +int drm_dp_bw_code_to_link_rate(u8 link_bw); + +static inline int +drm_dp_max_link_rate(u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return drm_dp_bw_code_to_link_rate(dpcd[DP_MAX_LINK_RATE]); +} #endif /* _DRM_DP_HELPER_H_ */ -- cgit v1.2.3-59-g8ed1b From 397fe15715ef1457d89f52666d0e249eb5eae64c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 22 Oct 2012 22:56:43 +0200 Subject: drm: extract drm_dp_max_lane_count helper Reviewed-by: Alex Deucher Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 17 ++--------------- drivers/gpu/drm/radeon/atombios_dp.c | 7 +------ include/drm/drm_dp_helper.h | 7 +++++++ 3 files changed, 10 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index f7b7bfc455e2..e0b91cb75d63 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -124,19 +124,6 @@ intel_edp_target_clock(struct intel_encoder *intel_encoder, return mode->clock; } -static int -intel_dp_max_lane_count(struct intel_dp *intel_dp) -{ - int max_lane_count = intel_dp->dpcd[DP_MAX_LANE_COUNT] & 0x1f; - switch (max_lane_count) { - case 1: case 2: case 4: - break; - default: - max_lane_count = 4; - } - return max_lane_count; -} - static int intel_dp_max_link_bw(struct intel_dp *intel_dp) { @@ -197,7 +184,7 @@ intel_dp_adjust_dithering(struct intel_dp *intel_dp, bool adjust_mode) { int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_dp)); - int max_lanes = intel_dp_max_lane_count(intel_dp); + int max_lanes = drm_dp_max_lane_count(intel_dp->dpcd); int max_rate, mode_rate; mode_rate = intel_dp_link_required(mode->clock, 24); @@ -699,7 +686,7 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct intel_connector *intel_connector = intel_dp->attached_connector; int lane_count, clock; - int max_lane_count = intel_dp_max_lane_count(intel_dp); + int max_lane_count = drm_dp_max_lane_count(intel_dp->dpcd); int max_clock = intel_dp_max_link_bw(intel_dp) == DP_LINK_BW_2_7 ? 1 : 0; int bpp, mode_rate; static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 }; diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 093e17d07574..064023bed480 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -347,11 +347,6 @@ static int dp_get_max_dp_pix_clock(int link_rate, return (link_rate * lane_num * 8) / bpp; } -static u8 dp_get_max_lane_number(u8 dpcd[DP_DPCD_SIZE]) -{ - return dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; -} - /***** radeon specific DP functions *****/ /* First get the min lane# when low rate is used according to pixel clock @@ -364,7 +359,7 @@ static int radeon_dp_get_dp_lane_number(struct drm_connector *connector, { int bpp = convert_bpc_to_bpp(radeon_get_monitor_bpc(connector)); int max_link_rate = drm_dp_max_link_rate(dpcd); - int max_lane_num = dp_get_max_lane_number(dpcd); + int max_lane_num = drm_dp_max_lane_count(dpcd); int lane_num; int max_dp_pix_clock; diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 455f8e05ca3f..c09d36741c94 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -346,4 +346,11 @@ drm_dp_max_link_rate(u8 dpcd[DP_RECEIVER_CAP_SIZE]) { return drm_dp_bw_code_to_link_rate(dpcd[DP_MAX_LINK_RATE]); } + +static inline u8 +drm_dp_max_lane_count(u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; +} + #endif /* _DRM_DP_HELPER_H_ */ -- cgit v1.2.3-59-g8ed1b From 0b3904ab2a48488e23332ac1ecd2d45961ec6718 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Thu, 25 Oct 2012 18:05:05 +0000 Subject: drm: Constify some function arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit None of drm_mode_debug_printmodeline(), drm_mode_equal(), drm_mode_width() or drm_mode_height() change the mode passed in, so make the arguments const. Signed-off-by: Ville Syrjälä Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_modes.c | 8 ++++---- include/drm/drm_crtc.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 59450f39bf96..d8da30e90db5 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -46,7 +46,7 @@ * * Describe @mode using DRM_DEBUG. */ -void drm_mode_debug_printmodeline(struct drm_display_mode *mode) +void drm_mode_debug_printmodeline(const struct drm_display_mode *mode) { DRM_DEBUG_KMS("Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d " "0x%x 0x%x\n", @@ -558,7 +558,7 @@ EXPORT_SYMBOL(drm_mode_list_concat); * RETURNS: * @mode->hdisplay */ -int drm_mode_width(struct drm_display_mode *mode) +int drm_mode_width(const struct drm_display_mode *mode) { return mode->hdisplay; @@ -579,7 +579,7 @@ EXPORT_SYMBOL(drm_mode_width); * RETURNS: * @mode->vdisplay */ -int drm_mode_height(struct drm_display_mode *mode) +int drm_mode_height(const struct drm_display_mode *mode) { return mode->vdisplay; } @@ -768,7 +768,7 @@ EXPORT_SYMBOL(drm_mode_duplicate); * RETURNS: * True if the modes are equal, false otherwise. */ -bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2) +bool drm_mode_equal(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2) { /* do clock check convert to PICOS so fb modes get matched * the same */ diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 3fa18b7e9497..49dd8c2eea7d 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -887,14 +887,14 @@ extern void drm_mode_remove(struct drm_connector *connector, struct drm_display_ extern void drm_mode_copy(struct drm_display_mode *dst, const struct drm_display_mode *src); extern struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, const struct drm_display_mode *mode); -extern void drm_mode_debug_printmodeline(struct drm_display_mode *mode); +extern void drm_mode_debug_printmodeline(const struct drm_display_mode *mode); extern void drm_mode_config_init(struct drm_device *dev); extern void drm_mode_config_reset(struct drm_device *dev); extern void drm_mode_config_cleanup(struct drm_device *dev); extern void drm_mode_set_name(struct drm_display_mode *mode); -extern bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2); -extern int drm_mode_width(struct drm_display_mode *mode); -extern int drm_mode_height(struct drm_display_mode *mode); +extern bool drm_mode_equal(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2); +extern int drm_mode_width(const struct drm_display_mode *mode); +extern int drm_mode_height(const struct drm_display_mode *mode); /* for us by fb module */ extern int drm_mode_attachmode_crtc(struct drm_device *dev, -- cgit v1.2.3-59-g8ed1b From a9dbfff1cbe5972ae0ef07b51530a70240ec9f2c Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Fri, 12 Oct 2012 16:58:36 +0200 Subject: drm/ttm: add ttm_bo_is_reserved Signed-off-by: Maarten Lankhorst Reviewed-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/ttm/ttm_bo.c | 12 ++++++------ include/drm/ttm/ttm_bo_api.h | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index bf6e4b5a73b5..e6bfcfdd3b60 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -162,9 +162,9 @@ int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, bool interruptible) { if (interruptible) { return wait_event_interruptible(bo->event_queue, - atomic_read(&bo->reserved) == 0); + !ttm_bo_is_reserved(bo)); } else { - wait_event(bo->event_queue, atomic_read(&bo->reserved) == 0); + wait_event(bo->event_queue, !ttm_bo_is_reserved(bo)); return 0; } } @@ -175,7 +175,7 @@ void ttm_bo_add_to_lru(struct ttm_buffer_object *bo) struct ttm_bo_device *bdev = bo->bdev; struct ttm_mem_type_manager *man; - BUG_ON(!atomic_read(&bo->reserved)); + BUG_ON(!ttm_bo_is_reserved(bo)); if (!(bo->mem.placement & TTM_PL_FLAG_NO_EVICT)) { @@ -756,7 +756,7 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible, goto out; } - BUG_ON(!atomic_read(&bo->reserved)); + BUG_ON(!ttm_bo_is_reserved(bo)); evict_mem = bo->mem; evict_mem.mm_node = NULL; @@ -1073,7 +1073,7 @@ int ttm_bo_move_buffer(struct ttm_buffer_object *bo, struct ttm_mem_reg mem; struct ttm_bo_device *bdev = bo->bdev; - BUG_ON(!atomic_read(&bo->reserved)); + BUG_ON(!ttm_bo_is_reserved(bo)); /* * FIXME: It's possible to pipeline buffer moves. @@ -1130,7 +1130,7 @@ int ttm_bo_validate(struct ttm_buffer_object *bo, { int ret; - BUG_ON(!atomic_read(&bo->reserved)); + BUG_ON(!ttm_bo_is_reserved(bo)); /* Check that range is valid */ if (placement->lpfn || placement->fpfn) if (placement->fpfn > placement->lpfn || diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h index e8028ade567f..36e8408e00fa 100644 --- a/include/drm/ttm/ttm_bo_api.h +++ b/include/drm/ttm/ttm_bo_api.h @@ -736,4 +736,18 @@ extern ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp, extern void ttm_bo_swapout_all(struct ttm_bo_device *bdev); +/** + * ttm_bo_is_reserved - return an indication if a ttm buffer object is reserved + * + * @bo: The buffer object to check. + * + * This function returns an indication if a bo is reserved or not, and should + * only be used to print an error when it is not from incorrect api usage, since + * there's no guarantee that it is the caller that is holding the reservation. + */ +static inline bool ttm_bo_is_reserved(struct ttm_buffer_object *bo) +{ + return atomic_read(&bo->reserved); +} + #endif -- cgit v1.2.3-59-g8ed1b From cfc1a062063bb5b8e3fa2b007043dfc9e27b5a40 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 27 Oct 2012 15:52:04 +0200 Subject: drm: add helper to sort panels to the head of the connector list Userspace seems to like this, see commit cb0953d734348e8862d6d7edc666cfb3bf6d8fae Author: Adam Jackson Date: Fri Jul 16 14:46:29 2010 -0400 drm/i915: Initialize LVDS and eDP outputs before anything else This makes them sort to the front in X, which makes them likely to be the primary outputs if you haven't specified a preference in your DE, which is likely to be what you want. Signed-off-by: Adam Jackson Signed-off-by: Eric Anholt Sorting the connector list after the fact is much easier than trying to be clever with the init sequence. Acked-by: Dave Airlie Reviewed-by: Adam Jackson Acked-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc_helper.c | 18 ++++++++++++++++++ include/drm/drm_crtc_helper.h | 2 ++ 2 files changed, 20 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 1227adf74dbc..710516815de7 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -39,6 +39,24 @@ #include #include +void drm_helper_move_panel_connectors_to_head(struct drm_device *dev) +{ + struct drm_connector *connector, *tmp; + struct list_head panel_list; + + INIT_LIST_HEAD(&panel_list); + + list_for_each_entry_safe(connector, tmp, + &dev->mode_config.connector_list, head) { + if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS || + connector->connector_type == DRM_MODE_CONNECTOR_eDP) + list_move_tail(&connector->head, &panel_list); + } + + list_splice(&panel_list, &dev->mode_config.connector_list); +} +EXPORT_SYMBOL(drm_helper_move_panel_connectors_to_head); + static bool drm_kms_helper_poll = true; module_param_named(poll, drm_kms_helper_poll, bool, 0600); diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index e01cc80c9c30..defee28f6b95 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -137,6 +137,8 @@ extern bool drm_helper_encoder_in_use(struct drm_encoder *encoder); extern void drm_helper_connector_dpms(struct drm_connector *connector, int mode); +extern void drm_helper_move_panel_connectors_to_head(struct drm_device *); + extern int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, struct drm_mode_fb_cmd2 *mode_cmd); -- cgit v1.2.3-59-g8ed1b From e76e9aebcdbfebae8f4cd147e3c0f800d36e97f3 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Sun, 4 Nov 2012 09:21:27 -0800 Subject: drm/i915: Stop using AGP layer for GEN6+ As a quick hack we make the old intel_gtt structure mutable so we can fool a bunch of the existing code which depends on elements in that data structure. We can/should try to remove this in a subsequent patch. This should preserve the old gtt init behavior which upon writing these patches seems incorrect. The next patch will fix these things. The one exception is VLV which doesn't have the preserved flush control write behavior. Since we want to do that for all GEN6+ stuff, we'll handle that in a later patch. Mainstream VLV support doesn't actually exist yet anyway. v2: Update the comment to remove the "voodoo" Check that the last pte written matches what we readback v3: actually kill cache_level_to_agp_type since most of the flags will disappear in an upcoming patch v4: v3 was actually not what we wanted (Daniel) Make the ggtt bind assertions better and stricter (Chris) Fix some uncaught errors at gtt init (Chris) Some other random stuff that Chris wanted v5: check for i==0 in gen6_ggtt_bind_object to shut up gcc (Ben) Signed-off-by: Ben Widawsky Reviewed-by [v4]: Chris Wilson [danvet: Make the cache_level -> agp_flags conversion for pre-gen6 a tad more robust by mapping everything != CACHE_NONE to the cached agp flag - we have a 1:1 uncached mapping, but different modes of cacheable (at least on later generations). Suggested by Chris Wilson.] Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 2 +- drivers/gpu/drm/i915/i915_dma.c | 16 +- drivers/gpu/drm/i915/i915_drv.h | 10 +- drivers/gpu/drm/i915/i915_gem.c | 12 +- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 2 +- drivers/gpu/drm/i915/i915_gem_gtt.c | 257 +++++++++++++++++++++++++---- drivers/gpu/drm/i915/i915_reg.h | 6 + include/drm/intel-gtt.h | 3 +- 8 files changed, 257 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 38390f7c6ab6..4dfbb80f0fd5 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -1686,7 +1686,7 @@ int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev, } EXPORT_SYMBOL(intel_gmch_probe); -const struct intel_gtt *intel_gtt_get(void) +struct intel_gtt *intel_gtt_get(void) { return &intel_private.base; } diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index ff06e3239ada..1eea5be43617 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1496,19 +1496,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto free_priv; } - ret = intel_gmch_probe(dev_priv->bridge_dev, dev->pdev, NULL); - if (!ret) { - DRM_ERROR("failed to set up gmch\n"); - ret = -EIO; + ret = i915_gem_gtt_init(dev); + if (ret) goto put_bridge; - } - - dev_priv->mm.gtt = intel_gtt_get(); - if (!dev_priv->mm.gtt) { - DRM_ERROR("Failed to initialize GTT\n"); - ret = -ENODEV; - goto put_gmch; - } i915_kick_out_firmware_fb(dev_priv); @@ -1683,7 +1673,7 @@ out_mtrrfree: out_rmmap: pci_iounmap(dev->pdev, dev_priv->regs); put_gmch: - intel_gmch_remove(); + i915_gem_gtt_fini(dev); put_bridge: pci_dev_put(dev_priv->bridge_dev); free_priv: diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c4339c2b1b57..f316916fe65e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -746,7 +746,7 @@ typedef struct drm_i915_private { struct { /** Bridge to intel-gtt-ko */ - const struct intel_gtt *gtt; + struct intel_gtt *gtt; /** Memory allocator for GTT stolen memory */ struct drm_mm stolen; /** Memory allocator for GTT */ @@ -1538,6 +1538,14 @@ void i915_gem_init_global_gtt(struct drm_device *dev, unsigned long start, unsigned long mappable_end, unsigned long end); +int i915_gem_gtt_init(struct drm_device *dev); +void i915_gem_gtt_fini(struct drm_device *dev); +extern inline void i915_gem_chipset_flush(struct drm_device *dev) +{ + if (INTEL_INFO(dev)->gen < 6) + intel_gtt_chipset_flush(); +} + /* i915_gem_evict.c */ int __must_check i915_gem_evict_something(struct drm_device *dev, int min_size, diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index d8eaebfea93e..c161fdbd830f 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -845,12 +845,12 @@ out: * domain anymore. */ if (obj->base.write_domain != I915_GEM_DOMAIN_CPU) { i915_gem_clflush_object(obj); - intel_gtt_chipset_flush(); + i915_gem_chipset_flush(dev); } } if (needs_clflush_after) - intel_gtt_chipset_flush(); + i915_gem_chipset_flush(dev); return ret; } @@ -3058,7 +3058,7 @@ i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj) return; i915_gem_clflush_object(obj); - intel_gtt_chipset_flush(); + i915_gem_chipset_flush(obj->base.dev); old_write_domain = obj->base.write_domain; obj->base.write_domain = 0; @@ -3959,7 +3959,7 @@ i915_gem_init_hw(struct drm_device *dev) drm_i915_private_t *dev_priv = dev->dev_private; int ret; - if (!intel_enable_gtt()) + if (INTEL_INFO(dev)->gen < 6 && !intel_enable_gtt()) return -EIO; if (IS_HASWELL(dev) && (I915_READ(0x120010) == 1)) @@ -4294,7 +4294,7 @@ void i915_gem_detach_phys_object(struct drm_device *dev, page_cache_release(page); } } - intel_gtt_chipset_flush(); + i915_gem_chipset_flush(dev); obj->phys_obj->cur_obj = NULL; obj->phys_obj = NULL; @@ -4381,7 +4381,7 @@ i915_gem_phys_pwrite(struct drm_device *dev, return -EFAULT; } - intel_gtt_chipset_flush(); + i915_gem_chipset_flush(dev); return 0; } diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 91d43d5c4526..d80e9dd00c48 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -672,7 +672,7 @@ i915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring, } if (flush_domains & I915_GEM_DOMAIN_CPU) - intel_gtt_chipset_flush(); + i915_gem_chipset_flush(ring->dev); if (flush_domains & I915_GEM_DOMAIN_GTT) wmb(); diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 06202fd6dbdd..e74be0c2c6a5 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -262,26 +262,6 @@ void i915_ppgtt_unbind_object(struct i915_hw_ppgtt *ppgtt, obj->base.size >> PAGE_SHIFT); } -/* XXX kill agp_type! */ -static unsigned int cache_level_to_agp_type(struct drm_device *dev, - enum i915_cache_level cache_level) -{ - switch (cache_level) { - case I915_CACHE_LLC_MLC: - /* Older chipsets do not have this extra level of CPU - * cacheing, so fallthrough and request the PTE simply - * as cached. - */ - if (INTEL_INFO(dev)->gen >= 6 && !IS_HASWELL(dev)) - return AGP_USER_CACHED_MEMORY_LLC_MLC; - case I915_CACHE_LLC: - return AGP_USER_CACHED_MEMORY; - default: - case I915_CACHE_NONE: - return AGP_USER_MEMORY; - } -} - static bool do_idling(struct drm_i915_private *dev_priv) { bool ret = dev_priv->mm.interruptible; @@ -304,13 +284,38 @@ static void undo_idling(struct drm_i915_private *dev_priv, bool interruptible) dev_priv->mm.interruptible = interruptible; } + +static void i915_ggtt_clear_range(struct drm_device *dev, + unsigned first_entry, + unsigned num_entries) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + gtt_pte_t scratch_pte; + volatile void __iomem *gtt_base = dev_priv->mm.gtt->gtt + first_entry; + const int max_entries = dev_priv->mm.gtt->gtt_total_entries - first_entry; + + if (INTEL_INFO(dev)->gen < 6) { + intel_gtt_clear_range(first_entry, num_entries); + return; + } + + if (WARN(num_entries > max_entries, + "First entry = %d; Num entries = %d (max=%d)\n", + first_entry, num_entries, max_entries)) + num_entries = max_entries; + + scratch_pte = pte_encode(dev, dev_priv->mm.gtt->scratch_page_dma, I915_CACHE_LLC); + memset_io(gtt_base, scratch_pte, num_entries * sizeof(scratch_pte)); + readl(gtt_base); +} + void i915_gem_restore_gtt_mappings(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj; /* First fill our portion of the GTT with scratch pages */ - intel_gtt_clear_range(dev_priv->mm.gtt_start / PAGE_SIZE, + i915_ggtt_clear_range(dev, dev_priv->mm.gtt_start / PAGE_SIZE, (dev_priv->mm.gtt_end - dev_priv->mm.gtt_start) / PAGE_SIZE); list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) { @@ -318,7 +323,7 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev) i915_gem_gtt_bind_object(obj, obj->cache_level); } - intel_gtt_chipset_flush(); + i915_gem_chipset_flush(dev); } int i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj) @@ -334,21 +339,69 @@ int i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj) return 0; } +/* + * Binds an object into the global gtt with the specified cache level. The object + * will be accessible to the GPU via commands whose operands reference offsets + * within the global GTT as well as accessible by the GPU through the GMADR + * mapped BAR (dev_priv->mm.gtt->gtt). + */ +static void gen6_ggtt_bind_object(struct drm_i915_gem_object *obj, + enum i915_cache_level level) +{ + struct drm_device *dev = obj->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct sg_table *st = obj->pages; + struct scatterlist *sg = st->sgl; + const int first_entry = obj->gtt_space->start >> PAGE_SHIFT; + const int max_entries = dev_priv->mm.gtt->gtt_total_entries - first_entry; + gtt_pte_t __iomem *gtt_entries = dev_priv->mm.gtt->gtt + first_entry; + int unused, i = 0; + unsigned int len, m = 0; + dma_addr_t addr; + + for_each_sg(st->sgl, sg, st->nents, unused) { + len = sg_dma_len(sg) >> PAGE_SHIFT; + for (m = 0; m < len; m++) { + addr = sg_dma_address(sg) + (m << PAGE_SHIFT); + gtt_entries[i] = pte_encode(dev, addr, level); + i++; + } + } + + BUG_ON(i > max_entries); + BUG_ON(i != obj->base.size / PAGE_SIZE); + + /* XXX: This serves as a posting read to make sure that the PTE has + * actually been updated. There is some concern that even though + * registers and PTEs are within the same BAR that they are potentially + * of NUMA access patterns. Therefore, even with the way we assume + * hardware should work, we must keep this posting read for paranoia. + */ + if (i != 0) + WARN_ON(readl(>t_entries[i-1]) != pte_encode(dev, addr, level)); +} + void i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj, enum i915_cache_level cache_level) { struct drm_device *dev = obj->base.dev; - unsigned int agp_type = cache_level_to_agp_type(dev, cache_level); + if (INTEL_INFO(dev)->gen < 6) { + unsigned int flags = (cache_level == I915_CACHE_NONE) ? + AGP_USER_MEMORY : AGP_USER_CACHED_MEMORY; + intel_gtt_insert_sg_entries(obj->pages, + obj->gtt_space->start >> PAGE_SHIFT, + flags); + } else { + gen6_ggtt_bind_object(obj, cache_level); + } - intel_gtt_insert_sg_entries(obj->pages, - obj->gtt_space->start >> PAGE_SHIFT, - agp_type); obj->has_global_gtt_mapping = 1; } void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj) { - intel_gtt_clear_range(obj->gtt_space->start >> PAGE_SHIFT, + i915_ggtt_clear_range(obj->base.dev, + obj->gtt_space->start >> PAGE_SHIFT, obj->base.size >> PAGE_SHIFT); obj->has_global_gtt_mapping = 0; @@ -406,5 +459,153 @@ void i915_gem_init_global_gtt(struct drm_device *dev, dev_priv->mm.mappable_gtt_total = min(end, mappable_end) - start; /* ... but ensure that we clear the entire range. */ - intel_gtt_clear_range(start / PAGE_SIZE, (end-start) / PAGE_SIZE); + i915_ggtt_clear_range(dev, start / PAGE_SIZE, (end-start) / PAGE_SIZE); +} + +static int setup_scratch_page(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct page *page; + dma_addr_t dma_addr; + + page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); + if (page == NULL) + return -ENOMEM; + get_page(page); + set_pages_uc(page, 1); + +#ifdef CONFIG_INTEL_IOMMU + dma_addr = pci_map_page(dev->pdev, page, 0, PAGE_SIZE, + PCI_DMA_BIDIRECTIONAL); + if (pci_dma_mapping_error(dev->pdev, dma_addr)) + return -EINVAL; +#else + dma_addr = page_to_phys(page); +#endif + dev_priv->mm.gtt->scratch_page = page; + dev_priv->mm.gtt->scratch_page_dma = dma_addr; + + return 0; +} + +static void teardown_scratch_page(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + set_pages_wb(dev_priv->mm.gtt->scratch_page, 1); + pci_unmap_page(dev->pdev, dev_priv->mm.gtt->scratch_page_dma, + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); + put_page(dev_priv->mm.gtt->scratch_page); + __free_page(dev_priv->mm.gtt->scratch_page); +} + +static inline unsigned int gen6_get_total_gtt_size(u16 snb_gmch_ctl) +{ + snb_gmch_ctl >>= SNB_GMCH_GGMS_SHIFT; + snb_gmch_ctl &= SNB_GMCH_GGMS_MASK; + return snb_gmch_ctl << 20; +} + +static inline unsigned int gen6_get_stolen_size(u16 snb_gmch_ctl) +{ + snb_gmch_ctl >>= SNB_GMCH_GMS_SHIFT; + snb_gmch_ctl &= SNB_GMCH_GMS_MASK; + return snb_gmch_ctl << 25; /* 32 MB units */ +} + +int i915_gem_gtt_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + phys_addr_t gtt_bus_addr; + u16 snb_gmch_ctl; + u32 tmp; + int ret; + + /* On modern platforms we need not worry ourself with the legacy + * hostbridge query stuff. Skip it entirely + */ + if (INTEL_INFO(dev)->gen < 6) { + ret = intel_gmch_probe(dev_priv->bridge_dev, dev->pdev, NULL); + if (!ret) { + DRM_ERROR("failed to set up gmch\n"); + return -EIO; + } + + dev_priv->mm.gtt = intel_gtt_get(); + if (!dev_priv->mm.gtt) { + DRM_ERROR("Failed to initialize GTT\n"); + intel_gmch_remove(); + return -ENODEV; + } + return 0; + } + + dev_priv->mm.gtt = kzalloc(sizeof(*dev_priv->mm.gtt), GFP_KERNEL); + if (!dev_priv->mm.gtt) + return -ENOMEM; + + if (!pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(40))) + pci_set_consistent_dma_mask(dev->pdev, DMA_BIT_MASK(40)); + + pci_read_config_dword(dev->pdev, PCI_BASE_ADDRESS_0, &tmp); + /* For GEN6+ the PTEs for the ggtt live at 2MB + BAR0 */ + gtt_bus_addr = (tmp & PCI_BASE_ADDRESS_MEM_MASK) + (2<<20); + + pci_read_config_dword(dev->pdev, PCI_BASE_ADDRESS_2, &tmp); + dev_priv->mm.gtt->gma_bus_addr = tmp & PCI_BASE_ADDRESS_MEM_MASK; + + /* i9xx_setup */ + pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &snb_gmch_ctl); + dev_priv->mm.gtt->gtt_total_entries = + gen6_get_total_gtt_size(snb_gmch_ctl) / sizeof(gtt_pte_t); + dev_priv->mm.gtt->stolen_size = gen6_get_stolen_size(snb_gmch_ctl); + + dev_priv->mm.gtt->gtt_mappable_entries = pci_resource_len(dev->pdev, 2) >> PAGE_SHIFT; + /* 64/512MB is the current min/max we actually know of, but this is just a + * coarse sanity check. + */ + if ((dev_priv->mm.gtt->gtt_mappable_entries >> 8) < 64 || + dev_priv->mm.gtt->gtt_mappable_entries > dev_priv->mm.gtt->gtt_total_entries) { + DRM_ERROR("Unknown GMADR entries (%d)\n", + dev_priv->mm.gtt->gtt_mappable_entries); + ret = -ENXIO; + goto err_out; + } + + ret = setup_scratch_page(dev); + if (ret) { + DRM_ERROR("Scratch setup failed\n"); + goto err_out; + } + + dev_priv->mm.gtt->gtt = ioremap(gtt_bus_addr, + dev_priv->mm.gtt->gtt_total_entries * sizeof(gtt_pte_t)); + if (!dev_priv->mm.gtt->gtt) { + DRM_ERROR("Failed to map the gtt page table\n"); + teardown_scratch_page(dev); + ret = -ENOMEM; + goto err_out; + } + + /* GMADR is the PCI aperture used by SW to access tiled GFX surfaces in a linear fashion. */ + DRM_INFO("Memory Usable by graphics device = %dK\n", dev_priv->mm.gtt->gtt_total_entries >> 10); + DRM_DEBUG_DRIVER("GMADR size = %dM\n", dev_priv->mm.gtt->gtt_mappable_entries >> 8); + DRM_DEBUG_DRIVER("GTT stolen size = %dM\n", dev_priv->mm.gtt->stolen_size >> 20); + + return 0; + +err_out: + kfree(dev_priv->mm.gtt); + if (INTEL_INFO(dev)->gen < 6) + intel_gmch_remove(); + return ret; +} + +void i915_gem_gtt_fini(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + iounmap(dev_priv->mm.gtt->gtt); + teardown_scratch_page(dev); + if (INTEL_INFO(dev)->gen < 6) + intel_gmch_remove(); + kfree(dev_priv->mm.gtt); } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 0866ac3d0a3f..449403f60e4f 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -41,6 +41,12 @@ */ #define INTEL_GMCH_CTRL 0x52 #define INTEL_GMCH_VGA_DISABLE (1 << 1) +#define SNB_GMCH_CTRL 0x50 +#define SNB_GMCH_GGMS_SHIFT 8 /* GTT Graphics Memory Size */ +#define SNB_GMCH_GGMS_MASK 0x3 +#define SNB_GMCH_GMS_SHIFT 3 /* Graphics Mode Select */ +#define SNB_GMCH_GMS_MASK 0x1f + /* PCI config space */ diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index 2e37e9f02e71..94e8f2c7f9e1 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -3,7 +3,7 @@ #ifndef _DRM_INTEL_GTT_H #define _DRM_INTEL_GTT_H -const struct intel_gtt { +struct intel_gtt { /* Size of memory reserved for graphics by the BIOS */ unsigned int stolen_size; /* Total number of gtt entries. */ @@ -17,6 +17,7 @@ const struct intel_gtt { unsigned int do_idle_maps : 1; /* Share the scratch page dma with ppgtts. */ dma_addr_t scratch_page_dma; + struct page *scratch_page; /* for ppgtt PDE access */ u32 __iomem *gtt; /* needed for ioremap in drm/i915 */ -- cgit v1.2.3-59-g8ed1b From 009946f89b7795699848a922fc2f7804390017d9 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Sun, 4 Nov 2012 09:21:29 -0800 Subject: drm/i915: Kill off now unused gen6+ AGP code v2: Accidently removed an ILK case in i9xx_setup (Nicely found by Chris) CC: Chris Wilson Reviewed-by [v1] : Jesse Barnes Signed-off-by: Ben Widawsky Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-agp.h | 91 ------------- drivers/char/agp/intel-gtt.c | 318 ++----------------------------------------- include/drm/intel-gtt.h | 4 - 3 files changed, 11 insertions(+), 402 deletions(-) (limited to 'include') diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h index 6ec0fff79bc2..1042c1b90376 100644 --- a/drivers/char/agp/intel-agp.h +++ b/drivers/char/agp/intel-agp.h @@ -62,12 +62,6 @@ #define I810_PTE_LOCAL 0x00000002 #define I810_PTE_VALID 0x00000001 #define I830_PTE_SYSTEM_CACHED 0x00000006 -/* GT PTE cache control fields */ -#define GEN6_PTE_UNCACHED 0x00000002 -#define HSW_PTE_UNCACHED 0x00000000 -#define GEN6_PTE_LLC 0x00000004 -#define GEN6_PTE_LLC_MLC 0x00000006 -#define GEN6_PTE_GFDT 0x00000008 #define I810_SMRAM_MISCC 0x70 #define I810_GFX_MEM_WIN_SIZE 0x00010000 @@ -97,7 +91,6 @@ #define G4x_GMCH_SIZE_VT_2M (G4x_GMCH_SIZE_2M | G4x_GMCH_SIZE_VT_EN) #define GFX_FLSH_CNTL 0x2170 /* 915+ */ -#define GFX_FLSH_CNTL_VLV 0x101008 #define I810_DRAM_CTL 0x3000 #define I810_DRAM_ROW_0 0x00000001 @@ -148,29 +141,6 @@ #define INTEL_I7505_AGPCTRL 0x70 #define INTEL_I7505_MCHCFG 0x50 -#define SNB_GMCH_CTRL 0x50 -#define SNB_GMCH_GMS_STOLEN_MASK 0xF8 -#define SNB_GMCH_GMS_STOLEN_32M (1 << 3) -#define SNB_GMCH_GMS_STOLEN_64M (2 << 3) -#define SNB_GMCH_GMS_STOLEN_96M (3 << 3) -#define SNB_GMCH_GMS_STOLEN_128M (4 << 3) -#define SNB_GMCH_GMS_STOLEN_160M (5 << 3) -#define SNB_GMCH_GMS_STOLEN_192M (6 << 3) -#define SNB_GMCH_GMS_STOLEN_224M (7 << 3) -#define SNB_GMCH_GMS_STOLEN_256M (8 << 3) -#define SNB_GMCH_GMS_STOLEN_288M (9 << 3) -#define SNB_GMCH_GMS_STOLEN_320M (0xa << 3) -#define SNB_GMCH_GMS_STOLEN_352M (0xb << 3) -#define SNB_GMCH_GMS_STOLEN_384M (0xc << 3) -#define SNB_GMCH_GMS_STOLEN_416M (0xd << 3) -#define SNB_GMCH_GMS_STOLEN_448M (0xe << 3) -#define SNB_GMCH_GMS_STOLEN_480M (0xf << 3) -#define SNB_GMCH_GMS_STOLEN_512M (0x10 << 3) -#define SNB_GTT_SIZE_0M (0 << 8) -#define SNB_GTT_SIZE_1M (1 << 8) -#define SNB_GTT_SIZE_2M (2 << 8) -#define SNB_GTT_SIZE_MASK (3 << 8) - /* pci devices ids */ #define PCI_DEVICE_ID_INTEL_E7221_HB 0x2588 #define PCI_DEVICE_ID_INTEL_E7221_IG 0x258a @@ -219,66 +189,5 @@ #define PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB 0x0062 #define PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB 0x006a #define PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG 0x0046 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB 0x0100 /* Desktop */ -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT1_IG 0x0102 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT2_IG 0x0112 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT2_PLUS_IG 0x0122 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB 0x0104 /* Mobile */ -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT1_IG 0x0106 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT2_IG 0x0116 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT2_PLUS_IG 0x0126 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_HB 0x0108 /* Server */ -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_IG 0x010A -#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_HB 0x0150 /* Desktop */ -#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_GT1_IG 0x0152 -#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_GT2_IG 0x0162 -#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_M_HB 0x0154 /* Mobile */ -#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_M_GT1_IG 0x0156 -#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_M_GT2_IG 0x0166 -#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_HB 0x0158 /* Server */ -#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_GT1_IG 0x015A -#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_GT2_IG 0x016A -#define PCI_DEVICE_ID_INTEL_VALLEYVIEW_HB 0x0F00 /* VLV1 */ -#define PCI_DEVICE_ID_INTEL_VALLEYVIEW_IG 0x0F30 -#define PCI_DEVICE_ID_INTEL_HASWELL_HB 0x0400 /* Desktop */ -#define PCI_DEVICE_ID_INTEL_HASWELL_D_GT1_IG 0x0402 -#define PCI_DEVICE_ID_INTEL_HASWELL_D_GT2_IG 0x0412 -#define PCI_DEVICE_ID_INTEL_HASWELL_D_GT2_PLUS_IG 0x0422 -#define PCI_DEVICE_ID_INTEL_HASWELL_M_HB 0x0404 /* Mobile */ -#define PCI_DEVICE_ID_INTEL_HASWELL_M_GT1_IG 0x0406 -#define PCI_DEVICE_ID_INTEL_HASWELL_M_GT2_IG 0x0416 -#define PCI_DEVICE_ID_INTEL_HASWELL_M_GT2_PLUS_IG 0x0426 -#define PCI_DEVICE_ID_INTEL_HASWELL_S_HB 0x0408 /* Server */ -#define PCI_DEVICE_ID_INTEL_HASWELL_S_GT1_IG 0x040a -#define PCI_DEVICE_ID_INTEL_HASWELL_S_GT2_IG 0x041a -#define PCI_DEVICE_ID_INTEL_HASWELL_S_GT2_PLUS_IG 0x042a -#define PCI_DEVICE_ID_INTEL_HASWELL_E_HB 0x0c04 -#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_D_GT1_IG 0x0C02 -#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_D_GT2_IG 0x0C12 -#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_D_GT2_PLUS_IG 0x0C22 -#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_M_GT1_IG 0x0C06 -#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_M_GT2_IG 0x0C16 -#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_M_GT2_PLUS_IG 0x0C26 -#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_S_GT1_IG 0x0C0A -#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_S_GT2_IG 0x0C1A -#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_S_GT2_PLUS_IG 0x0C2A -#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_D_GT1_IG 0x0A02 -#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_D_GT2_IG 0x0A12 -#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_D_GT2_PLUS_IG 0x0A22 -#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_M_GT1_IG 0x0A06 -#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_M_GT2_IG 0x0A16 -#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_M_GT2_PLUS_IG 0x0A26 -#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_S_GT1_IG 0x0A0A -#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_S_GT2_IG 0x0A1A -#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_S_GT2_PLUS_IG 0x0A2A -#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_D_GT1_IG 0x0D12 -#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_D_GT2_IG 0x0D22 -#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_D_GT2_PLUS_IG 0x0D32 -#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_M_GT1_IG 0x0D16 -#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_M_GT2_IG 0x0D26 -#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_M_GT2_PLUS_IG 0x0D36 -#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_S_GT1_IG 0x0D1A -#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_S_GT2_IG 0x0D2A -#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_S_GT2_PLUS_IG 0x0D3A #endif diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 4dfbb80f0fd5..dbd901e94ea6 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -367,62 +367,6 @@ static unsigned int intel_gtt_stolen_size(void) stolen_size = 0; break; } - } else if (INTEL_GTT_GEN == 6) { - /* - * SandyBridge has new memory control reg at 0x50.w - */ - u16 snb_gmch_ctl; - pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl); - switch (snb_gmch_ctl & SNB_GMCH_GMS_STOLEN_MASK) { - case SNB_GMCH_GMS_STOLEN_32M: - stolen_size = MB(32); - break; - case SNB_GMCH_GMS_STOLEN_64M: - stolen_size = MB(64); - break; - case SNB_GMCH_GMS_STOLEN_96M: - stolen_size = MB(96); - break; - case SNB_GMCH_GMS_STOLEN_128M: - stolen_size = MB(128); - break; - case SNB_GMCH_GMS_STOLEN_160M: - stolen_size = MB(160); - break; - case SNB_GMCH_GMS_STOLEN_192M: - stolen_size = MB(192); - break; - case SNB_GMCH_GMS_STOLEN_224M: - stolen_size = MB(224); - break; - case SNB_GMCH_GMS_STOLEN_256M: - stolen_size = MB(256); - break; - case SNB_GMCH_GMS_STOLEN_288M: - stolen_size = MB(288); - break; - case SNB_GMCH_GMS_STOLEN_320M: - stolen_size = MB(320); - break; - case SNB_GMCH_GMS_STOLEN_352M: - stolen_size = MB(352); - break; - case SNB_GMCH_GMS_STOLEN_384M: - stolen_size = MB(384); - break; - case SNB_GMCH_GMS_STOLEN_416M: - stolen_size = MB(416); - break; - case SNB_GMCH_GMS_STOLEN_448M: - stolen_size = MB(448); - break; - case SNB_GMCH_GMS_STOLEN_480M: - stolen_size = MB(480); - break; - case SNB_GMCH_GMS_STOLEN_512M: - stolen_size = MB(512); - break; - } } else { switch (gmch_ctrl & I855_GMCH_GMS_MASK) { case I855_GMCH_GMS_STOLEN_1M: @@ -556,29 +500,9 @@ static unsigned int i965_gtt_total_entries(void) static unsigned int intel_gtt_total_entries(void) { - int size; - if (IS_G33 || INTEL_GTT_GEN == 4 || INTEL_GTT_GEN == 5) return i965_gtt_total_entries(); - else if (INTEL_GTT_GEN == 6) { - u16 snb_gmch_ctl; - - pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl); - switch (snb_gmch_ctl & SNB_GTT_SIZE_MASK) { - default: - case SNB_GTT_SIZE_0M: - printk(KERN_ERR "Bad GTT size mask: 0x%04x.\n", snb_gmch_ctl); - size = MB(0); - break; - case SNB_GTT_SIZE_1M: - size = MB(1); - break; - case SNB_GTT_SIZE_2M: - size = MB(2); - break; - } - return size/4; - } else { + else { /* On previous hardware, the GTT size was just what was * required to map the aperture. */ @@ -778,9 +702,6 @@ bool intel_enable_gtt(void) { u8 __iomem *reg; - if (INTEL_GTT_GEN >= 6) - return true; - if (INTEL_GTT_GEN == 2) { u16 gmch_ctrl; @@ -1149,85 +1070,6 @@ static void i965_write_entry(dma_addr_t addr, writel(addr | pte_flags, intel_private.gtt + entry); } -static bool gen6_check_flags(unsigned int flags) -{ - return true; -} - -static void haswell_write_entry(dma_addr_t addr, unsigned int entry, - unsigned int flags) -{ - unsigned int type_mask = flags & ~AGP_USER_CACHED_MEMORY_GFDT; - unsigned int gfdt = flags & AGP_USER_CACHED_MEMORY_GFDT; - u32 pte_flags; - - if (type_mask == AGP_USER_MEMORY) - pte_flags = HSW_PTE_UNCACHED | I810_PTE_VALID; - else if (type_mask == AGP_USER_CACHED_MEMORY_LLC_MLC) { - pte_flags = GEN6_PTE_LLC_MLC | I810_PTE_VALID; - if (gfdt) - pte_flags |= GEN6_PTE_GFDT; - } else { /* set 'normal'/'cached' to LLC by default */ - pte_flags = GEN6_PTE_LLC | I810_PTE_VALID; - if (gfdt) - pte_flags |= GEN6_PTE_GFDT; - } - - /* gen6 has bit11-4 for physical addr bit39-32 */ - addr |= (addr >> 28) & 0xff0; - writel(addr | pte_flags, intel_private.gtt + entry); -} - -static void gen6_write_entry(dma_addr_t addr, unsigned int entry, - unsigned int flags) -{ - unsigned int type_mask = flags & ~AGP_USER_CACHED_MEMORY_GFDT; - unsigned int gfdt = flags & AGP_USER_CACHED_MEMORY_GFDT; - u32 pte_flags; - - if (type_mask == AGP_USER_MEMORY) - pte_flags = GEN6_PTE_UNCACHED | I810_PTE_VALID; - else if (type_mask == AGP_USER_CACHED_MEMORY_LLC_MLC) { - pte_flags = GEN6_PTE_LLC_MLC | I810_PTE_VALID; - if (gfdt) - pte_flags |= GEN6_PTE_GFDT; - } else { /* set 'normal'/'cached' to LLC by default */ - pte_flags = GEN6_PTE_LLC | I810_PTE_VALID; - if (gfdt) - pte_flags |= GEN6_PTE_GFDT; - } - - /* gen6 has bit11-4 for physical addr bit39-32 */ - addr |= (addr >> 28) & 0xff0; - writel(addr | pte_flags, intel_private.gtt + entry); -} - -static void valleyview_write_entry(dma_addr_t addr, unsigned int entry, - unsigned int flags) -{ - unsigned int type_mask = flags & ~AGP_USER_CACHED_MEMORY_GFDT; - unsigned int gfdt = flags & AGP_USER_CACHED_MEMORY_GFDT; - u32 pte_flags; - - if (type_mask == AGP_USER_MEMORY) - pte_flags = GEN6_PTE_UNCACHED | I810_PTE_VALID; - else { - pte_flags = GEN6_PTE_LLC | I810_PTE_VALID; - if (gfdt) - pte_flags |= GEN6_PTE_GFDT; - } - - /* gen6 has bit11-4 for physical addr bit39-32 */ - addr |= (addr >> 28) & 0xff0; - writel(addr | pte_flags, intel_private.gtt + entry); - - writel(1, intel_private.registers + GFX_FLSH_CNTL_VLV); -} - -static void gen6_cleanup(void) -{ -} - /* Certain Gen5 chipsets require require idling the GPU before * unmapping anything from the GTT when VT-d is enabled. */ @@ -1249,41 +1091,29 @@ static inline int needs_idle_maps(void) static int i9xx_setup(void) { - u32 reg_addr; + u32 reg_addr, gtt_addr; int size = KB(512); pci_read_config_dword(intel_private.pcidev, I915_MMADDR, ®_addr); reg_addr &= 0xfff80000; - if (INTEL_GTT_GEN >= 7) - size = MB(2); - intel_private.registers = ioremap(reg_addr, size); if (!intel_private.registers) return -ENOMEM; - if (INTEL_GTT_GEN == 3) { - u32 gtt_addr; - + switch (INTEL_GTT_GEN) { + case 3: pci_read_config_dword(intel_private.pcidev, I915_PTEADDR, >t_addr); intel_private.gtt_bus_addr = gtt_addr; - } else { - u32 gtt_offset; - - switch (INTEL_GTT_GEN) { - case 5: - case 6: - case 7: - gtt_offset = MB(2); - break; - case 4: - default: - gtt_offset = KB(512); - break; - } - intel_private.gtt_bus_addr = reg_addr + gtt_offset; + break; + case 5: + intel_private.gtt_bus_addr = reg_addr + MB(2); + break; + default: + intel_private.gtt_bus_addr = reg_addr + KB(512); + break; } if (needs_idle_maps()) @@ -1395,32 +1225,6 @@ static const struct intel_gtt_driver ironlake_gtt_driver = { .check_flags = i830_check_flags, .chipset_flush = i9xx_chipset_flush, }; -static const struct intel_gtt_driver sandybridge_gtt_driver = { - .gen = 6, - .setup = i9xx_setup, - .cleanup = gen6_cleanup, - .write_entry = gen6_write_entry, - .dma_mask_size = 40, - .check_flags = gen6_check_flags, - .chipset_flush = i9xx_chipset_flush, -}; -static const struct intel_gtt_driver haswell_gtt_driver = { - .gen = 6, - .setup = i9xx_setup, - .cleanup = gen6_cleanup, - .write_entry = haswell_write_entry, - .dma_mask_size = 40, - .check_flags = gen6_check_flags, - .chipset_flush = i9xx_chipset_flush, -}; -static const struct intel_gtt_driver valleyview_gtt_driver = { - .gen = 7, - .setup = i9xx_setup, - .cleanup = gen6_cleanup, - .write_entry = valleyview_write_entry, - .dma_mask_size = 40, - .check_flags = gen6_check_flags, -}; /* Table to describe Intel GMCH and AGP/PCIE GART drivers. At least one of * driver and gmch_driver must be non-null, and find_gmch will determine @@ -1501,106 +1305,6 @@ static const struct intel_gtt_driver_description { "HD Graphics", &ironlake_gtt_driver }, { PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, "HD Graphics", &ironlake_gtt_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT1_IG, - "Sandybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT2_IG, - "Sandybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT2_PLUS_IG, - "Sandybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT1_IG, - "Sandybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT2_IG, - "Sandybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT2_PLUS_IG, - "Sandybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_IG, - "Sandybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_IVYBRIDGE_GT1_IG, - "Ivybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_IVYBRIDGE_GT2_IG, - "Ivybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_IVYBRIDGE_M_GT1_IG, - "Ivybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_IVYBRIDGE_M_GT2_IG, - "Ivybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_GT1_IG, - "Ivybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_GT2_IG, - "Ivybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_VALLEYVIEW_IG, - "ValleyView", &valleyview_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_D_GT1_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_D_GT2_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_D_GT2_PLUS_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_M_GT1_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_M_GT2_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_M_GT2_PLUS_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_S_GT1_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_S_GT2_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_S_GT2_PLUS_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_SDV_D_GT1_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_SDV_D_GT2_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_SDV_D_GT2_PLUS_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_SDV_M_GT1_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_SDV_M_GT2_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_SDV_M_GT2_PLUS_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_SDV_S_GT1_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_SDV_S_GT2_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_SDV_S_GT2_PLUS_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_ULT_D_GT1_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_ULT_D_GT2_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_ULT_D_GT2_PLUS_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_ULT_M_GT1_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_ULT_M_GT2_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_ULT_M_GT2_PLUS_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_ULT_S_GT1_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_ULT_S_GT2_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_ULT_S_GT2_PLUS_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_CRW_D_GT1_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_CRW_D_GT2_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_CRW_D_GT2_PLUS_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_CRW_M_GT1_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_CRW_M_GT2_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_CRW_M_GT2_PLUS_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_CRW_S_GT1_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_CRW_S_GT2_IG, - "Haswell", &haswell_gtt_driver }, - { PCI_DEVICE_ID_INTEL_HASWELL_CRW_S_GT2_PLUS_IG, - "Haswell", &haswell_gtt_driver }, { 0, NULL, NULL } }; diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index 94e8f2c7f9e1..6eb76a1f11ab 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -40,10 +40,6 @@ void intel_gtt_clear_range(unsigned int first_entry, unsigned int num_entries); #define AGP_DCACHE_MEMORY 1 #define AGP_PHYS_MEMORY 2 -/* New caching attributes for gen6/sandybridge */ -#define AGP_USER_CACHED_MEMORY_LLC_MLC (AGP_USER_TYPES + 2) -#define AGP_USER_UNCACHED_MEMORY (AGP_USER_TYPES + 4) - /* flag for GFDT type */ #define AGP_USER_CACHED_MEMORY_GFDT (1 << 3) -- cgit v1.2.3-59-g8ed1b From a4799037c3234830e9feb1823d87f905fb4d080a Mon Sep 17 00:00:00 2001 From: Stephane Marchesin Date: Fri, 9 Nov 2012 16:21:05 +0000 Subject: drm: get cea video id code for a given display mode This patch adds support for getting CEA Video ID Code for a given display mode after matching with edid_cea_modes list. Its index in the list added with one, gives the desired code. This exported function will be used by hdmi drivers for composing AVI info frame data. Signed-off-by: Stephane Marchesin Signed-off-by: Rahul Sharma Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_edid.c | 20 ++++++++++++++++++++ include/drm/drm_crtc.h | 1 + 2 files changed, 21 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index fadcd44ff196..856dcd91b3d4 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1516,6 +1516,26 @@ u8 *drm_find_cea_extension(struct edid *edid) } EXPORT_SYMBOL(drm_find_cea_extension); +/* + * Looks for a CEA mode matching given drm_display_mode. + * Returns its CEA Video ID code, or 0 if not found. + */ +u8 drm_match_cea_mode(struct drm_display_mode *to_match) +{ + struct drm_display_mode *cea_mode; + u8 mode; + + for (mode = 0; mode < drm_num_cea_modes; mode++) { + cea_mode = (struct drm_display_mode *)&edid_cea_modes[mode]; + + if (drm_mode_equal(to_match, cea_mode)) + return mode + 1; + } + return 0; +} +EXPORT_SYMBOL(drm_match_cea_mode); + + static int do_cea_modes (struct drm_connector *connector, u8 *db, u8 len) { diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 49dd8c2eea7d..1f5f1d642a98 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1037,6 +1037,7 @@ extern int drm_mode_gamma_get_ioctl(struct drm_device *dev, extern int drm_mode_gamma_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern u8 *drm_find_cea_extension(struct edid *edid); +extern u8 drm_match_cea_mode(struct drm_display_mode *to_match); extern bool drm_detect_hdmi_monitor(struct edid *edid); extern bool drm_detect_monitor_audio(struct edid *edid); extern int drm_mode_page_flip_ioctl(struct drm_device *dev, -- cgit v1.2.3-59-g8ed1b From c6eefa1750ec0308956895027c3a79eee2ef9726 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 16 Oct 2012 22:48:40 +0000 Subject: drm: add drm_send_vblank_event() helper (v5) A helper that drivers can use to send vblank event after a pageflip. If the driver doesn't support proper vblank irq based time/seqn then just pass -1 for the pipe # to get do_gettimestamp() behavior (since there are a lot of drivers that don't use drm_vblank_count_and_time()) Also an internal send_vblank_event() helper for the various other code paths within drm_irq that also need to send vblank events. v1: original v2: add back 'vblwait->reply.sequence = seq' which should not have been deleted v3: add WARN_ON() in case lock is not held and comments v4: use WARN_ON_SMP() instead to fix issue with !SMP && !DEBUG_SPINLOCK as pointed out by Marcin Slusarz v5: update docbook Signed-off-by: Rob Clark Signed-off-by: Dave Airlie --- Documentation/DocBook/drm.tmpl | 20 +++--------- drivers/gpu/drm/drm_irq.c | 74 +++++++++++++++++++++++++++++------------- include/drm/drmP.h | 2 ++ 3 files changed, 59 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index b0300529ab13..c9cbb3fe0e6a 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -1141,23 +1141,13 @@ int max_width, max_height; the page_flip operation will be called with a non-NULL event argument pointing to a drm_pending_vblank_event instance. Upon page - flip completion the driver must fill the - event::event - sequence, tv_sec - and tv_usec fields with the associated - vertical blanking count and timestamp, add the event to the - drm_file list of events to be signaled, and wake - up any waiting process. This can be performed with + flip completion the driver must call drm_send_vblank_event + to fill in the event and send to wake up any waiting processes. + This can be performed with event.sequence = drm_vblank_count_and_time(..., &now); - event->event.tv_sec = now.tv_sec; - event->event.tv_usec = now.tv_usec; - spin_lock_irqsave(&dev->event_lock, flags); - list_add_tail(&event->base.link, &event->base.file_priv->event_list); - wake_up_interruptible(&event->base.file_priv->event_wait); + ... + drm_send_vblank_event(dev, pipe, event); spin_unlock_irqrestore(&dev->event_lock, flags); ]]> diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 3a3d0ce891b9..44602ef55afc 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -802,6 +802,46 @@ u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc, } EXPORT_SYMBOL(drm_vblank_count_and_time); +static void send_vblank_event(struct drm_device *dev, + struct drm_pending_vblank_event *e, + unsigned long seq, struct timeval *now) +{ + WARN_ON_SMP(!spin_is_locked(&dev->event_lock)); + e->event.sequence = seq; + e->event.tv_sec = now->tv_sec; + e->event.tv_usec = now->tv_usec; + + list_add_tail(&e->base.link, + &e->base.file_priv->event_list); + wake_up_interruptible(&e->base.file_priv->event_wait); + trace_drm_vblank_event_delivered(e->base.pid, e->pipe, + e->event.sequence); +} + +/** + * drm_send_vblank_event - helper to send vblank event after pageflip + * @dev: DRM device + * @crtc: CRTC in question + * @e: the event to send + * + * Updates sequence # and timestamp on event, and sends it to userspace. + * Caller must hold event lock. + */ +void drm_send_vblank_event(struct drm_device *dev, int crtc, + struct drm_pending_vblank_event *e) +{ + struct timeval now; + unsigned int seq; + if (crtc >= 0) { + seq = drm_vblank_count_and_time(dev, crtc, &now); + } else { + seq = 0; + do_gettimeofday(&now); + } + send_vblank_event(dev, e, seq, &now); +} +EXPORT_SYMBOL(drm_send_vblank_event); + /** * drm_update_vblank_count - update the master vblank counter * @dev: DRM device @@ -936,6 +976,13 @@ void drm_vblank_put(struct drm_device *dev, int crtc) } EXPORT_SYMBOL(drm_vblank_put); +/** + * drm_vblank_off - disable vblank events on a CRTC + * @dev: DRM device + * @crtc: CRTC in question + * + * Caller must hold event lock. + */ void drm_vblank_off(struct drm_device *dev, int crtc) { struct drm_pending_vblank_event *e, *t; @@ -955,15 +1002,9 @@ void drm_vblank_off(struct drm_device *dev, int crtc) DRM_DEBUG("Sending premature vblank event on disable: \ wanted %d, current %d\n", e->event.sequence, seq); - - e->event.sequence = seq; - e->event.tv_sec = now.tv_sec; - e->event.tv_usec = now.tv_usec; + list_del(&e->base.link); drm_vblank_put(dev, e->pipe); - list_move_tail(&e->base.link, &e->base.file_priv->event_list); - wake_up_interruptible(&e->base.file_priv->event_wait); - trace_drm_vblank_event_delivered(e->base.pid, e->pipe, - e->event.sequence); + send_vblank_event(dev, e, seq, &now); } spin_unlock_irqrestore(&dev->vbl_lock, irqflags); @@ -1107,15 +1148,9 @@ static int drm_queue_vblank_event(struct drm_device *dev, int pipe, e->event.sequence = vblwait->request.sequence; if ((seq - vblwait->request.sequence) <= (1 << 23)) { - e->event.sequence = seq; - e->event.tv_sec = now.tv_sec; - e->event.tv_usec = now.tv_usec; drm_vblank_put(dev, pipe); - list_add_tail(&e->base.link, &e->base.file_priv->event_list); - wake_up_interruptible(&e->base.file_priv->event_wait); + send_vblank_event(dev, e, seq, &now); vblwait->reply.sequence = seq; - trace_drm_vblank_event_delivered(current->pid, pipe, - vblwait->request.sequence); } else { /* drm_handle_vblank_events will call drm_vblank_put */ list_add_tail(&e->base.link, &dev->vblank_event_list); @@ -1256,14 +1291,9 @@ static void drm_handle_vblank_events(struct drm_device *dev, int crtc) DRM_DEBUG("vblank event on %d, current %d\n", e->event.sequence, seq); - e->event.sequence = seq; - e->event.tv_sec = now.tv_sec; - e->event.tv_usec = now.tv_usec; + list_del(&e->base.link); drm_vblank_put(dev, e->pipe); - list_move_tail(&e->base.link, &e->base.file_priv->event_list); - wake_up_interruptible(&e->base.file_priv->event_wait); - trace_drm_vblank_event_delivered(e->base.pid, e->pipe, - e->event.sequence); + send_vblank_event(dev, e, seq, &now); } spin_unlock_irqrestore(&dev->event_lock, flags); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 3fd82809b2d4..29eb23799aa4 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1431,6 +1431,8 @@ extern int drm_vblank_wait(struct drm_device *dev, unsigned int *vbl_seq); extern u32 drm_vblank_count(struct drm_device *dev, int crtc); extern u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc, struct timeval *vblanktime); +extern void drm_send_vblank_event(struct drm_device *dev, int crtc, + struct drm_pending_vblank_event *e); extern bool drm_handle_vblank(struct drm_device *dev, int crtc); extern int drm_vblank_get(struct drm_device *dev, int crtc); extern void drm_vblank_put(struct drm_device *dev, int crtc); -- cgit v1.2.3-59-g8ed1b From 3d3683f04a32da16df71e3b089da8f5a13059ef4 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 23 Oct 2012 18:23:32 +0000 Subject: drm: extract drm_kms_helper_hotplug_event Useful if drivers want to be slightly more clever about hotplug handling. Signed-off-by: Daniel Vetter Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc_helper.c | 17 +++++++++++------ include/drm/drm_crtc_helper.h | 1 + 2 files changed, 12 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index b04cf5332599..760a67534c6c 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -936,6 +936,15 @@ int drm_helper_resume_force_mode(struct drm_device *dev) } EXPORT_SYMBOL(drm_helper_resume_force_mode); +void drm_kms_helper_hotplug_event(struct drm_device *dev) +{ + /* send a uevent + call fbdev */ + drm_sysfs_hotplug_event(dev); + if (dev->mode_config.funcs->output_poll_changed) + dev->mode_config.funcs->output_poll_changed(dev); +} +EXPORT_SYMBOL(drm_kms_helper_hotplug_event); + #define DRM_OUTPUT_POLL_PERIOD (10*HZ) static void output_poll_execute(struct work_struct *work) { @@ -978,12 +987,8 @@ static void output_poll_execute(struct work_struct *work) mutex_unlock(&dev->mode_config.mutex); - if (changed) { - /* send a uevent + call fbdev */ - drm_sysfs_hotplug_event(dev); - if (dev->mode_config.funcs->output_poll_changed) - dev->mode_config.funcs->output_poll_changed(dev); - } + if (changed) + drm_kms_helper_hotplug_event(dev); if (repoll) schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD); diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index defee28f6b95..f43d556bf40b 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -164,6 +164,7 @@ extern int drm_helper_resume_force_mode(struct drm_device *dev); extern void drm_kms_helper_poll_init(struct drm_device *dev); extern void drm_kms_helper_poll_fini(struct drm_device *dev); extern void drm_helper_hpd_irq_event(struct drm_device *dev); +extern void drm_kms_helper_hotplug_event(struct drm_device *dev); extern void drm_kms_helper_poll_disable(struct drm_device *dev); extern void drm_kms_helper_poll_enable(struct drm_device *dev); -- cgit v1.2.3-59-g8ed1b From 816da85a0990c2b52cfffa77637d1c770d6790e9 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 23 Oct 2012 18:23:33 +0000 Subject: drm: handle HPD and polled connectors separately Instead of reusing the polling code for hpd handling, split them up. This has a few consequences: - Don't touch HPD capable connectors in the poll loop. - Only touch HPD capable connectors in drm_helper_hpd_irq_event. - We could run the HPD handling directly (because all callers already use their own work item), but for easier bisect that happens in it's own patch. The ultimate goal is that drivers grow some smarts about which connectors have received a hotplug event and only call the detect code of that connector. But that's a second step. v2: s/hdp/hpd/, noticed by Adam Jackson. I can't type. v3: Split out the work item removal as requested by Dave Airlie. This results in a temporary mode_config.hpd_irq_work item to keep things the same. v4: In the hpd_irq_event handler don't bail out if other bits than HPD are set. This is useful where e.g. hpd is unreliably, but mostly works. Drivers can then set both HPD and POLL flags, and users get the best of both worlds: Quick hotplug feedback if the hpd works, but still reliable detection with the polling. The poll loop already works the same, and doesn't bail if HPD is set. Signed-off-by: Daniel Vetter Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc_helper.c | 51 ++++++++++++++++++++++++++++++++------- include/drm/drm_crtc.h | 1 + 2 files changed, 43 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 760a67534c6c..9adbd2b311d0 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -960,9 +960,9 @@ static void output_poll_execute(struct work_struct *work) mutex_lock(&dev->mode_config.mutex); list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - /* if this is HPD or polled don't check it - - TV out for instance */ - if (!connector->polled) + /* Ignore HPD capable connectors and connectors where we don't + * want any hotplug detection at all for polling. */ + if (!connector->polled || connector->polled == DRM_CONNECTOR_POLL_HPD) continue; else if (connector->polled & (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT)) @@ -972,8 +972,7 @@ static void output_poll_execute(struct work_struct *work) /* if we are connected and don't want to poll for disconnect skip it */ if (old_status == connector_status_connected && - !(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT) && - !(connector->polled & DRM_CONNECTOR_POLL_HPD)) + !(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT)) continue; connector->status = connector->funcs->detect(connector, false); @@ -1020,9 +1019,12 @@ void drm_kms_helper_poll_enable(struct drm_device *dev) } EXPORT_SYMBOL(drm_kms_helper_poll_enable); +static void hpd_irq_event_execute(struct work_struct *work); + void drm_kms_helper_poll_init(struct drm_device *dev) { INIT_DELAYED_WORK(&dev->mode_config.output_poll_work, output_poll_execute); + INIT_DELAYED_WORK(&dev->mode_config.hpd_irq_work, hpd_irq_event_execute); dev->mode_config.poll_enabled = true; drm_kms_helper_poll_enable(dev); @@ -1035,14 +1037,45 @@ void drm_kms_helper_poll_fini(struct drm_device *dev) } EXPORT_SYMBOL(drm_kms_helper_poll_fini); -void drm_helper_hpd_irq_event(struct drm_device *dev) +static void hpd_irq_event_execute(struct work_struct *work) { + struct delayed_work *delayed_work = to_delayed_work(work); + struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.hpd_irq_work); + struct drm_connector *connector; + enum drm_connector_status old_status; + bool changed = false; + if (!dev->mode_config.poll_enabled) return; - /* kill timer and schedule immediate execution, this doesn't block */ - cancel_delayed_work(&dev->mode_config.output_poll_work); + mutex_lock(&dev->mode_config.mutex); + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + + /* Only handle HPD capable connectors. */ + if (!(connector->polled & DRM_CONNECTOR_POLL_HPD)) + continue; + + old_status = connector->status; + + connector->status = connector->funcs->detect(connector, false); + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n", + connector->base.id, + drm_get_connector_name(connector), + old_status, connector->status); + if (old_status != connector->status) + changed = true; + } + + mutex_unlock(&dev->mode_config.mutex); + + if (changed) + drm_kms_helper_hotplug_event(dev); +} + +void drm_helper_hpd_irq_event(struct drm_device *dev) +{ + cancel_delayed_work(&dev->mode_config.hpd_irq_work); if (drm_kms_helper_poll) - schedule_delayed_work(&dev->mode_config.output_poll_work, 0); + schedule_delayed_work(&dev->mode_config.hpd_irq_work, 0); } EXPORT_SYMBOL(drm_helper_hpd_irq_event); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 1f5f1d642a98..ccff8c9b3780 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -793,6 +793,7 @@ struct drm_mode_config { /* output poll support */ bool poll_enabled; struct delayed_work output_poll_work; + struct delayed_work hpd_irq_work; /* pointers to standard properties */ struct list_head property_blob_list; -- cgit v1.2.3-59-g8ed1b From 69787f7da6b2adc4054357a661aaa1701a9ca76f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 23 Oct 2012 18:23:34 +0000 Subject: drm: run the hpd irq event code directly All drivers already have a work item to run the hpd code, so we don't need to launch a new one in the helper code. Dave Airlie mentioned that the cancel+re-queue might paper over DP related hpd ping-pongs, hence why this is split out. Signed-off-by: Daniel Vetter Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc_helper.c | 14 +------------- include/drm/drm_crtc.h | 1 - 2 files changed, 1 insertion(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 9adbd2b311d0..a8a61e43f93b 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -1019,12 +1019,9 @@ void drm_kms_helper_poll_enable(struct drm_device *dev) } EXPORT_SYMBOL(drm_kms_helper_poll_enable); -static void hpd_irq_event_execute(struct work_struct *work); - void drm_kms_helper_poll_init(struct drm_device *dev) { INIT_DELAYED_WORK(&dev->mode_config.output_poll_work, output_poll_execute); - INIT_DELAYED_WORK(&dev->mode_config.hpd_irq_work, hpd_irq_event_execute); dev->mode_config.poll_enabled = true; drm_kms_helper_poll_enable(dev); @@ -1037,10 +1034,8 @@ void drm_kms_helper_poll_fini(struct drm_device *dev) } EXPORT_SYMBOL(drm_kms_helper_poll_fini); -static void hpd_irq_event_execute(struct work_struct *work) +void drm_helper_hpd_irq_event(struct drm_device *dev) { - struct delayed_work *delayed_work = to_delayed_work(work); - struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.hpd_irq_work); struct drm_connector *connector; enum drm_connector_status old_status; bool changed = false; @@ -1071,11 +1066,4 @@ static void hpd_irq_event_execute(struct work_struct *work) if (changed) drm_kms_helper_hotplug_event(dev); } - -void drm_helper_hpd_irq_event(struct drm_device *dev) -{ - cancel_delayed_work(&dev->mode_config.hpd_irq_work); - if (drm_kms_helper_poll) - schedule_delayed_work(&dev->mode_config.hpd_irq_work, 0); -} EXPORT_SYMBOL(drm_helper_hpd_irq_event); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index ccff8c9b3780..1f5f1d642a98 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -793,7 +793,6 @@ struct drm_mode_config { /* output poll support */ bool poll_enabled; struct delayed_work output_poll_work; - struct delayed_work hpd_irq_work; /* pointers to standard properties */ struct list_head property_blob_list; -- cgit v1.2.3-59-g8ed1b From 905bc9ff6575f78aab24c0261e8785425b5a0397 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 23 Oct 2012 18:23:36 +0000 Subject: drm: don't start the poll engine in probe_single_connector Actually there's a reason this stuff is there, and it's called commit e58f637bb96d5a0ae0919b9998b891d1ba7e47c9 Author: Chris Wilson Date: Fri Aug 20 09:13:36 2010 +0100 drm/kms: Add a module parameter to disable polling The idea has been that users can enable/disable polling at runtime. So the quick hack has been to just re-enable the output polling if xrandr asks for the latest state of the connectors. The problem with that hack is that when we force connectors to another state than what would be detected, we nicely ping-pong: - Userspace calls probe, gets the forced state, but polling starts again. - Polling notices that the state is actually different, wakes up userspace. - Repeat. As that commit already explains, the right fix would be to make the locking more fine-grained, so that hotplug detection on one output does not interfere with cursor updates on another crtc. But that is way too much work. So let's just safe this gross hack by caching the last-seen state of drm_kms_helper_poll for that driver, and only fire up the poll engine again if it changed from off to on. v2: Fixup the edge detection of drm_kms_helper_poll. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=49907 Tested-by: Tvrtko Ursulin Signed-off-by: Daniel Vetter Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc_helper.c | 7 ++++++- include/drm/drm_crtc.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index a8a61e43f93b..5ee192885c97 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -127,9 +127,14 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, connector->funcs->force(connector); } else { connector->status = connector->funcs->detect(connector, true); - drm_kms_helper_poll_enable(dev); } + /* Re-enable polling in case the global poll config changed. */ + if (drm_kms_helper_poll != dev->mode_config.poll_running) + drm_kms_helper_poll_enable(dev); + + dev->mode_config.poll_running = drm_kms_helper_poll; + if (connector->status == connector_status_disconnected) { DRM_DEBUG_KMS("[CONNECTOR:%d:%s] disconnected\n", connector->base.id, drm_get_connector_name(connector)); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 1f5f1d642a98..c0635b7f8696 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -792,6 +792,7 @@ struct drm_mode_config { /* output poll support */ bool poll_enabled; + bool poll_running; struct delayed_work output_poll_work; /* pointers to standard properties */ -- cgit v1.2.3-59-g8ed1b From c61eef726a78ae77b6ce223d01ea2130f465fe5c Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 23 Oct 2012 18:53:26 +0000 Subject: drm: add support for monotonic vblank timestamps Jumps in the vblank and page flip event timestamps cause trouble for clients, so we should avoid them. The timestamp we get currently with gettimeofday can jump, so use instead monotonic timestamps. For backward compatibility use a module flag to revert back to using gettimeofday timestamps. Add also a DRM_CAP_TIMESTAMP_MONOTONIC flag that is simply a read only version of the module flag, so that clients can query this without depending on sysfs. Signed-off-by: Imre Deak Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_ioctl.c | 3 +++ drivers/gpu/drm/drm_irq.c | 25 ++++++++++++++++++++----- drivers/gpu/drm/drm_stub.c | 8 ++++++++ include/drm/drmP.h | 1 + include/uapi/drm/drm.h | 1 + 5 files changed, 33 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 23dd97506f28..e77bd8b57df2 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -287,6 +287,9 @@ int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv) req->value |= dev->driver->prime_fd_to_handle ? DRM_PRIME_CAP_IMPORT : 0; req->value |= dev->driver->prime_handle_to_fd ? DRM_PRIME_CAP_EXPORT : 0; break; + case DRM_CAP_TIMESTAMP_MONOTONIC: + req->value = drm_timestamp_monotonic; + break; default: return -EINVAL; } diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index ef5b5f70735e..c0f0046d8078 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -633,7 +633,8 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, /* Get system timestamp after query. */ etime = ktime_get(); - mono_time_offset = ktime_get_monotonic_offset(); + if (!drm_timestamp_monotonic) + mono_time_offset = ktime_get_monotonic_offset(); preempt_enable(); @@ -691,7 +692,9 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, vbl_status |= 0x8; } - etime = ktime_sub(etime, mono_time_offset); + if (!drm_timestamp_monotonic) + etime = ktime_sub(etime, mono_time_offset); + /* save this only for debugging purposes */ tv_etime = ktime_to_timeval(etime); /* Subtract time delta from raw timestamp to get final @@ -714,6 +717,17 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, } EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos); +static struct timeval get_drm_timestamp(void) +{ + ktime_t now; + + now = ktime_get(); + if (!drm_timestamp_monotonic) + now = ktime_sub(now, ktime_get_monotonic_offset()); + + return ktime_to_timeval(now); +} + /** * drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent * vblank interval. @@ -751,9 +765,9 @@ u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc, } /* GPU high precision timestamp query unsupported or failed. - * Return gettimeofday timestamp as best estimate. + * Return current monotonic/gettimeofday timestamp as best estimate. */ - do_gettimeofday(tvblank); + *tvblank = get_drm_timestamp(); return 0; } @@ -842,7 +856,8 @@ void drm_send_vblank_event(struct drm_device *dev, int crtc, seq = drm_vblank_count_and_time(dev, crtc, &now); } else { seq = 0; - do_gettimeofday(&now); + + now = get_drm_timestamp(); } send_vblank_event(dev, e, seq, &now); } diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 6fcdd8ebcb3d..200e104f1fa0 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -46,16 +46,24 @@ EXPORT_SYMBOL(drm_vblank_offdelay); unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */ EXPORT_SYMBOL(drm_timestamp_precision); +/* + * Default to use monotonic timestamps for wait-for-vblank and page-flip + * complete events. + */ +unsigned int drm_timestamp_monotonic = 1; + MODULE_AUTHOR(CORE_AUTHOR); MODULE_DESCRIPTION(CORE_DESC); MODULE_LICENSE("GPL and additional rights"); MODULE_PARM_DESC(debug, "Enable debug output"); MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs]"); 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(vblankoffdelay, drm_vblank_offdelay, int, 0600); module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); +module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); struct idr drm_minors_idr; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 29eb23799aa4..fad21c927a38 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1505,6 +1505,7 @@ extern unsigned int drm_debug; extern unsigned int drm_vblank_offdelay; extern unsigned int drm_timestamp_precision; +extern unsigned int drm_timestamp_monotonic; extern struct class *drm_class; extern struct proc_dir_entry *drm_proc_root; diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index 1e3481edf062..8d1e2bbee83a 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -778,6 +778,7 @@ struct drm_event_vblank { #define DRM_CAP_DUMB_PREFERRED_DEPTH 0x3 #define DRM_CAP_DUMB_PREFER_SHADOW 0x4 #define DRM_CAP_PRIME 0x5 +#define DRM_CAP_TIMESTAMP_MONOTONIC 0x6 #define DRM_PRIME_CAP_IMPORT 0x1 #define DRM_PRIME_CAP_EXPORT 0x2 -- cgit v1.2.3-59-g8ed1b From 0b91c4a1cd7cc368763de2fe25b8ea64ea803c08 Mon Sep 17 00:00:00 2001 From: Marcin Slusarz Date: Tue, 6 Nov 2012 21:49:51 +0000 Subject: drm/ttm: remove ttm_buffer_object->buffer_start All drivers set it to 0 and nothing uses it. Signed-off-by: Marcin Slusarz Reviewed-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/ast/ast_ttm.c | 2 +- drivers/gpu/drm/cirrus/cirrus_ttm.c | 2 +- drivers/gpu/drm/mgag200/mgag200_ttm.c | 2 +- drivers/gpu/drm/nouveau/nouveau_bo.c | 2 +- drivers/gpu/drm/radeon/radeon_object.c | 2 +- drivers/gpu/drm/ttm/ttm_bo.c | 8 ++------ drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 4 ++-- include/drm/ttm/ttm_bo_api.h | 9 --------- 9 files changed, 10 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/ast/ast_ttm.c b/drivers/gpu/drm/ast/ast_ttm.c index 1a026ac2dfb4..0a54f65a8ebb 100644 --- a/drivers/gpu/drm/ast/ast_ttm.c +++ b/drivers/gpu/drm/ast/ast_ttm.c @@ -356,7 +356,7 @@ int ast_bo_create(struct drm_device *dev, int size, int align, ret = ttm_bo_init(&ast->ttm.bdev, &astbo->bo, size, ttm_bo_type_device, &astbo->placement, - align >> PAGE_SHIFT, 0, false, NULL, acc_size, + align >> PAGE_SHIFT, false, NULL, acc_size, NULL, ast_bo_ttm_destroy); if (ret) return ret; diff --git a/drivers/gpu/drm/cirrus/cirrus_ttm.c b/drivers/gpu/drm/cirrus/cirrus_ttm.c index bc83f835c830..90d770143cc2 100644 --- a/drivers/gpu/drm/cirrus/cirrus_ttm.c +++ b/drivers/gpu/drm/cirrus/cirrus_ttm.c @@ -361,7 +361,7 @@ int cirrus_bo_create(struct drm_device *dev, int size, int align, ret = ttm_bo_init(&cirrus->ttm.bdev, &cirrusbo->bo, size, ttm_bo_type_device, &cirrusbo->placement, - align >> PAGE_SHIFT, 0, false, NULL, acc_size, + align >> PAGE_SHIFT, false, NULL, acc_size, NULL, cirrus_bo_ttm_destroy); if (ret) return ret; diff --git a/drivers/gpu/drm/mgag200/mgag200_ttm.c b/drivers/gpu/drm/mgag200/mgag200_ttm.c index 1504699666c4..49d60a620122 100644 --- a/drivers/gpu/drm/mgag200/mgag200_ttm.c +++ b/drivers/gpu/drm/mgag200/mgag200_ttm.c @@ -355,7 +355,7 @@ int mgag200_bo_create(struct drm_device *dev, int size, int align, ret = ttm_bo_init(&mdev->ttm.bdev, &mgabo->bo, size, ttm_bo_type_device, &mgabo->placement, - align >> PAGE_SHIFT, 0, false, NULL, acc_size, + align >> PAGE_SHIFT, false, NULL, acc_size, NULL, mgag200_bo_ttm_destroy); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 35ac57f0aab6..3cbf1a8cf551 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -225,7 +225,7 @@ nouveau_bo_new(struct drm_device *dev, int size, int align, ret = ttm_bo_init(&drm->ttm.bdev, &nvbo->bo, size, type, &nvbo->placement, - align >> PAGE_SHIFT, 0, false, NULL, acc_size, sg, + align >> PAGE_SHIFT, false, NULL, acc_size, sg, nouveau_bo_del_ttm); if (ret) { /* ttm will call nouveau_bo_del_ttm if it fails.. */ diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 65c55556fd3f..7c4b4bb05a36 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -140,7 +140,7 @@ int radeon_bo_create(struct radeon_device *rdev, /* Kernel allocation are uninterruptible */ down_read(&rdev->pm.mclk_lock); r = ttm_bo_init(&rdev->mman.bdev, &bo->tbo, size, type, - &bo->placement, page_align, 0, !kernel, NULL, + &bo->placement, page_align, !kernel, NULL, acc_size, sg, &radeon_ttm_bo_destroy); up_read(&rdev->pm.mclk_lock); if (unlikely(r != 0)) { diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index e6bfcfdd3b60..f65182667825 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -1179,7 +1179,6 @@ int ttm_bo_init(struct ttm_bo_device *bdev, enum ttm_bo_type type, struct ttm_placement *placement, uint32_t page_alignment, - unsigned long buffer_start, bool interruptible, struct file *persistent_swap_storage, size_t acc_size, @@ -1200,7 +1199,6 @@ int ttm_bo_init(struct ttm_bo_device *bdev, return -ENOMEM; } - size += buffer_start & ~PAGE_MASK; num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; if (num_pages == 0) { pr_err("Illegal buffer object size\n"); @@ -1233,7 +1231,6 @@ int ttm_bo_init(struct ttm_bo_device *bdev, bo->mem.page_alignment = page_alignment; bo->mem.bus.io_reserved_vm = false; bo->mem.bus.io_reserved_count = 0; - bo->buffer_start = buffer_start & PAGE_MASK; bo->priv_flags = 0; bo->mem.placement = (TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED); bo->seq_valid = false; @@ -1306,7 +1303,6 @@ int ttm_bo_create(struct ttm_bo_device *bdev, enum ttm_bo_type type, struct ttm_placement *placement, uint32_t page_alignment, - unsigned long buffer_start, bool interruptible, struct file *persistent_swap_storage, struct ttm_buffer_object **p_bo) @@ -1321,8 +1317,8 @@ int ttm_bo_create(struct ttm_bo_device *bdev, acc_size = ttm_bo_acc_size(bdev, size, sizeof(struct ttm_buffer_object)); ret = ttm_bo_init(bdev, bo, size, type, placement, page_alignment, - buffer_start, interruptible, - persistent_swap_storage, acc_size, NULL, NULL); + interruptible, persistent_swap_storage, acc_size, + NULL, NULL); if (likely(ret == 0)) *p_bo = bo; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index ed3c1e7ddde9..9f37b72a472c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -292,7 +292,7 @@ static int vmw_dummy_query_bo_create(struct vmw_private *dev_priv) PAGE_SIZE, ttm_bo_type_device, &vmw_vram_sys_placement, - 0, 0, false, NULL, + 0, false, NULL, &dev_priv->dummy_query_bo); } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index da3c6b5b98a1..be87124a2769 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -954,7 +954,7 @@ int vmw_surface_evict(struct vmw_private *dev_priv, if (!srf->backup) { ret = ttm_bo_create(&dev_priv->bdev, srf->backup_size, ttm_bo_type_device, - &vmw_srf_placement, 0, 0, true, + &vmw_srf_placement, 0, true, NULL, &srf->backup); if (unlikely(ret != 0)) return ret; @@ -1566,7 +1566,7 @@ int vmw_dmabuf_init(struct vmw_private *dev_priv, ret = ttm_bo_init(bdev, &vmw_bo->base, size, ttm_bo_type_device, placement, - 0, 0, interruptible, + 0, interruptible, NULL, acc_size, NULL, bo_free); return ret; } diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h index 36e8408e00fa..a554c2e22d56 100644 --- a/include/drm/ttm/ttm_bo_api.h +++ b/include/drm/ttm/ttm_bo_api.h @@ -141,8 +141,6 @@ struct ttm_tt; * struct ttm_buffer_object * * @bdev: Pointer to the buffer object device structure. - * @buffer_start: The virtual user-space start address of ttm_bo_type_user - * buffers. * @type: The bo type. * @destroy: Destruction function. If NULL, kfree is used. * @num_pages: Actual number of pages. @@ -200,7 +198,6 @@ struct ttm_buffer_object { struct ttm_bo_global *glob; struct ttm_bo_device *bdev; - unsigned long buffer_start; enum ttm_bo_type type; void (*destroy) (struct ttm_buffer_object *); unsigned long num_pages; @@ -472,8 +469,6 @@ size_t ttm_bo_dma_acc_size(struct ttm_bo_device *bdev, * @type: Requested type of buffer object. * @flags: Initial placement flags. * @page_alignment: Data alignment in pages. - * @buffer_start: Virtual address of user space data backing a - * user buffer object. * @interruptible: If needing to sleep to wait for GPU resources, * sleep interruptible. * @persistent_swap_storage: Usually the swap storage is deleted for buffers @@ -505,7 +500,6 @@ extern int ttm_bo_init(struct ttm_bo_device *bdev, enum ttm_bo_type type, struct ttm_placement *placement, uint32_t page_alignment, - unsigned long buffer_start, bool interrubtible, struct file *persistent_swap_storage, size_t acc_size, @@ -521,8 +515,6 @@ extern int ttm_bo_init(struct ttm_bo_device *bdev, * @type: Requested type of buffer object. * @flags: Initial placement flags. * @page_alignment: Data alignment in pages. - * @buffer_start: Virtual address of user space data backing a - * user buffer object. * @interruptible: If needing to sleep while waiting for GPU resources, * sleep interruptible. * @persistent_swap_storage: Usually the swap storage is deleted for buffers @@ -545,7 +537,6 @@ extern int ttm_bo_create(struct ttm_bo_device *bdev, enum ttm_bo_type type, struct ttm_placement *placement, uint32_t page_alignment, - unsigned long buffer_start, bool interruptible, struct file *persistent_swap_storage, struct ttm_buffer_object **p_bo); -- cgit v1.2.3-59-g8ed1b From 830e2837f5f6811b77cf3aa72e63343a8e6e9ef6 Mon Sep 17 00:00:00 2001 From: Marcin Slusarz Date: Tue, 6 Nov 2012 21:49:53 +0000 Subject: drm/ttm: remove ttm_bo_device->nice_mode It's unused. Signed-off-by: Marcin Slusarz Reviewed-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/ttm/ttm_bo.c | 1 - include/drm/ttm/ttm_bo_driver.h | 3 --- 2 files changed, 4 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index f65182667825..4066e788cff8 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -1573,7 +1573,6 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev, goto out_no_addr_mm; INIT_DELAYED_WORK(&bdev->wq, ttm_bo_delayed_workqueue); - bdev->nice_mode = true; INIT_LIST_HEAD(&bdev->ddestroy); bdev->dev_mapping = NULL; bdev->glob = glob; diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index d803b92b0324..95628e7da5bf 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -521,8 +521,6 @@ struct ttm_bo_global { * lru_lock: Spinlock that protects the buffer+device lru lists and * ddestroy lists. * @val_seq: Current validation sequence. - * @nice_mode: Try nicely to wait for buffer idle when cleaning a manager. - * If a GPU lockup has been detected, this is forced to 0. * @dev_mapping: A pointer to the struct address_space representing the * device address space. * @wq: Work queue structure for the delayed delete workqueue. @@ -556,7 +554,6 @@ struct ttm_bo_device { * Protected by load / firstopen / lastclose /unload sync. */ - bool nice_mode; struct address_space *dev_mapping; /* -- cgit v1.2.3-59-g8ed1b From 91926741ecd28dc246875a39a908c2f6aadb2542 Mon Sep 17 00:00:00 2001 From: Marcin Slusarz Date: Tue, 6 Nov 2012 21:49:54 +0000 Subject: drm/ttm: remove ttm_mem_global->queue It's unused. Signed-off-by: Marcin Slusarz Reviewed-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/ttm/ttm_memory.c | 1 - include/drm/ttm/ttm_memory.h | 2 -- 2 files changed, 3 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/ttm/ttm_memory.c b/drivers/gpu/drm/ttm/ttm_memory.c index 479c6b0467ca..dbc2def887cd 100644 --- a/drivers/gpu/drm/ttm/ttm_memory.c +++ b/drivers/gpu/drm/ttm/ttm_memory.c @@ -367,7 +367,6 @@ int ttm_mem_global_init(struct ttm_mem_global *glob) spin_lock_init(&glob->lock); glob->swap_queue = create_singlethread_workqueue("ttm_swap"); INIT_WORK(&glob->work, ttm_shrink_work); - init_waitqueue_head(&glob->queue); ret = kobject_init_and_add( &glob->kobj, &ttm_mem_glob_kobj_type, ttm_get_kobj(), "memory_accounting"); if (unlikely(ret != 0)) { diff --git a/include/drm/ttm/ttm_memory.h b/include/drm/ttm/ttm_memory.h index d6d1da468c97..72dcbe81dd07 100644 --- a/include/drm/ttm/ttm_memory.h +++ b/include/drm/ttm/ttm_memory.h @@ -60,7 +60,6 @@ struct ttm_mem_shrink { * for the GPU, and this will otherwise block other workqueue tasks(?) * At this point we use only a single-threaded workqueue. * @work: The workqueue callback for the shrink queue. - * @queue: Wait queue for processes suspended waiting for memory. * @lock: Lock to protect the @shrink - and the memory accounting members, * that is, essentially the whole structure with some exceptions. * @zones: Array of pointers to accounting zones. @@ -80,7 +79,6 @@ struct ttm_mem_global { struct ttm_mem_shrink *shrink; struct workqueue_struct *swap_queue; struct work_struct work; - wait_queue_head_t queue; spinlock_t lock; struct ttm_mem_zone *zones[TTM_MEM_MAX_ZONES]; unsigned int num_zones; -- cgit v1.2.3-59-g8ed1b From 5fb4ef0e36b4c6ecc7fb025aaacb3b63b1114e87 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Fri, 12 Oct 2012 15:02:19 +0000 Subject: drm/ttm: remove sync_obj_arg member vmwgfx was its only user and always sets it to the same.. Signed-off-by: Maarten Lankhorst Reviewed-By: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/ttm/ttm_bo.c | 13 ++++--------- drivers/gpu/drm/ttm/ttm_bo_util.c | 1 - drivers/gpu/drm/ttm/ttm_execbuf_util.c | 1 - include/drm/ttm/ttm_bo_api.h | 2 -- include/drm/ttm/ttm_execbuf_util.h | 3 --- 5 files changed, 4 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 4066e788cff8..4cb3f493da76 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -501,7 +501,6 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo) struct ttm_bo_global *glob = bo->glob; struct ttm_bo_driver *driver; void *sync_obj = NULL; - void *sync_obj_arg; int put_count; int ret; @@ -537,7 +536,6 @@ queue: driver = bdev->driver; if (bo->sync_obj) sync_obj = driver->sync_obj_ref(bo->sync_obj); - sync_obj_arg = bo->sync_obj_arg; kref_get(&bo->list_kref); list_add_tail(&bo->ddestroy, &bdev->ddestroy); @@ -545,7 +543,7 @@ queue: spin_unlock(&bdev->fence_lock); if (sync_obj) { - driver->sync_obj_flush(sync_obj, sync_obj_arg); + driver->sync_obj_flush(sync_obj, NULL); driver->sync_obj_unref(&sync_obj); } schedule_delayed_work(&bdev->wq, @@ -1716,7 +1714,6 @@ int ttm_bo_wait(struct ttm_buffer_object *bo, struct ttm_bo_driver *driver = bo->bdev->driver; struct ttm_bo_device *bdev = bo->bdev; void *sync_obj; - void *sync_obj_arg; int ret = 0; if (likely(bo->sync_obj == NULL)) @@ -1724,7 +1721,7 @@ int ttm_bo_wait(struct ttm_buffer_object *bo, while (bo->sync_obj) { - if (driver->sync_obj_signaled(bo->sync_obj, bo->sync_obj_arg)) { + if (driver->sync_obj_signaled(bo->sync_obj, NULL)) { void *tmp_obj = bo->sync_obj; bo->sync_obj = NULL; clear_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags); @@ -1738,9 +1735,8 @@ int ttm_bo_wait(struct ttm_buffer_object *bo, return -EBUSY; sync_obj = driver->sync_obj_ref(bo->sync_obj); - sync_obj_arg = bo->sync_obj_arg; spin_unlock(&bdev->fence_lock); - ret = driver->sync_obj_wait(sync_obj, sync_obj_arg, + ret = driver->sync_obj_wait(sync_obj, NULL, lazy, interruptible); if (unlikely(ret != 0)) { driver->sync_obj_unref(&sync_obj); @@ -1748,8 +1744,7 @@ int ttm_bo_wait(struct ttm_buffer_object *bo, return ret; } spin_lock(&bdev->fence_lock); - if (likely(bo->sync_obj == sync_obj && - bo->sync_obj_arg == sync_obj_arg)) { + if (likely(bo->sync_obj == sync_obj)) { void *tmp_obj = bo->sync_obj; bo->sync_obj = NULL; clear_bit(TTM_BO_PRIV_FLAG_MOVING, diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index 2026060f03e0..81c74f53ce4f 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -630,7 +630,6 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo, bo->sync_obj = NULL; } bo->sync_obj = driver->sync_obj_ref(sync_obj); - bo->sync_obj_arg = sync_obj_arg; if (evict) { ret = ttm_bo_wait(bo, false, false, false); spin_unlock(&bdev->fence_lock); diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c index 1937069432c5..b227a9961a07 100644 --- a/drivers/gpu/drm/ttm/ttm_execbuf_util.c +++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c @@ -223,7 +223,6 @@ void ttm_eu_fence_buffer_objects(struct list_head *list, void *sync_obj) bo = entry->bo; entry->old_sync_obj = bo->sync_obj; bo->sync_obj = driver->sync_obj_ref(sync_obj); - bo->sync_obj_arg = entry->new_sync_obj_arg; ttm_bo_unreserve_locked(bo); entry->reserved = false; } diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h index a554c2e22d56..5ff938df5fce 100644 --- a/include/drm/ttm/ttm_bo_api.h +++ b/include/drm/ttm/ttm_bo_api.h @@ -170,7 +170,6 @@ struct ttm_tt; * @seq_valid: The value of @val_seq is valid. This value is protected by * the bo_device::lru_lock. * @reserved: Deadlock-free lock used for synchronization state transitions. - * @sync_obj_arg: Opaque argument to synchronization object function. * @sync_obj: Pointer to a synchronization object. * @priv_flags: Flags describing buffer object internal state. * @vm_rb: Rb node for the vm rb tree. @@ -252,7 +251,6 @@ struct ttm_buffer_object { * checking NULL while reserved but not holding the mentioned lock. */ - void *sync_obj_arg; void *sync_obj; unsigned long priv_flags; diff --git a/include/drm/ttm/ttm_execbuf_util.h b/include/drm/ttm/ttm_execbuf_util.h index 1926cae373ba..547e19f06e57 100644 --- a/include/drm/ttm/ttm_execbuf_util.h +++ b/include/drm/ttm/ttm_execbuf_util.h @@ -39,8 +39,6 @@ * * @head: list head for thread-private list. * @bo: refcounted buffer object pointer. - * @new_sync_obj_arg: New sync_obj_arg for @bo, to be used once - * adding a new sync object. * @reserved: Indicates whether @bo has been reserved for validation. * @removed: Indicates whether @bo has been removed from lru lists. * @put_count: Number of outstanding references on bo::list_kref. @@ -50,7 +48,6 @@ struct ttm_validate_buffer { struct list_head head; struct ttm_buffer_object *bo; - void *new_sync_obj_arg; bool reserved; bool removed; int put_count; -- cgit v1.2.3-59-g8ed1b From b03640b1de2eb349c2453d060d0bd0b0486e29b8 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Fri, 12 Oct 2012 15:03:11 +0000 Subject: drm/ttm: remove sync_obj_arg from ttm_bo_move_accel_cleanup Signed-off-by: Maarten Lankhorst Reviewed-By: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/nouveau/nouveau_bo.c | 2 +- drivers/gpu/drm/radeon/radeon_ttm.c | 2 +- drivers/gpu/drm/ttm/ttm_bo_util.c | 1 - include/drm/ttm/ttm_bo_driver.h | 3 --- 4 files changed, 2 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 3cbf1a8cf551..6a2592f7daae 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -566,7 +566,7 @@ nouveau_bo_move_accel_cleanup(struct nouveau_channel *chan, if (ret) return ret; - ret = ttm_bo_move_accel_cleanup(&nvbo->bo, fence, NULL, evict, + ret = ttm_bo_move_accel_cleanup(&nvbo->bo, fence, evict, no_wait_reserve, no_wait_gpu, new_mem); nouveau_fence_unref(&fence); return ret; diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 5ebe1b3e5db2..929be87204da 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -265,7 +265,7 @@ static int radeon_move_blit(struct ttm_buffer_object *bo, new_mem->num_pages * (PAGE_SIZE / RADEON_GPU_PAGE_SIZE), /* GPU pages */ &fence); /* FIXME: handle copy error */ - r = ttm_bo_move_accel_cleanup(bo, (void *)fence, NULL, + r = ttm_bo_move_accel_cleanup(bo, (void *)fence, evict, no_wait_reserve, no_wait_gpu, new_mem); radeon_fence_unref(&fence); return r; diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index 81c74f53ce4f..b9c4e515b1d8 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -611,7 +611,6 @@ EXPORT_SYMBOL(ttm_bo_kunmap); int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo, void *sync_obj, - void *sync_obj_arg, bool evict, bool no_wait_reserve, bool no_wait_gpu, struct ttm_mem_reg *new_mem) diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index 95628e7da5bf..fc5fad09dae4 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -970,8 +970,6 @@ extern void ttm_bo_free_old_node(struct ttm_buffer_object *bo); * * @bo: A pointer to a struct ttm_buffer_object. * @sync_obj: A sync object that signals when moving is complete. - * @sync_obj_arg: An argument to pass to the sync object idle / wait - * functions. * @evict: This is an evict move. Don't return until the buffer is idle. * @no_wait_reserve: Return immediately if other buffers are busy. * @no_wait_gpu: Return immediately if the GPU is busy. @@ -987,7 +985,6 @@ extern void ttm_bo_free_old_node(struct ttm_buffer_object *bo); extern int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo, void *sync_obj, - void *sync_obj_arg, bool evict, bool no_wait_reserve, bool no_wait_gpu, struct ttm_mem_reg *new_mem); -- cgit v1.2.3-59-g8ed1b From dedfdffd448aea2543b59fd504b92b8212ab3b7d Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Fri, 12 Oct 2012 15:04:00 +0000 Subject: drm/ttm: remove sync_arg from driver functions Signed-off-by: Maarten Lankhorst Reviewed-By: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/nouveau/nouveau_bo.c | 6 +++--- drivers/gpu/drm/radeon/radeon_ttm.c | 7 +++---- drivers/gpu/drm/ttm/ttm_bo.c | 6 +++--- drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c | 7 +++---- include/drm/ttm/ttm_bo_driver.h | 6 +++--- 5 files changed, 15 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 6a2592f7daae..4c950b4cf416 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -1472,19 +1472,19 @@ nouveau_bo_fence_ref(void *sync_obj) } static bool -nouveau_bo_fence_signalled(void *sync_obj, void *sync_arg) +nouveau_bo_fence_signalled(void *sync_obj) { return nouveau_fence_done(sync_obj); } static int -nouveau_bo_fence_wait(void *sync_obj, void *sync_arg, bool lazy, bool intr) +nouveau_bo_fence_wait(void *sync_obj, bool lazy, bool intr) { return nouveau_fence_wait(sync_obj, lazy, intr); } static int -nouveau_bo_fence_flush(void *sync_obj, void *sync_arg) +nouveau_bo_fence_flush(void *sync_obj) { return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 929be87204da..563c8edcb03b 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -471,13 +471,12 @@ static void radeon_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_re { } -static int radeon_sync_obj_wait(void *sync_obj, void *sync_arg, - bool lazy, bool interruptible) +static int radeon_sync_obj_wait(void *sync_obj, bool lazy, bool interruptible) { return radeon_fence_wait((struct radeon_fence *)sync_obj, interruptible); } -static int radeon_sync_obj_flush(void *sync_obj, void *sync_arg) +static int radeon_sync_obj_flush(void *sync_obj) { return 0; } @@ -492,7 +491,7 @@ static void *radeon_sync_obj_ref(void *sync_obj) return radeon_fence_ref((struct radeon_fence *)sync_obj); } -static bool radeon_sync_obj_signaled(void *sync_obj, void *sync_arg) +static bool radeon_sync_obj_signaled(void *sync_obj) { return radeon_fence_signaled((struct radeon_fence *)sync_obj); } diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 4cb3f493da76..d1e5326d442c 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -543,7 +543,7 @@ queue: spin_unlock(&bdev->fence_lock); if (sync_obj) { - driver->sync_obj_flush(sync_obj, NULL); + driver->sync_obj_flush(sync_obj); driver->sync_obj_unref(&sync_obj); } schedule_delayed_work(&bdev->wq, @@ -1721,7 +1721,7 @@ int ttm_bo_wait(struct ttm_buffer_object *bo, while (bo->sync_obj) { - if (driver->sync_obj_signaled(bo->sync_obj, NULL)) { + if (driver->sync_obj_signaled(bo->sync_obj)) { void *tmp_obj = bo->sync_obj; bo->sync_obj = NULL; clear_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags); @@ -1736,7 +1736,7 @@ int ttm_bo_wait(struct ttm_buffer_object *bo, sync_obj = driver->sync_obj_ref(bo->sync_obj); spin_unlock(&bdev->fence_lock); - ret = driver->sync_obj_wait(sync_obj, NULL, + ret = driver->sync_obj_wait(sync_obj, lazy, interruptible); if (unlikely(ret != 0)) { driver->sync_obj_unref(&sync_obj); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c index da12922b6313..ef1109c8fec8 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c @@ -310,21 +310,20 @@ static void vmw_sync_obj_unref(void **sync_obj) vmw_fence_obj_unreference((struct vmw_fence_obj **) sync_obj); } -static int vmw_sync_obj_flush(void *sync_obj, void *sync_arg) +static int vmw_sync_obj_flush(void *sync_obj) { vmw_fence_obj_flush((struct vmw_fence_obj *) sync_obj); return 0; } -static bool vmw_sync_obj_signaled(void *sync_obj, void *sync_arg) +static bool vmw_sync_obj_signaled(void *sync_obj) { return vmw_fence_obj_signaled((struct vmw_fence_obj *) sync_obj, DRM_VMW_FENCE_FLAG_EXEC); } -static int vmw_sync_obj_wait(void *sync_obj, void *sync_arg, - bool lazy, bool interruptible) +static int vmw_sync_obj_wait(void *sync_obj, bool lazy, bool interruptible) { return vmw_fence_obj_wait((struct vmw_fence_obj *) sync_obj, DRM_VMW_FENCE_FLAG_EXEC, diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index fc5fad09dae4..4789beee3b77 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -422,10 +422,10 @@ struct ttm_bo_driver { * documentation. */ - bool (*sync_obj_signaled) (void *sync_obj, void *sync_arg); - int (*sync_obj_wait) (void *sync_obj, void *sync_arg, + bool (*sync_obj_signaled) (void *sync_obj); + int (*sync_obj_wait) (void *sync_obj, bool lazy, bool interruptible); - int (*sync_obj_flush) (void *sync_obj, void *sync_arg); + int (*sync_obj_flush) (void *sync_obj); void (*sync_obj_unref) (void **sync_obj); void *(*sync_obj_ref) (void *sync_obj); -- cgit v1.2.3-59-g8ed1b From 4b20db3de8dab005b07c74161cb041db8c5ff3a7 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 6 Nov 2012 11:31:49 +0000 Subject: kref: Implement kref_get_unless_zero v3 This function is intended to simplify locking around refcounting for objects that can be looked up from a lookup structure, and which are removed from that lookup structure in the object destructor. Operations on such objects require at least a read lock around lookup + kref_get, and a write lock around kref_put + remove from lookup structure. Furthermore, RCU implementations become extremely tricky. With a lookup followed by a kref_get_unless_zero *with return value check* locking in the kref_put path can be deferred to the actual removal from the lookup structure and RCU lookups become trivial. v2: Formatting fixes. v3: Invert the return value. Signed-off-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- include/linux/kref.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'include') diff --git a/include/linux/kref.h b/include/linux/kref.h index 65af6887872f..4972e6e9ca93 100644 --- a/include/linux/kref.h +++ b/include/linux/kref.h @@ -111,4 +111,25 @@ static inline int kref_put_mutex(struct kref *kref, } return 0; } + +/** + * kref_get_unless_zero - Increment refcount for object unless it is zero. + * @kref: object. + * + * Return non-zero if the increment succeeded. Otherwise return 0. + * + * This function is intended to simplify locking around refcounting for + * objects that can be looked up from a lookup structure, and which are + * removed from that lookup structure in the object destructor. + * Operations on such objects require at least a read lock around + * lookup + kref_get, and a write lock around kref_put + remove from lookup + * structure. Furthermore, RCU implementations become extremely tricky. + * With a lookup followed by a kref_get_unless_zero *with return value check* + * locking in the kref_put path can be deferred to the actual removal from + * the lookup structure and RCU lookups become trivial. + */ +static inline int __must_check kref_get_unless_zero(struct kref *kref) +{ + return atomic_add_unless(&kref->refcount, 1, 0); +} #endif /* _KREF_H_ */ -- cgit v1.2.3-59-g8ed1b From cdad05216c2b2edfe92a9f87d6ae51aab277f3b2 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 6 Nov 2012 11:31:50 +0000 Subject: drm/ttm, drm/vmwgfx: Use RCU locking for object lookups v3 The mostly used lookup+get put+potential_destroy path of TTM objects is converted to use RCU locks. This will substantially decrease the amount of locked bus cycles during normal operation. Since we use kfree_rcu to free the objects, no rcu synchronization is needed at module unload time. v2: Don't touch include/linux/kref.h v3: Adapt to kref_get_unless_zero return value change Signed-off-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/ttm/ttm_object.c | 30 +++++++++++------------------- drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 8 ++++---- include/drm/ttm/ttm_object.h | 4 ++++ 3 files changed, 19 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/ttm/ttm_object.c b/drivers/gpu/drm/ttm/ttm_object.c index c7857874956a..f18eeb458139 100644 --- a/drivers/gpu/drm/ttm/ttm_object.c +++ b/drivers/gpu/drm/ttm/ttm_object.c @@ -80,7 +80,7 @@ struct ttm_object_file { */ struct ttm_object_device { - rwlock_t object_lock; + spinlock_t object_lock; struct drm_open_hash object_hash; atomic_t object_count; struct ttm_mem_global *mem_glob; @@ -157,12 +157,12 @@ int ttm_base_object_init(struct ttm_object_file *tfile, base->refcount_release = refcount_release; base->ref_obj_release = ref_obj_release; base->object_type = object_type; - write_lock(&tdev->object_lock); + spin_lock(&tdev->object_lock); kref_init(&base->refcount); ret = drm_ht_just_insert_please(&tdev->object_hash, &base->hash, (unsigned long)base, 31, 0, 0); - write_unlock(&tdev->object_lock); + spin_unlock(&tdev->object_lock); if (unlikely(ret != 0)) goto out_err0; @@ -186,30 +186,22 @@ static void ttm_release_base(struct kref *kref) container_of(kref, struct ttm_base_object, refcount); struct ttm_object_device *tdev = base->tfile->tdev; + spin_lock(&tdev->object_lock); (void)drm_ht_remove_item(&tdev->object_hash, &base->hash); - write_unlock(&tdev->object_lock); + spin_unlock(&tdev->object_lock); if (base->refcount_release) { ttm_object_file_unref(&base->tfile); base->refcount_release(&base); } - write_lock(&tdev->object_lock); } void ttm_base_object_unref(struct ttm_base_object **p_base) { struct ttm_base_object *base = *p_base; - struct ttm_object_device *tdev = base->tfile->tdev; *p_base = NULL; - /* - * Need to take the lock here to avoid racing with - * users trying to look up the object. - */ - - write_lock(&tdev->object_lock); kref_put(&base->refcount, ttm_release_base); - write_unlock(&tdev->object_lock); } EXPORT_SYMBOL(ttm_base_object_unref); @@ -221,14 +213,14 @@ struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file *tfile, struct drm_hash_item *hash; int ret; - read_lock(&tdev->object_lock); + rcu_read_lock(); ret = drm_ht_find_item(&tdev->object_hash, key, &hash); if (likely(ret == 0)) { base = drm_hash_entry(hash, struct ttm_base_object, hash); - kref_get(&base->refcount); + ret = kref_get_unless_zero(&base->refcount) ? 0 : -EINVAL; } - read_unlock(&tdev->object_lock); + rcu_read_unlock(); if (unlikely(ret != 0)) return NULL; @@ -426,7 +418,7 @@ struct ttm_object_device *ttm_object_device_init(struct ttm_mem_global return NULL; tdev->mem_glob = mem_glob; - rwlock_init(&tdev->object_lock); + spin_lock_init(&tdev->object_lock); atomic_set(&tdev->object_count, 0); ret = drm_ht_create(&tdev->object_hash, hash_order); @@ -444,9 +436,9 @@ void ttm_object_device_release(struct ttm_object_device **p_tdev) *p_tdev = NULL; - write_lock(&tdev->object_lock); + spin_lock(&tdev->object_lock); drm_ht_remove(&tdev->object_hash); - write_unlock(&tdev->object_lock); + spin_unlock(&tdev->object_lock); kfree(tdev); } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 596cef3c9189..292c988c54ea 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -351,7 +351,7 @@ static void vmw_user_context_free(struct vmw_resource *res) container_of(res, struct vmw_user_context, res); struct vmw_private *dev_priv = res->dev_priv; - kfree(ctx); + ttm_base_object_kfree(ctx, base); ttm_mem_global_free(vmw_mem_glob(dev_priv), vmw_user_context_size); } @@ -1143,7 +1143,7 @@ static void vmw_user_surface_free(struct vmw_resource *res) kfree(srf->offsets); kfree(srf->sizes); kfree(srf->snooper.image); - kfree(user_srf); + ttm_base_object_kfree(user_srf, base); ttm_mem_global_free(vmw_mem_glob(dev_priv), size); } @@ -1571,7 +1571,7 @@ static void vmw_user_dmabuf_destroy(struct ttm_buffer_object *bo) { struct vmw_user_dma_buffer *vmw_user_bo = vmw_user_dma_buffer(bo); - kfree(vmw_user_bo); + ttm_base_object_kfree(vmw_user_bo, base); } static void vmw_user_dmabuf_release(struct ttm_base_object **p_base) @@ -1759,7 +1759,7 @@ static void vmw_user_stream_free(struct vmw_resource *res) container_of(res, struct vmw_user_stream, stream.res); struct vmw_private *dev_priv = res->dev_priv; - kfree(stream); + ttm_base_object_kfree(stream, base); ttm_mem_global_free(vmw_mem_glob(dev_priv), vmw_user_stream_size); } diff --git a/include/drm/ttm/ttm_object.h b/include/drm/ttm/ttm_object.h index b01c563b2751..fc0cf0649901 100644 --- a/include/drm/ttm/ttm_object.h +++ b/include/drm/ttm/ttm_object.h @@ -40,6 +40,7 @@ #include #include #include +#include #include /** @@ -120,6 +121,7 @@ struct ttm_object_device; */ struct ttm_base_object { + struct rcu_head rhead; struct drm_hash_item hash; enum ttm_object_type object_type; bool shareable; @@ -268,4 +270,6 @@ extern struct ttm_object_device *ttm_object_device_init extern void ttm_object_device_release(struct ttm_object_device **p_tdev); +#define ttm_base_object_kfree(__object, __base)\ + kfree_rcu(__object, __base.rhead) #endif -- cgit v1.2.3-59-g8ed1b From 654aa79259a19f0d5e3cf9cb20aff56dc3b041b7 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 6 Nov 2012 14:39:43 +0100 Subject: drm/ttm: alter cpu_writers to return -EBUSY in ttm_execbuf_util reservations This is similar to other platforms that don't allow command submission to buffers locked on the cpu. Signed-off-by: Maarten Lankhorst Reviewed-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/ttm/ttm_bo.c | 13 +------------ drivers/gpu/drm/ttm/ttm_execbuf_util.c | 5 +---- include/drm/ttm/ttm_bo_api.h | 5 +++-- include/drm/ttm/ttm_bo_driver.h | 14 -------------- 4 files changed, 5 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 5f61f133b419..7426fe59108e 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -1053,16 +1053,6 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, } EXPORT_SYMBOL(ttm_bo_mem_space); -int ttm_bo_wait_cpu(struct ttm_buffer_object *bo, bool no_wait) -{ - if ((atomic_read(&bo->cpu_writers) > 0) && no_wait) - return -EBUSY; - - return wait_event_interruptible(bo->event_queue, - atomic_read(&bo->cpu_writers) == 0); -} -EXPORT_SYMBOL(ttm_bo_wait_cpu); - int ttm_bo_move_buffer(struct ttm_buffer_object *bo, struct ttm_placement *placement, bool interruptible, bool no_wait_reserve, @@ -1788,8 +1778,7 @@ EXPORT_SYMBOL(ttm_bo_synccpu_write_grab); void ttm_bo_synccpu_write_release(struct ttm_buffer_object *bo) { - if (atomic_dec_and_test(&bo->cpu_writers)) - wake_up_all(&bo->event_queue); + atomic_dec(&bo->cpu_writers); } EXPORT_SYMBOL(ttm_bo_synccpu_write_release); diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c index b227a9961a07..1986d006c264 100644 --- a/drivers/gpu/drm/ttm/ttm_execbuf_util.c +++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c @@ -185,10 +185,7 @@ retry_this_bo: ttm_eu_backoff_reservation_locked(list); spin_unlock(&glob->lru_lock); ttm_eu_list_ref_sub(list); - ret = ttm_bo_wait_cpu(bo, false); - if (ret) - return ret; - goto retry; + return -EBUSY; } } diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h index 5ff938df5fce..c6cae733ddef 100644 --- a/include/drm/ttm/ttm_bo_api.h +++ b/include/drm/ttm/ttm_bo_api.h @@ -424,8 +424,9 @@ extern void ttm_bo_unlock_delayed_workqueue(struct ttm_bo_device *bdev, * @no_wait: Return immediately if buffer is busy. * * Synchronizes a buffer object for CPU RW access. This means - * blocking command submission that affects the buffer and - * waiting for buffer idle. This lock is recursive. + * command submission that affects the buffer will return -EBUSY + * until ttm_bo_synccpu_write_release is called. + * * Returns * -EBUSY if the buffer is busy and no_wait is true. * -ERESTARTSYS if interrupted by a signal. diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index 4789beee3b77..dd96442cdc2a 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -726,20 +726,6 @@ extern void ttm_bo_mem_put(struct ttm_buffer_object *bo, extern void ttm_bo_mem_put_locked(struct ttm_buffer_object *bo, struct ttm_mem_reg *mem); -/** - * ttm_bo_wait_for_cpu - * - * @bo: Pointer to a struct ttm_buffer_object. - * @no_wait: Don't sleep while waiting. - * - * Wait until a buffer object is no longer sync'ed for CPU access. - * Returns: - * -EBUSY: Buffer object was sync'ed for CPU access. (only if no_wait == 1). - * -ERESTARTSYS: An interruptible sleep was interrupted by a signal. - */ - -extern int ttm_bo_wait_cpu(struct ttm_buffer_object *bo, bool no_wait); - extern void ttm_bo_global_release(struct drm_global_reference *ref); extern int ttm_bo_global_init(struct drm_global_reference *ref); -- cgit v1.2.3-59-g8ed1b From 384cc2f9688994dfd505011ba3b08e0a702030b0 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 20 Nov 2012 12:16:47 +0000 Subject: drm: Add a hash-tab rcu-safe API While hashtab should now be RCU-safe, Add a drm_ht_xxx_api for consumers to use to make it obvious what locking mechanism is used. Document the way the rcu-safe interface should be used. Don't use rcu-safe list traversal in modify operations where we should use a spinlock / mutex anyway. Signed-off-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_hashtab.c | 26 ++++++++++++++++++++++---- include/drm/drm_hashtab.h | 14 ++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_hashtab.c b/drivers/gpu/drm/drm_hashtab.c index 5729e390aa4e..80254547a3f8 100644 --- a/drivers/gpu/drm/drm_hashtab.c +++ b/drivers/gpu/drm/drm_hashtab.c @@ -67,7 +67,7 @@ void drm_ht_verbose_list(struct drm_open_hash *ht, unsigned long key) hashed_key = hash_long(key, ht->order); DRM_DEBUG("Key is 0x%08lx, Hashed key is 0x%08x\n", key, hashed_key); h_list = &ht->table[hashed_key]; - hlist_for_each_entry_rcu(entry, list, h_list, head) + hlist_for_each_entry(entry, list, h_list, head) DRM_DEBUG("count %d, key: 0x%08lx\n", count++, entry->key); } @@ -81,7 +81,7 @@ static struct hlist_node *drm_ht_find_key(struct drm_open_hash *ht, hashed_key = hash_long(key, ht->order); h_list = &ht->table[hashed_key]; - hlist_for_each_entry_rcu(entry, list, h_list, head) { + hlist_for_each_entry(entry, list, h_list, head) { if (entry->key == key) return list; if (entry->key > key) @@ -90,6 +90,24 @@ static struct hlist_node *drm_ht_find_key(struct drm_open_hash *ht, return NULL; } +static struct hlist_node *drm_ht_find_key_rcu(struct drm_open_hash *ht, + unsigned long key) +{ + struct drm_hash_item *entry; + struct hlist_head *h_list; + struct hlist_node *list; + unsigned int hashed_key; + + hashed_key = hash_long(key, ht->order); + h_list = &ht->table[hashed_key]; + hlist_for_each_entry_rcu(entry, list, h_list, head) { + if (entry->key == key) + return list; + if (entry->key > key) + break; + } + return NULL; +} int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item) { @@ -102,7 +120,7 @@ int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item) hashed_key = hash_long(key, ht->order); h_list = &ht->table[hashed_key]; parent = NULL; - hlist_for_each_entry_rcu(entry, list, h_list, head) { + hlist_for_each_entry(entry, list, h_list, head) { if (entry->key == key) return -EINVAL; if (entry->key > key) @@ -152,7 +170,7 @@ int drm_ht_find_item(struct drm_open_hash *ht, unsigned long key, { struct hlist_node *list; - list = drm_ht_find_key(ht, key); + list = drm_ht_find_key_rcu(ht, key); if (!list) return -EINVAL; diff --git a/include/drm/drm_hashtab.h b/include/drm/drm_hashtab.h index 3650d5d011ee..fce2ef3fdfff 100644 --- a/include/drm/drm_hashtab.h +++ b/include/drm/drm_hashtab.h @@ -61,5 +61,19 @@ extern int drm_ht_remove_key(struct drm_open_hash *ht, unsigned long key); extern int drm_ht_remove_item(struct drm_open_hash *ht, struct drm_hash_item *item); extern void drm_ht_remove(struct drm_open_hash *ht); +/* + * RCU-safe interface + * + * The user of this API needs to make sure that two or more instances of the + * hash table manipulation functions are never run simultaneously. + * The lookup function drm_ht_find_item_rcu may, however, run simultaneously + * with any of the manipulation functions as long as it's called from within + * an RCU read-locked section. + */ +#define drm_ht_insert_item_rcu drm_ht_insert_item +#define drm_ht_just_insert_please_rcu drm_ht_just_insert_please +#define drm_ht_remove_key_rcu drm_ht_remove_key +#define drm_ht_remove_item_rcu drm_ht_remove_item +#define drm_ht_find_item_rcu drm_ht_find_item #endif -- cgit v1.2.3-59-g8ed1b From 28164fdad85ec806f30c76fe98ed0e3abc91d2d7 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 1 Nov 2012 14:45:18 +0100 Subject: drm/doc: add new dp helpers into drm DocBook I didn't bother with documenting the really trivial new "extract something from dpcd" helpers, but the i2c over aux ch is now documented a bit. v2: Clarify the comment for i2c_dp_aux_add_bus a bit. v3: Fix more spelling fail spotted by Laurent Pinchart. Acked-by: Laurent Pinchart Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- Documentation/DocBook/drm.tmpl | 6 ++++++ drivers/gpu/drm/drm_dp_helper.c | 21 +++++++++++++++++++++ include/drm/drm_dp_helper.h | 8 ++++++++ 3 files changed, 35 insertions(+) (limited to 'include') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index cb8024e62dfc..4ee2304f82f9 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -2105,6 +2105,12 @@ void intel_crt_init(struct drm_device *dev) !Pdrivers/gpu/drm/drm_fb_helper.c fbdev helpers !Edrivers/gpu/drm/drm_fb_helper.c + + Display Port Helper Functions Reference +!Pdrivers/gpu/drm/drm_dp_helper.c dp helpers +!Iinclude/drm/drm_dp_helper.h +!Edrivers/gpu/drm/drm_dp_helper.c + diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 3c4cccd0d753..89e196627160 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -30,6 +30,15 @@ #include #include +/** + * DOC: dp helpers + * + * These functions contain some common logic and helpers at various abstraction + * levels to deal with Display Port sink devices and related things like DP aux + * channel transfers, EDID reading over DP aux channels, decoding certain DPCD + * blocks, ... + */ + /* Run a single AUX_CH I2C transaction, writing/reading data as necessary */ static int i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode, @@ -193,6 +202,18 @@ i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter) return 0; } +/** + * i2c_dp_aux_add_bus() - register an i2c adapter using the aux ch helper + * @adapter: i2c adapter to register + * + * This registers an i2c adapater that uses dp aux channel as it's underlaying + * transport. The driver needs to fill out the &i2c_algo_dp_aux_data structure + * and store it in the algo_data member of the @adapter argument. This will be + * used by the i2c over dp aux algorithm to drive the hardware. + * + * RETURNS: + * 0 on success, -ERRNO on failure. + */ int i2c_dp_aux_add_bus(struct i2c_adapter *adapter) { diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index c09d36741c94..e8e1417af3d9 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -312,6 +312,14 @@ #define MODE_I2C_READ 4 #define MODE_I2C_STOP 8 +/** + * struct i2c_algo_dp_aux_data - driver interface structure for i2c over dp + * aux algorithm + * @running: set by the algo indicating whether an i2c is ongoing or whether + * the i2c bus is quiescent + * @address: i2c target address for the currently ongoing transfer + * @aux_ch: driver callback to transfer a single byte of the i2c payload + */ struct i2c_algo_dp_aux_data { bool running; u16 address; -- cgit v1.2.3-59-g8ed1b From 374a868a726eb8a1cb28ba88805e51ce34222f8d Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 23 Nov 2012 12:09:26 -0200 Subject: drm: add drm_mode_cea_vic This function returns the VIC of the mode. This value can be used when creating AVI InfoFrames. Cc: Thierry Reding Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=50371 Signed-off-by: Paulo Zanoni Reviewed-by: Thierry Reding Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_edid.c | 19 +++++++++++++++++++ include/drm/drm_crtc.h | 1 + 2 files changed, 20 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 164820003546..011bd4f4635e 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2079,3 +2079,22 @@ int drm_add_modes_noedid(struct drm_connector *connector, return num_modes; } EXPORT_SYMBOL(drm_add_modes_noedid); + +/** + * drm_mode_cea_vic - return the CEA-861 VIC of a given mode + * @mode: mode + * + * RETURNS: + * The VIC number, 0 in case it's not a CEA-861 mode. + */ +uint8_t drm_mode_cea_vic(const struct drm_display_mode *mode) +{ + uint8_t i; + + for (i = 0; i < drm_num_cea_modes; i++) + if (drm_mode_equal(mode, &edid_cea_modes[i])) + return i + 1; + + return 0; +} +EXPORT_SYMBOL(drm_mode_cea_vic); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index c0635b7f8696..3538eda94ef5 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1055,6 +1055,7 @@ extern struct drm_display_mode *drm_gtf_mode_complex(struct drm_device *dev, int GTF_2C, int GTF_K, int GTF_2J); extern int drm_add_modes_noedid(struct drm_connector *connector, int hdisplay, int vdisplay); +extern uint8_t drm_mode_cea_vic(const struct drm_display_mode *mode); extern int drm_edid_header_is_valid(const u8 *raw_edid); extern bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid); -- cgit v1.2.3-59-g8ed1b From 4b9347dcbe3b426d19bda99b2061a2d1fe5a2dce Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 15 Oct 2012 16:03:51 +0200 Subject: common: DMA-mapping: add DMA_ATTR_FORCE_CONTIGUOUS attribute This patch adds DMA_ATTR_FORCE_CONTIGUOUS attribute to the DMA-mapping subsystem. By default DMA-mapping subsystem is allowed to assemble the buffer allocated by dma_alloc_attrs() function from individual pages if it can be mapped as contiguous chunk into device dma address space. By specifing this attribute the allocated buffer is forced to be contiguous also in physical memory. Signed-off-by: Marek Szyprowski --- Documentation/DMA-attributes.txt | 9 +++++++++ include/linux/dma-attrs.h | 1 + 2 files changed, 10 insertions(+) (limited to 'include') diff --git a/Documentation/DMA-attributes.txt b/Documentation/DMA-attributes.txt index f50309081ac7..e59480db9ee0 100644 --- a/Documentation/DMA-attributes.txt +++ b/Documentation/DMA-attributes.txt @@ -91,3 +91,12 @@ transferred to 'device' domain. This attribute can be also used for dma_unmap_{single,page,sg} functions family to force buffer to stay in device domain after releasing a mapping for it. Use this attribute with care! + +DMA_ATTR_FORCE_CONTIGUOUS +------------------------- + +By default DMA-mapping subsystem is allowed to assemble the buffer +allocated by dma_alloc_attrs() function from individual pages if it can +be mapped as contiguous chunk into device dma address space. By +specifing this attribute the allocated buffer is forced to be contiguous +also in physical memory. diff --git a/include/linux/dma-attrs.h b/include/linux/dma-attrs.h index f83f793223ff..c8e1831d7572 100644 --- a/include/linux/dma-attrs.h +++ b/include/linux/dma-attrs.h @@ -17,6 +17,7 @@ enum dma_attr { DMA_ATTR_NON_CONSISTENT, DMA_ATTR_NO_KERNEL_MAPPING, DMA_ATTR_SKIP_CPU_SYNC, + DMA_ATTR_FORCE_CONTIGUOUS, DMA_ATTR_MAX, }; -- cgit v1.2.3-59-g8ed1b From 584955632841c069678833f3320b4f6d21a8215e Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 11 Oct 2012 20:50:56 -0500 Subject: drm: remove legacy drm_connector_property fxns Replace references to and remove the connector property fxns, which have been superseded with the more general object property fxns: + drm_connector_attach_property -> drm_object_attach_property + drm_connector_property_set_value -> drm_object_property_set_value + drm_connector_property_get_value -> drm_object_property_get_value Signed-off-by: Rob Clark --- drivers/gpu/drm/drm_crtc.c | 31 +++++-------------------------- drivers/gpu/drm/drm_fb_helper.c | 2 +- drivers/gpu/drm/drm_sysfs.c | 6 +++--- include/drm/drm_crtc.h | 8 -------- 4 files changed, 9 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index d6d007275947..f2d667b8bee2 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -559,11 +559,11 @@ int drm_connector_init(struct drm_device *dev, dev->mode_config.num_connector++; if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL) - drm_connector_attach_property(connector, + drm_object_attach_property(&connector->base, dev->mode_config.edid_property, 0); - drm_connector_attach_property(connector, + drm_object_attach_property(&connector->base, dev->mode_config.dpms_property, 0); out: @@ -2928,27 +2928,6 @@ void drm_property_destroy(struct drm_device *dev, struct drm_property *property) } EXPORT_SYMBOL(drm_property_destroy); -void drm_connector_attach_property(struct drm_connector *connector, - struct drm_property *property, uint64_t init_val) -{ - drm_object_attach_property(&connector->base, property, init_val); -} -EXPORT_SYMBOL(drm_connector_attach_property); - -int drm_connector_property_set_value(struct drm_connector *connector, - struct drm_property *property, uint64_t value) -{ - return drm_object_property_set_value(&connector->base, property, value); -} -EXPORT_SYMBOL(drm_connector_property_set_value); - -int drm_connector_property_get_value(struct drm_connector *connector, - struct drm_property *property, uint64_t *val) -{ - return drm_object_property_get_value(&connector->base, property, val); -} -EXPORT_SYMBOL(drm_connector_property_get_value); - void drm_object_attach_property(struct drm_mode_object *obj, struct drm_property *property, uint64_t init_val) @@ -3185,7 +3164,7 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector, /* Delete edid, when there is none. */ if (!edid) { connector->edid_blob_ptr = NULL; - ret = drm_connector_property_set_value(connector, dev->mode_config.edid_property, 0); + ret = drm_object_property_set_value(&connector->base, dev->mode_config.edid_property, 0); return ret; } @@ -3195,7 +3174,7 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector, if (!connector->edid_blob_ptr) return -EINVAL; - ret = drm_connector_property_set_value(connector, + ret = drm_object_property_set_value(&connector->base, dev->mode_config.edid_property, connector->edid_blob_ptr->base.id); @@ -3262,7 +3241,7 @@ static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj, /* store the property value if successful */ if (!ret) - drm_connector_property_set_value(connector, property, value); + drm_object_property_set_value(&connector->base, property, value); return ret; } diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 05e623a5d095..954d175bd7fa 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -348,7 +348,7 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) for (j = 0; j < fb_helper->connector_count; j++) { connector = fb_helper->connector_info[j]->connector; connector->funcs->dpms(connector, dpms_mode); - drm_connector_property_set_value(connector, + drm_object_property_set_value(&connector->base, dev->mode_config.dpms_property, dpms_mode); } } diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 05cd8fe062af..02296653a058 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -182,7 +182,7 @@ static ssize_t dpms_show(struct device *device, uint64_t dpms_status; int ret; - ret = drm_connector_property_get_value(connector, + ret = drm_object_property_get_value(&connector->base, dev->mode_config.dpms_property, &dpms_status); if (ret) @@ -277,7 +277,7 @@ static ssize_t subconnector_show(struct device *device, return 0; } - ret = drm_connector_property_get_value(connector, prop, &subconnector); + ret = drm_object_property_get_value(&connector->base, prop, &subconnector); if (ret) return 0; @@ -318,7 +318,7 @@ static ssize_t select_subconnector_show(struct device *device, return 0; } - ret = drm_connector_property_get_value(connector, prop, &subconnector); + ret = drm_object_property_get_value(&connector->base, prop, &subconnector); if (ret) return 0; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index c0635b7f8696..ee9b0b59237f 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -920,12 +920,6 @@ extern void drm_mode_set_crtcinfo(struct drm_display_mode *p, extern void drm_mode_connector_list_update(struct drm_connector *connector); extern int drm_mode_connector_update_edid_property(struct drm_connector *connector, struct edid *edid); -extern int drm_connector_property_set_value(struct drm_connector *connector, - struct drm_property *property, - uint64_t value); -extern int drm_connector_property_get_value(struct drm_connector *connector, - struct drm_property *property, - uint64_t *value); extern int drm_object_property_set_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t val); @@ -947,8 +941,6 @@ extern int drmfb_remove(struct drm_device *dev, struct drm_framebuffer *fb); extern void drm_crtc_probe_connector_modes(struct drm_device *dev, int maxX, int maxY); extern bool drm_crtc_in_use(struct drm_crtc *crtc); -extern void drm_connector_attach_property(struct drm_connector *connector, - struct drm_property *property, uint64_t init_val); extern void drm_object_attach_property(struct drm_mode_object *obj, struct drm_property *property, uint64_t init_val); -- cgit v1.2.3-59-g8ed1b From 2a3098ff6c2109557868f9f230f4725312dcb882 Mon Sep 17 00:00:00 2001 From: Inki Dae Date: Sun, 4 Nov 2012 05:48:52 -0800 Subject: drm/exynos: add userptr feature for g2d module This patch adds userptr feautre for G2D module. The userptr means user space address allocated by malloc(). And the purpose of this feature is to make G2D's dma able to access the user space region. To user this feature, user should flag G2D_BUF_USRPTR to offset variable of struct drm_exynos_g2d_cmd and fill struct drm_exynos_g2d_userptr with user space address and size for it and then should set a pointer to drm_exynos_g2d_userptr object to data variable of struct drm_exynos_g2d_cmd. The last bit of offset variable is used to check if the cmdlist's buffer type is userptr or not. If userptr, the g2d driver gets user space address and size and then gets pages through get_user_pages(). (another case is counted as gem handle) Below is sample codes: static void set_cmd(struct drm_exynos_g2d_cmd *cmd, unsigned long offset, unsigned long data) { cmd->offset = offset; cmd->data = data; } static int solid_fill_test(int x, int y, unsigned long userptr) { struct drm_exynos_g2d_cmd cmd_gem[5]; struct drm_exynos_g2d_userptr g2d_userptr; unsigned int gem_nr = 0; ... g2d_userptr.userptr = userptr; g2d_userptr.size = x * y * 4; set_cmd(&cmd_gem[gem_nr++], DST_BASE_ADDR_REG | G2D_BUF_USERPTR, (unsigned long)&g2d_userptr); ... } int main(int argc, char **argv) { unsigned long addr; ... addr = malloc(x * y * 4); ... solid_fill_test(x, y, addr); ... } And next, the pages are mapped with iommu table and the device address is set to cmdlist so that G2D's dma can access it. As you may know, the pages from get_user_pages() are pinned. In other words, they CAN NOT be migrated and also swapped out. So the dma access would be safe. But the use of userptr feature has performance overhead so this patch also has memory pool to the userptr feature. Please, assume that user sends cmdlist filled with userptr and size every time to g2d driver, and the get_user_pages funcion will be called every time. The memory pool has maximum 64MB size and the userptr that user had ever sent, is holded in the memory pool. This meaning is that if the userptr from user is same as one in the memory pool, device address to the userptr in the memory pool is set to cmdlist. And last, the pages from get_user_pages() will be freed once user calls free() and the dma access is completed. Actually, get_user_pages() takes 2 reference counts if the user process has never accessed user region allocated by malloc(). Then, if the user calls free(), the page reference count becomes 1 and becomes 0 with put_page() call. And the reverse holds as well. This means how the pages backed are used by dma and freed. This patch is based on "drm/exynos: add iommu support for g2d", https://patchwork.kernel.org/patch/1629481/ Signed-off-by: Inki Dae Signed-off-by: Kyungmin Park --- drivers/gpu/drm/exynos/exynos_drm_drv.h | 3 +- drivers/gpu/drm/exynos/exynos_drm_g2d.c | 342 +++++++++++++++++++++++++++++--- drivers/gpu/drm/exynos/exynos_drm_gem.c | 123 ++++++++++++ drivers/gpu/drm/exynos/exynos_drm_gem.h | 45 +++++ include/uapi/drm/exynos_drm.h | 13 +- 5 files changed, 499 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 8c9f4b05fc17..9c9c2dc75828 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -231,8 +231,7 @@ struct exynos_drm_g2d_private { struct device *dev; struct list_head inuse_cmdlist; struct list_head event_list; - struct list_head gem_list; - unsigned int gem_nr; + struct list_head userptr_list; }; struct drm_exynos_file_private { diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index bac2399857d1..a9002adc4694 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -97,11 +97,19 @@ #define MAX_BUF_ADDR_NR 6 +/* maximum buffer pool size of userptr is 64MB as default */ +#define MAX_POOL (64 * 1024 * 1024) + +enum { + BUF_TYPE_GEM = 1, + BUF_TYPE_USERPTR, +}; + /* cmdlist data structure */ struct g2d_cmdlist { - u32 head; - u32 data[G2D_CMDLIST_DATA_NUM]; - u32 last; /* last data offset */ + u32 head; + unsigned long data[G2D_CMDLIST_DATA_NUM]; + u32 last; /* last data offset */ }; struct drm_exynos_pending_g2d_event { @@ -109,11 +117,26 @@ struct drm_exynos_pending_g2d_event { struct drm_exynos_g2d_event event; }; +struct g2d_cmdlist_userptr { + struct list_head list; + dma_addr_t dma_addr; + unsigned long userptr; + unsigned long size; + struct page **pages; + unsigned int npages; + struct sg_table *sgt; + struct vm_area_struct *vma; + atomic_t refcount; + bool in_pool; + bool out_of_list; +}; + struct g2d_cmdlist_node { struct list_head list; struct g2d_cmdlist *cmdlist; unsigned int map_nr; - unsigned int handles[MAX_BUF_ADDR_NR]; + unsigned long handles[MAX_BUF_ADDR_NR]; + unsigned int obj_type[MAX_BUF_ADDR_NR]; dma_addr_t dma_addr; struct drm_exynos_pending_g2d_event *event; @@ -152,6 +175,9 @@ struct g2d_data { struct list_head runqueue; struct mutex runqueue_mutex; struct kmem_cache *runqueue_slab; + + unsigned long current_pool; + unsigned long max_pool; }; static int g2d_init_cmdlist(struct g2d_data *g2d) @@ -256,6 +282,229 @@ add_to_list: list_add_tail(&node->event->base.link, &g2d_priv->event_list); } +static void g2d_userptr_put_dma_addr(struct drm_device *drm_dev, + unsigned long obj, + bool force) +{ + struct g2d_cmdlist_userptr *g2d_userptr = + (struct g2d_cmdlist_userptr *)obj; + + if (!obj) + return; + + if (force) + goto out; + + atomic_dec(&g2d_userptr->refcount); + + if (atomic_read(&g2d_userptr->refcount) > 0) + return; + + if (g2d_userptr->in_pool) + return; + +out: + exynos_gem_unmap_sgt_from_dma(drm_dev, g2d_userptr->sgt, + DMA_BIDIRECTIONAL); + + exynos_gem_put_pages_to_userptr(g2d_userptr->pages, + g2d_userptr->npages, + g2d_userptr->vma); + + if (!g2d_userptr->out_of_list) + list_del_init(&g2d_userptr->list); + + sg_free_table(g2d_userptr->sgt); + kfree(g2d_userptr->sgt); + g2d_userptr->sgt = NULL; + + kfree(g2d_userptr->pages); + kfree(g2d_userptr); + g2d_userptr->pages = NULL; + g2d_userptr = NULL; +} + +dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, + unsigned long userptr, + unsigned long size, + struct drm_file *filp, + unsigned long *obj) +{ + struct drm_exynos_file_private *file_priv = filp->driver_priv; + struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; + struct g2d_cmdlist_userptr *g2d_userptr; + struct g2d_data *g2d; + struct page **pages; + struct sg_table *sgt; + struct vm_area_struct *vma; + unsigned long start, end; + unsigned int npages, offset; + int ret; + + if (!size) { + DRM_ERROR("invalid userptr size.\n"); + return ERR_PTR(-EINVAL); + } + + g2d = dev_get_drvdata(g2d_priv->dev); + + /* check if userptr already exists in userptr_list. */ + list_for_each_entry(g2d_userptr, &g2d_priv->userptr_list, list) { + if (g2d_userptr->userptr == userptr) { + /* + * also check size because there could be same address + * and different size. + */ + if (g2d_userptr->size == size) { + atomic_inc(&g2d_userptr->refcount); + *obj = (unsigned long)g2d_userptr; + + return &g2d_userptr->dma_addr; + } + + /* + * at this moment, maybe g2d dma is accessing this + * g2d_userptr memory region so just remove this + * g2d_userptr object from userptr_list not to be + * referred again and also except it the userptr + * pool to be released after the dma access completion. + */ + g2d_userptr->out_of_list = true; + g2d_userptr->in_pool = false; + list_del_init(&g2d_userptr->list); + + break; + } + } + + g2d_userptr = kzalloc(sizeof(*g2d_userptr), GFP_KERNEL); + if (!g2d_userptr) { + DRM_ERROR("failed to allocate g2d_userptr.\n"); + return ERR_PTR(-ENOMEM); + } + + atomic_set(&g2d_userptr->refcount, 1); + + start = userptr & PAGE_MASK; + offset = userptr & ~PAGE_MASK; + end = PAGE_ALIGN(userptr + size); + npages = (end - start) >> PAGE_SHIFT; + g2d_userptr->npages = npages; + + pages = kzalloc(npages * sizeof(struct page *), GFP_KERNEL); + if (!pages) { + DRM_ERROR("failed to allocate pages.\n"); + kfree(g2d_userptr); + return ERR_PTR(-ENOMEM); + } + + vma = find_vma(current->mm, userptr); + if (!vma) { + DRM_ERROR("failed to get vm region.\n"); + ret = -EFAULT; + goto err_free_pages; + } + + if (vma->vm_end < userptr + size) { + DRM_ERROR("vma is too small.\n"); + ret = -EFAULT; + goto err_free_pages; + } + + g2d_userptr->vma = exynos_gem_get_vma(vma); + if (!g2d_userptr->vma) { + DRM_ERROR("failed to copy vma.\n"); + ret = -ENOMEM; + goto err_free_pages; + } + + g2d_userptr->size = size; + + ret = exynos_gem_get_pages_from_userptr(start & PAGE_MASK, + npages, pages, vma); + if (ret < 0) { + DRM_ERROR("failed to get user pages from userptr.\n"); + goto err_put_vma; + } + + g2d_userptr->pages = pages; + + sgt = kzalloc(sizeof *sgt, GFP_KERNEL); + if (!sgt) { + DRM_ERROR("failed to allocate sg table.\n"); + ret = -ENOMEM; + goto err_free_userptr; + } + + ret = sg_alloc_table_from_pages(sgt, pages, npages, offset, + size, GFP_KERNEL); + if (ret < 0) { + DRM_ERROR("failed to get sgt from pages.\n"); + goto err_free_sgt; + } + + g2d_userptr->sgt = sgt; + + ret = exynos_gem_map_sgt_with_dma(drm_dev, g2d_userptr->sgt, + DMA_BIDIRECTIONAL); + if (ret < 0) { + DRM_ERROR("failed to map sgt with dma region.\n"); + goto err_free_sgt; + } + + g2d_userptr->dma_addr = sgt->sgl[0].dma_address; + g2d_userptr->userptr = userptr; + + list_add_tail(&g2d_userptr->list, &g2d_priv->userptr_list); + + if (g2d->current_pool + (npages << PAGE_SHIFT) < g2d->max_pool) { + g2d->current_pool += npages << PAGE_SHIFT; + g2d_userptr->in_pool = true; + } + + *obj = (unsigned long)g2d_userptr; + + return &g2d_userptr->dma_addr; + +err_free_sgt: + sg_free_table(sgt); + kfree(sgt); + sgt = NULL; + +err_free_userptr: + exynos_gem_put_pages_to_userptr(g2d_userptr->pages, + g2d_userptr->npages, + g2d_userptr->vma); + +err_put_vma: + exynos_gem_put_vma(g2d_userptr->vma); + +err_free_pages: + kfree(pages); + kfree(g2d_userptr); + pages = NULL; + g2d_userptr = NULL; + + return ERR_PTR(ret); +} + +static void g2d_userptr_free_all(struct drm_device *drm_dev, + struct g2d_data *g2d, + struct drm_file *filp) +{ + struct drm_exynos_file_private *file_priv = filp->driver_priv; + struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; + struct g2d_cmdlist_userptr *g2d_userptr, *n; + + list_for_each_entry_safe(g2d_userptr, n, &g2d_priv->userptr_list, list) + if (g2d_userptr->in_pool) + g2d_userptr_put_dma_addr(drm_dev, + (unsigned long)g2d_userptr, + true); + + g2d->current_pool = 0; +} + static int g2d_map_cmdlist_gem(struct g2d_data *g2d, struct g2d_cmdlist_node *node, struct drm_device *drm_dev, @@ -272,10 +521,31 @@ static int g2d_map_cmdlist_gem(struct g2d_data *g2d, offset = cmdlist->last - (i * 2 + 1); handle = cmdlist->data[offset]; - addr = exynos_drm_gem_get_dma_addr(drm_dev, handle, file); - if (IS_ERR(addr)) { - node->map_nr = i; - return -EFAULT; + if (node->obj_type[i] == BUF_TYPE_GEM) { + addr = exynos_drm_gem_get_dma_addr(drm_dev, handle, + file); + if (IS_ERR(addr)) { + node->map_nr = i; + return -EFAULT; + } + } else { + struct drm_exynos_g2d_userptr g2d_userptr; + + if (copy_from_user(&g2d_userptr, (void __user *)handle, + sizeof(struct drm_exynos_g2d_userptr))) { + node->map_nr = i; + return -EFAULT; + } + + addr = g2d_userptr_get_dma_addr(drm_dev, + g2d_userptr.userptr, + g2d_userptr.size, + file, + &handle); + if (IS_ERR(addr)) { + node->map_nr = i; + return -EFAULT; + } } cmdlist->data[offset] = *addr; @@ -293,9 +563,14 @@ static void g2d_unmap_cmdlist_gem(struct g2d_data *g2d, int i; for (i = 0; i < node->map_nr; i++) { - unsigned int handle = node->handles[i]; + unsigned long handle = node->handles[i]; - exynos_drm_gem_put_dma_addr(subdrv->drm_dev, handle, filp); + if (node->obj_type[i] == BUF_TYPE_GEM) + exynos_drm_gem_put_dma_addr(subdrv->drm_dev, handle, + filp); + else + g2d_userptr_put_dma_addr(subdrv->drm_dev, handle, + false); node->handles[i] = 0; } @@ -438,15 +713,28 @@ static irqreturn_t g2d_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static int g2d_check_reg_offset(struct device *dev, struct g2d_cmdlist *cmdlist, +static int g2d_check_reg_offset(struct device *dev, + struct g2d_cmdlist_node *node, int nr, bool for_addr) { + struct g2d_cmdlist *cmdlist = node->cmdlist; int reg_offset; int index; int i; for (i = 0; i < nr; i++) { index = cmdlist->last - 2 * (i + 1); + + if (for_addr) { + /* check userptr buffer type. */ + reg_offset = (cmdlist->data[index] & + ~0x7fffffff) >> 31; + if (reg_offset) { + node->obj_type[i] = BUF_TYPE_USERPTR; + cmdlist->data[index] &= ~G2D_BUF_USERPTR; + } + } + reg_offset = cmdlist->data[index] & ~0xfffff000; if (reg_offset < G2D_VALID_START || reg_offset > G2D_VALID_END) @@ -463,6 +751,9 @@ static int g2d_check_reg_offset(struct device *dev, struct g2d_cmdlist *cmdlist, case G2D_MSK_BASE_ADDR: if (!for_addr) goto err; + + if (node->obj_type[i] != BUF_TYPE_USERPTR) + node->obj_type[i] = BUF_TYPE_GEM; break; default: if (for_addr) @@ -474,7 +765,7 @@ static int g2d_check_reg_offset(struct device *dev, struct g2d_cmdlist *cmdlist, return 0; err: - dev_err(dev, "Bad register offset: 0x%x\n", cmdlist->data[index]); + dev_err(dev, "Bad register offset: 0x%lx\n", cmdlist->data[index]); return -EINVAL; } @@ -574,7 +865,7 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, } /* Check size of cmdlist: last 2 is about G2D_BITBLT_START */ - size = cmdlist->last + req->cmd_nr * 2 + req->cmd_gem_nr * 2 + 2; + size = cmdlist->last + req->cmd_nr * 2 + req->cmd_buf_nr * 2 + 2; if (size > G2D_CMDLIST_DATA_NUM) { dev_err(dev, "cmdlist size is too big\n"); ret = -EINVAL; @@ -591,25 +882,25 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, } cmdlist->last += req->cmd_nr * 2; - ret = g2d_check_reg_offset(dev, cmdlist, req->cmd_nr, false); + ret = g2d_check_reg_offset(dev, node, req->cmd_nr, false); if (ret < 0) goto err_free_event; - node->map_nr = req->cmd_gem_nr; - if (req->cmd_gem_nr) { - struct drm_exynos_g2d_cmd *cmd_gem; + node->map_nr = req->cmd_buf_nr; + if (req->cmd_buf_nr) { + struct drm_exynos_g2d_cmd *cmd_buf; - cmd_gem = (struct drm_exynos_g2d_cmd *)(uint32_t)req->cmd_gem; + cmd_buf = (struct drm_exynos_g2d_cmd *)(uint32_t)req->cmd_buf; if (copy_from_user(cmdlist->data + cmdlist->last, - (void __user *)cmd_gem, - sizeof(*cmd_gem) * req->cmd_gem_nr)) { + (void __user *)cmd_buf, + sizeof(*cmd_buf) * req->cmd_buf_nr)) { ret = -EFAULT; goto err_free_event; } - cmdlist->last += req->cmd_gem_nr * 2; + cmdlist->last += req->cmd_buf_nr * 2; - ret = g2d_check_reg_offset(dev, cmdlist, req->cmd_gem_nr, true); + ret = g2d_check_reg_offset(dev, node, req->cmd_buf_nr, true); if (ret < 0) goto err_free_event; @@ -759,7 +1050,7 @@ static int g2d_open(struct drm_device *drm_dev, struct device *dev, INIT_LIST_HEAD(&g2d_priv->inuse_cmdlist); INIT_LIST_HEAD(&g2d_priv->event_list); - INIT_LIST_HEAD(&g2d_priv->gem_list); + INIT_LIST_HEAD(&g2d_priv->userptr_list); return 0; } @@ -793,6 +1084,9 @@ static void g2d_close(struct drm_device *drm_dev, struct device *dev, } mutex_unlock(&g2d->cmdlist_mutex); + /* release all g2d_userptr in pool. */ + g2d_userptr_free_all(drm_dev, g2d, file); + kfree(file_priv->g2d_priv); } @@ -863,6 +1157,8 @@ static int __devinit g2d_probe(struct platform_device *pdev) goto err_put_clk; } + g2d->max_pool = MAX_POOL; + platform_set_drvdata(pdev, g2d); subdrv = &g2d->subdrv; diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 5fdfb8f51a41..2cc6b3ae4e07 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -448,6 +448,129 @@ int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data, return 0; } +struct vm_area_struct *exynos_gem_get_vma(struct vm_area_struct *vma) +{ + struct vm_area_struct *vma_copy; + + vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL); + if (!vma_copy) + return NULL; + + if (vma->vm_ops && vma->vm_ops->open) + vma->vm_ops->open(vma); + + if (vma->vm_file) + get_file(vma->vm_file); + + memcpy(vma_copy, vma, sizeof(*vma)); + + vma_copy->vm_mm = NULL; + vma_copy->vm_next = NULL; + vma_copy->vm_prev = NULL; + + return vma_copy; +} + +void exynos_gem_put_vma(struct vm_area_struct *vma) +{ + if (!vma) + return; + + if (vma->vm_ops && vma->vm_ops->close) + vma->vm_ops->close(vma); + + if (vma->vm_file) + fput(vma->vm_file); + + kfree(vma); +} + +int exynos_gem_get_pages_from_userptr(unsigned long start, + unsigned int npages, + struct page **pages, + struct vm_area_struct *vma) +{ + int get_npages; + + /* the memory region mmaped with VM_PFNMAP. */ + if (vma_is_io(vma)) { + unsigned int i; + + for (i = 0; i < npages; ++i, start += PAGE_SIZE) { + unsigned long pfn; + int ret = follow_pfn(vma, start, &pfn); + if (ret) + return ret; + + pages[i] = pfn_to_page(pfn); + } + + if (i != npages) { + DRM_ERROR("failed to get user_pages.\n"); + return -EINVAL; + } + + return 0; + } + + get_npages = get_user_pages(current, current->mm, start, + npages, 1, 1, pages, NULL); + get_npages = max(get_npages, 0); + if (get_npages != npages) { + DRM_ERROR("failed to get user_pages.\n"); + while (get_npages) + put_page(pages[--get_npages]); + return -EFAULT; + } + + return 0; +} + +void exynos_gem_put_pages_to_userptr(struct page **pages, + unsigned int npages, + struct vm_area_struct *vma) +{ + if (!vma_is_io(vma)) { + unsigned int i; + + for (i = 0; i < npages; i++) { + set_page_dirty_lock(pages[i]); + + /* + * undo the reference we took when populating + * the table. + */ + put_page(pages[i]); + } + } +} + +int exynos_gem_map_sgt_with_dma(struct drm_device *drm_dev, + struct sg_table *sgt, + enum dma_data_direction dir) +{ + int nents; + + mutex_lock(&drm_dev->struct_mutex); + + nents = dma_map_sg(drm_dev->dev, sgt->sgl, sgt->nents, dir); + if (!nents) { + DRM_ERROR("failed to map sgl with dma.\n"); + mutex_unlock(&drm_dev->struct_mutex); + return nents; + } + + mutex_unlock(&drm_dev->struct_mutex); + return 0; +} + +void exynos_gem_unmap_sgt_from_dma(struct drm_device *drm_dev, + struct sg_table *sgt, + enum dma_data_direction dir) +{ + dma_unmap_sg(drm_dev->dev, sgt->sgl, sgt->nents, dir); +} + int exynos_drm_gem_init_object(struct drm_gem_object *obj) { DRM_DEBUG_KMS("%s\n", __FILE__); diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index 83d21ef1d1e9..4248d7fb698f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h @@ -35,22 +35,29 @@ * exynos drm gem buffer structure. * * @kvaddr: kernel virtual address to allocated memory region. + * *userptr: user space address. * @dma_addr: bus address(accessed by dma) to allocated memory region. * - this address could be physical address without IOMMU and * device address with IOMMU. + * @write: whether pages will be written to by the caller. * @sgt: sg table to transfer page data. * @pages: contain all pages to allocated memory region. * @page_size: could be 4K, 64K or 1MB. * @size: size of allocated memory region. + * @pfnmap: indicate whether memory region from userptr is mmaped with + * VM_PFNMAP or not. */ struct exynos_drm_gem_buf { void __iomem *kvaddr; + unsigned long userptr; dma_addr_t dma_addr; struct dma_attrs dma_attrs; + unsigned int write; struct sg_table *sgt; struct page **pages; unsigned long page_size; unsigned long size; + bool pfnmap; }; /* @@ -66,6 +73,7 @@ struct exynos_drm_gem_buf { * or at framebuffer creation. * @size: size requested from user, in bytes and this size is aligned * in page unit. + * @vma: a pointer to vm_area. * @flags: indicate memory type to allocated buffer and cache attruibute. * * P.S. this object would be transfered to user as kms_bo.handle so @@ -75,6 +83,7 @@ struct exynos_drm_gem_obj { struct drm_gem_object base; struct exynos_drm_gem_buf *buffer; unsigned long size; + struct vm_area_struct *vma; unsigned int flags; }; @@ -129,6 +138,10 @@ int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +/* map user space allocated by malloc to pages. */ +int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + /* get buffer information to memory region allocated by gem. */ int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); @@ -164,4 +177,36 @@ int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); /* set vm_flags and we can change the vm attribute to other one at here. */ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); +static inline int vma_is_io(struct vm_area_struct *vma) +{ + return !!(vma->vm_flags & (VM_IO | VM_PFNMAP)); +} + +/* get a copy of a virtual memory region. */ +struct vm_area_struct *exynos_gem_get_vma(struct vm_area_struct *vma); + +/* release a userspace virtual memory area. */ +void exynos_gem_put_vma(struct vm_area_struct *vma); + +/* get pages from user space. */ +int exynos_gem_get_pages_from_userptr(unsigned long start, + unsigned int npages, + struct page **pages, + struct vm_area_struct *vma); + +/* drop the reference to pages. */ +void exynos_gem_put_pages_to_userptr(struct page **pages, + unsigned int npages, + struct vm_area_struct *vma); + +/* map sgt with dma region. */ +int exynos_gem_map_sgt_with_dma(struct drm_device *drm_dev, + struct sg_table *sgt, + enum dma_data_direction dir); + +/* unmap sgt from dma region. */ +void exynos_gem_unmap_sgt_from_dma(struct drm_device *drm_dev, + struct sg_table *sgt, + enum dma_data_direction dir); + #endif diff --git a/include/uapi/drm/exynos_drm.h b/include/uapi/drm/exynos_drm.h index c0494d586e23..49f010f2b27f 100644 --- a/include/uapi/drm/exynos_drm.h +++ b/include/uapi/drm/exynos_drm.h @@ -133,17 +133,26 @@ struct drm_exynos_g2d_cmd { __u32 data; }; +enum drm_exynos_g2d_buf_type { + G2D_BUF_USERPTR = 1 << 31, +}; + enum drm_exynos_g2d_event_type { G2D_EVENT_NOT, G2D_EVENT_NONSTOP, G2D_EVENT_STOP, /* not yet */ }; +struct drm_exynos_g2d_userptr { + unsigned long userptr; + unsigned long size; +}; + struct drm_exynos_g2d_set_cmdlist { __u64 cmd; - __u64 cmd_gem; + __u64 cmd_buf; __u32 cmd_nr; - __u32 cmd_gem_nr; + __u32 cmd_buf_nr; /* for g2d event */ __u64 event_type; -- cgit v1.2.3-59-g8ed1b From 57f570838341507682b7705f1d950608ef2182bd Mon Sep 17 00:00:00 2001 From: Marek Olšák Date: Sun, 2 Dec 2012 21:03:33 +0100 Subject: drm/radeon: add a CS flag END_OF_FRAME MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No version bump is required because setting the flag on older DRM has no effect. This only reserves the bit and doesn't use it. I assume we will use it for buffer eviction heuristics. Signed-off-by: Marek Olšák --- include/uapi/drm/radeon_drm.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/uapi/drm/radeon_drm.h b/include/uapi/drm/radeon_drm.h index 4766c0f6a838..0c8a62c543f7 100644 --- a/include/uapi/drm/radeon_drm.h +++ b/include/uapi/drm/radeon_drm.h @@ -913,6 +913,7 @@ struct drm_radeon_gem_va { /* The first dword of RADEON_CHUNK_ID_FLAGS is a uint32 of these flags: */ #define RADEON_CS_KEEP_TILING_FLAGS 0x01 #define RADEON_CS_USE_VM 0x02 +#define RADEON_CS_END_OF_FRAME 0x04 /* a hint from userspace which CS is the last one */ /* The second dword of RADEON_CHUNK_ID_FLAGS is a uint32 that sets the ring type */ #define RADEON_CS_RING_GFX 0 #define RADEON_CS_RING_COMPUTE 1 -- cgit v1.2.3-59-g8ed1b From 2e1a7674f65eb2c9118ab59d9c8aa9c731da6b85 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 4 Dec 2012 12:55:37 -0500 Subject: drm/radeon: add new INFO ioctl requests Add requests to get the number of shader engines (SE) and the number of SH per SE. These are needed for geometry and tesselation shaders in the 3D driver as well as setting up PA_SC_RASTER_CONFIG on SI asics. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon_kms.c | 16 ++++++++++++++++ include/uapi/drm/radeon_drm.h | 4 ++++ 2 files changed, 20 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index dc781c49b96b..9c312f9afb68 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -361,6 +361,22 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) return -EINVAL; } break; + case RADEON_INFO_MAX_SE: + if (rdev->family >= CHIP_TAHITI) + value = rdev->config.si.max_shader_engines; + else if (rdev->family >= CHIP_CAYMAN) + value = rdev->config.cayman.max_shader_engines; + else if (rdev->family >= CHIP_CEDAR) + value = rdev->config.evergreen.num_ses; + else + value = 1; + break; + case RADEON_INFO_MAX_SH_PER_SE: + if (rdev->family >= CHIP_TAHITI) + value = rdev->config.si.max_sh_per_se; + else + return -EINVAL; + break; default: DRM_DEBUG_KMS("Invalid request %d\n", info->request); return -EINVAL; diff --git a/include/uapi/drm/radeon_drm.h b/include/uapi/drm/radeon_drm.h index 0c8a62c543f7..5645a878faec 100644 --- a/include/uapi/drm/radeon_drm.h +++ b/include/uapi/drm/radeon_drm.h @@ -967,6 +967,10 @@ struct drm_radeon_cs { #define RADEON_INFO_MAX_PIPES 0x10 /* timestamp for GL_ARB_timer_query (OpenGL), returns the current GPU clock */ #define RADEON_INFO_TIMESTAMP 0x11 +/* max shader engines (SE) - needed for geometry shaders, etc. */ +#define RADEON_INFO_MAX_SE 0x12 +/* max SH per SE */ +#define RADEON_INFO_MAX_SH_PER_SE 0x13 struct drm_radeon_info { uint32_t request; -- cgit v1.2.3-59-g8ed1b From 97a875cbdf89a4638eea57c2b456c7cc4e3e8b21 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Wed, 28 Nov 2012 11:25:44 +0000 Subject: drm/ttm: remove no_wait_reserve, v3 All items on the lru list are always reservable, so this is a stupid thing to keep. Not only that, it is used in a way which would guarantee deadlocks if it were ever to be set to block on reserve. This is a lot of churn, but mostly because of the removal of the argument which can be nested arbitrarily deeply in many places. No change of code in this patch except removal of the no_wait_reserve argument, the previous patch removed the use of no_wait_reserve. v2: - Warn if -EBUSY is returned on reservation, all objects on the list should be reservable. Adjusted patch slightly due to conflicts. v3: - Focus on no_wait_reserve removal only. Signed-off-by: Maarten Lankhorst Reviewed-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/ast/ast_ttm.c | 10 +++--- drivers/gpu/drm/cirrus/cirrus_ttm.c | 10 +++--- drivers/gpu/drm/mgag200/mgag200_ttm.c | 10 +++--- drivers/gpu/drm/nouveau/nouveau_bo.c | 55 +++++++++++++++----------------- drivers/gpu/drm/nouveau/nouveau_bo.h | 2 +- drivers/gpu/drm/nouveau/nouveau_gem.c | 2 +- drivers/gpu/drm/radeon/radeon_object.c | 8 ++--- drivers/gpu/drm/radeon/radeon_ttm.c | 31 +++++++++--------- drivers/gpu/drm/ttm/ttm_bo.c | 46 +++++++++++++------------- drivers/gpu/drm/ttm/ttm_bo_util.c | 6 ++-- drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c | 13 ++++---- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 4 +-- drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 2 +- include/drm/ttm/ttm_bo_api.h | 3 +- include/drm/ttm/ttm_bo_driver.h | 19 ++++------- 15 files changed, 107 insertions(+), 114 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/ast/ast_ttm.c b/drivers/gpu/drm/ast/ast_ttm.c index 0a54f65a8ebb..3602731a6112 100644 --- a/drivers/gpu/drm/ast/ast_ttm.c +++ b/drivers/gpu/drm/ast/ast_ttm.c @@ -186,11 +186,11 @@ static void ast_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg * static int ast_bo_move(struct ttm_buffer_object *bo, bool evict, bool interruptible, - bool no_wait_reserve, bool no_wait_gpu, + bool no_wait_gpu, struct ttm_mem_reg *new_mem) { int r; - r = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, new_mem); + r = ttm_bo_move_memcpy(bo, evict, no_wait_gpu, new_mem); return r; } @@ -383,7 +383,7 @@ int ast_bo_pin(struct ast_bo *bo, u32 pl_flag, u64 *gpu_addr) ast_ttm_placement(bo, pl_flag); for (i = 0; i < bo->placement.num_placement; i++) bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; - ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false); + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) return ret; @@ -406,7 +406,7 @@ int ast_bo_unpin(struct ast_bo *bo) for (i = 0; i < bo->placement.num_placement ; i++) bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT; - ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false); + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) return ret; @@ -431,7 +431,7 @@ int ast_bo_push_sysram(struct ast_bo *bo) for (i = 0; i < bo->placement.num_placement ; i++) bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; - ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false); + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) { DRM_ERROR("pushing to VRAM failed\n"); return ret; diff --git a/drivers/gpu/drm/cirrus/cirrus_ttm.c b/drivers/gpu/drm/cirrus/cirrus_ttm.c index 90d770143cc2..1413a26e4905 100644 --- a/drivers/gpu/drm/cirrus/cirrus_ttm.c +++ b/drivers/gpu/drm/cirrus/cirrus_ttm.c @@ -186,11 +186,11 @@ static void cirrus_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_re static int cirrus_bo_move(struct ttm_buffer_object *bo, bool evict, bool interruptible, - bool no_wait_reserve, bool no_wait_gpu, + bool no_wait_gpu, struct ttm_mem_reg *new_mem) { int r; - r = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, new_mem); + r = ttm_bo_move_memcpy(bo, evict, no_wait_gpu, new_mem); return r; } @@ -388,7 +388,7 @@ int cirrus_bo_pin(struct cirrus_bo *bo, u32 pl_flag, u64 *gpu_addr) cirrus_ttm_placement(bo, pl_flag); for (i = 0; i < bo->placement.num_placement; i++) bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; - ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false); + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) return ret; @@ -411,7 +411,7 @@ int cirrus_bo_unpin(struct cirrus_bo *bo) for (i = 0; i < bo->placement.num_placement ; i++) bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT; - ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false); + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) return ret; @@ -436,7 +436,7 @@ int cirrus_bo_push_sysram(struct cirrus_bo *bo) for (i = 0; i < bo->placement.num_placement ; i++) bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; - ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false); + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) { DRM_ERROR("pushing to VRAM failed\n"); return ret; diff --git a/drivers/gpu/drm/mgag200/mgag200_ttm.c b/drivers/gpu/drm/mgag200/mgag200_ttm.c index 49d60a620122..8fc9d9201945 100644 --- a/drivers/gpu/drm/mgag200/mgag200_ttm.c +++ b/drivers/gpu/drm/mgag200/mgag200_ttm.c @@ -186,11 +186,11 @@ static void mgag200_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_r static int mgag200_bo_move(struct ttm_buffer_object *bo, bool evict, bool interruptible, - bool no_wait_reserve, bool no_wait_gpu, + bool no_wait_gpu, struct ttm_mem_reg *new_mem) { int r; - r = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, new_mem); + r = ttm_bo_move_memcpy(bo, evict, no_wait_gpu, new_mem); return r; } @@ -382,7 +382,7 @@ int mgag200_bo_pin(struct mgag200_bo *bo, u32 pl_flag, u64 *gpu_addr) mgag200_ttm_placement(bo, pl_flag); for (i = 0; i < bo->placement.num_placement; i++) bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; - ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false); + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) return ret; @@ -405,7 +405,7 @@ int mgag200_bo_unpin(struct mgag200_bo *bo) for (i = 0; i < bo->placement.num_placement ; i++) bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT; - ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false); + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) return ret; @@ -430,7 +430,7 @@ int mgag200_bo_push_sysram(struct mgag200_bo *bo) for (i = 0; i < bo->placement.num_placement ; i++) bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; - ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false); + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) { DRM_ERROR("pushing to VRAM failed\n"); return ret; diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 4c950b4cf416..5614c89148cb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -315,7 +315,7 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype) nouveau_bo_placement_set(nvbo, memtype, 0); - ret = nouveau_bo_validate(nvbo, false, false, false); + ret = nouveau_bo_validate(nvbo, false, false); if (ret == 0) { switch (bo->mem.mem_type) { case TTM_PL_VRAM: @@ -351,7 +351,7 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo) nouveau_bo_placement_set(nvbo, bo->mem.placement, 0); - ret = nouveau_bo_validate(nvbo, false, false, false); + ret = nouveau_bo_validate(nvbo, false, false); if (ret == 0) { switch (bo->mem.mem_type) { case TTM_PL_VRAM: @@ -392,12 +392,12 @@ nouveau_bo_unmap(struct nouveau_bo *nvbo) int nouveau_bo_validate(struct nouveau_bo *nvbo, bool interruptible, - bool no_wait_reserve, bool no_wait_gpu) + bool no_wait_gpu) { int ret; - ret = ttm_bo_validate(&nvbo->bo, &nvbo->placement, interruptible, - no_wait_reserve, no_wait_gpu); + ret = ttm_bo_validate(&nvbo->bo, &nvbo->placement, + interruptible, no_wait_gpu); if (ret) return ret; @@ -556,8 +556,7 @@ nouveau_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl) static int nouveau_bo_move_accel_cleanup(struct nouveau_channel *chan, struct nouveau_bo *nvbo, bool evict, - bool no_wait_reserve, bool no_wait_gpu, - struct ttm_mem_reg *new_mem) + bool no_wait_gpu, struct ttm_mem_reg *new_mem) { struct nouveau_fence *fence = NULL; int ret; @@ -567,7 +566,7 @@ nouveau_bo_move_accel_cleanup(struct nouveau_channel *chan, return ret; ret = ttm_bo_move_accel_cleanup(&nvbo->bo, fence, evict, - no_wait_reserve, no_wait_gpu, new_mem); + no_wait_gpu, new_mem); nouveau_fence_unref(&fence); return ret; } @@ -965,8 +964,7 @@ nouveau_vma_getmap(struct nouveau_channel *chan, struct nouveau_bo *nvbo, static int nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr, - bool no_wait_reserve, bool no_wait_gpu, - struct ttm_mem_reg *new_mem) + bool no_wait_gpu, struct ttm_mem_reg *new_mem) { struct nouveau_drm *drm = nouveau_bdev(bo->bdev); struct nouveau_channel *chan = chan = drm->channel; @@ -995,7 +993,6 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr, ret = drm->ttm.move(chan, bo, &bo->mem, new_mem); if (ret == 0) { ret = nouveau_bo_move_accel_cleanup(chan, nvbo, evict, - no_wait_reserve, no_wait_gpu, new_mem); } @@ -1064,8 +1061,7 @@ nouveau_bo_move_init(struct nouveau_drm *drm) static int nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr, - bool no_wait_reserve, bool no_wait_gpu, - struct ttm_mem_reg *new_mem) + bool no_wait_gpu, struct ttm_mem_reg *new_mem) { u32 placement_memtype = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING; struct ttm_placement placement; @@ -1078,7 +1074,7 @@ nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr, tmp_mem = *new_mem; tmp_mem.mm_node = NULL; - ret = ttm_bo_mem_space(bo, &placement, &tmp_mem, intr, no_wait_reserve, no_wait_gpu); + ret = ttm_bo_mem_space(bo, &placement, &tmp_mem, intr, no_wait_gpu); if (ret) return ret; @@ -1086,11 +1082,11 @@ nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr, if (ret) goto out; - ret = nouveau_bo_move_m2mf(bo, true, intr, no_wait_reserve, no_wait_gpu, &tmp_mem); + ret = nouveau_bo_move_m2mf(bo, true, intr, no_wait_gpu, &tmp_mem); if (ret) goto out; - ret = ttm_bo_move_ttm(bo, true, no_wait_reserve, no_wait_gpu, new_mem); + ret = ttm_bo_move_ttm(bo, true, no_wait_gpu, new_mem); out: ttm_bo_mem_put(bo, &tmp_mem); return ret; @@ -1098,8 +1094,7 @@ out: static int nouveau_bo_move_flips(struct ttm_buffer_object *bo, bool evict, bool intr, - bool no_wait_reserve, bool no_wait_gpu, - struct ttm_mem_reg *new_mem) + bool no_wait_gpu, struct ttm_mem_reg *new_mem) { u32 placement_memtype = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING; struct ttm_placement placement; @@ -1112,15 +1107,15 @@ nouveau_bo_move_flips(struct ttm_buffer_object *bo, bool evict, bool intr, tmp_mem = *new_mem; tmp_mem.mm_node = NULL; - ret = ttm_bo_mem_space(bo, &placement, &tmp_mem, intr, no_wait_reserve, no_wait_gpu); + ret = ttm_bo_mem_space(bo, &placement, &tmp_mem, intr, no_wait_gpu); if (ret) return ret; - ret = ttm_bo_move_ttm(bo, true, no_wait_reserve, no_wait_gpu, &tmp_mem); + ret = ttm_bo_move_ttm(bo, true, no_wait_gpu, &tmp_mem); if (ret) goto out; - ret = nouveau_bo_move_m2mf(bo, true, intr, no_wait_reserve, no_wait_gpu, new_mem); + ret = nouveau_bo_move_m2mf(bo, true, intr, no_wait_gpu, new_mem); if (ret) goto out; @@ -1195,8 +1190,7 @@ nouveau_bo_vm_cleanup(struct ttm_buffer_object *bo, static int nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr, - bool no_wait_reserve, bool no_wait_gpu, - struct ttm_mem_reg *new_mem) + bool no_wait_gpu, struct ttm_mem_reg *new_mem) { struct nouveau_drm *drm = nouveau_bdev(bo->bdev); struct nouveau_bo *nvbo = nouveau_bo(bo); @@ -1220,23 +1214,26 @@ nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr, /* CPU copy if we have no accelerated method available */ if (!drm->ttm.move) { - ret = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, new_mem); + ret = ttm_bo_move_memcpy(bo, evict, no_wait_gpu, new_mem); goto out; } /* Hardware assisted copy. */ if (new_mem->mem_type == TTM_PL_SYSTEM) - ret = nouveau_bo_move_flipd(bo, evict, intr, no_wait_reserve, no_wait_gpu, new_mem); + ret = nouveau_bo_move_flipd(bo, evict, intr, + no_wait_gpu, new_mem); else if (old_mem->mem_type == TTM_PL_SYSTEM) - ret = nouveau_bo_move_flips(bo, evict, intr, no_wait_reserve, no_wait_gpu, new_mem); + ret = nouveau_bo_move_flips(bo, evict, intr, + no_wait_gpu, new_mem); else - ret = nouveau_bo_move_m2mf(bo, evict, intr, no_wait_reserve, no_wait_gpu, new_mem); + ret = nouveau_bo_move_m2mf(bo, evict, intr, + no_wait_gpu, new_mem); if (!ret) goto out; /* Fallback to software copy. */ - ret = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, new_mem); + ret = ttm_bo_move_memcpy(bo, evict, no_wait_gpu, new_mem); out: if (nv_device(drm->device)->card_type < NV_50) { @@ -1343,7 +1340,7 @@ nouveau_ttm_fault_reserve_notify(struct ttm_buffer_object *bo) nvbo->placement.fpfn = 0; nvbo->placement.lpfn = mappable; nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_VRAM, 0); - return nouveau_bo_validate(nvbo, false, true, false); + return nouveau_bo_validate(nvbo, false, false); } static int diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.h b/drivers/gpu/drm/nouveau/nouveau_bo.h index dec51b1098fe..25ca37989d2c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.h +++ b/drivers/gpu/drm/nouveau/nouveau_bo.h @@ -76,7 +76,7 @@ u32 nouveau_bo_rd32(struct nouveau_bo *, unsigned index); void nouveau_bo_wr32(struct nouveau_bo *, unsigned index, u32 val); void nouveau_bo_fence(struct nouveau_bo *, struct nouveau_fence *); int nouveau_bo_validate(struct nouveau_bo *, bool interruptible, - bool no_wait_reserve, bool no_wait_gpu); + bool no_wait_gpu); struct nouveau_vma * nouveau_bo_vma_find(struct nouveau_bo *, struct nouveau_vm *); diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 5e2f52158f19..8bf695c52f95 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -433,7 +433,7 @@ validate_list(struct nouveau_channel *chan, struct list_head *list, return ret; } - ret = nouveau_bo_validate(nvbo, true, false, false); + ret = nouveau_bo_validate(nvbo, true, false); if (unlikely(ret)) { if (ret != -ERESTARTSYS) NV_ERROR(drm, "fail ttm_validate\n"); diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index e6ee65cdfb5c..bfb332e616dc 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -250,7 +250,7 @@ int radeon_bo_pin_restricted(struct radeon_bo *bo, u32 domain, u64 max_offset, } for (i = 0; i < bo->placement.num_placement; i++) bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; - r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false, false); + r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false); if (likely(r == 0)) { bo->pin_count = 1; if (gpu_addr != NULL) @@ -279,7 +279,7 @@ int radeon_bo_unpin(struct radeon_bo *bo) return 0; for (i = 0; i < bo->placement.num_placement; i++) bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT; - r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false, false); + r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false); if (unlikely(r != 0)) dev_err(bo->rdev->dev, "%p validate failed for unpin\n", bo); return r; @@ -365,7 +365,7 @@ int radeon_bo_list_validate(struct list_head *head) retry: radeon_ttm_placement_from_domain(bo, domain); r = ttm_bo_validate(&bo->tbo, &bo->placement, - true, false, false); + true, false); if (unlikely(r)) { if (r != -ERESTARTSYS && domain == RADEON_GEM_DOMAIN_VRAM) { domain |= RADEON_GEM_DOMAIN_GTT; @@ -585,7 +585,7 @@ int radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo) /* hurrah the memory is not visible ! */ radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_VRAM); rbo->placement.lpfn = rdev->mc.visible_vram_size >> PAGE_SHIFT; - r = ttm_bo_validate(bo, &rbo->placement, false, true, false); + r = ttm_bo_validate(bo, &rbo->placement, false, false); if (unlikely(r != 0)) return r; offset = bo->mem.start << PAGE_SHIFT; diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 563c8edcb03b..1d8ff2f850ba 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -216,7 +216,7 @@ static void radeon_move_null(struct ttm_buffer_object *bo, } static int radeon_move_blit(struct ttm_buffer_object *bo, - bool evict, int no_wait_reserve, bool no_wait_gpu, + bool evict, bool no_wait_gpu, struct ttm_mem_reg *new_mem, struct ttm_mem_reg *old_mem) { @@ -266,14 +266,14 @@ static int radeon_move_blit(struct ttm_buffer_object *bo, &fence); /* FIXME: handle copy error */ r = ttm_bo_move_accel_cleanup(bo, (void *)fence, - evict, no_wait_reserve, no_wait_gpu, new_mem); + evict, no_wait_gpu, new_mem); radeon_fence_unref(&fence); return r; } static int radeon_move_vram_ram(struct ttm_buffer_object *bo, bool evict, bool interruptible, - bool no_wait_reserve, bool no_wait_gpu, + bool no_wait_gpu, struct ttm_mem_reg *new_mem) { struct radeon_device *rdev; @@ -294,7 +294,7 @@ static int radeon_move_vram_ram(struct ttm_buffer_object *bo, placement.busy_placement = &placements; placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT; r = ttm_bo_mem_space(bo, &placement, &tmp_mem, - interruptible, no_wait_reserve, no_wait_gpu); + interruptible, no_wait_gpu); if (unlikely(r)) { return r; } @@ -308,11 +308,11 @@ static int radeon_move_vram_ram(struct ttm_buffer_object *bo, if (unlikely(r)) { goto out_cleanup; } - r = radeon_move_blit(bo, true, no_wait_reserve, no_wait_gpu, &tmp_mem, old_mem); + r = radeon_move_blit(bo, true, no_wait_gpu, &tmp_mem, old_mem); if (unlikely(r)) { goto out_cleanup; } - r = ttm_bo_move_ttm(bo, true, no_wait_reserve, no_wait_gpu, new_mem); + r = ttm_bo_move_ttm(bo, true, no_wait_gpu, new_mem); out_cleanup: ttm_bo_mem_put(bo, &tmp_mem); return r; @@ -320,7 +320,7 @@ out_cleanup: static int radeon_move_ram_vram(struct ttm_buffer_object *bo, bool evict, bool interruptible, - bool no_wait_reserve, bool no_wait_gpu, + bool no_wait_gpu, struct ttm_mem_reg *new_mem) { struct radeon_device *rdev; @@ -340,15 +340,16 @@ static int radeon_move_ram_vram(struct ttm_buffer_object *bo, placement.num_busy_placement = 1; placement.busy_placement = &placements; placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT; - r = ttm_bo_mem_space(bo, &placement, &tmp_mem, interruptible, no_wait_reserve, no_wait_gpu); + r = ttm_bo_mem_space(bo, &placement, &tmp_mem, + interruptible, no_wait_gpu); if (unlikely(r)) { return r; } - r = ttm_bo_move_ttm(bo, true, no_wait_reserve, no_wait_gpu, &tmp_mem); + r = ttm_bo_move_ttm(bo, true, no_wait_gpu, &tmp_mem); if (unlikely(r)) { goto out_cleanup; } - r = radeon_move_blit(bo, true, no_wait_reserve, no_wait_gpu, new_mem, old_mem); + r = radeon_move_blit(bo, true, no_wait_gpu, new_mem, old_mem); if (unlikely(r)) { goto out_cleanup; } @@ -359,7 +360,7 @@ out_cleanup: static int radeon_bo_move(struct ttm_buffer_object *bo, bool evict, bool interruptible, - bool no_wait_reserve, bool no_wait_gpu, + bool no_wait_gpu, struct ttm_mem_reg *new_mem) { struct radeon_device *rdev; @@ -388,18 +389,18 @@ static int radeon_bo_move(struct ttm_buffer_object *bo, if (old_mem->mem_type == TTM_PL_VRAM && new_mem->mem_type == TTM_PL_SYSTEM) { r = radeon_move_vram_ram(bo, evict, interruptible, - no_wait_reserve, no_wait_gpu, new_mem); + no_wait_gpu, new_mem); } else if (old_mem->mem_type == TTM_PL_SYSTEM && new_mem->mem_type == TTM_PL_VRAM) { r = radeon_move_ram_vram(bo, evict, interruptible, - no_wait_reserve, no_wait_gpu, new_mem); + no_wait_gpu, new_mem); } else { - r = radeon_move_blit(bo, evict, no_wait_reserve, no_wait_gpu, new_mem, old_mem); + r = radeon_move_blit(bo, evict, no_wait_gpu, new_mem, old_mem); } if (r) { memcpy: - r = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, new_mem); + r = ttm_bo_move_memcpy(bo, evict, no_wait_gpu, new_mem); } return r; } diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 6059771d506e..a9151337d5b9 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -366,7 +366,7 @@ static int ttm_bo_add_ttm(struct ttm_buffer_object *bo, bool zero_alloc) static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, struct ttm_mem_reg *mem, bool evict, bool interruptible, - bool no_wait_reserve, bool no_wait_gpu) + bool no_wait_gpu) { struct ttm_bo_device *bdev = bo->bdev; bool old_is_pci = ttm_mem_reg_is_pci(bdev, &bo->mem); @@ -420,12 +420,12 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, if (!(old_man->flags & TTM_MEMTYPE_FLAG_FIXED) && !(new_man->flags & TTM_MEMTYPE_FLAG_FIXED)) - ret = ttm_bo_move_ttm(bo, evict, no_wait_reserve, no_wait_gpu, mem); + ret = ttm_bo_move_ttm(bo, evict, no_wait_gpu, mem); else if (bdev->driver->move) ret = bdev->driver->move(bo, evict, interruptible, - no_wait_reserve, no_wait_gpu, mem); + no_wait_gpu, mem); else - ret = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, mem); + ret = ttm_bo_move_memcpy(bo, evict, no_wait_gpu, mem); if (ret) { if (bdev->driver->move_notify) { @@ -749,7 +749,7 @@ void ttm_bo_unlock_delayed_workqueue(struct ttm_bo_device *bdev, int resched) EXPORT_SYMBOL(ttm_bo_unlock_delayed_workqueue); static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible, - bool no_wait_reserve, bool no_wait_gpu) + bool no_wait_gpu) { struct ttm_bo_device *bdev = bo->bdev; struct ttm_mem_reg evict_mem; @@ -780,7 +780,7 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible, placement.num_busy_placement = 0; bdev->driver->evict_flags(bo, &placement); ret = ttm_bo_mem_space(bo, &placement, &evict_mem, interruptible, - no_wait_reserve, no_wait_gpu); + no_wait_gpu); if (ret) { if (ret != -ERESTARTSYS) { pr_err("Failed to find memory space for buffer 0x%p eviction\n", @@ -791,7 +791,7 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible, } ret = ttm_bo_handle_move_mem(bo, &evict_mem, true, interruptible, - no_wait_reserve, no_wait_gpu); + no_wait_gpu); if (ret) { if (ret != -ERESTARTSYS) pr_err("Buffer eviction failed\n"); @@ -805,7 +805,7 @@ out: static int ttm_mem_evict_first(struct ttm_bo_device *bdev, uint32_t mem_type, - bool interruptible, bool no_wait_reserve, + bool interruptible, bool no_wait_gpu) { struct ttm_bo_global *glob = bdev->glob; @@ -841,7 +841,7 @@ static int ttm_mem_evict_first(struct ttm_bo_device *bdev, ttm_bo_list_ref_sub(bo, put_count, true); - ret = ttm_bo_evict(bo, interruptible, no_wait_reserve, no_wait_gpu); + ret = ttm_bo_evict(bo, interruptible, no_wait_gpu); ttm_bo_unreserve(bo); kref_put(&bo->list_kref, ttm_bo_release_list); @@ -866,7 +866,6 @@ static int ttm_bo_mem_force_space(struct ttm_buffer_object *bo, struct ttm_placement *placement, struct ttm_mem_reg *mem, bool interruptible, - bool no_wait_reserve, bool no_wait_gpu) { struct ttm_bo_device *bdev = bo->bdev; @@ -879,8 +878,8 @@ static int ttm_bo_mem_force_space(struct ttm_buffer_object *bo, return ret; if (mem->mm_node) break; - ret = ttm_mem_evict_first(bdev, mem_type, interruptible, - no_wait_reserve, no_wait_gpu); + ret = ttm_mem_evict_first(bdev, mem_type, + interruptible, no_wait_gpu); if (unlikely(ret != 0)) return ret; } while (1); @@ -945,7 +944,7 @@ static bool ttm_bo_mt_compatible(struct ttm_mem_type_manager *man, int ttm_bo_mem_space(struct ttm_buffer_object *bo, struct ttm_placement *placement, struct ttm_mem_reg *mem, - bool interruptible, bool no_wait_reserve, + bool interruptible, bool no_wait_gpu) { struct ttm_bo_device *bdev = bo->bdev; @@ -1036,7 +1035,7 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, } ret = ttm_bo_mem_force_space(bo, mem_type, placement, mem, - interruptible, no_wait_reserve, no_wait_gpu); + interruptible, no_wait_gpu); if (ret == 0 && mem->mm_node) { mem->placement = cur_flags; return 0; @@ -1051,7 +1050,7 @@ EXPORT_SYMBOL(ttm_bo_mem_space); int ttm_bo_move_buffer(struct ttm_buffer_object *bo, struct ttm_placement *placement, - bool interruptible, bool no_wait_reserve, + bool interruptible, bool no_wait_gpu) { int ret = 0; @@ -1078,10 +1077,12 @@ int ttm_bo_move_buffer(struct ttm_buffer_object *bo, /* * Determine where to move the buffer. */ - ret = ttm_bo_mem_space(bo, placement, &mem, interruptible, no_wait_reserve, no_wait_gpu); + ret = ttm_bo_mem_space(bo, placement, &mem, + interruptible, no_wait_gpu); if (ret) goto out_unlock; - ret = ttm_bo_handle_move_mem(bo, &mem, false, interruptible, no_wait_reserve, no_wait_gpu); + ret = ttm_bo_handle_move_mem(bo, &mem, false, + interruptible, no_wait_gpu); out_unlock: if (ret && mem.mm_node) ttm_bo_mem_put(bo, &mem); @@ -1110,7 +1111,7 @@ static int ttm_bo_mem_compat(struct ttm_placement *placement, int ttm_bo_validate(struct ttm_buffer_object *bo, struct ttm_placement *placement, - bool interruptible, bool no_wait_reserve, + bool interruptible, bool no_wait_gpu) { int ret; @@ -1126,7 +1127,8 @@ int ttm_bo_validate(struct ttm_buffer_object *bo, */ ret = ttm_bo_mem_compat(placement, &bo->mem); if (ret < 0) { - ret = ttm_bo_move_buffer(bo, placement, interruptible, no_wait_reserve, no_wait_gpu); + ret = ttm_bo_move_buffer(bo, placement, interruptible, + no_wait_gpu); if (ret) return ret; } else { @@ -1239,7 +1241,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev, goto out_err; } - ret = ttm_bo_validate(bo, placement, interruptible, false, false); + ret = ttm_bo_validate(bo, placement, interruptible, false); if (ret) goto out_err; @@ -1325,7 +1327,7 @@ static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev, spin_lock(&glob->lru_lock); while (!list_empty(&man->lru)) { spin_unlock(&glob->lru_lock); - ret = ttm_mem_evict_first(bdev, mem_type, false, false, false); + ret = ttm_mem_evict_first(bdev, mem_type, false, false); if (ret) { if (allow_errors) { return ret; @@ -1837,7 +1839,7 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink) evict_mem.mem_type = TTM_PL_SYSTEM; ret = ttm_bo_handle_move_mem(bo, &evict_mem, true, - false, false, false); + false, false); if (unlikely(ret != 0)) goto out; } diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index b9c4e515b1d8..9e9c5d2a5c74 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -43,7 +43,7 @@ void ttm_bo_free_old_node(struct ttm_buffer_object *bo) } int ttm_bo_move_ttm(struct ttm_buffer_object *bo, - bool evict, bool no_wait_reserve, + bool evict, bool no_wait_gpu, struct ttm_mem_reg *new_mem) { struct ttm_tt *ttm = bo->ttm; @@ -314,7 +314,7 @@ static int ttm_copy_ttm_io_page(struct ttm_tt *ttm, void *dst, } int ttm_bo_move_memcpy(struct ttm_buffer_object *bo, - bool evict, bool no_wait_reserve, bool no_wait_gpu, + bool evict, bool no_wait_gpu, struct ttm_mem_reg *new_mem) { struct ttm_bo_device *bdev = bo->bdev; @@ -611,7 +611,7 @@ EXPORT_SYMBOL(ttm_bo_kunmap); int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo, void *sync_obj, - bool evict, bool no_wait_reserve, + bool evict, bool no_wait_gpu, struct ttm_mem_reg *new_mem) { diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c index e88b0eb1a179..5fae06ad7e25 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c @@ -66,7 +66,7 @@ int vmw_dmabuf_to_placement(struct vmw_private *dev_priv, if (unlikely(ret != 0)) goto err; - ret = ttm_bo_validate(bo, placement, interruptible, false, false); + ret = ttm_bo_validate(bo, placement, interruptible, false); ttm_bo_unreserve(bo); @@ -123,7 +123,7 @@ int vmw_dmabuf_to_vram_or_gmr(struct vmw_private *dev_priv, else placement = &vmw_vram_gmr_placement; - ret = ttm_bo_validate(bo, placement, interruptible, false, false); + ret = ttm_bo_validate(bo, placement, interruptible, false); if (likely(ret == 0) || ret == -ERESTARTSYS) goto err_unreserve; @@ -138,7 +138,7 @@ int vmw_dmabuf_to_vram_or_gmr(struct vmw_private *dev_priv, else placement = &vmw_vram_placement; - ret = ttm_bo_validate(bo, placement, interruptible, false, false); + ret = ttm_bo_validate(bo, placement, interruptible, false); err_unreserve: ttm_bo_unreserve(bo); @@ -223,10 +223,9 @@ int vmw_dmabuf_to_start_of_vram(struct vmw_private *dev_priv, if (bo->mem.mem_type == TTM_PL_VRAM && bo->mem.start < bo->num_pages && bo->mem.start > 0) - (void) ttm_bo_validate(bo, &vmw_sys_placement, false, - false, false); + (void) ttm_bo_validate(bo, &vmw_sys_placement, false, false); - ret = ttm_bo_validate(bo, &placement, interruptible, false, false); + ret = ttm_bo_validate(bo, &placement, interruptible, false); /* For some reason we didn't up at the start of vram */ WARN_ON(ret == 0 && bo->offset != 0); @@ -315,7 +314,7 @@ void vmw_bo_pin(struct ttm_buffer_object *bo, bool pin) placement.num_placement = 1; placement.placement = &pl_flags; - ret = ttm_bo_validate(bo, &placement, false, true, true); + ret = ttm_bo_validate(bo, &placement, false, true); BUG_ON(ret != 0 || bo->mem.mem_type != old_mem_type); } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 534c96703c3f..394e6476105b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -1245,7 +1245,7 @@ static int vmw_validate_single_buffer(struct vmw_private *dev_priv, * used as a GMR, this will return -ENOMEM. */ - ret = ttm_bo_validate(bo, &vmw_vram_gmr_placement, true, false, false); + ret = ttm_bo_validate(bo, &vmw_vram_gmr_placement, true, false); if (likely(ret == 0 || ret == -ERESTARTSYS)) return ret; @@ -1255,7 +1255,7 @@ static int vmw_validate_single_buffer(struct vmw_private *dev_priv, */ DRM_INFO("Falling through to VRAM.\n"); - ret = ttm_bo_validate(bo, &vmw_vram_placement, true, false, false); + ret = ttm_bo_validate(bo, &vmw_vram_placement, true, false); return ret; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 0def4ff5b621..e01a17b407b2 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -1018,7 +1018,7 @@ int vmw_resource_check_buffer(struct vmw_resource *res, backup_dirty = res->backup_dirty; ret = ttm_bo_validate(&res->backup->base, res->func->backup_placement, - true, false, false); + true, false); if (unlikely(ret != 0)) goto out_no_validate; diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h index c6cae733ddef..3cb5d848fb66 100644 --- a/include/drm/ttm/ttm_bo_api.h +++ b/include/drm/ttm/ttm_bo_api.h @@ -337,7 +337,6 @@ extern int ttm_bo_wait(struct ttm_buffer_object *bo, bool lazy, * @bo: The buffer object. * @placement: Proposed placement for the buffer object. * @interruptible: Sleep interruptible if sleeping. - * @no_wait_reserve: Return immediately if other buffers are busy. * @no_wait_gpu: Return immediately if the GPU is busy. * * Changes placement and caching policy of the buffer object @@ -350,7 +349,7 @@ extern int ttm_bo_wait(struct ttm_buffer_object *bo, bool lazy, */ extern int ttm_bo_validate(struct ttm_buffer_object *bo, struct ttm_placement *placement, - bool interruptible, bool no_wait_reserve, + bool interruptible, bool no_wait_gpu); /** diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index dd96442cdc2a..e3a43a47d78c 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -394,7 +394,7 @@ struct ttm_bo_driver { */ int (*move) (struct ttm_buffer_object *bo, bool evict, bool interruptible, - bool no_wait_reserve, bool no_wait_gpu, + bool no_wait_gpu, struct ttm_mem_reg *new_mem); /** @@ -703,7 +703,6 @@ extern bool ttm_mem_reg_is_pci(struct ttm_bo_device *bdev, * @proposed_placement: Proposed new placement for the buffer object. * @mem: A struct ttm_mem_reg. * @interruptible: Sleep interruptible when sliping. - * @no_wait_reserve: Return immediately if other buffers are busy. * @no_wait_gpu: Return immediately if the GPU is busy. * * Allocate memory space for the buffer object pointed to by @bo, using @@ -719,7 +718,7 @@ extern int ttm_bo_mem_space(struct ttm_buffer_object *bo, struct ttm_placement *placement, struct ttm_mem_reg *mem, bool interruptible, - bool no_wait_reserve, bool no_wait_gpu); + bool no_wait_gpu); extern void ttm_bo_mem_put(struct ttm_buffer_object *bo, struct ttm_mem_reg *mem); @@ -901,7 +900,6 @@ extern int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, * * @bo: A pointer to a struct ttm_buffer_object. * @evict: 1: This is an eviction. Don't try to pipeline. - * @no_wait_reserve: Return immediately if other buffers are busy. * @no_wait_gpu: Return immediately if the GPU is busy. * @new_mem: struct ttm_mem_reg indicating where to move. * @@ -916,15 +914,14 @@ extern int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, */ extern int ttm_bo_move_ttm(struct ttm_buffer_object *bo, - bool evict, bool no_wait_reserve, - bool no_wait_gpu, struct ttm_mem_reg *new_mem); + bool evict, bool no_wait_gpu, + struct ttm_mem_reg *new_mem); /** * ttm_bo_move_memcpy * * @bo: A pointer to a struct ttm_buffer_object. * @evict: 1: This is an eviction. Don't try to pipeline. - * @no_wait_reserve: Return immediately if other buffers are busy. * @no_wait_gpu: Return immediately if the GPU is busy. * @new_mem: struct ttm_mem_reg indicating where to move. * @@ -939,8 +936,8 @@ extern int ttm_bo_move_ttm(struct ttm_buffer_object *bo, */ extern int ttm_bo_move_memcpy(struct ttm_buffer_object *bo, - bool evict, bool no_wait_reserve, - bool no_wait_gpu, struct ttm_mem_reg *new_mem); + bool evict, bool no_wait_gpu, + struct ttm_mem_reg *new_mem); /** * ttm_bo_free_old_node @@ -957,7 +954,6 @@ extern void ttm_bo_free_old_node(struct ttm_buffer_object *bo); * @bo: A pointer to a struct ttm_buffer_object. * @sync_obj: A sync object that signals when moving is complete. * @evict: This is an evict move. Don't return until the buffer is idle. - * @no_wait_reserve: Return immediately if other buffers are busy. * @no_wait_gpu: Return immediately if the GPU is busy. * @new_mem: struct ttm_mem_reg indicating where to move. * @@ -971,8 +967,7 @@ extern void ttm_bo_free_old_node(struct ttm_buffer_object *bo); extern int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo, void *sync_obj, - bool evict, bool no_wait_reserve, - bool no_wait_gpu, + bool evict, bool no_wait_gpu, struct ttm_mem_reg *new_mem); /** * ttm_io_prot -- cgit v1.2.3-59-g8ed1b From 278a334cbc96d3da66d56235b8ce84081e9a1892 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 13 Dec 2012 12:27:28 -0500 Subject: drm/radeon: enable the async DMA rings in the CS ioctl This enables the functionality added in the previous patches. Userspace acceleration drivers can use the CS ioctl to submit command buffers to the async DMA rings. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon_cs.c | 12 ++++++++++++ include/uapi/drm/radeon_drm.h | 1 + 2 files changed, 13 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index 1b32a5ab972d..396baba0141a 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -112,6 +112,18 @@ static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority } else p->ring = RADEON_RING_TYPE_GFX_INDEX; break; + case RADEON_CS_RING_DMA: + if (p->rdev->family >= CHIP_CAYMAN) { + if (p->priority > 0) + p->ring = R600_RING_TYPE_DMA_INDEX; + else + p->ring = CAYMAN_RING_TYPE_DMA1_INDEX; + } else if (p->rdev->family >= CHIP_R600) { + p->ring = R600_RING_TYPE_DMA_INDEX; + } else { + return -EINVAL; + } + break; } return 0; } diff --git a/include/uapi/drm/radeon_drm.h b/include/uapi/drm/radeon_drm.h index 5645a878faec..eeda91774c8a 100644 --- a/include/uapi/drm/radeon_drm.h +++ b/include/uapi/drm/radeon_drm.h @@ -917,6 +917,7 @@ struct drm_radeon_gem_va { /* The second dword of RADEON_CHUNK_ID_FLAGS is a uint32 that sets the ring type */ #define RADEON_CS_RING_GFX 0 #define RADEON_CS_RING_COMPUTE 1 +#define RADEON_CS_RING_DMA 2 /* The third dword of RADEON_CHUNK_ID_FLAGS is a sint32 that sets the priority */ /* 0 = normal, + = higher priority, - = lower priority */ -- cgit v1.2.3-59-g8ed1b From cb471f14b5eebfed22bb9f2d0f06601f171c574a Mon Sep 17 00:00:00 2001 From: Eunchul Kim Date: Fri, 14 Dec 2012 18:10:31 +0900 Subject: drm/exynos: add ipp subsystem This patch adds Image Post Processing(IPP) support for exynos drm driver. IPP supports image scaler/rotator and input/output DMA operations using IPP subsystem framework to control FIMC, Rotator and GSC hardware and supports some user interfaces for user side. And each IPP-based drivers support Memory to Memory operations with various converting. And in case of FIMC hardware, it also supports Writeback and Display output operations through local path. Features: - Memory to Memory operation support. - Various pixel formats support. - Image scaling support. - Color Space Conversion support. - Image crop operation support. - Rotate operation support to 90, 180 or 270 degree. - Flip operation support to vertical, horizontal or both. - Writeback operation support to display blended image of FIMD fifo on screen A summary to IPP Subsystem operations: First of all, user should get property capabilities from IPP subsystem and set these properties to hardware registers for desired operations. The properties could be pixel format, position, rotation degree and flip operation. And next, user should set source and destination buffer data using DRM_EXYNOS_IPP_QUEUE_BUF ioctl command with gem handles to source and destinition buffers. And next, user can control user-desired hardware with desired operations such as play, stop, pause and resume controls. And finally, user can aware of dma operation completion and also get destination buffer that it contains user-desried result through dequeue command. IOCTL commands: - DRM_EXYNOS_IPP_GET_PROPERTY . get ipp driver capabilitis and id. - DRM_EXYNOS_IPP_SET_PROPERTY . set format, position, rotation, flip to source and destination buffers - DRM_EXYNOS_IPP_QUEUE_BUF . enqueue/dequeue buffer and make event list. - DRM_EXYNOS_IPP_CMD_CTRL . play/stop/pause/resume control. Event: - DRM_EXYNOS_IPP_EVENT . a event to notify dma operation completion to user side. Basic control flow: Open -> Get properties -> User choose desired IPP sub driver(FIMC, Rotator or GSCALER) -> Set Property -> Create gem handle -> Enqueue to source and destination buffers -> Command control(Play) -> Event is notified to User -> User gets destinition buffer complated -> (Enqueue to source and destination buffers -> Event is notified to User) * N -> Queue/Dequeue to source and destination buffers -> Command control(Stop) -> Free gem handle -> Close Changelog v1 ~ v5: - added comments, code fixups and cleanups. Signed-off-by: Eunchul Kim Signed-off-by: Jinyoung Jeon Signed-off-by: Inki Dae Signed-off-by: Kyungmin Park --- drivers/gpu/drm/exynos/Kconfig | 6 + drivers/gpu/drm/exynos/Makefile | 1 + drivers/gpu/drm/exynos/exynos_drm_drv.c | 24 + drivers/gpu/drm/exynos/exynos_drm_drv.h | 7 + drivers/gpu/drm/exynos/exynos_drm_ipp.c | 2042 +++++++++++++++++++++++++++++++ drivers/gpu/drm/exynos/exynos_drm_ipp.h | 266 ++++ include/uapi/drm/exynos_drm.h | 190 +++ 7 files changed, 2536 insertions(+) create mode 100644 drivers/gpu/drm/exynos/exynos_drm_ipp.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_ipp.h (limited to 'include') diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 86fb75d3fcad..80ab242e2739 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -45,3 +45,9 @@ config DRM_EXYNOS_G2D depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_G2D help Choose this option if you want to use Exynos G2D for DRM. + +config DRM_EXYNOS_IPP + bool "Exynos DRM IPP" + depends on DRM_EXYNOS + help + Choose this option if you want to use IPP feature for DRM. diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index 26813b8a5056..6c536ce4d95b 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile @@ -16,5 +16,6 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o \ exynos_drm_hdmi.o exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o +exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 4a1168d3e907..0eb8a972e21c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -40,6 +40,7 @@ #include "exynos_drm_vidi.h" #include "exynos_drm_dmabuf.h" #include "exynos_drm_g2d.h" +#include "exynos_drm_ipp.h" #include "exynos_drm_iommu.h" #define DRIVER_NAME "exynos" @@ -249,6 +250,14 @@ static struct drm_ioctl_desc exynos_ioctls[] = { exynos_g2d_set_cmdlist_ioctl, DRM_UNLOCKED | DRM_AUTH), DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, exynos_g2d_exec_ioctl, DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_PROPERTY, + exynos_drm_ipp_get_property, DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(EXYNOS_IPP_SET_PROPERTY, + exynos_drm_ipp_set_property, DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(EXYNOS_IPP_QUEUE_BUF, + exynos_drm_ipp_queue_buf, DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(EXYNOS_IPP_CMD_CTRL, + exynos_drm_ipp_cmd_ctrl, DRM_UNLOCKED | DRM_AUTH), }; static const struct file_operations exynos_drm_driver_fops = { @@ -363,6 +372,12 @@ static int __init exynos_drm_init(void) goto out_g2d; #endif +#ifdef CONFIG_DRM_EXYNOS_IPP + ret = platform_driver_register(&ipp_driver); + if (ret < 0) + goto out_ipp; +#endif + ret = platform_driver_register(&exynos_drm_platform_driver); if (ret < 0) goto out_drm; @@ -380,6 +395,11 @@ out: platform_driver_unregister(&exynos_drm_platform_driver); out_drm: +#ifdef CONFIG_DRM_EXYNOS_IPP + platform_driver_unregister(&ipp_driver); +out_ipp: +#endif + #ifdef CONFIG_DRM_EXYNOS_G2D platform_driver_unregister(&g2d_driver); out_g2d: @@ -416,6 +436,10 @@ static void __exit exynos_drm_exit(void) platform_driver_unregister(&exynos_drm_platform_driver); +#ifdef CONFIG_DRM_EXYNOS_IPP + platform_driver_unregister(&ipp_driver); +#endif + #ifdef CONFIG_DRM_EXYNOS_G2D platform_driver_unregister(&g2d_driver); #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index e4ea74df4fc2..44ab3c7b6a90 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -232,8 +232,14 @@ struct exynos_drm_g2d_private { struct list_head userptr_list; }; +struct exynos_drm_ipp_private { + struct device *dev; + struct list_head event_list; +}; + struct drm_exynos_file_private { struct exynos_drm_g2d_private *g2d_priv; + struct exynos_drm_ipp_private *ipp_priv; }; /* @@ -343,4 +349,5 @@ extern struct platform_driver mixer_driver; extern struct platform_driver exynos_drm_common_hdmi_driver; extern struct platform_driver vidi_driver; extern struct platform_driver g2d_driver; +extern struct platform_driver ipp_driver; #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c new file mode 100644 index 000000000000..c640935ab7d7 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c @@ -0,0 +1,2042 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Authors: + * Eunchul Kim + * Jinyoung Jeon + * Sangmin Lee + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "exynos_drm_drv.h" +#include "exynos_drm_gem.h" +#include "exynos_drm_ipp.h" + +/* + * IPP is stand for Image Post Processing and + * supports image scaler/rotator and input/output DMA operations. + * using FIMC, GSC, Rotator, so on. + * IPP is integration device driver of same attribute h/w + */ + +/* + * TODO + * 1. expand command control id. + * 2. integrate property and config. + * 3. removed send_event id check routine. + * 4. compare send_event id if needed. + * 5. free subdrv_remove notifier callback list if needed. + * 6. need to check subdrv_open about multi-open. + * 7. need to power_on implement power and sysmmu ctrl. + */ + +#define get_ipp_context(dev) platform_get_drvdata(to_platform_device(dev)) +#define ipp_is_m2m_cmd(c) (c == IPP_CMD_M2M) + +/* + * A structure of event. + * + * @base: base of event. + * @event: ipp event. + */ +struct drm_exynos_ipp_send_event { + struct drm_pending_event base; + struct drm_exynos_ipp_event event; +}; + +/* + * A structure of memory node. + * + * @list: list head to memory queue information. + * @ops_id: id of operations. + * @prop_id: id of property. + * @buf_id: id of buffer. + * @buf_info: gem objects and dma address, size. + * @filp: a pointer to drm_file. + */ +struct drm_exynos_ipp_mem_node { + struct list_head list; + enum drm_exynos_ops_id ops_id; + u32 prop_id; + u32 buf_id; + struct drm_exynos_ipp_buf_info buf_info; + struct drm_file *filp; +}; + +/* + * A structure of ipp context. + * + * @subdrv: prepare initialization using subdrv. + * @ipp_lock: lock for synchronization of access to ipp_idr. + * @prop_lock: lock for synchronization of access to prop_idr. + * @ipp_idr: ipp driver idr. + * @prop_idr: property idr. + * @event_workq: event work queue. + * @cmd_workq: command work queue. + */ +struct ipp_context { + struct exynos_drm_subdrv subdrv; + struct mutex ipp_lock; + struct mutex prop_lock; + struct idr ipp_idr; + struct idr prop_idr; + struct workqueue_struct *event_workq; + struct workqueue_struct *cmd_workq; +}; + +static LIST_HEAD(exynos_drm_ippdrv_list); +static DEFINE_MUTEX(exynos_drm_ippdrv_lock); +static BLOCKING_NOTIFIER_HEAD(exynos_drm_ippnb_list); + +int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv) +{ + DRM_DEBUG_KMS("%s\n", __func__); + + if (!ippdrv) + return -EINVAL; + + mutex_lock(&exynos_drm_ippdrv_lock); + list_add_tail(&ippdrv->drv_list, &exynos_drm_ippdrv_list); + mutex_unlock(&exynos_drm_ippdrv_lock); + + return 0; +} + +int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv) +{ + DRM_DEBUG_KMS("%s\n", __func__); + + if (!ippdrv) + return -EINVAL; + + mutex_lock(&exynos_drm_ippdrv_lock); + list_del(&ippdrv->drv_list); + mutex_unlock(&exynos_drm_ippdrv_lock); + + return 0; +} + +static int ipp_create_id(struct idr *id_idr, struct mutex *lock, void *obj, + u32 *idp) +{ + int ret; + + DRM_DEBUG_KMS("%s\n", __func__); + +again: + /* ensure there is space available to allocate a handle */ + if (idr_pre_get(id_idr, GFP_KERNEL) == 0) { + DRM_ERROR("failed to get idr.\n"); + return -ENOMEM; + } + + /* do the allocation under our mutexlock */ + mutex_lock(lock); + ret = idr_get_new_above(id_idr, obj, 1, (int *)idp); + mutex_unlock(lock); + if (ret == -EAGAIN) + goto again; + + return ret; +} + +static void *ipp_find_obj(struct idr *id_idr, struct mutex *lock, u32 id) +{ + void *obj; + + DRM_DEBUG_KMS("%s:id[%d]\n", __func__, id); + + mutex_lock(lock); + + /* find object using handle */ + obj = idr_find(id_idr, id); + if (!obj) { + DRM_ERROR("failed to find object.\n"); + mutex_unlock(lock); + return ERR_PTR(-ENODEV); + } + + mutex_unlock(lock); + + return obj; +} + +static inline bool ipp_check_dedicated(struct exynos_drm_ippdrv *ippdrv, + enum drm_exynos_ipp_cmd cmd) +{ + /* + * check dedicated flag and WB, OUTPUT operation with + * power on state. + */ + if (ippdrv->dedicated || (!ipp_is_m2m_cmd(cmd) && + !pm_runtime_suspended(ippdrv->dev))) + return true; + + return false; +} + +static struct exynos_drm_ippdrv *ipp_find_driver(struct ipp_context *ctx, + struct drm_exynos_ipp_property *property) +{ + struct exynos_drm_ippdrv *ippdrv; + u32 ipp_id = property->ipp_id; + + DRM_DEBUG_KMS("%s:ipp_id[%d]\n", __func__, ipp_id); + + if (ipp_id) { + /* find ipp driver using idr */ + ippdrv = ipp_find_obj(&ctx->ipp_idr, &ctx->ipp_lock, + ipp_id); + if (IS_ERR_OR_NULL(ippdrv)) { + DRM_ERROR("not found ipp%d driver.\n", ipp_id); + return ippdrv; + } + + /* + * WB, OUTPUT opertion not supported multi-operation. + * so, make dedicated state at set property ioctl. + * when ipp driver finished operations, clear dedicated flags. + */ + if (ipp_check_dedicated(ippdrv, property->cmd)) { + DRM_ERROR("already used choose device.\n"); + return ERR_PTR(-EBUSY); + } + + /* + * This is necessary to find correct device in ipp drivers. + * ipp drivers have different abilities, + * so need to check property. + */ + if (ippdrv->check_property && + ippdrv->check_property(ippdrv->dev, property)) { + DRM_ERROR("not support property.\n"); + return ERR_PTR(-EINVAL); + } + + return ippdrv; + } else { + /* + * This case is search all ipp driver for finding. + * user application don't set ipp_id in this case, + * so ipp subsystem search correct driver in driver list. + */ + list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { + if (ipp_check_dedicated(ippdrv, property->cmd)) { + DRM_DEBUG_KMS("%s:used device.\n", __func__); + continue; + } + + if (ippdrv->check_property && + ippdrv->check_property(ippdrv->dev, property)) { + DRM_DEBUG_KMS("%s:not support property.\n", + __func__); + continue; + } + + return ippdrv; + } + + DRM_ERROR("not support ipp driver operations.\n"); + } + + return ERR_PTR(-ENODEV); +} + +static struct exynos_drm_ippdrv *ipp_find_drv_by_handle(u32 prop_id) +{ + struct exynos_drm_ippdrv *ippdrv; + struct drm_exynos_ipp_cmd_node *c_node; + int count = 0; + + DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, prop_id); + + if (list_empty(&exynos_drm_ippdrv_list)) { + DRM_DEBUG_KMS("%s:ippdrv_list is empty.\n", __func__); + return ERR_PTR(-ENODEV); + } + + /* + * This case is search ipp driver by prop_id handle. + * sometimes, ipp subsystem find driver by prop_id. + * e.g PAUSE state, queue buf, command contro. + */ + list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { + DRM_DEBUG_KMS("%s:count[%d]ippdrv[0x%x]\n", __func__, + count++, (int)ippdrv); + + if (!list_empty(&ippdrv->cmd_list)) { + list_for_each_entry(c_node, &ippdrv->cmd_list, list) + if (c_node->property.prop_id == prop_id) + return ippdrv; + } + } + + return ERR_PTR(-ENODEV); +} + +int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_ipp_private *priv = file_priv->ipp_priv; + struct device *dev = priv->dev; + struct ipp_context *ctx = get_ipp_context(dev); + struct drm_exynos_ipp_prop_list *prop_list = data; + struct exynos_drm_ippdrv *ippdrv; + int count = 0; + + DRM_DEBUG_KMS("%s\n", __func__); + + if (!ctx) { + DRM_ERROR("invalid context.\n"); + return -EINVAL; + } + + if (!prop_list) { + DRM_ERROR("invalid property parameter.\n"); + return -EINVAL; + } + + DRM_DEBUG_KMS("%s:ipp_id[%d]\n", __func__, prop_list->ipp_id); + + if (!prop_list->ipp_id) { + list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) + count++; + /* + * Supports ippdrv list count for user application. + * First step user application getting ippdrv count. + * and second step getting ippdrv capability using ipp_id. + */ + prop_list->count = count; + } else { + /* + * Getting ippdrv capability by ipp_id. + * some deivce not supported wb, output interface. + * so, user application detect correct ipp driver + * using this ioctl. + */ + ippdrv = ipp_find_obj(&ctx->ipp_idr, &ctx->ipp_lock, + prop_list->ipp_id); + if (!ippdrv) { + DRM_ERROR("not found ipp%d driver.\n", + prop_list->ipp_id); + return -EINVAL; + } + + prop_list = ippdrv->prop_list; + } + + return 0; +} + +static void ipp_print_property(struct drm_exynos_ipp_property *property, + int idx) +{ + struct drm_exynos_ipp_config *config = &property->config[idx]; + struct drm_exynos_pos *pos = &config->pos; + struct drm_exynos_sz *sz = &config->sz; + + DRM_DEBUG_KMS("%s:prop_id[%d]ops[%s]fmt[0x%x]\n", + __func__, property->prop_id, idx ? "dst" : "src", config->fmt); + + DRM_DEBUG_KMS("%s:pos[%d %d %d %d]sz[%d %d]f[%d]r[%d]\n", + __func__, pos->x, pos->y, pos->w, pos->h, + sz->hsize, sz->vsize, config->flip, config->degree); +} + +static int ipp_find_and_set_property(struct drm_exynos_ipp_property *property) +{ + struct exynos_drm_ippdrv *ippdrv; + struct drm_exynos_ipp_cmd_node *c_node; + u32 prop_id = property->prop_id; + + DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, prop_id); + + ippdrv = ipp_find_drv_by_handle(prop_id); + if (IS_ERR_OR_NULL(ippdrv)) { + DRM_ERROR("failed to get ipp driver.\n"); + return -EINVAL; + } + + /* + * Find command node using command list in ippdrv. + * when we find this command no using prop_id. + * return property information set in this command node. + */ + list_for_each_entry(c_node, &ippdrv->cmd_list, list) { + if ((c_node->property.prop_id == prop_id) && + (c_node->state == IPP_STATE_STOP)) { + DRM_DEBUG_KMS("%s:found cmd[%d]ippdrv[0x%x]\n", + __func__, property->cmd, (int)ippdrv); + + c_node->property = *property; + return 0; + } + } + + DRM_ERROR("failed to search property.\n"); + + return -EINVAL; +} + +static struct drm_exynos_ipp_cmd_work *ipp_create_cmd_work(void) +{ + struct drm_exynos_ipp_cmd_work *cmd_work; + + DRM_DEBUG_KMS("%s\n", __func__); + + cmd_work = kzalloc(sizeof(*cmd_work), GFP_KERNEL); + if (!cmd_work) { + DRM_ERROR("failed to alloc cmd_work.\n"); + return ERR_PTR(-ENOMEM); + } + + INIT_WORK((struct work_struct *)cmd_work, ipp_sched_cmd); + + return cmd_work; +} + +static struct drm_exynos_ipp_event_work *ipp_create_event_work(void) +{ + struct drm_exynos_ipp_event_work *event_work; + + DRM_DEBUG_KMS("%s\n", __func__); + + event_work = kzalloc(sizeof(*event_work), GFP_KERNEL); + if (!event_work) { + DRM_ERROR("failed to alloc event_work.\n"); + return ERR_PTR(-ENOMEM); + } + + INIT_WORK((struct work_struct *)event_work, ipp_sched_event); + + return event_work; +} + +int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_ipp_private *priv = file_priv->ipp_priv; + struct device *dev = priv->dev; + struct ipp_context *ctx = get_ipp_context(dev); + struct drm_exynos_ipp_property *property = data; + struct exynos_drm_ippdrv *ippdrv; + struct drm_exynos_ipp_cmd_node *c_node; + int ret, i; + + DRM_DEBUG_KMS("%s\n", __func__); + + if (!ctx) { + DRM_ERROR("invalid context.\n"); + return -EINVAL; + } + + if (!property) { + DRM_ERROR("invalid property parameter.\n"); + return -EINVAL; + } + + /* + * This is log print for user application property. + * user application set various property. + */ + for_each_ipp_ops(i) + ipp_print_property(property, i); + + /* + * set property ioctl generated new prop_id. + * but in this case already asigned prop_id using old set property. + * e.g PAUSE state. this case supports find current prop_id and use it + * instead of allocation. + */ + if (property->prop_id) { + DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, property->prop_id); + return ipp_find_and_set_property(property); + } + + /* find ipp driver using ipp id */ + ippdrv = ipp_find_driver(ctx, property); + if (IS_ERR_OR_NULL(ippdrv)) { + DRM_ERROR("failed to get ipp driver.\n"); + return -EINVAL; + } + + /* allocate command node */ + c_node = kzalloc(sizeof(*c_node), GFP_KERNEL); + if (!c_node) { + DRM_ERROR("failed to allocate map node.\n"); + return -ENOMEM; + } + + /* create property id */ + ret = ipp_create_id(&ctx->prop_idr, &ctx->prop_lock, c_node, + &property->prop_id); + if (ret) { + DRM_ERROR("failed to create id.\n"); + goto err_clear; + } + + DRM_DEBUG_KMS("%s:created prop_id[%d]cmd[%d]ippdrv[0x%x]\n", + __func__, property->prop_id, property->cmd, (int)ippdrv); + + /* stored property information and ippdrv in private data */ + c_node->priv = priv; + c_node->property = *property; + c_node->state = IPP_STATE_IDLE; + + c_node->start_work = ipp_create_cmd_work(); + if (IS_ERR_OR_NULL(c_node->start_work)) { + DRM_ERROR("failed to create start work.\n"); + goto err_clear; + } + + c_node->stop_work = ipp_create_cmd_work(); + if (IS_ERR_OR_NULL(c_node->stop_work)) { + DRM_ERROR("failed to create stop work.\n"); + goto err_free_start; + } + + c_node->event_work = ipp_create_event_work(); + if (IS_ERR_OR_NULL(c_node->event_work)) { + DRM_ERROR("failed to create event work.\n"); + goto err_free_stop; + } + + mutex_init(&c_node->cmd_lock); + mutex_init(&c_node->mem_lock); + mutex_init(&c_node->event_lock); + + init_completion(&c_node->start_complete); + init_completion(&c_node->stop_complete); + + for_each_ipp_ops(i) + INIT_LIST_HEAD(&c_node->mem_list[i]); + + INIT_LIST_HEAD(&c_node->event_list); + list_splice_init(&priv->event_list, &c_node->event_list); + list_add_tail(&c_node->list, &ippdrv->cmd_list); + + /* make dedicated state without m2m */ + if (!ipp_is_m2m_cmd(property->cmd)) + ippdrv->dedicated = true; + + return 0; + +err_free_stop: + kfree(c_node->stop_work); +err_free_start: + kfree(c_node->start_work); +err_clear: + kfree(c_node); + return ret; +} + +static void ipp_clean_cmd_node(struct drm_exynos_ipp_cmd_node *c_node) +{ + DRM_DEBUG_KMS("%s\n", __func__); + + /* delete list */ + list_del(&c_node->list); + + /* destroy mutex */ + mutex_destroy(&c_node->cmd_lock); + mutex_destroy(&c_node->mem_lock); + mutex_destroy(&c_node->event_lock); + + /* free command node */ + kfree(c_node->start_work); + kfree(c_node->stop_work); + kfree(c_node->event_work); + kfree(c_node); +} + +static int ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node) +{ + struct drm_exynos_ipp_property *property = &c_node->property; + struct drm_exynos_ipp_mem_node *m_node; + struct list_head *head; + int ret, i, count[EXYNOS_DRM_OPS_MAX] = { 0, }; + + DRM_DEBUG_KMS("%s\n", __func__); + + mutex_lock(&c_node->mem_lock); + + for_each_ipp_ops(i) { + /* source/destination memory list */ + head = &c_node->mem_list[i]; + + if (list_empty(head)) { + DRM_DEBUG_KMS("%s:%s memory empty.\n", __func__, + i ? "dst" : "src"); + continue; + } + + /* find memory node entry */ + list_for_each_entry(m_node, head, list) { + DRM_DEBUG_KMS("%s:%s,count[%d]m_node[0x%x]\n", __func__, + i ? "dst" : "src", count[i], (int)m_node); + count[i]++; + } + } + + DRM_DEBUG_KMS("%s:min[%d]max[%d]\n", __func__, + min(count[EXYNOS_DRM_OPS_SRC], count[EXYNOS_DRM_OPS_DST]), + max(count[EXYNOS_DRM_OPS_SRC], count[EXYNOS_DRM_OPS_DST])); + + /* + * M2M operations should be need paired memory address. + * so, need to check minimum count about src, dst. + * other case not use paired memory, so use maximum count + */ + if (ipp_is_m2m_cmd(property->cmd)) + ret = min(count[EXYNOS_DRM_OPS_SRC], + count[EXYNOS_DRM_OPS_DST]); + else + ret = max(count[EXYNOS_DRM_OPS_SRC], + count[EXYNOS_DRM_OPS_DST]); + + mutex_unlock(&c_node->mem_lock); + + return ret; +} + +static struct drm_exynos_ipp_mem_node + *ipp_find_mem_node(struct drm_exynos_ipp_cmd_node *c_node, + struct drm_exynos_ipp_queue_buf *qbuf) +{ + struct drm_exynos_ipp_mem_node *m_node; + struct list_head *head; + int count = 0; + + DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, qbuf->buf_id); + + /* source/destination memory list */ + head = &c_node->mem_list[qbuf->ops_id]; + + /* find memory node from memory list */ + list_for_each_entry(m_node, head, list) { + DRM_DEBUG_KMS("%s:count[%d]m_node[0x%x]\n", + __func__, count++, (int)m_node); + + /* compare buffer id */ + if (m_node->buf_id == qbuf->buf_id) + return m_node; + } + + return NULL; +} + +static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv, + struct drm_exynos_ipp_cmd_node *c_node, + struct drm_exynos_ipp_mem_node *m_node) +{ + struct exynos_drm_ipp_ops *ops = NULL; + int ret = 0; + + DRM_DEBUG_KMS("%s:node[0x%x]\n", __func__, (int)m_node); + + if (!m_node) { + DRM_ERROR("invalid queue node.\n"); + return -EFAULT; + } + + mutex_lock(&c_node->mem_lock); + + DRM_DEBUG_KMS("%s:ops_id[%d]\n", __func__, m_node->ops_id); + + /* get operations callback */ + ops = ippdrv->ops[m_node->ops_id]; + if (!ops) { + DRM_ERROR("not support ops.\n"); + ret = -EFAULT; + goto err_unlock; + } + + /* set address and enable irq */ + if (ops->set_addr) { + ret = ops->set_addr(ippdrv->dev, &m_node->buf_info, + m_node->buf_id, IPP_BUF_ENQUEUE); + if (ret) { + DRM_ERROR("failed to set addr.\n"); + goto err_unlock; + } + } + +err_unlock: + mutex_unlock(&c_node->mem_lock); + return ret; +} + +static struct drm_exynos_ipp_mem_node + *ipp_get_mem_node(struct drm_device *drm_dev, + struct drm_file *file, + struct drm_exynos_ipp_cmd_node *c_node, + struct drm_exynos_ipp_queue_buf *qbuf) +{ + struct drm_exynos_ipp_mem_node *m_node; + struct drm_exynos_ipp_buf_info buf_info; + void *addr; + int i; + + DRM_DEBUG_KMS("%s\n", __func__); + + mutex_lock(&c_node->mem_lock); + + m_node = kzalloc(sizeof(*m_node), GFP_KERNEL); + if (!m_node) { + DRM_ERROR("failed to allocate queue node.\n"); + goto err_unlock; + } + + /* clear base address for error handling */ + memset(&buf_info, 0x0, sizeof(buf_info)); + + /* operations, buffer id */ + m_node->ops_id = qbuf->ops_id; + m_node->prop_id = qbuf->prop_id; + m_node->buf_id = qbuf->buf_id; + + DRM_DEBUG_KMS("%s:m_node[0x%x]ops_id[%d]\n", __func__, + (int)m_node, qbuf->ops_id); + DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]\n", __func__, + qbuf->prop_id, m_node->buf_id); + + for_each_ipp_planar(i) { + DRM_DEBUG_KMS("%s:i[%d]handle[0x%x]\n", __func__, + i, qbuf->handle[i]); + + /* get dma address by handle */ + if (qbuf->handle[i]) { + addr = exynos_drm_gem_get_dma_addr(drm_dev, + qbuf->handle[i], file); + if (IS_ERR(addr)) { + DRM_ERROR("failed to get addr.\n"); + goto err_clear; + } + + buf_info.handles[i] = qbuf->handle[i]; + buf_info.base[i] = *(dma_addr_t *) addr; + DRM_DEBUG_KMS("%s:i[%d]base[0x%x]hd[0x%x]\n", + __func__, i, buf_info.base[i], + (int)buf_info.handles[i]); + } + } + + m_node->filp = file; + m_node->buf_info = buf_info; + list_add_tail(&m_node->list, &c_node->mem_list[qbuf->ops_id]); + + mutex_unlock(&c_node->mem_lock); + return m_node; + +err_clear: + kfree(m_node); +err_unlock: + mutex_unlock(&c_node->mem_lock); + return ERR_PTR(-EFAULT); +} + +static int ipp_put_mem_node(struct drm_device *drm_dev, + struct drm_exynos_ipp_cmd_node *c_node, + struct drm_exynos_ipp_mem_node *m_node) +{ + int i; + + DRM_DEBUG_KMS("%s:node[0x%x]\n", __func__, (int)m_node); + + if (!m_node) { + DRM_ERROR("invalid dequeue node.\n"); + return -EFAULT; + } + + if (list_empty(&m_node->list)) { + DRM_ERROR("empty memory node.\n"); + return -ENOMEM; + } + + mutex_lock(&c_node->mem_lock); + + DRM_DEBUG_KMS("%s:ops_id[%d]\n", __func__, m_node->ops_id); + + /* put gem buffer */ + for_each_ipp_planar(i) { + unsigned long handle = m_node->buf_info.handles[i]; + if (handle) + exynos_drm_gem_put_dma_addr(drm_dev, handle, + m_node->filp); + } + + /* delete list in queue */ + list_del(&m_node->list); + kfree(m_node); + + mutex_unlock(&c_node->mem_lock); + + return 0; +} + +static void ipp_free_event(struct drm_pending_event *event) +{ + kfree(event); +} + +static int ipp_get_event(struct drm_device *drm_dev, + struct drm_file *file, + struct drm_exynos_ipp_cmd_node *c_node, + struct drm_exynos_ipp_queue_buf *qbuf) +{ + struct drm_exynos_ipp_send_event *e; + unsigned long flags; + + DRM_DEBUG_KMS("%s:ops_id[%d]buf_id[%d]\n", __func__, + qbuf->ops_id, qbuf->buf_id); + + e = kzalloc(sizeof(*e), GFP_KERNEL); + + if (!e) { + DRM_ERROR("failed to allocate event.\n"); + spin_lock_irqsave(&drm_dev->event_lock, flags); + file->event_space += sizeof(e->event); + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + return -ENOMEM; + } + + /* make event */ + e->event.base.type = DRM_EXYNOS_IPP_EVENT; + e->event.base.length = sizeof(e->event); + e->event.user_data = qbuf->user_data; + e->event.prop_id = qbuf->prop_id; + e->event.buf_id[EXYNOS_DRM_OPS_DST] = qbuf->buf_id; + e->base.event = &e->event.base; + e->base.file_priv = file; + e->base.destroy = ipp_free_event; + list_add_tail(&e->base.link, &c_node->event_list); + + return 0; +} + +static void ipp_put_event(struct drm_exynos_ipp_cmd_node *c_node, + struct drm_exynos_ipp_queue_buf *qbuf) +{ + struct drm_exynos_ipp_send_event *e, *te; + int count = 0; + + DRM_DEBUG_KMS("%s\n", __func__); + + if (list_empty(&c_node->event_list)) { + DRM_DEBUG_KMS("%s:event_list is empty.\n", __func__); + return; + } + + list_for_each_entry_safe(e, te, &c_node->event_list, base.link) { + DRM_DEBUG_KMS("%s:count[%d]e[0x%x]\n", + __func__, count++, (int)e); + + /* + * quf == NULL condition means all event deletion. + * stop operations want to delete all event list. + * another case delete only same buf id. + */ + if (!qbuf) { + /* delete list */ + list_del(&e->base.link); + kfree(e); + } + + /* compare buffer id */ + if (qbuf && (qbuf->buf_id == + e->event.buf_id[EXYNOS_DRM_OPS_DST])) { + /* delete list */ + list_del(&e->base.link); + kfree(e); + return; + } + } +} + +void ipp_handle_cmd_work(struct device *dev, + struct exynos_drm_ippdrv *ippdrv, + struct drm_exynos_ipp_cmd_work *cmd_work, + struct drm_exynos_ipp_cmd_node *c_node) +{ + struct ipp_context *ctx = get_ipp_context(dev); + + cmd_work->ippdrv = ippdrv; + cmd_work->c_node = c_node; + queue_work(ctx->cmd_workq, (struct work_struct *)cmd_work); +} + +static int ipp_queue_buf_with_run(struct device *dev, + struct drm_exynos_ipp_cmd_node *c_node, + struct drm_exynos_ipp_mem_node *m_node, + struct drm_exynos_ipp_queue_buf *qbuf) +{ + struct exynos_drm_ippdrv *ippdrv; + struct drm_exynos_ipp_property *property; + struct exynos_drm_ipp_ops *ops; + int ret; + + DRM_DEBUG_KMS("%s\n", __func__); + + ippdrv = ipp_find_drv_by_handle(qbuf->prop_id); + if (IS_ERR_OR_NULL(ippdrv)) { + DRM_ERROR("failed to get ipp driver.\n"); + return -EFAULT; + } + + ops = ippdrv->ops[qbuf->ops_id]; + if (!ops) { + DRM_ERROR("failed to get ops.\n"); + return -EFAULT; + } + + property = &c_node->property; + + if (c_node->state != IPP_STATE_START) { + DRM_DEBUG_KMS("%s:bypass for invalid state.\n" , __func__); + return 0; + } + + if (!ipp_check_mem_list(c_node)) { + DRM_DEBUG_KMS("%s:empty memory.\n", __func__); + return 0; + } + + /* + * If set destination buffer and enabled clock, + * then m2m operations need start operations at queue_buf + */ + if (ipp_is_m2m_cmd(property->cmd)) { + struct drm_exynos_ipp_cmd_work *cmd_work = c_node->start_work; + + cmd_work->ctrl = IPP_CTRL_PLAY; + ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node); + } else { + ret = ipp_set_mem_node(ippdrv, c_node, m_node); + if (ret) { + DRM_ERROR("failed to set m node.\n"); + return ret; + } + } + + return 0; +} + +static void ipp_clean_queue_buf(struct drm_device *drm_dev, + struct drm_exynos_ipp_cmd_node *c_node, + struct drm_exynos_ipp_queue_buf *qbuf) +{ + struct drm_exynos_ipp_mem_node *m_node, *tm_node; + + DRM_DEBUG_KMS("%s\n", __func__); + + if (!list_empty(&c_node->mem_list[qbuf->ops_id])) { + /* delete list */ + list_for_each_entry_safe(m_node, tm_node, + &c_node->mem_list[qbuf->ops_id], list) { + if (m_node->buf_id == qbuf->buf_id && + m_node->ops_id == qbuf->ops_id) + ipp_put_mem_node(drm_dev, c_node, m_node); + } + } +} + +int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_ipp_private *priv = file_priv->ipp_priv; + struct device *dev = priv->dev; + struct ipp_context *ctx = get_ipp_context(dev); + struct drm_exynos_ipp_queue_buf *qbuf = data; + struct drm_exynos_ipp_cmd_node *c_node; + struct drm_exynos_ipp_mem_node *m_node; + int ret; + + DRM_DEBUG_KMS("%s\n", __func__); + + if (!qbuf) { + DRM_ERROR("invalid buf parameter.\n"); + return -EINVAL; + } + + if (qbuf->ops_id >= EXYNOS_DRM_OPS_MAX) { + DRM_ERROR("invalid ops parameter.\n"); + return -EINVAL; + } + + DRM_DEBUG_KMS("%s:prop_id[%d]ops_id[%s]buf_id[%d]buf_type[%d]\n", + __func__, qbuf->prop_id, qbuf->ops_id ? "dst" : "src", + qbuf->buf_id, qbuf->buf_type); + + /* find command node */ + c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock, + qbuf->prop_id); + if (!c_node) { + DRM_ERROR("failed to get command node.\n"); + return -EFAULT; + } + + /* buffer control */ + switch (qbuf->buf_type) { + case IPP_BUF_ENQUEUE: + /* get memory node */ + m_node = ipp_get_mem_node(drm_dev, file, c_node, qbuf); + if (IS_ERR(m_node)) { + DRM_ERROR("failed to get m_node.\n"); + return PTR_ERR(m_node); + } + + /* + * first step get event for destination buffer. + * and second step when M2M case run with destination buffer + * if needed. + */ + if (qbuf->ops_id == EXYNOS_DRM_OPS_DST) { + /* get event for destination buffer */ + ret = ipp_get_event(drm_dev, file, c_node, qbuf); + if (ret) { + DRM_ERROR("failed to get event.\n"); + goto err_clean_node; + } + + /* + * M2M case run play control for streaming feature. + * other case set address and waiting. + */ + ret = ipp_queue_buf_with_run(dev, c_node, m_node, qbuf); + if (ret) { + DRM_ERROR("failed to run command.\n"); + goto err_clean_node; + } + } + break; + case IPP_BUF_DEQUEUE: + mutex_lock(&c_node->cmd_lock); + + /* put event for destination buffer */ + if (qbuf->ops_id == EXYNOS_DRM_OPS_DST) + ipp_put_event(c_node, qbuf); + + ipp_clean_queue_buf(drm_dev, c_node, qbuf); + + mutex_unlock(&c_node->cmd_lock); + break; + default: + DRM_ERROR("invalid buffer control.\n"); + return -EINVAL; + } + + return 0; + +err_clean_node: + DRM_ERROR("clean memory nodes.\n"); + + ipp_clean_queue_buf(drm_dev, c_node, qbuf); + return ret; +} + +static bool exynos_drm_ipp_check_valid(struct device *dev, + enum drm_exynos_ipp_ctrl ctrl, enum drm_exynos_ipp_state state) +{ + DRM_DEBUG_KMS("%s\n", __func__); + + if (ctrl != IPP_CTRL_PLAY) { + if (pm_runtime_suspended(dev)) { + DRM_ERROR("pm:runtime_suspended.\n"); + goto err_status; + } + } + + switch (ctrl) { + case IPP_CTRL_PLAY: + if (state != IPP_STATE_IDLE) + goto err_status; + break; + case IPP_CTRL_STOP: + if (state == IPP_STATE_STOP) + goto err_status; + break; + case IPP_CTRL_PAUSE: + if (state != IPP_STATE_START) + goto err_status; + break; + case IPP_CTRL_RESUME: + if (state != IPP_STATE_STOP) + goto err_status; + break; + default: + DRM_ERROR("invalid state.\n"); + goto err_status; + break; + } + + return true; + +err_status: + DRM_ERROR("invalid status:ctrl[%d]state[%d]\n", ctrl, state); + return false; +} + +int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_ipp_private *priv = file_priv->ipp_priv; + struct exynos_drm_ippdrv *ippdrv = NULL; + struct device *dev = priv->dev; + struct ipp_context *ctx = get_ipp_context(dev); + struct drm_exynos_ipp_cmd_ctrl *cmd_ctrl = data; + struct drm_exynos_ipp_cmd_work *cmd_work; + struct drm_exynos_ipp_cmd_node *c_node; + + DRM_DEBUG_KMS("%s\n", __func__); + + if (!ctx) { + DRM_ERROR("invalid context.\n"); + return -EINVAL; + } + + if (!cmd_ctrl) { + DRM_ERROR("invalid control parameter.\n"); + return -EINVAL; + } + + DRM_DEBUG_KMS("%s:ctrl[%d]prop_id[%d]\n", __func__, + cmd_ctrl->ctrl, cmd_ctrl->prop_id); + + ippdrv = ipp_find_drv_by_handle(cmd_ctrl->prop_id); + if (IS_ERR(ippdrv)) { + DRM_ERROR("failed to get ipp driver.\n"); + return PTR_ERR(ippdrv); + } + + c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock, + cmd_ctrl->prop_id); + if (!c_node) { + DRM_ERROR("invalid command node list.\n"); + return -EINVAL; + } + + if (!exynos_drm_ipp_check_valid(ippdrv->dev, cmd_ctrl->ctrl, + c_node->state)) { + DRM_ERROR("invalid state.\n"); + return -EINVAL; + } + + switch (cmd_ctrl->ctrl) { + case IPP_CTRL_PLAY: + if (pm_runtime_suspended(ippdrv->dev)) + pm_runtime_get_sync(ippdrv->dev); + c_node->state = IPP_STATE_START; + + cmd_work = c_node->start_work; + cmd_work->ctrl = cmd_ctrl->ctrl; + ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node); + c_node->state = IPP_STATE_START; + break; + case IPP_CTRL_STOP: + cmd_work = c_node->stop_work; + cmd_work->ctrl = cmd_ctrl->ctrl; + ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node); + + if (!wait_for_completion_timeout(&c_node->stop_complete, + msecs_to_jiffies(300))) { + DRM_ERROR("timeout stop:prop_id[%d]\n", + c_node->property.prop_id); + } + + c_node->state = IPP_STATE_STOP; + ippdrv->dedicated = false; + ipp_clean_cmd_node(c_node); + + if (list_empty(&ippdrv->cmd_list)) + pm_runtime_put_sync(ippdrv->dev); + break; + case IPP_CTRL_PAUSE: + cmd_work = c_node->stop_work; + cmd_work->ctrl = cmd_ctrl->ctrl; + ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node); + + if (!wait_for_completion_timeout(&c_node->stop_complete, + msecs_to_jiffies(200))) { + DRM_ERROR("timeout stop:prop_id[%d]\n", + c_node->property.prop_id); + } + + c_node->state = IPP_STATE_STOP; + break; + case IPP_CTRL_RESUME: + c_node->state = IPP_STATE_START; + cmd_work = c_node->start_work; + cmd_work->ctrl = cmd_ctrl->ctrl; + ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node); + break; + default: + DRM_ERROR("could not support this state currently.\n"); + return -EINVAL; + } + + DRM_DEBUG_KMS("%s:done ctrl[%d]prop_id[%d]\n", __func__, + cmd_ctrl->ctrl, cmd_ctrl->prop_id); + + return 0; +} + +int exynos_drm_ippnb_register(struct notifier_block *nb) +{ + return blocking_notifier_chain_register( + &exynos_drm_ippnb_list, nb); +} + +int exynos_drm_ippnb_unregister(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister( + &exynos_drm_ippnb_list, nb); +} + +int exynos_drm_ippnb_send_event(unsigned long val, void *v) +{ + return blocking_notifier_call_chain( + &exynos_drm_ippnb_list, val, v); +} + +static int ipp_set_property(struct exynos_drm_ippdrv *ippdrv, + struct drm_exynos_ipp_property *property) +{ + struct exynos_drm_ipp_ops *ops = NULL; + bool swap = false; + int ret, i; + + if (!property) { + DRM_ERROR("invalid property parameter.\n"); + return -EINVAL; + } + + DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, property->prop_id); + + /* reset h/w block */ + if (ippdrv->reset && + ippdrv->reset(ippdrv->dev)) { + DRM_ERROR("failed to reset.\n"); + return -EINVAL; + } + + /* set source,destination operations */ + for_each_ipp_ops(i) { + struct drm_exynos_ipp_config *config = + &property->config[i]; + + ops = ippdrv->ops[i]; + if (!ops || !config) { + DRM_ERROR("not support ops and config.\n"); + return -EINVAL; + } + + /* set format */ + if (ops->set_fmt) { + ret = ops->set_fmt(ippdrv->dev, config->fmt); + if (ret) { + DRM_ERROR("not support format.\n"); + return ret; + } + } + + /* set transform for rotation, flip */ + if (ops->set_transf) { + ret = ops->set_transf(ippdrv->dev, config->degree, + config->flip, &swap); + if (ret) { + DRM_ERROR("not support tranf.\n"); + return -EINVAL; + } + } + + /* set size */ + if (ops->set_size) { + ret = ops->set_size(ippdrv->dev, swap, &config->pos, + &config->sz); + if (ret) { + DRM_ERROR("not support size.\n"); + return ret; + } + } + } + + return 0; +} + +static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv, + struct drm_exynos_ipp_cmd_node *c_node) +{ + struct drm_exynos_ipp_mem_node *m_node; + struct drm_exynos_ipp_property *property = &c_node->property; + struct list_head *head; + int ret, i; + + DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, property->prop_id); + + /* store command info in ippdrv */ + ippdrv->cmd = c_node; + + if (!ipp_check_mem_list(c_node)) { + DRM_DEBUG_KMS("%s:empty memory.\n", __func__); + return -ENOMEM; + } + + /* set current property in ippdrv */ + ret = ipp_set_property(ippdrv, property); + if (ret) { + DRM_ERROR("failed to set property.\n"); + ippdrv->cmd = NULL; + return ret; + } + + /* check command */ + switch (property->cmd) { + case IPP_CMD_M2M: + for_each_ipp_ops(i) { + /* source/destination memory list */ + head = &c_node->mem_list[i]; + + m_node = list_first_entry(head, + struct drm_exynos_ipp_mem_node, list); + if (!m_node) { + DRM_ERROR("failed to get node.\n"); + ret = -EFAULT; + return ret; + } + + DRM_DEBUG_KMS("%s:m_node[0x%x]\n", + __func__, (int)m_node); + + ret = ipp_set_mem_node(ippdrv, c_node, m_node); + if (ret) { + DRM_ERROR("failed to set m node.\n"); + return ret; + } + } + break; + case IPP_CMD_WB: + /* destination memory list */ + head = &c_node->mem_list[EXYNOS_DRM_OPS_DST]; + + list_for_each_entry(m_node, head, list) { + ret = ipp_set_mem_node(ippdrv, c_node, m_node); + if (ret) { + DRM_ERROR("failed to set m node.\n"); + return ret; + } + } + break; + case IPP_CMD_OUTPUT: + /* source memory list */ + head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC]; + + list_for_each_entry(m_node, head, list) { + ret = ipp_set_mem_node(ippdrv, c_node, m_node); + if (ret) { + DRM_ERROR("failed to set m node.\n"); + return ret; + } + } + break; + default: + DRM_ERROR("invalid operations.\n"); + return -EINVAL; + } + + DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, property->cmd); + + /* start operations */ + if (ippdrv->start) { + ret = ippdrv->start(ippdrv->dev, property->cmd); + if (ret) { + DRM_ERROR("failed to start ops.\n"); + return ret; + } + } + + return 0; +} + +static int ipp_stop_property(struct drm_device *drm_dev, + struct exynos_drm_ippdrv *ippdrv, + struct drm_exynos_ipp_cmd_node *c_node) +{ + struct drm_exynos_ipp_mem_node *m_node, *tm_node; + struct drm_exynos_ipp_property *property = &c_node->property; + struct list_head *head; + int ret = 0, i; + + DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, property->prop_id); + + /* put event */ + ipp_put_event(c_node, NULL); + + /* check command */ + switch (property->cmd) { + case IPP_CMD_M2M: + for_each_ipp_ops(i) { + /* source/destination memory list */ + head = &c_node->mem_list[i]; + + if (list_empty(head)) { + DRM_DEBUG_KMS("%s:mem_list is empty.\n", + __func__); + break; + } + + list_for_each_entry_safe(m_node, tm_node, + head, list) { + ret = ipp_put_mem_node(drm_dev, c_node, + m_node); + if (ret) { + DRM_ERROR("failed to put m_node.\n"); + goto err_clear; + } + } + } + break; + case IPP_CMD_WB: + /* destination memory list */ + head = &c_node->mem_list[EXYNOS_DRM_OPS_DST]; + + if (list_empty(head)) { + DRM_DEBUG_KMS("%s:mem_list is empty.\n", __func__); + break; + } + + list_for_each_entry_safe(m_node, tm_node, head, list) { + ret = ipp_put_mem_node(drm_dev, c_node, m_node); + if (ret) { + DRM_ERROR("failed to put m_node.\n"); + goto err_clear; + } + } + break; + case IPP_CMD_OUTPUT: + /* source memory list */ + head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC]; + + if (list_empty(head)) { + DRM_DEBUG_KMS("%s:mem_list is empty.\n", __func__); + break; + } + + list_for_each_entry_safe(m_node, tm_node, head, list) { + ret = ipp_put_mem_node(drm_dev, c_node, m_node); + if (ret) { + DRM_ERROR("failed to put m_node.\n"); + goto err_clear; + } + } + break; + default: + DRM_ERROR("invalid operations.\n"); + ret = -EINVAL; + goto err_clear; + } + +err_clear: + /* stop operations */ + if (ippdrv->stop) + ippdrv->stop(ippdrv->dev, property->cmd); + + return ret; +} + +void ipp_sched_cmd(struct work_struct *work) +{ + struct drm_exynos_ipp_cmd_work *cmd_work = + (struct drm_exynos_ipp_cmd_work *)work; + struct exynos_drm_ippdrv *ippdrv; + struct drm_exynos_ipp_cmd_node *c_node; + struct drm_exynos_ipp_property *property; + int ret; + + DRM_DEBUG_KMS("%s\n", __func__); + + ippdrv = cmd_work->ippdrv; + if (!ippdrv) { + DRM_ERROR("invalid ippdrv list.\n"); + return; + } + + c_node = cmd_work->c_node; + if (!c_node) { + DRM_ERROR("invalid command node list.\n"); + return; + } + + mutex_lock(&c_node->cmd_lock); + + property = &c_node->property; + if (!property) { + DRM_ERROR("failed to get property:prop_id[%d]\n", + c_node->property.prop_id); + goto err_unlock; + } + + switch (cmd_work->ctrl) { + case IPP_CTRL_PLAY: + case IPP_CTRL_RESUME: + ret = ipp_start_property(ippdrv, c_node); + if (ret) { + DRM_ERROR("failed to start property:prop_id[%d]\n", + c_node->property.prop_id); + goto err_unlock; + } + + /* + * M2M case supports wait_completion of transfer. + * because M2M case supports single unit operation + * with multiple queue. + * M2M need to wait completion of data transfer. + */ + if (ipp_is_m2m_cmd(property->cmd)) { + if (!wait_for_completion_timeout + (&c_node->start_complete, msecs_to_jiffies(200))) { + DRM_ERROR("timeout event:prop_id[%d]\n", + c_node->property.prop_id); + goto err_unlock; + } + } + break; + case IPP_CTRL_STOP: + case IPP_CTRL_PAUSE: + ret = ipp_stop_property(ippdrv->drm_dev, ippdrv, + c_node); + if (ret) { + DRM_ERROR("failed to stop property.\n"); + goto err_unlock; + } + + complete(&c_node->stop_complete); + break; + default: + DRM_ERROR("unknown control type\n"); + break; + } + + DRM_DEBUG_KMS("%s:ctrl[%d] done.\n", __func__, cmd_work->ctrl); + +err_unlock: + mutex_unlock(&c_node->cmd_lock); +} + +static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv, + struct drm_exynos_ipp_cmd_node *c_node, int *buf_id) +{ + struct drm_device *drm_dev = ippdrv->drm_dev; + struct drm_exynos_ipp_property *property = &c_node->property; + struct drm_exynos_ipp_mem_node *m_node; + struct drm_exynos_ipp_queue_buf qbuf; + struct drm_exynos_ipp_send_event *e; + struct list_head *head; + struct timeval now; + unsigned long flags; + u32 tbuf_id[EXYNOS_DRM_OPS_MAX] = {0, }; + int ret, i; + + for_each_ipp_ops(i) + DRM_DEBUG_KMS("%s:%s buf_id[%d]\n", __func__, + i ? "dst" : "src", buf_id[i]); + + if (!drm_dev) { + DRM_ERROR("failed to get drm_dev.\n"); + return -EINVAL; + } + + if (!property) { + DRM_ERROR("failed to get property.\n"); + return -EINVAL; + } + + if (list_empty(&c_node->event_list)) { + DRM_DEBUG_KMS("%s:event list is empty.\n", __func__); + return 0; + } + + if (!ipp_check_mem_list(c_node)) { + DRM_DEBUG_KMS("%s:empty memory.\n", __func__); + return 0; + } + + /* check command */ + switch (property->cmd) { + case IPP_CMD_M2M: + for_each_ipp_ops(i) { + /* source/destination memory list */ + head = &c_node->mem_list[i]; + + m_node = list_first_entry(head, + struct drm_exynos_ipp_mem_node, list); + if (!m_node) { + DRM_ERROR("empty memory node.\n"); + return -ENOMEM; + } + + tbuf_id[i] = m_node->buf_id; + DRM_DEBUG_KMS("%s:%s buf_id[%d]\n", __func__, + i ? "dst" : "src", tbuf_id[i]); + + ret = ipp_put_mem_node(drm_dev, c_node, m_node); + if (ret) + DRM_ERROR("failed to put m_node.\n"); + } + break; + case IPP_CMD_WB: + /* clear buf for finding */ + memset(&qbuf, 0x0, sizeof(qbuf)); + qbuf.ops_id = EXYNOS_DRM_OPS_DST; + qbuf.buf_id = buf_id[EXYNOS_DRM_OPS_DST]; + + /* get memory node entry */ + m_node = ipp_find_mem_node(c_node, &qbuf); + if (!m_node) { + DRM_ERROR("empty memory node.\n"); + return -ENOMEM; + } + + tbuf_id[EXYNOS_DRM_OPS_DST] = m_node->buf_id; + + ret = ipp_put_mem_node(drm_dev, c_node, m_node); + if (ret) + DRM_ERROR("failed to put m_node.\n"); + break; + case IPP_CMD_OUTPUT: + /* source memory list */ + head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC]; + + m_node = list_first_entry(head, + struct drm_exynos_ipp_mem_node, list); + if (!m_node) { + DRM_ERROR("empty memory node.\n"); + return -ENOMEM; + } + + tbuf_id[EXYNOS_DRM_OPS_SRC] = m_node->buf_id; + + ret = ipp_put_mem_node(drm_dev, c_node, m_node); + if (ret) + DRM_ERROR("failed to put m_node.\n"); + break; + default: + DRM_ERROR("invalid operations.\n"); + return -EINVAL; + } + + if (tbuf_id[EXYNOS_DRM_OPS_DST] != buf_id[EXYNOS_DRM_OPS_DST]) + DRM_ERROR("failed to match buf_id[%d %d]prop_id[%d]\n", + tbuf_id[1], buf_id[1], property->prop_id); + + /* + * command node have event list of destination buffer + * If destination buffer enqueue to mem list, + * then we make event and link to event list tail. + * so, we get first event for first enqueued buffer. + */ + e = list_first_entry(&c_node->event_list, + struct drm_exynos_ipp_send_event, base.link); + + if (!e) { + DRM_ERROR("empty event.\n"); + return -EINVAL; + } + + do_gettimeofday(&now); + DRM_DEBUG_KMS("%s:tv_sec[%ld]tv_usec[%ld]\n" + , __func__, now.tv_sec, now.tv_usec); + e->event.tv_sec = now.tv_sec; + e->event.tv_usec = now.tv_usec; + e->event.prop_id = property->prop_id; + + /* set buffer id about source destination */ + for_each_ipp_ops(i) + e->event.buf_id[i] = tbuf_id[i]; + + spin_lock_irqsave(&drm_dev->event_lock, flags); + list_move_tail(&e->base.link, &e->base.file_priv->event_list); + wake_up_interruptible(&e->base.file_priv->event_wait); + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + + DRM_DEBUG_KMS("%s:done cmd[%d]prop_id[%d]buf_id[%d]\n", __func__, + property->cmd, property->prop_id, tbuf_id[EXYNOS_DRM_OPS_DST]); + + return 0; +} + +void ipp_sched_event(struct work_struct *work) +{ + struct drm_exynos_ipp_event_work *event_work = + (struct drm_exynos_ipp_event_work *)work; + struct exynos_drm_ippdrv *ippdrv; + struct drm_exynos_ipp_cmd_node *c_node; + int ret; + + if (!event_work) { + DRM_ERROR("failed to get event_work.\n"); + return; + } + + DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, + event_work->buf_id[EXYNOS_DRM_OPS_DST]); + + ippdrv = event_work->ippdrv; + if (!ippdrv) { + DRM_ERROR("failed to get ipp driver.\n"); + return; + } + + c_node = ippdrv->cmd; + if (!c_node) { + DRM_ERROR("failed to get command node.\n"); + return; + } + + /* + * IPP supports command thread, event thread synchronization. + * If IPP close immediately from user land, then IPP make + * synchronization with command thread, so make complete event. + * or going out operations. + */ + if (c_node->state != IPP_STATE_START) { + DRM_DEBUG_KMS("%s:bypass state[%d]prop_id[%d]\n", + __func__, c_node->state, c_node->property.prop_id); + goto err_completion; + } + + mutex_lock(&c_node->event_lock); + + ret = ipp_send_event(ippdrv, c_node, event_work->buf_id); + if (ret) { + DRM_ERROR("failed to send event.\n"); + goto err_completion; + } + +err_completion: + if (ipp_is_m2m_cmd(c_node->property.cmd)) + complete(&c_node->start_complete); + + mutex_unlock(&c_node->event_lock); +} + +static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev) +{ + struct ipp_context *ctx = get_ipp_context(dev); + struct exynos_drm_ippdrv *ippdrv; + int ret, count = 0; + + DRM_DEBUG_KMS("%s\n", __func__); + + /* get ipp driver entry */ + list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { + ippdrv->drm_dev = drm_dev; + + ret = ipp_create_id(&ctx->ipp_idr, &ctx->ipp_lock, ippdrv, + &ippdrv->ipp_id); + if (ret) { + DRM_ERROR("failed to create id.\n"); + goto err_idr; + } + + DRM_DEBUG_KMS("%s:count[%d]ippdrv[0x%x]ipp_id[%d]\n", __func__, + count++, (int)ippdrv, ippdrv->ipp_id); + + if (ippdrv->ipp_id == 0) { + DRM_ERROR("failed to get ipp_id[%d]\n", + ippdrv->ipp_id); + goto err_idr; + } + + /* store parent device for node */ + ippdrv->parent_dev = dev; + + /* store event work queue and handler */ + ippdrv->event_workq = ctx->event_workq; + ippdrv->sched_event = ipp_sched_event; + INIT_LIST_HEAD(&ippdrv->cmd_list); + } + + return 0; + +err_idr: + idr_remove_all(&ctx->ipp_idr); + idr_remove_all(&ctx->prop_idr); + idr_destroy(&ctx->ipp_idr); + idr_destroy(&ctx->prop_idr); + return ret; +} + +static void ipp_subdrv_remove(struct drm_device *drm_dev, struct device *dev) +{ + struct exynos_drm_ippdrv *ippdrv; + + DRM_DEBUG_KMS("%s\n", __func__); + + /* get ipp driver entry */ + list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { + ippdrv->drm_dev = NULL; + exynos_drm_ippdrv_unregister(ippdrv); + } +} + +static int ipp_subdrv_open(struct drm_device *drm_dev, struct device *dev, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_ipp_private *priv; + + DRM_DEBUG_KMS("%s\n", __func__); + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + DRM_ERROR("failed to allocate priv.\n"); + return -ENOMEM; + } + priv->dev = dev; + file_priv->ipp_priv = priv; + + INIT_LIST_HEAD(&priv->event_list); + + DRM_DEBUG_KMS("%s:done priv[0x%x]\n", __func__, (int)priv); + + return 0; +} + +static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_ipp_private *priv = file_priv->ipp_priv; + struct exynos_drm_ippdrv *ippdrv = NULL; + struct drm_exynos_ipp_cmd_node *c_node, *tc_node; + int count = 0; + + DRM_DEBUG_KMS("%s:for priv[0x%x]\n", __func__, (int)priv); + + if (list_empty(&exynos_drm_ippdrv_list)) { + DRM_DEBUG_KMS("%s:ippdrv_list is empty.\n", __func__); + goto err_clear; + } + + list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { + if (list_empty(&ippdrv->cmd_list)) + continue; + + list_for_each_entry_safe(c_node, tc_node, + &ippdrv->cmd_list, list) { + DRM_DEBUG_KMS("%s:count[%d]ippdrv[0x%x]\n", + __func__, count++, (int)ippdrv); + + if (c_node->priv == priv) { + /* + * userland goto unnormal state. process killed. + * and close the file. + * so, IPP didn't called stop cmd ctrl. + * so, we are make stop operation in this state. + */ + if (c_node->state == IPP_STATE_START) { + ipp_stop_property(drm_dev, ippdrv, + c_node); + c_node->state = IPP_STATE_STOP; + } + + ippdrv->dedicated = false; + ipp_clean_cmd_node(c_node); + if (list_empty(&ippdrv->cmd_list)) + pm_runtime_put_sync(ippdrv->dev); + } + } + } + +err_clear: + kfree(priv); + return; +} + +static int __devinit ipp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ipp_context *ctx; + struct exynos_drm_subdrv *subdrv; + int ret; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + DRM_DEBUG_KMS("%s\n", __func__); + + mutex_init(&ctx->ipp_lock); + mutex_init(&ctx->prop_lock); + + idr_init(&ctx->ipp_idr); + idr_init(&ctx->prop_idr); + + /* + * create single thread for ipp event + * IPP supports event thread for IPP drivers. + * IPP driver send event_work to this thread. + * and IPP event thread send event to user process. + */ + ctx->event_workq = create_singlethread_workqueue("ipp_event"); + if (!ctx->event_workq) { + dev_err(dev, "failed to create event workqueue\n"); + ret = -EINVAL; + goto err_clear; + } + + /* + * create single thread for ipp command + * IPP supports command thread for user process. + * user process make command node using set property ioctl. + * and make start_work and send this work to command thread. + * and then this command thread start property. + */ + ctx->cmd_workq = create_singlethread_workqueue("ipp_cmd"); + if (!ctx->cmd_workq) { + dev_err(dev, "failed to create cmd workqueue\n"); + ret = -EINVAL; + goto err_event_workq; + } + + /* set sub driver informations */ + subdrv = &ctx->subdrv; + subdrv->dev = dev; + subdrv->probe = ipp_subdrv_probe; + subdrv->remove = ipp_subdrv_remove; + subdrv->open = ipp_subdrv_open; + subdrv->close = ipp_subdrv_close; + + platform_set_drvdata(pdev, ctx); + + ret = exynos_drm_subdrv_register(subdrv); + if (ret < 0) { + DRM_ERROR("failed to register drm ipp device.\n"); + goto err_cmd_workq; + } + + dev_info(&pdev->dev, "drm ipp registered successfully.\n"); + + return 0; + +err_cmd_workq: + destroy_workqueue(ctx->cmd_workq); +err_event_workq: + destroy_workqueue(ctx->event_workq); +err_clear: + kfree(ctx); + return ret; +} + +static int __devexit ipp_remove(struct platform_device *pdev) +{ + struct ipp_context *ctx = platform_get_drvdata(pdev); + + DRM_DEBUG_KMS("%s\n", __func__); + + /* unregister sub driver */ + exynos_drm_subdrv_unregister(&ctx->subdrv); + + /* remove,destroy ipp idr */ + idr_remove_all(&ctx->ipp_idr); + idr_remove_all(&ctx->prop_idr); + idr_destroy(&ctx->ipp_idr); + idr_destroy(&ctx->prop_idr); + + mutex_destroy(&ctx->ipp_lock); + mutex_destroy(&ctx->prop_lock); + + /* destroy command, event work queue */ + destroy_workqueue(ctx->cmd_workq); + destroy_workqueue(ctx->event_workq); + + kfree(ctx); + + return 0; +} + +static int ipp_power_ctrl(struct ipp_context *ctx, bool enable) +{ + DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int ipp_suspend(struct device *dev) +{ + struct ipp_context *ctx = get_ipp_context(dev); + + DRM_DEBUG_KMS("%s\n", __func__); + + if (pm_runtime_suspended(dev)) + return 0; + + return ipp_power_ctrl(ctx, false); +} + +static int ipp_resume(struct device *dev) +{ + struct ipp_context *ctx = get_ipp_context(dev); + + DRM_DEBUG_KMS("%s\n", __func__); + + if (!pm_runtime_suspended(dev)) + return ipp_power_ctrl(ctx, true); + + return 0; +} +#endif + +#ifdef CONFIG_PM_RUNTIME +static int ipp_runtime_suspend(struct device *dev) +{ + struct ipp_context *ctx = get_ipp_context(dev); + + DRM_DEBUG_KMS("%s\n", __func__); + + return ipp_power_ctrl(ctx, false); +} + +static int ipp_runtime_resume(struct device *dev) +{ + struct ipp_context *ctx = get_ipp_context(dev); + + DRM_DEBUG_KMS("%s\n", __func__); + + return ipp_power_ctrl(ctx, true); +} +#endif + +static const struct dev_pm_ops ipp_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ipp_suspend, ipp_resume) + SET_RUNTIME_PM_OPS(ipp_runtime_suspend, ipp_runtime_resume, NULL) +}; + +struct platform_driver ipp_driver = { + .probe = ipp_probe, + .remove = __devexit_p(ipp_remove), + .driver = { + .name = "exynos-drm-ipp", + .owner = THIS_MODULE, + .pm = &ipp_pm_ops, + }, +}; + diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.h b/drivers/gpu/drm/exynos/exynos_drm_ipp.h new file mode 100644 index 000000000000..28ffac95386c --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.h @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * + * Authors: + * Eunchul Kim + * Jinyoung Jeon + * Sangmin Lee + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _EXYNOS_DRM_IPP_H_ +#define _EXYNOS_DRM_IPP_H_ + +#define for_each_ipp_ops(pos) \ + for (pos = 0; pos < EXYNOS_DRM_OPS_MAX; pos++) +#define for_each_ipp_planar(pos) \ + for (pos = 0; pos < EXYNOS_DRM_PLANAR_MAX; pos++) + +#define IPP_GET_LCD_WIDTH _IOR('F', 302, int) +#define IPP_GET_LCD_HEIGHT _IOR('F', 303, int) +#define IPP_SET_WRITEBACK _IOW('F', 304, u32) + +/* definition of state */ +enum drm_exynos_ipp_state { + IPP_STATE_IDLE, + IPP_STATE_START, + IPP_STATE_STOP, +}; + +/* + * A structure of command work information. + * @work: work structure. + * @ippdrv: current work ippdrv. + * @c_node: command node information. + * @ctrl: command control. + */ +struct drm_exynos_ipp_cmd_work { + struct work_struct work; + struct exynos_drm_ippdrv *ippdrv; + struct drm_exynos_ipp_cmd_node *c_node; + enum drm_exynos_ipp_ctrl ctrl; +}; + +/* + * A structure of command node. + * + * @priv: IPP private infomation. + * @list: list head to command queue information. + * @event_list: list head of event. + * @mem_list: list head to source,destination memory queue information. + * @cmd_lock: lock for synchronization of access to ioctl. + * @mem_lock: lock for synchronization of access to memory nodes. + * @event_lock: lock for synchronization of access to scheduled event. + * @start_complete: completion of start of command. + * @stop_complete: completion of stop of command. + * @property: property information. + * @start_work: start command work structure. + * @stop_work: stop command work structure. + * @event_work: event work structure. + * @state: state of command node. + */ +struct drm_exynos_ipp_cmd_node { + struct exynos_drm_ipp_private *priv; + struct list_head list; + struct list_head event_list; + struct list_head mem_list[EXYNOS_DRM_OPS_MAX]; + struct mutex cmd_lock; + struct mutex mem_lock; + struct mutex event_lock; + struct completion start_complete; + struct completion stop_complete; + struct drm_exynos_ipp_property property; + struct drm_exynos_ipp_cmd_work *start_work; + struct drm_exynos_ipp_cmd_work *stop_work; + struct drm_exynos_ipp_event_work *event_work; + enum drm_exynos_ipp_state state; +}; + +/* + * A structure of buffer information. + * + * @gem_objs: Y, Cb, Cr each gem object. + * @base: Y, Cb, Cr each planar address. + */ +struct drm_exynos_ipp_buf_info { + unsigned long handles[EXYNOS_DRM_PLANAR_MAX]; + dma_addr_t base[EXYNOS_DRM_PLANAR_MAX]; +}; + +/* + * A structure of wb setting infomation. + * + * @enable: enable flag for wb. + * @refresh: HZ of the refresh rate. + */ +struct drm_exynos_ipp_set_wb { + __u32 enable; + __u32 refresh; +}; + +/* + * A structure of event work information. + * + * @work: work structure. + * @ippdrv: current work ippdrv. + * @buf_id: id of src, dst buffer. + */ +struct drm_exynos_ipp_event_work { + struct work_struct work; + struct exynos_drm_ippdrv *ippdrv; + u32 buf_id[EXYNOS_DRM_OPS_MAX]; +}; + +/* + * A structure of source,destination operations. + * + * @set_fmt: set format of image. + * @set_transf: set transform(rotations, flip). + * @set_size: set size of region. + * @set_addr: set address for dma. + */ +struct exynos_drm_ipp_ops { + int (*set_fmt)(struct device *dev, u32 fmt); + int (*set_transf)(struct device *dev, + enum drm_exynos_degree degree, + enum drm_exynos_flip flip, bool *swap); + int (*set_size)(struct device *dev, int swap, + struct drm_exynos_pos *pos, struct drm_exynos_sz *sz); + int (*set_addr)(struct device *dev, + struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, + enum drm_exynos_ipp_buf_type buf_type); +}; + +/* + * A structure of ipp driver. + * + * @drv_list: list head for registed sub driver information. + * @parent_dev: parent device information. + * @dev: platform device. + * @drm_dev: drm device. + * @ipp_id: id of ipp driver. + * @dedicated: dedicated ipp device. + * @ops: source, destination operations. + * @event_workq: event work queue. + * @cmd: current command information. + * @cmd_list: list head for command information. + * @prop_list: property informations of current ipp driver. + * @check_property: check property about format, size, buffer. + * @reset: reset ipp block. + * @start: ipp each device start. + * @stop: ipp each device stop. + * @sched_event: work schedule handler. + */ +struct exynos_drm_ippdrv { + struct list_head drv_list; + struct device *parent_dev; + struct device *dev; + struct drm_device *drm_dev; + u32 ipp_id; + bool dedicated; + struct exynos_drm_ipp_ops *ops[EXYNOS_DRM_OPS_MAX]; + struct workqueue_struct *event_workq; + struct drm_exynos_ipp_cmd_node *cmd; + struct list_head cmd_list; + struct drm_exynos_ipp_prop_list *prop_list; + + int (*check_property)(struct device *dev, + struct drm_exynos_ipp_property *property); + int (*reset)(struct device *dev); + int (*start)(struct device *dev, enum drm_exynos_ipp_cmd cmd); + void (*stop)(struct device *dev, enum drm_exynos_ipp_cmd cmd); + void (*sched_event)(struct work_struct *work); +}; + +#ifdef CONFIG_DRM_EXYNOS_IPP +extern int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv); +extern int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv); +extern int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data, + struct drm_file *file); +extern int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, + struct drm_file *file); +extern int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data, + struct drm_file *file); +extern int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data, + struct drm_file *file); +extern int exynos_drm_ippnb_register(struct notifier_block *nb); +extern int exynos_drm_ippnb_unregister(struct notifier_block *nb); +extern int exynos_drm_ippnb_send_event(unsigned long val, void *v); +extern void ipp_sched_cmd(struct work_struct *work); +extern void ipp_sched_event(struct work_struct *work); + +#else +static inline int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv) +{ + return -ENODEV; +} + +static inline int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv) +{ + return -ENODEV; +} + +static inline int exynos_drm_ipp_get_property(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + return -ENOTTY; +} + +static inline int exynos_drm_ipp_set_property(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + return -ENOTTY; +} + +static inline int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, + void *data, + struct drm_file *file) +{ + return -ENOTTY; +} + +static inline int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, + void *data, + struct drm_file *file) +{ + return -ENOTTY; +} + +static inline int exynos_drm_ippnb_register(struct notifier_block *nb) +{ + return -ENODEV; +} + +static inline int exynos_drm_ippnb_unregister(struct notifier_block *nb) +{ + return -ENODEV; +} + +static inline int exynos_drm_ippnb_send_event(unsigned long val, void *v) +{ + return -ENOTTY; +} +#endif + +#endif /* _EXYNOS_DRM_IPP_H_ */ + diff --git a/include/uapi/drm/exynos_drm.h b/include/uapi/drm/exynos_drm.h index 49f010f2b27f..e7f52c334005 100644 --- a/include/uapi/drm/exynos_drm.h +++ b/include/uapi/drm/exynos_drm.h @@ -163,6 +163,170 @@ struct drm_exynos_g2d_exec { __u64 async; }; +enum drm_exynos_ops_id { + EXYNOS_DRM_OPS_SRC, + EXYNOS_DRM_OPS_DST, + EXYNOS_DRM_OPS_MAX, +}; + +struct drm_exynos_sz { + __u32 hsize; + __u32 vsize; +}; + +struct drm_exynos_pos { + __u32 x; + __u32 y; + __u32 w; + __u32 h; +}; + +enum drm_exynos_flip { + EXYNOS_DRM_FLIP_NONE = (0 << 0), + EXYNOS_DRM_FLIP_VERTICAL = (1 << 0), + EXYNOS_DRM_FLIP_HORIZONTAL = (1 << 1), +}; + +enum drm_exynos_degree { + EXYNOS_DRM_DEGREE_0, + EXYNOS_DRM_DEGREE_90, + EXYNOS_DRM_DEGREE_180, + EXYNOS_DRM_DEGREE_270, +}; + +enum drm_exynos_planer { + EXYNOS_DRM_PLANAR_Y, + EXYNOS_DRM_PLANAR_CB, + EXYNOS_DRM_PLANAR_CR, + EXYNOS_DRM_PLANAR_MAX, +}; + +/** + * A structure for ipp supported property list. + * + * @version: version of this structure. + * @ipp_id: id of ipp driver. + * @count: count of ipp driver. + * @writeback: flag of writeback supporting. + * @flip: flag of flip supporting. + * @degree: flag of degree information. + * @csc: flag of csc supporting. + * @crop: flag of crop supporting. + * @scale: flag of scale supporting. + * @refresh_min: min hz of refresh. + * @refresh_max: max hz of refresh. + * @crop_min: crop min resolution. + * @crop_max: crop max resolution. + * @scale_min: scale min resolution. + * @scale_max: scale max resolution. + */ +struct drm_exynos_ipp_prop_list { + __u32 version; + __u32 ipp_id; + __u32 count; + __u32 writeback; + __u32 flip; + __u32 degree; + __u32 csc; + __u32 crop; + __u32 scale; + __u32 refresh_min; + __u32 refresh_max; + __u32 reserved; + struct drm_exynos_sz crop_min; + struct drm_exynos_sz crop_max; + struct drm_exynos_sz scale_min; + struct drm_exynos_sz scale_max; +}; + +/** + * A structure for ipp config. + * + * @ops_id: property of operation directions. + * @flip: property of mirror, flip. + * @degree: property of rotation degree. + * @fmt: property of image format. + * @sz: property of image size. + * @pos: property of image position(src-cropped,dst-scaler). + */ +struct drm_exynos_ipp_config { + enum drm_exynos_ops_id ops_id; + enum drm_exynos_flip flip; + enum drm_exynos_degree degree; + __u32 fmt; + struct drm_exynos_sz sz; + struct drm_exynos_pos pos; +}; + +enum drm_exynos_ipp_cmd { + IPP_CMD_NONE, + IPP_CMD_M2M, + IPP_CMD_WB, + IPP_CMD_OUTPUT, + IPP_CMD_MAX, +}; + +/** + * A structure for ipp property. + * + * @config: source, destination config. + * @cmd: definition of command. + * @ipp_id: id of ipp driver. + * @prop_id: id of property. + * @refresh_rate: refresh rate. + */ +struct drm_exynos_ipp_property { + struct drm_exynos_ipp_config config[EXYNOS_DRM_OPS_MAX]; + enum drm_exynos_ipp_cmd cmd; + __u32 ipp_id; + __u32 prop_id; + __u32 refresh_rate; +}; + +enum drm_exynos_ipp_buf_type { + IPP_BUF_ENQUEUE, + IPP_BUF_DEQUEUE, +}; + +/** + * A structure for ipp buffer operations. + * + * @ops_id: operation directions. + * @buf_type: definition of buffer. + * @prop_id: id of property. + * @buf_id: id of buffer. + * @handle: Y, Cb, Cr each planar handle. + * @user_data: user data. + */ +struct drm_exynos_ipp_queue_buf { + enum drm_exynos_ops_id ops_id; + enum drm_exynos_ipp_buf_type buf_type; + __u32 prop_id; + __u32 buf_id; + __u32 handle[EXYNOS_DRM_PLANAR_MAX]; + __u32 reserved; + __u64 user_data; +}; + +enum drm_exynos_ipp_ctrl { + IPP_CTRL_PLAY, + IPP_CTRL_STOP, + IPP_CTRL_PAUSE, + IPP_CTRL_RESUME, + IPP_CTRL_MAX, +}; + +/** + * A structure for ipp start/stop operations. + * + * @prop_id: id of property. + * @ctrl: definition of control. + */ +struct drm_exynos_ipp_cmd_ctrl { + __u32 prop_id; + enum drm_exynos_ipp_ctrl ctrl; +}; + #define DRM_EXYNOS_GEM_CREATE 0x00 #define DRM_EXYNOS_GEM_MAP_OFFSET 0x01 #define DRM_EXYNOS_GEM_MMAP 0x02 @@ -175,6 +339,12 @@ struct drm_exynos_g2d_exec { #define DRM_EXYNOS_G2D_SET_CMDLIST 0x21 #define DRM_EXYNOS_G2D_EXEC 0x22 +/* IPP - Image Post Processing */ +#define DRM_EXYNOS_IPP_GET_PROPERTY 0x30 +#define DRM_EXYNOS_IPP_SET_PROPERTY 0x31 +#define DRM_EXYNOS_IPP_QUEUE_BUF 0x32 +#define DRM_EXYNOS_IPP_CMD_CTRL 0x33 + #define DRM_IOCTL_EXYNOS_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \ DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create) @@ -197,8 +367,18 @@ struct drm_exynos_g2d_exec { #define DRM_IOCTL_EXYNOS_G2D_EXEC DRM_IOWR(DRM_COMMAND_BASE + \ DRM_EXYNOS_G2D_EXEC, struct drm_exynos_g2d_exec) +#define DRM_IOCTL_EXYNOS_IPP_GET_PROPERTY DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_EXYNOS_IPP_GET_PROPERTY, struct drm_exynos_ipp_prop_list) +#define DRM_IOCTL_EXYNOS_IPP_SET_PROPERTY DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_EXYNOS_IPP_SET_PROPERTY, struct drm_exynos_ipp_property) +#define DRM_IOCTL_EXYNOS_IPP_QUEUE_BUF DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_EXYNOS_IPP_QUEUE_BUF, struct drm_exynos_ipp_queue_buf) +#define DRM_IOCTL_EXYNOS_IPP_CMD_CTRL DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_EXYNOS_IPP_CMD_CTRL, struct drm_exynos_ipp_cmd_ctrl) + /* EXYNOS specific events */ #define DRM_EXYNOS_G2D_EVENT 0x80000000 +#define DRM_EXYNOS_IPP_EVENT 0x80000001 struct drm_exynos_g2d_event { struct drm_event base; @@ -209,4 +389,14 @@ struct drm_exynos_g2d_event { __u32 reserved; }; +struct drm_exynos_ipp_event { + struct drm_event base; + __u64 user_data; + __u32 tv_sec; + __u32 tv_usec; + __u32 prop_id; + __u32 reserved; + __u32 buf_id[EXYNOS_DRM_OPS_MAX]; +}; + #endif /* _UAPI_EXYNOS_DRM_H_ */ -- cgit v1.2.3-59-g8ed1b From 16102edb49b6cc7fbb68b10c04a42b78fbceb3ed Mon Sep 17 00:00:00 2001 From: Eunchul Kim Date: Fri, 14 Dec 2012 17:58:55 +0900 Subject: drm/exynos: add fimc ipp driver FIMC is stand for Fully Interfactive Mobile Camera and supports image scaler/rotator/crop/flip/csc and input/output DMA operations and also supports writeback and display output operations. This driver is registered to IPP subsystem framework to be used by user side and user can control the FIMC hardware through some interfaces of IPP subsystem framework. Changelog v6: - fix build warning. Changelog v1 ~ v5: - add comments, code fixups and cleanups. Signed-off-by: Eunchul Kim Signed-off-by: Jinyoung Jeon Signed-off-by: Inki Dae Signed-off-by: Kyungmin Park --- drivers/gpu/drm/exynos/Kconfig | 6 + drivers/gpu/drm/exynos/Makefile | 1 + drivers/gpu/drm/exynos/exynos_drm_drv.c | 15 + drivers/gpu/drm/exynos/exynos_drm_drv.h | 1 + drivers/gpu/drm/exynos/exynos_drm_fimc.c | 2001 ++++++++++++++++++++++++++++++ drivers/gpu/drm/exynos/exynos_drm_fimc.h | 37 + drivers/gpu/drm/exynos/regs-fimc.h | 669 ++++++++++ include/drm/exynos_drm.h | 26 + 8 files changed, 2756 insertions(+) create mode 100644 drivers/gpu/drm/exynos/exynos_drm_fimc.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_fimc.h create mode 100644 drivers/gpu/drm/exynos/regs-fimc.h (limited to 'include') diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 80ab242e2739..66c02df360e7 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -51,3 +51,9 @@ config DRM_EXYNOS_IPP depends on DRM_EXYNOS help Choose this option if you want to use IPP feature for DRM. + +config DRM_EXYNOS_FIMC + bool "Exynos DRM FIMC" + depends on DRM_EXYNOS_IPP + help + Choose this option if you want to use Exynos FIMC for DRM. diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index 6c536ce4d95b..9710024ed000 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile @@ -17,5 +17,6 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o \ exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o +exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC) += exynos_drm_fimc.o obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 0eb8a972e21c..73f02ac53bad 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -372,6 +372,12 @@ static int __init exynos_drm_init(void) goto out_g2d; #endif +#ifdef CONFIG_DRM_EXYNOS_FIMC + ret = platform_driver_register(&fimc_driver); + if (ret < 0) + goto out_fimc; +#endif + #ifdef CONFIG_DRM_EXYNOS_IPP ret = platform_driver_register(&ipp_driver); if (ret < 0) @@ -400,6 +406,11 @@ out_drm: out_ipp: #endif +#ifdef CONFIG_DRM_EXYNOS_FIMC + platform_driver_unregister(&fimc_driver); +out_fimc: +#endif + #ifdef CONFIG_DRM_EXYNOS_G2D platform_driver_unregister(&g2d_driver); out_g2d: @@ -440,6 +451,10 @@ static void __exit exynos_drm_exit(void) platform_driver_unregister(&ipp_driver); #endif +#ifdef CONFIG_DRM_EXYNOS_FIMC + platform_driver_unregister(&fimc_driver); +#endif + #ifdef CONFIG_DRM_EXYNOS_G2D platform_driver_unregister(&g2d_driver); #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 44ab3c7b6a90..63803508e4df 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -349,5 +349,6 @@ extern struct platform_driver mixer_driver; extern struct platform_driver exynos_drm_common_hdmi_driver; extern struct platform_driver vidi_driver; extern struct platform_driver g2d_driver; +extern struct platform_driver fimc_driver; extern struct platform_driver ipp_driver; #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.c b/drivers/gpu/drm/exynos/exynos_drm_fimc.c new file mode 100644 index 000000000000..61ea24296b52 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.c @@ -0,0 +1,2001 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Authors: + * Eunchul Kim + * Jinyoung Jeon + * Sangmin Lee + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include "regs-fimc.h" +#include "exynos_drm_ipp.h" +#include "exynos_drm_fimc.h" + +/* + * FIMC is stand for Fully Interactive Mobile Camera and + * supports image scaler/rotator and input/output DMA operations. + * input DMA reads image data from the memory. + * output DMA writes image data to memory. + * FIMC supports image rotation and image effect functions. + * + * M2M operation : supports crop/scale/rotation/csc so on. + * Memory ----> FIMC H/W ----> Memory. + * Writeback operation : supports cloned screen with FIMD. + * FIMD ----> FIMC H/W ----> Memory. + * Output operation : supports direct display using local path. + * Memory ----> FIMC H/W ----> FIMD. + */ + +/* + * TODO + * 1. check suspend/resume api if needed. + * 2. need to check use case platform_device_id. + * 3. check src/dst size with, height. + * 4. added check_prepare api for right register. + * 5. need to add supported list in prop_list. + * 6. check prescaler/scaler optimization. + */ + +#define FIMC_MAX_DEVS 4 +#define FIMC_MAX_SRC 2 +#define FIMC_MAX_DST 32 +#define FIMC_SHFACTOR 10 +#define FIMC_BUF_STOP 1 +#define FIMC_BUF_START 2 +#define FIMC_REG_SZ 32 +#define FIMC_WIDTH_ITU_709 1280 +#define FIMC_REFRESH_MAX 60 +#define FIMC_REFRESH_MIN 12 +#define FIMC_CROP_MAX 8192 +#define FIMC_CROP_MIN 32 +#define FIMC_SCALE_MAX 4224 +#define FIMC_SCALE_MIN 32 + +#define get_fimc_context(dev) platform_get_drvdata(to_platform_device(dev)) +#define get_ctx_from_ippdrv(ippdrv) container_of(ippdrv,\ + struct fimc_context, ippdrv); +#define fimc_read(offset) readl(ctx->regs + (offset)) +#define fimc_write(cfg, offset) writel(cfg, ctx->regs + (offset)) + +enum fimc_wb { + FIMC_WB_NONE, + FIMC_WB_A, + FIMC_WB_B, +}; + +/* + * A structure of scaler. + * + * @range: narrow, wide. + * @bypass: unused scaler path. + * @up_h: horizontal scale up. + * @up_v: vertical scale up. + * @hratio: horizontal ratio. + * @vratio: vertical ratio. + */ +struct fimc_scaler { + bool range; + bool bypass; + bool up_h; + bool up_v; + u32 hratio; + u32 vratio; +}; + +/* + * A structure of scaler capability. + * + * find user manual table 43-1. + * @in_hori: scaler input horizontal size. + * @bypass: scaler bypass mode. + * @dst_h_wo_rot: target horizontal size without output rotation. + * @dst_h_rot: target horizontal size with output rotation. + * @rl_w_wo_rot: real width without input rotation. + * @rl_h_rot: real height without output rotation. + */ +struct fimc_capability { + /* scaler */ + u32 in_hori; + u32 bypass; + /* output rotator */ + u32 dst_h_wo_rot; + u32 dst_h_rot; + /* input rotator */ + u32 rl_w_wo_rot; + u32 rl_h_rot; +}; + +/* + * A structure of fimc driver data. + * + * @parent_clk: name of parent clock. + */ +struct fimc_driverdata { + char *parent_clk; +}; + +/* + * A structure of fimc context. + * + * @ippdrv: prepare initialization using ippdrv. + * @regs_res: register resources. + * @regs: memory mapped io registers. + * @lock: locking of operations. + * @sclk_fimc_clk: fimc source clock. + * @fimc_clk: fimc clock. + * @wb_clk: writeback a clock. + * @wb_b_clk: writeback b clock. + * @sc: scaler infomations. + * @odr: ordering of YUV. + * @ver: fimc version. + * @pol: porarity of writeback. + * @id: fimc id. + * @irq: irq number. + * @suspended: qos operations. + */ +struct fimc_context { + struct exynos_drm_ippdrv ippdrv; + struct resource *regs_res; + void __iomem *regs; + struct mutex lock; + struct clk *sclk_fimc_clk; + struct clk *fimc_clk; + struct clk *wb_clk; + struct clk *wb_b_clk; + struct fimc_scaler sc; + struct fimc_driverdata *ddata; + struct exynos_drm_ipp_pol pol; + int id; + int irq; + bool suspended; +}; + +static void fimc_sw_reset(struct fimc_context *ctx, bool pattern) +{ + u32 cfg; + + DRM_DEBUG_KMS("%s:pattern[%d]\n", __func__, pattern); + + cfg = fimc_read(EXYNOS_CISRCFMT); + cfg |= EXYNOS_CISRCFMT_ITU601_8BIT; + if (pattern) + cfg |= EXYNOS_CIGCTRL_TESTPATTERN_COLOR_BAR; + + fimc_write(cfg, EXYNOS_CISRCFMT); + + /* s/w reset */ + cfg = fimc_read(EXYNOS_CIGCTRL); + cfg |= (EXYNOS_CIGCTRL_SWRST); + fimc_write(cfg, EXYNOS_CIGCTRL); + + /* s/w reset complete */ + cfg = fimc_read(EXYNOS_CIGCTRL); + cfg &= ~EXYNOS_CIGCTRL_SWRST; + fimc_write(cfg, EXYNOS_CIGCTRL); + + /* reset sequence */ + fimc_write(0x0, EXYNOS_CIFCNTSEQ); +} + +static void fimc_set_camblk_fimd0_wb(struct fimc_context *ctx) +{ + u32 camblk_cfg; + + DRM_DEBUG_KMS("%s\n", __func__); + + camblk_cfg = readl(SYSREG_CAMERA_BLK); + camblk_cfg &= ~(SYSREG_FIMD0WB_DEST_MASK); + camblk_cfg |= ctx->id << (SYSREG_FIMD0WB_DEST_SHIFT); + + writel(camblk_cfg, SYSREG_CAMERA_BLK); +} + +static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb) +{ + u32 cfg; + + DRM_DEBUG_KMS("%s:wb[%d]\n", __func__, wb); + + cfg = fimc_read(EXYNOS_CIGCTRL); + cfg &= ~(EXYNOS_CIGCTRL_TESTPATTERN_MASK | + EXYNOS_CIGCTRL_SELCAM_ITU_MASK | + EXYNOS_CIGCTRL_SELCAM_MIPI_MASK | + EXYNOS_CIGCTRL_SELCAM_FIMC_MASK | + EXYNOS_CIGCTRL_SELWB_CAMIF_MASK | + EXYNOS_CIGCTRL_SELWRITEBACK_MASK); + + switch (wb) { + case FIMC_WB_A: + cfg |= (EXYNOS_CIGCTRL_SELWRITEBACK_A | + EXYNOS_CIGCTRL_SELWB_CAMIF_WRITEBACK); + break; + case FIMC_WB_B: + cfg |= (EXYNOS_CIGCTRL_SELWRITEBACK_B | + EXYNOS_CIGCTRL_SELWB_CAMIF_WRITEBACK); + break; + case FIMC_WB_NONE: + default: + cfg |= (EXYNOS_CIGCTRL_SELCAM_ITU_A | + EXYNOS_CIGCTRL_SELWRITEBACK_A | + EXYNOS_CIGCTRL_SELCAM_MIPI_A | + EXYNOS_CIGCTRL_SELCAM_FIMC_ITU); + break; + } + + fimc_write(cfg, EXYNOS_CIGCTRL); +} + +static void fimc_set_polarity(struct fimc_context *ctx, + struct exynos_drm_ipp_pol *pol) +{ + u32 cfg; + + DRM_DEBUG_KMS("%s:inv_pclk[%d]inv_vsync[%d]\n", + __func__, pol->inv_pclk, pol->inv_vsync); + DRM_DEBUG_KMS("%s:inv_href[%d]inv_hsync[%d]\n", + __func__, pol->inv_href, pol->inv_hsync); + + cfg = fimc_read(EXYNOS_CIGCTRL); + cfg &= ~(EXYNOS_CIGCTRL_INVPOLPCLK | EXYNOS_CIGCTRL_INVPOLVSYNC | + EXYNOS_CIGCTRL_INVPOLHREF | EXYNOS_CIGCTRL_INVPOLHSYNC); + + if (pol->inv_pclk) + cfg |= EXYNOS_CIGCTRL_INVPOLPCLK; + if (pol->inv_vsync) + cfg |= EXYNOS_CIGCTRL_INVPOLVSYNC; + if (pol->inv_href) + cfg |= EXYNOS_CIGCTRL_INVPOLHREF; + if (pol->inv_hsync) + cfg |= EXYNOS_CIGCTRL_INVPOLHSYNC; + + fimc_write(cfg, EXYNOS_CIGCTRL); +} + +static void fimc_handle_jpeg(struct fimc_context *ctx, bool enable) +{ + u32 cfg; + + DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); + + cfg = fimc_read(EXYNOS_CIGCTRL); + if (enable) + cfg |= EXYNOS_CIGCTRL_CAM_JPEG; + else + cfg &= ~EXYNOS_CIGCTRL_CAM_JPEG; + + fimc_write(cfg, EXYNOS_CIGCTRL); +} + +static void fimc_handle_irq(struct fimc_context *ctx, bool enable, + bool overflow, bool level) +{ + u32 cfg; + + DRM_DEBUG_KMS("%s:enable[%d]overflow[%d]level[%d]\n", __func__, + enable, overflow, level); + + cfg = fimc_read(EXYNOS_CIGCTRL); + if (enable) { + cfg &= ~(EXYNOS_CIGCTRL_IRQ_OVFEN | EXYNOS_CIGCTRL_IRQ_LEVEL); + cfg |= EXYNOS_CIGCTRL_IRQ_ENABLE; + if (overflow) + cfg |= EXYNOS_CIGCTRL_IRQ_OVFEN; + if (level) + cfg |= EXYNOS_CIGCTRL_IRQ_LEVEL; + } else + cfg &= ~(EXYNOS_CIGCTRL_IRQ_OVFEN | EXYNOS_CIGCTRL_IRQ_ENABLE); + + fimc_write(cfg, EXYNOS_CIGCTRL); +} + +static void fimc_clear_irq(struct fimc_context *ctx) +{ + u32 cfg; + + DRM_DEBUG_KMS("%s\n", __func__); + + cfg = fimc_read(EXYNOS_CIGCTRL); + cfg |= EXYNOS_CIGCTRL_IRQ_CLR; + fimc_write(cfg, EXYNOS_CIGCTRL); +} + +static bool fimc_check_ovf(struct fimc_context *ctx) +{ + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg, status, flag; + + status = fimc_read(EXYNOS_CISTATUS); + flag = EXYNOS_CISTATUS_OVFIY | EXYNOS_CISTATUS_OVFICB | + EXYNOS_CISTATUS_OVFICR; + + DRM_DEBUG_KMS("%s:flag[0x%x]\n", __func__, flag); + + if (status & flag) { + cfg = fimc_read(EXYNOS_CIWDOFST); + cfg |= (EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB | + EXYNOS_CIWDOFST_CLROVFICR); + + fimc_write(cfg, EXYNOS_CIWDOFST); + + cfg = fimc_read(EXYNOS_CIWDOFST); + cfg &= ~(EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB | + EXYNOS_CIWDOFST_CLROVFICR); + + fimc_write(cfg, EXYNOS_CIWDOFST); + + dev_err(ippdrv->dev, "occured overflow at %d, status 0x%x.\n", + ctx->id, status); + return true; + } + + return false; +} + +static bool fimc_check_frame_end(struct fimc_context *ctx) +{ + u32 cfg; + + cfg = fimc_read(EXYNOS_CISTATUS); + + DRM_DEBUG_KMS("%s:cfg[0x%x]\n", __func__, cfg); + + if (!(cfg & EXYNOS_CISTATUS_FRAMEEND)) + return false; + + cfg &= ~(EXYNOS_CISTATUS_FRAMEEND); + fimc_write(cfg, EXYNOS_CISTATUS); + + return true; +} + +static int fimc_get_buf_id(struct fimc_context *ctx) +{ + u32 cfg; + int frame_cnt, buf_id; + + DRM_DEBUG_KMS("%s\n", __func__); + + cfg = fimc_read(EXYNOS_CISTATUS2); + frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(cfg); + + if (frame_cnt == 0) + frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_PRESENT(cfg); + + DRM_DEBUG_KMS("%s:present[%d]before[%d]\n", __func__, + EXYNOS_CISTATUS2_GET_FRAMECOUNT_PRESENT(cfg), + EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(cfg)); + + if (frame_cnt == 0) { + DRM_ERROR("failed to get frame count.\n"); + return -EIO; + } + + buf_id = frame_cnt - 1; + DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, buf_id); + + return buf_id; +} + +static void fimc_handle_lastend(struct fimc_context *ctx, bool enable) +{ + u32 cfg; + + DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); + + cfg = fimc_read(EXYNOS_CIOCTRL); + if (enable) + cfg |= EXYNOS_CIOCTRL_LASTENDEN; + else + cfg &= ~EXYNOS_CIOCTRL_LASTENDEN; + + fimc_write(cfg, EXYNOS_CIOCTRL); +} + + +static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt) +{ + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + + DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); + + /* RGB */ + cfg = fimc_read(EXYNOS_CISCCTRL); + cfg &= ~EXYNOS_CISCCTRL_INRGB_FMT_RGB_MASK; + + switch (fmt) { + case DRM_FORMAT_RGB565: + cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB565; + fimc_write(cfg, EXYNOS_CISCCTRL); + return 0; + case DRM_FORMAT_RGB888: + case DRM_FORMAT_XRGB8888: + cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB888; + fimc_write(cfg, EXYNOS_CISCCTRL); + return 0; + default: + /* bypass */ + break; + } + + /* YUV */ + cfg = fimc_read(EXYNOS_MSCTRL); + cfg &= ~(EXYNOS_MSCTRL_ORDER2P_SHIFT_MASK | + EXYNOS_MSCTRL_C_INT_IN_2PLANE | + EXYNOS_MSCTRL_ORDER422_YCBYCR); + + switch (fmt) { + case DRM_FORMAT_YUYV: + cfg |= EXYNOS_MSCTRL_ORDER422_YCBYCR; + break; + case DRM_FORMAT_YVYU: + cfg |= EXYNOS_MSCTRL_ORDER422_YCRYCB; + break; + case DRM_FORMAT_UYVY: + cfg |= EXYNOS_MSCTRL_ORDER422_CBYCRY; + break; + case DRM_FORMAT_VYUY: + case DRM_FORMAT_YUV444: + cfg |= EXYNOS_MSCTRL_ORDER422_CRYCBY; + break; + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV61: + cfg |= (EXYNOS_MSCTRL_ORDER2P_LSB_CRCB | + EXYNOS_MSCTRL_C_INT_IN_2PLANE); + break; + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + cfg |= EXYNOS_MSCTRL_C_INT_IN_3PLANE; + break; + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV12MT: + case DRM_FORMAT_NV16: + cfg |= (EXYNOS_MSCTRL_ORDER2P_LSB_CBCR | + EXYNOS_MSCTRL_C_INT_IN_2PLANE); + break; + default: + dev_err(ippdrv->dev, "inavlid source yuv order 0x%x.\n", fmt); + return -EINVAL; + } + + fimc_write(cfg, EXYNOS_MSCTRL); + + return 0; +} + +static int fimc_src_set_fmt(struct device *dev, u32 fmt) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + + DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); + + cfg = fimc_read(EXYNOS_MSCTRL); + cfg &= ~EXYNOS_MSCTRL_INFORMAT_RGB; + + switch (fmt) { + case DRM_FORMAT_RGB565: + case DRM_FORMAT_RGB888: + case DRM_FORMAT_XRGB8888: + cfg |= EXYNOS_MSCTRL_INFORMAT_RGB; + break; + case DRM_FORMAT_YUV444: + cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR420; + break; + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR422_1PLANE; + break; + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + case DRM_FORMAT_YUV422: + cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR422; + break; + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV12MT: + cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR420; + break; + default: + dev_err(ippdrv->dev, "inavlid source format 0x%x.\n", fmt); + return -EINVAL; + } + + fimc_write(cfg, EXYNOS_MSCTRL); + + cfg = fimc_read(EXYNOS_CIDMAPARAM); + cfg &= ~EXYNOS_CIDMAPARAM_R_MODE_MASK; + + if (fmt == DRM_FORMAT_NV12MT) + cfg |= EXYNOS_CIDMAPARAM_R_MODE_64X32; + else + cfg |= EXYNOS_CIDMAPARAM_R_MODE_LINEAR; + + fimc_write(cfg, EXYNOS_CIDMAPARAM); + + return fimc_src_set_fmt_order(ctx, fmt); +} + +static int fimc_src_set_transf(struct device *dev, + enum drm_exynos_degree degree, + enum drm_exynos_flip flip, bool *swap) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg1, cfg2; + + DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__, + degree, flip); + + cfg1 = fimc_read(EXYNOS_MSCTRL); + cfg1 &= ~(EXYNOS_MSCTRL_FLIP_X_MIRROR | + EXYNOS_MSCTRL_FLIP_Y_MIRROR); + + cfg2 = fimc_read(EXYNOS_CITRGFMT); + cfg2 &= ~EXYNOS_CITRGFMT_INROT90_CLOCKWISE; + + switch (degree) { + case EXYNOS_DRM_DEGREE_0: + if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg1 |= EXYNOS_MSCTRL_FLIP_X_MIRROR; + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg1 |= EXYNOS_MSCTRL_FLIP_Y_MIRROR; + break; + case EXYNOS_DRM_DEGREE_90: + cfg2 |= EXYNOS_CITRGFMT_INROT90_CLOCKWISE; + if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg1 |= EXYNOS_MSCTRL_FLIP_X_MIRROR; + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg1 |= EXYNOS_MSCTRL_FLIP_Y_MIRROR; + break; + case EXYNOS_DRM_DEGREE_180: + cfg1 |= (EXYNOS_MSCTRL_FLIP_X_MIRROR | + EXYNOS_MSCTRL_FLIP_Y_MIRROR); + if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg1 &= ~EXYNOS_MSCTRL_FLIP_X_MIRROR; + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg1 &= ~EXYNOS_MSCTRL_FLIP_Y_MIRROR; + break; + case EXYNOS_DRM_DEGREE_270: + cfg1 |= (EXYNOS_MSCTRL_FLIP_X_MIRROR | + EXYNOS_MSCTRL_FLIP_Y_MIRROR); + cfg2 |= EXYNOS_CITRGFMT_INROT90_CLOCKWISE; + if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg1 &= ~EXYNOS_MSCTRL_FLIP_X_MIRROR; + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg1 &= ~EXYNOS_MSCTRL_FLIP_Y_MIRROR; + break; + default: + dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree); + return -EINVAL; + } + + fimc_write(cfg1, EXYNOS_MSCTRL); + fimc_write(cfg2, EXYNOS_CITRGFMT); + *swap = (cfg2 & EXYNOS_CITRGFMT_INROT90_CLOCKWISE) ? 1 : 0; + + return 0; +} + +static int fimc_set_window(struct fimc_context *ctx, + struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) +{ + u32 cfg, h1, h2, v1, v2; + + /* cropped image */ + h1 = pos->x; + h2 = sz->hsize - pos->w - pos->x; + v1 = pos->y; + v2 = sz->vsize - pos->h - pos->y; + + DRM_DEBUG_KMS("%s:x[%d]y[%d]w[%d]h[%d]hsize[%d]vsize[%d]\n", + __func__, pos->x, pos->y, pos->w, pos->h, sz->hsize, sz->vsize); + DRM_DEBUG_KMS("%s:h1[%d]h2[%d]v1[%d]v2[%d]\n", __func__, + h1, h2, v1, v2); + + /* + * set window offset 1, 2 size + * check figure 43-21 in user manual + */ + cfg = fimc_read(EXYNOS_CIWDOFST); + cfg &= ~(EXYNOS_CIWDOFST_WINHOROFST_MASK | + EXYNOS_CIWDOFST_WINVEROFST_MASK); + cfg |= (EXYNOS_CIWDOFST_WINHOROFST(h1) | + EXYNOS_CIWDOFST_WINVEROFST(v1)); + cfg |= EXYNOS_CIWDOFST_WINOFSEN; + fimc_write(cfg, EXYNOS_CIWDOFST); + + cfg = (EXYNOS_CIWDOFST2_WINHOROFST2(h2) | + EXYNOS_CIWDOFST2_WINVEROFST2(v2)); + fimc_write(cfg, EXYNOS_CIWDOFST2); + + return 0; +} + +static int fimc_src_set_size(struct device *dev, int swap, + struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct drm_exynos_pos img_pos = *pos; + struct drm_exynos_sz img_sz = *sz; + u32 cfg; + + DRM_DEBUG_KMS("%s:swap[%d]hsize[%d]vsize[%d]\n", + __func__, swap, sz->hsize, sz->vsize); + + /* original size */ + cfg = (EXYNOS_ORGISIZE_HORIZONTAL(img_sz.hsize) | + EXYNOS_ORGISIZE_VERTICAL(img_sz.vsize)); + + fimc_write(cfg, EXYNOS_ORGISIZE); + + DRM_DEBUG_KMS("%s:x[%d]y[%d]w[%d]h[%d]\n", __func__, + pos->x, pos->y, pos->w, pos->h); + + if (swap) { + img_pos.w = pos->h; + img_pos.h = pos->w; + img_sz.hsize = sz->vsize; + img_sz.vsize = sz->hsize; + } + + /* set input DMA image size */ + cfg = fimc_read(EXYNOS_CIREAL_ISIZE); + cfg &= ~(EXYNOS_CIREAL_ISIZE_HEIGHT_MASK | + EXYNOS_CIREAL_ISIZE_WIDTH_MASK); + cfg |= (EXYNOS_CIREAL_ISIZE_WIDTH(img_pos.w) | + EXYNOS_CIREAL_ISIZE_HEIGHT(img_pos.h)); + fimc_write(cfg, EXYNOS_CIREAL_ISIZE); + + /* + * set input FIFO image size + * for now, we support only ITU601 8 bit mode + */ + cfg = (EXYNOS_CISRCFMT_ITU601_8BIT | + EXYNOS_CISRCFMT_SOURCEHSIZE(img_sz.hsize) | + EXYNOS_CISRCFMT_SOURCEVSIZE(img_sz.vsize)); + fimc_write(cfg, EXYNOS_CISRCFMT); + + /* offset Y(RGB), Cb, Cr */ + cfg = (EXYNOS_CIIYOFF_HORIZONTAL(img_pos.x) | + EXYNOS_CIIYOFF_VERTICAL(img_pos.y)); + fimc_write(cfg, EXYNOS_CIIYOFF); + cfg = (EXYNOS_CIICBOFF_HORIZONTAL(img_pos.x) | + EXYNOS_CIICBOFF_VERTICAL(img_pos.y)); + fimc_write(cfg, EXYNOS_CIICBOFF); + cfg = (EXYNOS_CIICROFF_HORIZONTAL(img_pos.x) | + EXYNOS_CIICROFF_VERTICAL(img_pos.y)); + fimc_write(cfg, EXYNOS_CIICROFF); + + return fimc_set_window(ctx, &img_pos, &img_sz); +} + +static int fimc_src_set_addr(struct device *dev, + struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, + enum drm_exynos_ipp_buf_type buf_type) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd; + struct drm_exynos_ipp_property *property; + struct drm_exynos_ipp_config *config; + + if (!c_node) { + DRM_ERROR("failed to get c_node.\n"); + return -EINVAL; + } + + property = &c_node->property; + if (!property) { + DRM_ERROR("failed to get property.\n"); + return -EINVAL; + } + + DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]buf_type[%d]\n", __func__, + property->prop_id, buf_id, buf_type); + + if (buf_id > FIMC_MAX_SRC) { + dev_info(ippdrv->dev, "inavlid buf_id %d.\n", buf_id); + return -ENOMEM; + } + + /* address register set */ + switch (buf_type) { + case IPP_BUF_ENQUEUE: + config = &property->config[EXYNOS_DRM_OPS_SRC]; + fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y], + EXYNOS_CIIYSA(buf_id)); + + if (config->fmt == DRM_FORMAT_YVU420) { + fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], + EXYNOS_CIICBSA(buf_id)); + fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], + EXYNOS_CIICRSA(buf_id)); + } else { + fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], + EXYNOS_CIICBSA(buf_id)); + fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], + EXYNOS_CIICRSA(buf_id)); + } + break; + case IPP_BUF_DEQUEUE: + fimc_write(0x0, EXYNOS_CIIYSA(buf_id)); + fimc_write(0x0, EXYNOS_CIICBSA(buf_id)); + fimc_write(0x0, EXYNOS_CIICRSA(buf_id)); + break; + default: + /* bypass */ + break; + } + + return 0; +} + +static struct exynos_drm_ipp_ops fimc_src_ops = { + .set_fmt = fimc_src_set_fmt, + .set_transf = fimc_src_set_transf, + .set_size = fimc_src_set_size, + .set_addr = fimc_src_set_addr, +}; + +static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt) +{ + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + + DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); + + /* RGB */ + cfg = fimc_read(EXYNOS_CISCCTRL); + cfg &= ~EXYNOS_CISCCTRL_OUTRGB_FMT_RGB_MASK; + + switch (fmt) { + case DRM_FORMAT_RGB565: + cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB565; + fimc_write(cfg, EXYNOS_CISCCTRL); + return 0; + case DRM_FORMAT_RGB888: + cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888; + fimc_write(cfg, EXYNOS_CISCCTRL); + return 0; + case DRM_FORMAT_XRGB8888: + cfg |= (EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888 | + EXYNOS_CISCCTRL_EXTRGB_EXTENSION); + fimc_write(cfg, EXYNOS_CISCCTRL); + break; + default: + /* bypass */ + break; + } + + /* YUV */ + cfg = fimc_read(EXYNOS_CIOCTRL); + cfg &= ~(EXYNOS_CIOCTRL_ORDER2P_MASK | + EXYNOS_CIOCTRL_ORDER422_MASK | + EXYNOS_CIOCTRL_YCBCR_PLANE_MASK); + + switch (fmt) { + case DRM_FORMAT_XRGB8888: + cfg |= EXYNOS_CIOCTRL_ALPHA_OUT; + break; + case DRM_FORMAT_YUYV: + cfg |= EXYNOS_CIOCTRL_ORDER422_YCBYCR; + break; + case DRM_FORMAT_YVYU: + cfg |= EXYNOS_CIOCTRL_ORDER422_YCRYCB; + break; + case DRM_FORMAT_UYVY: + cfg |= EXYNOS_CIOCTRL_ORDER422_CBYCRY; + break; + case DRM_FORMAT_VYUY: + cfg |= EXYNOS_CIOCTRL_ORDER422_CRYCBY; + break; + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV61: + cfg |= EXYNOS_CIOCTRL_ORDER2P_LSB_CRCB; + cfg |= EXYNOS_CIOCTRL_YCBCR_2PLANE; + break; + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + cfg |= EXYNOS_CIOCTRL_YCBCR_3PLANE; + break; + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV12MT: + case DRM_FORMAT_NV16: + cfg |= EXYNOS_CIOCTRL_ORDER2P_LSB_CBCR; + cfg |= EXYNOS_CIOCTRL_YCBCR_2PLANE; + break; + default: + dev_err(ippdrv->dev, "inavlid target yuv order 0x%x.\n", fmt); + return -EINVAL; + } + + fimc_write(cfg, EXYNOS_CIOCTRL); + + return 0; +} + +static int fimc_dst_set_fmt(struct device *dev, u32 fmt) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + + DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); + + cfg = fimc_read(EXYNOS_CIEXTEN); + + if (fmt == DRM_FORMAT_AYUV) { + cfg |= EXYNOS_CIEXTEN_YUV444_OUT; + fimc_write(cfg, EXYNOS_CIEXTEN); + } else { + cfg &= ~EXYNOS_CIEXTEN_YUV444_OUT; + fimc_write(cfg, EXYNOS_CIEXTEN); + + cfg = fimc_read(EXYNOS_CITRGFMT); + cfg &= ~EXYNOS_CITRGFMT_OUTFORMAT_MASK; + + switch (fmt) { + case DRM_FORMAT_RGB565: + case DRM_FORMAT_RGB888: + case DRM_FORMAT_XRGB8888: + cfg |= EXYNOS_CITRGFMT_OUTFORMAT_RGB; + break; + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + cfg |= EXYNOS_CITRGFMT_OUTFORMAT_YCBCR422_1PLANE; + break; + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + case DRM_FORMAT_YUV422: + cfg |= EXYNOS_CITRGFMT_OUTFORMAT_YCBCR422; + break; + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV12MT: + case DRM_FORMAT_NV21: + cfg |= EXYNOS_CITRGFMT_OUTFORMAT_YCBCR420; + break; + default: + dev_err(ippdrv->dev, "inavlid target format 0x%x.\n", + fmt); + return -EINVAL; + } + + fimc_write(cfg, EXYNOS_CITRGFMT); + } + + cfg = fimc_read(EXYNOS_CIDMAPARAM); + cfg &= ~EXYNOS_CIDMAPARAM_W_MODE_MASK; + + if (fmt == DRM_FORMAT_NV12MT) + cfg |= EXYNOS_CIDMAPARAM_W_MODE_64X32; + else + cfg |= EXYNOS_CIDMAPARAM_W_MODE_LINEAR; + + fimc_write(cfg, EXYNOS_CIDMAPARAM); + + return fimc_dst_set_fmt_order(ctx, fmt); +} + +static int fimc_dst_set_transf(struct device *dev, + enum drm_exynos_degree degree, + enum drm_exynos_flip flip, bool *swap) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + + DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__, + degree, flip); + + cfg = fimc_read(EXYNOS_CITRGFMT); + cfg &= ~EXYNOS_CITRGFMT_FLIP_MASK; + cfg &= ~EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE; + + switch (degree) { + case EXYNOS_DRM_DEGREE_0: + if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg |= EXYNOS_CITRGFMT_FLIP_X_MIRROR; + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg |= EXYNOS_CITRGFMT_FLIP_Y_MIRROR; + break; + case EXYNOS_DRM_DEGREE_90: + cfg |= EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE; + if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg |= EXYNOS_CITRGFMT_FLIP_X_MIRROR; + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg |= EXYNOS_CITRGFMT_FLIP_Y_MIRROR; + break; + case EXYNOS_DRM_DEGREE_180: + cfg |= (EXYNOS_CITRGFMT_FLIP_X_MIRROR | + EXYNOS_CITRGFMT_FLIP_Y_MIRROR); + if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg &= ~EXYNOS_CITRGFMT_FLIP_X_MIRROR; + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg &= ~EXYNOS_CITRGFMT_FLIP_Y_MIRROR; + break; + case EXYNOS_DRM_DEGREE_270: + cfg |= (EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE | + EXYNOS_CITRGFMT_FLIP_X_MIRROR | + EXYNOS_CITRGFMT_FLIP_Y_MIRROR); + if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg &= ~EXYNOS_CITRGFMT_FLIP_X_MIRROR; + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg &= ~EXYNOS_CITRGFMT_FLIP_Y_MIRROR; + break; + default: + dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree); + return -EINVAL; + } + + fimc_write(cfg, EXYNOS_CITRGFMT); + *swap = (cfg & EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE) ? 1 : 0; + + return 0; +} + +static int fimc_get_ratio_shift(u32 src, u32 dst, u32 *ratio, u32 *shift) +{ + DRM_DEBUG_KMS("%s:src[%d]dst[%d]\n", __func__, src, dst); + + if (src >= dst * 64) { + DRM_ERROR("failed to make ratio and shift.\n"); + return -EINVAL; + } else if (src >= dst * 32) { + *ratio = 32; + *shift = 5; + } else if (src >= dst * 16) { + *ratio = 16; + *shift = 4; + } else if (src >= dst * 8) { + *ratio = 8; + *shift = 3; + } else if (src >= dst * 4) { + *ratio = 4; + *shift = 2; + } else if (src >= dst * 2) { + *ratio = 2; + *shift = 1; + } else { + *ratio = 1; + *shift = 0; + } + + return 0; +} + +static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc, + struct drm_exynos_pos *src, struct drm_exynos_pos *dst) +{ + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg, cfg_ext, shfactor; + u32 pre_dst_width, pre_dst_height; + u32 pre_hratio, hfactor, pre_vratio, vfactor; + int ret = 0; + u32 src_w, src_h, dst_w, dst_h; + + cfg_ext = fimc_read(EXYNOS_CITRGFMT); + if (cfg_ext & EXYNOS_CITRGFMT_INROT90_CLOCKWISE) { + src_w = src->h; + src_h = src->w; + } else { + src_w = src->w; + src_h = src->h; + } + + if (cfg_ext & EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE) { + dst_w = dst->h; + dst_h = dst->w; + } else { + dst_w = dst->w; + dst_h = dst->h; + } + + ret = fimc_get_ratio_shift(src_w, dst_w, &pre_hratio, &hfactor); + if (ret) { + dev_err(ippdrv->dev, "failed to get ratio horizontal.\n"); + return ret; + } + + ret = fimc_get_ratio_shift(src_h, dst_h, &pre_vratio, &vfactor); + if (ret) { + dev_err(ippdrv->dev, "failed to get ratio vertical.\n"); + return ret; + } + + pre_dst_width = src_w / pre_hratio; + pre_dst_height = src_h / pre_vratio; + DRM_DEBUG_KMS("%s:pre_dst_width[%d]pre_dst_height[%d]\n", __func__, + pre_dst_width, pre_dst_height); + DRM_DEBUG_KMS("%s:pre_hratio[%d]hfactor[%d]pre_vratio[%d]vfactor[%d]\n", + __func__, pre_hratio, hfactor, pre_vratio, vfactor); + + sc->hratio = (src_w << 14) / (dst_w << hfactor); + sc->vratio = (src_h << 14) / (dst_h << vfactor); + sc->up_h = (dst_w >= src_w) ? true : false; + sc->up_v = (dst_h >= src_h) ? true : false; + DRM_DEBUG_KMS("%s:hratio[%d]vratio[%d]up_h[%d]up_v[%d]\n", + __func__, sc->hratio, sc->vratio, sc->up_h, sc->up_v); + + shfactor = FIMC_SHFACTOR - (hfactor + vfactor); + DRM_DEBUG_KMS("%s:shfactor[%d]\n", __func__, shfactor); + + cfg = (EXYNOS_CISCPRERATIO_SHFACTOR(shfactor) | + EXYNOS_CISCPRERATIO_PREHORRATIO(pre_hratio) | + EXYNOS_CISCPRERATIO_PREVERRATIO(pre_vratio)); + fimc_write(cfg, EXYNOS_CISCPRERATIO); + + cfg = (EXYNOS_CISCPREDST_PREDSTWIDTH(pre_dst_width) | + EXYNOS_CISCPREDST_PREDSTHEIGHT(pre_dst_height)); + fimc_write(cfg, EXYNOS_CISCPREDST); + + return ret; +} + +static void fimc_set_scaler(struct fimc_context *ctx, struct fimc_scaler *sc) +{ + u32 cfg, cfg_ext; + + DRM_DEBUG_KMS("%s:range[%d]bypass[%d]up_h[%d]up_v[%d]\n", + __func__, sc->range, sc->bypass, sc->up_h, sc->up_v); + DRM_DEBUG_KMS("%s:hratio[%d]vratio[%d]\n", + __func__, sc->hratio, sc->vratio); + + cfg = fimc_read(EXYNOS_CISCCTRL); + cfg &= ~(EXYNOS_CISCCTRL_SCALERBYPASS | + EXYNOS_CISCCTRL_SCALEUP_H | EXYNOS_CISCCTRL_SCALEUP_V | + EXYNOS_CISCCTRL_MAIN_V_RATIO_MASK | + EXYNOS_CISCCTRL_MAIN_H_RATIO_MASK | + EXYNOS_CISCCTRL_CSCR2Y_WIDE | + EXYNOS_CISCCTRL_CSCY2R_WIDE); + + if (sc->range) + cfg |= (EXYNOS_CISCCTRL_CSCR2Y_WIDE | + EXYNOS_CISCCTRL_CSCY2R_WIDE); + if (sc->bypass) + cfg |= EXYNOS_CISCCTRL_SCALERBYPASS; + if (sc->up_h) + cfg |= EXYNOS_CISCCTRL_SCALEUP_H; + if (sc->up_v) + cfg |= EXYNOS_CISCCTRL_SCALEUP_V; + + cfg |= (EXYNOS_CISCCTRL_MAINHORRATIO((sc->hratio >> 6)) | + EXYNOS_CISCCTRL_MAINVERRATIO((sc->vratio >> 6))); + fimc_write(cfg, EXYNOS_CISCCTRL); + + cfg_ext = fimc_read(EXYNOS_CIEXTEN); + cfg_ext &= ~EXYNOS_CIEXTEN_MAINHORRATIO_EXT_MASK; + cfg_ext &= ~EXYNOS_CIEXTEN_MAINVERRATIO_EXT_MASK; + cfg_ext |= (EXYNOS_CIEXTEN_MAINHORRATIO_EXT(sc->hratio) | + EXYNOS_CIEXTEN_MAINVERRATIO_EXT(sc->vratio)); + fimc_write(cfg_ext, EXYNOS_CIEXTEN); +} + +static int fimc_dst_set_size(struct device *dev, int swap, + struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct drm_exynos_pos img_pos = *pos; + struct drm_exynos_sz img_sz = *sz; + u32 cfg; + + DRM_DEBUG_KMS("%s:swap[%d]hsize[%d]vsize[%d]\n", + __func__, swap, sz->hsize, sz->vsize); + + /* original size */ + cfg = (EXYNOS_ORGOSIZE_HORIZONTAL(img_sz.hsize) | + EXYNOS_ORGOSIZE_VERTICAL(img_sz.vsize)); + + fimc_write(cfg, EXYNOS_ORGOSIZE); + + DRM_DEBUG_KMS("%s:x[%d]y[%d]w[%d]h[%d]\n", + __func__, pos->x, pos->y, pos->w, pos->h); + + /* CSC ITU */ + cfg = fimc_read(EXYNOS_CIGCTRL); + cfg &= ~EXYNOS_CIGCTRL_CSC_MASK; + + if (sz->hsize >= FIMC_WIDTH_ITU_709) + cfg |= EXYNOS_CIGCTRL_CSC_ITU709; + else + cfg |= EXYNOS_CIGCTRL_CSC_ITU601; + + fimc_write(cfg, EXYNOS_CIGCTRL); + + if (swap) { + img_pos.w = pos->h; + img_pos.h = pos->w; + img_sz.hsize = sz->vsize; + img_sz.vsize = sz->hsize; + } + + /* target image size */ + cfg = fimc_read(EXYNOS_CITRGFMT); + cfg &= ~(EXYNOS_CITRGFMT_TARGETH_MASK | + EXYNOS_CITRGFMT_TARGETV_MASK); + cfg |= (EXYNOS_CITRGFMT_TARGETHSIZE(img_pos.w) | + EXYNOS_CITRGFMT_TARGETVSIZE(img_pos.h)); + fimc_write(cfg, EXYNOS_CITRGFMT); + + /* target area */ + cfg = EXYNOS_CITAREA_TARGET_AREA(img_pos.w * img_pos.h); + fimc_write(cfg, EXYNOS_CITAREA); + + /* offset Y(RGB), Cb, Cr */ + cfg = (EXYNOS_CIOYOFF_HORIZONTAL(img_pos.x) | + EXYNOS_CIOYOFF_VERTICAL(img_pos.y)); + fimc_write(cfg, EXYNOS_CIOYOFF); + cfg = (EXYNOS_CIOCBOFF_HORIZONTAL(img_pos.x) | + EXYNOS_CIOCBOFF_VERTICAL(img_pos.y)); + fimc_write(cfg, EXYNOS_CIOCBOFF); + cfg = (EXYNOS_CIOCROFF_HORIZONTAL(img_pos.x) | + EXYNOS_CIOCROFF_VERTICAL(img_pos.y)); + fimc_write(cfg, EXYNOS_CIOCROFF); + + return 0; +} + +static int fimc_dst_get_buf_seq(struct fimc_context *ctx) +{ + u32 cfg, i, buf_num = 0; + u32 mask = 0x00000001; + + cfg = fimc_read(EXYNOS_CIFCNTSEQ); + + for (i = 0; i < FIMC_REG_SZ; i++) + if (cfg & (mask << i)) + buf_num++; + + DRM_DEBUG_KMS("%s:buf_num[%d]\n", __func__, buf_num); + + return buf_num; +} + +static int fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id, + enum drm_exynos_ipp_buf_type buf_type) +{ + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + bool enable; + u32 cfg; + u32 mask = 0x00000001 << buf_id; + int ret = 0; + + DRM_DEBUG_KMS("%s:buf_id[%d]buf_type[%d]\n", __func__, + buf_id, buf_type); + + mutex_lock(&ctx->lock); + + /* mask register set */ + cfg = fimc_read(EXYNOS_CIFCNTSEQ); + + switch (buf_type) { + case IPP_BUF_ENQUEUE: + enable = true; + break; + case IPP_BUF_DEQUEUE: + enable = false; + break; + default: + dev_err(ippdrv->dev, "invalid buf ctrl parameter.\n"); + ret = -EINVAL; + goto err_unlock; + } + + /* sequence id */ + cfg &= (~mask); + cfg |= (enable << buf_id); + fimc_write(cfg, EXYNOS_CIFCNTSEQ); + + /* interrupt enable */ + if (buf_type == IPP_BUF_ENQUEUE && + fimc_dst_get_buf_seq(ctx) >= FIMC_BUF_START) + fimc_handle_irq(ctx, true, false, true); + + /* interrupt disable */ + if (buf_type == IPP_BUF_DEQUEUE && + fimc_dst_get_buf_seq(ctx) <= FIMC_BUF_STOP) + fimc_handle_irq(ctx, false, false, true); + +err_unlock: + mutex_unlock(&ctx->lock); + return ret; +} + +static int fimc_dst_set_addr(struct device *dev, + struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, + enum drm_exynos_ipp_buf_type buf_type) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd; + struct drm_exynos_ipp_property *property; + struct drm_exynos_ipp_config *config; + + if (!c_node) { + DRM_ERROR("failed to get c_node.\n"); + return -EINVAL; + } + + property = &c_node->property; + if (!property) { + DRM_ERROR("failed to get property.\n"); + return -EINVAL; + } + + DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]buf_type[%d]\n", __func__, + property->prop_id, buf_id, buf_type); + + if (buf_id > FIMC_MAX_DST) { + dev_info(ippdrv->dev, "inavlid buf_id %d.\n", buf_id); + return -ENOMEM; + } + + /* address register set */ + switch (buf_type) { + case IPP_BUF_ENQUEUE: + config = &property->config[EXYNOS_DRM_OPS_DST]; + + fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y], + EXYNOS_CIOYSA(buf_id)); + + if (config->fmt == DRM_FORMAT_YVU420) { + fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], + EXYNOS_CIOCBSA(buf_id)); + fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], + EXYNOS_CIOCRSA(buf_id)); + } else { + fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], + EXYNOS_CIOCBSA(buf_id)); + fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], + EXYNOS_CIOCRSA(buf_id)); + } + break; + case IPP_BUF_DEQUEUE: + fimc_write(0x0, EXYNOS_CIOYSA(buf_id)); + fimc_write(0x0, EXYNOS_CIOCBSA(buf_id)); + fimc_write(0x0, EXYNOS_CIOCRSA(buf_id)); + break; + default: + /* bypass */ + break; + } + + return fimc_dst_set_buf_seq(ctx, buf_id, buf_type); +} + +static struct exynos_drm_ipp_ops fimc_dst_ops = { + .set_fmt = fimc_dst_set_fmt, + .set_transf = fimc_dst_set_transf, + .set_size = fimc_dst_set_size, + .set_addr = fimc_dst_set_addr, +}; + +static int fimc_clk_ctrl(struct fimc_context *ctx, bool enable) +{ + DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); + + if (enable) { + clk_enable(ctx->sclk_fimc_clk); + clk_enable(ctx->fimc_clk); + clk_enable(ctx->wb_clk); + ctx->suspended = false; + } else { + clk_disable(ctx->sclk_fimc_clk); + clk_disable(ctx->fimc_clk); + clk_disable(ctx->wb_clk); + ctx->suspended = true; + } + + return 0; +} + +static irqreturn_t fimc_irq_handler(int irq, void *dev_id) +{ + struct fimc_context *ctx = dev_id; + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd; + struct drm_exynos_ipp_event_work *event_work = + c_node->event_work; + int buf_id; + + DRM_DEBUG_KMS("%s:fimc id[%d]\n", __func__, ctx->id); + + fimc_clear_irq(ctx); + if (fimc_check_ovf(ctx)) + return IRQ_NONE; + + if (!fimc_check_frame_end(ctx)) + return IRQ_NONE; + + buf_id = fimc_get_buf_id(ctx); + if (buf_id < 0) + return IRQ_HANDLED; + + DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, buf_id); + + if (fimc_dst_set_buf_seq(ctx, buf_id, IPP_BUF_DEQUEUE) < 0) { + DRM_ERROR("failed to dequeue.\n"); + return IRQ_HANDLED; + } + + event_work->ippdrv = ippdrv; + event_work->buf_id[EXYNOS_DRM_OPS_DST] = buf_id; + queue_work(ippdrv->event_workq, (struct work_struct *)event_work); + + return IRQ_HANDLED; +} + +static int fimc_init_prop_list(struct exynos_drm_ippdrv *ippdrv) +{ + struct drm_exynos_ipp_prop_list *prop_list; + + DRM_DEBUG_KMS("%s\n", __func__); + + prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL); + if (!prop_list) { + DRM_ERROR("failed to alloc property list.\n"); + return -ENOMEM; + } + + prop_list->version = 1; + prop_list->writeback = 1; + prop_list->refresh_min = FIMC_REFRESH_MIN; + prop_list->refresh_max = FIMC_REFRESH_MAX; + prop_list->flip = (1 << EXYNOS_DRM_FLIP_NONE) | + (1 << EXYNOS_DRM_FLIP_VERTICAL) | + (1 << EXYNOS_DRM_FLIP_HORIZONTAL); + prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) | + (1 << EXYNOS_DRM_DEGREE_90) | + (1 << EXYNOS_DRM_DEGREE_180) | + (1 << EXYNOS_DRM_DEGREE_270); + prop_list->csc = 1; + prop_list->crop = 1; + prop_list->crop_max.hsize = FIMC_CROP_MAX; + prop_list->crop_max.vsize = FIMC_CROP_MAX; + prop_list->crop_min.hsize = FIMC_CROP_MIN; + prop_list->crop_min.vsize = FIMC_CROP_MIN; + prop_list->scale = 1; + prop_list->scale_max.hsize = FIMC_SCALE_MAX; + prop_list->scale_max.vsize = FIMC_SCALE_MAX; + prop_list->scale_min.hsize = FIMC_SCALE_MIN; + prop_list->scale_min.vsize = FIMC_SCALE_MIN; + + ippdrv->prop_list = prop_list; + + return 0; +} + +static inline bool fimc_check_drm_flip(enum drm_exynos_flip flip) +{ + switch (flip) { + case EXYNOS_DRM_FLIP_NONE: + case EXYNOS_DRM_FLIP_VERTICAL: + case EXYNOS_DRM_FLIP_HORIZONTAL: + return true; + default: + DRM_DEBUG_KMS("%s:invalid flip\n", __func__); + return false; + } +} + +static int fimc_ippdrv_check_property(struct device *dev, + struct drm_exynos_ipp_property *property) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + struct drm_exynos_ipp_prop_list *pp = ippdrv->prop_list; + struct drm_exynos_ipp_config *config; + struct drm_exynos_pos *pos; + struct drm_exynos_sz *sz; + bool swap; + int i; + + DRM_DEBUG_KMS("%s\n", __func__); + + for_each_ipp_ops(i) { + if ((i == EXYNOS_DRM_OPS_SRC) && + (property->cmd == IPP_CMD_WB)) + continue; + + config = &property->config[i]; + pos = &config->pos; + sz = &config->sz; + + /* check for flip */ + if (!fimc_check_drm_flip(config->flip)) { + DRM_ERROR("invalid flip.\n"); + goto err_property; + } + + /* check for degree */ + switch (config->degree) { + case EXYNOS_DRM_DEGREE_90: + case EXYNOS_DRM_DEGREE_270: + swap = true; + break; + case EXYNOS_DRM_DEGREE_0: + case EXYNOS_DRM_DEGREE_180: + swap = false; + break; + default: + DRM_ERROR("invalid degree.\n"); + goto err_property; + } + + /* check for buffer bound */ + if ((pos->x + pos->w > sz->hsize) || + (pos->y + pos->h > sz->vsize)) { + DRM_ERROR("out of buf bound.\n"); + goto err_property; + } + + /* check for crop */ + if ((i == EXYNOS_DRM_OPS_SRC) && (pp->crop)) { + if (swap) { + if ((pos->h < pp->crop_min.hsize) || + (sz->vsize > pp->crop_max.hsize) || + (pos->w < pp->crop_min.vsize) || + (sz->hsize > pp->crop_max.vsize)) { + DRM_ERROR("out of crop size.\n"); + goto err_property; + } + } else { + if ((pos->w < pp->crop_min.hsize) || + (sz->hsize > pp->crop_max.hsize) || + (pos->h < pp->crop_min.vsize) || + (sz->vsize > pp->crop_max.vsize)) { + DRM_ERROR("out of crop size.\n"); + goto err_property; + } + } + } + + /* check for scale */ + if ((i == EXYNOS_DRM_OPS_DST) && (pp->scale)) { + if (swap) { + if ((pos->h < pp->scale_min.hsize) || + (sz->vsize > pp->scale_max.hsize) || + (pos->w < pp->scale_min.vsize) || + (sz->hsize > pp->scale_max.vsize)) { + DRM_ERROR("out of scale size.\n"); + goto err_property; + } + } else { + if ((pos->w < pp->scale_min.hsize) || + (sz->hsize > pp->scale_max.hsize) || + (pos->h < pp->scale_min.vsize) || + (sz->vsize > pp->scale_max.vsize)) { + DRM_ERROR("out of scale size.\n"); + goto err_property; + } + } + } + } + + return 0; + +err_property: + for_each_ipp_ops(i) { + if ((i == EXYNOS_DRM_OPS_SRC) && + (property->cmd == IPP_CMD_WB)) + continue; + + config = &property->config[i]; + pos = &config->pos; + sz = &config->sz; + + DRM_ERROR("[%s]f[%d]r[%d]pos[%d %d %d %d]sz[%d %d]\n", + i ? "dst" : "src", config->flip, config->degree, + pos->x, pos->y, pos->w, pos->h, + sz->hsize, sz->vsize); + } + + return -EINVAL; +} + +static void fimc_clear_addr(struct fimc_context *ctx) +{ + int i; + + DRM_DEBUG_KMS("%s:\n", __func__); + + for (i = 0; i < FIMC_MAX_SRC; i++) { + fimc_write(0, EXYNOS_CIIYSA(i)); + fimc_write(0, EXYNOS_CIICBSA(i)); + fimc_write(0, EXYNOS_CIICRSA(i)); + } + + for (i = 0; i < FIMC_MAX_DST; i++) { + fimc_write(0, EXYNOS_CIOYSA(i)); + fimc_write(0, EXYNOS_CIOCBSA(i)); + fimc_write(0, EXYNOS_CIOCRSA(i)); + } +} + +static int fimc_ippdrv_reset(struct device *dev) +{ + struct fimc_context *ctx = get_fimc_context(dev); + + DRM_DEBUG_KMS("%s\n", __func__); + + /* reset h/w block */ + fimc_sw_reset(ctx, false); + + /* reset scaler capability */ + memset(&ctx->sc, 0x0, sizeof(ctx->sc)); + + fimc_clear_addr(ctx); + + return 0; +} + +static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd; + struct drm_exynos_ipp_property *property; + struct drm_exynos_ipp_config *config; + struct drm_exynos_pos img_pos[EXYNOS_DRM_OPS_MAX]; + struct drm_exynos_ipp_set_wb set_wb; + int ret, i; + u32 cfg0, cfg1; + + DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd); + + if (!c_node) { + DRM_ERROR("failed to get c_node.\n"); + return -EINVAL; + } + + property = &c_node->property; + if (!property) { + DRM_ERROR("failed to get property.\n"); + return -EINVAL; + } + + fimc_handle_irq(ctx, true, false, true); + + for_each_ipp_ops(i) { + config = &property->config[i]; + img_pos[i] = config->pos; + } + + ret = fimc_set_prescaler(ctx, &ctx->sc, + &img_pos[EXYNOS_DRM_OPS_SRC], + &img_pos[EXYNOS_DRM_OPS_DST]); + if (ret) { + dev_err(dev, "failed to set precalser.\n"); + return ret; + } + + /* If set ture, we can save jpeg about screen */ + fimc_handle_jpeg(ctx, false); + fimc_set_scaler(ctx, &ctx->sc); + fimc_set_polarity(ctx, &ctx->pol); + + switch (cmd) { + case IPP_CMD_M2M: + fimc_set_type_ctrl(ctx, FIMC_WB_NONE); + fimc_handle_lastend(ctx, false); + + /* setup dma */ + cfg0 = fimc_read(EXYNOS_MSCTRL); + cfg0 &= ~EXYNOS_MSCTRL_INPUT_MASK; + cfg0 |= EXYNOS_MSCTRL_INPUT_MEMORY; + fimc_write(cfg0, EXYNOS_MSCTRL); + break; + case IPP_CMD_WB: + fimc_set_type_ctrl(ctx, FIMC_WB_A); + fimc_handle_lastend(ctx, true); + + /* setup FIMD */ + fimc_set_camblk_fimd0_wb(ctx); + + set_wb.enable = 1; + set_wb.refresh = property->refresh_rate; + exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb); + break; + case IPP_CMD_OUTPUT: + default: + ret = -EINVAL; + dev_err(dev, "invalid operations.\n"); + return ret; + } + + /* Reset status */ + fimc_write(0x0, EXYNOS_CISTATUS); + + cfg0 = fimc_read(EXYNOS_CIIMGCPT); + cfg0 &= ~EXYNOS_CIIMGCPT_IMGCPTEN_SC; + cfg0 |= EXYNOS_CIIMGCPT_IMGCPTEN_SC; + + /* Scaler */ + cfg1 = fimc_read(EXYNOS_CISCCTRL); + cfg1 &= ~EXYNOS_CISCCTRL_SCAN_MASK; + cfg1 |= (EXYNOS_CISCCTRL_PROGRESSIVE | + EXYNOS_CISCCTRL_SCALERSTART); + + fimc_write(cfg1, EXYNOS_CISCCTRL); + + /* Enable image capture*/ + cfg0 |= EXYNOS_CIIMGCPT_IMGCPTEN; + fimc_write(cfg0, EXYNOS_CIIMGCPT); + + /* Disable frame end irq */ + cfg0 = fimc_read(EXYNOS_CIGCTRL); + cfg0 &= ~EXYNOS_CIGCTRL_IRQ_END_DISABLE; + fimc_write(cfg0, EXYNOS_CIGCTRL); + + cfg0 = fimc_read(EXYNOS_CIOCTRL); + cfg0 &= ~EXYNOS_CIOCTRL_WEAVE_MASK; + fimc_write(cfg0, EXYNOS_CIOCTRL); + + if (cmd == IPP_CMD_M2M) { + cfg0 = fimc_read(EXYNOS_MSCTRL); + cfg0 |= EXYNOS_MSCTRL_ENVID; + fimc_write(cfg0, EXYNOS_MSCTRL); + + cfg0 = fimc_read(EXYNOS_MSCTRL); + cfg0 |= EXYNOS_MSCTRL_ENVID; + fimc_write(cfg0, EXYNOS_MSCTRL); + } + + return 0; +} + +static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct drm_exynos_ipp_set_wb set_wb = {0, 0}; + u32 cfg; + + DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd); + + switch (cmd) { + case IPP_CMD_M2M: + /* Source clear */ + cfg = fimc_read(EXYNOS_MSCTRL); + cfg &= ~EXYNOS_MSCTRL_INPUT_MASK; + cfg &= ~EXYNOS_MSCTRL_ENVID; + fimc_write(cfg, EXYNOS_MSCTRL); + break; + case IPP_CMD_WB: + exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb); + break; + case IPP_CMD_OUTPUT: + default: + dev_err(dev, "invalid operations.\n"); + break; + } + + fimc_handle_irq(ctx, false, false, true); + + /* reset sequence */ + fimc_write(0x0, EXYNOS_CIFCNTSEQ); + + /* Scaler disable */ + cfg = fimc_read(EXYNOS_CISCCTRL); + cfg &= ~EXYNOS_CISCCTRL_SCALERSTART; + fimc_write(cfg, EXYNOS_CISCCTRL); + + /* Disable image capture */ + cfg = fimc_read(EXYNOS_CIIMGCPT); + cfg &= ~(EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN); + fimc_write(cfg, EXYNOS_CIIMGCPT); + + /* Enable frame end irq */ + cfg = fimc_read(EXYNOS_CIGCTRL); + cfg |= EXYNOS_CIGCTRL_IRQ_END_DISABLE; + fimc_write(cfg, EXYNOS_CIGCTRL); +} + +static int __devinit fimc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fimc_context *ctx; + struct clk *parent_clk; + struct resource *res; + struct exynos_drm_ippdrv *ippdrv; + struct exynos_drm_fimc_pdata *pdata; + struct fimc_driverdata *ddata; + int ret; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(dev, "no platform data specified.\n"); + return -EINVAL; + } + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ddata = (struct fimc_driverdata *) + platform_get_device_id(pdev)->driver_data; + + /* clock control */ + ctx->sclk_fimc_clk = clk_get(dev, "sclk_fimc"); + if (IS_ERR(ctx->sclk_fimc_clk)) { + dev_err(dev, "failed to get src fimc clock.\n"); + ret = PTR_ERR(ctx->sclk_fimc_clk); + goto err_ctx; + } + clk_enable(ctx->sclk_fimc_clk); + + ctx->fimc_clk = clk_get(dev, "fimc"); + if (IS_ERR(ctx->fimc_clk)) { + dev_err(dev, "failed to get fimc clock.\n"); + ret = PTR_ERR(ctx->fimc_clk); + clk_disable(ctx->sclk_fimc_clk); + clk_put(ctx->sclk_fimc_clk); + goto err_ctx; + } + + ctx->wb_clk = clk_get(dev, "pxl_async0"); + if (IS_ERR(ctx->wb_clk)) { + dev_err(dev, "failed to get writeback a clock.\n"); + ret = PTR_ERR(ctx->wb_clk); + clk_disable(ctx->sclk_fimc_clk); + clk_put(ctx->sclk_fimc_clk); + clk_put(ctx->fimc_clk); + goto err_ctx; + } + + ctx->wb_b_clk = clk_get(dev, "pxl_async1"); + if (IS_ERR(ctx->wb_b_clk)) { + dev_err(dev, "failed to get writeback b clock.\n"); + ret = PTR_ERR(ctx->wb_b_clk); + clk_disable(ctx->sclk_fimc_clk); + clk_put(ctx->sclk_fimc_clk); + clk_put(ctx->fimc_clk); + clk_put(ctx->wb_clk); + goto err_ctx; + } + + parent_clk = clk_get(dev, ddata->parent_clk); + + if (IS_ERR(parent_clk)) { + dev_err(dev, "failed to get parent clock.\n"); + ret = PTR_ERR(parent_clk); + clk_disable(ctx->sclk_fimc_clk); + clk_put(ctx->sclk_fimc_clk); + clk_put(ctx->fimc_clk); + clk_put(ctx->wb_clk); + clk_put(ctx->wb_b_clk); + goto err_ctx; + } + + if (clk_set_parent(ctx->sclk_fimc_clk, parent_clk)) { + dev_err(dev, "failed to set parent.\n"); + ret = -EINVAL; + clk_put(parent_clk); + clk_disable(ctx->sclk_fimc_clk); + clk_put(ctx->sclk_fimc_clk); + clk_put(ctx->fimc_clk); + clk_put(ctx->wb_clk); + clk_put(ctx->wb_b_clk); + goto err_ctx; + } + + clk_put(parent_clk); + clk_set_rate(ctx->sclk_fimc_clk, pdata->clk_rate); + + /* resource memory */ + ctx->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!ctx->regs_res) { + dev_err(dev, "failed to find registers.\n"); + ret = -ENOENT; + goto err_clk; + } + + ctx->regs = devm_request_and_ioremap(dev, ctx->regs_res); + if (!ctx->regs) { + dev_err(dev, "failed to map registers.\n"); + ret = -ENXIO; + goto err_clk; + } + + /* resource irq */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, "failed to request irq resource.\n"); + ret = -ENOENT; + goto err_get_regs; + } + + ctx->irq = res->start; + ret = request_threaded_irq(ctx->irq, NULL, fimc_irq_handler, + IRQF_ONESHOT, "drm_fimc", ctx); + if (ret < 0) { + dev_err(dev, "failed to request irq.\n"); + goto err_get_regs; + } + + /* context initailization */ + ctx->id = pdev->id; + ctx->pol = pdata->pol; + ctx->ddata = ddata; + + ippdrv = &ctx->ippdrv; + ippdrv->dev = dev; + ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &fimc_src_ops; + ippdrv->ops[EXYNOS_DRM_OPS_DST] = &fimc_dst_ops; + ippdrv->check_property = fimc_ippdrv_check_property; + ippdrv->reset = fimc_ippdrv_reset; + ippdrv->start = fimc_ippdrv_start; + ippdrv->stop = fimc_ippdrv_stop; + ret = fimc_init_prop_list(ippdrv); + if (ret < 0) { + dev_err(dev, "failed to init property list.\n"); + goto err_get_irq; + } + + DRM_DEBUG_KMS("%s:id[%d]ippdrv[0x%x]\n", __func__, ctx->id, + (int)ippdrv); + + mutex_init(&ctx->lock); + platform_set_drvdata(pdev, ctx); + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret = exynos_drm_ippdrv_register(ippdrv); + if (ret < 0) { + dev_err(dev, "failed to register drm fimc device.\n"); + goto err_ippdrv_register; + } + + dev_info(&pdev->dev, "drm fimc registered successfully.\n"); + + return 0; + +err_ippdrv_register: + devm_kfree(dev, ippdrv->prop_list); + pm_runtime_disable(dev); +err_get_irq: + free_irq(ctx->irq, ctx); +err_get_regs: + devm_iounmap(dev, ctx->regs); +err_clk: + clk_put(ctx->sclk_fimc_clk); + clk_put(ctx->fimc_clk); + clk_put(ctx->wb_clk); + clk_put(ctx->wb_b_clk); +err_ctx: + devm_kfree(dev, ctx); + return ret; +} + +static int __devexit fimc_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fimc_context *ctx = get_fimc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + + devm_kfree(dev, ippdrv->prop_list); + exynos_drm_ippdrv_unregister(ippdrv); + mutex_destroy(&ctx->lock); + + pm_runtime_set_suspended(dev); + pm_runtime_disable(dev); + + free_irq(ctx->irq, ctx); + devm_iounmap(dev, ctx->regs); + + clk_put(ctx->sclk_fimc_clk); + clk_put(ctx->fimc_clk); + clk_put(ctx->wb_clk); + clk_put(ctx->wb_b_clk); + + devm_kfree(dev, ctx); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int fimc_suspend(struct device *dev) +{ + struct fimc_context *ctx = get_fimc_context(dev); + + DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + + if (pm_runtime_suspended(dev)) + return 0; + + return fimc_clk_ctrl(ctx, false); +} + +static int fimc_resume(struct device *dev) +{ + struct fimc_context *ctx = get_fimc_context(dev); + + DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + + if (!pm_runtime_suspended(dev)) + return fimc_clk_ctrl(ctx, true); + + return 0; +} +#endif + +#ifdef CONFIG_PM_RUNTIME +static int fimc_runtime_suspend(struct device *dev) +{ + struct fimc_context *ctx = get_fimc_context(dev); + + DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + + return fimc_clk_ctrl(ctx, false); +} + +static int fimc_runtime_resume(struct device *dev) +{ + struct fimc_context *ctx = get_fimc_context(dev); + + DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + + return fimc_clk_ctrl(ctx, true); +} +#endif + +static struct fimc_driverdata exynos4210_fimc_data = { + .parent_clk = "mout_mpll", +}; + +static struct fimc_driverdata exynos4410_fimc_data = { + .parent_clk = "mout_mpll_user", +}; + +static struct platform_device_id fimc_driver_ids[] = { + { + .name = "exynos4210-fimc", + .driver_data = (unsigned long)&exynos4210_fimc_data, + }, { + .name = "exynos4412-fimc", + .driver_data = (unsigned long)&exynos4410_fimc_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, fimc_driver_ids); + +static const struct dev_pm_ops fimc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fimc_suspend, fimc_resume) + SET_RUNTIME_PM_OPS(fimc_runtime_suspend, fimc_runtime_resume, NULL) +}; + +struct platform_driver fimc_driver = { + .probe = fimc_probe, + .remove = __devexit_p(fimc_remove), + .id_table = fimc_driver_ids, + .driver = { + .name = "exynos-drm-fimc", + .owner = THIS_MODULE, + .pm = &fimc_pm_ops, + }, +}; + diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.h b/drivers/gpu/drm/exynos/exynos_drm_fimc.h new file mode 100644 index 000000000000..dc970fa0d888 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * + * Authors: + * Eunchul Kim + * Jinyoung Jeon + * Sangmin Lee + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _EXYNOS_DRM_FIMC_H_ +#define _EXYNOS_DRM_FIMC_H_ + +/* + * TODO + * FIMD output interface notifier callback. + */ + +#endif /* _EXYNOS_DRM_FIMC_H_ */ diff --git a/drivers/gpu/drm/exynos/regs-fimc.h b/drivers/gpu/drm/exynos/regs-fimc.h new file mode 100644 index 000000000000..b4f9ca1fd851 --- /dev/null +++ b/drivers/gpu/drm/exynos/regs-fimc.h @@ -0,0 +1,669 @@ +/* drivers/gpu/drm/exynos/regs-fimc.h + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Register definition file for Samsung Camera Interface (FIMC) driver + * + * 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. +*/ + +#ifndef EXYNOS_REGS_FIMC_H +#define EXYNOS_REGS_FIMC_H + +/* + * Register part +*/ +/* Input source format */ +#define EXYNOS_CISRCFMT (0x00) +/* Window offset */ +#define EXYNOS_CIWDOFST (0x04) +/* Global control */ +#define EXYNOS_CIGCTRL (0x08) +/* Window offset 2 */ +#define EXYNOS_CIWDOFST2 (0x14) +/* Y 1st frame start address for output DMA */ +#define EXYNOS_CIOYSA1 (0x18) +/* Y 2nd frame start address for output DMA */ +#define EXYNOS_CIOYSA2 (0x1c) +/* Y 3rd frame start address for output DMA */ +#define EXYNOS_CIOYSA3 (0x20) +/* Y 4th frame start address for output DMA */ +#define EXYNOS_CIOYSA4 (0x24) +/* Cb 1st frame start address for output DMA */ +#define EXYNOS_CIOCBSA1 (0x28) +/* Cb 2nd frame start address for output DMA */ +#define EXYNOS_CIOCBSA2 (0x2c) +/* Cb 3rd frame start address for output DMA */ +#define EXYNOS_CIOCBSA3 (0x30) +/* Cb 4th frame start address for output DMA */ +#define EXYNOS_CIOCBSA4 (0x34) +/* Cr 1st frame start address for output DMA */ +#define EXYNOS_CIOCRSA1 (0x38) +/* Cr 2nd frame start address for output DMA */ +#define EXYNOS_CIOCRSA2 (0x3c) +/* Cr 3rd frame start address for output DMA */ +#define EXYNOS_CIOCRSA3 (0x40) +/* Cr 4th frame start address for output DMA */ +#define EXYNOS_CIOCRSA4 (0x44) +/* Target image format */ +#define EXYNOS_CITRGFMT (0x48) +/* Output DMA control */ +#define EXYNOS_CIOCTRL (0x4c) +/* Pre-scaler control 1 */ +#define EXYNOS_CISCPRERATIO (0x50) +/* Pre-scaler control 2 */ +#define EXYNOS_CISCPREDST (0x54) +/* Main scaler control */ +#define EXYNOS_CISCCTRL (0x58) +/* Target area */ +#define EXYNOS_CITAREA (0x5c) +/* Status */ +#define EXYNOS_CISTATUS (0x64) +/* Status2 */ +#define EXYNOS_CISTATUS2 (0x68) +/* Image capture enable command */ +#define EXYNOS_CIIMGCPT (0xc0) +/* Capture sequence */ +#define EXYNOS_CICPTSEQ (0xc4) +/* Image effects */ +#define EXYNOS_CIIMGEFF (0xd0) +/* Y frame start address for input DMA */ +#define EXYNOS_CIIYSA0 (0xd4) +/* Cb frame start address for input DMA */ +#define EXYNOS_CIICBSA0 (0xd8) +/* Cr frame start address for input DMA */ +#define EXYNOS_CIICRSA0 (0xdc) +/* Input DMA Y Line Skip */ +#define EXYNOS_CIILINESKIP_Y (0xec) +/* Input DMA Cb Line Skip */ +#define EXYNOS_CIILINESKIP_CB (0xf0) +/* Input DMA Cr Line Skip */ +#define EXYNOS_CIILINESKIP_CR (0xf4) +/* Real input DMA image size */ +#define EXYNOS_CIREAL_ISIZE (0xf8) +/* Input DMA control */ +#define EXYNOS_MSCTRL (0xfc) +/* Y frame start address for input DMA */ +#define EXYNOS_CIIYSA1 (0x144) +/* Cb frame start address for input DMA */ +#define EXYNOS_CIICBSA1 (0x148) +/* Cr frame start address for input DMA */ +#define EXYNOS_CIICRSA1 (0x14c) +/* Output DMA Y offset */ +#define EXYNOS_CIOYOFF (0x168) +/* Output DMA CB offset */ +#define EXYNOS_CIOCBOFF (0x16c) +/* Output DMA CR offset */ +#define EXYNOS_CIOCROFF (0x170) +/* Input DMA Y offset */ +#define EXYNOS_CIIYOFF (0x174) +/* Input DMA CB offset */ +#define EXYNOS_CIICBOFF (0x178) +/* Input DMA CR offset */ +#define EXYNOS_CIICROFF (0x17c) +/* Input DMA original image size */ +#define EXYNOS_ORGISIZE (0x180) +/* Output DMA original image size */ +#define EXYNOS_ORGOSIZE (0x184) +/* Real output DMA image size */ +#define EXYNOS_CIEXTEN (0x188) +/* DMA parameter */ +#define EXYNOS_CIDMAPARAM (0x18c) +/* MIPI CSI image format */ +#define EXYNOS_CSIIMGFMT (0x194) +/* FIMC Clock Source Select */ +#define EXYNOS_MISC_FIMC (0x198) + +/* Add for FIMC v5.1 */ +/* Output Frame Buffer Sequence */ +#define EXYNOS_CIFCNTSEQ (0x1fc) +/* Y 5th frame start address for output DMA */ +#define EXYNOS_CIOYSA5 (0x200) +/* Y 6th frame start address for output DMA */ +#define EXYNOS_CIOYSA6 (0x204) +/* Y 7th frame start address for output DMA */ +#define EXYNOS_CIOYSA7 (0x208) +/* Y 8th frame start address for output DMA */ +#define EXYNOS_CIOYSA8 (0x20c) +/* Y 9th frame start address for output DMA */ +#define EXYNOS_CIOYSA9 (0x210) +/* Y 10th frame start address for output DMA */ +#define EXYNOS_CIOYSA10 (0x214) +/* Y 11th frame start address for output DMA */ +#define EXYNOS_CIOYSA11 (0x218) +/* Y 12th frame start address for output DMA */ +#define EXYNOS_CIOYSA12 (0x21c) +/* Y 13th frame start address for output DMA */ +#define EXYNOS_CIOYSA13 (0x220) +/* Y 14th frame start address for output DMA */ +#define EXYNOS_CIOYSA14 (0x224) +/* Y 15th frame start address for output DMA */ +#define EXYNOS_CIOYSA15 (0x228) +/* Y 16th frame start address for output DMA */ +#define EXYNOS_CIOYSA16 (0x22c) +/* Y 17th frame start address for output DMA */ +#define EXYNOS_CIOYSA17 (0x230) +/* Y 18th frame start address for output DMA */ +#define EXYNOS_CIOYSA18 (0x234) +/* Y 19th frame start address for output DMA */ +#define EXYNOS_CIOYSA19 (0x238) +/* Y 20th frame start address for output DMA */ +#define EXYNOS_CIOYSA20 (0x23c) +/* Y 21th frame start address for output DMA */ +#define EXYNOS_CIOYSA21 (0x240) +/* Y 22th frame start address for output DMA */ +#define EXYNOS_CIOYSA22 (0x244) +/* Y 23th frame start address for output DMA */ +#define EXYNOS_CIOYSA23 (0x248) +/* Y 24th frame start address for output DMA */ +#define EXYNOS_CIOYSA24 (0x24c) +/* Y 25th frame start address for output DMA */ +#define EXYNOS_CIOYSA25 (0x250) +/* Y 26th frame start address for output DMA */ +#define EXYNOS_CIOYSA26 (0x254) +/* Y 27th frame start address for output DMA */ +#define EXYNOS_CIOYSA27 (0x258) +/* Y 28th frame start address for output DMA */ +#define EXYNOS_CIOYSA28 (0x25c) +/* Y 29th frame start address for output DMA */ +#define EXYNOS_CIOYSA29 (0x260) +/* Y 30th frame start address for output DMA */ +#define EXYNOS_CIOYSA30 (0x264) +/* Y 31th frame start address for output DMA */ +#define EXYNOS_CIOYSA31 (0x268) +/* Y 32th frame start address for output DMA */ +#define EXYNOS_CIOYSA32 (0x26c) + +/* CB 5th frame start address for output DMA */ +#define EXYNOS_CIOCBSA5 (0x270) +/* CB 6th frame start address for output DMA */ +#define EXYNOS_CIOCBSA6 (0x274) +/* CB 7th frame start address for output DMA */ +#define EXYNOS_CIOCBSA7 (0x278) +/* CB 8th frame start address for output DMA */ +#define EXYNOS_CIOCBSA8 (0x27c) +/* CB 9th frame start address for output DMA */ +#define EXYNOS_CIOCBSA9 (0x280) +/* CB 10th frame start address for output DMA */ +#define EXYNOS_CIOCBSA10 (0x284) +/* CB 11th frame start address for output DMA */ +#define EXYNOS_CIOCBSA11 (0x288) +/* CB 12th frame start address for output DMA */ +#define EXYNOS_CIOCBSA12 (0x28c) +/* CB 13th frame start address for output DMA */ +#define EXYNOS_CIOCBSA13 (0x290) +/* CB 14th frame start address for output DMA */ +#define EXYNOS_CIOCBSA14 (0x294) +/* CB 15th frame start address for output DMA */ +#define EXYNOS_CIOCBSA15 (0x298) +/* CB 16th frame start address for output DMA */ +#define EXYNOS_CIOCBSA16 (0x29c) +/* CB 17th frame start address for output DMA */ +#define EXYNOS_CIOCBSA17 (0x2a0) +/* CB 18th frame start address for output DMA */ +#define EXYNOS_CIOCBSA18 (0x2a4) +/* CB 19th frame start address for output DMA */ +#define EXYNOS_CIOCBSA19 (0x2a8) +/* CB 20th frame start address for output DMA */ +#define EXYNOS_CIOCBSA20 (0x2ac) +/* CB 21th frame start address for output DMA */ +#define EXYNOS_CIOCBSA21 (0x2b0) +/* CB 22th frame start address for output DMA */ +#define EXYNOS_CIOCBSA22 (0x2b4) +/* CB 23th frame start address for output DMA */ +#define EXYNOS_CIOCBSA23 (0x2b8) +/* CB 24th frame start address for output DMA */ +#define EXYNOS_CIOCBSA24 (0x2bc) +/* CB 25th frame start address for output DMA */ +#define EXYNOS_CIOCBSA25 (0x2c0) +/* CB 26th frame start address for output DMA */ +#define EXYNOS_CIOCBSA26 (0x2c4) +/* CB 27th frame start address for output DMA */ +#define EXYNOS_CIOCBSA27 (0x2c8) +/* CB 28th frame start address for output DMA */ +#define EXYNOS_CIOCBSA28 (0x2cc) +/* CB 29th frame start address for output DMA */ +#define EXYNOS_CIOCBSA29 (0x2d0) +/* CB 30th frame start address for output DMA */ +#define EXYNOS_CIOCBSA30 (0x2d4) +/* CB 31th frame start address for output DMA */ +#define EXYNOS_CIOCBSA31 (0x2d8) +/* CB 32th frame start address for output DMA */ +#define EXYNOS_CIOCBSA32 (0x2dc) + +/* CR 5th frame start address for output DMA */ +#define EXYNOS_CIOCRSA5 (0x2e0) +/* CR 6th frame start address for output DMA */ +#define EXYNOS_CIOCRSA6 (0x2e4) +/* CR 7th frame start address for output DMA */ +#define EXYNOS_CIOCRSA7 (0x2e8) +/* CR 8th frame start address for output DMA */ +#define EXYNOS_CIOCRSA8 (0x2ec) +/* CR 9th frame start address for output DMA */ +#define EXYNOS_CIOCRSA9 (0x2f0) +/* CR 10th frame start address for output DMA */ +#define EXYNOS_CIOCRSA10 (0x2f4) +/* CR 11th frame start address for output DMA */ +#define EXYNOS_CIOCRSA11 (0x2f8) +/* CR 12th frame start address for output DMA */ +#define EXYNOS_CIOCRSA12 (0x2fc) +/* CR 13th frame start address for output DMA */ +#define EXYNOS_CIOCRSA13 (0x300) +/* CR 14th frame start address for output DMA */ +#define EXYNOS_CIOCRSA14 (0x304) +/* CR 15th frame start address for output DMA */ +#define EXYNOS_CIOCRSA15 (0x308) +/* CR 16th frame start address for output DMA */ +#define EXYNOS_CIOCRSA16 (0x30c) +/* CR 17th frame start address for output DMA */ +#define EXYNOS_CIOCRSA17 (0x310) +/* CR 18th frame start address for output DMA */ +#define EXYNOS_CIOCRSA18 (0x314) +/* CR 19th frame start address for output DMA */ +#define EXYNOS_CIOCRSA19 (0x318) +/* CR 20th frame start address for output DMA */ +#define EXYNOS_CIOCRSA20 (0x31c) +/* CR 21th frame start address for output DMA */ +#define EXYNOS_CIOCRSA21 (0x320) +/* CR 22th frame start address for output DMA */ +#define EXYNOS_CIOCRSA22 (0x324) +/* CR 23th frame start address for output DMA */ +#define EXYNOS_CIOCRSA23 (0x328) +/* CR 24th frame start address for output DMA */ +#define EXYNOS_CIOCRSA24 (0x32c) +/* CR 25th frame start address for output DMA */ +#define EXYNOS_CIOCRSA25 (0x330) +/* CR 26th frame start address for output DMA */ +#define EXYNOS_CIOCRSA26 (0x334) +/* CR 27th frame start address for output DMA */ +#define EXYNOS_CIOCRSA27 (0x338) +/* CR 28th frame start address for output DMA */ +#define EXYNOS_CIOCRSA28 (0x33c) +/* CR 29th frame start address for output DMA */ +#define EXYNOS_CIOCRSA29 (0x340) +/* CR 30th frame start address for output DMA */ +#define EXYNOS_CIOCRSA30 (0x344) +/* CR 31th frame start address for output DMA */ +#define EXYNOS_CIOCRSA31 (0x348) +/* CR 32th frame start address for output DMA */ +#define EXYNOS_CIOCRSA32 (0x34c) + +/* + * Macro part +*/ +/* frame start address 1 ~ 4, 5 ~ 32 */ +/* Number of Default PingPong Memory */ +#define DEF_PP 4 +#define EXYNOS_CIOYSA(__x) \ + (((__x) < DEF_PP) ? \ + (EXYNOS_CIOYSA1 + (__x) * 4) : \ + (EXYNOS_CIOYSA5 + ((__x) - DEF_PP) * 4)) +#define EXYNOS_CIOCBSA(__x) \ + (((__x) < DEF_PP) ? \ + (EXYNOS_CIOCBSA1 + (__x) * 4) : \ + (EXYNOS_CIOCBSA5 + ((__x) - DEF_PP) * 4)) +#define EXYNOS_CIOCRSA(__x) \ + (((__x) < DEF_PP) ? \ + (EXYNOS_CIOCRSA1 + (__x) * 4) : \ + (EXYNOS_CIOCRSA5 + ((__x) - DEF_PP) * 4)) +/* Number of Default PingPong Memory */ +#define DEF_IPP 1 +#define EXYNOS_CIIYSA(__x) \ + (((__x) < DEF_IPP) ? \ + (EXYNOS_CIIYSA0) : (EXYNOS_CIIYSA1)) +#define EXYNOS_CIICBSA(__x) \ + (((__x) < DEF_IPP) ? \ + (EXYNOS_CIICBSA0) : (EXYNOS_CIICBSA1)) +#define EXYNOS_CIICRSA(__x) \ + (((__x) < DEF_IPP) ? \ + (EXYNOS_CIICRSA0) : (EXYNOS_CIICRSA1)) + +#define EXYNOS_CISRCFMT_SOURCEHSIZE(x) ((x) << 16) +#define EXYNOS_CISRCFMT_SOURCEVSIZE(x) ((x) << 0) + +#define EXYNOS_CIWDOFST_WINHOROFST(x) ((x) << 16) +#define EXYNOS_CIWDOFST_WINVEROFST(x) ((x) << 0) + +#define EXYNOS_CIWDOFST2_WINHOROFST2(x) ((x) << 16) +#define EXYNOS_CIWDOFST2_WINVEROFST2(x) ((x) << 0) + +#define EXYNOS_CITRGFMT_TARGETHSIZE(x) (((x) & 0x1fff) << 16) +#define EXYNOS_CITRGFMT_TARGETVSIZE(x) (((x) & 0x1fff) << 0) + +#define EXYNOS_CISCPRERATIO_SHFACTOR(x) ((x) << 28) +#define EXYNOS_CISCPRERATIO_PREHORRATIO(x) ((x) << 16) +#define EXYNOS_CISCPRERATIO_PREVERRATIO(x) ((x) << 0) + +#define EXYNOS_CISCPREDST_PREDSTWIDTH(x) ((x) << 16) +#define EXYNOS_CISCPREDST_PREDSTHEIGHT(x) ((x) << 0) + +#define EXYNOS_CISCCTRL_MAINHORRATIO(x) ((x) << 16) +#define EXYNOS_CISCCTRL_MAINVERRATIO(x) ((x) << 0) + +#define EXYNOS_CITAREA_TARGET_AREA(x) ((x) << 0) + +#define EXYNOS_CISTATUS_GET_FRAME_COUNT(x) (((x) >> 26) & 0x3) +#define EXYNOS_CISTATUS_GET_FRAME_END(x) (((x) >> 17) & 0x1) +#define EXYNOS_CISTATUS_GET_LAST_CAPTURE_END(x) (((x) >> 16) & 0x1) +#define EXYNOS_CISTATUS_GET_LCD_STATUS(x) (((x) >> 9) & 0x1) +#define EXYNOS_CISTATUS_GET_ENVID_STATUS(x) (((x) >> 8) & 0x1) + +#define EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(x) (((x) >> 7) & 0x3f) +#define EXYNOS_CISTATUS2_GET_FRAMECOUNT_PRESENT(x) ((x) & 0x3f) + +#define EXYNOS_CIIMGEFF_FIN(x) ((x & 0x7) << 26) +#define EXYNOS_CIIMGEFF_PAT_CB(x) ((x) << 13) +#define EXYNOS_CIIMGEFF_PAT_CR(x) ((x) << 0) + +#define EXYNOS_CIILINESKIP(x) (((x) & 0xf) << 24) + +#define EXYNOS_CIREAL_ISIZE_HEIGHT(x) ((x) << 16) +#define EXYNOS_CIREAL_ISIZE_WIDTH(x) ((x) << 0) + +#define EXYNOS_MSCTRL_SUCCESSIVE_COUNT(x) ((x) << 24) +#define EXYNOS_MSCTRL_GET_INDMA_STATUS(x) ((x) & 0x1) + +#define EXYNOS_CIOYOFF_VERTICAL(x) ((x) << 16) +#define EXYNOS_CIOYOFF_HORIZONTAL(x) ((x) << 0) + +#define EXYNOS_CIOCBOFF_VERTICAL(x) ((x) << 16) +#define EXYNOS_CIOCBOFF_HORIZONTAL(x) ((x) << 0) + +#define EXYNOS_CIOCROFF_VERTICAL(x) ((x) << 16) +#define EXYNOS_CIOCROFF_HORIZONTAL(x) ((x) << 0) + +#define EXYNOS_CIIYOFF_VERTICAL(x) ((x) << 16) +#define EXYNOS_CIIYOFF_HORIZONTAL(x) ((x) << 0) + +#define EXYNOS_CIICBOFF_VERTICAL(x) ((x) << 16) +#define EXYNOS_CIICBOFF_HORIZONTAL(x) ((x) << 0) + +#define EXYNOS_CIICROFF_VERTICAL(x) ((x) << 16) +#define EXYNOS_CIICROFF_HORIZONTAL(x) ((x) << 0) + +#define EXYNOS_ORGISIZE_VERTICAL(x) ((x) << 16) +#define EXYNOS_ORGISIZE_HORIZONTAL(x) ((x) << 0) + +#define EXYNOS_ORGOSIZE_VERTICAL(x) ((x) << 16) +#define EXYNOS_ORGOSIZE_HORIZONTAL(x) ((x) << 0) + +#define EXYNOS_CIEXTEN_TARGETH_EXT(x) ((((x) & 0x2000) >> 13) << 26) +#define EXYNOS_CIEXTEN_TARGETV_EXT(x) ((((x) & 0x2000) >> 13) << 24) +#define EXYNOS_CIEXTEN_MAINHORRATIO_EXT(x) (((x) & 0x3F) << 10) +#define EXYNOS_CIEXTEN_MAINVERRATIO_EXT(x) ((x) & 0x3F) + +/* + * Bit definition part +*/ +/* Source format register */ +#define EXYNOS_CISRCFMT_ITU601_8BIT (1 << 31) +#define EXYNOS_CISRCFMT_ITU656_8BIT (0 << 31) +#define EXYNOS_CISRCFMT_ITU601_16BIT (1 << 29) +#define EXYNOS_CISRCFMT_ORDER422_YCBYCR (0 << 14) +#define EXYNOS_CISRCFMT_ORDER422_YCRYCB (1 << 14) +#define EXYNOS_CISRCFMT_ORDER422_CBYCRY (2 << 14) +#define EXYNOS_CISRCFMT_ORDER422_CRYCBY (3 << 14) +/* ITU601 16bit only */ +#define EXYNOS_CISRCFMT_ORDER422_Y4CBCRCBCR (0 << 14) +/* ITU601 16bit only */ +#define EXYNOS_CISRCFMT_ORDER422_Y4CRCBCRCB (1 << 14) + +/* Window offset register */ +#define EXYNOS_CIWDOFST_WINOFSEN (1 << 31) +#define EXYNOS_CIWDOFST_CLROVFIY (1 << 30) +#define EXYNOS_CIWDOFST_CLROVRLB (1 << 29) +#define EXYNOS_CIWDOFST_WINHOROFST_MASK (0x7ff << 16) +#define EXYNOS_CIWDOFST_CLROVFICB (1 << 15) +#define EXYNOS_CIWDOFST_CLROVFICR (1 << 14) +#define EXYNOS_CIWDOFST_WINVEROFST_MASK (0xfff << 0) + +/* Global control register */ +#define EXYNOS_CIGCTRL_SWRST (1 << 31) +#define EXYNOS_CIGCTRL_CAMRST_A (1 << 30) +#define EXYNOS_CIGCTRL_SELCAM_ITU_B (0 << 29) +#define EXYNOS_CIGCTRL_SELCAM_ITU_A (1 << 29) +#define EXYNOS_CIGCTRL_SELCAM_ITU_MASK (1 << 29) +#define EXYNOS_CIGCTRL_TESTPATTERN_NORMAL (0 << 27) +#define EXYNOS_CIGCTRL_TESTPATTERN_COLOR_BAR (1 << 27) +#define EXYNOS_CIGCTRL_TESTPATTERN_HOR_INC (2 << 27) +#define EXYNOS_CIGCTRL_TESTPATTERN_VER_INC (3 << 27) +#define EXYNOS_CIGCTRL_TESTPATTERN_MASK (3 << 27) +#define EXYNOS_CIGCTRL_TESTPATTERN_SHIFT (27) +#define EXYNOS_CIGCTRL_INVPOLPCLK (1 << 26) +#define EXYNOS_CIGCTRL_INVPOLVSYNC (1 << 25) +#define EXYNOS_CIGCTRL_INVPOLHREF (1 << 24) +#define EXYNOS_CIGCTRL_IRQ_OVFEN (1 << 22) +#define EXYNOS_CIGCTRL_HREF_MASK (1 << 21) +#define EXYNOS_CIGCTRL_IRQ_EDGE (0 << 20) +#define EXYNOS_CIGCTRL_IRQ_LEVEL (1 << 20) +#define EXYNOS_CIGCTRL_IRQ_CLR (1 << 19) +#define EXYNOS_CIGCTRL_IRQ_END_DISABLE (1 << 18) +#define EXYNOS_CIGCTRL_IRQ_DISABLE (0 << 16) +#define EXYNOS_CIGCTRL_IRQ_ENABLE (1 << 16) +#define EXYNOS_CIGCTRL_SHADOW_DISABLE (1 << 12) +#define EXYNOS_CIGCTRL_CAM_JPEG (1 << 8) +#define EXYNOS_CIGCTRL_SELCAM_MIPI_B (0 << 7) +#define EXYNOS_CIGCTRL_SELCAM_MIPI_A (1 << 7) +#define EXYNOS_CIGCTRL_SELCAM_MIPI_MASK (1 << 7) +#define EXYNOS_CIGCTRL_SELWB_CAMIF_CAMERA (0 << 6) +#define EXYNOS_CIGCTRL_SELWB_CAMIF_WRITEBACK (1 << 6) +#define EXYNOS_CIGCTRL_SELWRITEBACK_MASK (1 << 10) +#define EXYNOS_CIGCTRL_SELWRITEBACK_A (1 << 10) +#define EXYNOS_CIGCTRL_SELWRITEBACK_B (0 << 10) +#define EXYNOS_CIGCTRL_SELWB_CAMIF_MASK (1 << 6) +#define EXYNOS_CIGCTRL_CSC_ITU601 (0 << 5) +#define EXYNOS_CIGCTRL_CSC_ITU709 (1 << 5) +#define EXYNOS_CIGCTRL_CSC_MASK (1 << 5) +#define EXYNOS_CIGCTRL_INVPOLHSYNC (1 << 4) +#define EXYNOS_CIGCTRL_SELCAM_FIMC_ITU (0 << 3) +#define EXYNOS_CIGCTRL_SELCAM_FIMC_MIPI (1 << 3) +#define EXYNOS_CIGCTRL_SELCAM_FIMC_MASK (1 << 3) +#define EXYNOS_CIGCTRL_PROGRESSIVE (0 << 0) +#define EXYNOS_CIGCTRL_INTERLACE (1 << 0) + +/* Window offset2 register */ +#define EXYNOS_CIWDOFST_WINHOROFST2_MASK (0xfff << 16) +#define EXYNOS_CIWDOFST_WINVEROFST2_MASK (0xfff << 16) + +/* Target format register */ +#define EXYNOS_CITRGFMT_INROT90_CLOCKWISE (1 << 31) +#define EXYNOS_CITRGFMT_OUTFORMAT_YCBCR420 (0 << 29) +#define EXYNOS_CITRGFMT_OUTFORMAT_YCBCR422 (1 << 29) +#define EXYNOS_CITRGFMT_OUTFORMAT_YCBCR422_1PLANE (2 << 29) +#define EXYNOS_CITRGFMT_OUTFORMAT_RGB (3 << 29) +#define EXYNOS_CITRGFMT_OUTFORMAT_MASK (3 << 29) +#define EXYNOS_CITRGFMT_FLIP_SHIFT (14) +#define EXYNOS_CITRGFMT_FLIP_NORMAL (0 << 14) +#define EXYNOS_CITRGFMT_FLIP_X_MIRROR (1 << 14) +#define EXYNOS_CITRGFMT_FLIP_Y_MIRROR (2 << 14) +#define EXYNOS_CITRGFMT_FLIP_180 (3 << 14) +#define EXYNOS_CITRGFMT_FLIP_MASK (3 << 14) +#define EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE (1 << 13) +#define EXYNOS_CITRGFMT_TARGETV_MASK (0x1fff << 0) +#define EXYNOS_CITRGFMT_TARGETH_MASK (0x1fff << 16) + +/* Output DMA control register */ +#define EXYNOS_CIOCTRL_WEAVE_OUT (1 << 31) +#define EXYNOS_CIOCTRL_WEAVE_MASK (1 << 31) +#define EXYNOS_CIOCTRL_LASTENDEN (1 << 30) +#define EXYNOS_CIOCTRL_ORDER2P_LSB_CBCR (0 << 24) +#define EXYNOS_CIOCTRL_ORDER2P_LSB_CRCB (1 << 24) +#define EXYNOS_CIOCTRL_ORDER2P_MSB_CRCB (2 << 24) +#define EXYNOS_CIOCTRL_ORDER2P_MSB_CBCR (3 << 24) +#define EXYNOS_CIOCTRL_ORDER2P_SHIFT (24) +#define EXYNOS_CIOCTRL_ORDER2P_MASK (3 << 24) +#define EXYNOS_CIOCTRL_YCBCR_3PLANE (0 << 3) +#define EXYNOS_CIOCTRL_YCBCR_2PLANE (1 << 3) +#define EXYNOS_CIOCTRL_YCBCR_PLANE_MASK (1 << 3) +#define EXYNOS_CIOCTRL_LASTIRQ_ENABLE (1 << 2) +#define EXYNOS_CIOCTRL_ALPHA_OUT (0xff << 4) +#define EXYNOS_CIOCTRL_ORDER422_YCBYCR (0 << 0) +#define EXYNOS_CIOCTRL_ORDER422_YCRYCB (1 << 0) +#define EXYNOS_CIOCTRL_ORDER422_CBYCRY (2 << 0) +#define EXYNOS_CIOCTRL_ORDER422_CRYCBY (3 << 0) +#define EXYNOS_CIOCTRL_ORDER422_MASK (3 << 0) + +/* Main scaler control register */ +#define EXYNOS_CISCCTRL_SCALERBYPASS (1 << 31) +#define EXYNOS_CISCCTRL_SCALEUP_H (1 << 30) +#define EXYNOS_CISCCTRL_SCALEUP_V (1 << 29) +#define EXYNOS_CISCCTRL_CSCR2Y_NARROW (0 << 28) +#define EXYNOS_CISCCTRL_CSCR2Y_WIDE (1 << 28) +#define EXYNOS_CISCCTRL_CSCY2R_NARROW (0 << 27) +#define EXYNOS_CISCCTRL_CSCY2R_WIDE (1 << 27) +#define EXYNOS_CISCCTRL_LCDPATHEN_FIFO (1 << 26) +#define EXYNOS_CISCCTRL_PROGRESSIVE (0 << 25) +#define EXYNOS_CISCCTRL_INTERLACE (1 << 25) +#define EXYNOS_CISCCTRL_SCAN_MASK (1 << 25) +#define EXYNOS_CISCCTRL_SCALERSTART (1 << 15) +#define EXYNOS_CISCCTRL_INRGB_FMT_RGB565 (0 << 13) +#define EXYNOS_CISCCTRL_INRGB_FMT_RGB666 (1 << 13) +#define EXYNOS_CISCCTRL_INRGB_FMT_RGB888 (2 << 13) +#define EXYNOS_CISCCTRL_INRGB_FMT_RGB_MASK (3 << 13) +#define EXYNOS_CISCCTRL_OUTRGB_FMT_RGB565 (0 << 11) +#define EXYNOS_CISCCTRL_OUTRGB_FMT_RGB666 (1 << 11) +#define EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888 (2 << 11) +#define EXYNOS_CISCCTRL_OUTRGB_FMT_RGB_MASK (3 << 11) +#define EXYNOS_CISCCTRL_EXTRGB_NORMAL (0 << 10) +#define EXYNOS_CISCCTRL_EXTRGB_EXTENSION (1 << 10) +#define EXYNOS_CISCCTRL_ONE2ONE (1 << 9) +#define EXYNOS_CISCCTRL_MAIN_V_RATIO_MASK (0x1ff << 0) +#define EXYNOS_CISCCTRL_MAIN_H_RATIO_MASK (0x1ff << 16) + +/* Status register */ +#define EXYNOS_CISTATUS_OVFIY (1 << 31) +#define EXYNOS_CISTATUS_OVFICB (1 << 30) +#define EXYNOS_CISTATUS_OVFICR (1 << 29) +#define EXYNOS_CISTATUS_VSYNC (1 << 28) +#define EXYNOS_CISTATUS_SCALERSTART (1 << 26) +#define EXYNOS_CISTATUS_WINOFSTEN (1 << 25) +#define EXYNOS_CISTATUS_IMGCPTEN (1 << 22) +#define EXYNOS_CISTATUS_IMGCPTENSC (1 << 21) +#define EXYNOS_CISTATUS_VSYNC_A (1 << 20) +#define EXYNOS_CISTATUS_VSYNC_B (1 << 19) +#define EXYNOS_CISTATUS_OVRLB (1 << 18) +#define EXYNOS_CISTATUS_FRAMEEND (1 << 17) +#define EXYNOS_CISTATUS_LASTCAPTUREEND (1 << 16) +#define EXYNOS_CISTATUS_VVALID_A (1 << 15) +#define EXYNOS_CISTATUS_VVALID_B (1 << 14) + +/* Image capture enable register */ +#define EXYNOS_CIIMGCPT_IMGCPTEN (1 << 31) +#define EXYNOS_CIIMGCPT_IMGCPTEN_SC (1 << 30) +#define EXYNOS_CIIMGCPT_CPT_FREN_ENABLE (1 << 25) +#define EXYNOS_CIIMGCPT_CPT_FRMOD_EN (0 << 18) +#define EXYNOS_CIIMGCPT_CPT_FRMOD_CNT (1 << 18) + +/* Image effects register */ +#define EXYNOS_CIIMGEFF_IE_DISABLE (0 << 30) +#define EXYNOS_CIIMGEFF_IE_ENABLE (1 << 30) +#define EXYNOS_CIIMGEFF_IE_SC_BEFORE (0 << 29) +#define EXYNOS_CIIMGEFF_IE_SC_AFTER (1 << 29) +#define EXYNOS_CIIMGEFF_FIN_BYPASS (0 << 26) +#define EXYNOS_CIIMGEFF_FIN_ARBITRARY (1 << 26) +#define EXYNOS_CIIMGEFF_FIN_NEGATIVE (2 << 26) +#define EXYNOS_CIIMGEFF_FIN_ARTFREEZE (3 << 26) +#define EXYNOS_CIIMGEFF_FIN_EMBOSSING (4 << 26) +#define EXYNOS_CIIMGEFF_FIN_SILHOUETTE (5 << 26) +#define EXYNOS_CIIMGEFF_FIN_MASK (7 << 26) +#define EXYNOS_CIIMGEFF_PAT_CBCR_MASK ((0xff < 13) | (0xff < 0)) + +/* Real input DMA size register */ +#define EXYNOS_CIREAL_ISIZE_AUTOLOAD_ENABLE (1 << 31) +#define EXYNOS_CIREAL_ISIZE_ADDR_CH_DISABLE (1 << 30) +#define EXYNOS_CIREAL_ISIZE_HEIGHT_MASK (0x3FFF << 16) +#define EXYNOS_CIREAL_ISIZE_WIDTH_MASK (0x3FFF << 0) + +/* Input DMA control register */ +#define EXYNOS_MSCTRL_FIELD_MASK (1 << 31) +#define EXYNOS_MSCTRL_FIELD_WEAVE (1 << 31) +#define EXYNOS_MSCTRL_FIELD_NORMAL (0 << 31) +#define EXYNOS_MSCTRL_BURST_CNT (24) +#define EXYNOS_MSCTRL_BURST_CNT_MASK (0xf << 24) +#define EXYNOS_MSCTRL_ORDER2P_LSB_CBCR (0 << 16) +#define EXYNOS_MSCTRL_ORDER2P_LSB_CRCB (1 << 16) +#define EXYNOS_MSCTRL_ORDER2P_MSB_CRCB (2 << 16) +#define EXYNOS_MSCTRL_ORDER2P_MSB_CBCR (3 << 16) +#define EXYNOS_MSCTRL_ORDER2P_SHIFT (16) +#define EXYNOS_MSCTRL_ORDER2P_SHIFT_MASK (0x3 << 16) +#define EXYNOS_MSCTRL_C_INT_IN_3PLANE (0 << 15) +#define EXYNOS_MSCTRL_C_INT_IN_2PLANE (1 << 15) +#define EXYNOS_MSCTRL_FLIP_SHIFT (13) +#define EXYNOS_MSCTRL_FLIP_NORMAL (0 << 13) +#define EXYNOS_MSCTRL_FLIP_X_MIRROR (1 << 13) +#define EXYNOS_MSCTRL_FLIP_Y_MIRROR (2 << 13) +#define EXYNOS_MSCTRL_FLIP_180 (3 << 13) +#define EXYNOS_MSCTRL_FLIP_MASK (3 << 13) +#define EXYNOS_MSCTRL_ORDER422_CRYCBY (0 << 4) +#define EXYNOS_MSCTRL_ORDER422_YCRYCB (1 << 4) +#define EXYNOS_MSCTRL_ORDER422_CBYCRY (2 << 4) +#define EXYNOS_MSCTRL_ORDER422_YCBYCR (3 << 4) +#define EXYNOS_MSCTRL_INPUT_EXTCAM (0 << 3) +#define EXYNOS_MSCTRL_INPUT_MEMORY (1 << 3) +#define EXYNOS_MSCTRL_INPUT_MASK (1 << 3) +#define EXYNOS_MSCTRL_INFORMAT_YCBCR420 (0 << 1) +#define EXYNOS_MSCTRL_INFORMAT_YCBCR422 (1 << 1) +#define EXYNOS_MSCTRL_INFORMAT_YCBCR422_1PLANE (2 << 1) +#define EXYNOS_MSCTRL_INFORMAT_RGB (3 << 1) +#define EXYNOS_MSCTRL_ENVID (1 << 0) + +/* DMA parameter register */ +#define EXYNOS_CIDMAPARAM_R_MODE_LINEAR (0 << 29) +#define EXYNOS_CIDMAPARAM_R_MODE_CONFTILE (1 << 29) +#define EXYNOS_CIDMAPARAM_R_MODE_16X16 (2 << 29) +#define EXYNOS_CIDMAPARAM_R_MODE_64X32 (3 << 29) +#define EXYNOS_CIDMAPARAM_R_MODE_MASK (3 << 29) +#define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_64 (0 << 24) +#define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_128 (1 << 24) +#define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_256 (2 << 24) +#define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_512 (3 << 24) +#define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_1024 (4 << 24) +#define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_2048 (5 << 24) +#define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_4096 (6 << 24) +#define EXYNOS_CIDMAPARAM_R_TILE_VSIZE_1 (0 << 20) +#define EXYNOS_CIDMAPARAM_R_TILE_VSIZE_2 (1 << 20) +#define EXYNOS_CIDMAPARAM_R_TILE_VSIZE_4 (2 << 20) +#define EXYNOS_CIDMAPARAM_R_TILE_VSIZE_8 (3 << 20) +#define EXYNOS_CIDMAPARAM_R_TILE_VSIZE_16 (4 << 20) +#define EXYNOS_CIDMAPARAM_R_TILE_VSIZE_32 (5 << 20) +#define EXYNOS_CIDMAPARAM_W_MODE_LINEAR (0 << 13) +#define EXYNOS_CIDMAPARAM_W_MODE_CONFTILE (1 << 13) +#define EXYNOS_CIDMAPARAM_W_MODE_16X16 (2 << 13) +#define EXYNOS_CIDMAPARAM_W_MODE_64X32 (3 << 13) +#define EXYNOS_CIDMAPARAM_W_MODE_MASK (3 << 13) +#define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_64 (0 << 8) +#define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_128 (1 << 8) +#define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_256 (2 << 8) +#define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_512 (3 << 8) +#define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_1024 (4 << 8) +#define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_2048 (5 << 8) +#define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_4096 (6 << 8) +#define EXYNOS_CIDMAPARAM_W_TILE_VSIZE_1 (0 << 4) +#define EXYNOS_CIDMAPARAM_W_TILE_VSIZE_2 (1 << 4) +#define EXYNOS_CIDMAPARAM_W_TILE_VSIZE_4 (2 << 4) +#define EXYNOS_CIDMAPARAM_W_TILE_VSIZE_8 (3 << 4) +#define EXYNOS_CIDMAPARAM_W_TILE_VSIZE_16 (4 << 4) +#define EXYNOS_CIDMAPARAM_W_TILE_VSIZE_32 (5 << 4) + +/* Gathering Extension register */ +#define EXYNOS_CIEXTEN_TARGETH_EXT_MASK (1 << 26) +#define EXYNOS_CIEXTEN_TARGETV_EXT_MASK (1 << 24) +#define EXYNOS_CIEXTEN_MAINHORRATIO_EXT_MASK (0x3F << 10) +#define EXYNOS_CIEXTEN_MAINVERRATIO_EXT_MASK (0x3F) +#define EXYNOS_CIEXTEN_YUV444_OUT (1 << 22) + +/* FIMC Clock Source Select register */ +#define EXYNOS_CLKSRC_HCLK (0 << 1) +#define EXYNOS_CLKSRC_HCLK_MASK (1 << 1) +#define EXYNOS_CLKSRC_SCLK (1 << 1) + +/* SYSREG for FIMC writeback */ +#define SYSREG_CAMERA_BLK (S3C_VA_SYS + 0x0218) +#define SYSREG_ISP_BLK (S3C_VA_SYS + 0x020c) +#define SYSREG_FIMD0WB_DEST_MASK (0x3 << 23) +#define SYSREG_FIMD0WB_DEST_SHIFT 23 + +#endif /* EXYNOS_REGS_FIMC_H */ diff --git a/include/drm/exynos_drm.h b/include/drm/exynos_drm.h index 3c13a3a4b158..808dad29607a 100644 --- a/include/drm/exynos_drm.h +++ b/include/drm/exynos_drm.h @@ -85,4 +85,30 @@ struct exynos_drm_hdmi_pdata { int (*get_hpd)(void); }; +/** + * Platform Specific Structure for DRM based IPP. + * + * @inv_pclk: if set 1. invert pixel clock + * @inv_vsync: if set 1. invert vsync signal for wb + * @inv_href: if set 1. invert href signal + * @inv_hsync: if set 1. invert hsync signal for wb + */ +struct exynos_drm_ipp_pol { + unsigned int inv_pclk; + unsigned int inv_vsync; + unsigned int inv_href; + unsigned int inv_hsync; +}; + +/** + * Platform Specific Structure for DRM based FIMC. + * + * @pol: current hardware block polarity settings. + * @clk_rate: current hardware clock rate. + */ +struct exynos_drm_fimc_pdata { + struct exynos_drm_ipp_pol pol; + int clk_rate; +}; + #endif /* _EXYNOS_DRM_H_ */ -- cgit v1.2.3-59-g8ed1b