From f930ddd0583c1a9e68d80a27d4e5077e795007b1 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 21 Nov 2012 15:55:21 +0100 Subject: drm/i915: remove duplicate register #defines Somehow a chunk of unused register defines ended up in the middle of the PLL defines. They go back to the original kms merging. The only used #define is SR01, move it to the register name together with the other legacy vga stuff. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 97fbd9d1823b..462b50a3d88e 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -142,6 +142,7 @@ #define VGA_MSR_CGA_MODE (1<<0) #define VGA_SR_INDEX 0x3c4 +#define SR01 1 #define VGA_SR_DATA 0x3c5 #define VGA_AR_INDEX 0x3c0 @@ -938,23 +939,6 @@ #define DPLL_LOCK_VLV (1<<15) #define DPLL_INTEGRATED_CLOCK_VLV (1<<13) -#define SRX_INDEX 0x3c4 -#define SRX_DATA 0x3c5 -#define SR01 1 -#define SR01_SCREEN_OFF (1<<5) - -#define PPCR 0x61204 -#define PPCR_ON (1<<0) - -#define DVOB 0x61140 -#define DVOB_ON (1<<31) -#define DVOC 0x61160 -#define DVOC_ON (1<<31) -#define LVDS 0x61180 -#define LVDS_ON (1<<31) - -/* Scratch pad debug 0 reg: - */ #define DPLL_FPA01_P1_POST_DIV_MASK_I830 0x001f0000 /* * The i830 generation, in LVDS mode, defines P1 as the bit number set within -- cgit v1.2.3 From ca9c46c5c77987acf1bf7137bf85e9221bc459ba Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 27 Nov 2012 20:34:58 +0200 Subject: drm/i915: Kill i915_gem_execbuffer_wait_for_flips() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As per Chris Wilson's suggestion make i915_gem_execbuffer_wait_for_flips() go away. This was used to stall the GPU ring while there are pending page flips involving the relevant BO. Ie. while the BO is still being scanned out by the display controller. The recommended alternative is to use the page flip events to wait for the page flips to fully complete before reusing the BO of the old front buffer. Or use more buffers. Signed-off-by: Ville Syrjälä Reviewed-by: Chris Wilson Acked-by: Kristian Høgsberg Acked-by: Jesse Barnes [danvet: don't remove obj->pending_flips, still required due to reorder patches.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 42 ------------------------------ drivers/gpu/drm/i915/intel_display.c | 7 ----- 2 files changed, 49 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index ee8f97f0539e..802d925b2b57 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -601,45 +601,12 @@ err: return ret; } -static int -i915_gem_execbuffer_wait_for_flips(struct intel_ring_buffer *ring, u32 flips) -{ - u32 plane, flip_mask; - int ret; - - /* Check for any pending flips. As we only maintain a flip queue depth - * of 1, we can simply insert a WAIT for the next display flip prior - * to executing the batch and avoid stalling the CPU. - */ - - for (plane = 0; flips >> plane; plane++) { - if (((flips >> plane) & 1) == 0) - continue; - - if (plane) - flip_mask = MI_WAIT_FOR_PLANE_B_FLIP; - else - flip_mask = MI_WAIT_FOR_PLANE_A_FLIP; - - ret = intel_ring_begin(ring, 2); - if (ret) - return ret; - - intel_ring_emit(ring, MI_WAIT_FOR_EVENT | flip_mask); - intel_ring_emit(ring, MI_NOOP); - intel_ring_advance(ring); - } - - return 0; -} - static int i915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring, struct list_head *objects) { struct drm_i915_gem_object *obj; uint32_t flush_domains = 0; - uint32_t flips = 0; int ret; list_for_each_entry(obj, objects, exec_list) { @@ -650,18 +617,9 @@ i915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring, if (obj->base.write_domain & I915_GEM_DOMAIN_CPU) i915_gem_clflush_object(obj); - if (obj->base.pending_write_domain) - flips |= atomic_read(&obj->pending_flip); - flush_domains |= obj->base.write_domain; } - if (flips) { - ret = i915_gem_execbuffer_wait_for_flips(ring, flips); - if (ret) - return ret; - } - if (flush_domains & I915_GEM_DOMAIN_CPU) i915_gem_chipset_flush(ring->dev); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 78d12c471472..8cdf3a1226dc 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6945,8 +6945,6 @@ static void do_intel_finish_page_flip(struct drm_device *dev, obj = work->old_fb_obj; - atomic_clear_mask(1 << intel_crtc->plane, - &obj->pending_flip.counter); wake_up(&dev_priv->pending_flip_queue); queue_work(dev_priv->wq, &work->work); @@ -7292,10 +7290,6 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, work->enable_stall_check = true; - /* Block clients from rendering to the new back buffer until - * the flip occurs and the object is no longer visible. - */ - atomic_add(1 << intel_crtc->plane, &work->old_fb_obj->pending_flip); atomic_inc(&intel_crtc->unpin_work_count); ret = dev_priv->display.queue_flip(dev, crtc, fb, obj); @@ -7312,7 +7306,6 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, cleanup_pending: atomic_dec(&intel_crtc->unpin_work_count); - atomic_sub(1 << intel_crtc->plane, &work->old_fb_obj->pending_flip); drm_gem_object_unreference(&work->old_fb_obj->base); drm_gem_object_unreference(&obj->base); mutex_unlock(&dev->struct_mutex); -- cgit v1.2.3 From 04b97b3422c3403696f3bcb52140b7ebeaee9d3c Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 27 Nov 2012 17:06:53 +0000 Subject: drm/i915/debugfs: Prune a couple of superfluous leading zeros from bo domains As we do not have any domains occupying the high bits, there is no point in always printing the leading 00. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 8afc0dd7de64..62619e39533f 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -102,7 +102,7 @@ static const char *cache_level_str(int type) static void describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) { - seq_printf(m, "%p: %s%s %8zdKiB %04x %04x %d %d %d%s%s%s", + seq_printf(m, "%p: %s%s %8zdKiB %02x %02x %d %d %d%s%s%s", &obj->base, get_pin_flag(obj), get_tiling_flag(obj), @@ -608,7 +608,7 @@ static void print_error_buffers(struct seq_file *m, seq_printf(m, "%s [%d]:\n", name, count); while (count--) { - seq_printf(m, " %08x %8u %04x %04x %x %x%s%s%s%s%s%s%s", + seq_printf(m, " %08x %8u %02x %02x %x %x%s%s%s%s%s%s%s", err->gtt_offset, err->size, err->read_domains, -- cgit v1.2.3 From dafd226c4f54eded10ba43c37789a6aa20b59c32 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 26 Nov 2012 17:22:07 +0100 Subject: drm/i915: add encoder->pre_pll_enable callback Currently we have two encoder specific bits in the common mode_set functions: - lvds pin pair enabling - dp m/n setting and computation Now the lvds stuff needs to happen before the pll is enabled. Since that is done in the crtc_mode_set functions, we need to add a new callback to be able to move them to the encoder code (where they belong). The dp m/n stuff is a giant mess anyway (since it also confuses itself with the fdi link m/n handling), so that needs to be handled separately. I think that we can move the pll enabling down quite a bit, which might allow us to eventually merge encoder->pre_enable with this new pre_pll_enable callback. But for now this will allow us to clean things up a bit. Note that vlv doesn't support lvds, hence we don't need to change anything in there. v2: Fixup commit message, both suggested from Paulo Zanoni. - dp m/n doesn't need to happen before pll enabling - lvds doesn't exist on vlv, hence no changes required in the vlv pll function. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 14 ++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 1 + 2 files changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8cdf3a1226dc..55d3e3dbc137 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4464,6 +4464,7 @@ static void i9xx_update_pll(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_encoder *encoder; int pipe = intel_crtc->pipe; u32 dpll; bool is_sdvo; @@ -4532,6 +4533,10 @@ static void i9xx_update_pll(struct drm_crtc *crtc, POSTING_READ(DPLL(pipe)); udelay(150); + for_each_encoder_on_crtc(dev, crtc, encoder) + if (encoder->pre_pll_enable) + encoder->pre_pll_enable(encoder); + /* The LVDS pin pair needs to be on before the DPLLs are enabled. * This is an exception to the general rule that mode_set doesn't turn * things on. @@ -4576,6 +4581,7 @@ static void i8xx_update_pll(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_encoder *encoder; int pipe = intel_crtc->pipe; u32 dpll; @@ -4609,6 +4615,10 @@ static void i8xx_update_pll(struct drm_crtc *crtc, POSTING_READ(DPLL(pipe)); udelay(150); + for_each_encoder_on_crtc(dev, crtc, encoder) + if (encoder->pre_pll_enable) + encoder->pre_pll_enable(encoder); + /* The LVDS pin pair needs to be on before the DPLLs are enabled. * This is an exception to the general rule that mode_set doesn't turn * things on. @@ -5537,6 +5547,10 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(TRANSDPLINK_N1(pipe), 0); } + for_each_encoder_on_crtc(dev, crtc, encoder) + if (encoder->pre_pll_enable) + encoder->pre_pll_enable(encoder); + if (intel_crtc->pch_pll) { I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 522061ca0685..a74c572f1d5e 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -153,6 +153,7 @@ struct intel_encoder { bool cloneable; bool connectors_active; void (*hot_plug)(struct intel_encoder *); + void (*pre_pll_enable)(struct intel_encoder *); void (*pre_enable)(struct intel_encoder *); void (*enable)(struct intel_encoder *); void (*disable)(struct intel_encoder *); -- cgit v1.2.3 From a210b028f07690c127733addbbe137e8f4cad30c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 26 Nov 2012 17:22:08 +0100 Subject: drm/i915: replace ad-hoc dual-link lvds checks ... with is_dual_link_lvds introduced in commit b03543857fd75876b96e10d4320b775e95041bb7 Author: Takashi Iwai Date: Tue Mar 20 13:07:05 2012 +0100 drm/i915: Check VBIOS value for determining LVDS dual channel mode, too All these checks predate this commit and have simply been overlooked. Since we don't support switching between single-link and dual-link modes anyway, this different checks could at best only get in the way of refactorings, and in the worst case cause inconsistencies. v2: Update the comment, we now have a solid way to figure out whether we need dual-link lvds or not (falling back to vbt values as a last resort). We still don't know how to switch between dual-link and single link so leave that part intact. I'm not sure though whether switching between these two modes makes any sense - we always drive the panel at its fixed mode (with a fixed bpc) anyway ... Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 55d3e3dbc137..be19b6d1ca52 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -690,13 +690,11 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, intel_clock_t clock; int err = target; - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && - (I915_READ(LVDS)) != 0) { + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { /* - * For LVDS, if the panel is on, just rely on its current - * settings for dual-channel. We haven't figured out how to - * reliably set up different single/dual channel state, if we - * even can. + * For LVDS just rely on its current settings for dual-channel. + * We haven't figured out how to reliably set up different + * single/dual channel state, if we even can. */ if (is_dual_link_lvds(dev_priv, LVDS)) clock.p2 = limit->p2.p2_fast; @@ -766,8 +764,7 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, lvds_reg = PCH_LVDS; else lvds_reg = LVDS; - if ((I915_READ(lvds_reg) & LVDS_CLKB_POWER_MASK) == - LVDS_CLKB_POWER_UP) + if (is_dual_link_lvds(dev_priv, lvds_reg)) clock.p2 = limit->p2.p2_fast; else clock.p2 = limit->p2.p2_slow; @@ -5359,7 +5356,7 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, if (is_lvds) { if ((intel_panel_use_ssc(dev_priv) && dev_priv->lvds_ssc_freq == 100) || - (I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP) + is_dual_link_lvds(dev_priv, PCH_LVDS)) factor = 25; } else if (is_sdvo && is_tv) factor = 20; -- cgit v1.2.3 From 1974cad0ee4ce84e5cb792e49c4f0d9421e0312c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 26 Nov 2012 17:22:09 +0100 Subject: drm/i915: move is_dual_link_lvds to intel_lvds.c Just a prep patch to make this a property of intel_lvds. Makes more sense, removes clutter from intel_display.c and eventually I want to move all the encoder special cases wrt clock handling to encoders anyway. v2: Add an intel_ prefixe to is_dual_link_lvds since it's non-static now. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 60 +++--------------------------------- drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_lvds.c | 53 +++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 55 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index be19b6d1ca52..2705da329ddf 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -472,61 +472,14 @@ static void vlv_init_dpio(struct drm_device *dev) POSTING_READ(DPIO_CTL); } -static int intel_dual_link_lvds_callback(const struct dmi_system_id *id) -{ - DRM_INFO("Forcing lvds to dual link mode on %s\n", id->ident); - return 1; -} - -static const struct dmi_system_id intel_dual_link_lvds[] = { - { - .callback = intel_dual_link_lvds_callback, - .ident = "Apple MacBook Pro (Core i5/i7 Series)", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro8,2"), - }, - }, - { } /* terminating entry */ -}; - -static bool is_dual_link_lvds(struct drm_i915_private *dev_priv, - unsigned int reg) -{ - unsigned int val; - - /* use the module option value if specified */ - if (i915_lvds_channel_mode > 0) - return i915_lvds_channel_mode == 2; - - if (dmi_check_system(intel_dual_link_lvds)) - return true; - - if (dev_priv->lvds_val) - val = dev_priv->lvds_val; - else { - /* BIOS should set the proper LVDS register value at boot, but - * in reality, it doesn't set the value when the lid is closed; - * we need to check "the value to be set" in VBT when LVDS - * register is uninitialized. - */ - val = I915_READ(reg); - if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED))) - val = dev_priv->bios_lvds_val; - dev_priv->lvds_val = val; - } - return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP; -} - static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc, int refclk) { struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; const intel_limit_t *limit; if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { - if (is_dual_link_lvds(dev_priv, PCH_LVDS)) { + if (intel_is_dual_link_lvds(dev)) { /* LVDS dual channel */ if (refclk == 100000) limit = &intel_limits_ironlake_dual_lvds_100m; @@ -550,11 +503,10 @@ static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc, static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; const intel_limit_t *limit; if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { - if (is_dual_link_lvds(dev_priv, LVDS)) + if (intel_is_dual_link_lvds(dev)) /* LVDS with dual channel */ limit = &intel_limits_g4x_dual_channel_lvds; else @@ -686,7 +638,6 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; intel_clock_t clock; int err = target; @@ -696,7 +647,7 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, * We haven't figured out how to reliably set up different * single/dual channel state, if we even can. */ - if (is_dual_link_lvds(dev_priv, LVDS)) + if (intel_is_dual_link_lvds(dev)) clock.p2 = limit->p2.p2_fast; else clock.p2 = limit->p2.p2_slow; @@ -749,7 +700,6 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, intel_clock_t *best_clock) { struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; intel_clock_t clock; int max_n; bool found; @@ -764,7 +714,7 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, lvds_reg = PCH_LVDS; else lvds_reg = LVDS; - if (is_dual_link_lvds(dev_priv, lvds_reg)) + if (intel_is_dual_link_lvds(dev)) clock.p2 = limit->p2.p2_fast; else clock.p2 = limit->p2.p2_slow; @@ -5356,7 +5306,7 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, if (is_lvds) { if ((intel_panel_use_ssc(dev_priv) && dev_priv->lvds_ssc_freq == 100) || - is_dual_link_lvds(dev_priv, PCH_LVDS)) + intel_is_dual_link_lvds(dev)) factor = 25; } else if (is_sdvo && is_tv) factor = 20; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index a74c572f1d5e..7ca7772eaccd 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -441,6 +441,7 @@ extern void intel_mark_idle(struct drm_device *dev); extern void intel_mark_fb_busy(struct drm_i915_gem_object *obj); extern void intel_mark_fb_idle(struct drm_i915_gem_object *obj); extern bool intel_lvds_init(struct drm_device *dev); +extern bool intel_is_dual_link_lvds(struct drm_device *dev); extern void intel_dp_init(struct drm_device *dev, int output_reg, enum port port); extern void intel_dp_init_connector(struct intel_digital_port *intel_dig_port, diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index b9a660a53677..4158a8839433 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -903,6 +903,59 @@ static bool lvds_is_present_in_vbt(struct drm_device *dev, return false; } +static int intel_dual_link_lvds_callback(const struct dmi_system_id *id) +{ + DRM_INFO("Forcing lvds to dual link mode on %s\n", id->ident); + return 1; +} + +static const struct dmi_system_id intel_dual_link_lvds[] = { + { + .callback = intel_dual_link_lvds_callback, + .ident = "Apple MacBook Pro (Core i5/i7 Series)", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro8,2"), + }, + }, + { } /* terminating entry */ +}; + +bool intel_is_dual_link_lvds(struct drm_device *dev) +{ + unsigned int val; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 lvds_reg; + + if (HAS_PCH_SPLIT(dev)) { + lvds_reg = PCH_LVDS; + } else { + lvds_reg = LVDS; + } + + /* use the module option value if specified */ + if (i915_lvds_channel_mode > 0) + return i915_lvds_channel_mode == 2; + + if (dmi_check_system(intel_dual_link_lvds)) + return true; + + if (dev_priv->lvds_val) + val = dev_priv->lvds_val; + else { + /* BIOS should set the proper LVDS register value at boot, but + * in reality, it doesn't set the value when the lid is closed; + * we need to check "the value to be set" in VBT when LVDS + * register is uninitialized. + */ + val = I915_READ(lvds_reg); + if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED))) + val = dev_priv->bios_lvds_val; + dev_priv->lvds_val = val; + } + return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP; +} + static bool intel_lvds_supported(struct drm_device *dev) { /* With the introduction of the PCH we gained a dedicated -- cgit v1.2.3 From 13c7d8703127334890c894ddab13b3a92a26580a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 26 Nov 2012 17:22:10 +0100 Subject: drm/i915: track is_dual_link in intel_lvds Yeah, all users (both the clock selection special cases and the lvds pin pair stuff) are still in common code, but this will change. v2: Rebase on top of Jani Nikula's panel rework. v3: Incorporate review from Paulo Zanoni: - s/__is_dual_link_lvds/compute_is_dual_link_lvds - kill dev_priv->lvds_val - drop spurious whitespace change v4: Add a debug printk to display the dual-link status, as suggested by Paulo Zanoni in review. Reviewed-by: Paulo Zanoni (v3) Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 1 - drivers/gpu/drm/i915/intel_lvds.c | 44 +++++++++++++++++++++++++++------------ 2 files changed, 31 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 79589bbff3db..9be7efd43c6a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -705,7 +705,6 @@ typedef struct drm_i915_private { unsigned int display_clock_mode:1; int lvds_ssc_freq; unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */ - unsigned int lvds_val; /* used for checking LVDS channel mode */ struct { int rate; int lanes; diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 4158a8839433..31fdbfd48e44 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -52,6 +52,7 @@ struct intel_lvds_encoder { u32 pfit_control; u32 pfit_pgm_ratios; bool pfit_dirty; + bool is_dual_link; struct intel_lvds_connector *attached_connector; }; @@ -922,6 +923,23 @@ static const struct dmi_system_id intel_dual_link_lvds[] = { }; bool intel_is_dual_link_lvds(struct drm_device *dev) +{ + struct intel_encoder *encoder; + struct intel_lvds_encoder *lvds_encoder; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, + base.head) { + if (encoder->type == INTEL_OUTPUT_LVDS) { + lvds_encoder = to_lvds_encoder(&encoder->base); + + return lvds_encoder->is_dual_link; + } + } + + return false; +} + +static bool compute_is_dual_link_lvds(struct drm_device *dev) { unsigned int val; struct drm_i915_private *dev_priv = dev->dev_private; @@ -940,19 +958,15 @@ bool intel_is_dual_link_lvds(struct drm_device *dev) if (dmi_check_system(intel_dual_link_lvds)) return true; - if (dev_priv->lvds_val) - val = dev_priv->lvds_val; - else { - /* BIOS should set the proper LVDS register value at boot, but - * in reality, it doesn't set the value when the lid is closed; - * we need to check "the value to be set" in VBT when LVDS - * register is uninitialized. - */ - val = I915_READ(lvds_reg); - if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED))) - val = dev_priv->bios_lvds_val; - dev_priv->lvds_val = val; - } + /* BIOS should set the proper LVDS register value at boot, but + * in reality, it doesn't set the value when the lid is closed; + * we need to check "the value to be set" in VBT when LVDS + * register is uninitialized. + */ + val = I915_READ(lvds_reg); + if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED))) + val = dev_priv->bios_lvds_val; + return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP; } @@ -1162,6 +1176,10 @@ bool intel_lvds_init(struct drm_device *dev) goto failed; out: + lvds_encoder->is_dual_link = compute_is_dual_link_lvds(dev); + DRM_DEBUG_KMS("detected %s-link lvds configuration\n", + lvds_encoder->is_dual_link ? "dual" : "single"); + /* * Unlock registers and just * leave them unlocked -- cgit v1.2.3 From 7dec060675125f17a547950d715018a63131a437 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Sep 2012 14:12:25 +0200 Subject: drm/i915: add intel_lvds->reg To ditch at least some of the PCH_SPLIT ? PCH_LVDS : LVDS code ... v2: Rebase on top of Jani Nikula's panel rework. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_lvds.c | 48 ++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 31fdbfd48e44..6b6ed642b29f 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -53,6 +53,7 @@ struct intel_lvds_encoder { u32 pfit_pgm_ratios; bool pfit_dirty; bool is_dual_link; + u32 reg; struct intel_lvds_connector *attached_connector; }; @@ -72,15 +73,10 @@ static bool intel_lvds_get_hw_state(struct intel_encoder *encoder, { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 lvds_reg, tmp; - - if (HAS_PCH_SPLIT(dev)) { - lvds_reg = PCH_LVDS; - } else { - lvds_reg = LVDS; - } + struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); + u32 tmp; - tmp = I915_READ(lvds_reg); + tmp = I915_READ(lvds_encoder->reg); if (!(tmp & LVDS_PORT_EN)) return false; @@ -102,19 +98,17 @@ static void intel_enable_lvds(struct intel_encoder *encoder) struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); struct drm_i915_private *dev_priv = dev->dev_private; - u32 ctl_reg, lvds_reg, stat_reg; + u32 ctl_reg, stat_reg; if (HAS_PCH_SPLIT(dev)) { ctl_reg = PCH_PP_CONTROL; - lvds_reg = PCH_LVDS; stat_reg = PCH_PP_STATUS; } else { ctl_reg = PP_CONTROL; - lvds_reg = LVDS; stat_reg = PP_STATUS; } - I915_WRITE(lvds_reg, I915_READ(lvds_reg) | LVDS_PORT_EN); + I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) | LVDS_PORT_EN); if (lvds_encoder->pfit_dirty) { /* @@ -133,7 +127,7 @@ static void intel_enable_lvds(struct intel_encoder *encoder) } I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON); - POSTING_READ(lvds_reg); + POSTING_READ(lvds_encoder->reg); if (wait_for((I915_READ(stat_reg) & PP_ON) != 0, 1000)) DRM_ERROR("timed out waiting for panel to power on\n"); @@ -145,15 +139,13 @@ static void intel_disable_lvds(struct intel_encoder *encoder) struct drm_device *dev = encoder->base.dev; struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); struct drm_i915_private *dev_priv = dev->dev_private; - u32 ctl_reg, lvds_reg, stat_reg; + u32 ctl_reg, stat_reg; if (HAS_PCH_SPLIT(dev)) { ctl_reg = PCH_PP_CONTROL; - lvds_reg = PCH_LVDS; stat_reg = PCH_PP_STATUS; } else { ctl_reg = PP_CONTROL; - lvds_reg = LVDS; stat_reg = PP_STATUS; } @@ -168,8 +160,8 @@ static void intel_disable_lvds(struct intel_encoder *encoder) lvds_encoder->pfit_dirty = true; } - I915_WRITE(lvds_reg, I915_READ(lvds_reg) & ~LVDS_PORT_EN); - POSTING_READ(lvds_reg); + I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) & ~LVDS_PORT_EN); + POSTING_READ(lvds_encoder->reg); } static int intel_lvds_mode_valid(struct drm_connector *connector, @@ -939,17 +931,11 @@ bool intel_is_dual_link_lvds(struct drm_device *dev) return false; } -static bool compute_is_dual_link_lvds(struct drm_device *dev) +static bool compute_is_dual_link_lvds(struct intel_lvds_encoder *lvds_encoder) { + struct drm_device *dev = lvds_encoder->base.base.dev; unsigned int val; struct drm_i915_private *dev_priv = dev->dev_private; - u32 lvds_reg; - - if (HAS_PCH_SPLIT(dev)) { - lvds_reg = PCH_LVDS; - } else { - lvds_reg = LVDS; - } /* use the module option value if specified */ if (i915_lvds_channel_mode > 0) @@ -963,7 +949,7 @@ static bool compute_is_dual_link_lvds(struct drm_device *dev) * we need to check "the value to be set" in VBT when LVDS * register is uninitialized. */ - val = I915_READ(lvds_reg); + val = I915_READ(lvds_encoder->reg); if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED))) val = dev_priv->bios_lvds_val; @@ -1076,6 +1062,12 @@ bool intel_lvds_init(struct drm_device *dev) connector->interlace_allowed = false; connector->doublescan_allowed = false; + if (HAS_PCH_SPLIT(dev)) { + lvds_encoder->reg = PCH_LVDS; + } else { + lvds_encoder->reg = LVDS; + } + /* create the scaling mode property */ drm_mode_create_scaling_mode_property(dev); drm_object_attach_property(&connector->base, @@ -1176,7 +1168,7 @@ bool intel_lvds_init(struct drm_device *dev) goto failed; out: - lvds_encoder->is_dual_link = compute_is_dual_link_lvds(dev); + lvds_encoder->is_dual_link = compute_is_dual_link_lvds(lvds_encoder); DRM_DEBUG_KMS("detected %s-link lvds configuration\n", lvds_encoder->is_dual_link ? "dual" : "single"); -- cgit v1.2.3 From fc683091eb57692679f629784f42dce453877430 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 26 Nov 2012 17:22:12 +0100 Subject: drm/i915: move intel_update_lvds to intel_lvds->pre_pll_enable A few things needed to change: - HAS_PCH_SPLIT since ilk+ is not yet converted to this. - s/LVDS/intel_lvds->reg/ to prep for ilk conversion - replace the clock.p2 == 7 check with a is_dual_link check - s/adjusted_mode/intel_lvds->fixed_mode v2: Rebase on top of Jani Nikula's panel rework. I'm wondering whether we shouldn't add an attached_panel pointer to intel_encoder, to replace the encoder private ->attached_connector pointers, since that's essentially what we need. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 59 ------------------------------------ drivers/gpu/drm/i915/intel_lvds.c | 57 ++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 59 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2705da329ddf..bb080fbde808 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4257,51 +4257,6 @@ static void i9xx_update_pll_dividers(struct drm_crtc *crtc, } } -static void intel_update_lvds(struct drm_crtc *crtc, intel_clock_t *clock, - struct drm_display_mode *adjusted_mode) -{ - struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int pipe = intel_crtc->pipe; - u32 temp; - - temp = I915_READ(LVDS); - temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; - if (pipe == 1) { - temp |= LVDS_PIPEB_SELECT; - } else { - temp &= ~LVDS_PIPEB_SELECT; - } - /* set the corresponsding LVDS_BORDER bit */ - temp |= dev_priv->lvds_border_bits; - /* Set the B0-B3 data pairs corresponding to whether we're going to - * set the DPLLs for dual-channel mode or not. - */ - if (clock->p2 == 7) - temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; - else - temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); - - /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) - * appropriately here, but we need to look more thoroughly into how - * panels behave in the two modes. - */ - /* set the dithering flag on LVDS as needed */ - if (INTEL_INFO(dev)->gen >= 4) { - if (dev_priv->lvds_dither) - temp |= LVDS_ENABLE_DITHER; - else - temp &= ~LVDS_ENABLE_DITHER; - } - temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY); - if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) - temp |= LVDS_HSYNC_POLARITY; - if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) - temp |= LVDS_VSYNC_POLARITY; - I915_WRITE(LVDS, temp); -} - static void vlv_update_pll(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, @@ -4484,13 +4439,6 @@ static void i9xx_update_pll(struct drm_crtc *crtc, if (encoder->pre_pll_enable) encoder->pre_pll_enable(encoder); - /* The LVDS pin pair needs to be on before the DPLLs are enabled. - * This is an exception to the general rule that mode_set doesn't turn - * things on. - */ - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) - intel_update_lvds(crtc, clock, adjusted_mode); - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) intel_dp_set_m_n(crtc, mode, adjusted_mode); @@ -4566,13 +4514,6 @@ static void i8xx_update_pll(struct drm_crtc *crtc, if (encoder->pre_pll_enable) encoder->pre_pll_enable(encoder); - /* The LVDS pin pair needs to be on before the DPLLs are enabled. - * This is an exception to the general rule that mode_set doesn't turn - * things on. - */ - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) - intel_update_lvds(crtc, clock, adjusted_mode); - I915_WRITE(DPLL(pipe), dpll); /* Wait for the clocks to stabilize. */ diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 6b6ed642b29f..c93ec03974d6 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -89,6 +89,62 @@ static bool intel_lvds_get_hw_state(struct intel_encoder *encoder, return true; } +/* The LVDS pin pair needs to be on before the DPLLs are enabled. + * This is an exception to the general rule that mode_set doesn't turn + * things on. + */ +static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder) +{ + struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); + struct drm_display_mode *fixed_mode = + lvds_encoder->attached_connector->base.panel.fixed_mode; + int pipe = intel_crtc->pipe; + u32 temp; + + /* pch split platforms are not yet converted. */ + if (HAS_PCH_SPLIT(dev)) + return; + + temp = I915_READ(lvds_encoder->reg); + temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; + if (pipe == 1) { + temp |= LVDS_PIPEB_SELECT; + } else { + temp &= ~LVDS_PIPEB_SELECT; + } + /* set the corresponsding LVDS_BORDER bit */ + temp |= dev_priv->lvds_border_bits; + /* Set the B0-B3 data pairs corresponding to whether we're going to + * set the DPLLs for dual-channel mode or not. + */ + if (lvds_encoder->is_dual_link) + temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; + else + temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); + + /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) + * appropriately here, but we need to look more thoroughly into how + * panels behave in the two modes. + */ + /* set the dithering flag on LVDS as needed */ + if (INTEL_INFO(dev)->gen >= 4) { + if (dev_priv->lvds_dither) + temp |= LVDS_ENABLE_DITHER; + else + temp &= ~LVDS_ENABLE_DITHER; + } + temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY); + if (fixed_mode->flags & DRM_MODE_FLAG_NHSYNC) + temp |= LVDS_HSYNC_POLARITY; + if (fixed_mode->flags & DRM_MODE_FLAG_NVSYNC) + temp |= LVDS_VSYNC_POLARITY; + + I915_WRITE(lvds_encoder->reg, temp); +} + /** * Sets the power state for the panel. */ @@ -1041,6 +1097,7 @@ bool intel_lvds_init(struct drm_device *dev) DRM_MODE_ENCODER_LVDS); intel_encoder->enable = intel_enable_lvds; + intel_encoder->pre_pll_enable = intel_pre_pll_enable_lvds; intel_encoder->disable = intel_disable_lvds; intel_encoder->get_hw_state = intel_lvds_get_hw_state; intel_connector->get_hw_state = intel_connector_get_hw_state; -- cgit v1.2.3 From 62810e5a9df3f47b7261e5d78fc1c33e550f2171 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 26 Nov 2012 17:22:13 +0100 Subject: drm/i915: enable intel_lvds->pre_pll_enable for ilk+, too Only two things needed adjustment: - pipe select for PCH_CPT - There's no dithering bit on ilk+ in the lvds ctl reg Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 40 ------------------------------------ drivers/gpu/drm/i915/intel_lvds.c | 24 ++++++++++++++-------- 2 files changed, 15 insertions(+), 49 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index bb080fbde808..5958b3bd8650 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5322,7 +5322,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, bool ok, has_reduced_clock = false; bool is_lvds = false, is_dp = false, is_cpu_edp = false; struct intel_encoder *encoder; - u32 temp; int ret; bool dither, fdi_config_ok; @@ -5386,45 +5385,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, } else intel_put_pch_pll(intel_crtc); - /* The LVDS pin pair needs to be on before the DPLLs are enabled. - * This is an exception to the general rule that mode_set doesn't turn - * things on. - */ - if (is_lvds) { - temp = I915_READ(PCH_LVDS); - temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; - if (HAS_PCH_CPT(dev)) { - temp &= ~PORT_TRANS_SEL_MASK; - temp |= PORT_TRANS_SEL_CPT(pipe); - } else { - if (pipe == 1) - temp |= LVDS_PIPEB_SELECT; - else - temp &= ~LVDS_PIPEB_SELECT; - } - - /* set the corresponsding LVDS_BORDER bit */ - temp |= dev_priv->lvds_border_bits; - /* Set the B0-B3 data pairs corresponding to whether we're going to - * set the DPLLs for dual-channel mode or not. - */ - if (clock.p2 == 7) - temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; - else - temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); - - /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) - * appropriately here, but we need to look more thoroughly into how - * panels behave in the two modes. - */ - temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY); - if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) - temp |= LVDS_HSYNC_POLARITY; - if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) - temp |= LVDS_VSYNC_POLARITY; - I915_WRITE(PCH_LVDS, temp); - } - if (is_dp && !is_cpu_edp) { intel_dp_set_m_n(crtc, mode, adjusted_mode); } else { diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index c93ec03974d6..778106961e80 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -104,17 +104,20 @@ static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder) int pipe = intel_crtc->pipe; u32 temp; - /* pch split platforms are not yet converted. */ - if (HAS_PCH_SPLIT(dev)) - return; - temp = I915_READ(lvds_encoder->reg); temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; - if (pipe == 1) { - temp |= LVDS_PIPEB_SELECT; + + if (HAS_PCH_CPT(dev)) { + temp &= ~PORT_TRANS_SEL_MASK; + temp |= PORT_TRANS_SEL_CPT(pipe); } else { - temp &= ~LVDS_PIPEB_SELECT; + if (pipe == 1) { + temp |= LVDS_PIPEB_SELECT; + } else { + temp &= ~LVDS_PIPEB_SELECT; + } } + /* set the corresponsding LVDS_BORDER bit */ temp |= dev_priv->lvds_border_bits; /* Set the B0-B3 data pairs corresponding to whether we're going to @@ -129,8 +132,11 @@ static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder) * appropriately here, but we need to look more thoroughly into how * panels behave in the two modes. */ - /* set the dithering flag on LVDS as needed */ - if (INTEL_INFO(dev)->gen >= 4) { + + /* Set the dithering flag on LVDS as needed, note that there is no + * special lvds dither control bit on pch-split platforms, dithering is + * only controlled through the PIPECONF reg. */ + if (INTEL_INFO(dev)->gen == 4) { if (dev_priv->lvds_dither) temp |= LVDS_ENABLE_DITHER; else -- cgit v1.2.3 From a39a68054f63da0ea3b4806e1bfad79670a93d9f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Nov 2012 15:40:05 +0100 Subject: drm/i915: simplify shmem pwrite/pread slowpath handling The shmem paths for pwrite/pread used a clever trick to hold onto a single page when dropping the big dev->struct_mutex for the slowpath. But this ran the risk of reinstating (or not completely purging) the backing storage when dropping purgeable objects. Hence the code needed to keep track of whether it ever dropped the lock, and if it did, manually check whether it needs to re-purge the backing storage. But thanks to the pages pin count introduced in commit a5570178c059cec59e9835be20bc8546377fa7b5 Author: Chris Wilson Date: Tue Sep 4 21:02:54 2012 +0100 drm/i915: Pin backing pages whilst exporting through a dmabuf vmap which allowed us to pin the backing storage and remove that page reference trick from shmem_pwrite/read in commit f60d7f0c1d55a935475ab394955cafddefaa6533 Author: Chris Wilson Date: Tue Sep 4 21:02:56 2012 +0100 drm/i915: Pin backing pages for pread and commit 755d22184f1e5015b040acee794542d9cf8a16c5 Author: Chris Wilson Date: Tue Sep 4 21:02:55 2012 +0100 drm/i915: Pin backing pages for pwrite we can now abolish this check. The slowpath cleanup completely disappears from pread, and for pwrite we're only left with the domain fixup in case someone moved the object out of the cpu domain from under us. A follow-on patch will optimize that a notch more. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 4ef8b5714337..36f629a79d88 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -407,7 +407,6 @@ i915_gem_shmem_pread(struct drm_device *dev, loff_t offset; int shmem_page_offset, page_length, ret = 0; int obj_do_bit17_swizzling, page_do_bit17_swizzling; - int hit_slowpath = 0; int prefaulted = 0; int needs_clflush = 0; struct scatterlist *sg; @@ -469,7 +468,6 @@ i915_gem_shmem_pread(struct drm_device *dev, if (ret == 0) goto next_page; - hit_slowpath = 1; mutex_unlock(&dev->struct_mutex); if (!prefaulted) { @@ -502,12 +500,6 @@ next_page: out: i915_gem_object_unpin_pages(obj); - if (hit_slowpath) { - /* Fixup: Kill any reinstated backing storage pages */ - if (obj->madv == __I915_MADV_PURGED) - i915_gem_object_truncate(obj); - } - return ret; } @@ -838,11 +830,8 @@ out: i915_gem_object_unpin_pages(obj); if (hit_slowpath) { - /* Fixup: Kill any reinstated backing storage pages */ - if (obj->madv == __I915_MADV_PURGED) - i915_gem_object_truncate(obj); - /* and flush dirty cachelines in case the object isn't in the cpu write - * domain anymore. */ + /* Fixup: Flush dirty cachelines in case the object isn't in the + * cpu write domain anymore. */ if (obj->base.write_domain != I915_GEM_DOMAIN_CPU) { i915_gem_clflush_object(obj); i915_gem_chipset_flush(dev); -- cgit v1.2.3 From 8dcf015eb967c718962c0690330d9a94d56f2c5d Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Nov 2012 16:53:58 +0100 Subject: drm/i915: optimize the shmem_pwrite slowpath handling Since we drop dev->struct_mutex when going through the slowpath, the object might have been moved out of the cpu domain. Hence we need to clflush the entire object to ensure that after the ioctl returns, everything is coherent again (interwoven writes are ill-defined anyway). But we only need to do this if we start in the cpu domain and the object requires flushing for coherency. So don't do the flushing if the object is coherent anyway or if we've done in-line clfushing already. v2: i915_gem_clflush_object already checks whether the object is coherent and if so, drops the flushing. Hence we don't need to check that ourselves, simplifying the condition. v3: Reorder the checks for better clarity (and adjust the comment accordingly), suggested by Chris Wilson. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 36f629a79d88..5c8df572cd17 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -830,9 +830,13 @@ out: i915_gem_object_unpin_pages(obj); if (hit_slowpath) { - /* Fixup: Flush dirty cachelines in case the object isn't in the - * cpu write domain anymore. */ - if (obj->base.write_domain != I915_GEM_DOMAIN_CPU) { + /* + * Fixup: Flush cpu caches in case we didn't flush the dirty + * cachelines in-line while writing and the object moved + * out of the cpu write domain while we've dropped the lock. + */ + if (!needs_clflush_after && + obj->base.write_domain != I915_GEM_DOMAIN_CPU) { i915_gem_clflush_object(obj); i915_gem_chipset_flush(dev); } -- cgit v1.2.3 From c1f63f9d688e68088ef743a48a3aa8e0dc01a160 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 23 Nov 2012 15:30:37 -0200 Subject: drm/i915: intel_prepare_ddi_buffers should be static It's not even declared on header files. Signed-off-by: Paulo Zanoni Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 852012b6fc5b..16d3e7854ced 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -84,7 +84,8 @@ static enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder) * in either FDI or DP modes only, as HDMI connections will work with both * of those */ -void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port, bool use_fdi_mode) +static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port, + bool use_fdi_mode) { struct drm_i915_private *dev_priv = dev->dev_private; u32 reg; -- cgit v1.2.3 From 20749730e39bba1c6100bec0e0d1a45c99db559e Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 23 Nov 2012 15:30:38 -0200 Subject: drm/i915: remove Haswell code from ironlake_fdi_pll_enable This function is not called on Haswell anymore. Signed-off-by: Paulo Zanoni Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5958b3bd8650..511b397b5aaa 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2787,18 +2787,14 @@ static void ironlake_fdi_pll_enable(struct intel_crtc *intel_crtc) POSTING_READ(reg); udelay(200); - /* On Haswell, the PLL configuration for ports and pipes is handled - * separately, as part of DDI setup */ - if (!IS_HASWELL(dev)) { - /* Enable CPU FDI TX PLL, always on for Ironlake */ - reg = FDI_TX_CTL(pipe); - temp = I915_READ(reg); - if ((temp & FDI_TX_PLL_ENABLE) == 0) { - I915_WRITE(reg, temp | FDI_TX_PLL_ENABLE); + /* Enable CPU FDI TX PLL, always on for Ironlake */ + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); + if ((temp & FDI_TX_PLL_ENABLE) == 0) { + I915_WRITE(reg, temp | FDI_TX_PLL_ENABLE); - POSTING_READ(reg); - udelay(100); - } + POSTING_READ(reg); + udelay(100); } } -- cgit v1.2.3 From affa935440733a79c5a9eb0e5357e2564ca4b355 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 23 Nov 2012 15:30:39 -0200 Subject: drm/i915: add HAS_DDI check And use it whenever we call code that uses the DDIs. We already have intel_ddi.c and prefix every function with intel_ddi_something instead of haswell_something, so I think replacing the checks with HAS_DDI makes more sense. Just a cosmetical change, yes I know, but I have this OCD... Signed-off-by: Paulo Zanoni Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/intel_crt.c | 2 +- drivers/gpu/drm/i915/intel_ddi.c | 2 +- drivers/gpu/drm/i915/intel_display.c | 19 +++++++++---------- drivers/gpu/drm/i915/intel_dp.c | 8 ++++---- drivers/gpu/drm/i915/intel_hdmi.c | 4 ++-- 6 files changed, 19 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 9be7efd43c6a..e908c498ed87 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1208,6 +1208,8 @@ struct drm_i915_file_private { #define HAS_PIPE_CONTROL(dev) (INTEL_INFO(dev)->gen >= 5) +#define HAS_DDI(dev) (IS_HASWELL(dev)) + #define INTEL_PCH_DEVICE_ID_MASK 0xff00 #define INTEL_PCH_IBX_DEVICE_ID_TYPE 0x3b00 #define INTEL_PCH_CPT_DEVICE_ID_TYPE 0x1c00 diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 5c7774396e10..bc07b3f0d5e7 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -771,7 +771,7 @@ void intel_crt_init(struct drm_device *dev) crt->base.disable = intel_disable_crt; crt->base.enable = intel_enable_crt; - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) crt->base.get_hw_state = intel_ddi_get_hw_state; else crt->base.get_hw_state = intel_crt_get_hw_state; diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 16d3e7854ced..197dede1870a 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -115,7 +115,7 @@ void intel_prepare_ddi(struct drm_device *dev) { int port; - if (IS_HASWELL(dev)) { + if (HAS_DDI(dev)) { for (port = PORT_A; port < PORT_E; port++) intel_prepare_ddi_buffers(dev, port, false); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 511b397b5aaa..52b6b0e811bc 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1072,8 +1072,8 @@ static void assert_fdi_tx(struct drm_i915_private *dev_priv, enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, pipe); - if (IS_HASWELL(dev_priv->dev)) { - /* On Haswell, DDI is used instead of FDI_TX_CTL */ + if (HAS_DDI(dev_priv->dev)) { + /* DDI does not have a specific FDI_TX register */ reg = TRANS_DDI_FUNC_CTL(cpu_transcoder); val = I915_READ(reg); cur_state = !!(val & TRANS_DDI_FUNC_ENABLE); @@ -1117,7 +1117,7 @@ static void assert_fdi_tx_pll_enabled(struct drm_i915_private *dev_priv, return; /* On Haswell, DDI ports are responsible for the FDI PLL setup */ - if (IS_HASWELL(dev_priv->dev)) + if (HAS_DDI(dev_priv->dev)) return; reg = FDI_TX_CTL(pipe); @@ -7976,7 +7976,7 @@ static const struct drm_crtc_funcs intel_crtc_funcs = { static void intel_cpu_pll_init(struct drm_device *dev) { - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) intel_ddi_pll_init(dev); } @@ -8112,11 +8112,10 @@ static void intel_setup_outputs(struct drm_device *dev) I915_WRITE(PFIT_CONTROL, 0); } - if (!(IS_HASWELL(dev) && - (I915_READ(DDI_BUF_CTL(PORT_A)) & DDI_A_4_LANES))) + if (!(HAS_DDI(dev) && (I915_READ(DDI_BUF_CTL(PORT_A)) & DDI_A_4_LANES))) intel_crt_init(dev); - if (IS_HASWELL(dev)) { + if (HAS_DDI(dev)) { int found; /* Haswell uses DDI functions to detect digital outputs */ @@ -8360,7 +8359,7 @@ static void intel_init_display(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; /* We always want a DPMS function */ - if (IS_HASWELL(dev)) { + if (HAS_DDI(dev)) { dev_priv->display.crtc_mode_set = haswell_crtc_mode_set; dev_priv->display.crtc_enable = haswell_crtc_enable; dev_priv->display.crtc_disable = haswell_crtc_disable; @@ -8849,7 +8848,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, struct intel_encoder *encoder; struct intel_connector *connector; - if (IS_HASWELL(dev)) { + if (HAS_DDI(dev)) { tmp = I915_READ(TRANS_DDI_FUNC_CTL(TRANSCODER_EDP)); if (tmp & TRANS_DDI_FUNC_ENABLE) { @@ -8890,7 +8889,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, crtc->active ? "enabled" : "disabled"); } - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) intel_ddi_setup_hw_pll_state(dev); list_for_each_entry(encoder, &dev->mode_config.encoder_list, diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index d76258dcb8f8..f2904380a6f6 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -379,7 +379,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, * clock divider. */ if (is_cpu_edp(intel_dp)) { - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) aux_clock_divider = intel_ddi_get_cdclk_freq(dev_priv) >> 1; else if (IS_VALLEYVIEW(dev)) aux_clock_divider = 100; @@ -1791,7 +1791,7 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) int voltage_tries, loop_tries; uint32_t DP = intel_dp->DP; - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) intel_ddi_prepare_link_retrain(encoder); /* Write the link configuration data */ @@ -1981,7 +1981,7 @@ intel_dp_link_down(struct intel_dp *intel_dp) * intel_ddi_prepare_link_retrain will take care of redoing the link * train. */ - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) return; if (WARN_ON((I915_READ(intel_dp->output_reg) & DP_PORT_EN) == 0)) @@ -2742,7 +2742,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, intel_connector_attach_encoder(intel_connector, intel_encoder); drm_sysfs_connector_add(connector); - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) intel_connector->get_hw_state = intel_ddi_connector_get_hw_state; else intel_connector->get_hw_state = intel_connector_get_hw_state; diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 2ee9821b9d93..53df0a88a6a8 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -48,7 +48,7 @@ assert_hdmi_port_disabled(struct intel_hdmi *intel_hdmi) struct drm_i915_private *dev_priv = dev->dev_private; uint32_t enabled_bits; - enabled_bits = IS_HASWELL(dev) ? DDI_BUF_CTL_ENABLE : SDVO_ENABLE; + enabled_bits = HAS_DDI(dev) ? DDI_BUF_CTL_ENABLE : SDVO_ENABLE; WARN(I915_READ(intel_hdmi->sdvox_reg) & enabled_bits, "HDMI port enabled, expecting disabled\n"); @@ -1013,7 +1013,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, intel_hdmi->set_infoframes = cpt_set_infoframes; } - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) intel_connector->get_hw_state = intel_ddi_connector_get_hw_state; else intel_connector->get_hw_state = intel_connector_get_hw_state; -- cgit v1.2.3 From 0d536cb4e911c2496073f268e9730a9badaafa55 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 23 Nov 2012 16:46:41 -0200 Subject: drm/i915: invert the log inside intel_prepare_ddi Do an early return in case we don't have DDI instead of having the whole function inside an "if" statement. Signed-off-by: Paulo Zanoni Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 197dede1870a..ad936c68e4cb 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -115,16 +115,17 @@ void intel_prepare_ddi(struct drm_device *dev) { int port; - if (HAS_DDI(dev)) { - for (port = PORT_A; port < PORT_E; port++) - intel_prepare_ddi_buffers(dev, port, false); + if (!HAS_DDI(dev)) + return; - /* DDI E is the suggested one to work in FDI mode, so program is as such by - * default. It will have to be re-programmed in case a digital DP output - * will be detected on it - */ - intel_prepare_ddi_buffers(dev, PORT_E, true); - } + for (port = PORT_A; port < PORT_E; port++) + intel_prepare_ddi_buffers(dev, port, false); + + /* DDI E is the suggested one to work in FDI mode, so program is as such + * by default. It will have to be re-programmed in case a digital DP + * output will be detected on it + */ + intel_prepare_ddi_buffers(dev, PORT_E, true); } static const long hsw_ddi_buf_ctl_values[] = { -- cgit v1.2.3 From 9fa5f6522e6eecb5ab20192a264a29ba4f2f4e85 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Thu, 29 Nov 2012 11:31:29 -0200 Subject: drm/i915: kill intel_dp_link_clock() Use drm_dp_bw_code_to_link_rate insead. It's the same thing, but supports DP_LINK_BW_5_4 and is also used by the other drivers. Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index f2904380a6f6..b51043e651b5 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -148,15 +148,6 @@ intel_dp_max_link_bw(struct intel_dp *intel_dp) return max_link_bw; } -static int -intel_dp_link_clock(uint8_t link_bw) -{ - if (link_bw == DP_LINK_BW_2_7) - return 270000; - else - return 162000; -} - /* * The units on the numbers in the next two are... bizarre. Examples will * make it clearer; this one parallels an example in the eDP spec. @@ -191,7 +182,8 @@ intel_dp_adjust_dithering(struct intel_dp *intel_dp, struct drm_display_mode *mode, bool adjust_mode) { - int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_dp)); + int max_link_clock = + drm_dp_bw_code_to_link_rate(intel_dp_max_link_bw(intel_dp)); int max_lanes = drm_dp_max_lane_count(intel_dp->dpcd); int max_rate, mode_rate; @@ -722,12 +714,15 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, for (clock = 0; clock <= max_clock; clock++) { for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) { - int link_avail = intel_dp_max_data_rate(intel_dp_link_clock(bws[clock]), lane_count); + int link_bw_clock = + drm_dp_bw_code_to_link_rate(bws[clock]); + int link_avail = intel_dp_max_data_rate(link_bw_clock, + lane_count); if (mode_rate <= link_avail) { intel_dp->link_bw = bws[clock]; intel_dp->lane_count = lane_count; - adjusted_mode->clock = intel_dp_link_clock(intel_dp->link_bw); + adjusted_mode->clock = link_bw_clock; DRM_DEBUG_KMS("DP link bw %02x lane " "count %d clock %d bpp %d\n", intel_dp->link_bw, intel_dp->lane_count, -- cgit v1.2.3 From 99057c81037ee45e94e5c7e6b403b73caccbaf9e Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 29 Nov 2012 12:45:06 -0800 Subject: i915: convert struct spinlock to spinlock_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit spinlock_t should always be used. LD drivers/gpu/drm/i915/built-in.o CHECK drivers/gpu/drm/i915/i915_drv.c CC [M] drivers/gpu/drm/i915/i915_drv.o CHECK drivers/gpu/drm/i915/i915_dma.c CC [M] drivers/gpu/drm/i915/i915_dma.o CHECK drivers/gpu/drm/i915/i915_irq.c CC [M] drivers/gpu/drm/i915/i915_irq.o CHECK drivers/gpu/drm/i915/i915_debugfs.c drivers/gpu/drm/i915/i915_debugfs.c:558:31: warning: dereference of noderef expression drivers/gpu/drm/i915/i915_debugfs.c:558:39: warning: dereference of noderef expression drivers/gpu/drm/i915/i915_debugfs.c:558:51: warning: dereference of noderef expression drivers/gpu/drm/i915/i915_debugfs.c:558:63: warning: dereference of noderef expression CC [M] drivers/gpu/drm/i915/i915_debugfs.o CHECK drivers/gpu/drm/i915/i915_suspend.c CC [M] drivers/gpu/drm/i915/i915_suspend.o CHECK drivers/gpu/drm/i915/i915_gem.c drivers/gpu/drm/i915/i915_gem.c:3703:14: warning: incorrect type in assignment (different base types) drivers/gpu/drm/i915/i915_gem.c:3703:14: expected unsigned int [unsigned] [usertype] mask drivers/gpu/drm/i915/i915_gem.c:3703:14: got restricted gfp_t drivers/gpu/drm/i915/i915_gem.c:3706:22: warning: invalid assignment: &= drivers/gpu/drm/i915/i915_gem.c:3706:22: left side has type unsigned int drivers/gpu/drm/i915/i915_gem.c:3706:22: right side has type restricted gfp_t drivers/gpu/drm/i915/i915_gem.c:3707:22: warning: invalid assignment: |= drivers/gpu/drm/i915/i915_gem.c:3707:22: left side has type unsigned int drivers/gpu/drm/i915/i915_gem.c:3707:22: right side has type restricted gfp_t drivers/gpu/drm/i915/i915_gem.c:3711:39: warning: incorrect type in argument 2 (different base types) drivers/gpu/drm/i915/i915_gem.c:3711:39: expected restricted gfp_t [usertype] mask drivers/gpu/drm/i915/i915_gem.c:3711:39: got unsigned int [unsigned] [usertype] mask CC [M] drivers/gpu/drm/i915/i915_gem.o CHECK drivers/gpu/drm/i915/i915_gem_context.c CC [M] drivers/gpu/drm/i915/i915_gem_context.o CHECK drivers/gpu/drm/i915/i915_gem_debug.c CC [M] drivers/gpu/drm/i915/i915_gem_debug.o CHECK drivers/gpu/drm/i915/i915_gem_evict.c CC [M] drivers/gpu/drm/i915/i915_gem_evict.o CHECK drivers/gpu/drm/i915/i915_gem_execbuffer.c CC [M] drivers/gpu/drm/i915/i915_gem_execbuffer.o CHECK drivers/gpu/drm/i915/i915_gem_gtt.c CC [M] drivers/gpu/drm/i915/i915_gem_gtt.o CHECK drivers/gpu/drm/i915/i915_gem_stolen.c CC [M] drivers/gpu/drm/i915/i915_gem_stolen.o CHECK drivers/gpu/drm/i915/i915_gem_tiling.c CC [M] drivers/gpu/drm/i915/i915_gem_tiling.o CHECK drivers/gpu/drm/i915/i915_sysfs.c CC [M] drivers/gpu/drm/i915/i915_sysfs.o CHECK drivers/gpu/drm/i915/i915_trace_points.c CC [M] drivers/gpu/drm/i915/i915_trace_points.o CHECK drivers/gpu/drm/i915/intel_display.c drivers/gpu/drm/i915/intel_display.c:1736:9: warning: mixing different enum types drivers/gpu/drm/i915/intel_display.c:1736:9: int enum transcoder versus drivers/gpu/drm/i915/intel_display.c:1736:9: int enum pipe drivers/gpu/drm/i915/intel_display.c:3659:48: warning: mixing different enum types drivers/gpu/drm/i915/intel_display.c:3659:48: int enum pipe versus drivers/gpu/drm/i915/intel_display.c:3659:48: int enum transcoder CC [M] drivers/gpu/drm/i915/intel_display.o CHECK drivers/gpu/drm/i915/intel_crt.c CC [M] drivers/gpu/drm/i915/intel_crt.o CHECK drivers/gpu/drm/i915/intel_lvds.c CC [M] drivers/gpu/drm/i915/intel_lvds.o CHECK drivers/gpu/drm/i915/intel_bios.c drivers/gpu/drm/i915/intel_bios.c:706:60: warning: incorrect type in initializer (different address spaces) drivers/gpu/drm/i915/intel_bios.c:706:60: expected struct vbt_header *vbt drivers/gpu/drm/i915/intel_bios.c:706:60: got void [noderef] *vbt drivers/gpu/drm/i915/intel_bios.c:726:42: warning: incorrect type in argument 1 (different address spaces) drivers/gpu/drm/i915/intel_bios.c:726:42: expected void const * drivers/gpu/drm/i915/intel_bios.c:726:42: got unsigned char [noderef] [usertype] * drivers/gpu/drm/i915/intel_bios.c:727:40: warning: cast removes address space of expression drivers/gpu/drm/i915/intel_bios.c:738:24: warning: cast removes address space of expression CC [M] drivers/gpu/drm/i915/intel_bios.o CHECK drivers/gpu/drm/i915/intel_ddi.c drivers/gpu/drm/i915/intel_ddi.c:87:6: warning: symbol 'intel_prepare_ddi_buffers' was not declared. Should it be static? drivers/gpu/drm/i915/intel_ddi.c:1036:34: warning: mixing different enum types drivers/gpu/drm/i915/intel_ddi.c:1036:34: int enum pipe versus drivers/gpu/drm/i915/intel_ddi.c:1036:34: int enum transcoder CC [M] drivers/gpu/drm/i915/intel_ddi.o drivers/gpu/drm/i915/intel_ddi.c: In function ‘intel_ddi_setup_hw_pll_state’: drivers/gpu/drm/i915/intel_ddi.c:1129:2: warning: ‘port’ may be used uninitialized in this function [-Wmaybe-uninitialized] drivers/gpu/drm/i915/intel_ddi.c:1111:12: note: ‘port’ was declared here CHECK drivers/gpu/drm/i915/intel_dp.c CC [M] drivers/gpu/drm/i915/intel_dp.o CHECK drivers/gpu/drm/i915/intel_hdmi.c CC [M] drivers/gpu/drm/i915/intel_hdmi.o CHECK drivers/gpu/drm/i915/intel_sdvo.c CC [M] drivers/gpu/drm/i915/intel_sdvo.o CHECK drivers/gpu/drm/i915/intel_modes.c CC [M] drivers/gpu/drm/i915/intel_modes.o CHECK drivers/gpu/drm/i915/intel_panel.c CC [M] drivers/gpu/drm/i915/intel_panel.o CHECK drivers/gpu/drm/i915/intel_pm.c drivers/gpu/drm/i915/intel_pm.c:2173:1: warning: symbol 'mchdev_lock' was not declared. Should it be static? CC [M] drivers/gpu/drm/i915/intel_pm.o CHECK drivers/gpu/drm/i915/intel_i2c.c CC [M] drivers/gpu/drm/i915/intel_i2c.o CHECK drivers/gpu/drm/i915/intel_fb.c CC [M] drivers/gpu/drm/i915/intel_fb.o CHECK drivers/gpu/drm/i915/intel_tv.c CC [M] drivers/gpu/drm/i915/intel_tv.o CHECK drivers/gpu/drm/i915/intel_dvo.c CC [M] drivers/gpu/drm/i915/intel_dvo.o CHECK drivers/gpu/drm/i915/intel_ringbuffer.c CC [M] drivers/gpu/drm/i915/intel_ringbuffer.o CHECK drivers/gpu/drm/i915/intel_overlay.c CC [M] drivers/gpu/drm/i915/intel_overlay.o CHECK drivers/gpu/drm/i915/intel_sprite.c CC [M] drivers/gpu/drm/i915/intel_sprite.o CHECK drivers/gpu/drm/i915/intel_opregion.c CC [M] drivers/gpu/drm/i915/intel_opregion.o CHECK drivers/gpu/drm/i915/dvo_ch7xxx.c CC [M] drivers/gpu/drm/i915/dvo_ch7xxx.o CHECK drivers/gpu/drm/i915/dvo_ch7017.c CC [M] drivers/gpu/drm/i915/dvo_ch7017.o CHECK drivers/gpu/drm/i915/dvo_ivch.c CC [M] drivers/gpu/drm/i915/dvo_ivch.o CHECK drivers/gpu/drm/i915/dvo_tfp410.c CC [M] drivers/gpu/drm/i915/dvo_tfp410.o CHECK drivers/gpu/drm/i915/dvo_sil164.c CC [M] drivers/gpu/drm/i915/dvo_sil164.o CHECK drivers/gpu/drm/i915/dvo_ns2501.c CC [M] drivers/gpu/drm/i915/dvo_ns2501.o CHECK drivers/gpu/drm/i915/i915_gem_dmabuf.c CC [M] drivers/gpu/drm/i915/i915_gem_dmabuf.o CHECK drivers/gpu/drm/i915/i915_ioc32.c CC [M] drivers/gpu/drm/i915/i915_ioc32.o CHECK drivers/gpu/drm/i915/intel_acpi.c CC [M] drivers/gpu/drm/i915/intel_acpi.o LD [M] drivers/gpu/drm/i915/i915.o Building modules, stage 2. MODPOST 1 modules CC drivers/gpu/drm/i915/i915.mod.o LD [M] drivers/gpu/drm/i915/i915.ko Cc: Daniel Vetter Cc: intel-gfx@lists.freedesktop.org Cc: dri-devel@lists.freedesktop.org Reported-by: Hauke Mehrtens Signed-off-by: Luis R. Rodriguez Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e908c498ed87..31ab43b79736 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -628,7 +628,7 @@ typedef struct drm_i915_private { /** forcewake_count is protected by gt_lock */ unsigned forcewake_count; /** gt_lock is also taken in irq contexts. */ - struct spinlock gt_lock; + spinlock_t gt_lock; struct intel_gmbus gmbus[GMBUS_NUM_PORTS]; @@ -1128,7 +1128,7 @@ struct drm_i915_gem_request { struct drm_i915_file_private { struct { - struct spinlock lock; + spinlock_t lock; struct list_head request_list; } mm; struct idr context_idr; -- cgit v1.2.3 From acd15b6cc20f85bcef9e08b6ed4f142c34791c32 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 30 Nov 2012 11:24:50 +0100 Subject: drm/i915: optimize ilk/snb irq handler We only need to read/write the south interrupt register if the corresponding bit is set in the north master interrupt register. Noticed while reading our interrupt handling code. Same optimization has already been applied on ivb in commit 0e43406bcc1868a316eea6012a0a09d992c53521 Author: Chris Wilson Date: Wed May 9 21:45:44 2012 +0100 drm/i915: Simplify interrupt processing for IvyBridge We can take advantage that the PCH_IIR is a subordinate register to reduce one of the required IIR reads, and that we only need to clear interrupts handled to reduce the writes. And by simply tidying the code we can reduce the line count and hopefully make it more readable. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 6cd3dc94b472..26753ee6571d 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -758,7 +758,7 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) struct drm_device *dev = (struct drm_device *) arg; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int ret = IRQ_NONE; - u32 de_iir, gt_iir, de_ier, pch_iir, pm_iir; + u32 de_iir, gt_iir, de_ier, pm_iir; atomic_inc(&dev_priv->irq_received); @@ -769,11 +769,9 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) de_iir = I915_READ(DEIIR); gt_iir = I915_READ(GTIIR); - pch_iir = I915_READ(SDEIIR); pm_iir = I915_READ(GEN6_PMIIR); - if (de_iir == 0 && gt_iir == 0 && pch_iir == 0 && - (!IS_GEN6(dev) || pm_iir == 0)) + if (de_iir == 0 && gt_iir == 0 && (!IS_GEN6(dev) || pm_iir == 0)) goto done; ret = IRQ_HANDLED; @@ -804,10 +802,15 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) /* check event from PCH */ if (de_iir & DE_PCH_EVENT) { + u32 pch_iir = I915_READ(SDEIIR); + if (HAS_PCH_CPT(dev)) cpt_irq_handler(dev, pch_iir); else ibx_irq_handler(dev, pch_iir); + + /* should clear PCH hotplug event before clear CPU irq */ + I915_WRITE(SDEIIR, pch_iir); } if (IS_GEN5(dev) && de_iir & DE_PCU_EVENT) @@ -816,8 +819,6 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) if (IS_GEN6(dev) && pm_iir & GEN6_PM_DEFERRED_EVENTS) gen6_queue_rps_work(dev_priv, pm_iir); - /* should clear PCH hotplug event before clear CPU irq */ - I915_WRITE(SDEIIR, pch_iir); I915_WRITE(GTIIR, gt_iir); I915_WRITE(DEIIR, de_iir); I915_WRITE(GEN6_PMIIR, pm_iir); -- cgit v1.2.3 From 5973c7ee519e2a240c68b290a1836bdb25ed3701 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:16 +0000 Subject: drm: Introduce drm_mm_create_block() To be used later by i915 to preallocate exact blocks of space from the range manager. Signed-off-by: Chris Wilson Cc: Dave Airlie Acked-by: Dave Airlie Cc: dri-devel@lists.freedesktop.org Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_mm.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_mm.h | 4 ++++ 2 files changed, 54 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index 0761a03cdbb2..bd203b61a72b 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -161,6 +161,56 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, } } +struct drm_mm_node *drm_mm_create_block(struct drm_mm *mm, + unsigned long start, + unsigned long size, + bool atomic) +{ + struct drm_mm_node *hole, *node; + unsigned long end = start + size; + + list_for_each_entry(hole, &mm->hole_stack, hole_stack) { + unsigned long hole_start; + unsigned long hole_end; + + BUG_ON(!hole->hole_follows); + hole_start = drm_mm_hole_node_start(hole); + hole_end = drm_mm_hole_node_end(hole); + + if (hole_start > start || hole_end < end) + continue; + + node = drm_mm_kmalloc(mm, atomic); + if (unlikely(node == NULL)) + return NULL; + + node->start = start; + node->size = size; + node->mm = mm; + node->allocated = 1; + + INIT_LIST_HEAD(&node->hole_stack); + list_add(&node->node_list, &hole->node_list); + + if (start == hole_start) { + hole->hole_follows = 0; + list_del_init(&hole->hole_stack); + } + + node->hole_follows = 0; + if (end != hole_end) { + list_add(&node->hole_stack, &mm->hole_stack); + node->hole_follows = 1; + } + + return node; + } + + WARN(1, "no hole found for block 0x%lx + 0x%lx\n", start, size); + return NULL; +} +EXPORT_SYMBOL(drm_mm_create_block); + struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *hole_node, unsigned long size, unsigned alignment, diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index 06d7f798a08c..4020f9661c3c 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -102,6 +102,10 @@ static inline bool drm_mm_initialized(struct drm_mm *mm) /* * Basic range manager support (drm_mm.c) */ +extern struct drm_mm_node *drm_mm_create_block(struct drm_mm *mm, + unsigned long start, + unsigned long size, + bool atomic); extern struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *node, unsigned long size, unsigned alignment, -- cgit v1.2.3 From 9e8944ab564f2e3dde90a518cd32048c58918608 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:17 +0000 Subject: drm: Introduce an iterator over holes in the drm_mm range manager This will be used i915 in forthcoming patches in order to measure the largest contiguous chunk of memory available for enabling chipset features. v2: Try to make the macro marginally safer and more readable by not depending upon the drm_mm_hole_node_end() being non-zero. Note that we need to open code list_for_each() in order to update the hole_start, hole_end variable on each iteration and keep the macro sane. v3: Tidy up few BUG_ONs that fell foul of adding additional tests to drm_mm_hole_node_start(). Signed-off-by: Chris Wilson Cc: Dave Airlie Acked-by: Dave Airlie Cc: dri-devel@lists.freedesktop.org Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_mm.c | 62 ++++++++++++++++-------------------------------- include/drm/drm_mm.h | 36 ++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 41 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index bd203b61a72b..b751b8e1e205 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -102,20 +102,6 @@ int drm_mm_pre_get(struct drm_mm *mm) } EXPORT_SYMBOL(drm_mm_pre_get); -static inline unsigned long drm_mm_hole_node_start(struct drm_mm_node *hole_node) -{ - return hole_node->start + hole_node->size; -} - -static inline unsigned long drm_mm_hole_node_end(struct drm_mm_node *hole_node) -{ - struct drm_mm_node *next_node = - list_entry(hole_node->node_list.next, struct drm_mm_node, - node_list); - - return next_node->start; -} - static void drm_mm_insert_helper(struct drm_mm_node *hole_node, struct drm_mm_node *node, unsigned long size, unsigned alignment, @@ -127,7 +113,7 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, unsigned long adj_start = hole_start; unsigned long adj_end = hole_end; - BUG_ON(!hole_node->hole_follows || node->allocated); + BUG_ON(node->allocated); if (mm->color_adjust) mm->color_adjust(hole_node, color, &adj_start, &adj_end); @@ -155,7 +141,7 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, BUG_ON(node->start + node->size > adj_end); node->hole_follows = 0; - if (node->start + node->size < hole_end) { + if (__drm_mm_hole_node_start(node) < hole_end) { list_add(&node->hole_stack, &mm->hole_stack); node->hole_follows = 1; } @@ -168,15 +154,10 @@ struct drm_mm_node *drm_mm_create_block(struct drm_mm *mm, { struct drm_mm_node *hole, *node; unsigned long end = start + size; + unsigned long hole_start; + unsigned long hole_end; - list_for_each_entry(hole, &mm->hole_stack, hole_stack) { - unsigned long hole_start; - unsigned long hole_end; - - BUG_ON(!hole->hole_follows); - hole_start = drm_mm_hole_node_start(hole); - hole_end = drm_mm_hole_node_end(hole); - + drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { if (hole_start > start || hole_end < end) continue; @@ -293,7 +274,7 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, BUG_ON(node->start + node->size > end); node->hole_follows = 0; - if (node->start + node->size < hole_end) { + if (__drm_mm_hole_node_start(node) < hole_end) { list_add(&node->hole_stack, &mm->hole_stack); node->hole_follows = 1; } @@ -358,12 +339,13 @@ void drm_mm_remove_node(struct drm_mm_node *node) list_entry(node->node_list.prev, struct drm_mm_node, node_list); if (node->hole_follows) { - BUG_ON(drm_mm_hole_node_start(node) - == drm_mm_hole_node_end(node)); + BUG_ON(__drm_mm_hole_node_start(node) == + __drm_mm_hole_node_end(node)); list_del(&node->hole_stack); } else - BUG_ON(drm_mm_hole_node_start(node) - != drm_mm_hole_node_end(node)); + BUG_ON(__drm_mm_hole_node_start(node) != + __drm_mm_hole_node_end(node)); + if (!prev_node->hole_follows) { prev_node->hole_follows = 1; @@ -421,6 +403,8 @@ struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, { struct drm_mm_node *entry; struct drm_mm_node *best; + unsigned long adj_start; + unsigned long adj_end; unsigned long best_size; BUG_ON(mm->scanned_blocks); @@ -428,17 +412,13 @@ struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, best = NULL; best_size = ~0UL; - list_for_each_entry(entry, &mm->hole_stack, hole_stack) { - unsigned long adj_start = drm_mm_hole_node_start(entry); - unsigned long adj_end = drm_mm_hole_node_end(entry); - + drm_mm_for_each_hole(entry, mm, adj_start, adj_end) { if (mm->color_adjust) { mm->color_adjust(entry, color, &adj_start, &adj_end); if (adj_end <= adj_start) continue; } - BUG_ON(!entry->hole_follows); if (!check_free_hole(adj_start, adj_end, size, alignment)) continue; @@ -465,6 +445,8 @@ struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, { struct drm_mm_node *entry; struct drm_mm_node *best; + unsigned long adj_start; + unsigned long adj_end; unsigned long best_size; BUG_ON(mm->scanned_blocks); @@ -472,13 +454,11 @@ struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, best = NULL; best_size = ~0UL; - list_for_each_entry(entry, &mm->hole_stack, hole_stack) { - unsigned long adj_start = drm_mm_hole_node_start(entry) < start ? - start : drm_mm_hole_node_start(entry); - unsigned long adj_end = drm_mm_hole_node_end(entry) > end ? - end : drm_mm_hole_node_end(entry); - - BUG_ON(!entry->hole_follows); + drm_mm_for_each_hole(entry, mm, adj_start, adj_end) { + if (adj_start < start) + adj_start = start; + if (adj_end > end) + adj_end = end; if (mm->color_adjust) { mm->color_adjust(entry, color, &adj_start, &adj_end); diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index 4020f9661c3c..cd453653f634 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -89,6 +89,29 @@ static inline bool drm_mm_initialized(struct drm_mm *mm) { return mm->hole_stack.next; } + +static inline unsigned long __drm_mm_hole_node_start(struct drm_mm_node *hole_node) +{ + return hole_node->start + hole_node->size; +} + +static inline unsigned long drm_mm_hole_node_start(struct drm_mm_node *hole_node) +{ + BUG_ON(!hole_node->hole_follows); + return __drm_mm_hole_node_start(hole_node); +} + +static inline unsigned long __drm_mm_hole_node_end(struct drm_mm_node *hole_node) +{ + return list_entry(hole_node->node_list.next, + struct drm_mm_node, node_list)->start; +} + +static inline unsigned long drm_mm_hole_node_end(struct drm_mm_node *hole_node) +{ + return __drm_mm_hole_node_end(hole_node); +} + #define drm_mm_for_each_node(entry, mm) list_for_each_entry(entry, \ &(mm)->head_node.node_list, \ node_list) @@ -99,6 +122,19 @@ static inline bool drm_mm_initialized(struct drm_mm *mm) entry != NULL; entry = next, \ next = entry ? list_entry(entry->node_list.next, \ struct drm_mm_node, node_list) : NULL) \ + +/* Note that we need to unroll list_for_each_entry in order to inline + * setting hole_start and hole_end on each iteration and keep the + * macro sane. + */ +#define drm_mm_for_each_hole(entry, mm, hole_start, hole_end) \ + for (entry = list_entry((mm)->hole_stack.next, struct drm_mm_node, hole_stack); \ + &entry->hole_stack != &(mm)->hole_stack ? \ + hole_start = drm_mm_hole_node_start(entry), \ + hole_end = drm_mm_hole_node_end(entry), \ + 1 : 0; \ + entry = list_entry(entry->hole_stack.next, struct drm_mm_node, hole_stack)) + /* * Basic range manager support (drm_mm.c) */ -- cgit v1.2.3 From e12a2d53ae45a69aea499b64f75e7222cca0f12f Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:18 +0000 Subject: drm/i915: Fix detection of base of stolen memory The routine to query the base of stolen memory was using the wrong registers and the wrong encodings on virtually every platform. It was not until the G33 refresh, that a PCI config register was introduced that explicitly said where the stolen memory was. Prior to 865G there was not even a register that said where the end of usable low memory was and where the stolen memory began (or ended depending upon chipset). Before then, one has to look at the BIOS memory maps to find the Top of Memory. Alas that is not exported by arch/x86 and so we have to resort to disabling stolen memory on gen2 for the time being. Then SandyBridge enlarged the PCI register to a full 32-bits and change the encoding of the address, so even though we happened to be querying the right register, we read the wrong bits and ended up using address 0 for our stolen data, i.e. notably FBC. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_gem_stolen.c | 81 +++++++++++++++++----------------- 2 files changed, 42 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 31ab43b79736..21360a7e2c8f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -765,6 +765,7 @@ typedef struct drm_i915_private { unsigned long gtt_start; unsigned long gtt_mappable_end; unsigned long gtt_end; + unsigned long stolen_base; /* limited to low memory (32-bit) */ struct io_mapping *gtt_mapping; phys_addr_t gtt_base_addr; diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 8e91083b126f..be24312cd32f 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -42,56 +42,50 @@ * for is a boon. */ -#define PTE_ADDRESS_MASK 0xfffff000 -#define PTE_ADDRESS_MASK_HIGH 0x000000f0 /* i915+ */ -#define PTE_MAPPING_TYPE_UNCACHED (0 << 1) -#define PTE_MAPPING_TYPE_DCACHE (1 << 1) /* i830 only */ -#define PTE_MAPPING_TYPE_CACHED (3 << 1) -#define PTE_MAPPING_TYPE_MASK (3 << 1) -#define PTE_VALID (1 << 0) - -/** - * i915_stolen_to_phys - take an offset into stolen memory and turn it into - * a physical one - * @dev: drm device - * @offset: address to translate - * - * Some chip functions require allocations from stolen space and need the - * physical address of the memory in question. - */ -static unsigned long i915_stolen_to_phys(struct drm_device *dev, u32 offset) +static unsigned long i915_stolen_to_physical(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct pci_dev *pdev = dev_priv->bridge_dev; u32 base; -#if 0 /* On the machines I have tested the Graphics Base of Stolen Memory - * is unreliable, so compute the base by subtracting the stolen memory - * from the Top of Low Usable DRAM which is where the BIOS places - * the graphics stolen memory. + * is unreliable, so on those compute the base by subtracting the + * stolen memory from the Top of Low Usable DRAM which is where the + * BIOS places the graphics stolen memory. + * + * On gen2, the layout is slightly different with the Graphics Segment + * immediately following Top of Memory (or Top of Usable DRAM). Note + * it appears that TOUD is only reported by 865g, so we just use the + * top of memory as determined by the e820 probe. + * + * XXX gen2 requires an unavailable symbol and 945gm fails with + * its value of TOLUD. */ - if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) { - /* top 32bits are reserved = 0 */ + base = 0; + if (INTEL_INFO(dev)->gen >= 6) { + /* Read Base Data of Stolen Memory Register (BDSM) directly. + * Note that there is also a MCHBAR miror at 0x1080c0 or + * we could use device 2:0x5c instead. + */ + pci_read_config_dword(pdev, 0xB0, &base); + base &= ~4095; /* lower bits used for locking register */ + } else if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) { + /* Read Graphics Base of Stolen Memory directly */ pci_read_config_dword(pdev, 0xA4, &base); - } else { - /* XXX presume 8xx is the same as i915 */ - pci_bus_read_config_dword(pdev->bus, 2, 0x5C, &base); - } -#else - if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) { - u16 val; - pci_read_config_word(pdev, 0xb0, &val); - base = val >> 4 << 20; - } else { +#if 0 + } else if (IS_GEN3(dev)) { u8 val; + /* Stolen is immediately below Top of Low Usable DRAM */ pci_read_config_byte(pdev, 0x9c, &val); base = val >> 3 << 27; - } - base -= dev_priv->mm.gtt->stolen_size; + base -= dev_priv->mm.gtt->stolen_size; + } else { + /* Stolen is immediately above Top of Memory */ + base = max_low_pfn_mapped << PAGE_SHIFT; #endif + } - return base + offset; + return base; } static void i915_warn_stolen(struct drm_device *dev) @@ -116,7 +110,7 @@ static void i915_setup_compression(struct drm_device *dev, int size) if (!compressed_fb) goto err; - cfb_base = i915_stolen_to_phys(dev, compressed_fb->start); + cfb_base = dev_priv->mm.stolen_base + compressed_fb->start; if (!cfb_base) goto err_fb; @@ -129,7 +123,7 @@ static void i915_setup_compression(struct drm_device *dev, int size) if (!compressed_llb) goto err_fb; - ll_base = i915_stolen_to_phys(dev, compressed_llb->start); + ll_base = dev_priv->mm.stolen_base + compressed_llb->start; if (!ll_base) goto err_llb; } @@ -148,7 +142,7 @@ static void i915_setup_compression(struct drm_device *dev, int size) } DRM_DEBUG_KMS("FBC base 0x%08lx, ll base 0x%08lx, size %dM\n", - cfb_base, ll_base, size >> 20); + (long)cfb_base, (long)ll_base, size >> 20); return; err_llb: @@ -180,6 +174,13 @@ int i915_gem_init_stolen(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; unsigned long prealloc_size = dev_priv->mm.gtt->stolen_size; + dev_priv->mm.stolen_base = i915_stolen_to_physical(dev); + if (dev_priv->mm.stolen_base == 0) + return 0; + + DRM_DEBUG_KMS("found %d bytes of stolen memory at %08lx\n", + dev_priv->mm.gtt->stolen_size, dev_priv->mm.stolen_base); + /* Basic memrange allocator for stolen space */ drm_mm_init(&dev_priv->mm.stolen, 0, prealloc_size); -- cgit v1.2.3 From ed2f3452677e46a4270c25c1b7fa3e060fdd501e Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:19 +0000 Subject: drm/i915: Avoid clearing preallocated regions from the GTT As yet we do not do any preallocation (chicken-and-egg problem), but we may like to preserve anything already allocated by the BIOS or grub and reuse for own purposes after initialising the driver. Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/i915_gem_gtt.c | 32 +++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 21360a7e2c8f..f16101fa8b4a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -935,6 +935,8 @@ enum i915_cache_level { I915_CACHE_LLC_MLC, /* gen6+, in docs at least! */ }; +#define I915_GTT_RESERVED ((struct drm_mm_node *)0x1) + struct drm_i915_gem_object_ops { /* Interface between the GEM object and its backing storage. * get_pages() is called once prior to the use of the associated set diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index f7ac61ee1504..cc1be53a65f9 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -531,20 +531,46 @@ void i915_gem_init_global_gtt(struct drm_device *dev, unsigned long end) { drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_mm_node *entry; + struct drm_i915_gem_object *obj; + unsigned long hole_start, hole_end; - /* Substract the guard page ... */ + /* Subtract the guard page ... */ drm_mm_init(&dev_priv->mm.gtt_space, start, end - start - PAGE_SIZE); if (!HAS_LLC(dev)) dev_priv->mm.gtt_space.color_adjust = i915_gtt_color_adjust; + /* Mark any preallocated objects as occupied */ + list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) { + DRM_DEBUG_KMS("reserving preallocated space: %x + %zx\n", + obj->gtt_offset, obj->base.size); + + BUG_ON(obj->gtt_space != I915_GTT_RESERVED); + obj->gtt_space = drm_mm_create_block(&dev_priv->mm.gtt_space, + obj->gtt_offset, + obj->base.size, + false); + obj->has_global_gtt_mapping = 1; + } + dev_priv->mm.gtt_start = start; dev_priv->mm.gtt_mappable_end = mappable_end; dev_priv->mm.gtt_end = end; dev_priv->mm.gtt_total = end - start; dev_priv->mm.mappable_gtt_total = min(end, mappable_end) - start; - /* ... but ensure that we clear the entire range. */ - i915_ggtt_clear_range(dev, start / PAGE_SIZE, (end-start) / PAGE_SIZE); + /* Clear any non-preallocated blocks */ + drm_mm_for_each_hole(entry, &dev_priv->mm.gtt_space, + hole_start, hole_end) { + DRM_DEBUG_KMS("clearing unused GTT space: [%lx, %lx]\n", + hole_start, hole_end); + i915_ggtt_clear_range(dev, + hole_start / PAGE_SIZE, + (hole_end-hole_start) / PAGE_SIZE); + } + + /* And finally clear the reserved guard page */ + i915_ggtt_clear_range(dev, end / PAGE_SIZE - 1, 1); } static int setup_scratch_page(struct drm_device *dev) -- cgit v1.2.3 From 11be49eb4db24c5e971edef160afb87788cc270c Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:20 +0000 Subject: drm/i915: Delay allocation of stolen space for FBC As FBC is commonly disabled due to limitations of the chipset upon output configurations, on many systems FBC is never enabled. For those systems, it is advantageous to make use of the stolen memory for other objects and so we defer allocation of the FBC chunk until we actually require it. This increases the likelihood of that allocation failing, but that in turns means that we are already taking advantage of the stolen memory! As well as delaying the allocation from driver initialisation until the first use of FBC, we also return the stolen block after we finish using it - allowing greater flexibility in our usage of stolen space. A side effect of this is that we can then attempt to allocate only the required amount of space (with a little slack to reduce reallocation rate and avoid fragmentation). Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 + drivers/gpu/drm/i915/i915_gem_stolen.c | 108 +++++++++++++++------------------ drivers/gpu/drm/i915/intel_display.c | 3 + drivers/gpu/drm/i915/intel_pm.c | 15 +++-- 4 files changed, 64 insertions(+), 64 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index f16101fa8b4a..6969c722edf6 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1571,6 +1571,8 @@ int i915_gem_evict_everything(struct drm_device *dev); /* i915_gem_stolen.c */ int i915_gem_init_stolen(struct drm_device *dev); +int i915_gem_stolen_setup_compression(struct drm_device *dev, int size); +void i915_gem_stolen_cleanup_compression(struct drm_device *dev); void i915_gem_cleanup_stolen(struct drm_device *dev); /* i915_gem_tiling.c */ diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index be24312cd32f..10ca473d85f2 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -88,33 +88,27 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev) return base; } -static void i915_warn_stolen(struct drm_device *dev) -{ - DRM_INFO("not enough stolen space for compressed buffer, disabling\n"); - DRM_INFO("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n"); -} - -static void i915_setup_compression(struct drm_device *dev, int size) +static int i915_setup_compression(struct drm_device *dev, int size) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_mm_node *compressed_fb, *uninitialized_var(compressed_llb); - unsigned long cfb_base; - unsigned long ll_base = 0; - /* Just in case the BIOS is doing something questionable. */ - intel_disable_fbc(dev); - - compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0); + /* Try to over-allocate to reduce reallocations and fragmentation */ + compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, + size <<= 1, 4096, 0); + if (!compressed_fb) + compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, + size >>= 1, 4096, 0); if (compressed_fb) compressed_fb = drm_mm_get_block(compressed_fb, size, 4096); if (!compressed_fb) goto err; - cfb_base = dev_priv->mm.stolen_base + compressed_fb->start; - if (!cfb_base) - goto err_fb; - - if (!(IS_GM45(dev) || HAS_PCH_SPLIT(dev))) { + if (HAS_PCH_SPLIT(dev)) + I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start); + else if (IS_GM45(dev)) { + I915_WRITE(DPFC_CB_BASE, compressed_fb->start); + } else { compressed_llb = drm_mm_search_free(&dev_priv->mm.stolen, 4096, 4096, 0); if (compressed_llb) @@ -123,56 +117,68 @@ static void i915_setup_compression(struct drm_device *dev, int size) if (!compressed_llb) goto err_fb; - ll_base = dev_priv->mm.stolen_base + compressed_llb->start; - if (!ll_base) - goto err_llb; + dev_priv->compressed_llb = compressed_llb; + + I915_WRITE(FBC_CFB_BASE, + dev_priv->mm.stolen_base + compressed_fb->start); + I915_WRITE(FBC_LL_BASE, + dev_priv->mm.stolen_base + compressed_llb->start); } + dev_priv->compressed_fb = compressed_fb; dev_priv->cfb_size = size; - dev_priv->compressed_fb = compressed_fb; - if (HAS_PCH_SPLIT(dev)) - I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start); - else if (IS_GM45(dev)) { - I915_WRITE(DPFC_CB_BASE, compressed_fb->start); - } else { - I915_WRITE(FBC_CFB_BASE, cfb_base); - I915_WRITE(FBC_LL_BASE, ll_base); - dev_priv->compressed_llb = compressed_llb; - } + DRM_DEBUG_KMS("reserved %d bytes of contiguous stolen space for FBC\n", + size); - DRM_DEBUG_KMS("FBC base 0x%08lx, ll base 0x%08lx, size %dM\n", - (long)cfb_base, (long)ll_base, size >> 20); - return; + return 0; -err_llb: - drm_mm_put_block(compressed_llb); err_fb: drm_mm_put_block(compressed_fb); err: - dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; - i915_warn_stolen(dev); + return -ENOSPC; +} + +int i915_gem_stolen_setup_compression(struct drm_device *dev, int size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (dev_priv->mm.stolen_base == 0) + return -ENODEV; + + if (size < dev_priv->cfb_size) + return 0; + + /* Release any current block */ + i915_gem_stolen_cleanup_compression(dev); + + return i915_setup_compression(dev, size); } -static void i915_cleanup_compression(struct drm_device *dev) +void i915_gem_stolen_cleanup_compression(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - drm_mm_put_block(dev_priv->compressed_fb); + if (dev_priv->cfb_size == 0) + return; + + if (dev_priv->compressed_fb) + drm_mm_put_block(dev_priv->compressed_fb); + if (dev_priv->compressed_llb) drm_mm_put_block(dev_priv->compressed_llb); + + dev_priv->cfb_size = 0; } void i915_gem_cleanup_stolen(struct drm_device *dev) { - if (I915_HAS_FBC(dev) && i915_powersave) - i915_cleanup_compression(dev); + i915_gem_stolen_cleanup_compression(dev); } int i915_gem_init_stolen(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - unsigned long prealloc_size = dev_priv->mm.gtt->stolen_size; dev_priv->mm.stolen_base = i915_stolen_to_physical(dev); if (dev_priv->mm.stolen_base == 0) @@ -182,21 +188,7 @@ int i915_gem_init_stolen(struct drm_device *dev) dev_priv->mm.gtt->stolen_size, dev_priv->mm.stolen_base); /* Basic memrange allocator for stolen space */ - drm_mm_init(&dev_priv->mm.stolen, 0, prealloc_size); - - /* Try to set up FBC with a reasonable compressed buffer size */ - if (I915_HAS_FBC(dev) && i915_powersave) { - int cfb_size; - - /* Leave 1M for line length buffer & misc. */ - - /* Try to get a 32M buffer... */ - if (prealloc_size > (36*1024*1024)) - cfb_size = 32*1024*1024; - else /* fall back to 7/8 of the stolen space */ - cfb_size = prealloc_size * 7 / 8; - i915_setup_compression(dev, cfb_size); - } + drm_mm_init(&dev_priv->mm.stolen, 0, dev_priv->mm.gtt->stolen_size); return 0; } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 52b6b0e811bc..d303f2a90e70 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8655,6 +8655,9 @@ void intel_modeset_init(struct drm_device *dev) /* Just disable it once at startup */ i915_disable_vga(dev); intel_setup_outputs(dev); + + /* Just in case the BIOS is doing something questionable. */ + intel_disable_fbc(dev); } static void diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index f595b8d56cc3..abfff29ed13a 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -440,12 +440,6 @@ void intel_update_fbc(struct drm_device *dev) dev_priv->no_fbc_reason = FBC_MODULE_PARAM; goto out_disable; } - if (intel_fb->obj->base.size > dev_priv->cfb_size) { - DRM_DEBUG_KMS("framebuffer too large, disabling " - "compression\n"); - dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; - goto out_disable; - } if ((crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) || (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN)) { DRM_DEBUG_KMS("mode incompatible with compression, " @@ -479,6 +473,14 @@ void intel_update_fbc(struct drm_device *dev) if (in_dbg_master()) goto out_disable; + if (i915_gem_stolen_setup_compression(dev, intel_fb->obj->base.size)) { + DRM_INFO("not enough stolen space for compressed buffer (need %zd bytes), disabling\n", intel_fb->obj->base.size); + DRM_INFO("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n"); + DRM_DEBUG_KMS("framebuffer too large, disabling compression\n"); + dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; + goto out_disable; + } + /* If the scanout has not changed, don't modify the FBC settings. * Note that we make the fundamental assumption that the fb->obj * cannot be unpinned (and have its GTT offset and fence revoked) @@ -526,6 +528,7 @@ out_disable: DRM_DEBUG_KMS("unsupported config, disabling FBC\n"); intel_disable_fbc(dev); } + i915_gem_stolen_cleanup_compression(dev); } static void i915_pineview_get_mem_freq(struct drm_device *dev) -- cgit v1.2.3 From c1ad11fce86405c955873974f58ab305d894be78 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:21 +0000 Subject: drm/i915: Allow objects to be created with no backing pages, but stolen space In order to accommodate objects that are not backed by struct pages, but instead point into a contiguous region of stolen space, we need to make various changes to avoid dereferencing obj->pages or obj->base.filp. First introduce a marker for the stolen object, that specifies its offset into the stolen region and implies that it has no backing pages. Signed-off-by: Chris Wilson Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 2 ++ drivers/gpu/drm/i915/i915_drv.h | 2 ++ 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 62619e39533f..58e667687741 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -124,6 +124,8 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) if (obj->gtt_space != NULL) seq_printf(m, " (gtt offset: %08x, size: %08x)", obj->gtt_offset, (unsigned int)obj->gtt_space->size); + if (obj->stolen) + seq_printf(m, " (stolen: %08lx)", obj->stolen->start); if (obj->pin_mappable || obj->fault_mappable) { char s[3], *t = s; if (obj->pin_mappable) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 6969c722edf6..1646d036db09 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -962,6 +962,8 @@ struct drm_i915_gem_object { /** Current space allocated to this object in the GTT, if any. */ struct drm_mm_node *gtt_space; + /** Stolen memory for this object, instead of being backed by shmem. */ + struct drm_mm_node *stolen; struct list_head gtt_list; /** This object's place on the active/inactive lists */ -- cgit v1.2.3 From 960e3564bfa464f92549383d41659f2aaeee1420 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:23 +0000 Subject: drm/i915: Support readback of stolen objects upon error Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 26753ee6571d..7a0ddee9751a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -929,6 +929,14 @@ i915_error_object_create(struct drm_i915_private *dev_priv, reloc_offset); memcpy_fromio(d, s, PAGE_SIZE); io_mapping_unmap_atomic(s); + } else if (src->stolen) { + unsigned long offset; + + offset = dev_priv->mm.stolen_base; + offset += src->stolen->start; + offset += i << PAGE_SHIFT; + + memcpy_fromio(d, (void *)offset, PAGE_SIZE); } else { struct page *page; void *s; -- cgit v1.2.3 From 0104fdbb84d7adb0e377ed05bf75eba97b007544 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:26 +0000 Subject: drm/i915: Introduce i915_gem_object_create_stolen() Allow for the creation of GEM objects backed by stolen memory. As these are not backed by ordinary pages, we create a fake dma mapping and store the address in the scatterlist rather than obj->pages. v2: Mark _i915_gem_object_create_stolen() as static, as noticed by Jesse Barnes. Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 3 + drivers/gpu/drm/i915/i915_gem.c | 1 + drivers/gpu/drm/i915/i915_gem_stolen.c | 125 +++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 1646d036db09..d1d68f0b55e8 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1576,6 +1576,9 @@ int i915_gem_init_stolen(struct drm_device *dev); int i915_gem_stolen_setup_compression(struct drm_device *dev, int size); void i915_gem_stolen_cleanup_compression(struct drm_device *dev); void i915_gem_cleanup_stolen(struct drm_device *dev); +struct drm_i915_gem_object * +i915_gem_object_create_stolen(struct drm_device *dev, u32 size); +void i915_gem_object_release_stolen(struct drm_i915_gem_object *obj); /* i915_gem_tiling.c */ void i915_gem_detect_bit_6_swizzle(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 5c8df572cd17..3de62b0127a5 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3772,6 +3772,7 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) obj->pages_pin_count = 0; i915_gem_object_put_pages(obj); i915_gem_object_free_mmap_offset(obj); + i915_gem_object_release_stolen(obj); BUG_ON(obj->pages); diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 10ca473d85f2..7299d632663c 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -192,3 +192,128 @@ int i915_gem_init_stolen(struct drm_device *dev) return 0; } + +static struct sg_table * +i915_pages_create_for_stolen(struct drm_device *dev, + u32 offset, u32 size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct sg_table *st; + struct scatterlist *sg; + + DRM_DEBUG_DRIVER("offset=0x%x, size=%d\n", offset, size); + BUG_ON(offset > dev_priv->mm.gtt->stolen_size - size); + + /* We hide that we have no struct page backing our stolen object + * by wrapping the contiguous physical allocation with a fake + * dma mapping in a single scatterlist. + */ + + st = kmalloc(sizeof(*st), GFP_KERNEL); + if (st == NULL) + return NULL; + + if (sg_alloc_table(st, 1, GFP_KERNEL)) { + kfree(st); + return NULL; + } + + sg = st->sgl; + sg->offset = offset; + sg->length = size; + + sg_dma_address(sg) = (dma_addr_t)dev_priv->mm.stolen_base + offset; + sg_dma_len(sg) = size; + + return st; +} + +static int i915_gem_object_get_pages_stolen(struct drm_i915_gem_object *obj) +{ + BUG(); + return -EINVAL; +} + +static void i915_gem_object_put_pages_stolen(struct drm_i915_gem_object *obj) +{ + /* Should only be called during free */ + sg_free_table(obj->pages); + kfree(obj->pages); +} + +static const struct drm_i915_gem_object_ops i915_gem_object_stolen_ops = { + .get_pages = i915_gem_object_get_pages_stolen, + .put_pages = i915_gem_object_put_pages_stolen, +}; + +static struct drm_i915_gem_object * +_i915_gem_object_create_stolen(struct drm_device *dev, + struct drm_mm_node *stolen) +{ + struct drm_i915_gem_object *obj; + + obj = kzalloc(sizeof(*obj), GFP_KERNEL); + if (obj == NULL) + return NULL; + + if (drm_gem_private_object_init(dev, &obj->base, stolen->size)) + goto cleanup; + + i915_gem_object_init(obj, &i915_gem_object_stolen_ops); + + obj->pages = i915_pages_create_for_stolen(dev, + stolen->start, stolen->size); + if (obj->pages == NULL) + goto cleanup; + + obj->has_dma_mapping = true; + obj->pages_pin_count = 1; + obj->stolen = stolen; + + obj->base.write_domain = I915_GEM_DOMAIN_GTT; + obj->base.read_domains = I915_GEM_DOMAIN_GTT; + obj->cache_level = I915_CACHE_NONE; + + return obj; + +cleanup: + kfree(obj); + return NULL; +} + +struct drm_i915_gem_object * +i915_gem_object_create_stolen(struct drm_device *dev, u32 size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj; + struct drm_mm_node *stolen; + + if (dev_priv->mm.stolen_base == 0) + return NULL; + + DRM_DEBUG_KMS("creating stolen object: size=%x\n", size); + if (size == 0) + return NULL; + + stolen = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0); + if (stolen) + stolen = drm_mm_get_block(stolen, size, 4096); + if (stolen == NULL) + return NULL; + + obj = _i915_gem_object_create_stolen(dev, stolen); + if (obj) + return obj; + + drm_mm_put_block(stolen); + return NULL; +} + +void +i915_gem_object_release_stolen(struct drm_i915_gem_object *obj) +{ + if (obj->stolen) { + drm_mm_put_block(obj->stolen); + obj->stolen = NULL; + } +} -- cgit v1.2.3 From 0ffb0ff283cca16f72caf29c44496d83b0c291fb Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:27 +0000 Subject: drm/i915: Allocate fbcon from stolen memory Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Acked-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_fb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 7b30b5c2c4ee..b7773e548911 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -83,7 +83,9 @@ static int intelfb_create(struct intel_fbdev *ifbdev, size = mode_cmd.pitches[0] * mode_cmd.height; size = ALIGN(size, PAGE_SIZE); - obj = i915_gem_alloc_object(dev, size); + obj = i915_gem_object_create_stolen(dev, size); + if (obj == NULL) + obj = i915_gem_alloc_object(dev, size); if (!obj) { DRM_ERROR("failed to allocate framebuffer\n"); ret = -ENOMEM; -- cgit v1.2.3 From ebc052e0c65f84f68626e388ffa1704b3a190ef7 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:28 +0000 Subject: drm/i915: Allocate ringbuffers from stolen memory Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Acked-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ringbuffer.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index bc7cf7c63108..36e1e13ae946 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1113,7 +1113,11 @@ static int intel_init_ring_buffer(struct drm_device *dev, return ret; } - obj = i915_gem_alloc_object(dev, ring->size); + obj = NULL; + if (!HAS_LLC(dev)) + obj = i915_gem_object_create_stolen(dev, ring->size); + if (obj == NULL) + obj = i915_gem_alloc_object(dev, ring->size); if (obj == NULL) { DRM_ERROR("Failed to allocate ringbuffer\n"); ret = -ENOMEM; -- cgit v1.2.3 From 8040513870399f1cb032cb8bc805df5042fedcdf Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:29 +0000 Subject: drm/i915: Allocate overlay registers from stolen memory Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Acked-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_overlay.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index d7bc817f51a0..fabe0acf808d 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -1333,8 +1333,10 @@ void intel_setup_overlay(struct drm_device *dev) overlay->dev = dev; - reg_bo = i915_gem_alloc_object(dev, PAGE_SIZE); - if (!reg_bo) + reg_bo = i915_gem_object_create_stolen(dev, PAGE_SIZE); + if (reg_bo == NULL) + reg_bo = i915_gem_alloc_object(dev, PAGE_SIZE); + if (reg_bo == NULL) goto out_free; overlay->reg_bo = reg_bo; -- cgit v1.2.3 From 42dcedd4f2e715dc0313e359c8288e6397843fff Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:30 +0000 Subject: drm/i915: Use a slab for object allocation The primary purpose of this was to debug some use-after-free memory corruption that was causing an OOPS inside drm/i915. As it turned out the corruption was being caused elsewhere and i915.ko as a major user of many objects was being hit hardest. Indeed as we do frequent the generic kmalloc caches, dedicating one to ourselves (or at least naming one for us depending upon the core) aids debugging our own slab usage. Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 3 +++ drivers/gpu/drm/i915/i915_drv.h | 4 ++++ drivers/gpu/drm/i915/i915_gem.c | 28 +++++++++++++++++++++++----- drivers/gpu/drm/i915/i915_gem_dmabuf.c | 5 ++--- drivers/gpu/drm/i915/i915_gem_stolen.c | 4 ++-- 5 files changed, 34 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 80ed75117b6d..2635ee6a34d4 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1736,6 +1736,9 @@ int i915_driver_unload(struct drm_device *dev) destroy_workqueue(dev_priv->wq); + if (dev_priv->slab) + kmem_cache_destroy(dev_priv->slab); + pci_dev_put(dev_priv->bridge_dev); kfree(dev->dev_private); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index d1d68f0b55e8..e2944e9bc750 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -614,6 +614,7 @@ struct intel_l3_parity { typedef struct drm_i915_private { struct drm_device *dev; + struct kmem_cache *slab; const struct intel_device_info *info; @@ -1379,12 +1380,15 @@ int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, int i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); void i915_gem_load(struct drm_device *dev); +void *i915_gem_object_alloc(struct drm_device *dev); +void i915_gem_object_free(struct drm_i915_gem_object *obj); int i915_gem_init_object(struct drm_gem_object *obj); void i915_gem_object_init(struct drm_i915_gem_object *obj, const struct drm_i915_gem_object_ops *ops); struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, size_t size); void i915_gem_free_object(struct drm_gem_object *obj); + int __must_check i915_gem_object_pin(struct drm_i915_gem_object *obj, uint32_t alignment, bool map_and_fenceable, diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 3de62b0127a5..dfe7174a7c03 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -192,6 +192,18 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, return 0; } +void *i915_gem_object_alloc(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + return kmem_cache_alloc(dev_priv->slab, GFP_KERNEL | __GFP_ZERO); +} + +void i915_gem_object_free(struct drm_i915_gem_object *obj) +{ + struct drm_i915_private *dev_priv = obj->base.dev->dev_private; + kmem_cache_free(dev_priv->slab, obj); +} + static int i915_gem_create(struct drm_file *file, struct drm_device *dev, @@ -215,7 +227,7 @@ i915_gem_create(struct drm_file *file, if (ret) { drm_gem_object_release(&obj->base); i915_gem_info_remove_obj(dev->dev_private, obj->base.size); - kfree(obj); + i915_gem_object_free(obj); return ret; } @@ -3695,12 +3707,12 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, struct address_space *mapping; u32 mask; - obj = kzalloc(sizeof(*obj), GFP_KERNEL); + obj = i915_gem_object_alloc(dev); if (obj == NULL) return NULL; if (drm_gem_object_init(dev, &obj->base, size) != 0) { - kfree(obj); + i915_gem_object_free(obj); return NULL; } @@ -3783,7 +3795,7 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) i915_gem_info_remove_obj(dev_priv, obj->base.size); kfree(obj->bit_17); - kfree(obj); + i915_gem_object_free(obj); } int @@ -4101,8 +4113,14 @@ init_ring_lists(struct intel_ring_buffer *ring) void i915_gem_load(struct drm_device *dev) { - int i; drm_i915_private_t *dev_priv = dev->dev_private; + int i; + + dev_priv->slab = + kmem_cache_create("i915_gem_object", + sizeof(struct drm_i915_gem_object), 0, + SLAB_HWCACHE_ALIGN, + NULL); INIT_LIST_HEAD(&dev_priv->mm.active_list); INIT_LIST_HEAD(&dev_priv->mm.inactive_list); diff --git a/drivers/gpu/drm/i915/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/i915_gem_dmabuf.c index 773ef77b6c22..defb888ef7f5 100644 --- a/drivers/gpu/drm/i915/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c @@ -276,8 +276,7 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev, if (IS_ERR(attach)) return ERR_CAST(attach); - - obj = kzalloc(sizeof(*obj), GFP_KERNEL); + obj = i915_gem_object_alloc(dev); if (obj == NULL) { ret = -ENOMEM; goto fail_detach; @@ -285,7 +284,7 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev, ret = drm_gem_private_object_init(dev, &obj->base, dma_buf->size); if (ret) { - kfree(obj); + i915_gem_object_free(obj); goto fail_detach; } diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 7299d632663c..f817b0cac116 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -252,7 +252,7 @@ _i915_gem_object_create_stolen(struct drm_device *dev, { struct drm_i915_gem_object *obj; - obj = kzalloc(sizeof(*obj), GFP_KERNEL); + obj = i915_gem_object_alloc(dev); if (obj == NULL) return NULL; @@ -277,7 +277,7 @@ _i915_gem_object_create_stolen(struct drm_device *dev, return obj; cleanup: - kfree(obj); + i915_gem_object_free(obj); return NULL; } -- cgit v1.2.3 From 4239ca779dbe47a310a3106d9f4cd5458014bdb6 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Mon, 3 Dec 2012 16:26:16 +0000 Subject: drm/i915: Fix dieing -> dying typo Signed-off-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index dfe7174a7c03..b68bc02745db 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2818,7 +2818,7 @@ static bool i915_gem_valid_gtt_space(struct drm_device *dev, /* On non-LLC machines we have to be careful when putting differing * types of snoopable memory together to avoid the prefetcher - * crossing memory domains and dieing. + * crossing memory domains and dying. */ if (HAS_LLC(dev)) return true; -- cgit v1.2.3 From 2a2d548240ff5f5bd826640bdf8baa016ab6cec4 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 3 Dec 2012 11:49:06 +0000 Subject: drm/i915: Tighten the checks for invalid relocation domains Be specific for the GPU domains so that we can detect if userspace ever passed in an invalid combination, as well as accurately reflect the known GPU domains when printing state. Fixes i-g-t/gem_exec_bad_domains References: https://bugs.freedesktop.org/show_bug.cgi?id=57826 Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e2944e9bc750..3473298a137f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -83,7 +83,12 @@ enum port { }; #define port_name(p) ((p) + 'A') -#define I915_GEM_GPU_DOMAINS (~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT)) +#define I915_GEM_GPU_DOMAINS \ + (I915_GEM_DOMAIN_RENDER | \ + I915_GEM_DOMAIN_SAMPLER | \ + I915_GEM_DOMAIN_COMMAND | \ + I915_GEM_DOMAIN_INSTRUCTION | \ + I915_GEM_DOMAIN_VERTEX) #define for_each_pipe(p) for ((p) = 0; (p) < dev_priv->num_pipe; (p)++) -- cgit v1.2.3 From c1f093e09c4ceb583b04d11e767bb3201812e4d2 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 3 Dec 2012 11:49:07 +0000 Subject: drm/i915: Remove check for conflicting relocation write-domains Simply use the last write-domain set for the object in the batch, trusting userspace to have correctly flushed the caches between usage as a write target. This check dates back from the golden age of having only a single operation per batch with the kernel repeating it for each cliprect, and conflicts both with userspace trying to efficiently batch multiple operations and with reducing the kernel overhead of relocation processing. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 802d925b2b57..6cd3e1c1629e 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -150,17 +150,6 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, reloc->write_domain); return ret; } - if (unlikely(reloc->write_domain && target_obj->pending_write_domain && - reloc->write_domain != target_obj->pending_write_domain)) { - DRM_DEBUG("Write domain conflict: " - "obj %p target %d offset %d " - "new %08x old %08x\n", - obj, reloc->target_handle, - (int) reloc->offset, - reloc->write_domain, - target_obj->pending_write_domain); - return ret; - } target_obj->pending_read_domains |= reloc->read_domains; target_obj->pending_write_domain |= reloc->write_domain; -- cgit v1.2.3 From 1a240d4de2ccf40de5796a4d1dbb3a0236051fc9 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 29 Nov 2012 22:18:51 +0100 Subject: drm/i915: fixup sparse warnings - __iomem where there is none (I love how we mix these things up). - Use gfp_t instead of an other plain type. - Unconfuse one place about enum pipe vs enum transcoder - for the pch transcoder we actually use the pipe enum. Fixup the other cases where we assign the pipe to the cpu transcoder with explicit casts. - Declare the mch_lock properly in a header. There is still a decent mess in intel_bios.c about __iomem, but heck, this is x86 and we're allowed to do that. Makes-sparse-happy: Chris Wilson [danvet: Use a space after the cast consistently and fix up the newly-added cast in i915_irq.c to properly use __iomem.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 4 ++-- drivers/gpu/drm/i915/i915_drv.h | 3 +++ drivers/gpu/drm/i915/i915_gem.c | 2 +- drivers/gpu/drm/i915/i915_irq.c | 5 +---- drivers/gpu/drm/i915/intel_ddi.c | 2 +- drivers/gpu/drm/i915/intel_display.c | 9 +++++---- 6 files changed, 13 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 58e667687741..ce60506b574b 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -546,11 +546,11 @@ static int i915_hws_info(struct seq_file *m, void *data) struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; struct intel_ring_buffer *ring; - const volatile u32 __iomem *hws; + const u32 *hws; int i; ring = &dev_priv->ring[(uintptr_t)node->info_ent->data]; - hws = (volatile u32 __iomem *)ring->status_page.page_addr; + hws = ring->status_page.page_addr; if (hws == NULL) return 0; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 3473298a137f..bb0eb0f8f6b3 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -577,6 +577,9 @@ struct intel_gen6_power_mgmt { struct mutex hw_lock; }; +/* defined intel_pm.c */ +extern spinlock_t mchdev_lock; + struct intel_ilk_power_mgmt { u8 cur_delay; u8 min_delay; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index b68bc02745db..cb941448c2bc 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3705,7 +3705,7 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, { struct drm_i915_gem_object *obj; struct address_space *mapping; - u32 mask; + gfp_t mask; obj = i915_gem_object_alloc(dev); if (obj == NULL) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 7a0ddee9751a..68e7cafa13de 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -300,9 +300,6 @@ static void i915_hotplug_work_func(struct work_struct *work) drm_helper_hpd_irq_event(dev); } -/* defined intel_pm.c */ -extern spinlock_t mchdev_lock; - static void ironlake_handle_rps_change(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -936,7 +933,7 @@ i915_error_object_create(struct drm_i915_private *dev_priv, offset += src->stolen->start; offset += i << PAGE_SHIFT; - memcpy_fromio(d, (void *)offset, PAGE_SIZE); + memcpy_fromio(d, (void __iomem *) offset, PAGE_SIZE); } else { struct page *page; void *s; diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index ad936c68e4cb..f02b3feff504 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1044,7 +1044,7 @@ bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector) if (port == PORT_A) cpu_transcoder = TRANSCODER_EDP; else - cpu_transcoder = pipe; + cpu_transcoder = (enum transcoder) pipe; tmp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder)); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d303f2a90e70..093a163d4b1c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1671,7 +1671,7 @@ static void lpt_enable_pch_transcoder(struct drm_i915_private *dev_priv, BUG_ON(dev_priv->info->gen < 5); /* FDI must be feeding us bits for PCH ports */ - assert_fdi_tx_enabled(dev_priv, cpu_transcoder); + assert_fdi_tx_enabled(dev_priv, (enum pipe) cpu_transcoder); assert_fdi_rx_enabled(dev_priv, TRANSCODER_A); /* Workaround: set timing override bit. */ @@ -1759,7 +1759,7 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, { enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, pipe); - enum transcoder pch_transcoder; + enum pipe pch_transcoder; int reg; u32 val; @@ -1779,7 +1779,8 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, if (pch_port) { /* if driving the PCH, we need FDI enabled */ assert_fdi_rx_pll_enabled(dev_priv, pch_transcoder); - assert_fdi_tx_pll_enabled(dev_priv, cpu_transcoder); + assert_fdi_tx_pll_enabled(dev_priv, + (enum pipe) cpu_transcoder); } /* FIXME: assert CPU port conditions for SNB+ */ } @@ -3598,7 +3599,7 @@ static void haswell_crtc_off(struct drm_crtc *crtc) /* Stop saying we're using TRANSCODER_EDP because some other CRTC might * start using it. */ - intel_crtc->cpu_transcoder = intel_crtc->pipe; + intel_crtc->cpu_transcoder = (enum transcoder) intel_crtc->pipe; intel_ddi_put_crtc_pll(crtc); } -- cgit v1.2.3 From 6823627b933decf5842fcfc37cc71c549f56c6a2 Mon Sep 17 00:00:00 2001 From: Rémi Cardona Date: Fri, 28 Sep 2012 08:59:32 -0300 Subject: [media] dw2102: Declare MODULE_FIRMWARE usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémi Cardona Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/dw2102.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 9382895b1b88..937c744217c8 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -80,6 +80,15 @@ #define DW2102_RC_QUERY (0x1a00) #define DW2102_LED_CTRL (0x1b00) +#define DW2101_FIRMWARE "dvb-usb-dw2101.fw" +#define DW2102_FIRMWARE "dvb-usb-dw2102.fw" +#define DW2104_FIRMWARE "dvb-usb-dw2104.fw" +#define DW3101_FIRMWARE "dvb-usb-dw3101.fw" +#define S630_FIRMWARE "dvb-usb-s630.fw" +#define S660_FIRMWARE "dvb-usb-s660.fw" +#define P1100_FIRMWARE "dvb-usb-p1100.fw" +#define P7500_FIRMWARE "dvb-usb-p7500.fw" + #define err_str "did not find the firmware file. (%s) " \ "Please see linux/Documentation/dvb/ for more details " \ "on firmware-problems." @@ -1478,13 +1487,12 @@ static int dw2102_load_firmware(struct usb_device *dev, u8 reset; u8 reset16[] = {0, 0, 0, 0, 0, 0, 0}; const struct firmware *fw; - const char *fw_2101 = "dvb-usb-dw2101.fw"; switch (dev->descriptor.idProduct) { case 0x2101: - ret = request_firmware(&fw, fw_2101, &dev->dev); + ret = request_firmware(&fw, DW2101_FIRMWARE, &dev->dev); if (ret != 0) { - err(err_str, fw_2101); + err(err_str, DW2101_FIRMWARE); return ret; } break; @@ -1586,7 +1594,7 @@ static int dw2102_load_firmware(struct usb_device *dev, static struct dvb_usb_device_properties dw2102_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, - .firmware = "dvb-usb-dw2102.fw", + .firmware = DW2102_FIRMWARE, .no_reconnect = 1, .i2c_algo = &dw2102_serit_i2c_algo, @@ -1641,7 +1649,7 @@ static struct dvb_usb_device_properties dw2102_properties = { static struct dvb_usb_device_properties dw2104_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, - .firmware = "dvb-usb-dw2104.fw", + .firmware = DW2104_FIRMWARE, .no_reconnect = 1, .i2c_algo = &dw2104_i2c_algo, @@ -1691,7 +1699,7 @@ static struct dvb_usb_device_properties dw2104_properties = { static struct dvb_usb_device_properties dw3101_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, - .firmware = "dvb-usb-dw3101.fw", + .firmware = DW3101_FIRMWARE, .no_reconnect = 1, .i2c_algo = &dw3101_i2c_algo, @@ -1739,7 +1747,7 @@ static struct dvb_usb_device_properties s6x0_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, .size_of_priv = sizeof(struct s6x0_state), - .firmware = "dvb-usb-s630.fw", + .firmware = S630_FIRMWARE, .no_reconnect = 1, .i2c_algo = &s6x0_i2c_algo, @@ -1879,7 +1887,7 @@ static int dw2102_probe(struct usb_interface *intf, return -ENOMEM; /* copy default structure */ /* fill only different fields */ - p1100->firmware = "dvb-usb-p1100.fw"; + p1100->firmware = P1100_FIRMWARE; p1100->devices[0] = d1100; p1100->rc.legacy.rc_map_table = rc_map_tbs_table; p1100->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table); @@ -1891,7 +1899,7 @@ static int dw2102_probe(struct usb_interface *intf, kfree(p1100); return -ENOMEM; } - s660->firmware = "dvb-usb-s660.fw"; + s660->firmware = S660_FIRMWARE; s660->num_device_descs = 3; s660->devices[0] = d660; s660->devices[1] = d480_1; @@ -1905,7 +1913,7 @@ static int dw2102_probe(struct usb_interface *intf, kfree(s660); return -ENOMEM; } - p7500->firmware = "dvb-usb-p7500.fw"; + p7500->firmware = P7500_FIRMWARE; p7500->devices[0] = d7500; p7500->rc.legacy.rc_map_table = rc_map_tbs_table; p7500->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table); @@ -1949,3 +1957,11 @@ MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104," " Geniatech SU3000 devices"); MODULE_VERSION("0.1"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(DW2101_FIRMWARE); +MODULE_FIRMWARE(DW2102_FIRMWARE); +MODULE_FIRMWARE(DW2104_FIRMWARE); +MODULE_FIRMWARE(DW3101_FIRMWARE); +MODULE_FIRMWARE(S630_FIRMWARE); +MODULE_FIRMWARE(S660_FIRMWARE); +MODULE_FIRMWARE(P1100_FIRMWARE); +MODULE_FIRMWARE(P7500_FIRMWARE); -- cgit v1.2.3 From 16427faf28674451a7a0485ab0a929402f355ffd Mon Sep 17 00:00:00 2001 From: Julian Scheel Date: Thu, 4 Oct 2012 10:04:28 -0300 Subject: [media] tm6000: Add parameter to keep urb bufs allocated On systems where it cannot be assured that enough continous memory is available all the time it can be very useful to only allocate the memory once when it is needed the first time. Afterwards the initially allocated memory will be reused, so it is ensured that the memory will stay available until the driver is unloaded. [mchehab@redhat.com: Codingstyle fixups] Signed-off-by: Julian Scheel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-video.c | 112 ++++++++++++++++++++++++++------ drivers/media/usb/tm6000/tm6000.h | 5 ++ 2 files changed, 98 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index f656fd7a39a2..82968017a253 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -49,12 +49,15 @@ #define TM6000_MIN_BUF 4 #define TM6000_DEF_BUF 8 +#define TM6000_NUM_URB_BUF 8 + #define TM6000_MAX_ISO_PACKETS 46 /* Max number of ISO packets */ /* Declare static vars that will be used as parameters */ static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ static int radio_nr = -1; /* /dev/radioN, -1 for autodetect */ +static int keep_urb; /* keep urb buffers allocated */ /* Debug level */ int tm6000_debug; @@ -537,6 +540,71 @@ static void tm6000_irq_callback(struct urb *urb) urb->status); } +/* + * Allocate URB buffers + */ +static int tm6000_alloc_urb_buffers(struct tm6000_core *dev) +{ + int num_bufs = TM6000_NUM_URB_BUF; + int i; + + if (dev->urb_buffer != NULL) + return 0; + + dev->urb_buffer = kmalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!dev->urb_buffer) { + tm6000_err("cannot allocate memory for urb buffers\n"); + return -ENOMEM; + } + + dev->urb_dma = kmalloc(sizeof(dma_addr_t *)*num_bufs, GFP_KERNEL); + if (!dev->urb_dma) { + tm6000_err("cannot allocate memory for urb dma pointers\n"); + return -ENOMEM; + } + + for (i = 0; i < num_bufs; i++) { + dev->urb_buffer[i] = usb_alloc_coherent( + dev->udev, dev->urb_size, + GFP_KERNEL, &dev->urb_dma[i]); + if (!dev->urb_buffer[i]) { + tm6000_err("unable to allocate %i bytes for transfer buffer %i\n", + dev->urb_size, i); + return -ENOMEM; + } + memset(dev->urb_buffer[i], 0, dev->urb_size); + } + + return 0; +} + +/* + * Free URB buffers + */ +static int tm6000_free_urb_buffers(struct tm6000_core *dev) +{ + int i; + + if (dev->urb_buffer == NULL) + return 0; + + for (i = 0; i < TM6000_NUM_URB_BUF; i++) { + if (dev->urb_buffer[i]) { + usb_free_coherent(dev->udev, + dev->urb_size, + dev->urb_buffer[i], + dev->urb_dma[i]); + dev->urb_buffer[i] = NULL; + } + } + kfree(dev->urb_buffer); + kfree(dev->urb_dma); + dev->urb_buffer = NULL; + dev->urb_dma = NULL; + + return 0; +} + /* * Stop and Deallocate URBs */ @@ -551,18 +619,15 @@ static void tm6000_uninit_isoc(struct tm6000_core *dev) if (urb) { usb_kill_urb(urb); usb_unlink_urb(urb); - if (dev->isoc_ctl.transfer_buffer[i]) { - usb_free_coherent(dev->udev, - urb->transfer_buffer_length, - dev->isoc_ctl.transfer_buffer[i], - urb->transfer_dma); - } usb_free_urb(urb); dev->isoc_ctl.urb[i] = NULL; } dev->isoc_ctl.transfer_buffer[i] = NULL; } + if (!keep_urb) + tm6000_free_urb_buffers(dev); + kfree(dev->isoc_ctl.urb); kfree(dev->isoc_ctl.transfer_buffer); @@ -572,12 +637,13 @@ static void tm6000_uninit_isoc(struct tm6000_core *dev) } /* - * Allocate URBs and start IRQ + * Assign URBs and start IRQ */ static int tm6000_prepare_isoc(struct tm6000_core *dev) { struct tm6000_dmaqueue *dma_q = &dev->vidq; - int i, j, sb_size, pipe, size, max_packets, num_bufs = 8; + int i, j, sb_size, pipe, size, max_packets; + int num_bufs = TM6000_NUM_URB_BUF; struct urb *urb; /* De-allocates all pending stuff */ @@ -605,6 +671,7 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev) max_packets = TM6000_MAX_ISO_PACKETS; sb_size = max_packets * size; + dev->urb_size = sb_size; dev->isoc_ctl.num_bufs = num_bufs; @@ -627,6 +694,17 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev) max_packets, num_bufs, sb_size, dev->isoc_in.maxsize, size); + + if (!dev->urb_buffer && tm6000_alloc_urb_buffers(dev) < 0) { + tm6000_err("cannot allocate memory for urb buffers\n"); + + /* call free, as some buffers might have been allocated */ + tm6000_free_urb_buffers(dev); + kfree(dev->isoc_ctl.urb); + kfree(dev->isoc_ctl.transfer_buffer); + return -ENOMEM; + } + /* allocate urbs and transfer buffers */ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { urb = usb_alloc_urb(max_packets, GFP_KERNEL); @@ -638,17 +716,8 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev) } dev->isoc_ctl.urb[i] = urb; - dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev, - sb_size, GFP_KERNEL, &urb->transfer_dma); - if (!dev->isoc_ctl.transfer_buffer[i]) { - tm6000_err("unable to allocate %i bytes for transfer" - " buffer %i%s\n", - sb_size, i, - in_interrupt() ? " while in int" : ""); - tm6000_uninit_isoc(dev); - return -ENOMEM; - } - memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); + urb->transfer_dma = dev->urb_dma[i]; + dev->isoc_ctl.transfer_buffer[i] = dev->urb_buffer[i]; usb_fill_bulk_urb(urb, dev->udev, pipe, dev->isoc_ctl.transfer_buffer[i], sb_size, @@ -1826,6 +1895,9 @@ int tm6000_v4l2_unregister(struct tm6000_core *dev) { video_unregister_device(dev->vfd); + /* if URB buffers are still allocated free them now */ + tm6000_free_urb_buffers(dev); + if (dev->radio_dev) { if (video_is_registered(dev->radio_dev)) video_unregister_device(dev->radio_dev); @@ -1851,3 +1923,5 @@ MODULE_PARM_DESC(debug, "activates debug info"); module_param(vid_limit, int, 0644); MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); +module_param(keep_urb, bool, 0); +MODULE_PARM_DESC(keep_urb, "Keep urb buffers allocated even when the device is closed by the user"); diff --git a/drivers/media/usb/tm6000/tm6000.h b/drivers/media/usb/tm6000/tm6000.h index 6df418658c9c..173dcd7a7284 100644 --- a/drivers/media/usb/tm6000/tm6000.h +++ b/drivers/media/usb/tm6000/tm6000.h @@ -264,6 +264,11 @@ struct tm6000_core { spinlock_t slock; + /* urb dma buffers */ + char **urb_buffer; + dma_addr_t *urb_dma; + unsigned int urb_size; + unsigned long quirks; }; -- cgit v1.2.3 From 605a410325535e0d9dd3c539ac144fc52e0bda21 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Wed, 28 Nov 2012 17:15:51 -0300 Subject: [media] soc_camera: fix VIDIOC_S_CROP ioctl Sometimes VIDIOC_S_CROP ioctl doesn't work, soc-camera driver reports: soc-camera-pdrv soc-camera-pdrv.0: S_CROP denied: getting current crop failed The VIDIOC_G_CROP documentation states that the type field needs to be set to the respective buffer type when querying, so the check in .g_crop() of the subdevices returns -EINVAL if the type is not set properly. Here the uninitialized local variable 'current_crop' is passed to the .g_crop() and this leads to the observed error. Initialize the type field of the local 'current_crop' before get_crop call. Signed-off-by: Anatolij Gustschin Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/soc_camera.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 4e3735679f17..54da3a5f900c 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -908,6 +908,8 @@ static int soc_camera_s_crop(struct file *file, void *fh, dev_dbg(icd->pdev, "S_CROP(%ux%u@%u:%u)\n", rect->width, rect->height, rect->left, rect->top); + current_crop.type = a->type; + /* If get_crop fails, we'll let host and / or client drivers decide */ ret = ici->ops->get_crop(icd, ¤t_crop); -- cgit v1.2.3 From bc1ebd704751bb6b86612e63fdfac56918364d07 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 26 Nov 2012 14:32:48 -0300 Subject: [media] media: sh-vou: fix compiler warnings sh-vou causes several "may be used uninitialized" warnings. Even though they all are purely theoretical, it is better to fix them. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_vou.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index a1c87f0ceaab..7494858a2158 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -207,6 +207,7 @@ static void sh_vou_stream_start(struct sh_vou_device *vou_dev, #endif switch (vou_dev->pix.pixelformat) { + default: case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV16: row_coeff = 1; @@ -595,9 +596,9 @@ static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std) */ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std) { - unsigned int best_err = UINT_MAX, best, width_max, height_max, - img_height_max; - int i, idx; + unsigned int best_err = UINT_MAX, best = geo->in_width, + width_max, height_max, img_height_max; + int i, idx = 0; if (std & V4L2_STD_525_60) { width_max = 858; -- cgit v1.2.3 From 4a8a8042be289f6d53fc6cbfbed1722db14d7af1 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Mon, 19 Nov 2012 18:36:09 -0300 Subject: [media] mx2_camera: use GFP_ATOMIC under spin lock Found using the following semantic patch: @@ @@ spin_lock_irqsave(...); ... when != spin_unlock_irqrestore(...); * GFP_KERNEL Signed-off-by: Cyril Roelandt Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/mx2_camera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 9a55f4c4c7f4..77529f860386 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -879,7 +879,7 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) pcdev->discard_size = icd->user_height * bytesperline; pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, pcdev->discard_size, &pcdev->discard_buffer_dma, - GFP_KERNEL); + GFP_ATOMIC); if (!pcdev->discard_buffer) { spin_unlock_irqrestore(&pcdev->lock, flags); return -ENOMEM; -- cgit v1.2.3 From 5b97fabdc815b4b60bac2328b58c5c7274debf58 Mon Sep 17 00:00:00 2001 From: Vasu Dev Date: Tue, 9 Oct 2012 01:43:24 +0000 Subject: libfc: fix REC handling Currently fc_fcp_timeout doesn't check FC_RP_FLAGS_REC_SUPPORTED flag first, this prevents REC request ever going out at all to the target having REC support. So this patches fixes the fc_fcp_timeout by checking FC_RP_FLAGS_REC_SUPPORTED flag first. The changed order won't cause any issue during clearing FC_RP_FLAGS_REC_SUPPORTED on failed IO with target not supporting FC_RP_FLAGS_REC_SUPPORTED, since retry on failed IO would succeed. Signed-off-by: Vasu Dev Tested-by: Ross Brattain Signed-off-by: Robert Love --- drivers/scsi/libfc/fc_fcp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c index fcb9d0b20ee4..09c81b2f2169 100644 --- a/drivers/scsi/libfc/fc_fcp.c +++ b/drivers/scsi/libfc/fc_fcp.c @@ -1381,10 +1381,10 @@ static void fc_fcp_timeout(unsigned long data) fsp->state |= FC_SRB_FCP_PROCESSING_TMO; - if (fsp->state & FC_SRB_RCV_STATUS) - fc_fcp_complete_locked(fsp); - else if (rpriv->flags & FC_RP_FLAGS_REC_SUPPORTED) + if (rpriv->flags & FC_RP_FLAGS_REC_SUPPORTED) fc_fcp_rec(fsp); + else if (fsp->state & FC_SRB_RCV_STATUS) + fc_fcp_complete_locked(fsp); else fc_fcp_recovery(fsp, FC_TIMED_OUT); fsp->state &= ~FC_SRB_FCP_PROCESSING_TMO; -- cgit v1.2.3 From ef60f674344cdb6d1da199f6b8d7d7016813cc6f Mon Sep 17 00:00:00 2001 From: Robert Love Date: Tue, 27 Nov 2012 06:53:19 +0000 Subject: libfcoe: Save some memory and optimize name lookups Instead of creating a structure with an enum and a pointer to a string, simply allocate an array of strings and use the enum values for the indicies. This means that we do not need to iterate through the list of entries when looking up a string name by its enum key. This will also help with a latter patch that will add more fcoe_sysfs attributes that will also use the fcoe_enum_name_search macro. One attribute will also do a reverse lookup which requires less code when the enum-to-string mappings are organized as this patch makes them to be. Signed-off-by: Robert Love Acked-by: Neil Horman --- drivers/scsi/fcoe/fcoe_sysfs.c | 44 +++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/fcoe/fcoe_sysfs.c b/drivers/scsi/fcoe/fcoe_sysfs.c index 5e751689a089..e6fce2862a33 100644 --- a/drivers/scsi/fcoe/fcoe_sysfs.c +++ b/drivers/scsi/fcoe/fcoe_sysfs.c @@ -210,25 +210,23 @@ static ssize_t show_fcoe_fcf_device_##field(struct device *dev, \ #define fcoe_enum_name_search(title, table_type, table) \ static const char *get_fcoe_##title##_name(enum table_type table_key) \ { \ - int i; \ - char *name = NULL; \ - \ - for (i = 0; i < ARRAY_SIZE(table); i++) { \ - if (table[i].value == table_key) { \ - name = table[i].name; \ - break; \ - } \ - } \ - return name; \ + if (table_key < 0 || table_key >= ARRAY_SIZE(table)) \ + return NULL; \ + return table[table_key]; \ } -static struct { - enum fcf_state value; - char *name; -} fcf_state_names[] = { - { FCOE_FCF_STATE_UNKNOWN, "Unknown" }, - { FCOE_FCF_STATE_DISCONNECTED, "Disconnected" }, - { FCOE_FCF_STATE_CONNECTED, "Connected" }, +static char *fip_conn_type_names[] = { + [ FIP_CONN_TYPE_UNKNOWN ] = "Unknown", + [ FIP_CONN_TYPE_FABRIC ] = "Fabric", + [ FIP_CONN_TYPE_VN2VN ] = "VN2VN", +}; +fcoe_enum_name_search(ctlr_mode, fip_conn_type, fip_conn_type_names) +#define FCOE_CTLR_MODE_MAX_NAMELEN 50 + +static char *fcf_state_names[] = { + [ FCOE_FCF_STATE_UNKNOWN ] = "Unknown", + [ FCOE_FCF_STATE_DISCONNECTED ] = "Disconnected", + [ FCOE_FCF_STATE_CONNECTED ] = "Connected", }; fcoe_enum_name_search(fcf_state, fcf_state, fcf_state_names) #define FCOE_FCF_STATE_MAX_NAMELEN 50 @@ -246,17 +244,7 @@ static ssize_t show_fcf_state(struct device *dev, } static FCOE_DEVICE_ATTR(fcf, state, S_IRUGO, show_fcf_state, NULL); -static struct { - enum fip_conn_type value; - char *name; -} fip_conn_type_names[] = { - { FIP_CONN_TYPE_UNKNOWN, "Unknown" }, - { FIP_CONN_TYPE_FABRIC, "Fabric" }, - { FIP_CONN_TYPE_VN2VN, "VN2VN" }, -}; -fcoe_enum_name_search(ctlr_mode, fip_conn_type, fip_conn_type_names) -#define FCOE_CTLR_MODE_MAX_NAMELEN 50 - +#define FCOE_MAX_MODENAME_LEN 20 static ssize_t show_ctlr_mode(struct device *dev, struct device_attribute *attr, char *buf) -- cgit v1.2.3 From 40633219a0ff4953601c7b2326f8faabc0171fd9 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Tue, 4 Dec 2012 15:12:00 +0200 Subject: drm/i915: Add debugfs entry to read/write next_seqno This is needed for testing seqno wrapping. Be careful to not bump next_seqno more than 0x7FFFFFFF at a time (between some handled requests) as i915_seqno_passed() can't handle bigger difference in between. v2: Address review comments from Chris Wilson. Signed-off-by: Mika Kuoppala Acked-by: Chris Wilson [danvet: Squash in fixup to properly remove the debugfs file on driver unload again.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 88 +++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index ce60506b574b..602058fb5e25 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -843,6 +843,86 @@ static const struct file_operations i915_error_state_fops = { .release = i915_error_state_release, }; +static ssize_t +i915_next_seqno_read(struct file *filp, + char __user *ubuf, + size_t max, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + drm_i915_private_t *dev_priv = dev->dev_private; + char buf[80]; + int len; + int ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + len = snprintf(buf, sizeof(buf), + "next_seqno : 0x%x\n", + dev_priv->next_seqno); + + mutex_unlock(&dev->struct_mutex); + + if (len > sizeof(buf)) + len = sizeof(buf); + + return simple_read_from_buffer(ubuf, max, ppos, buf, len); +} + +static ssize_t +i915_next_seqno_write(struct file *filp, + const char __user *ubuf, + size_t cnt, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + drm_i915_private_t *dev_priv = dev->dev_private; + char buf[20]; + u32 val = 1; + int ret; + + if (cnt > 0) { + if (cnt > sizeof(buf) - 1) + return -EINVAL; + + if (copy_from_user(buf, ubuf, cnt)) + return -EFAULT; + buf[cnt] = 0; + + ret = kstrtouint(buf, 0, &val); + if (ret < 0) + return ret; + } + + if (val == 0) + return -EINVAL; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + if (i915_seqno_passed(val, dev_priv->next_seqno)) { + dev_priv->next_seqno = val; + DRM_DEBUG_DRIVER("Advancing seqno to %u\n", val); + } else { + ret = -EINVAL; + } + + mutex_unlock(&dev->struct_mutex); + + return ret ?: cnt; +} + +static const struct file_operations i915_next_seqno_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = i915_next_seqno_read, + .write = i915_next_seqno_write, + .llseek = default_llseek, +}; + static int i915_rstdby_delays(struct seq_file *m, void *unused) { struct drm_info_node *node = (struct drm_info_node *) m->private; @@ -2107,6 +2187,12 @@ int i915_debugfs_init(struct drm_minor *minor) if (ret) return ret; + ret = i915_debugfs_create(minor->debugfs_root, minor, + "i915_next_seqno", + &i915_next_seqno_fops); + if (ret) + return ret; + return drm_debugfs_create_files(i915_debugfs_list, I915_DEBUGFS_ENTRIES, minor->debugfs_root, minor); @@ -2130,6 +2216,8 @@ void i915_debugfs_cleanup(struct drm_minor *minor) 1, minor); drm_debugfs_remove_files((struct drm_info_list *) &i915_error_state_fops, 1, minor); + drm_debugfs_remove_files((struct drm_info_list *) &i915_next_seqno_fops, + 1, minor); } #endif /* CONFIG_DEBUG_FS */ -- cgit v1.2.3 From 43a7b924a920729ff775e6a847f671d0ea456801 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Tue, 4 Dec 2012 15:12:01 +0200 Subject: drm/i915: Fix debugfs seqno info print to use uint seqno's are u32 so print accordingly Signed-off-by: Mika Kuoppala Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 602058fb5e25..7e516eebdc80 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -389,7 +389,7 @@ static void i915_ring_seqno_info(struct seq_file *m, struct intel_ring_buffer *ring) { if (ring->get_seqno) { - seq_printf(m, "Current sequence (%s): %d\n", + seq_printf(m, "Current sequence (%s): %u\n", ring->name, ring->get_seqno(ring, false)); } } -- cgit v1.2.3 From cbcc80dff3896015385c67d6be0beb3399999e5c Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Tue, 4 Dec 2012 15:12:03 +0200 Subject: drm/i915: Split intel_ring_begin In preparation for handling ring seqno wrapping, split intel_ring_begin into helper part which doesn't allocate seqno. Signed-off-by: Mika Kuoppala Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ringbuffer.c | 37 ++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 36e1e13ae946..01a660a7c167 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1363,11 +1363,31 @@ intel_ring_alloc_seqno(struct intel_ring_buffer *ring) return i915_gem_get_seqno(ring->dev, &ring->outstanding_lazy_request); } +static int __intel_ring_begin(struct intel_ring_buffer *ring, + int bytes) +{ + int ret; + + if (unlikely(ring->tail + bytes > ring->effective_size)) { + ret = intel_wrap_ring_buffer(ring); + if (unlikely(ret)) + return ret; + } + + if (unlikely(ring->space < bytes)) { + ret = ring_wait_for_space(ring, bytes); + if (unlikely(ret)) + return ret; + } + + ring->space -= bytes; + return 0; +} + int intel_ring_begin(struct intel_ring_buffer *ring, int num_dwords) { drm_i915_private_t *dev_priv = ring->dev->dev_private; - int n = 4*num_dwords; int ret; ret = i915_gem_check_wedge(dev_priv, dev_priv->mm.interruptible); @@ -1379,20 +1399,7 @@ int intel_ring_begin(struct intel_ring_buffer *ring, if (ret) return ret; - if (unlikely(ring->tail + n > ring->effective_size)) { - ret = intel_wrap_ring_buffer(ring); - if (unlikely(ret)) - return ret; - } - - if (unlikely(ring->space < n)) { - ret = ring_wait_for_space(ring, n); - if (unlikely(ret)) - return ret; - } - - ring->space -= n; - return 0; + return __intel_ring_begin(ring, num_dwords * sizeof(uint32_t)); } void intel_ring_advance(struct intel_ring_buffer *ring) -- cgit v1.2.3 From 498d2ac15ce0fc08edb005a7faf9ed6b5aa028d8 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Tue, 4 Dec 2012 15:12:04 +0200 Subject: drm/i915: Add intel_ring_handle_seqno wrap If there are pre-wrap values in semaphore-mbox registers after wrap, syncing against some after-wrap request will complete immediately. Fix this by emitting ring commands to set mbox registers to zero when the wrap happens. v2: Use __intel_ring_begin to emit ring commands, from Chris Wilson. Signed-off-by: Mika Kuoppala Reviewed-by: Chris Wilson [danvet: Add a small comment to handle_seqno_wrap.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 4 ++++ drivers/gpu/drm/i915/intel_ringbuffer.c | 22 ++++++++++++++++++++++ drivers/gpu/drm/i915/intel_ringbuffer.h | 2 +- 3 files changed, 27 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index cb941448c2bc..02d315164aa9 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1952,6 +1952,10 @@ i915_gem_handle_seqno_wrap(struct drm_device *dev) i915_gem_retire_requests(dev); for_each_ring(ring, dev_priv, i) { + ret = intel_ring_handle_seqno_wrap(ring); + if (ret) + return ret; + for (j = 0; j < ARRAY_SIZE(ring->sync_seqno); j++) ring->sync_seqno[j] = 0; } diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 01a660a7c167..0d03dc649c5e 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1402,6 +1402,28 @@ int intel_ring_begin(struct intel_ring_buffer *ring, return __intel_ring_begin(ring, num_dwords * sizeof(uint32_t)); } +int intel_ring_handle_seqno_wrap(struct intel_ring_buffer *ring) +{ + int ret; + + BUG_ON(ring->outstanding_lazy_request); + + if (INTEL_INFO(ring->dev)->gen < 6) + return 0; + + ret = __intel_ring_begin(ring, 6 * sizeof(uint32_t)); + if (ret) + return ret; + + /* Leaving a stale, pre-wrap seqno behind in the mboxes will result in + * post-wrap semaphore waits completing immediately. Clear them. */ + update_mboxes(ring, ring->signal_mbox[0]); + update_mboxes(ring, ring->signal_mbox[1]); + intel_ring_advance(ring); + + return 0; +} + void intel_ring_advance(struct intel_ring_buffer *ring) { struct drm_i915_private *dev_priv = ring->dev->dev_private; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index d4b7416fa1b0..b4a533e53fd1 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -196,7 +196,7 @@ static inline void intel_ring_emit(struct intel_ring_buffer *ring, } void intel_ring_advance(struct intel_ring_buffer *ring); int __must_check intel_ring_idle(struct intel_ring_buffer *ring); - +int __must_check intel_ring_handle_seqno_wrap(struct intel_ring_buffer *ring); int intel_ring_flush_all_caches(struct intel_ring_buffer *ring); int intel_ring_invalidate_all_caches(struct intel_ring_buffer *ring); -- cgit v1.2.3 From 4a06e201dae98ed893bae677b7b05bff29fcbab0 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:40 +0100 Subject: drm/i915: haswell has the same irq handlers as ivb No need to have the exaxt same code twice. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 68e7cafa13de..2aa23e7fc2ab 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2711,7 +2711,7 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_uninstall = valleyview_irq_uninstall; dev->driver->enable_vblank = valleyview_enable_vblank; dev->driver->disable_vblank = valleyview_disable_vblank; - } else if (IS_IVYBRIDGE(dev)) { + } else if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) { /* Share pre & uninstall handlers with ILK/SNB */ dev->driver->irq_handler = ivybridge_irq_handler; dev->driver->irq_preinstall = ironlake_irq_preinstall; @@ -2719,14 +2719,6 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_uninstall = ironlake_irq_uninstall; dev->driver->enable_vblank = ivybridge_enable_vblank; dev->driver->disable_vblank = ivybridge_disable_vblank; - } else if (IS_HASWELL(dev)) { - /* Share interrupts handling with IVB */ - dev->driver->irq_handler = ivybridge_irq_handler; - dev->driver->irq_preinstall = ironlake_irq_preinstall; - dev->driver->irq_postinstall = ivybridge_irq_postinstall; - dev->driver->irq_uninstall = ironlake_irq_uninstall; - dev->driver->enable_vblank = ivybridge_enable_vblank; - dev->driver->disable_vblank = ivybridge_disable_vblank; } else if (HAS_PCH_SPLIT(dev)) { dev->driver->irq_handler = ironlake_irq_handler; dev->driver->irq_preinstall = ironlake_irq_preinstall; -- cgit v1.2.3 From d83779a9cb9374977c1c05364b4d7dfe9ec68ef3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:41 +0100 Subject: drm/i915: don't handle PIPE_LEGACY_BLC_EVENT_STATUS on vlv This is for legacy legacy stuff, and checking with the leftover pipe from the previous loop is propably not what we want. Since pipe == 2 after the loop ... Then we only assing a variable and do nothing with it. Cc: Jesse Barnes Reviewed-by: Jesse Barnes Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 2aa23e7fc2ab..935b6b7bae73 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -530,7 +530,6 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) unsigned long irqflags; int pipe; u32 pipe_stats[I915_MAX_PIPES]; - bool blc_event; atomic_inc(&dev_priv->irq_received); @@ -587,9 +586,6 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) I915_READ(PORT_HOTPLUG_STAT); } - if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) - blc_event = true; - if (pm_iir & GEN6_PM_DEFERRED_EVENTS) gen6_queue_rps_work(dev_priv, pm_iir); -- cgit v1.2.3 From 61bac78e03d4385e225cb2837e33974feda489c2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 21:03:21 +0100 Subject: drm/i915: setup the hangcheck timer early ... together with all the other irq related resources in intel_irq_init. I've managed to oops in the notify_ring function on my ilk, presumably because of the powerctx setup call to i915_gpu_idle. Note that this is only a problem with the reorder irq setup sequence for irq-driver gmbus/dp aux. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 3 --- drivers/gpu/drm/i915/i915_irq.c | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 2635ee6a34d4..901f8fc3ed88 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1611,9 +1611,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) intel_opregion_init(dev); acpi_video_register(); - setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, - (unsigned long) dev); - if (IS_GEN5(dev)) intel_gpu_ips_init(dev_priv); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 935b6b7bae73..568820bf4587 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2687,6 +2687,9 @@ void intel_irq_init(struct drm_device *dev) INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work); INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work); + setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, + (unsigned long) dev); + dev->driver->get_vblank_counter = i915_get_vblank_counter; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) { -- cgit v1.2.3 From 52d7ecedac3f96fb562cb482c139015372728638 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 21:03:22 +0100 Subject: drm/i915: reorder setup sequence to have irqs for output setup Otherwise the new&shiny irq-driven gmbus and dp aux code won't work that well. Noticed since the dp aux code doesn't have an automatic fallback with a timeout (since the hw provides for that already). v2: Simple move drm_irq_install before intel_modeset_gem_init, as suggested by Ben Widawsky. v3: Now that interrupts are enabled before all connectors are fully set up, we might fall over serving a HPD interrupt while things are still being set up. Instead of jumping through massive hoops and complicating the code with a separate hpd irq enable step, simply block out the hotplug work item from doing anything until things are in place. v4: Actually, we can enable hotplug processing only after the fbdev is fully set up, since we call down into the fbdev from the hotplug work functions. So stick the hpd enabling right next to the poll helper initialization. v5: We need to enable irqs before intel_modeset_init, since that function sets up the outputs. v6: Fixup cleanup sequence, too. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 23 ++++++++++++++--------- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_irq.c | 4 ++++ 3 files changed, 19 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 901f8fc3ed88..14d679ab52b7 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1294,19 +1294,21 @@ static int i915_load_modeset_init(struct drm_device *dev) if (ret) goto cleanup_vga_switcheroo; + ret = drm_irq_install(dev); + if (ret) + goto cleanup_gem_stolen; + + /* Important: The output setup functions called by modeset_init need + * working irqs for e.g. gmbus and dp aux transfers. */ intel_modeset_init(dev); ret = i915_gem_init(dev); if (ret) - goto cleanup_gem_stolen; - - intel_modeset_gem_init(dev); + goto cleanup_irq; INIT_WORK(&dev_priv->console_resume_work, intel_console_resume); - ret = drm_irq_install(dev); - if (ret) - goto cleanup_gem; + intel_modeset_gem_init(dev); /* Always safe in the mode setting case. */ /* FIXME: do pre/post-mode set stuff in core KMS code */ @@ -1314,7 +1316,10 @@ static int i915_load_modeset_init(struct drm_device *dev) ret = intel_fbdev_init(dev); if (ret) - goto cleanup_irq; + goto cleanup_gem; + + /* Only enable hotplug handling once the fbdev is fully set up. */ + dev_priv->enable_hotplug_processing = true; drm_kms_helper_poll_init(dev); @@ -1323,13 +1328,13 @@ static int i915_load_modeset_init(struct drm_device *dev) return 0; -cleanup_irq: - drm_irq_uninstall(dev); cleanup_gem: mutex_lock(&dev->struct_mutex); i915_gem_cleanup_ringbuffer(dev); mutex_unlock(&dev->struct_mutex); i915_gem_cleanup_aliasing_ppgtt(dev); +cleanup_irq: + drm_irq_uninstall(dev); cleanup_gem_stolen: i915_gem_cleanup_stolen(dev); cleanup_vga_switcheroo: diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index bb0eb0f8f6b3..e7411f363b3d 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -673,6 +673,7 @@ typedef struct drm_i915_private { u32 hotplug_supported_mask; struct work_struct hotplug_work; + bool enable_hotplug_processing; int num_pipe; int num_pch_pll; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 568820bf4587..02a13ab148df 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -287,6 +287,10 @@ static void i915_hotplug_work_func(struct work_struct *work) struct drm_mode_config *mode_config = &dev->mode_config; struct intel_encoder *encoder; + /* HPD irq before everything is fully set up. */ + if (!dev_priv->enable_hotplug_processing) + return; + mutex_lock(&mode_config->mutex); DRM_DEBUG_KMS("running encoder hotplug functions\n"); -- cgit v1.2.3 From 61168c53f5be309332006dbd967aaac501cde8b2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:43 +0100 Subject: drm/i915: extract gmbus_wait_hw_status The gmbus interrupt generation is rather fiddly: We can only ever enable one interrupt source (but we always want to check for NAK in addition to the real bit). And the bits in the gmbus status register don't map at all to the bis in the irq register. To prepare for this mess, start by extracting the hw status wait loop into it's own function, consolidate the NAK error handling a bit. To keep things flexible, pass in the status bit we care about (in addition to any NAK signalling). v2: I've failed to notice that the sense of GMBUS_ACTIVE is inverted, Chris Wilson gladly pointed that out for me. To keep things simple, ignore that case for now (we only need to idle the gmbus controller at the end of an entire i2c transaction, not after every message). Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_i2c.c | 46 ++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index 3ef5af15b812..a16eecdf1ed1 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -202,6 +202,24 @@ intel_gpio_setup(struct intel_gmbus *bus, u32 pin) algo->data = bus; } +static int +gmbus_wait_hw_status(struct drm_i915_private *dev_priv, + u32 gmbus2_status) +{ + int ret; + int reg_offset = dev_priv->gpio_mmio_base; + u32 gmbus2; + + ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & + (GMBUS_SATOER | gmbus2_status), + 50); + + if (gmbus2 & GMBUS_SATOER) + return -ENXIO; + + return ret; +} + static int gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg, u32 gmbus1_index) @@ -219,15 +237,10 @@ gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg, while (len) { int ret; u32 val, loop = 0; - u32 gmbus2; - ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & - (GMBUS_SATOER | GMBUS_HW_RDY), - 50); + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY); if (ret) - return -ETIMEDOUT; - if (gmbus2 & GMBUS_SATOER) - return -ENXIO; + return ret; val = I915_READ(GMBUS3 + reg_offset); do { @@ -261,7 +274,6 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg) GMBUS_SLAVE_WRITE | GMBUS_SW_RDY); while (len) { int ret; - u32 gmbus2; val = loop = 0; do { @@ -270,13 +282,9 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg) I915_WRITE(GMBUS3 + reg_offset, val); - ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & - (GMBUS_SATOER | GMBUS_HW_RDY), - 50); + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY); if (ret) - return -ETIMEDOUT; - if (gmbus2 & GMBUS_SATOER) - return -ENXIO; + return ret; } return 0; } @@ -345,8 +353,6 @@ gmbus_xfer(struct i2c_adapter *adapter, I915_WRITE(GMBUS0 + reg_offset, bus->reg0); for (i = 0; i < num; i++) { - u32 gmbus2; - if (gmbus_is_index_read(msgs, i, num)) { ret = gmbus_xfer_index_read(dev_priv, &msgs[i]); i += 1; /* set i to the index of the read xfer */ @@ -361,13 +367,11 @@ gmbus_xfer(struct i2c_adapter *adapter, if (ret == -ENXIO) goto clear_err; - ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & - (GMBUS_SATOER | GMBUS_HW_WAIT_PHASE), - 50); + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE); + if (ret == -ENXIO) + goto clear_err; if (ret) goto timeout; - if (gmbus2 & GMBUS_SATOER) - goto clear_err; } /* Generate a STOP condition on the bus. Note that gmbus can't generata -- cgit v1.2.3 From 515ac2bb95f609bc4a0d2ad5f7011b3264b2bb21 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:44 +0100 Subject: drm/i915: wire up gmbus irq handler Only enables the interrupt and puts a irq handler into place, doesn't do anything yet. Unfortunately there's no gmbus interrupt support for gen2/3 (safe for pnv, but there the irq is marked as "Test mode"). v2: Wire up the irq handler for vlv and gen4 properly. v3: i915_enable_pipestat expects the mask bit, not the status bits ... and for added hilarity those are rather inconsistently named. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 02a13ab148df..956b95f1f113 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -525,6 +525,11 @@ static void gen6_queue_rps_work(struct drm_i915_private *dev_priv, queue_work(dev_priv->wq, &dev_priv->rps.work); } +static void gmbus_irq_handler(struct drm_device *dev) +{ + DRM_DEBUG_DRIVER("GMBUS interrupt\n"); +} + static irqreturn_t valleyview_irq_handler(int irq, void *arg) { struct drm_device *dev = (struct drm_device *) arg; @@ -590,6 +595,9 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) I915_READ(PORT_HOTPLUG_STAT); } + if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS) + gmbus_irq_handler(dev); + if (pm_iir & GEN6_PM_DEFERRED_EVENTS) gen6_queue_rps_work(dev_priv, pm_iir); @@ -616,7 +624,7 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir) SDE_AUDIO_POWER_SHIFT); if (pch_iir & SDE_GMBUS) - DRM_DEBUG_DRIVER("PCH GMBUS interrupt\n"); + gmbus_irq_handler(dev); if (pch_iir & SDE_AUDIO_HDCP_MASK) DRM_DEBUG_DRIVER("PCH HDCP audio interrupt\n"); @@ -662,7 +670,7 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) DRM_DEBUG_DRIVER("AUX channel interrupt\n"); if (pch_iir & SDE_GMBUS_CPT) - DRM_DEBUG_DRIVER("PCH GMBUS interrupt\n"); + gmbus_irq_handler(dev); if (pch_iir & SDE_AUDIO_CP_REQ_CPT) DRM_DEBUG_DRIVER("Audio CP request interrupt\n"); @@ -1880,12 +1888,14 @@ static int ironlake_irq_postinstall(struct drm_device *dev) hotplug_mask = (SDE_CRT_HOTPLUG_CPT | SDE_PORTB_HOTPLUG_CPT | SDE_PORTC_HOTPLUG_CPT | - SDE_PORTD_HOTPLUG_CPT); + SDE_PORTD_HOTPLUG_CPT | + SDE_GMBUS_CPT); } else { hotplug_mask = (SDE_CRT_HOTPLUG | SDE_PORTB_HOTPLUG | SDE_PORTC_HOTPLUG | SDE_PORTD_HOTPLUG | + SDE_GMBUS | SDE_AUX_MASK); } @@ -1945,7 +1955,8 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) hotplug_mask = (SDE_CRT_HOTPLUG_CPT | SDE_PORTB_HOTPLUG_CPT | SDE_PORTC_HOTPLUG_CPT | - SDE_PORTD_HOTPLUG_CPT); + SDE_PORTD_HOTPLUG_CPT | + SDE_GMBUS_CPT); dev_priv->pch_irq_mask = ~hotplug_mask; I915_WRITE(SDEIIR, I915_READ(SDEIIR)); @@ -1999,6 +2010,7 @@ static int valleyview_irq_postinstall(struct drm_device *dev) POSTING_READ(VLV_IER); i915_enable_pipestat(dev_priv, 0, pipestat_enable); + i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE); i915_enable_pipestat(dev_priv, 1, pipestat_enable); I915_WRITE(VLV_IIR, 0xffffffff); @@ -2483,6 +2495,7 @@ static int i965_irq_postinstall(struct drm_device *dev) dev_priv->pipestat[0] = 0; dev_priv->pipestat[1] = 0; + i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE); /* * Enable some error detection, note the instruction error mask @@ -2636,6 +2649,9 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) if (blc_event || (iir & I915_ASLE_INTERRUPT)) intel_opregion_asle_intr(dev); + if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS) + gmbus_irq_handler(dev); + /* With MSI, interrupts are only generated when iir * transitions from zero to nonzero. If another bit got * set while we were handling the existing iir bits, then -- cgit v1.2.3 From 28c70f162a315bdcfbe0bf940a740ef8bfb918d6 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:45 +0100 Subject: drm/i915: use the gmbus irq for waits We need two special things to properly wire this up: - Add another argument to gmbus_wait_hw_status to pass in the correct interrupt bit in gmbus4. - Since we can only get an irq for one of the two events we want, hand-roll the wait_event_timeout code so that we wake up every jiffie and can check for NAKs. This way we also subsume gmbus support for platforms without interrupts (or where those are not yet enabled). The important bit really is to only enable one gmbus interrupt source at the same time - with that piece of lore figured out, this seems to work flawlessly. Ben Widawsky rightfully complained the lack of measurements for the claimed benefits (especially since the first version was actually broken and fell back to bit-banging). Previously reading the 256 byte hdmi EDID takes about 72 ms here. With this patch it's down to 33 ms. Given that transfering the 256 bytes over i2c at wire speed takes 20.5ms alone, the reduction in additional overhead is rather nice. v2: Chris Wilson wondered whether GMBUS4 might contain some set bits when booting up an hence result in some spurious interrupts. Since we clear GMBUS4 after every wait and we do gmbus transfer really early in the setup sequence to detect displays the window is small, but still be paranoid and clear it properly. v3: Clarify the comment that gmbus irq generation can only support one kind of event, why it bothers us and how we work around that limit. Cc: Daniel Kurtz Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 3 +++ drivers/gpu/drm/i915/i915_irq.c | 4 ++++ drivers/gpu/drm/i915/intel_i2c.c | 45 ++++++++++++++++++++++++++++++---------- 3 files changed, 41 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e7411f363b3d..3843383e13cc 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -641,6 +641,7 @@ typedef struct drm_i915_private { struct intel_gmbus gmbus[GMBUS_NUM_PORTS]; + /** gmbus_mutex protects against concurrent usage of the single hw gmbus * controller on different i2c buses. */ struct mutex gmbus_mutex; @@ -650,6 +651,8 @@ typedef struct drm_i915_private { */ uint32_t gpio_mmio_base; + wait_queue_head_t gmbus_wait_queue; + struct pci_dev *bridge_dev; struct intel_ring_buffer ring[I915_NUM_RINGS]; uint32_t next_seqno; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 956b95f1f113..917a48de08fa 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -527,7 +527,11 @@ static void gen6_queue_rps_work(struct drm_i915_private *dev_priv, static void gmbus_irq_handler(struct drm_device *dev) { + struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private; + DRM_DEBUG_DRIVER("GMBUS interrupt\n"); + + wake_up_all(&dev_priv->gmbus_wait_queue); } static irqreturn_t valleyview_irq_handler(int irq, void *arg) diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index a16eecdf1ed1..8b7189253cb4 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -63,6 +63,7 @@ intel_i2c_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; I915_WRITE(dev_priv->gpio_mmio_base + GMBUS0, 0); + I915_WRITE(dev_priv->gpio_mmio_base + GMBUS4, 0); } static void intel_i2c_quirk_set(struct drm_i915_private *dev_priv, bool enable) @@ -204,20 +205,38 @@ intel_gpio_setup(struct intel_gmbus *bus, u32 pin) static int gmbus_wait_hw_status(struct drm_i915_private *dev_priv, - u32 gmbus2_status) + u32 gmbus2_status, + u32 gmbus4_irq_en) { - int ret; + int i; int reg_offset = dev_priv->gpio_mmio_base; - u32 gmbus2; + u32 gmbus2 = 0; + DEFINE_WAIT(wait); + + /* Important: The hw handles only the first bit, so set only one! Since + * we also need to check for NAKs besides the hw ready/idle signal, we + * need to wake up periodically and check that ourselves. */ + I915_WRITE(GMBUS4 + reg_offset, gmbus4_irq_en); - ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & - (GMBUS_SATOER | gmbus2_status), - 50); + for (i = 0; i < msecs_to_jiffies(50) + 1; i++) { + prepare_to_wait(&dev_priv->gmbus_wait_queue, &wait, + TASK_UNINTERRUPTIBLE); + + gmbus2 = I915_READ(GMBUS2 + reg_offset); + if (gmbus2 & (GMBUS_SATOER | gmbus2_status)) + break; + + schedule_timeout(1); + } + finish_wait(&dev_priv->gmbus_wait_queue, &wait); + + I915_WRITE(GMBUS4 + reg_offset, 0); if (gmbus2 & GMBUS_SATOER) return -ENXIO; - - return ret; + if (gmbus2 & gmbus2_status) + return 0; + return -ETIMEDOUT; } static int @@ -238,7 +257,8 @@ gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg, int ret; u32 val, loop = 0; - ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY); + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY, + GMBUS_HW_RDY_EN); if (ret) return ret; @@ -282,7 +302,8 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg) I915_WRITE(GMBUS3 + reg_offset, val); - ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY); + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY, + GMBUS_HW_RDY_EN); if (ret) return ret; } @@ -367,7 +388,8 @@ gmbus_xfer(struct i2c_adapter *adapter, if (ret == -ENXIO) goto clear_err; - ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE); + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE, + GMBUS_HW_WAIT_EN); if (ret == -ENXIO) goto clear_err; if (ret) @@ -473,6 +495,7 @@ int intel_setup_gmbus(struct drm_device *dev) dev_priv->gpio_mmio_base = 0; mutex_init(&dev_priv->gmbus_mutex); + init_waitqueue_head(&dev_priv->gmbus_wait_queue); for (i = 0; i < GMBUS_NUM_PORTS; i++) { struct intel_gmbus *bus = &dev_priv->gmbus[i]; -- cgit v1.2.3 From 2c438c0273b76d6cb158f8bdd0aa3ebf66e48a28 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:46 +0100 Subject: drm/i915: use gmbus irq to wait for gmbus idle GMBUS_ACTIVE has inverted sense and so doesn't fit into the wait_hw_status helper, hence create a new gmbus_wait_idle functions. Also, we only care about the idle irq event and nothing else, which allows us to use the wait_event_timeout helper directly without jumping through hoops to catch NAKs. Since gen2/3 don't have gmbus interrupts, handle them separately with the old wait_for macro. This shaves another few ms off reading EDID from a hdmi screen on my testbox here. EDID reading with interrupt driven gmbus is now as fast as with busy-looping gmbus at 28 ms here (with negligible cpu overhead). Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_i2c.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index 8b7189253cb4..1fc31191b910 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -203,6 +203,7 @@ intel_gpio_setup(struct intel_gmbus *bus, u32 pin) algo->data = bus; } +#define HAS_GMBUS_IRQ(dev) (INTEL_INFO(dev)->gen >= 4) static int gmbus_wait_hw_status(struct drm_i915_private *dev_priv, u32 gmbus2_status, @@ -239,6 +240,31 @@ gmbus_wait_hw_status(struct drm_i915_private *dev_priv, return -ETIMEDOUT; } +static int +gmbus_wait_idle(struct drm_i915_private *dev_priv) +{ + int ret; + int reg_offset = dev_priv->gpio_mmio_base; + +#define C ((I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0) + + if (!HAS_GMBUS_IRQ(dev_priv->dev)) + return wait_for(C, 10); + + /* Important: The hw handles only the first bit, so set only one! */ + I915_WRITE(GMBUS4 + reg_offset, GMBUS_IDLE_EN); + + ret = wait_event_timeout(dev_priv->gmbus_wait_queue, C, 10); + + I915_WRITE(GMBUS4 + reg_offset, 0); + + if (ret) + return 0; + else + return -ETIMEDOUT; +#undef C +} + static int gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg, u32 gmbus1_index) @@ -406,8 +432,7 @@ gmbus_xfer(struct i2c_adapter *adapter, * We will re-enable it at the start of the next xfer, * till then let it sleep. */ - if (wait_for((I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0, - 10)) { + if (gmbus_wait_idle(dev_priv)) { DRM_DEBUG_KMS("GMBUS [%s] timed out waiting for idle\n", adapter->name); ret = -ETIMEDOUT; @@ -431,8 +456,7 @@ clear_err: * it's slow responding and only answers on the 2nd retry. */ ret = -ENXIO; - if (wait_for((I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0, - 10)) { + if (gmbus_wait_idle(dev_priv)) { DRM_DEBUG_KMS("GMBUS [%s] timed out after NAK\n", adapter->name); ret = -ETIMEDOUT; -- cgit v1.2.3 From ce99c2569dfcec9662993961db7f433e160c50f3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:47 +0100 Subject: drm/i915: wire up do aux channel done interrupt Doesn't do anything yet than call dp_aux_irq_handler. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 917a48de08fa..1e245e501e5a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -534,6 +534,11 @@ static void gmbus_irq_handler(struct drm_device *dev) wake_up_all(&dev_priv->gmbus_wait_queue); } +static void dp_aux_irq_handler(struct drm_device *dev) +{ + DRM_DEBUG_DRIVER("AUX channel interrupt\n"); +} + static irqreturn_t valleyview_irq_handler(int irq, void *arg) { struct drm_device *dev = (struct drm_device *) arg; @@ -627,6 +632,9 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir) (pch_iir & SDE_AUDIO_POWER_MASK) >> SDE_AUDIO_POWER_SHIFT); + if (pch_iir & SDE_AUX_MASK) + dp_aux_irq_handler(dev); + if (pch_iir & SDE_GMBUS) gmbus_irq_handler(dev); @@ -671,7 +679,7 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) SDE_AUDIO_POWER_SHIFT_CPT); if (pch_iir & SDE_AUX_MASK_CPT) - DRM_DEBUG_DRIVER("AUX channel interrupt\n"); + dp_aux_irq_handler(dev); if (pch_iir & SDE_GMBUS_CPT) gmbus_irq_handler(dev); @@ -712,6 +720,9 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) de_iir = I915_READ(DEIIR); if (de_iir) { + if (de_iir & DE_AUX_CHANNEL_A_IVB) + dp_aux_irq_handler(dev); + if (de_iir & DE_GSE_IVB) intel_opregion_gse_intr(dev); @@ -790,6 +801,9 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) else snb_gt_irq_handler(dev, dev_priv, gt_iir); + if (de_iir & DE_AUX_CHANNEL_A) + dp_aux_irq_handler(dev); + if (de_iir & DE_GSE) intel_opregion_gse_intr(dev); @@ -1858,7 +1872,8 @@ static int ironlake_irq_postinstall(struct drm_device *dev) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; /* enable kind of interrupts always enabled */ u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT | - DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE; + DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE | + DE_AUX_CHANNEL_A; u32 render_irqs; u32 hotplug_mask; @@ -1893,7 +1908,8 @@ static int ironlake_irq_postinstall(struct drm_device *dev) SDE_PORTB_HOTPLUG_CPT | SDE_PORTC_HOTPLUG_CPT | SDE_PORTD_HOTPLUG_CPT | - SDE_GMBUS_CPT); + SDE_GMBUS_CPT | + SDE_AUX_MASK_CPT); } else { hotplug_mask = (SDE_CRT_HOTPLUG | SDE_PORTB_HOTPLUG | @@ -1930,7 +1946,8 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) DE_MASTER_IRQ_CONTROL | DE_GSE_IVB | DE_PCH_EVENT_IVB | DE_PLANEC_FLIP_DONE_IVB | DE_PLANEB_FLIP_DONE_IVB | - DE_PLANEA_FLIP_DONE_IVB; + DE_PLANEA_FLIP_DONE_IVB | + DE_AUX_CHANNEL_A_IVB; u32 render_irqs; u32 hotplug_mask; @@ -1960,7 +1977,8 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) SDE_PORTB_HOTPLUG_CPT | SDE_PORTC_HOTPLUG_CPT | SDE_PORTD_HOTPLUG_CPT | - SDE_GMBUS_CPT); + SDE_GMBUS_CPT | + SDE_AUX_MASK_CPT); dev_priv->pch_irq_mask = ~hotplug_mask; I915_WRITE(SDEIIR, I915_READ(SDEIIR)); -- cgit v1.2.3 From 9ee32fea5fe810ec06af3a15e4c65478de56d4f5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:48 +0100 Subject: drm/i915: irq-drive the dp aux communication At least on the platforms that have a dp aux irq and also have it enabled - vlvhsw should have one, too. But I don't have a machine to test this on. Judging from docs there's no dp aux interrupt for gm45. Also, I only have an ivb cpu edp machine, so the dp aux A code for snb/ilk is untested. For dpcd probing when nothing is connected it slashes about 5ms of cpu time (cpu time is now negligible), which agrees with 3 * 5 400 usec timeouts. A previous version of this patch increases the time required to go through the dp_detect cycle (which includes reading the edid) from around 33 ms to around 40 ms. Experiments indicated that this is purely due to the irq latency - the hw doesn't allow us to queue up dp aux transactions and hence irq latency directly affects throughput. gmbus is much better, there we have a 8 byte buffer, and we get the irq once another 4 bytes can be queued up. But by using the pm_qos interface to request the lowest possible cpu wake-up latency this slowdown completely disappeared. Since all our output detection logic is single-threaded with the mode_config mutex right now anyway, I've decide not ot play fancy and to just reuse the gmbus wait queue. But this would definitely prep the way to run dp detection on different ports in parallel v2: Add a timeout for dp aux transfers when using interrupts - the hw _does_ prevent this with the hw-based 400 usec timeout, but if the irq somehow doesn't arrive we're screwed. Lesson learned while developing this ;-) v3: While at it also convert the busy-loop to wait_for_atomic, so that we don't run the risk of an infinite loop any more. v4: Ensure we have the smallest possible irq latency by using the pm_qos interface. v5: Add a comment to the code to explain why we frob pm_qos. Suggested by Chris Wilson. v6: Disable dp irq for vlv, that's easier than trying to get at docs and hw. v7: Squash in a fix for Haswell that Paulo Zanoni tracked down - the dp aux registers aren't at a fixed offset any more, but can be on the PCH while the DP port is on the cpu die. Reviewed-by: Imre Deak (v6) Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 1 + drivers/gpu/drm/i915/i915_drv.h | 4 +++ drivers/gpu/drm/i915/i915_irq.c | 6 ++++ drivers/gpu/drm/i915/intel_dp.c | 79 ++++++++++++++++++++++++++++++++++------- 4 files changed, 77 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 14d679ab52b7..93630669c6dc 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1737,6 +1737,7 @@ int i915_driver_unload(struct drm_device *dev) intel_teardown_mchbar(dev); destroy_workqueue(dev_priv->wq); + pm_qos_remove_request(&dev_priv->pm_qos); if (dev_priv->slab) kmem_cache_destroy(dev_priv->slab); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 3843383e13cc..a00ee3da632f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -40,6 +40,7 @@ #include #include #include +#include /* General customization: */ @@ -665,6 +666,9 @@ typedef struct drm_i915_private { /* protects the irq masks */ spinlock_t irq_lock; + /* To control wakeup latency, e.g. for irq-driven dp aux transfers. */ + struct pm_qos_request pm_qos; + /* DPIO indirect register protection */ spinlock_t dpio_lock; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 1e245e501e5a..58bb11b58796 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -536,7 +536,11 @@ static void gmbus_irq_handler(struct drm_device *dev) static void dp_aux_irq_handler(struct drm_device *dev) { + struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private; + DRM_DEBUG_DRIVER("AUX channel interrupt\n"); + + wake_up_all(&dev_priv->gmbus_wait_queue); } static irqreturn_t valleyview_irq_handler(int irq, void *arg) @@ -2732,6 +2736,8 @@ void intel_irq_init(struct drm_device *dev) setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, (unsigned long) dev); + pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, 0); + dev->driver->get_vblank_counter = i915_get_vblank_counter; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) { diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index b51043e651b5..ada1b316195c 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -322,6 +322,48 @@ intel_dp_check_edp(struct intel_dp *intel_dp) } } +static uint32_t +intel_dp_aux_wait_done(struct intel_dp *intel_dp, bool has_aux_irq) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = intel_dig_port->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t ch_ctl = intel_dp->output_reg + 0x10; + uint32_t status; + bool done; + + if (IS_HASWELL(dev)) { + switch (intel_dig_port->port) { + case PORT_A: + ch_ctl = DPA_AUX_CH_CTL; + break; + case PORT_B: + ch_ctl = PCH_DPB_AUX_CH_CTL; + break; + case PORT_C: + ch_ctl = PCH_DPC_AUX_CH_CTL; + break; + case PORT_D: + ch_ctl = PCH_DPD_AUX_CH_CTL; + break; + default: + BUG(); + } + } + +#define C (((status = I915_READ(ch_ctl)) & DP_AUX_CH_CTL_SEND_BUSY) == 0) + if (has_aux_irq) + done = wait_event_timeout(dev_priv->gmbus_wait_queue, C, 10); + else + done = wait_for_atomic(C, 10) == 0; + if (!done) + DRM_ERROR("dp aux hw did not signal timeout (has irq: %i)!\n", + has_aux_irq); +#undef C + + return status; +} + static int intel_dp_aux_ch(struct intel_dp *intel_dp, uint8_t *send, int send_bytes, @@ -333,11 +375,17 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, struct drm_i915_private *dev_priv = dev->dev_private; uint32_t ch_ctl = output_reg + 0x10; uint32_t ch_data = ch_ctl + 4; - int i; - int recv_bytes; + int i, ret, recv_bytes; uint32_t status; uint32_t aux_clock_divider; int try, precharge; + bool has_aux_irq = INTEL_INFO(dev)->gen >= 5 && !IS_VALLEYVIEW(dev); + + /* dp aux is extremely sensitive to irq latency, hence request the + * lowest possible wakeup latency and so prevent the cpu from going into + * deep sleep states. + */ + pm_qos_update_request(&dev_priv->pm_qos, 0); if (IS_HASWELL(dev)) { switch (intel_dig_port->port) { @@ -400,7 +448,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, if (try == 3) { WARN(1, "dp_aux_ch not started status 0x%08x\n", I915_READ(ch_ctl)); - return -EBUSY; + ret = -EBUSY; + goto out; } /* Must try at least 3 times according to DP spec */ @@ -413,6 +462,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, /* Send the command and wait for it to complete */ I915_WRITE(ch_ctl, DP_AUX_CH_CTL_SEND_BUSY | + (has_aux_irq ? DP_AUX_CH_CTL_INTERRUPT : 0) | DP_AUX_CH_CTL_TIME_OUT_400us | (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) | (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | @@ -420,12 +470,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, DP_AUX_CH_CTL_DONE | DP_AUX_CH_CTL_TIME_OUT_ERROR | DP_AUX_CH_CTL_RECEIVE_ERROR); - for (;;) { - status = I915_READ(ch_ctl); - if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) - break; - udelay(100); - } + + status = intel_dp_aux_wait_done(intel_dp, has_aux_irq); /* Clear done status and any errors */ I915_WRITE(ch_ctl, @@ -443,7 +489,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, if ((status & DP_AUX_CH_CTL_DONE) == 0) { DRM_ERROR("dp_aux_ch not done status 0x%08x\n", status); - return -EBUSY; + ret = -EBUSY; + goto out; } /* Check for timeout or receive error. @@ -451,14 +498,16 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, */ if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) { DRM_ERROR("dp_aux_ch receive error status 0x%08x\n", status); - return -EIO; + ret = -EIO; + goto out; } /* Timeouts occur when the device isn't connected, so they're * "normal" -- don't fill the kernel log with these */ if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) { DRM_DEBUG_KMS("dp_aux_ch timeout status 0x%08x\n", status); - return -ETIMEDOUT; + ret = -ETIMEDOUT; + goto out; } /* Unload any bytes sent back from the other side */ @@ -471,7 +520,11 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, unpack_aux(I915_READ(ch_data + i), recv + i, recv_bytes - i); - return recv_bytes; + ret = recv_bytes; +out: + pm_qos_update_request(&dev_priv->pm_qos, PM_QOS_DEFAULT_VALUE); + + return ret; } /* Write data to the aux channel in native mode */ -- cgit v1.2.3 From ef04f00d12312e43b13ba8f79435763e25aaff5c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 21:03:59 +0100 Subject: drm/i915: use _NOTRACE for gmbus/dp aux wait loops Less clutter in the traces. And in both cases we yell rather loud into the logs if we time out. Patch suggested by Chris Wilson. v2: Annotate another I915_READ in dp_aux to be consistent - we filter out all register io in wait_for and similar loops. Chris also suggested to mark all dp_aux register access as _NOTRACE, but I think we should keep all functionally relevant access around, and filter unneeded bits in userspace after the trace is captured. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 4 ++-- drivers/gpu/drm/i915/intel_i2c.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index ada1b316195c..3f633cadc8ba 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -351,7 +351,7 @@ intel_dp_aux_wait_done(struct intel_dp *intel_dp, bool has_aux_irq) } } -#define C (((status = I915_READ(ch_ctl)) & DP_AUX_CH_CTL_SEND_BUSY) == 0) +#define C (((status = I915_READ_NOTRACE(ch_ctl)) & DP_AUX_CH_CTL_SEND_BUSY) == 0) if (has_aux_irq) done = wait_event_timeout(dev_priv->gmbus_wait_queue, C, 10); else @@ -439,7 +439,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, /* Try to wait for any previous AUX channel activity */ for (try = 0; try < 3; try++) { - status = I915_READ(ch_ctl); + status = I915_READ_NOTRACE(ch_ctl); if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) break; msleep(1); diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index 1fc31191b910..7f0904170963 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -223,7 +223,7 @@ gmbus_wait_hw_status(struct drm_i915_private *dev_priv, prepare_to_wait(&dev_priv->gmbus_wait_queue, &wait, TASK_UNINTERRUPTIBLE); - gmbus2 = I915_READ(GMBUS2 + reg_offset); + gmbus2 = I915_READ_NOTRACE(GMBUS2 + reg_offset); if (gmbus2 & (GMBUS_SATOER | gmbus2_status)) break; @@ -246,7 +246,7 @@ gmbus_wait_idle(struct drm_i915_private *dev_priv) int ret; int reg_offset = dev_priv->gpio_mmio_base; -#define C ((I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0) +#define C ((I915_READ_NOTRACE(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0) if (!HAS_GMBUS_IRQ(dev_priv->dev)) return wait_for(C, 10); -- cgit v1.2.3 From 6ef6a450b9a4c8851d63e58aa62ee6a34c01c156 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 5 Dec 2012 22:37:17 +0800 Subject: drm/i915: Remove duplicate and unused register #defines in i915_reg.h TRANS_DP_VIDEO_AUDIO is not used at all. The other 3 has duplicated #defines. Signed-off-by: Dexuan Cui Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 462b50a3d88e..f2a5ea694684 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1875,8 +1875,6 @@ #define PFIT_SCALING_PILLAR (2 << 26) #define PFIT_SCALING_LETTER (3 << 26) #define PFIT_PGM_RATIOS 0x61234 -#define PFIT_VERT_SCALE_MASK 0xfff00000 -#define PFIT_HORIZ_SCALE_MASK 0x0000fff0 /* Pre-965 */ #define PFIT_VERT_SCALE_SHIFT 20 #define PFIT_VERT_SCALE_MASK 0xfff00000 @@ -3799,8 +3797,6 @@ #define TRANS_FSYNC_DELAY_HB2 (1<<27) #define TRANS_FSYNC_DELAY_HB3 (2<<27) #define TRANS_FSYNC_DELAY_HB4 (3<<27) -#define TRANS_DP_AUDIO_ONLY (1<<26) -#define TRANS_DP_VIDEO_AUDIO (0<<26) #define TRANS_INTERLACE_MASK (7<<21) #define TRANS_PROGRESSIVE (0<<21) #define TRANS_INTERLACED (3<<21) -- cgit v1.2.3 From 36dacf5b8b3c0bab7010943c34d2dbcc09a0d6f3 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Thu, 6 Dec 2012 09:44:52 -0200 Subject: drm/i915: be less verbose when handling gmbus/aux irqs Having 9500 lines repeated on dmesg does not help me at all. Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 58bb11b58796..2e1d80de6ccc 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -529,8 +529,6 @@ static void gmbus_irq_handler(struct drm_device *dev) { struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private; - DRM_DEBUG_DRIVER("GMBUS interrupt\n"); - wake_up_all(&dev_priv->gmbus_wait_queue); } @@ -538,8 +536,6 @@ static void dp_aux_irq_handler(struct drm_device *dev) { struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private; - DRM_DEBUG_DRIVER("AUX channel interrupt\n"); - wake_up_all(&dev_priv->gmbus_wait_queue); } -- cgit v1.2.3 From ed7ef43989b3a8235b1cbf1e2b025aa4af3368a6 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 6 Dec 2012 14:24:21 +0100 Subject: drm/i915: rip out pre-DDI stuff from haswell_crtc_mode_set Especially getting rid of all things lvds is ... great! v2: Drop the two additional pre-hsw hunks noticed by Paulo Zanoni. v3: - handle DP ports correctly (spoted by Paulo) - don't leave {} behind for a single-line block (again spotted by Paulo) - kill another if (IBX || CPT) block Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 128 +---------------------------------- 1 file changed, 2 insertions(+), 126 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 093a163d4b1c..5865cd9ddc3a 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5461,20 +5461,13 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; int num_connectors = 0; - intel_clock_t clock, reduced_clock; - u32 dpll = 0, fp = 0, fp2 = 0; - bool ok, has_reduced_clock = false; - bool is_lvds = false, is_dp = false, is_cpu_edp = false; + bool is_dp = false, is_cpu_edp = false; struct intel_encoder *encoder; - u32 temp; int ret; bool dither; for_each_encoder_on_crtc(dev, crtc, encoder) { switch (encoder->type) { - case INTEL_OUTPUT_LVDS: - is_lvds = true; - break; case INTEL_OUTPUT_DISPLAYPORT: is_dp = true; break; @@ -5508,143 +5501,26 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, if (!intel_ddi_pll_mode_set(crtc, adjusted_mode->clock)) return -EINVAL; - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) { - ok = ironlake_compute_clocks(crtc, adjusted_mode, &clock, - &has_reduced_clock, - &reduced_clock); - if (!ok) { - DRM_ERROR("Couldn't find PLL settings for mode!\n"); - return -EINVAL; - } - } - /* Ensure that the cursor is valid for the new mode before changing... */ intel_crtc_update_cursor(crtc, true); /* determine panel color depth */ dither = intel_choose_pipe_bpp_dither(crtc, fb, &intel_crtc->bpp, adjusted_mode); - if (is_lvds && dev_priv->lvds_dither) - dither = true; DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe); drm_mode_debug_printmodeline(mode); - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) { - fp = clock.n << 16 | clock.m1 << 8 | clock.m2; - if (has_reduced_clock) - fp2 = reduced_clock.n << 16 | reduced_clock.m1 << 8 | - reduced_clock.m2; - - dpll = ironlake_compute_dpll(intel_crtc, adjusted_mode, &clock, - fp); - - /* CPU eDP is the only output that doesn't need a PCH PLL of its - * own on pre-Haswell/LPT generation */ - if (!is_cpu_edp) { - struct intel_pch_pll *pll; - - pll = intel_get_pch_pll(intel_crtc, dpll, fp); - if (pll == NULL) { - DRM_DEBUG_DRIVER("failed to find PLL for pipe %d\n", - pipe); - return -EINVAL; - } - } else - intel_put_pch_pll(intel_crtc); - - /* The LVDS pin pair needs to be on before the DPLLs are - * enabled. This is an exception to the general rule that - * mode_set doesn't turn things on. - */ - if (is_lvds) { - temp = I915_READ(PCH_LVDS); - temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; - if (HAS_PCH_CPT(dev)) { - temp &= ~PORT_TRANS_SEL_MASK; - temp |= PORT_TRANS_SEL_CPT(pipe); - } else { - if (pipe == 1) - temp |= LVDS_PIPEB_SELECT; - else - temp &= ~LVDS_PIPEB_SELECT; - } - - /* set the corresponsding LVDS_BORDER bit */ - temp |= dev_priv->lvds_border_bits; - /* Set the B0-B3 data pairs corresponding to whether - * we're going to set the DPLLs for dual-channel mode or - * not. - */ - if (clock.p2 == 7) - temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; - else - temp &= ~(LVDS_B0B3_POWER_UP | - LVDS_CLKB_POWER_UP); - - /* It would be nice to set 24 vs 18-bit mode - * (LVDS_A3_POWER_UP) appropriately here, but we need to - * look more thoroughly into how panels behave in the - * two modes. - */ - temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY); - if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) - temp |= LVDS_HSYNC_POLARITY; - if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) - temp |= LVDS_VSYNC_POLARITY; - I915_WRITE(PCH_LVDS, temp); - } - } - - if (is_dp && !is_cpu_edp) { + if (is_dp && !is_cpu_edp) intel_dp_set_m_n(crtc, mode, adjusted_mode); - } else { - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) { - /* For non-DP output, clear any trans DP clock recovery - * setting.*/ - I915_WRITE(TRANSDATA_M1(pipe), 0); - I915_WRITE(TRANSDATA_N1(pipe), 0); - I915_WRITE(TRANSDPLINK_M1(pipe), 0); - I915_WRITE(TRANSDPLINK_N1(pipe), 0); - } - } intel_crtc->lowfreq_avail = false; - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) { - if (intel_crtc->pch_pll) { - I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll); - - /* Wait for the clocks to stabilize. */ - POSTING_READ(intel_crtc->pch_pll->pll_reg); - udelay(150); - - /* The pixel multiplier can only be updated once the - * DPLL is enabled and the clocks are stable. - * - * So write it again. - */ - I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll); - } - - if (intel_crtc->pch_pll) { - if (is_lvds && has_reduced_clock && i915_powersave) { - I915_WRITE(intel_crtc->pch_pll->fp1_reg, fp2); - intel_crtc->lowfreq_avail = true; - } else { - I915_WRITE(intel_crtc->pch_pll->fp1_reg, fp); - } - } - } intel_set_pipe_timings(intel_crtc, mode, adjusted_mode); if (!is_dp || is_cpu_edp) ironlake_set_m_n(crtc, mode, adjusted_mode); - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) - if (is_cpu_edp) - ironlake_set_pll_edp(crtc, adjusted_mode->clock); - haswell_set_pipeconf(crtc, adjusted_mode, dither); /* Set up the display plane register */ -- cgit v1.2.3 From ea9b6006b51b79cfbb87c1ca81923761b7799c0f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 29 Nov 2012 15:59:31 +0100 Subject: drm/i915: move set_pll_edp to intel_dp.c Now that we enable the cpu edp pll in intel_dp->pre_enable and no longer in crtc_mode_set, we can also move the modeset part to the intel_dp->mode_set callback. Previously this was not possible because the encoder ->mode_set callbacks are called after the crtc mode set callback. v2: Rebase on top of copy&pasted hsw crtc_mode_set. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 40 ------------------------------------ drivers/gpu/drm/i915/intel_dp.c | 40 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5865cd9ddc3a..f3e58a43886b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2294,43 +2294,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, return 0; } -static void ironlake_set_pll_edp(struct drm_crtc *crtc, int clock) -{ - struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - u32 dpa_ctl; - - DRM_DEBUG_KMS("eDP PLL enable for clock %d\n", clock); - dpa_ctl = I915_READ(DP_A); - dpa_ctl &= ~DP_PLL_FREQ_MASK; - - if (clock < 200000) { - u32 temp; - dpa_ctl |= DP_PLL_FREQ_160MHZ; - /* workaround for 160Mhz: - 1) program 0x4600c bits 15:0 = 0x8124 - 2) program 0x46010 bit 0 = 1 - 3) program 0x46034 bit 24 = 1 - 4) program 0x64000 bit 14 = 1 - */ - temp = I915_READ(0x4600c); - temp &= 0xffff0000; - I915_WRITE(0x4600c, temp | 0x8124); - - temp = I915_READ(0x46010); - I915_WRITE(0x46010, temp | 1); - - temp = I915_READ(0x46034); - I915_WRITE(0x46034, temp | (1 << 24)); - } else { - dpa_ctl |= DP_PLL_FREQ_270MHZ; - } - I915_WRITE(DP_A, dpa_ctl); - - POSTING_READ(DP_A); - udelay(500); -} - static void intel_fdi_normal_train(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -5429,9 +5392,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, fdi_config_ok = ironlake_check_fdi_lanes(intel_crtc); - if (is_cpu_edp) - ironlake_set_pll_edp(crtc, adjusted_mode->clock); - ironlake_set_pipeconf(crtc, adjusted_mode, dither); intel_wait_for_vblank(dev, pipe); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 3f633cadc8ba..e525f0302d8f 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -899,6 +899,43 @@ void intel_dp_init_link_config(struct intel_dp *intel_dp) } } +static void ironlake_set_pll_edp(struct drm_crtc *crtc, int clock) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 dpa_ctl; + + DRM_DEBUG_KMS("eDP PLL enable for clock %d\n", clock); + dpa_ctl = I915_READ(DP_A); + dpa_ctl &= ~DP_PLL_FREQ_MASK; + + if (clock < 200000) { + u32 temp; + dpa_ctl |= DP_PLL_FREQ_160MHZ; + /* workaround for 160Mhz: + 1) program 0x4600c bits 15:0 = 0x8124 + 2) program 0x46010 bit 0 = 1 + 3) program 0x46034 bit 24 = 1 + 4) program 0x64000 bit 14 = 1 + */ + temp = I915_READ(0x4600c); + temp &= 0xffff0000; + I915_WRITE(0x4600c, temp | 0x8124); + + temp = I915_READ(0x46010); + I915_WRITE(0x46010, temp | 1); + + temp = I915_READ(0x46034); + I915_WRITE(0x46034, temp | (1 << 24)); + } else { + dpa_ctl |= DP_PLL_FREQ_270MHZ; + } + I915_WRITE(DP_A, dpa_ctl); + + POSTING_READ(DP_A); + udelay(500); +} + static void intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -998,6 +1035,9 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, } else { intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT; } + + if (is_cpu_edp(intel_dp)) + ironlake_set_pll_edp(crtc, adjusted_mode->clock); } #define IDLE_ON_MASK (PP_ON | 0 | PP_SEQUENCE_MASK | 0 | PP_SEQUENCE_STATE_MASK) -- cgit v1.2.3 From 1ce17038093f02e708e5816f645bbec63aff8bd2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 29 Nov 2012 15:59:32 +0100 Subject: drm/i915: rip out pre-production ilk cpu edp w/a MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While reading docs I've noticed that this special workaround to select the 1.6 GHz DP clock only applies to pre-production ilk machines. Since the registers we're touching here are rather undocumented and might be harmful on later chips, rip it out. For the Bspec reference of this w/a look in "vol4g CPU Display Registers [DevILK]", Section 4.1.7.1 "DP_A—DisplayPort A Control Register", "DP_PLL_Frequency_Select". v2: Keep a debug message as a hint in case something regresses. Requested by Chris Wilson. Reviewed-by: Paulo Zanoni Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index e525f0302d8f..c01163d22d17 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -910,26 +910,15 @@ static void ironlake_set_pll_edp(struct drm_crtc *crtc, int clock) dpa_ctl &= ~DP_PLL_FREQ_MASK; if (clock < 200000) { - u32 temp; + /* For a long time we've carried around a ILK-DevA w/a for the + * 160MHz clock. If we're really unlucky, it's still required. + */ + DRM_DEBUG_KMS("160MHz cpu eDP clock, might need ilk devA w/a\n"); dpa_ctl |= DP_PLL_FREQ_160MHZ; - /* workaround for 160Mhz: - 1) program 0x4600c bits 15:0 = 0x8124 - 2) program 0x46010 bit 0 = 1 - 3) program 0x46034 bit 24 = 1 - 4) program 0x64000 bit 14 = 1 - */ - temp = I915_READ(0x4600c); - temp &= 0xffff0000; - I915_WRITE(0x4600c, temp | 0x8124); - - temp = I915_READ(0x46010); - I915_WRITE(0x46010, temp | 1); - - temp = I915_READ(0x46034); - I915_WRITE(0x46034, temp | (1 << 24)); } else { dpa_ctl |= DP_PLL_FREQ_270MHZ; } + I915_WRITE(DP_A, dpa_ctl); POSTING_READ(DP_A); -- cgit v1.2.3 From ab527efc2feadcab1cad0740be307cb4153b493f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 29 Nov 2012 15:59:33 +0100 Subject: drm/i915: use wait_for_vblank instead of msleep(17) 17 ms is eerily close to 60 Hz ^-1 Unfortunately this goes back to the original DP enabling for ilk, and unfortunately does not come with a reason for it's existance attached. Some closer inspection of the code and DP specs shows that we set the idle link pattern before we disable the port. And it seems like that the DP spec (or at least our hw) only switch to the idle pattern on the next vblank. Hence a vblank wait at this spot makes _much_ more sense than a really long wait. v2: Rebase fixup. v3: Add comment requested by Paulo Zanoni saying that we don't really know what this wait is for. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index c01163d22d17..89164c553b2d 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2041,6 +2041,8 @@ intel_dp_link_down(struct intel_dp *intel_dp) struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); struct drm_device *dev = intel_dig_port->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = + to_intel_crtc(intel_dig_port->base.base.crtc); uint32_t DP = intel_dp->DP; /* @@ -2075,7 +2077,8 @@ intel_dp_link_down(struct intel_dp *intel_dp) } POSTING_READ(intel_dp->output_reg); - msleep(17); + /* We don't really know why we're doing this */ + intel_wait_for_vblank(dev, intel_crtc->pipe); if (HAS_PCH_IBX(dev) && I915_READ(intel_dp->output_reg) & DP_PIPEB_SELECT) { @@ -2107,7 +2110,7 @@ intel_dp_link_down(struct intel_dp *intel_dp) POSTING_READ(intel_dp->output_reg); msleep(50); } else - intel_wait_for_vblank(dev, to_intel_crtc(crtc)->pipe); + intel_wait_for_vblank(dev, intel_crtc->pipe); } DP &= ~DP_AUDIO_OUTPUT_ENABLE; -- cgit v1.2.3 From ff50afe9aceb6264a4fbe40459da75170fb9a2a2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 29 Nov 2012 15:59:34 +0100 Subject: drm/i915: WARN on !crtc in intel_dp_link_down This could have happened with the old crtc helper based modeset code, but can't happen any longer with the new code. Hence put in a WARN and adjust the comment. If no one hits this, we can eventually remove it (like a few other such cases across our code). Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 89164c553b2d..84652ca3b161 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2098,15 +2098,10 @@ intel_dp_link_down(struct intel_dp *intel_dp) /* Changes to enable or select take place the vblank * after being written. */ - if (crtc == NULL) { - /* We can arrive here never having been attached - * to a CRTC, for instance, due to inheriting - * random state from the BIOS. - * - * If the pipe is not running, play safe and - * wait for the clocks to stabilise before - * continuing. - */ + if (WARN_ON(crtc == NULL)) { + /* We should never try to disable a port without a crtc + * attached. For paranoia keep the code around for a + * bit. */ POSTING_READ(intel_dp->output_reg); msleep(50); } else -- cgit v1.2.3 From 2f0c2ad18b88691496e23d1ddbc2d0af8f6df5fa Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 29 Nov 2012 15:59:35 +0100 Subject: drm/i915: drop unnecessary clearing of pch dp transcoder timings This has originally been added in commit 8db9d77b1b14fd730561f64beea8c00e4478d7c5 Author: Zhenyu Wang Date: Wed Apr 7 16:15:54 2010 +0800 drm/i915: Support for Cougarpoint PCH display pipeline probably to combat issues with hw state left behind by the BIOS. And indeed, I've checked out that specific revision, and there is no DP support yet. So the pch dp transcoder won't be correctly disabled, and that's important since it requires a rether special disable dance: Just writing 0 to TRANS_DP_CTL won't cut it, since we need to select the NONE port when disabling, too. And indeed, things seem to still work, so let's just remove this. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f3e58a43886b..953796c84fbd 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5345,15 +5345,8 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, } else intel_put_pch_pll(intel_crtc); - if (is_dp && !is_cpu_edp) { + if (is_dp && !is_cpu_edp) intel_dp_set_m_n(crtc, mode, adjusted_mode); - } else { - /* For non-DP output, clear any trans DP clock recovery setting.*/ - I915_WRITE(TRANSDATA_M1(pipe), 0); - I915_WRITE(TRANSDATA_N1(pipe), 0); - I915_WRITE(TRANSDPLINK_M1(pipe), 0); - I915_WRITE(TRANSDPLINK_N1(pipe), 0); - } for_each_encoder_on_crtc(dev, crtc, encoder) if (encoder->pre_pll_enable) -- cgit v1.2.3 From e69d0bc1c67520c302e070ac078975ea9c786de8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 29 Nov 2012 15:59:36 +0100 Subject: drm/i915: extract common link_m_n helpers Both the dp and fdi code use the exact same computations (ignore minor differences in conversion between bits and bytes). This makes it even more apparent that we have a _massive_ mess between cpu transcoder/fdi link/pch transcoder and pch link settings. And also that we have hilarious amounts of confusion between edp and dp (despite that they're identical at a link level). Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 13 ++++++++++++ drivers/gpu/drm/i915/intel_display.c | 31 +++++++++------------------- drivers/gpu/drm/i915/intel_dp.c | 39 +++--------------------------------- 3 files changed, 26 insertions(+), 57 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index a00ee3da632f..e9ac3603fbdb 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -107,6 +107,19 @@ struct intel_pch_pll { }; #define I915_NUM_PLLS 2 +/* Used by dp and fdi links */ +struct intel_link_m_n { + uint32_t tu; + uint32_t gmch_m; + uint32_t gmch_n; + uint32_t link_m; + uint32_t link_n; +}; + +void intel_link_compute_m_n(int bpp, int nlanes, + int pixel_clock, int link_clock, + struct intel_link_m_n *m_n); + struct intel_ddi_plls { int spll_refcount; int wrpll1_refcount; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 953796c84fbd..34832bc04931 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3951,16 +3951,8 @@ static int i830_get_display_clock_speed(struct drm_device *dev) return 133000; } -struct fdi_m_n { - u32 tu; - u32 gmch_m; - u32 gmch_n; - u32 link_m; - u32 link_n; -}; - static void -fdi_reduce_ratio(u32 *num, u32 *den) +intel_reduce_ratio(uint32_t *num, uint32_t *den) { while (*num > 0xffffff || *den > 0xffffff) { *num >>= 1; @@ -3968,20 +3960,18 @@ fdi_reduce_ratio(u32 *num, u32 *den) } } -static void -ironlake_compute_m_n(int bits_per_pixel, int nlanes, int pixel_clock, - int link_clock, struct fdi_m_n *m_n) +void +intel_link_compute_m_n(int bits_per_pixel, int nlanes, + int pixel_clock, int link_clock, + struct intel_link_m_n *m_n) { - m_n->tu = 64; /* default size */ - - /* BUG_ON(pixel_clock > INT_MAX / 36); */ + m_n->tu = 64; m_n->gmch_m = bits_per_pixel * pixel_clock; m_n->gmch_n = link_clock * nlanes * 8; - fdi_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); - + intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); m_n->link_m = pixel_clock; m_n->link_n = link_clock; - fdi_reduce_ratio(&m_n->link_m, &m_n->link_n); + intel_reduce_ratio(&m_n->link_m, &m_n->link_n); } static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv) @@ -5095,7 +5085,7 @@ static void ironlake_set_m_n(struct drm_crtc *crtc, struct intel_crtc *intel_crtc = to_intel_crtc(crtc); enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder; struct intel_encoder *intel_encoder, *edp_encoder = NULL; - struct fdi_m_n m_n = {0}; + struct intel_link_m_n m_n = {0}; int target_clock, pixel_multiplier, lane, link_bw; bool is_dp = false, is_cpu_edp = false; @@ -5153,8 +5143,7 @@ static void ironlake_set_m_n(struct drm_crtc *crtc, if (pixel_multiplier > 1) link_bw *= pixel_multiplier; - ironlake_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw, - &m_n); + intel_link_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw, &m_n); I915_WRITE(PIPE_DATA_M1(cpu_transcoder), TU_SIZE(m_n.tu) | m_n.gmch_m); I915_WRITE(PIPE_DATA_N1(cpu_transcoder), m_n.gmch_n); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 84652ca3b161..b2130bc6c297 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -790,39 +790,6 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, return false; } -struct intel_dp_m_n { - uint32_t tu; - uint32_t gmch_m; - uint32_t gmch_n; - uint32_t link_m; - uint32_t link_n; -}; - -static void -intel_reduce_ratio(uint32_t *num, uint32_t *den) -{ - while (*num > 0xffffff || *den > 0xffffff) { - *num >>= 1; - *den >>= 1; - } -} - -static void -intel_dp_compute_m_n(int bpp, - int nlanes, - int pixel_clock, - int link_clock, - struct intel_dp_m_n *m_n) -{ - m_n->tu = 64; - m_n->gmch_m = (pixel_clock * bpp) >> 3; - m_n->gmch_n = link_clock * nlanes; - intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); - m_n->link_m = pixel_clock; - m_n->link_n = link_clock; - intel_reduce_ratio(&m_n->link_m, &m_n->link_n); -} - void intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -833,7 +800,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int lane_count = 4; - struct intel_dp_m_n m_n; + struct intel_link_m_n m_n; int pipe = intel_crtc->pipe; enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder; @@ -856,8 +823,8 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, * the number of bytes_per_pixel post-LUT, which we always * set up for 8-bits of R/G/B, or 3 bytes total. */ - intel_dp_compute_m_n(intel_crtc->bpp, lane_count, - mode->clock, adjusted_mode->clock, &m_n); + intel_link_compute_m_n(intel_crtc->bpp, lane_count, + mode->clock, adjusted_mode->clock, &m_n); if (IS_HASWELL(dev)) { I915_WRITE(PIPE_DATA_M1(cpu_transcoder), -- cgit v1.2.3 From e9b73c67390a5d4faec1d22cbdf24cd6fcca53f6 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 3 Dec 2012 21:03:14 +0000 Subject: drm/i915: Reduce memory pressure during shrinker by preallocating swizzle pages On a machine with bit17 swizzling, we need to store the bit17 of the physical page address in put-pages. This requires a memory allocation, on average less than a page, which may be difficult to satisfy is the request to put-pages is on behalf of the shrinker. We could allow that allocation to pull from the reserved memory pools, but it seems much safer to preallocate the array for tiled objects on affected machines. v2: Export i915_gem_object_needs_bit17_swizzle() for reuse. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 10 ++++++++++ drivers/gpu/drm/i915/i915_gem.c | 8 -------- drivers/gpu/drm/i915/i915_gem_tiling.c | 12 ++++++++++++ 3 files changed, 22 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e9ac3603fbdb..2ab476df1dc2 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -30,6 +30,8 @@ #ifndef _I915_DRV_H_ #define _I915_DRV_H_ +#include + #include "i915_reg.h" #include "intel_bios.h" #include "intel_ringbuffer.h" @@ -1614,6 +1616,14 @@ i915_gem_object_create_stolen(struct drm_device *dev, u32 size); void i915_gem_object_release_stolen(struct drm_i915_gem_object *obj); /* i915_gem_tiling.c */ +inline static bool i915_gem_object_needs_bit17_swizzle(struct drm_i915_gem_object *obj) +{ + drm_i915_private_t *dev_priv = obj->base.dev->dev_private; + + return dev_priv->mm.bit_6_swizzle_x == I915_BIT_6_SWIZZLE_9_10_17 && + obj->tiling_mode != I915_TILING_NONE; +} + void i915_gem_detect_bit_6_swizzle(struct drm_device *dev); void i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj); void i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 02d315164aa9..e4b233df576f 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -271,14 +271,6 @@ i915_gem_create_ioctl(struct drm_device *dev, void *data, args->size, &args->handle); } -static int i915_gem_object_needs_bit17_swizzle(struct drm_i915_gem_object *obj) -{ - drm_i915_private_t *dev_priv = obj->base.dev->dev_private; - - return dev_priv->mm.bit_6_swizzle_x == I915_BIT_6_SWIZZLE_9_10_17 && - obj->tiling_mode != I915_TILING_NONE; -} - static inline int __copy_to_user_swizzled(char __user *cpu_vaddr, const char *gpu_vaddr, int gpu_offset, diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index cedbfd7b3dfa..65f1d4f3f775 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -396,6 +396,18 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, /* we have to maintain this existing ABI... */ args->stride = obj->stride; args->tiling_mode = obj->tiling_mode; + + /* Try to preallocate memory required to save swizzling on put-pages */ + if (i915_gem_object_needs_bit17_swizzle(obj)) { + if (obj->bit_17 == NULL) { + obj->bit_17 = kmalloc(BITS_TO_LONGS(obj->base.size >> PAGE_SHIFT) * + sizeof(long), GFP_KERNEL); + } + } else { + kfree(obj->bit_17); + obj->bit_17 = NULL; + } + drm_gem_object_unreference(&obj->base); mutex_unlock(&dev->struct_mutex); -- cgit v1.2.3 From 97a19a247c23e286814a5ac7ec0825d0ff82a16c Mon Sep 17 00:00:00 2001 From: Tomas Janousek Date: Sat, 8 Dec 2012 13:48:13 +0100 Subject: drm/i915: don't prevent CPU idle states Commit 9ee32fea5f unconditionally prevents the CPU from entering idle states until intel_dp_aux_ch completes for the first time, which never happens on my DisplayPort-less intel gfx, causing the CPU to get rather hot. Signed-off-by: Tomas Janousek Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 2e1d80de6ccc..914ecf4acfa6 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2732,7 +2732,7 @@ void intel_irq_init(struct drm_device *dev) setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, (unsigned long) dev); - pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, 0); + pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); dev->driver->get_vblank_counter = i915_get_vblank_counter; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ -- cgit v1.2.3 From 378a6a77ae59d3312627b996ded94e23166d9e63 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 7 Dec 2012 14:18:29 +0530 Subject: drm/i915: Remove duplicate inclusion of drm/drm_edid.h drm/drm_edid.h was included twice. Signed-off-by: Sachin Kamat Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_modes.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c index b00f1c83adce..49249bb97485 100644 --- a/drivers/gpu/drm/i915/intel_modes.c +++ b/drivers/gpu/drm/i915/intel_modes.c @@ -28,7 +28,6 @@ #include #include #include -#include #include "intel_drv.h" #include "i915_drv.h" -- cgit v1.2.3 From 3ac18232946aacf0a9807c3143f8449ed4aa68f4 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Fri, 7 Dec 2012 07:54:26 -0700 Subject: i915: intel_set_mode: Reduce stack allocation from 500 bytes to 2 pointers smatch warning: drivers/gpu/drm/i915/intel_display.c:7019 intel_set_mode() warn: function puts 500 bytes on stack Refactor so that saved_mode and saved_hwmode are dynamically allocated as opposed to being automatic variables. 500 bytes seems like it could run the potential for blowing the kernel stack. Cc: Daniel Vetter Cc: David Airlie Cc: dri-devel@lists.freedesktop.org Signed-off-by: Tim Gardner Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 34832bc04931..115bf626037c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7398,11 +7398,18 @@ bool intel_set_mode(struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode; + struct drm_display_mode *adjusted_mode, *saved_mode, *saved_hwmode; struct intel_crtc *intel_crtc; unsigned disable_pipes, prepare_pipes, modeset_pipes; bool ret = true; + saved_mode = kmalloc(2 * sizeof(*saved_mode), GFP_KERNEL); + if (!saved_mode) { + DRM_ERROR("i915: Could not allocate saved display mode.\n"); + return false; + } + saved_hwmode = saved_mode + 1; + intel_modeset_affected_pipes(crtc, &modeset_pipes, &prepare_pipes, &disable_pipes); @@ -7412,8 +7419,8 @@ bool intel_set_mode(struct drm_crtc *crtc, for_each_intel_crtc_masked(dev, disable_pipes, intel_crtc) intel_crtc_disable(&intel_crtc->base); - saved_hwmode = crtc->hwmode; - saved_mode = crtc->mode; + *saved_hwmode = crtc->hwmode; + *saved_mode = crtc->mode; /* Hack: Because we don't (yet) support global modeset on multiple * crtcs, we don't keep track of the new mode for more than one crtc. @@ -7424,7 +7431,8 @@ bool intel_set_mode(struct drm_crtc *crtc, if (modeset_pipes) { adjusted_mode = intel_modeset_adjusted_mode(crtc, mode); if (IS_ERR(adjusted_mode)) { - return false; + ret = false; + goto out; } } @@ -7476,12 +7484,14 @@ bool intel_set_mode(struct drm_crtc *crtc, done: drm_mode_destroy(dev, adjusted_mode); if (!ret && crtc->enabled) { - crtc->hwmode = saved_hwmode; - crtc->mode = saved_mode; + crtc->hwmode = *saved_hwmode; + crtc->mode = *saved_mode; } else { intel_modeset_check_state(dev); } +out: + kfree(saved_mode); return ret; } -- cgit v1.2.3 From f72b3435c1a75406d82d6e252bb78f009efd4bd9 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Mon, 10 Dec 2012 15:41:48 +0200 Subject: drm/i915: Don't emit semaphore wait if wrap happened If wrap just happened we need to prevent emitting waits for pre wrap values. Detect this and emit no-ops instead. v2: Use olr > seqno to detect wrap instead of *seqno == 0 as suggested by Chris Wilson. v3: Use last used seqno to detect the wraparound. From Chris Wilson v4: Fixed unnecessary last_seqno assigment References: https://bugs.freedesktop.org/show_bug.cgi?id=57967 Signed-off-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 +- drivers/gpu/drm/i915/i915_gem.c | 2 +- drivers/gpu/drm/i915/intel_ringbuffer.c | 26 +++++++++++++++++++++----- 3 files changed, 23 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 2ab476df1dc2..9da67824dde3 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -671,7 +671,7 @@ typedef struct drm_i915_private { struct pci_dev *bridge_dev; struct intel_ring_buffer ring[I915_NUM_RINGS]; - uint32_t next_seqno; + uint32_t last_seqno, next_seqno; drm_dma_handle_t *status_page_dmah; struct resource mch_res; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index e4b233df576f..a81b78a59bd9 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1969,7 +1969,7 @@ i915_gem_get_seqno(struct drm_device *dev, u32 *seqno) dev_priv->next_seqno = 1; } - *seqno = dev_priv->next_seqno++; + *seqno = dev_priv->last_seqno = dev_priv->next_seqno++; return 0; } diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 0d03dc649c5e..69bbe7bb9f6d 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -596,6 +596,13 @@ gen6_add_request(struct intel_ring_buffer *ring) return 0; } +static inline bool i915_gem_has_seqno_wrapped(struct drm_device *dev, + u32 seqno) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + return dev_priv->last_seqno < seqno; +} + /** * intel_ring_sync - sync the waiter to the signaller on seqno * @@ -626,11 +633,20 @@ gen6_ring_sync(struct intel_ring_buffer *waiter, if (ret) return ret; - intel_ring_emit(waiter, - dw1 | signaller->semaphore_register[waiter->id]); - intel_ring_emit(waiter, seqno); - intel_ring_emit(waiter, 0); - intel_ring_emit(waiter, MI_NOOP); + /* If seqno wrap happened, omit the wait with no-ops */ + if (likely(!i915_gem_has_seqno_wrapped(waiter->dev, seqno))) { + intel_ring_emit(waiter, + dw1 | + signaller->semaphore_register[waiter->id]); + intel_ring_emit(waiter, seqno); + intel_ring_emit(waiter, 0); + intel_ring_emit(waiter, MI_NOOP); + } else { + intel_ring_emit(waiter, MI_NOOP); + intel_ring_emit(waiter, MI_NOOP); + intel_ring_emit(waiter, MI_NOOP); + intel_ring_emit(waiter, MI_NOOP); + } intel_ring_advance(waiter); return 0; -- cgit v1.2.3 From 107f27a5df8ad33a351d6e82fe95ff92b428f72e Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 10 Dec 2012 13:56:17 +0200 Subject: drm/i915: Open-code i915_gpu_idle() for handling seqno wrapping The complication is that during seqno wrapping we must be extremely careful not to write to any ring as that will require a new seqno, and so would recurse back into the seqno wrap handler. So we cannot call i915_gpu_idle() as that does additional work beyond simply retiring the current set of requests, and instead must do the minimal work ourselves during seqno wrapping. Signed-off-by: Chris Wilson Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index a81b78a59bd9..bc73f98c3631 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1938,11 +1938,15 @@ i915_gem_handle_seqno_wrap(struct drm_device *dev) if (ret == 0) return ret; - ret = i915_gpu_idle(dev); - if (ret) - return ret; - + /* Carefully retire all requests without writing to the rings */ + for_each_ring(ring, dev_priv, i) { + ret = intel_ring_idle(ring); + if (ret) + return ret; + } i915_gem_retire_requests(dev); + + /* Finally reset hw state */ for_each_ring(ring, dev_priv, i) { ret = intel_ring_handle_seqno_wrap(ring); if (ret) -- cgit v1.2.3 From 9e8e36879f268f1652e11b1c8560bbb67cf4f08e Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Tue, 4 Dec 2012 15:12:05 +0200 Subject: drm/i915: Set initial seqno value close to wrap boundary To gain confidence in the wrap handling, make it happen quite soon after the boot. Signed-off-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index bc73f98c3631..6380c6083cb2 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3943,7 +3943,7 @@ i915_gem_init_hw(struct drm_device *dev) goto cleanup_bsd_ring; } - dev_priv->next_seqno = 1; + dev_priv->next_seqno = (u32)-1 - 0x1000; /* * XXX: There was some w/a described somewhere suggesting loading -- cgit v1.2.3 From 20afbda209d708be66944907966486d0c1331cb8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 14:05:07 +0100 Subject: drm/i915: Fixup hpd irq register setup ordering For GMCH platforms we set up the hpd irq registers in the irq postinstall hook. But since we only enable the irq sources we actually need in PORT_HOTPLUG_EN/STATUS, taking dev_priv->hotplug_supported_mask into account, no hpd interrupt sources is enabled since commit 52d7ecedac3f96fb562cb482c139015372728638 Author: Daniel Vetter Date: Sat Dec 1 21:03:22 2012 +0100 drm/i915: reorder setup sequence to have irqs for output setup Wrongly set-up interrupts also lead to broken hw-based load-detection on at least GM45, resulting in ghost VGA/TV-out outputs. To fix this, delay the hotplug register setup until after all outputs are set up, by moving it into a new dev_priv->display.hpd_irq_callback. We might also move the PCH_SPLIT platforms to such a setup eventually. Another funny part is that we need to delay the fbdev initial config probing until after the hpd regs are setup, for otherwise it'll detect ghost outputs. But we can only enable the hpd interrupt handling itself (and the output polling) _after_ that initial scan, due to massive locking brain-damage in the fbdev setup code. Add a big comment to explain this cute little dragon lair. v2: Encapsulate all the fbdev handling by wrapping the move call into intel_fbdev_initial_config in intel_fb.c. Requested by Chris Wilson. v3: Applied bikeshed from Jesse Barnes. v4: Imre Deak noticed that we also need to call intel_hpd_init after the drm_irqinstall calls in the gpu reset and resume paths - otherwise hotplug will be broken. Also improve the comment a bit about why hpd_init needs to be called before we set up the initial fbdev config. Bugzilla: Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=54943 Reported-by: Chris Wilson Reviewed-by: Jesse Barnes (v3) Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 15 ++++++++++ drivers/gpu/drm/i915/i915_drv.c | 2 ++ drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/i915_irq.c | 63 +++++++++++++++++++++++++++++++--------- drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_fb.c | 10 ++++++- 6 files changed, 79 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 93630669c6dc..0c2ab40ab2ed 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1318,6 +1318,21 @@ static int i915_load_modeset_init(struct drm_device *dev) if (ret) goto cleanup_gem; + /* Only enable hotplug handling once the fbdev is fully set up. */ + intel_hpd_init(dev); + + /* + * Some ports require correctly set-up hpd registers for detection to + * work properly (leading to ghost connected connector status), e.g. VGA + * on gm45. Hence we can only set up the initial fbdev config after hpd + * irqs are fully enabled. Now we should scan for the initial config + * only once hotplug handling is enabled, but due to screwed-up locking + * around kms/fbdev init we can't protect the fdbev initial config + * scanning against hotplug events. Hence do this first and ignore the + * tiny window where we will loose hotplug notifactions. + */ + intel_fbdev_initial_config(dev); + /* Only enable hotplug handling once the fbdev is fully set up. */ dev_priv->enable_hotplug_processing = true; diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index a12921892446..fbd0b28b7200 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -566,6 +566,7 @@ static int __i915_drm_thaw(struct drm_device *dev) intel_modeset_init_hw(dev); intel_modeset_setup_hw_state(dev, false); drm_irq_install(dev); + intel_hpd_init(dev); } intel_opregion_init(dev); @@ -871,6 +872,7 @@ int i915_reset(struct drm_device *dev) drm_irq_uninstall(dev); drm_irq_install(dev); + intel_hpd_init(dev); } else { mutex_unlock(&dev->struct_mutex); } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 9da67824dde3..e55303e2f74e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -297,6 +297,7 @@ struct drm_i915_display_funcs { struct drm_i915_gem_object *obj); int (*update_plane)(struct drm_crtc *crtc, struct drm_framebuffer *fb, int x, int y); + void (*hpd_irq_setup)(struct drm_device *dev); /* clock updates for mode set */ /* cursor updates */ /* render clock increase/decrease */ @@ -1343,6 +1344,7 @@ void i915_hangcheck_elapsed(unsigned long data); void i915_handle_error(struct drm_device *dev, bool wedged); extern void intel_irq_init(struct drm_device *dev); +extern void intel_hpd_init(struct drm_device *dev); extern void intel_gt_init(struct drm_device *dev); extern void intel_gt_reset(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 914ecf4acfa6..551f370657b7 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1995,7 +1995,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 enable_mask; - u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN); u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV; u32 render_irqs; u16 msid; @@ -2024,6 +2023,9 @@ static int valleyview_irq_postinstall(struct drm_device *dev) msid |= (1<<14); pci_write_config_word(dev_priv->dev->pdev, 0x98, msid); + I915_WRITE(PORT_HOTPLUG_EN, 0); + POSTING_READ(PORT_HOTPLUG_EN); + I915_WRITE(VLV_IMR, dev_priv->irq_mask); I915_WRITE(VLV_IER, enable_mask); I915_WRITE(VLV_IIR, 0xffffffff); @@ -2053,6 +2055,15 @@ static int valleyview_irq_postinstall(struct drm_device *dev) #endif I915_WRITE(VLV_MASTER_IER, MASTER_INTERRUPT_ENABLE); + + return 0; +} + +static void valleyview_hpd_irq_setup(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN); + /* Note HDMI and DP share bits */ if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) hotplug_en |= HDMIB_HOTPLUG_INT_EN; @@ -2070,8 +2081,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev) } I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); - - return 0; } static void valleyview_irq_uninstall(struct drm_device *dev) @@ -2301,6 +2310,9 @@ static int i915_irq_postinstall(struct drm_device *dev) I915_USER_INTERRUPT; if (I915_HAS_HOTPLUG(dev)) { + I915_WRITE(PORT_HOTPLUG_EN, 0); + POSTING_READ(PORT_HOTPLUG_EN); + /* Enable in IER... */ enable_mask |= I915_DISPLAY_PORT_INTERRUPT; /* and unmask in IMR */ @@ -2311,8 +2323,18 @@ static int i915_irq_postinstall(struct drm_device *dev) I915_WRITE(IER, enable_mask); POSTING_READ(IER); + intel_opregion_enable_asle(dev); + + return 0; +} + +static void i915_hpd_irq_setup(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 hotplug_en; + if (I915_HAS_HOTPLUG(dev)) { - u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN); + hotplug_en = I915_READ(PORT_HOTPLUG_EN); if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) hotplug_en |= HDMIB_HOTPLUG_INT_EN; @@ -2333,10 +2355,6 @@ static int i915_irq_postinstall(struct drm_device *dev) I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); } - - intel_opregion_enable_asle(dev); - - return 0; } static irqreturn_t i915_irq_handler(int irq, void *arg) @@ -2496,7 +2514,6 @@ static void i965_irq_preinstall(struct drm_device * dev) static int i965_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 hotplug_en; u32 enable_mask; u32 error_mask; @@ -2538,6 +2555,19 @@ static int i965_irq_postinstall(struct drm_device *dev) I915_WRITE(IER, enable_mask); POSTING_READ(IER); + I915_WRITE(PORT_HOTPLUG_EN, 0); + POSTING_READ(PORT_HOTPLUG_EN); + + intel_opregion_enable_asle(dev); + + return 0; +} + +static void i965_hpd_irq_setup(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 hotplug_en; + /* Note HDMI and DP share hotplug bits */ hotplug_en = 0; if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) @@ -2572,10 +2602,6 @@ static int i965_irq_postinstall(struct drm_device *dev) /* Ignore TV since it's buggy */ I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); - - intel_opregion_enable_asle(dev); - - return 0; } static irqreturn_t i965_irq_handler(int irq, void *arg) @@ -2754,6 +2780,7 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_uninstall = valleyview_irq_uninstall; dev->driver->enable_vblank = valleyview_enable_vblank; dev->driver->disable_vblank = valleyview_disable_vblank; + dev_priv->display.hpd_irq_setup = valleyview_hpd_irq_setup; } else if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) { /* Share pre & uninstall handlers with ILK/SNB */ dev->driver->irq_handler = ivybridge_irq_handler; @@ -2780,13 +2807,23 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_postinstall = i915_irq_postinstall; dev->driver->irq_uninstall = i915_irq_uninstall; dev->driver->irq_handler = i915_irq_handler; + dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup; } else { dev->driver->irq_preinstall = i965_irq_preinstall; dev->driver->irq_postinstall = i965_irq_postinstall; dev->driver->irq_uninstall = i965_irq_uninstall; dev->driver->irq_handler = i965_irq_handler; + dev_priv->display.hpd_irq_setup = i965_hpd_irq_setup; } dev->driver->enable_vblank = i915_enable_vblank; dev->driver->disable_vblank = i915_disable_vblank; } } + +void intel_hpd_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (dev_priv->display.hpd_irq_setup) + dev_priv->display.hpd_irq_setup(dev); +} diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 7ca7772eaccd..22728f2c1d83 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -587,6 +587,7 @@ extern int intel_framebuffer_init(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd, struct drm_i915_gem_object *obj); extern int intel_fbdev_init(struct drm_device *dev); +extern void intel_fbdev_initial_config(struct drm_device *dev); extern void intel_fbdev_fini(struct drm_device *dev); extern void intel_fbdev_set_suspend(struct drm_device *dev, int state); extern void intel_prepare_page_flip(struct drm_device *dev, int plane); diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index b7773e548911..822896782cd0 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -243,10 +243,18 @@ int intel_fbdev_init(struct drm_device *dev) } drm_fb_helper_single_add_all_connectors(&ifbdev->helper); - drm_fb_helper_initial_config(&ifbdev->helper, 32); + return 0; } +void intel_fbdev_initial_config(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + + /* Due to peculiar init order wrt to hpd handling this is separate. */ + drm_fb_helper_initial_config(&dev_priv->fbdev->helper, 32); +} + void intel_fbdev_fini(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; -- cgit v1.2.3 From 09153000b8ca32a539a1207edebabd0d40b6c61b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 12 Dec 2012 14:06:44 +0100 Subject: drm/i915: rework locking for intel_dpio|sbi_read|write Spinning for up to 200 us with interrupts locked out is not good. So let's just spin (and even that seems to be excessive). And we don't call these functions from interrupt context, so this is not required. Besides that doing anything in interrupt contexts which might take a few hundred us is a no-go. So just convert the entire thing to a mutex. Also move the mutex-grabbing out of the read/write functions (add a WARN_ON(!is_locked)) instead) since all callers are nicely grouped together. Finally the real motivation for this change: Dont grab the modeset mutex in the dpio debugfs file, we don't need that consistency. And correctness of the dpio interface is ensured with the dpio_lock. Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 4 +-- drivers/gpu/drm/i915/i915_dma.c | 2 +- drivers/gpu/drm/i915/i915_drv.h | 2 +- drivers/gpu/drm/i915/intel_display.c | 53 ++++++++++++++---------------------- 4 files changed, 25 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 7e516eebdc80..7047c4a9fb9e 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1633,7 +1633,7 @@ static int i915_dpio_info(struct seq_file *m, void *data) return 0; } - ret = mutex_lock_interruptible(&dev->mode_config.mutex); + ret = mutex_lock_interruptible(&dev_priv->dpio_lock); if (ret) return ret; @@ -1662,7 +1662,7 @@ static int i915_dpio_info(struct seq_file *m, void *data) seq_printf(m, "DPIO_FASTCLK_DISABLE: 0x%08x\n", intel_dpio_read(dev_priv, DPIO_FASTCLK_DISABLE)); - mutex_unlock(&dev->mode_config.mutex); + mutex_unlock(&dev_priv->dpio_lock); return 0; } diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 0c2ab40ab2ed..a99c46d1e49a 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1599,7 +1599,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) spin_lock_init(&dev_priv->irq_lock); spin_lock_init(&dev_priv->error_lock); spin_lock_init(&dev_priv->rps.lock); - spin_lock_init(&dev_priv->dpio_lock); + mutex_init(&dev_priv->dpio_lock); mutex_init(&dev_priv->rps.hw_lock); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e55303e2f74e..514aee895ecb 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -686,7 +686,7 @@ typedef struct drm_i915_private { struct pm_qos_request pm_qos; /* DPIO indirect register protection */ - spinlock_t dpio_lock; + struct mutex dpio_lock; /** Cached value of IMR to avoid reads in updating the bitfield */ u32 pipestat[2]; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 115bf626037c..2a01b09221fb 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -416,13 +416,11 @@ static const intel_limit_t intel_limits_vlv_dp = { u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg) { - unsigned long flags; - u32 val = 0; + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - spin_lock_irqsave(&dev_priv->dpio_lock, flags); if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { DRM_ERROR("DPIO idle wait timed out\n"); - goto out_unlock; + return 0; } I915_WRITE(DPIO_REG, reg); @@ -430,24 +428,20 @@ u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg) DPIO_BYTE); if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { DRM_ERROR("DPIO read wait timed out\n"); - goto out_unlock; + return 0; } - val = I915_READ(DPIO_DATA); -out_unlock: - spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); - return val; + return I915_READ(DPIO_DATA); } static void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, u32 val) { - unsigned long flags; + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - spin_lock_irqsave(&dev_priv->dpio_lock, flags); if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { DRM_ERROR("DPIO idle wait timed out\n"); - goto out_unlock; + return; } I915_WRITE(DPIO_DATA, val); @@ -456,9 +450,6 @@ static void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, DPIO_BYTE); if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) DRM_ERROR("DPIO write wait timed out\n"); - -out_unlock: - spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); } static void vlv_init_dpio(struct drm_device *dev) @@ -1455,13 +1446,12 @@ static void intel_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) static void intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value) { - unsigned long flags; + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - spin_lock_irqsave(&dev_priv->dpio_lock, flags); if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0, 100)) { DRM_ERROR("timeout waiting for SBI to become ready\n"); - goto out_unlock; + return; } I915_WRITE(SBI_ADDR, @@ -1475,24 +1465,19 @@ intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value) if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_BUSY | SBI_RESPONSE_FAIL)) == 0, 100)) { DRM_ERROR("timeout waiting for SBI to complete write transaction\n"); - goto out_unlock; + return; } - -out_unlock: - spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); } static u32 intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg) { - unsigned long flags; - u32 value = 0; + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - spin_lock_irqsave(&dev_priv->dpio_lock, flags); if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0, 100)) { DRM_ERROR("timeout waiting for SBI to become ready\n"); - goto out_unlock; + return 0; } I915_WRITE(SBI_ADDR, @@ -1504,14 +1489,10 @@ intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg) if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_BUSY | SBI_RESPONSE_FAIL)) == 0, 100)) { DRM_ERROR("timeout waiting for SBI to complete read transaction\n"); - goto out_unlock; + return 0; } - value = I915_READ(SBI_DATA); - -out_unlock: - spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); - return value; + return I915_READ(SBI_DATA); } /** @@ -2924,6 +2905,8 @@ static void lpt_program_iclkip(struct drm_crtc *crtc) u32 divsel, phaseinc, auxdiv, phasedir = 0; u32 temp; + mutex_lock(&dev_priv->dpio_lock); + /* It is necessary to ungate the pixclk gate prior to programming * the divisors, and gate it back when it is done. */ @@ -3005,6 +2988,8 @@ static void lpt_program_iclkip(struct drm_crtc *crtc) udelay(24); I915_WRITE(PIXCLK_GATE, PIXCLK_GATE_UNGATE); + + mutex_unlock(&dev_priv->dpio_lock); } /* @@ -4222,6 +4207,8 @@ static void vlv_update_pll(struct drm_crtc *crtc, bool is_sdvo; u32 temp; + mutex_lock(&dev_priv->dpio_lock); + is_sdvo = intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO) || intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI); @@ -4305,6 +4292,8 @@ static void vlv_update_pll(struct drm_crtc *crtc, temp |= (1 << 21); intel_dpio_write(dev_priv, DPIO_DATA_CHANNEL2, temp); } + + mutex_unlock(&dev_priv->dpio_lock); } static void i9xx_update_pll(struct drm_crtc *crtc, -- cgit v1.2.3 From b696519e51910943f084ea0fc8bdceceae8383ef Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Thu, 13 Dec 2012 16:08:59 +0000 Subject: drm/i915: Cleanup SHOTPLUG_CTL status bits definitions Those status bits don't follow the usual pattern: _MASK (those bits are write 1 to clear, useful to select the value we want to read) and the values shifted by the same amount. Cleaned that that up when poking at the register for testing purposes, might as well upstream that cleanup. Reviewed-by: Jani Nikula Signed-off-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index f2a5ea694684..f83480403ccc 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3558,27 +3558,30 @@ #define PORTD_PULSE_DURATION_6ms (2 << 18) #define PORTD_PULSE_DURATION_100ms (3 << 18) #define PORTD_PULSE_DURATION_MASK (3 << 18) -#define PORTD_HOTPLUG_NO_DETECT (0) -#define PORTD_HOTPLUG_SHORT_DETECT (1 << 16) -#define PORTD_HOTPLUG_LONG_DETECT (1 << 17) +#define PORTD_HOTPLUG_STATUS_MASK (0x3 << 16) +#define PORTD_HOTPLUG_NO_DETECT (0 << 16) +#define PORTD_HOTPLUG_SHORT_DETECT (1 << 16) +#define PORTD_HOTPLUG_LONG_DETECT (2 << 16) #define PORTC_HOTPLUG_ENABLE (1 << 12) #define PORTC_PULSE_DURATION_2ms (0) #define PORTC_PULSE_DURATION_4_5ms (1 << 10) #define PORTC_PULSE_DURATION_6ms (2 << 10) #define PORTC_PULSE_DURATION_100ms (3 << 10) #define PORTC_PULSE_DURATION_MASK (3 << 10) -#define PORTC_HOTPLUG_NO_DETECT (0) -#define PORTC_HOTPLUG_SHORT_DETECT (1 << 8) -#define PORTC_HOTPLUG_LONG_DETECT (1 << 9) +#define PORTC_HOTPLUG_STATUS_MASK (0x3 << 8) +#define PORTC_HOTPLUG_NO_DETECT (0 << 8) +#define PORTC_HOTPLUG_SHORT_DETECT (1 << 8) +#define PORTC_HOTPLUG_LONG_DETECT (2 << 8) #define PORTB_HOTPLUG_ENABLE (1 << 4) #define PORTB_PULSE_DURATION_2ms (0) #define PORTB_PULSE_DURATION_4_5ms (1 << 2) #define PORTB_PULSE_DURATION_6ms (2 << 2) #define PORTB_PULSE_DURATION_100ms (3 << 2) #define PORTB_PULSE_DURATION_MASK (3 << 2) -#define PORTB_HOTPLUG_NO_DETECT (0) -#define PORTB_HOTPLUG_SHORT_DETECT (1 << 0) -#define PORTB_HOTPLUG_LONG_DETECT (1 << 1) +#define PORTB_HOTPLUG_STATUS_MASK (0x3 << 0) +#define PORTB_HOTPLUG_NO_DETECT (0 << 0) +#define PORTB_HOTPLUG_SHORT_DETECT (1 << 0) +#define PORTB_HOTPLUG_LONG_DETECT (2 << 0) #define PCH_GPIOA 0xc5010 #define PCH_GPIOB 0xc5014 -- cgit v1.2.3 From b0ea7d37a8f63eeec5ae80b4a6403cfba01da02f Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Thu, 13 Dec 2012 16:09:00 +0000 Subject: drm/i915/hdmi: Read the HPD status before trying to read the EDID If you unplug the hdmi connector slowly enough, the hotplug interrupt fires but then the kernel code tries to read the EDID and succeeds (because the connector is still half connected, the HPD pin is shorter than the others, and DDC works). Since EDID succeeds it thinks the monitor is still connected. To prevent that, read the live HPD status in the hotplug handler before trying to read the EDID. v2: Rename the function to ibx_ (Chris Wilson) Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=55372 Signed-off-by: Damien Lespiau Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 33 +++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 3 +++ drivers/gpu/drm/i915/intel_hdmi.c | 9 +++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2a01b09221fb..6adeabda2587 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -985,6 +985,39 @@ void intel_wait_for_pipe_off(struct drm_device *dev, int pipe) } } +/* + * ibx_digital_port_connected - is the specified port connected? + * @dev_priv: i915 private structure + * @port: the port to test + * + * Returns true if @port is connected, false otherwise. + */ +bool ibx_digital_port_connected(struct drm_i915_private *dev_priv, + struct intel_digital_port *port) +{ + u32 bit; + + /* XXX: IBX has different SDEISR bits */ + if (HAS_PCH_IBX(dev_priv->dev)) + return true; + + switch(port->port) { + case PORT_B: + bit = SDE_PORTB_HOTPLUG_CPT; + break; + case PORT_C: + bit = SDE_PORTC_HOTPLUG_CPT; + break; + case PORT_D: + bit = SDE_PORTD_HOTPLUG_CPT; + break; + default: + return true; + } + + return I915_READ(SDEISR) & bit; +} + static const char *state_string(bool enabled) { return enabled ? "on" : "off"; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 22728f2c1d83..53d4c8fec2a0 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -545,6 +545,9 @@ hdmi_to_dig_port(struct intel_hdmi *intel_hdmi) return container_of(intel_hdmi, struct intel_digital_port, hdmi); } +bool ibx_digital_port_connected(struct drm_i915_private *dev_priv, + struct intel_digital_port *port); + extern void intel_connector_attach_encoder(struct intel_connector *connector, struct intel_encoder *encoder); extern struct drm_encoder *intel_best_encoder(struct drm_connector *connector); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 53df0a88a6a8..9f834d324cfd 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -793,16 +793,21 @@ static bool g4x_hdmi_connected(struct intel_hdmi *intel_hdmi) static enum drm_connector_status intel_hdmi_detect(struct drm_connector *connector, bool force) { + struct drm_device *dev = connector->dev; struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); struct intel_digital_port *intel_dig_port = hdmi_to_dig_port(intel_hdmi); struct intel_encoder *intel_encoder = &intel_dig_port->base; - struct drm_i915_private *dev_priv = connector->dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct edid *edid; enum drm_connector_status status = connector_status_disconnected; - if (IS_G4X(connector->dev) && !g4x_hdmi_connected(intel_hdmi)) + + if (IS_G4X(dev) && !g4x_hdmi_connected(intel_hdmi)) return status; + else if (HAS_PCH_SPLIT(dev) && + !ibx_digital_port_connected(dev_priv, intel_dig_port)) + return status; intel_hdmi->has_hdmi_sink = false; intel_hdmi->has_audio = false; -- cgit v1.2.3 From 1b4696394aeb8a550f8537e92ae6cc65f444dca0 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Thu, 13 Dec 2012 16:09:01 +0000 Subject: drm/i915/dp: Read the HPD status before trying to read the DPCD Just like: Author: Damien Lespiau Date: Wed Dec 12 19:37:22 2012 +0000 drm/i915/hdmi: Read the HPD status before trying to read the EDID But this time for DiplayPort. v2: Adapt to the ibx_ name change and don't add commit hash (Chris Wilson, Jani Nikula) Reviewed-by: Jani Nikula Signed-off-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index b2130bc6c297..19a0d8984079 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2248,6 +2248,8 @@ static enum drm_connector_status ironlake_dp_detect(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); enum drm_connector_status status; /* Can't disconnect eDP, but you can close the lid... */ @@ -2258,6 +2260,9 @@ ironlake_dp_detect(struct intel_dp *intel_dp) return status; } + if (!ibx_digital_port_connected(dev_priv, intel_dig_port)) + return connector_status_disconnected; + return intel_dp_detect_dpcd(intel_dp); } -- cgit v1.2.3 From 577c7a505b5601a9a441039dd37543775f9af8f5 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Thu, 13 Dec 2012 16:09:02 +0000 Subject: drm/i915/dp: Log the DPCD only if we have successfully retrieved one Moving the DPCD just after a successful read will allow to: - log all DPCD reads (eDP ones, changes signalled by HPD IRQ) - don't log it if we haven't been able to read it v2: Be sure to log the DPCD when a downstream port does not have HPD support and the branch device asserts HPD (Jani Nikula) Signed-off-by: Damien Lespiau Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 19a0d8984079..5ed8fb3435ee 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2084,10 +2084,16 @@ intel_dp_link_down(struct intel_dp *intel_dp) static bool intel_dp_get_dpcd(struct intel_dp *intel_dp) { + char dpcd_hex_dump[sizeof(intel_dp->dpcd) * 3]; + if (intel_dp_aux_native_read_retry(intel_dp, 0x000, intel_dp->dpcd, sizeof(intel_dp->dpcd)) == 0) return false; /* aux transfer failed */ + hex_dump_to_buffer(intel_dp->dpcd, sizeof(intel_dp->dpcd), + 32, 1, dpcd_hex_dump, sizeof(dpcd_hex_dump), false); + DRM_DEBUG_KMS("DPCD: %s\n", dpcd_hex_dump); + if (intel_dp->dpcd[DP_DPCD_REV] == 0) return false; /* DPCD not present */ @@ -2353,7 +2359,6 @@ intel_dp_detect(struct drm_connector *connector, bool force) struct drm_device *dev = connector->dev; enum drm_connector_status status; struct edid *edid = NULL; - char dpcd_hex_dump[sizeof(intel_dp->dpcd) * 3]; intel_dp->has_audio = false; @@ -2362,10 +2367,6 @@ intel_dp_detect(struct drm_connector *connector, bool force) else status = g4x_dp_detect(intel_dp); - hex_dump_to_buffer(intel_dp->dpcd, sizeof(intel_dp->dpcd), - 32, 1, dpcd_hex_dump, sizeof(dpcd_hex_dump), false); - DRM_DEBUG_KMS("DPCD: %s\n", dpcd_hex_dump); - if (status != connector_status_connected) return status; -- cgit v1.2.3 From c36346e302b3a36bfb10b58167b69ab7ac95f10a Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Thu, 13 Dec 2012 16:09:03 +0000 Subject: drm/i915: Implement ibx_digital_port_connected() for IBX CPT+ PCHs have different bit definition to read the HPD live status. I don't have an ILK with digital ports handy, which is why this patch is separate from the CPT+ implementation. If the docs don't lie, it should all be fine though. Signed-off-by: Damien Lespiau Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 44 +++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6adeabda2587..7f1b07c9061d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -997,22 +997,34 @@ bool ibx_digital_port_connected(struct drm_i915_private *dev_priv, { u32 bit; - /* XXX: IBX has different SDEISR bits */ - if (HAS_PCH_IBX(dev_priv->dev)) - return true; - - switch(port->port) { - case PORT_B: - bit = SDE_PORTB_HOTPLUG_CPT; - break; - case PORT_C: - bit = SDE_PORTC_HOTPLUG_CPT; - break; - case PORT_D: - bit = SDE_PORTD_HOTPLUG_CPT; - break; - default: - return true; + if (HAS_PCH_IBX(dev_priv->dev)) { + switch(port->port) { + case PORT_B: + bit = SDE_PORTB_HOTPLUG; + break; + case PORT_C: + bit = SDE_PORTC_HOTPLUG; + break; + case PORT_D: + bit = SDE_PORTD_HOTPLUG; + break; + default: + return true; + } + } else { + switch(port->port) { + case PORT_B: + bit = SDE_PORTB_HOTPLUG_CPT; + break; + case PORT_C: + bit = SDE_PORTC_HOTPLUG_CPT; + break; + case PORT_D: + bit = SDE_PORTD_HOTPLUG_CPT; + break; + default: + return true; + } } return I915_READ(SDEISR) & bit; -- cgit v1.2.3 From 3f8c65d60467c6ccf4d690f5266567d6c423ae7d Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Thu, 13 Dec 2012 16:09:04 +0000 Subject: drm/i915: Remove stale comment about intel_dp_detect() The function doesn't use any of the registers mentioned, nor does it return true or false. Hard to do worse. Remove it, the function is absolutely descriptive enough to not need any comment. Signed-off-by: Damien Lespiau Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 5ed8fb3435ee..185bf4e7614e 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2343,13 +2343,6 @@ intel_dp_get_edid_modes(struct drm_connector *connector, struct i2c_adapter *ada return intel_ddc_get_modes(connector, adapter); } - -/** - * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect DP connection. - * - * \return true if DP port is connected. - * \return false if DP port is disconnected. - */ static enum drm_connector_status intel_dp_detect(struct drm_connector *connector, bool force) { -- cgit v1.2.3 From 3993de6183885a099163b9562a2ea9c07b994a0e Mon Sep 17 00:00:00 2001 From: Robert Love Date: Tue, 27 Nov 2012 06:53:24 +0000 Subject: libfcoe: Add fcoe_sysfs debug logging level Add a macro to print fcoe_sysfs debug statements. Signed-off-by: Robert Love Acked-by: Neil Horman --- drivers/scsi/fcoe/fcoe_sysfs.c | 7 +++++++ drivers/scsi/fcoe/libfcoe.h | 11 ++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/fcoe/fcoe_sysfs.c b/drivers/scsi/fcoe/fcoe_sysfs.c index e6fce2862a33..9c74374b53cd 100644 --- a/drivers/scsi/fcoe/fcoe_sysfs.c +++ b/drivers/scsi/fcoe/fcoe_sysfs.c @@ -24,6 +24,13 @@ #include +/* + * OK to include local libfcoe.h for debug_logging, but cannot include + * otherwise non-netdev based fcoe solutions would have + * have to include more than fcoe_sysfs.h. + */ +#include "libfcoe.h" + static atomic_t ctlr_num; static atomic_t fcf_num; diff --git a/drivers/scsi/fcoe/libfcoe.h b/drivers/scsi/fcoe/libfcoe.h index 6af5fc3a17d8..63d8faedca9f 100644 --- a/drivers/scsi/fcoe/libfcoe.h +++ b/drivers/scsi/fcoe/libfcoe.h @@ -2,9 +2,10 @@ #define _FCOE_LIBFCOE_H_ extern unsigned int libfcoe_debug_logging; -#define LIBFCOE_LOGGING 0x01 /* General logging, not categorized */ -#define LIBFCOE_FIP_LOGGING 0x02 /* FIP logging */ -#define LIBFCOE_TRANSPORT_LOGGING 0x04 /* FCoE transport logging */ +#define LIBFCOE_LOGGING 0x01 /* General logging, not categorized */ +#define LIBFCOE_FIP_LOGGING 0x02 /* FIP logging */ +#define LIBFCOE_TRANSPORT_LOGGING 0x04 /* FCoE transport logging */ +#define LIBFCOE_SYSFS_LOGGING 0x08 /* fcoe_sysfs logging */ #define LIBFCOE_CHECK_LOGGING(LEVEL, CMD) \ do { \ @@ -28,4 +29,8 @@ do { \ printk(KERN_INFO "%s: " fmt, \ __func__, ##args);) +#define LIBFCOE_SYSFS_DBG(cdev, fmt, args...) \ + LIBFCOE_CHECK_LOGGING(LIBFCOE_SYSFS_LOGGING, \ + pr_info("ctlr_%d: " fmt, cdev->id, ##args);) + #endif /* _FCOE_LIBFCOE_H_ */ -- cgit v1.2.3 From 6a891b071b640e1de44c4a5117fa2c974dcfa84a Mon Sep 17 00:00:00 2001 From: Robert Love Date: Tue, 27 Nov 2012 06:53:30 +0000 Subject: libfcoe, fcoe, bnx2fc: Add new fcoe control interface This patch does a few things. 1) Makes /sys/bus/fcoe/ctlr_{create,destroy} interfaces. These interfaces take an and will either create an FCoE Controller or destroy an FCoE Controller depending on which file is written to. The new FCoE Controller will start in a DISABLED state and will not do discovery or login until it is ENABLED. This pause will allow us to configure the FCoE Controller before enabling it. 2) Makes the 'mode' attribute of a fcoe_ctlr_device writale. This allows the user to configure the mode in which the FCoE Controller will start in when it is ENABLED. Possible modes are 'Fabric', or 'VN2VN'. The default mode for a fcoe_ctlr{,_device} is 'Fabric'. Drivers must implement the set_fcoe_ctlr_mode routine to support this feature. libfcoe offers an exported routine to set a FCoE Controller's mode. The mode can only be changed when the FCoE Controller is DISABLED. This patch also removes the get_fcoe_ctlr_mode pointer in the fcoe_sysfs function template, the code in fcoe_ctlr.c to get the mode and the assignment of the fcoe_sysfs function pointer to the fcoe_ctlr.c implementation (in fcoe and bnx2fc). fcoe_sysfs can return that value for the mode without consulting the LLD. 3) Make a 'enabled' attribute of a fcoe_ctlr_device. On a read, fcoe_sysfs will return the attribute's value. On a write, fcoe_sysfs will call the LLD (if there is a callback) to notifiy that the enalbed state has changed. This patch maintains the old FCoE control interfaces as module parameters, but it adds comments pointing out that the old interfaces are deprecated. Signed-off-by: Robert Love Acked-by: Neil Horman --- Documentation/ABI/testing/sysfs-bus-fcoe | 41 ++++++++- drivers/scsi/bnx2fc/bnx2fc_fcoe.c | 1 - drivers/scsi/fcoe/fcoe.c | 1 - drivers/scsi/fcoe/fcoe_sysfs.c | 137 +++++++++++++++++++++++++++++-- drivers/scsi/fcoe/fcoe_transport.c | 104 +++++++++++++++++++++++ include/scsi/fcoe_sysfs.h | 11 ++- include/scsi/libfcoe.h | 16 +++- 7 files changed, 298 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-bus-fcoe b/Documentation/ABI/testing/sysfs-bus-fcoe index 971dc22e6c2a..21640eaad371 100644 --- a/Documentation/ABI/testing/sysfs-bus-fcoe +++ b/Documentation/ABI/testing/sysfs-bus-fcoe @@ -1,14 +1,53 @@ +What: /sys/bus/fcoe/ +Date: August 2012 +KernelVersion: TBD +Contact: Robert Love , devel@open-fcoe.org +Description: The FCoE bus. Attributes in this directory are control interfaces. +Attributes: + + ctlr_create: 'FCoE Controller' instance creation interface. Writing an + to this file will allocate and populate sysfs with a + fcoe_ctlr_device (ctlr_X). The user can then configure any + per-port settings and finally write to the fcoe_ctlr_device's + 'start' attribute to begin the kernel's discovery and login + process. + + ctlr_destroy: 'FCoE Controller' instance removal interface. Writing a + fcoe_ctlr_device's sysfs name to this file will log the + fcoe_ctlr_device out of the fabric or otherwise connected + FCoE devices. It will also free all kernel memory allocated + for this fcoe_ctlr_device and any structures associated + with it, this includes the scsi_host. + What: /sys/bus/fcoe/devices/ctlr_X Date: March 2012 KernelVersion: TBD Contact: Robert Love , devel@open-fcoe.org -Description: 'FCoE Controller' instances on the fcoe bus +Description: 'FCoE Controller' instances on the fcoe bus. + The FCoE Controller now has a three stage creation process. + 1) Write interface name to ctlr_create 2) Configure the FCoE + Controller (ctlr_X) 3) Enable the FCoE Controller to begin + discovery and login. The FCoE Controller is destroyed by + writing it's name, i.e. ctlr_X to the ctlr_delete file. + Attributes: fcf_dev_loss_tmo: Device loss timeout peroid (see below). Changing this value will change the dev_loss_tmo for all FCFs discovered by this controller. + mode: Display or change the FCoE Controller's mode. Possible + modes are 'Fabric' and 'VN2VN'. If a FCoE Controller + is started in 'Fabric' mode then FIP FCF discovery is + initiated and ultimately a fabric login is attempted. + If a FCoE Controller is started in 'VN2VN' mode then + FIP VN2VN discovery and login is performed. A FCoE + Controller only supports one mode at a time. + + enabled: Whether an FCoE controller is enabled or disabled. + 0 if disabled, 1 if enabled. Writing either 0 or 1 + to this file will enable or disable the FCoE controller. + lesb/link_fail: Link Error Status Block (LESB) link failure count. lesb/vlink_fail: Link Error Status Block (LESB) virtual link diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index e0558656c646..9b38794d5665 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -2555,7 +2555,6 @@ module_init(bnx2fc_mod_init); module_exit(bnx2fc_mod_exit); static struct fcoe_sysfs_function_template bnx2fc_fcoe_sysfs_templ = { - .get_fcoe_ctlr_mode = fcoe_ctlr_get_fip_mode, .get_fcoe_ctlr_link_fail = bnx2fc_ctlr_get_lesb, .get_fcoe_ctlr_vlink_fail = bnx2fc_ctlr_get_lesb, .get_fcoe_ctlr_miss_fka = bnx2fc_ctlr_get_lesb, diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 666b7ac4475f..9bd982d2c07f 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -155,7 +155,6 @@ static void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *); static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *); static struct fcoe_sysfs_function_template fcoe_sysfs_templ = { - .get_fcoe_ctlr_mode = fcoe_ctlr_get_fip_mode, .get_fcoe_ctlr_link_fail = fcoe_ctlr_get_lesb, .get_fcoe_ctlr_vlink_fail = fcoe_ctlr_get_lesb, .get_fcoe_ctlr_miss_fka = fcoe_ctlr_get_lesb, diff --git a/drivers/scsi/fcoe/fcoe_sysfs.c b/drivers/scsi/fcoe/fcoe_sysfs.c index 9c74374b53cd..8c05ae017f5b 100644 --- a/drivers/scsi/fcoe/fcoe_sysfs.c +++ b/drivers/scsi/fcoe/fcoe_sysfs.c @@ -21,8 +21,10 @@ #include #include #include +#include #include +#include /* * OK to include local libfcoe.h for debug_logging, but cannot include @@ -78,6 +80,8 @@ MODULE_PARM_DESC(fcf_dev_loss_tmo, ((x)->lesb.lesb_err_block) #define fcoe_ctlr_fcs_error(x) \ ((x)->lesb.lesb_fcs_error) +#define fcoe_ctlr_enabled(x) \ + ((x)->enabled) #define fcoe_fcf_state(x) \ ((x)->state) #define fcoe_fcf_fabric_name(x) \ @@ -228,7 +232,18 @@ static char *fip_conn_type_names[] = { [ FIP_CONN_TYPE_VN2VN ] = "VN2VN", }; fcoe_enum_name_search(ctlr_mode, fip_conn_type, fip_conn_type_names) -#define FCOE_CTLR_MODE_MAX_NAMELEN 50 + +static enum fip_conn_type fcoe_parse_mode(const char *buf) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fip_conn_type_names); i++) { + if (strcasecmp(buf, fip_conn_type_names[i]) == 0) + return i; + } + + return FIP_CONN_TYPE_UNKNOWN; +} static char *fcf_state_names[] = { [ FCOE_FCF_STATE_UNKNOWN ] = "Unknown", @@ -259,17 +274,116 @@ static ssize_t show_ctlr_mode(struct device *dev, struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); const char *name; - if (ctlr->f->get_fcoe_ctlr_mode) - ctlr->f->get_fcoe_ctlr_mode(ctlr); - name = get_fcoe_ctlr_mode_name(ctlr->mode); if (!name) return -EINVAL; - return snprintf(buf, FCOE_CTLR_MODE_MAX_NAMELEN, + return snprintf(buf, FCOE_MAX_MODENAME_LEN, "%s\n", name); } -static FCOE_DEVICE_ATTR(ctlr, mode, S_IRUGO, - show_ctlr_mode, NULL); + +static ssize_t store_ctlr_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); + char mode[FCOE_MAX_MODENAME_LEN + 1]; + + if (count > FCOE_MAX_MODENAME_LEN) + return -EINVAL; + + strncpy(mode, buf, count); + + if (mode[count - 1] == '\n') + mode[count - 1] = '\0'; + else + mode[count] = '\0'; + + switch (ctlr->enabled) { + case FCOE_CTLR_ENABLED: + LIBFCOE_SYSFS_DBG(ctlr, "Cannot change mode when enabled."); + return -EBUSY; + case FCOE_CTLR_DISABLED: + if (!ctlr->f->set_fcoe_ctlr_mode) { + LIBFCOE_SYSFS_DBG(ctlr, + "Mode change not supported by LLD."); + return -ENOTSUPP; + } + + ctlr->mode = fcoe_parse_mode(mode); + if (ctlr->mode == FIP_CONN_TYPE_UNKNOWN) { + LIBFCOE_SYSFS_DBG(ctlr, + "Unknown mode %s provided.", buf); + return -EINVAL; + } + + ctlr->f->set_fcoe_ctlr_mode(ctlr); + LIBFCOE_SYSFS_DBG(ctlr, "Mode changed to %s.", buf); + + return count; + case FCOE_CTLR_UNUSED: + default: + LIBFCOE_SYSFS_DBG(ctlr, "Mode change not supported."); + return -ENOTSUPP; + }; +} + +static FCOE_DEVICE_ATTR(ctlr, mode, S_IRUGO | S_IWUSR, + show_ctlr_mode, store_ctlr_mode); + +static ssize_t store_ctlr_enabled(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); + int rc; + + switch (ctlr->enabled) { + case FCOE_CTLR_ENABLED: + if (*buf == '1') + return count; + ctlr->enabled = FCOE_CTLR_DISABLED; + break; + case FCOE_CTLR_DISABLED: + if (*buf == '0') + return count; + ctlr->enabled = FCOE_CTLR_ENABLED; + break; + case FCOE_CTLR_UNUSED: + return -ENOTSUPP; + }; + + rc = ctlr->f->set_fcoe_ctlr_enabled(ctlr); + if (rc) + return rc; + + return count; +} + +static char *ctlr_enabled_state_names[] = { + [ FCOE_CTLR_ENABLED ] = "1", + [ FCOE_CTLR_DISABLED ] = "0", +}; +fcoe_enum_name_search(ctlr_enabled_state, ctlr_enabled_state, + ctlr_enabled_state_names) +#define FCOE_CTLR_ENABLED_MAX_NAMELEN 50 + +static ssize_t show_ctlr_enabled_state(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); + const char *name; + + name = get_fcoe_ctlr_enabled_state_name(ctlr->enabled); + if (!name) + return -EINVAL; + return snprintf(buf, FCOE_CTLR_ENABLED_MAX_NAMELEN, + "%s\n", name); +} + +static FCOE_DEVICE_ATTR(ctlr, enabled, S_IRUGO | S_IWUSR, + show_ctlr_enabled_state, + store_ctlr_enabled); static ssize_t store_private_fcoe_ctlr_fcf_dev_loss_tmo(struct device *dev, @@ -354,6 +468,7 @@ static struct attribute_group fcoe_ctlr_lesb_attr_group = { static struct attribute *fcoe_ctlr_attrs[] = { &device_attr_fcoe_ctlr_fcf_dev_loss_tmo.attr, + &device_attr_fcoe_ctlr_enabled.attr, &device_attr_fcoe_ctlr_mode.attr, NULL, }; @@ -438,9 +553,16 @@ struct device_type fcoe_fcf_device_type = { .release = fcoe_fcf_device_release, }; +struct bus_attribute fcoe_bus_attr_group[] = { + __ATTR(ctlr_create, S_IWUSR, NULL, fcoe_ctlr_create_store), + __ATTR(ctlr_destroy, S_IWUSR, NULL, fcoe_ctlr_destroy_store), + __ATTR_NULL +}; + struct bus_type fcoe_bus_type = { .name = "fcoe", .match = &fcoe_bus_match, + .bus_attrs = fcoe_bus_attr_group, }; /** @@ -561,6 +683,7 @@ struct fcoe_ctlr_device *fcoe_ctlr_device_add(struct device *parent, ctlr->id = atomic_inc_return(&ctlr_num) - 1; ctlr->f = f; + ctlr->mode = FIP_CONN_TYPE_FABRIC; INIT_LIST_HEAD(&ctlr->fcfs); mutex_init(&ctlr->lock); ctlr->dev.parent = parent; diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c index ac76d8a042d7..83e78f922054 100644 --- a/drivers/scsi/fcoe/fcoe_transport.c +++ b/drivers/scsi/fcoe/fcoe_transport.c @@ -627,6 +627,110 @@ static int libfcoe_device_notification(struct notifier_block *notifier, return NOTIFY_OK; } +ssize_t fcoe_ctlr_create_store(struct bus_type *bus, + const char *buf, size_t count) +{ + struct net_device *netdev = NULL; + struct fcoe_transport *ft = NULL; + struct fcoe_ctlr_device *ctlr_dev = NULL; + int rc = 0; + int err; + + mutex_lock(&ft_mutex); + + netdev = fcoe_if_to_netdev(buf); + if (!netdev) { + LIBFCOE_TRANSPORT_DBG("Invalid device %s.\n", buf); + rc = -ENODEV; + goto out_nodev; + } + + ft = fcoe_netdev_map_lookup(netdev); + if (ft) { + LIBFCOE_TRANSPORT_DBG("transport %s already has existing " + "FCoE instance on %s.\n", + ft->name, netdev->name); + rc = -EEXIST; + goto out_putdev; + } + + ft = fcoe_transport_lookup(netdev); + if (!ft) { + LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n", + netdev->name); + rc = -ENODEV; + goto out_putdev; + } + + /* pass to transport create */ + err = ft->alloc ? ft->alloc(netdev) : -ENODEV; + if (err) { + fcoe_del_netdev_mapping(netdev); + rc = -ENOMEM; + goto out_putdev; + } + + err = fcoe_add_netdev_mapping(netdev, ft); + if (err) { + LIBFCOE_TRANSPORT_DBG("failed to add new netdev mapping " + "for FCoE transport %s for %s.\n", + ft->name, netdev->name); + rc = -ENODEV; + goto out_putdev; + } + + LIBFCOE_TRANSPORT_DBG("transport %s %s to create fcoe on %s.\n", + ft->name, (ctlr_dev) ? "succeeded" : "failed", + netdev->name); + +out_putdev: + dev_put(netdev); +out_nodev: + mutex_unlock(&ft_mutex); + if (rc) + return rc; + return count; +} + +ssize_t fcoe_ctlr_destroy_store(struct bus_type *bus, + const char *buf, size_t count) +{ + int rc = -ENODEV; + struct net_device *netdev = NULL; + struct fcoe_transport *ft = NULL; + + mutex_lock(&ft_mutex); + + netdev = fcoe_if_to_netdev(buf); + if (!netdev) { + LIBFCOE_TRANSPORT_DBG("invalid device %s.\n", buf); + goto out_nodev; + } + + ft = fcoe_netdev_map_lookup(netdev); + if (!ft) { + LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n", + netdev->name); + goto out_putdev; + } + + /* pass to transport destroy */ + rc = ft->destroy(netdev); + if (rc) + goto out_putdev; + + fcoe_del_netdev_mapping(netdev); + LIBFCOE_TRANSPORT_DBG("transport %s %s to destroy fcoe on %s.\n", + ft->name, (rc) ? "failed" : "succeeded", + netdev->name); + rc = count; /* required for successful return */ +out_putdev: + dev_put(netdev); +out_nodev: + mutex_unlock(&ft_mutex); + return rc; +} +EXPORT_SYMBOL(fcoe_ctlr_destroy_store); /** * fcoe_transport_create() - Create a fcoe interface diff --git a/include/scsi/fcoe_sysfs.h b/include/scsi/fcoe_sysfs.h index 604cb9bb3e76..7e2314870341 100644 --- a/include/scsi/fcoe_sysfs.h +++ b/include/scsi/fcoe_sysfs.h @@ -34,7 +34,8 @@ struct fcoe_sysfs_function_template { void (*get_fcoe_ctlr_symb_err)(struct fcoe_ctlr_device *); void (*get_fcoe_ctlr_err_block)(struct fcoe_ctlr_device *); void (*get_fcoe_ctlr_fcs_error)(struct fcoe_ctlr_device *); - void (*get_fcoe_ctlr_mode)(struct fcoe_ctlr_device *); + void (*set_fcoe_ctlr_mode)(struct fcoe_ctlr_device *); + int (*set_fcoe_ctlr_enabled)(struct fcoe_ctlr_device *); void (*get_fcoe_fcf_selected)(struct fcoe_fcf_device *); void (*get_fcoe_fcf_vlan_id)(struct fcoe_fcf_device *); }; @@ -48,6 +49,12 @@ enum fip_conn_type { FIP_CONN_TYPE_VN2VN, }; +enum ctlr_enabled_state { + FCOE_CTLR_ENABLED, + FCOE_CTLR_DISABLED, + FCOE_CTLR_UNUSED, +}; + struct fcoe_ctlr_device { u32 id; @@ -64,6 +71,8 @@ struct fcoe_ctlr_device { int fcf_dev_loss_tmo; enum fip_conn_type mode; + enum ctlr_enabled_state enabled; + /* expected in host order for displaying */ struct fcoe_fc_els_lesb lesb; }; diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h index 8742d853a3b8..6add37ac8609 100644 --- a/include/scsi/libfcoe.h +++ b/include/scsi/libfcoe.h @@ -289,8 +289,11 @@ static inline bool is_fip_mode(struct fcoe_ctlr *fip) * @attached: whether this transport is already attached * @list: list linkage to all attached transports * @match: handler to allow the transport driver to match up a given netdev + * @alloc: handler to allocate per-instance FCoE structures + * (no discovery or login) * @create: handler to sysfs entry of create for FCoE instances - * @destroy: handler to sysfs entry of destroy for FCoE instances + * @destroy: handler to delete per-instance FCoE structures + * (frees all memory) * @enable: handler to sysfs entry of enable for FCoE instances * @disable: handler to sysfs entry of disable for FCoE instances */ @@ -299,6 +302,7 @@ struct fcoe_transport { bool attached; struct list_head list; bool (*match) (struct net_device *device); + int (*alloc) (struct net_device *device); int (*create) (struct net_device *device, enum fip_state fip_mode); int (*destroy) (struct net_device *device); int (*enable) (struct net_device *device); @@ -356,7 +360,7 @@ int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen, /* FCoE Sysfs helpers */ void fcoe_fcf_get_selected(struct fcoe_fcf_device *); -void fcoe_ctlr_get_fip_mode(struct fcoe_ctlr_device *); +void fcoe_ctlr_set_fip_mode(struct fcoe_ctlr_device *); /** * struct netdev_list @@ -372,4 +376,12 @@ struct fcoe_netdev_mapping { int fcoe_transport_attach(struct fcoe_transport *ft); int fcoe_transport_detach(struct fcoe_transport *ft); +/* sysfs store handler for ctrl_control interface */ +ssize_t fcoe_ctlr_create_store(struct bus_type *bus, + const char *buf, size_t count); +ssize_t fcoe_ctlr_destroy_store(struct bus_type *bus, + const char *buf, size_t count); + #endif /* _LIBFCOE_H */ + + -- cgit v1.2.3 From 435c86679a24ead623c8a47ca31038e250a75e05 Mon Sep 17 00:00:00 2001 From: Robert Love Date: Tue, 27 Nov 2012 06:53:35 +0000 Subject: fcoe: Use the fcoe_sysfs control interface This patch adds support for the new fcoe_sysfs control interface to fcoe.ko. It keeps the deprecated interface in tact and therefore either the legacy or the new control interfaces can be used. A mixed mode is not supported. A user must either use the new interfaces or the old ones, but not both. The fcoe_ctlr's link state is now driven by both the netdev link state as well as the fcoe_ctlr_device's enabled attribute. The link must be up and the fcoe_ctlr_device must be enabled before the FCoE Controller starts discovery or login. Signed-off-by: Robert Love Acked-by: Neil Horman --- drivers/scsi/fcoe/fcoe.c | 147 +++++++++++++++++++++++++++++++++++++----- drivers/scsi/fcoe/fcoe_ctlr.c | 17 +++-- 2 files changed, 140 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 9bd982d2c07f..21927f7952d8 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -117,6 +117,11 @@ static int fcoe_destroy(struct net_device *netdev); static int fcoe_enable(struct net_device *netdev); static int fcoe_disable(struct net_device *netdev); +/* fcoe_syfs control interface handlers */ +static int fcoe_ctlr_alloc(struct net_device *netdev); +static int fcoe_ctlr_enabled(struct fcoe_ctlr_device *cdev); + + static struct fc_seq *fcoe_elsct_send(struct fc_lport *, u32 did, struct fc_frame *, unsigned int op, @@ -155,6 +160,8 @@ static void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *); static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *); static struct fcoe_sysfs_function_template fcoe_sysfs_templ = { + .set_fcoe_ctlr_mode = fcoe_ctlr_set_fip_mode, + .set_fcoe_ctlr_enabled = fcoe_ctlr_enabled, .get_fcoe_ctlr_link_fail = fcoe_ctlr_get_lesb, .get_fcoe_ctlr_vlink_fail = fcoe_ctlr_get_lesb, .get_fcoe_ctlr_miss_fka = fcoe_ctlr_get_lesb, @@ -1963,6 +1970,7 @@ static int fcoe_dcb_app_notification(struct notifier_block *notifier, static int fcoe_device_notification(struct notifier_block *notifier, ulong event, void *ptr) { + struct fcoe_ctlr_device *cdev; struct fc_lport *lport = NULL; struct net_device *netdev = ptr; struct fcoe_ctlr *ctlr; @@ -2019,13 +2027,29 @@ static int fcoe_device_notification(struct notifier_block *notifier, fcoe_link_speed_update(lport); - if (link_possible && !fcoe_link_ok(lport)) - fcoe_ctlr_link_up(ctlr); - else if (fcoe_ctlr_link_down(ctlr)) { - stats = per_cpu_ptr(lport->stats, get_cpu()); - stats->LinkFailureCount++; - put_cpu(); - fcoe_clean_pending_queue(lport); + cdev = fcoe_ctlr_to_ctlr_dev(ctlr); + + if (link_possible && !fcoe_link_ok(lport)) { + switch (cdev->enabled) { + case FCOE_CTLR_DISABLED: + pr_info("Link up while interface is disabled.\n"); + break; + case FCOE_CTLR_ENABLED: + case FCOE_CTLR_UNUSED: + fcoe_ctlr_link_up(ctlr); + }; + } else if (fcoe_ctlr_link_down(ctlr)) { + switch (cdev->enabled) { + case FCOE_CTLR_DISABLED: + pr_info("Link down while interface is disabled.\n"); + break; + case FCOE_CTLR_ENABLED: + case FCOE_CTLR_UNUSED: + stats = per_cpu_ptr(lport->stats, get_cpu()); + stats->LinkFailureCount++; + put_cpu(); + fcoe_clean_pending_queue(lport); + }; } out: return rc; @@ -2038,6 +2062,8 @@ out: * Called from fcoe transport. * * Returns: 0 for success + * + * Deprecated: use fcoe_ctlr_enabled() */ static int fcoe_disable(struct net_device *netdev) { @@ -2096,6 +2122,33 @@ out: return rc; } +/** + * fcoe_ctlr_enabled() - Enable or disable an FCoE Controller + * @cdev: The FCoE Controller that is being enabled or disabled + * + * fcoe_sysfs will ensure that the state of 'enabled' has + * changed, so no checking is necessary here. This routine simply + * calls fcoe_enable or fcoe_disable, both of which are deprecated. + * When those routines are removed the functionality can be merged + * here. + */ +static int fcoe_ctlr_enabled(struct fcoe_ctlr_device *cdev) +{ + struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(cdev); + struct fc_lport *lport = ctlr->lp; + struct net_device *netdev = fcoe_netdev(lport); + + switch (cdev->enabled) { + case FCOE_CTLR_ENABLED: + return fcoe_enable(netdev); + case FCOE_CTLR_DISABLED: + return fcoe_disable(netdev); + case FCOE_CTLR_UNUSED: + default: + return -ENOTSUPP; + }; +} + /** * fcoe_destroy() - Destroy a FCoE interface * @netdev : The net_device object the Ethernet interface to create on @@ -2203,16 +2256,26 @@ static void fcoe_dcb_create(struct fcoe_interface *fcoe) #endif } +enum fcoe_create_link_state { + FCOE_CREATE_LINK_DOWN, + FCOE_CREATE_LINK_UP, +}; + /** - * fcoe_create() - Create a fcoe interface - * @netdev : The net_device object the Ethernet interface to create on - * @fip_mode: The FIP mode for this creation + * _fcoe_create() - (internal) Create a fcoe interface + * @netdev : The net_device object the Ethernet interface to create on + * @fip_mode: The FIP mode for this creation + * @link_state: The ctlr link state on creation * - * Called from fcoe transport + * Called from either the libfcoe 'create' module parameter + * via fcoe_create or from fcoe_syfs's ctlr_create file. * - * Returns: 0 for success + * libfcoe's 'create' module parameter is deprecated so some + * consolidation of code can be done when that interface is + * removed. */ -static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode) +static int _fcoe_create(struct net_device *netdev, enum fip_state fip_mode, + enum fcoe_create_link_state link_state) { int rc = 0; struct fcoe_ctlr_device *ctlr_dev; @@ -2259,7 +2322,26 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode) /* start FIP Discovery and FLOGI */ lport->boot_time = jiffies; fc_fabric_login(lport); - if (!fcoe_link_ok(lport)) { + + /* + * If the fcoe_ctlr_device is to be set to DISABLED + * it must be done after the lport is added to the + * hostlist, but before the rtnl_lock is released. + * This is because the rtnl_lock protects the + * hostlist that fcoe_device_notification uses. If + * the FCoE Controller is intended to be created + * DISABLED then 'enabled' needs to be considered + * handling link events. 'enabled' must be set + * before the lport can be found in the hostlist + * when a link up event is received. + */ + if (link_state == FCOE_CREATE_LINK_UP) + ctlr_dev->enabled = FCOE_CTLR_ENABLED; + else + ctlr_dev->enabled = FCOE_CTLR_DISABLED; + + if (link_state == FCOE_CREATE_LINK_UP && + !fcoe_link_ok(lport)) { rtnl_unlock(); fcoe_ctlr_link_up(ctlr); mutex_unlock(&fcoe_config_mutex); @@ -2273,6 +2355,37 @@ out_nortnl: return rc; } +/** + * fcoe_create() - Create a fcoe interface + * @netdev : The net_device object the Ethernet interface to create on + * @fip_mode: The FIP mode for this creation + * + * Called from fcoe transport + * + * Returns: 0 for success + */ +static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode) +{ + return _fcoe_create(netdev, fip_mode, FCOE_CREATE_LINK_UP); +} + +/** + * fcoe_ctlr_alloc() - Allocate a fcoe interface from fcoe_sysfs + * @netdev: The net_device to be used by the allocated FCoE Controller + * + * This routine is called from fcoe_sysfs. It will start the fcoe_ctlr + * in a link_down state. The allows the user an opportunity to configure + * the FCoE Controller from sysfs before enabling the FCoE Controller. + * + * Creating in with this routine starts the FCoE Controller in Fabric + * mode. The user can change to VN2VN or another mode before enabling. + */ +static int fcoe_ctlr_alloc(struct net_device *netdev) +{ + return _fcoe_create(netdev, FIP_MODE_FABRIC, + FCOE_CREATE_LINK_DOWN); +} + /** * fcoe_link_speed_update() - Update the supported and actual link speeds * @lport: The local port to update speeds for @@ -2374,10 +2487,13 @@ static int fcoe_reset(struct Scsi_Host *shost) struct fcoe_port *port = lport_priv(lport); struct fcoe_interface *fcoe = port->priv; struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe); + struct fcoe_ctlr_device *cdev = fcoe_ctlr_to_ctlr_dev(ctlr); fcoe_ctlr_link_down(ctlr); fcoe_clean_pending_queue(ctlr->lp); - if (!fcoe_link_ok(ctlr->lp)) + + if (cdev->enabled != FCOE_CTLR_DISABLED && + !fcoe_link_ok(ctlr->lp)) fcoe_ctlr_link_up(ctlr); return 0; } @@ -2450,6 +2566,7 @@ static struct fcoe_transport fcoe_sw_transport = { .attached = false, .list = LIST_HEAD_INIT(fcoe_sw_transport.list), .match = fcoe_match, + .alloc = fcoe_ctlr_alloc, .create = fcoe_create, .destroy = fcoe_destroy, .enable = fcoe_enable, diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c index 2ebe03a4b51d..75834255bf07 100644 --- a/drivers/scsi/fcoe/fcoe_ctlr.c +++ b/drivers/scsi/fcoe/fcoe_ctlr.c @@ -2864,22 +2864,21 @@ void fcoe_fcf_get_selected(struct fcoe_fcf_device *fcf_dev) } EXPORT_SYMBOL(fcoe_fcf_get_selected); -void fcoe_ctlr_get_fip_mode(struct fcoe_ctlr_device *ctlr_dev) +void fcoe_ctlr_set_fip_mode(struct fcoe_ctlr_device *ctlr_dev) { struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev); mutex_lock(&ctlr->ctlr_mutex); - switch (ctlr->mode) { - case FIP_MODE_FABRIC: - ctlr_dev->mode = FIP_CONN_TYPE_FABRIC; - break; - case FIP_MODE_VN2VN: - ctlr_dev->mode = FIP_CONN_TYPE_VN2VN; + switch (ctlr_dev->mode) { + case FIP_CONN_TYPE_VN2VN: + ctlr->mode = FIP_MODE_VN2VN; break; + case FIP_CONN_TYPE_FABRIC: default: - ctlr_dev->mode = FIP_CONN_TYPE_UNKNOWN; + ctlr->mode = FIP_MODE_FABRIC; break; } + mutex_unlock(&ctlr->ctlr_mutex); } -EXPORT_SYMBOL(fcoe_ctlr_get_fip_mode); +EXPORT_SYMBOL(fcoe_ctlr_set_fip_mode); -- cgit v1.2.3 From 6e89ea3f1032d5c762f1ae9b0bd2039794596813 Mon Sep 17 00:00:00 2001 From: Robert Love Date: Tue, 27 Nov 2012 06:53:40 +0000 Subject: bnx2fc: Use the fcoe_sysfs control interface This patch adds support for the new fcoe_sysfs control interface to bnx2fc.ko. It keeps the deprecated interface in tact and therefore either the legacy or the new control interfaces can be used. A mixed mode is not supported. A user must either use the new interfaces or the old ones, but not both. The fcoe_ctlr's link state is now driven by both the netdev link state as well as the fcoe_ctlr_device's enabled attribute. The link must be up and the fcoe_ctlr_device must be enabled before the FCoE Controller starts discovery or login. Signed-off-by: Robert Love Acked-by: Neil Horman --- drivers/scsi/bnx2fc/bnx2fc_fcoe.c | 168 +++++++++++++++++++++++++++++++------- 1 file changed, 139 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index 9b38794d5665..8d975ef4eb69 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -62,6 +62,10 @@ static int bnx2fc_destroy(struct net_device *net_device); static int bnx2fc_enable(struct net_device *netdev); static int bnx2fc_disable(struct net_device *netdev); +/* fcoe_syfs control interface handlers */ +static int bnx2fc_ctlr_alloc(struct net_device *netdev); +static int bnx2fc_ctlr_enabled(struct fcoe_ctlr_device *cdev); + static void bnx2fc_recv_frame(struct sk_buff *skb); static void bnx2fc_start_disc(struct bnx2fc_interface *interface); @@ -864,6 +868,7 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event, u16 vlan_id) { struct bnx2fc_hba *hba = (struct bnx2fc_hba *)context; + struct fcoe_ctlr_device *cdev; struct fc_lport *lport; struct fc_lport *vport; struct bnx2fc_interface *interface, *tmp; @@ -925,28 +930,45 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event, bnx2fc_link_speed_update(lport); + cdev = fcoe_ctlr_to_ctlr_dev(ctlr); + if (link_possible && !bnx2fc_link_ok(lport)) { - /* Reset max recv frame size to default */ - fc_set_mfs(lport, BNX2FC_MFS); - /* - * ctlr link up will only be handled during - * enable to avoid sending discovery solicitation - * on a stale vlan - */ - if (interface->enabled) - fcoe_ctlr_link_up(ctlr); + switch (cdev->enabled) { + case FCOE_CTLR_DISABLED: + pr_info("Link up while interface is disabled.\n"); + break; + case FCOE_CTLR_ENABLED: + case FCOE_CTLR_UNUSED: + /* Reset max recv frame size to default */ + fc_set_mfs(lport, BNX2FC_MFS); + /* + * ctlr link up will only be handled during + * enable to avoid sending discovery + * solicitation on a stale vlan + */ + if (interface->enabled) + fcoe_ctlr_link_up(ctlr); + }; } else if (fcoe_ctlr_link_down(ctlr)) { - mutex_lock(&lport->lp_mutex); - list_for_each_entry(vport, &lport->vports, list) - fc_host_port_type(vport->host) = - FC_PORTTYPE_UNKNOWN; - mutex_unlock(&lport->lp_mutex); - fc_host_port_type(lport->host) = FC_PORTTYPE_UNKNOWN; - per_cpu_ptr(lport->stats, - get_cpu())->LinkFailureCount++; - put_cpu(); - fcoe_clean_pending_queue(lport); - wait_for_upload = 1; + switch (cdev->enabled) { + case FCOE_CTLR_DISABLED: + pr_info("Link down while interface is disabled.\n"); + break; + case FCOE_CTLR_ENABLED: + case FCOE_CTLR_UNUSED: + mutex_lock(&lport->lp_mutex); + list_for_each_entry(vport, &lport->vports, list) + fc_host_port_type(vport->host) = + FC_PORTTYPE_UNKNOWN; + mutex_unlock(&lport->lp_mutex); + fc_host_port_type(lport->host) = + FC_PORTTYPE_UNKNOWN; + per_cpu_ptr(lport->stats, + get_cpu())->LinkFailureCount++; + put_cpu(); + fcoe_clean_pending_queue(lport); + wait_for_upload = 1; + }; } } mutex_unlock(&bnx2fc_dev_lock); @@ -1996,7 +2018,9 @@ static void bnx2fc_ulp_init(struct cnic_dev *dev) set_bit(BNX2FC_CNIC_REGISTERED, &hba->reg_with_cnic); } - +/** + * Deperecated: Use bnx2fc_enabled() + */ static int bnx2fc_disable(struct net_device *netdev) { struct bnx2fc_interface *interface; @@ -2022,7 +2046,9 @@ static int bnx2fc_disable(struct net_device *netdev) return rc; } - +/** + * Deprecated: Use bnx2fc_enabled() + */ static int bnx2fc_enable(struct net_device *netdev) { struct bnx2fc_interface *interface; @@ -2048,17 +2074,57 @@ static int bnx2fc_enable(struct net_device *netdev) } /** - * bnx2fc_create - Create bnx2fc FCoE interface + * bnx2fc_ctlr_enabled() - Enable or disable an FCoE Controller + * @cdev: The FCoE Controller that is being enabled or disabled * - * @buffer: The name of Ethernet interface to create on - * @kp: The associated kernel param + * fcoe_sysfs will ensure that the state of 'enabled' has + * changed, so no checking is necessary here. This routine simply + * calls fcoe_enable or fcoe_disable, both of which are deprecated. + * When those routines are removed the functionality can be merged + * here. + */ +static int bnx2fc_ctlr_enabled(struct fcoe_ctlr_device *cdev) +{ + struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(cdev); + struct fc_lport *lport = ctlr->lp; + struct net_device *netdev = bnx2fc_netdev(lport); + + switch (cdev->enabled) { + case FCOE_CTLR_ENABLED: + return bnx2fc_enable(netdev); + case FCOE_CTLR_DISABLED: + return bnx2fc_disable(netdev); + case FCOE_CTLR_UNUSED: + default: + return -ENOTSUPP; + }; +} + +enum bnx2fc_create_link_state { + BNX2FC_CREATE_LINK_DOWN, + BNX2FC_CREATE_LINK_UP, +}; + +/** + * _bnx2fc_create() - Create bnx2fc FCoE interface + * @netdev : The net_device object the Ethernet interface to create on + * @fip_mode: The FIP mode for this creation + * @link_state: The ctlr link state on creation * - * Called from sysfs. + * Called from either the libfcoe 'create' module parameter + * via fcoe_create or from fcoe_syfs's ctlr_create file. + * + * libfcoe's 'create' module parameter is deprecated so some + * consolidation of code can be done when that interface is + * removed. * * Returns: 0 for success */ -static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode) +static int _bnx2fc_create(struct net_device *netdev, + enum fip_state fip_mode, + enum bnx2fc_create_link_state link_state) { + struct fcoe_ctlr_device *cdev; struct fcoe_ctlr *ctlr; struct bnx2fc_interface *interface; struct bnx2fc_hba *hba; @@ -2153,7 +2219,15 @@ static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode) /* Make this master N_port */ ctlr->lp = lport; - if (!bnx2fc_link_ok(lport)) { + cdev = fcoe_ctlr_to_ctlr_dev(ctlr); + + if (link_state == BNX2FC_CREATE_LINK_UP) + cdev->enabled = FCOE_CTLR_ENABLED; + else + cdev->enabled = FCOE_CTLR_DISABLED; + + if (link_state == BNX2FC_CREATE_LINK_UP && + !bnx2fc_link_ok(lport)) { fcoe_ctlr_link_up(ctlr); fc_host_port_type(lport->host) = FC_PORTTYPE_NPORT; set_bit(ADAPTER_STATE_READY, &interface->hba->adapter_state); @@ -2161,7 +2235,10 @@ static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode) BNX2FC_HBA_DBG(lport, "create: START DISC\n"); bnx2fc_start_disc(interface); - interface->enabled = true; + + if (link_state == BNX2FC_CREATE_LINK_UP) + interface->enabled = true; + /* * Release from kref_init in bnx2fc_interface_setup, on success * lport should be holding a reference taken in bnx2fc_if_create @@ -2186,6 +2263,37 @@ mod_err: return rc; } +/** + * bnx2fc_create() - Create a bnx2fc interface + * @netdev : The net_device object the Ethernet interface to create on + * @fip_mode: The FIP mode for this creation + * + * Called from fcoe transport + * + * Returns: 0 for success + */ +static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode) +{ + return _bnx2fc_create(netdev, fip_mode, BNX2FC_CREATE_LINK_UP); +} + +/** + * bnx2fc_ctlr_alloc() - Allocate a bnx2fc interface from fcoe_sysfs + * @netdev: The net_device to be used by the allocated FCoE Controller + * + * This routine is called from fcoe_sysfs. It will start the fcoe_ctlr + * in a link_down state. The allows the user an opportunity to configure + * the FCoE Controller from sysfs before enabling the FCoE Controller. + * + * Creating in with this routine starts the FCoE Controller in Fabric + * mode. The user can change to VN2VN or another mode before enabling. + */ +static int bnx2fc_ctlr_alloc(struct net_device *netdev) +{ + return _bnx2fc_create(netdev, FIP_MODE_FABRIC, + BNX2FC_CREATE_LINK_DOWN); +} + /** * bnx2fc_find_hba_for_cnic - maps cnic instance to bnx2fc hba instance * @@ -2311,6 +2419,7 @@ static struct fcoe_transport bnx2fc_transport = { .name = {"bnx2fc"}, .attached = false, .list = LIST_HEAD_INIT(bnx2fc_transport.list), + .alloc = bnx2fc_ctlr_alloc, .match = bnx2fc_match, .create = bnx2fc_create, .destroy = bnx2fc_destroy, @@ -2555,6 +2664,7 @@ module_init(bnx2fc_mod_init); module_exit(bnx2fc_mod_exit); static struct fcoe_sysfs_function_template bnx2fc_fcoe_sysfs_templ = { + .set_fcoe_ctlr_enabled = bnx2fc_ctlr_enabled, .get_fcoe_ctlr_link_fail = bnx2fc_ctlr_get_lesb, .get_fcoe_ctlr_vlink_fail = bnx2fc_ctlr_get_lesb, .get_fcoe_ctlr_miss_fka = bnx2fc_ctlr_get_lesb, -- cgit v1.2.3 From 8e6c5363dc52afbc60011c2c079bf4c4d26b1272 Mon Sep 17 00:00:00 2001 From: Robert Love Date: Tue, 4 Dec 2012 02:14:53 +0000 Subject: libfc, libfcoe, fcoe: Convert debug_logging macros to pr_info Convert libfc, libfcoe and fcoe's debug_logging macros to use pr_info() instead of printk(KERN_INFO, ...). checkpatch.pl now complains about this, so convert libfcoe to preferred method. Signed-off-by: Robert Love Tested-by: Marcus Dennis --- drivers/scsi/fcoe/fcoe.h | 6 +++--- drivers/scsi/fcoe/libfcoe.h | 9 ++++----- drivers/scsi/libfc/fc_libfc.h | 38 +++++++++++++++++++------------------- 3 files changed, 26 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index b42dc32cb5eb..2b53672bf932 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -55,12 +55,12 @@ do { \ #define FCOE_DBG(fmt, args...) \ FCOE_CHECK_LOGGING(FCOE_LOGGING, \ - printk(KERN_INFO "fcoe: " fmt, ##args);) + pr_info("fcoe: " fmt, ##args);) #define FCOE_NETDEV_DBG(netdev, fmt, args...) \ FCOE_CHECK_LOGGING(FCOE_NETDEV_LOGGING, \ - printk(KERN_INFO "fcoe: %s: " fmt, \ - netdev->name, ##args);) + pr_info("fcoe: %s: " fmt, \ + netdev->name, ##args);) /** * struct fcoe_interface - A FCoE interface diff --git a/drivers/scsi/fcoe/libfcoe.h b/drivers/scsi/fcoe/libfcoe.h index 63d8faedca9f..d3bb16d11401 100644 --- a/drivers/scsi/fcoe/libfcoe.h +++ b/drivers/scsi/fcoe/libfcoe.h @@ -17,17 +17,16 @@ do { \ #define LIBFCOE_DBG(fmt, args...) \ LIBFCOE_CHECK_LOGGING(LIBFCOE_LOGGING, \ - printk(KERN_INFO "libfcoe: " fmt, ##args);) + pr_info("libfcoe: " fmt, ##args);) #define LIBFCOE_FIP_DBG(fip, fmt, args...) \ LIBFCOE_CHECK_LOGGING(LIBFCOE_FIP_LOGGING, \ - printk(KERN_INFO "host%d: fip: " fmt, \ - (fip)->lp->host->host_no, ##args);) + pr_info("host%d: fip: " fmt, \ + (fip)->lp->host->host_no, ##args);) #define LIBFCOE_TRANSPORT_DBG(fmt, args...) \ LIBFCOE_CHECK_LOGGING(LIBFCOE_TRANSPORT_LOGGING, \ - printk(KERN_INFO "%s: " fmt, \ - __func__, ##args);) + pr_info("%s: " fmt, __func__, ##args);) #define LIBFCOE_SYSFS_DBG(cdev, fmt, args...) \ LIBFCOE_CHECK_LOGGING(LIBFCOE_SYSFS_LOGGING, \ diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h index c2830cc66d6a..b74189d89322 100644 --- a/drivers/scsi/libfc/fc_libfc.h +++ b/drivers/scsi/libfc/fc_libfc.h @@ -41,25 +41,25 @@ extern unsigned int fc_debug_logging; #define FC_LIBFC_DBG(fmt, args...) \ FC_CHECK_LOGGING(FC_LIBFC_LOGGING, \ - printk(KERN_INFO "libfc: " fmt, ##args)) + pr_info("libfc: " fmt, ##args)) #define FC_LPORT_DBG(lport, fmt, args...) \ FC_CHECK_LOGGING(FC_LPORT_LOGGING, \ - printk(KERN_INFO "host%u: lport %6.6x: " fmt, \ - (lport)->host->host_no, \ - (lport)->port_id, ##args)) + pr_info("host%u: lport %6.6x: " fmt, \ + (lport)->host->host_no, \ + (lport)->port_id, ##args)) -#define FC_DISC_DBG(disc, fmt, args...) \ - FC_CHECK_LOGGING(FC_DISC_LOGGING, \ - printk(KERN_INFO "host%u: disc: " fmt, \ - fc_disc_lport(disc)->host->host_no, \ - ##args)) +#define FC_DISC_DBG(disc, fmt, args...) \ + FC_CHECK_LOGGING(FC_DISC_LOGGING, \ + pr_info("host%u: disc: " fmt, \ + fc_disc_lport(disc)->host->host_no, \ + ##args)) #define FC_RPORT_ID_DBG(lport, port_id, fmt, args...) \ FC_CHECK_LOGGING(FC_RPORT_LOGGING, \ - printk(KERN_INFO "host%u: rport %6.6x: " fmt, \ - (lport)->host->host_no, \ - (port_id), ##args)) + pr_info("host%u: rport %6.6x: " fmt, \ + (lport)->host->host_no, \ + (port_id), ##args)) #define FC_RPORT_DBG(rdata, fmt, args...) \ FC_RPORT_ID_DBG((rdata)->local_port, (rdata)->ids.port_id, fmt, ##args) @@ -70,13 +70,13 @@ extern unsigned int fc_debug_logging; if ((pkt)->seq_ptr) { \ struct fc_exch *_ep = NULL; \ _ep = fc_seq_exch((pkt)->seq_ptr); \ - printk(KERN_INFO "host%u: fcp: %6.6x: " \ + pr_info("host%u: fcp: %6.6x: " \ "xid %04x-%04x: " fmt, \ (pkt)->lp->host->host_no, \ (pkt)->rport->port_id, \ (_ep)->oxid, (_ep)->rxid, ##args); \ } else { \ - printk(KERN_INFO "host%u: fcp: %6.6x: " fmt, \ + pr_info("host%u: fcp: %6.6x: " fmt, \ (pkt)->lp->host->host_no, \ (pkt)->rport->port_id, ##args); \ } \ @@ -84,14 +84,14 @@ extern unsigned int fc_debug_logging; #define FC_EXCH_DBG(exch, fmt, args...) \ FC_CHECK_LOGGING(FC_EXCH_LOGGING, \ - printk(KERN_INFO "host%u: xid %4x: " fmt, \ - (exch)->lp->host->host_no, \ - exch->xid, ##args)) + pr_info("host%u: xid %4x: " fmt, \ + (exch)->lp->host->host_no, \ + exch->xid, ##args)) #define FC_SCSI_DBG(lport, fmt, args...) \ FC_CHECK_LOGGING(FC_SCSI_LOGGING, \ - printk(KERN_INFO "host%u: scsi: " fmt, \ - (lport)->host->host_no, ##args)) + pr_info("host%u: scsi: " fmt, \ + (lport)->host->host_no, ##args)) /* * FC-4 Providers. -- cgit v1.2.3 From 66524ec9d0aeaa8bc59077c7c5f78d09ec9eeb9d Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Thu, 6 Dec 2012 06:23:43 +0000 Subject: fcoe: add support to the get_netdev() for fcoe_interface Adds support to fcoe_port's newly added get_netdev fucntion pointer. Signed-off-by: Yi Zou Cc: Bhanu Prakash Gollapudi Tested-by: Marcus Dennis Signed-off-by: Robert Love --- drivers/scsi/fcoe/fcoe.c | 1 + include/scsi/libfcoe.h | 12 ++++++++++++ 2 files changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 21927f7952d8..4cec9ddc03ba 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -1118,6 +1118,7 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, port = lport_priv(lport); port->lport = lport; port->priv = fcoe; + port->get_netdev = fcoe_netdev; port->max_queue_depth = FCOE_MAX_QUEUE_DEPTH; port->min_queue_depth = FCOE_MIN_QUEUE_DEPTH; INIT_WORK(&port->destroy_work, fcoe_destroy_work); diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h index 52bba7138069..746bc587ae34 100644 --- a/include/scsi/libfcoe.h +++ b/include/scsi/libfcoe.h @@ -353,6 +353,18 @@ struct fcoe_port { u8 data_src_addr[ETH_ALEN]; struct net_device * (*get_netdev)(const struct fc_lport *lport); }; + +/** + * fcoe_get_netdev() - Return the net device associated with a local port + * @lport: The local port to get the net device from + */ +static inline struct net_device *fcoe_get_netdev(const struct fc_lport *lport) +{ + struct fcoe_port *port = ((struct fcoe_port *)lport_priv(lport)); + + return (port->get_netdev) ? port->get_netdev(lport) : NULL; +} + void fcoe_clean_pending_queue(struct fc_lport *); void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb); void fcoe_queue_timer(ulong lport); -- cgit v1.2.3 From 03702689fcc985e9cb45b57099ebd5066f674739 Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Thu, 6 Dec 2012 06:23:58 +0000 Subject: libfcoe, fcoe: move fcoe_link_speed_update() to libfcoe and export it With the previous patch, fcoe_link_speed_update() can be moved into libfcoe and exported to used by fcoe, bnx2fc, and etc. Signed-off-by: Yi Zou Cc: Bhanu Prakash Gollapudi Tested-by: Marcus Dennis Signed-off-by: Robert Love --- drivers/scsi/fcoe/fcoe.c | 35 ----------------------------------- drivers/scsi/fcoe/fcoe_transport.c | 35 +++++++++++++++++++++++++++++++++++ include/scsi/libfcoe.h | 1 + 3 files changed, 36 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 4cec9ddc03ba..64bb53156af0 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -82,7 +82,6 @@ static int fcoe_rcv(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *); static int fcoe_percpu_receive_thread(void *); static void fcoe_percpu_clean(struct fc_lport *); -static int fcoe_link_speed_update(struct fc_lport *); static int fcoe_link_ok(struct fc_lport *); static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *); @@ -2387,40 +2386,6 @@ static int fcoe_ctlr_alloc(struct net_device *netdev) FCOE_CREATE_LINK_DOWN); } -/** - * fcoe_link_speed_update() - Update the supported and actual link speeds - * @lport: The local port to update speeds for - * - * Returns: 0 if the ethtool query was successful - * -1 if the ethtool query failed - */ -static int fcoe_link_speed_update(struct fc_lport *lport) -{ - struct net_device *netdev = fcoe_netdev(lport); - struct ethtool_cmd ecmd; - - if (!__ethtool_get_settings(netdev, &ecmd)) { - lport->link_supported_speeds &= - ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT); - if (ecmd.supported & (SUPPORTED_1000baseT_Half | - SUPPORTED_1000baseT_Full)) - lport->link_supported_speeds |= FC_PORTSPEED_1GBIT; - if (ecmd.supported & SUPPORTED_10000baseT_Full) - lport->link_supported_speeds |= - FC_PORTSPEED_10GBIT; - switch (ethtool_cmd_speed(&ecmd)) { - case SPEED_1000: - lport->link_speed = FC_PORTSPEED_1GBIT; - break; - case SPEED_10000: - lport->link_speed = FC_PORTSPEED_10GBIT; - break; - } - return 0; - } - return -1; -} - /** * fcoe_link_ok() - Check if the link is OK for a local port * @lport: The local port to check link on diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c index 83e78f922054..3c6846f1268b 100644 --- a/drivers/scsi/fcoe/fcoe_transport.c +++ b/drivers/scsi/fcoe/fcoe_transport.c @@ -83,6 +83,41 @@ static struct notifier_block libfcoe_notifier = { .notifier_call = libfcoe_device_notification, }; +/** + * fcoe_link_speed_update() - Update the supported and actual link speeds + * @lport: The local port to update speeds for + * + * Returns: 0 if the ethtool query was successful + * -1 if the ethtool query failed + */ +int fcoe_link_speed_update(struct fc_lport *lport) +{ + struct net_device *netdev = fcoe_get_netdev(lport); + struct ethtool_cmd ecmd; + + if (!__ethtool_get_settings(netdev, &ecmd)) { + lport->link_supported_speeds &= + ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT); + if (ecmd.supported & (SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full)) + lport->link_supported_speeds |= FC_PORTSPEED_1GBIT; + if (ecmd.supported & SUPPORTED_10000baseT_Full) + lport->link_supported_speeds |= + FC_PORTSPEED_10GBIT; + switch (ethtool_cmd_speed(&ecmd)) { + case SPEED_1000: + lport->link_speed = FC_PORTSPEED_1GBIT; + break; + case SPEED_10000: + lport->link_speed = FC_PORTSPEED_10GBIT; + break; + } + return 0; + } + return -1; +} +EXPORT_SYMBOL_GPL(fcoe_link_speed_update); + void __fcoe_get_lesb(struct fc_lport *lport, struct fc_els_lesb *fc_lesb, struct net_device *netdev) diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h index 746bc587ae34..6c59ba7af28a 100644 --- a/include/scsi/libfcoe.h +++ b/include/scsi/libfcoe.h @@ -260,6 +260,7 @@ void __fcoe_get_lesb(struct fc_lport *lport, struct fc_els_lesb *fc_lesb, struct net_device *netdev); void fcoe_wwn_to_str(u64 wwn, char *buf, int len); int fcoe_validate_vport_create(struct fc_vport *vport); +int fcoe_link_speed_update(struct fc_lport *); /** * is_fip_mode() - returns true if FIP mode selected. -- cgit v1.2.3 From 57c2728fa806aff08703e5739620454d723bc865 Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Thu, 6 Dec 2012 06:24:13 +0000 Subject: libfcoe, fcoe: consolidate the fcoe_ctlr_get_lesb/fcoe_get_lesb Similarly they can be moved into libfcoe instead of being private to fcoe now. Also add comments particularly on the term LESB to the corresponding function. Signed-off-by: Yi Zou Cc: Bhanu Prakash Gollapudi Tested-by: Marcus Dennis Signed-off-by: Robert Love --- drivers/scsi/fcoe/fcoe.c | 40 ---------------------------- drivers/scsi/fcoe/fcoe_transport.c | 54 ++++++++++++++++++++++++++++++++++++++ include/scsi/libfcoe.h | 2 ++ 3 files changed, 56 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 64bb53156af0..0b333681a645 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -130,8 +130,6 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *, void *, u32 timeout); static void fcoe_recv_frame(struct sk_buff *skb); -static void fcoe_get_lesb(struct fc_lport *, struct fc_els_lesb *); - /* notification function for packets from net device */ static struct notifier_block fcoe_notifier = { .notifier_call = fcoe_device_notification, @@ -155,7 +153,6 @@ static int fcoe_vport_create(struct fc_vport *, bool disabled); static int fcoe_vport_disable(struct fc_vport *, bool disable); static void fcoe_set_vport_symbolic_name(struct fc_vport *); static void fcoe_set_port_id(struct fc_lport *, u32, struct fc_frame *); -static void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *); static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *); static struct fcoe_sysfs_function_template fcoe_sysfs_templ = { @@ -2858,43 +2855,6 @@ static void fcoe_set_vport_symbolic_name(struct fc_vport *vport) NULL, NULL, 3 * lport->r_a_tov); } -/** - * fcoe_get_lesb() - Fill the FCoE Link Error Status Block - * @lport: the local port - * @fc_lesb: the link error status block - */ -static void fcoe_get_lesb(struct fc_lport *lport, - struct fc_els_lesb *fc_lesb) -{ - struct net_device *netdev = fcoe_netdev(lport); - - __fcoe_get_lesb(lport, fc_lesb, netdev); -} - -static void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev) -{ - struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev); - struct net_device *netdev = fcoe_netdev(fip->lp); - struct fcoe_fc_els_lesb *fcoe_lesb; - struct fc_els_lesb fc_lesb; - - __fcoe_get_lesb(fip->lp, &fc_lesb, netdev); - fcoe_lesb = (struct fcoe_fc_els_lesb *)(&fc_lesb); - - ctlr_dev->lesb.lesb_link_fail = - ntohl(fcoe_lesb->lesb_link_fail); - ctlr_dev->lesb.lesb_vlink_fail = - ntohl(fcoe_lesb->lesb_vlink_fail); - ctlr_dev->lesb.lesb_miss_fka = - ntohl(fcoe_lesb->lesb_miss_fka); - ctlr_dev->lesb.lesb_symb_err = - ntohl(fcoe_lesb->lesb_symb_err); - ctlr_dev->lesb.lesb_err_block = - ntohl(fcoe_lesb->lesb_err_block); - ctlr_dev->lesb.lesb_fcs_error = - ntohl(fcoe_lesb->lesb_fcs_error); -} - static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *fcf_dev) { struct fcoe_ctlr_device *ctlr_dev = diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c index 3c6846f1268b..dd1aeb45f4cd 100644 --- a/drivers/scsi/fcoe/fcoe_transport.c +++ b/drivers/scsi/fcoe/fcoe_transport.c @@ -118,6 +118,15 @@ int fcoe_link_speed_update(struct fc_lport *lport) } EXPORT_SYMBOL_GPL(fcoe_link_speed_update); +/** + * __fcoe_get_lesb() - Get the Link Error Status Block (LESB) for a given lport + * @lport: The local port to update speeds for + * @fc_lesb: Pointer to the LESB to be filled up + * @netdev: Pointer to the netdev that is associated with the lport + * + * Note, the Link Error Status Block (LESB) for FCoE is defined in FC-BB-6 + * Clause 7.11 in v1.04. + */ void __fcoe_get_lesb(struct fc_lport *lport, struct fc_els_lesb *fc_lesb, struct net_device *netdev) @@ -147,6 +156,51 @@ void __fcoe_get_lesb(struct fc_lport *lport, } EXPORT_SYMBOL_GPL(__fcoe_get_lesb); +/** + * fcoe_get_lesb() - Fill the FCoE Link Error Status Block + * @lport: the local port + * @fc_lesb: the link error status block + */ +void fcoe_get_lesb(struct fc_lport *lport, + struct fc_els_lesb *fc_lesb) +{ + struct net_device *netdev = fcoe_get_netdev(lport); + + __fcoe_get_lesb(lport, fc_lesb, netdev); +} +EXPORT_SYMBOL_GPL(fcoe_get_lesb); + +/** + * fcoe_ctlr_get_lesb() - Get the Link Error Status Block (LESB) for a given + * fcoe controller device + * @ctlr_dev: The given fcoe controller device + * + */ +void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev) +{ + struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev); + struct net_device *netdev = fcoe_get_netdev(fip->lp); + struct fcoe_fc_els_lesb *fcoe_lesb; + struct fc_els_lesb fc_lesb; + + __fcoe_get_lesb(fip->lp, &fc_lesb, netdev); + fcoe_lesb = (struct fcoe_fc_els_lesb *)(&fc_lesb); + + ctlr_dev->lesb.lesb_link_fail = + ntohl(fcoe_lesb->lesb_link_fail); + ctlr_dev->lesb.lesb_vlink_fail = + ntohl(fcoe_lesb->lesb_vlink_fail); + ctlr_dev->lesb.lesb_miss_fka = + ntohl(fcoe_lesb->lesb_miss_fka); + ctlr_dev->lesb.lesb_symb_err = + ntohl(fcoe_lesb->lesb_symb_err); + ctlr_dev->lesb.lesb_err_block = + ntohl(fcoe_lesb->lesb_err_block); + ctlr_dev->lesb.lesb_fcs_error = + ntohl(fcoe_lesb->lesb_fcs_error); +} +EXPORT_SYMBOL_GPL(fcoe_ctlr_get_lesb); + void fcoe_wwn_to_str(u64 wwn, char *buf, int len) { u8 wwpn[8]; diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h index 6c59ba7af28a..4427393115ea 100644 --- a/include/scsi/libfcoe.h +++ b/include/scsi/libfcoe.h @@ -261,6 +261,8 @@ void __fcoe_get_lesb(struct fc_lport *lport, struct fc_els_lesb *fc_lesb, void fcoe_wwn_to_str(u64 wwn, char *buf, int len); int fcoe_validate_vport_create(struct fc_vport *vport); int fcoe_link_speed_update(struct fc_lport *); +void fcoe_get_lesb(struct fc_lport *, struct fc_els_lesb *); +void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev); /** * is_fip_mode() - returns true if FIP mode selected. -- cgit v1.2.3 From c3d7909b865274ffa74fa2715c6663caa51a80e1 Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Thu, 6 Dec 2012 06:24:29 +0000 Subject: bnx2fc: add support to get_netdev for bnx2f_interface Adds support to fcoe_port's newly added get_netdev fucntion pointer for bnx2fc. Signed-off-by: Yi Zou Cc: Bhanu Prakash Gollapudi Tested-by: Marcus Dennis Signed-off-by: Robert Love --- drivers/scsi/bnx2fc/bnx2fc_fcoe.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index 8d975ef4eb69..9b9ccc94e7ae 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -119,7 +119,7 @@ static inline struct net_device *bnx2fc_netdev(const struct fc_lport *lport) static void bnx2fc_get_lesb(struct fc_lport *lport, struct fc_els_lesb *fc_lesb) { - struct net_device *netdev = bnx2fc_netdev(lport); + struct net_device *netdev = fcoe_get_netdev(lport); __fcoe_get_lesb(lport, fc_lesb, netdev); } @@ -127,7 +127,7 @@ static void bnx2fc_get_lesb(struct fc_lport *lport, static void bnx2fc_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev) { struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev); - struct net_device *netdev = bnx2fc_netdev(fip->lp); + struct net_device *netdev = fcoe_get_netdev(fip->lp); struct fcoe_fc_els_lesb *fcoe_lesb; struct fc_els_lesb fc_lesb; @@ -1499,6 +1499,7 @@ static struct fc_lport *bnx2fc_if_create(struct bnx2fc_interface *interface, port = lport_priv(lport); port->lport = lport; port->priv = interface; + port->get_netdev = bnx2fc_netdev; INIT_WORK(&port->destroy_work, bnx2fc_destroy_work); /* Configure fcoe_port */ -- cgit v1.2.3 From 0e0f9cd6a80dc883dab4c82c17edc1abe485cbd9 Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Thu, 6 Dec 2012 06:24:44 +0000 Subject: bnx2fc: use fcoe_link_speed_update() from the exported symbol in libfcoe We have fcoe_link_speed_update() in libfcoe ready for use now, take out the bnx2fc version which is almost the same. Signed-off-by: Yi Zou Cc: Bhanu Prakash Gollapudi Tested-by: Marcus Dennis Signed-off-by: Robert Love --- drivers/scsi/bnx2fc/bnx2fc_fcoe.c | 33 ++------------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index 9b9ccc94e7ae..a1c687c699ee 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -738,35 +738,6 @@ static int bnx2fc_shost_config(struct fc_lport *lport, struct device *dev) return 0; } -static void bnx2fc_link_speed_update(struct fc_lport *lport) -{ - struct fcoe_port *port = lport_priv(lport); - struct bnx2fc_interface *interface = port->priv; - struct net_device *netdev = interface->netdev; - struct ethtool_cmd ecmd; - - if (!__ethtool_get_settings(netdev, &ecmd)) { - lport->link_supported_speeds &= - ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT); - if (ecmd.supported & (SUPPORTED_1000baseT_Half | - SUPPORTED_1000baseT_Full)) - lport->link_supported_speeds |= FC_PORTSPEED_1GBIT; - if (ecmd.supported & SUPPORTED_10000baseT_Full) - lport->link_supported_speeds |= FC_PORTSPEED_10GBIT; - - switch (ethtool_cmd_speed(&ecmd)) { - case SPEED_1000: - lport->link_speed = FC_PORTSPEED_1GBIT; - break; - case SPEED_2500: - lport->link_speed = FC_PORTSPEED_2GBIT; - break; - case SPEED_10000: - lport->link_speed = FC_PORTSPEED_10GBIT; - break; - } - } -} static int bnx2fc_link_ok(struct fc_lport *lport) { struct fcoe_port *port = lport_priv(lport); @@ -824,7 +795,7 @@ static int bnx2fc_net_config(struct fc_lport *lport, struct net_device *netdev) port->fcoe_pending_queue_active = 0; setup_timer(&port->timer, fcoe_queue_timer, (unsigned long) lport); - bnx2fc_link_speed_update(lport); + fcoe_link_speed_update(lport); if (!lport->vport) { if (fcoe_get_wwn(netdev, &wwnn, NETDEV_FCOE_WWNN)) @@ -928,7 +899,7 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event, BNX2FC_HBA_DBG(lport, "netevent handler - event=%s %ld\n", interface->netdev->name, event); - bnx2fc_link_speed_update(lport); + fcoe_link_speed_update(lport); cdev = fcoe_ctlr_to_ctlr_dev(ctlr); -- cgit v1.2.3 From b8b3e697d15165090583bed850879b1f3b250db0 Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Thu, 6 Dec 2012 06:24:59 +0000 Subject: bnx2fc: use fcoe_get_lesb/fcoe_ctlr_get_lesb() directly from libfcoe Drop the bnx2fc_xxx versions as they are basically the same. Signed-off-by: Yi Zou Cc: Bhanu Prakash Gollapudi Tested-by: Marcus Dennis Signed-off-by: Robert Love --- drivers/scsi/bnx2fc/bnx2fc_fcoe.c | 53 ++++++--------------------------------- 1 file changed, 7 insertions(+), 46 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index a1c687c699ee..4d5c72159346 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -93,7 +93,6 @@ static void bnx2fc_port_shutdown(struct fc_lport *lport); static void bnx2fc_stop(struct bnx2fc_interface *interface); static int __init bnx2fc_mod_init(void); static void __exit bnx2fc_mod_exit(void); -static void bnx2fc_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev); unsigned int bnx2fc_debug_level; module_param_named(debug_logging, bnx2fc_debug_level, int, S_IRUGO|S_IWUSR); @@ -111,44 +110,6 @@ static inline struct net_device *bnx2fc_netdev(const struct fc_lport *lport) ((struct fcoe_port *)lport_priv(lport))->priv)->netdev; } -/** - * bnx2fc_get_lesb() - Fill the FCoE Link Error Status Block - * @lport: the local port - * @fc_lesb: the link error status block - */ -static void bnx2fc_get_lesb(struct fc_lport *lport, - struct fc_els_lesb *fc_lesb) -{ - struct net_device *netdev = fcoe_get_netdev(lport); - - __fcoe_get_lesb(lport, fc_lesb, netdev); -} - -static void bnx2fc_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev) -{ - struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev); - struct net_device *netdev = fcoe_get_netdev(fip->lp); - struct fcoe_fc_els_lesb *fcoe_lesb; - struct fc_els_lesb fc_lesb; - - __fcoe_get_lesb(fip->lp, &fc_lesb, netdev); - fcoe_lesb = (struct fcoe_fc_els_lesb *)(&fc_lesb); - - ctlr_dev->lesb.lesb_link_fail = - ntohl(fcoe_lesb->lesb_link_fail); - ctlr_dev->lesb.lesb_vlink_fail = - ntohl(fcoe_lesb->lesb_vlink_fail); - ctlr_dev->lesb.lesb_miss_fka = - ntohl(fcoe_lesb->lesb_miss_fka); - ctlr_dev->lesb.lesb_symb_err = - ntohl(fcoe_lesb->lesb_symb_err); - ctlr_dev->lesb.lesb_err_block = - ntohl(fcoe_lesb->lesb_err_block); - ctlr_dev->lesb.lesb_fcs_error = - ntohl(fcoe_lesb->lesb_fcs_error); -} -EXPORT_SYMBOL(bnx2fc_ctlr_get_lesb); - static void bnx2fc_fcf_get_vlan_id(struct fcoe_fcf_device *fcf_dev) { struct fcoe_ctlr_device *ctlr_dev = @@ -2637,12 +2598,12 @@ module_exit(bnx2fc_mod_exit); static struct fcoe_sysfs_function_template bnx2fc_fcoe_sysfs_templ = { .set_fcoe_ctlr_enabled = bnx2fc_ctlr_enabled, - .get_fcoe_ctlr_link_fail = bnx2fc_ctlr_get_lesb, - .get_fcoe_ctlr_vlink_fail = bnx2fc_ctlr_get_lesb, - .get_fcoe_ctlr_miss_fka = bnx2fc_ctlr_get_lesb, - .get_fcoe_ctlr_symb_err = bnx2fc_ctlr_get_lesb, - .get_fcoe_ctlr_err_block = bnx2fc_ctlr_get_lesb, - .get_fcoe_ctlr_fcs_error = bnx2fc_ctlr_get_lesb, + .get_fcoe_ctlr_link_fail = fcoe_ctlr_get_lesb, + .get_fcoe_ctlr_vlink_fail = fcoe_ctlr_get_lesb, + .get_fcoe_ctlr_miss_fka = fcoe_ctlr_get_lesb, + .get_fcoe_ctlr_symb_err = fcoe_ctlr_get_lesb, + .get_fcoe_ctlr_err_block = fcoe_ctlr_get_lesb, + .get_fcoe_ctlr_fcs_error = fcoe_ctlr_get_lesb, .get_fcoe_fcf_selected = fcoe_fcf_get_selected, .get_fcoe_fcf_vlan_id = bnx2fc_fcf_get_vlan_id, @@ -2749,7 +2710,7 @@ static struct libfc_function_template bnx2fc_libfc_fcn_templ = { .elsct_send = bnx2fc_elsct_send, .fcp_abort_io = bnx2fc_abort_io, .fcp_cleanup = bnx2fc_cleanup, - .get_lesb = bnx2fc_get_lesb, + .get_lesb = fcoe_get_lesb, .rport_event_callback = bnx2fc_rport_event_handler, }; -- cgit v1.2.3 From 4f670ff8eb4cb3e9e6ae0c0c6976faa0a4503751 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 7 Dec 2012 14:10:14 +0000 Subject: debris left by "[SCSI] libfcoe: Remove mutex_trylock/restart_syscall checks" AFAICS, the situation for fcoe_transport_disable() seems to be the same as for fcoe_transport_enable(). IOW, shouldn't it have restart_syscall() removed as well? I don't see any in-tree ->disable() instances that could return -ERESTARTSYS, anyway... Signed-off-by: Al Viro Signed-off-by: Robert Love --- drivers/scsi/fcoe/fcoe_transport.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c index dd1aeb45f4cd..f3a5a53e8631 100644 --- a/drivers/scsi/fcoe/fcoe_transport.c +++ b/drivers/scsi/fcoe/fcoe_transport.c @@ -962,11 +962,7 @@ out_putdev: dev_put(netdev); out_nodev: mutex_unlock(&ft_mutex); - - if (rc == -ERESTARTSYS) - return restart_syscall(); - else - return rc; + return rc; } /** -- cgit v1.2.3 From eb119bd612906519cacef2536a9a524c2da5f7fb Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 16 Dec 2012 12:43:36 +0000 Subject: drm/i915: Access to snooped system memory through the GTT is incoherent We ignore all the user requests to handle flushing to the GTT domain if the user requests such on a snoopable bo, and as such access through the GTT to such pages remains incoherent. The specs even warn that such behaviour is undefined - a strong reason never to do so. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 6380c6083cb2..d15c86279d02 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1341,6 +1341,12 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) trace_i915_gem_object_fault(obj, page_offset, true, write); + /* Access to snoopable pages through the GTT is incoherent. */ + if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(dev)) { + ret = -EINVAL; + goto unlock; + } + /* Now bind it into the GTT if needed */ ret = i915_gem_object_pin(obj, 0, true, false); if (ret) -- cgit v1.2.3 From 88afe715dd5469bc24ca7a19ac62dd3c241cab48 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 16 Dec 2012 12:15:41 +0000 Subject: drm/i915: Clear the stolen fb before enabling As the stolen memory region will contain the contents of whatever was last there, it invariably contains garbage. To be consistent with the shmemfs backed fb and the expectations of the fb layer, we need to clear the fb prior to installing it as an fbcon. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=58111 Signed-off-by: Chris Wilson [danvet: Fixup sparse __iomem confusion reported by Wu Fengguang.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_fb.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 822896782cd0..71d55801c0d9 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -155,6 +155,13 @@ static int intelfb_create(struct intel_fbdev *ifbdev, drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); drm_fb_helper_fill_var(info, &ifbdev->helper, sizes->fb_width, sizes->fb_height); + /* If the object is shmemfs backed, it will have given us zeroed pages. + * If the object is stolen however, it will be full of whatever + * garbage was left in there. + */ + if (ifbdev->ifb.obj->stolen) + memset_io(info->screen_base, 0, info->screen_size); + /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08x, bo %p\n", -- cgit v1.2.3 From 681e5811f88b7ecca77734421adafb62f19563be Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Thu, 6 Dec 2012 11:12:38 -0200 Subject: drm/i915: check for the PCH when setting pch_transcoder Don't check the CPU, it doesn't have any PCH transcoder. Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 7f1b07c9061d..fa73b4ad88bb 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1789,7 +1789,7 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, int reg; u32 val; - if (IS_HASWELL(dev_priv->dev)) + if (HAS_PCH_LPT(dev_priv->dev)) pch_transcoder = TRANSCODER_A; else pch_transcoder = pipe; -- cgit v1.2.3 From a0e63c22ee6daae8fda98e1c6eaff5f5f1e2ba31 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Thu, 6 Dec 2012 11:12:39 -0200 Subject: drm/i915: remove leftover display.update_wm assignment This was moved to intel_init_pm. Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index fa73b4ad88bb..f02ec7b7d6eb 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8284,8 +8284,7 @@ static void intel_init_display(struct drm_device *dev) } else if (IS_HASWELL(dev)) { dev_priv->display.fdi_link_train = hsw_fdi_link_train; dev_priv->display.write_eld = haswell_write_eld; - } else - dev_priv->display.update_wm = NULL; + } } else if (IS_G4X(dev)) { dev_priv->display.write_eld = g4x_write_eld; } -- cgit v1.2.3 From f0a3424e964ec8e934497a0724cd1d58690fd9ab Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Thu, 6 Dec 2012 16:51:50 -0200 Subject: drm/i915: add intel_dp_set_signal_levels So we can de-duplicate code that's inside intel_dp_start_link_train and intel_dp_complete_link_train. V2: Rebase since patch 3/5 was discarded. Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 69 ++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 185bf4e7614e..2d3b26832397 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1587,7 +1587,7 @@ intel_get_adjust_train(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_ST } static uint32_t -intel_dp_signal_levels(uint8_t train_set) +intel_gen4_signal_levels(uint8_t train_set) { uint32_t signal_levels = 0; @@ -1685,7 +1685,7 @@ intel_gen7_edp_signal_levels(uint8_t train_set) /* Gen7.5's (HSW) DP voltage swing and pre-emphasis control */ static uint32_t -intel_dp_signal_levels_hsw(uint8_t train_set) +intel_hsw_signal_levels(uint8_t train_set) { int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK | DP_TRAIN_PRE_EMPHASIS_MASK); @@ -1717,6 +1717,34 @@ intel_dp_signal_levels_hsw(uint8_t train_set) } } +/* Properly updates "DP" with the correct signal levels. */ +static void +intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = intel_dig_port->base.base.dev; + uint32_t signal_levels, mask; + uint8_t train_set = intel_dp->train_set[0]; + + if (IS_HASWELL(dev)) { + signal_levels = intel_hsw_signal_levels(train_set); + mask = DDI_BUF_EMP_MASK; + } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) { + signal_levels = intel_gen7_edp_signal_levels(train_set); + mask = EDP_LINK_TRAIN_VOL_EMP_MASK_IVB; + } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) { + signal_levels = intel_gen6_edp_signal_levels(train_set); + mask = EDP_LINK_TRAIN_VOL_EMP_MASK_SNB; + } else { + signal_levels = intel_gen4_signal_levels(train_set); + mask = DP_VOLTAGE_MASK | DP_PRE_EMPHASIS_MASK; + } + + DRM_DEBUG_KMS("Using signal levels %08x\n", signal_levels); + + *DP = (*DP & ~mask) | signal_levels; +} + static bool intel_dp_set_link_train(struct intel_dp *intel_dp, uint32_t dp_reg_value, @@ -1853,24 +1881,8 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) for (;;) { /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ uint8_t link_status[DP_LINK_STATUS_SIZE]; - uint32_t signal_levels; - - if (IS_HASWELL(dev)) { - signal_levels = intel_dp_signal_levels_hsw( - intel_dp->train_set[0]); - DP = (DP & ~DDI_BUF_EMP_MASK) | signal_levels; - } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) { - signal_levels = intel_gen7_edp_signal_levels(intel_dp->train_set[0]); - DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_IVB) | signal_levels; - } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) { - signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]); - DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels; - } else { - signal_levels = intel_dp_signal_levels(intel_dp->train_set[0]); - DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; - } - DRM_DEBUG_KMS("training pattern 1 signal levels %08x\n", - signal_levels); + + intel_dp_set_signal_levels(intel_dp, &DP); /* Set training pattern 1 */ if (!intel_dp_set_link_train(intel_dp, DP, @@ -1926,7 +1938,6 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) void intel_dp_complete_link_train(struct intel_dp *intel_dp) { - struct drm_device *dev = intel_dp_to_dev(intel_dp); bool channel_eq = false; int tries, cr_tries; uint32_t DP = intel_dp->DP; @@ -1936,8 +1947,6 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) cr_tries = 0; channel_eq = false; for (;;) { - /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ - uint32_t signal_levels; uint8_t link_status[DP_LINK_STATUS_SIZE]; if (cr_tries > 5) { @@ -1946,19 +1955,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) break; } - if (IS_HASWELL(dev)) { - signal_levels = intel_dp_signal_levels_hsw(intel_dp->train_set[0]); - DP = (DP & ~DDI_BUF_EMP_MASK) | signal_levels; - } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) { - signal_levels = intel_gen7_edp_signal_levels(intel_dp->train_set[0]); - DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_IVB) | signal_levels; - } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) { - signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]); - DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels; - } else { - signal_levels = intel_dp_signal_levels(intel_dp->train_set[0]); - DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; - } + intel_dp_set_signal_levels(intel_dp, &DP); /* channel eq pattern */ if (!intel_dp_set_link_train(intel_dp, DP, -- cgit v1.2.3 From dfd07d72cf70cc8845006b698b55369f5d9c2733 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 17 Dec 2012 11:21:38 +0100 Subject: drm/i915: clean up PIPECONF bpc #defines Ilk+ somehow used #defines in near the PIPESTAT definitions, which decently confused me. Earlier platforms called it BPP instead of BPC. Clean this all up. Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 15 +++++---------- drivers/gpu/drm/i915/intel_display.c | 28 ++++++++++++++-------------- 2 files changed, 19 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index f83480403ccc..5b3020f836b9 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2648,11 +2648,11 @@ #define PIPECONF_INTERLACED_DBL_ILK (4 << 21) /* ilk/snb only */ #define PIPECONF_PFIT_PF_INTERLACED_DBL_ILK (5 << 21) /* ilk/snb only */ #define PIPECONF_CXSR_DOWNCLOCK (1<<16) -#define PIPECONF_BPP_MASK (0x000000e0) -#define PIPECONF_BPP_8 (0<<5) -#define PIPECONF_BPP_10 (1<<5) -#define PIPECONF_BPP_6 (2<<5) -#define PIPECONF_BPP_12 (3<<5) +#define PIPECONF_BPC_MASK (0x7 << 5) +#define PIPECONF_8BPC (0<<5) +#define PIPECONF_10BPC (1<<5) +#define PIPECONF_6BPC (2<<5) +#define PIPECONF_12BPC (3<<5) #define PIPECONF_DITHER_EN (1<<4) #define PIPECONF_DITHER_TYPE_MASK (0x0000000c) #define PIPECONF_DITHER_TYPE_SP (0<<2) @@ -2696,11 +2696,6 @@ #define PIPE_START_VBLANK_INTERRUPT_STATUS (1UL<<2) /* 965 or later */ #define PIPE_VBLANK_INTERRUPT_STATUS (1UL<<1) #define PIPE_OVERLAY_UPDATED_STATUS (1UL<<0) -#define PIPE_BPC_MASK (7 << 5) /* Ironlake */ -#define PIPE_8BPC (0 << 5) -#define PIPE_10BPC (1 << 5) -#define PIPE_6BPC (2 << 5) -#define PIPE_12BPC (3 << 5) #define PIPESRC(pipe) _PIPE(pipe, _PIPEASRC, _PIPEBSRC) #define PIPECONF(tran) _TRANSCODER(tran, _PIPEACONF, _PIPEBCONF) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f02ec7b7d6eb..1bb68169aaf0 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1669,8 +1669,8 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv, * make the BPC in transcoder be consistent with * that in pipeconf reg. */ - val &= ~PIPE_BPC_MASK; - val |= pipeconf_val & PIPE_BPC_MASK; + val &= ~PIPECONF_BPC_MASK; + val |= pipeconf_val & PIPECONF_BPC_MASK; } val &= ~TRANS_INTERLACE_MASK; @@ -2764,7 +2764,7 @@ static void ironlake_fdi_pll_enable(struct intel_crtc *intel_crtc) temp = I915_READ(reg); temp &= ~((0x7 << 19) | (0x7 << 16)); temp |= (intel_crtc->fdi_lanes - 1) << 19; - temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11; + temp |= (I915_READ(PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; I915_WRITE(reg, temp | FDI_RX_PLL_ENABLE); POSTING_READ(reg); @@ -2845,7 +2845,7 @@ static void ironlake_fdi_disable(struct drm_crtc *crtc) reg = FDI_RX_CTL(pipe); temp = I915_READ(reg); temp &= ~(0x7 << 16); - temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11; + temp |= (I915_READ(PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; I915_WRITE(reg, temp & ~FDI_RX_ENABLE); POSTING_READ(reg); @@ -2876,7 +2876,7 @@ static void ironlake_fdi_disable(struct drm_crtc *crtc) } /* BPC in FDI rx is consistent with that in PIPECONF */ temp &= ~(0x07 << 16); - temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11; + temp |= (I915_READ(PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; I915_WRITE(reg, temp); POSTING_READ(reg); @@ -3115,7 +3115,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) if (HAS_PCH_CPT(dev) && (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) || intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))) { - u32 bpc = (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) >> 5; + u32 bpc = (I915_READ(PIPECONF(pipe)) & PIPECONF_BPC_MASK) >> 5; reg = TRANS_DP_CTL(pipe); temp = I915_READ(reg); temp &= ~(TRANS_DP_PORT_SEL_MASK | @@ -4686,10 +4686,10 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, } /* default to 8bpc */ - pipeconf &= ~(PIPECONF_BPP_MASK | PIPECONF_DITHER_EN); + pipeconf &= ~(PIPECONF_BPC_MASK | PIPECONF_DITHER_EN); if (is_dp) { if (adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC) { - pipeconf |= PIPECONF_BPP_6 | + pipeconf |= PIPECONF_6BPC | PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_SP; } @@ -4697,7 +4697,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, if (IS_VALLEYVIEW(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) { if (adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC) { - pipeconf |= PIPECONF_BPP_6 | + pipeconf |= PIPECONF_6BPC | PIPECONF_ENABLE | I965_PIPECONF_ACTIVE; } @@ -4907,19 +4907,19 @@ static void ironlake_set_pipeconf(struct drm_crtc *crtc, val = I915_READ(PIPECONF(pipe)); - val &= ~PIPE_BPC_MASK; + val &= ~PIPECONF_BPC_MASK; switch (intel_crtc->bpp) { case 18: - val |= PIPE_6BPC; + val |= PIPECONF_6BPC; break; case 24: - val |= PIPE_8BPC; + val |= PIPECONF_8BPC; break; case 30: - val |= PIPE_10BPC; + val |= PIPECONF_10BPC; break; case 36: - val |= PIPE_12BPC; + val |= PIPECONF_12BPC; break; default: /* Case prevented by intel_choose_pipe_bpp_dither. */ -- cgit v1.2.3 From ffc809886b6cbeb0964130bdbcf62504ceeec18e Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Wed, 28 Nov 2012 01:17:50 -0300 Subject: [media] au0828: add missing model 72281, usb id 2040:7270 to the model matrix Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/au0828/au0828-cards.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c index 0cb7c28dcb17..d12bdd3589f5 100644 --- a/drivers/media/usb/au0828/au0828-cards.c +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -170,6 +170,7 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ case 72261: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ + case 72281: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and analog video */ case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ break; @@ -333,6 +334,8 @@ struct usb_device_id au0828_usb_id_table[] = { .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, { USB_DEVICE(0x2040, 0x7213), .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x7270), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, { }, }; -- cgit v1.2.3 From c70ffd5968476ffe9fe2a8bfdc88956ecef92d2a Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Wed, 28 Nov 2012 11:46:24 -0300 Subject: [media] au0828: update model matrix entries for 72261, 72271 & 72281 Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/au0828/au0828-cards.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c index d12bdd3589f5..cf309d8102c0 100644 --- a/drivers/media/usb/au0828/au0828-cards.c +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -169,8 +169,9 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ - case 72261: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ - case 72281: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ + case 72261: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ + case 72271: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ + case 72281: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and analog video */ case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ break; -- cgit v1.2.3 From 8a4e786660f512b029b56d94d1b8f0201e67aab3 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 4 Dec 2012 11:30:00 -0300 Subject: [media] au0828: remove forced dependency of VIDEO_AU0828 on VIDEO_V4L2 This patch removes the dependendency of VIDEO_AU0828 on VIDEO_V4L2 by creating a new Kconfig option, VIDEO_AU0828_V4L2, which enables analog video capture support and depends on VIDEO_V4L2 itself. With VIDEO_AU0828_V4L2 disabled, the driver will only support digital television and will not depend on the v4l2-core. With VIDEO_AU0828_V4L2 enabled, the driver will be built with the analog v4l2 support included. By default, the VIDEO_AU0828_V4L2 option will be set to Y, so as to preserve the original behavior. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/Kconfig | 2 +- drivers/media/usb/au0828/Kconfig | 17 ++++++++++++++--- drivers/media/usb/au0828/Makefile | 6 +++++- drivers/media/usb/au0828/au0828-cards.c | 4 ++++ drivers/media/usb/au0828/au0828-core.c | 13 ++++++++++++- drivers/media/usb/au0828/au0828-i2c.c | 4 ++++ drivers/media/usb/au0828/au0828.h | 2 ++ 7 files changed, 42 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index 6746994d03fe..0a7d520636a9 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -21,7 +21,6 @@ endif if MEDIA_ANALOG_TV_SUPPORT comment "Analog TV USB devices" -source "drivers/media/usb/au0828/Kconfig" source "drivers/media/usb/pvrusb2/Kconfig" source "drivers/media/usb/hdpvr/Kconfig" source "drivers/media/usb/tlg2300/Kconfig" @@ -31,6 +30,7 @@ endif if (MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT) comment "Analog/digital TV USB devices" +source "drivers/media/usb/au0828/Kconfig" source "drivers/media/usb/cx231xx/Kconfig" source "drivers/media/usb/tm6000/Kconfig" endif diff --git a/drivers/media/usb/au0828/Kconfig b/drivers/media/usb/au0828/Kconfig index 1766c0ce93be..953a37c613b1 100644 --- a/drivers/media/usb/au0828/Kconfig +++ b/drivers/media/usb/au0828/Kconfig @@ -1,17 +1,28 @@ config VIDEO_AU0828 tristate "Auvitek AU0828 support" - depends on I2C && INPUT && DVB_CORE && USB && VIDEO_V4L2 + depends on I2C && INPUT && DVB_CORE && USB select I2C_ALGOBIT select VIDEO_TVEEPROM select VIDEOBUF_VMALLOC select DVB_AU8522_DTV if MEDIA_SUBDRV_AUTOSELECT - select DVB_AU8522_V4L if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT ---help--- - This is a video4linux driver for Auvitek's USB device. + This is a hybrid analog/digital tv capture driver for + Auvitek's AU0828 USB device. To compile this driver as a module, choose M here: the module will be called au0828 + +config VIDEO_AU0828_V4L2 + bool "Auvitek AU0828 v4l2 analog video support" + depends on VIDEO_AU0828 && VIDEO_V4L2 + select DVB_AU8522_V4L if MEDIA_SUBDRV_AUTOSELECT + default y + ---help--- + This is a video4linux driver for Auvitek's USB device. + + Choose Y here to include support for v4l2 analog video + capture within the au0828 driver. diff --git a/drivers/media/usb/au0828/Makefile b/drivers/media/usb/au0828/Makefile index 98cc20cc0ffb..be3bdf698022 100644 --- a/drivers/media/usb/au0828/Makefile +++ b/drivers/media/usb/au0828/Makefile @@ -1,4 +1,8 @@ -au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o au0828-video.o au0828-vbi.o +au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o + +ifeq ($(CONFIG_VIDEO_AU0828_V4L2),y) + au0828-objs += au0828-video.o au0828-vbi.o +endif obj-$(CONFIG_VIDEO_AU0828) += au0828.o diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c index cf309d8102c0..7b5b7420066d 100644 --- a/drivers/media/usb/au0828/au0828-cards.c +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -188,9 +188,11 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) void au0828_card_setup(struct au0828_dev *dev) { static u8 eeprom[256]; +#ifdef CONFIG_VIDEO_AU0828_V4L2 struct tuner_setup tun_setup; struct v4l2_subdev *sd; unsigned int mode_mask = T_ANALOG_TV; +#endif dprintk(1, "%s()\n", __func__); @@ -211,6 +213,7 @@ void au0828_card_setup(struct au0828_dev *dev) break; } +#ifdef CONFIG_VIDEO_AU0828_V4L2 if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) { /* Load the analog demodulator driver (note this would need to be abstracted out if we ever need to support a different @@ -236,6 +239,7 @@ void au0828_card_setup(struct au0828_dev *dev) v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); } +#endif } /* diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c index 745a80a798c8..1e6f40ef1c6b 100644 --- a/drivers/media/usb/au0828/au0828-core.c +++ b/drivers/media/usb/au0828/au0828-core.c @@ -134,13 +134,17 @@ static void au0828_usb_disconnect(struct usb_interface *interface) /* Digital TV */ au0828_dvb_unregister(dev); +#ifdef CONFIG_VIDEO_AU0828_V4L2 if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) au0828_analog_unregister(dev); +#endif /* I2C */ au0828_i2c_unregister(dev); +#ifdef CONFIG_VIDEO_AU0828_V4L2 v4l2_device_unregister(&dev->v4l2_dev); +#endif usb_set_intfdata(interface, NULL); @@ -155,7 +159,10 @@ static void au0828_usb_disconnect(struct usb_interface *interface) static int au0828_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { - int ifnum, retval; + int ifnum; +#ifdef CONFIG_VIDEO_AU0828_V4L2 + int retval; +#endif struct au0828_dev *dev; struct usb_device *usbdev = interface_to_usbdev(interface); @@ -194,6 +201,7 @@ static int au0828_usb_probe(struct usb_interface *interface, dev->usbdev = usbdev; dev->boardnr = id->driver_info; +#ifdef CONFIG_VIDEO_AU0828_V4L2 /* Create the v4l2_device */ retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); if (retval) { @@ -203,6 +211,7 @@ static int au0828_usb_probe(struct usb_interface *interface, kfree(dev); return -EIO; } +#endif /* Power Up the bridge */ au0828_write(dev, REG_600, 1 << 4); @@ -216,9 +225,11 @@ static int au0828_usb_probe(struct usb_interface *interface, /* Setup */ au0828_card_setup(dev); +#ifdef CONFIG_VIDEO_AU0828_V4L2 /* Analog TV */ if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) au0828_analog_register(dev, interface); +#endif /* Digital TV */ au0828_dvb_register(dev); diff --git a/drivers/media/usb/au0828/au0828-i2c.c b/drivers/media/usb/au0828/au0828-i2c.c index 4ded17fe1957..20d69b565255 100644 --- a/drivers/media/usb/au0828/au0828-i2c.c +++ b/drivers/media/usb/au0828/au0828-i2c.c @@ -378,7 +378,11 @@ int au0828_i2c_register(struct au0828_dev *dev) dev->i2c_adap.algo = &dev->i2c_algo; dev->i2c_adap.algo_data = dev; +#ifdef CONFIG_VIDEO_AU0828_V4L2 i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); +#else + i2c_set_adapdata(&dev->i2c_adap, dev); +#endif i2c_add_adapter(&dev->i2c_adap); dev->i2c_client.adapter = &dev->i2c_adap; diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h index 66a56ef7bbe4..e579ff69ca4a 100644 --- a/drivers/media/usb/au0828/au0828.h +++ b/drivers/media/usb/au0828/au0828.h @@ -199,8 +199,10 @@ struct au0828_dev { struct au0828_dvb dvb; struct work_struct restart_streaming; +#ifdef CONFIG_VIDEO_AU0828_V4L2 /* Analog */ struct v4l2_device v4l2_dev; +#endif int users; unsigned int resources; /* resources in use */ struct video_device *vdev; -- cgit v1.2.3 From 5b7d8de7d2328f7b25fe4645eafee7e48f9b7df3 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 4 Dec 2012 12:46:38 -0300 Subject: [media] au0828: break au0828_card_setup() down into smaller functions Pull the analog frontend setup code out of au0828_card_setup into its own seperate function, au0828_card_analog_fe_setup(). Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/au0828/au0828-cards.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c index 7b5b7420066d..88e35dfa33d4 100644 --- a/drivers/media/usb/au0828/au0828-cards.c +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -185,14 +185,11 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) __func__, tv.model); } +void au0828_card_analog_fe_setup(struct au0828_dev *dev); + void au0828_card_setup(struct au0828_dev *dev) { static u8 eeprom[256]; -#ifdef CONFIG_VIDEO_AU0828_V4L2 - struct tuner_setup tun_setup; - struct v4l2_subdev *sd; - unsigned int mode_mask = T_ANALOG_TV; -#endif dprintk(1, "%s()\n", __func__); @@ -213,7 +210,16 @@ void au0828_card_setup(struct au0828_dev *dev) break; } + au0828_card_analog_fe_setup(dev); +} + +void au0828_card_analog_fe_setup(struct au0828_dev *dev) +{ #ifdef CONFIG_VIDEO_AU0828_V4L2 + struct tuner_setup tun_setup; + struct v4l2_subdev *sd; + unsigned int mode_mask = T_ANALOG_TV; + if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) { /* Load the analog demodulator driver (note this would need to be abstracted out if we ever need to support a different -- cgit v1.2.3 From 55c171a6d90dc0574021f9c836127cfd1a7d2e30 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 14 Dec 2012 17:02:16 +0100 Subject: KVM: s390: Handle hosts not supporting s390-virtio. Running under a kvm host does not necessarily imply the presence of a page mapped above the main memory with the virtio information; however, the code includes a hard coded access to that page. Instead, check for the presence of the page and exit gracefully before we hit an addressing exception if it does not exist. Reviewed-by: Marcelo Tosatti Reviewed-by: Alexander Graf Signed-off-by: Cornelia Huck cc: stable@vger.kernel.org Signed-off-by: Gleb Natapov --- drivers/s390/kvm/kvm_virtio.c | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index 7dabef624da3..b846b6c4130a 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -421,6 +421,26 @@ static void kvm_extint_handler(struct ext_code ext_code, } } +/* + * For s390-virtio, we expect a page above main storage containing + * the virtio configuration. Try to actually load from this area + * in order to figure out if the host provides this page. + */ +static int __init test_devices_support(unsigned long addr) +{ + int ret = -EIO; + + asm volatile( + "0: lura 0,%1\n" + "1: xgr %0,%0\n" + "2:\n" + EX_TABLE(0b,2b) + EX_TABLE(1b,2b) + : "+d" (ret) + : "a" (addr) + : "0", "cc"); + return ret; +} /* * Init function for virtio * devices are in a single page above top of "normal" mem @@ -432,21 +452,23 @@ static int __init kvm_devices_init(void) if (!MACHINE_IS_KVM) return -ENODEV; + if (test_devices_support(real_memory_size) < 0) + return -ENODEV; + + rc = vmem_add_mapping(real_memory_size, PAGE_SIZE); + if (rc) + return rc; + + kvm_devices = (void *) real_memory_size; + kvm_root = root_device_register("kvm_s390"); if (IS_ERR(kvm_root)) { rc = PTR_ERR(kvm_root); printk(KERN_ERR "Could not register kvm_s390 root device"); + vmem_remove_mapping(real_memory_size, PAGE_SIZE); return rc; } - rc = vmem_add_mapping(real_memory_size, PAGE_SIZE); - if (rc) { - root_device_unregister(kvm_root); - return rc; - } - - kvm_devices = (void *) real_memory_size; - INIT_WORK(&hotplug_work, hotplug_devices); service_subclass_irq_register(); -- cgit v1.2.3 From 7e64e0597fd67c975bfa3e76401bfbcdd5ae0ff9 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 14 Dec 2012 17:02:18 +0100 Subject: KVM: s390: Add a channel I/O based virtio transport driver. Add a driver for kvm guests that matches virtual ccw devices provided by the host as virtio bridge devices. These virtio-ccw devices use a special set of channel commands in order to perform virtio functions. Reviewed-by: Marcelo Tosatti Reviewed-by: Alexander Graf Signed-off-by: Cornelia Huck Signed-off-by: Gleb Natapov --- arch/s390/include/asm/irq.h | 1 + arch/s390/kernel/irq.c | 1 + drivers/s390/kvm/Makefile | 2 +- drivers/s390/kvm/virtio_ccw.c | 853 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 856 insertions(+), 1 deletion(-) create mode 100644 drivers/s390/kvm/virtio_ccw.c (limited to 'drivers') diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index e6972f85d2b0..aa6d0d74cec9 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h @@ -35,6 +35,7 @@ enum interruption_class { IOINT_CSC, IOINT_PCI, IOINT_MSI, + IOINT_VIR, NMI_NMI, NR_IRQS, }; diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index bf24293970ce..a9806ea3ebd7 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -60,6 +60,7 @@ static const struct irq_class intrclass_names[] = { [IOINT_CSC] = {.name = "CSC", .desc = "[I/O] CHSC Subchannel"}, [IOINT_PCI] = {.name = "PCI", .desc = "[I/O] PCI Interrupt" }, [IOINT_MSI] = {.name = "MSI", .desc = "[I/O] MSI Interrupt" }, + [IOINT_VIR] = {.name = "VIR", .desc = "[I/O] Virtual I/O Devices"}, [NMI_NMI] = {.name = "NMI", .desc = "[NMI] Machine Check"}, }; diff --git a/drivers/s390/kvm/Makefile b/drivers/s390/kvm/Makefile index 0815690ac1e0..241891a57caf 100644 --- a/drivers/s390/kvm/Makefile +++ b/drivers/s390/kvm/Makefile @@ -6,4 +6,4 @@ # it under the terms of the GNU General Public License (version 2 only) # as published by the Free Software Foundation. -obj-$(CONFIG_S390_GUEST) += kvm_virtio.o +obj-$(CONFIG_S390_GUEST) += kvm_virtio.o virtio_ccw.o diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c new file mode 100644 index 000000000000..1a5aff31d752 --- /dev/null +++ b/drivers/s390/kvm/virtio_ccw.c @@ -0,0 +1,853 @@ +/* + * ccw based virtio transport + * + * Copyright IBM Corp. 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (version 2 only) + * as published by the Free Software Foundation. + * + * Author(s): Cornelia Huck + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * virtio related functions + */ + +struct vq_config_block { + __u16 index; + __u16 num; +} __packed; + +#define VIRTIO_CCW_CONFIG_SIZE 0x100 +/* same as PCI config space size, should be enough for all drivers */ + +struct virtio_ccw_device { + struct virtio_device vdev; + __u8 status; + __u8 config[VIRTIO_CCW_CONFIG_SIZE]; + struct ccw_device *cdev; + struct ccw1 *ccw; + __u32 area; + __u32 curr_io; + int err; + wait_queue_head_t wait_q; + spinlock_t lock; + struct list_head virtqueues; + unsigned long indicators; + unsigned long indicators2; + struct vq_config_block *config_block; +}; + +struct vq_info_block { + __u64 queue; + __u32 align; + __u16 index; + __u16 num; +} __packed; + +struct virtio_feature_desc { + __u32 features; + __u8 index; +} __packed; + +struct virtio_ccw_vq_info { + struct virtqueue *vq; + int num; + void *queue; + struct vq_info_block *info_block; + struct list_head node; +}; + +#define KVM_VIRTIO_CCW_RING_ALIGN 4096 + +#define KVM_S390_VIRTIO_CCW_NOTIFY 3 + +#define CCW_CMD_SET_VQ 0x13 +#define CCW_CMD_VDEV_RESET 0x33 +#define CCW_CMD_SET_IND 0x43 +#define CCW_CMD_SET_CONF_IND 0x53 +#define CCW_CMD_READ_FEAT 0x12 +#define CCW_CMD_WRITE_FEAT 0x11 +#define CCW_CMD_READ_CONF 0x22 +#define CCW_CMD_WRITE_CONF 0x21 +#define CCW_CMD_WRITE_STATUS 0x31 +#define CCW_CMD_READ_VQ_CONF 0x32 + +#define VIRTIO_CCW_DOING_SET_VQ 0x00010000 +#define VIRTIO_CCW_DOING_RESET 0x00040000 +#define VIRTIO_CCW_DOING_READ_FEAT 0x00080000 +#define VIRTIO_CCW_DOING_WRITE_FEAT 0x00100000 +#define VIRTIO_CCW_DOING_READ_CONFIG 0x00200000 +#define VIRTIO_CCW_DOING_WRITE_CONFIG 0x00400000 +#define VIRTIO_CCW_DOING_WRITE_STATUS 0x00800000 +#define VIRTIO_CCW_DOING_SET_IND 0x01000000 +#define VIRTIO_CCW_DOING_READ_VQ_CONF 0x02000000 +#define VIRTIO_CCW_DOING_SET_CONF_IND 0x04000000 +#define VIRTIO_CCW_INTPARM_MASK 0xffff0000 + +static struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev) +{ + return container_of(vdev, struct virtio_ccw_device, vdev); +} + +static int doing_io(struct virtio_ccw_device *vcdev, __u32 flag) +{ + unsigned long flags; + __u32 ret; + + spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags); + if (vcdev->err) + ret = 0; + else + ret = vcdev->curr_io & flag; + spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags); + return ret; +} + +static int ccw_io_helper(struct virtio_ccw_device *vcdev, __u32 intparm) +{ + int ret; + unsigned long flags; + int flag = intparm & VIRTIO_CCW_INTPARM_MASK; + + spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags); + ret = ccw_device_start(vcdev->cdev, vcdev->ccw, intparm, 0, 0); + if (!ret) + vcdev->curr_io |= flag; + spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags); + wait_event(vcdev->wait_q, doing_io(vcdev, flag) == 0); + return ret ? ret : vcdev->err; +} + +static inline long do_kvm_notify(struct subchannel_id schid, + unsigned long queue_index) +{ + register unsigned long __nr asm("1") = KVM_S390_VIRTIO_CCW_NOTIFY; + register struct subchannel_id __schid asm("2") = schid; + register unsigned long __index asm("3") = queue_index; + register long __rc asm("2"); + + asm volatile ("diag 2,4,0x500\n" + : "=d" (__rc) : "d" (__nr), "d" (__schid), "d" (__index) + : "memory", "cc"); + return __rc; +} + +static void virtio_ccw_kvm_notify(struct virtqueue *vq) +{ + struct virtio_ccw_vq_info *info = vq->priv; + struct virtio_ccw_device *vcdev; + struct subchannel_id schid; + + vcdev = to_vc_device(info->vq->vdev); + ccw_device_get_schid(vcdev->cdev, &schid); + do_kvm_notify(schid, virtqueue_get_queue_index(vq)); +} + +static int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev, int index) +{ + vcdev->config_block->index = index; + vcdev->ccw->cmd_code = CCW_CMD_READ_VQ_CONF; + vcdev->ccw->flags = 0; + vcdev->ccw->count = sizeof(struct vq_config_block); + vcdev->ccw->cda = (__u32)(unsigned long)(vcdev->config_block); + ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_VQ_CONF); + return vcdev->config_block->num; +} + +static void virtio_ccw_del_vq(struct virtqueue *vq) +{ + struct virtio_ccw_device *vcdev = to_vc_device(vq->vdev); + struct virtio_ccw_vq_info *info = vq->priv; + unsigned long flags; + unsigned long size; + int ret; + unsigned int index = virtqueue_get_queue_index(vq); + + /* Remove from our list. */ + spin_lock_irqsave(&vcdev->lock, flags); + list_del(&info->node); + spin_unlock_irqrestore(&vcdev->lock, flags); + + /* Release from host. */ + info->info_block->queue = 0; + info->info_block->align = 0; + info->info_block->index = index; + info->info_block->num = 0; + vcdev->ccw->cmd_code = CCW_CMD_SET_VQ; + vcdev->ccw->flags = 0; + vcdev->ccw->count = sizeof(*info->info_block); + vcdev->ccw->cda = (__u32)(unsigned long)(info->info_block); + ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_VQ | index); + /* + * -ENODEV isn't considered an error: The device is gone anyway. + * This may happen on device detach. + */ + if (ret && (ret != -ENODEV)) + dev_warn(&vq->vdev->dev, "Error %d while deleting queue %d", + ret, index); + + vring_del_virtqueue(vq); + size = PAGE_ALIGN(vring_size(info->num, KVM_VIRTIO_CCW_RING_ALIGN)); + free_pages_exact(info->queue, size); + kfree(info->info_block); + kfree(info); +} + +static void virtio_ccw_del_vqs(struct virtio_device *vdev) +{ + struct virtqueue *vq, *n; + + list_for_each_entry_safe(vq, n, &vdev->vqs, list) + virtio_ccw_del_vq(vq); +} + +static struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev, + int i, vq_callback_t *callback, + const char *name) +{ + struct virtio_ccw_device *vcdev = to_vc_device(vdev); + int err; + struct virtqueue *vq; + struct virtio_ccw_vq_info *info; + unsigned long size; + unsigned long flags; + + /* Allocate queue. */ + info = kzalloc(sizeof(struct virtio_ccw_vq_info), GFP_KERNEL); + if (!info) { + dev_warn(&vcdev->cdev->dev, "no info\n"); + err = -ENOMEM; + goto out_err; + } + info->info_block = kzalloc(sizeof(*info->info_block), + GFP_DMA | GFP_KERNEL); + if (!info->info_block) { + dev_warn(&vcdev->cdev->dev, "no info block\n"); + err = -ENOMEM; + goto out_err; + } + info->num = virtio_ccw_read_vq_conf(vcdev, i); + size = PAGE_ALIGN(vring_size(info->num, KVM_VIRTIO_CCW_RING_ALIGN)); + info->queue = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO); + if (info->queue == NULL) { + dev_warn(&vcdev->cdev->dev, "no queue\n"); + err = -ENOMEM; + goto out_err; + } + + vq = vring_new_virtqueue(i, info->num, KVM_VIRTIO_CCW_RING_ALIGN, vdev, + true, info->queue, virtio_ccw_kvm_notify, + callback, name); + if (!vq) { + /* For now, we fail if we can't get the requested size. */ + dev_warn(&vcdev->cdev->dev, "no vq\n"); + err = -ENOMEM; + free_pages_exact(info->queue, size); + goto out_err; + } + info->vq = vq; + vq->priv = info; + + /* Register it with the host. */ + info->info_block->queue = (__u64)info->queue; + info->info_block->align = KVM_VIRTIO_CCW_RING_ALIGN; + info->info_block->index = i; + info->info_block->num = info->num; + vcdev->ccw->cmd_code = CCW_CMD_SET_VQ; + vcdev->ccw->flags = 0; + vcdev->ccw->count = sizeof(*info->info_block); + vcdev->ccw->cda = (__u32)(unsigned long)(info->info_block); + err = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_VQ | i); + if (err) { + dev_warn(&vcdev->cdev->dev, "SET_VQ failed\n"); + free_pages_exact(info->queue, size); + info->vq = NULL; + vq->priv = NULL; + goto out_err; + } + + /* Save it to our list. */ + spin_lock_irqsave(&vcdev->lock, flags); + list_add(&info->node, &vcdev->virtqueues); + spin_unlock_irqrestore(&vcdev->lock, flags); + + return vq; + +out_err: + if (info) + kfree(info->info_block); + kfree(info); + return ERR_PTR(err); +} + +static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, + struct virtqueue *vqs[], + vq_callback_t *callbacks[], + const char *names[]) +{ + struct virtio_ccw_device *vcdev = to_vc_device(vdev); + unsigned long *indicatorp = NULL; + int ret, i; + + for (i = 0; i < nvqs; ++i) { + vqs[i] = virtio_ccw_setup_vq(vdev, i, callbacks[i], names[i]); + if (IS_ERR(vqs[i])) { + ret = PTR_ERR(vqs[i]); + vqs[i] = NULL; + goto out; + } + } + ret = -ENOMEM; + /* We need a data area under 2G to communicate. */ + indicatorp = kmalloc(sizeof(&vcdev->indicators), GFP_DMA | GFP_KERNEL); + if (!indicatorp) + goto out; + *indicatorp = (unsigned long) &vcdev->indicators; + /* Register queue indicators with host. */ + vcdev->indicators = 0; + vcdev->ccw->cmd_code = CCW_CMD_SET_IND; + vcdev->ccw->flags = 0; + vcdev->ccw->count = sizeof(vcdev->indicators); + vcdev->ccw->cda = (__u32)(unsigned long) indicatorp; + ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_IND); + if (ret) + goto out; + /* Register indicators2 with host for config changes */ + *indicatorp = (unsigned long) &vcdev->indicators2; + vcdev->indicators2 = 0; + vcdev->ccw->cmd_code = CCW_CMD_SET_CONF_IND; + vcdev->ccw->flags = 0; + vcdev->ccw->count = sizeof(vcdev->indicators2); + vcdev->ccw->cda = (__u32)(unsigned long) indicatorp; + ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_CONF_IND); + if (ret) + goto out; + + kfree(indicatorp); + return 0; +out: + kfree(indicatorp); + virtio_ccw_del_vqs(vdev); + return ret; +} + +static void virtio_ccw_reset(struct virtio_device *vdev) +{ + struct virtio_ccw_device *vcdev = to_vc_device(vdev); + + /* Zero status bits. */ + vcdev->status = 0; + + /* Send a reset ccw on device. */ + vcdev->ccw->cmd_code = CCW_CMD_VDEV_RESET; + vcdev->ccw->flags = 0; + vcdev->ccw->count = 0; + vcdev->ccw->cda = 0; + ccw_io_helper(vcdev, VIRTIO_CCW_DOING_RESET); +} + +static u32 virtio_ccw_get_features(struct virtio_device *vdev) +{ + struct virtio_ccw_device *vcdev = to_vc_device(vdev); + struct virtio_feature_desc features; + int ret; + + /* Read the feature bits from the host. */ + /* TODO: Features > 32 bits */ + features.index = 0; + vcdev->ccw->cmd_code = CCW_CMD_READ_FEAT; + vcdev->ccw->flags = 0; + vcdev->ccw->count = sizeof(features); + vcdev->ccw->cda = vcdev->area; + ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_FEAT); + if (ret) + return 0; + + memcpy(&features, (void *)(unsigned long)vcdev->area, + sizeof(features)); + return le32_to_cpu(features.features); +} + +static void virtio_ccw_finalize_features(struct virtio_device *vdev) +{ + struct virtio_ccw_device *vcdev = to_vc_device(vdev); + struct virtio_feature_desc features; + int i; + + /* Give virtio_ring a chance to accept features. */ + vring_transport_features(vdev); + + for (i = 0; i < sizeof(*vdev->features) / sizeof(features.features); + i++) { + int highbits = i % 2 ? 32 : 0; + features.index = i; + features.features = cpu_to_le32(vdev->features[i / 2] + >> highbits); + memcpy((void *)(unsigned long)vcdev->area, &features, + sizeof(features)); + /* Write the feature bits to the host. */ + vcdev->ccw->cmd_code = CCW_CMD_WRITE_FEAT; + vcdev->ccw->flags = 0; + vcdev->ccw->count = sizeof(features); + vcdev->ccw->cda = vcdev->area; + ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_FEAT); + } +} + +static void virtio_ccw_get_config(struct virtio_device *vdev, + unsigned int offset, void *buf, unsigned len) +{ + struct virtio_ccw_device *vcdev = to_vc_device(vdev); + int ret; + + /* Read the config area from the host. */ + vcdev->ccw->cmd_code = CCW_CMD_READ_CONF; + vcdev->ccw->flags = 0; + vcdev->ccw->count = offset + len; + vcdev->ccw->cda = vcdev->area; + ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_CONFIG); + if (ret) + return; + + memcpy(vcdev->config, (void *)(unsigned long)vcdev->area, + sizeof(vcdev->config)); + memcpy(buf, &vcdev->config[offset], len); +} + +static void virtio_ccw_set_config(struct virtio_device *vdev, + unsigned int offset, const void *buf, + unsigned len) +{ + struct virtio_ccw_device *vcdev = to_vc_device(vdev); + + memcpy(&vcdev->config[offset], buf, len); + /* Write the config area to the host. */ + memcpy((void *)(unsigned long)vcdev->area, vcdev->config, + sizeof(vcdev->config)); + vcdev->ccw->cmd_code = CCW_CMD_WRITE_CONF; + vcdev->ccw->flags = 0; + vcdev->ccw->count = offset + len; + vcdev->ccw->cda = vcdev->area; + ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_CONFIG); +} + +static u8 virtio_ccw_get_status(struct virtio_device *vdev) +{ + struct virtio_ccw_device *vcdev = to_vc_device(vdev); + + return vcdev->status; +} + +static void virtio_ccw_set_status(struct virtio_device *vdev, u8 status) +{ + struct virtio_ccw_device *vcdev = to_vc_device(vdev); + + /* Write the status to the host. */ + vcdev->status = status; + memcpy((void *)(unsigned long)vcdev->area, &status, sizeof(status)); + vcdev->ccw->cmd_code = CCW_CMD_WRITE_STATUS; + vcdev->ccw->flags = 0; + vcdev->ccw->count = sizeof(status); + vcdev->ccw->cda = vcdev->area; + ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_STATUS); +} + +static struct virtio_config_ops virtio_ccw_config_ops = { + .get_features = virtio_ccw_get_features, + .finalize_features = virtio_ccw_finalize_features, + .get = virtio_ccw_get_config, + .set = virtio_ccw_set_config, + .get_status = virtio_ccw_get_status, + .set_status = virtio_ccw_set_status, + .reset = virtio_ccw_reset, + .find_vqs = virtio_ccw_find_vqs, + .del_vqs = virtio_ccw_del_vqs, +}; + + +/* + * ccw bus driver related functions + */ + +static void virtio_ccw_release_dev(struct device *_d) +{ + struct virtio_device *dev = container_of(_d, struct virtio_device, + dev); + struct virtio_ccw_device *vcdev = to_vc_device(dev); + + kfree((void *)(unsigned long)vcdev->area); + kfree(vcdev->config_block); + kfree(vcdev->ccw); + kfree(vcdev); +} + +static int irb_is_error(struct irb *irb) +{ + if (scsw_cstat(&irb->scsw) != 0) + return 1; + if (scsw_dstat(&irb->scsw) & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) + return 1; + if (scsw_cc(&irb->scsw) != 0) + return 1; + return 0; +} + +static struct virtqueue *virtio_ccw_vq_by_ind(struct virtio_ccw_device *vcdev, + int index) +{ + struct virtio_ccw_vq_info *info; + unsigned long flags; + struct virtqueue *vq; + + vq = NULL; + spin_lock_irqsave(&vcdev->lock, flags); + list_for_each_entry(info, &vcdev->virtqueues, node) { + if (virtqueue_get_queue_index(info->vq) == index) { + vq = info->vq; + break; + } + } + spin_unlock_irqrestore(&vcdev->lock, flags); + return vq; +} + +static void virtio_ccw_int_handler(struct ccw_device *cdev, + unsigned long intparm, + struct irb *irb) +{ + __u32 activity = intparm & VIRTIO_CCW_INTPARM_MASK; + struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); + int i; + struct virtqueue *vq; + struct virtio_driver *drv; + + /* Check if it's a notification from the host. */ + if ((intparm == 0) && + (scsw_stctl(&irb->scsw) == + (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND))) { + /* OK */ + } + if (irb_is_error(irb)) + vcdev->err = -EIO; /* XXX - use real error */ + if (vcdev->curr_io & activity) { + switch (activity) { + case VIRTIO_CCW_DOING_READ_FEAT: + case VIRTIO_CCW_DOING_WRITE_FEAT: + case VIRTIO_CCW_DOING_READ_CONFIG: + case VIRTIO_CCW_DOING_WRITE_CONFIG: + case VIRTIO_CCW_DOING_WRITE_STATUS: + case VIRTIO_CCW_DOING_SET_VQ: + case VIRTIO_CCW_DOING_SET_IND: + case VIRTIO_CCW_DOING_SET_CONF_IND: + case VIRTIO_CCW_DOING_RESET: + case VIRTIO_CCW_DOING_READ_VQ_CONF: + vcdev->curr_io &= ~activity; + wake_up(&vcdev->wait_q); + break; + default: + /* don't know what to do... */ + dev_warn(&cdev->dev, "Suspicious activity '%08x'\n", + activity); + WARN_ON(1); + break; + } + } + for_each_set_bit(i, &vcdev->indicators, + sizeof(vcdev->indicators) * BITS_PER_BYTE) { + /* The bit clear must happen before the vring kick. */ + clear_bit(i, &vcdev->indicators); + barrier(); + vq = virtio_ccw_vq_by_ind(vcdev, i); + vring_interrupt(0, vq); + } + if (test_bit(0, &vcdev->indicators2)) { + drv = container_of(vcdev->vdev.dev.driver, + struct virtio_driver, driver); + + if (drv && drv->config_changed) + drv->config_changed(&vcdev->vdev); + clear_bit(0, &vcdev->indicators2); + } +} + +/* + * We usually want to autoonline all devices, but give the admin + * a way to exempt devices from this. + */ +#define __DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \ + (8*sizeof(long))) +static unsigned long devs_no_auto[__MAX_SSID + 1][__DEV_WORDS]; + +static char *no_auto = ""; + +module_param(no_auto, charp, 0444); +MODULE_PARM_DESC(no_auto, "list of ccw bus id ranges not to be auto-onlined"); + +static int virtio_ccw_check_autoonline(struct ccw_device *cdev) +{ + struct ccw_dev_id id; + + ccw_device_get_id(cdev, &id); + if (test_bit(id.devno, devs_no_auto[id.ssid])) + return 0; + return 1; +} + +static void virtio_ccw_auto_online(void *data, async_cookie_t cookie) +{ + struct ccw_device *cdev = data; + int ret; + + ret = ccw_device_set_online(cdev); + if (ret) + dev_warn(&cdev->dev, "Failed to set online: %d\n", ret); +} + +static int virtio_ccw_probe(struct ccw_device *cdev) +{ + cdev->handler = virtio_ccw_int_handler; + + if (virtio_ccw_check_autoonline(cdev)) + async_schedule(virtio_ccw_auto_online, cdev); + return 0; +} + +static void virtio_ccw_remove(struct ccw_device *cdev) +{ + struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); + + if (cdev->online) { + unregister_virtio_device(&vcdev->vdev); + dev_set_drvdata(&cdev->dev, NULL); + } + cdev->handler = NULL; +} + +static int virtio_ccw_offline(struct ccw_device *cdev) +{ + struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); + + unregister_virtio_device(&vcdev->vdev); + dev_set_drvdata(&cdev->dev, NULL); + return 0; +} + + +/* Area needs to be big enough to fit status, features or configuration. */ +#define VIRTIO_AREA_SIZE VIRTIO_CCW_CONFIG_SIZE /* biggest possible use */ + +static int virtio_ccw_online(struct ccw_device *cdev) +{ + int ret; + struct virtio_ccw_device *vcdev; + + vcdev = kzalloc(sizeof(*vcdev), GFP_KERNEL); + if (!vcdev) { + dev_warn(&cdev->dev, "Could not get memory for virtio\n"); + ret = -ENOMEM; + goto out_free; + } + vcdev->area = (__u32)(unsigned long)kzalloc(VIRTIO_AREA_SIZE, + GFP_DMA | GFP_KERNEL); + if (!vcdev->area) { + dev_warn(&cdev->dev, "Cound not get memory for virtio\n"); + ret = -ENOMEM; + goto out_free; + } + vcdev->config_block = kzalloc(sizeof(*vcdev->config_block), + GFP_DMA | GFP_KERNEL); + if (!vcdev->config_block) { + ret = -ENOMEM; + goto out_free; + } + vcdev->ccw = kzalloc(sizeof(*vcdev->ccw), GFP_DMA | GFP_KERNEL); + if (!vcdev->ccw) { + ret = -ENOMEM; + goto out_free; + } + + vcdev->vdev.dev.parent = &cdev->dev; + vcdev->vdev.dev.release = virtio_ccw_release_dev; + vcdev->vdev.config = &virtio_ccw_config_ops; + vcdev->cdev = cdev; + init_waitqueue_head(&vcdev->wait_q); + INIT_LIST_HEAD(&vcdev->virtqueues); + spin_lock_init(&vcdev->lock); + + dev_set_drvdata(&cdev->dev, vcdev); + vcdev->vdev.id.vendor = cdev->id.cu_type; + vcdev->vdev.id.device = cdev->id.cu_model; + ret = register_virtio_device(&vcdev->vdev); + if (ret) { + dev_warn(&cdev->dev, "Failed to register virtio device: %d\n", + ret); + goto out_put; + } + return 0; +out_put: + dev_set_drvdata(&cdev->dev, NULL); + put_device(&vcdev->vdev.dev); + return ret; +out_free: + if (vcdev) { + kfree((void *)(unsigned long)vcdev->area); + kfree(vcdev->config_block); + kfree(vcdev->ccw); + } + kfree(vcdev); + return ret; +} + +static int virtio_ccw_cio_notify(struct ccw_device *cdev, int event) +{ + /* TODO: Check whether we need special handling here. */ + return 0; +} + +static struct ccw_device_id virtio_ids[] = { + { CCW_DEVICE(0x3832, 0) }, + {}, +}; +MODULE_DEVICE_TABLE(ccw, virtio_ids); + +static struct ccw_driver virtio_ccw_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "virtio_ccw", + }, + .ids = virtio_ids, + .probe = virtio_ccw_probe, + .remove = virtio_ccw_remove, + .set_offline = virtio_ccw_offline, + .set_online = virtio_ccw_online, + .notify = virtio_ccw_cio_notify, + .int_class = IOINT_VIR, +}; + +static int __init pure_hex(char **cp, unsigned int *val, int min_digit, + int max_digit, int max_val) +{ + int diff; + + diff = 0; + *val = 0; + + while (diff <= max_digit) { + int value = hex_to_bin(**cp); + + if (value < 0) + break; + *val = *val * 16 + value; + (*cp)++; + diff++; + } + + if ((diff < min_digit) || (diff > max_digit) || (*val > max_val)) + return 1; + + return 0; +} + +static int __init parse_busid(char *str, unsigned int *cssid, + unsigned int *ssid, unsigned int *devno) +{ + char *str_work; + int rc, ret; + + rc = 1; + + if (*str == '\0') + goto out; + + str_work = str; + ret = pure_hex(&str_work, cssid, 1, 2, __MAX_CSSID); + if (ret || (str_work[0] != '.')) + goto out; + str_work++; + ret = pure_hex(&str_work, ssid, 1, 1, __MAX_SSID); + if (ret || (str_work[0] != '.')) + goto out; + str_work++; + ret = pure_hex(&str_work, devno, 4, 4, __MAX_SUBCHANNEL); + if (ret || (str_work[0] != '\0')) + goto out; + + rc = 0; +out: + return rc; +} + +static void __init no_auto_parse(void) +{ + unsigned int from_cssid, to_cssid, from_ssid, to_ssid, from, to; + char *parm, *str; + int rc; + + str = no_auto; + while ((parm = strsep(&str, ","))) { + rc = parse_busid(strsep(&parm, "-"), &from_cssid, + &from_ssid, &from); + if (rc) + continue; + if (parm != NULL) { + rc = parse_busid(parm, &to_cssid, + &to_ssid, &to); + if ((from_ssid > to_ssid) || + ((from_ssid == to_ssid) && (from > to))) + rc = -EINVAL; + } else { + to_cssid = from_cssid; + to_ssid = from_ssid; + to = from; + } + if (rc) + continue; + while ((from_ssid < to_ssid) || + ((from_ssid == to_ssid) && (from <= to))) { + set_bit(from, devs_no_auto[from_ssid]); + from++; + if (from > __MAX_SUBCHANNEL) { + from_ssid++; + from = 0; + } + } + } +} + +static int __init virtio_ccw_init(void) +{ + /* parse no_auto string before we do anything further */ + no_auto_parse(); + return ccw_driver_register(&virtio_ccw_driver); +} +module_init(virtio_ccw_init); + +static void __exit virtio_ccw_exit(void) +{ + ccw_driver_unregister(&virtio_ccw_driver); +} +module_exit(virtio_ccw_exit); -- cgit v1.2.3 From 4d7bb01162b768ca0f7e50aa3f31790d8d08402c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 18 Dec 2012 15:24:37 +0100 Subject: drm/i915: fixup overlay stolen memory leak We need to clean up the overlay first, before taking down the stolen memory allocator. This regression has been introducec in commit 8040513870399f1cb032cb8bc805df5042fedcdf Author: Chris Wilson Date: Thu Nov 15 11:32:29 2012 +0000 drm/i915: Allocate overlay registers from stolen memory v2: Rework the patch a bit as suggested by Chris Wilson: - move the overlay teardown up, into the modeset cleanup - move the stolen mm takedown into i915_gem_cleanup_stolen Cc: Chris Wilson Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 3 --- drivers/gpu/drm/i915/i915_gem_stolen.c | 3 +++ drivers/gpu/drm/i915/intel_display.c | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index a99c46d1e49a..272e500c920e 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1737,9 +1737,6 @@ int i915_driver_unload(struct drm_device *dev) mutex_unlock(&dev->struct_mutex); i915_gem_cleanup_aliasing_ppgtt(dev); i915_gem_cleanup_stolen(dev); - drm_mm_takedown(&dev_priv->mm.stolen); - - intel_cleanup_overlay(dev); if (!I915_NEED_GFX_HWS(dev)) i915_free_hws(dev); diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index f817b0cac116..f21ae17e298f 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -173,7 +173,10 @@ void i915_gem_stolen_cleanup_compression(struct drm_device *dev) void i915_gem_cleanup_stolen(struct drm_device *dev) { + struct drm_i915_private *dev_priv = dev->dev_private; + i915_gem_stolen_cleanup_compression(dev); + drm_mm_takedown(&dev_priv->mm.stolen); } int i915_gem_init_stolen(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1bb68169aaf0..f7f4ef17cbe4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8869,6 +8869,8 @@ void intel_modeset_cleanup(struct drm_device *dev) flush_scheduled_work(); drm_mode_config_cleanup(dev); + + intel_cleanup_overlay(dev); } /* -- cgit v1.2.3 From 0176fd4d25d684ea38014b8e2c3221790d5c94d8 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 16 Dec 2012 13:24:59 -0300 Subject: [media] tda10071: add tuner_i2c_addr to struct tda10071_config The default i2c address for the tuner is 0x14, allow this to be overridden with a configuration parameter Signed-off-by: Michael Krufky Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/tda10071.c | 2 +- drivers/media/dvb-frontends/tda10071.h | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 16a4bc54dbe7..710362916f83 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -1064,7 +1064,7 @@ static int tda10071_init(struct dvb_frontend *fe) cmd.args[2] = 0x00; cmd.args[3] = 0x00; cmd.args[4] = 0x00; - cmd.args[5] = 0x14; + cmd.args[5] = (priv->cfg.tuner_i2c_addr) ? priv->cfg.tuner_i2c_addr : 0x14; cmd.args[6] = 0x00; cmd.args[7] = 0x03; cmd.args[8] = 0x02; diff --git a/drivers/media/dvb-frontends/tda10071.h b/drivers/media/dvb-frontends/tda10071.h index 21163c4b555c..a20d5c41e048 100644 --- a/drivers/media/dvb-frontends/tda10071.h +++ b/drivers/media/dvb-frontends/tda10071.h @@ -30,6 +30,12 @@ struct tda10071_config { */ u8 i2c_address; + /* Tuner I2C address. + * Default: 0x14 + * Values: 0x14, 0x54, ... + */ + u8 tuner_i2c_addr; + /* Max bytes I2C provider can write at once. * Note: Buffer is taken from the stack currently! * Default: none, must set -- cgit v1.2.3 From 7c62f5a11c2c7f5a6a93a41d5fb1084ebd125d9a Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 15 Dec 2012 23:34:09 -0300 Subject: [media] cx23885: add basic DVB-S2 support for Hauppauge HVR-4400 Add basic DVB-S2 support for the Hauppauge HVR-4400 PCIe board. Thanks to Antti Palosaari and Devin Heitmueller for their suggestions and testing. Signed-off-by: Michael Krufky Reviewed-by: Devin Heitmueller Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx23885/Kconfig | 2 ++ drivers/media/pci/cx23885/cx23885-cards.c | 38 ++++++++++++++++++++++++++++++- drivers/media/pci/cx23885/cx23885-dvb.c | 27 ++++++++++++++++++++++ drivers/media/pci/cx23885/cx23885.h | 1 + 4 files changed, 67 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig index eafa1144b17d..733d6c8ebe4c 100644 --- a/drivers/media/pci/cx23885/Kconfig +++ b/drivers/media/pci/cx23885/Kconfig @@ -26,6 +26,8 @@ config VIDEO_CX23885 select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10071 if MEDIA_SUBDRV_AUTOSELECT + select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2063 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index 6277e145f0b8..7a79a17105c4 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -572,7 +572,11 @@ struct cx23885_board cx23885_boards[] = { [CX23885_BOARD_PROF_8000] = { .name = "Prof Revolution DVB-S2 8000", .portb = CX23885_MPEG_DVB, - } + }, + [CX23885_BOARD_HAUPPAUGE_HVR4400] = { + .name = "Hauppauge WinTV-HVR4400", + .portb = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -788,6 +792,22 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x8000, .subdevice = 0x3034, .card = CX23885_BOARD_PROF_8000, + }, { + .subvendor = 0x0070, + .subdevice = 0xc108, + .card = CX23885_BOARD_HAUPPAUGE_HVR4400, + }, { + .subvendor = 0x0070, + .subdevice = 0xc138, + .card = CX23885_BOARD_HAUPPAUGE_HVR4400, + }, { + .subvendor = 0x0070, + .subdevice = 0xc12a, + .card = CX23885_BOARD_HAUPPAUGE_HVR4400, + }, { + .subvendor = 0x0070, + .subdevice = 0xc1f8, + .card = CX23885_BOARD_HAUPPAUGE_HVR4400, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -1301,6 +1321,16 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* enable irq */ cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/ break; + case CX23885_BOARD_HAUPPAUGE_HVR4400: + /* GPIO-8 tda10071 demod reset */ + + /* Put the parts into reset and back */ + cx23885_gpio_enable(dev, GPIO_8, 1); + cx23885_gpio_clear(dev, GPIO_8); + mdelay(100); + cx23885_gpio_set(dev, GPIO_8); + mdelay(100); + break; } } @@ -1509,6 +1539,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1210: case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: + case CX23885_BOARD_HAUPPAUGE_HVR4400: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xc0); break; @@ -1581,6 +1612,11 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; + case CX23885_BOARD_HAUPPAUGE_HVR4400: + ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 2f5b902e63ae..cf84c534f005 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -66,6 +66,8 @@ #include "stv090x.h" #include "stb6100.h" #include "stb6100_cfg.h" +#include "tda10071.h" +#include "a8293.h" static unsigned int debug; @@ -659,6 +661,20 @@ static struct mt2063_config terratec_mt2063_config[] = { }, }; +static const struct tda10071_config hauppauge_tda10071_config = { + .i2c_address = 0x05, + .tuner_i2c_addr = 0x54, + .i2c_wr_max = 64, + .ts_mode = TDA10071_TS_SERIAL, + .spec_inv = 0, + .xtal = 40444000, /* 40.444 MHz */ + .pll_multiplier = 20, +}; + +static const struct a8293_config hauppauge_a8293_config = { + .i2c_addr = 0x0b, +}; + static int netup_altera_fpga_rw(void *device, int flag, int data, int read) { struct cx23885_dev *dev = (struct cx23885_dev *)device; @@ -1242,6 +1258,17 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend->ops.set_voltage = p8000_set_voltage; } break; + case CX23885_BOARD_HAUPPAUGE_HVR4400: + i2c_bus = &dev->i2c_bus[0]; + fe0->dvb.frontend = dvb_attach(tda10071_attach, + &hauppauge_tda10071_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(a8293_attach, fe0->dvb.frontend, + &i2c_bus->i2c_adap, + &hauppauge_a8293_config); + } + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h index 67f40d31450b..61889b25a2af 100644 --- a/drivers/media/pci/cx23885/cx23885.h +++ b/drivers/media/pci/cx23885/cx23885.h @@ -91,6 +91,7 @@ #define CX23885_BOARD_TEVII_S471 35 #define CX23885_BOARD_HAUPPAUGE_HVR1255_22111 36 #define CX23885_BOARD_PROF_8000 37 +#define CX23885_BOARD_HAUPPAUGE_HVR4400 38 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 -- cgit v1.2.3 From 4e791048e0ae2ea5fe8dbbb0a4898c9e680bc643 Mon Sep 17 00:00:00 2001 From: Frank Schäfer Date: Tue, 4 Dec 2012 16:13:33 -0300 Subject: [media] tda18271: add missing entries for qam_7 to tda18271_update_std_map() and tda18271_dump_std_map() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/tda18271-fe.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/media/tuners/tda18271-fe.c b/drivers/media/tuners/tda18271-fe.c index 72c26fd77922..e7786862dab2 100644 --- a/drivers/media/tuners/tda18271-fe.c +++ b/drivers/media/tuners/tda18271-fe.c @@ -1122,6 +1122,7 @@ static int tda18271_dump_std_map(struct dvb_frontend *fe) tda18271_dump_std_item(dvbt_7, "dvbt 7"); tda18271_dump_std_item(dvbt_8, "dvbt 8"); tda18271_dump_std_item(qam_6, "qam 6 "); + tda18271_dump_std_item(qam_7, "qam 7 "); tda18271_dump_std_item(qam_8, "qam 8 "); return 0; @@ -1149,6 +1150,7 @@ static int tda18271_update_std_map(struct dvb_frontend *fe, tda18271_update_std(dvbt_7, "dvbt 7"); tda18271_update_std(dvbt_8, "dvbt 8"); tda18271_update_std(qam_6, "qam 6"); + tda18271_update_std(qam_7, "qam 7"); tda18271_update_std(qam_8, "qam 8"); return 0; -- cgit v1.2.3 From 7dbf9d6e0fd8d42398955de119ccba6c35813c88 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 18 Dec 2012 10:31:22 -0800 Subject: drm/i915: BUG() if fences are used on unsupported platform Signed-off-by: Ben Widawsky Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 2 +- drivers/gpu/drm/i915/i915_irq.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index d15c86279d02..ad03dea210bd 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2649,7 +2649,7 @@ static void i915_gem_write_fence(struct drm_device *dev, int reg, case 4: i965_write_fence_reg(dev, reg, obj); break; case 3: i915_write_fence_reg(dev, reg, obj); break; case 2: i830_write_fence_reg(dev, reg, obj); break; - default: break; + default: BUG(); } } diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 551f370657b7..6ba0573e7f16 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1106,6 +1106,8 @@ static void i915_gem_record_fences(struct drm_device *dev, error->fence[i] = I915_READ(FENCE_REG_830_0 + (i * 4)); break; + default: + BUG(); } } -- cgit v1.2.3 From 8782e26c0cf3444a0d11400277bb44de710a0142 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 18 Dec 2012 10:31:23 -0800 Subject: drm/i915: Bug on unsupported swizzled platforms Signed-off-by: Ben Widawsky Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index ad03dea210bd..6685861cd176 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3897,8 +3897,10 @@ void i915_gem_init_swizzling(struct drm_device *dev) I915_WRITE(TILECTL, I915_READ(TILECTL) | TILECTL_SWZCTL); if (IS_GEN6(dev)) I915_WRITE(ARB_MODE, _MASKED_BIT_ENABLE(ARB_MODE_SWIZZLE_SNB)); - else + else if (IS_GEN7(dev)) I915_WRITE(ARB_MODE, _MASKED_BIT_ENABLE(ARB_MODE_SWIZZLE_IVB)); + else + BUG(); } static bool -- cgit v1.2.3 From 079a43f67f41274b137d8be1fb6093c8f6b6a762 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 18 Dec 2012 10:31:24 -0800 Subject: drm/i915: Missed conversion to gtt_pte_t commit f61c0609073133375ace61f74b0e4e7cf631406b Author: Ben Widawsky Date: Mon Oct 22 11:44:43 2012 -0700 drm/i915: introduce gtt_pte_t Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index cc1be53a65f9..ea646b4c312a 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -282,7 +282,7 @@ void i915_gem_init_ppgtt(struct drm_device *dev) uint32_t pd_offset; struct intel_ring_buffer *ring; struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt; - uint32_t __iomem *pd_addr; + gtt_pte_t __iomem *pd_addr; uint32_t pd_entry; int i; @@ -290,7 +290,7 @@ void i915_gem_init_ppgtt(struct drm_device *dev) return; - pd_addr = dev_priv->mm.gtt->gtt + ppgtt->pd_offset/sizeof(uint32_t); + pd_addr = dev_priv->mm.gtt->gtt + ppgtt->pd_offset/sizeof(gtt_pte_t); for (i = 0; i < ppgtt->num_pd_entries; i++) { dma_addr_t pt_addr; -- cgit v1.2.3 From b70ec5bf439b35a18e702f88078d393261c3e3f2 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Wed, 19 Dec 2012 11:13:05 +0200 Subject: drm/i915: Introduce ring set_seqno In preparation for setting per ring initial seqno values add ring::set_seqno(). Signed-off-by: Mika Kuoppala Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ringbuffer.c | 20 ++++++++++++++++++++ drivers/gpu/drm/i915/intel_ringbuffer.h | 9 +++++++++ 2 files changed, 29 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 69bbe7bb9f6d..f536a9951ab3 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -727,6 +727,12 @@ ring_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency) return intel_read_status_page(ring, I915_GEM_HWS_INDEX); } +static void +ring_set_seqno(struct intel_ring_buffer *ring, u32 seqno) +{ + intel_write_status_page(ring, I915_GEM_HWS_INDEX, seqno); +} + static u32 pc_render_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency) { @@ -734,6 +740,13 @@ pc_render_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency) return pc->cpu_page[0]; } +static void +pc_render_set_seqno(struct intel_ring_buffer *ring, u32 seqno) +{ + struct pipe_control *pc = ring->private; + pc->cpu_page[0] = seqno; +} + static bool gen5_ring_get_irq(struct intel_ring_buffer *ring) { @@ -1602,6 +1615,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev) ring->irq_put = gen6_ring_put_irq; ring->irq_enable_mask = GT_USER_INTERRUPT; ring->get_seqno = gen6_ring_get_seqno; + ring->set_seqno = ring_set_seqno; ring->sync_to = gen6_ring_sync; ring->semaphore_register[0] = MI_SEMAPHORE_SYNC_INVALID; ring->semaphore_register[1] = MI_SEMAPHORE_SYNC_RV; @@ -1612,6 +1626,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev) ring->add_request = pc_render_add_request; ring->flush = gen4_render_ring_flush; ring->get_seqno = pc_render_get_seqno; + ring->set_seqno = pc_render_set_seqno; ring->irq_get = gen5_ring_get_irq; ring->irq_put = gen5_ring_put_irq; ring->irq_enable_mask = GT_USER_INTERRUPT | GT_PIPE_NOTIFY; @@ -1622,6 +1637,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev) else ring->flush = gen4_render_ring_flush; ring->get_seqno = ring_get_seqno; + ring->set_seqno = ring_set_seqno; if (IS_GEN2(dev)) { ring->irq_get = i8xx_ring_get_irq; ring->irq_put = i8xx_ring_put_irq; @@ -1672,6 +1688,7 @@ int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size) else ring->flush = gen4_render_ring_flush; ring->get_seqno = ring_get_seqno; + ring->set_seqno = ring_set_seqno; if (IS_GEN2(dev)) { ring->irq_get = i8xx_ring_get_irq; ring->irq_put = i8xx_ring_put_irq; @@ -1732,6 +1749,7 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev) ring->flush = gen6_ring_flush; ring->add_request = gen6_add_request; ring->get_seqno = gen6_ring_get_seqno; + ring->set_seqno = ring_set_seqno; ring->irq_enable_mask = GEN6_BSD_USER_INTERRUPT; ring->irq_get = gen6_ring_get_irq; ring->irq_put = gen6_ring_put_irq; @@ -1747,6 +1765,7 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev) ring->flush = bsd_ring_flush; ring->add_request = i9xx_add_request; ring->get_seqno = ring_get_seqno; + ring->set_seqno = ring_set_seqno; if (IS_GEN5(dev)) { ring->irq_enable_mask = GT_BSD_USER_INTERRUPT; ring->irq_get = gen5_ring_get_irq; @@ -1776,6 +1795,7 @@ int intel_init_blt_ring_buffer(struct drm_device *dev) ring->flush = blt_ring_flush; ring->add_request = gen6_add_request; ring->get_seqno = gen6_ring_get_seqno; + ring->set_seqno = ring_set_seqno; ring->irq_enable_mask = GEN6_BLITTER_USER_INTERRUPT; ring->irq_get = gen6_ring_get_irq; ring->irq_put = gen6_ring_put_irq; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index b4a533e53fd1..4a7cd67742da 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -79,6 +79,8 @@ struct intel_ring_buffer { */ u32 (*get_seqno)(struct intel_ring_buffer *ring, bool lazy_coherency); + void (*set_seqno)(struct intel_ring_buffer *ring, + u32 seqno); int (*dispatch_execbuffer)(struct intel_ring_buffer *ring, u32 offset, u32 length, unsigned flags); @@ -166,6 +168,13 @@ intel_read_status_page(struct intel_ring_buffer *ring, return ring->status_page.page_addr[reg]; } +static inline void +intel_write_status_page(struct intel_ring_buffer *ring, + int reg, u32 value) +{ + ring->status_page.page_addr[reg] = value; +} + /** * Reads a dword out of the status page, which is written to from the command * queue by automatic updates, MI_REPORT_HEAD, MI_STORE_DATA_INDEX, or -- cgit v1.2.3 From f7e98ad4d4a8afa043126a6f24d0a154a684e081 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Wed, 19 Dec 2012 11:13:06 +0200 Subject: drm/i915: Initialize hardware semaphore state on ring init Hardware status page needs to have proper seqno set as our initial seqno can be arbitrary. If initial seqno is close to wrap boundary on init and i915_seqno_passed() (31bit space) refers to hw status page which contains zero, errorneous result will be returned. v2: clear mboxes and set hws page directly instead of going through rings. Suggested by Chris Wilson. v3: hws needs to be updated for all gens. Noticed by Chris Wilson. References: https://bugs.freedesktop.org/show_bug.cgi?id=58230 Signed-off-by: Mika Kuoppala Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 8 +++----- drivers/gpu/drm/i915/intel_ringbuffer.c | 24 +++++++++--------------- drivers/gpu/drm/i915/intel_ringbuffer.h | 2 +- 3 files changed, 13 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 6685861cd176..51282b2c4943 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1954,9 +1954,7 @@ i915_gem_handle_seqno_wrap(struct drm_device *dev) /* Finally reset hw state */ for_each_ring(ring, dev_priv, i) { - ret = intel_ring_handle_seqno_wrap(ring); - if (ret) - return ret; + intel_ring_init_seqno(ring, 0); for (j = 0; j < ARRAY_SIZE(ring->sync_seqno); j++) ring->sync_seqno[j] = 0; @@ -3935,6 +3933,8 @@ i915_gem_init_hw(struct drm_device *dev) i915_gem_init_swizzling(dev); + dev_priv->next_seqno = dev_priv->last_seqno = (u32)~0 - 0x1000; + ret = intel_init_render_ring_buffer(dev); if (ret) return ret; @@ -3951,8 +3951,6 @@ i915_gem_init_hw(struct drm_device *dev) goto cleanup_bsd_ring; } - dev_priv->next_seqno = (u32)-1 - 0x1000; - /* * XXX: There was some w/a described somewhere suggesting loading * contexts before PPGTT. diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index f536a9951ab3..2bd074ad6f54 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1184,6 +1184,8 @@ static int intel_init_ring_buffer(struct drm_device *dev, if (IS_I830(ring->dev) || IS_845G(ring->dev)) ring->effective_size -= 128; + intel_ring_init_seqno(ring, dev_priv->last_seqno); + return 0; err_unmap: @@ -1431,26 +1433,18 @@ int intel_ring_begin(struct intel_ring_buffer *ring, return __intel_ring_begin(ring, num_dwords * sizeof(uint32_t)); } -int intel_ring_handle_seqno_wrap(struct intel_ring_buffer *ring) +void intel_ring_init_seqno(struct intel_ring_buffer *ring, u32 seqno) { - int ret; + struct drm_i915_private *dev_priv = ring->dev->dev_private; BUG_ON(ring->outstanding_lazy_request); - if (INTEL_INFO(ring->dev)->gen < 6) - return 0; - - ret = __intel_ring_begin(ring, 6 * sizeof(uint32_t)); - if (ret) - return ret; - - /* Leaving a stale, pre-wrap seqno behind in the mboxes will result in - * post-wrap semaphore waits completing immediately. Clear them. */ - update_mboxes(ring, ring->signal_mbox[0]); - update_mboxes(ring, ring->signal_mbox[1]); - intel_ring_advance(ring); + if (INTEL_INFO(ring->dev)->gen >= 6) { + I915_WRITE(RING_SYNC_0(ring->mmio_base), 0); + I915_WRITE(RING_SYNC_1(ring->mmio_base), 0); + } - return 0; + ring->set_seqno(ring, seqno); } void intel_ring_advance(struct intel_ring_buffer *ring) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 4a7cd67742da..e7b9a6aac955 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -205,7 +205,7 @@ static inline void intel_ring_emit(struct intel_ring_buffer *ring, } void intel_ring_advance(struct intel_ring_buffer *ring); int __must_check intel_ring_idle(struct intel_ring_buffer *ring); -int __must_check intel_ring_handle_seqno_wrap(struct intel_ring_buffer *ring); +void intel_ring_init_seqno(struct intel_ring_buffer *ring, u32 seqno); int intel_ring_flush_all_caches(struct intel_ring_buffer *ring); int intel_ring_invalidate_all_caches(struct intel_ring_buffer *ring); -- cgit v1.2.3 From ba1a7067c00f4cbdd99b00a570ff072639ae59f2 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Wed, 19 Dec 2012 11:13:07 +0200 Subject: drm/i915: Always clear semaphore mboxes on seqno wrap In preparation for setting the seqno to arbitrary value on init or through debugfs. We need to always clear the semaphores and set the hws page seqno index by calling intel_ring_init_seqno(). v2: rewrote the commit message as suggested by Chris Wilson. Signed-off-by: Mika Kuoppala Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 51282b2c4943..bb38bca02559 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1932,18 +1932,6 @@ i915_gem_handle_seqno_wrap(struct drm_device *dev) struct intel_ring_buffer *ring; int ret, i, j; - /* The hardware uses various monotonic 32-bit counters, if we - * detect that they will wraparound we need to idle the GPU - * and reset those counters. - */ - ret = 0; - for_each_ring(ring, dev_priv, i) { - for (j = 0; j < ARRAY_SIZE(ring->sync_seqno); j++) - ret |= ring->sync_seqno[j] != 0; - } - if (ret == 0) - return ret; - /* Carefully retire all requests without writing to the rings */ for_each_ring(ring, dev_priv, i) { ret = intel_ring_idle(ring); -- cgit v1.2.3 From fca26bb45375266d9d9b8b4b57fee905ac38fe3c Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Wed, 19 Dec 2012 11:13:08 +0200 Subject: drm/i915: Introduce i915_gem_set_seqno() This function can be used to set the driver's next_seqno to arbitrary value. i915_gem_set_seqno() will idle the gpu, retire outstanding requests, clear the semaphore mailboxes and set the hardware status page's seqno index. Signed-off-by: Mika Kuoppala Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 4 ++-- drivers/gpu/drm/i915/i915_gem.c | 32 +++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 514aee895ecb..b0faa9149a1a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1479,8 +1479,8 @@ i915_seqno_passed(uint32_t seq1, uint32_t seq2) return (int32_t)(seq1 - seq2) >= 0; } -extern int i915_gem_get_seqno(struct drm_device *dev, u32 *seqno); - +int __must_check i915_gem_get_seqno(struct drm_device *dev, u32 *seqno); +int __must_check i915_gem_set_seqno(struct drm_device *dev, u32 seqno); int __must_check i915_gem_object_get_fence(struct drm_i915_gem_object *obj); int __must_check i915_gem_object_put_fence(struct drm_i915_gem_object *obj); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index bb38bca02559..23b883a135db 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1926,7 +1926,7 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj) } static int -i915_gem_handle_seqno_wrap(struct drm_device *dev) +i915_gem_init_seqno(struct drm_device *dev, u32 seqno) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_ring_buffer *ring; @@ -1942,7 +1942,7 @@ i915_gem_handle_seqno_wrap(struct drm_device *dev) /* Finally reset hw state */ for_each_ring(ring, dev_priv, i) { - intel_ring_init_seqno(ring, 0); + intel_ring_init_seqno(ring, seqno); for (j = 0; j < ARRAY_SIZE(ring->sync_seqno); j++) ring->sync_seqno[j] = 0; @@ -1951,6 +1951,32 @@ i915_gem_handle_seqno_wrap(struct drm_device *dev) return 0; } +int i915_gem_set_seqno(struct drm_device *dev, u32 seqno) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int ret; + + if (seqno == 0) + return -EINVAL; + + /* HWS page needs to be set less than what we + * will inject to ring + */ + ret = i915_gem_init_seqno(dev, seqno - 1); + if (ret) + return ret; + + /* Carefully set the last_seqno value so that wrap + * detection still works + */ + dev_priv->next_seqno = seqno; + dev_priv->last_seqno = seqno - 1; + if (dev_priv->last_seqno == 0) + dev_priv->last_seqno--; + + return 0; +} + int i915_gem_get_seqno(struct drm_device *dev, u32 *seqno) { @@ -1958,7 +1984,7 @@ i915_gem_get_seqno(struct drm_device *dev, u32 *seqno) /* reserve 0 for non-seqno */ if (dev_priv->next_seqno == 0) { - int ret = i915_gem_handle_seqno_wrap(dev); + int ret = i915_gem_init_seqno(dev, 0); if (ret) return ret; -- cgit v1.2.3 From e94fbaa8750a8f20c14718633764fba2e6755825 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Wed, 19 Dec 2012 11:13:09 +0200 Subject: drm/i915: Make next_seqno debugs entry to use i915_gem_set_seqno This debugs entry can be used to set arbitrary value to next_seqno. Use i915_gem_set_seqno instead of poking next_seqno. v2: nasty details of next_seqno and last_seqno handling moved inside i915_gem_set_seqno as suggested by Chris Wilson. Signed-off-by: Mika Kuoppala Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 7047c4a9fb9e..882a7352b9b9 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -878,7 +878,6 @@ i915_next_seqno_write(struct file *filp, loff_t *ppos) { struct drm_device *dev = filp->private_data; - drm_i915_private_t *dev_priv = dev->dev_private; char buf[20]; u32 val = 1; int ret; @@ -896,19 +895,11 @@ i915_next_seqno_write(struct file *filp, return ret; } - if (val == 0) - return -EINVAL; - ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) return ret; - if (i915_seqno_passed(val, dev_priv->next_seqno)) { - dev_priv->next_seqno = val; - DRM_DEBUG_DRIVER("Advancing seqno to %u\n", val); - } else { - ret = -EINVAL; - } + ret = i915_gem_set_seqno(dev, val); mutex_unlock(&dev->struct_mutex); -- cgit v1.2.3 From 71d5e7af12894497882b0497f331b6a2b6c97d23 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 13 Nov 2012 10:35:38 -0300 Subject: [media] omap_vout: Drop overlay format enumeration Enumerating formats for output overlays doesn't make sense, as the pixel format is defined by the display API, not the V4L2 API. Drop the vidioc_enum_fmt_vid_overlay ioctl operation. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap/omap_vout.c | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 837cb6db747f..90f890875b8b 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -1232,21 +1232,6 @@ static int vidioc_s_fmt_vid_overlay(struct file *file, void *fh, return ret; } -static int vidioc_enum_fmt_vid_overlay(struct file *file, void *fh, - struct v4l2_fmtdesc *fmt) -{ - int index = fmt->index; - - if (index >= NUM_OUTPUT_FORMATS) - return -EINVAL; - - fmt->flags = omap_formats[index].flags; - strlcpy(fmt->description, omap_formats[index].description, - sizeof(fmt->description)); - fmt->pixelformat = omap_formats[index].pixelformat; - return 0; -} - static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f) { @@ -1862,7 +1847,6 @@ static const struct v4l2_ioctl_ops vout_ioctl_ops = { .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, - .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, .vidioc_cropcap = vidioc_cropcap, .vidioc_g_crop = vidioc_g_crop, -- cgit v1.2.3 From 7b2607c99e36854987df2f78eb540cd712490450 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 13 Nov 2012 10:00:22 -0300 Subject: [media] omap_vout: Use the output overlay ioctl operations The omap_vout device implements the output overlay API, use the corresponding ioctl operations. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap/omap_vout.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 90f890875b8b..f7ad54106bc5 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -1845,9 +1845,9 @@ static const struct v4l2_ioctl_ops vout_ioctl_ops = { .vidioc_s_fbuf = vidioc_s_fbuf, .vidioc_g_fbuf = vidioc_g_fbuf, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, - .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, - .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, + .vidioc_try_fmt_vid_out_overlay = vidioc_try_fmt_vid_overlay, + .vidioc_s_fmt_vid_out_overlay = vidioc_s_fmt_vid_overlay, + .vidioc_g_fmt_vid_out_overlay = vidioc_g_fmt_vid_overlay, .vidioc_cropcap = vidioc_cropcap, .vidioc_g_crop = vidioc_g_crop, .vidioc_s_crop = vidioc_s_crop, -- cgit v1.2.3 From b1252eb83fe57b838c19e2c65cba685c93696693 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 11 Sep 2012 06:32:17 -0300 Subject: [media] media: mem2mem: make reference to struct m2m_ops in the core const The mem2mem core doesn't change struct m2m_ops, provided by the driver, make references to it const. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 4 ++-- include/media/v4l2-mem2mem.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 438ea45d1074..da99cf727162 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -62,7 +62,7 @@ struct v4l2_m2m_dev { struct list_head job_queue; spinlock_t job_spinlock; - struct v4l2_m2m_ops *m2m_ops; + const struct v4l2_m2m_ops *m2m_ops; }; static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx, @@ -519,7 +519,7 @@ EXPORT_SYMBOL(v4l2_m2m_mmap); * * Usually called from driver's probe() function. */ -struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops) +struct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops) { struct v4l2_m2m_dev *m2m_dev; diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 7e82d2b193d5..d3eef01da648 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -125,7 +125,7 @@ unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct vm_area_struct *vma); -struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops); +struct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops); void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev); struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct v4l2_m2m_dev *m2m_dev, -- cgit v1.2.3 From d7e5008f7c2077d856e40a3af746f1a47028b5f2 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 18 Dec 2012 10:31:25 -0800 Subject: drm/i915: Move even more gtt code to i915_gem_gtt This really should have been part of the kill agp series. Signed-off-by: Ben Widawsky Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 7 ++--- drivers/gpu/drm/i915/i915_gem.c | 51 ++------------------------------ drivers/gpu/drm/i915/i915_gem_gtt.c | 59 ++++++++++++++++++++++++++++++++++--- 3 files changed, 61 insertions(+), 56 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index b0faa9149a1a..15799e783b7a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1587,10 +1587,9 @@ void i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj, enum i915_cache_level cache_level); void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj); void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj); -void i915_gem_init_global_gtt(struct drm_device *dev, - unsigned long start, - unsigned long mappable_end, - unsigned long end); +void i915_gem_init_global_gtt(struct drm_device *dev); +void i915_gem_setup_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); static inline void i915_gem_chipset_flush(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 23b883a135db..ad98db5d22ea 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -163,8 +163,8 @@ i915_gem_init_ioctl(struct drm_device *dev, void *data, return -ENODEV; mutex_lock(&dev->struct_mutex); - i915_gem_init_global_gtt(dev, args->gtt_start, - args->gtt_end, args->gtt_end); + i915_gem_setup_global_gtt(dev, args->gtt_start, args->gtt_end, + args->gtt_end); mutex_unlock(&dev->struct_mutex); return 0; @@ -3981,58 +3981,13 @@ cleanup_render_ring: return ret; } -static bool -intel_enable_ppgtt(struct drm_device *dev) -{ - if (i915_enable_ppgtt >= 0) - return i915_enable_ppgtt; - -#ifdef CONFIG_INTEL_IOMMU - /* Disable ppgtt on SNB if VT-d is on. */ - if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped) - return false; -#endif - - return true; -} - int i915_gem_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - unsigned long gtt_size, mappable_size; int ret; - gtt_size = dev_priv->mm.gtt->gtt_total_entries << PAGE_SHIFT; - mappable_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; - mutex_lock(&dev->struct_mutex); - if (intel_enable_ppgtt(dev) && HAS_ALIASING_PPGTT(dev)) { - /* PPGTT pdes are stolen from global gtt ptes, so shrink the - * aperture accordingly when using aliasing ppgtt. */ - gtt_size -= I915_PPGTT_PD_ENTRIES*PAGE_SIZE; - - i915_gem_init_global_gtt(dev, 0, mappable_size, gtt_size); - - ret = i915_gem_init_aliasing_ppgtt(dev); - if (ret) { - mutex_unlock(&dev->struct_mutex); - return ret; - } - } else { - /* Let GEM Manage all of the aperture. - * - * However, leave one page at the end still bound to the scratch - * page. There are a number of places where the hardware - * apparently prefetches past the end of the object, and we've - * seen multiple hangs with the GPU head pointer stuck in a - * batchbuffer bound at the last page of the aperture. One page - * should be enough to keep any prefetching inside of the - * aperture. - */ - i915_gem_init_global_gtt(dev, 0, mappable_size, - gtt_size); - } - + i915_gem_init_global_gtt(dev); ret = i915_gem_init_hw(dev); mutex_unlock(&dev->struct_mutex); if (ret) { diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index ea646b4c312a..61b3e728be34 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -525,10 +525,10 @@ static void i915_gtt_color_adjust(struct drm_mm_node *node, } } -void i915_gem_init_global_gtt(struct drm_device *dev, - unsigned long start, - unsigned long mappable_end, - unsigned long end) +void i915_gem_setup_global_gtt(struct drm_device *dev, + unsigned long start, + unsigned long mappable_end, + unsigned long end) { drm_i915_private_t *dev_priv = dev->dev_private; struct drm_mm_node *entry; @@ -573,6 +573,57 @@ void i915_gem_init_global_gtt(struct drm_device *dev, i915_ggtt_clear_range(dev, end / PAGE_SIZE - 1, 1); } +static bool +intel_enable_ppgtt(struct drm_device *dev) +{ + if (i915_enable_ppgtt >= 0) + return i915_enable_ppgtt; + +#ifdef CONFIG_INTEL_IOMMU + /* Disable ppgtt on SNB if VT-d is on. */ + if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped) + return false; +#endif + + return true; +} + +void i915_gem_init_global_gtt(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long gtt_size, mappable_size; + int ret; + + gtt_size = dev_priv->mm.gtt->gtt_total_entries << PAGE_SHIFT; + mappable_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; + + if (intel_enable_ppgtt(dev) && HAS_ALIASING_PPGTT(dev)) { + /* PPGTT pdes are stolen from global gtt ptes, so shrink the + * aperture accordingly when using aliasing ppgtt. */ + gtt_size -= I915_PPGTT_PD_ENTRIES*PAGE_SIZE; + + i915_gem_setup_global_gtt(dev, 0, mappable_size, gtt_size); + + ret = i915_gem_init_aliasing_ppgtt(dev); + if (ret) { + mutex_unlock(&dev->struct_mutex); + return; + } + } else { + /* Let GEM Manage all of the aperture. + * + * However, leave one page at the end still bound to the scratch + * page. There are a number of places where the hardware + * apparently prefetches past the end of the object, and we've + * seen multiple hangs with the GPU head pointer stuck in a + * batchbuffer bound at the last page of the aperture. One page + * should be enough to keep any prefetching inside of the + * aperture. + */ + i915_gem_setup_global_gtt(dev, 0, mappable_size, gtt_size); + } +} + static int setup_scratch_page(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; -- cgit v1.2.3 From 06e5598fce5ce89fe8bf081398296e5b08d993dd Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 18 Dec 2012 10:31:26 -0800 Subject: drm/i915: Move GSM mapping into dev_priv This removes an unused field from the AGP structure and moves it into the dev_priv structure (with a slightly better name). This builds upon the kill-agp series already merged. GSM is a well defined term in the bspec: GSM: Graphics Stolen Memory GTT stolen space is defined for storage of the GFX GTT entries in physical memory. IA can not access GSM directly , it can only access via GTTMMADR. GT can access GSM directly or through GTTMMADR. This is not the entire stolen space. Signed-off-by: Ben Widawsky Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 1 - drivers/gpu/drm/i915/i915_drv.h | 3 +++ drivers/gpu/drm/i915/i915_gem_gtt.c | 14 +++++++------- include/drm/intel-gtt.h | 2 -- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index dbd901e94ea6..c8d9dcb15db0 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -602,7 +602,6 @@ static int intel_gtt_init(void) iounmap(intel_private.registers); return -ENOMEM; } - intel_private.base.gtt = intel_private.gtt; global_cache_flush(); /* FIXME: ? */ diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 15799e783b7a..ae88d87be951 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -800,6 +800,9 @@ typedef struct drm_i915_private { unsigned long gtt_end; unsigned long stolen_base; /* limited to low memory (32-bit) */ + /** "Graphics Stolen Memory" holds the global PTEs */ + uint32_t __iomem *gsm; + struct io_mapping *gtt_mapping; phys_addr_t gtt_base_addr; int gtt_mtrr; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 61b3e728be34..912389e38a7d 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -290,7 +290,7 @@ void i915_gem_init_ppgtt(struct drm_device *dev) return; - pd_addr = dev_priv->mm.gtt->gtt + ppgtt->pd_offset/sizeof(gtt_pte_t); + pd_addr = dev_priv->mm.gsm + ppgtt->pd_offset/sizeof(gtt_pte_t); for (i = 0; i < ppgtt->num_pd_entries; i++) { dma_addr_t pt_addr; @@ -367,7 +367,7 @@ static void i915_ggtt_clear_range(struct drm_device *dev, { struct drm_i915_private *dev_priv = dev->dev_private; gtt_pte_t scratch_pte; - gtt_pte_t __iomem *gtt_base = dev_priv->mm.gtt->gtt + first_entry; + gtt_pte_t __iomem *gtt_base = (gtt_pte_t __iomem *) dev_priv->mm.gsm + first_entry; const int max_entries = dev_priv->mm.gtt->gtt_total_entries - first_entry; int i; @@ -432,7 +432,7 @@ static void gen6_ggtt_bind_object(struct drm_i915_gem_object *obj, 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; + gtt_pte_t __iomem *gtt_entries = dev_priv->mm.gsm + first_entry; int unused, i = 0; unsigned int len, m = 0; dma_addr_t addr; @@ -747,9 +747,9 @@ int i915_gem_gtt_init(struct drm_device *dev) goto err_out; } - dev_priv->mm.gtt->gtt = ioremap_wc(gtt_bus_addr, - dev_priv->mm.gtt->gtt_total_entries * sizeof(gtt_pte_t)); - if (!dev_priv->mm.gtt->gtt) { + dev_priv->mm.gsm = ioremap_wc(gtt_bus_addr, + dev_priv->mm.gtt->gtt_total_entries * sizeof(gtt_pte_t)); + if (!dev_priv->mm.gsm) { DRM_ERROR("Failed to map the gtt page table\n"); teardown_scratch_page(dev); ret = -ENOMEM; @@ -773,7 +773,7 @@ err_out: void i915_gem_gtt_fini(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - iounmap(dev_priv->mm.gtt->gtt); + iounmap(dev_priv->mm.gsm); teardown_scratch_page(dev); if (INTEL_INFO(dev)->gen < 6) intel_gmch_remove(); diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index 6eb76a1f11ab..3e3a166a2690 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -18,8 +18,6 @@ struct intel_gtt { /* 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 */ phys_addr_t gma_bus_addr; } *intel_gtt_get(void); -- cgit v1.2.3 From 1c45140d3da0c98fa7eda2cd10f9805232a5e9ac Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 18 Dec 2012 10:31:27 -0800 Subject: drm/i915: Make GSM void The iomapping of the register region has historically been a uint32_t for the obvious reason that our PTE size was always 4b. In the future however, we cannot make this assumption. By making the type void, it makes the upcoming pointer math we will do much easier, and hopefully gives the compiler opportunities to warn us when we do stupid things. v2: Cast to __iomem, caught by Ville Signed-off-by: Ben Widawsky [danvet: Fixup __iomem issue for real.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 +- drivers/gpu/drm/i915/i915_gem_gtt.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ae88d87be951..d2b93a4a3d71 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -801,7 +801,7 @@ typedef struct drm_i915_private { unsigned long stolen_base; /* limited to low memory (32-bit) */ /** "Graphics Stolen Memory" holds the global PTEs */ - uint32_t __iomem *gsm; + void __iomem *gsm; struct io_mapping *gtt_mapping; phys_addr_t gtt_base_addr; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 912389e38a7d..eac2cec71652 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -290,7 +290,7 @@ void i915_gem_init_ppgtt(struct drm_device *dev) return; - pd_addr = dev_priv->mm.gsm + ppgtt->pd_offset/sizeof(gtt_pte_t); + pd_addr = (gtt_pte_t __iomem*)dev_priv->mm.gsm + ppgtt->pd_offset/sizeof(gtt_pte_t); for (i = 0; i < ppgtt->num_pd_entries; i++) { dma_addr_t pt_addr; @@ -432,7 +432,8 @@ static void gen6_ggtt_bind_object(struct drm_i915_gem_object *obj, 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.gsm + first_entry; + gtt_pte_t __iomem *gtt_entries = + (gtt_pte_t __iomem *)dev_priv->mm.gsm + first_entry; int unused, i = 0; unsigned int len, m = 0; dma_addr_t addr; -- cgit v1.2.3 From 05efa71bdc0e352edc9189fdf66af6e96eadd1c9 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 5 Oct 2012 07:43:41 -0300 Subject: [media] media: add a VEU MEM2MEM format conversion and scaling driver Video Engine Unit (VEU) is an IP block, found in multiple SuperH and ARM- based sh-mobile and r-mobile SoCs, capable of processing video data. It can perform colour-space conversion, scaling and several filtering transformations. This patch adds an initial implementation of a mem2mem V4L2 driver for VEU. So far only conversion from NV12 to RGB565 is supported. Further functionality shall be added in the future. This driver is based on a VEU vidix driver by Magnus Damm. Signed-off-by: Guennadi Liakhovetski Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 9 + drivers/media/platform/Makefile | 2 + drivers/media/platform/sh_veu.c | 1264 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 1275 insertions(+) create mode 100644 drivers/media/platform/sh_veu.c (limited to 'drivers') diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 3dcfea612c42..c071b079de23 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -202,6 +202,15 @@ config VIDEO_SAMSUNG_EXYNOS_GSC help This is a v4l2 driver for Samsung EXYNOS5 SoC G-Scaler. +config VIDEO_SH_VEU + tristate "SuperH VEU mem2mem video processing driver" + depends on VIDEO_DEV && VIDEO_V4L2 + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + Support for the Video Engine Unit (VEU) on SuperH and + SH-Mobile SoCs. + endif # V4L_MEM2MEM_DRIVERS menuconfig V4L_TEST_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 4817d2802171..42089ba3600f 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -25,6 +25,8 @@ obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o obj-$(CONFIG_VIDEO_CODA) += coda.o +obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o + obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/ diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c new file mode 100644 index 000000000000..7365fb5a2615 --- /dev/null +++ b/drivers/media/platform/sh_veu.c @@ -0,0 +1,1264 @@ +/* + * sh-mobile VEU mem2mem driver + * + * Copyright (C) 2012 Renesas Electronics Corporation + * Author: Guennadi Liakhovetski, + * Copyright (C) 2008 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License as + * published by the Free Software Foundation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define VEU_STR 0x00 /* start register */ +#define VEU_SWR 0x10 /* src: line length */ +#define VEU_SSR 0x14 /* src: image size */ +#define VEU_SAYR 0x18 /* src: y/rgb plane address */ +#define VEU_SACR 0x1c /* src: c plane address */ +#define VEU_BSSR 0x20 /* bundle mode register */ +#define VEU_EDWR 0x30 /* dst: line length */ +#define VEU_DAYR 0x34 /* dst: y/rgb plane address */ +#define VEU_DACR 0x38 /* dst: c plane address */ +#define VEU_TRCR 0x50 /* transform control */ +#define VEU_RFCR 0x54 /* resize scale */ +#define VEU_RFSR 0x58 /* resize clip */ +#define VEU_ENHR 0x5c /* enhance */ +#define VEU_FMCR 0x70 /* filter mode */ +#define VEU_VTCR 0x74 /* lowpass vertical */ +#define VEU_HTCR 0x78 /* lowpass horizontal */ +#define VEU_APCR 0x80 /* color match */ +#define VEU_ECCR 0x84 /* color replace */ +#define VEU_AFXR 0x90 /* fixed mode */ +#define VEU_SWPR 0x94 /* swap */ +#define VEU_EIER 0xa0 /* interrupt mask */ +#define VEU_EVTR 0xa4 /* interrupt event */ +#define VEU_STAR 0xb0 /* status */ +#define VEU_BSRR 0xb4 /* reset */ + +#define VEU_MCR00 0x200 /* color conversion matrix coefficient 00 */ +#define VEU_MCR01 0x204 /* color conversion matrix coefficient 01 */ +#define VEU_MCR02 0x208 /* color conversion matrix coefficient 02 */ +#define VEU_MCR10 0x20c /* color conversion matrix coefficient 10 */ +#define VEU_MCR11 0x210 /* color conversion matrix coefficient 11 */ +#define VEU_MCR12 0x214 /* color conversion matrix coefficient 12 */ +#define VEU_MCR20 0x218 /* color conversion matrix coefficient 20 */ +#define VEU_MCR21 0x21c /* color conversion matrix coefficient 21 */ +#define VEU_MCR22 0x220 /* color conversion matrix coefficient 22 */ +#define VEU_COFFR 0x224 /* color conversion offset */ +#define VEU_CBR 0x228 /* color conversion clip */ + +/* + * 4092x4092 max size is the normal case. In some cases it can be reduced to + * 2048x2048, in other cases it can be 4092x8188 or even 8188x8188. + */ +#define MAX_W 4092 +#define MAX_H 4092 +#define MIN_W 8 +#define MIN_H 8 +#define ALIGN_W 4 + +/* 3 buffers of 2048 x 1536 - 3 megapixels @ 16bpp */ +#define VIDEO_MEM_LIMIT ALIGN(2048 * 1536 * 2 * 3, 1024 * 1024) + +#define MEM2MEM_DEF_TRANSLEN 1 + +struct sh_veu_dev; + +struct sh_veu_file { + struct sh_veu_dev *veu_dev; + bool cfg_needed; +}; + +struct sh_veu_format { + char *name; + u32 fourcc; + unsigned int depth; + unsigned int ydepth; +}; + +/* video data format */ +struct sh_veu_vfmt { + /* Replace with v4l2_rect */ + struct v4l2_rect frame; + unsigned int bytesperline; + unsigned int offset_y; + unsigned int offset_c; + const struct sh_veu_format *fmt; +}; + +struct sh_veu_dev { + struct v4l2_device v4l2_dev; + struct video_device vdev; + struct v4l2_m2m_dev *m2m_dev; + struct device *dev; + struct v4l2_m2m_ctx *m2m_ctx; + struct sh_veu_vfmt vfmt_out; + struct sh_veu_vfmt vfmt_in; + /* Only single user per direction so far */ + struct sh_veu_file *capture; + struct sh_veu_file *output; + struct mutex fop_lock; + void __iomem *base; + struct vb2_alloc_ctx *alloc_ctx; + spinlock_t lock; + bool is_2h; + unsigned int xaction; + bool aborting; +}; + +enum sh_veu_fmt_idx { + SH_VEU_FMT_NV12, + SH_VEU_FMT_NV16, + SH_VEU_FMT_NV24, + SH_VEU_FMT_RGB332, + SH_VEU_FMT_RGB444, + SH_VEU_FMT_RGB565, + SH_VEU_FMT_RGB666, + SH_VEU_FMT_RGB24, +}; + +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 + +#define DEFAULT_IN_WIDTH VGA_WIDTH +#define DEFAULT_IN_HEIGHT VGA_HEIGHT +#define DEFAULT_IN_FMTIDX SH_VEU_FMT_NV12 +#define DEFAULT_OUT_WIDTH VGA_WIDTH +#define DEFAULT_OUT_HEIGHT VGA_HEIGHT +#define DEFAULT_OUT_FMTIDX SH_VEU_FMT_RGB565 + +/* + * Alignment: Y-plane should be 4-byte aligned for NV12 and NV16, and 8-byte + * aligned for NV24. + */ +static const struct sh_veu_format sh_veu_fmt[] = { + [SH_VEU_FMT_NV12] = { .ydepth = 8, .depth = 12, .name = "NV12", .fourcc = V4L2_PIX_FMT_NV12 }, + [SH_VEU_FMT_NV16] = { .ydepth = 8, .depth = 16, .name = "NV16", .fourcc = V4L2_PIX_FMT_NV16 }, + [SH_VEU_FMT_NV24] = { .ydepth = 8, .depth = 24, .name = "NV24", .fourcc = V4L2_PIX_FMT_NV24 }, + [SH_VEU_FMT_RGB332] = { .ydepth = 8, .depth = 8, .name = "RGB332", .fourcc = V4L2_PIX_FMT_RGB332 }, + [SH_VEU_FMT_RGB444] = { .ydepth = 16, .depth = 16, .name = "RGB444", .fourcc = V4L2_PIX_FMT_RGB444 }, + [SH_VEU_FMT_RGB565] = { .ydepth = 16, .depth = 16, .name = "RGB565", .fourcc = V4L2_PIX_FMT_RGB565 }, + [SH_VEU_FMT_RGB666] = { .ydepth = 32, .depth = 32, .name = "BGR666", .fourcc = V4L2_PIX_FMT_BGR666 }, + [SH_VEU_FMT_RGB24] = { .ydepth = 24, .depth = 24, .name = "RGB24", .fourcc = V4L2_PIX_FMT_RGB24 }, +}; + +#define DEFAULT_IN_VFMT (struct sh_veu_vfmt){ \ + .frame = { \ + .width = VGA_WIDTH, \ + .height = VGA_HEIGHT, \ + }, \ + .bytesperline = (VGA_WIDTH * sh_veu_fmt[DEFAULT_IN_FMTIDX].ydepth) >> 3, \ + .fmt = &sh_veu_fmt[DEFAULT_IN_FMTIDX], \ +} + +#define DEFAULT_OUT_VFMT (struct sh_veu_vfmt){ \ + .frame = { \ + .width = VGA_WIDTH, \ + .height = VGA_HEIGHT, \ + }, \ + .bytesperline = (VGA_WIDTH * sh_veu_fmt[DEFAULT_OUT_FMTIDX].ydepth) >> 3, \ + .fmt = &sh_veu_fmt[DEFAULT_OUT_FMTIDX], \ +} + +/* + * TODO: add support for further output formats: + * SH_VEU_FMT_NV12, + * SH_VEU_FMT_NV16, + * SH_VEU_FMT_NV24, + * SH_VEU_FMT_RGB332, + * SH_VEU_FMT_RGB444, + * SH_VEU_FMT_RGB666, + * SH_VEU_FMT_RGB24, + */ + +static const int sh_veu_fmt_out[] = { + SH_VEU_FMT_RGB565, +}; + +/* + * TODO: add support for further input formats: + * SH_VEU_FMT_NV16, + * SH_VEU_FMT_NV24, + * SH_VEU_FMT_RGB565, + * SH_VEU_FMT_RGB666, + * SH_VEU_FMT_RGB24, + */ +static const int sh_veu_fmt_in[] = { + SH_VEU_FMT_NV12, +}; + +static enum v4l2_colorspace sh_veu_4cc2cspace(u32 fourcc) +{ + switch (fourcc) { + default: + BUG(); + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV24: + return V4L2_COLORSPACE_JPEG; + case V4L2_PIX_FMT_RGB332: + case V4L2_PIX_FMT_RGB444: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_BGR666: + case V4L2_PIX_FMT_RGB24: + return V4L2_COLORSPACE_SRGB; + } +} + +static u32 sh_veu_reg_read(struct sh_veu_dev *veu, unsigned int reg) +{ + return ioread32(veu->base + reg); +} + +static void sh_veu_reg_write(struct sh_veu_dev *veu, unsigned int reg, + u32 value) +{ + iowrite32(value, veu->base + reg); +} + + /* ========== mem2mem callbacks ========== */ + +static void sh_veu_job_abort(void *priv) +{ + struct sh_veu_dev *veu = priv; + + /* Will cancel the transaction in the next interrupt handler */ + veu->aborting = true; +} + +static void sh_veu_lock(void *priv) +{ + struct sh_veu_dev *veu = priv; + + mutex_lock(&veu->fop_lock); +} + +static void sh_veu_unlock(void *priv) +{ + struct sh_veu_dev *veu = priv; + + mutex_unlock(&veu->fop_lock); +} + +static void sh_veu_process(struct sh_veu_dev *veu, + struct vb2_buffer *src_buf, + struct vb2_buffer *dst_buf) +{ + dma_addr_t addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + + sh_veu_reg_write(veu, VEU_DAYR, addr + veu->vfmt_out.offset_y); + sh_veu_reg_write(veu, VEU_DACR, veu->vfmt_out.offset_c ? + addr + veu->vfmt_out.offset_c : 0); + dev_dbg(veu->dev, "%s(): dst base %x, y: %x, c: %x\n", __func__, + addr, veu->vfmt_out.offset_y, veu->vfmt_out.offset_c); + + addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); + sh_veu_reg_write(veu, VEU_SAYR, addr + veu->vfmt_in.offset_y); + sh_veu_reg_write(veu, VEU_SACR, veu->vfmt_in.offset_c ? + addr + veu->vfmt_in.offset_c : 0); + dev_dbg(veu->dev, "%s(): src base %x, y: %x, c: %x\n", __func__, + addr, veu->vfmt_in.offset_y, veu->vfmt_in.offset_c); + + sh_veu_reg_write(veu, VEU_STR, 1); + + sh_veu_reg_write(veu, VEU_EIER, 1); /* enable interrupt in VEU */ +} + +/** + * sh_veu_device_run() - prepares and starts the device + * + * This will be called by the framework when it decides to schedule a particular + * instance. + */ +static void sh_veu_device_run(void *priv) +{ + struct sh_veu_dev *veu = priv; + struct vb2_buffer *src_buf, *dst_buf; + + src_buf = v4l2_m2m_next_src_buf(veu->m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(veu->m2m_ctx); + + if (src_buf && dst_buf) + sh_veu_process(veu, src_buf, dst_buf); +} + + /* ========== video ioctls ========== */ + +static bool sh_veu_is_streamer(struct sh_veu_dev *veu, struct sh_veu_file *veu_file, + enum v4l2_buf_type type) +{ + return (type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + veu_file == veu->capture) || + (type == V4L2_BUF_TYPE_VIDEO_OUTPUT && + veu_file == veu->output); +} + +static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); + +/* + * It is not unusual to have video nodes open()ed multiple times. While some + * V4L2 operations are non-intrusive, like querying formats and various + * parameters, others, like setting formats, starting and stopping streaming, + * queuing and dequeuing buffers, directly affect hardware configuration and / + * or execution. This function verifies availability of the requested interface + * and, if available, reserves it for the requesting user. + */ +static int sh_veu_stream_init(struct sh_veu_dev *veu, struct sh_veu_file *veu_file, + enum v4l2_buf_type type) +{ + struct sh_veu_file **stream; + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + stream = &veu->capture; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + stream = &veu->output; + break; + default: + return -EINVAL; + } + + if (*stream == veu_file) + return 0; + + if (*stream) + return -EBUSY; + + *stream = veu_file; + + return 0; +} + +static int sh_veu_context_init(struct sh_veu_dev *veu) +{ + if (veu->m2m_ctx) + return 0; + + veu->m2m_ctx = v4l2_m2m_ctx_init(veu->m2m_dev, veu, + sh_veu_queue_init); + + if (IS_ERR(veu->m2m_ctx)) + return PTR_ERR(veu->m2m_ctx); + + return 0; +} + +static int sh_veu_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strlcpy(cap->driver, "sh-veu", sizeof(cap->driver)); + strlcpy(cap->card, "sh-mobile VEU", sizeof(cap->card)); + strlcpy(cap->bus_info, "platform:sh-veu", sizeof(cap->bus_info)); + cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + + return 0; +} + +static int sh_veu_enum_fmt(struct v4l2_fmtdesc *f, const int *fmt, int fmt_num) +{ + if (f->index >= fmt_num) + return -EINVAL; + + strlcpy(f->description, sh_veu_fmt[fmt[f->index]].name, sizeof(f->description)); + f->pixelformat = sh_veu_fmt[fmt[f->index]].fourcc; + return 0; +} + +static int sh_veu_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return sh_veu_enum_fmt(f, sh_veu_fmt_out, ARRAY_SIZE(sh_veu_fmt_out)); +} + +static int sh_veu_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return sh_veu_enum_fmt(f, sh_veu_fmt_in, ARRAY_SIZE(sh_veu_fmt_in)); +} + +static struct sh_veu_vfmt *sh_veu_get_vfmt(struct sh_veu_dev *veu, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &veu->vfmt_out; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return &veu->vfmt_in; + default: + return NULL; + } +} + +static int sh_veu_g_fmt(struct sh_veu_file *veu_file, struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + struct sh_veu_dev *veu = veu_file->veu_dev; + struct sh_veu_vfmt *vfmt; + + vfmt = sh_veu_get_vfmt(veu, f->type); + + pix->width = vfmt->frame.width; + pix->height = vfmt->frame.height; + pix->field = V4L2_FIELD_NONE; + pix->pixelformat = vfmt->fmt->fourcc; + pix->colorspace = sh_veu_4cc2cspace(pix->pixelformat); + pix->bytesperline = vfmt->bytesperline; + pix->sizeimage = vfmt->bytesperline * pix->height * + vfmt->fmt->depth / vfmt->fmt->ydepth; + pix->priv = 0; + dev_dbg(veu->dev, "%s(): type: %d, size %u @ %ux%u, fmt %x\n", __func__, + f->type, pix->sizeimage, pix->width, pix->height, pix->pixelformat); + + return 0; +} + +static int sh_veu_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + return sh_veu_g_fmt(priv, f); +} + +static int sh_veu_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + return sh_veu_g_fmt(priv, f); +} + +static int sh_veu_try_fmt(struct v4l2_format *f, const struct sh_veu_format *fmt) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + unsigned int y_bytes_used; + + /* + * V4L2 specification suggests, that the driver should correct the + * format struct if any of the dimensions is unsupported + */ + switch (pix->field) { + default: + case V4L2_FIELD_ANY: + pix->field = V4L2_FIELD_NONE; + /* fall through: continue handling V4L2_FIELD_NONE */ + case V4L2_FIELD_NONE: + break; + } + + v4l_bound_align_image(&pix->width, MIN_W, MAX_W, ALIGN_W, + &pix->height, MIN_H, MAX_H, 0, 0); + + y_bytes_used = (pix->width * fmt->ydepth) >> 3; + + if (pix->bytesperline < y_bytes_used) + pix->bytesperline = y_bytes_used; + pix->sizeimage = pix->height * pix->bytesperline * fmt->depth / fmt->ydepth; + + pix->pixelformat = fmt->fourcc; + pix->colorspace = sh_veu_4cc2cspace(pix->pixelformat); + pix->priv = 0; + + pr_debug("%s(): type: %d, size %u\n", __func__, f->type, pix->sizeimage); + + return 0; +} + +static const struct sh_veu_format *sh_veu_find_fmt(const struct v4l2_format *f) +{ + const int *fmt; + int i, n, dflt; + + pr_debug("%s(%d;%d)\n", __func__, f->type, f->fmt.pix.field); + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + fmt = sh_veu_fmt_out; + n = ARRAY_SIZE(sh_veu_fmt_out); + dflt = DEFAULT_OUT_FMTIDX; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + default: + fmt = sh_veu_fmt_in; + n = ARRAY_SIZE(sh_veu_fmt_in); + dflt = DEFAULT_IN_FMTIDX; + break; + } + + for (i = 0; i < n; i++) + if (sh_veu_fmt[fmt[i]].fourcc == f->fmt.pix.pixelformat) + return &sh_veu_fmt[fmt[i]]; + + return &sh_veu_fmt[dflt]; +} + +static int sh_veu_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + const struct sh_veu_format *fmt; + + fmt = sh_veu_find_fmt(f); + if (!fmt) + /* wrong buffer type */ + return -EINVAL; + + return sh_veu_try_fmt(f, fmt); +} + +static int sh_veu_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + const struct sh_veu_format *fmt; + + fmt = sh_veu_find_fmt(f); + if (!fmt) + /* wrong buffer type */ + return -EINVAL; + + return sh_veu_try_fmt(f, fmt); +} + +static void sh_veu_colour_offset(struct sh_veu_dev *veu, struct sh_veu_vfmt *vfmt) +{ + /* dst_left and dst_top validity will be verified in CROP / COMPOSE */ + unsigned int left = vfmt->frame.left & ~0x03; + unsigned int top = vfmt->frame.top; + dma_addr_t offset = ((left * veu->vfmt_out.fmt->depth) >> 3) + + top * veu->vfmt_out.bytesperline; + unsigned int y_line; + + vfmt->offset_y = offset; + + switch (vfmt->fmt->fourcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV24: + y_line = ALIGN(vfmt->frame.width, 16); + vfmt->offset_c = offset + y_line * vfmt->frame.height; + break; + case V4L2_PIX_FMT_RGB332: + case V4L2_PIX_FMT_RGB444: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_BGR666: + case V4L2_PIX_FMT_RGB24: + vfmt->offset_c = 0; + break; + default: + BUG(); + } +} + +static int sh_veu_s_fmt(struct sh_veu_file *veu_file, struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + struct sh_veu_dev *veu = veu_file->veu_dev; + struct sh_veu_vfmt *vfmt; + struct vb2_queue *vq; + int ret = sh_veu_context_init(veu); + if (ret < 0) + return ret; + + vq = v4l2_m2m_get_vq(veu->m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + if (vb2_is_busy(vq)) { + v4l2_err(&veu_file->veu_dev->v4l2_dev, "%s queue busy\n", __func__); + return -EBUSY; + } + + vfmt = sh_veu_get_vfmt(veu, f->type); + /* called after try_fmt(), hence vfmt != NULL. Implicit BUG_ON() below */ + + vfmt->fmt = sh_veu_find_fmt(f); + /* vfmt->fmt != NULL following the same argument as above */ + vfmt->frame.width = pix->width; + vfmt->frame.height = pix->height; + vfmt->bytesperline = pix->bytesperline; + + sh_veu_colour_offset(veu, vfmt); + + /* + * We could also verify and require configuration only if any parameters + * actually have changed, but it is unlikely, that the user requests the + * same configuration several times without closing the device. + */ + veu_file->cfg_needed = true; + + dev_dbg(veu->dev, + "Setting format for type %d, wxh: %dx%d, fmt: %x\n", + f->type, pix->width, pix->height, vfmt->fmt->fourcc); + + return 0; +} + +static int sh_veu_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret = sh_veu_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + return sh_veu_s_fmt(priv, f); +} + +static int sh_veu_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret = sh_veu_try_fmt_vid_out(file, priv, f); + if (ret) + return ret; + + return sh_veu_s_fmt(priv, f); +} + +static int sh_veu_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *reqbufs) +{ + struct sh_veu_file *veu_file = priv; + struct sh_veu_dev *veu = veu_file->veu_dev; + int ret = sh_veu_context_init(veu); + if (ret < 0) + return ret; + + ret = sh_veu_stream_init(veu, veu_file, reqbufs->type); + if (ret < 0) + return ret; + + return v4l2_m2m_reqbufs(file, veu->m2m_ctx, reqbufs); +} + +static int sh_veu_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct sh_veu_file *veu_file = priv; + + if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, buf->type)) + return -EBUSY; + + return v4l2_m2m_querybuf(file, veu_file->veu_dev->m2m_ctx, buf); +} + +static int sh_veu_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct sh_veu_file *veu_file = priv; + + dev_dbg(veu_file->veu_dev->dev, "%s(%d)\n", __func__, buf->type); + if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, buf->type)) + return -EBUSY; + + return v4l2_m2m_qbuf(file, veu_file->veu_dev->m2m_ctx, buf); +} + +static int sh_veu_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct sh_veu_file *veu_file = priv; + + dev_dbg(veu_file->veu_dev->dev, "%s(%d)\n", __func__, buf->type); + if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, buf->type)) + return -EBUSY; + + return v4l2_m2m_dqbuf(file, veu_file->veu_dev->m2m_ctx, buf); +} + +static void sh_veu_calc_scale(struct sh_veu_dev *veu, + int size_in, int size_out, int crop_out, + u32 *mant, u32 *frac, u32 *rep) +{ + u32 fixpoint; + + /* calculate FRAC and MANT */ + *rep = *mant = *frac = 0; + + if (size_in == size_out) { + if (crop_out != size_out) + *mant = 1; /* needed for cropping */ + return; + } + + /* VEU2H special upscale */ + if (veu->is_2h && size_out > size_in) { + u32 fixpoint = (4096 * size_in) / size_out; + *mant = fixpoint / 4096; + *frac = (fixpoint - (*mant * 4096)) & ~0x07; + + switch (*frac) { + case 0x800: + *rep = 1; + break; + case 0x400: + *rep = 3; + break; + case 0x200: + *rep = 7; + break; + } + if (*rep) + return; + } + + fixpoint = (4096 * (size_in - 1)) / (size_out + 1); + *mant = fixpoint / 4096; + *frac = fixpoint - (*mant * 4096); + + if (*frac & 0x07) { + /* + * FIXME: do we really have to round down twice in the + * up-scaling case? + */ + *frac &= ~0x07; + if (size_out > size_in) + *frac -= 8; /* round down if scaling up */ + else + *frac += 8; /* round up if scaling down */ + } +} + +static unsigned long sh_veu_scale_v(struct sh_veu_dev *veu, + int size_in, int size_out, int crop_out) +{ + u32 mant, frac, value, rep; + + sh_veu_calc_scale(veu, size_in, size_out, crop_out, &mant, &frac, &rep); + + /* set scale */ + value = (sh_veu_reg_read(veu, VEU_RFCR) & ~0xffff0000) | + (((mant << 12) | frac) << 16); + + sh_veu_reg_write(veu, VEU_RFCR, value); + + /* set clip */ + value = (sh_veu_reg_read(veu, VEU_RFSR) & ~0xffff0000) | + (((rep << 12) | crop_out) << 16); + + sh_veu_reg_write(veu, VEU_RFSR, value); + + return ALIGN((size_in * crop_out) / size_out, 4); +} + +static unsigned long sh_veu_scale_h(struct sh_veu_dev *veu, + int size_in, int size_out, int crop_out) +{ + u32 mant, frac, value, rep; + + sh_veu_calc_scale(veu, size_in, size_out, crop_out, &mant, &frac, &rep); + + /* set scale */ + value = (sh_veu_reg_read(veu, VEU_RFCR) & ~0xffff) | + (mant << 12) | frac; + + sh_veu_reg_write(veu, VEU_RFCR, value); + + /* set clip */ + value = (sh_veu_reg_read(veu, VEU_RFSR) & ~0xffff) | + (rep << 12) | crop_out; + + sh_veu_reg_write(veu, VEU_RFSR, value); + + return ALIGN((size_in * crop_out) / size_out, 4); +} + +static void sh_veu_configure(struct sh_veu_dev *veu) +{ + u32 src_width, src_stride, src_height; + u32 dst_width, dst_stride, dst_height; + u32 real_w, real_h; + + /* reset VEU */ + sh_veu_reg_write(veu, VEU_BSRR, 0x100); + + src_width = veu->vfmt_in.frame.width; + src_height = veu->vfmt_in.frame.height; + src_stride = ALIGN(veu->vfmt_in.frame.width, 16); + + dst_width = real_w = veu->vfmt_out.frame.width; + dst_height = real_h = veu->vfmt_out.frame.height; + /* Datasheet is unclear - whether it's always number of bytes or not */ + dst_stride = veu->vfmt_out.bytesperline; + + /* + * So far real_w == dst_width && real_h == dst_height, but it wasn't + * necessarily the case in the original vidix driver, so, it may change + * here in the future too. + */ + src_width = sh_veu_scale_h(veu, src_width, real_w, dst_width); + src_height = sh_veu_scale_v(veu, src_height, real_h, dst_height); + + sh_veu_reg_write(veu, VEU_SWR, src_stride); + sh_veu_reg_write(veu, VEU_SSR, src_width | (src_height << 16)); + sh_veu_reg_write(veu, VEU_BSSR, 0); /* not using bundle mode */ + + sh_veu_reg_write(veu, VEU_EDWR, dst_stride); + sh_veu_reg_write(veu, VEU_DACR, 0); /* unused for RGB */ + + sh_veu_reg_write(veu, VEU_SWPR, 0x67); + sh_veu_reg_write(veu, VEU_TRCR, (6 << 16) | (0 << 14) | 2 | 4); + + if (veu->is_2h) { + sh_veu_reg_write(veu, VEU_MCR00, 0x0cc5); + sh_veu_reg_write(veu, VEU_MCR01, 0x0950); + sh_veu_reg_write(veu, VEU_MCR02, 0x0000); + + sh_veu_reg_write(veu, VEU_MCR10, 0x397f); + sh_veu_reg_write(veu, VEU_MCR11, 0x0950); + sh_veu_reg_write(veu, VEU_MCR12, 0x3ccd); + + sh_veu_reg_write(veu, VEU_MCR20, 0x0000); + sh_veu_reg_write(veu, VEU_MCR21, 0x0950); + sh_veu_reg_write(veu, VEU_MCR22, 0x1023); + + sh_veu_reg_write(veu, VEU_COFFR, 0x00800010); + } +} + +static int sh_veu_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct sh_veu_file *veu_file = priv; + + if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, type)) + return -EBUSY; + + if (veu_file->cfg_needed) { + struct sh_veu_dev *veu = veu_file->veu_dev; + veu_file->cfg_needed = false; + sh_veu_configure(veu_file->veu_dev); + veu->xaction = 0; + veu->aborting = false; + } + + return v4l2_m2m_streamon(file, veu_file->veu_dev->m2m_ctx, type); +} + +static int sh_veu_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct sh_veu_file *veu_file = priv; + + if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, type)) + return -EBUSY; + + return v4l2_m2m_streamoff(file, veu_file->veu_dev->m2m_ctx, type); +} + +static const struct v4l2_ioctl_ops sh_veu_ioctl_ops = { + .vidioc_querycap = sh_veu_querycap, + + .vidioc_enum_fmt_vid_cap = sh_veu_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = sh_veu_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = sh_veu_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = sh_veu_s_fmt_vid_cap, + + .vidioc_enum_fmt_vid_out = sh_veu_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = sh_veu_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = sh_veu_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = sh_veu_s_fmt_vid_out, + + .vidioc_reqbufs = sh_veu_reqbufs, + .vidioc_querybuf = sh_veu_querybuf, + + .vidioc_qbuf = sh_veu_qbuf, + .vidioc_dqbuf = sh_veu_dqbuf, + + .vidioc_streamon = sh_veu_streamon, + .vidioc_streamoff = sh_veu_streamoff, +}; + + /* ========== Queue operations ========== */ + +static int sh_veu_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *f, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct sh_veu_dev *veu = vb2_get_drv_priv(vq); + struct sh_veu_vfmt *vfmt; + unsigned int size, count = *nbuffers; + + if (f) { + const struct v4l2_pix_format *pix = &f->fmt.pix; + const struct sh_veu_format *fmt = sh_veu_find_fmt(f); + struct v4l2_format ftmp = *f; + + if (fmt->fourcc != pix->pixelformat) + return -EINVAL; + sh_veu_try_fmt(&ftmp, fmt); + if (ftmp.fmt.pix.width != pix->width || + ftmp.fmt.pix.height != pix->height) + return -EINVAL; + size = pix->bytesperline ? pix->bytesperline * pix->height : + pix->width * pix->height * fmt->depth >> 3; + } else { + vfmt = sh_veu_get_vfmt(veu, vq->type); + size = vfmt->bytesperline * vfmt->frame.height; + } + + if (count < 2) + *nbuffers = count = 2; + + if (size * count > VIDEO_MEM_LIMIT) { + count = VIDEO_MEM_LIMIT / size; + *nbuffers = count; + } + + *nplanes = 1; + sizes[0] = size; + alloc_ctxs[0] = veu->alloc_ctx; + + dev_dbg(veu->dev, "get %d buffer(s) of size %d each.\n", count, size); + + return 0; +} + +static int sh_veu_buf_prepare(struct vb2_buffer *vb) +{ + struct sh_veu_dev *veu = vb2_get_drv_priv(vb->vb2_queue); + struct sh_veu_vfmt *vfmt; + unsigned int sizeimage; + + vfmt = sh_veu_get_vfmt(veu, vb->vb2_queue->type); + sizeimage = vfmt->bytesperline * vfmt->frame.height * + vfmt->fmt->depth / vfmt->fmt->ydepth; + + if (vb2_plane_size(vb, 0) < sizeimage) { + dev_dbg(veu->dev, "%s data will not fit into plane (%lu < %u)\n", + __func__, vb2_plane_size(vb, 0), sizeimage); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, sizeimage); + + return 0; +} + +static void sh_veu_buf_queue(struct vb2_buffer *vb) +{ + struct sh_veu_dev *veu = vb2_get_drv_priv(vb->vb2_queue); + dev_dbg(veu->dev, "%s(%d)\n", __func__, vb->v4l2_buf.type); + v4l2_m2m_buf_queue(veu->m2m_ctx, vb); +} + +static void sh_veu_wait_prepare(struct vb2_queue *q) +{ + sh_veu_unlock(vb2_get_drv_priv(q)); +} + +static void sh_veu_wait_finish(struct vb2_queue *q) +{ + sh_veu_lock(vb2_get_drv_priv(q)); +} + +static const struct vb2_ops sh_veu_qops = { + .queue_setup = sh_veu_queue_setup, + .buf_prepare = sh_veu_buf_prepare, + .buf_queue = sh_veu_buf_queue, + .wait_prepare = sh_veu_wait_prepare, + .wait_finish = sh_veu_wait_finish, +}; + +static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + int ret; + + memset(src_vq, 0, sizeof(*src_vq)); + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_MMAP | VB2_USERPTR; + src_vq->drv_priv = priv; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->ops = &sh_veu_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + + ret = vb2_queue_init(src_vq); + if (ret < 0) + return ret; + + memset(dst_vq, 0, sizeof(*dst_vq)); + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; + dst_vq->drv_priv = priv; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->ops = &sh_veu_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + + return vb2_queue_init(dst_vq); +} + + /* ========== File operations ========== */ + +static int sh_veu_open(struct file *file) +{ + struct sh_veu_dev *veu = video_drvdata(file); + struct sh_veu_file *veu_file; + + veu_file = kzalloc(sizeof(*veu_file), GFP_KERNEL); + if (!veu_file) + return -ENOMEM; + + veu_file->veu_dev = veu; + veu_file->cfg_needed = true; + + file->private_data = veu_file; + + pm_runtime_get_sync(veu->dev); + + dev_dbg(veu->dev, "Created instance %p\n", veu_file); + + return 0; +} + +static int sh_veu_release(struct file *file) +{ + struct sh_veu_dev *veu = video_drvdata(file); + struct sh_veu_file *veu_file = file->private_data; + + dev_dbg(veu->dev, "Releasing instance %p\n", veu_file); + + pm_runtime_put(veu->dev); + + if (veu_file == veu->capture) { + veu->capture = NULL; + vb2_queue_release(v4l2_m2m_get_vq(veu->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)); + } + + if (veu_file == veu->output) { + veu->output = NULL; + vb2_queue_release(v4l2_m2m_get_vq(veu->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT)); + } + + if (!veu->output && !veu->capture && veu->m2m_ctx) { + v4l2_m2m_ctx_release(veu->m2m_ctx); + veu->m2m_ctx = NULL; + } + + kfree(veu_file); + + return 0; +} + +static unsigned int sh_veu_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct sh_veu_file *veu_file = file->private_data; + + return v4l2_m2m_poll(file, veu_file->veu_dev->m2m_ctx, wait); +} + +static int sh_veu_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct sh_veu_file *veu_file = file->private_data; + + return v4l2_m2m_mmap(file, veu_file->veu_dev->m2m_ctx, vma); +} + +static const struct v4l2_file_operations sh_veu_fops = { + .owner = THIS_MODULE, + .open = sh_veu_open, + .release = sh_veu_release, + .poll = sh_veu_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = sh_veu_mmap, +}; + +static const struct video_device sh_veu_videodev = { + .name = "sh-veu", + .fops = &sh_veu_fops, + .ioctl_ops = &sh_veu_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, + .vfl_dir = VFL_DIR_M2M, +}; + +static const struct v4l2_m2m_ops sh_veu_m2m_ops = { + .device_run = sh_veu_device_run, + .job_abort = sh_veu_job_abort, +}; + +static irqreturn_t sh_veu_bh(int irq, void *dev_id) +{ + struct sh_veu_dev *veu = dev_id; + + if (veu->xaction == MEM2MEM_DEF_TRANSLEN || veu->aborting) { + v4l2_m2m_job_finish(veu->m2m_dev, veu->m2m_ctx); + veu->xaction = 0; + } else { + sh_veu_device_run(veu); + } + + return IRQ_HANDLED; +} + +static irqreturn_t sh_veu_isr(int irq, void *dev_id) +{ + struct sh_veu_dev *veu = dev_id; + struct vb2_buffer *dst; + struct vb2_buffer *src; + u32 status = sh_veu_reg_read(veu, VEU_EVTR); + + /* bundle read mode not used */ + if (!(status & 1)) + return IRQ_NONE; + + /* disable interrupt in VEU */ + sh_veu_reg_write(veu, VEU_EIER, 0); + /* halt operation */ + sh_veu_reg_write(veu, VEU_STR, 0); + /* ack int, write 0 to clear bits */ + sh_veu_reg_write(veu, VEU_EVTR, status & ~1); + + /* conversion completed */ + dst = v4l2_m2m_dst_buf_remove(veu->m2m_ctx); + src = v4l2_m2m_src_buf_remove(veu->m2m_ctx); + if (!src || !dst) + return IRQ_NONE; + + spin_lock(&veu->lock); + v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); + spin_unlock(&veu->lock); + + veu->xaction++; + + if (!veu->aborting) + return IRQ_WAKE_THREAD; + + return IRQ_HANDLED; +} + +static int __devinit sh_veu_probe(struct platform_device *pdev) +{ + struct sh_veu_dev *veu; + struct resource *reg_res; + struct video_device *vdev; + int irq, ret; + + reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + + if (!reg_res || irq <= 0) { + dev_err(&pdev->dev, "Insufficient VEU platform information.\n"); + return -ENODEV; + } + + veu = devm_kzalloc(&pdev->dev, sizeof(*veu), GFP_KERNEL); + if (!veu) + return -ENOMEM; + + veu->is_2h = resource_size(reg_res) == 0x22c; + + veu->base = devm_request_and_ioremap(&pdev->dev, reg_res); + if (!veu->base) + return -ENOMEM; + + ret = devm_request_threaded_irq(&pdev->dev, irq, sh_veu_isr, sh_veu_bh, + 0, "veu", veu); + if (ret < 0) + return ret; + + ret = v4l2_device_register(&pdev->dev, &veu->v4l2_dev); + if (ret < 0) { + dev_err(&pdev->dev, "Error registering v4l2 device\n"); + return ret; + } + + vdev = &veu->vdev; + + veu->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(veu->alloc_ctx)) { + ret = PTR_ERR(veu->alloc_ctx); + goto einitctx; + } + + *vdev = sh_veu_videodev; + spin_lock_init(&veu->lock); + mutex_init(&veu->fop_lock); + vdev->lock = &veu->fop_lock; + + video_set_drvdata(vdev, veu); + + veu->dev = &pdev->dev; + veu->vfmt_out = DEFAULT_OUT_VFMT; + veu->vfmt_in = DEFAULT_IN_VFMT; + + veu->m2m_dev = v4l2_m2m_init(&sh_veu_m2m_ops); + if (IS_ERR(veu->m2m_dev)) { + ret = PTR_ERR(veu->m2m_dev); + v4l2_err(&veu->v4l2_dev, "Failed to init mem2mem device: %d\n", ret); + goto em2minit; + } + + pm_runtime_enable(&pdev->dev); + pm_runtime_resume(&pdev->dev); + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + pm_runtime_suspend(&pdev->dev); + if (ret < 0) + goto evidreg; + + return ret; + +evidreg: + pm_runtime_disable(&pdev->dev); + v4l2_m2m_release(veu->m2m_dev); +em2minit: + vb2_dma_contig_cleanup_ctx(veu->alloc_ctx); +einitctx: + v4l2_device_unregister(&veu->v4l2_dev); + return ret; +} + +static int __devexit sh_veu_remove(struct platform_device *pdev) +{ + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct sh_veu_dev *veu = container_of(v4l2_dev, + struct sh_veu_dev, v4l2_dev); + + video_unregister_device(&veu->vdev); + pm_runtime_disable(&pdev->dev); + v4l2_m2m_release(veu->m2m_dev); + vb2_dma_contig_cleanup_ctx(veu->alloc_ctx); + v4l2_device_unregister(&veu->v4l2_dev); + + return 0; +} + +static struct platform_driver __refdata sh_veu_pdrv = { + .remove = __devexit_p(sh_veu_remove), + .driver = { + .name = "sh_veu", + .owner = THIS_MODULE, + }, +}; + +static int __init sh_veu_init(void) +{ + return platform_driver_probe(&sh_veu_pdrv, sh_veu_probe); +} + +static void __exit sh_veu_exit(void) +{ + platform_driver_unregister(&sh_veu_pdrv); +} + +module_init(sh_veu_init); +module_exit(sh_veu_exit); + +MODULE_DESCRIPTION("sh-mobile VEU mem2mem driver"); +MODULE_AUTHOR("Guennadi Liakhovetski, "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From c9a8d89673276a8a9410c68521c0b9523ed10493 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 27 Sep 2012 06:40:30 -0300 Subject: [media] media: soc-camera: use managed devm_regulator_bulk_get() Using device-managed devm_regulator_bulk_get() eliminates the need to release regulators explicitly. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/soc_camera.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 54da3a5f900c..a8ca956e7a40 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -1139,8 +1139,8 @@ static int soc_camera_probe(struct soc_camera_device *icd) if (ret < 0) return ret; - ret = regulator_bulk_get(icd->pdev, icl->num_regulators, - icl->regulators); + ret = devm_regulator_bulk_get(icd->pdev, icl->num_regulators, + icl->regulators); if (ret < 0) goto ereg; @@ -1244,7 +1244,6 @@ eadddev: evdc: ici->ops->remove(icd); eadd: - regulator_bulk_free(icl->num_regulators, icl->regulators); ereg: v4l2_ctrl_handler_free(&icd->ctrl_handler); return ret; @@ -1278,8 +1277,6 @@ static int soc_camera_remove(struct soc_camera_device *icd) } soc_camera_free_user_formats(icd); - regulator_bulk_free(icl->num_regulators, icl->regulators); - return 0; } -- cgit v1.2.3 From 57f1b1c8fd705f48eb9fcd87d054e1dc46b1cedc Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 14 Sep 2012 12:00:24 -0300 Subject: [media] media: sh-mobile-ceu-camera: runtime PM suspending doesn't have to be synchronous In both error and clean up cases there is no need to wait for runtime PM to finish suspending the device. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 2d8861c0e8f2..bf66cb4b63e0 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -572,7 +572,7 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) ret = v4l2_subdev_call(csi2_sd, core, s_power, 1); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { - pm_runtime_put_sync(ici->v4l2_dev.dev); + pm_runtime_put(ici->v4l2_dev.dev); return ret; } @@ -612,7 +612,7 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) } spin_unlock_irq(&pcdev->lock); - pm_runtime_put_sync(ici->v4l2_dev.dev); + pm_runtime_put(ici->v4l2_dev.dev); dev_info(icd->parent, "SuperH Mobile CEU driver detached from camera %d\n", -- cgit v1.2.3 From cea4c9e46c4f656a81c93f09b5e3bde38bebb160 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 8 Oct 2012 10:02:55 -0300 Subject: [media] media: soc-camera: remove superfluous JPEG checking Explicit checks for the JPEG pixel format in soc_mbus_bytes_per_line() and soc_mbus_image_size() are superfluous, because also without them these functions will perform correctly. The former will return 0 based on packing == SOC_MBUS_PACKING_VARIABLE and the latter will simply multiply the user-provided line length by the image height to obtain a frame buffer size estimate. The original version of the "media: soc_camera: don't clear pix->sizeimage in JPEG mode" patch was correct and my amendment, adding these two checks was superfluous. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/soc_mediabus.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/soc_camera/soc_mediabus.c b/drivers/media/platform/soc_camera/soc_mediabus.c index a397812635d6..89dce097a827 100644 --- a/drivers/media/platform/soc_camera/soc_mediabus.c +++ b/drivers/media/platform/soc_camera/soc_mediabus.c @@ -378,9 +378,6 @@ EXPORT_SYMBOL(soc_mbus_samples_per_pixel); s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf) { - if (mf->fourcc == V4L2_PIX_FMT_JPEG) - return 0; - if (mf->layout != SOC_MBUS_LAYOUT_PACKED) return width * mf->bits_per_sample / 8; @@ -403,9 +400,6 @@ EXPORT_SYMBOL(soc_mbus_bytes_per_line); s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf, u32 bytes_per_line, u32 height) { - if (mf->fourcc == V4L2_PIX_FMT_JPEG) - return 0; - if (mf->layout == SOC_MBUS_LAYOUT_PACKED) return bytes_per_line * height; -- cgit v1.2.3 From f8cabc3628f047fc45c62f0c4a38a88febbf8383 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 10 Oct 2012 05:02:30 -0300 Subject: [media] media: sh_mobile_csi2: use managed memory and resource allocations Use managed allocations to simplify error handling and clean up paths. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/sh_mobile_csi2.c | 23 ++++------------------ 1 file changed, 4 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/soc_camera/sh_mobile_csi2.c b/drivers/media/platform/soc_camera/sh_mobile_csi2.c index 05286500b4d4..c573be702641 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_csi2.c +++ b/drivers/media/platform/soc_camera/sh_mobile_csi2.c @@ -318,23 +318,16 @@ static __devinit int sh_csi2_probe(struct platform_device *pdev) return -EINVAL; } - priv = kzalloc(sizeof(struct sh_csi2), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_csi2), GFP_KERNEL); if (!priv) return -ENOMEM; priv->irq = irq; - if (!request_mem_region(res->start, resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "CSI2 register region already claimed\n"); - ret = -EBUSY; - goto ereqreg; - } - - priv->base = ioremap(res->start, resource_size(res)); + priv->base = devm_request_and_ioremap(&pdev->dev, res); if (!priv->base) { - ret = -ENXIO; dev_err(&pdev->dev, "Unable to ioremap CSI2 registers.\n"); - goto eremap; + return -ENXIO; } priv->pdev = pdev; @@ -357,11 +350,7 @@ static __devinit int sh_csi2_probe(struct platform_device *pdev) return 0; esdreg: - iounmap(priv->base); -eremap: - release_mem_region(res->start, resource_size(res)); -ereqreg: - kfree(priv); + platform_set_drvdata(pdev, NULL); return ret; } @@ -369,14 +358,10 @@ ereqreg: static __devexit int sh_csi2_remove(struct platform_device *pdev) { struct sh_csi2 *priv = platform_get_drvdata(pdev); - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); v4l2_device_unregister_subdev(&priv->subdev); pm_runtime_disable(&pdev->dev); - iounmap(priv->base); - release_mem_region(res->start, resource_size(res)); platform_set_drvdata(pdev, NULL); - kfree(priv); return 0; } -- cgit v1.2.3 From a500a185462d08f63ec3c8a0ba3185d1b84cb85d Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 10 Oct 2012 05:18:51 -0300 Subject: [media] sh_mobile_ceu_camera: use managed memory and resource allocations Use managed allocations to simplify error handling and clean up paths. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- .../platform/soc_camera/sh_mobile_ceu_camera.c | 32 ++++++---------------- 1 file changed, 9 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index bf66cb4b63e0..27eeca15bbf0 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -2088,15 +2088,13 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (!res || (int)irq <= 0) { dev_err(&pdev->dev, "Not enough CEU platform resources.\n"); - err = -ENODEV; - goto exit; + return -ENODEV; } - pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL); + pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL); if (!pcdev) { dev_err(&pdev->dev, "Could not allocate pcdev\n"); - err = -ENOMEM; - goto exit; + return -ENOMEM; } INIT_LIST_HEAD(&pcdev->capture); @@ -2105,19 +2103,17 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) pcdev->pdata = pdev->dev.platform_data; if (!pcdev->pdata) { - err = -EINVAL; dev_err(&pdev->dev, "CEU platform data not set.\n"); - goto exit_kfree; + return -EINVAL; } pcdev->max_width = pcdev->pdata->max_width ? : 2560; pcdev->max_height = pcdev->pdata->max_height ? : 1920; - base = ioremap_nocache(res->start, resource_size(res)); + base = devm_request_and_ioremap(&pdev->dev, res); if (!base) { - err = -ENXIO; dev_err(&pdev->dev, "Unable to ioremap CEU registers.\n"); - goto exit_kfree; + return -ENXIO; } pcdev->irq = irq; @@ -2133,16 +2129,15 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) DMA_MEMORY_EXCLUSIVE); if (!err) { dev_err(&pdev->dev, "Unable to declare CEU memory.\n"); - err = -ENXIO; - goto exit_iounmap; + return -ENXIO; } pcdev->video_limit = resource_size(res); } /* request irq */ - err = request_irq(pcdev->irq, sh_mobile_ceu_irq, IRQF_DISABLED, - dev_name(&pdev->dev), pcdev); + err = devm_request_irq(&pdev->dev, pcdev->irq, sh_mobile_ceu_irq, + IRQF_DISABLED, dev_name(&pdev->dev), pcdev); if (err) { dev_err(&pdev->dev, "Unable to register CEU interrupt.\n"); goto exit_release_mem; @@ -2246,15 +2241,9 @@ exit_free_ctx: vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); exit_free_clk: pm_runtime_disable(&pdev->dev); - free_irq(pcdev->irq, pcdev); exit_release_mem: if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) dma_release_declared_memory(&pdev->dev); -exit_iounmap: - iounmap(base); -exit_kfree: - kfree(pcdev); -exit: return err; } @@ -2267,10 +2256,8 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev) soc_camera_host_unregister(soc_host); pm_runtime_disable(&pdev->dev); - free_irq(pcdev->irq, pcdev); if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) dma_release_declared_memory(&pdev->dev); - iounmap(pcdev->base); vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); if (csi2_pdev && csi2_pdev->dev.driver) { struct module *csi2_drv = csi2_pdev->dev.driver->owner; @@ -2279,7 +2266,6 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev) platform_device_put(csi2_pdev); module_put(csi2_drv); } - kfree(pcdev); return 0; } -- cgit v1.2.3 From 6ec5575c381de50b17e68796435f20ce1b27de79 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 30 Oct 2012 11:29:00 -0300 Subject: [media] media: mx2_camera: Add image size HW limits The CSI on i.MX27 has some constraints regarding image width. This patch makes sure those requirements are met in try_fmt(). Signed-off-by: Javier Martin [g.liakhovetski@gmx.de: make constraint i.MX27-specific] Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/mx2_camera.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 77529f860386..2c148028d8c0 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -1394,8 +1394,6 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, return -EINVAL; } - /* FIXME: implement MX27 limits */ - /* limit to MX25 hardware capabilities */ if (cpu_is_mx25()) { if (xlate->host_fmt->bits_per_sample <= 8) @@ -1427,6 +1425,12 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, pix->sizeimage = soc_mbus_image_size(xlate->host_fmt, pix->bytesperline, pix->height); } + } else { + /* + * Width must be a multiple of 8 as requested by the CSI. + * (Table 39-2 in the i.MX27 Reference Manual). + */ + pix->width &= ~0x7; } /* limit to sensor capabilities */ -- cgit v1.2.3 From 59dad49e5c176d8c9c0d06f6b744d0a54c578310 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 20 Dec 2012 14:53:26 -0200 Subject: [media] sh_veu.c: fix two compilation warnings drivers/media/platform/sh_veu.c:269:2: warning: format '%x' expects argument of type 'unsigned int', but argument 5 has type 'dma_addr_t' [-Wformat] drivers/media/platform/sh_veu.c:276:2: warning: format '%x' expects argument of type 'unsigned int', but argument 5 has type 'dma_addr_t' [-Wformat] Cc: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_veu.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index 7365fb5a2615..a0186768479d 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -266,15 +266,17 @@ static void sh_veu_process(struct sh_veu_dev *veu, sh_veu_reg_write(veu, VEU_DAYR, addr + veu->vfmt_out.offset_y); sh_veu_reg_write(veu, VEU_DACR, veu->vfmt_out.offset_c ? addr + veu->vfmt_out.offset_c : 0); - dev_dbg(veu->dev, "%s(): dst base %x, y: %x, c: %x\n", __func__, - addr, veu->vfmt_out.offset_y, veu->vfmt_out.offset_c); + dev_dbg(veu->dev, "%s(): dst base %lx, y: %x, c: %x\n", __func__, + (unsigned long)addr, + veu->vfmt_out.offset_y, veu->vfmt_out.offset_c); addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); sh_veu_reg_write(veu, VEU_SAYR, addr + veu->vfmt_in.offset_y); sh_veu_reg_write(veu, VEU_SACR, veu->vfmt_in.offset_c ? addr + veu->vfmt_in.offset_c : 0); - dev_dbg(veu->dev, "%s(): src base %x, y: %x, c: %x\n", __func__, - addr, veu->vfmt_in.offset_y, veu->vfmt_in.offset_c); + dev_dbg(veu->dev, "%s(): src base %lx, y: %x, c: %x\n", __func__, + (unsigned long)addr, + veu->vfmt_in.offset_y, veu->vfmt_in.offset_c); sh_veu_reg_write(veu, VEU_STR, 1); -- cgit v1.2.3 From b391b0ef0f9399dcba8fa025ab5e5d4229847467 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 20 Dec 2012 15:21:23 -0200 Subject: [media] tm6000-video.c: warning fix drivers/media/usb/tm6000/tm6000-video.c: In function '__check_keep_urb': drivers/media/usb/tm6000/tm6000-video.c:1926:1: warning: return from incompatible pointer type [enabled by default] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index 82968017a253..1edc2517603f 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -57,7 +57,7 @@ static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ static int radio_nr = -1; /* /dev/radioN, -1 for autodetect */ -static int keep_urb; /* keep urb buffers allocated */ +static bool keep_urb; /* keep urb buffers allocated */ /* Debug level */ int tm6000_debug; -- cgit v1.2.3 From 4bb891ebf60eb43ebd04e09bbcad24013067873f Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 24 Oct 2012 08:14:16 -0300 Subject: [media] ivtv: ivtv-driver: Replace 'flush_work_sync()' Since commit 43829731d (workqueue: deprecate flush[_delayed]_work_sync()), flush_work() should be used instead of flush_work_sync(). Signed-off-by: Fabio Estevam Acked-by: Tejun Heo Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ivtv/ivtv-driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c index 74e9a5032364..5d0a5df61078 100644 --- a/drivers/media/pci/ivtv/ivtv-driver.c +++ b/drivers/media/pci/ivtv/ivtv-driver.c @@ -304,7 +304,7 @@ static void request_modules(struct ivtv *dev) static void flush_request_modules(struct ivtv *dev) { - flush_work_sync(&dev->request_module_wk); + flush_work(&dev->request_module_wk); } #else #define request_modules(dev) -- cgit v1.2.3 From c0c36b941b6f0be6ac74f340040cbb29d6a0b06c Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 19 Dec 2012 16:08:43 +0000 Subject: drm/i915: Return the real error code from intel_set_mode() Note: This patch also adds a little helper intel_crtc_restore_mode for the common case where we do a full modeset but with the same parameters, e.g. to undo bios damage or update a property. Signed-off-by: Chris Wilson Reviewed-by: Rodrigo Vivi [danvet: Added note.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 56 ++++++++++++++++++------------------ drivers/gpu/drm/i915/intel_dp.c | 7 ++--- drivers/gpu/drm/i915/intel_drv.h | 5 ++-- drivers/gpu/drm/i915/intel_hdmi.c | 7 ++--- drivers/gpu/drm/i915/intel_lvds.c | 3 +- drivers/gpu/drm/i915/intel_sdvo.c | 7 ++--- drivers/gpu/drm/i915/intel_tv.c | 3 +- 7 files changed, 39 insertions(+), 49 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f7f4ef17cbe4..1464e472ce44 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6313,7 +6313,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, return false; } - if (!intel_set_mode(crtc, mode, 0, 0, fb)) { + if (intel_set_mode(crtc, mode, 0, 0, fb)) { DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n"); if (old->release_fb) old->release_fb->funcs->destroy(old->release_fb); @@ -7426,22 +7426,20 @@ intel_modeset_check_state(struct drm_device *dev) } } -bool intel_set_mode(struct drm_crtc *crtc, - struct drm_display_mode *mode, - int x, int y, struct drm_framebuffer *fb) +int intel_set_mode(struct drm_crtc *crtc, + struct drm_display_mode *mode, + int x, int y, struct drm_framebuffer *fb) { struct drm_device *dev = crtc->dev; drm_i915_private_t *dev_priv = dev->dev_private; struct drm_display_mode *adjusted_mode, *saved_mode, *saved_hwmode; struct intel_crtc *intel_crtc; unsigned disable_pipes, prepare_pipes, modeset_pipes; - bool ret = true; + int ret = 0; saved_mode = kmalloc(2 * sizeof(*saved_mode), GFP_KERNEL); - if (!saved_mode) { - DRM_ERROR("i915: Could not allocate saved display mode.\n"); - return false; - } + if (!saved_mode) + return -ENOMEM; saved_hwmode = saved_mode + 1; intel_modeset_affected_pipes(crtc, &modeset_pipes, @@ -7465,7 +7463,7 @@ bool intel_set_mode(struct drm_crtc *crtc, if (modeset_pipes) { adjusted_mode = intel_modeset_adjusted_mode(crtc, mode); if (IS_ERR(adjusted_mode)) { - ret = false; + ret = PTR_ERR(adjusted_mode); goto out; } } @@ -7492,11 +7490,11 @@ bool intel_set_mode(struct drm_crtc *crtc, * on the DPLL. */ for_each_intel_crtc_masked(dev, modeset_pipes, intel_crtc) { - ret = !intel_crtc_mode_set(&intel_crtc->base, - mode, adjusted_mode, - x, y, fb); - if (!ret) - goto done; + ret = intel_crtc_mode_set(&intel_crtc->base, + mode, adjusted_mode, + x, y, fb); + if (ret) + goto done; } /* Now enable the clocks, plane, pipe, and connectors that we set up. */ @@ -7517,7 +7515,7 @@ bool intel_set_mode(struct drm_crtc *crtc, /* FIXME: add subpixel order */ done: drm_mode_destroy(dev, adjusted_mode); - if (!ret && crtc->enabled) { + if (ret && crtc->enabled) { crtc->hwmode = *saved_hwmode; crtc->mode = *saved_mode; } else { @@ -7529,6 +7527,11 @@ out: return ret; } +void intel_crtc_restore_mode(struct drm_crtc *crtc) +{ + intel_set_mode(crtc, &crtc->mode, crtc->x, crtc->y, crtc->fb); +} + #undef for_each_intel_crtc_masked static void intel_set_config_free(struct intel_set_config *config) @@ -7798,11 +7801,11 @@ static int intel_crtc_set_config(struct drm_mode_set *set) drm_mode_debug_printmodeline(set->mode); } - if (!intel_set_mode(set->crtc, set->mode, - set->x, set->y, set->fb)) { - DRM_ERROR("failed to set mode on [CRTC:%d]\n", - set->crtc->base.id); - ret = -EINVAL; + ret = intel_set_mode(set->crtc, set->mode, + set->x, set->y, set->fb); + if (ret) { + DRM_ERROR("failed to set mode on [CRTC:%d], err = %d\n", + set->crtc->base.id, ret); goto fail; } } else if (config->fb_changed) { @@ -7819,8 +7822,8 @@ fail: /* Try to restore the config */ if (config->mode_changed && - !intel_set_mode(save_set.crtc, save_set.mode, - save_set.x, save_set.y, save_set.fb)) + intel_set_mode(save_set.crtc, save_set.mode, + save_set.x, save_set.y, save_set.fb)) DRM_ERROR("failed to restore config after modeset failure\n"); out_config: @@ -8804,11 +8807,8 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, } if (force_restore) { - for_each_pipe(pipe) { - crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); - intel_set_mode(&crtc->base, &crtc->base.mode, - crtc->base.x, crtc->base.y, crtc->base.fb); - } + for_each_pipe(pipe) + intel_crtc_restore_mode(dev_priv->pipe_to_crtc_mapping[pipe]); } else { intel_modeset_update_staged_output_state(dev); } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 2d3b26832397..1dd89d5fe511 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2483,11 +2483,8 @@ intel_dp_set_property(struct drm_connector *connector, return -EINVAL; done: - if (intel_encoder->base.crtc) { - struct drm_crtc *crtc = intel_encoder->base.crtc; - intel_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, crtc->fb); - } + if (intel_encoder->base.crtc) + intel_crtc_restore_mode(intel_encoder->base.crtc); return 0; } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 53d4c8fec2a0..116580b623dd 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -501,9 +501,10 @@ struct intel_set_config { bool mode_changed; }; -extern bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, - int x, int y, struct drm_framebuffer *old_fb); +extern int intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, + int x, int y, struct drm_framebuffer *old_fb); extern void intel_modeset_disable(struct drm_device *dev); +extern void intel_crtc_restore_mode(struct drm_crtc *crtc); extern void intel_crtc_load_lut(struct drm_crtc *crtc); extern void intel_crtc_update_dpms(struct drm_crtc *crtc); extern void intel_encoder_noop(struct drm_encoder *encoder); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 9f834d324cfd..6387f9b0df99 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -917,11 +917,8 @@ intel_hdmi_set_property(struct drm_connector *connector, return -EINVAL; done: - if (intel_dig_port->base.base.crtc) { - struct drm_crtc *crtc = intel_dig_port->base.base.crtc; - intel_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, crtc->fb); - } + if (intel_dig_port->base.base.crtc) + intel_crtc_restore_mode(intel_dig_port->base.base.crtc); return 0; } diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 778106961e80..8c61876dbe95 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -646,8 +646,7 @@ static int intel_lvds_set_property(struct drm_connector *connector, * If the CRTC is enabled, the display will be changed * according to the new panel fitting mode. */ - intel_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, crtc->fb); + intel_crtc_restore_mode(crtc); } } diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 0e03985b0fe4..ea2e79f63d2b 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1997,11 +1997,8 @@ set_value: done: - if (intel_sdvo->base.base.crtc) { - struct drm_crtc *crtc = intel_sdvo->base.base.crtc; - intel_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, crtc->fb); - } + if (intel_sdvo->base.base.crtc) + intel_crtc_restore_mode(intel_sdvo->base.base.crtc); return 0; #undef CHECK_PROPERTY diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index ea93520c1278..984a113c5d13 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1479,8 +1479,7 @@ intel_tv_set_property(struct drm_connector *connector, struct drm_property *prop } if (changed && crtc) - intel_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, crtc->fb); + intel_crtc_restore_mode(crtc); out: return ret; } -- cgit v1.2.3 From 80f5ab097b87c86581cb9736a8e55c5a3047d4bb Mon Sep 17 00:00:00 2001 From: Shaun Ruffell Date: Sun, 19 Aug 2012 01:11:24 -0300 Subject: edac: edac_mc no longer deals with kobjects directly There are no more embedded kobjects in struct mem_ctl_info. Remove a header and a comment that does not reflect the code anymore. Signed-off-by: Shaun Ruffell Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/edac_mc.c | 7 ------- include/linux/edac.h | 1 - 2 files changed, 8 deletions(-) (limited to 'drivers') diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 281f566a5513..a641f623fffd 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -441,13 +441,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, mci->op_state = OP_ALLOC; - /* at this point, the root kobj is valid, and in order to - * 'free' the object, then the function: - * edac_mc_unregister_sysfs_main_kobj() must be called - * which will perform kobj unregistration and the actual free - * will occur during the kobject callback operation - */ - return mci; error: diff --git a/include/linux/edac.h b/include/linux/edac.h index 1b8c02b36f76..4784213c819d 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -14,7 +14,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3 From da14d93d95ee3923b6e690220bdb7ce191e76d95 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 25 Oct 2012 09:07:21 -0200 Subject: sb_edac: add a missing /n on a debug message [ 17.024963] EDAC DEBUG: get_memory_layout: TOHM: 132.160 GB (0x0000002043ffffff)<7>[ 17.024971] EDAC DEBUG: get_memory_layout: SAD#0 DRAM up to 33.792 GB (0x0000000840000000) Interleave: 8:6 reg=0x000083c3 Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/sb_edac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c index 5715b7c2c517..9ad24242ea18 100644 --- a/drivers/edac/sb_edac.c +++ b/drivers/edac/sb_edac.c @@ -639,7 +639,7 @@ static void get_memory_layout(const struct mem_ctl_info *mci) tmp_mb = (1 + pvt->tohm) >> 20; mb = div_u64_rem(tmp_mb, 1000, &kb); - edac_dbg(0, "TOHM: %u.%03u GB (0x%016Lx)", mb, kb, (u64)pvt->tohm); + edac_dbg(0, "TOHM: %u.%03u GB (0x%016Lx)\n", mb, kb, (u64)pvt->tohm); /* * Step 2) Get SAD range and SAD Interleave list -- cgit v1.2.3 From c31d34fe92e9d0b146907d7294269ee03e1b403f Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Sun, 29 Jan 2012 23:04:32 +0100 Subject: i7core_edac: fix erroneous size of static array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove size from lookup arrays and mark them as const. Reviewed-by: Jesper Juhl Signed-off-by: Niklas Söderlund Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 10c8c00d6469..ad5f934c95d3 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -420,21 +420,21 @@ static inline int numdimms(u32 dimms) static inline int numrank(u32 rank) { - static int ranks[4] = { 1, 2, 4, -EINVAL }; + static const int ranks[] = { 1, 2, 4, -EINVAL }; return ranks[rank & 0x3]; } static inline int numbank(u32 bank) { - static int banks[4] = { 4, 8, 16, -EINVAL }; + static const int banks[] = { 4, 8, 16, -EINVAL }; return banks[bank & 0x3]; } static inline int numrow(u32 row) { - static int rows[8] = { + static const int rows[] = { 1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, -EINVAL, -EINVAL, -EINVAL, }; @@ -444,7 +444,7 @@ static inline int numrow(u32 row) static inline int numcol(u32 col) { - static int cols[8] = { + static const int cols[] = { 1 << 10, 1 << 11, 1 << 12, -EINVAL, }; return cols[col & 0x3]; -- cgit v1.2.3 From 1c069100c1f5577ecde06b3a366b73f520854c4e Mon Sep 17 00:00:00 2001 From: Lans Zhang Date: Thu, 20 Dec 2012 10:16:07 +0800 Subject: i7core_edac: fix kernel crash on unloading i7core_edac. It is easy to trigger this crash on 3.7.0: root@intel_westmere_ep-3:~# modprobe -r i7core_edac EDAC PCI: Removed device 0 for i7core_edac EDAC PCI controller: DEV 0000:fe:03.0 EDAC MC: Removed device 1 for i7core_edac.c i7 core #1: DEV 0000:fe:03.0 EDAC PCI: Removed device 1 for i7core_edac EDAC PCI controller: DEV 0000:ff:03.0 EDAC MC: Removed device 0 for i7core_edac.c i7 core #0: DEV 0000:ff:03.0 BUG: unable to handle kernel NULL pointer dereference at 0000000000000110 IP: [] __blocking_notifier_call_chain+0x29/0x80 PGD 1eaae7067 PUD 1e96e4067 PMD 0 Oops: 0000 [#1] PREEMPT SMP Modules linked in: minix acpi_cpufreq freq_table mperf ioatdma processor edac_core(-) iTCO_wdt coretemp evdev hwmon lpc_ich dca mfd_core crc32c_intel ioapic [last unloaded: i7core_edac] CPU 3 Pid: 1268, comm: modprobe Not tainted 3.7.0-WR5.0.1.0_standard+ #30 Intel Corporation S5520HC/S5520HC RIP: 0010:[] [] __blocking_notifier_call_chain+0x29/0x80 RSP: 0018:ffff8801eb12de28 EFLAGS: 00010246 RAX: 0000000000000000 RBX: 00000000000000f0 RCX: 00000000ffffffff RDX: ffff88012b452800 RSI: 0000000000000002 RDI: 00000000000000f0 RBP: ffff8801eb12de68 R08: 0000000000000000 R09: ffffea0004ad1118 R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000 R13: ffff8801eb12dee8 R14: ffff88012b452800 R15: 000000000060e518 FS: 00007f9ea95a9700(0000) GS:ffff8801efc20000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000110 CR3: 00000001262f1000 CR4: 00000000000007e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process modprobe (pid: 1268, threadinfo ffff8801eb12c000, task ffff8801e8421690) Stack: ffff88012c802a00 ffff88012b445ec0 ffff88012c802300 ffff88012b452800 0000000000000000 ffff8801eb12dee8 000000000060e080 000000000060e518 ffff8801eb12de78 ffffffff82069f56 ffff8801eb12dea8 ffffffff824ead7c Call Trace: [] blocking_notifier_call_chain+0x16/0x20 [] device_del+0x3c/0x1d0 [] edac_mc_sysfs_exit+0x1c/0x2f [edac_core] [] edac_exit+0x4f/0x56 [edac_core] [] sys_delete_module+0x17a/0x240 [] ? vm_munmap+0x5c/0x80 [] system_call_fastpath+0x16/0x1b Code: 90 90 55 48 89 e5 48 83 ec 40 48 89 5d d8 4c 89 65 e0 4c 89 6d e8 4c 89 75 f0 4c 89 7d f8 66 66 66 66 90 31 c0 49 89 d6 48 89 fb <48> 8b 57 20 49 89 f5 41 89 cf 4c 8d 67 20 48 85 d2 74 2c 4c 89 RIP [] __blocking_notifier_call_chain+0x29/0x80 RSP CR2: 0000000000000110 ---[ end trace b69acf12ccad1c0d ]--- Usually, edac_subsys is grabbed one time by pci at initialization. But edac_subsys may be released several times if multiple pci MCs exist. The fix just makes the operations balanced. Signed-off-by: Lans Zhang Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/edac_pci_sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/edac/edac_pci_sysfs.c b/drivers/edac/edac_pci_sysfs.c index dc6e905ee1a5..7684426fafa2 100644 --- a/drivers/edac/edac_pci_sysfs.c +++ b/drivers/edac/edac_pci_sysfs.c @@ -429,8 +429,8 @@ static void edac_pci_main_kobj_teardown(void) if (atomic_dec_return(&edac_pci_sysfs_refcount) == 0) { edac_dbg(0, "called kobject_put on main kobj\n"); kobject_put(edac_pci_top_main_kobj); + edac_put_sysfs_subsys(); } - edac_put_sysfs_subsys(); } /* -- cgit v1.2.3 From aecede4c45ae32944e822ef98d4837733837887d Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Wed, 7 Nov 2012 03:37:07 -0300 Subject: [media] exynos-gsc: Adding tiled multi-planar format to G-Scaler Adding V4L2_PIX_FMT_NV12MT_16X16 to G-Scaler supported formats. If the output or input format is V4L2_PIX_FMT_NV12MT_16X16, configure G-Scaler to use GSC_IN_TILE_MODE. [s.nawrocki: shortened the pixel format description] Signed-off-by: Shaik Ameer Basha Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-core.c | 9 +++++++++ drivers/media/platform/exynos-gsc/gsc-core.h | 5 +++++ drivers/media/platform/exynos-gsc/gsc-regs.c | 6 ++++++ 3 files changed, 20 insertions(+) (limited to 'drivers') diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index cc7b218d047c..6d6f65d8c1e5 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -185,6 +185,15 @@ static const struct gsc_fmt gsc_formats[] = { .corder = GSC_CRCB, .num_planes = 3, .num_comp = 3, + }, { + .name = "YUV 4:2:0 n.c. 2p, Y/CbCr tiled", + .pixelformat = V4L2_PIX_FMT_NV12MT_16X16, + .depth = { 8, 4 }, + .color = GSC_YUV420, + .yorder = GSC_LSB_Y, + .corder = GSC_CBCR, + .num_planes = 2, + .num_comp = 2, } }; diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h index 5f157efd24f0..cc19bba09bd1 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.h +++ b/drivers/media/platform/exynos-gsc/gsc-core.h @@ -427,6 +427,11 @@ static inline void gsc_ctx_state_lock_clear(u32 state, struct gsc_ctx *ctx) spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags); } +static inline int is_tiled(const struct gsc_fmt *fmt) +{ + return fmt->pixelformat == V4L2_PIX_FMT_NV12MT_16X16; +} + static inline void gsc_hw_enable_control(struct gsc_dev *dev, bool on) { u32 cfg = readl(dev->regs + GSC_ENABLE); diff --git a/drivers/media/platform/exynos-gsc/gsc-regs.c b/drivers/media/platform/exynos-gsc/gsc-regs.c index 0146b354dc22..6f5b5a486cf3 100644 --- a/drivers/media/platform/exynos-gsc/gsc-regs.c +++ b/drivers/media/platform/exynos-gsc/gsc-regs.c @@ -214,6 +214,9 @@ void gsc_hw_set_in_image_format(struct gsc_ctx *ctx) break; } + if (is_tiled(frame->fmt)) + cfg |= GSC_IN_TILE_C_16x8 | GSC_IN_TILE_MODE; + writel(cfg, dev->regs + GSC_IN_CON); } @@ -334,6 +337,9 @@ void gsc_hw_set_out_image_format(struct gsc_ctx *ctx) break; } + if (is_tiled(frame->fmt)) + cfg |= GSC_OUT_TILE_C_16x8 | GSC_OUT_TILE_MODE; + end_set: writel(cfg, dev->regs + GSC_OUT_CON); } -- cgit v1.2.3 From f60e160e126bdd8f0d928cd8b3fce54659597394 Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Thu, 22 Nov 2012 02:25:06 -0300 Subject: [media] exynos-gsc: propagate timestamps from src to dst buffers Make gsc-m2m propagate the timestamp field from source to destination buffers. Signed-off-by: John Sheu Signed-off-by: Shaik Ameer Basha Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-m2m.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index c267c57c76fd..a8e5050340aa 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -99,22 +99,28 @@ static void gsc_m2m_job_abort(void *priv) gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); } -static int gsc_fill_addr(struct gsc_ctx *ctx) +static int gsc_get_bufs(struct gsc_ctx *ctx) { struct gsc_frame *s_frame, *d_frame; - struct vb2_buffer *vb = NULL; + struct vb2_buffer *src_vb, *dst_vb; int ret; s_frame = &ctx->s_frame; d_frame = &ctx->d_frame; - vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - ret = gsc_prepare_addr(ctx, vb, s_frame, &s_frame->addr); + src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + ret = gsc_prepare_addr(ctx, src_vb, s_frame, &s_frame->addr); + if (ret) + return ret; + + dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + ret = gsc_prepare_addr(ctx, dst_vb, d_frame, &d_frame->addr); if (ret) return ret; - vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - return gsc_prepare_addr(ctx, vb, d_frame, &d_frame->addr); + dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; + + return 0; } static void gsc_m2m_device_run(void *priv) @@ -148,7 +154,7 @@ static void gsc_m2m_device_run(void *priv) goto put_device; } - ret = gsc_fill_addr(ctx); + ret = gsc_get_bufs(ctx); if (ret) { pr_err("Wrong address"); goto put_device; -- cgit v1.2.3 From 2c8cc13f36b0563c62aa18454c8f853c287fdfe9 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 23 Nov 2012 08:04:42 -0300 Subject: [media] exynos-gsc: Fix checkpatch warning in gsc-m2m.c Fixes the following warning: WARNING: space prohibited between function name and open parenthesis '(' FILE: media/platform/exynos-gsc/gsc-m2m.c:606: ctx = kzalloc(sizeof (*ctx), GFP_KERNEL); Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-m2m.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index a8e5050340aa..0d06d6c6f373 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -603,7 +603,7 @@ static int gsc_m2m_open(struct file *file) if (mutex_lock_interruptible(&gsc->lock)) return -ERESTARTSYS; - ctx = kzalloc(sizeof (*ctx), GFP_KERNEL); + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) { ret = -ENOMEM; goto unlock; -- cgit v1.2.3 From 9318ab69c50b82f9f513a20955ebd2cb1f482adc Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 26 Nov 2012 03:20:19 -0300 Subject: [media] exynos-gsc: Rearrange error messages for valid prints In case of clk_prepare failure, the function gsc_clk_get also prints "failed to get clock" which is not correct. Hence move the error messages to their respective blocks. While at it, also renamed the labels meaningfully. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-core.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index 6d6f65d8c1e5..45bcfa7506db 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -1017,25 +1017,26 @@ static int gsc_clk_get(struct gsc_dev *gsc) dev_dbg(&gsc->pdev->dev, "gsc_clk_get Called\n"); gsc->clock = clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME); - if (IS_ERR(gsc->clock)) - goto err_print; + if (IS_ERR(gsc->clock)) { + dev_err(&gsc->pdev->dev, "failed to get clock~~~: %s\n", + GSC_CLOCK_GATE_NAME); + goto err_clk_get; + } ret = clk_prepare(gsc->clock); if (ret < 0) { + dev_err(&gsc->pdev->dev, "clock prepare failed for clock: %s\n", + GSC_CLOCK_GATE_NAME); clk_put(gsc->clock); gsc->clock = NULL; - goto err; + goto err_clk_prepare; } return 0; -err: - dev_err(&gsc->pdev->dev, "clock prepare failed for clock: %s\n", - GSC_CLOCK_GATE_NAME); +err_clk_prepare: gsc_clk_put(gsc); -err_print: - dev_err(&gsc->pdev->dev, "failed to get clock~~~: %s\n", - GSC_CLOCK_GATE_NAME); +err_clk_get: return -ENXIO; } -- cgit v1.2.3 From 21ae96d3973b926e89edf52bae560475105455ef Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 26 Nov 2012 03:20:20 -0300 Subject: [media] exynos-gsc: Correct the clock handling Make sure there is no unbalanced clk_unprepare call and add missing clock release in the driver's remove() callback. Signed-off-by: Sylwester Nawrocki Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-core.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index 45bcfa7506db..c8b82c006bd0 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -1002,12 +1002,11 @@ static void *gsc_get_drv_data(struct platform_device *pdev) static void gsc_clk_put(struct gsc_dev *gsc) { - if (IS_ERR_OR_NULL(gsc->clock)) - return; - - clk_unprepare(gsc->clock); - clk_put(gsc->clock); - gsc->clock = NULL; + if (!IS_ERR(gsc->clock)) { + clk_unprepare(gsc->clock); + clk_put(gsc->clock); + gsc->clock = NULL; + } } static int gsc_clk_get(struct gsc_dev *gsc) @@ -1028,7 +1027,7 @@ static int gsc_clk_get(struct gsc_dev *gsc) dev_err(&gsc->pdev->dev, "clock prepare failed for clock: %s\n", GSC_CLOCK_GATE_NAME); clk_put(gsc->clock); - gsc->clock = NULL; + gsc->clock = ERR_PTR(-EINVAL); goto err_clk_prepare; } @@ -1106,6 +1105,7 @@ static int gsc_probe(struct platform_device *pdev) init_waitqueue_head(&gsc->irq_queue); spin_lock_init(&gsc->slock); mutex_init(&gsc->lock); + gsc->clock = ERR_PTR(-EINVAL); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); gsc->regs = devm_request_and_ioremap(dev, res); @@ -1169,6 +1169,7 @@ static int __devexit gsc_remove(struct platform_device *pdev) vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx); pm_runtime_disable(&pdev->dev); + gsc_clk_put(gsc); dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); return 0; -- cgit v1.2.3 From e2732ae5dd9c765732dda6b7120eb74896562c22 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 26 Nov 2012 03:20:21 -0300 Subject: [media] exynos-gsc: Use devm_clk_get() devm_clk_get() is a device managed function and makes error handling a bit simpler. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-core.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index c8b82c006bd0..0c22ad570500 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -1002,11 +1002,8 @@ static void *gsc_get_drv_data(struct platform_device *pdev) static void gsc_clk_put(struct gsc_dev *gsc) { - if (!IS_ERR(gsc->clock)) { + if (!IS_ERR(gsc->clock)) clk_unprepare(gsc->clock); - clk_put(gsc->clock); - gsc->clock = NULL; - } } static int gsc_clk_get(struct gsc_dev *gsc) @@ -1015,28 +1012,22 @@ static int gsc_clk_get(struct gsc_dev *gsc) dev_dbg(&gsc->pdev->dev, "gsc_clk_get Called\n"); - gsc->clock = clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME); + gsc->clock = devm_clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME); if (IS_ERR(gsc->clock)) { dev_err(&gsc->pdev->dev, "failed to get clock~~~: %s\n", GSC_CLOCK_GATE_NAME); - goto err_clk_get; + return PTR_ERR(gsc->clock); } ret = clk_prepare(gsc->clock); if (ret < 0) { dev_err(&gsc->pdev->dev, "clock prepare failed for clock: %s\n", GSC_CLOCK_GATE_NAME); - clk_put(gsc->clock); gsc->clock = ERR_PTR(-EINVAL); - goto err_clk_prepare; + return ret; } return 0; - -err_clk_prepare: - gsc_clk_put(gsc); -err_clk_get: - return -ENXIO; } static int gsc_m2m_suspend(struct gsc_dev *gsc) -- cgit v1.2.3 From 1b5901331ff3af4bdc1b998a056a248c9924e2d1 Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Tue, 27 Nov 2012 09:48:58 -0300 Subject: [media] exynos-gsc: modify number of output/capture buffers G-Scaler src buffer count as well as destination buffer count is increased to 32. This is required for G-Scaler to interface with MFC, as MFC demands 32 capture buffers for some H264 streams. Signed-off-by: Shaik Ameer Basha Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index 0c22ad570500..ae885c7ebc41 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -944,8 +944,8 @@ static struct gsc_variant gsc_v_100_variant = { .pix_max = &gsc_v_100_max, .pix_min = &gsc_v_100_min, .pix_align = &gsc_v_100_align, - .in_buf_cnt = 8, - .out_buf_cnt = 16, + .in_buf_cnt = 32, + .out_buf_cnt = 32, .sc_up_max = 8, .sc_down_max = 16, .poly_sc_down_max = 4, -- cgit v1.2.3 From abd23295648a9e3ae72a806e70a510d3dcd8b374 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 15 Sep 2012 07:51:47 -0300 Subject: [media] v4l: Helper function for obtaining timestamps v4l2_get_timestamp() produces a monotonic timestamp but unlike ktime_get_ts(), it uses struct timeval instead of struct timespec, saving the drivers the conversion job when getting timestamps for v4l2_buffer's timestamp field. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-common.c | 10 ++++++++++ include/media/v4l2-common.h | 2 ++ 2 files changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 380ddd89fa4c..614316f9b7a4 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -978,3 +978,13 @@ const struct v4l2_frmsize_discrete *v4l2_find_nearest_format( return best; } EXPORT_SYMBOL_GPL(v4l2_find_nearest_format); + +void v4l2_get_timestamp(struct timeval *tv) +{ + struct timespec ts; + + ktime_get_ts(&ts); + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; +} +EXPORT_SYMBOL_GPL(v4l2_get_timestamp); diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 1a0b2db4c5d3..ec7c9c00b256 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -225,4 +225,6 @@ bool v4l2_detect_gtf(unsigned frame_height, unsigned hfreq, unsigned vsync, struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait); +void v4l2_get_timestamp(struct timeval *tv); + #endif /* V4L2_COMMON_H_ */ -- cgit v1.2.3 From 8e6057b510aad354e017c6dfca7f386a0eb91b63 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 15 Sep 2012 15:14:42 -0300 Subject: [media] v4l: Convert drivers to use monotonic timestamps Convert drivers using wall clock time (CLOCK_REALTIME) to timestamp from the monotonic timer (CLOCK_MONOTONIC). Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146/saa7146_fops.c | 2 +- drivers/media/pci/bt8xx/bttv-driver.c | 6 +++--- drivers/media/pci/cx23885/cx23885-core.c | 2 +- drivers/media/pci/cx23885/cx23885-video.c | 2 +- drivers/media/pci/cx25821/cx25821-video.c | 2 +- drivers/media/pci/cx88/cx88-core.c | 2 +- drivers/media/pci/meye/meye.c | 4 ++-- drivers/media/pci/saa7134/saa7134-core.c | 2 +- drivers/media/pci/sta2x11/sta2x11_vip.c | 2 +- drivers/media/pci/zoran/zoran_device.c | 4 ++-- drivers/media/platform/blackfin/bfin_capture.c | 4 +--- drivers/media/platform/davinci/vpfe_capture.c | 5 +---- drivers/media/platform/davinci/vpif_capture.c | 2 +- drivers/media/platform/davinci/vpif_display.c | 6 +++--- drivers/media/platform/fsl-viu.c | 2 +- drivers/media/platform/omap/omap_vout.c | 2 +- drivers/media/platform/omap24xxcam.c | 2 +- drivers/media/platform/sh_vou.c | 2 +- drivers/media/platform/soc_camera/atmel-isi.c | 2 +- drivers/media/platform/soc_camera/mx1_camera.c | 2 +- drivers/media/platform/soc_camera/mx2_camera.c | 4 ++-- drivers/media/platform/soc_camera/mx3_camera.c | 2 +- drivers/media/platform/soc_camera/omap1_camera.c | 2 +- drivers/media/platform/soc_camera/pxa_camera.c | 2 +- drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c | 2 +- drivers/media/platform/timblogiw.c | 2 +- drivers/media/platform/vino.c | 8 ++++---- drivers/media/platform/vivi.c | 6 ++---- drivers/media/usb/au0828/au0828-video.c | 4 ++-- drivers/media/usb/cpia2/cpia2_usb.c | 2 +- drivers/media/usb/cx231xx/cx231xx-417.c | 4 ++-- drivers/media/usb/cx231xx/cx231xx-vbi.c | 2 +- drivers/media/usb/cx231xx/cx231xx-video.c | 2 +- drivers/media/usb/em28xx/em28xx-video.c | 4 ++-- drivers/media/usb/pwc/pwc-if.c | 3 ++- drivers/media/usb/s2255/s2255drv.c | 6 ++---- drivers/media/usb/sn9c102/sn9c102_core.c | 3 ++- drivers/media/usb/stk1160/stk1160-video.c | 2 +- drivers/media/usb/stkwebcam/stk-webcam.c | 2 +- drivers/media/usb/tlg2300/pd-video.c | 2 +- drivers/media/usb/tm6000/tm6000-video.c | 2 +- drivers/media/usb/usbvision/usbvision-core.c | 2 +- drivers/media/usb/zr364xx/zr364xx.c | 6 ++---- 43 files changed, 61 insertions(+), 70 deletions(-) (limited to 'drivers') diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c index b3890bd49df6..2652f9155c34 100644 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -105,7 +105,7 @@ void saa7146_buffer_finish(struct saa7146_dev *dev, } q->curr->vb.state = state; - do_gettimeofday(&q->curr->vb.ts); + v4l2_get_timestamp(&q->curr->vb.ts); wake_up(&q->curr->vb.done); q->curr = NULL; diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index de6f41f19187..346458bba2c5 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -3835,7 +3835,7 @@ bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup, { struct timeval ts; - do_gettimeofday(&ts); + v4l2_get_timestamp(&ts); if (wakeup->top == wakeup->bottom) { if (NULL != wakeup->top && curr->top != wakeup->top) { @@ -3878,7 +3878,7 @@ bttv_irq_wakeup_vbi(struct bttv *btv, struct bttv_buffer *wakeup, if (NULL == wakeup) return; - do_gettimeofday(&ts); + v4l2_get_timestamp(&ts); wakeup->vb.ts = ts; wakeup->vb.field_count = btv->field_count; wakeup->vb.state = state; @@ -3949,7 +3949,7 @@ bttv_irq_wakeup_top(struct bttv *btv) btv->curr.top = NULL; bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0); - do_gettimeofday(&wakeup->vb.ts); + v4l2_get_timestamp(&wakeup->vb.ts); wakeup->vb.field_count = btv->field_count; wakeup->vb.state = VIDEOBUF_DONE; wake_up(&wakeup->vb.done); diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 065ecd54bda3..c7572594d434 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -439,7 +439,7 @@ void cx23885_wakeup(struct cx23885_tsport *port, if ((s16) (count - buf->count) < 0) break; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i, count, buf->count); buf->vb.state = VIDEOBUF_DONE; diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 1a21926ca412..83975313e0f2 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -300,7 +300,7 @@ void cx23885_video_wakeup(struct cx23885_dev *dev, if ((s16) (count - buf->count) < 0) break; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i, count, buf->count); buf->vb.state = VIDEOBUF_DONE; diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index 53b16dd70320..d4de021dc844 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -130,7 +130,7 @@ void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, if ((s16) (count - buf->count) < 0) break; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); buf->vb.state = VIDEOBUF_DONE; list_del(&buf->vb.queue); wake_up(&buf->vb.done); diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c index 19a58754c6e1..39f095c37ffd 100644 --- a/drivers/media/pci/cx88/cx88-core.c +++ b/drivers/media/pci/cx88/cx88-core.c @@ -549,7 +549,7 @@ void cx88_wakeup(struct cx88_core *core, * up to 32767 buffers in flight... */ if ((s16) (count - buf->count) < 0) break; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i, count, buf->count); buf->vb.state = VIDEOBUF_DONE; diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c index ae7d32027bf7..288adea55e33 100644 --- a/drivers/media/pci/meye/meye.c +++ b/drivers/media/pci/meye/meye.c @@ -811,7 +811,7 @@ again: mchip_hsize() * mchip_vsize() * 2); meye.grab_buffer[reqnr].size = mchip_hsize() * mchip_vsize() * 2; meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; - do_gettimeofday(&meye.grab_buffer[reqnr].timestamp); + v4l2_get_timestamp(&meye.grab_buffer[reqnr].timestamp); meye.grab_buffer[reqnr].sequence = sequence++; kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr, sizeof(int), &meye.doneq_lock); @@ -832,7 +832,7 @@ again: size); meye.grab_buffer[reqnr].size = size; meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; - do_gettimeofday(&meye.grab_buffer[reqnr].timestamp); + v4l2_get_timestamp(&meye.grab_buffer[reqnr].timestamp); meye.grab_buffer[reqnr].sequence = sequence++; kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr, sizeof(int), &meye.doneq_lock); diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c index 8976d0e65813..e1aeb51e25ba 100644 --- a/drivers/media/pci/saa7134/saa7134-core.c +++ b/drivers/media/pci/saa7134/saa7134-core.c @@ -308,7 +308,7 @@ void saa7134_buffer_finish(struct saa7134_dev *dev, /* finish current buffer */ q->curr->vb.state = state; - do_gettimeofday(&q->curr->vb.ts); + v4l2_get_timestamp(&q->curr->vb.ts); wake_up(&q->curr->vb.done); q->curr = NULL; } diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index 4c10205264d4..ed1337a89a26 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -1088,7 +1088,7 @@ static irqreturn_t vip_irq(int irq, struct sta2x11_vip *vip) REG_WRITE(vip, DVP_CTL, REG_READ(vip, DVP_CTL) & ~DVP_CTL_ENA); if (vip->active) { - do_gettimeofday(&vip->active->ts); + v4l2_get_timestamp(&vip->active->ts); vip->active->field_count++; vip->active->state = VIDEOBUF_DONE; wake_up(&vip->active->done); diff --git a/drivers/media/pci/zoran/zoran_device.c b/drivers/media/pci/zoran/zoran_device.c index a4cd504b8eee..519164c572c8 100644 --- a/drivers/media/pci/zoran/zoran_device.c +++ b/drivers/media/pci/zoran/zoran_device.c @@ -1169,7 +1169,7 @@ zoran_reap_stat_com (struct zoran *zr) } frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; buffer = &zr->jpg_buffers.buffer[frame]; - do_gettimeofday(&buffer->bs.timestamp); + v4l2_get_timestamp(&buffer->bs.timestamp); if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { buffer->bs.length = (stat_com & 0x7fffff) >> 1; @@ -1407,7 +1407,7 @@ zoran_irq (int irq, zr->v4l_buffers.buffer[zr->v4l_grab_frame].state = BUZ_STATE_DONE; zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.seq = zr->v4l_grab_seq; - do_gettimeofday(&zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.timestamp); + v4l2_get_timestamp(&zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.timestamp); zr->v4l_grab_frame = NO_GRAB_ACTIVE; zr->v4l_pend_tail++; } diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index ec476ef5b709..d422d3c379e4 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -484,15 +484,13 @@ static irqreturn_t bcap_isr(int irq, void *dev_id) { struct ppi_if *ppi = dev_id; struct bcap_device *bcap_dev = ppi->priv; - struct timeval timevalue; struct vb2_buffer *vb = &bcap_dev->cur_frm->vb; dma_addr_t addr; spin_lock(&bcap_dev->lock); if (bcap_dev->cur_frm != bcap_dev->next_frm) { - do_gettimeofday(&timevalue); - vb->v4l2_buf.timestamp = timevalue; + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); vb2_buffer_done(vb, VB2_BUF_STATE_DONE); bcap_dev->cur_frm = bcap_dev->next_frm; } diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index 8be492cd8ed4..65f4264bd5b4 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -560,10 +560,7 @@ static void vpfe_schedule_bottom_field(struct vpfe_device *vpfe_dev) static void vpfe_process_buffer_complete(struct vpfe_device *vpfe_dev) { - struct timeval timevalue; - - do_gettimeofday(&timevalue); - vpfe_dev->cur_frm->ts = timevalue; + v4l2_get_timestamp(&vpfe_dev->cur_frm->ts); vpfe_dev->cur_frm->state = VIDEOBUF_DONE; vpfe_dev->cur_frm->size = vpfe_dev->fmt.fmt.pix.sizeimage; wake_up_interruptible(&vpfe_dev->cur_frm->done); diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index a409ccefb380..5892d2bc8eee 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -411,7 +411,7 @@ static struct vb2_ops video_qops = { */ static void vpif_process_buffer_complete(struct common_obj *common) { - do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&common->cur_frm->vb.v4l2_buf.timestamp); vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_DONE); /* Make curFrm pointing to nextFrm */ diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 9f2b603be9c9..dd249c96126d 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -402,7 +402,7 @@ static void process_interlaced_mode(int fid, struct common_obj *common) /* one frame is displayed If next frame is * available, release cur_frm and move on */ /* Copy frame display time */ - do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&common->cur_frm->vb.v4l2_buf.timestamp); /* Change status of the cur_frm */ vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_DONE); @@ -462,8 +462,8 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id) if (!channel_first_int[i][channel_id]) { /* Mark status of the cur_frm to * done and unlock semaphore on it */ - do_gettimeofday(&common->cur_frm->vb. - v4l2_buf.timestamp); + v4l2_get_timestamp(&common->cur_frm->vb. + v4l2_buf.timestamp); vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_DONE); /* Make cur_frm pointing to next_frm */ diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index a8ddb0cacab8..d464509d0f0e 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -1181,7 +1181,7 @@ static void viu_capture_intr(struct viu_dev *dev, u32 status) if (waitqueue_active(&buf->vb.done)) { list_del(&buf->vb.queue); - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; wake_up(&buf->vb.done); diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index f7ad54106bc5..c74b0d48dcd6 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -597,7 +597,7 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus) return; spin_lock(&vout->vbq_lock); - do_gettimeofday(&timevalue); + v4l2_get_timestamp(&timevalue); switch (cur_display->type) { case OMAP_DISPLAY_TYPE_DSI: diff --git a/drivers/media/platform/omap24xxcam.c b/drivers/media/platform/omap24xxcam.c index 70f45c381318..eda3274abf8e 100644 --- a/drivers/media/platform/omap24xxcam.c +++ b/drivers/media/platform/omap24xxcam.c @@ -402,7 +402,7 @@ static void omap24xxcam_vbq_complete(struct omap24xxcam_sgdma *sgdma, omap24xxcam_core_disable(cam); spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); vb->field_count = atomic_add_return(2, &fh->field_count); if (csr & csr_error) { vb->state = VIDEOBUF_ERROR; diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 7494858a2158..1039ae82401b 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -1092,7 +1092,7 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id) list_del(&vb->queue); vb->state = VIDEOBUF_DONE; - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); vb->field_count++; wake_up(&vb->done); diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 6274a91c25c7..c8d748a31944 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -166,7 +166,7 @@ static irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi) struct frame_buffer *buf = isi->active; list_del_init(&buf->list); - do_gettimeofday(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); vb->v4l2_buf.sequence = isi->sequence++; vb2_buffer_done(vb, VB2_BUF_STATE_DONE); } diff --git a/drivers/media/platform/soc_camera/mx1_camera.c b/drivers/media/platform/soc_camera/mx1_camera.c index 032b8c9097f9..674ded646b66 100644 --- a/drivers/media/platform/soc_camera/mx1_camera.c +++ b/drivers/media/platform/soc_camera/mx1_camera.c @@ -307,7 +307,7 @@ static void mx1_camera_wakeup(struct mx1_camera_dev *pcdev, /* _init is used to debug races, see comment in mx1_camera_reqbufs() */ list_del_init(&vb->queue); vb->state = VIDEOBUF_DONE; - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); vb->field_count++; wake_up(&vb->done); diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 2c148028d8c0..3c5ba63cd311 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -516,7 +516,7 @@ static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb, dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - do_gettimeofday(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); vb->v4l2_buf.sequence++; vb2_buffer_done(vb, VB2_BUF_STATE_DONE); @@ -1561,7 +1561,7 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev, vb2_get_plane_payload(vb, 0)); list_del_init(&buf->internal.queue); - do_gettimeofday(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); vb->v4l2_buf.sequence = pcdev->frame_count; if (err) vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c index 261f6e9e1b17..e6bc06bca496 100644 --- a/drivers/media/platform/soc_camera/mx3_camera.c +++ b/drivers/media/platform/soc_camera/mx3_camera.c @@ -156,7 +156,7 @@ static void mx3_cam_dma_done(void *arg) struct mx3_camera_buffer *buf = to_mx3_vb(vb); list_del_init(&buf->queue); - do_gettimeofday(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); vb->v4l2_buf.field = mx3_cam->field; vb->v4l2_buf.sequence = mx3_cam->sequence++; vb2_buffer_done(vb, VB2_BUF_STATE_DONE); diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c index 13636a585106..b573bd5899de 100644 --- a/drivers/media/platform/soc_camera/omap1_camera.c +++ b/drivers/media/platform/soc_camera/omap1_camera.c @@ -591,7 +591,7 @@ static void videobuf_done(struct omap1_cam_dev *pcdev, suspend_capture(pcdev); } vb->state = result; - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); if (result != VIDEOBUF_ERROR) vb->field_count++; wake_up(&vb->done); diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c index 3434ffe79c6e..8ff961eec39d 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/soc_camera/pxa_camera.c @@ -681,7 +681,7 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, /* _init is used to debug races, see comment in pxa_camera_reqbufs() */ list_del_init(&vb->queue); vb->state = VIDEOBUF_DONE; - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); vb->field_count++; wake_up(&vb->done); dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s dequeud buffer (vb=0x%p)\n", diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 27eeca15bbf0..9f021043cfe6 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -516,7 +516,7 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) pcdev->active = NULL; ret = sh_mobile_ceu_capture(pcdev); - do_gettimeofday(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); if (!ret) { vb->v4l2_buf.field = pcdev->field; vb->v4l2_buf.sequence = pcdev->sequence++; diff --git a/drivers/media/platform/timblogiw.c b/drivers/media/platform/timblogiw.c index 02194c056b00..9de014100a0f 100644 --- a/drivers/media/platform/timblogiw.c +++ b/drivers/media/platform/timblogiw.c @@ -130,7 +130,7 @@ static void timblogiw_dma_cb(void *data) if (vb->state != VIDEOBUF_ERROR) { list_del(&vb->queue); - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); vb->field_count = fh->frame_count * 2; vb->state = VIDEOBUF_DONE; diff --git a/drivers/media/platform/vino.c b/drivers/media/platform/vino.c index 70b0bf4b2900..28350e78b564 100644 --- a/drivers/media/platform/vino.c +++ b/drivers/media/platform/vino.c @@ -2474,8 +2474,8 @@ static irqreturn_t vino_interrupt(int irq, void *dev_id) if ((!handled_a) && (done_a || skip_a)) { if (!skip_a) { - do_gettimeofday(&vino_drvdata-> - a.int_data.timestamp); + v4l2_get_timestamp( + &vino_drvdata->a.int_data.timestamp); vino_drvdata->a.int_data.frame_counter = fc_a; } vino_drvdata->a.int_data.skip = skip_a; @@ -2489,8 +2489,8 @@ static irqreturn_t vino_interrupt(int irq, void *dev_id) if ((!handled_b) && (done_b || skip_b)) { if (!skip_b) { - do_gettimeofday(&vino_drvdata-> - b.int_data.timestamp); + v4l2_get_timestamp( + &vino_drvdata->b.int_data.timestamp); vino_drvdata->b.int_data.frame_counter = fc_b; } vino_drvdata->b.int_data.skip = skip_b; diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index 0d59b9db83cb..c2f424f32450 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -554,7 +554,6 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) { int wmax = dev->width; int hmax = dev->height; - struct timeval ts; void *vbuf = vb2_plane_vaddr(&buf->vb, 0); unsigned ms; char str[100]; @@ -622,8 +621,7 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; dev->field_count++; buf->vb.v4l2_buf.sequence = dev->field_count >> 1; - do_gettimeofday(&ts); - buf->vb.v4l2_buf.timestamp = ts; + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); } static void vivi_thread_tick(struct vivi_dev *dev) @@ -645,7 +643,7 @@ static void vivi_thread_tick(struct vivi_dev *dev) list_del(&buf->list); spin_unlock_irqrestore(&dev->slock, flags); - do_gettimeofday(&buf->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); /* Fill buffer */ vivi_fillbuff(dev, buf); diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 45387aab10c7..8b9e8268e911 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -304,7 +304,7 @@ static inline void buffer_filled(struct au0828_dev *dev, buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dev->isoc_ctl.buf = NULL; @@ -321,7 +321,7 @@ static inline void vbi_buffer_filled(struct au0828_dev *dev, buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dev->isoc_ctl.vbi_buf = NULL; diff --git a/drivers/media/usb/cpia2/cpia2_usb.c b/drivers/media/usb/cpia2/cpia2_usb.c index 95b5d6e7cdc4..be1719283609 100644 --- a/drivers/media/usb/cpia2/cpia2_usb.c +++ b/drivers/media/usb/cpia2/cpia2_usb.c @@ -328,7 +328,7 @@ static void cpia2_usb_complete(struct urb *urb) continue; } DBG("Start of frame pattern found\n"); - do_gettimeofday(&cam->workbuff->timestamp); + v4l2_get_timestamp(&cam->workbuff->timestamp); cam->workbuff->seq = cam->frame_count++; cam->workbuff->data[0] = 0xFF; cam->workbuff->data[1] = 0xD8; diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index b024e5197a75..28688dbcb609 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -1291,7 +1291,7 @@ static void buffer_copy(struct cx231xx *dev, char *data, int len, struct urb *ur buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); list_del(&buf->vb.queue); wake_up(&buf->vb.done); dma_q->mpeg_buffer_completed = 0; @@ -1327,7 +1327,7 @@ static void buffer_filled(char *data, int len, struct urb *urb, memcpy(vbuf, data, len); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); list_del(&buf->vb.queue); wake_up(&buf->vb.done); diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c index ac7db52f404f..46e3892557c2 100644 --- a/drivers/media/usb/cx231xx/cx231xx-vbi.c +++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c @@ -530,7 +530,7 @@ static inline void vbi_buffer_filled(struct cx231xx *dev, buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dev->vbi_mode.bulk_ctl.buf = NULL; diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index fedf7852a355..239cb913be5c 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -235,7 +235,7 @@ static inline void buffer_filled(struct cx231xx *dev, cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); if (dev->USE_ISO) dev->video_mode.isoc_ctl.buf = NULL; diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 1e553d357380..766ad125fc08 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -163,7 +163,7 @@ static inline void buffer_filled(struct em28xx *dev, em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dev->isoc_ctl.vid_buf = NULL; @@ -180,7 +180,7 @@ static inline void vbi_buffer_filled(struct em28xx *dev, buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dev->isoc_ctl.vbi_buf = NULL; diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 5210239cbaee..21c15233c6ae 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -316,7 +316,8 @@ static void pwc_isoc_handler(struct urb *urb) struct pwc_frame_buf *fbuf = pdev->fill_buf; if (pdev->vsync == 1) { - do_gettimeofday(&fbuf->vb.v4l2_buf.timestamp); + v4l2_get_timestamp( + &fbuf->vb.v4l2_buf.timestamp); pdev->vsync = 2; } diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 8ebec0d7bf59..498c57ea5d32 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -593,7 +593,7 @@ static int s2255_got_frame(struct s2255_channel *channel, int jpgsize) buf = list_entry(dma_q->active.next, struct s2255_buffer, vb.queue); list_del(&buf->vb.queue); - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); s2255_fillbuff(channel, buf, jpgsize); wake_up(&buf->vb.done); dprintk(2, "%s: [buf/i] [%p/%d]\n", __func__, buf, buf->vb.i); @@ -629,7 +629,6 @@ static void s2255_fillbuff(struct s2255_channel *channel, struct s2255_buffer *buf, int jpgsize) { int pos = 0; - struct timeval ts; const char *tmpbuf; char *vbuf = videobuf_to_vmalloc(&buf->vb); unsigned long last_frame; @@ -674,8 +673,7 @@ static void s2255_fillbuff(struct s2255_channel *channel, /* tell v4l buffer was filled */ buf->vb.field_count = channel->frame_count * 2; - do_gettimeofday(&ts); - buf->vb.ts = ts; + v4l2_get_timestamp(&buf->vb.ts); buf->vb.state = VIDEOBUF_DONE; } diff --git a/drivers/media/usb/sn9c102/sn9c102_core.c b/drivers/media/usb/sn9c102/sn9c102_core.c index 73605864fffa..8dbf0c721c4a 100644 --- a/drivers/media/usb/sn9c102/sn9c102_core.c +++ b/drivers/media/usb/sn9c102/sn9c102_core.c @@ -773,7 +773,8 @@ end_of_frame: img); if ((*f)->buf.bytesused == 0) - do_gettimeofday(&(*f)->buf.timestamp); + v4l2_get_timestamp( + &(*f)->buf.timestamp); (*f)->buf.bytesused += img; diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c index fa3671de02aa..0a4ee85f4399 100644 --- a/drivers/media/usb/stk1160/stk1160-video.c +++ b/drivers/media/usb/stk1160/stk1160-video.c @@ -101,7 +101,7 @@ void stk1160_buffer_done(struct stk1160 *dev) buf->vb.v4l2_buf.sequence = dev->field_count >> 1; buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; buf->vb.v4l2_buf.bytesused = buf->bytesused; - do_gettimeofday(&buf->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); vb2_set_plane_payload(&buf->vb, 0, buf->bytesused); vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index 5d3c032d733c..bf56904decb4 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -1113,7 +1113,7 @@ static int stk_vidioc_dqbuf(struct file *filp, sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE; sbuf->v4lbuf.sequence = ++dev->sequence; - do_gettimeofday(&sbuf->v4lbuf.timestamp); + v4l2_get_timestamp(&sbuf->v4lbuf.timestamp); *buf = sbuf->v4lbuf; return 0; diff --git a/drivers/media/usb/tlg2300/pd-video.c b/drivers/media/usb/tlg2300/pd-video.c index 3082bfa9b2c5..21723378bb8f 100644 --- a/drivers/media/usb/tlg2300/pd-video.c +++ b/drivers/media/usb/tlg2300/pd-video.c @@ -212,7 +212,7 @@ static void submit_frame(struct front_face *front) front->curr_frame = NULL; vb->state = VIDEOBUF_DONE; vb->field_count++; - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); wake_up(&vb->done); } diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index 1edc2517603f..e3c567c27918 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -194,7 +194,7 @@ static inline void buffer_filled(struct tm6000_core *dev, dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n", buf, buf->vb.i); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); list_del(&buf->vb.queue); wake_up(&buf->vb.done); diff --git a/drivers/media/usb/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c index c9b2042f8bdf..816b1cffab7d 100644 --- a/drivers/media/usb/usbvision/usbvision-core.c +++ b/drivers/media/usb/usbvision/usbvision-core.c @@ -1169,7 +1169,7 @@ static void usbvision_parse_data(struct usb_usbvision *usbvision) if (newstate == parse_state_next_frame) { frame->grabstate = frame_state_done; - do_gettimeofday(&(frame->timestamp)); + v4l2_get_timestamp(&(frame->timestamp)); frame->sequence = usbvision->frame_num; spin_lock_irqsave(&usbvision->queue_lock, lock_flags); diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index 39edd4442932..74d56df3347f 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -501,7 +501,6 @@ static void zr364xx_fillbuff(struct zr364xx_camera *cam, int jpgsize) { int pos = 0; - struct timeval ts; const char *tmpbuf; char *vbuf = videobuf_to_vmalloc(&buf->vb); unsigned long last_frame; @@ -530,8 +529,7 @@ static void zr364xx_fillbuff(struct zr364xx_camera *cam, /* tell v4l buffer was filled */ buf->vb.field_count = cam->frame_count * 2; - do_gettimeofday(&ts); - buf->vb.ts = ts; + v4l2_get_timestamp(&buf->vb.ts); buf->vb.state = VIDEOBUF_DONE; } @@ -559,7 +557,7 @@ static int zr364xx_got_frame(struct zr364xx_camera *cam, int jpgsize) goto unlock; } list_del(&buf->vb.queue); - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); DBG("[%p/%d] wakeup\n", buf, buf->vb.i); zr364xx_fillbuff(cam, buf, jpgsize); wake_up(&buf->vb.done); -- cgit v1.2.3 From 1b18e7a0be859911b22138ce27258687efc528b8 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 22 Oct 2012 17:10:16 -0300 Subject: [media] v4l: Tell user space we're using monotonic timestamps Set buffer timestamp flags for videobuf, videobuf2 and drivers that use neither. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/meye/meye.c | 4 ++-- drivers/media/pci/zoran/zoran_driver.c | 2 +- drivers/media/platform/omap3isp/ispqueue.c | 1 + drivers/media/platform/vino.c | 3 +++ drivers/media/usb/cpia2/cpia2_v4l.c | 5 ++++- drivers/media/usb/sn9c102/sn9c102_core.c | 2 +- drivers/media/usb/stkwebcam/stk-webcam.c | 1 + drivers/media/usb/usbvision/usbvision-video.c | 5 +++-- drivers/media/v4l2-core/videobuf-core.c | 2 +- drivers/media/v4l2-core/videobuf2-core.c | 10 ++++++---- 10 files changed, 23 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c index 288adea55e33..ac7ab6edb06d 100644 --- a/drivers/media/pci/meye/meye.c +++ b/drivers/media/pci/meye/meye.c @@ -1426,7 +1426,7 @@ static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) return -EINVAL; buf->bytesused = meye.grab_buffer[index].size; - buf->flags = V4L2_BUF_FLAG_MAPPED; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; if (meye.grab_buffer[index].state == MEYE_BUF_USING) buf->flags |= V4L2_BUF_FLAG_QUEUED; @@ -1499,7 +1499,7 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) buf->index = reqnr; buf->bytesused = meye.grab_buffer[reqnr].size; - buf->flags = V4L2_BUF_FLAG_MAPPED; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buf->field = V4L2_FIELD_NONE; buf->timestamp = meye.grab_buffer[reqnr].timestamp; buf->sequence = meye.grab_buffer[reqnr].sequence; diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c index 53f12c7466b0..33521a4f23a7 100644 --- a/drivers/media/pci/zoran/zoran_driver.c +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -1334,7 +1334,7 @@ static int zoran_v4l2_buffer_status(struct zoran_fh *fh, struct zoran *zr = fh->zr; unsigned long flags; - buf->flags = V4L2_BUF_FLAG_MAPPED; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; switch (fh->map_mode) { case ZORAN_MAP_MODE_RAW: diff --git a/drivers/media/platform/omap3isp/ispqueue.c b/drivers/media/platform/omap3isp/ispqueue.c index 15bf3eab2224..6599963cdd9b 100644 --- a/drivers/media/platform/omap3isp/ispqueue.c +++ b/drivers/media/platform/omap3isp/ispqueue.c @@ -674,6 +674,7 @@ static int isp_video_queue_alloc(struct isp_video_queue *queue, buf->vbuf.index = i; buf->vbuf.length = size; buf->vbuf.type = queue->type; + buf->vbuf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buf->vbuf.field = V4L2_FIELD_NONE; buf->vbuf.memory = memory; diff --git a/drivers/media/platform/vino.c b/drivers/media/platform/vino.c index 28350e78b564..eb5d6f955709 100644 --- a/drivers/media/platform/vino.c +++ b/drivers/media/platform/vino.c @@ -3410,6 +3410,9 @@ static void vino_v4l2_get_buffer_status(struct vino_channel_settings *vcs, if (fb->map_count > 0) b->flags |= V4L2_BUF_FLAG_MAPPED; + b->flags &= ~V4L2_BUF_FLAG_TIMESTAMP_MASK; + b->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + b->index = fb->id; b->memory = (vcs->fb_queue.type == VINO_MEMORY_MMAP) ? V4L2_MEMORY_MMAP : V4L2_MEMORY_USERPTR; diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c index aeb9d2275725..d5d42b6e94be 100644 --- a/drivers/media/usb/cpia2/cpia2_v4l.c +++ b/drivers/media/usb/cpia2/cpia2_v4l.c @@ -825,6 +825,8 @@ static int cpia2_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) else buf->flags = 0; + buf->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + switch (cam->buffers[buf->index].status) { case FRAME_EMPTY: case FRAME_ERROR: @@ -943,7 +945,8 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) buf->index = frame; buf->bytesused = cam->buffers[buf->index].length; - buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE + | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buf->field = V4L2_FIELD_NONE; buf->timestamp = cam->buffers[buf->index].timestamp; buf->sequence = cam->buffers[buf->index].seq; diff --git a/drivers/media/usb/sn9c102/sn9c102_core.c b/drivers/media/usb/sn9c102/sn9c102_core.c index 8dbf0c721c4a..6bda81aebf87 100644 --- a/drivers/media/usb/sn9c102/sn9c102_core.c +++ b/drivers/media/usb/sn9c102/sn9c102_core.c @@ -173,7 +173,7 @@ sn9c102_request_buffers(struct sn9c102_device* cam, u32 count, cam->frame[i].buf.sequence = 0; cam->frame[i].buf.field = V4L2_FIELD_NONE; cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; - cam->frame[i].buf.flags = 0; + cam->frame[i].buf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; } return cam->nbuffers; diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index bf56904decb4..52296f7ec966 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -466,6 +466,7 @@ static int stk_setup_siobuf(struct stk_camera *dev, int index) buf->dev = dev; buf->v4lbuf.index = index; buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf->v4lbuf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buf->v4lbuf.field = V4L2_FIELD_NONE; buf->v4lbuf.memory = V4L2_MEMORY_MMAP; buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length; diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index 5c36a57e6590..c6bc8ce67375 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -761,7 +761,7 @@ static int vidioc_querybuf(struct file *file, if (vb->index >= usbvision->num_frames) return -EINVAL; /* Updating the corresponding frame state */ - vb->flags = 0; + vb->flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; frame = &usbvision->frame[vb->index]; if (frame->grabstate >= frame_state_ready) vb->flags |= V4L2_BUF_FLAG_QUEUED; @@ -843,7 +843,8 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *vb) vb->memory = V4L2_MEMORY_MMAP; vb->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | - V4L2_BUF_FLAG_DONE; + V4L2_BUF_FLAG_DONE | + V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vb->index = f->index; vb->sequence = f->sequence; vb->timestamp = f->timestamp; diff --git a/drivers/media/v4l2-core/videobuf-core.c b/drivers/media/v4l2-core/videobuf-core.c index 5449e8aa984a..fb5ee5dd8fe9 100644 --- a/drivers/media/v4l2-core/videobuf-core.c +++ b/drivers/media/v4l2-core/videobuf-core.c @@ -340,7 +340,7 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, break; } - b->flags = 0; + b->flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; if (vb->map) b->flags |= V4L2_BUF_FLAG_MAPPED; diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 9f81be23a81f..85e3c221dadc 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -40,9 +40,10 @@ module_param(debug, int, 0644); #define call_qop(q, op, args...) \ (((q)->ops->op) ? ((q)->ops->op(args)) : 0) -#define V4L2_BUFFER_STATE_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \ +#define V4L2_BUFFER_MASK_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \ V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \ - V4L2_BUF_FLAG_PREPARED) + V4L2_BUF_FLAG_PREPARED | \ + V4L2_BUF_FLAG_TIMESTAMP_MASK) /** * __vb2_buf_mem_alloc() - allocate video memory for the given buffer @@ -401,7 +402,8 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) /* * Clear any buffer state related flags. */ - b->flags &= ~V4L2_BUFFER_STATE_FLAGS; + b->flags &= ~V4L2_BUFFER_MASK_FLAGS; + b->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; switch (vb->state) { case VB2_BUF_STATE_QUEUED: @@ -939,7 +941,7 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b vb->v4l2_buf.field = b->field; vb->v4l2_buf.timestamp = b->timestamp; - vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_STATE_FLAGS; + vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS; } /** -- cgit v1.2.3 From 1bc05e77db1b0c2f5ec3917a9b21d64c01342b5f Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 26 Nov 2012 11:08:26 -0300 Subject: [media] s5p-fimc: Fix horizontal/vertical image flip Setting FIMC_REG_CITRGFMT_FLIP_X_MIRROR bit causes X-axis image flip (vertical flip) and thus it corresponds to V4L2_CID_VFLIP. Likewise, setting FIMC_REG_CITRGFMT_FLIP_Y_MIRROR bit causes Y-axis image flip (horizontal flip) and thus it corresponds to V4L2_CID_HFLIP. Currently the driver does X-axis flip when V4L2_CID_HFLIP is set and Y-axis flip for V4L2_CID_VFLIP. Fix this incorrect assignment by setting proper FIMC_REG_CITRGFMT register bits for ctx->hflip and ctx->vflip. Reported-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-reg.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.c b/drivers/media/platform/s5p-fimc/fimc-reg.c index 2c9d0c06c9e8..9c3c461a5099 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.c +++ b/drivers/media/platform/s5p-fimc/fimc-reg.c @@ -44,9 +44,9 @@ static u32 fimc_hw_get_in_flip(struct fimc_ctx *ctx) u32 flip = FIMC_REG_MSCTRL_FLIP_NORMAL; if (ctx->hflip) - flip = FIMC_REG_MSCTRL_FLIP_X_MIRROR; - if (ctx->vflip) flip = FIMC_REG_MSCTRL_FLIP_Y_MIRROR; + if (ctx->vflip) + flip = FIMC_REG_MSCTRL_FLIP_X_MIRROR; if (ctx->rotation <= 90) return flip; @@ -59,9 +59,9 @@ static u32 fimc_hw_get_target_flip(struct fimc_ctx *ctx) u32 flip = FIMC_REG_CITRGFMT_FLIP_NORMAL; if (ctx->hflip) - flip |= FIMC_REG_CITRGFMT_FLIP_X_MIRROR; - if (ctx->vflip) flip |= FIMC_REG_CITRGFMT_FLIP_Y_MIRROR; + if (ctx->vflip) + flip |= FIMC_REG_CITRGFMT_FLIP_X_MIRROR; if (ctx->rotation <= 90) return flip; -- cgit v1.2.3 From ef2c83262a989dc20268dc25d4ce3725ac9d7886 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 23 Nov 2012 15:17:40 -0300 Subject: [media] s5p-csis: Correct the event counters logging The counter field is unsigned so >= 0 condition always evaluates to true. Fix this to log events for which counter is > 0 or for all when in debug mode. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/mipi-csis.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index 4c961b1b68e6..9528ee65a843 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -401,12 +401,12 @@ static void s5pcsis_log_counters(struct csis_state *state, bool non_errors) spin_lock_irqsave(&state->slock, flags); - for (i--; i >= 0; i--) - if (state->events[i].counter >= 0) + for (i--; i >= 0; i--) { + if (state->events[i].counter > 0 || debug) v4l2_info(&state->sd, "%s events: %d\n", state->events[i].name, state->events[i].counter); - + } spin_unlock_irqrestore(&state->slock, flags); } -- cgit v1.2.3 From a62082ffa11eb78ea67788b4fb6dbb32ae27464b Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 16 Nov 2012 16:52:57 -0300 Subject: [media] fimc-lite: Register dump function cleanup Use v4l2_info() to make it possible to identify which FIMC-LITE device instance the logs refer to. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-lite-reg.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c b/drivers/media/platform/s5p-fimc/fimc-lite-reg.c index a22d7eb05c82..ad63ebf082c5 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite-reg.c @@ -292,9 +292,11 @@ void flite_hw_dump_regs(struct fimc_lite *dev, const char *label) }; u32 i; - pr_info("--- %s ---\n", label); + v4l2_info(&dev->subdev, "--- %s ---\n", label); + for (i = 0; i < ARRAY_SIZE(registers); i++) { u32 cfg = readl(dev->regs + registers[i].offset); - pr_info("%s: %s:\t0x%08x\n", __func__, registers[i].name, cfg); + v4l2_info(&dev->subdev, "%9s: 0x%08x\n", + registers[i].name, cfg); } } -- cgit v1.2.3 From 35f29248548b86df50b0b9b280f3b759529fe853 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 22 Nov 2012 14:01:39 -0300 Subject: [media] s5p-fimc: Clean up capture enable/disable helpers The FIMC FIFO output is not supported in the driver due to some hardware issues thus we can remove some code as out_path is always FIMC_IO_DMA. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-reg.c | 38 +++++++++++++----------------- drivers/media/platform/s5p-fimc/fimc-reg.h | 4 ++-- 2 files changed, 18 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.c b/drivers/media/platform/s5p-fimc/fimc-reg.c index 9c3c461a5099..f0c34ca41894 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.c +++ b/drivers/media/platform/s5p-fimc/fimc-reg.c @@ -344,30 +344,31 @@ void fimc_hw_set_mainscaler(struct fimc_ctx *ctx) } } -void fimc_hw_en_capture(struct fimc_ctx *ctx) +void fimc_hw_enable_capture(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; + u32 cfg; - u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); - - if (ctx->out_path == FIMC_IO_DMA) { - /* one shot mode */ - cfg |= FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE | - FIMC_REG_CIIMGCPT_IMGCPTEN; - } else { - /* Continuous frame capture mode (freerun). */ - cfg &= ~(FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE | - FIMC_REG_CIIMGCPT_CPT_FRMOD_CNT); - cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN; - } + cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); + cfg |= FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE; if (ctx->scaler.enabled) cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN_SC; + else + cfg &= FIMC_REG_CIIMGCPT_IMGCPTEN_SC; cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN; writel(cfg, dev->regs + FIMC_REG_CIIMGCPT); } +void fimc_hw_disable_capture(struct fimc_dev *dev) +{ + u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); + cfg &= ~(FIMC_REG_CIIMGCPT_IMGCPTEN | + FIMC_REG_CIIMGCPT_IMGCPTEN_SC); + writel(cfg, dev->regs + FIMC_REG_CIIMGCPT); +} + void fimc_hw_set_effect(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; @@ -737,13 +738,6 @@ void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on) writel(cfg, dev->regs + FIMC_REG_MSCTRL); } -void fimc_hw_dis_capture(struct fimc_dev *dev) -{ - u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); - cfg &= ~(FIMC_REG_CIIMGCPT_IMGCPTEN | FIMC_REG_CIIMGCPT_IMGCPTEN_SC); - writel(cfg, dev->regs + FIMC_REG_CIIMGCPT); -} - /* Return an index to the buffer actually being written. */ s32 fimc_hw_get_frame_index(struct fimc_dev *dev) { @@ -776,13 +770,13 @@ s32 fimc_hw_get_prev_frame_index(struct fimc_dev *dev) void fimc_activate_capture(struct fimc_ctx *ctx) { fimc_hw_enable_scaler(ctx->fimc_dev, ctx->scaler.enabled); - fimc_hw_en_capture(ctx); + fimc_hw_enable_capture(ctx); } void fimc_deactivate_capture(struct fimc_dev *fimc) { fimc_hw_en_lastirq(fimc, true); - fimc_hw_dis_capture(fimc); + fimc_hw_disable_capture(fimc); fimc_hw_enable_scaler(fimc, false); fimc_hw_en_lastirq(fimc, false); } diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.h b/drivers/media/platform/s5p-fimc/fimc-reg.h index b6abfc7b72ac..f3e0b78a3736 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.h +++ b/drivers/media/platform/s5p-fimc/fimc-reg.h @@ -287,7 +287,7 @@ void fimc_hw_en_lastirq(struct fimc_dev *fimc, int enable); void fimc_hw_en_irq(struct fimc_dev *fimc, int enable); void fimc_hw_set_prescaler(struct fimc_ctx *ctx); void fimc_hw_set_mainscaler(struct fimc_ctx *ctx); -void fimc_hw_en_capture(struct fimc_ctx *ctx); +void fimc_hw_enable_capture(struct fimc_ctx *ctx); void fimc_hw_set_effect(struct fimc_ctx *ctx); void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx); void fimc_hw_set_in_dma(struct fimc_ctx *ctx); @@ -306,7 +306,7 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, void fimc_hw_clear_irq(struct fimc_dev *dev); void fimc_hw_enable_scaler(struct fimc_dev *dev, bool on); void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on); -void fimc_hw_dis_capture(struct fimc_dev *dev); +void fimc_hw_disable_capture(struct fimc_dev *dev); s32 fimc_hw_get_frame_index(struct fimc_dev *dev); s32 fimc_hw_get_prev_frame_index(struct fimc_dev *dev); void fimc_activate_capture(struct fimc_ctx *ctx); -- cgit v1.2.3 From 405f230c44d80976e0ecfc04015ca3bd976dd284 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 2 Aug 2012 10:27:46 -0300 Subject: [media] s5p-fimc: Add variant data structure for Exynos4x12 Add variant data structures for Exynos4212 and Exynos4412 SoC. Add 'const' qualifier for the variant description structures. Also remove has_cam_if flags from FIMC3 on Exynos4210 SoC is it has no interconnections the camera ports. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-capture.c | 8 +-- drivers/media/platform/s5p-fimc/fimc-core.c | 90 +++++++++++++++++++------- drivers/media/platform/s5p-fimc/fimc-core.h | 8 ++- drivers/media/platform/s5p-fimc/fimc-m2m.c | 2 +- drivers/media/platform/s5p-fimc/fimc-reg.c | 2 +- 5 files changed, 78 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index fdb6740248a7..52d8d885d287 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -626,8 +626,8 @@ static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx, { bool rotation = ctx->rotation == 90 || ctx->rotation == 270; struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_variant *var = fimc->variant; - struct fimc_pix_limit *pl = var->pix_limit; + const struct fimc_variant *var = fimc->variant; + const struct fimc_pix_limit *pl = var->pix_limit; struct fimc_frame *dst = &ctx->d_frame; u32 depth, min_w, max_w, min_h, align_h = 3; u32 mask = FMT_FLAGS_CAM; @@ -699,8 +699,8 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx, { bool rotate = ctx->rotation == 90 || ctx->rotation == 270; struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_variant *var = fimc->variant; - struct fimc_pix_limit *pl = var->pix_limit; + const struct fimc_variant *var = fimc->variant; + const struct fimc_pix_limit *pl = var->pix_limit; struct fimc_frame *sink = &ctx->s_frame; u32 max_w, max_h, min_w = 0, min_h = 0, min_sz; u32 align_sz = 0, align_h = 4; diff --git a/drivers/media/platform/s5p-fimc/fimc-core.c b/drivers/media/platform/s5p-fimc/fimc-core.c index 8d0d2b94a135..2a1558a37a41 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.c +++ b/drivers/media/platform/s5p-fimc/fimc-core.c @@ -241,7 +241,7 @@ static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift) int fimc_set_scaler_info(struct fimc_ctx *ctx) { - struct fimc_variant *variant = ctx->fimc_dev->variant; + const struct fimc_variant *variant = ctx->fimc_dev->variant; struct device *dev = &ctx->fimc_dev->pdev->dev; struct fimc_scaler *sc = &ctx->scaler; struct fimc_frame *s_frame = &ctx->s_frame; @@ -440,7 +440,7 @@ void fimc_set_yuv_order(struct fimc_ctx *ctx) void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) { - struct fimc_variant *variant = ctx->fimc_dev->variant; + const struct fimc_variant *variant = ctx->fimc_dev->variant; u32 i, depth = 0; for (i = 0; i < f->fmt->colplanes; i++) @@ -524,7 +524,7 @@ static int fimc_set_color_effect(struct fimc_ctx *ctx, enum v4l2_colorfx colorfx static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl) { struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_variant *variant = fimc->variant; + const struct fimc_variant *variant = fimc->variant; unsigned int flags = FIMC_DST_FMT | FIMC_SRC_FMT; int ret = 0; @@ -591,7 +591,7 @@ static const struct v4l2_ctrl_ops fimc_ctrl_ops = { int fimc_ctrls_create(struct fimc_ctx *ctx) { - struct fimc_variant *variant = ctx->fimc_dev->variant; + const struct fimc_variant *variant = ctx->fimc_dev->variant; unsigned int max_alpha = fimc_get_alpha_mask(ctx->d_frame.fmt); struct fimc_ctrls *ctrls = &ctx->ctrls; struct v4l2_ctrl_handler *handler = &ctrls->handler; @@ -881,7 +881,7 @@ static int fimc_m2m_resume(struct fimc_dev *fimc) static int fimc_probe(struct platform_device *pdev) { - struct fimc_drvdata *drv_data = fimc_get_drvdata(pdev); + const struct fimc_drvdata *drv_data = fimc_get_drvdata(pdev); struct s5p_platform_fimc *pdata; struct fimc_dev *fimc; struct resource *res; @@ -1053,7 +1053,7 @@ static int __devexit fimc_remove(struct platform_device *pdev) } /* Image pixel limits, similar across several FIMC HW revisions. */ -static struct fimc_pix_limit s5p_pix_limit[4] = { +static const struct fimc_pix_limit s5p_pix_limit[4] = { [0] = { .scaler_en_w = 3264, .scaler_dis_w = 8192, @@ -1088,7 +1088,7 @@ static struct fimc_pix_limit s5p_pix_limit[4] = { }, }; -static struct fimc_variant fimc0_variant_s5p = { +static const struct fimc_variant fimc0_variant_s5p = { .has_inp_rot = 1, .has_out_rot = 1, .has_cam_if = 1, @@ -1100,7 +1100,7 @@ static struct fimc_variant fimc0_variant_s5p = { .pix_limit = &s5p_pix_limit[0], }; -static struct fimc_variant fimc2_variant_s5p = { +static const struct fimc_variant fimc2_variant_s5p = { .has_cam_if = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, @@ -1110,7 +1110,7 @@ static struct fimc_variant fimc2_variant_s5p = { .pix_limit = &s5p_pix_limit[1], }; -static struct fimc_variant fimc0_variant_s5pv210 = { +static const struct fimc_variant fimc0_variant_s5pv210 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, @@ -1123,7 +1123,7 @@ static struct fimc_variant fimc0_variant_s5pv210 = { .pix_limit = &s5p_pix_limit[1], }; -static struct fimc_variant fimc1_variant_s5pv210 = { +static const struct fimc_variant fimc1_variant_s5pv210 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, @@ -1137,7 +1137,7 @@ static struct fimc_variant fimc1_variant_s5pv210 = { .pix_limit = &s5p_pix_limit[2], }; -static struct fimc_variant fimc2_variant_s5pv210 = { +static const struct fimc_variant fimc2_variant_s5pv210 = { .has_cam_if = 1, .pix_hoff = 1, .min_inp_pixsize = 16, @@ -1148,7 +1148,7 @@ static struct fimc_variant fimc2_variant_s5pv210 = { .pix_limit = &s5p_pix_limit[2], }; -static struct fimc_variant fimc0_variant_exynos4 = { +static const struct fimc_variant fimc0_variant_exynos4210 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, @@ -1164,9 +1164,8 @@ static struct fimc_variant fimc0_variant_exynos4 = { .pix_limit = &s5p_pix_limit[1], }; -static struct fimc_variant fimc3_variant_exynos4 = { +static const struct fimc_variant fimc3_variant_exynos4210 = { .pix_hoff = 1, - .has_cam_if = 1, .has_cistatus2 = 1, .has_mainscaler_ext = 1, .has_alpha = 1, @@ -1178,8 +1177,38 @@ static struct fimc_variant fimc3_variant_exynos4 = { .pix_limit = &s5p_pix_limit[3], }; +static const struct fimc_variant fimc0_variant_exynos4x12 = { + .pix_hoff = 1, + .has_inp_rot = 1, + .has_out_rot = 1, + .has_cam_if = 1, + .has_isp_wb = 1, + .has_cistatus2 = 1, + .has_mainscaler_ext = 1, + .has_alpha = 1, + .min_inp_pixsize = 16, + .min_out_pixsize = 16, + .hor_offs_align = 2, + .min_vsize_align = 1, + .out_buf_count = 32, + .pix_limit = &s5p_pix_limit[1], +}; + +static const struct fimc_variant fimc3_variant_exynos4x12 = { + .pix_hoff = 1, + .has_cistatus2 = 1, + .has_mainscaler_ext = 1, + .has_alpha = 1, + .min_inp_pixsize = 16, + .min_out_pixsize = 16, + .hor_offs_align = 2, + .min_vsize_align = 1, + .out_buf_count = 32, + .pix_limit = &s5p_pix_limit[3], +}; + /* S5PC100 */ -static struct fimc_drvdata fimc_drvdata_s5p = { +static const struct fimc_drvdata fimc_drvdata_s5p = { .variant = { [0] = &fimc0_variant_s5p, [1] = &fimc0_variant_s5p, @@ -1190,7 +1219,7 @@ static struct fimc_drvdata fimc_drvdata_s5p = { }; /* S5PV210, S5PC110 */ -static struct fimc_drvdata fimc_drvdata_s5pv210 = { +static const struct fimc_drvdata fimc_drvdata_s5pv210 = { .variant = { [0] = &fimc0_variant_s5pv210, [1] = &fimc1_variant_s5pv210, @@ -1201,18 +1230,30 @@ static struct fimc_drvdata fimc_drvdata_s5pv210 = { }; /* EXYNOS4210, S5PV310, S5PC210 */ -static struct fimc_drvdata fimc_drvdata_exynos4 = { +static const struct fimc_drvdata fimc_drvdata_exynos4210 = { + .variant = { + [0] = &fimc0_variant_exynos4210, + [1] = &fimc0_variant_exynos4210, + [2] = &fimc0_variant_exynos4210, + [3] = &fimc3_variant_exynos4210, + }, + .num_entities = 4, + .lclk_frequency = 166000000UL, +}; + +/* EXYNOS4212, EXYNOS4412 */ +static const struct fimc_drvdata fimc_drvdata_exynos4x12 = { .variant = { - [0] = &fimc0_variant_exynos4, - [1] = &fimc0_variant_exynos4, - [2] = &fimc0_variant_exynos4, - [3] = &fimc3_variant_exynos4, + [0] = &fimc0_variant_exynos4x12, + [1] = &fimc0_variant_exynos4x12, + [2] = &fimc0_variant_exynos4x12, + [3] = &fimc3_variant_exynos4x12, }, .num_entities = 4, .lclk_frequency = 166000000UL, }; -static struct platform_device_id fimc_driver_ids[] = { +static const struct platform_device_id fimc_driver_ids[] = { { .name = "s5p-fimc", .driver_data = (unsigned long)&fimc_drvdata_s5p, @@ -1221,7 +1262,10 @@ static struct platform_device_id fimc_driver_ids[] = { .driver_data = (unsigned long)&fimc_drvdata_s5pv210, }, { .name = "exynos4-fimc", - .driver_data = (unsigned long)&fimc_drvdata_exynos4, + .driver_data = (unsigned long)&fimc_drvdata_exynos4210, + }, { + .name = "exynos4x12-fimc", + .driver_data = (unsigned long)&fimc_drvdata_exynos4x12, }, {}, }; diff --git a/drivers/media/platform/s5p-fimc/fimc-core.h b/drivers/media/platform/s5p-fimc/fimc-core.h index c0040d792499..424ff960f0d9 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.h +++ b/drivers/media/platform/s5p-fimc/fimc-core.h @@ -372,6 +372,7 @@ struct fimc_pix_limit { * @has_mainscaler_ext: 1 if extended mainscaler ratios in CIEXTEN register * are present in this IP revision * @has_cam_if: set if this instance has a camera input interface + * @has_isp_wb: set if this instance has ISP writeback input * @pix_limit: pixel size constraints for the scaler * @min_inp_pixsize: minimum input pixel size * @min_out_pixsize: minimum output pixel size @@ -386,8 +387,9 @@ struct fimc_variant { unsigned int has_cistatus2:1; unsigned int has_mainscaler_ext:1; unsigned int has_cam_if:1; + unsigned int has_isp_wb:1; unsigned int has_alpha:1; - struct fimc_pix_limit *pix_limit; + const struct fimc_pix_limit *pix_limit; u16 min_inp_pixsize; u16 min_out_pixsize; u16 hor_offs_align; @@ -402,7 +404,7 @@ struct fimc_variant { * @lclk_frequency: local bus clock frequency */ struct fimc_drvdata { - struct fimc_variant *variant[FIMC_MAX_DEVS]; + const struct fimc_variant *variant[FIMC_MAX_DEVS]; int num_entities; unsigned long lclk_frequency; }; @@ -435,7 +437,7 @@ struct fimc_dev { struct mutex lock; struct platform_device *pdev; struct s5p_platform_fimc *pdata; - struct fimc_variant *variant; + const struct fimc_variant *variant; u16 id; struct clk *clock[MAX_FIMC_CLOCKS]; void __iomem *regs; diff --git a/drivers/media/platform/s5p-fimc/fimc-m2m.c b/drivers/media/platform/s5p-fimc/fimc-m2m.c index 1d21da4bd24b..1d57f3b87aa3 100644 --- a/drivers/media/platform/s5p-fimc/fimc-m2m.c +++ b/drivers/media/platform/s5p-fimc/fimc-m2m.c @@ -300,7 +300,7 @@ static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh, static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f) { struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_variant *variant = fimc->variant; + const struct fimc_variant *variant = fimc->variant; struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; struct fimc_fmt *fmt; u32 max_w, mod_x, mod_y; diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.c b/drivers/media/platform/s5p-fimc/fimc-reg.c index f0c34ca41894..c05d0444192f 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.c +++ b/drivers/media/platform/s5p-fimc/fimc-reg.c @@ -312,7 +312,7 @@ static void fimc_hw_set_scaler(struct fimc_ctx *ctx) void fimc_hw_set_mainscaler(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; - struct fimc_variant *variant = dev->variant; + const struct fimc_variant *variant = dev->variant; struct fimc_scaler *sc = &ctx->scaler; u32 cfg; -- cgit v1.2.3 From e26991b49a132626130428d64222a355ffdd7139 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 29 Aug 2012 14:35:21 -0300 Subject: [media] s5p-csis: Add support for raw Bayer pixel formats The MIPI CSIS device supports MIPI CSI-2 RAW8, RAW10, RAW12 data types. Add related media bus pixel format definitions. This doesn't cover all possible supported media bus pixel formats. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/mipi-csis.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index 9528ee65a843..36916d3f199a 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -220,6 +220,18 @@ static const struct csis_pix_format s5pcsis_formats[] = { .code = V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8, .fmt_reg = S5PCSIS_CFG_FMT_USER(1), .data_alignment = 32, + }, { + .code = V4L2_MBUS_FMT_SGRBG8_1X8, + .fmt_reg = S5PCSIS_CFG_FMT_RAW8, + .data_alignment = 24, + }, { + .code = V4L2_MBUS_FMT_SGRBG10_1X10, + .fmt_reg = S5PCSIS_CFG_FMT_RAW10, + .data_alignment = 24, + }, { + .code = V4L2_MBUS_FMT_SGRBG12_1X12, + .fmt_reg = S5PCSIS_CFG_FMT_RAW12, + .data_alignment = 24, } }; -- cgit v1.2.3 From cd65a645a4f5e456607067734f9a11385c9dce7b Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 2 Nov 2012 14:20:27 -0300 Subject: [media] s5p-csis: Enable only data lanes that are actively used Enable only MIPI CSI-2 data lanes at the DPHY that are actively used, rather than unmasking all unconditionally. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/mipi-csis.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index 36916d3f199a..8ec7c3b79659 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -273,7 +273,8 @@ static void s5pcsis_reset(struct csis_state *state) static void s5pcsis_system_enable(struct csis_state *state, int on) { - u32 val; + struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data; + u32 val, mask; val = s5pcsis_read(state, S5PCSIS_CTRL); if (on) @@ -283,10 +284,11 @@ static void s5pcsis_system_enable(struct csis_state *state, int on) s5pcsis_write(state, S5PCSIS_CTRL, val); val = s5pcsis_read(state, S5PCSIS_DPHYCTRL); - if (on) - val |= S5PCSIS_DPHYCTRL_ENABLE; - else - val &= ~S5PCSIS_DPHYCTRL_ENABLE; + val &= ~S5PCSIS_DPHYCTRL_ENABLE; + if (on) { + mask = (1 << (pdata->lanes + 1)) - 1; + val |= (mask & S5PCSIS_DPHYCTRL_ENABLE); + } s5pcsis_write(state, S5PCSIS_DPHYCTRL, val); } -- cgit v1.2.3 From a2fea0dfddf95b7f1e7adb3630c7d07a92cfb09b Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 28 Sep 2012 11:05:53 -0300 Subject: [media] s5p-csis: Add registers logging for debugging Dump registers contents together with the event counters state in VIDIOC_LOG_STATUS ioctl. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/mipi-csis.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index 8ec7c3b79659..8a06f1402f37 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -383,6 +383,30 @@ err: return -ENXIO; } +static void dump_regs(struct csis_state *state, const char *label) +{ + struct { + u32 offset; + const char * const name; + } registers[] = { + { 0x00, "CTRL" }, + { 0x04, "DPHYCTRL" }, + { 0x08, "CONFIG" }, + { 0x0c, "DPHYSTS" }, + { 0x10, "INTMSK" }, + { 0x2c, "RESOL" }, + { 0x38, "SDW_CONFIG" }, + }; + u32 i; + + v4l2_info(&state->sd, "--- %s ---\n", label); + + for (i = 0; i < ARRAY_SIZE(registers); i++) { + u32 cfg = s5pcsis_read(state, registers[i].offset); + v4l2_info(&state->sd, "%10s: 0x%08x\n", registers[i].name, cfg); + } +} + static void s5pcsis_start_stream(struct csis_state *state) { s5pcsis_reset(state); @@ -583,7 +607,11 @@ static int s5pcsis_log_status(struct v4l2_subdev *sd) { struct csis_state *state = sd_to_csis_state(sd); + mutex_lock(&state->lock); s5pcsis_log_counters(state, true); + if (debug && (state->flags & ST_POWERED)) + dump_regs(state, __func__); + mutex_unlock(&state->lock); return 0; } -- cgit v1.2.3 From 588c87be0b44ccf44e321eeae52c674a67a6adc0 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 27 Nov 2012 11:57:42 -0300 Subject: [media] s5p-fimc: Add sensor group ids for fimc-is Add subdev group id definition for FIMC-IS ISP and sensor subdev. While at it rename all group id definitions to start with GRP_ID. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-mdevice.c | 21 +++++++++++---------- drivers/media/platform/s5p-fimc/fimc-mdevice.h | 12 +++++++----- 2 files changed, 18 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 1bd5678cfeb9..280a4d6321ed 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -62,16 +62,17 @@ static void fimc_pipeline_prepare(struct fimc_pipeline *p, sd = media_entity_to_v4l2_subdev(pad->entity); switch (sd->grp_id) { - case SENSOR_GROUP_ID: + case GRP_ID_FIMC_IS_SENSOR: + case GRP_ID_SENSOR: p->subdevs[IDX_SENSOR] = sd; break; - case CSIS_GROUP_ID: + case GRP_ID_CSIS: p->subdevs[IDX_CSIS] = sd; break; - case FLITE_GROUP_ID: + case GRP_ID_FLITE: p->subdevs[IDX_FLITE] = sd; break; - case FIMC_GROUP_ID: + case GRP_ID_FIMC: /* No need to control FIMC subdev through subdev ops */ break; default: @@ -269,7 +270,7 @@ static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd, return ERR_PTR(-EPROBE_DEFER); } v4l2_set_subdev_hostdata(sd, s_info); - sd->grp_id = SENSOR_GROUP_ID; + sd->grp_id = GRP_ID_SENSOR; v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n", s_info->pdata.board_info->type); @@ -351,7 +352,7 @@ static int fimc_register_callback(struct device *dev, void *p) return 0; sd = &fimc->vid_cap.subdev; - sd->grp_id = FIMC_GROUP_ID; + sd->grp_id = GRP_ID_FIMC; v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops); ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); @@ -374,7 +375,7 @@ static int fimc_lite_register_callback(struct device *dev, void *p) if (fimc == NULL || fimc->index >= FIMC_LITE_MAX_DEVS) return 0; - fimc->subdev.grp_id = FLITE_GROUP_ID; + fimc->subdev.grp_id = GRP_ID_FLITE; v4l2_set_subdev_hostdata(&fimc->subdev, (void *)&fimc_pipeline_ops); ret = v4l2_device_register_subdev(&fmd->v4l2_dev, &fimc->subdev); @@ -404,7 +405,7 @@ static int csis_register_callback(struct device *dev, void *p) v4l2_info(sd, "csis%d sd: %s\n", pdev->id, sd->name); id = pdev->id < 0 ? 0 : pdev->id; - sd->grp_id = CSIS_GROUP_ID; + sd->grp_id = GRP_ID_CSIS; ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); if (!ret) @@ -828,11 +829,11 @@ static int fimc_md_link_notify(struct media_pad *source, sd = media_entity_to_v4l2_subdev(sink->entity); switch (sd->grp_id) { - case FLITE_GROUP_ID: + case GRP_ID_FLITE: fimc_lite = v4l2_get_subdevdata(sd); pipeline = &fimc_lite->pipeline; break; - case FIMC_GROUP_ID: + case GRP_ID_FIMC: fimc = v4l2_get_subdevdata(sd); pipeline = &fimc->pipeline; break; diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.h b/drivers/media/platform/s5p-fimc/fimc-mdevice.h index 2d8d41d82620..da7d9922cf58 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.h +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.h @@ -22,11 +22,13 @@ #include "mipi-csis.h" /* Group IDs of sensor, MIPI-CSIS, FIMC-LITE and the writeback subdevs. */ -#define SENSOR_GROUP_ID (1 << 8) -#define CSIS_GROUP_ID (1 << 9) -#define WRITEBACK_GROUP_ID (1 << 10) -#define FIMC_GROUP_ID (1 << 11) -#define FLITE_GROUP_ID (1 << 12) +#define GRP_ID_SENSOR (1 << 8) +#define GRP_ID_FIMC_IS_SENSOR (1 << 9) +#define GRP_ID_WRITEBACK (1 << 10) +#define GRP_ID_CSIS (1 << 11) +#define GRP_ID_FIMC (1 << 12) +#define GRP_ID_FLITE (1 << 13) +#define GRP_ID_FIMC_IS (1 << 14) #define FIMC_MAX_SENSORS 8 #define FIMC_MAX_CAMCLKS 2 -- cgit v1.2.3 From 6319d6a002beb2644b4a9058a12a04b2bbf98502 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 28 Nov 2012 15:41:01 -0300 Subject: [media] fimc-lite: Add ISP FIFO output support Add second source media pad for the FIFO data output to FIMC-IS and implement subdev s_stream op for configurations where FIMC-LITE is used as a glue logic between FIMC-IS and MIPI-CSIS or an image sensor. The second source media pad will be linked to the FIMC-LITE video node. For proper configuration the attached image sensor/video encoder properties are needed, like video bus type, signal polarities, etc. For this purpose there is a small routine added that walks the pipeline and returns the sensor subdev. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-lite.c | 146 +++++++++++++++++++------ drivers/media/platform/s5p-fimc/fimc-lite.h | 7 +- drivers/media/platform/s5p-fimc/fimc-mdevice.c | 2 +- 3 files changed, 120 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/s5p-fimc/fimc-lite.c index 1b309a72f09f..765b8e4cbf4e 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite.c @@ -120,25 +120,29 @@ static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat, return def_fmt; } -static int fimc_lite_hw_init(struct fimc_lite *fimc) +static int fimc_lite_hw_init(struct fimc_lite *fimc, bool isp_output) { struct fimc_pipeline *pipeline = &fimc->pipeline; - struct fimc_sensor_info *sensor; + struct v4l2_subdev *sensor; + struct fimc_sensor_info *si; unsigned long flags; - if (pipeline->subdevs[IDX_SENSOR] == NULL) + sensor = isp_output ? fimc->sensor : pipeline->subdevs[IDX_SENSOR]; + + if (sensor == NULL) return -ENXIO; if (fimc->fmt == NULL) return -EINVAL; - sensor = v4l2_get_subdev_hostdata(pipeline->subdevs[IDX_SENSOR]); + /* Get sensor configuration data from the sensor subdev */ + si = v4l2_get_subdev_hostdata(sensor); spin_lock_irqsave(&fimc->slock, flags); - flite_hw_set_camera_bus(fimc, &sensor->pdata); + flite_hw_set_camera_bus(fimc, &si->pdata); flite_hw_set_source_format(fimc, &fimc->inp_frame); flite_hw_set_window_offset(fimc, &fimc->inp_frame); - flite_hw_set_output_dma(fimc, &fimc->out_frame, true); + flite_hw_set_output_dma(fimc, &fimc->out_frame, !isp_output); flite_hw_set_interrupt_mask(fimc); flite_hw_set_test_pattern(fimc, fimc->test_pattern->val); @@ -296,7 +300,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) fimc->frame_count = 0; - ret = fimc_lite_hw_init(fimc); + ret = fimc_lite_hw_init(fimc, false); if (ret) { fimc_lite_reinit(fimc, false); return ret; @@ -460,6 +464,11 @@ static int fimc_lite_open(struct file *file) if (mutex_lock_interruptible(&fimc->lock)) return -ERESTARTSYS; + if (fimc->out_path != FIMC_IO_DMA) { + ret = -EBUSY; + goto done; + } + set_bit(ST_FLITE_IN_USE, &fimc->state); ret = pm_runtime_get_sync(&fimc->pdev->dev); if (ret < 0) @@ -962,6 +971,29 @@ static const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = { .vidioc_streamoff = fimc_lite_streamoff, }; +/* Called with the media graph mutex held */ +static struct v4l2_subdev *__find_remote_sensor(struct media_entity *me) +{ + struct media_pad *pad = &me->pads[0]; + struct v4l2_subdev *sd; + + while (pad->flags & MEDIA_PAD_FL_SINK) { + /* source pad */ + pad = media_entity_remote_source(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + sd = media_entity_to_v4l2_subdev(pad->entity); + + if (sd->grp_id == GRP_ID_FIMC_IS_SENSOR) + return sd; + /* sink pad */ + pad = &sd->entity.pads[0]; + } + return NULL; +} + /* Capture subdev media entity operations */ static int fimc_lite_link_setup(struct media_entity *entity, const struct media_pad *local, @@ -970,46 +1002,59 @@ static int fimc_lite_link_setup(struct media_entity *entity, struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct fimc_lite *fimc = v4l2_get_subdevdata(sd); unsigned int remote_ent_type = media_entity_type(remote->entity); + int ret = 0; if (WARN_ON(fimc == NULL)) return 0; v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x", - __func__, local->entity->name, remote->entity->name, + __func__, remote->entity->name, local->entity->name, flags, fimc->source_subdev_grp_id); - switch (local->index) { - case FIMC_SD_PAD_SINK: - if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV) - return -EINVAL; + mutex_lock(&fimc->lock); + switch (local->index) { + case FLITE_SD_PAD_SINK: + if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV) { + ret = -EINVAL; + break; + } if (flags & MEDIA_LNK_FL_ENABLED) { - if (fimc->source_subdev_grp_id != 0) - return -EBUSY; - fimc->source_subdev_grp_id = sd->grp_id; - return 0; + if (fimc->source_subdev_grp_id == 0) + fimc->source_subdev_grp_id = sd->grp_id; + else + ret = -EBUSY; + } else { + fimc->source_subdev_grp_id = 0; + fimc->sensor = NULL; } + break; - fimc->source_subdev_grp_id = 0; + case FLITE_SD_PAD_SOURCE_DMA: + if (!(flags & MEDIA_LNK_FL_ENABLED)) + fimc->out_path = FIMC_IO_NONE; + else if (remote_ent_type == MEDIA_ENT_T_DEVNODE) + fimc->out_path = FIMC_IO_DMA; + else + ret = -EINVAL; break; - case FIMC_SD_PAD_SOURCE: - if (!(flags & MEDIA_LNK_FL_ENABLED)) { + case FLITE_SD_PAD_SOURCE_ISP: + if (!(flags & MEDIA_LNK_FL_ENABLED)) fimc->out_path = FIMC_IO_NONE; - return 0; - } - if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV) + else if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV) fimc->out_path = FIMC_IO_ISP; else - fimc->out_path = FIMC_IO_DMA; + ret = -EINVAL; break; default: v4l2_err(sd, "Invalid pad index\n"); - return -EINVAL; + ret = -EINVAL; } - return 0; + mutex_unlock(&fimc->lock); + return ret; } static const struct media_entity_operations fimc_lite_subdev_media_ops = { @@ -1188,13 +1233,49 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on) { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + unsigned long flags; + int ret; - if (fimc->out_path == FIMC_IO_DMA) + /* + * Find sensor subdev linked to FIMC-LITE directly or through + * MIPI-CSIS. This is required for configuration where FIMC-LITE + * is used as a subdev only and feeds data internally to FIMC-IS. + * The pipeline links are protected through entity.stream_count + * so there is no need to take the media graph mutex here. + */ + fimc->sensor = __find_remote_sensor(&sd->entity); + + mutex_lock(&fimc->lock); + if (fimc->out_path != FIMC_IO_ISP) { + mutex_unlock(&fimc->lock); return -ENOIOCTLCMD; + } - /* TODO: */ + if (on) { + flite_hw_reset(fimc); + ret = fimc_lite_hw_init(fimc, true); + if (!ret) { + spin_lock_irqsave(&fimc->slock, flags); + flite_hw_capture_start(fimc); + spin_unlock_irqrestore(&fimc->slock, flags); + } + } else { + set_bit(ST_FLITE_OFF, &fimc->state); - return 0; + spin_lock_irqsave(&fimc->slock, flags); + flite_hw_capture_stop(fimc); + spin_unlock_irqrestore(&fimc->slock, flags); + + ret = wait_event_timeout(fimc->irq_queue, + !test_bit(ST_FLITE_OFF, &fimc->state), + msecs_to_jiffies(200)); + if (ret == 0) + v4l2_err(sd, "s_stream(0) timeout\n"); + clear_bit(ST_FLITE_RUN, &fimc->state); + } + + mutex_unlock(&fimc->lock); + return ret; } static int fimc_lite_subdev_s_power(struct v4l2_subdev *sd, int on) @@ -1347,9 +1428,10 @@ static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc) sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "FIMC-LITE.%d", fimc->index); - fimc->subdev_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; - fimc->subdev_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM, + fimc->subdev_pads[FLITE_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + fimc->subdev_pads[FLITE_SD_PAD_SOURCE_DMA].flags = MEDIA_PAD_FL_SOURCE; + fimc->subdev_pads[FLITE_SD_PAD_SOURCE_ISP].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&sd->entity, FLITE_SD_PADS_NUM, fimc->subdev_pads, 0); if (ret) return ret; @@ -1518,7 +1600,7 @@ static int fimc_lite_resume(struct device *dev) INIT_LIST_HEAD(&fimc->active_buf_q); fimc_pipeline_call(fimc, open, &fimc->pipeline, &fimc->vfd.entity, false); - fimc_lite_hw_init(fimc); + fimc_lite_hw_init(fimc, fimc->out_path == FIMC_IO_ISP); clear_bit(ST_FLITE_SUSPENDED, &fimc->state); for (i = 0; i < fimc->reqbufs_count; i++) { diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.h b/drivers/media/platform/s5p-fimc/fimc-lite.h index 3081db35c5b0..4576922952f3 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.h +++ b/drivers/media/platform/s5p-fimc/fimc-lite.h @@ -45,8 +45,9 @@ enum { }; #define FLITE_SD_PAD_SINK 0 -#define FLITE_SD_PAD_SOURCE 1 -#define FLITE_SD_PADS_NUM 2 +#define FLITE_SD_PAD_SOURCE_DMA 1 +#define FLITE_SD_PAD_SOURCE_ISP 2 +#define FLITE_SD_PADS_NUM 3 struct flite_variant { unsigned short max_width; @@ -104,6 +105,7 @@ struct flite_buffer { * @subdev: FIMC-LITE subdev * @vd_pad: media (sink) pad for the capture video node * @subdev_pads: the subdev media pads + * @sensor: sensor subdev attached to FIMC-LITE directly or through MIPI-CSIS * @ctrl_handler: v4l2 control handler * @test_pattern: test pattern controls * @index: FIMC-LITE platform device index @@ -139,6 +141,7 @@ struct fimc_lite { struct v4l2_subdev subdev; struct media_pad vd_pad; struct media_pad subdev_pads[FLITE_SD_PADS_NUM]; + struct v4l2_subdev *sensor; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_ctrl *test_pattern; u32 index; diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 280a4d6321ed..d0028740640a 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -603,7 +603,7 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) source = &fimc->subdev.entity; sink = &fimc->vfd.entity; /* FIMC-LITE's subdev and video node */ - ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE, + ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_DMA, sink, 0, flags); if (ret) break; -- cgit v1.2.3 From 1c9f5bd7cb8a74965e7a19eead5d77c35bf30db7 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Thu, 22 Nov 2012 12:13:27 -0300 Subject: [media] s5p-fimc: Add support for sensors with multiple pads Some sensors can have more than one pad (case of S5C73M3). In such cases FIMC assumes the last pad of the sensor is the source pad. Signed-off-by: Andrzej Hajda Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-capture.c | 6 ++++-- drivers/media/platform/s5p-fimc/fimc-mdevice.c | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index 52d8d885d287..50c0da9c788b 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -884,14 +884,16 @@ static int fimc_get_sensor_frame_desc(struct v4l2_subdev *sensor, { struct v4l2_mbus_frame_desc fd; int i, ret; + int pad; for (i = 0; i < num_planes; i++) fd.entry[i].length = plane_fmt[i].sizeimage; + pad = sensor->entity.num_pads - 1; if (try) - ret = v4l2_subdev_call(sensor, pad, set_frame_desc, 0, &fd); + ret = v4l2_subdev_call(sensor, pad, set_frame_desc, pad, &fd); else - ret = v4l2_subdev_call(sensor, pad, get_frame_desc, 0, &fd); + ret = v4l2_subdev_call(sensor, pad, get_frame_desc, pad, &fd); if (ret < 0) return ret; diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index d0028740640a..8b43f982c12d 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -659,7 +659,8 @@ static int fimc_md_create_links(struct fimc_md *fmd) "but s5p-csis module is not loaded!\n")) return -EINVAL; - ret = media_entity_create_link(&sensor->entity, 0, + pad = sensor->entity.num_pads - 1; + ret = media_entity_create_link(&sensor->entity, pad, &csis->entity, CSIS_PAD_SINK, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); -- cgit v1.2.3 From 04881127340c43fc5b8dbc2c381c1928ee22559e Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Thu, 22 Nov 2012 08:59:06 -0300 Subject: [media] gspca - stv06xx: Fix a regression with the bridge/sensor vv6410 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Setting the H and V flip controls at webcam connection time prevents the webcam to work correctly. This patch checks if the webcam is streaming before setting the flips. It does not set the flips (nor other controls) at webcam start time. Tested-by: Philippe ROUBACH Signed-off-by: Jean-François Moine Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c index 748e1421d6d8..cbb153180d59 100644 --- a/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c @@ -52,9 +52,13 @@ static int vv6410_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_HFLIP: + if (!gspca_dev->streaming) + return 0; err = vv6410_set_hflip(gspca_dev, ctrl->val); break; case V4L2_CID_VFLIP: + if (!gspca_dev->streaming) + return 0; err = vv6410_set_vflip(gspca_dev, ctrl->val); break; case V4L2_CID_GAIN: -- cgit v1.2.3 From 143dd34597a2e051cb0938b28387b94742d4e4e4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 29 Nov 2012 06:39:49 -0300 Subject: [media] gspca-pac207: Add a led_invert module parameter Some cams have their led connected in such a way that on = off and visa versa unfortunately we cannot tell this from the driver in any way, so add a module parameter for this. Reported-by: Yuri Glushkov Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/pac207.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/gspca/pac207.c b/drivers/media/usb/gspca/pac207.c index d236d1791f78..1f253dfebe47 100644 --- a/drivers/media/usb/gspca/pac207.c +++ b/drivers/media/usb/gspca/pac207.c @@ -55,6 +55,11 @@ MODULE_LICENSE("GPL"); #define PAC207_AUTOGAIN_DEADZONE 30 +/* global parameters */ +static int led_invert; +module_param(led_invert, int, 0644); +MODULE_PARM_DESC(led_invert, "Invert led"); + /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ @@ -187,10 +192,14 @@ static int sd_config(struct gspca_dev *gspca_dev, /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - pac207_write_reg(gspca_dev, 0x41, 0x00); - /* Bit_0=Image Format, - * Bit_1=LED, - * Bit_2=Compression test mode enable */ + u8 mode; + + /* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */ + if (led_invert) + mode = 0x02; + else + mode = 0x00; + pac207_write_reg(gspca_dev, 0x41, mode); pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ return gspca_dev->usb_err; @@ -303,7 +312,11 @@ static int sd_start(struct gspca_dev *gspca_dev) pac207_write_reg(gspca_dev, 0x02, v4l2_ctrl_g_ctrl(gspca_dev->exposure)); /* PXCK = 12MHz /n */ - mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */ + /* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */ + if (led_invert) + mode = 0x00; + else + mode = 0x02; if (gspca_dev->width == 176) { /* 176x144 */ mode |= 0x01; PDEBUG(D_STREAM, "pac207_start mode 176x144"); @@ -325,8 +338,15 @@ static int sd_start(struct gspca_dev *gspca_dev) static void sd_stopN(struct gspca_dev *gspca_dev) { + u8 mode; + + /* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */ + if (led_invert) + mode = 0x02; + else + mode = 0x00; pac207_write_reg(gspca_dev, 0x40, 0x00); /* Stop ISO pipe */ - pac207_write_reg(gspca_dev, 0x41, 0x00); /* Turn of LED */ + pac207_write_reg(gspca_dev, 0x41, mode); /* Turn off LED */ pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ } -- cgit v1.2.3 From 7a29ee2e37b3f9675f46c87998c67b68c315c54a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 29 Nov 2012 06:56:45 -0300 Subject: [media] stk-webcam: Add an upside down dmi table, and add the Asus G1 to it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stk-webcam module is inserted upside-down in some webcams, so we need to de hflip and vflip by default on some models. Note that this patch inverts the value of the controls as reported by the control API in this case so that for the user they still make sense (iow not doing any flipping from the ctrl API pov results in an upright image). Reported-by: Jose Gómez Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/stkwebcam/stk-webcam.c | 56 ++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index 52296f7ec966..4cbab085e348 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -38,12 +39,12 @@ #include "stk-webcam.h" -static bool hflip; -module_param(hflip, bool, 0444); +static int hflip = -1; +module_param(hflip, int, 0444); MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 0"); -static bool vflip; -module_param(vflip, bool, 0444); +static int vflip = -1; +module_param(vflip, int, 0444); MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 0"); static int debug; @@ -62,6 +63,19 @@ static struct usb_device_id stkwebcam_table[] = { }; MODULE_DEVICE_TABLE(usb, stkwebcam_table); +/* The stk webcam laptop module is mounted upside down in some laptops :( */ +static const struct dmi_system_id stk_upside_down_dmi_table[] = { + { + .ident = "ASUS G1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "G1") + } + }, + {} +}; + + /* * Basic stuff */ @@ -817,10 +831,16 @@ static int stk_vidioc_g_ctrl(struct file *filp, c->value = dev->vsettings.brightness; break; case V4L2_CID_HFLIP: - c->value = dev->vsettings.hflip; + if (dmi_check_system(stk_upside_down_dmi_table)) + c->value = !dev->vsettings.hflip; + else + c->value = dev->vsettings.hflip; break; case V4L2_CID_VFLIP: - c->value = dev->vsettings.vflip; + if (dmi_check_system(stk_upside_down_dmi_table)) + c->value = !dev->vsettings.vflip; + else + c->value = dev->vsettings.vflip; break; default: return -EINVAL; @@ -837,10 +857,16 @@ static int stk_vidioc_s_ctrl(struct file *filp, dev->vsettings.brightness = c->value; return stk_sensor_set_brightness(dev, c->value >> 8); case V4L2_CID_HFLIP: - dev->vsettings.hflip = c->value; + if (dmi_check_system(stk_upside_down_dmi_table)) + dev->vsettings.hflip = !c->value; + else + dev->vsettings.hflip = c->value; return 0; case V4L2_CID_VFLIP: - dev->vsettings.vflip = c->value; + if (dmi_check_system(stk_upside_down_dmi_table)) + dev->vsettings.vflip = !c->value; + else + dev->vsettings.vflip = c->value; return 0; default: return -EINVAL; @@ -1276,8 +1302,18 @@ static int stk_camera_probe(struct usb_interface *interface, dev->interface = interface; usb_get_intf(interface); - dev->vsettings.vflip = vflip; - dev->vsettings.hflip = hflip; + if (hflip != -1) + dev->vsettings.hflip = hflip; + else if (dmi_check_system(stk_upside_down_dmi_table)) + dev->vsettings.hflip = 1; + else + dev->vsettings.hflip = 0; + if (vflip != -1) + dev->vsettings.vflip = vflip; + else if (dmi_check_system(stk_upside_down_dmi_table)) + dev->vsettings.vflip = 1; + else + dev->vsettings.vflip = 0; dev->n_sbufs = 0; set_present(dev); -- cgit v1.2.3 From c3075388881fbafda3b0991fbefc6a5eef4d483c Mon Sep 17 00:00:00 2001 From: Jacob Schloss Date: Sun, 9 Dec 2012 20:18:25 -0300 Subject: [media] gspca_kinect: add Kinect for Windows USB id Add the USB ID for the Kinect for Windows RGB camera so it can be used with the gspca_kinect driver. Signed-off-by: Jacob Schloss Signed-off-by: Antonio Ospite Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/kinect.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/usb/gspca/kinect.c b/drivers/media/usb/gspca/kinect.c index 40ad6687ee5d..3773a8a745df 100644 --- a/drivers/media/usb/gspca/kinect.c +++ b/drivers/media/usb/gspca/kinect.c @@ -381,6 +381,7 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x045e, 0x02ae)}, + {USB_DEVICE(0x045e, 0x02bf)}, {} }; -- cgit v1.2.3 From e52ec68015b81ccd5f0950d56328b78f43ae2985 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 12 Dec 2012 05:58:12 -0300 Subject: [media] gspca: Use module_usb_driver macro module_usb_driver eliminates a lot of boilerplate by replacing module_init() and module_exit() calls. Signed-off-by: Sachin Kamat Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/jl2005bcd.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/gspca/jl2005bcd.c b/drivers/media/usb/gspca/jl2005bcd.c index 62ba80d9b998..fdaeeb14453f 100644 --- a/drivers/media/usb/gspca/jl2005bcd.c +++ b/drivers/media/usb/gspca/jl2005bcd.c @@ -536,20 +536,4 @@ static struct usb_driver sd_driver = { #endif }; -/* -- module insert / remove -- */ -static int __init sd_mod_init(void) -{ - int ret; - - ret = usb_register(&sd_driver); - if (ret < 0) - return ret; - return 0; -} -static void __exit sd_mod_exit(void) -{ - usb_deregister(&sd_driver); -} - -module_init(sd_mod_init); -module_exit(sd_mod_exit); +module_usb_driver(sd_driver); -- cgit v1.2.3 From d83fcb793b8a17d2ea29e3450295094c12693f80 Mon Sep 17 00:00:00 2001 From: Erik Andrén Date: Mon, 10 Dec 2012 16:35:19 -0300 Subject: [media] gspca_stv06xx: Disable flip controls for vv6410 sensor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disable the hardware VFLIP and HFLIP controls for now as we lack a mechanism to adjust the frame offset, thus rending a bayerimage not compliant with the announced format. Signed-off-by: Erik Andrén Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c index cbb153180d59..e95fa8997d22 100644 --- a/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c @@ -98,11 +98,14 @@ static int vv6410_init_controls(struct sd *sd) { struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; - v4l2_ctrl_handler_init(hdl, 4); - v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_handler_init(hdl, 2); + /* Disable the hardware VFLIP and HFLIP as we currently lack a + mechanism to adjust the image offset in such a way that + we don't need to renegotiate the announced format */ + /* v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, */ + /* V4L2_CID_HFLIP, 0, 1, 1, 0); */ + /* v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, */ + /* V4L2_CID_VFLIP, 0, 1, 1, 0); */ v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, V4L2_CID_EXPOSURE, 0, 32768, 1, 20000); v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, -- cgit v1.2.3 From 8547fd18e9009ea0f010c5ec7463d61efa72b555 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 21 Dec 2012 11:01:48 -0300 Subject: [media] gspca_t613: Fix compiling with GSPCA_DEBUG defined Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/t613.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/gspca/t613.c b/drivers/media/usb/gspca/t613.c index 8bc6c3ceec2c..b92d4ef2de6e 100644 --- a/drivers/media/usb/gspca/t613.c +++ b/drivers/media/usb/gspca/t613.c @@ -494,7 +494,7 @@ static void setcolors(struct gspca_dev *gspca_dev, s32 val) static void setgamma(struct gspca_dev *gspca_dev, s32 val) { - PDEBUG(D_CONF, "Gamma: %d", sd->gamma); + PDEBUG(D_CONF, "Gamma: %d", val); reg_w_ixbuf(gspca_dev, 0x90, gamma_table[val], sizeof gamma_table[0]); } -- cgit v1.2.3 From 18fa0d36ad5b451d835ca79301265649334214c0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 21 Dec 2012 08:08:47 -0300 Subject: [media] gspca_sonixb: Properly wait between i2c writes We must wait for the previous i2c write to complete before starting a new one. Sofar we were getting away with this, but it seems that some parts of the usb-subsystem has been sped up making us go to fast :) This fixes streaming on sn9c103 based cams not working with an "i2c_w error" error. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/sonixb.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/gspca/sonixb.c b/drivers/media/usb/gspca/sonixb.c index 70511d5f9538..1220340e7602 100644 --- a/drivers/media/usb/gspca/sonixb.c +++ b/drivers/media/usb/gspca/sonixb.c @@ -496,7 +496,7 @@ static void reg_w(struct gspca_dev *gspca_dev, } } -static void i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer) +static void i2c_w(struct gspca_dev *gspca_dev, const u8 *buf) { int retry = 60; @@ -504,16 +504,19 @@ static void i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer) return; /* is i2c ready */ - reg_w(gspca_dev, 0x08, buffer, 8); + reg_w(gspca_dev, 0x08, buf, 8); while (retry--) { if (gspca_dev->usb_err < 0) return; - msleep(10); + msleep(1); reg_r(gspca_dev, 0x08); if (gspca_dev->usb_buf[0] & 0x04) { if (gspca_dev->usb_buf[0] & 0x08) { dev_err(gspca_dev->v4l2_dev.dev, - "i2c write error\n"); + "i2c error writing %02x %02x %02x %02x" + " %02x %02x %02x %02x\n", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); gspca_dev->usb_err = -EIO; } return; @@ -530,7 +533,7 @@ static void i2c_w_vector(struct gspca_dev *gspca_dev, for (;;) { if (gspca_dev->usb_err < 0) return; - reg_w(gspca_dev, 0x08, *buffer, 8); + i2c_w(gspca_dev, *buffer); len -= 8; if (len <= 0) break; -- cgit v1.2.3 From 2bffebc1e6dcd49e5e92b348c9a1c6bcea8c2f5e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 21 Dec 2012 11:17:42 -0300 Subject: [media] gspca_sonixj: Add a small delay after i2c_w1 We already have the same delay in i2c_w8, but it was missing from i2c_w1, adding this delay fixes the Microsoft VX-3000 camera often (but not always) streaming video data with a very green-ish tint. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/sonixj.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/usb/gspca/sonixj.c b/drivers/media/usb/gspca/sonixj.c index 5a86047b846f..36307a9028a9 100644 --- a/drivers/media/usb/gspca/sonixj.c +++ b/drivers/media/usb/gspca/sonixj.c @@ -1550,6 +1550,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) 0, gspca_dev->usb_buf, 8, 500); + msleep(2); if (ret < 0) { pr_err("i2c_w1 err %d\n", ret); gspca_dev->usb_err = ret; -- cgit v1.2.3 From 47800bc43eda104d49ef51aefe549f90c00922d0 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 3 Dec 2012 06:24:32 -0300 Subject: [media] s5p-fimc: Improved pipeline try format routine Make the pipeline try format routine more generic to support any number of subdevs in the pipeline, rather than hard coding it for only a sensor, MIPI-CSIS and FIMC subdevs and the FIMC video node. Signed-off-by: Andrzej Hajda Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-capture.c | 91 +++++++++++++++++--------- 1 file changed, 61 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index 50c0da9c788b..95e6a7820b5e 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -793,6 +793,21 @@ static int fimc_cap_enum_fmt_mplane(struct file *file, void *priv, return 0; } +static struct media_entity *fimc_pipeline_get_head(struct media_entity *me) +{ + struct media_pad *pad = &me->pads[0]; + + while (!(pad->flags & MEDIA_PAD_FL_SOURCE)) { + pad = media_entity_remote_source(pad); + if (!pad) + break; + me = pad->entity; + pad = &me->pads[0]; + } + + return me; +} + /** * fimc_pipeline_try_format - negotiate and/or set formats at pipeline * elements @@ -808,19 +823,23 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, { struct fimc_dev *fimc = ctx->fimc_dev; struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR]; - struct v4l2_subdev *csis = fimc->pipeline.subdevs[IDX_CSIS]; struct v4l2_subdev_format sfmt; struct v4l2_mbus_framefmt *mf = &sfmt.format; - struct fimc_fmt *ffmt = NULL; - int ret, i = 0; + struct media_entity *me; + struct fimc_fmt *ffmt; + struct media_pad *pad; + int ret, i = 1; + u32 fcc; if (WARN_ON(!sd || !tfmt)) return -EINVAL; memset(&sfmt, 0, sizeof(sfmt)); sfmt.format = *tfmt; - sfmt.which = set ? V4L2_SUBDEV_FORMAT_ACTIVE : V4L2_SUBDEV_FORMAT_TRY; + + me = fimc_pipeline_get_head(&sd->entity); + while (1) { ffmt = fimc_find_format(NULL, mf->code != 0 ? &mf->code : NULL, FMT_FLAGS_CAM, i++); @@ -833,40 +852,52 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, } mf->code = tfmt->code = ffmt->mbus_code; - ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt); - if (ret) - return ret; - if (mf->code != tfmt->code) { - mf->code = 0; - continue; - } - if (mf->width != tfmt->width || mf->height != tfmt->height) { - u32 fcc = ffmt->fourcc; - tfmt->width = mf->width; - tfmt->height = mf->height; - ffmt = fimc_capture_try_format(ctx, - &tfmt->width, &tfmt->height, - NULL, &fcc, FIMC_SD_PAD_SOURCE); - if (ffmt && ffmt->mbus_code) - mf->code = ffmt->mbus_code; - if (mf->width != tfmt->width || - mf->height != tfmt->height) - continue; - tfmt->code = mf->code; + /* set format on all pipeline subdevs */ + while (me != &fimc->vid_cap.subdev.entity) { + sd = media_entity_to_v4l2_subdev(me); + + sfmt.pad = 0; + ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt); + if (ret) + return ret; + + if (me->pads[0].flags & MEDIA_PAD_FL_SINK) { + sfmt.pad = me->num_pads - 1; + mf->code = tfmt->code; + ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, + &sfmt); + if (ret) + return ret; + } + + pad = media_entity_remote_source(&me->pads[sfmt.pad]); + if (!pad) + return -EINVAL; + me = pad->entity; } - if (csis) - ret = v4l2_subdev_call(csis, pad, set_fmt, NULL, &sfmt); - if (mf->code == tfmt->code && - mf->width == tfmt->width && mf->height == tfmt->height) - break; + if (mf->code != tfmt->code) + continue; + + fcc = ffmt->fourcc; + tfmt->width = mf->width; + tfmt->height = mf->height; + ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height, + NULL, &fcc, FIMC_SD_PAD_SINK); + ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height, + NULL, &fcc, FIMC_SD_PAD_SOURCE); + if (ffmt && ffmt->mbus_code) + mf->code = ffmt->mbus_code; + if (mf->width != tfmt->width || mf->height != tfmt->height) + continue; + tfmt->code = mf->code; + break; } if (fmt_id && ffmt) *fmt_id = ffmt; *tfmt = *mf; - dbg("code: 0x%x, %dx%d, %p", mf->code, mf->width, mf->height, ffmt); return 0; } -- cgit v1.2.3 From c1819fc5dbe4582575965e587ba61184ab9b45fa Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Tue, 21 Aug 2012 05:27:59 -0300 Subject: [media] davinci: vpss: dm365: enable ISP registers enable the clocks required for VPFE to work in PCCR register, and enbale ISIF out on BCR to get the correct operation from ISIF. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpss.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c index 146e4b01ac17..1c96ce8269d4 100644 --- a/drivers/media/platform/davinci/vpss.c +++ b/drivers/media/platform/davinci/vpss.c @@ -51,7 +51,18 @@ MODULE_AUTHOR("Texas Instruments"); /* VENCINT - vpss_int8 */ #define DM355_VPSSBL_EVTSEL_DEFAULT 0x4 -#define DM365_ISP5_PCCR 0x04 +#define DM365_ISP5_PCCR 0x04 +#define DM365_ISP5_PCCR_BL_CLK_ENABLE BIT(0) +#define DM365_ISP5_PCCR_ISIF_CLK_ENABLE BIT(1) +#define DM365_ISP5_PCCR_H3A_CLK_ENABLE BIT(2) +#define DM365_ISP5_PCCR_RSZ_CLK_ENABLE BIT(3) +#define DM365_ISP5_PCCR_IPIPE_CLK_ENABLE BIT(4) +#define DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE BIT(5) +#define DM365_ISP5_PCCR_RSV BIT(6) + +#define DM365_ISP5_BCR 0x08 +#define DM365_ISP5_BCR_ISIF_OUT_ENABLE BIT(1) + #define DM365_ISP5_INTSEL1 0x10 #define DM365_ISP5_INTSEL2 0x14 #define DM365_ISP5_INTSEL3 0x18 @@ -426,6 +437,16 @@ static int __devinit vpss_probe(struct platform_device *pdev) oper_cfg.hw_ops.enable_clock = dm365_enable_clock; oper_cfg.hw_ops.select_ccdc_source = dm365_select_ccdc_source; /* Setup vpss interrupts */ + isp5_write((isp5_read(DM365_ISP5_PCCR) | + DM365_ISP5_PCCR_BL_CLK_ENABLE | + DM365_ISP5_PCCR_ISIF_CLK_ENABLE | + DM365_ISP5_PCCR_H3A_CLK_ENABLE | + DM365_ISP5_PCCR_RSZ_CLK_ENABLE | + DM365_ISP5_PCCR_IPIPE_CLK_ENABLE | + DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE | + DM365_ISP5_PCCR_RSV), DM365_ISP5_PCCR); + isp5_write((isp5_read(DM365_ISP5_BCR) | + DM365_ISP5_BCR_ISIF_OUT_ENABLE), DM365_ISP5_BCR); isp5_write(DM365_ISP5_INTSEL1_DEFAULT, DM365_ISP5_INTSEL1); isp5_write(DM365_ISP5_INTSEL2_DEFAULT, DM365_ISP5_INTSEL2); isp5_write(DM365_ISP5_INTSEL3_DEFAULT, DM365_ISP5_INTSEL3); -- cgit v1.2.3 From 3de939419cfaf613b87cf89adc5dda97ca1720e0 Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Tue, 21 Aug 2012 05:50:27 -0300 Subject: [media] davinci: vpss: dm365: set vpss clk ctrl request_mem_region for VPSS_CLK_CTRL register and ioremap. and enable clocks appropriately. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpss.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c index 1c96ce8269d4..e4ad63d15265 100644 --- a/drivers/media/platform/davinci/vpss.c +++ b/drivers/media/platform/davinci/vpss.c @@ -69,6 +69,11 @@ MODULE_AUTHOR("Texas Instruments"); #define DM365_ISP5_CCDCMUX 0x20 #define DM365_ISP5_PG_FRAME_SIZE 0x28 #define DM365_VPBE_CLK_CTRL 0x00 + +#define VPSS_CLK_CTRL 0x01c40044 +#define VPSS_CLK_CTRL_VENCCLKEN BIT(3) +#define VPSS_CLK_CTRL_DACCLKEN BIT(4) + /* * vpss interrupts. VDINT0 - vpss_int0, VDINT1 - vpss_int1, * AF - vpss_int3 @@ -112,6 +117,7 @@ struct vpss_hw_ops { struct vpss_oper_config { __iomem void *vpss_regs_base0; __iomem void *vpss_regs_base1; + resource_size_t *vpss_regs_base2; enum vpss_platform_type platform; spinlock_t vpss_lock; struct vpss_hw_ops hw_ops; @@ -492,11 +498,20 @@ static struct platform_driver vpss_driver = { static void vpss_exit(void) { + iounmap(oper_cfg.vpss_regs_base2); + release_mem_region(VPSS_CLK_CTRL, 4); platform_driver_unregister(&vpss_driver); } static int __init vpss_init(void) { + if (!request_mem_region(VPSS_CLK_CTRL, 4, "vpss_clock_control")) + return -EBUSY; + + oper_cfg.vpss_regs_base2 = ioremap(VPSS_CLK_CTRL, 4); + writel(VPSS_CLK_CTRL_VENCCLKEN | + VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2); + return platform_driver_register(&vpss_driver); } subsys_initcall(vpss_init); -- cgit v1.2.3 From d31c100250bb07b6d317e1cfc44614b45a16154a Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Tue, 21 Aug 2012 05:56:21 -0300 Subject: [media] davinci/vpss: add helper functions for setting hw params Add vpss helper functions to be used in the main driver for setting hardware parameters. Add interface functions to set sync polarity, interrupt completion and pageframe size in vpss to be used by the main driver. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpss.c | 32 ++++++++++++++++++++++++++++++++ include/media/davinci/vpss.h | 16 ++++++++++++++++ 2 files changed, 48 insertions(+) (limited to 'drivers') diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c index e4ad63d15265..d945f94053a8 100644 --- a/drivers/media/platform/davinci/vpss.c +++ b/drivers/media/platform/davinci/vpss.c @@ -111,6 +111,12 @@ struct vpss_hw_ops { void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel); /* clear wbl overflow bit */ int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel); + /* set sync polarity */ + void (*set_sync_pol)(struct vpss_sync_pol); + /* set the PG_FRAME_SIZE register*/ + void (*set_pg_frame_size)(struct vpss_pg_frame_size); + /* check and clear interrupt if occured */ + int (*dma_complete_interrupt)(void); }; /* vpss configuration */ @@ -175,6 +181,14 @@ static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) bl_regw(src_sel << VPSS_HSSISEL_SHIFT, DM355_VPSSBL_CCDCMUX); } +int vpss_dma_complete_interrupt(void) +{ + if (!oper_cfg.hw_ops.dma_complete_interrupt) + return 2; + return oper_cfg.hw_ops.dma_complete_interrupt(); +} +EXPORT_SYMBOL(vpss_dma_complete_interrupt); + int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) { if (!oper_cfg.hw_ops.select_ccdc_source) @@ -200,6 +214,15 @@ static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) return 0; } +void vpss_set_sync_pol(struct vpss_sync_pol sync) +{ + if (!oper_cfg.hw_ops.set_sync_pol) + return; + + oper_cfg.hw_ops.set_sync_pol(sync); +} +EXPORT_SYMBOL(vpss_set_sync_pol); + int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) { if (!oper_cfg.hw_ops.clear_wbl_overflow) @@ -365,6 +388,15 @@ void dm365_vpss_set_sync_pol(struct vpss_sync_pol sync) } EXPORT_SYMBOL(dm365_vpss_set_sync_pol); +void vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size) +{ + if (!oper_cfg.hw_ops.set_pg_frame_size) + return; + + oper_cfg.hw_ops.set_pg_frame_size(frame_size); +} +EXPORT_SYMBOL(vpss_set_pg_frame_size); + void dm365_vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size) { int current_reg = ((frame_size.hlpfr >> 1) - 1) << 16; diff --git a/include/media/davinci/vpss.h b/include/media/davinci/vpss.h index b586495bcd53..153473daaa32 100644 --- a/include/media/davinci/vpss.h +++ b/include/media/davinci/vpss.h @@ -105,4 +105,20 @@ enum vpss_wbl_sel { }; /* clear wbl overflow flag for DM6446 */ int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel); + +/* set sync polarity*/ +void vpss_set_sync_pol(struct vpss_sync_pol sync); +/* set the PG_FRAME_SIZE register */ +void vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size); +/* + * vpss_check_and_clear_interrupt - check and clear interrupt + * @irq - common enumerator for IRQ + * + * Following return values used:- + * 0 - interrupt occurred and cleared + * 1 - interrupt not occurred + * 2 - interrupt status not available + */ +int vpss_dma_complete_interrupt(void); + #endif -- cgit v1.2.3 From 91825400ba9a8a081d0efe7ac21e55d9fd6545f9 Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Wed, 28 Nov 2012 02:02:02 -0300 Subject: [media] davinci: vpfe: add v4l2 capture driver with media interface Add the vpfe capture driver which implements media controller interface. The driver supports the DM365 sub ip units for capture namely - ISIF, IPIPE, IPIPEIF, Resizer. This file represents the main driver which does isr registration, v4l2 device registration, media registration and platform driver registrations. It calls the appropriate subdevs from here to create subdevices and media entities. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/davinci_vpfe/davinci_vpfe_user.h | 1290 ++++++++++++++++++++ drivers/staging/media/davinci_vpfe/vpfe.h | 86 ++ .../staging/media/davinci_vpfe/vpfe_mc_capture.c | 740 +++++++++++ .../staging/media/davinci_vpfe/vpfe_mc_capture.h | 97 ++ 4 files changed, 2213 insertions(+) create mode 100644 drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h create mode 100644 drivers/staging/media/davinci_vpfe/vpfe.h create mode 100644 drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c create mode 100644 drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h (limited to 'drivers') diff --git a/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h b/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h new file mode 100644 index 000000000000..7b7e7b26c1e8 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h @@ -0,0 +1,1290 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _DAVINCI_VPFE_USER_H +#define _DAVINCI_VPFE_USER_H + +#include +#include + +/* + * Private IOCTL + * + * VIDIOC_VPFE_ISIF_S_RAW_PARAMS: Set raw params in isif + * VIDIOC_VPFE_ISIF_G_RAW_PARAMS: Get raw params from isif + * VIDIOC_VPFE_PRV_S_CONFIG: Set ipipe engine configuration + * VIDIOC_VPFE_PRV_G_CONFIG: Get ipipe engine configuration + * VIDIOC_VPFE_RSZ_S_CONFIG: Set resizer engine configuration + * VIDIOC_VPFE_RSZ_G_CONFIG: Get resizer engine configuration + */ + +#define VIDIOC_VPFE_ISIF_S_RAW_PARAMS \ + _IOW('V', BASE_VIDIOC_PRIVATE + 1, struct vpfe_isif_raw_config) +#define VIDIOC_VPFE_ISIF_G_RAW_PARAMS \ + _IOR('V', BASE_VIDIOC_PRIVATE + 2, struct vpfe_isif_raw_config) +#define VIDIOC_VPFE_IPIPE_S_CONFIG \ + _IOWR('P', BASE_VIDIOC_PRIVATE + 3, struct vpfe_ipipe_config) +#define VIDIOC_VPFE_IPIPE_G_CONFIG \ + _IOWR('P', BASE_VIDIOC_PRIVATE + 4, struct vpfe_ipipe_config) +#define VIDIOC_VPFE_RSZ_S_CONFIG \ + _IOWR('R', BASE_VIDIOC_PRIVATE + 5, struct vpfe_rsz_config) +#define VIDIOC_VPFE_RSZ_G_CONFIG \ + _IOWR('R', BASE_VIDIOC_PRIVATE + 6, struct vpfe_rsz_config) + +/* + * Private Control's for ISIF + */ +#define VPFE_ISIF_CID_CRGAIN (V4L2_CID_USER_BASE | 0xa001) +#define VPFE_ISIF_CID_CGRGAIN (V4L2_CID_USER_BASE | 0xa002) +#define VPFE_ISIF_CID_CGBGAIN (V4L2_CID_USER_BASE | 0xa003) +#define VPFE_ISIF_CID_CBGAIN (V4L2_CID_USER_BASE | 0xa004) +#define VPFE_ISIF_CID_GAIN_OFFSET (V4L2_CID_USER_BASE | 0xa005) + +/* + * Private Control's for ISIF and IPIPEIF + */ +#define VPFE_CID_DPCM_PREDICTOR (V4L2_CID_USER_BASE | 0xa006) + +/************************************************************************ + * Vertical Defect Correction parameters + ***********************************************************************/ + +/** + * vertical defect correction methods + */ +enum vpfe_isif_vdfc_corr_mode { + /* Defect level subtraction. Just fed through if saturating */ + VPFE_ISIF_VDFC_NORMAL, + /** + * Defect level subtraction. Horizontal interpolation ((i-2)+(i+2))/2 + * if data saturating + */ + VPFE_ISIF_VDFC_HORZ_INTERPOL_IF_SAT, + /* Horizontal interpolation (((i-2)+(i+2))/2) */ + VPFE_ISIF_VDFC_HORZ_INTERPOL +}; + +/** + * Max Size of the Vertical Defect Correction table + */ +#define VPFE_ISIF_VDFC_TABLE_SIZE 8 + +/** + * Values used for shifting up the vdfc defect level + */ +enum vpfe_isif_vdfc_shift { + /* No Shift */ + VPFE_ISIF_VDFC_NO_SHIFT, + /* Shift by 1 bit */ + VPFE_ISIF_VDFC_SHIFT_1, + /* Shift by 2 bit */ + VPFE_ISIF_VDFC_SHIFT_2, + /* Shift by 3 bit */ + VPFE_ISIF_VDFC_SHIFT_3, + /* Shift by 4 bit */ + VPFE_ISIF_VDFC_SHIFT_4 +}; + +/** + * Defect Correction (DFC) table entry + */ +struct vpfe_isif_vdfc_entry { + /* vertical position of defect */ + unsigned short pos_vert; + /* horizontal position of defect */ + unsigned short pos_horz; + /** + * Defect level of Vertical line defect position. This is subtracted + * from the data at the defect position + */ + unsigned char level_at_pos; + /** + * Defect level of the pixels upper than the vertical line defect. + * This is subtracted from the data + */ + unsigned char level_up_pixels; + /** + * Defect level of the pixels lower than the vertical line defect. + * This is subtracted from the data + */ + unsigned char level_low_pixels; +}; + +/** + * Structure for Defect Correction (DFC) parameter + */ +struct vpfe_isif_dfc { + /* enable vertical defect correction */ + unsigned char en; + /* Correction methods */ + enum vpfe_isif_vdfc_corr_mode corr_mode; + /** + * 0 - whole line corrected, 1 - not + * pixels upper than the defect + */ + unsigned char corr_whole_line; + /** + * defect level shift value. level_at_pos, level_upper_pos, + * and level_lower_pos can be shifted up by this value + */ + enum vpfe_isif_vdfc_shift def_level_shift; + /* defect saturation level */ + unsigned short def_sat_level; + /* number of vertical defects. Max is VPFE_ISIF_VDFC_TABLE_SIZE */ + short num_vdefects; + /* VDFC table ptr */ + struct vpfe_isif_vdfc_entry table[VPFE_ISIF_VDFC_TABLE_SIZE]; +}; + +/************************************************************************ +* Digital/Black clamp or DC Subtract parameters +************************************************************************/ +/** + * Horizontal Black Clamp modes + */ +enum vpfe_isif_horz_bc_mode { + /** + * Horizontal clamp disabled. Only vertical clamp + * value is subtracted + */ + VPFE_ISIF_HORZ_BC_DISABLE, + /** + * Horizontal clamp value is calculated and subtracted + * from image data along with vertical clamp value + */ + VPFE_ISIF_HORZ_BC_CLAMP_CALC_ENABLED, + /** + * Horizontal clamp value calculated from previous image + * is subtracted from image data along with vertical clamp + * value. How the horizontal clamp value for the first image + * is calculated in this case ??? + */ + VPFE_ISIF_HORZ_BC_CLAMP_NOT_UPDATED +}; + +/** + * Base window selection for Horizontal Black Clamp calculations + */ +enum vpfe_isif_horz_bc_base_win_sel { + /* Select Most left window for bc calculation */ + VPFE_ISIF_SEL_MOST_LEFT_WIN, + + /* Select Most right window for bc calculation */ + VPFE_ISIF_SEL_MOST_RIGHT_WIN, +}; + +/* Size of window in horizontal direction for horizontal bc */ +enum vpfe_isif_horz_bc_sz_h { + VPFE_ISIF_HORZ_BC_SZ_H_2PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_4PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_8PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_16PIXELS +}; + +/* Size of window in vertcal direction for vertical bc */ +enum vpfe_isif_horz_bc_sz_v { + VPFE_ISIF_HORZ_BC_SZ_H_32PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_64PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_128PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_256PIXELS +}; + +/** + * Structure for Horizontal Black Clamp config params + */ +struct vpfe_isif_horz_bclamp { + /* horizontal clamp mode */ + enum vpfe_isif_horz_bc_mode mode; + /** + * pixel value limit enable. + * 0 - limit disabled + * 1 - pixel value limited to 1023 + */ + unsigned char clamp_pix_limit; + /** + * Select most left or right window for clamp val + * calculation + */ + enum vpfe_isif_horz_bc_base_win_sel base_win_sel_calc; + /* Window count per color for calculation. range 1-32 */ + unsigned char win_count_calc; + /* Window start position - horizontal for calculation. 0 - 8191 */ + unsigned short win_start_h_calc; + /* Window start position - vertical for calculation 0 - 8191 */ + unsigned short win_start_v_calc; + /* Width of the sample window in pixels for calculation */ + enum vpfe_isif_horz_bc_sz_h win_h_sz_calc; + /* Height of the sample window in pixels for calculation */ + enum vpfe_isif_horz_bc_sz_v win_v_sz_calc; +}; + +/** + * Black Clamp vertical reset values + */ +enum vpfe_isif_vert_bc_reset_val_sel { + /* Reset value used is the clamp value calculated */ + VPFE_ISIF_VERT_BC_USE_HORZ_CLAMP_VAL, + /* Reset value used is reset_clamp_val configured */ + VPFE_ISIF_VERT_BC_USE_CONFIG_CLAMP_VAL, + /* No update, previous image value is used */ + VPFE_ISIF_VERT_BC_NO_UPDATE +}; + +enum vpfe_isif_vert_bc_sz_h { + VPFE_ISIF_VERT_BC_SZ_H_2PIXELS, + VPFE_ISIF_VERT_BC_SZ_H_4PIXELS, + VPFE_ISIF_VERT_BC_SZ_H_8PIXELS, + VPFE_ISIF_VERT_BC_SZ_H_16PIXELS, + VPFE_ISIF_VERT_BC_SZ_H_32PIXELS, + VPFE_ISIF_VERT_BC_SZ_H_64PIXELS +}; + +/** + * Structure for Vertical Black Clamp configuration params + */ +struct vpfe_isif_vert_bclamp { + /* Reset value selection for vertical clamp calculation */ + enum vpfe_isif_vert_bc_reset_val_sel reset_val_sel; + /* U12 value if reset_sel = ISIF_BC_VERT_USE_CONFIG_CLAMP_VAL */ + unsigned short reset_clamp_val; + /** + * U8Q8. Line average coefficient used in vertical clamp + * calculation + */ + unsigned char line_ave_coef; + /* Width in pixels of the optical black region used for calculation. */ + enum vpfe_isif_vert_bc_sz_h ob_h_sz_calc; + /* Height of the optical black region for calculation */ + unsigned short ob_v_sz_calc; + /* Optical black region start position - horizontal. 0 - 8191 */ + unsigned short ob_start_h; + /* Optical black region start position - vertical 0 - 8191 */ + unsigned short ob_start_v; +}; + +/** + * Structure for Black Clamp configuration params + */ +struct vpfe_isif_black_clamp { + /** + * this offset value is added irrespective of the clamp + * enable status. S13 + */ + unsigned short dc_offset; + /** + * Enable black/digital clamp value to be subtracted + * from the image data + */ + unsigned char en; + /** + * black clamp mode. same/separate clamp for 4 colors + * 0 - disable - same clamp value for all colors + * 1 - clamp value calculated separately for all colors + */ + unsigned char bc_mode_color; + /* Vertical start position for bc subtraction */ + unsigned short vert_start_sub; + /* Black clamp for horizontal direction */ + struct vpfe_isif_horz_bclamp horz; + /* Black clamp for vertical direction */ + struct vpfe_isif_vert_bclamp vert; +}; + +/************************************************************************* +** Color Space Conversion (CSC) +*************************************************************************/ +/** + * Number of Coefficient values used for CSC + */ +#define VPFE_ISIF_CSC_NUM_COEFF 16 + +struct float_8_bit { + /* 8 bit integer part */ + __u8 integer; + /* 8 bit decimal part */ + __u8 decimal; +}; + +struct float_16_bit { + /* 16 bit integer part */ + __u16 integer; + /* 16 bit decimal part */ + __u16 decimal; +}; + +/************************************************************************* +** Color Space Conversion parameters +*************************************************************************/ +/** + * Structure used for CSC config params + */ +struct vpfe_isif_color_space_conv { + /* Enable color space conversion */ + unsigned char en; + /** + * csc coefficient table. S8Q5, M00 at index 0, M01 at index 1, and + * so forth + */ + struct float_8_bit coeff[VPFE_ISIF_CSC_NUM_COEFF]; +}; + +enum vpfe_isif_datasft { + /* No Shift */ + VPFE_ISIF_NO_SHIFT, + /* 1 bit Shift */ + VPFE_ISIF_1BIT_SHIFT, + /* 2 bit Shift */ + VPFE_ISIF_2BIT_SHIFT, + /* 3 bit Shift */ + VPFE_ISIF_3BIT_SHIFT, + /* 4 bit Shift */ + VPFE_ISIF_4BIT_SHIFT, + /* 5 bit Shift */ + VPFE_ISIF_5BIT_SHIFT, + /* 6 bit Shift */ + VPFE_ISIF_6BIT_SHIFT +}; + +#define VPFE_ISIF_LINEAR_TAB_SIZE 192 +/************************************************************************* +** Linearization parameters +*************************************************************************/ +/** + * Structure for Sensor data linearization + */ +struct vpfe_isif_linearize { + /* Enable or Disable linearization of data */ + unsigned char en; + /* Shift value applied */ + enum vpfe_isif_datasft corr_shft; + /* scale factor applied U11Q10 */ + struct float_16_bit scale_fact; + /* Size of the linear table */ + unsigned short table[VPFE_ISIF_LINEAR_TAB_SIZE]; +}; + +/************************************************************************* +** ISIF Raw configuration parameters +*************************************************************************/ +enum vpfe_isif_fmt_mode { + VPFE_ISIF_SPLIT, + VPFE_ISIF_COMBINE +}; + +enum vpfe_isif_lnum { + VPFE_ISIF_1LINE, + VPFE_ISIF_2LINES, + VPFE_ISIF_3LINES, + VPFE_ISIF_4LINES +}; + +enum vpfe_isif_line { + VPFE_ISIF_1STLINE, + VPFE_ISIF_2NDLINE, + VPFE_ISIF_3RDLINE, + VPFE_ISIF_4THLINE +}; + +struct vpfe_isif_fmtplen { + /** + * number of program entries for SET0, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + unsigned short plen0; + /** + * number of program entries for SET1, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + unsigned short plen1; + /** + * number of program entries for SET2, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + unsigned short plen2; + /** + * number of program entries for SET3, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + unsigned short plen3; +}; + +struct vpfe_isif_fmt_cfg { + /* Split or combine or line alternate */ + enum vpfe_isif_fmt_mode fmtmode; + /* enable or disable line alternating mode */ + unsigned char ln_alter_en; + /* Split/combine line number */ + enum vpfe_isif_lnum lnum; + /* Address increment Range 1 - 16 */ + unsigned int addrinc; +}; + +struct vpfe_isif_fmt_addr_ptr { + /* Initial address */ + unsigned int init_addr; + /* output line number */ + enum vpfe_isif_line out_line; +}; + +struct vpfe_isif_fmtpgm_ap { + /* program address pointer */ + unsigned char pgm_aptr; + /* program address increment or decrement */ + unsigned char pgmupdt; +}; + +struct vpfe_isif_data_formatter { + /* Enable/Disable data formatter */ + unsigned char en; + /* data formatter configuration */ + struct vpfe_isif_fmt_cfg cfg; + /* Formatter program entries length */ + struct vpfe_isif_fmtplen plen; + /* first pixel in a line fed to formatter */ + unsigned short fmtrlen; + /* HD interval for output line. Only valid when split line */ + unsigned short fmthcnt; + /* formatter address pointers */ + struct vpfe_isif_fmt_addr_ptr fmtaddr_ptr[16]; + /* program enable/disable */ + unsigned char pgm_en[32]; + /* program address pointers */ + struct vpfe_isif_fmtpgm_ap fmtpgm_ap[32]; +}; + +struct vpfe_isif_df_csc { + /* Color Space Conversion configuration, 0 - csc, 1 - df */ + unsigned int df_or_csc; + /* csc configuration valid if df_or_csc is 0 */ + struct vpfe_isif_color_space_conv csc; + /* data formatter configuration valid if df_or_csc is 1 */ + struct vpfe_isif_data_formatter df; + /* start pixel in a line at the input */ + unsigned int start_pix; + /* number of pixels in input line */ + unsigned int num_pixels; + /* start line at the input */ + unsigned int start_line; + /* number of lines at the input */ + unsigned int num_lines; +}; + +struct vpfe_isif_gain_offsets_adj { + /* Enable or Disable Gain adjustment for SDRAM data */ + unsigned char gain_sdram_en; + /* Enable or Disable Gain adjustment for IPIPE data */ + unsigned char gain_ipipe_en; + /* Enable or Disable Gain adjustment for H3A data */ + unsigned char gain_h3a_en; + /* Enable or Disable Gain adjustment for SDRAM data */ + unsigned char offset_sdram_en; + /* Enable or Disable Gain adjustment for IPIPE data */ + unsigned char offset_ipipe_en; + /* Enable or Disable Gain adjustment for H3A data */ + unsigned char offset_h3a_en; +}; + +struct vpfe_isif_cul { + /* Horizontal Cull pattern for odd lines */ + unsigned char hcpat_odd; + /* Horizontal Cull pattern for even lines */ + unsigned char hcpat_even; + /* Vertical Cull pattern */ + unsigned char vcpat; + /* Enable or disable lpf. Apply when cull is enabled */ + unsigned char en_lpf; +}; + +/* all the stuff in this struct will be provided by userland */ +struct vpfe_isif_raw_config { + /* Linearization parameters for image sensor data input */ + struct vpfe_isif_linearize linearize; + /* Data formatter or CSC */ + struct vpfe_isif_df_csc df_csc; + /* Defect Pixel Correction (DFC) confguration */ + struct vpfe_isif_dfc dfc; + /* Black/Digital Clamp configuration */ + struct vpfe_isif_black_clamp bclamp; + /* Gain, offset adjustments */ + struct vpfe_isif_gain_offsets_adj gain_offset; + /* Culling */ + struct vpfe_isif_cul culling; + /* horizontal offset for Gain/LSC/DFC */ + unsigned short horz_offset; + /* vertical offset for Gain/LSC/DFC */ + unsigned short vert_offset; +}; + +/********************************************************************** + IPIPE API Structures +**********************************************************************/ + +/* IPIPE module configurations */ + +/* IPIPE input configuration */ +#define VPFE_IPIPE_INPUT_CONFIG (1 << 0) +/* LUT based Defect Pixel Correction */ +#define VPFE_IPIPE_LUTDPC (1 << 1) +/* On the fly (OTF) Defect Pixel Correction */ +#define VPFE_IPIPE_OTFDPC (1 << 2) +/* Noise Filter - 1 */ +#define VPFE_IPIPE_NF1 (1 << 3) +/* Noise Filter - 2 */ +#define VPFE_IPIPE_NF2 (1 << 4) +/* White Balance. Also a control ID */ +#define VPFE_IPIPE_WB (1 << 5) +/* 1st RGB to RBG Blend module */ +#define VPFE_IPIPE_RGB2RGB_1 (1 << 6) +/* 2nd RGB to RBG Blend module */ +#define VPFE_IPIPE_RGB2RGB_2 (1 << 7) +/* Gamma Correction */ +#define VPFE_IPIPE_GAMMA (1 << 8) +/* 3D LUT color conversion */ +#define VPFE_IPIPE_3D_LUT (1 << 9) +/* RGB to YCbCr module */ +#define VPFE_IPIPE_RGB2YUV (1 << 10) +/* YUV 422 conversion module */ +#define VPFE_IPIPE_YUV422_CONV (1 << 11) +/* Edge Enhancement */ +#define VPFE_IPIPE_YEE (1 << 12) +/* Green Imbalance Correction */ +#define VPFE_IPIPE_GIC (1 << 13) +/* CFA Interpolation */ +#define VPFE_IPIPE_CFA (1 << 14) +/* Chroma Artifact Reduction */ +#define VPFE_IPIPE_CAR (1 << 15) +/* Chroma Gain Suppression */ +#define VPFE_IPIPE_CGS (1 << 16) +/* Global brightness and contrast control */ +#define VPFE_IPIPE_GBCE (1 << 17) + +#define VPFE_IPIPE_MAX_MODULES 18 + +struct ipipe_float_u16 { + unsigned short integer; + unsigned short decimal; +}; + +struct ipipe_float_s16 { + short integer; + unsigned short decimal; +}; + +struct ipipe_float_u8 { + unsigned char integer; + unsigned char decimal; +}; + +/* Copy method selection for vertical correction + * Used when ipipe_dfc_corr_meth is IPIPE_DPC_CTORB_AFTER_HINT + */ +enum vpfe_ipipe_dpc_corr_meth { + /* replace by black or white dot specified by repl_white */ + VPFE_IPIPE_DPC_REPL_BY_DOT = 0, + /* Copy from left */ + VPFE_IPIPE_DPC_CL = 1, + /* Copy from right */ + VPFE_IPIPE_DPC_CR = 2, + /* Horizontal interpolation */ + VPFE_IPIPE_DPC_H_INTP = 3, + /* Vertical interpolation */ + VPFE_IPIPE_DPC_V_INTP = 4, + /* Copy from top */ + VPFE_IPIPE_DPC_CT = 5, + /* Copy from bottom */ + VPFE_IPIPE_DPC_CB = 6, + /* 2D interpolation */ + VPFE_IPIPE_DPC_2D_INTP = 7, +}; + +struct vpfe_ipipe_lutdpc_entry { + /* Horizontal position */ + unsigned short horz_pos; + /* vertical position */ + unsigned short vert_pos; + enum vpfe_ipipe_dpc_corr_meth method; +}; + +#define VPFE_IPIPE_MAX_SIZE_DPC 256 + +/* Structure for configuring DPC module */ +struct vpfe_ipipe_lutdpc { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* 0 - replace with black dot, 1 - white dot when correction + * method is IPIPE_DFC_REPL_BY_DOT=0, + */ + unsigned char repl_white; + /* number of entries in the correction table. Currently only + * support up-to 256 entries. infinite mode is not supported + */ + unsigned short dpc_size; + struct vpfe_ipipe_lutdpc_entry table[VPFE_IPIPE_MAX_SIZE_DPC]; +}; + +enum vpfe_ipipe_otfdpc_det_meth { + VPFE_IPIPE_DPC_OTF_MIN_MAX, + VPFE_IPIPE_DPC_OTF_MIN_MAX2 +}; + +struct vpfe_ipipe_otfdpc_thr { + unsigned short r; + unsigned short gr; + unsigned short gb; + unsigned short b; +}; + +enum vpfe_ipipe_otfdpc_alg { + VPFE_IPIPE_OTFDPC_2_0, + VPFE_IPIPE_OTFDPC_3_0 +}; + +struct vpfe_ipipe_otfdpc_2_0_cfg { + /* defect detection threshold for MIN_MAX2 method (DPC 2.0 alg) */ + struct vpfe_ipipe_otfdpc_thr det_thr; + /* defect correction threshold for MIN_MAX2 method (DPC 2.0 alg) or + * maximum value for MIN_MAX method + */ + struct vpfe_ipipe_otfdpc_thr corr_thr; +}; + +struct vpfe_ipipe_otfdpc_3_0_cfg { + /* DPC3.0 activity adj shf. activity = (max2-min2) >> (6 -shf) + */ + unsigned char act_adj_shf; + /* DPC3.0 detection threshold, THR */ + unsigned short det_thr; + /* DPC3.0 detection threshold slope, SLP */ + unsigned short det_slp; + /* DPC3.0 detection threshold min, MIN */ + unsigned short det_thr_min; + /* DPC3.0 detection threshold max, MAX */ + unsigned short det_thr_max; + /* DPC3.0 correction threshold, THR */ + unsigned short corr_thr; + /* DPC3.0 correction threshold slope, SLP */ + unsigned short corr_slp; + /* DPC3.0 correction threshold min, MIN */ + unsigned short corr_thr_min; + /* DPC3.0 correction threshold max, MAX */ + unsigned short corr_thr_max; +}; + +struct vpfe_ipipe_otfdpc { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* defect detection method */ + enum vpfe_ipipe_otfdpc_det_meth det_method; + /* Algorithm used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is + * used + */ + enum vpfe_ipipe_otfdpc_alg alg; + union { + /* if alg is IPIPE_OTFDPC_2_0 */ + struct vpfe_ipipe_otfdpc_2_0_cfg dpc_2_0; + /* if alg is IPIPE_OTFDPC_3_0 */ + struct vpfe_ipipe_otfdpc_3_0_cfg dpc_3_0; + } alg_cfg; +}; + +/* Threshold values table size */ +#define VPFE_IPIPE_NF_THR_TABLE_SIZE 8 +/* Intensity values table size */ +#define VPFE_IPIPE_NF_STR_TABLE_SIZE 8 + +/* NF, sampling method for green pixels */ +enum vpfe_ipipe_nf_sampl_meth { + /* Same as R or B */ + VPFE_IPIPE_NF_BOX, + /* Diamond mode */ + VPFE_IPIPE_NF_DIAMOND +}; + +/* Structure for configuring NF module */ +struct vpfe_ipipe_nf { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* Sampling method for green pixels */ + enum vpfe_ipipe_nf_sampl_meth gr_sample_meth; + /* Down shift value in LUT reference address + */ + unsigned char shft_val; + /* Spread value in NF algorithm + */ + unsigned char spread_val; + /* Apply LSC gain to threshold. Enable this only if + * LSC is enabled in ISIF + */ + unsigned char apply_lsc_gain; + /* Threshold values table */ + unsigned short thr[VPFE_IPIPE_NF_THR_TABLE_SIZE]; + /* intensity values table */ + unsigned char str[VPFE_IPIPE_NF_STR_TABLE_SIZE]; + /* Edge detection minimum threshold */ + unsigned short edge_det_min_thr; + /* Edge detection maximum threshold */ + unsigned short edge_det_max_thr; +}; + +enum vpfe_ipipe_gic_alg { + VPFE_IPIPE_GIC_ALG_CONST_GAIN, + VPFE_IPIPE_GIC_ALG_ADAPT_GAIN +}; + +enum vpfe_ipipe_gic_thr_sel { + VPFE_IPIPE_GIC_THR_REG, + VPFE_IPIPE_GIC_THR_NF +}; + +enum vpfe_ipipe_gic_wt_fn_type { + /* Use difference as index */ + VPFE_IPIPE_GIC_WT_FN_TYP_DIF, + /* Use weight function as index */ + VPFE_IPIPE_GIC_WT_FN_TYP_HP_VAL +}; + +/* structure for Green Imbalance Correction */ +struct vpfe_ipipe_gic { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* 0 - Constant gain , 1 - Adaptive gain algorithm */ + enum vpfe_ipipe_gic_alg gic_alg; + /* GIC gain or weight. Used for Constant gain and Adaptive algorithms + */ + unsigned short gain; + /* Threshold selection. GIC register values or NF2 thr table */ + enum vpfe_ipipe_gic_thr_sel thr_sel; + /* thr1. Used when thr_sel is IPIPE_GIC_THR_REG */ + unsigned short thr; + /* this value is used for thr2-thr1, thr3-thr2 or + * thr4-thr3 when wt_fn_type is index. Otherwise it + * is the + */ + unsigned short slope; + /* Apply LSC gain to threshold. Enable this only if + * LSC is enabled in ISIF & thr_sel is IPIPE_GIC_THR_REG + */ + unsigned char apply_lsc_gain; + /* Multiply Nf2 threshold by this gain. Use this when thr_sel + * is IPIPE_GIC_THR_NF + */ + struct ipipe_float_u8 nf2_thr_gain; + /* Weight function uses difference as index or high pass value. + * Used for adaptive gain algorithm + */ + enum vpfe_ipipe_gic_wt_fn_type wt_fn_type; +}; + +/* Structure for configuring WB module */ +struct vpfe_ipipe_wb { + /* Offset (S12) for R */ + short ofst_r; + /* Offset (S12) for Gr */ + short ofst_gr; + /* Offset (S12) for Gb */ + short ofst_gb; + /* Offset (S12) for B */ + short ofst_b; + /* Gain (U13Q9) for Red */ + struct ipipe_float_u16 gain_r; + /* Gain (U13Q9) for Gr */ + struct ipipe_float_u16 gain_gr; + /* Gain (U13Q9) for Gb */ + struct ipipe_float_u16 gain_gb; + /* Gain (U13Q9) for Blue */ + struct ipipe_float_u16 gain_b; +}; + +enum vpfe_ipipe_cfa_alg { + /* Algorithm is 2DirAC */ + VPFE_IPIPE_CFA_ALG_2DIRAC, + /* Algorithm is 2DirAC + Digital Antialiasing (DAA) */ + VPFE_IPIPE_CFA_ALG_2DIRAC_DAA, + /* Algorithm is DAA */ + VPFE_IPIPE_CFA_ALG_DAA +}; + +/* Structure for CFA Interpolation */ +struct vpfe_ipipe_cfa { + /* 2DirAC or 2DirAC + DAA */ + enum vpfe_ipipe_cfa_alg alg; + /* 2Dir CFA HP value Low Threshold */ + unsigned short hpf_thr_2dir; + /* 2Dir CFA HP value slope */ + unsigned short hpf_slp_2dir; + /* 2Dir CFA HP mix threshold */ + unsigned short hp_mix_thr_2dir; + /* 2Dir CFA HP mix slope */ + unsigned short hp_mix_slope_2dir; + /* 2Dir Direction threshold */ + unsigned short dir_thr_2dir; + /* 2Dir Direction slope */ + unsigned short dir_slope_2dir; + /* 2Dir Non Directional Weight */ + unsigned short nd_wt_2dir; + /* DAA Mono Hue Fraction */ + unsigned short hue_fract_daa; + /* DAA Mono Edge threshold */ + unsigned short edge_thr_daa; + /* DAA Mono threshold minimum */ + unsigned short thr_min_daa; + /* DAA Mono threshold slope */ + unsigned short thr_slope_daa; + /* DAA Mono slope minimum */ + unsigned short slope_min_daa; + /* DAA Mono slope slope */ + unsigned short slope_slope_daa; + /* DAA Mono LP wight */ + unsigned short lp_wt_daa; +}; + +/* Struct for configuring RGB2RGB blending module */ +struct vpfe_ipipe_rgb2rgb { + /* Matrix coefficient for RR S12Q8 for ID = 1 and S11Q8 for ID = 2 */ + struct ipipe_float_s16 coef_rr; + /* Matrix coefficient for GR S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_gr; + /* Matrix coefficient for BR S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_br; + /* Matrix coefficient for RG S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_rg; + /* Matrix coefficient for GG S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_gg; + /* Matrix coefficient for BG S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_bg; + /* Matrix coefficient for RB S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_rb; + /* Matrix coefficient for GB S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_gb; + /* Matrix coefficient for BB S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_bb; + /* Output offset for R S13/S11 */ + int out_ofst_r; + /* Output offset for G S13/S11 */ + int out_ofst_g; + /* Output offset for B S13/S11 */ + int out_ofst_b; +}; + +#define VPFE_IPIPE_MAX_SIZE_GAMMA 512 + +enum vpfe_ipipe_gamma_tbl_size { + VPFE_IPIPE_GAMMA_TBL_SZ_64 = 64, + VPFE_IPIPE_GAMMA_TBL_SZ_128 = 128, + VPFE_IPIPE_GAMMA_TBL_SZ_256 = 256, + VPFE_IPIPE_GAMMA_TBL_SZ_512 = 512, +}; + +enum vpfe_ipipe_gamma_tbl_sel { + VPFE_IPIPE_GAMMA_TBL_RAM = 0, + VPFE_IPIPE_GAMMA_TBL_ROM = 1, +}; + +struct vpfe_ipipe_gamma_entry { + /* 10 bit slope */ + short slope; + /* 10 bit offset */ + unsigned short offset; +}; + +/* Structure for configuring Gamma correction module */ +struct vpfe_ipipe_gamma { + /* 0 - Enable Gamma correction for Red + * 1 - bypass Gamma correction. Data is divided by 16 + */ + unsigned char bypass_r; + /* 0 - Enable Gamma correction for Blue + * 1 - bypass Gamma correction. Data is divided by 16 + */ + unsigned char bypass_b; + /* 0 - Enable Gamma correction for Green + * 1 - bypass Gamma correction. Data is divided by 16 + */ + unsigned char bypass_g; + /* IPIPE_GAMMA_TBL_RAM or IPIPE_GAMMA_TBL_ROM */ + enum vpfe_ipipe_gamma_tbl_sel tbl_sel; + /* Table size for RAM gamma table. + */ + enum vpfe_ipipe_gamma_tbl_size tbl_size; + /* R table */ + struct vpfe_ipipe_gamma_entry table_r[VPFE_IPIPE_MAX_SIZE_GAMMA]; + /* Blue table */ + struct vpfe_ipipe_gamma_entry table_b[VPFE_IPIPE_MAX_SIZE_GAMMA]; + /* Green table */ + struct vpfe_ipipe_gamma_entry table_g[VPFE_IPIPE_MAX_SIZE_GAMMA]; +}; + +#define VPFE_IPIPE_MAX_SIZE_3D_LUT 729 + +struct vpfe_ipipe_3d_lut_entry { + /* 10 bit entry for red */ + unsigned short r; + /* 10 bit entry for green */ + unsigned short g; + /* 10 bit entry for blue */ + unsigned short b; +}; + +/* structure for 3D-LUT */ +struct vpfe_ipipe_3d_lut { + /* enable/disable 3D lut */ + unsigned char en; + /* 3D - LUT table entry */ + struct vpfe_ipipe_3d_lut_entry table[VPFE_IPIPE_MAX_SIZE_3D_LUT]; +}; + +/* Struct for configuring rgb2ycbcr module */ +struct vpfe_ipipe_rgb2yuv { + /* Matrix coefficient for RY S12Q8 */ + struct ipipe_float_s16 coef_ry; + /* Matrix coefficient for GY S12Q8 */ + struct ipipe_float_s16 coef_gy; + /* Matrix coefficient for BY S12Q8 */ + struct ipipe_float_s16 coef_by; + /* Matrix coefficient for RCb S12Q8 */ + struct ipipe_float_s16 coef_rcb; + /* Matrix coefficient for GCb S12Q8 */ + struct ipipe_float_s16 coef_gcb; + /* Matrix coefficient for BCb S12Q8 */ + struct ipipe_float_s16 coef_bcb; + /* Matrix coefficient for RCr S12Q8 */ + struct ipipe_float_s16 coef_rcr; + /* Matrix coefficient for GCr S12Q8 */ + struct ipipe_float_s16 coef_gcr; + /* Matrix coefficient for BCr S12Q8 */ + struct ipipe_float_s16 coef_bcr; + /* Output offset for R S11 */ + int out_ofst_y; + /* Output offset for Cb S11 */ + int out_ofst_cb; + /* Output offset for Cr S11 */ + int out_ofst_cr; +}; + +enum vpfe_ipipe_gbce_type { + VPFE_IPIPE_GBCE_Y_VAL_TBL = 0, + VPFE_IPIPE_GBCE_GAIN_TBL = 1, +}; + +#define VPFE_IPIPE_MAX_SIZE_GBCE_LUT 1024 + +/* structure for Global brightness and Contrast */ +struct vpfe_ipipe_gbce { + /* enable/disable GBCE */ + unsigned char en; + /* Y - value table or Gain table */ + enum vpfe_ipipe_gbce_type type; + /* ptr to LUT for GBCE with 1024 entries */ + unsigned short table[VPFE_IPIPE_MAX_SIZE_GBCE_LUT]; +}; + +/* Chrominance position. Applicable only for YCbCr input + * Applied after edge enhancement + */ +enum vpfe_chr_pos { + /* Co-siting, same position with luminance */ + VPFE_IPIPE_YUV422_CHR_POS_COSITE = 0, + /* Centering, In the middle of luminance */ + VPFE_IPIPE_YUV422_CHR_POS_CENTRE = 1, +}; + +/* Structure for configuring yuv422 conversion module */ +struct vpfe_ipipe_yuv422_conv { + /* Max Chrominance value */ + unsigned char en_chrom_lpf; + /* 1 - enable LPF for chrminance, 0 - disable */ + enum vpfe_chr_pos chrom_pos; +}; + +#define VPFE_IPIPE_MAX_SIZE_YEE_LUT 1024 + +enum vpfe_ipipe_yee_merge_meth { + VPFE_IPIPE_YEE_ABS_MAX = 0, + VPFE_IPIPE_YEE_EE_ES = 1, +}; + +/* Structure for configuring YUV Edge Enhancement module */ +struct vpfe_ipipe_yee { + /* 1 - enable enhancement, 0 - disable */ + unsigned char en; + /* enable/disable halo reduction in edge sharpner */ + unsigned char en_halo_red; + /* Merge method between Edge Enhancer and Edge sharpner */ + enum vpfe_ipipe_yee_merge_meth merge_meth; + /* HPF Shift length */ + unsigned char hpf_shft; + /* HPF Coefficient 00, S10 */ + short hpf_coef_00; + /* HPF Coefficient 01, S10 */ + short hpf_coef_01; + /* HPF Coefficient 02, S10 */ + short hpf_coef_02; + /* HPF Coefficient 10, S10 */ + short hpf_coef_10; + /* HPF Coefficient 11, S10 */ + short hpf_coef_11; + /* HPF Coefficient 12, S10 */ + short hpf_coef_12; + /* HPF Coefficient 20, S10 */ + short hpf_coef_20; + /* HPF Coefficient 21, S10 */ + short hpf_coef_21; + /* HPF Coefficient 22, S10 */ + short hpf_coef_22; + /* Lower threshold before referring to LUT */ + unsigned short yee_thr; + /* Edge sharpener Gain */ + unsigned short es_gain; + /* Edge sharpener lower threshold */ + unsigned short es_thr1; + /* Edge sharpener upper threshold */ + unsigned short es_thr2; + /* Edge sharpener gain on gradient */ + unsigned short es_gain_grad; + /* Edge sharpener offset on gradient */ + unsigned short es_ofst_grad; + /* Ptr to EE table. Must have 1024 entries */ + short table[VPFE_IPIPE_MAX_SIZE_YEE_LUT]; +}; + +enum vpfe_ipipe_car_meth { + /* Chromatic Gain Control */ + VPFE_IPIPE_CAR_CHR_GAIN_CTRL = 0, + /* Dynamic switching between CHR_GAIN_CTRL + * and MED_FLTR + */ + VPFE_IPIPE_CAR_DYN_SWITCH = 1, + /* Median Filter */ + VPFE_IPIPE_CAR_MED_FLTR = 2, +}; + +enum vpfe_ipipe_car_hpf_type { + VPFE_IPIPE_CAR_HPF_Y = 0, + VPFE_IPIPE_CAR_HPF_H = 1, + VPFE_IPIPE_CAR_HPF_V = 2, + VPFE_IPIPE_CAR_HPF_2D = 3, + /* 2D HPF from YUV Edge Enhancement */ + VPFE_IPIPE_CAR_HPF_2D_YEE = 4, +}; + +struct vpfe_ipipe_car_gain { + /* csup_gain */ + unsigned char gain; + /* csup_shf. */ + unsigned char shft; + /* gain minimum */ + unsigned short gain_min; +}; + +/* Structure for Chromatic Artifact Reduction */ +struct vpfe_ipipe_car { + /* enable/disable */ + unsigned char en; + /* Gain control or Dynamic switching */ + enum vpfe_ipipe_car_meth meth; + /* Gain1 function configuration for Gain control */ + struct vpfe_ipipe_car_gain gain1; + /* Gain2 function configuration for Gain control */ + struct vpfe_ipipe_car_gain gain2; + /* HPF type used for CAR */ + enum vpfe_ipipe_car_hpf_type hpf; + /* csup_thr: HPF threshold for Gain control */ + unsigned char hpf_thr; + /* Down shift value for hpf. 2 bits */ + unsigned char hpf_shft; + /* switch limit for median filter */ + unsigned char sw0; + /* switch coefficient for Gain control */ + unsigned char sw1; +}; + +/* structure for Chromatic Gain Suppression */ +struct vpfe_ipipe_cgs { + /* enable/disable */ + unsigned char en; + /* gain1 bright side threshold */ + unsigned char h_thr; + /* gain1 bright side slope */ + unsigned char h_slope; + /* gain1 down shift value for bright side */ + unsigned char h_shft; + /* gain1 bright side minimum gain */ + unsigned char h_min; +}; + +/* Max pixels allowed in the input. If above this either decimation + * or frame division mode to be enabled + */ +#define VPFE_IPIPE_MAX_INPUT_WIDTH 2600 + +struct vpfe_ipipe_input_config { + unsigned int vst; + unsigned int hst; +}; + +/** + * struct vpfe_ipipe_config - IPIPE engine configuration (user) + * @input_config: Pointer to structure for ipipe configuration. + * @flag: Specifies which ISP IPIPE functions should be enabled. + * @lutdpc: Pointer to luma enhancement structure. + * @otfdpc: Pointer to structure for defect correction. + * @nf1: Pointer to structure for Noise Filter. + * @nf2: Pointer to structure for Noise Filter. + * @gic: Pointer to structure for Green Imbalance. + * @wbal: Pointer to structure for White Balance. + * @cfa: Pointer to structure containing the CFA interpolation. + * @rgb2rgb1: Pointer to structure for RGB to RGB Blending. + * @rgb2rgb2: Pointer to structure for RGB to RGB Blending. + * @gamma: Pointer to gamma structure. + * @lut: Pointer to structure for 3D LUT. + * @rgb2yuv: Pointer to structure for RGB-YCbCr conversion. + * @gbce: Pointer to structure for Global Brightness,Contrast Control. + * @yuv422_conv: Pointer to structure for YUV 422 conversion. + * @yee: Pointer to structure for Edge Enhancer. + * @car: Pointer to structure for Chromatic Artifact Reduction. + * @cgs: Pointer to structure for Chromatic Gain Suppression. + */ +struct vpfe_ipipe_config { + __u32 flag; + struct vpfe_ipipe_input_config __user *input_config; + struct vpfe_ipipe_lutdpc __user *lutdpc; + struct vpfe_ipipe_otfdpc __user *otfdpc; + struct vpfe_ipipe_nf __user *nf1; + struct vpfe_ipipe_nf __user *nf2; + struct vpfe_ipipe_gic __user *gic; + struct vpfe_ipipe_wb __user *wbal; + struct vpfe_ipipe_cfa __user *cfa; + struct vpfe_ipipe_rgb2rgb __user *rgb2rgb1; + struct vpfe_ipipe_rgb2rgb __user *rgb2rgb2; + struct vpfe_ipipe_gamma __user *gamma; + struct vpfe_ipipe_3d_lut __user *lut; + struct vpfe_ipipe_rgb2yuv __user *rgb2yuv; + struct vpfe_ipipe_gbce __user *gbce; + struct vpfe_ipipe_yuv422_conv __user *yuv422_conv; + struct vpfe_ipipe_yee __user *yee; + struct vpfe_ipipe_car __user *car; + struct vpfe_ipipe_cgs __user *cgs; +}; + +/******************************************************************* +** Resizer API structures +*******************************************************************/ +/* Interpolation types used for horizontal rescale */ +enum vpfe_rsz_intp_t { + VPFE_RSZ_INTP_CUBIC, + VPFE_RSZ_INTP_LINEAR +}; + +/* Horizontal LPF intensity selection */ +enum vpfe_rsz_h_lpf_lse_t { + VPFE_RSZ_H_LPF_LSE_INTERN, + VPFE_RSZ_H_LPF_LSE_USER_VAL +}; + +enum vpfe_rsz_down_scale_ave_sz { + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + VPFE_IPIPE_DWN_SCALE_1_OVER_4, + VPFE_IPIPE_DWN_SCALE_1_OVER_8, + VPFE_IPIPE_DWN_SCALE_1_OVER_16, + VPFE_IPIPE_DWN_SCALE_1_OVER_32, + VPFE_IPIPE_DWN_SCALE_1_OVER_64, + VPFE_IPIPE_DWN_SCALE_1_OVER_128, + VPFE_IPIPE_DWN_SCALE_1_OVER_256 +}; + +struct vpfe_rsz_output_spec { + /* enable horizontal flip */ + unsigned char h_flip; + /* enable vertical flip */ + unsigned char v_flip; + /* line start offset for y. */ + unsigned int vst_y; + /* line start offset for c. Only for 420 */ + unsigned int vst_c; + /* vertical rescale interpolation type, YCbCr or Luminance */ + enum vpfe_rsz_intp_t v_typ_y; + /* vertical rescale interpolation type for Chrominance */ + enum vpfe_rsz_intp_t v_typ_c; + /* vertical lpf intensity - Luminance */ + unsigned char v_lpf_int_y; + /* vertical lpf intensity - Chrominance */ + unsigned char v_lpf_int_c; + /* horizontal rescale interpolation types, YCbCr or Luminance */ + enum vpfe_rsz_intp_t h_typ_y; + /* horizontal rescale interpolation types, Chrominance */ + enum vpfe_rsz_intp_t h_typ_c; + /* horizontal lpf intensity - Luminance */ + unsigned char h_lpf_int_y; + /* horizontal lpf intensity - Chrominance */ + unsigned char h_lpf_int_c; + /* Use down scale mode for scale down */ + unsigned char en_down_scale; + /* if downscale, set the downscale more average size for horizontal + * direction. Used only if output width and height is less than + * input sizes + */ + enum vpfe_rsz_down_scale_ave_sz h_dscale_ave_sz; + /* if downscale, set the downscale more average size for vertical + * direction. Used only if output width and height is less than + * input sizes + */ + enum vpfe_rsz_down_scale_ave_sz v_dscale_ave_sz; + /* Y offset. If set, the offset would be added to the base address + */ + unsigned int user_y_ofst; + /* C offset. If set, the offset would be added to the base address + */ + unsigned int user_c_ofst; +}; + +struct vpfe_rsz_config_params { + unsigned int vst; + /* horizontal start position of the image + * data to IPIPE + */ + unsigned int hst; + /* output spec of the image data coming out of resizer - 0(UYVY). + */ + struct vpfe_rsz_output_spec output1; + /* output spec of the image data coming out of resizer - 1(UYVY). + */ + struct vpfe_rsz_output_spec output2; + /* 0 , chroma sample at odd pixel, 1 - even pixel */ + unsigned char chroma_sample_even; + unsigned char frame_div_mode_en; + unsigned char yuv_y_min; + unsigned char yuv_y_max; + unsigned char yuv_c_min; + unsigned char yuv_c_max; + enum vpfe_chr_pos out_chr_pos; + unsigned char bypass; +}; + +/* Structure for VIDIOC_VPFE_RSZ_[S/G]_CONFIG IOCTLs */ +struct vpfe_rsz_config { + struct vpfe_rsz_config_params *config; +}; + +#endif /* _DAVINCI_VPFE_USER_H */ diff --git a/drivers/staging/media/davinci_vpfe/vpfe.h b/drivers/staging/media/davinci_vpfe/vpfe.h new file mode 100644 index 000000000000..0587bc52a840 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/vpfe.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _VPFE_H +#define _VPFE_H + +#ifdef __KERNEL__ +#include +#include +#include + +#include + +#define CAPTURE_DRV_NAME "vpfe-capture" + +struct vpfe_route { + __u32 input; + __u32 output; +}; + +enum vpfe_subdev_id { + VPFE_SUBDEV_TVP5146 = 1, + VPFE_SUBDEV_MT9T031 = 2, + VPFE_SUBDEV_TVP7002 = 3, + VPFE_SUBDEV_MT9P031 = 4, +}; + +struct vpfe_ext_subdev_info { + /* v4l2 subdev */ + struct v4l2_subdev *subdev; + /* Sub device module name */ + char module_name[32]; + /* Sub device group id */ + int grp_id; + /* Number of inputs supported */ + int num_inputs; + /* inputs available at the sub device */ + struct v4l2_input *inputs; + /* Sub dev routing information for each input */ + struct vpfe_route *routes; + /* ccdc bus/interface configuration */ + struct vpfe_hw_if_param ccdc_if_params; + /* i2c subdevice board info */ + struct i2c_board_info board_info; + /* Is this a camera sub device ? */ + unsigned is_camera:1; + /* check if sub dev supports routing */ + unsigned can_route:1; + /* registered ? */ + unsigned registered:1; +}; + +struct vpfe_config { + /* Number of sub devices connected to vpfe */ + int num_subdevs; + /* information about each subdev */ + struct vpfe_ext_subdev_info *sub_devs; + /* evm card info */ + char *card_name; + /* setup function for the input path */ + int (*setup_input)(enum vpfe_subdev_id id); + /* number of clocks */ + int num_clocks; + /* clocks used for vpfe capture */ + char *clocks[]; +}; +#endif +#endif diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c new file mode 100644 index 000000000000..7b351717bcbb --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c @@ -0,0 +1,740 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + * + * + * Driver name : VPFE Capture driver + * VPFE Capture driver allows applications to capture and stream video + * frames on DaVinci SoCs (DM6446, DM355 etc) from a YUV source such as + * TVP5146 or Raw Bayer RGB image data from an image sensor + * such as Microns' MT9T001, MT9T031 etc. + * + * These SoCs have, in common, a Video Processing Subsystem (VPSS) that + * consists of a Video Processing Front End (VPFE) for capturing + * video/raw image data and Video Processing Back End (VPBE) for displaying + * YUV data through an in-built analog encoder or Digital LCD port. This + * driver is for capture through VPFE. A typical EVM using these SoCs have + * following high level configuration. + * + * decoder(TVP5146/ YUV/ + * MT9T001) --> Raw Bayer RGB ---> MUX -> VPFE (CCDC/ISIF) + * data input | | + * V | + * SDRAM | + * V + * Image Processor + * | + * V + * SDRAM + * The data flow happens from a decoder connected to the VPFE over a + * YUV embedded (BT.656/BT.1120) or separate sync or raw bayer rgb interface + * and to the input of VPFE through an optional MUX (if more inputs are + * to be interfaced on the EVM). The input data is first passed through + * CCDC (CCD Controller, a.k.a Image Sensor Interface, ISIF). The CCDC + * does very little or no processing on YUV data and does pre-process Raw + * Bayer RGB data through modules such as Defect Pixel Correction (DFC) + * Color Space Conversion (CSC), data gain/offset etc. After this, data + * can be written to SDRAM or can be connected to the image processing + * block such as IPIPE (on DM355/DM365 only). + * + * Features supported + * - MMAP IO + * - USERPTR IO + * - Capture using TVP5146 over BT.656 + * - Support for interfacing decoders using sub device model + * - Work with DM365 or DM355 or DM6446 CCDC to do Raw Bayer + * RGB/YUV data capture to SDRAM. + * - Chaining of Image Processor + * - SINGLE-SHOT mode + */ + +#include +#include +#include + +#include "vpfe.h" +#include "vpfe_mc_capture.h" + +static bool debug; +static bool interface; + +module_param(interface, bool, S_IRUGO); +module_param(debug, bool, 0644); + +/** + * VPFE capture can be used for capturing video such as from TVP5146 or TVP7002 + * and for capture raw bayer data from camera sensors such as mt9p031. At this + * point there is problem in co-existence of mt9p031 and tvp5146 due to i2c + * address collision. So set the variable below from bootargs to do either video + * capture or camera capture. + * interface = 0 - video capture (from TVP514x or such), + * interface = 1 - Camera capture (from mt9p031 or such) + * Re-visit this when we fix the co-existence issue + */ +MODULE_PARM_DESC(interface, "interface 0-1 (default:0)"); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +MODULE_DESCRIPTION("VPFE Video for Linux Capture Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); + +/* map mbus_fmt to pixelformat */ +void mbus_to_pix(const struct v4l2_mbus_framefmt *mbus, + struct v4l2_pix_format *pix) +{ + switch (mbus->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->bytesperline = pix->width * 2; + break; + + case V4L2_MBUS_FMT_YUYV8_2X8: + pix->pixelformat = V4L2_PIX_FMT_YUYV; + pix->bytesperline = pix->width * 2; + break; + + case V4L2_MBUS_FMT_YUYV10_1X20: + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->bytesperline = pix->width * 2; + break; + + case V4L2_MBUS_FMT_SGRBG12_1X12: + pix->pixelformat = V4L2_PIX_FMT_SBGGR16; + pix->bytesperline = pix->width * 2; + break; + + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + pix->pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8; + pix->bytesperline = pix->width; + break; + + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + pix->pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8; + pix->bytesperline = pix->width; + break; + + case V4L2_MBUS_FMT_YDYUYDYV8_1X16: + pix->pixelformat = V4L2_PIX_FMT_NV12; + pix->bytesperline = pix->width; + break; + + case V4L2_MBUS_FMT_Y8_1X8: + pix->pixelformat = V4L2_PIX_FMT_GREY; + pix->bytesperline = pix->width; + break; + + case V4L2_MBUS_FMT_UV8_1X8: + pix->pixelformat = V4L2_PIX_FMT_UV8; + pix->bytesperline = pix->width; + break; + + default: + pr_err("Invalid mbus code set\n"); + } + /* pitch should be 32 bytes aligned */ + pix->bytesperline = ALIGN(pix->bytesperline, 32); + if (pix->pixelformat == V4L2_PIX_FMT_NV12) + pix->sizeimage = pix->bytesperline * pix->height + + ((pix->bytesperline * pix->height) >> 1); + else + pix->sizeimage = pix->bytesperline * pix->height; +} + +/* ISR for VINT0*/ +static irqreturn_t vpfe_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_isr\n"); + vpfe_isif_buffer_isr(&vpfe_dev->vpfe_isif); + vpfe_resizer_buffer_isr(&vpfe_dev->vpfe_resizer); + return IRQ_HANDLED; +} + +/* vpfe_vdint1_isr() - isr handler for VINT1 interrupt */ +static irqreturn_t vpfe_vdint1_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_vdint1_isr\n"); + vpfe_isif_vidint1_isr(&vpfe_dev->vpfe_isif); + return IRQ_HANDLED; +} + +/* vpfe_imp_dma_isr() - ISR for ipipe dma completion */ +static irqreturn_t vpfe_imp_dma_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_imp_dma_isr\n"); + vpfe_ipipeif_ss_buffer_isr(&vpfe_dev->vpfe_ipipeif); + vpfe_resizer_dma_isr(&vpfe_dev->vpfe_resizer); + return IRQ_HANDLED; +} + +/* + * vpfe_disable_clock() - Disable clocks for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Disables clocks defined in vpfe configuration. The function + * assumes that at least one clock is to be defined which is + * true as of now. + */ +static void vpfe_disable_clock(struct vpfe_device *vpfe_dev) +{ + struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; + int i; + + for (i = 0; i < vpfe_cfg->num_clocks; i++) { + clk_disable_unprepare(vpfe_dev->clks[i]); + clk_put(vpfe_dev->clks[i]); + } + kzfree(vpfe_dev->clks); + v4l2_info(vpfe_dev->pdev->driver, "vpfe capture clocks disabled\n"); +} + +/* + * vpfe_enable_clock() - Enable clocks for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Enables clocks defined in vpfe configuration. The function + * assumes that at least one clock is to be defined which is + * true as of now. + */ +static int vpfe_enable_clock(struct vpfe_device *vpfe_dev) +{ + struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; + int ret = -EFAULT; + int i; + + if (!vpfe_cfg->num_clocks) + return 0; + + vpfe_dev->clks = kzalloc(vpfe_cfg->num_clocks * + sizeof(struct clock *), GFP_KERNEL); + if (vpfe_dev->clks == NULL) { + v4l2_err(vpfe_dev->pdev->driver, "Memory allocation failed\n"); + return -ENOMEM; + } + + for (i = 0; i < vpfe_cfg->num_clocks; i++) { + if (vpfe_cfg->clocks[i] == NULL) { + v4l2_err(vpfe_dev->pdev->driver, + "clock %s is not defined in vpfe config\n", + vpfe_cfg->clocks[i]); + goto out; + } + + vpfe_dev->clks[i] = + clk_get(vpfe_dev->pdev, vpfe_cfg->clocks[i]); + if (vpfe_dev->clks[i] == NULL) { + v4l2_err(vpfe_dev->pdev->driver, + "Failed to get clock %s\n", + vpfe_cfg->clocks[i]); + goto out; + } + + if (clk_prepare_enable(vpfe_dev->clks[i])) { + v4l2_err(vpfe_dev->pdev->driver, + "vpfe clock %s not enabled\n", + vpfe_cfg->clocks[i]); + goto out; + } + + v4l2_info(vpfe_dev->pdev->driver, "vpss clock %s enabled", + vpfe_cfg->clocks[i]); + } + + return 0; +out: + for (i = 0; i < vpfe_cfg->num_clocks; i++) + if (vpfe_dev->clks[i]) { + clk_disable_unprepare(vpfe_dev->clks[i]); + clk_put(vpfe_dev->clks[i]); + } + + v4l2_err(vpfe_dev->pdev->driver, "Failed to enable clocks\n"); + kzfree(vpfe_dev->clks); + + return ret; +} + +/* + * vpfe_detach_irq() - Detach IRQs for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Detach all IRQs defined in vpfe configuration. + */ +static void vpfe_detach_irq(struct vpfe_device *vpfe_dev) +{ + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + free_irq(vpfe_dev->ccdc_irq1, vpfe_dev); + free_irq(vpfe_dev->imp_dma_irq, vpfe_dev); +} + +/* + * vpfe_attach_irq() - Attach IRQs for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Attach all IRQs defined in vpfe configuration. + */ +static int vpfe_attach_irq(struct vpfe_device *vpfe_dev) +{ + int ret = 0; + + ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED, + "vpfe_capture0", vpfe_dev); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error: requesting VINT0 interrupt\n"); + return ret; + } + + ret = request_irq(vpfe_dev->ccdc_irq1, vpfe_vdint1_isr, IRQF_DISABLED, + "vpfe_capture1", vpfe_dev); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error: requesting VINT1 interrupt\n"); + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + return ret; + } + + ret = request_irq(vpfe_dev->imp_dma_irq, vpfe_imp_dma_isr, + IRQF_DISABLED, "Imp_Sdram_Irq", vpfe_dev); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error: requesting IMP IRQ interrupt\n"); + free_irq(vpfe_dev->ccdc_irq1, vpfe_dev); + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + return ret; + } + + return 0; +} + +/* + * register_i2c_devices() - register all i2c v4l2 subdevs + * @vpfe_dev - ptr to vpfe capture device + * + * register all i2c v4l2 subdevs + */ +static int register_i2c_devices(struct vpfe_device *vpfe_dev) +{ + struct vpfe_ext_subdev_info *sdinfo; + struct vpfe_config *vpfe_cfg; + struct i2c_adapter *i2c_adap; + unsigned int num_subdevs; + int ret; + int i; + int k; + + vpfe_cfg = vpfe_dev->cfg; + i2c_adap = i2c_get_adapter(1); + num_subdevs = vpfe_cfg->num_subdevs; + vpfe_dev->sd = + kzalloc(sizeof(struct v4l2_subdev *)*num_subdevs, GFP_KERNEL); + if (vpfe_dev->sd == NULL) { + v4l2_err(&vpfe_dev->v4l2_dev, + "unable to allocate memory for subdevice\n"); + return -ENOMEM; + } + + for (i = 0, k = 0; i < num_subdevs; i++) { + sdinfo = &vpfe_cfg->sub_devs[i]; + /* + * register subdevices based on interface setting. Currently + * tvp5146 and mt9p031 cannot co-exists due to i2c address + * conflicts. So only one of them is registered. Re-visit this + * once we have support for i2c switch handling in i2c driver + * framework + */ + if (interface == sdinfo->is_camera) { + /* setup input path */ + if (vpfe_cfg->setup_input && + vpfe_cfg->setup_input(sdinfo->grp_id) < 0) { + ret = -EFAULT; + v4l2_info(&vpfe_dev->v4l2_dev, + "could not setup input for %s\n", + sdinfo->module_name); + goto probe_sd_out; + } + /* Load up the subdevice */ + vpfe_dev->sd[k] = + v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev, + i2c_adap, &sdinfo->board_info, + NULL); + if (vpfe_dev->sd[k]) { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s registered\n", + sdinfo->module_name); + + vpfe_dev->sd[k]->grp_id = sdinfo->grp_id; + k++; + + sdinfo->registered = 1; + } + } else { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s is not registered\n", + sdinfo->module_name); + } + } + vpfe_dev->num_ext_subdevs = k; + + return 0; + +probe_sd_out: + kzfree(vpfe_dev->sd); + + return ret; +} + +/* + * vpfe_register_entities() - register all v4l2 subdevs and media entities + * @vpfe_dev - ptr to vpfe capture device + * + * register all v4l2 subdevs, media entities, and creates links + * between entities + */ +static int vpfe_register_entities(struct vpfe_device *vpfe_dev) +{ + unsigned int flags = 0; + int ret; + int i; + + /* register i2c devices first */ + ret = register_i2c_devices(vpfe_dev); + if (ret) + return ret; + + /* register rest of the sub-devs */ + ret = vpfe_isif_register_entities(&vpfe_dev->vpfe_isif, + &vpfe_dev->v4l2_dev); + if (ret) + return ret; + + ret = vpfe_ipipeif_register_entities(&vpfe_dev->vpfe_ipipeif, + &vpfe_dev->v4l2_dev); + if (ret) + goto out_isif_register; + + ret = vpfe_ipipe_register_entities(&vpfe_dev->vpfe_ipipe, + &vpfe_dev->v4l2_dev); + if (ret) + goto out_ipipeif_register; + + ret = vpfe_resizer_register_entities(&vpfe_dev->vpfe_resizer, + &vpfe_dev->v4l2_dev); + if (ret) + goto out_ipipe_register; + + /* create links now, starting with external(i2c) entities */ + for (i = 0; i < vpfe_dev->num_ext_subdevs; i++) + /* if entity has no pads (ex: amplifier), + cant establish link */ + if (vpfe_dev->sd[i]->entity.num_pads) { + ret = media_entity_create_link(&vpfe_dev->sd[i]->entity, + 0, &vpfe_dev->vpfe_isif.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + } + + ret = media_entity_create_link(&vpfe_dev->vpfe_isif.subdev.entity, 1, + &vpfe_dev->vpfe_ipipeif.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = media_entity_create_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1, + &vpfe_dev->vpfe_ipipe.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = media_entity_create_link(&vpfe_dev->vpfe_ipipe.subdev.entity, + 1, &vpfe_dev->vpfe_resizer.crop_resizer.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = media_entity_create_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1, + &vpfe_dev->vpfe_resizer.crop_resizer.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = v4l2_device_register_subdev_nodes(&vpfe_dev->v4l2_dev); + if (ret < 0) + goto out_resizer_register; + + return 0; + +out_resizer_register: + vpfe_resizer_unregister_entities(&vpfe_dev->vpfe_resizer); +out_ipipe_register: + vpfe_ipipe_unregister_entities(&vpfe_dev->vpfe_ipipe); +out_ipipeif_register: + vpfe_ipipeif_unregister_entities(&vpfe_dev->vpfe_ipipeif); +out_isif_register: + vpfe_isif_unregister_entities(&vpfe_dev->vpfe_isif); + + return ret; +} + +/* + * vpfe_unregister_entities() - unregister all v4l2 subdevs and media entities + * @vpfe_dev - ptr to vpfe capture device + * + * unregister all v4l2 subdevs and media entities + */ +static void vpfe_unregister_entities(struct vpfe_device *vpfe_dev) +{ + vpfe_isif_unregister_entities(&vpfe_dev->vpfe_isif); + vpfe_ipipeif_unregister_entities(&vpfe_dev->vpfe_ipipeif); + vpfe_ipipe_unregister_entities(&vpfe_dev->vpfe_ipipe); + vpfe_resizer_unregister_entities(&vpfe_dev->vpfe_resizer); +} + +/* + * vpfe_cleanup_modules() - cleanup all non-i2c v4l2 subdevs + * @vpfe_dev - ptr to vpfe capture device + * @pdev - pointer to platform device + * + * cleanup all v4l2 subdevs + */ +static void vpfe_cleanup_modules(struct vpfe_device *vpfe_dev, + struct platform_device *pdev) +{ + vpfe_isif_cleanup(&vpfe_dev->vpfe_isif, pdev); + vpfe_ipipeif_cleanup(&vpfe_dev->vpfe_ipipeif, pdev); + vpfe_ipipe_cleanup(&vpfe_dev->vpfe_ipipe, pdev); + vpfe_resizer_cleanup(&vpfe_dev->vpfe_resizer, pdev); +} + +/* + * vpfe_initialize_modules() - initialize all non-i2c v4l2 subdevs + * @vpfe_dev - ptr to vpfe capture device + * @pdev - pointer to platform device + * + * intialize all v4l2 subdevs and media entities + */ +static int vpfe_initialize_modules(struct vpfe_device *vpfe_dev, + struct platform_device *pdev) +{ + int ret; + + ret = vpfe_isif_init(&vpfe_dev->vpfe_isif, pdev); + if (ret) + return ret; + + ret = vpfe_ipipeif_init(&vpfe_dev->vpfe_ipipeif, pdev); + if (ret) + goto out_isif_init; + + ret = vpfe_ipipe_init(&vpfe_dev->vpfe_ipipe, pdev); + if (ret) + goto out_ipipeif_init; + + ret = vpfe_resizer_init(&vpfe_dev->vpfe_resizer, pdev); + if (ret) + goto out_ipipe_init; + + return 0; + +out_ipipe_init: + vpfe_ipipe_cleanup(&vpfe_dev->vpfe_ipipe, pdev); +out_ipipeif_init: + vpfe_ipipeif_cleanup(&vpfe_dev->vpfe_ipipeif, pdev); +out_isif_init: + vpfe_isif_cleanup(&vpfe_dev->vpfe_isif, pdev); + + return ret; +} + +/* + * vpfe_probe() : vpfe probe function + * @pdev: platform device pointer + * + * This function creates device entries by register itself to the V4L2 driver + * and initializes fields of each device objects + */ +static int vpfe_probe(struct platform_device *pdev) +{ + struct vpfe_device *vpfe_dev; + struct resource *res1; + int ret = -ENOMEM; + + vpfe_dev = kzalloc(sizeof(*vpfe_dev), GFP_KERNEL); + if (!vpfe_dev) { + v4l2_err(pdev->dev.driver, + "Failed to allocate memory for vpfe_dev\n"); + return ret; + } + + if (pdev->dev.platform_data == NULL) { + v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + vpfe_dev->cfg = pdev->dev.platform_data; + if (vpfe_dev->cfg->card_name == NULL || + vpfe_dev->cfg->sub_devs == NULL) { + v4l2_err(pdev->dev.driver, "null ptr in vpfe_cfg\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + /* Get VINT0 irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT0\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + vpfe_dev->ccdc_irq0 = res1->start; + + /* Get VINT1 irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT1\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + vpfe_dev->ccdc_irq1 = res1->start; + + /* Get DMA irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 2); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for DMA\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + vpfe_dev->imp_dma_irq = res1->start; + + vpfe_dev->pdev = &pdev->dev; + + /* enable vpss clocks */ + ret = vpfe_enable_clock(vpfe_dev); + if (ret) + goto probe_free_dev_mem; + + if (vpfe_initialize_modules(vpfe_dev, pdev)) + goto probe_disable_clock; + + vpfe_dev->media_dev.dev = vpfe_dev->pdev; + strcpy((char *)&vpfe_dev->media_dev.model, "davinci-media"); + + ret = media_device_register(&vpfe_dev->media_dev); + if (ret) { + v4l2_err(pdev->dev.driver, + "Unable to register media device.\n"); + goto probe_out_entities_cleanup; + } + + vpfe_dev->v4l2_dev.mdev = &vpfe_dev->media_dev; + ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev); + if (ret) { + v4l2_err(pdev->dev.driver, "Unable to register v4l2 device.\n"); + goto probe_out_media_unregister; + } + + v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n"); + /* set the driver data in platform device */ + platform_set_drvdata(pdev, vpfe_dev); + /* register subdevs/entities */ + if (vpfe_register_entities(vpfe_dev)) + goto probe_out_v4l2_unregister; + + ret = vpfe_attach_irq(vpfe_dev); + if (ret) + goto probe_out_entities_unregister; + + return 0; + +probe_out_entities_unregister: + vpfe_unregister_entities(vpfe_dev); + kzfree(vpfe_dev->sd); +probe_out_v4l2_unregister: + v4l2_device_unregister(&vpfe_dev->v4l2_dev); +probe_out_media_unregister: + media_device_unregister(&vpfe_dev->media_dev); +probe_out_entities_cleanup: + vpfe_cleanup_modules(vpfe_dev, pdev); +probe_disable_clock: + vpfe_disable_clock(vpfe_dev); +probe_free_dev_mem: + kzfree(vpfe_dev); + + return ret; +} + +/* + * vpfe_remove : This function un-registers device from V4L2 driver + */ +static int vpfe_remove(struct platform_device *pdev) +{ + struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev); + + v4l2_info(pdev->dev.driver, "vpfe_remove\n"); + + kzfree(vpfe_dev->sd); + vpfe_detach_irq(vpfe_dev); + vpfe_unregister_entities(vpfe_dev); + vpfe_cleanup_modules(vpfe_dev, pdev); + v4l2_device_unregister(&vpfe_dev->v4l2_dev); + media_device_unregister(&vpfe_dev->media_dev); + vpfe_disable_clock(vpfe_dev); + kzfree(vpfe_dev); + + return 0; +} + +static struct platform_driver vpfe_driver = { + .driver = { + .name = CAPTURE_DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = vpfe_probe, + .remove = vpfe_remove, +}; + +/** + * vpfe_init : This function registers device driver + */ +static __init int vpfe_init(void) +{ + /* Register driver to the kernel */ + return platform_driver_register(&vpfe_driver); +} + +/** + * vpfe_cleanup : This function un-registers device driver + */ +static void vpfe_cleanup(void) +{ + platform_driver_unregister(&vpfe_driver); +} + +module_init(vpfe_init); +module_exit(vpfe_cleanup); diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h new file mode 100644 index 000000000000..68f6fe43a5b5 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _DAVINCI_VPFE_MC_CAPTURE_H +#define _DAVINCI_VPFE_MC_CAPTURE_H + +#include "dm365_ipipe.h" +#include "dm365_ipipeif.h" +#include "dm365_isif.h" +#include "dm365_resizer.h" +#include "vpfe_video.h" + +#define VPFE_MAJOR_RELEASE 0 +#define VPFE_MINOR_RELEASE 0 +#define VPFE_BUILD 1 +#define VPFE_CAPTURE_VERSION_CODE ((VPFE_MAJOR_RELEASE << 16) | \ + (VPFE_MINOR_RELEASE << 8) | \ + VPFE_BUILD) + +/* IPIPE hardware limits */ +#define IPIPE_MAX_OUTPUT_WIDTH_A 2176 +#define IPIPE_MAX_OUTPUT_WIDTH_B 640 + +/* Based on max resolution supported. QXGA */ +#define IPIPE_MAX_OUTPUT_HEIGHT_A 1536 +/* Based on max resolution supported. VGA */ +#define IPIPE_MAX_OUTPUT_HEIGHT_B 480 + +#define to_vpfe_device(ptr_module) \ + container_of(ptr_module, struct vpfe_device, vpfe_##ptr_module) +#define to_device(ptr_module) \ + (to_vpfe_device(ptr_module)->dev) + +struct vpfe_device { + /* external registered sub devices */ + struct v4l2_subdev **sd; + /* number of registered external subdevs */ + unsigned int num_ext_subdevs; + /* vpfe cfg */ + struct vpfe_config *cfg; + /* clock ptrs for vpfe capture */ + struct clk **clks; + /* V4l2 device */ + struct v4l2_device v4l2_dev; + /* parent device */ + struct device *pdev; + /* IRQ number for DMA transfer completion at the image processor */ + unsigned int imp_dma_irq; + /* CCDC IRQs used when CCDC/ISIF output to SDRAM */ + unsigned int ccdc_irq0; + unsigned int ccdc_irq1; + /* maximum video memory that is available*/ + unsigned int video_limit; + /* media device */ + struct media_device media_dev; + /* ccdc subdevice */ + struct vpfe_isif_device vpfe_isif; + /* ipipeif subdevice */ + struct vpfe_ipipeif_device vpfe_ipipeif; + /* ipipe subdevice */ + struct vpfe_ipipe_device vpfe_ipipe; + /* resizer subdevice */ + struct vpfe_resizer_device vpfe_resizer; +}; + +/* File handle structure */ +struct vpfe_fh { + struct v4l2_fh vfh; + struct vpfe_video_device *video; + /* Indicates whether this file handle is doing IO */ + u8 io_allowed; + /* Used to keep track priority of this instance */ + enum v4l2_priority prio; +}; + +void mbus_to_pix(const struct v4l2_mbus_framefmt *mbus, + struct v4l2_pix_format *pix); + +#endif /* _DAVINCI_VPFE_MC_CAPTURE_H */ -- cgit v1.2.3 From 622897da67b38290a2a0fbbebdd13f2448a1b5e0 Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Wed, 28 Nov 2012 02:03:38 -0300 Subject: [media] davinci: vpfe: add v4l2 video driver support Add a generic video driver functionality to be used by all the vpfe drivers for davinci SoCs. The functionality includes all the standard v4l2 interfaces including streaming. The video node interface can be used both as an input and output node for both continuous and single shot modes. Also supports dv_presets to include HD modes, wth support for both user pointer IO and mmap. The buffering mechanism is based on videobuf2 interface. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/davinci_vpfe/vpfe_video.c | 1620 +++++++++++++++++++++++ drivers/staging/media/davinci_vpfe/vpfe_video.h | 155 +++ 2 files changed, 1775 insertions(+) create mode 100644 drivers/staging/media/davinci_vpfe/vpfe_video.c create mode 100644 drivers/staging/media/davinci_vpfe/vpfe_video.h (limited to 'drivers') diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c new file mode 100644 index 000000000000..99ccbebea598 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c @@ -0,0 +1,1620 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#include +#include + +#include + +#include "vpfe.h" +#include "vpfe_mc_capture.h" + +/* minimum number of buffers needed in cont-mode */ +#define MIN_NUM_BUFFERS 3 + +static int debug; + +/* get v4l2 subdev pointer to external subdev which is active */ +static struct media_entity *vpfe_get_input_entity + (struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct media_pad *remote; + + remote = media_entity_remote_source(&vpfe_dev->vpfe_isif.pads[0]); + if (remote == NULL) { + pr_err("Invalid media connection to isif/ccdc\n"); + return NULL; + } + return remote->entity; +} + +/* updates external subdev(sensor/decoder) which is active */ +static int vpfe_update_current_ext_subdev(struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_config *vpfe_cfg; + struct v4l2_subdev *subdev; + struct media_pad *remote; + int i; + + remote = media_entity_remote_source(&vpfe_dev->vpfe_isif.pads[0]); + if (remote == NULL) { + pr_err("Invalid media connection to isif/ccdc\n"); + return -EINVAL; + } + + subdev = media_entity_to_v4l2_subdev(remote->entity); + vpfe_cfg = vpfe_dev->pdev->platform_data; + for (i = 0; i < vpfe_cfg->num_subdevs; i++) { + if (!strcmp(vpfe_cfg->sub_devs[i].module_name, subdev->name)) { + video->current_ext_subdev = &vpfe_cfg->sub_devs[i]; + break; + } + } + + /* if user not linked decoder/sensor to isif/ccdc */ + if (i == vpfe_cfg->num_subdevs) { + pr_err("Invalid media chain connection to isif/ccdc\n"); + return -EINVAL; + } + /* find the v4l2 subdev pointer */ + for (i = 0; i < vpfe_dev->num_ext_subdevs; i++) { + if (!strcmp(video->current_ext_subdev->module_name, + vpfe_dev->sd[i]->name)) + video->current_ext_subdev->subdev = vpfe_dev->sd[i]; + } + return 0; +} + +/* get the subdev which is connected to the output video node */ +static struct v4l2_subdev * +vpfe_video_remote_subdev(struct vpfe_video_device *video, u32 *pad) +{ + struct media_pad *remote = media_entity_remote_source(&video->pad); + + if (remote == NULL || remote->entity->type != MEDIA_ENT_T_V4L2_SUBDEV) + return NULL; + if (pad) + *pad = remote->index; + return media_entity_to_v4l2_subdev(remote->entity); +} + +/* get the format set at output pad of the adjacent subdev */ +static int +__vpfe_video_get_format(struct vpfe_video_device *video, + struct v4l2_format *format) +{ + struct v4l2_subdev_format fmt; + struct v4l2_subdev *subdev; + struct media_pad *remote; + u32 pad; + int ret; + + subdev = vpfe_video_remote_subdev(video, &pad); + if (subdev == NULL) + return -EINVAL; + + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + remote = media_entity_remote_source(&video->pad); + fmt.pad = remote->index; + + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret == -ENOIOCTLCMD) + return -EINVAL; + + format->type = video->type; + /* convert mbus_format to v4l2_format */ + v4l2_fill_pix_format(&format->fmt.pix, &fmt.format); + mbus_to_pix(&fmt.format, &format->fmt.pix); + + return 0; +} + +/* make a note of pipeline details */ +static void vpfe_prepare_pipeline(struct vpfe_video_device *video) +{ + struct media_entity *entity = &video->video_dev.entity; + struct media_device *mdev = entity->parent; + struct vpfe_pipeline *pipe = &video->pipe; + struct vpfe_video_device *far_end = NULL; + struct media_entity_graph graph; + + pipe->input_num = 0; + pipe->output_num = 0; + + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + pipe->inputs[pipe->input_num++] = video; + else + pipe->outputs[pipe->output_num++] = video; + + mutex_lock(&mdev->graph_mutex); + media_entity_graph_walk_start(&graph, entity); + while ((entity = media_entity_graph_walk_next(&graph))) { + if (entity == &video->video_dev.entity) + continue; + if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + continue; + far_end = to_vpfe_video(media_entity_to_video_device(entity)); + if (far_end->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + pipe->inputs[pipe->input_num++] = far_end; + else + pipe->outputs[pipe->output_num++] = far_end; + } + mutex_unlock(&mdev->graph_mutex); +} + +/* update pipe state selected by user */ +static int vpfe_update_pipe_state(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + int ret; + + vpfe_prepare_pipeline(video); + + /* Find out if there is any input video + if yes, it is single shot. + */ + if (pipe->input_num == 0) { + pipe->state = VPFE_PIPELINE_STREAM_CONTINUOUS; + ret = vpfe_update_current_ext_subdev(video); + if (ret) { + pr_err("Invalid external subdev\n"); + return ret; + } + } else { + pipe->state = VPFE_PIPELINE_STREAM_SINGLESHOT; + } + video->initialized = 1; + video->skip_frame_count = 1; + video->skip_frame_count_init = 1; + return 0; +} + +/* checks wether pipeline is ready for enabling */ +int vpfe_video_is_pipe_ready(struct vpfe_pipeline *pipe) +{ + int i; + + for (i = 0; i < pipe->input_num; i++) + if (!pipe->inputs[i]->started || + pipe->inputs[i]->state != VPFE_VIDEO_BUFFER_QUEUED) + return 0; + for (i = 0; i < pipe->output_num; i++) + if (!pipe->outputs[i]->started || + pipe->outputs[i]->state != VPFE_VIDEO_BUFFER_QUEUED) + return 0; + return 1; +} + +/** + * Validate a pipeline by checking both ends of all links for format + * discrepancies. + * + * Return 0 if all formats match, or -EPIPE if at least one link is found with + * different formats on its two ends. + */ +static int vpfe_video_validate_pipeline(struct vpfe_pipeline *pipe) +{ + struct v4l2_subdev_format fmt_source; + struct v4l2_subdev_format fmt_sink; + struct v4l2_subdev *subdev; + struct media_pad *pad; + int ret; + + /* + * Should not matter if it is output[0] or 1 as + * the general ideas is to traverse backwards and + * the fact that the out video node always has the + * format of the connected pad. + */ + subdev = vpfe_video_remote_subdev(pipe->outputs[0], NULL); + if (subdev == NULL) + return -EPIPE; + + while (1) { + /* Retrieve the sink format */ + pad = &subdev->entity.pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt_sink.pad = pad->index; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, + &fmt_sink); + + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + + /* Retrieve the source format */ + pad = media_entity_remote_source(pad); + if (pad == NULL || + pad->entity->type != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + subdev = media_entity_to_v4l2_subdev(pad->entity); + + fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt_source.pad = pad->index; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source); + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + + /* Check if the two ends match */ + if (fmt_source.format.code != fmt_sink.format.code || + fmt_source.format.width != fmt_sink.format.width || + fmt_source.format.height != fmt_sink.format.height) + return -EPIPE; + } + return 0; +} + +/* + * vpfe_pipeline_enable() - Enable streaming on a pipeline + * @vpfe_dev: vpfe device + * @pipe: vpfe pipeline + * + * Walk the entities chain starting at the pipeline output video node and start + * all modules in the chain in the given mode. + * + * Return 0 if successful, or the return value of the failed video::s_stream + * operation otherwise. + */ +static int vpfe_pipeline_enable(struct vpfe_pipeline *pipe) +{ + struct media_entity_graph graph; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_device *mdev; + int ret = 0; + + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) + entity = vpfe_get_input_entity(pipe->outputs[0]); + else + entity = &pipe->inputs[0]->video_dev.entity; + + mdev = entity->parent; + mutex_lock(&mdev->graph_mutex); + media_entity_graph_walk_start(&graph, entity); + while ((entity = media_entity_graph_walk_next(&graph))) { + + if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + continue; + subdev = media_entity_to_v4l2_subdev(entity); + ret = v4l2_subdev_call(subdev, video, s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + break; + } + mutex_unlock(&mdev->graph_mutex); + return ret; +} + +/* + * vpfe_pipeline_disable() - Disable streaming on a pipeline + * @vpfe_dev: vpfe device + * @pipe: VPFE pipeline + * + * Walk the entities chain starting at the pipeline output video node and stop + * all modules in the chain. + * + * Return 0 if all modules have been properly stopped, or -ETIMEDOUT if a module + * can't be stopped. + */ +static int vpfe_pipeline_disable(struct vpfe_pipeline *pipe) +{ + struct media_entity_graph graph; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_device *mdev; + int ret = 0; + + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) + entity = vpfe_get_input_entity(pipe->outputs[0]); + else + entity = &pipe->inputs[0]->video_dev.entity; + + mdev = entity->parent; + mutex_lock(&mdev->graph_mutex); + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + + if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + continue; + subdev = media_entity_to_v4l2_subdev(entity); + ret = v4l2_subdev_call(subdev, video, s_stream, 0); + if (ret < 0 && ret != -ENOIOCTLCMD) + break; + } + mutex_unlock(&mdev->graph_mutex); + + return (ret == 0) ? ret : -ETIMEDOUT ; +} + +/* + * vpfe_pipeline_set_stream() - Enable/disable streaming on a pipeline + * @vpfe_dev: VPFE device + * @pipe: VPFE pipeline + * @state: Stream state (stopped or active) + * + * Set the pipeline to the given stream state. + * + * Return 0 if successfull, or the return value of the failed video::s_stream + * operation otherwise. + */ +static int vpfe_pipeline_set_stream(struct vpfe_pipeline *pipe, + enum vpfe_pipeline_stream_state state) +{ + if (state == VPFE_PIPELINE_STREAM_STOPPED) + return vpfe_pipeline_disable(pipe); + + return vpfe_pipeline_enable(pipe); +} + +static int all_videos_stopped(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + int i; + + for (i = 0; i < pipe->input_num; i++) + if (pipe->inputs[i]->started) + return 0; + for (i = 0; i < pipe->output_num; i++) + if (pipe->outputs[i]->started) + return 0; + return 1; +} + +/* + * vpfe_open() - open video device + * @file: file pointer + * + * initialize media pipeline state, allocate memory for file handle + * + * Return 0 if successful, or the return -ENODEV otherwise. + */ +static int vpfe_open(struct file *file) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_fh *handle; + + /* Allocate memory for the file handle object */ + handle = kzalloc(sizeof(struct vpfe_fh), GFP_KERNEL); + + if (handle == NULL) + return -ENOMEM; + + v4l2_fh_init(&handle->vfh, &video->video_dev); + v4l2_fh_add(&handle->vfh); + + mutex_lock(&video->lock); + /* If decoder is not initialized. initialize it */ + if (!video->initialized && vpfe_update_pipe_state(video)) { + mutex_unlock(&video->lock); + return -ENODEV; + } + /* Increment device users counter */ + video->usrs++; + /* Set io_allowed member to false */ + handle->io_allowed = 0; + v4l2_prio_open(&video->prio, &handle->prio); + handle->video = video; + file->private_data = &handle->vfh; + mutex_unlock(&video->lock); + + return 0; +} + +/* get the next buffer available from dma queue */ +static unsigned long +vpfe_video_get_next_buffer(struct vpfe_video_device *video) +{ + video->cur_frm = video->next_frm = + list_entry(video->dma_queue.next, + struct vpfe_cap_buffer, list); + + list_del(&video->next_frm->list); + video->next_frm->vb.state = VB2_BUF_STATE_ACTIVE; + return vb2_dma_contig_plane_dma_addr(&video->next_frm->vb, 0); +} + +/* schedule the next buffer which is available on dma queue */ +void vpfe_video_schedule_next_buffer(struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + + if (list_empty(&video->dma_queue)) + return; + + video->next_frm = list_entry(video->dma_queue.next, + struct vpfe_cap_buffer, list); + + if (VPFE_PIPELINE_STREAM_SINGLESHOT == video->pipe.state) + video->cur_frm = video->next_frm; + + list_del(&video->next_frm->list); + video->next_frm->vb.state = VB2_BUF_STATE_ACTIVE; + addr = vb2_dma_contig_plane_dma_addr(&video->next_frm->vb, 0); + video->ops->queue(vpfe_dev, addr); + video->state = VPFE_VIDEO_BUFFER_QUEUED; +} + +/* schedule the buffer for capturing bottom field */ +void vpfe_video_schedule_bottom_field(struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + + addr = vb2_dma_contig_plane_dma_addr(&video->cur_frm->vb, 0); + addr += video->field_off; + video->ops->queue(vpfe_dev, addr); +} + +/* make buffer available for dequeue */ +void vpfe_video_process_buffer_complete(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + + do_gettimeofday(&video->cur_frm->vb.v4l2_buf.timestamp); + vb2_buffer_done(&video->cur_frm->vb, VB2_BUF_STATE_DONE); + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) + video->cur_frm = video->next_frm; +} + +/* vpfe_stop_capture() - stop streaming */ +static void vpfe_stop_capture(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + + video->started = 0; + + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return; + if (all_videos_stopped(video)) + vpfe_pipeline_set_stream(pipe, + VPFE_PIPELINE_STREAM_STOPPED); +} + +/* + * vpfe_release() - release video device + * @file: file pointer + * + * deletes buffer queue, frees the buffers and the vpfe file handle + * + * Return 0 + */ +static int vpfe_release(struct file *file) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct v4l2_fh *vfh = file->private_data; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = container_of(vfh, struct vpfe_fh, vfh); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_release\n"); + + /* Get the device lock */ + mutex_lock(&video->lock); + /* if this instance is doing IO */ + if (fh->io_allowed) { + if (video->started) { + vpfe_stop_capture(video); + /* mark pipe state as stopped in vpfe_release(), + as app might call streamon() after streamoff() + in which case driver has to start streaming. + */ + video->pipe.state = VPFE_PIPELINE_STREAM_STOPPED; + vb2_streamoff(&video->buffer_queue, + video->buffer_queue.type); + } + video->io_usrs = 0; + /* Free buffers allocated */ + vb2_queue_release(&video->buffer_queue); + vb2_dma_contig_cleanup_ctx(video->alloc_ctx); + } + /* Decrement device users counter */ + video->usrs--; + /* Close the priority */ + v4l2_prio_close(&video->prio, fh->prio); + /* If this is the last file handle */ + if (!video->usrs) + video->initialized = 0; + mutex_unlock(&video->lock); + file->private_data = NULL; + /* Free memory allocated to file handle object */ + v4l2_fh_del(vfh); + kzfree(fh); + return 0; +} + +/* + * vpfe_mmap() - It is used to map kernel space buffers + * into user spaces + */ +static int vpfe_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_mmap\n"); + return vb2_mmap(&video->buffer_queue, vma); +} + +/* + * vpfe_poll() - It is used for select/poll system call + */ +static unsigned int vpfe_poll(struct file *file, poll_table *wait) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_poll\n"); + if (video->started) + return vb2_poll(&video->buffer_queue, file, wait); + return 0; +} + +/* vpfe capture driver file operations */ +static const struct v4l2_file_operations vpfe_fops = { + .owner = THIS_MODULE, + .open = vpfe_open, + .release = vpfe_release, + .unlocked_ioctl = video_ioctl2, + .mmap = vpfe_mmap, + .poll = vpfe_poll +}; + +/* + * vpfe_querycap() - query capabilities of video device + * @file: file pointer + * @priv: void pointer + * @cap: pointer to v4l2_capability structure + * + * fills v4l2 capabilities structure + * + * Return 0 + */ +static int vpfe_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n"); + + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + else + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + cap->device_caps = cap->capabilities; + cap->version = VPFE_CAPTURE_VERSION_CODE; + strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); + strlcpy(cap->bus_info, "VPFE", sizeof(cap->bus_info)); + strlcpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card)); + + return 0; +} + +/* + * vpfe_g_fmt() - get the format which is active on video device + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_format structure + * + * fills v4l2 format structure with active format + * + * Return 0 + */ +static int vpfe_g_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt\n"); + /* Fill in the information about format */ + *fmt = video->fmt; + return 0; +} + +/* + * vpfe_enum_fmt() - enum formats supported on media chain + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_fmtdesc structure + * + * fills v4l2_fmtdesc structure with output format set on adjacent subdev, + * only one format is enumearted as subdevs are already configured + * + * Return 0 if successfull, error code otherwise + */ +static int vpfe_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev_format sd_fmt; + struct v4l2_mbus_framefmt mbus; + struct v4l2_subdev *subdev; + struct v4l2_format format; + struct media_pad *remote; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt\n"); + + /* since already subdev pad format is set, + only one pixel format is available */ + if (fmt->index > 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid index\n"); + return -EINVAL; + } + /* get the remote pad */ + remote = media_entity_remote_source(&video->pad); + if (remote == NULL) { + v4l2_err(&vpfe_dev->v4l2_dev, + "invalid remote pad for video node\n"); + return -EINVAL; + } + /* get the remote subdev */ + subdev = vpfe_video_remote_subdev(video, NULL); + if (subdev == NULL) { + v4l2_err(&vpfe_dev->v4l2_dev, + "invalid remote subdev for video node\n"); + return -EINVAL; + } + sd_fmt.pad = remote->index; + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + /* get output format of remote subdev */ + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &sd_fmt); + if (ret) { + v4l2_err(&vpfe_dev->v4l2_dev, + "invalid remote subdev for video node\n"); + return ret; + } + /* convert to pix format */ + mbus.code = sd_fmt.format.code; + mbus_to_pix(&mbus, &format.fmt.pix); + /* copy the result */ + fmt->pixelformat = format.fmt.pix.pixelformat; + + return 0; +} + +/* + * vpfe_s_fmt() - set the format on video device + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_format structure + * + * validate and set the format on video device + * + * Return 0 on success, error code otherwise + */ +static int vpfe_s_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_format format; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_fmt\n"); + /* If streaming is started, return error */ + if (video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + /* get adjacent subdev's output pad format */ + ret = __vpfe_video_get_format(video, &format); + if (ret) + return ret; + *fmt = format; + video->fmt = *fmt; + return 0; +} + +/* + * vpfe_try_fmt() - try the format on video device + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_format structure + * + * validate the format, update with correct format + * based on output format set on adjacent subdev + * + * Return 0 on success, error code otherwise + */ +static int vpfe_try_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_format format; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_try_fmt\n"); + /* get adjacent subdev's output pad format */ + ret = __vpfe_video_get_format(video, &format); + if (ret) + return ret; + + *fmt = format; + return 0; +} + +/* + * vpfe_enum_input() - enum inputs supported on media chain + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_fmtdesc structure + * + * fills v4l2_input structure with input available on media chain, + * only one input is enumearted as media chain is setup by this time + * + * Return 0 if successfull, -EINVAL is media chain is invalid + */ +static int vpfe_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_ext_subdev_info *sdinfo = video->current_ext_subdev; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_input\n"); + /* enumerate from the subdev user has choosen through mc */ + if (inp->index < sdinfo->num_inputs) { + memcpy(inp, &sdinfo->inputs[inp->index], + sizeof(struct v4l2_input)); + return 0; + } + return -EINVAL; +} + +/* + * vpfe_g_input() - get index of the input which is active + * @file: file pointer + * @priv: void pointer + * @index: pointer to unsigned int + * + * set index with input index which is active + */ +static int vpfe_g_input(struct file *file, void *priv, unsigned int *index) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_input\n"); + + *index = video->current_input; + return 0; +} + +/* + * vpfe_s_input() - set input which is pointed by input index + * @file: file pointer + * @priv: void pointer + * @index: pointer to unsigned int + * + * set input on external subdev + * + * Return 0 on success, error code otherwise + */ +static int vpfe_s_input(struct file *file, void *priv, unsigned int index) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sdinfo; + struct vpfe_route *route; + struct v4l2_input *inps; + u32 output; + u32 input; + int ret; + int i; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_input\n"); + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + /* + * If streaming is started return device busy + * error + */ + if (video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is on\n"); + ret = -EBUSY; + goto unlock_out; + } + + sdinfo = video->current_ext_subdev; + if (!sdinfo->registered) { + ret = -EINVAL; + goto unlock_out; + } + if (vpfe_dev->cfg->setup_input && + vpfe_dev->cfg->setup_input(sdinfo->grp_id) < 0) { + ret = -EFAULT; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "couldn't setup input for %s\n", + sdinfo->module_name); + goto unlock_out; + } + route = &sdinfo->routes[index]; + if (route && sdinfo->can_route) { + input = route->input; + output = route->output; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, video, + s_routing, input, output, 0); + if (ret) { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "s_input:error in setting input in decoder\n"); + ret = -EINVAL; + goto unlock_out; + } + } + /* set standards set by subdev in video device */ + for (i = 0; i < sdinfo->num_inputs; i++) { + inps = &sdinfo->inputs[i]; + video->video_dev.tvnorms |= inps->std; + } + video->current_input = index; +unlock_out: + mutex_unlock(&video->lock); + return ret; +} + +/* + * vpfe_querystd() - query std which is being input on external subdev + * @file: file pointer + * @priv: void pointer + * @std_id: pointer to v4l2_std_id structure + * + * call external subdev through v4l2_device_call_until_err to + * get the std that is being active. + * + * Return 0 on success, error code otherwise + */ +static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querystd\n"); + + ret = mutex_lock_interruptible(&video->lock); + sdinfo = video->current_ext_subdev; + if (ret) + return ret; + /* Call querystd function of decoder device */ + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, querystd, std_id); + mutex_unlock(&video->lock); + return ret; +} + +/* + * vpfe_s_std() - set std on external subdev + * @file: file pointer + * @priv: void pointer + * @std_id: pointer to v4l2_std_id structure + * + * set std pointed by std_id on external subdev by calling it using + * v4l2_device_call_until_err + * + * Return 0 on success, error code otherwise + */ +static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_std\n"); + + /* Call decoder driver function to set the standard */ + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + sdinfo = video->current_ext_subdev; + /* If streaming is started, return device busy error */ + if (video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "streaming is started\n"); + ret = -EBUSY; + goto unlock_out; + } + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, s_std, *std_id); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n"); + video->stdid = V4L2_STD_UNKNOWN; + goto unlock_out; + } + video->stdid = *std_id; +unlock_out: + mutex_unlock(&video->lock); + return ret; +} + +static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_std\n"); + *tvnorm = video->stdid; + return 0; +} + +/* + * vpfe_enum_dv_timings() - enumerate dv_timings which are supported by + * to external subdev + * @file: file pointer + * @priv: void pointer + * @timings: pointer to v4l2_enum_dv_timings structure + * + * enum dv_timings's which are supported by external subdev through + * v4l2_subdev_call + * + * Return 0 on success, error code otherwise + */ +static int +vpfe_enum_dv_timings(struct file *file, void *fh, + struct v4l2_enum_dv_timings *timings) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev *subdev = video->current_ext_subdev->subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_dv_timings\n"); + return v4l2_subdev_call(subdev, video, enum_dv_timings, timings); +} + +/* + * vpfe_query_dv_timings() - query the dv_timings which is being input + * to external subdev + * @file: file pointer + * @priv: void pointer + * @timings: pointer to v4l2_dv_timings structure + * + * get dv_timings which is being input on external subdev through + * v4l2_subdev_call + * + * Return 0 on success, error code otherwise + */ +static int +vpfe_query_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev *subdev = video->current_ext_subdev->subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_query_dv_timings\n"); + return v4l2_subdev_call(subdev, video, query_dv_timings, timings); +} + +/* + * vpfe_s_dv_timings() - set dv_preset on external subdev + * @file: file pointer + * @priv: void pointer + * @timings: pointer to v4l2_dv_timings structure + * + * set dv_timings pointed by preset on external subdev through + * v4l2_device_call_until_err, this configures amplifier also + * + * Return 0 on success, error code otherwise + */ +static int +vpfe_s_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_dv_timings\n"); + + video->stdid = V4L2_STD_UNKNOWN; + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + video->current_ext_subdev->grp_id, + video, s_dv_timings, timings); +} + +/* + * vpfe_g_dv_timings() - get dv_preset which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @timings: pointer to v4l2_dv_timings structure + * + * get dv_preset which is set on external subdev through + * v4l2_subdev_call + * + * Return 0 on success, error code otherwise + */ +static int +vpfe_g_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev *subdev = video->current_ext_subdev->subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_dv_timings\n"); + return v4l2_subdev_call(subdev, video, g_dv_timings, timings); +} + +/* + * Videobuf operations + */ +/* + * vpfe_buffer_queue_setup : Callback function for buffer setup. + * @vq: vb2_queue ptr + * @fmt: v4l2 format + * @nbuffers: ptr to number of buffers requested by application + * @nplanes:: contains number of distinct video planes needed to hold a frame + * @sizes[]: contains the size (in bytes) of each plane. + * @alloc_ctxs: ptr to allocation context + * + * This callback function is called when reqbuf() is called to adjust + * the buffer nbuffers and buffer size + */ +static int +vpfe_buffer_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct vpfe_fh *fh = vb2_get_drv_priv(vq); + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_pipeline *pipe = &video->pipe; + unsigned long size; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_queue_setup\n"); + size = video->fmt.fmt.pix.sizeimage; + + if (vpfe_dev->video_limit) { + while (size * *nbuffers > vpfe_dev->video_limit) + (*nbuffers)--; + } + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) { + if (*nbuffers < MIN_NUM_BUFFERS) + *nbuffers = MIN_NUM_BUFFERS; + } + *nplanes = 1; + sizes[0] = size; + alloc_ctxs[0] = video->alloc_ctx; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "nbuffers=%d, size=%lu\n", *nbuffers, size); + return 0; +} + +/* + * vpfe_buffer_prepare : callback function for buffer prepare + * @vb: ptr to vb2_buffer + * + * This is the callback function for buffer prepare when vb2_qbuf() + * function is called. The buffer is prepared and user space virtual address + * or user address is converted into physical address + */ +static int vpfe_buffer_prepare(struct vb2_buffer *vb) +{ + struct vpfe_fh *fh = vb2_get_drv_priv(vb->vb2_queue); + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n"); + + if (vb->state != VB2_BUF_STATE_ACTIVE && + vb->state != VB2_BUF_STATE_PREPARED) + return 0; + + /* Initialize buffer */ + vb2_set_plane_payload(vb, 0, video->fmt.fmt.pix.sizeimage); + if (vb2_plane_vaddr(vb, 0) && + vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) + return -EINVAL; + + addr = vb2_dma_contig_plane_dma_addr(vb, 0); + /* Make sure user addresses are aligned to 32 bytes */ + if (!ALIGN(addr, 32)) + return -EINVAL; + + return 0; +} + +static void vpfe_buffer_queue(struct vb2_buffer *vb) +{ + /* Get the file handle object and device object */ + struct vpfe_fh *fh = vb2_get_drv_priv(vb->vb2_queue); + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_pipeline *pipe = &video->pipe; + struct vpfe_cap_buffer *buf = container_of(vb, + struct vpfe_cap_buffer, vb); + unsigned long flags; + unsigned long empty; + unsigned long addr; + + spin_lock_irqsave(&video->dma_queue_lock, flags); + empty = list_empty(&video->dma_queue); + /* add the buffer to the DMA queue */ + list_add_tail(&buf->list, &video->dma_queue); + spin_unlock_irqrestore(&video->dma_queue_lock, flags); + /* this case happens in case of single shot */ + if (empty && video->started && pipe->state == + VPFE_PIPELINE_STREAM_SINGLESHOT && + video->state == VPFE_VIDEO_BUFFER_NOT_QUEUED) { + spin_lock(&video->dma_queue_lock); + addr = vpfe_video_get_next_buffer(video); + video->ops->queue(vpfe_dev, addr); + + video->state = VPFE_VIDEO_BUFFER_QUEUED; + spin_unlock(&video->dma_queue_lock); + + /* enable h/w each time in single shot */ + if (vpfe_video_is_pipe_ready(pipe)) + vpfe_pipeline_set_stream(pipe, + VPFE_PIPELINE_STREAM_SINGLESHOT); + } +} + +/* vpfe_start_capture() - start streaming on all the subdevs */ +static int vpfe_start_capture(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + int ret = 0; + + video->started = 1; + if (vpfe_video_is_pipe_ready(pipe)) + ret = vpfe_pipeline_set_stream(pipe, pipe->state); + + return ret; +} + +static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct vpfe_fh *fh = vb2_get_drv_priv(vq); + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + int ret; + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + goto streamoff; + + /* Get the next frame from the buffer queue */ + video->cur_frm = video->next_frm = + list_entry(video->dma_queue.next, struct vpfe_cap_buffer, list); + /* Remove buffer from the buffer queue */ + list_del(&video->cur_frm->list); + /* Mark state of the current frame to active */ + video->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; + /* Initialize field_id and started member */ + video->field_id = 0; + addr = vb2_dma_contig_plane_dma_addr(&video->cur_frm->vb, 0); + video->ops->queue(vpfe_dev, addr); + video->state = VPFE_VIDEO_BUFFER_QUEUED; + + ret = vpfe_start_capture(video); + if (ret) + goto unlock_out; + + mutex_unlock(&video->lock); + + return ret; +unlock_out: + mutex_unlock(&video->lock); +streamoff: + ret = vb2_streamoff(&video->buffer_queue, video->buffer_queue.type); + return 0; +} + +static int vpfe_buffer_init(struct vb2_buffer *vb) +{ + struct vpfe_cap_buffer *buf = container_of(vb, + struct vpfe_cap_buffer, vb); + + INIT_LIST_HEAD(&buf->list); + return 0; +} + +/* abort streaming and wait for last buffer */ +static int vpfe_stop_streaming(struct vb2_queue *vq) +{ + struct vpfe_fh *fh = vb2_get_drv_priv(vq); + struct vpfe_video_device *video = fh->video; + + if (!vb2_is_streaming(vq)) + return 0; + /* release all active buffers */ + while (!list_empty(&video->dma_queue)) { + video->next_frm = list_entry(video->dma_queue.next, + struct vpfe_cap_buffer, list); + list_del(&video->next_frm->list); + vb2_buffer_done(&video->next_frm->vb, VB2_BUF_STATE_ERROR); + } + return 0; +} + +static void vpfe_buf_cleanup(struct vb2_buffer *vb) +{ + struct vpfe_fh *fh = vb2_get_drv_priv(vb->vb2_queue); + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_cap_buffer *buf = container_of(vb, + struct vpfe_cap_buffer, vb); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buf_cleanup\n"); + if (vb->state == VB2_BUF_STATE_ACTIVE) + list_del_init(&buf->list); +} + +static struct vb2_ops video_qops = { + .queue_setup = vpfe_buffer_queue_setup, + .buf_init = vpfe_buffer_init, + .buf_prepare = vpfe_buffer_prepare, + .start_streaming = vpfe_start_streaming, + .stop_streaming = vpfe_stop_streaming, + .buf_cleanup = vpfe_buf_cleanup, + .buf_queue = vpfe_buffer_queue, +}; + +/* + * vpfe_reqbufs() - supported REQBUF only once opening + * the device. + */ +static int vpfe_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req_buf) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + struct vb2_queue *q; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != req_buf->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + + if (video->io_usrs != 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Only one IO user allowed\n"); + ret = -EBUSY; + goto unlock_out; + } + video->memory = req_buf->memory; + + /* Initialize videobuf2 queue as per the buffer type */ + video->alloc_ctx = vb2_dma_contig_init_ctx(vpfe_dev->pdev); + if (IS_ERR(video->alloc_ctx)) { + v4l2_err(&vpfe_dev->v4l2_dev, "Failed to get the context\n"); + return PTR_ERR(video->alloc_ctx); + } + + q = &video->buffer_queue; + q->type = req_buf->type; + q->io_modes = VB2_MMAP | VB2_USERPTR; + q->drv_priv = fh; + q->ops = &video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct vpfe_cap_buffer); + + ret = vb2_queue_init(q); + if (ret) { + v4l2_err(&vpfe_dev->v4l2_dev, "vb2_queue_init() failed\n"); + vb2_dma_contig_cleanup_ctx(vpfe_dev->pdev); + return ret; + } + + fh->io_allowed = 1; + video->io_usrs = 1; + INIT_LIST_HEAD(&video->dma_queue); + ret = vb2_reqbufs(&video->buffer_queue, req_buf); + +unlock_out: + mutex_unlock(&video->lock); + return ret; +} + +/* + * vpfe_querybuf() - query buffers for exchange + */ +static int vpfe_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querybuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + if (video->memory != V4L2_MEMORY_MMAP) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid memory\n"); + return -EINVAL; + } + + /* Call vb2_querybuf to get information */ + return vb2_querybuf(&video->buffer_queue, buf); +} + +/* + * vpfe_qbuf() - queue buffers for capture or processing + */ +static int vpfe_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_qbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != p->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != p->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + /* + * If this file handle is not allowed to do IO, + * return error + */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + return vb2_qbuf(&video->buffer_queue, p); +} + +/* + * vpfe_dqbuf() - deque buffer which is done with processing + */ +static int vpfe_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_dqbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + return vb2_dqbuf(&video->buffer_queue, + buf, (file->f_flags & O_NONBLOCK)); +} + +/* + * vpfe_streamon() - get dv_preset which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @buf_type: enum v4l2_buf_type + * + * queue buffer onto hardware for capture/processing and + * start all the subdevs which are in media chain + * + * Return 0 on success, error code otherwise + */ +static int vpfe_streamon(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_pipeline *pipe = &video->pipe; + struct vpfe_fh *fh = file->private_data; + struct vpfe_ext_subdev_info *sdinfo; + int ret = -EINVAL; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamon\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return ret; + } + /* If file handle is not allowed IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + sdinfo = video->current_ext_subdev; + /* If buffer queue is empty, return error */ + if (list_empty(&video->buffer_queue.queued_list)) { + v4l2_err(&vpfe_dev->v4l2_dev, "buffer queue is empty\n"); + return -EIO; + } + /* Validate the pipeline */ + if (V4L2_BUF_TYPE_VIDEO_CAPTURE == buf_type) { + ret = vpfe_video_validate_pipeline(pipe); + if (ret < 0) + return ret; + } + /* Call vb2_streamon to start streaming */ + return vb2_streamon(&video->buffer_queue, buf_type); +} + +/* + * vpfe_streamoff() - get dv_preset which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @buf_type: enum v4l2_buf_type + * + * stop all the subdevs which are in media chain + * + * Return 0 on success, error code otherwise + */ +static int vpfe_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamoff\n"); + + if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + buf_type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* If io is allowed for this file handle, return error */ + if (!fh->io_allowed) { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + /* If streaming is not started, return error */ + if (!video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "device is not started\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + + vpfe_stop_capture(video); + ret = vb2_streamoff(&video->buffer_queue, buf_type); + mutex_unlock(&video->lock); + + return ret; +} + +/* vpfe capture ioctl operations */ +static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { + .vidioc_querycap = vpfe_querycap, + .vidioc_g_fmt_vid_cap = vpfe_g_fmt, + .vidioc_s_fmt_vid_cap = vpfe_s_fmt, + .vidioc_try_fmt_vid_cap = vpfe_try_fmt, + .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt, + .vidioc_g_fmt_vid_out = vpfe_g_fmt, + .vidioc_s_fmt_vid_out = vpfe_s_fmt, + .vidioc_try_fmt_vid_out = vpfe_try_fmt, + .vidioc_enum_fmt_vid_out = vpfe_enum_fmt, + .vidioc_enum_input = vpfe_enum_input, + .vidioc_g_input = vpfe_g_input, + .vidioc_s_input = vpfe_s_input, + .vidioc_querystd = vpfe_querystd, + .vidioc_s_std = vpfe_s_std, + .vidioc_g_std = vpfe_g_std, + .vidioc_enum_dv_timings = vpfe_enum_dv_timings, + .vidioc_query_dv_timings = vpfe_query_dv_timings, + .vidioc_s_dv_timings = vpfe_s_dv_timings, + .vidioc_g_dv_timings = vpfe_g_dv_timings, + .vidioc_reqbufs = vpfe_reqbufs, + .vidioc_querybuf = vpfe_querybuf, + .vidioc_qbuf = vpfe_qbuf, + .vidioc_dqbuf = vpfe_dqbuf, + .vidioc_streamon = vpfe_streamon, + .vidioc_streamoff = vpfe_streamoff, +}; + +/* VPFE video init function */ +int vpfe_video_init(struct vpfe_video_device *video, const char *name) +{ + const char *direction; + int ret; + + switch (video->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + direction = "output"; + video->pad.flags = MEDIA_PAD_FL_SINK; + video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + direction = "input"; + video->pad.flags = MEDIA_PAD_FL_SOURCE; + video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + break; + + default: + return -EINVAL; + } + /* Initialize field of video device */ + video->video_dev.release = video_device_release; + video->video_dev.fops = &vpfe_fops; + video->video_dev.ioctl_ops = &vpfe_ioctl_ops; + video->video_dev.minor = -1; + video->video_dev.tvnorms = 0; + snprintf(video->video_dev.name, sizeof(video->video_dev.name), + "DAVINCI VIDEO %s %s", name, direction); + + /* Initialize prio member of device object */ + v4l2_prio_init(&video->prio); + spin_lock_init(&video->irqlock); + spin_lock_init(&video->dma_queue_lock); + mutex_init(&video->lock); + ret = media_entity_init(&video->video_dev.entity, + 1, &video->pad, 0); + if (ret < 0) + return ret; + + video_set_drvdata(&video->video_dev, video); + + return 0; +} + +/* vpfe video device register function */ +int vpfe_video_register(struct vpfe_video_device *video, + struct v4l2_device *vdev) +{ + int ret; + + video->video_dev.v4l2_dev = vdev; + + ret = video_register_device(&video->video_dev, VFL_TYPE_GRABBER, -1); + if (ret < 0) + pr_err("%s: could not register video device (%d)\n", + __func__, ret); + return ret; +} + +/* vpfe video device unregister function */ +void vpfe_video_unregister(struct vpfe_video_device *video) +{ + if (video_is_registered(&video->video_dev)) { + media_entity_cleanup(&video->video_dev.entity); + video_unregister_device(&video->video_dev); + } +} diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.h b/drivers/staging/media/davinci_vpfe/vpfe_video.h new file mode 100644 index 000000000000..bf8af01d4a1b --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _DAVINCI_VPFE_VIDEO_H +#define _DAVINCI_VPFE_VIDEO_H + +#include + +struct vpfe_device; + +/* + * struct vpfe_video_operations - VPFE video operations + * @queue: Resume streaming when a buffer is queued. Called on VIDIOC_QBUF + * if there was no buffer previously queued. + */ +struct vpfe_video_operations { + int(*queue) (struct vpfe_device *vpfe_dev, unsigned long addr); +}; + +enum vpfe_pipeline_stream_state { + VPFE_PIPELINE_STREAM_STOPPED = 0, + VPFE_PIPELINE_STREAM_CONTINUOUS = 1, + VPFE_PIPELINE_STREAM_SINGLESHOT = 2, +}; + +enum vpfe_video_state { + /* indicates that buffer is not queued */ + VPFE_VIDEO_BUFFER_NOT_QUEUED = 0, + /* indicates that buffer is queued */ + VPFE_VIDEO_BUFFER_QUEUED = 1, +}; + +struct vpfe_pipeline { + /* media pipeline */ + struct media_pipeline *pipe; + /* state of the pipeline, continuous, + * single-shot or stopped + */ + enum vpfe_pipeline_stream_state state; + /* number of active input video entities */ + unsigned int input_num; + /* number of active output video entities */ + unsigned int output_num; + /* input video nodes in case of single-shot mode */ + struct vpfe_video_device *inputs[10]; + /* capturing video nodes */ + struct vpfe_video_device *outputs[10]; +}; + +#define to_vpfe_pipeline(__e) \ + container_of((__e)->pipe, struct vpfe_pipeline, pipe) + +#define to_vpfe_video(vdev) \ + container_of(vdev, struct vpfe_video_device, video_dev) + +struct vpfe_cap_buffer { + struct vb2_buffer vb; + struct list_head list; +}; + +struct vpfe_video_device { + /* vpfe device */ + struct vpfe_device *vpfe_dev; + /* video dev */ + struct video_device video_dev; + /* media pad of video entity */ + struct media_pad pad; + /* video operations supported by video device */ + const struct vpfe_video_operations *ops; + /* type of the video buffers used by user */ + enum v4l2_buf_type type; + /* Indicates id of the field which is being captured */ + u32 field_id; + /* pipeline for which video device is part of */ + struct vpfe_pipeline pipe; + /* Indicates whether streaming started */ + u8 started; + /* Indicates state of the stream */ + unsigned int state; + /* current input at the sub device */ + int current_input; + /* + * This field keeps track of type of buffer exchange mechanism + * user has selected + */ + enum v4l2_memory memory; + /* Used to keep track of state of the priority */ + struct v4l2_prio_state prio; + /* number of open instances of the channel */ + u32 usrs; + /* flag to indicate whether decoder is initialized */ + u8 initialized; + /* skip frame count */ + u8 skip_frame_count; + /* skip frame count init value */ + u8 skip_frame_count_init; + /* time per frame for skipping */ + struct v4l2_fract timeperframe; + /* ptr to currently selected sub device */ + struct vpfe_ext_subdev_info *current_ext_subdev; + /* Pointer pointing to current vpfe_cap_buffer */ + struct vpfe_cap_buffer *cur_frm; + /* Pointer pointing to next vpfe_cap_buffer */ + struct vpfe_cap_buffer *next_frm; + /* Used to store pixel format */ + struct v4l2_format fmt; + struct vb2_queue buffer_queue; + /* allocator-specific contexts for each plane */ + struct vb2_alloc_ctx *alloc_ctx; + /* Queue of filled frames */ + struct list_head dma_queue; + spinlock_t irqlock; + /* IRQ lock for DMA queue */ + spinlock_t dma_queue_lock; + /* lock used to access this structure */ + struct mutex lock; + /* number of users performing IO */ + u32 io_usrs; + /* Currently selected or default standard */ + v4l2_std_id stdid; + /* + * offset where second field starts from the starting of the + * buffer for field seperated YCbCr formats + */ + u32 field_off; +}; + +int vpfe_video_is_pipe_ready(struct vpfe_pipeline *pipe); +void vpfe_video_unregister(struct vpfe_video_device *video); +int vpfe_video_register(struct vpfe_video_device *video, + struct v4l2_device *vdev); +int vpfe_video_init(struct vpfe_video_device *video, const char *name); +void vpfe_video_process_buffer_complete(struct vpfe_video_device *video); +void vpfe_video_schedule_bottom_field(struct vpfe_video_device *video); +void vpfe_video_schedule_next_buffer(struct vpfe_video_device *video); + +#endif /* _DAVINCI_VPFE_VIDEO_H */ -- cgit v1.2.3 From f7fa454dc1771155a2ceaf490e1dec9d4c2613bd Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Wed, 28 Nov 2012 02:06:17 -0300 Subject: [media] davinci: vpfe: dm365: add IPIPEIF driver based on media framework add support for dm365 IPIPEIF driver based on media framework. The IPIPEIF is exposed as a subdev, and it supports features like fault pixel correction, dark frame subtraction and other necessary hardware setup. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/davinci_vpfe/dm365_ipipeif.c | 1071 ++++++++++++++++++++ drivers/staging/media/davinci_vpfe/dm365_ipipeif.h | 233 +++++ .../media/davinci_vpfe/dm365_ipipeif_user.h | 93 ++ 3 files changed, 1397 insertions(+) create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipeif.c create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipeif.h create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h (limited to 'drivers') diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c new file mode 100644 index 000000000000..c8cae51d3d00 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c @@ -0,0 +1,1071 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#include "dm365_ipipeif.h" +#include "vpfe_mc_capture.h" + +static const unsigned int ipipeif_input_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_SGRBG12_1X12, + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_UV8_1X8, + V4L2_MBUS_FMT_YDYUYDYV8_1X16, + V4L2_MBUS_FMT_SBGGR8_1X8, +}; + +static const unsigned int ipipeif_output_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_SGRBG12_1X12, + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_UV8_1X8, + V4L2_MBUS_FMT_YDYUYDYV8_1X16, + V4L2_MBUS_FMT_SBGGR8_1X8, + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, + V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8, +}; + +static int +ipipeif_get_pack_mode(enum v4l2_mbus_pixelcode in_pix_fmt) +{ + switch (in_pix_fmt) { + case V4L2_MBUS_FMT_SBGGR8_1X8: + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_UV8_1X8: + return IPIPEIF_5_1_PACK_8_BIT; + + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + return IPIPEIF_5_1_PACK_8_BIT_A_LAW; + + case V4L2_MBUS_FMT_SGRBG12_1X12: + return IPIPEIF_5_1_PACK_16_BIT; + + case V4L2_MBUS_FMT_SBGGR12_1X12: + return IPIPEIF_5_1_PACK_12_BIT; + + default: + return IPIPEIF_5_1_PACK_16_BIT; + } +} + +static inline u32 ipipeif_read(void *addr, u32 offset) +{ + return readl(addr + offset); +} + +static inline void ipipeif_write(u32 val, void *addr, u32 offset) +{ + writel(val, addr + offset); +} + +static void ipipeif_config_dpc(void *addr, struct ipipeif_dpc *dpc) +{ + u32 val = 0; + + if (dpc->en) { + val = (dpc->en & 1) << IPIPEIF_DPC2_EN_SHIFT; + val |= dpc->thr & IPIPEIF_DPC2_THR_MASK; + } + ipipeif_write(val, addr, IPIPEIF_DPC2); +} + +#define IPIPEIF_MODE_CONTINUOUS 0 +#define IPIPEIF_MODE_ONE_SHOT 1 + +static int get_oneshot_mode(enum ipipeif_input_entity input) +{ + if (input == IPIPEIF_INPUT_MEMORY) + return IPIPEIF_MODE_ONE_SHOT; + else if (input == IPIPEIF_INPUT_ISIF) + return IPIPEIF_MODE_CONTINUOUS; + + return -EINVAL; +} + +static int +ipipeif_get_cfg_src1(struct vpfe_ipipeif_device *ipipeif) +{ + struct v4l2_mbus_framefmt *informat; + + informat = &ipipeif->formats[IPIPEIF_PAD_SINK]; + if (ipipeif->input == IPIPEIF_INPUT_MEMORY && + (informat->code == V4L2_MBUS_FMT_Y8_1X8 || + informat->code == V4L2_MBUS_FMT_UV8_1X8)) + return IPIPEIF_CCDC; + + return IPIPEIF_SRC1_PARALLEL_PORT; +} + +static int +ipipeif_get_data_shift(struct vpfe_ipipeif_device *ipipeif) +{ + struct v4l2_mbus_framefmt *informat; + + informat = &ipipeif->formats[IPIPEIF_PAD_SINK]; + + switch (informat->code) { + case V4L2_MBUS_FMT_SGRBG12_1X12: + return IPIPEIF_5_1_BITS11_0; + + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_UV8_1X8: + return IPIPEIF_5_1_BITS11_0; + + default: + return IPIPEIF_5_1_BITS7_0; + } +} + +static enum ipipeif_input_source +ipipeif_get_source(struct vpfe_ipipeif_device *ipipeif) +{ + struct v4l2_mbus_framefmt *informat; + + informat = &ipipeif->formats[IPIPEIF_PAD_SINK]; + if (ipipeif->input == IPIPEIF_INPUT_ISIF) + return IPIPEIF_CCDC; + + if (informat->code == V4L2_MBUS_FMT_UYVY8_2X8) + return IPIPEIF_SDRAM_YUV; + + return IPIPEIF_SDRAM_RAW; +} + +void vpfe_ipipeif_ss_buffer_isr(struct vpfe_ipipeif_device *ipipeif) +{ + struct vpfe_video_device *video_in = &ipipeif->video_in; + + if (ipipeif->input != IPIPEIF_INPUT_MEMORY) + return; + + spin_lock(&video_in->dma_queue_lock); + vpfe_video_process_buffer_complete(video_in); + video_in->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; + vpfe_video_schedule_next_buffer(video_in); + spin_unlock(&video_in->dma_queue_lock); +} + +int vpfe_ipipeif_decimation_enabled(struct vpfe_device *vpfe_dev) +{ + struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif; + + return ipipeif->config.decimation; +} + +int vpfe_ipipeif_get_rsz(struct vpfe_device *vpfe_dev) +{ + struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif; + + return ipipeif->config.rsz; +} + +#define RD_DATA_15_2 0x7 + +/* + * ipipeif_hw_setup() - This function sets up IPIPEIF + * @sd: pointer to v4l2 subdev structure + * return -EINVAL or zero on success + */ +static int ipipeif_hw_setup(struct v4l2_subdev *sd) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *informat, *outformat; + struct ipipeif_params params = ipipeif->config; + enum ipipeif_input_source ipipeif_source; + enum v4l2_mbus_pixelcode isif_port_if; + void *ipipeif_base_addr; + unsigned int val; + int data_shift; + int pack_mode; + int source1; + + ipipeif_base_addr = ipipeif->ipipeif_base_addr; + + /* Enable clock to IPIPEIF and IPIPE */ + vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1); + + informat = &ipipeif->formats[IPIPEIF_PAD_SINK]; + outformat = &ipipeif->formats[IPIPEIF_PAD_SOURCE]; + + /* Combine all the fields to make CFG1 register of IPIPEIF */ + val = get_oneshot_mode(ipipeif->input); + if (val < 0) { + pr_err("ipipeif: links setup required"); + return -EINVAL; + } + val = val << ONESHOT_SHIFT; + + ipipeif_source = ipipeif_get_source(ipipeif); + val |= ipipeif_source << INPSRC_SHIFT; + + val |= params.clock_select << CLKSEL_SHIFT; + val |= params.avg_filter << AVGFILT_SHIFT; + val |= params.decimation << DECIM_SHIFT; + + pack_mode = ipipeif_get_pack_mode(informat->code); + val |= pack_mode << PACK8IN_SHIFT; + + source1 = ipipeif_get_cfg_src1(ipipeif); + val |= source1 << INPSRC1_SHIFT; + + data_shift = ipipeif_get_data_shift(ipipeif); + if (ipipeif_source != IPIPEIF_SDRAM_YUV) + val |= data_shift << DATASFT_SHIFT; + else + val &= ~(RD_DATA_15_2 << DATASFT_SHIFT); + + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG1); + + switch (ipipeif_source) { + case IPIPEIF_CCDC: + ipipeif_write(ipipeif->gain, ipipeif_base_addr, IPIPEIF_GAIN); + break; + + case IPIPEIF_SDRAM_RAW: + case IPIPEIF_CCDC_DARKFM: + ipipeif_write(ipipeif->gain, ipipeif_base_addr, IPIPEIF_GAIN); + /* fall through */ + case IPIPEIF_SDRAM_YUV: + val |= data_shift << DATASFT_SHIFT; + ipipeif_write(params.ppln, ipipeif_base_addr, IPIPEIF_PPLN); + ipipeif_write(params.lpfr, ipipeif_base_addr, IPIPEIF_LPFR); + ipipeif_write(informat->width, ipipeif_base_addr, IPIPEIF_HNUM); + ipipeif_write(informat->height, + ipipeif_base_addr, IPIPEIF_VNUM); + break; + + default: + return -EINVAL; + } + + /*check if decimation is enable or not */ + if (params.decimation) + ipipeif_write(params.rsz, ipipeif_base_addr, IPIPEIF_RSZ); + + /* Setup sync alignment and initial rsz position */ + val = params.if_5_1.align_sync & 1; + val <<= IPIPEIF_INIRSZ_ALNSYNC_SHIFT; + val |= params.if_5_1.rsz_start & IPIPEIF_INIRSZ_MASK; + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_INIRSZ); + isif_port_if = informat->code; + + if (isif_port_if == V4L2_MBUS_FMT_Y8_1X8) + isif_port_if = V4L2_MBUS_FMT_YUYV8_1X16; + else if (isif_port_if == V4L2_MBUS_FMT_UV8_1X8) + isif_port_if = V4L2_MBUS_FMT_SGRBG12_1X12; + + /* Enable DPCM decompression */ + switch (ipipeif_source) { + case IPIPEIF_SDRAM_RAW: + val = 0; + if (outformat->code == V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8) { + val = 1; + val |= (IPIPEIF_DPCM_8BIT_10BIT & 1) << + IPIPEIF_DPCM_BITS_SHIFT; + val |= (ipipeif->dpcm_predictor & 1) << + IPIPEIF_DPCM_PRED_SHIFT; + } + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_DPCM); + + /* set DPC */ + ipipeif_config_dpc(ipipeif_base_addr, ¶ms.if_5_1.dpc); + + ipipeif_write(params.if_5_1.clip, + ipipeif_base_addr, IPIPEIF_OCLIP); + + /* fall through for SDRAM YUV mode */ + /* configure CFG2 */ + val = ipipeif_read(ipipeif_base_addr, IPIPEIF_CFG2); + switch (isif_port_if) { + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_Y8_1X8: + RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG2); + break; + + default: + RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + RESETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG2); + break; + } + + case IPIPEIF_SDRAM_YUV: + /* Set clock divider */ + if (params.clock_select == IPIPEIF_SDRAM_CLK) { + val = ipipeif_read(ipipeif_base_addr, IPIPEIF_CLKDIV); + val |= (params.if_5_1.clk_div.m - 1) << + IPIPEIF_CLKDIV_M_SHIFT; + val |= (params.if_5_1.clk_div.n - 1); + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CLKDIV); + } + break; + + case IPIPEIF_CCDC: + case IPIPEIF_CCDC_DARKFM: + /* set DPC */ + ipipeif_config_dpc(ipipeif_base_addr, ¶ms.if_5_1.dpc); + + /* Set DF gain & threshold control */ + val = 0; + if (params.if_5_1.df_gain_en) { + val = params.if_5_1.df_gain_thr & + IPIPEIF_DF_GAIN_THR_MASK; + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_DFSGTH); + val = (params.if_5_1.df_gain_en & 1) << + IPIPEIF_DF_GAIN_EN_SHIFT; + val |= params.if_5_1.df_gain & + IPIPEIF_DF_GAIN_MASK; + } + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_DFSGVL); + /* configure CFG2 */ + val = VPFE_PINPOL_POSITIVE << IPIPEIF_CFG2_HDPOL_SHIFT; + val |= VPFE_PINPOL_POSITIVE << IPIPEIF_CFG2_VDPOL_SHIFT; + + switch (isif_port_if) { + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_YUYV10_1X20: + RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + break; + + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_YUYV10_2X10: + SETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + val |= IPIPEIF_CBCR_Y << IPIPEIF_CFG2_YUV8P_SHIFT; + break; + + default: + /* Bayer */ + ipipeif_write(params.if_5_1.clip, ipipeif_base_addr, + IPIPEIF_OCLIP); + } + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG2); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int +ipipeif_set_config(struct v4l2_subdev *sd, struct ipipeif_params *config) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct device *dev = ipipeif->subdev.v4l2_dev->dev; + + if (!config) { + dev_err(dev, "Invalid configuration pointer\n"); + return -EINVAL; + } + + ipipeif->config.clock_select = config->clock_select; + ipipeif->config.ppln = config->ppln; + ipipeif->config.lpfr = config->lpfr; + ipipeif->config.rsz = config->rsz; + ipipeif->config.decimation = config->decimation; + if (ipipeif->config.decimation && + (ipipeif->config.rsz < IPIPEIF_RSZ_MIN || + ipipeif->config.rsz > IPIPEIF_RSZ_MAX)) { + dev_err(dev, "rsz range is %d to %d\n", + IPIPEIF_RSZ_MIN, IPIPEIF_RSZ_MAX); + return -EINVAL; + } + + ipipeif->config.avg_filter = config->avg_filter; + + ipipeif->config.if_5_1.df_gain_thr = config->if_5_1.df_gain_thr; + ipipeif->config.if_5_1.df_gain = config->if_5_1.df_gain; + ipipeif->config.if_5_1.df_gain_en = config->if_5_1.df_gain_en; + + ipipeif->config.if_5_1.rsz_start = config->if_5_1.rsz_start; + ipipeif->config.if_5_1.align_sync = config->if_5_1.align_sync; + ipipeif->config.if_5_1.clip = config->if_5_1.clip; + + ipipeif->config.if_5_1.dpc.en = config->if_5_1.dpc.en; + ipipeif->config.if_5_1.dpc.thr = config->if_5_1.dpc.thr; + + ipipeif->config.if_5_1.clk_div.m = config->if_5_1.clk_div.m; + ipipeif->config.if_5_1.clk_div.n = config->if_5_1.clk_div.n; + + return 0; +} + +static int +ipipeif_get_config(struct v4l2_subdev *sd, void __user *arg) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct ipipeif_params *config = (struct ipipeif_params *)arg; + struct device *dev = ipipeif->subdev.v4l2_dev->dev; + + if (!arg) { + dev_err(dev, "Invalid configuration pointer\n"); + return -EINVAL; + } + + config->clock_select = ipipeif->config.clock_select; + config->ppln = ipipeif->config.ppln; + config->lpfr = ipipeif->config.lpfr; + config->rsz = ipipeif->config.rsz; + config->decimation = ipipeif->config.decimation; + config->avg_filter = ipipeif->config.avg_filter; + + config->if_5_1.df_gain_thr = ipipeif->config.if_5_1.df_gain_thr; + config->if_5_1.df_gain = ipipeif->config.if_5_1.df_gain; + config->if_5_1.df_gain_en = ipipeif->config.if_5_1.df_gain_en; + + config->if_5_1.rsz_start = ipipeif->config.if_5_1.rsz_start; + config->if_5_1.align_sync = ipipeif->config.if_5_1.align_sync; + config->if_5_1.clip = ipipeif->config.if_5_1.clip; + + config->if_5_1.dpc.en = ipipeif->config.if_5_1.dpc.en; + config->if_5_1.dpc.thr = ipipeif->config.if_5_1.dpc.thr; + + config->if_5_1.clk_div.m = ipipeif->config.if_5_1.clk_div.m; + config->if_5_1.clk_div.n = ipipeif->config.if_5_1.clk_div.n; + + return 0; +} + +/* + * ipipeif_ioctl() - Handle ipipeif module private ioctl's + * @sd: pointer to v4l2 subdev structure + * @cmd: configuration command + * @arg: configuration argument + */ +static long ipipeif_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + struct ipipeif_params *config = (struct ipipeif_params *)arg; + int ret = -ENOIOCTLCMD; + + switch (cmd) { + case VIDIOC_VPFE_IPIPEIF_S_CONFIG: + ret = ipipeif_set_config(sd, config); + break; + + case VIDIOC_VPFE_IPIPEIF_G_CONFIG: + ret = ipipeif_get_config(sd, arg); + break; + } + return ret; +} + +/* + * ipipeif_s_ctrl() - Handle set control subdev method + * @ctrl: pointer to v4l2 control structure + */ +static int ipipeif_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vpfe_ipipeif_device *ipipeif = + container_of(ctrl->handler, struct vpfe_ipipeif_device, ctrls); + + switch (ctrl->id) { + case VPFE_CID_DPCM_PREDICTOR: + ipipeif->dpcm_predictor = ctrl->val; + break; + + case V4L2_CID_GAIN: + ipipeif->gain = ctrl->val; + break; + + default: + return -EINVAL; + } + + return 0; +} + +#define ENABLE_IPIPEIF 0x1 + +void vpfe_ipipeif_enable(struct vpfe_device *vpfe_dev) +{ + struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif; + void *ipipeif_base_addr = ipipeif->ipipeif_base_addr; + unsigned char val; + + if (ipipeif->input != IPIPEIF_INPUT_MEMORY) + return; + + do { + val = ipipeif_read(ipipeif_base_addr, IPIPEIF_ENABLE); + } while (val & 0x1); + + ipipeif_write(ENABLE_IPIPEIF, ipipeif_base_addr, IPIPEIF_ENABLE); +} + +/* + * ipipeif_set_stream() - Enable/Disable streaming on ipipeif subdev + * @sd: pointer to v4l2 subdev structure + * @enable: 1 == Enable, 0 == Disable + */ +static int ipipeif_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(ipipeif); + int ret = 0; + + if (!enable) + return ret; + + ret = ipipeif_hw_setup(sd); + if (!ret) + vpfe_ipipeif_enable(vpfe_dev); + + return ret; +} + +/* + * ipipeif_enum_mbus_code() - Handle pixel format enumeration + * @sd: pointer to v4l2 subdev structure + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * return -EINVAL or zero on success + */ +static int ipipeif_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + switch (code->pad) { + case IPIPEIF_PAD_SINK: + if (code->index >= ARRAY_SIZE(ipipeif_input_fmts)) + return -EINVAL; + + code->code = ipipeif_input_fmts[code->index]; + break; + + case IPIPEIF_PAD_SOURCE: + if (code->index >= ARRAY_SIZE(ipipeif_output_fmts)) + return -EINVAL; + + code->code = ipipeif_output_fmts[code->index]; + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * ipipeif_get_format() - Handle get format by pads subdev method + * @sd: pointer to v4l2 subdev structure + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + */ +static int +ipipeif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + fmt->format = ipipeif->formats[fmt->pad]; + else + fmt->format = *(v4l2_subdev_get_try_format(fh, fmt->pad)); + + return 0; +} + +#define MIN_OUT_WIDTH 32 +#define MIN_OUT_HEIGHT 32 + +/* + * ipipeif_try_format() - Handle try format by pad subdev method + * @ipipeif: VPFE ipipeif device. + * @fh: V4L2 subdev file handle. + * @pad: pad num. + * @fmt: pointer to v4l2 format structure. + * @which : wanted subdev format + */ +static void +ipipeif_try_format(struct vpfe_ipipeif_device *ipipeif, + struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + unsigned int max_out_height; + unsigned int max_out_width; + unsigned int i; + + max_out_width = IPIPE_MAX_OUTPUT_WIDTH_A; + max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_A; + + if (pad == IPIPEIF_PAD_SINK) { + for (i = 0; i < ARRAY_SIZE(ipipeif_input_fmts); i++) + if (fmt->code == ipipeif_input_fmts[i]) + break; + + /* If not found, use SBGGR10 as default */ + if (i >= ARRAY_SIZE(ipipeif_input_fmts)) + fmt->code = V4L2_MBUS_FMT_SGRBG12_1X12; + } else if (pad == IPIPEIF_PAD_SOURCE) { + for (i = 0; i < ARRAY_SIZE(ipipeif_output_fmts); i++) + if (fmt->code == ipipeif_output_fmts[i]) + break; + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(ipipeif_output_fmts)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + } + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_HEIGHT, max_out_width); + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_WIDTH, max_out_height); +} + +static int +ipipeif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + ipipeif_try_format(ipipeif, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + ipipeif_try_format(ipipeif, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * __ipipeif_get_format() - helper function for getting ipipeif format + * @ipipeif: pointer to ipipeif private structure. + * @pad: pad number. + * @fh: V4L2 subdev file handle. + * @which: wanted subdev format. + * + */ +static struct v4l2_mbus_framefmt * +__ipipeif_get_format(struct vpfe_ipipeif_device *ipipeif, + struct v4l2_subdev_fh *fh, unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + + return &ipipeif->formats[pad]; +} + +/* + * ipipeif_set_format() - Handle set format by pads subdev method + * @sd: pointer to v4l2 subdev structure + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int +ipipeif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __ipipeif_get_format(ipipeif, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + ipipeif_try_format(ipipeif, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (fmt->pad == IPIPEIF_PAD_SINK && + ipipeif->input != IPIPEIF_INPUT_NONE) + ipipeif->formats[fmt->pad] = fmt->format; + else if (fmt->pad == IPIPEIF_PAD_SOURCE && + ipipeif->output != IPIPEIF_OUTPUT_NONE) + ipipeif->formats[fmt->pad] = fmt->format; + else + return -EINVAL; + + return 0; +} + +static void ipipeif_set_default_config(struct vpfe_ipipeif_device *ipipeif) +{ +#define WIDTH_I 640 +#define HEIGHT_I 480 + + const struct ipipeif_params ipipeif_defaults = { + .clock_select = IPIPEIF_SDRAM_CLK, + .ppln = WIDTH_I + 8, + .lpfr = HEIGHT_I + 10, + .rsz = 16, /* resize ratio 16/rsz */ + .decimation = IPIPEIF_DECIMATION_OFF, + .avg_filter = IPIPEIF_AVG_OFF, + .if_5_1 = { + .clk_div = { + .m = 1, /* clock = sdram clock * (m/n) */ + .n = 6 + }, + .clip = 4095, + }, + }; + memset(&ipipeif->config, 0, sizeof(struct ipipeif_params)); + memcpy(&ipipeif->config, &ipipeif_defaults, + sizeof(struct ipipeif_params)); +} + +/* + * ipipeif_init_formats() - Initialize formats on all pads + * @sd: VPFE ipipeif V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int +ipipeif_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + format.pad = IPIPEIF_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG12_1X12; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; + ipipeif_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = IPIPEIF_PAD_SOURCE; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; + ipipeif_set_format(sd, fh, &format); + + ipipeif_set_default_config(ipipeif); + + return 0; +} + +/* + * ipipeif_video_in_queue() - ipipeif video in queue + * @vpfe_dev: vpfe device pointer + * @addr: buffer address + */ +static int +ipipeif_video_in_queue(struct vpfe_device *vpfe_dev, unsigned long addr) +{ + struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif; + void *ipipeif_base_addr = ipipeif->ipipeif_base_addr; + unsigned int adofs; + u32 val; + + if (ipipeif->input != IPIPEIF_INPUT_MEMORY) + return -EINVAL; + + switch (ipipeif->formats[IPIPEIF_PAD_SINK].code) { + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_UV8_1X8: + case V4L2_MBUS_FMT_YDYUYDYV8_1X16: + adofs = ipipeif->formats[IPIPEIF_PAD_SINK].width; + break; + + default: + adofs = ipipeif->formats[IPIPEIF_PAD_SINK].width << 1; + break; + } + + /* adjust the line len to be a multiple of 32 */ + adofs += 31; + adofs &= ~0x1f; + val = (adofs >> 5) & IPIPEIF_ADOFS_LSB_MASK; + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_ADOFS); + + /* lower sixteen bit */ + val = (addr >> IPIPEIF_ADDRL_SHIFT) & IPIPEIF_ADDRL_MASK; + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_ADDRL); + + /* upper next seven bit */ + val = (addr >> IPIPEIF_ADDRU_SHIFT) & IPIPEIF_ADDRU_MASK; + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_ADDRU); + + return 0; +} + +/* subdev core operations */ +static const struct v4l2_subdev_core_ops ipipeif_v4l2_core_ops = { + .ioctl = ipipeif_ioctl, +}; + +static const struct v4l2_ctrl_ops ipipeif_ctrl_ops = { + .s_ctrl = ipipeif_s_ctrl, +}; + +static const struct v4l2_ctrl_config vpfe_ipipeif_dpcm_pred = { + .ops = &ipipeif_ctrl_ops, + .id = VPFE_CID_DPCM_PREDICTOR, + .name = "DPCM Predictor", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 1, + .step = 1, + .def = 0, +}; + +/* subdev file operations */ +static const struct v4l2_subdev_internal_ops ipipeif_v4l2_internal_ops = { + .open = ipipeif_init_formats, +}; + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops ipipeif_v4l2_video_ops = { + .s_stream = ipipeif_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops ipipeif_v4l2_pad_ops = { + .enum_mbus_code = ipipeif_enum_mbus_code, + .enum_frame_size = ipipeif_enum_frame_size, + .get_fmt = ipipeif_get_format, + .set_fmt = ipipeif_set_format, +}; + +/* subdev operations */ +static const struct v4l2_subdev_ops ipipeif_v4l2_ops = { + .core = &ipipeif_v4l2_core_ops, + .video = &ipipeif_v4l2_video_ops, + .pad = &ipipeif_v4l2_pad_ops, +}; + +static const struct vpfe_video_operations video_in_ops = { + .queue = ipipeif_video_in_queue, +}; + +static int +ipipeif_link_setup(struct media_entity *entity, const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe = to_vpfe_device(ipipeif); + + switch (local->index | media_entity_type(remote->entity)) { + case IPIPEIF_PAD_SINK | MEDIA_ENT_T_DEVNODE: + /* Single shot mode */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ipipeif->input = IPIPEIF_INPUT_NONE; + break; + } + ipipeif->input = IPIPEIF_INPUT_MEMORY; + break; + + case IPIPEIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* read from isif */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ipipeif->input = IPIPEIF_INPUT_NONE; + break; + } + if (ipipeif->input != IPIPEIF_INPUT_NONE) + return -EBUSY; + + ipipeif->input = IPIPEIF_INPUT_ISIF; + break; + + case IPIPEIF_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ipipeif->output = IPIPEIF_OUTPUT_NONE; + break; + } + if (remote->entity == &vpfe->vpfe_ipipe.subdev.entity) + /* connencted to ipipe */ + ipipeif->output = IPIPEIF_OUTPUT_IPIPE; + else if (remote->entity == &vpfe->vpfe_resizer. + crop_resizer.subdev.entity) + /* connected to resizer */ + ipipeif->output = IPIPEIF_OUTPUT_RESIZER; + else + return -EINVAL; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static const struct media_entity_operations ipipeif_media_ops = { + .link_setup = ipipeif_link_setup, +}; + +/* + * vpfe_ipipeif_unregister_entities() - Unregister entity + * @ipipeif - pointer to ipipeif subdevice structure. + */ +void vpfe_ipipeif_unregister_entities(struct vpfe_ipipeif_device *ipipeif) +{ + /* unregister video device */ + vpfe_video_unregister(&ipipeif->video_in); + + /* cleanup entity */ + media_entity_cleanup(&ipipeif->subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&ipipeif->subdev); +} + +int +vpfe_ipipeif_register_entities(struct vpfe_ipipeif_device *ipipeif, + struct v4l2_device *vdev) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(ipipeif); + unsigned int flags; + int ret; + + /* Register the subdev */ + ret = v4l2_device_register_subdev(vdev, &ipipeif->subdev); + if (ret < 0) + return ret; + + ret = vpfe_video_register(&ipipeif->video_in, vdev); + if (ret) { + pr_err("Failed to register ipipeif video-in device\n"); + goto fail; + } + ipipeif->video_in.vpfe_dev = vpfe_dev; + + flags = 0; + ret = media_entity_create_link(&ipipeif->video_in.video_dev.entity, 0, + &ipipeif->subdev.entity, 0, flags); + if (ret < 0) + goto fail; + + return 0; +fail: + v4l2_device_unregister_subdev(&ipipeif->subdev); + + return ret; +} + +#define IPIPEIF_GAIN_HIGH 0x3ff +#define IPIPEIF_DEFAULT_GAIN 0x200 + +int vpfe_ipipeif_init(struct vpfe_ipipeif_device *ipipeif, + struct platform_device *pdev) +{ + struct v4l2_subdev *sd = &ipipeif->subdev; + struct media_pad *pads = &ipipeif->pads[0]; + struct media_entity *me = &sd->entity; + static resource_size_t res_len; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 3); + if (!res) + return -ENOENT; + + res_len = resource_size(res); + res = request_mem_region(res->start, res_len, res->name); + if (!res) + return -EBUSY; + + ipipeif->ipipeif_base_addr = ioremap_nocache(res->start, res_len); + if (!ipipeif->ipipeif_base_addr) { + ret = -EBUSY; + goto fail; + } + + v4l2_subdev_init(sd, &ipipeif_v4l2_ops); + + sd->internal_ops = &ipipeif_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI IPIPEIF", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + + v4l2_set_subdevdata(sd, ipipeif); + + sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + pads[IPIPEIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[IPIPEIF_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + ipipeif->input = IPIPEIF_INPUT_NONE; + ipipeif->output = IPIPEIF_OUTPUT_NONE; + me->ops = &ipipeif_media_ops; + + ret = media_entity_init(me, IPIPEIF_NUM_PADS, pads, 0); + if (ret) + goto fail; + + v4l2_ctrl_handler_init(&ipipeif->ctrls, 2); + v4l2_ctrl_new_std(&ipipeif->ctrls, &ipipeif_ctrl_ops, + V4L2_CID_GAIN, 0, + IPIPEIF_GAIN_HIGH, 1, IPIPEIF_DEFAULT_GAIN); + v4l2_ctrl_new_custom(&ipipeif->ctrls, &vpfe_ipipeif_dpcm_pred, NULL); + v4l2_ctrl_handler_setup(&ipipeif->ctrls); + sd->ctrl_handler = &ipipeif->ctrls; + + ipipeif->video_in.ops = &video_in_ops; + ipipeif->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + ret = vpfe_video_init(&ipipeif->video_in, "IPIPEIF"); + if (ret) { + pr_err("Failed to init IPIPEIF video-in device\n"); + goto fail; + } + ipipeif_set_default_config(ipipeif); + return 0; +fail: + release_mem_region(res->start, res_len); + return ret; +} + +void +vpfe_ipipeif_cleanup(struct vpfe_ipipeif_device *ipipeif, + struct platform_device *pdev) +{ + struct resource *res; + + v4l2_ctrl_handler_free(&ipipeif->ctrls); + iounmap(ipipeif->ipipeif_base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 3); + if (res) + release_mem_region(res->start, + res->end - res->start + 1); + +} diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.h b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.h new file mode 100644 index 000000000000..608701fc5fed --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.h @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _DAVINCI_VPFE_DM365_IPIPEIF_H +#define _DAVINCI_VPFE_DM365_IPIPEIF_H + +#include + +#include +#include +#include + +#include "dm365_ipipeif_user.h" +#include "vpfe_video.h" + +/* IPIPE base specific types */ +enum ipipeif_data_shift { + IPIPEIF_BITS15_2 = 0, + IPIPEIF_BITS14_1 = 1, + IPIPEIF_BITS13_0 = 2, + IPIPEIF_BITS12_0 = 3, + IPIPEIF_BITS11_0 = 4, + IPIPEIF_BITS10_0 = 5, + IPIPEIF_BITS9_0 = 6, +}; + +enum ipipeif_clkdiv { + IPIPEIF_DIVIDE_HALF = 0, + IPIPEIF_DIVIDE_THIRD = 1, + IPIPEIF_DIVIDE_FOURTH = 2, + IPIPEIF_DIVIDE_FIFTH = 3, + IPIPEIF_DIVIDE_SIXTH = 4, + IPIPEIF_DIVIDE_EIGHTH = 5, + IPIPEIF_DIVIDE_SIXTEENTH = 6, + IPIPEIF_DIVIDE_THIRTY = 7, +}; + +enum ipipeif_pack_mode { + IPIPEIF_PACK_16_BIT = 0, + IPIPEIF_PACK_8_BIT = 1, +}; + +enum ipipeif_5_1_pack_mode { + IPIPEIF_5_1_PACK_16_BIT = 0, + IPIPEIF_5_1_PACK_8_BIT = 1, + IPIPEIF_5_1_PACK_8_BIT_A_LAW = 2, + IPIPEIF_5_1_PACK_12_BIT = 3 +}; + +enum ipipeif_input_source { + IPIPEIF_CCDC = 0, + IPIPEIF_SDRAM_RAW = 1, + IPIPEIF_CCDC_DARKFM = 2, + IPIPEIF_SDRAM_YUV = 3, +}; + +enum ipipeif_ialaw { + IPIPEIF_ALAW_OFF = 0, + IPIPEIF_ALAW_ON = 1, +}; + +enum ipipeif_input_src1 { + IPIPEIF_SRC1_PARALLEL_PORT = 0, + IPIPEIF_SRC1_SDRAM_RAW = 1, + IPIPEIF_SRC1_ISIF_DARKFM = 2, + IPIPEIF_SRC1_SDRAM_YUV = 3, +}; + +enum ipipeif_dfs_dir { + IPIPEIF_PORT_MINUS_SDRAM = 0, + IPIPEIF_SDRAM_MINUS_PORT = 1, +}; + +enum ipipeif_chroma_phase { + IPIPEIF_CBCR_Y = 0, + IPIPEIF_Y_CBCR = 1, +}; + +enum ipipeif_dpcm_type { + IPIPEIF_DPCM_8BIT_10BIT = 0, + IPIPEIF_DPCM_8BIT_12BIT = 1, +}; + +/* data shift for IPIPE 5.1 */ +enum ipipeif_5_1_data_shift { + IPIPEIF_5_1_BITS11_0 = 0, + IPIPEIF_5_1_BITS10_0 = 1, + IPIPEIF_5_1_BITS9_0 = 2, + IPIPEIF_5_1_BITS8_0 = 3, + IPIPEIF_5_1_BITS7_0 = 4, + IPIPEIF_5_1_BITS15_4 = 5, +}; + +#define IPIPEIF_PAD_SINK 0 +#define IPIPEIF_PAD_SOURCE 1 + +#define IPIPEIF_NUM_PADS 2 + +enum ipipeif_input_entity { + IPIPEIF_INPUT_NONE = 0, + IPIPEIF_INPUT_ISIF = 1, + IPIPEIF_INPUT_MEMORY = 2, +}; + +enum ipipeif_output_entity { + IPIPEIF_OUTPUT_NONE = 0, + IPIPEIF_OUTPUT_IPIPE = 1, + IPIPEIF_OUTPUT_RESIZER = 2, +}; + +struct vpfe_ipipeif_device { + struct v4l2_subdev subdev; + struct media_pad pads[IPIPEIF_NUM_PADS]; + struct v4l2_mbus_framefmt formats[IPIPEIF_NUM_PADS]; + enum ipipeif_input_entity input; + unsigned int output; + struct vpfe_video_device video_in; + struct v4l2_ctrl_handler ctrls; + void *__iomem ipipeif_base_addr; + struct ipipeif_params config; + int dpcm_predictor; + int gain; +}; + +/* IPIPEIF Register Offsets from the base address */ +#define IPIPEIF_ENABLE 0x00 +#define IPIPEIF_CFG1 0x04 +#define IPIPEIF_PPLN 0x08 +#define IPIPEIF_LPFR 0x0c +#define IPIPEIF_HNUM 0x10 +#define IPIPEIF_VNUM 0x14 +#define IPIPEIF_ADDRU 0x18 +#define IPIPEIF_ADDRL 0x1c +#define IPIPEIF_ADOFS 0x20 +#define IPIPEIF_RSZ 0x24 +#define IPIPEIF_GAIN 0x28 + +/* Below registers are available only on IPIPE 5.1 */ +#define IPIPEIF_DPCM 0x2c +#define IPIPEIF_CFG2 0x30 +#define IPIPEIF_INIRSZ 0x34 +#define IPIPEIF_OCLIP 0x38 +#define IPIPEIF_DTUDF 0x3c +#define IPIPEIF_CLKDIV 0x40 +#define IPIPEIF_DPC1 0x44 +#define IPIPEIF_DPC2 0x48 +#define IPIPEIF_DFSGVL 0x4c +#define IPIPEIF_DFSGTH 0x50 +#define IPIPEIF_RSZ3A 0x54 +#define IPIPEIF_INIRSZ3A 0x58 +#define IPIPEIF_RSZ_MIN 16 +#define IPIPEIF_RSZ_MAX 112 +#define IPIPEIF_RSZ_CONST 16 +#define SETBIT(reg, bit) (reg = ((reg) | ((0x00000001)<<(bit)))) +#define RESETBIT(reg, bit) (reg = ((reg) & (~(0x00000001<<(bit))))) + +#define IPIPEIF_ADOFS_LSB_MASK 0x1ff +#define IPIPEIF_ADOFS_LSB_SHIFT 5 +#define IPIPEIF_ADOFS_MSB_MASK 0x200 +#define IPIPEIF_ADDRU_MASK 0x7ff +#define IPIPEIF_ADDRL_SHIFT 5 +#define IPIPEIF_ADDRL_MASK 0xffff +#define IPIPEIF_ADDRU_SHIFT 21 +#define IPIPEIF_ADDRMSB_SHIFT 31 +#define IPIPEIF_ADDRMSB_LEFT_SHIFT 10 + +/* CFG1 Masks and shifts */ +#define ONESHOT_SHIFT 0 +#define DECIM_SHIFT 1 +#define INPSRC_SHIFT 2 +#define CLKDIV_SHIFT 4 +#define AVGFILT_SHIFT 7 +#define PACK8IN_SHIFT 8 +#define IALAW_SHIFT 9 +#define CLKSEL_SHIFT 10 +#define DATASFT_SHIFT 11 +#define INPSRC1_SHIFT 14 + +/* DPC2 */ +#define IPIPEIF_DPC2_EN_SHIFT 12 +#define IPIPEIF_DPC2_THR_MASK 0xfff +/* Applicable for IPIPE 5.1 */ +#define IPIPEIF_DF_GAIN_EN_SHIFT 10 +#define IPIPEIF_DF_GAIN_MASK 0x3ff +#define IPIPEIF_DF_GAIN_THR_MASK 0xfff +/* DPCM */ +#define IPIPEIF_DPCM_BITS_SHIFT 2 +#define IPIPEIF_DPCM_PRED_SHIFT 1 +/* CFG2 */ +#define IPIPEIF_CFG2_HDPOL_SHIFT 1 +#define IPIPEIF_CFG2_VDPOL_SHIFT 2 +#define IPIPEIF_CFG2_YUV8_SHIFT 6 +#define IPIPEIF_CFG2_YUV16_SHIFT 3 +#define IPIPEIF_CFG2_YUV8P_SHIFT 7 + +/* INIRSZ */ +#define IPIPEIF_INIRSZ_ALNSYNC_SHIFT 13 +#define IPIPEIF_INIRSZ_MASK 0x1fff + +/* CLKDIV */ +#define IPIPEIF_CLKDIV_M_SHIFT 8 + +void vpfe_ipipeif_enable(struct vpfe_device *vpfe_dev); +void vpfe_ipipeif_ss_buffer_isr(struct vpfe_ipipeif_device *ipipeif); +int vpfe_ipipeif_decimation_enabled(struct vpfe_device *vpfe_dev); +int vpfe_ipipeif_get_rsz(struct vpfe_device *vpfe_dev); +void vpfe_ipipeif_cleanup(struct vpfe_ipipeif_device *ipipeif, + struct platform_device *pdev); +int vpfe_ipipeif_init(struct vpfe_ipipeif_device *ipipeif, + struct platform_device *pdev); +int vpfe_ipipeif_register_entities(struct vpfe_ipipeif_device *ipipeif, + struct v4l2_device *vdev); +void vpfe_ipipeif_unregister_entities(struct vpfe_ipipeif_device *ipipeif); + +#endif /* _DAVINCI_VPFE_DM365_IPIPEIF_H */ diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h b/drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h new file mode 100644 index 000000000000..e2a69b59554a --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _DAVINCI_VPFE_DM365_IPIPEIF_USER_H +#define _DAVINCI_VPFE_DM365_IPIPEIF_USER_H + +/* clockdiv for IPIPE 5.1 */ +struct ipipeif_5_1_clkdiv { + unsigned char m; + unsigned char n; +}; + +enum ipipeif_decimation { + IPIPEIF_DECIMATION_OFF, + IPIPEIF_DECIMATION_ON +}; + +/* DPC at the if for IPIPE 5.1 */ +struct ipipeif_dpc { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* threshold */ + unsigned short thr; +}; + +enum ipipeif_clock { + IPIPEIF_PIXCEL_CLK, + IPIPEIF_SDRAM_CLK +}; + +enum ipipeif_avg_filter { + IPIPEIF_AVG_OFF, + IPIPEIF_AVG_ON +}; + +struct ipipeif_5_1 { + struct ipipeif_5_1_clkdiv clk_div; + /* Defect pixel correction */ + struct ipipeif_dpc dpc; + /* clipped to this value */ + unsigned short clip; + /* Align HSync and VSync to rsz_start */ + unsigned char align_sync; + /* resizer start position */ + unsigned int rsz_start; + /* DF gain enable */ + unsigned char df_gain_en; + /* DF gain value */ + unsigned short df_gain; + /* DF gain threshold value */ + unsigned short df_gain_thr; +}; + +struct ipipeif_params { + enum ipipeif_clock clock_select; + unsigned int ppln; + unsigned int lpfr; + unsigned char rsz; + enum ipipeif_decimation decimation; + enum ipipeif_avg_filter avg_filter; + /* IPIPE 5.1 */ + struct ipipeif_5_1 if_5_1; +}; + +/* + * Private IOCTL + * VIDIOC_VPFE_IPIPEIF_S_CONFIG: Set IPIEIF configuration + * VIDIOC_VPFE_IPIPEIF_G_CONFIG: Get IPIEIF configuration + */ +#define VIDIOC_VPFE_IPIPEIF_S_CONFIG \ + _IOWR('I', BASE_VIDIOC_PRIVATE + 1, struct ipipeif_params) +#define VIDIOC_VPFE_IPIPEIF_G_CONFIG \ + _IOWR('I', BASE_VIDIOC_PRIVATE + 2, struct ipipeif_params) + +#endif /* _DAVINCI_VPFE_DM365_IPIPEIF_USER_H */ -- cgit v1.2.3 From 6a630533c4461c1e6dabfab5f11d9e98cb743bc2 Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Wed, 28 Nov 2012 02:09:35 -0300 Subject: [media] davinci: vpfe: dm365: add ISIF driver based on media framework add support for ISIF as a subdevice for dm365 vpfe capture driver. ISIF is responsible for capturing video data both in raw bayer format on sync seperate signals and YUV through BT656/1120 interfaces. ISIF is exposed as a subdev for the vpfe driver and allows users to use the driver through standard media controller interface. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/davinci_vpfe/dm365_isif.c | 2104 ++++++++++++++++++++ drivers/staging/media/davinci_vpfe/dm365_isif.h | 203 ++ .../staging/media/davinci_vpfe/dm365_isif_regs.h | 294 +++ 3 files changed, 2601 insertions(+) create mode 100644 drivers/staging/media/davinci_vpfe/dm365_isif.c create mode 100644 drivers/staging/media/davinci_vpfe/dm365_isif.h create mode 100644 drivers/staging/media/davinci_vpfe/dm365_isif_regs.h (limited to 'drivers') diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c new file mode 100644 index 000000000000..ebeea72e176a --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c @@ -0,0 +1,2104 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#include "dm365_isif.h" +#include "vpfe_mc_capture.h" + +#define MAX_WIDTH 4096 +#define MAX_HEIGHT 4096 + +static const unsigned int isif_fmts[] = { + V4L2_MBUS_FMT_YUYV8_2X8, + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_YUYV8_1X16, + V4L2_MBUS_FMT_YUYV10_1X20, + V4L2_MBUS_FMT_SGRBG12_1X12, + V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8, + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, +}; + +#define ISIF_COLPTN_R_Ye 0x0 +#define ISIF_COLPTN_Gr_Cy 0x1 +#define ISIF_COLPTN_Gb_G 0x2 +#define ISIF_COLPTN_B_Mg 0x3 + +#define ISIF_CCOLP_CP01_0 0 +#define ISIF_CCOLP_CP03_2 2 +#define ISIF_CCOLP_CP05_4 4 +#define ISIF_CCOLP_CP07_6 6 +#define ISIF_CCOLP_CP11_0 8 +#define ISIF_CCOLP_CP13_2 10 +#define ISIF_CCOLP_CP15_4 12 +#define ISIF_CCOLP_CP17_6 14 + +static const u32 isif_sgrbg_pattern = + ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP01_0 | + ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP03_2 | + ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP05_4 | + ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP07_6 | + ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP11_0 | + ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP13_2 | + ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP15_4 | + ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP17_6; + +static const u32 isif_srggb_pattern = + ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP01_0 | + ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP03_2 | + ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP05_4 | + ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP07_6 | + ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP11_0 | + ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP13_2 | + ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP15_4 | + ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP17_6; + +static inline u32 isif_read(void *__iomem base_addr, u32 offset) +{ + return readl(base_addr + offset); +} + +static inline void isif_write(void *__iomem base_addr, u32 val, u32 offset) +{ + writel(val, base_addr + offset); +} + +static inline u32 isif_merge(void *__iomem base_addr, u32 mask, u32 val, + u32 offset) +{ + u32 new_val = (isif_read(base_addr, offset) & ~mask) | (val & mask); + + isif_write(base_addr, new_val, offset); + + return new_val; +} + +static void isif_enable_output_to_sdram(struct vpfe_isif_device *isif, int en) +{ + isif_merge(isif->isif_cfg.base_addr, ISIF_SYNCEN_WEN_MASK, + en << ISIF_SYNCEN_WEN_SHIFT, SYNCEN); +} + +static inline void +isif_regw_lin_tbl(struct vpfe_isif_device *isif, u32 val, u32 offset, int i) +{ + if (!i) + writel(val, isif->isif_cfg.linear_tbl0_addr + offset); + else + writel(val, isif->isif_cfg.linear_tbl1_addr + offset); +} + +static void isif_disable_all_modules(struct vpfe_isif_device *isif) +{ + /* disable BC */ + isif_write(isif->isif_cfg.base_addr, 0, CLAMPCFG); + /* disable vdfc */ + isif_write(isif->isif_cfg.base_addr, 0, DFCCTL); + /* disable CSC */ + isif_write(isif->isif_cfg.base_addr, 0, CSCCTL); + /* disable linearization */ + isif_write(isif->isif_cfg.base_addr, 0, LINCFG0); +} + +static void isif_enable(struct vpfe_isif_device *isif, int en) +{ + if (!en) + /* Before disable isif, disable all ISIF modules */ + isif_disable_all_modules(isif); + + /* + * wait for next VD. Assume lowest scan rate is 12 Hz. So + * 100 msec delay is good enough + */ + msleep(100); + isif_merge(isif->isif_cfg.base_addr, ISIF_SYNCEN_VDHDEN_MASK, + en, SYNCEN); +} + +/* + * ISIF helper functions + */ + +#define DM365_ISIF_MDFS_OFFSET 15 +#define DM365_ISIF_MDFS_MASK 0x1 + +/* get field id in isif hardware */ +enum v4l2_field vpfe_isif_get_fid(struct vpfe_device *vpfe_dev) +{ + struct vpfe_isif_device *isif = &vpfe_dev->vpfe_isif; + u32 field_status; + + field_status = isif_read(isif->isif_cfg.base_addr, MODESET); + field_status = (field_status >> DM365_ISIF_MDFS_OFFSET) & + DM365_ISIF_MDFS_MASK; + return field_status; +} + +static int +isif_set_pixel_format(struct vpfe_isif_device *isif, unsigned int pixfmt) +{ + if (isif->formats[ISIF_PAD_SINK].code == V4L2_MBUS_FMT_SGRBG12_1X12) { + if (pixfmt == V4L2_PIX_FMT_SBGGR16) + isif->isif_cfg.data_pack = ISIF_PACK_16BIT; + else if ((pixfmt == V4L2_PIX_FMT_SGRBG10DPCM8) || + (pixfmt == V4L2_PIX_FMT_SGRBG10ALAW8)) + isif->isif_cfg.data_pack = ISIF_PACK_8BIT; + else + return -EINVAL; + + isif->isif_cfg.bayer.pix_fmt = ISIF_PIXFMT_RAW; + isif->isif_cfg.bayer.v4l2_pix_fmt = pixfmt; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + isif->isif_cfg.ycbcr.pix_order = ISIF_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + isif->isif_cfg.ycbcr.pix_order = ISIF_PIXORDER_CBYCRY; + else + return -EINVAL; + + isif->isif_cfg.data_pack = ISIF_PACK_8BIT; + isif->isif_cfg.ycbcr.v4l2_pix_fmt = pixfmt; + } + + return 0; +} + +static int +isif_set_frame_format(struct vpfe_isif_device *isif, + enum isif_frmfmt frm_fmt) +{ + if (isif->formats[ISIF_PAD_SINK].code == V4L2_MBUS_FMT_SGRBG12_1X12) + isif->isif_cfg.bayer.frm_fmt = frm_fmt; + else + isif->isif_cfg.ycbcr.frm_fmt = frm_fmt; + + return 0; +} + +static int isif_set_image_window(struct vpfe_isif_device *isif) +{ + struct v4l2_rect *win = &isif->crop; + + if (isif->formats[ISIF_PAD_SINK].code == V4L2_MBUS_FMT_SGRBG12_1X12) { + isif->isif_cfg.bayer.win.top = win->top; + isif->isif_cfg.bayer.win.left = win->left; + isif->isif_cfg.bayer.win.width = win->width; + isif->isif_cfg.bayer.win.height = win->height; + return 0; + } + isif->isif_cfg.ycbcr.win.top = win->top; + isif->isif_cfg.ycbcr.win.left = win->left; + isif->isif_cfg.ycbcr.win.width = win->width; + isif->isif_cfg.ycbcr.win.height = win->height; + + return 0; +} + +static int +isif_set_buftype(struct vpfe_isif_device *isif, enum isif_buftype buf_type) +{ + if (isif->formats[ISIF_PAD_SINK].code == V4L2_MBUS_FMT_SGRBG12_1X12) + isif->isif_cfg.bayer.buf_type = buf_type; + else + isif->isif_cfg.ycbcr.buf_type = buf_type; + + return 0; +} + +/* configure format in isif hardware */ +static int +isif_config_format(struct vpfe_device *vpfe_dev, unsigned int pad) +{ + struct vpfe_isif_device *vpfe_isif = &vpfe_dev->vpfe_isif; + enum isif_frmfmt frm_fmt = ISIF_FRMFMT_INTERLACED; + struct v4l2_pix_format format; + int ret = 0; + + v4l2_fill_pix_format(&format, &vpfe_dev->vpfe_isif.formats[pad]); + mbus_to_pix(&vpfe_dev->vpfe_isif.formats[pad], &format); + + if (isif_set_pixel_format(vpfe_isif, format.pixelformat) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Failed to set pixel format in isif\n"); + return -EINVAL; + } + + /* call for s_crop will override these values */ + vpfe_isif->crop.left = 0; + vpfe_isif->crop.top = 0; + vpfe_isif->crop.width = format.width; + vpfe_isif->crop.height = format.height; + + /* configure the image window */ + isif_set_image_window(vpfe_isif); + + switch (vpfe_dev->vpfe_isif.formats[pad].field) { + case V4L2_FIELD_INTERLACED: + /* do nothing, since it is default */ + ret = isif_set_buftype(vpfe_isif, ISIF_BUFTYPE_FLD_INTERLEAVED); + break; + + case V4L2_FIELD_NONE: + frm_fmt = ISIF_FRMFMT_PROGRESSIVE; + /* buffer type only applicable for interlaced scan */ + break; + + case V4L2_FIELD_SEQ_TB: + ret = isif_set_buftype(vpfe_isif, ISIF_BUFTYPE_FLD_SEPARATED); + break; + + default: + return -EINVAL; + } + + /* set the frame format */ + if (!ret) + ret = isif_set_frame_format(vpfe_isif, frm_fmt); + + return ret; +} + +/* + * isif_try_format() - Try video format on a pad + * @isif: VPFE isif device + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + */ +static void +isif_try_format(struct vpfe_isif_device *isif, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + unsigned int width = fmt->format.width; + unsigned int height = fmt->format.height; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(isif_fmts); i++) { + if (fmt->format.code == isif_fmts[i]) + break; + } + + /* If not found, use YUYV8_2x8 as default */ + if (i >= ARRAY_SIZE(isif_fmts)) + fmt->format.code = V4L2_MBUS_FMT_YUYV8_2X8; + + /* Clamp the size. */ + fmt->format.width = clamp_t(u32, width, 32, MAX_WIDTH); + fmt->format.height = clamp_t(u32, height, 32, MAX_HEIGHT); + + /* The data formatter truncates the number of horizontal output + * pixels to a multiple of 16. To avoid clipping data, allow + * callers to request an output size bigger than the input size + * up to the nearest multiple of 16. + */ + if (fmt->pad == ISIF_PAD_SOURCE) + fmt->format.width &= ~15; +} + +/* + * vpfe_isif_buffer_isr() - isif module non-progressive buffer scheduling isr + * @isif: Pointer to isif subdevice. + */ +void vpfe_isif_buffer_isr(struct vpfe_isif_device *isif) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(isif); + struct vpfe_video_device *video = &isif->video_out; + enum v4l2_field field; + int fid; + + if (!video->started) + return; + + field = video->fmt.fmt.pix.field; + + if (field == V4L2_FIELD_NONE) { + /* handle progressive frame capture */ + if (video->cur_frm != video->next_frm) + vpfe_video_process_buffer_complete(video); + return; + } + + /* interlaced or TB capture check which field we + * are in hardware + */ + fid = vpfe_isif_get_fid(vpfe_dev); + + /* switch the software maintained field id */ + video->field_id ^= 1; + if (fid == video->field_id) { + /* we are in-sync here,continue */ + if (fid == 0) { + /* + * One frame is just being captured. If the + * next frame is available, release the current + * frame and move on + */ + if (video->cur_frm != video->next_frm) + vpfe_video_process_buffer_complete(video); + /* + * based on whether the two fields are stored + * interleavely or separately in memory, + * reconfigure the ISIF memory address + */ + if (field == V4L2_FIELD_SEQ_TB) + vpfe_video_schedule_bottom_field(video); + return; + } + /* + * if one field is just being captured configure + * the next frame get the next frame from the + * empty queue if no frame is available hold on + * to the current buffer + */ + spin_lock(&video->dma_queue_lock); + if (!list_empty(&video->dma_queue) && + video->cur_frm == video->next_frm) + vpfe_video_schedule_next_buffer(video); + spin_unlock(&video->dma_queue_lock); + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + video->field_id = fid; + } +} + +/* + * vpfe_isif_vidint1_isr() - ISIF module progressive buffer scheduling isr + * @isif: Pointer to isif subdevice. + */ +void vpfe_isif_vidint1_isr(struct vpfe_isif_device *isif) +{ + struct vpfe_video_device *video = &isif->video_out; + + if (!video->started) + return; + + spin_lock(&video->dma_queue_lock); + if (video->fmt.fmt.pix.field == V4L2_FIELD_NONE && + !list_empty(&video->dma_queue) && video->cur_frm == video->next_frm) + vpfe_video_schedule_next_buffer(video); + + spin_unlock(&video->dma_queue_lock); +} + +/* + * VPFE video operations + */ + +static int isif_video_queue(struct vpfe_device *vpfe_dev, unsigned long addr) +{ + struct vpfe_isif_device *isif = &vpfe_dev->vpfe_isif; + + isif_write(isif->isif_cfg.base_addr, (addr >> 21) & + ISIF_CADU_BITS, CADU); + isif_write(isif->isif_cfg.base_addr, (addr >> 5) & + ISIF_CADL_BITS, CADL); + + return 0; +} + +static const struct vpfe_video_operations isif_video_ops = { + .queue = isif_video_queue, +}; + +/* + * V4L2 subdev operations + */ + +/* Parameter operations */ +static int isif_get_params(struct v4l2_subdev *sd, void *params) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + + /* only raw module parameters can be set through the IOCTL */ + if (isif->formats[ISIF_PAD_SINK].code != V4L2_MBUS_FMT_SGRBG12_1X12) + return -EINVAL; + memcpy(params, &isif->isif_cfg.bayer.config_params, + sizeof(isif->isif_cfg.bayer.config_params)); + return 0; +} + +static int isif_validate_df_csc_params(struct vpfe_isif_df_csc *df_csc) +{ + struct vpfe_isif_color_space_conv *csc; + int err = -EINVAL; + int csc_df_en; + int i; + + if (!df_csc->df_or_csc) { + /* csc configuration */ + csc = &df_csc->csc; + if (csc->en) { + csc_df_en = 1; + for (i = 0; i < VPFE_ISIF_CSC_NUM_COEFF; i++) + if (csc->coeff[i].integer > + ISIF_CSC_COEF_INTEG_MASK || + csc->coeff[i].decimal > + ISIF_CSC_COEF_DECIMAL_MASK) { + pr_err("Invalid CSC coefficients\n"); + return err; + } + } + } + if (df_csc->start_pix > ISIF_DF_CSC_SPH_MASK) { + pr_err("Invalid df_csc start pix value\n"); + return err; + } + + if (df_csc->num_pixels > ISIF_DF_NUMPIX) { + pr_err("Invalid df_csc num pixels value\n"); + return err; + } + + if (df_csc->start_line > ISIF_DF_CSC_LNH_MASK) { + pr_err("Invalid df_csc start_line value\n"); + return err; + } + + if (df_csc->num_lines > ISIF_DF_NUMLINES) { + pr_err("Invalid df_csc num_lines value\n"); + return err; + } + + return 0; +} + +#define DM365_ISIF_MAX_VDFLSFT 4 +#define DM365_ISIF_MAX_VDFSLV 4095 +#define DM365_ISIF_MAX_DFCMEM0 0x1fff +#define DM365_ISIF_MAX_DFCMEM1 0x1fff + +static int isif_validate_dfc_params(struct vpfe_isif_dfc *dfc) +{ + int err = -EINVAL; + int i; + + if (!dfc->en) + return 0; + + if (dfc->corr_whole_line > 1) { + pr_err("Invalid corr_whole_line value\n"); + return err; + } + + if (dfc->def_level_shift > DM365_ISIF_MAX_VDFLSFT) { + pr_err("Invalid def_level_shift value\n"); + return err; + } + + if (dfc->def_sat_level > DM365_ISIF_MAX_VDFSLV) { + pr_err("Invalid def_sat_level value\n"); + return err; + } + + if (!dfc->num_vdefects || + dfc->num_vdefects > VPFE_ISIF_VDFC_TABLE_SIZE) { + pr_err("Invalid num_vdefects value\n"); + return err; + } + + for (i = 0; i < VPFE_ISIF_VDFC_TABLE_SIZE; i++) { + if (dfc->table[i].pos_vert > DM365_ISIF_MAX_DFCMEM0) { + pr_err("Invalid pos_vert value\n"); + return err; + } + if (dfc->table[i].pos_horz > DM365_ISIF_MAX_DFCMEM1) { + pr_err("Invalid pos_horz value\n"); + return err; + } + } + + return 0; +} + +#define DM365_ISIF_MAX_CLVRV 0xfff +#define DM365_ISIF_MAX_CLDC 0x1fff +#define DM365_ISIF_MAX_CLHSH 0x1fff +#define DM365_ISIF_MAX_CLHSV 0x1fff +#define DM365_ISIF_MAX_CLVSH 0x1fff +#define DM365_ISIF_MAX_CLVSV 0x1fff +#define DM365_ISIF_MAX_HEIGHT_BLACK_REGION 0x1fff + +static int isif_validate_bclamp_params(struct vpfe_isif_black_clamp *bclamp) +{ + int err = -EINVAL; + + if (bclamp->dc_offset > DM365_ISIF_MAX_CLDC) { + pr_err("Invalid bclamp dc_offset value\n"); + return err; + } + if (!bclamp->en) + return 0; + if (bclamp->horz.clamp_pix_limit > 1) { + pr_err("Invalid bclamp horz clamp_pix_limit value\n"); + return err; + } + if (bclamp->horz.win_count_calc < 1 || + bclamp->horz.win_count_calc > 32) { + pr_err("Invalid bclamp horz win_count_calc value\n"); + return err; + } + if (bclamp->horz.win_start_h_calc > DM365_ISIF_MAX_CLHSH) { + pr_err("Invalid bclamp win_start_v_calc value\n"); + return err; + } + + if (bclamp->horz.win_start_v_calc > DM365_ISIF_MAX_CLHSV) { + pr_err("Invalid bclamp win_start_v_calc value\n"); + return err; + } + if (bclamp->vert.reset_clamp_val > DM365_ISIF_MAX_CLVRV) { + pr_err("Invalid bclamp reset_clamp_val value\n"); + return err; + } + if (bclamp->vert.ob_v_sz_calc > DM365_ISIF_MAX_HEIGHT_BLACK_REGION) { + pr_err("Invalid bclamp ob_v_sz_calc value\n"); + return err; + } + if (bclamp->vert.ob_start_h > DM365_ISIF_MAX_CLVSH) { + pr_err("Invalid bclamp ob_start_h value\n"); + return err; + } + if (bclamp->vert.ob_start_v > DM365_ISIF_MAX_CLVSV) { + pr_err("Invalid bclamp ob_start_h value\n"); + return err; + } + return 0; +} + +static int +isif_validate_raw_params(struct vpfe_isif_raw_config *params) +{ + int ret; + + ret = isif_validate_df_csc_params(¶ms->df_csc); + if (ret) + return ret; + ret = isif_validate_dfc_params(¶ms->dfc); + if (ret) + return ret; + ret = isif_validate_bclamp_params(¶ms->bclamp); + return ret; +} + +static int isif_set_params(struct v4l2_subdev *sd, void *params) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct vpfe_isif_raw_config isif_raw_params; + int ret = -EINVAL; + + /* only raw module parameters can be set through the IOCTL */ + if (isif->formats[ISIF_PAD_SINK].code != V4L2_MBUS_FMT_SGRBG12_1X12) + return ret; + + memcpy(&isif_raw_params, params, sizeof(isif_raw_params)); + if (!isif_validate_raw_params(&isif_raw_params)) { + memcpy(&isif->isif_cfg.bayer.config_params, &isif_raw_params, + sizeof(isif_raw_params)); + ret = 0; + } + return ret; +} +/* + * isif_ioctl() - isif module private ioctl's + * @sd: VPFE isif V4L2 subdevice + * @cmd: ioctl command + * @arg: ioctl argument + * + * Return 0 on success or a negative error code otherwise. + */ +static long isif_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + int ret; + + switch (cmd) { + case VIDIOC_VPFE_ISIF_S_RAW_PARAMS: + ret = isif_set_params(sd, arg); + break; + + case VIDIOC_VPFE_ISIF_G_RAW_PARAMS: + ret = isif_get_params(sd, arg); + break; + + default: + ret = -ENOIOCTLCMD; + } + return ret; +} + +static void isif_config_gain_offset(struct vpfe_isif_device *isif) +{ + struct vpfe_isif_gain_offsets_adj *gain_off_ptr = + &isif->isif_cfg.bayer.config_params.gain_offset; + void *__iomem base = isif->isif_cfg.base_addr; + u32 val; + + val = ((gain_off_ptr->gain_sdram_en & 1) << GAIN_SDRAM_EN_SHIFT) | + ((gain_off_ptr->gain_ipipe_en & 1) << GAIN_IPIPE_EN_SHIFT) | + ((gain_off_ptr->gain_h3a_en & 1) << GAIN_H3A_EN_SHIFT) | + ((gain_off_ptr->offset_sdram_en & 1) << OFST_SDRAM_EN_SHIFT) | + ((gain_off_ptr->offset_ipipe_en & 1) << OFST_IPIPE_EN_SHIFT) | + ((gain_off_ptr->offset_h3a_en & 1) << OFST_H3A_EN_SHIFT); + isif_merge(base, GAIN_OFFSET_EN_MASK, val, CGAMMAWD); + + isif_write(base, isif->isif_cfg.isif_gain_params.cr_gain, CRGAIN); + isif_write(base, isif->isif_cfg.isif_gain_params.cgr_gain, CGRGAIN); + isif_write(base, isif->isif_cfg.isif_gain_params.cgb_gain, CGBGAIN); + isif_write(base, isif->isif_cfg.isif_gain_params.cb_gain, CBGAIN); + isif_write(base, isif->isif_cfg.isif_gain_params.offset & OFFSET_MASK, + COFSTA); + +} + +static void isif_config_bclamp(struct vpfe_isif_device *isif, + struct vpfe_isif_black_clamp *bc) +{ + u32 val; + + /** + * DC Offset is always added to image data irrespective of bc enable + * status + */ + val = bc->dc_offset & ISIF_BC_DCOFFSET_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLDCOFST); + + if (!bc->en) + return; + + val = (bc->bc_mode_color & ISIF_BC_MODE_COLOR_MASK) << + ISIF_BC_MODE_COLOR_SHIFT; + + /* Enable BC and horizontal clamp caculation paramaters */ + val = val | 1 | ((bc->horz.mode & ISIF_HORZ_BC_MODE_MASK) << + ISIF_HORZ_BC_MODE_SHIFT); + + isif_write(isif->isif_cfg.base_addr, val, CLAMPCFG); + + if (bc->horz.mode != VPFE_ISIF_HORZ_BC_DISABLE) { + /* + * Window count for calculation + * Base window selection + * pixel limit + * Horizontal size of window + * vertical size of the window + * Horizontal start position of the window + * Vertical start position of the window + */ + val = (bc->horz.win_count_calc & ISIF_HORZ_BC_WIN_COUNT_MASK) | + ((bc->horz.base_win_sel_calc & 1) << + ISIF_HORZ_BC_WIN_SEL_SHIFT) | + ((bc->horz.clamp_pix_limit & 1) << + ISIF_HORZ_BC_PIX_LIMIT_SHIFT) | + ((bc->horz.win_h_sz_calc & + ISIF_HORZ_BC_WIN_H_SIZE_MASK) << + ISIF_HORZ_BC_WIN_H_SIZE_SHIFT) | + ((bc->horz.win_v_sz_calc & + ISIF_HORZ_BC_WIN_V_SIZE_MASK) << + ISIF_HORZ_BC_WIN_V_SIZE_SHIFT); + + isif_write(isif->isif_cfg.base_addr, val, CLHWIN0); + + val = bc->horz.win_start_h_calc & ISIF_HORZ_BC_WIN_START_H_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLHWIN1); + + val = bc->horz.win_start_v_calc & ISIF_HORZ_BC_WIN_START_V_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLHWIN2); + } + + /* vertical clamp caculation paramaters */ + /* OB H Valid */ + val = bc->vert.ob_h_sz_calc & ISIF_VERT_BC_OB_H_SZ_MASK; + + /* Reset clamp value sel for previous line */ + val |= (bc->vert.reset_val_sel & ISIF_VERT_BC_RST_VAL_SEL_MASK) << + ISIF_VERT_BC_RST_VAL_SEL_SHIFT; + + /* Line average coefficient */ + val |= bc->vert.line_ave_coef << ISIF_VERT_BC_LINE_AVE_COEF_SHIFT; + isif_write(isif->isif_cfg.base_addr, val, CLVWIN0); + + /* Configured reset value */ + if (bc->vert.reset_val_sel == VPFE_ISIF_VERT_BC_USE_CONFIG_CLAMP_VAL) { + val = bc->vert.reset_clamp_val & ISIF_VERT_BC_RST_VAL_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLVRV); + } + + /* Optical Black horizontal start position */ + val = bc->vert.ob_start_h & ISIF_VERT_BC_OB_START_HORZ_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLVWIN1); + + /* Optical Black vertical start position */ + val = bc->vert.ob_start_v & ISIF_VERT_BC_OB_START_VERT_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLVWIN2); + + val = bc->vert.ob_v_sz_calc & ISIF_VERT_BC_OB_VERT_SZ_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLVWIN3); + + /* Vertical start position for BC subtraction */ + val = bc->vert_start_sub & ISIF_BC_VERT_START_SUB_V_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLSV); +} + +/* This function will configure the window size to be capture in ISIF reg */ +static void +isif_setwin(struct vpfe_isif_device *isif, struct v4l2_rect *image_win, + enum isif_frmfmt frm_fmt, int ppc, int mode) +{ + int horz_nr_pixels; + int vert_nr_lines; + int horz_start; + int vert_start; + int mid_img; + + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = (image_win->width << (ppc - 1)) - 1; + + /* Writing the horizontal info into the registers */ + isif_write(isif->isif_cfg.base_addr, + horz_start & START_PX_HOR_MASK, SPH); + isif_write(isif->isif_cfg.base_addr, + horz_nr_pixels & NUM_PX_HOR_MASK, LNH); + vert_start = image_win->top; + + if (frm_fmt == ISIF_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* To account for VD since line 0 doesn't have any data */ + vert_start += 1; + } else { + /* To account for VD since line 0 doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* configure VDINT0 and VDINT1 */ + mid_img = vert_start + (image_win->height / 2); + isif_write(isif->isif_cfg.base_addr, mid_img, VDINT1); + } + + if (!mode) + isif_write(isif->isif_cfg.base_addr, 0, VDINT0); + else + isif_write(isif->isif_cfg.base_addr, vert_nr_lines, VDINT0); + isif_write(isif->isif_cfg.base_addr, + vert_start & START_VER_ONE_MASK, SLV0); + isif_write(isif->isif_cfg.base_addr, + vert_start & START_VER_TWO_MASK, SLV1); + isif_write(isif->isif_cfg.base_addr, + vert_nr_lines & NUM_LINES_VER, LNV); +} + +#define DM365_ISIF_DFCMWR_MEMORY_WRITE 1 +#define DM365_ISIF_DFCMRD_MEMORY_READ 0x2 + +static void +isif_config_dfc(struct vpfe_isif_device *isif, struct vpfe_isif_dfc *vdfc) +{ +#define DFC_WRITE_WAIT_COUNT 1000 + u32 count = DFC_WRITE_WAIT_COUNT; + u32 val; + int i; + + if (!vdfc->en) + return; + + /* Correction mode */ + val = (vdfc->corr_mode & ISIF_VDFC_CORR_MOD_MASK) << + ISIF_VDFC_CORR_MOD_SHIFT; + + /* Correct whole line or partial */ + if (vdfc->corr_whole_line) + val |= 1 << ISIF_VDFC_CORR_WHOLE_LN_SHIFT; + + /* level shift value */ + val |= (vdfc->def_level_shift & ISIF_VDFC_LEVEL_SHFT_MASK) << + ISIF_VDFC_LEVEL_SHFT_SHIFT; + + isif_write(isif->isif_cfg.base_addr, val, DFCCTL); + + /* Defect saturation level */ + val = vdfc->def_sat_level & ISIF_VDFC_SAT_LEVEL_MASK; + isif_write(isif->isif_cfg.base_addr, val, VDFSATLV); + + isif_write(isif->isif_cfg.base_addr, vdfc->table[0].pos_vert & + ISIF_VDFC_POS_MASK, DFCMEM0); + isif_write(isif->isif_cfg.base_addr, vdfc->table[0].pos_horz & + ISIF_VDFC_POS_MASK, DFCMEM1); + if (vdfc->corr_mode == VPFE_ISIF_VDFC_NORMAL || + vdfc->corr_mode == VPFE_ISIF_VDFC_HORZ_INTERPOL_IF_SAT) { + isif_write(isif->isif_cfg.base_addr, + vdfc->table[0].level_at_pos, DFCMEM2); + isif_write(isif->isif_cfg.base_addr, + vdfc->table[0].level_up_pixels, DFCMEM3); + isif_write(isif->isif_cfg.base_addr, + vdfc->table[0].level_low_pixels, DFCMEM4); + } + + val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); + /* set DFCMARST and set DFCMWR */ + val |= 1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT; + val |= 1; + isif_write(isif->isif_cfg.base_addr, val, DFCMEMCTL); + + while (count && (isif_read(isif->isif_cfg.base_addr, DFCMEMCTL) & 0x01)) + count--; + + val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); + if (!count) { + pr_debug("defect table write timeout !!\n"); + return; + } + + for (i = 1; i < vdfc->num_vdefects; i++) { + isif_write(isif->isif_cfg.base_addr, vdfc->table[i].pos_vert & + ISIF_VDFC_POS_MASK, DFCMEM0); + + isif_write(isif->isif_cfg.base_addr, vdfc->table[i].pos_horz & + ISIF_VDFC_POS_MASK, DFCMEM1); + + if (vdfc->corr_mode == VPFE_ISIF_VDFC_NORMAL || + vdfc->corr_mode == VPFE_ISIF_VDFC_HORZ_INTERPOL_IF_SAT) { + isif_write(isif->isif_cfg.base_addr, + vdfc->table[i].level_at_pos, DFCMEM2); + isif_write(isif->isif_cfg.base_addr, + vdfc->table[i].level_up_pixels, DFCMEM3); + isif_write(isif->isif_cfg.base_addr, + vdfc->table[i].level_low_pixels, DFCMEM4); + } + val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); + /* clear DFCMARST and set DFCMWR */ + val &= ~(1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT); + val |= 1; + isif_write(isif->isif_cfg.base_addr, val, DFCMEMCTL); + + count = DFC_WRITE_WAIT_COUNT; + while (count && (isif_read(isif->isif_cfg.base_addr, + DFCMEMCTL) & 0x01)) + count--; + + val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); + if (!count) { + pr_debug("defect table write timeout !!\n"); + return; + } + } + if (vdfc->num_vdefects < VPFE_ISIF_VDFC_TABLE_SIZE) { + /* Extra cycle needed */ + isif_write(isif->isif_cfg.base_addr, 0, DFCMEM0); + isif_write(isif->isif_cfg.base_addr, + DM365_ISIF_MAX_DFCMEM1, DFCMEM1); + isif_write(isif->isif_cfg.base_addr, + DM365_ISIF_DFCMWR_MEMORY_WRITE, DFCMEMCTL); + } + /* enable VDFC */ + isif_merge(isif->isif_cfg.base_addr, (1 << ISIF_VDFC_EN_SHIFT), + (1 << ISIF_VDFC_EN_SHIFT), DFCCTL); + + isif_merge(isif->isif_cfg.base_addr, (1 << ISIF_VDFC_EN_SHIFT), + (0 << ISIF_VDFC_EN_SHIFT), DFCCTL); + + isif_write(isif->isif_cfg.base_addr, 0x6, DFCMEMCTL); + for (i = 0 ; i < vdfc->num_vdefects; i++) { + count = DFC_WRITE_WAIT_COUNT; + while (count && + (isif_read(isif->isif_cfg.base_addr, DFCMEMCTL) & 0x2)) + count--; + val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); + if (!count) { + pr_debug("defect table write timeout !!\n"); + return; + } + isif_write(isif->isif_cfg.base_addr, + DM365_ISIF_DFCMRD_MEMORY_READ, DFCMEMCTL); + } +} + +static void +isif_config_csc(struct vpfe_isif_device *isif, struct vpfe_isif_df_csc *df_csc) +{ + u32 val1; + u32 val2; + u32 i; + + if (!df_csc->csc.en) { + isif_write(isif->isif_cfg.base_addr, 0, CSCCTL); + return; + } + /* initialize all bits to 0 */ + val1 = 0; + for (i = 0; i < VPFE_ISIF_CSC_NUM_COEFF; i++) { + if ((i % 2) == 0) { + /* CSCM - LSB */ + val1 = ((df_csc->csc.coeff[i].integer & + ISIF_CSC_COEF_INTEG_MASK) << + ISIF_CSC_COEF_INTEG_SHIFT) | + ((df_csc->csc.coeff[i].decimal & + ISIF_CSC_COEF_DECIMAL_MASK)); + } else { + + /* CSCM - MSB */ + val2 = ((df_csc->csc.coeff[i].integer & + ISIF_CSC_COEF_INTEG_MASK) << + ISIF_CSC_COEF_INTEG_SHIFT) | + ((df_csc->csc.coeff[i].decimal & + ISIF_CSC_COEF_DECIMAL_MASK)); + val2 <<= ISIF_CSCM_MSB_SHIFT; + val2 |= val1; + isif_write(isif->isif_cfg.base_addr, val2, + (CSCM0 + ((i-1) << 1))); + } + } + /* program the active area */ + isif_write(isif->isif_cfg.base_addr, df_csc->start_pix & + ISIF_DF_CSC_SPH_MASK, FMTSPH); + /* + * one extra pixel as required for CSC. Actually number of + * pixel - 1 should be configured in this register. So we + * need to subtract 1 before writing to FMTSPH, but we will + * not do this since csc requires one extra pixel + */ + isif_write(isif->isif_cfg.base_addr, df_csc->num_pixels & + ISIF_DF_CSC_SPH_MASK, FMTLNH); + isif_write(isif->isif_cfg.base_addr, df_csc->start_line & + ISIF_DF_CSC_SPH_MASK, FMTSLV); + /* + * one extra line as required for CSC. See reason documented for + * num_pixels + */ + isif_write(isif->isif_cfg.base_addr, df_csc->num_lines & + ISIF_DF_CSC_SPH_MASK, FMTLNV); + /* Enable CSC */ + isif_write(isif->isif_cfg.base_addr, 1, CSCCTL); +} + +static void +isif_config_linearization(struct vpfe_isif_device *isif, + struct vpfe_isif_linearize *linearize) +{ + u32 val; + u32 i; + + if (!linearize->en) { + isif_write(isif->isif_cfg.base_addr, 0, LINCFG0); + return; + } + /* shift value for correction */ + val = (linearize->corr_shft & ISIF_LIN_CORRSFT_MASK) << + ISIF_LIN_CORRSFT_SHIFT; + /* enable */ + val |= 1; + isif_write(isif->isif_cfg.base_addr, val, LINCFG0); + /* Scale factor */ + val = (linearize->scale_fact.integer & 1) << + ISIF_LIN_SCALE_FACT_INTEG_SHIFT; + val |= linearize->scale_fact.decimal & ISIF_LIN_SCALE_FACT_DECIMAL_MASK; + isif_write(isif->isif_cfg.base_addr, val, LINCFG1); + + for (i = 0; i < VPFE_ISIF_LINEAR_TAB_SIZE; i++) { + val = linearize->table[i] & ISIF_LIN_ENTRY_MASK; + if (i%2) + isif_regw_lin_tbl(isif, val, ((i >> 1) << 2), 1); + else + isif_regw_lin_tbl(isif, val, ((i >> 1) << 2), 0); + } +} + +static void +isif_config_culling(struct vpfe_isif_device *isif, struct vpfe_isif_cul *cul) +{ + u32 val; + + /* Horizontal pattern */ + val = cul->hcpat_even << CULL_PAT_EVEN_LINE_SHIFT; + val |= cul->hcpat_odd; + isif_write(isif->isif_cfg.base_addr, val, CULH); + /* vertical pattern */ + isif_write(isif->isif_cfg.base_addr, cul->vcpat, CULV); + /* LPF */ + isif_merge(isif->isif_cfg.base_addr, ISIF_LPF_MASK << ISIF_LPF_SHIFT, + cul->en_lpf << ISIF_LPF_SHIFT, MODESET); +} + +static int isif_get_pix_fmt(u32 mbus_code) +{ + switch (mbus_code) { + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_SGRBG12_1X12: + return ISIF_PIXFMT_RAW; + + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_YUYV10_2X10: + case V4L2_MBUS_FMT_Y8_1X8: + return ISIF_PIXFMT_YCBCR_8BIT; + + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_YUYV10_1X20: + return ISIF_PIXFMT_YCBCR_16BIT; + + default: + break; + } + return -EINVAL; +} + +#define ISIF_INTERLACE_INVERSE_MODE 0x4b6d +#define ISIF_INTERLACE_NON_INVERSE_MODE 0x0b6d +#define ISIF_PROGRESSIVE_INVERSE_MODE 0x4000 +#define ISIF_PROGRESSIVE_NON_INVERSE_MODE 0x0000 + +static int isif_config_raw(struct v4l2_subdev *sd, int mode) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct isif_params_raw *params = &isif->isif_cfg.bayer; + struct vpfe_isif_raw_config *module_params = + &isif->isif_cfg.bayer.config_params; + struct v4l2_mbus_framefmt *format; + int pix_fmt; + u32 val; + + format = &isif->formats[ISIF_PAD_SINK]; + + /* In case of user has set BT656IF earlier, it should be reset + * when configuring for raw input. + */ + isif_write(isif->isif_cfg.base_addr, 0, REC656IF); + /* Configure CCDCFG register + * Set CCD Not to swap input since input is RAW data + * Set FID detection function to Latch at V-Sync + * Set WENLOG - isif valid area + * Set TRGSEL + * Set EXTRG + * Packed to 8 or 16 bits + */ + val = ISIF_YCINSWP_RAW | ISIF_CCDCFG_FIDMD_LATCH_VSYNC | + ISIF_CCDCFG_WENLOG_AND | ISIF_CCDCFG_TRGSEL_WEN | + ISIF_CCDCFG_EXTRG_DISABLE | (isif->isif_cfg.data_pack & + ISIF_DATA_PACK_MASK); + isif_write(isif->isif_cfg.base_addr, val, CCDCFG); + + pix_fmt = isif_get_pix_fmt(format->code); + if (pix_fmt < 0) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + /* + * Configure the vertical sync polarity(MODESET.VDPOL) + * Configure the horizontal sync polarity (MODESET.HDPOL) + * Configure frame id polarity (MODESET.FLDPOL) + * Configure data polarity + * Configure External WEN Selection + * Configure frame format(progressive or interlace) + * Configure pixel format (Input mode) + * Configure the data shift + */ + val = ISIF_VDHDOUT_INPUT | ((params->vd_pol & ISIF_VD_POL_MASK) << + ISIF_VD_POL_SHIFT) | ((params->hd_pol & ISIF_HD_POL_MASK) << + ISIF_HD_POL_SHIFT) | ((params->fid_pol & ISIF_FID_POL_MASK) << + ISIF_FID_POL_SHIFT) | ((ISIF_DATAPOL_NORMAL & + ISIF_DATAPOL_MASK) << ISIF_DATAPOL_SHIFT) | ((ISIF_EXWEN_DISABLE & + ISIF_EXWEN_MASK) << ISIF_EXWEN_SHIFT) | ((params->frm_fmt & + ISIF_FRM_FMT_MASK) << ISIF_FRM_FMT_SHIFT) | ((pix_fmt & + ISIF_INPUT_MASK) << ISIF_INPUT_SHIFT); + + /* currently only V4L2_MBUS_FMT_SGRBG12_1X12 is + * supported. shift appropriately depending on + * different MBUS fmt's added + */ + if (format->code == V4L2_MBUS_FMT_SGRBG12_1X12) + val |= ((VPFE_ISIF_NO_SHIFT & + ISIF_DATASFT_MASK) << ISIF_DATASFT_SHIFT); + + isif_write(isif->isif_cfg.base_addr, val, MODESET); + /* + * Configure GAMMAWD register + * CFA pattern setting + */ + val = (params->cfa_pat & ISIF_GAMMAWD_CFA_MASK) << + ISIF_GAMMAWD_CFA_SHIFT; + /* Gamma msb */ + if (params->v4l2_pix_fmt == V4L2_PIX_FMT_SGRBG10ALAW8) + val = val | ISIF_ALAW_ENABLE; + + val = val | ((params->data_msb & ISIF_ALAW_GAMA_WD_MASK) << + ISIF_ALAW_GAMA_WD_SHIFT); + + isif_write(isif->isif_cfg.base_addr, val, CGAMMAWD); + /* Configure DPCM compression settings */ + if (params->v4l2_pix_fmt == V4L2_PIX_FMT_SGRBG10DPCM8) { + val = 1 << ISIF_DPCM_EN_SHIFT; + val |= (params->dpcm_predictor & + ISIF_DPCM_PREDICTOR_MASK) << ISIF_DPCM_PREDICTOR_SHIFT; + } + isif_write(isif->isif_cfg.base_addr, val, MISC); + /* Configure Gain & Offset */ + isif_config_gain_offset(isif); + /* Configure Color pattern */ + if (format->code == V4L2_MBUS_FMT_SGRBG12_1X12) + val = isif_sgrbg_pattern; + else + /* default set to rggb */ + val = isif_srggb_pattern; + + isif_write(isif->isif_cfg.base_addr, val, CCOLP); + + /* Configure HSIZE register */ + val = (params->horz_flip_en & ISIF_HSIZE_FLIP_MASK) << + ISIF_HSIZE_FLIP_SHIFT; + + /* calculate line offset in 32 bytes based on pack value */ + if (isif->isif_cfg.data_pack == ISIF_PACK_8BIT) + val |= ((params->win.width + 31) >> 5) & ISIF_LINEOFST_MASK; + else if (isif->isif_cfg.data_pack == ISIF_PACK_12BIT) + val |= ((((params->win.width + (params->win.width >> 2)) + + 31) >> 5) & ISIF_LINEOFST_MASK); + else + val |= (((params->win.width * 2) + 31) >> 5) & + ISIF_LINEOFST_MASK; + isif_write(isif->isif_cfg.base_addr, val, HSIZE); + /* Configure SDOFST register */ + if (params->frm_fmt == ISIF_FRMFMT_INTERLACED) { + if (params->image_invert_en) + /* For interlace inverse mode */ + isif_write(isif->isif_cfg.base_addr, + ISIF_INTERLACE_INVERSE_MODE, SDOFST); + else + /* For interlace non inverse mode */ + isif_write(isif->isif_cfg.base_addr, + ISIF_INTERLACE_NON_INVERSE_MODE, SDOFST); + } else if (params->frm_fmt == ISIF_FRMFMT_PROGRESSIVE) { + if (params->image_invert_en) + isif_write(isif->isif_cfg.base_addr, + ISIF_PROGRESSIVE_INVERSE_MODE, SDOFST); + else + /* For progessive non inverse mode */ + isif_write(isif->isif_cfg.base_addr, + ISIF_PROGRESSIVE_NON_INVERSE_MODE, SDOFST); + } + /* Configure video window */ + isif_setwin(isif, ¶ms->win, params->frm_fmt, 1, mode); + /* Configure Black Clamp */ + isif_config_bclamp(isif, &module_params->bclamp); + /* Configure Vertical Defection Pixel Correction */ + isif_config_dfc(isif, &module_params->dfc); + if (!module_params->df_csc.df_or_csc) + /* Configure Color Space Conversion */ + isif_config_csc(isif, &module_params->df_csc); + + isif_config_linearization(isif, &module_params->linearize); + /* Configure Culling */ + isif_config_culling(isif, &module_params->culling); + /* Configure Horizontal and vertical offsets(DFC,LSC,Gain) */ + val = module_params->horz_offset & ISIF_DATA_H_OFFSET_MASK; + isif_write(isif->isif_cfg.base_addr, val, DATAHOFST); + + val = module_params->vert_offset & ISIF_DATA_V_OFFSET_MASK; + isif_write(isif->isif_cfg.base_addr, val, DATAVOFST); + + return 0; +} + +#define DM365_ISIF_HSIZE_MASK 0xffffffe0 +#define DM365_ISIF_SDOFST_2_LINES 0x00000249 + +/* This function will configure ISIF for YCbCr parameters. */ +static int isif_config_ycbcr(struct v4l2_subdev *sd, int mode) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct isif_ycbcr_config *params = &isif->isif_cfg.ycbcr; + struct v4l2_mbus_framefmt *format; + int pix_fmt; + u32 modeset; + u32 ccdcfg; + + format = &isif->formats[ISIF_PAD_SINK]; + /* + * first reset the ISIF + * all registers have default values after reset + * This is important since we assume default values to be set in + * a lot of registers that we didn't touch + */ + /* start with all bits zero */ + ccdcfg = modeset = 0; + pix_fmt = isif_get_pix_fmt(format->code); + if (pix_fmt < 0) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + /* configure pixel format or input mode */ + modeset = modeset | ((pix_fmt & ISIF_INPUT_MASK) << + ISIF_INPUT_SHIFT) | ((params->frm_fmt & ISIF_FRM_FMT_MASK) << + ISIF_FRM_FMT_SHIFT) | (((params->fid_pol & + ISIF_FID_POL_MASK) << ISIF_FID_POL_SHIFT)) | + (((params->hd_pol & ISIF_HD_POL_MASK) << ISIF_HD_POL_SHIFT)) | + (((params->vd_pol & ISIF_VD_POL_MASK) << ISIF_VD_POL_SHIFT)); + /* pack the data to 8-bit CCDCCFG */ + switch (format->code) { + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_UYVY8_2X8: + if (pix_fmt != ISIF_PIXFMT_YCBCR_8BIT) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + modeset |= ((VPFE_PINPOL_NEGATIVE & ISIF_VD_POL_MASK) << + ISIF_VD_POL_SHIFT); + isif_write(isif->isif_cfg.base_addr, 3, REC656IF); + ccdcfg = ccdcfg | ISIF_PACK_8BIT | ISIF_YCINSWP_YCBCR; + break; + + case V4L2_MBUS_FMT_YUYV10_2X10: + if (pix_fmt != ISIF_PIXFMT_YCBCR_8BIT) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + /* setup BT.656, embedded sync */ + isif_write(isif->isif_cfg.base_addr, 3, REC656IF); + /* enable 10 bit mode in ccdcfg */ + ccdcfg = ccdcfg | ISIF_PACK_8BIT | ISIF_YCINSWP_YCBCR | + ISIF_BW656_ENABLE; + break; + + case V4L2_MBUS_FMT_YUYV10_1X20: + if (pix_fmt != ISIF_PIXFMT_YCBCR_16BIT) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + isif_write(isif->isif_cfg.base_addr, 3, REC656IF); + break; + + case V4L2_MBUS_FMT_Y8_1X8: + ccdcfg |= ISIF_PACK_8BIT; + ccdcfg |= ISIF_YCINSWP_YCBCR; + if (pix_fmt != ISIF_PIXFMT_YCBCR_8BIT) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + break; + + case V4L2_MBUS_FMT_YUYV8_1X16: + if (pix_fmt != ISIF_PIXFMT_YCBCR_16BIT) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + break; + + default: + /* should never come here */ + pr_debug("Invalid interface type\n"); + return -EINVAL; + } + isif_write(isif->isif_cfg.base_addr, modeset, MODESET); + /* Set up pix order */ + ccdcfg |= (params->pix_order & ISIF_PIX_ORDER_MASK) << + ISIF_PIX_ORDER_SHIFT; + isif_write(isif->isif_cfg.base_addr, ccdcfg, CCDCFG); + /* configure video window */ + if (format->code == V4L2_MBUS_FMT_YUYV10_1X20 || + format->code == V4L2_MBUS_FMT_YUYV8_1X16) + isif_setwin(isif, ¶ms->win, params->frm_fmt, 1, mode); + else + isif_setwin(isif, ¶ms->win, params->frm_fmt, 2, mode); + + /* + * configure the horizontal line offset + * this is done by rounding up width to a multiple of 16 pixels + * and multiply by two to account for y:cb:cr 4:2:2 data + */ + isif_write(isif->isif_cfg.base_addr, + ((((params->win.width * 2) + 31) & + DM365_ISIF_HSIZE_MASK) >> 5), HSIZE); + + /* configure the memory line offset */ + if (params->frm_fmt == ISIF_FRMFMT_INTERLACED && + params->buf_type == ISIF_BUFTYPE_FLD_INTERLEAVED) + /* two fields are interleaved in memory */ + isif_write(isif->isif_cfg.base_addr, + DM365_ISIF_SDOFST_2_LINES, SDOFST); + return 0; +} + +static int isif_configure(struct v4l2_subdev *sd, int mode) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = &isif->formats[ISIF_PAD_SINK]; + + switch (format->code) { + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_SGRBG12_1X12: + return isif_config_raw(sd, mode); + + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_YUYV10_2X10: + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_YUYV10_1X20: + return isif_config_ycbcr(sd, mode); + + default: + break; + } + return -EINVAL; +} + +/* + * isif_set_stream() - Enable/Disable streaming on the ISIF module + * @sd: VPFE ISIF V4L2 subdevice + * @enable: Enable/disable stream + */ +static int isif_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + int ret; + + if (enable) { + ret = isif_configure(sd, + (isif->output == ISIF_OUTPUT_MEMORY) ? 0 : 1); + if (ret) + return ret; + if (isif->output == ISIF_OUTPUT_MEMORY) + isif_enable_output_to_sdram(isif, 1); + isif_enable(isif, 1); + } else { + isif_enable(isif, 0); + isif_enable_output_to_sdram(isif, 0); + } + + return 0; +} + +/* + * __isif_get_format() - helper function for getting isif format + * @isif: pointer to isif private structure. + * @pad: pad number. + * @fh: V4L2 subdev file handle. + * @which: wanted subdev format. + */ +static struct v4l2_mbus_framefmt * +__isif_get_format(struct vpfe_isif_device *isif, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_subdev_format fmt; + + fmt.pad = pad; + fmt.which = which; + + return v4l2_subdev_get_try_format(fh, pad); + } + return &isif->formats[pad]; +} + +/* +* isif_set_format() - set format on pad +* @sd : VPFE ISIF device +* @fh : V4L2 subdev file handle +* @fmt : pointer to v4l2 subdev format structure +* +* Return 0 on success or -EINVAL if format or pad is invalid +*/ +static int +isif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(isif); + struct v4l2_mbus_framefmt *format; + + format = __isif_get_format(isif, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + isif_try_format(isif, fh, fmt); + memcpy(format, &fmt->format, sizeof(*format)); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (fmt->pad == ISIF_PAD_SOURCE) + return isif_config_format(vpfe_dev, fmt->pad); + + return 0; +} + +/* + * isif_get_format() - Retrieve the video format on a pad + * @sd: VPFE ISIF V4L2 subdevice + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * + * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond + * to the format type. + */ +static int +isif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __isif_get_format(vpfe_isif, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + memcpy(&fmt->format, format, sizeof(fmt->format)); + + return 0; +} + +/* + * isif_enum_frame_size() - enum frame sizes on pads + * @sd: VPFE isif V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_frame_size_enum structure + */ +static int +isif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct v4l2_subdev_format format; + + if (fse->index != 0) + return -EINVAL; + + format.pad = fse->pad; + format.format.code = fse->code; + format.format.width = 1; + format.format.height = 1; + format.which = V4L2_SUBDEV_FORMAT_TRY; + isif_try_format(isif, fh, &format); + fse->min_width = format.format.width; + fse->min_height = format.format.height; + + if (format.format.code != fse->code) + return -EINVAL; + + format.pad = fse->pad; + format.format.code = fse->code; + format.format.width = -1; + format.format.height = -1; + format.which = V4L2_SUBDEV_FORMAT_TRY; + isif_try_format(isif, fh, &format); + fse->max_width = format.format.width; + fse->max_height = format.format.height; + + return 0; +} + +/* + * isif_enum_mbus_code() - enum mbus codes for pads + * @sd: VPFE isif V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + */ +static int +isif_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + switch (code->pad) { + case ISIF_PAD_SINK: + case ISIF_PAD_SOURCE: + if (code->index >= ARRAY_SIZE(isif_fmts)) + return -EINVAL; + code->code = isif_fmts[code->index]; + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * isif_pad_set_crop() - set crop rectangle on pad + * @sd: VPFE isif V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * + * Return 0 on success, -EINVAL if pad is invalid + */ +static int +isif_pad_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + /* check wether its a valid pad */ + if (crop->pad != ISIF_PAD_SINK) + return -EINVAL; + + format = __isif_get_format(vpfe_isif, fh, crop->pad, crop->which); + if (format == NULL) + return -EINVAL; + + /* check wether crop rect is within limits */ + if (crop->rect.top < 0 || crop->rect.left < 0 || + (crop->rect.left + crop->rect.width > + vpfe_isif->formats[ISIF_PAD_SINK].width) || + (crop->rect.top + crop->rect.height > + vpfe_isif->formats[ISIF_PAD_SINK].height)) { + crop->rect.left = 0; + crop->rect.top = 0; + crop->rect.width = format->width; + crop->rect.height = format->height; + } + /* adjust the width to 16 pixel boundry */ + crop->rect.width = ((crop->rect.width + 15) & ~0xf); + vpfe_isif->crop = crop->rect; + if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + isif_set_image_window(vpfe_isif); + } else { + struct v4l2_rect *rect; + + rect = v4l2_subdev_get_try_crop(fh, ISIF_PAD_SINK); + memcpy(rect, &vpfe_isif->crop, sizeof(*rect)); + } + return 0; +} + +/* + * isif_pad_get_crop() - get crop rectangle on pad + * @sd: VPFE isif V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * + * Return 0 on success, -EINVAL if pad is invalid + */ +static int +isif_pad_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd); + + /* check wether its a valid pad */ + if (crop->pad != ISIF_PAD_SINK) + return -EINVAL; + + if (crop->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_rect *rect; + rect = v4l2_subdev_get_try_crop(fh, ISIF_PAD_SINK); + memcpy(&crop->rect, rect, sizeof(*rect)); + } else { + crop->rect = vpfe_isif->crop; + } + + return 0; +} + +/* + * isif_init_formats() - Initialize formats on all pads + * @sd: VPFE isif V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int +isif_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + struct v4l2_subdev_crop crop; + + memset(&format, 0, sizeof(format)); + format.pad = ISIF_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG12_1X12; + format.format.width = MAX_WIDTH; + format.format.height = MAX_HEIGHT; + isif_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = ISIF_PAD_SOURCE; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG12_1X12; + format.format.width = MAX_WIDTH; + format.format.height = MAX_HEIGHT; + isif_set_format(sd, fh, &format); + + memset(&crop, 0, sizeof(crop)); + crop.pad = ISIF_PAD_SINK; + crop.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + crop.rect.width = MAX_WIDTH; + crop.rect.height = MAX_HEIGHT; + isif_pad_set_crop(sd, fh, &crop); + + return 0; +} + +/* subdev core operations */ +static const struct v4l2_subdev_core_ops isif_v4l2_core_ops = { + .ioctl = isif_ioctl, +}; + +/* subdev file operations */ +static const struct v4l2_subdev_internal_ops isif_v4l2_internal_ops = { + .open = isif_init_formats, +}; + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops isif_v4l2_video_ops = { + .s_stream = isif_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops isif_v4l2_pad_ops = { + .enum_mbus_code = isif_enum_mbus_code, + .enum_frame_size = isif_enum_frame_size, + .get_fmt = isif_get_format, + .set_fmt = isif_set_format, + .set_crop = isif_pad_set_crop, + .get_crop = isif_pad_get_crop, +}; + +/* subdev operations */ +static const struct v4l2_subdev_ops isif_v4l2_ops = { + .core = &isif_v4l2_core_ops, + .video = &isif_v4l2_video_ops, + .pad = &isif_v4l2_pad_ops, +}; + +/* + * Media entity operations + */ + +/* + * isif_link_setup() - Setup isif connections + * @entity: isif media entity + * @local: Pad at the local end of the link + * @remote: Pad at the remote end of the link + * @flags: Link flags + * + * return -EINVAL or zero on success + */ +static int +isif_link_setup(struct media_entity *entity, const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + + switch (local->index | media_entity_type(remote->entity)) { + case ISIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* read from decoder/sensor */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + isif->input = ISIF_INPUT_NONE; + break; + } + if (isif->input != ISIF_INPUT_NONE) + return -EBUSY; + isif->input = ISIF_INPUT_PARALLEL; + break; + + case ISIF_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + /* write to memory */ + if (flags & MEDIA_LNK_FL_ENABLED) + isif->output = ISIF_OUTPUT_MEMORY; + else + isif->output = ISIF_OUTPUT_NONE; + break; + + case ISIF_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + if (flags & MEDIA_LNK_FL_ENABLED) + isif->output = ISIF_OUTPUT_IPIPEIF; + else + isif->output = ISIF_OUTPUT_NONE; + break; + + default: + return -EINVAL; + } + + return 0; +} +static const struct media_entity_operations isif_media_ops = { + .link_setup = isif_link_setup, +}; + +/* + * vpfe_isif_unregister_entities() - isif unregister entity + * @isif - pointer to isif subdevice structure. + */ +void vpfe_isif_unregister_entities(struct vpfe_isif_device *isif) +{ + vpfe_video_unregister(&isif->video_out); + /* cleanup entity */ + media_entity_cleanup(&isif->subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&isif->subdev); +} + +static void isif_restore_defaults(struct vpfe_isif_device *isif) +{ + enum vpss_ccdc_source_sel source = VPSS_CCDCIN; + int i; + + memset(&isif->isif_cfg.bayer.config_params, 0, + sizeof(struct vpfe_isif_raw_config)); + + isif->isif_cfg.bayer.config_params.linearize.corr_shft = + VPFE_ISIF_NO_SHIFT; + isif->isif_cfg.bayer.config_params.linearize.scale_fact.integer = 1; + isif->isif_cfg.bayer.config_params.culling.hcpat_odd = + ISIF_CULLING_HCAPT_ODD; + isif->isif_cfg.bayer.config_params.culling.hcpat_even = + ISIF_CULLING_HCAPT_EVEN; + isif->isif_cfg.bayer.config_params.culling.vcpat = ISIF_CULLING_VCAPT; + /* Enable clock to ISIF, IPIPEIF and BL */ + vpss_enable_clock(VPSS_CCDC_CLOCK, 1); + vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1); + vpss_enable_clock(VPSS_BL_CLOCK, 1); + + /* set all registers to default value */ + for (i = 0; i <= 0x1f8; i += 4) + isif_write(isif->isif_cfg.base_addr, 0, i); + /* no culling support */ + isif_write(isif->isif_cfg.base_addr, 0xffff, CULH); + isif_write(isif->isif_cfg.base_addr, 0xff, CULV); + + /* Set default offset and gain */ + isif_config_gain_offset(isif); + vpss_select_ccdc_source(source); +} + +/* + * vpfe_isif_register_entities() - isif register entity + * @isif - pointer to isif subdevice structure. + * @vdev: pointer to v4l2 device structure. + */ +int vpfe_isif_register_entities(struct vpfe_isif_device *isif, + struct v4l2_device *vdev) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(isif); + unsigned int flags; + int ret; + + /* Register the subdev */ + ret = v4l2_device_register_subdev(vdev, &isif->subdev); + if (ret < 0) + return ret; + + isif_restore_defaults(isif); + ret = vpfe_video_register(&isif->video_out, vdev); + if (ret) { + pr_err("Failed to register isif video out device\n"); + goto out_video_register; + } + isif->video_out.vpfe_dev = vpfe_dev; + flags = 0; + /* connect isif to video node */ + ret = media_entity_create_link(&isif->subdev.entity, 1, + &isif->video_out.video_dev.entity, + 0, flags); + if (ret < 0) + goto out_create_link; + return 0; +out_create_link: + vpfe_video_unregister(&isif->video_out); +out_video_register: + v4l2_device_unregister_subdev(&isif->subdev); + return ret; +} + +/* ------------------------------------------------------------------- + * V4L2 subdev control operations + */ + +static int vpfe_isif_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vpfe_isif_device *isif = + container_of(ctrl->handler, struct vpfe_isif_device, ctrls); + struct isif_oper_config *config = &isif->isif_cfg; + + switch (ctrl->id) { + case VPFE_CID_DPCM_PREDICTOR: + config->bayer.dpcm_predictor = ctrl->val; + break; + + case VPFE_ISIF_CID_CRGAIN: + config->isif_gain_params.cr_gain = ctrl->val; + break; + + case VPFE_ISIF_CID_CGRGAIN: + config->isif_gain_params.cgr_gain = ctrl->val; + break; + + case VPFE_ISIF_CID_CGBGAIN: + config->isif_gain_params.cgb_gain = ctrl->val; + break; + + case VPFE_ISIF_CID_CBGAIN: + config->isif_gain_params.cb_gain = ctrl->val; + break; + + case VPFE_ISIF_CID_GAIN_OFFSET: + config->isif_gain_params.offset = ctrl->val; + break; + + default: + return -EINVAL; + } + return 0; +} + +static const struct v4l2_ctrl_ops vpfe_isif_ctrl_ops = { + .s_ctrl = vpfe_isif_s_ctrl, +}; + +static const struct v4l2_ctrl_config vpfe_isif_dpcm_pred = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_CID_DPCM_PREDICTOR, + .name = "DPCM Predictor", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 1, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config vpfe_isif_crgain = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_ISIF_CID_CRGAIN, + .name = "CRGAIN", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config vpfe_isif_cgrgain = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_ISIF_CID_CGRGAIN, + .name = "CGRGAIN", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config vpfe_isif_cgbgain = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_ISIF_CID_CGBGAIN, + .name = "CGBGAIN", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config vpfe_isif_cbgain = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_ISIF_CID_CBGAIN, + .name = "CBGAIN", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config vpfe_isif_gain_offset = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_ISIF_CID_GAIN_OFFSET, + .name = "Gain Offset", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0, +}; + +static void isif_remove(struct vpfe_isif_device *isif, + struct platform_device *pdev) +{ + struct resource *res; + int i = 0; + + iounmap(isif->isif_cfg.base_addr); + iounmap(isif->isif_cfg.linear_tbl0_addr); + iounmap(isif->isif_cfg.linear_tbl1_addr); + + while (i < 3) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (res) + release_mem_region(res->start, + res->end - res->start + 1); + i++; + } +} + +static void isif_config_defaults(struct vpfe_isif_device *isif) +{ + isif->isif_cfg.ycbcr.v4l2_pix_fmt = V4L2_PIX_FMT_UYVY; + isif->isif_cfg.ycbcr.pix_fmt = ISIF_PIXFMT_YCBCR_8BIT; + isif->isif_cfg.ycbcr.frm_fmt = ISIF_FRMFMT_INTERLACED; + isif->isif_cfg.ycbcr.fid_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.ycbcr.vd_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.ycbcr.hd_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.ycbcr.pix_order = ISIF_PIXORDER_CBYCRY; + isif->isif_cfg.ycbcr.buf_type = ISIF_BUFTYPE_FLD_INTERLEAVED; + + isif->isif_cfg.bayer.v4l2_pix_fmt = V4L2_PIX_FMT_SGRBG10ALAW8; + isif->isif_cfg.bayer.pix_fmt = ISIF_PIXFMT_RAW; + isif->isif_cfg.bayer.frm_fmt = ISIF_FRMFMT_PROGRESSIVE; + isif->isif_cfg.bayer.fid_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.bayer.vd_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.bayer.hd_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.bayer.cfa_pat = ISIF_CFA_PAT_MOSAIC; + isif->isif_cfg.bayer.data_msb = ISIF_BIT_MSB_11; + isif->isif_cfg.data_pack = ISIF_PACK_8BIT; +} +/* + * vpfe_isif_init() - Initialize V4L2 subdev and media entity + * @isif: VPFE isif module + * @pdev: Pointer to platform device structure. + * Return 0 on success and a negative error code on failure. + */ +int vpfe_isif_init(struct vpfe_isif_device *isif, struct platform_device *pdev) +{ + struct v4l2_subdev *sd = &isif->subdev; + struct media_pad *pads = &isif->pads[0]; + struct media_entity *me = &sd->entity; + static resource_size_t res_len; + struct resource *res; + void *__iomem addr; + int status; + int i = 0; + + /* Get the ISIF base address, linearization table0 and table1 addr. */ + while (i < 3) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) { + status = -ENOENT; + goto fail_nobase_res; + } + res_len = res->end - res->start + 1; + res = request_mem_region(res->start, res_len, res->name); + if (!res) { + status = -EBUSY; + goto fail_nobase_res; + } + addr = ioremap_nocache(res->start, res_len); + if (!addr) { + status = -EBUSY; + goto fail_base_iomap; + } + switch (i) { + case 0: + /* ISIF base address */ + isif->isif_cfg.base_addr = addr; + break; + case 1: + /* ISIF linear tbl0 address */ + isif->isif_cfg.linear_tbl0_addr = addr; + break; + default: + /* ISIF linear tbl0 address */ + isif->isif_cfg.linear_tbl1_addr = addr; + break; + } + i++; + } + davinci_cfg_reg(DM365_VIN_CAM_WEN); + davinci_cfg_reg(DM365_VIN_CAM_VD); + davinci_cfg_reg(DM365_VIN_CAM_HD); + davinci_cfg_reg(DM365_VIN_YIN4_7_EN); + davinci_cfg_reg(DM365_VIN_YIN0_3_EN); + + /* queue ops */ + isif->video_out.ops = &isif_video_ops; + v4l2_subdev_init(sd, &isif_v4l2_ops); + sd->internal_ops = &isif_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI ISIF", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, isif); + sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + pads[ISIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[ISIF_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + isif->input = ISIF_INPUT_NONE; + isif->output = ISIF_OUTPUT_NONE; + me->ops = &isif_media_ops; + status = media_entity_init(me, ISIF_PADS_NUM, pads, 0); + if (status) + goto isif_fail; + isif->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + status = vpfe_video_init(&isif->video_out, "ISIF"); + if (status) { + pr_err("Failed to init isif-out video device\n"); + goto isif_fail; + } + v4l2_ctrl_handler_init(&isif->ctrls, 6); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_crgain, NULL); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_cgrgain, NULL); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_cgbgain, NULL); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_cbgain, NULL); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_gain_offset, NULL); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_dpcm_pred, NULL); + + v4l2_ctrl_handler_setup(&isif->ctrls); + sd->ctrl_handler = &isif->ctrls; + isif_config_defaults(isif); + return 0; +fail_base_iomap: + release_mem_region(res->start, res_len); + i--; +fail_nobase_res: + if (isif->isif_cfg.base_addr) + iounmap(isif->isif_cfg.base_addr); + if (isif->isif_cfg.linear_tbl0_addr) + iounmap(isif->isif_cfg.linear_tbl0_addr); + + while (i >= 0) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + release_mem_region(res->start, res_len); + i--; + } + return status; +isif_fail: + v4l2_ctrl_handler_free(&isif->ctrls); + isif_remove(isif, pdev); + return status; +} + +/* + * vpfe_isif_cleanup - isif module cleanup + * @isif: pointer to isif subdevice + * @dev: pointer to platform device structure + */ +void +vpfe_isif_cleanup(struct vpfe_isif_device *isif, struct platform_device *pdev) +{ + isif_remove(isif, pdev); +} diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.h b/drivers/staging/media/davinci_vpfe/dm365_isif.h new file mode 100644 index 000000000000..473fd2cfe350 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_isif.h @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _DAVINCI_VPFE_DM365_ISIF_H +#define _DAVINCI_VPFE_DM365_ISIF_H + +#include + +#include + +#include +#include +#include + +#include "davinci_vpfe_user.h" +#include "dm365_isif_regs.h" +#include "vpfe_video.h" + +#define ISIF_CULLING_HCAPT_ODD 0xff +#define ISIF_CULLING_HCAPT_EVEN 0xff +#define ISIF_CULLING_VCAPT 0xff + +#define ISIF_CADU_BITS 0x07ff +#define ISIF_CADL_BITS 0x0ffff + +enum isif_pixfmt { + ISIF_PIXFMT_RAW = 0, + ISIF_PIXFMT_YCBCR_16BIT = 1, + ISIF_PIXFMT_YCBCR_8BIT = 2, +}; + +enum isif_frmfmt { + ISIF_FRMFMT_PROGRESSIVE = 0, + ISIF_FRMFMT_INTERLACED = 1, +}; + +/* PIXEL ORDER IN MEMORY from LSB to MSB */ +/* only applicable for 8-bit input mode */ +enum isif_pixorder { + ISIF_PIXORDER_YCBYCR = 0, + ISIF_PIXORDER_CBYCRY = 1, +}; + +enum isif_buftype { + ISIF_BUFTYPE_FLD_INTERLEAVED = 0, + ISIF_BUFTYPE_FLD_SEPARATED = 1, +}; + +struct isif_ycbcr_config { + /* v4l2 pixel format */ + unsigned long v4l2_pix_fmt; + /* isif pixel format */ + enum isif_pixfmt pix_fmt; + /* isif frame format */ + enum isif_frmfmt frm_fmt; + /* isif crop window */ + struct v4l2_rect win; + /* field polarity */ + enum vpfe_pin_pol fid_pol; + /* interface VD polarity */ + enum vpfe_pin_pol vd_pol; + /* interface HD polarity */ + enum vpfe_pin_pol hd_pol; + /* isif pix order. Only used for ycbcr capture */ + enum isif_pixorder pix_order; + /* isif buffer type. Only used for ycbcr capture */ + enum isif_buftype buf_type; +}; + +enum isif_cfa_pattern { + ISIF_CFA_PAT_MOSAIC = 0, + ISIF_CFA_PAT_STRIPE = 1, +}; + +enum isif_data_msb { + /* MSB b15 */ + ISIF_BIT_MSB_15 = 0, + /* MSB b14 */ + ISIF_BIT_MSB_14 = 1, + /* MSB b13 */ + ISIF_BIT_MSB_13 = 2, + /* MSB b12 */ + ISIF_BIT_MSB_12 = 3, + /* MSB b11 */ + ISIF_BIT_MSB_11 = 4, + /* MSB b10 */ + ISIF_BIT_MSB_10 = 5, + /* MSB b9 */ + ISIF_BIT_MSB_9 = 6, + /* MSB b8 */ + ISIF_BIT_MSB_8 = 7, + /* MSB b7 */ + ISIF_BIT_MSB_7 = 8, +}; + +struct isif_params_raw { + /* v4l2 pixel format */ + unsigned long v4l2_pix_fmt; + /* isif pixel format */ + enum isif_pixfmt pix_fmt; + /* isif frame format */ + enum isif_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field polarity */ + enum vpfe_pin_pol fid_pol; + /* interface VD polarity */ + enum vpfe_pin_pol vd_pol; + /* interface HD polarity */ + enum vpfe_pin_pol hd_pol; + /* buffer type. Applicable for interlaced mode */ + enum isif_buftype buf_type; + /* cfa pattern */ + enum isif_cfa_pattern cfa_pat; + /* Data MSB position */ + enum isif_data_msb data_msb; + /* Enable horizontal flip */ + unsigned char horz_flip_en; + /* Enable image invert vertically */ + unsigned char image_invert_en; + unsigned char dpcm_predictor; + struct vpfe_isif_raw_config config_params; +}; + +enum isif_data_pack { + ISIF_PACK_16BIT = 0, + ISIF_PACK_12BIT = 1, + ISIF_PACK_8BIT = 2, +}; + +struct isif_gain_values { + unsigned int cr_gain; + unsigned int cgr_gain; + unsigned int cgb_gain; + unsigned int cb_gain; + unsigned int offset; +}; + +struct isif_oper_config { + struct isif_ycbcr_config ycbcr; + struct isif_params_raw bayer; + enum isif_data_pack data_pack; + struct isif_gain_values isif_gain_params; + void *__iomem base_addr; + void *__iomem linear_tbl0_addr; + void *__iomem linear_tbl1_addr; +}; + +#define ISIF_PAD_SINK 0 +#define ISIF_PAD_SOURCE 1 + +#define ISIF_PADS_NUM 2 + +enum isif_input_entity { + ISIF_INPUT_NONE = 0, + ISIF_INPUT_PARALLEL = 1, +}; + +#define ISIF_OUTPUT_NONE (0) +#define ISIF_OUTPUT_MEMORY (1 << 0) +#define ISIF_OUTPUT_IPIPEIF (1 << 1) + +struct vpfe_isif_device { + struct v4l2_subdev subdev; + struct media_pad pads[ISIF_PADS_NUM]; + struct v4l2_mbus_framefmt formats[ISIF_PADS_NUM]; + enum isif_input_entity input; + unsigned int output; + struct v4l2_ctrl_handler ctrls; + struct v4l2_rect crop; + struct isif_oper_config isif_cfg; + struct vpfe_video_device video_out; +}; + +enum v4l2_field vpfe_isif_get_fid(struct vpfe_device *vpfe_dev); +void vpfe_isif_unregister_entities(struct vpfe_isif_device *isif); +int vpfe_isif_register_entities(struct vpfe_isif_device *isif, + struct v4l2_device *dev); +int vpfe_isif_init(struct vpfe_isif_device *isif, struct platform_device *pdev); +void vpfe_isif_cleanup(struct vpfe_isif_device *vpfe_isif, + struct platform_device *pdev); +void vpfe_isif_vidint1_isr(struct vpfe_isif_device *isif); +void vpfe_isif_buffer_isr(struct vpfe_isif_device *isif); + +#endif /* _DAVINCI_VPFE_DM365_ISIF_H */ diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif_regs.h b/drivers/staging/media/davinci_vpfe/dm365_isif_regs.h new file mode 100644 index 000000000000..8aceabb43f8e --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_isif_regs.h @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _DAVINCI_VPFE_DM365_ISIF_REGS_H +#define _DAVINCI_VPFE_DM365_ISIF_REGS_H + +/* ISIF registers relative offsets */ +#define SYNCEN 0x00 +#define MODESET 0x04 +#define HDW 0x08 +#define VDW 0x0c +#define PPLN 0x10 +#define LPFR 0x14 +#define SPH 0x18 +#define LNH 0x1c +#define SLV0 0x20 +#define SLV1 0x24 +#define LNV 0x28 +#define CULH 0x2c +#define CULV 0x30 +#define HSIZE 0x34 +#define SDOFST 0x38 +#define CADU 0x3c +#define CADL 0x40 +#define LINCFG0 0x44 +#define LINCFG1 0x48 +#define CCOLP 0x4c +#define CRGAIN 0x50 +#define CGRGAIN 0x54 +#define CGBGAIN 0x58 +#define CBGAIN 0x5c +#define COFSTA 0x60 +#define FLSHCFG0 0x64 +#define FLSHCFG1 0x68 +#define FLSHCFG2 0x6c +#define VDINT0 0x70 +#define VDINT1 0x74 +#define VDINT2 0x78 +#define MISC 0x7c +#define CGAMMAWD 0x80 +#define REC656IF 0x84 +#define CCDCFG 0x88 +/***************************************************** +* Defect Correction registers +*****************************************************/ +#define DFCCTL 0x8c +#define VDFSATLV 0x90 +#define DFCMEMCTL 0x94 +#define DFCMEM0 0x98 +#define DFCMEM1 0x9c +#define DFCMEM2 0xa0 +#define DFCMEM3 0xa4 +#define DFCMEM4 0xa8 +/**************************************************** +* Black Clamp registers +****************************************************/ +#define CLAMPCFG 0xac +#define CLDCOFST 0xb0 +#define CLSV 0xb4 +#define CLHWIN0 0xb8 +#define CLHWIN1 0xbc +#define CLHWIN2 0xc0 +#define CLVRV 0xc4 +#define CLVWIN0 0xc8 +#define CLVWIN1 0xcc +#define CLVWIN2 0xd0 +#define CLVWIN3 0xd4 +/**************************************************** +* Lense Shading Correction +****************************************************/ +#define DATAHOFST 0xd8 +#define DATAVOFST 0xdc +#define LSCHVAL 0xe0 +#define LSCVVAL 0xe4 +#define TWODLSCCFG 0xe8 +#define TWODLSCOFST 0xec +#define TWODLSCINI 0xf0 +#define TWODLSCGRBU 0xf4 +#define TWODLSCGRBL 0xf8 +#define TWODLSCGROF 0xfc +#define TWODLSCORBU 0x100 +#define TWODLSCORBL 0x104 +#define TWODLSCOROF 0x108 +#define TWODLSCIRQEN 0x10c +#define TWODLSCIRQST 0x110 +/**************************************************** +* Data formatter +****************************************************/ +#define FMTCFG 0x114 +#define FMTPLEN 0x118 +#define FMTSPH 0x11c +#define FMTLNH 0x120 +#define FMTSLV 0x124 +#define FMTLNV 0x128 +#define FMTRLEN 0x12c +#define FMTHCNT 0x130 +#define FMTAPTR_BASE 0x134 +/* Below macro for addresses FMTAPTR0 - FMTAPTR15 */ +#define FMTAPTR(i) (FMTAPTR_BASE + (i * 4)) +#define FMTPGMVF0 0x174 +#define FMTPGMVF1 0x178 +#define FMTPGMAPU0 0x17c +#define FMTPGMAPU1 0x180 +#define FMTPGMAPS0 0x184 +#define FMTPGMAPS1 0x188 +#define FMTPGMAPS2 0x18c +#define FMTPGMAPS3 0x190 +#define FMTPGMAPS4 0x194 +#define FMTPGMAPS5 0x198 +#define FMTPGMAPS6 0x19c +#define FMTPGMAPS7 0x1a0 +/************************************************ +* Color Space Converter +************************************************/ +#define CSCCTL 0x1a4 +#define CSCM0 0x1a8 +#define CSCM1 0x1ac +#define CSCM2 0x1b0 +#define CSCM3 0x1b4 +#define CSCM4 0x1b8 +#define CSCM5 0x1bc +#define CSCM6 0x1c0 +#define CSCM7 0x1c4 +#define OBWIN0 0x1c8 +#define OBWIN1 0x1cc +#define OBWIN2 0x1d0 +#define OBWIN3 0x1d4 +#define OBVAL0 0x1d8 +#define OBVAL1 0x1dc +#define OBVAL2 0x1e0 +#define OBVAL3 0x1e4 +#define OBVAL4 0x1e8 +#define OBVAL5 0x1ec +#define OBVAL6 0x1f0 +#define OBVAL7 0x1f4 +#define CLKCTL 0x1f8 + +/* Masks & Shifts below */ +#define START_PX_HOR_MASK 0x7fff +#define NUM_PX_HOR_MASK 0x7fff +#define START_VER_ONE_MASK 0x7fff +#define START_VER_TWO_MASK 0x7fff +#define NUM_LINES_VER 0x7fff + +/* gain - offset masks */ +#define OFFSET_MASK 0xfff +#define GAIN_SDRAM_EN_SHIFT 12 +#define GAIN_IPIPE_EN_SHIFT 13 +#define GAIN_H3A_EN_SHIFT 14 +#define OFST_SDRAM_EN_SHIFT 8 +#define OFST_IPIPE_EN_SHIFT 9 +#define OFST_H3A_EN_SHIFT 10 +#define GAIN_OFFSET_EN_MASK 0x7700 + +/* Culling */ +#define CULL_PAT_EVEN_LINE_SHIFT 8 + +/* CCDCFG register */ +#define ISIF_YCINSWP_RAW (0x00 << 4) +#define ISIF_YCINSWP_YCBCR (0x01 << 4) +#define ISIF_CCDCFG_FIDMD_LATCH_VSYNC (0x00 << 6) +#define ISIF_CCDCFG_WENLOG_AND (0x00 << 8) +#define ISIF_CCDCFG_TRGSEL_WEN (0x00 << 9) +#define ISIF_CCDCFG_EXTRG_DISABLE (0x00 << 10) +#define ISIF_LATCH_ON_VSYNC_DISABLE (0x01 << 15) +#define ISIF_LATCH_ON_VSYNC_ENABLE (0x00 << 15) +#define ISIF_DATA_PACK_MASK 0x03 +#define ISIF_PIX_ORDER_SHIFT 11 +#define ISIF_PIX_ORDER_MASK 0x01 +#define ISIF_BW656_ENABLE (0x01 << 5) + +/* MODESET registers */ +#define ISIF_VDHDOUT_INPUT (0x00 << 0) +#define ISIF_INPUT_MASK 0x03 +#define ISIF_INPUT_SHIFT 12 +#define ISIF_FID_POL_MASK 0x01 +#define ISIF_FID_POL_SHIFT 4 +#define ISIF_HD_POL_MASK 0x01 +#define ISIF_HD_POL_SHIFT 3 +#define ISIF_VD_POL_MASK 0x01 +#define ISIF_VD_POL_SHIFT 2 +#define ISIF_DATAPOL_NORMAL 0x00 +#define ISIF_DATAPOL_MASK 0x01 +#define ISIF_DATAPOL_SHIFT 6 +#define ISIF_EXWEN_DISABLE 0x00 +#define ISIF_EXWEN_MASK 0x01 +#define ISIF_EXWEN_SHIFT 5 +#define ISIF_FRM_FMT_MASK 0x01 +#define ISIF_FRM_FMT_SHIFT 7 +#define ISIF_DATASFT_MASK 0x07 +#define ISIF_DATASFT_SHIFT 8 +#define ISIF_LPF_SHIFT 14 +#define ISIF_LPF_MASK 0x1 + +/* GAMMAWD registers */ +#define ISIF_ALAW_GAMA_WD_MASK 0xf +#define ISIF_ALAW_GAMA_WD_SHIFT 1 +#define ISIF_ALAW_ENABLE 0x01 +#define ISIF_GAMMAWD_CFA_MASK 0x01 +#define ISIF_GAMMAWD_CFA_SHIFT 5 + +/* HSIZE registers */ +#define ISIF_HSIZE_FLIP_MASK 0x01 +#define ISIF_HSIZE_FLIP_SHIFT 12 +#define ISIF_LINEOFST_MASK 0xfff + +/* MISC registers */ +#define ISIF_DPCM_EN_SHIFT 12 +#define ISIF_DPCM_PREDICTOR_SHIFT 13 +#define ISIF_DPCM_PREDICTOR_MASK 1 + +/* Black clamp related */ +#define ISIF_BC_DCOFFSET_MASK 0x1fff +#define ISIF_BC_MODE_COLOR_MASK 1 +#define ISIF_BC_MODE_COLOR_SHIFT 4 +#define ISIF_HORZ_BC_MODE_MASK 3 +#define ISIF_HORZ_BC_MODE_SHIFT 1 +#define ISIF_HORZ_BC_WIN_COUNT_MASK 0x1f +#define ISIF_HORZ_BC_WIN_SEL_SHIFT 5 +#define ISIF_HORZ_BC_PIX_LIMIT_SHIFT 6 +#define ISIF_HORZ_BC_WIN_H_SIZE_MASK 3 +#define ISIF_HORZ_BC_WIN_H_SIZE_SHIFT 8 +#define ISIF_HORZ_BC_WIN_V_SIZE_MASK 3 +#define ISIF_HORZ_BC_WIN_V_SIZE_SHIFT 12 +#define ISIF_HORZ_BC_WIN_START_H_MASK 0x1fff +#define ISIF_HORZ_BC_WIN_START_V_MASK 0x1fff +#define ISIF_VERT_BC_OB_H_SZ_MASK 7 +#define ISIF_VERT_BC_RST_VAL_SEL_MASK 3 +#define ISIF_VERT_BC_RST_VAL_SEL_SHIFT 4 +#define ISIF_VERT_BC_LINE_AVE_COEF_SHIFT 8 +#define ISIF_VERT_BC_OB_START_HORZ_MASK 0x1fff +#define ISIF_VERT_BC_OB_START_VERT_MASK 0x1fff +#define ISIF_VERT_BC_OB_VERT_SZ_MASK 0x1fff +#define ISIF_VERT_BC_RST_VAL_MASK 0xfff +#define ISIF_BC_VERT_START_SUB_V_MASK 0x1fff + +/* VDFC registers */ +#define ISIF_VDFC_EN_SHIFT 4 +#define ISIF_VDFC_CORR_MOD_MASK 3 +#define ISIF_VDFC_CORR_MOD_SHIFT 5 +#define ISIF_VDFC_CORR_WHOLE_LN_SHIFT 7 +#define ISIF_VDFC_LEVEL_SHFT_MASK 7 +#define ISIF_VDFC_LEVEL_SHFT_SHIFT 8 +#define ISIF_VDFC_SAT_LEVEL_MASK 0xfff +#define ISIF_VDFC_POS_MASK 0x1fff +#define ISIF_DFCMEMCTL_DFCMARST_SHIFT 2 + +/* CSC registers */ +#define ISIF_CSC_COEF_INTEG_MASK 7 +#define ISIF_CSC_COEF_DECIMAL_MASK 0x1f +#define ISIF_CSC_COEF_INTEG_SHIFT 5 +#define ISIF_CSCM_MSB_SHIFT 8 +#define ISIF_DF_CSC_SPH_MASK 0x1fff +#define ISIF_DF_CSC_LNH_MASK 0x1fff +#define ISIF_DF_CSC_SLV_MASK 0x1fff +#define ISIF_DF_CSC_LNV_MASK 0x1fff +#define ISIF_DF_NUMLINES 0x7fff +#define ISIF_DF_NUMPIX 0x1fff + +/* Offsets for LSC/DFC/Gain */ +#define ISIF_DATA_H_OFFSET_MASK 0x1fff +#define ISIF_DATA_V_OFFSET_MASK 0x1fff + +/* Linearization */ +#define ISIF_LIN_CORRSFT_MASK 7 +#define ISIF_LIN_CORRSFT_SHIFT 4 +#define ISIF_LIN_SCALE_FACT_INTEG_SHIFT 10 +#define ISIF_LIN_SCALE_FACT_DECIMAL_MASK 0x3ff +#define ISIF_LIN_ENTRY_MASK 0x3ff + +/* masks and shifts*/ +#define ISIF_SYNCEN_VDHDEN_MASK (1 << 0) +#define ISIF_SYNCEN_WEN_MASK (1 << 1) +#define ISIF_SYNCEN_WEN_SHIFT 1 + +#endif /* _DAVINCI_VPFE_DM365_ISIF_REGS_H */ -- cgit v1.2.3 From da43b6ccadcfe801e0316503334f8281b8679210 Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Wed, 28 Nov 2012 02:14:10 -0300 Subject: [media] davinci: vpfe: dm365: add IPIPE support for media controller driver Add the IPIPE subdevice to the DM365 vpfe driver. The IPIPE is the major sub IP in the DM365 capture VPFE hardware and implements black clamping, color space conversion, edge enhancements etc. the block is exposed as a subdevice and implements media controller based setup and a private IOCTL for fine grain control. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/davinci_vpfe/dm365_ipipe.c | 1863 ++++++++++++++++++++++ drivers/staging/media/davinci_vpfe/dm365_ipipe.h | 179 +++ 2 files changed, 2042 insertions(+) create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipe.c create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipe.h (limited to 'drivers') diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c new file mode 100644 index 000000000000..92853539cc0a --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c @@ -0,0 +1,1863 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + * + * + * IPIPE allows fine tuning of the input image using different + * tuning modules in IPIPE. Some examples :- Noise filter, Defect + * pixel correction etc. It essentially operate on Bayer Raw data + * or YUV raw data. To do image tuning, application call, + * + */ + +#include + +#include "dm365_ipipe.h" +#include "dm365_ipipe_hw.h" +#include "vpfe_mc_capture.h" + +#define MIN_OUT_WIDTH 32 +#define MIN_OUT_HEIGHT 32 + +/* ipipe input format's */ +static const unsigned int ipipe_input_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_SGRBG12_1X12, + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, + V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8, +}; + +/* ipipe output format's */ +static const unsigned int ipipe_output_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, +}; + +static int ipipe_validate_lutdpc_params(struct vpfe_ipipe_lutdpc *lutdpc) +{ + int i; + + if (lutdpc->en > 1 || lutdpc->repl_white > 1 || + lutdpc->dpc_size > LUT_DPC_MAX_SIZE) + return -EINVAL; + + if (lutdpc->en && !lutdpc->table) + return -EINVAL; + + for (i = 0; i < lutdpc->dpc_size; i++) + if (lutdpc->table[i].horz_pos > LUT_DPC_H_POS_MASK || + lutdpc->table[i].vert_pos > LUT_DPC_V_POS_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_lutdpc_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_lutdpc *lutdpc = &ipipe->config.lutdpc; + struct vpfe_ipipe_lutdpc *dpc_param; + struct device *dev; + + if (!param) { + memset((void *)lutdpc, 0, sizeof(struct vpfe_ipipe_lutdpc)); + goto success; + } + + dev = ipipe->subdev.v4l2_dev->dev; + dpc_param = (struct vpfe_ipipe_lutdpc *)param; + lutdpc->en = dpc_param->en; + lutdpc->repl_white = dpc_param->repl_white; + lutdpc->dpc_size = dpc_param->dpc_size; + memcpy(&lutdpc->table, &dpc_param->table, + (dpc_param->dpc_size * sizeof(struct vpfe_ipipe_lutdpc_entry))); + if (ipipe_validate_lutdpc_params(lutdpc) < 0) + return -EINVAL; + +success: + ipipe_set_lutdpc_regs(ipipe->base_addr, ipipe->isp5_base_addr, lutdpc); + + return 0; +} + +static int ipipe_get_lutdpc_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_lutdpc *lut_param = (struct vpfe_ipipe_lutdpc *)param; + struct vpfe_ipipe_lutdpc *lutdpc = &ipipe->config.lutdpc; + + lut_param->en = lutdpc->en; + lut_param->repl_white = lutdpc->repl_white; + lut_param->dpc_size = lutdpc->dpc_size; + memcpy(&lut_param->table, &lutdpc->table, + (lutdpc->dpc_size * sizeof(struct vpfe_ipipe_lutdpc_entry))); + + return 0; +} + +static int ipipe_set_input_config(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_input_config *config = &ipipe->config.input_config; + + if (!param) + memset(config, 0, sizeof(struct vpfe_ipipe_input_config)); + else + memcpy(config, param, sizeof(struct vpfe_ipipe_input_config)); + return 0; +} + +static int ipipe_get_input_config(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_input_config *config = &ipipe->config.input_config; + + if (!param) + return -EINVAL; + + memcpy(param, config, sizeof(struct vpfe_ipipe_input_config)); + + return 0; +} + +static int ipipe_validate_otfdpc_params(struct vpfe_ipipe_otfdpc *dpc_param) +{ + struct vpfe_ipipe_otfdpc_2_0_cfg *dpc_2_0; + struct vpfe_ipipe_otfdpc_3_0_cfg *dpc_3_0; + + if (dpc_param->en > 1) + return -EINVAL; + + if (dpc_param->alg == VPFE_IPIPE_OTFDPC_2_0) { + dpc_2_0 = &dpc_param->alg_cfg.dpc_2_0; + if (dpc_2_0->det_thr.r > OTFDPC_DPC2_THR_MASK || + dpc_2_0->det_thr.gr > OTFDPC_DPC2_THR_MASK || + dpc_2_0->det_thr.gb > OTFDPC_DPC2_THR_MASK || + dpc_2_0->det_thr.b > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.r > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.gr > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.gb > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.b > OTFDPC_DPC2_THR_MASK) + return -EINVAL; + return 0; + } + + dpc_3_0 = &dpc_param->alg_cfg.dpc_3_0; + + if (dpc_3_0->act_adj_shf > OTF_DPC3_0_SHF_MASK || + dpc_3_0->det_thr > OTF_DPC3_0_DET_MASK || + dpc_3_0->det_slp > OTF_DPC3_0_SLP_MASK || + dpc_3_0->det_thr_min > OTF_DPC3_0_DET_MASK || + dpc_3_0->det_thr_max > OTF_DPC3_0_DET_MASK || + dpc_3_0->corr_thr > OTF_DPC3_0_CORR_MASK || + dpc_3_0->corr_slp > OTF_DPC3_0_SLP_MASK || + dpc_3_0->corr_thr_min > OTF_DPC3_0_CORR_MASK || + dpc_3_0->corr_thr_max > OTF_DPC3_0_CORR_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_otfdpc_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_otfdpc *dpc_param = (struct vpfe_ipipe_otfdpc *)param; + struct vpfe_ipipe_otfdpc *otfdpc = &ipipe->config.otfdpc; + struct device *dev; + + if (!param) { + memset((void *)otfdpc, 0, sizeof(struct ipipe_otfdpc_2_0)); + goto success; + } + dev = ipipe->subdev.v4l2_dev->dev; + memcpy(otfdpc, dpc_param, sizeof(struct vpfe_ipipe_otfdpc)); + if (ipipe_validate_otfdpc_params(otfdpc) < 0) { + dev_err(dev, "Invalid otfdpc params\n"); + return -EINVAL; + } + +success: + ipipe_set_otfdpc_regs(ipipe->base_addr, otfdpc); + + return 0; +} + +static int ipipe_get_otfdpc_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_otfdpc *dpc_param = (struct vpfe_ipipe_otfdpc *)param; + struct vpfe_ipipe_otfdpc *otfdpc = &ipipe->config.otfdpc; + + memcpy(dpc_param, otfdpc, sizeof(struct vpfe_ipipe_otfdpc)); + return 0; +} + +static int ipipe_validate_nf_params(struct vpfe_ipipe_nf *nf_param) +{ + int i; + + if (nf_param->en > 1 || nf_param->shft_val > D2F_SHFT_VAL_MASK || + nf_param->spread_val > D2F_SPR_VAL_MASK || + nf_param->apply_lsc_gain > 1 || + nf_param->edge_det_min_thr > D2F_EDGE_DET_THR_MASK || + nf_param->edge_det_max_thr > D2F_EDGE_DET_THR_MASK) + return -EINVAL; + + for (i = 0; i < VPFE_IPIPE_NF_THR_TABLE_SIZE; i++) + if (nf_param->thr[i] > D2F_THR_VAL_MASK) + return -EINVAL; + + for (i = 0; i < VPFE_IPIPE_NF_STR_TABLE_SIZE; i++) + if (nf_param->str[i] > D2F_STR_VAL_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_nf_params(struct vpfe_ipipe_device *ipipe, + unsigned int id, void *param) +{ + struct vpfe_ipipe_nf *nf_param = (struct vpfe_ipipe_nf *)param; + struct vpfe_ipipe_nf *nf = &ipipe->config.nf1; + struct device *dev; + + if (id == IPIPE_D2F_2ND) + nf = &ipipe->config.nf2; + + if (!nf_param) { + memset((void *)nf, 0, sizeof(struct vpfe_ipipe_nf)); + goto success; + } + + dev = ipipe->subdev.v4l2_dev->dev; + memcpy(nf, nf_param, sizeof(struct vpfe_ipipe_nf)); + if (ipipe_validate_nf_params(nf) < 0) { + dev_err(dev, "Invalid nf params\n"); + return -EINVAL; + } + +success: + ipipe_set_d2f_regs(ipipe->base_addr, id, nf); + + return 0; +} + +static int ipipe_set_nf1_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_set_nf_params(ipipe, IPIPE_D2F_1ST, param); +} + +static int ipipe_set_nf2_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_set_nf_params(ipipe, IPIPE_D2F_2ND, param); +} + +static int ipipe_get_nf_params(struct vpfe_ipipe_device *ipipe, + unsigned int id, void *param) +{ + struct vpfe_ipipe_nf *nf_param = (struct vpfe_ipipe_nf *)param; + struct vpfe_ipipe_nf *nf = &ipipe->config.nf1; + + if (id == IPIPE_D2F_2ND) + nf = &ipipe->config.nf2; + + memcpy(nf_param, nf, sizeof(struct vpfe_ipipe_nf)); + + return 0; +} + +static int ipipe_get_nf1_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_get_nf_params(ipipe, IPIPE_D2F_1ST, param); +} + +static int ipipe_get_nf2_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_get_nf_params(ipipe, IPIPE_D2F_2ND, param); +} + +static int ipipe_validate_gic_params(struct vpfe_ipipe_gic *gic) +{ + if (gic->en > 1 || gic->gain > GIC_GAIN_MASK || + gic->thr > GIC_THR_MASK || gic->slope > GIC_SLOPE_MASK || + gic->apply_lsc_gain > 1 || + gic->nf2_thr_gain.integer > GIC_NFGAN_INT_MASK || + gic->nf2_thr_gain.decimal > GIC_NFGAN_DECI_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_gic_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gic *gic_param = (struct vpfe_ipipe_gic *)param; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_gic *gic = &ipipe->config.gic; + + if (!gic_param) { + memset((void *)gic, 0, sizeof(struct vpfe_ipipe_gic)); + goto success; + } + + memcpy(gic, gic_param, sizeof(struct vpfe_ipipe_gic)); + if (ipipe_validate_gic_params(gic) < 0) { + dev_err(dev, "Invalid gic params\n"); + return -EINVAL; + } + +success: + ipipe_set_gic_regs(ipipe->base_addr, gic); + + return 0; +} + +static int ipipe_get_gic_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gic *gic_param = (struct vpfe_ipipe_gic *)param; + struct vpfe_ipipe_gic *gic = &ipipe->config.gic; + + memcpy(gic_param, gic, sizeof(struct vpfe_ipipe_gic)); + + return 0; +} + +static int ipipe_validate_wb_params(struct vpfe_ipipe_wb *wbal) +{ + if (wbal->ofst_r > WB_OFFSET_MASK || + wbal->ofst_gr > WB_OFFSET_MASK || + wbal->ofst_gb > WB_OFFSET_MASK || + wbal->ofst_b > WB_OFFSET_MASK || + wbal->gain_r.integer > WB_GAIN_INT_MASK || + wbal->gain_r.decimal > WB_GAIN_DECI_MASK || + wbal->gain_gr.integer > WB_GAIN_INT_MASK || + wbal->gain_gr.decimal > WB_GAIN_DECI_MASK || + wbal->gain_gb.integer > WB_GAIN_INT_MASK || + wbal->gain_gb.decimal > WB_GAIN_DECI_MASK || + wbal->gain_b.integer > WB_GAIN_INT_MASK || + wbal->gain_b.decimal > WB_GAIN_DECI_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_wb_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_wb *wb_param = (struct vpfe_ipipe_wb *)param; + struct vpfe_ipipe_wb *wbal = &ipipe->config.wbal; + + if (!wb_param) { + const struct vpfe_ipipe_wb wb_defaults = { + .gain_r = {2, 0x0}, + .gain_gr = {2, 0x0}, + .gain_gb = {2, 0x0}, + .gain_b = {2, 0x0} + }; + memcpy(wbal, &wb_defaults, sizeof(struct vpfe_ipipe_wb)); + goto success; + } + + memcpy(wbal, wb_param, sizeof(struct vpfe_ipipe_wb)); + if (ipipe_validate_wb_params(wbal) < 0) + return -EINVAL; + +success: + ipipe_set_wb_regs(ipipe->base_addr, wbal); + + return 0; +} + +static int ipipe_get_wb_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_wb *wb_param = (struct vpfe_ipipe_wb *)param; + struct vpfe_ipipe_wb *wbal = &ipipe->config.wbal; + + memcpy(wb_param, wbal, sizeof(struct vpfe_ipipe_wb)); + return 0; +} + +static int ipipe_validate_cfa_params(struct vpfe_ipipe_cfa *cfa) +{ + if (cfa->hpf_thr_2dir > CFA_HPF_THR_2DIR_MASK || + cfa->hpf_slp_2dir > CFA_HPF_SLOPE_2DIR_MASK || + cfa->hp_mix_thr_2dir > CFA_HPF_MIX_THR_2DIR_MASK || + cfa->hp_mix_slope_2dir > CFA_HPF_MIX_SLP_2DIR_MASK || + cfa->dir_thr_2dir > CFA_DIR_THR_2DIR_MASK || + cfa->dir_slope_2dir > CFA_DIR_SLP_2DIR_MASK || + cfa->nd_wt_2dir > CFA_ND_WT_2DIR_MASK || + cfa->hue_fract_daa > CFA_DAA_HUE_FRA_MASK || + cfa->edge_thr_daa > CFA_DAA_EDG_THR_MASK || + cfa->thr_min_daa > CFA_DAA_THR_MIN_MASK || + cfa->thr_slope_daa > CFA_DAA_THR_SLP_MASK || + cfa->slope_min_daa > CFA_DAA_SLP_MIN_MASK || + cfa->slope_slope_daa > CFA_DAA_SLP_SLP_MASK || + cfa->lp_wt_daa > CFA_DAA_LP_WT_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_cfa_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_cfa *cfa_param = (struct vpfe_ipipe_cfa *)param; + struct vpfe_ipipe_cfa *cfa = &ipipe->config.cfa; + + if (!cfa_param) { + memset(cfa, 0, sizeof(struct vpfe_ipipe_cfa)); + cfa->alg = VPFE_IPIPE_CFA_ALG_2DIRAC; + goto success; + } + + memcpy(cfa, cfa_param, sizeof(struct vpfe_ipipe_cfa)); + if (ipipe_validate_cfa_params(cfa) < 0) + return -EINVAL; + +success: + ipipe_set_cfa_regs(ipipe->base_addr, cfa); + + return 0; +} + +static int ipipe_get_cfa_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_cfa *cfa_param = (struct vpfe_ipipe_cfa *)param; + struct vpfe_ipipe_cfa *cfa = &ipipe->config.cfa; + + memcpy(cfa_param, cfa, sizeof(struct vpfe_ipipe_cfa)); + return 0; +} + +static int +ipipe_validate_rgb2rgb_params(struct vpfe_ipipe_rgb2rgb *rgb2rgb, + unsigned int id) +{ + u32 gain_int_upper = RGB2RGB_1_GAIN_INT_MASK; + u32 offset_upper = RGB2RGB_1_OFST_MASK; + + if (id == IPIPE_RGB2RGB_2) { + offset_upper = RGB2RGB_2_OFST_MASK; + gain_int_upper = RGB2RGB_2_GAIN_INT_MASK; + } + + if (rgb2rgb->coef_rr.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_rr.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_gr.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gr.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_br.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_br.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_rg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_rg.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_gg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gg.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_bg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_bg.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_rb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_rb.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_gb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gb.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_bb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_bb.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->out_ofst_r > offset_upper || + rgb2rgb->out_ofst_g > offset_upper || + rgb2rgb->out_ofst_b > offset_upper) + return -EINVAL; + + return 0; +} + +static int ipipe_set_rgb2rgb_params(struct vpfe_ipipe_device *ipipe, + unsigned int id, void *param) +{ + struct vpfe_ipipe_rgb2rgb *rgb2rgb = &ipipe->config.rgb2rgb1; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_rgb2rgb *rgb2rgb_param; + + rgb2rgb_param = (struct vpfe_ipipe_rgb2rgb *)param; + + if (id == IPIPE_RGB2RGB_2) + rgb2rgb = &ipipe->config.rgb2rgb2; + + if (!rgb2rgb_param) { + const struct vpfe_ipipe_rgb2rgb rgb2rgb_defaults = { + .coef_rr = {1, 0}, /* 256 */ + .coef_gr = {0, 0}, + .coef_br = {0, 0}, + .coef_rg = {0, 0}, + .coef_gg = {1, 0}, /* 256 */ + .coef_bg = {0, 0}, + .coef_rb = {0, 0}, + .coef_gb = {0, 0}, + .coef_bb = {1, 0}, /* 256 */ + }; + /* Copy defaults for rgb2rgb conversion */ + memcpy(rgb2rgb, &rgb2rgb_defaults, + sizeof(struct vpfe_ipipe_rgb2rgb)); + goto success; + } + + memcpy(rgb2rgb, rgb2rgb_param, sizeof(struct vpfe_ipipe_rgb2rgb)); + if (ipipe_validate_rgb2rgb_params(rgb2rgb, id) < 0) { + dev_err(dev, "Invalid rgb2rgb params\n"); + return -EINVAL; + } + +success: + ipipe_set_rgb2rgb_regs(ipipe->base_addr, id, rgb2rgb); + + return 0; +} + +static int +ipipe_set_rgb2rgb_1_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_set_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_1, param); +} + +static int +ipipe_set_rgb2rgb_2_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_set_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_2, param); +} + +static int ipipe_get_rgb2rgb_params(struct vpfe_ipipe_device *ipipe, + unsigned int id, void *param) +{ + struct vpfe_ipipe_rgb2rgb *rgb2rgb = &ipipe->config.rgb2rgb1; + struct vpfe_ipipe_rgb2rgb *rgb2rgb_param; + + rgb2rgb_param = (struct vpfe_ipipe_rgb2rgb *)param; + + if (id == IPIPE_RGB2RGB_2) + rgb2rgb = &ipipe->config.rgb2rgb2; + + memcpy(rgb2rgb_param, rgb2rgb, sizeof(struct vpfe_ipipe_rgb2rgb)); + + return 0; +} + +static int +ipipe_get_rgb2rgb_1_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_get_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_1, param); +} + +static int +ipipe_get_rgb2rgb_2_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_get_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_2, param); +} + +static int +ipipe_validate_gamma_entry(struct vpfe_ipipe_gamma_entry *table, int size) +{ + int i; + + if (!table) + return -EINVAL; + + for (i = 0; i < size; i++) + if (table[i].slope > GAMMA_MASK || + table[i].offset > GAMMA_MASK) + return -EINVAL; + + return 0; +} + +static int +ipipe_validate_gamma_params(struct vpfe_ipipe_gamma *gamma, struct device *dev) +{ + int table_size; + int err; + + if (gamma->bypass_r > 1 || + gamma->bypass_b > 1 || + gamma->bypass_g > 1) + return -EINVAL; + + if (gamma->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM) + return 0; + + table_size = gamma->tbl_size; + if (!gamma->bypass_r) { + err = ipipe_validate_gamma_entry(gamma->table_r, table_size); + if (err) { + dev_err(dev, "GAMMA R - table entry invalid\n"); + return err; + } + } + + if (!gamma->bypass_b) { + err = ipipe_validate_gamma_entry(gamma->table_b, table_size); + if (err) { + dev_err(dev, "GAMMA B - table entry invalid\n"); + return err; + } + } + + if (!gamma->bypass_g) { + err = ipipe_validate_gamma_entry(gamma->table_g, table_size); + if (err) { + dev_err(dev, "GAMMA G - table entry invalid\n"); + return err; + } + } + + return 0; +} + +static int +ipipe_set_gamma_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gamma *gamma_param = (struct vpfe_ipipe_gamma *)param; + struct vpfe_ipipe_gamma *gamma = &ipipe->config.gamma; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + int table_size; + + if (!gamma_param) { + memset(gamma, 0, sizeof(struct vpfe_ipipe_gamma)); + gamma->tbl_sel = VPFE_IPIPE_GAMMA_TBL_ROM; + goto success; + } + + gamma->bypass_r = gamma_param->bypass_r; + gamma->bypass_b = gamma_param->bypass_b; + gamma->bypass_g = gamma_param->bypass_g; + gamma->tbl_sel = gamma_param->tbl_sel; + gamma->tbl_size = gamma_param->tbl_size; + + if (ipipe_validate_gamma_params(gamma, dev) < 0) + return -EINVAL; + + if (gamma_param->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM) + goto success; + + table_size = gamma->tbl_size; + if (!gamma_param->bypass_r) + memcpy(&gamma->table_r, &gamma_param->table_r, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + + if (!gamma_param->bypass_b) + memcpy(&gamma->table_b, &gamma_param->table_b, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + + if (!gamma_param->bypass_g) + memcpy(&gamma->table_g, &gamma_param->table_g, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + +success: + ipipe_set_gamma_regs(ipipe->base_addr, ipipe->isp5_base_addr, gamma); + + return 0; +} + +static int ipipe_get_gamma_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gamma *gamma_param = (struct vpfe_ipipe_gamma *)param; + struct vpfe_ipipe_gamma *gamma = &ipipe->config.gamma; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + int table_size; + + gamma_param->bypass_r = gamma->bypass_r; + gamma_param->bypass_g = gamma->bypass_g; + gamma_param->bypass_b = gamma->bypass_b; + gamma_param->tbl_sel = gamma->tbl_sel; + gamma_param->tbl_size = gamma->tbl_size; + + if (gamma->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM) + return 0; + + table_size = gamma->tbl_size; + + if (!gamma->bypass_r && !gamma_param->table_r) { + dev_err(dev, + "ipipe_get_gamma_params: table ptr empty for R\n"); + return -EINVAL; + } + memcpy(gamma_param->table_r, gamma->table_r, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + + if (!gamma->bypass_g && !gamma_param->table_g) { + dev_err(dev, "ipipe_get_gamma_params: table ptr empty for G\n"); + return -EINVAL; + } + memcpy(gamma_param->table_g, gamma->table_g, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + + if (!gamma->bypass_b && !gamma_param->table_b) { + dev_err(dev, "ipipe_get_gamma_params: table ptr empty for B\n"); + return -EINVAL; + } + memcpy(gamma_param->table_b, gamma->table_b, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + + return 0; +} + +static int ipipe_validate_3d_lut_params(struct vpfe_ipipe_3d_lut *lut) +{ + int i; + + if (!lut->en) + return 0; + + for (i = 0; i < VPFE_IPIPE_MAX_SIZE_3D_LUT; i++) + if (lut->table[i].r > D3_LUT_ENTRY_MASK || + lut->table[i].g > D3_LUT_ENTRY_MASK || + lut->table[i].b > D3_LUT_ENTRY_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_get_3d_lut_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_3d_lut *lut_param = (struct vpfe_ipipe_3d_lut *)param; + struct vpfe_ipipe_3d_lut *lut = &ipipe->config.lut; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + + lut_param->en = lut->en; + if (!lut_param->table) { + dev_err(dev, "ipipe_get_3d_lut_params: Invalid table ptr\n"); + return -EINVAL; + } + + memcpy(lut_param->table, &lut->table, + (VPFE_IPIPE_MAX_SIZE_3D_LUT * + sizeof(struct vpfe_ipipe_3d_lut_entry))); + + return 0; +} + +static int +ipipe_set_3d_lut_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_3d_lut *lut_param = (struct vpfe_ipipe_3d_lut *)param; + struct vpfe_ipipe_3d_lut *lut = &ipipe->config.lut; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + + if (!lut_param) { + memset(lut, 0, sizeof(struct vpfe_ipipe_3d_lut)); + goto success; + } + + memcpy(lut, lut_param, sizeof(struct vpfe_ipipe_3d_lut)); + if (ipipe_validate_3d_lut_params(lut) < 0) { + dev_err(dev, "Invalid 3D-LUT Params\n"); + return -EINVAL; + } + +success: + ipipe_set_3d_lut_regs(ipipe->base_addr, ipipe->isp5_base_addr, lut); + + return 0; +} + +static int ipipe_validate_rgb2yuv_params(struct vpfe_ipipe_rgb2yuv *rgb2yuv) +{ + if (rgb2yuv->coef_ry.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_ry.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_gy.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_gy.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_by.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_by.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_rcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_rcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_gcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_gcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_bcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_bcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_rcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_rcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_gcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_gcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_bcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_bcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->out_ofst_y > RGB2YCBCR_OFST_MASK || + rgb2yuv->out_ofst_cb > RGB2YCBCR_OFST_MASK || + rgb2yuv->out_ofst_cr > RGB2YCBCR_OFST_MASK) + return -EINVAL; + + return 0; +} + +static int +ipipe_set_rgb2yuv_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_rgb2yuv *rgb2yuv = &ipipe->config.rgb2yuv; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_rgb2yuv *rgb2yuv_param; + + rgb2yuv_param = (struct vpfe_ipipe_rgb2yuv *)param; + if (!rgb2yuv_param) { + /* Defaults for rgb2yuv conversion */ + const struct vpfe_ipipe_rgb2yuv rgb2yuv_defaults = { + .coef_ry = {0, 0x4d}, + .coef_gy = {0, 0x96}, + .coef_by = {0, 0x1d}, + .coef_rcb = {0xf, 0xd5}, + .coef_gcb = {0xf, 0xab}, + .coef_bcb = {0, 0x80}, + .coef_rcr = {0, 0x80}, + .coef_gcr = {0xf, 0x95}, + .coef_bcr = {0xf, 0xeb}, + .out_ofst_cb = 0x80, + .out_ofst_cr = 0x80, + }; + /* Copy defaults for rgb2yuv conversion */ + memcpy(rgb2yuv, &rgb2yuv_defaults, + sizeof(struct vpfe_ipipe_rgb2yuv)); + goto success; + } + + memcpy(rgb2yuv, rgb2yuv_param, sizeof(struct vpfe_ipipe_rgb2yuv)); + if (ipipe_validate_rgb2yuv_params(rgb2yuv) < 0) { + dev_err(dev, "Invalid rgb2yuv params\n"); + return -EINVAL; + } + +success: + ipipe_set_rgb2ycbcr_regs(ipipe->base_addr, rgb2yuv); + + return 0; +} + +static int +ipipe_get_rgb2yuv_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_rgb2yuv *rgb2yuv = &ipipe->config.rgb2yuv; + struct vpfe_ipipe_rgb2yuv *rgb2yuv_param; + + rgb2yuv_param = (struct vpfe_ipipe_rgb2yuv *)param; + memcpy(rgb2yuv_param, rgb2yuv, sizeof(struct vpfe_ipipe_rgb2yuv)); + return 0; +} + +static int ipipe_validate_gbce_params(struct vpfe_ipipe_gbce *gbce) +{ + u32 max = GBCE_Y_VAL_MASK; + int i; + + if (!gbce->en) + return 0; + + if (gbce->type == VPFE_IPIPE_GBCE_GAIN_TBL) + max = GBCE_GAIN_VAL_MASK; + + for (i = 0; i < VPFE_IPIPE_MAX_SIZE_GBCE_LUT; i++) + if (gbce->table[i] > max) + return -EINVAL; + + return 0; +} + +static int ipipe_set_gbce_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gbce *gbce_param = (struct vpfe_ipipe_gbce *)param; + struct vpfe_ipipe_gbce *gbce = &ipipe->config.gbce; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + + if (!gbce_param) { + memset(gbce, 0 , sizeof(struct vpfe_ipipe_gbce)); + } else { + memcpy(gbce, gbce_param, sizeof(struct vpfe_ipipe_gbce)); + if (ipipe_validate_gbce_params(gbce) < 0) { + dev_err(dev, "Invalid gbce params\n"); + return -EINVAL; + } + } + + ipipe_set_gbce_regs(ipipe->base_addr, ipipe->isp5_base_addr, gbce); + + return 0; +} + +static int ipipe_get_gbce_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gbce *gbce_param = (struct vpfe_ipipe_gbce *)param; + struct vpfe_ipipe_gbce *gbce = &ipipe->config.gbce; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + + gbce_param->en = gbce->en; + gbce_param->type = gbce->type; + if (!gbce_param->table) { + dev_err(dev, "ipipe_get_gbce_params: Invalid table ptr\n"); + return -EINVAL; + } + + memcpy(gbce_param->table, gbce->table, + (VPFE_IPIPE_MAX_SIZE_GBCE_LUT * sizeof(unsigned short))); + + return 0; +} + +static int +ipipe_validate_yuv422_conv_params(struct vpfe_ipipe_yuv422_conv *yuv422_conv) +{ + if (yuv422_conv->en_chrom_lpf > 1) + return -EINVAL; + + return 0; +} + +static int +ipipe_set_yuv422_conv_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_yuv422_conv *yuv422_conv = &ipipe->config.yuv422_conv; + struct vpfe_ipipe_yuv422_conv *yuv422_conv_param; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + + yuv422_conv_param = (struct vpfe_ipipe_yuv422_conv *)param; + if (!yuv422_conv_param) { + memset(yuv422_conv, 0, sizeof(struct vpfe_ipipe_yuv422_conv)); + yuv422_conv->chrom_pos = VPFE_IPIPE_YUV422_CHR_POS_COSITE; + } else { + memcpy(yuv422_conv, yuv422_conv_param, + sizeof(struct vpfe_ipipe_yuv422_conv)); + if (ipipe_validate_yuv422_conv_params(yuv422_conv) < 0) { + dev_err(dev, "Invalid yuv422 params\n"); + return -EINVAL; + } + } + + ipipe_set_yuv422_conv_regs(ipipe->base_addr, yuv422_conv); + + return 0; +} + +static int +ipipe_get_yuv422_conv_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_yuv422_conv *yuv422_conv = &ipipe->config.yuv422_conv; + struct vpfe_ipipe_yuv422_conv *yuv422_conv_param; + + yuv422_conv_param = (struct vpfe_ipipe_yuv422_conv *)param; + memcpy(yuv422_conv_param, yuv422_conv, + sizeof(struct vpfe_ipipe_yuv422_conv)); + + return 0; +} + +static int ipipe_validate_yee_params(struct vpfe_ipipe_yee *yee) +{ + int i; + + if (yee->en > 1 || + yee->en_halo_red > 1 || + yee->hpf_shft > YEE_HPF_SHIFT_MASK) + return -EINVAL; + + if (yee->hpf_coef_00 > YEE_COEF_MASK || + yee->hpf_coef_01 > YEE_COEF_MASK || + yee->hpf_coef_02 > YEE_COEF_MASK || + yee->hpf_coef_10 > YEE_COEF_MASK || + yee->hpf_coef_11 > YEE_COEF_MASK || + yee->hpf_coef_12 > YEE_COEF_MASK || + yee->hpf_coef_20 > YEE_COEF_MASK || + yee->hpf_coef_21 > YEE_COEF_MASK || + yee->hpf_coef_22 > YEE_COEF_MASK) + return -EINVAL; + + if (yee->yee_thr > YEE_THR_MASK || + yee->es_gain > YEE_ES_GAIN_MASK || + yee->es_thr1 > YEE_ES_THR1_MASK || + yee->es_thr2 > YEE_THR_MASK || + yee->es_gain_grad > YEE_THR_MASK || + yee->es_ofst_grad > YEE_THR_MASK) + return -EINVAL; + + for (i = 0; i < VPFE_IPIPE_MAX_SIZE_YEE_LUT ; i++) + if (yee->table[i] > YEE_ENTRY_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_yee_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_yee *yee_param = (struct vpfe_ipipe_yee *)param; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_yee *yee = &ipipe->config.yee; + + if (!yee_param) { + memset(yee, 0, sizeof(struct vpfe_ipipe_yee)); + } else { + memcpy(yee, yee_param, sizeof(struct vpfe_ipipe_yee)); + if (ipipe_validate_yee_params(yee) < 0) { + dev_err(dev, "Invalid yee params\n"); + return -EINVAL; + } + } + + ipipe_set_ee_regs(ipipe->base_addr, ipipe->isp5_base_addr, yee); + + return 0; +} + +static int ipipe_get_yee_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_yee *yee_param = (struct vpfe_ipipe_yee *)param; + struct vpfe_ipipe_yee *yee = &ipipe->config.yee; + + yee_param->en = yee->en; + yee_param->en_halo_red = yee->en_halo_red; + yee_param->merge_meth = yee->merge_meth; + yee_param->hpf_shft = yee->hpf_shft; + yee_param->hpf_coef_00 = yee->hpf_coef_00; + yee_param->hpf_coef_01 = yee->hpf_coef_01; + yee_param->hpf_coef_02 = yee->hpf_coef_02; + yee_param->hpf_coef_10 = yee->hpf_coef_10; + yee_param->hpf_coef_11 = yee->hpf_coef_11; + yee_param->hpf_coef_12 = yee->hpf_coef_12; + yee_param->hpf_coef_20 = yee->hpf_coef_20; + yee_param->hpf_coef_21 = yee->hpf_coef_21; + yee_param->hpf_coef_22 = yee->hpf_coef_22; + yee_param->yee_thr = yee->yee_thr; + yee_param->es_gain = yee->es_gain; + yee_param->es_thr1 = yee->es_thr1; + yee_param->es_thr2 = yee->es_thr2; + yee_param->es_gain_grad = yee->es_gain_grad; + yee_param->es_ofst_grad = yee->es_ofst_grad; + memcpy(yee_param->table, &yee->table, + (VPFE_IPIPE_MAX_SIZE_YEE_LUT * sizeof(short))); + + return 0; +} + +static int ipipe_validate_car_params(struct vpfe_ipipe_car *car) +{ + if (car->en > 1 || car->hpf_shft > CAR_HPF_SHIFT_MASK || + car->gain1.shft > CAR_GAIN1_SHFT_MASK || + car->gain1.gain_min > CAR_GAIN_MIN_MASK || + car->gain2.shft > CAR_GAIN2_SHFT_MASK || + car->gain2.gain_min > CAR_GAIN_MIN_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_car_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_car *car_param = (struct vpfe_ipipe_car *)param; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_car *car = &ipipe->config.car; + + if (!car_param) { + memset(car , 0, sizeof(struct vpfe_ipipe_car)); + } else { + memcpy(car, car_param, sizeof(struct vpfe_ipipe_car)); + if (ipipe_validate_car_params(car) < 0) { + dev_err(dev, "Invalid car params\n"); + return -EINVAL; + } + } + + ipipe_set_car_regs(ipipe->base_addr, car); + + return 0; +} + +static int ipipe_get_car_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_car *car_param = (struct vpfe_ipipe_car *)param; + struct vpfe_ipipe_car *car = &ipipe->config.car; + + memcpy(car_param, car, sizeof(struct vpfe_ipipe_car)); + return 0; +} + +static int ipipe_validate_cgs_params(struct vpfe_ipipe_cgs *cgs) +{ + if (cgs->en > 1 || cgs->h_shft > CAR_SHIFT_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_cgs_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_cgs *cgs_param = (struct vpfe_ipipe_cgs *)param; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_cgs *cgs = &ipipe->config.cgs; + + if (!cgs_param) { + memset(cgs, 0, sizeof(struct vpfe_ipipe_cgs)); + } else { + memcpy(cgs, cgs_param, sizeof(struct vpfe_ipipe_cgs)); + if (ipipe_validate_cgs_params(cgs) < 0) { + dev_err(dev, "Invalid cgs params\n"); + return -EINVAL; + } + } + + ipipe_set_cgs_regs(ipipe->base_addr, cgs); + + return 0; +} + +static int ipipe_get_cgs_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_cgs *cgs_param = (struct vpfe_ipipe_cgs *)param; + struct vpfe_ipipe_cgs *cgs = &ipipe->config.cgs; + + memcpy(cgs_param, cgs, sizeof(struct vpfe_ipipe_cgs)); + + return 0; +} + +static const struct ipipe_module_if ipipe_modules[VPFE_IPIPE_MAX_MODULES] = { + /* VPFE_IPIPE_INPUT_CONFIG */ { + offsetof(struct ipipe_module_params, input_config), + FIELD_SIZEOF(struct ipipe_module_params, input_config), + offsetof(struct vpfe_ipipe_config, input_config), + ipipe_set_input_config, + ipipe_get_input_config, + }, /* VPFE_IPIPE_LUTDPC */ { + offsetof(struct ipipe_module_params, lutdpc), + FIELD_SIZEOF(struct ipipe_module_params, lutdpc), + offsetof(struct vpfe_ipipe_config, lutdpc), + ipipe_set_lutdpc_params, + ipipe_get_lutdpc_params, + }, /* VPFE_IPIPE_OTFDPC */ { + offsetof(struct ipipe_module_params, otfdpc), + FIELD_SIZEOF(struct ipipe_module_params, otfdpc), + offsetof(struct vpfe_ipipe_config, otfdpc), + ipipe_set_otfdpc_params, + ipipe_get_otfdpc_params, + }, /* VPFE_IPIPE_NF1 */ { + offsetof(struct ipipe_module_params, nf1), + FIELD_SIZEOF(struct ipipe_module_params, nf1), + offsetof(struct vpfe_ipipe_config, nf1), + ipipe_set_nf1_params, + ipipe_get_nf1_params, + }, /* VPFE_IPIPE_NF2 */ { + offsetof(struct ipipe_module_params, nf2), + FIELD_SIZEOF(struct ipipe_module_params, nf2), + offsetof(struct vpfe_ipipe_config, nf2), + ipipe_set_nf2_params, + ipipe_get_nf2_params, + }, /* VPFE_IPIPE_WB */ { + offsetof(struct ipipe_module_params, wbal), + FIELD_SIZEOF(struct ipipe_module_params, wbal), + offsetof(struct vpfe_ipipe_config, wbal), + ipipe_set_wb_params, + ipipe_get_wb_params, + }, /* VPFE_IPIPE_RGB2RGB_1 */ { + offsetof(struct ipipe_module_params, rgb2rgb1), + FIELD_SIZEOF(struct ipipe_module_params, rgb2rgb1), + offsetof(struct vpfe_ipipe_config, rgb2rgb1), + ipipe_set_rgb2rgb_1_params, + ipipe_get_rgb2rgb_1_params, + }, /* VPFE_IPIPE_RGB2RGB_2 */ { + offsetof(struct ipipe_module_params, rgb2rgb2), + FIELD_SIZEOF(struct ipipe_module_params, rgb2rgb2), + offsetof(struct vpfe_ipipe_config, rgb2rgb2), + ipipe_set_rgb2rgb_2_params, + ipipe_get_rgb2rgb_2_params, + }, /* VPFE_IPIPE_GAMMA */ { + offsetof(struct ipipe_module_params, gamma), + FIELD_SIZEOF(struct ipipe_module_params, gamma), + offsetof(struct vpfe_ipipe_config, gamma), + ipipe_set_gamma_params, + ipipe_get_gamma_params, + }, /* VPFE_IPIPE_3D_LUT */ { + offsetof(struct ipipe_module_params, lut), + FIELD_SIZEOF(struct ipipe_module_params, lut), + offsetof(struct vpfe_ipipe_config, lut), + ipipe_set_3d_lut_params, + ipipe_get_3d_lut_params, + }, /* VPFE_IPIPE_RGB2YUV */ { + offsetof(struct ipipe_module_params, rgb2yuv), + FIELD_SIZEOF(struct ipipe_module_params, rgb2yuv), + offsetof(struct vpfe_ipipe_config, rgb2yuv), + ipipe_set_rgb2yuv_params, + ipipe_get_rgb2yuv_params, + }, /* VPFE_IPIPE_YUV422_CONV */ { + offsetof(struct ipipe_module_params, yuv422_conv), + FIELD_SIZEOF(struct ipipe_module_params, yuv422_conv), + offsetof(struct vpfe_ipipe_config, yuv422_conv), + ipipe_set_yuv422_conv_params, + ipipe_get_yuv422_conv_params, + }, /* VPFE_IPIPE_YEE */ { + offsetof(struct ipipe_module_params, yee), + FIELD_SIZEOF(struct ipipe_module_params, yee), + offsetof(struct vpfe_ipipe_config, yee), + ipipe_set_yee_params, + ipipe_get_yee_params, + }, /* VPFE_IPIPE_GIC */ { + offsetof(struct ipipe_module_params, gic), + FIELD_SIZEOF(struct ipipe_module_params, gic), + offsetof(struct vpfe_ipipe_config, gic), + ipipe_set_gic_params, + ipipe_get_gic_params, + }, /* VPFE_IPIPE_CFA */ { + offsetof(struct ipipe_module_params, cfa), + FIELD_SIZEOF(struct ipipe_module_params, cfa), + offsetof(struct vpfe_ipipe_config, cfa), + ipipe_set_cfa_params, + ipipe_get_cfa_params, + }, /* VPFE_IPIPE_CAR */ { + offsetof(struct ipipe_module_params, car), + FIELD_SIZEOF(struct ipipe_module_params, car), + offsetof(struct vpfe_ipipe_config, car), + ipipe_set_car_params, + ipipe_get_car_params, + }, /* VPFE_IPIPE_CGS */ { + offsetof(struct ipipe_module_params, cgs), + FIELD_SIZEOF(struct ipipe_module_params, cgs), + offsetof(struct vpfe_ipipe_config, cgs), + ipipe_set_cgs_params, + ipipe_get_cgs_params, + }, /* VPFE_IPIPE_GBCE */ { + offsetof(struct ipipe_module_params, gbce), + FIELD_SIZEOF(struct ipipe_module_params, gbce), + offsetof(struct vpfe_ipipe_config, gbce), + ipipe_set_gbce_params, + ipipe_get_gbce_params, + }, +}; + +static int ipipe_s_config(struct v4l2_subdev *sd, struct vpfe_ipipe_config *cfg) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + unsigned int i; + int rval = 0; + + for (i = 0; i < ARRAY_SIZE(ipipe_modules); i++) { + unsigned int bit = 1 << i; + if (cfg->flag & bit) { + const struct ipipe_module_if *module_if = + &ipipe_modules[i]; + struct ipipe_module_params *params; + void __user *from = *(void * __user *) + ((void *)cfg + module_if->config_offset); + size_t size; + void *to; + + params = kmalloc(sizeof(struct ipipe_module_params), + GFP_KERNEL); + to = (void *)params + module_if->param_offset; + size = module_if->param_size; + + if (to && from && size) { + if (copy_from_user(to, from, size)) { + rval = -EFAULT; + break; + } + rval = module_if->set(ipipe, to); + if (rval) + goto error; + } else if (to && !from && size) { + rval = module_if->set(ipipe, NULL); + if (rval) + goto error; + } + kfree(params); + } + } +error: + return rval; +} + +static int ipipe_g_config(struct v4l2_subdev *sd, struct vpfe_ipipe_config *cfg) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + unsigned int i; + int rval = 0; + + for (i = 1; i < ARRAY_SIZE(ipipe_modules); i++) { + unsigned int bit = 1 << i; + if (cfg->flag & bit) { + const struct ipipe_module_if *module_if = + &ipipe_modules[i]; + struct ipipe_module_params *params; + void __user *to = *(void * __user *) + ((void *)cfg + module_if->config_offset); + size_t size; + void *from; + + params = kmalloc(sizeof(struct ipipe_module_params), + GFP_KERNEL); + from = (void *)params + module_if->param_offset; + size = module_if->param_size; + + if (to && from && size) { + rval = module_if->get(ipipe, from); + if (rval) + goto error; + if (copy_to_user(to, from, size)) { + rval = -EFAULT; + break; + } + } + kfree(params); + } + } +error: + return rval; +} + +/* + * ipipe_ioctl() - Handle ipipe module private ioctl's + * @sd: pointer to v4l2 subdev structure + * @cmd: configuration command + * @arg: configuration argument + */ +static long ipipe_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + int ret = 0; + + switch (cmd) { + case VIDIOC_VPFE_IPIPE_S_CONFIG: + ret = ipipe_s_config(sd, arg); + break; + + case VIDIOC_VPFE_IPIPE_G_CONFIG: + ret = ipipe_g_config(sd, arg); + break; + + default: + ret = -ENOIOCTLCMD; + } + return ret; +} + +void vpfe_ipipe_enable(struct vpfe_device *vpfe_dev, int en) +{ + struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif; + struct vpfe_ipipe_device *ipipe = &vpfe_dev->vpfe_ipipe; + unsigned char val; + + if (ipipe->input == IPIPE_INPUT_NONE) + return; + + /* ipipe is set to single shot */ + if (ipipeif->input == IPIPEIF_INPUT_MEMORY && en) { + /* for single-shot mode, need to wait for h/w to + * reset many register bits + */ + do { + val = regr_ip(vpfe_dev->vpfe_ipipe.base_addr, + IPIPE_SRC_EN); + } while (val); + } + regw_ip(vpfe_dev->vpfe_ipipe.base_addr, en, IPIPE_SRC_EN); +} + +/* + * ipipe_set_stream() - Enable/Disable streaming on the ipipe subdevice + * @sd: pointer to v4l2 subdev structure + * @enable: 1 == Enable, 0 == Disable + */ +static int ipipe_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe); + + if (enable && ipipe->input != IPIPE_INPUT_NONE && + ipipe->output != IPIPE_OUTPUT_NONE) { + if (config_ipipe_hw(ipipe) < 0) + return -EINVAL; + } + + vpfe_ipipe_enable(vpfe_dev, enable); + + return 0; +} + +/* + * __ipipe_get_format() - helper function for getting ipipe format + * @ipipe: pointer to ipipe private structure. + * @pad: pad number. + * @fh: V4L2 subdev file handle. + * @which: wanted subdev format. + * + */ +static struct v4l2_mbus_framefmt * +__ipipe_get_format(struct vpfe_ipipe_device *ipipe, + struct v4l2_subdev_fh *fh, unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + + return &ipipe->formats[pad]; +} + +/* + * ipipe_try_format() - Handle try format by pad subdev method + * @ipipe: VPFE ipipe device. + * @fh: V4L2 subdev file handle. + * @pad: pad num. + * @fmt: pointer to v4l2 format structure. + * @which : wanted subdev format + */ +static void +ipipe_try_format(struct vpfe_ipipe_device *ipipe, + struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + unsigned int max_out_height; + unsigned int max_out_width; + unsigned int i; + + max_out_width = IPIPE_MAX_OUTPUT_WIDTH_A; + max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_A; + + if (pad == IPIPE_PAD_SINK) { + for (i = 0; i < ARRAY_SIZE(ipipe_input_fmts); i++) + if (fmt->code == ipipe_input_fmts[i]) + break; + + /* If not found, use SBGGR10 as default */ + if (i >= ARRAY_SIZE(ipipe_input_fmts)) + fmt->code = V4L2_MBUS_FMT_SGRBG12_1X12; + } else if (pad == IPIPE_PAD_SOURCE) { + for (i = 0; i < ARRAY_SIZE(ipipe_output_fmts); i++) + if (fmt->code == ipipe_output_fmts[i]) + break; + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(ipipe_output_fmts)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + } + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_HEIGHT, max_out_width); + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_WIDTH, max_out_height); +} + +/* + * ipipe_set_format() - Handle set format by pads subdev method + * @sd: pointer to v4l2 subdev structure + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int +ipipe_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __ipipe_get_format(ipipe, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + ipipe_try_format(ipipe, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (fmt->pad == IPIPE_PAD_SINK && + (ipipe->input == IPIPE_INPUT_CCDC || + ipipe->input == IPIPE_INPUT_MEMORY)) + ipipe->formats[fmt->pad] = fmt->format; + else if (fmt->pad == IPIPE_PAD_SOURCE && + ipipe->output == IPIPE_OUTPUT_RESIZER) + ipipe->formats[fmt->pad] = fmt->format; + else + return -EINVAL; + + return 0; +} + +/* + * ipipe_get_format() - Handle get format by pads subdev method. + * @sd: pointer to v4l2 subdev structure. + * @fh: V4L2 subdev file handle. + * @fmt: pointer to v4l2 subdev format structure. + */ +static int +ipipe_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + fmt->format = ipipe->formats[fmt->pad]; + else + fmt->format = *(v4l2_subdev_get_try_format(fh, fmt->pad)); + + return 0; +} + +/* + * ipipe_enum_frame_size() - enum frame sizes on pads + * @sd: pointer to v4l2 subdev structure. + * @fh: V4L2 subdev file handle. + * @fse: pointer to v4l2_subdev_frame_size_enum structure. + */ +static int +ipipe_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + ipipe_try_format(ipipe, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + ipipe_try_format(ipipe, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * ipipe_enum_mbus_code() - enum mbus codes for pads + * @sd: pointer to v4l2 subdev structure. + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + */ +static int +ipipe_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + switch (code->pad) { + case IPIPE_PAD_SINK: + if (code->index >= ARRAY_SIZE(ipipe_input_fmts)) + return -EINVAL; + code->code = ipipe_input_fmts[code->index]; + break; + + case IPIPE_PAD_SOURCE: + if (code->index >= ARRAY_SIZE(ipipe_output_fmts)) + return -EINVAL; + code->code = ipipe_output_fmts[code->index]; + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * ipipe_s_ctrl() - Handle set control subdev method + * @ctrl: pointer to v4l2 control structure + */ +static int ipipe_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vpfe_ipipe_device *ipipe = + container_of(ctrl->handler, struct vpfe_ipipe_device, ctrls); + struct ipipe_lum_adj *lum_adj = &ipipe->config.lum_adj; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + lum_adj->brightness = ctrl->val; + ipipe_set_lum_adj_regs(ipipe->base_addr, lum_adj); + break; + + case V4L2_CID_CONTRAST: + lum_adj->contrast = ctrl->val; + ipipe_set_lum_adj_regs(ipipe->base_addr, lum_adj); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * ipipe_init_formats() - Initialize formats on all pads + * @sd: pointer to v4l2 subdev structure. + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int +ipipe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + format.pad = IPIPE_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG12_1X12; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; + ipipe_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = IPIPE_PAD_SOURCE; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; + ipipe_set_format(sd, fh, &format); + + return 0; +} + +/* subdev core operations */ +static const struct v4l2_subdev_core_ops ipipe_v4l2_core_ops = { + .ioctl = ipipe_ioctl, +}; + +static const struct v4l2_ctrl_ops ipipe_ctrl_ops = { + .s_ctrl = ipipe_s_ctrl, +}; + +/* subdev file operations */ +static const struct v4l2_subdev_internal_ops ipipe_v4l2_internal_ops = { + .open = ipipe_init_formats, +}; + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops ipipe_v4l2_video_ops = { + .s_stream = ipipe_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops ipipe_v4l2_pad_ops = { + .enum_mbus_code = ipipe_enum_mbus_code, + .enum_frame_size = ipipe_enum_frame_size, + .get_fmt = ipipe_get_format, + .set_fmt = ipipe_set_format, +}; + +/* v4l2 subdev operation */ +static const struct v4l2_subdev_ops ipipe_v4l2_ops = { + .core = &ipipe_v4l2_core_ops, + .video = &ipipe_v4l2_video_ops, + .pad = &ipipe_v4l2_pad_ops, +}; + +/* + * Media entity operations + */ + +/* + * ipipe_link_setup() - Setup ipipe connections + * @entity: ipipe media entity + * @local: Pad at the local end of the link + * @remote: Pad at the remote end of the link + * @flags: Link flags + * + * return -EINVAL or zero on success + */ +static int +ipipe_link_setup(struct media_entity *entity, const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe); + u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; + + switch (local->index | media_entity_type(remote->entity)) { + case IPIPE_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ipipe->input = IPIPE_INPUT_NONE; + break; + } + if (ipipe->input != IPIPE_INPUT_NONE) + return -EBUSY; + if (ipipeif_sink == IPIPEIF_INPUT_MEMORY) + ipipe->input = IPIPE_INPUT_MEMORY; + else + ipipe->input = IPIPE_INPUT_CCDC; + break; + + case IPIPE_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + /* out to RESIZER */ + if (flags & MEDIA_LNK_FL_ENABLED) + ipipe->output = IPIPE_OUTPUT_RESIZER; + else + ipipe->output = IPIPE_OUTPUT_NONE; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static const struct media_entity_operations ipipe_media_ops = { + .link_setup = ipipe_link_setup, +}; + +/* + * vpfe_ipipe_unregister_entities() - ipipe unregister entity + * @vpfe_ipipe: pointer to ipipe subdevice structure. + */ +void vpfe_ipipe_unregister_entities(struct vpfe_ipipe_device *vpfe_ipipe) +{ + /* cleanup entity */ + media_entity_cleanup(&vpfe_ipipe->subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&vpfe_ipipe->subdev); +} + +/* + * vpfe_ipipe_register_entities() - ipipe register entity + * @ipipe: pointer to ipipe subdevice structure. + * @vdev: pointer to v4l2 device structure. + */ +int +vpfe_ipipe_register_entities(struct vpfe_ipipe_device *ipipe, + struct v4l2_device *vdev) +{ + int ret; + + /* Register the subdev */ + ret = v4l2_device_register_subdev(vdev, &ipipe->subdev); + if (ret) { + pr_err("Failed to register ipipe as v4l2 subdevice\n"); + return ret; + } + + return ret; +} + +#define IPIPE_CONTRAST_HIGH 0xff +#define IPIPE_BRIGHT_HIGH 0xff + +/* + * vpfe_ipipe_init() - ipipe module initialization. + * @ipipe: pointer to ipipe subdevice structure. + * @pdev: platform device pointer. + */ +int +vpfe_ipipe_init(struct vpfe_ipipe_device *ipipe, struct platform_device *pdev) +{ + struct media_pad *pads = &ipipe->pads[0]; + struct v4l2_subdev *sd = &ipipe->subdev; + struct media_entity *me = &sd->entity; + static resource_size_t res_len; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 4); + if (!res) + return -ENOENT; + + res_len = resource_size(res); + res = request_mem_region(res->start, res_len, res->name); + if (!res) + return -EBUSY; + ipipe->base_addr = ioremap_nocache(res->start, res_len); + if (!ipipe->base_addr) + return -EBUSY; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 6); + if (!res) + return -ENOENT; + ipipe->isp5_base_addr = ioremap_nocache(res->start, res_len); + if (!ipipe->isp5_base_addr) + return -EBUSY; + + v4l2_subdev_init(sd, &ipipe_v4l2_ops); + sd->internal_ops = &ipipe_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI IPIPE", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, ipipe); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[IPIPE_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[IPIPE_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ipipe->input = IPIPE_INPUT_NONE; + ipipe->output = IPIPE_OUTPUT_NONE; + + me->ops = &ipipe_media_ops; + v4l2_ctrl_handler_init(&ipipe->ctrls, 2); + v4l2_ctrl_new_std(&ipipe->ctrls, &ipipe_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, + IPIPE_BRIGHT_HIGH, 1, 16); + v4l2_ctrl_new_std(&ipipe->ctrls, &ipipe_ctrl_ops, + V4L2_CID_CONTRAST, 0, + IPIPE_CONTRAST_HIGH, 1, 16); + + + v4l2_ctrl_handler_setup(&ipipe->ctrls); + sd->ctrl_handler = &ipipe->ctrls; + + return media_entity_init(me, IPIPE_PADS_NUM, pads, 0); +} + +/* + * vpfe_ipipe_cleanup() - ipipe subdevice cleanup. + * @ipipe: pointer to ipipe subdevice + * @dev: pointer to platform device + */ +void vpfe_ipipe_cleanup(struct vpfe_ipipe_device *ipipe, + struct platform_device *pdev) +{ + struct resource *res; + + v4l2_ctrl_handler_free(&ipipe->ctrls); + + iounmap(ipipe->base_addr); + iounmap(ipipe->isp5_base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 4); + if (res) + release_mem_region(res->start, res->end - res->start + 1); +} diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.h b/drivers/staging/media/davinci_vpfe/dm365_ipipe.h new file mode 100644 index 000000000000..cf4204603eb8 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.h @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _DAVINCI_VPFE_DM365_IPIPE_H +#define _DAVINCI_VPFE_DM365_IPIPE_H + +#include + +#include +#include + +#include "davinci_vpfe_user.h" +#include "vpfe_video.h" + +#define CEIL(a, b) (((a) + (b-1)) / (b)) + +enum ipipe_noise_filter { + IPIPE_D2F_1ST = 0, + IPIPE_D2F_2ND = 1, +}; + +/* Used for driver storage */ +struct ipipe_otfdpc_2_0 { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* defect detection method */ + enum vpfe_ipipe_otfdpc_det_meth det_method; + /* Algorithm used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is + * used + */ + enum vpfe_ipipe_otfdpc_alg alg; + struct vpfe_ipipe_otfdpc_2_0_cfg otfdpc_2_0; +}; + +struct ipipe_otfdpc_3_0 { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* defect detection method */ + enum vpfe_ipipe_otfdpc_det_meth det_method; + /* Algorithm used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is + * used + */ + enum vpfe_ipipe_otfdpc_alg alg; + struct vpfe_ipipe_otfdpc_3_0_cfg otfdpc_3_0; +}; + +/* Structure for configuring Luminance Adjustment module */ +struct ipipe_lum_adj { + /* Brightness adjustments */ + unsigned char brightness; + /* contrast adjustments */ + unsigned char contrast; +}; + +enum ipipe_rgb2rgb { + IPIPE_RGB2RGB_1 = 0, + IPIPE_RGB2RGB_2 = 1, +}; + +struct ipipe_module_params { + __u32 flag; + struct vpfe_ipipe_input_config input_config; + struct vpfe_ipipe_lutdpc lutdpc; + struct vpfe_ipipe_otfdpc otfdpc; + struct vpfe_ipipe_nf nf1; + struct vpfe_ipipe_nf nf2; + struct vpfe_ipipe_gic gic; + struct vpfe_ipipe_wb wbal; + struct vpfe_ipipe_cfa cfa; + struct vpfe_ipipe_rgb2rgb rgb2rgb1; + struct vpfe_ipipe_rgb2rgb rgb2rgb2; + struct vpfe_ipipe_gamma gamma; + struct vpfe_ipipe_3d_lut lut; + struct vpfe_ipipe_rgb2yuv rgb2yuv; + struct vpfe_ipipe_gbce gbce; + struct vpfe_ipipe_yuv422_conv yuv422_conv; + struct vpfe_ipipe_yee yee; + struct vpfe_ipipe_car car; + struct vpfe_ipipe_cgs cgs; + struct ipipe_lum_adj lum_adj; +}; + +#define IPIPE_PAD_SINK 0 +#define IPIPE_PAD_SOURCE 1 + +#define IPIPE_PADS_NUM 2 + +#define IPIPE_OUTPUT_NONE 0 +#define IPIPE_OUTPUT_RESIZER (1 << 0) + +enum ipipe_input_entity { + IPIPE_INPUT_NONE = 0, + IPIPE_INPUT_MEMORY = 1, + IPIPE_INPUT_CCDC = 2, +}; + + +struct vpfe_ipipe_device { + struct v4l2_subdev subdev; + struct media_pad pads[IPIPE_PADS_NUM]; + struct v4l2_mbus_framefmt formats[IPIPE_PADS_NUM]; + enum ipipe_input_entity input; + unsigned int output; + struct v4l2_ctrl_handler ctrls; + void *__iomem base_addr; + void *__iomem isp5_base_addr; + struct ipipe_module_params config; +}; + +struct ipipe_module_if { + unsigned int param_offset; + unsigned int param_size; + unsigned int config_offset; + int (*set)(struct vpfe_ipipe_device *ipipe, void *param); + int (*get)(struct vpfe_ipipe_device *ipipe, void *param); +}; + +/* data paths */ +enum ipipe_data_paths { + IPIPE_RAW2YUV, + /* Bayer RAW input to YCbCr output */ + IPIPE_RAW2RAW, + /* Bayer Raw to Bayer output */ + IPIPE_RAW2BOX, + /* Bayer Raw to Boxcar output */ + IPIPE_YUV2YUV + /* YUV Raw to YUV Raw output */ +}; + +#define IPIPE_COLPTN_R_Ye 0x0 +#define IPIPE_COLPTN_Gr_Cy 0x1 +#define IPIPE_COLPTN_Gb_G 0x2 +#define IPIPE_COLPTN_B_Mg 0x3 + +#define COLPAT_EE_SHIFT 0 +#define COLPAT_EO_SHIFT 2 +#define COLPAT_OE_SHIFT 4 +#define COLPAT_OO_SHIFT 6 + +#define ipipe_sgrbg_pattern \ + (IPIPE_COLPTN_Gr_Cy << COLPAT_EE_SHIFT | \ + IPIPE_COLPTN_R_Ye << COLPAT_EO_SHIFT | \ + IPIPE_COLPTN_B_Mg << COLPAT_OE_SHIFT | \ + IPIPE_COLPTN_Gb_G << COLPAT_OO_SHIFT) + +#define ipipe_srggb_pattern \ + (IPIPE_COLPTN_R_Ye << COLPAT_EE_SHIFT | \ + IPIPE_COLPTN_Gr_Cy << COLPAT_EO_SHIFT | \ + IPIPE_COLPTN_Gb_G << COLPAT_OE_SHIFT | \ + IPIPE_COLPTN_B_Mg << COLPAT_OO_SHIFT) + +int vpfe_ipipe_register_entities(struct vpfe_ipipe_device *ipipe, + struct v4l2_device *v4l2_dev); +int vpfe_ipipe_init(struct vpfe_ipipe_device *ipipe, + struct platform_device *pdev); +void vpfe_ipipe_unregister_entities(struct vpfe_ipipe_device *ipipe); +void vpfe_ipipe_cleanup(struct vpfe_ipipe_device *ipipe, + struct platform_device *pdev); +void vpfe_ipipe_enable(struct vpfe_device *vpfe_dev, int en); + +#endif /* _DAVINCI_VPFE_DM365_IPIPE_H */ -- cgit v1.2.3 From 26c0e9992c266ad05278da72902a5f9724962c65 Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Wed, 28 Nov 2012 02:16:35 -0300 Subject: [media] davinci: vpfe: dm365: add IPIPE hardware layer support IPIPE is the hardware IP which implements the functionality required for resizer, ipipe(colorspace converter) and the associated hardware support. This patch implements hardware setup including coefficient programming for various hardware filters, gamma, cfa and clock enabling. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/davinci_vpfe/dm365_ipipe_hw.c | 1048 ++++++++++++++++++++ .../staging/media/davinci_vpfe/dm365_ipipe_hw.h | 559 +++++++++++ 2 files changed, 1607 insertions(+) create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h (limited to 'drivers') diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c new file mode 100644 index 000000000000..e027b92b54ef --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c @@ -0,0 +1,1048 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#include "dm365_ipipe_hw.h" + +#define IPIPE_MODE_CONTINUOUS 0 +#define IPIPE_MODE_SINGLE_SHOT 1 + +static void ipipe_clock_enable(void *__iomem base_addr) +{ + /* enable IPIPE MMR for register write access */ + regw_ip(base_addr, IPIPE_GCK_MMR_DEFAULT, IPIPE_GCK_MMR); + + /* enable the clock wb,cfa,dfc,d2f,pre modules */ + regw_ip(base_addr, IPIPE_GCK_PIX_DEFAULT, IPIPE_GCK_PIX); +} + +static void +rsz_set_common_params(void *__iomem rsz_base, struct resizer_params *params) +{ + struct rsz_common_params *rsz_common = ¶ms->rsz_common; + u32 val; + + /* Set mode */ + regw_rsz(rsz_base, params->oper_mode, RSZ_SRC_MODE); + + /* data source selection and bypass */ + val = (rsz_common->passthrough << RSZ_BYPASS_SHIFT) | + rsz_common->source; + regw_rsz(rsz_base, val, RSZ_SRC_FMT0); + + /* src image selection */ + val = (rsz_common->raw_flip & 1) | + (rsz_common->src_img_fmt << RSZ_SRC_IMG_FMT_SHIFT) | + ((rsz_common->y_c & 1) << RSZ_SRC_Y_C_SEL_SHIFT); + regw_rsz(rsz_base, val, RSZ_SRC_FMT1); + + regw_rsz(rsz_base, rsz_common->vps & IPIPE_RSZ_VPS_MASK, RSZ_SRC_VPS); + regw_rsz(rsz_base, rsz_common->hps & IPIPE_RSZ_HPS_MASK, RSZ_SRC_HPS); + regw_rsz(rsz_base, rsz_common->vsz & IPIPE_RSZ_VSZ_MASK, RSZ_SRC_VSZ); + regw_rsz(rsz_base, rsz_common->hsz & IPIPE_RSZ_HSZ_MASK, RSZ_SRC_HSZ); + regw_rsz(rsz_base, rsz_common->yuv_y_min, RSZ_YUV_Y_MIN); + regw_rsz(rsz_base, rsz_common->yuv_y_max, RSZ_YUV_Y_MAX); + regw_rsz(rsz_base, rsz_common->yuv_c_min, RSZ_YUV_C_MIN); + regw_rsz(rsz_base, rsz_common->yuv_c_max, RSZ_YUV_C_MAX); + /* chromatic position */ + regw_rsz(rsz_base, rsz_common->out_chr_pos, RSZ_YUV_PHS); +} + +static void +rsz_set_rsz_regs(void *__iomem rsz_base, unsigned int rsz_id, + struct resizer_params *params) +{ + struct resizer_scale_param *rsc_params; + struct rsz_ext_mem_param *ext_mem; + struct resizer_rgb *rgb; + u32 reg_base; + u32 val; + + rsc_params = ¶ms->rsz_rsc_param[rsz_id]; + rgb = ¶ms->rsz2rgb[rsz_id]; + ext_mem = ¶ms->ext_mem_param[rsz_id]; + + if (rsz_id == RSZ_A) { + val = rsc_params->h_flip << RSZA_H_FLIP_SHIFT; + val |= rsc_params->v_flip << RSZA_V_FLIP_SHIFT; + reg_base = RSZ_EN_A; + } else { + val = rsc_params->h_flip << RSZB_H_FLIP_SHIFT; + val |= rsc_params->v_flip << RSZB_V_FLIP_SHIFT; + reg_base = RSZ_EN_B; + } + /* update flip settings */ + regw_rsz(rsz_base, val, RSZ_SEQ); + + regw_rsz(rsz_base, params->oper_mode, reg_base + RSZ_MODE); + + val = (rsc_params->cen << RSZ_CEN_SHIFT) | rsc_params->yen; + regw_rsz(rsz_base, val, reg_base + RSZ_420); + + regw_rsz(rsz_base, rsc_params->i_vps & RSZ_VPS_MASK, + reg_base + RSZ_I_VPS); + regw_rsz(rsz_base, rsc_params->i_hps & RSZ_HPS_MASK, + reg_base + RSZ_I_HPS); + regw_rsz(rsz_base, rsc_params->o_vsz & RSZ_O_VSZ_MASK, + reg_base + RSZ_O_VSZ); + regw_rsz(rsz_base, rsc_params->o_hsz & RSZ_O_HSZ_MASK, + reg_base + RSZ_O_HSZ); + regw_rsz(rsz_base, rsc_params->v_phs_y & RSZ_V_PHS_MASK, + reg_base + RSZ_V_PHS_Y); + regw_rsz(rsz_base, rsc_params->v_phs_c & RSZ_V_PHS_MASK, + reg_base + RSZ_V_PHS_C); + + /* keep this additional adjustment to zero for now */ + regw_rsz(rsz_base, rsc_params->v_dif & RSZ_V_DIF_MASK, + reg_base + RSZ_V_DIF); + + val = (rsc_params->v_typ_y & 1) | + ((rsc_params->v_typ_c & 1) << RSZ_TYP_C_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_V_TYP); + + val = (rsc_params->v_lpf_int_y & RSZ_LPF_INT_MASK) | + ((rsc_params->v_lpf_int_c & RSZ_LPF_INT_MASK) << + RSZ_LPF_INT_C_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_V_LPF); + + regw_rsz(rsz_base, rsc_params->h_phs & + RSZ_H_PHS_MASK, reg_base + RSZ_H_PHS); + + regw_rsz(rsz_base, 0, reg_base + RSZ_H_PHS_ADJ); + regw_rsz(rsz_base, rsc_params->h_dif & + RSZ_H_DIF_MASK, reg_base + RSZ_H_DIF); + + val = (rsc_params->h_typ_y & 1) | + ((rsc_params->h_typ_c & 1) << RSZ_TYP_C_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_H_TYP); + + val = (rsc_params->h_lpf_int_y & RSZ_LPF_INT_MASK) | + ((rsc_params->h_lpf_int_c & RSZ_LPF_INT_MASK) << + RSZ_LPF_INT_C_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_H_LPF); + + regw_rsz(rsz_base, rsc_params->dscale_en & 1, reg_base + RSZ_DWN_EN); + + val = (rsc_params->h_dscale_ave_sz & RSZ_DWN_SCALE_AV_SZ_MASK) | + ((rsc_params->v_dscale_ave_sz & RSZ_DWN_SCALE_AV_SZ_MASK) << + RSZ_DWN_SCALE_AV_SZ_V_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_DWN_AV); + + /* setting rgb conversion parameters */ + regw_rsz(rsz_base, rgb->rgb_en, reg_base + RSZ_RGB_EN); + + val = (rgb->rgb_typ << RSZ_RGB_TYP_SHIFT) | + (rgb->rgb_msk0 << RSZ_RGB_MSK0_SHIFT) | + (rgb->rgb_msk1 << RSZ_RGB_MSK1_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_RGB_TYP); + + regw_rsz(rsz_base, rgb->rgb_alpha_val & RSZ_RGB_ALPHA_MASK, + reg_base + RSZ_RGB_BLD); + + /* setting external memory parameters */ + regw_rsz(rsz_base, ext_mem->rsz_sdr_oft_y, reg_base + RSZ_SDR_Y_OFT); + regw_rsz(rsz_base, ext_mem->rsz_sdr_ptr_s_y, + reg_base + RSZ_SDR_Y_PTR_S); + regw_rsz(rsz_base, ext_mem->rsz_sdr_ptr_e_y, + reg_base + RSZ_SDR_Y_PTR_E); + regw_rsz(rsz_base, ext_mem->rsz_sdr_oft_c, reg_base + RSZ_SDR_C_OFT); + regw_rsz(rsz_base, ext_mem->rsz_sdr_ptr_s_c, + reg_base + RSZ_SDR_C_PTR_S); + regw_rsz(rsz_base, (ext_mem->rsz_sdr_ptr_e_c >> 1), + reg_base + RSZ_SDR_C_PTR_E); +} + +/*set the registers of either RSZ0 or RSZ1 */ +static void +ipipe_setup_resizer(void *__iomem rsz_base, struct resizer_params *params) +{ + /* enable MMR gate to write to Resizer */ + regw_rsz(rsz_base, 1, RSZ_GCK_MMR); + + /* Enable resizer if it is not in bypass mode */ + if (params->rsz_common.passthrough) + regw_rsz(rsz_base, 0, RSZ_GCK_SDR); + else + regw_rsz(rsz_base, 1, RSZ_GCK_SDR); + + rsz_set_common_params(rsz_base, params); + + regw_rsz(rsz_base, params->rsz_en[RSZ_A], RSZ_EN_A); + + if (params->rsz_en[RSZ_A]) + /*setting rescale parameters */ + rsz_set_rsz_regs(rsz_base, RSZ_A, params); + + regw_rsz(rsz_base, params->rsz_en[RSZ_B], RSZ_EN_B); + + if (params->rsz_en[RSZ_B]) + rsz_set_rsz_regs(rsz_base, RSZ_B, params); +} + +static u32 ipipe_get_color_pat(enum v4l2_mbus_pixelcode pix) +{ + switch (pix) { + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_SGRBG12_1X12: + return ipipe_sgrbg_pattern; + + default: + return ipipe_srggb_pattern; + } +} + +static int ipipe_get_data_path(struct vpfe_ipipe_device *ipipe) +{ + enum v4l2_mbus_pixelcode temp_pix_fmt; + + switch (ipipe->formats[IPIPE_PAD_SINK].code) { + case V4L2_MBUS_FMT_SBGGR8_1X8: + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_SGRBG12_1X12: + temp_pix_fmt = V4L2_MBUS_FMT_SGRBG12_1X12; + break; + + default: + temp_pix_fmt = V4L2_MBUS_FMT_UYVY8_2X8; + } + + if (temp_pix_fmt == V4L2_MBUS_FMT_SGRBG12_1X12) { + if (ipipe->formats[IPIPE_PAD_SOURCE].code == + V4L2_MBUS_FMT_SGRBG12_1X12) + return IPIPE_RAW2RAW; + return IPIPE_RAW2YUV; + } + + return IPIPE_YUV2YUV; +} + +static int get_ipipe_mode(struct vpfe_ipipe_device *ipipe) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe); + u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; + + if (ipipeif_sink == IPIPEIF_INPUT_MEMORY) + return IPIPE_MODE_SINGLE_SHOT; + else if (ipipeif_sink == IPIPEIF_INPUT_ISIF) + return IPIPE_MODE_CONTINUOUS; + + return -EINVAL; +} + +int config_ipipe_hw(struct vpfe_ipipe_device *ipipe) +{ + struct vpfe_ipipe_input_config *config = &ipipe->config.input_config; + void __iomem *ipipe_base = ipipe->base_addr; + struct v4l2_mbus_framefmt *outformat; + u32 color_pat; + u32 ipipe_mode; + u32 data_path; + + /* enable clock to IPIPE */ + vpss_enable_clock(VPSS_IPIPE_CLOCK, 1); + ipipe_clock_enable(ipipe_base); + + if (ipipe->input == IPIPE_INPUT_NONE) { + regw_ip(ipipe_base, 0, IPIPE_SRC_EN); + return 0; + } + + ipipe_mode = get_ipipe_mode(ipipe); + if (ipipe < 0) { + pr_err("Failed to get ipipe mode"); + return -EINVAL; + } + regw_ip(ipipe_base, ipipe_mode, IPIPE_SRC_MODE); + + data_path = ipipe_get_data_path(ipipe); + regw_ip(ipipe_base, data_path, IPIPE_SRC_FMT); + + regw_ip(ipipe_base, config->vst & IPIPE_RSZ_VPS_MASK, IPIPE_SRC_VPS); + regw_ip(ipipe_base, config->hst & IPIPE_RSZ_HPS_MASK, IPIPE_SRC_HPS); + + outformat = &ipipe->formats[IPIPE_PAD_SOURCE]; + regw_ip(ipipe_base, (outformat->height + 1) & IPIPE_RSZ_VSZ_MASK, + IPIPE_SRC_VSZ); + regw_ip(ipipe_base, (outformat->width + 1) & IPIPE_RSZ_HSZ_MASK, + IPIPE_SRC_HSZ); + + if (data_path == IPIPE_RAW2YUV || + data_path == IPIPE_RAW2RAW) { + color_pat = + ipipe_get_color_pat(ipipe->formats[IPIPE_PAD_SINK].code); + regw_ip(ipipe_base, color_pat, IPIPE_SRC_COL); + } + + return 0; +} + +/* + * config_rsz_hw() - Performs hardware setup of resizer. + */ +int config_rsz_hw(struct vpfe_resizer_device *resizer, + struct resizer_params *config) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + void *__iomem ipipe_base = vpfe_dev->vpfe_ipipe.base_addr; + void *__iomem rsz_base = vpfe_dev->vpfe_resizer.base_addr; + + /* enable VPSS clock */ + vpss_enable_clock(VPSS_IPIPE_CLOCK, 1); + ipipe_clock_enable(ipipe_base); + + ipipe_setup_resizer(rsz_base, config); + + return 0; +} + +static void +rsz_set_y_address(void *__iomem rsz_base, unsigned int address, + unsigned int offset) +{ + u32 val; + + val = address & SET_LOW_ADDR; + regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_BAD_L); + regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_SAD_L); + + val = (address & SET_HIGH_ADDR) >> 16; + regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_BAD_H); + regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_SAD_H); +} + +static void +rsz_set_c_address(void *__iomem rsz_base, unsigned int address, + unsigned int offset) +{ + u32 val; + + val = address & SET_LOW_ADDR; + regw_rsz(rsz_base, val, offset + RSZ_SDR_C_BAD_L); + regw_rsz(rsz_base, val, offset + RSZ_SDR_C_SAD_L); + + val = (address & SET_HIGH_ADDR) >> 16; + regw_rsz(rsz_base, val, offset + RSZ_SDR_C_BAD_H); + regw_rsz(rsz_base, val, offset + RSZ_SDR_C_SAD_H); +} + +/* + * resizer_set_outaddr() - set the address for given resize_no + * @rsz_base: resizer base address + * @params: pointer to ipipe_params structure + * @resize_no: 0 - Resizer-A, 1 - Resizer B + * @address: the address to set + */ +int +resizer_set_outaddr(void *__iomem rsz_base, struct resizer_params *params, + int resize_no, unsigned int address) +{ + struct resizer_scale_param *rsc_param; + struct rsz_ext_mem_param *mem_param; + struct rsz_common_params *rsz_common; + unsigned int rsz_start_add; + unsigned int val; + + if (resize_no != RSZ_A && resize_no != RSZ_B) + return -EINVAL; + + mem_param = ¶ms->ext_mem_param[resize_no]; + rsc_param = ¶ms->rsz_rsc_param[resize_no]; + rsz_common = ¶ms->rsz_common; + + if (resize_no == RSZ_A) + rsz_start_add = RSZ_EN_A; + else + rsz_start_add = RSZ_EN_B; + + /* y_c = 0 for y, = 1 for c */ + if (rsz_common->src_img_fmt == RSZ_IMG_420) { + if (rsz_common->y_c) { + /* C channel */ + val = address + mem_param->flip_ofst_c; + rsz_set_c_address(rsz_base, val, rsz_start_add); + } else { + val = address + mem_param->flip_ofst_y; + rsz_set_y_address(rsz_base, val, rsz_start_add); + } + } else { + if (rsc_param->cen && rsc_param->yen) { + /* 420 */ + val = address + mem_param->c_offset + + mem_param->flip_ofst_c + + mem_param->user_y_ofst + + mem_param->user_c_ofst; + if (resize_no == RSZ_B) + val += + params->ext_mem_param[RSZ_A].user_y_ofst + + params->ext_mem_param[RSZ_A].user_c_ofst; + /* set C address */ + rsz_set_c_address(rsz_base, val, rsz_start_add); + } + val = address + mem_param->flip_ofst_y + mem_param->user_y_ofst; + if (resize_no == RSZ_B) + val += params->ext_mem_param[RSZ_A].user_y_ofst + + params->ext_mem_param[RSZ_A].user_c_ofst; + /* set Y address */ + rsz_set_y_address(rsz_base, val, rsz_start_add); + } + /* resizer must be enabled */ + regw_rsz(rsz_base, params->rsz_en[resize_no], rsz_start_add); + + return 0; +} + +void +ipipe_set_lutdpc_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, + struct vpfe_ipipe_lutdpc *dpc) +{ + u32 max_tbl_size = LUT_DPC_MAX_SIZE >> 1; + u32 lut_start_addr = DPC_TB0_START_ADDR; + u32 val; + u32 count; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, dpc->en, DPC_LUT_EN); + + if (dpc->en != 1) + return; + + val = LUTDPC_TBL_256_EN | (dpc->repl_white & 1); + regw_ip(base_addr, val, DPC_LUT_SEL); + regw_ip(base_addr, LUT_DPC_START_ADDR, DPC_LUT_ADR); + regw_ip(base_addr, dpc->dpc_size, DPC_LUT_SIZ & LUT_DPC_SIZE_MASK); + + if (dpc->table == NULL) + return; + + for (count = 0; count < dpc->dpc_size; count++) { + if (count >= max_tbl_size) + lut_start_addr = DPC_TB1_START_ADDR; + val = (dpc->table[count].horz_pos & LUT_DPC_H_POS_MASK) | + ((dpc->table[count].vert_pos & LUT_DPC_V_POS_MASK) << + LUT_DPC_V_POS_SHIFT) | (dpc->table[count].method << + LUT_DPC_CORR_METH_SHIFT); + w_ip_table(isp5_base_addr, val, (lut_start_addr + + ((count % max_tbl_size) << 2))); + } +} + +static void +set_dpc_thresholds(void *__iomem base_addr, + struct vpfe_ipipe_otfdpc_2_0_cfg *dpc_thr) +{ + regw_ip(base_addr, dpc_thr->corr_thr.r & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2C_THR_R); + regw_ip(base_addr, dpc_thr->corr_thr.gr & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2C_THR_GR); + regw_ip(base_addr, dpc_thr->corr_thr.gb & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2C_THR_GB); + regw_ip(base_addr, dpc_thr->corr_thr.b & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2C_THR_B); + regw_ip(base_addr, dpc_thr->det_thr.r & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2D_THR_R); + regw_ip(base_addr, dpc_thr->det_thr.gr & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2D_THR_GR); + regw_ip(base_addr, dpc_thr->det_thr.gb & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2D_THR_GB); + regw_ip(base_addr, dpc_thr->det_thr.b & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2D_THR_B); +} + +void ipipe_set_otfdpc_regs(void *__iomem base_addr, + struct vpfe_ipipe_otfdpc *otfdpc) +{ + struct vpfe_ipipe_otfdpc_2_0_cfg *dpc_2_0 = &otfdpc->alg_cfg.dpc_2_0; + struct vpfe_ipipe_otfdpc_3_0_cfg *dpc_3_0 = &otfdpc->alg_cfg.dpc_3_0; + u32 val; + + ipipe_clock_enable(base_addr); + + regw_ip(base_addr, (otfdpc->en & 1), DPC_OTF_EN); + if (!otfdpc->en) + return; + + /* dpc enabled */ + val = (otfdpc->det_method << OTF_DET_METHOD_SHIFT) | otfdpc->alg; + regw_ip(base_addr, val, DPC_OTF_TYP); + + if (otfdpc->det_method == VPFE_IPIPE_DPC_OTF_MIN_MAX) { + /* ALG= 0, TYP = 0, DPC_OTF_2D_THR_[x]=0 + * DPC_OTF_2C_THR_[x] = Maximum thresohld + * MinMax method + */ + dpc_2_0->det_thr.r = dpc_2_0->det_thr.gb = + dpc_2_0->det_thr.gr = dpc_2_0->det_thr.b = 0; + set_dpc_thresholds(base_addr, dpc_2_0); + return; + } + /* MinMax2 */ + if (otfdpc->alg == VPFE_IPIPE_OTFDPC_2_0) { + set_dpc_thresholds(base_addr, dpc_2_0); + return; + } + regw_ip(base_addr, dpc_3_0->act_adj_shf & + OTF_DPC3_0_SHF_MASK, DPC_OTF_3_SHF); + /* Detection thresholds */ + regw_ip(base_addr, ((dpc_3_0->det_thr & OTF_DPC3_0_THR_MASK) << + OTF_DPC3_0_THR_SHIFT), DPC_OTF_3D_THR); + regw_ip(base_addr, dpc_3_0->det_slp & + OTF_DPC3_0_SLP_MASK, DPC_OTF_3D_SLP); + regw_ip(base_addr, dpc_3_0->det_thr_min & + OTF_DPC3_0_DET_MASK, DPC_OTF_3D_MIN); + regw_ip(base_addr, dpc_3_0->det_thr_max & + OTF_DPC3_0_DET_MASK, DPC_OTF_3D_MAX); + /* Correction thresholds */ + regw_ip(base_addr, ((dpc_3_0->corr_thr & OTF_DPC3_0_THR_MASK) << + OTF_DPC3_0_THR_SHIFT), DPC_OTF_3C_THR); + regw_ip(base_addr, dpc_3_0->corr_slp & + OTF_DPC3_0_SLP_MASK, DPC_OTF_3C_SLP); + regw_ip(base_addr, dpc_3_0->corr_thr_min & + OTF_DPC3_0_CORR_MASK, DPC_OTF_3C_MIN); + regw_ip(base_addr, dpc_3_0->corr_thr_max & + OTF_DPC3_0_CORR_MASK, DPC_OTF_3C_MAX); +} + +/* 2D Noise filter */ +void +ipipe_set_d2f_regs(void *__iomem base_addr, unsigned int id, + struct vpfe_ipipe_nf *noise_filter) +{ + + u32 offset = D2F_1ST; + int count; + u32 val; + + if (id == IPIPE_D2F_2ND) + offset = D2F_2ND; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, noise_filter->en & 1, offset + D2F_EN); + if (!noise_filter->en) + return; + + /*noise filter enabled */ + /* Combine all the fields to make D2F_CFG register of IPIPE */ + val = ((noise_filter->spread_val & D2F_SPR_VAL_MASK) << + D2F_SPR_VAL_SHIFT) | ((noise_filter->shft_val & + D2F_SHFT_VAL_MASK) << D2F_SHFT_VAL_SHIFT) | + (noise_filter->gr_sample_meth << D2F_SAMPLE_METH_SHIFT) | + ((noise_filter->apply_lsc_gain & 1) << + D2F_APPLY_LSC_GAIN_SHIFT) | D2F_USE_SPR_REG_VAL; + regw_ip(base_addr, val, offset + D2F_TYP); + + /* edge detection minimum */ + regw_ip(base_addr, noise_filter->edge_det_min_thr & + D2F_EDGE_DET_THR_MASK, offset + D2F_EDG_MIN); + + /* edge detection maximum */ + regw_ip(base_addr, noise_filter->edge_det_max_thr & + D2F_EDGE_DET_THR_MASK, offset + D2F_EDG_MAX); + + for (count = 0; count < VPFE_IPIPE_NF_STR_TABLE_SIZE; count++) + regw_ip(base_addr, + (noise_filter->str[count] & D2F_STR_VAL_MASK), + offset + D2F_STR + count * 4); + + for (count = 0; count < VPFE_IPIPE_NF_THR_TABLE_SIZE; count++) + regw_ip(base_addr, noise_filter->thr[count] & D2F_THR_VAL_MASK, + offset + D2F_THR + count * 4); +} + +#define IPIPE_U8Q5(decimal, integer) \ + (((decimal & 0x1f) | ((integer & 0x7) << 5))) + +/* Green Imbalance Correction */ +void ipipe_set_gic_regs(void *__iomem base_addr, struct vpfe_ipipe_gic *gic) +{ + u32 val; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, gic->en & 1, GIC_EN); + + if (!gic->en) + return; + + /*gic enabled */ + val = (gic->wt_fn_type << GIC_TYP_SHIFT) | + (gic->thr_sel << GIC_THR_SEL_SHIFT) | + ((gic->apply_lsc_gain & 1) << GIC_APPLY_LSC_GAIN_SHIFT); + regw_ip(base_addr, val, GIC_TYP); + + regw_ip(base_addr, gic->gain & GIC_GAIN_MASK, GIC_GAN); + + if (gic->gic_alg != VPFE_IPIPE_GIC_ALG_ADAPT_GAIN) { + /* Constant Gain. Set threshold to maximum */ + regw_ip(base_addr, GIC_THR_MASK, GIC_THR); + return; + } + + if (gic->thr_sel == VPFE_IPIPE_GIC_THR_REG) { + regw_ip(base_addr, gic->thr & GIC_THR_MASK, GIC_THR); + regw_ip(base_addr, gic->slope & GIC_SLOPE_MASK, GIC_SLP); + } else { + /* Use NF thresholds */ + val = IPIPE_U8Q5(gic->nf2_thr_gain.decimal, + gic->nf2_thr_gain.integer); + regw_ip(base_addr, val, GIC_NFGAN); + } +} + +#define IPIPE_U13Q9(decimal, integer) \ + (((decimal & 0x1ff) | ((integer & 0xf) << 9))) +/* White balance */ +void ipipe_set_wb_regs(void *__iomem base_addr, struct vpfe_ipipe_wb *wb) +{ + u32 val; + + ipipe_clock_enable(base_addr); + /* Ofsets. S12 */ + regw_ip(base_addr, wb->ofst_r & WB_OFFSET_MASK, WB2_OFT_R); + regw_ip(base_addr, wb->ofst_gr & WB_OFFSET_MASK, WB2_OFT_GR); + regw_ip(base_addr, wb->ofst_gb & WB_OFFSET_MASK, WB2_OFT_GB); + regw_ip(base_addr, wb->ofst_b & WB_OFFSET_MASK, WB2_OFT_B); + + /* Gains. U13Q9 */ + val = IPIPE_U13Q9(wb->gain_r.decimal, wb->gain_r.integer); + regw_ip(base_addr, val, WB2_WGN_R); + + val = IPIPE_U13Q9(wb->gain_gr.decimal, wb->gain_gr.integer); + regw_ip(base_addr, val, WB2_WGN_GR); + + val = IPIPE_U13Q9(wb->gain_gb.decimal, wb->gain_gb.integer); + regw_ip(base_addr, val, WB2_WGN_GB); + + val = IPIPE_U13Q9(wb->gain_b.decimal, wb->gain_b.integer); + regw_ip(base_addr, val, WB2_WGN_B); +} + +/* CFA */ +void ipipe_set_cfa_regs(void *__iomem base_addr, struct vpfe_ipipe_cfa *cfa) +{ + ipipe_clock_enable(base_addr); + + regw_ip(base_addr, cfa->alg, CFA_MODE); + regw_ip(base_addr, cfa->hpf_thr_2dir & CFA_HPF_THR_2DIR_MASK, + CFA_2DIR_HPF_THR); + regw_ip(base_addr, cfa->hpf_slp_2dir & CFA_HPF_SLOPE_2DIR_MASK, + CFA_2DIR_HPF_SLP); + regw_ip(base_addr, cfa->hp_mix_thr_2dir & CFA_HPF_MIX_THR_2DIR_MASK, + CFA_2DIR_MIX_THR); + regw_ip(base_addr, cfa->hp_mix_slope_2dir & CFA_HPF_MIX_SLP_2DIR_MASK, + CFA_2DIR_MIX_SLP); + regw_ip(base_addr, cfa->dir_thr_2dir & CFA_DIR_THR_2DIR_MASK, + CFA_2DIR_DIR_THR); + regw_ip(base_addr, cfa->dir_slope_2dir & CFA_DIR_SLP_2DIR_MASK, + CFA_2DIR_DIR_SLP); + regw_ip(base_addr, cfa->nd_wt_2dir & CFA_ND_WT_2DIR_MASK, + CFA_2DIR_NDWT); + regw_ip(base_addr, cfa->hue_fract_daa & CFA_DAA_HUE_FRA_MASK, + CFA_MONO_HUE_FRA); + regw_ip(base_addr, cfa->edge_thr_daa & CFA_DAA_EDG_THR_MASK, + CFA_MONO_EDG_THR); + regw_ip(base_addr, cfa->thr_min_daa & CFA_DAA_THR_MIN_MASK, + CFA_MONO_THR_MIN); + regw_ip(base_addr, cfa->thr_slope_daa & CFA_DAA_THR_SLP_MASK, + CFA_MONO_THR_SLP); + regw_ip(base_addr, cfa->slope_min_daa & CFA_DAA_SLP_MIN_MASK, + CFA_MONO_SLP_MIN); + regw_ip(base_addr, cfa->slope_slope_daa & CFA_DAA_SLP_SLP_MASK, + CFA_MONO_SLP_SLP); + regw_ip(base_addr, cfa->lp_wt_daa & CFA_DAA_LP_WT_MASK, + CFA_MONO_LPWT); +} + +void +ipipe_set_rgb2rgb_regs(void *__iomem base_addr, unsigned int id, + struct vpfe_ipipe_rgb2rgb *rgb) +{ + u32 offset_mask = RGB2RGB_1_OFST_MASK; + u32 offset = RGB1_MUL_BASE; + u32 integ_mask = 0xf; + u32 val; + + ipipe_clock_enable(base_addr); + + if (id == IPIPE_RGB2RGB_2) { + /* For second RGB module, gain integer is 3 bits instead + of 4, offset has 11 bits insread of 13 */ + offset = RGB2_MUL_BASE; + integ_mask = 0x7; + offset_mask = RGB2RGB_2_OFST_MASK; + } + /* Gains */ + val = (rgb->coef_rr.decimal & 0xff) | + ((rgb->coef_rr.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_RR); + val = (rgb->coef_gr.decimal & 0xff) | + ((rgb->coef_gr.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_GR); + val = (rgb->coef_br.decimal & 0xff) | + ((rgb->coef_br.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_BR); + val = (rgb->coef_rg.decimal & 0xff) | + ((rgb->coef_rg.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_RG); + val = (rgb->coef_gg.decimal & 0xff) | + ((rgb->coef_gg.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_GG); + val = (rgb->coef_bg.decimal & 0xff) | + ((rgb->coef_bg.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_BG); + val = (rgb->coef_rb.decimal & 0xff) | + ((rgb->coef_rb.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_RB); + val = (rgb->coef_gb.decimal & 0xff) | + ((rgb->coef_gb.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_GB); + val = (rgb->coef_bb.decimal & 0xff) | + ((rgb->coef_bb.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_BB); + + /* Offsets */ + regw_ip(base_addr, rgb->out_ofst_r & offset_mask, offset + RGB_OFT_OR); + regw_ip(base_addr, rgb->out_ofst_g & offset_mask, offset + RGB_OFT_OG); + regw_ip(base_addr, rgb->out_ofst_b & offset_mask, offset + RGB_OFT_OB); +} + +static void +ipipe_update_gamma_tbl(void *__iomem isp5_base_addr, + struct vpfe_ipipe_gamma_entry *table, int size, u32 addr) +{ + int count; + u32 val; + + for (count = 0; count < size; count++) { + val = table[count].slope & GAMMA_MASK; + val |= (table[count].offset & GAMMA_MASK) << GAMMA_SHIFT; + w_ip_table(isp5_base_addr, val, (addr + (count * 4))); + } +} + +void +ipipe_set_gamma_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, + struct vpfe_ipipe_gamma *gamma) +{ + int table_size; + u32 val; + + ipipe_clock_enable(base_addr); + val = (gamma->bypass_r << GAMMA_BYPR_SHIFT) | + (gamma->bypass_b << GAMMA_BYPG_SHIFT) | + (gamma->bypass_g << GAMMA_BYPB_SHIFT) | + (gamma->tbl_sel << GAMMA_TBL_SEL_SHIFT) | + (gamma->tbl_size << GAMMA_TBL_SIZE_SHIFT); + + regw_ip(base_addr, val, GMM_CFG); + + if (gamma->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM) + return; + + table_size = gamma->tbl_size; + + if (!gamma->bypass_r && gamma->table_r != NULL) + ipipe_update_gamma_tbl(isp5_base_addr, gamma->table_r, + table_size, GAMMA_R_START_ADDR); + if (!gamma->bypass_b && gamma->table_b != NULL) + ipipe_update_gamma_tbl(isp5_base_addr, gamma->table_b, + table_size, GAMMA_B_START_ADDR); + if (!gamma->bypass_g && gamma->table_g != NULL) + ipipe_update_gamma_tbl(isp5_base_addr, gamma->table_g, + table_size, GAMMA_G_START_ADDR); +} + +void +ipipe_set_3d_lut_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, + struct vpfe_ipipe_3d_lut *lut_3d) +{ + struct vpfe_ipipe_3d_lut_entry *tbl; + u32 bnk_index; + u32 tbl_index; + u32 val; + u32 i; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, lut_3d->en, D3LUT_EN); + + if (!lut_3d->en) + return; + + /* lut_3d enabled */ + if (!lut_3d->table) + return; + + /* valied table */ + tbl = lut_3d->table; + for (i = 0 ; i < VPFE_IPIPE_MAX_SIZE_3D_LUT; i++) { + /* Each entry has 0-9 (B), 10-19 (G) and + 20-29 R values */ + val = tbl[i].b & D3_LUT_ENTRY_MASK; + val |= (tbl[i].g & D3_LUT_ENTRY_MASK) << + D3_LUT_ENTRY_G_SHIFT; + val |= (tbl[i].r & D3_LUT_ENTRY_MASK) << + D3_LUT_ENTRY_R_SHIFT; + bnk_index = i % 4; + tbl_index = i >> 2; + tbl_index <<= 2; + if (bnk_index == 0) + w_ip_table(isp5_base_addr, val, + tbl_index + D3L_TB0_START_ADDR); + else if (bnk_index == 1) + w_ip_table(isp5_base_addr, val, + tbl_index + D3L_TB1_START_ADDR); + else if (bnk_index == 2) + w_ip_table(isp5_base_addr, val, + tbl_index + D3L_TB2_START_ADDR); + else + w_ip_table(isp5_base_addr, val, + tbl_index + D3L_TB3_START_ADDR); + } +} + +/* Lumina adjustments */ +void +ipipe_set_lum_adj_regs(void *__iomem base_addr, struct ipipe_lum_adj *lum_adj) +{ + u32 val; + + ipipe_clock_enable(base_addr); + + /* combine fields of YUV_ADJ to set brightness and contrast */ + val = lum_adj->contrast << LUM_ADJ_CONTR_SHIFT | + lum_adj->brightness << LUM_ADJ_BRIGHT_SHIFT; + regw_ip(base_addr, val, YUV_ADJ); +} + +#define IPIPE_S12Q8(decimal, integer) \ + (((decimal & 0xff) | ((integer & 0xf) << 8))) + +void ipipe_set_rgb2ycbcr_regs(void *__iomem base_addr, + struct vpfe_ipipe_rgb2yuv *yuv) +{ + u32 val; + + /* S10Q8 */ + ipipe_clock_enable(base_addr); + val = IPIPE_S12Q8(yuv->coef_ry.decimal, yuv->coef_ry.integer); + regw_ip(base_addr, val, YUV_MUL_RY); + val = IPIPE_S12Q8(yuv->coef_gy.decimal, yuv->coef_gy.integer); + regw_ip(base_addr, val, YUV_MUL_GY); + val = IPIPE_S12Q8(yuv->coef_by.decimal, yuv->coef_by.integer); + regw_ip(base_addr, val, YUV_MUL_BY); + val = IPIPE_S12Q8(yuv->coef_rcb.decimal, yuv->coef_rcb.integer); + regw_ip(base_addr, val, YUV_MUL_RCB); + val = IPIPE_S12Q8(yuv->coef_gcb.decimal, yuv->coef_gcb.integer); + regw_ip(base_addr, val, YUV_MUL_GCB); + val = IPIPE_S12Q8(yuv->coef_bcb.decimal, yuv->coef_bcb.integer); + regw_ip(base_addr, val, YUV_MUL_BCB); + val = IPIPE_S12Q8(yuv->coef_rcr.decimal, yuv->coef_rcr.integer); + regw_ip(base_addr, val, YUV_MUL_RCR); + val = IPIPE_S12Q8(yuv->coef_gcr.decimal, yuv->coef_gcr.integer); + regw_ip(base_addr, val, YUV_MUL_GCR); + val = IPIPE_S12Q8(yuv->coef_bcr.decimal, yuv->coef_bcr.integer); + regw_ip(base_addr, val, YUV_MUL_BCR); + regw_ip(base_addr, yuv->out_ofst_y & RGB2YCBCR_OFST_MASK, YUV_OFT_Y); + regw_ip(base_addr, yuv->out_ofst_cb & RGB2YCBCR_OFST_MASK, YUV_OFT_CB); + regw_ip(base_addr, yuv->out_ofst_cr & RGB2YCBCR_OFST_MASK, YUV_OFT_CR); +} + +/* YUV 422 conversion */ +void +ipipe_set_yuv422_conv_regs(void *__iomem base_addr, + struct vpfe_ipipe_yuv422_conv *conv) +{ + u32 val; + + ipipe_clock_enable(base_addr); + + /* Combine all the fields to make YUV_PHS register of IPIPE */ + val = (conv->chrom_pos << 0) | (conv->en_chrom_lpf << 1); + regw_ip(base_addr, val, YUV_PHS); +} + +void +ipipe_set_gbce_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, + struct vpfe_ipipe_gbce *gbce) +{ + unsigned int count; + u32 mask = GBCE_Y_VAL_MASK; + + if (gbce->type == VPFE_IPIPE_GBCE_GAIN_TBL) + mask = GBCE_GAIN_VAL_MASK; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, gbce->en & 1, GBCE_EN); + + if (!gbce->en) + return; + + regw_ip(base_addr, gbce->type, GBCE_TYP); + + if (!gbce->table) + return; + + for (count = 0; count < VPFE_IPIPE_MAX_SIZE_GBCE_LUT ; count += 2) + w_ip_table(isp5_base_addr, ((gbce->table[count + 1] & mask) << + GBCE_ENTRY_SHIFT) | (gbce->table[count] & mask), + ((count/2) << 2) + GBCE_TB_START_ADDR); +} + +void +ipipe_set_ee_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, + struct vpfe_ipipe_yee *ee) +{ + unsigned int count; + u32 val; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, ee->en, YEE_EN); + + if (!ee->en) + return; + + val = ee->en_halo_red & 1; + val |= ee->merge_meth << YEE_HALO_RED_EN_SHIFT; + regw_ip(base_addr, val, YEE_TYP); + + regw_ip(base_addr, ee->hpf_shft, YEE_SHF); + regw_ip(base_addr, ee->hpf_coef_00 & YEE_COEF_MASK, YEE_MUL_00); + regw_ip(base_addr, ee->hpf_coef_01 & YEE_COEF_MASK, YEE_MUL_01); + regw_ip(base_addr, ee->hpf_coef_02 & YEE_COEF_MASK, YEE_MUL_02); + regw_ip(base_addr, ee->hpf_coef_10 & YEE_COEF_MASK, YEE_MUL_10); + regw_ip(base_addr, ee->hpf_coef_11 & YEE_COEF_MASK, YEE_MUL_11); + regw_ip(base_addr, ee->hpf_coef_12 & YEE_COEF_MASK, YEE_MUL_12); + regw_ip(base_addr, ee->hpf_coef_20 & YEE_COEF_MASK, YEE_MUL_20); + regw_ip(base_addr, ee->hpf_coef_21 & YEE_COEF_MASK, YEE_MUL_21); + regw_ip(base_addr, ee->hpf_coef_22 & YEE_COEF_MASK, YEE_MUL_22); + regw_ip(base_addr, ee->yee_thr & YEE_THR_MASK, YEE_THR); + regw_ip(base_addr, ee->es_gain & YEE_ES_GAIN_MASK, YEE_E_GAN); + regw_ip(base_addr, ee->es_thr1 & YEE_ES_THR1_MASK, YEE_E_THR1); + regw_ip(base_addr, ee->es_thr2 & YEE_THR_MASK, YEE_E_THR2); + regw_ip(base_addr, ee->es_gain_grad & YEE_THR_MASK, YEE_G_GAN); + regw_ip(base_addr, ee->es_ofst_grad & YEE_THR_MASK, YEE_G_OFT); + + if (ee->table == NULL) + return; + + for (count = 0; count < VPFE_IPIPE_MAX_SIZE_YEE_LUT; count += 2) + w_ip_table(isp5_base_addr, ((ee->table[count + 1] & + YEE_ENTRY_MASK) << YEE_ENTRY_SHIFT) | + (ee->table[count] & YEE_ENTRY_MASK), + ((count/2) << 2) + YEE_TB_START_ADDR); +} + +/* Chromatic Artifact Correction. CAR */ +static void ipipe_set_mf(void *__iomem base_addr) +{ + /* typ to dynamic switch */ + regw_ip(base_addr, VPFE_IPIPE_CAR_DYN_SWITCH, CAR_TYP); + /* Set SW0 to maximum */ + regw_ip(base_addr, CAR_MF_THR, CAR_SW); +} + +static void +ipipe_set_gain_ctrl(void *__iomem base_addr, struct vpfe_ipipe_car *car) +{ + regw_ip(base_addr, VPFE_IPIPE_CAR_CHR_GAIN_CTRL, CAR_TYP); + regw_ip(base_addr, car->hpf, CAR_HPF_TYP); + regw_ip(base_addr, car->hpf_shft & CAR_HPF_SHIFT_MASK, CAR_HPF_SHF); + regw_ip(base_addr, car->hpf_thr, CAR_HPF_THR); + regw_ip(base_addr, car->gain1.gain, CAR_GN1_GAN); + regw_ip(base_addr, car->gain1.shft & CAR_GAIN1_SHFT_MASK, CAR_GN1_SHF); + regw_ip(base_addr, car->gain1.gain_min & CAR_GAIN_MIN_MASK, + CAR_GN1_MIN); + regw_ip(base_addr, car->gain2.gain, CAR_GN2_GAN); + regw_ip(base_addr, car->gain2.shft & CAR_GAIN2_SHFT_MASK, CAR_GN2_SHF); + regw_ip(base_addr, car->gain2.gain_min & CAR_GAIN_MIN_MASK, + CAR_GN2_MIN); +} + +void ipipe_set_car_regs(void *__iomem base_addr, struct vpfe_ipipe_car *car) +{ + u32 val; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, car->en, CAR_EN); + + if (!car->en) + return; + + switch (car->meth) { + case VPFE_IPIPE_CAR_MED_FLTR: + ipipe_set_mf(base_addr); + break; + + case VPFE_IPIPE_CAR_CHR_GAIN_CTRL: + ipipe_set_gain_ctrl(base_addr, car); + break; + + default: + /* Dynamic switch between MF and Gain Ctrl. */ + ipipe_set_mf(base_addr); + ipipe_set_gain_ctrl(base_addr, car); + /* Set the threshold for switching between + * the two Here we overwrite the MF SW0 value + */ + regw_ip(base_addr, VPFE_IPIPE_CAR_DYN_SWITCH, CAR_TYP); + val = car->sw1; + val <<= CAR_SW1_SHIFT; + val |= car->sw0; + regw_ip(base_addr, val, CAR_SW); + } +} + +/* Chromatic Gain Suppression */ +void ipipe_set_cgs_regs(void *__iomem base_addr, struct vpfe_ipipe_cgs *cgs) +{ + ipipe_clock_enable(base_addr); + regw_ip(base_addr, cgs->en, CGS_EN); + + if (!cgs->en) + return; + + /* Set the bright side parameters */ + regw_ip(base_addr, cgs->h_thr, CGS_GN1_H_THR); + regw_ip(base_addr, cgs->h_slope, CGS_GN1_H_GAN); + regw_ip(base_addr, cgs->h_shft & CAR_SHIFT_MASK, CGS_GN1_H_SHF); + regw_ip(base_addr, cgs->h_min, CGS_GN1_H_MIN); +} + +void rsz_src_enable(void *__iomem rsz_base, int enable) +{ + regw_rsz(rsz_base, enable, RSZ_SRC_EN); +} + +int rsz_enable(void *__iomem rsz_base, int rsz_id, int enable) +{ + if (rsz_id == RSZ_A) { + regw_rsz(rsz_base, enable, RSZ_EN_A); + /* We always enable RSZ_A. RSZ_B is enable upon request from + * application. So enable RSZ_SRC_EN along with RSZ_A + */ + regw_rsz(rsz_base, enable, RSZ_SRC_EN); + } else if (rsz_id == RSZ_B) { + regw_rsz(rsz_base, enable, RSZ_EN_B); + } else { + BUG(); + } + + return 0; +} diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h new file mode 100644 index 000000000000..010fdb247faf --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h @@ -0,0 +1,559 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _DAVINCI_VPFE_DM365_IPIPE_HW_H +#define _DAVINCI_VPFE_DM365_IPIPE_HW_H + +#include "vpfe_mc_capture.h" + +#define SET_LOW_ADDR 0x0000ffff +#define SET_HIGH_ADDR 0xffff0000 + +/* Below are the internal tables */ +#define DPC_TB0_START_ADDR 0x8000 +#define DPC_TB1_START_ADDR 0x8400 + +#define GAMMA_R_START_ADDR 0xa800 +#define GAMMA_G_START_ADDR 0xb000 +#define GAMMA_B_START_ADDR 0xb800 + +/* RAM table addresses for edge enhancement correction*/ +#define YEE_TB_START_ADDR 0x8800 + +/* RAM table address for GBC LUT */ +#define GBCE_TB_START_ADDR 0x9000 + +/* RAM table for 3D NF LUT */ +#define D3L_TB0_START_ADDR 0x9800 +#define D3L_TB1_START_ADDR 0x9c00 +#define D3L_TB2_START_ADDR 0xa000 +#define D3L_TB3_START_ADDR 0xa400 + +/* IPIPE Register Offsets from the base address */ +#define IPIPE_SRC_EN 0x0000 +#define IPIPE_SRC_MODE 0x0004 +#define IPIPE_SRC_FMT 0x0008 +#define IPIPE_SRC_COL 0x000c +#define IPIPE_SRC_VPS 0x0010 +#define IPIPE_SRC_VSZ 0x0014 +#define IPIPE_SRC_HPS 0x0018 +#define IPIPE_SRC_HSZ 0x001c + +#define IPIPE_SEL_SBU 0x0020 + +#define IPIPE_DMA_STA 0x0024 +#define IPIPE_GCK_MMR 0x0028 +#define IPIPE_GCK_PIX 0x002c +#define IPIPE_RESERVED0 0x0030 + +/* Defect Correction */ +#define DPC_LUT_EN 0x0034 +#define DPC_LUT_SEL 0x0038 +#define DPC_LUT_ADR 0x003c +#define DPC_LUT_SIZ 0x0040 +#define DPC_OTF_EN 0x0044 +#define DPC_OTF_TYP 0x0048 +#define DPC_OTF_2D_THR_R 0x004c +#define DPC_OTF_2D_THR_GR 0x0050 +#define DPC_OTF_2D_THR_GB 0x0054 +#define DPC_OTF_2D_THR_B 0x0058 +#define DPC_OTF_2C_THR_R 0x005c +#define DPC_OTF_2C_THR_GR 0x0060 +#define DPC_OTF_2C_THR_GB 0x0064 +#define DPC_OTF_2C_THR_B 0x0068 +#define DPC_OTF_3_SHF 0x006c +#define DPC_OTF_3D_THR 0x0070 +#define DPC_OTF_3D_SLP 0x0074 +#define DPC_OTF_3D_MIN 0x0078 +#define DPC_OTF_3D_MAX 0x007c +#define DPC_OTF_3C_THR 0x0080 +#define DPC_OTF_3C_SLP 0x0084 +#define DPC_OTF_3C_MIN 0x0088 +#define DPC_OTF_3C_MAX 0x008c + +/* Lense Shading Correction */ +#define LSC_VOFT 0x90 +#define LSC_VA2 0x94 +#define LSC_VA1 0x98 +#define LSC_VS 0x9c +#define LSC_HOFT 0xa0 +#define LSC_HA2 0xa4 +#define LSC_HA1 0xa8 +#define LSC_HS 0xac +#define LSC_GAIN_R 0xb0 +#define LSC_GAIN_GR 0xb4 +#define LSC_GAIN_GB 0xb8 +#define LSC_GAIN_B 0xbc +#define LSC_OFT_R 0xc0 +#define LSC_OFT_GR 0xc4 +#define LSC_OFT_GB 0xc8 +#define LSC_OFT_B 0xcc +#define LSC_SHF 0xd0 +#define LSC_MAX 0xd4 + +/* Noise Filter 1. Ofsets from start address given */ +#define D2F_1ST 0xd8 +#define D2F_EN 0x0 +#define D2F_TYP 0x4 +#define D2F_THR 0x8 +#define D2F_STR 0x28 +#define D2F_SPR 0x48 +#define D2F_EDG_MIN 0x68 +#define D2F_EDG_MAX 0x6c + +/* Noise Filter 2 */ +#define D2F_2ND 0x148 + +/* GIC */ +#define GIC_EN 0x1b8 +#define GIC_TYP 0x1bc +#define GIC_GAN 0x1c0 +#define GIC_NFGAN 0x1c4 +#define GIC_THR 0x1c8 +#define GIC_SLP 0x1cc + +/* White Balance */ +#define WB2_OFT_R 0x1d0 +#define WB2_OFT_GR 0x1d4 +#define WB2_OFT_GB 0x1d8 +#define WB2_OFT_B 0x1dc +#define WB2_WGN_R 0x1e0 +#define WB2_WGN_GR 0x1e4 +#define WB2_WGN_GB 0x1e8 +#define WB2_WGN_B 0x1ec + +/* CFA interpolation */ +#define CFA_MODE 0x1f0 +#define CFA_2DIR_HPF_THR 0x1f4 +#define CFA_2DIR_HPF_SLP 0x1f8 +#define CFA_2DIR_MIX_THR 0x1fc +#define CFA_2DIR_MIX_SLP 0x200 +#define CFA_2DIR_DIR_THR 0x204 +#define CFA_2DIR_DIR_SLP 0x208 +#define CFA_2DIR_NDWT 0x20c +#define CFA_MONO_HUE_FRA 0x210 +#define CFA_MONO_EDG_THR 0x214 +#define CFA_MONO_THR_MIN 0x218 +#define CFA_MONO_THR_SLP 0x21c +#define CFA_MONO_SLP_MIN 0x220 +#define CFA_MONO_SLP_SLP 0x224 +#define CFA_MONO_LPWT 0x228 + +/* RGB to RGB conversiona - 1st */ +#define RGB1_MUL_BASE 0x22c +/* Offsets from base */ +#define RGB_MUL_RR 0x0 +#define RGB_MUL_GR 0x4 +#define RGB_MUL_BR 0x8 +#define RGB_MUL_RG 0xc +#define RGB_MUL_GG 0x10 +#define RGB_MUL_BG 0x14 +#define RGB_MUL_RB 0x18 +#define RGB_MUL_GB 0x1c +#define RGB_MUL_BB 0x20 +#define RGB_OFT_OR 0x24 +#define RGB_OFT_OG 0x28 +#define RGB_OFT_OB 0x2c + +/* Gamma */ +#define GMM_CFG 0x25c + +/* RGB to RGB conversiona - 2nd */ +#define RGB2_MUL_BASE 0x260 + +/* 3D LUT */ +#define D3LUT_EN 0x290 + +/* RGB to YUV(YCbCr) conversion */ +#define YUV_ADJ 0x294 +#define YUV_MUL_RY 0x298 +#define YUV_MUL_GY 0x29c +#define YUV_MUL_BY 0x2a0 +#define YUV_MUL_RCB 0x2a4 +#define YUV_MUL_GCB 0x2a8 +#define YUV_MUL_BCB 0x2ac +#define YUV_MUL_RCR 0x2b0 +#define YUV_MUL_GCR 0x2b4 +#define YUV_MUL_BCR 0x2b8 +#define YUV_OFT_Y 0x2bc +#define YUV_OFT_CB 0x2c0 +#define YUV_OFT_CR 0x2c4 +#define YUV_PHS 0x2c8 + +/* Global Brightness and Contrast */ +#define GBCE_EN 0x2cc +#define GBCE_TYP 0x2d0 + +/* Edge Enhancer */ +#define YEE_EN 0x2d4 +#define YEE_TYP 0x2d8 +#define YEE_SHF 0x2dc +#define YEE_MUL_00 0x2e0 +#define YEE_MUL_01 0x2e4 +#define YEE_MUL_02 0x2e8 +#define YEE_MUL_10 0x2ec +#define YEE_MUL_11 0x2f0 +#define YEE_MUL_12 0x2f4 +#define YEE_MUL_20 0x2f8 +#define YEE_MUL_21 0x2fc +#define YEE_MUL_22 0x300 +#define YEE_THR 0x304 +#define YEE_E_GAN 0x308 +#define YEE_E_THR1 0x30c +#define YEE_E_THR2 0x310 +#define YEE_G_GAN 0x314 +#define YEE_G_OFT 0x318 + +/* Chroma Artifact Reduction */ +#define CAR_EN 0x31c +#define CAR_TYP 0x320 +#define CAR_SW 0x324 +#define CAR_HPF_TYP 0x328 +#define CAR_HPF_SHF 0x32c +#define CAR_HPF_THR 0x330 +#define CAR_GN1_GAN 0x334 +#define CAR_GN1_SHF 0x338 +#define CAR_GN1_MIN 0x33c +#define CAR_GN2_GAN 0x340 +#define CAR_GN2_SHF 0x344 +#define CAR_GN2_MIN 0x348 + +/* Chroma Gain Suppression */ +#define CGS_EN 0x34c +#define CGS_GN1_L_THR 0x350 +#define CGS_GN1_L_GAN 0x354 +#define CGS_GN1_L_SHF 0x358 +#define CGS_GN1_L_MIN 0x35c +#define CGS_GN1_H_THR 0x360 +#define CGS_GN1_H_GAN 0x364 +#define CGS_GN1_H_SHF 0x368 +#define CGS_GN1_H_MIN 0x36c +#define CGS_GN2_L_THR 0x370 +#define CGS_GN2_L_GAN 0x374 +#define CGS_GN2_L_SHF 0x378 +#define CGS_GN2_L_MIN 0x37c + +/* Resizer */ +#define RSZ_SRC_EN 0x0 +#define RSZ_SRC_MODE 0x4 +#define RSZ_SRC_FMT0 0x8 +#define RSZ_SRC_FMT1 0xc +#define RSZ_SRC_VPS 0x10 +#define RSZ_SRC_VSZ 0x14 +#define RSZ_SRC_HPS 0x18 +#define RSZ_SRC_HSZ 0x1c +#define RSZ_DMA_RZA 0x20 +#define RSZ_DMA_RZB 0x24 +#define RSZ_DMA_STA 0x28 +#define RSZ_GCK_MMR 0x2c +#define RSZ_RESERVED0 0x30 +#define RSZ_GCK_SDR 0x34 +#define RSZ_IRQ_RZA 0x38 +#define RSZ_IRQ_RZB 0x3c +#define RSZ_YUV_Y_MIN 0x40 +#define RSZ_YUV_Y_MAX 0x44 +#define RSZ_YUV_C_MIN 0x48 +#define RSZ_YUV_C_MAX 0x4c +#define RSZ_YUV_PHS 0x50 +#define RSZ_SEQ 0x54 + +/* Resizer Rescale Parameters */ +#define RSZ_EN_A 0x58 +#define RSZ_EN_B 0xe8 +/* offset of the registers to be added with base register of + either RSZ0 or RSZ1 +*/ +#define RSZ_MODE 0x4 +#define RSZ_420 0x8 +#define RSZ_I_VPS 0xc +#define RSZ_I_HPS 0x10 +#define RSZ_O_VSZ 0x14 +#define RSZ_O_HSZ 0x18 +#define RSZ_V_PHS_Y 0x1c +#define RSZ_V_PHS_C 0x20 +#define RSZ_V_DIF 0x24 +#define RSZ_V_TYP 0x28 +#define RSZ_V_LPF 0x2c +#define RSZ_H_PHS 0x30 +#define RSZ_H_PHS_ADJ 0x34 +#define RSZ_H_DIF 0x38 +#define RSZ_H_TYP 0x3c +#define RSZ_H_LPF 0x40 +#define RSZ_DWN_EN 0x44 +#define RSZ_DWN_AV 0x48 + +/* Resizer RGB Conversion Parameters */ +#define RSZ_RGB_EN 0x4c +#define RSZ_RGB_TYP 0x50 +#define RSZ_RGB_BLD 0x54 + +/* Resizer External Memory Parameters */ +#define RSZ_SDR_Y_BAD_H 0x58 +#define RSZ_SDR_Y_BAD_L 0x5c +#define RSZ_SDR_Y_SAD_H 0x60 +#define RSZ_SDR_Y_SAD_L 0x64 +#define RSZ_SDR_Y_OFT 0x68 +#define RSZ_SDR_Y_PTR_S 0x6c +#define RSZ_SDR_Y_PTR_E 0x70 +#define RSZ_SDR_C_BAD_H 0x74 +#define RSZ_SDR_C_BAD_L 0x78 +#define RSZ_SDR_C_SAD_H 0x7c +#define RSZ_SDR_C_SAD_L 0x80 +#define RSZ_SDR_C_OFT 0x84 +#define RSZ_SDR_C_PTR_S 0x88 +#define RSZ_SDR_C_PTR_E 0x8c + +/* Macro for resizer */ +#define RSZ_YUV_Y_MIN 0x40 +#define RSZ_YUV_Y_MAX 0x44 +#define RSZ_YUV_C_MIN 0x48 +#define RSZ_YUV_C_MAX 0x4c + +#define IPIPE_GCK_MMR_DEFAULT 1 +#define IPIPE_GCK_PIX_DEFAULT 0xe +#define RSZ_GCK_MMR_DEFAULT 1 +#define RSZ_GCK_SDR_DEFAULT 1 + +/* LUTDPC */ +#define LUTDPC_TBL_256_EN 0 +#define LUTDPC_INF_TBL_EN 1 +#define LUT_DPC_START_ADDR 0 +#define LUT_DPC_H_POS_MASK 0x1fff +#define LUT_DPC_V_POS_MASK 0x1fff +#define LUT_DPC_V_POS_SHIFT 13 +#define LUT_DPC_CORR_METH_SHIFT 26 +#define LUT_DPC_MAX_SIZE 256 +#define LUT_DPC_SIZE_MASK 0x3ff + +/* OTFDPC */ +#define OTFDPC_DPC2_THR_MASK 0xfff +#define OTF_DET_METHOD_SHIFT 1 +#define OTF_DPC3_0_SHF_MASK 3 +#define OTF_DPC3_0_THR_SHIFT 6 +#define OTF_DPC3_0_THR_MASK 0x3f +#define OTF_DPC3_0_SLP_MASK 0x3f +#define OTF_DPC3_0_DET_MASK 0xfff +#define OTF_DPC3_0_CORR_MASK 0xfff + +/* NF (D2F) */ +#define D2F_SPR_VAL_MASK 0x1f +#define D2F_SPR_VAL_SHIFT 0 +#define D2F_SHFT_VAL_MASK 3 +#define D2F_SHFT_VAL_SHIFT 5 +#define D2F_SAMPLE_METH_SHIFT 7 +#define D2F_APPLY_LSC_GAIN_SHIFT 8 +#define D2F_USE_SPR_REG_VAL 0 +#define D2F_STR_VAL_MASK 0x1f +#define D2F_THR_VAL_MASK 0x3ff +#define D2F_EDGE_DET_THR_MASK 0x7ff + +/* Green Imbalance Correction */ +#define GIC_TYP_SHIFT 0 +#define GIC_THR_SEL_SHIFT 1 +#define GIC_APPLY_LSC_GAIN_SHIFT 2 +#define GIC_GAIN_MASK 0xff +#define GIC_THR_MASK 0xfff +#define GIC_SLOPE_MASK 0xfff +#define GIC_NFGAN_INT_MASK 7 +#define GIC_NFGAN_DECI_MASK 0x1f + +/* WB */ +#define WB_OFFSET_MASK 0xfff +#define WB_GAIN_INT_MASK 0xf +#define WB_GAIN_DECI_MASK 0x1ff + +/* CFA */ +#define CFA_HPF_THR_2DIR_MASK 0x1fff +#define CFA_HPF_SLOPE_2DIR_MASK 0x3ff +#define CFA_HPF_MIX_THR_2DIR_MASK 0x1fff +#define CFA_HPF_MIX_SLP_2DIR_MASK 0x3ff +#define CFA_DIR_THR_2DIR_MASK 0x3ff +#define CFA_DIR_SLP_2DIR_MASK 0x7f +#define CFA_ND_WT_2DIR_MASK 0x3f +#define CFA_DAA_HUE_FRA_MASK 0x3f +#define CFA_DAA_EDG_THR_MASK 0xff +#define CFA_DAA_THR_MIN_MASK 0x3ff +#define CFA_DAA_THR_SLP_MASK 0x3ff +#define CFA_DAA_SLP_MIN_MASK 0x3ff +#define CFA_DAA_SLP_SLP_MASK 0x3ff +#define CFA_DAA_LP_WT_MASK 0x3f + +/* RGB2RGB */ +#define RGB2RGB_1_OFST_MASK 0x1fff +#define RGB2RGB_1_GAIN_INT_MASK 0xf +#define RGB2RGB_GAIN_DECI_MASK 0xff +#define RGB2RGB_2_OFST_MASK 0x7ff +#define RGB2RGB_2_GAIN_INT_MASK 0x7 + +/* Gamma */ +#define GAMMA_BYPR_SHIFT 0 +#define GAMMA_BYPG_SHIFT 1 +#define GAMMA_BYPB_SHIFT 2 +#define GAMMA_TBL_SEL_SHIFT 4 +#define GAMMA_TBL_SIZE_SHIFT 5 +#define GAMMA_MASK 0x3ff +#define GAMMA_SHIFT 10 + +/* 3D LUT */ +#define D3_LUT_ENTRY_MASK 0x3ff +#define D3_LUT_ENTRY_R_SHIFT 20 +#define D3_LUT_ENTRY_G_SHIFT 10 +#define D3_LUT_ENTRY_B_SHIFT 0 + +/* Lumina adj */ +#define LUM_ADJ_CONTR_SHIFT 0 +#define LUM_ADJ_BRIGHT_SHIFT 8 + +/* RGB2YCbCr */ +#define RGB2YCBCR_OFST_MASK 0x7ff +#define RGB2YCBCR_COEF_INT_MASK 0xf +#define RGB2YCBCR_COEF_DECI_MASK 0xff + +/* GBCE */ +#define GBCE_Y_VAL_MASK 0xff +#define GBCE_GAIN_VAL_MASK 0x3ff +#define GBCE_ENTRY_SHIFT 10 + +/* Edge Enhancements */ +#define YEE_HALO_RED_EN_SHIFT 1 +#define YEE_HPF_SHIFT_MASK 0xf +#define YEE_COEF_MASK 0x3ff +#define YEE_THR_MASK 0x3f +#define YEE_ES_GAIN_MASK 0xfff +#define YEE_ES_THR1_MASK 0xfff +#define YEE_ENTRY_SHIFT 9 +#define YEE_ENTRY_MASK 0x1ff + +/* CAR */ +#define CAR_MF_THR 0xff +#define CAR_SW1_SHIFT 8 +#define CAR_GAIN1_SHFT_MASK 7 +#define CAR_GAIN_MIN_MASK 0x1ff +#define CAR_GAIN2_SHFT_MASK 0xf +#define CAR_HPF_SHIFT_MASK 3 + +/* CGS */ +#define CAR_SHIFT_MASK 3 + +/* Resizer */ +#define RSZ_BYPASS_SHIFT 1 +#define RSZ_SRC_IMG_FMT_SHIFT 1 +#define RSZ_SRC_Y_C_SEL_SHIFT 2 +#define IPIPE_RSZ_VPS_MASK 0xffff +#define IPIPE_RSZ_HPS_MASK 0xffff +#define IPIPE_RSZ_VSZ_MASK 0x1fff +#define IPIPE_RSZ_HSZ_MASK 0x1fff +#define RSZ_HPS_MASK 0x1fff +#define RSZ_VPS_MASK 0x1fff +#define RSZ_O_HSZ_MASK 0x1fff +#define RSZ_O_VSZ_MASK 0x1fff +#define RSZ_V_PHS_MASK 0x3fff +#define RSZ_V_DIF_MASK 0x3fff + +#define RSZA_H_FLIP_SHIFT 0 +#define RSZA_V_FLIP_SHIFT 1 +#define RSZB_H_FLIP_SHIFT 2 +#define RSZB_V_FLIP_SHIFT 3 +#define RSZ_A 0 +#define RSZ_B 1 +#define RSZ_CEN_SHIFT 1 +#define RSZ_YEN_SHIFT 0 +#define RSZ_TYP_Y_SHIFT 0 +#define RSZ_TYP_C_SHIFT 1 +#define RSZ_LPF_INT_MASK 0x3f +#define RSZ_LPF_INT_MASK 0x3f +#define RSZ_LPF_INT_C_SHIFT 6 +#define RSZ_H_PHS_MASK 0x3fff +#define RSZ_H_DIF_MASK 0x3fff +#define RSZ_DIFF_DOWN_THR 256 +#define RSZ_DWN_SCALE_AV_SZ_V_SHIFT 3 +#define RSZ_DWN_SCALE_AV_SZ_MASK 7 +#define RSZ_RGB_MSK1_SHIFT 2 +#define RSZ_RGB_MSK0_SHIFT 1 +#define RSZ_RGB_TYP_SHIFT 0 +#define RSZ_RGB_ALPHA_MASK 0xff + +static inline u32 regr_ip(void *__iomem addr, u32 offset) +{ + return readl(addr + offset); +} + +static inline void regw_ip(void *__iomem addr, u32 val, u32 offset) +{ + writel(val, addr + offset); +} + +static inline u32 w_ip_table(void *__iomem addr, u32 val, u32 offset) +{ + writel(val, addr + offset); + + return val; +} + +static inline u32 regr_rsz(void *__iomem addr, u32 offset) +{ + return readl(addr + offset); +} + +static inline u32 regw_rsz(void *__iomem addr, u32 val, u32 offset) +{ + writel(val, addr + offset); + + return val; +} + +int config_ipipe_hw(struct vpfe_ipipe_device *ipipe); +int resizer_set_outaddr(void *__iomem rsz_base, struct resizer_params *params, + int resize_no, unsigned int address); +int rsz_enable(void *__iomem rsz_base, int rsz_id, int enable); +void rsz_src_enable(void *__iomem rsz_base, int enable); +void rsz_set_in_pix_format(unsigned char y_c); +int config_rsz_hw(struct vpfe_resizer_device *resizer, + struct resizer_params *config); +void ipipe_set_d2f_regs(void *__iomem base_addr, unsigned int id, + struct vpfe_ipipe_nf *noise_filter); +void ipipe_set_rgb2rgb_regs(void *__iomem base_addr, unsigned int id, + struct vpfe_ipipe_rgb2rgb *rgb); +void ipipe_set_yuv422_conv_regs(void *__iomem base_addr, + struct vpfe_ipipe_yuv422_conv *conv); +void ipipe_set_lum_adj_regs(void *__iomem base_addr, + struct ipipe_lum_adj *lum_adj); +void ipipe_set_rgb2ycbcr_regs(void *__iomem base_addr, + struct vpfe_ipipe_rgb2yuv *yuv); +void ipipe_set_lutdpc_regs(void *__iomem base_addr, + void *__iomem isp5_base_addr, struct vpfe_ipipe_lutdpc *lutdpc); +void ipipe_set_otfdpc_regs(void *__iomem base_addr, + struct vpfe_ipipe_otfdpc *otfdpc); +void ipipe_set_3d_lut_regs(void *__iomem base_addr, + void *__iomem isp5_base_addr, struct vpfe_ipipe_3d_lut *lut_3d); +void ipipe_set_gamma_regs(void *__iomem base_addr, + void *__iomem isp5_base_addr, struct vpfe_ipipe_gamma *gamma); +void ipipe_set_ee_regs(void *__iomem base_addr, + void *__iomem isp5_base_addr, struct vpfe_ipipe_yee *ee); +void ipipe_set_gbce_regs(void *__iomem base_addr, + void *__iomem isp5_base_addr, struct vpfe_ipipe_gbce *gbce); +void ipipe_set_gic_regs(void *__iomem base_addr, struct vpfe_ipipe_gic *gic); +void ipipe_set_cfa_regs(void *__iomem base_addr, struct vpfe_ipipe_cfa *cfa); +void ipipe_set_car_regs(void *__iomem base_addr, struct vpfe_ipipe_car *car); +void ipipe_set_cgs_regs(void *__iomem base_addr, struct vpfe_ipipe_cgs *cgs); +void ipipe_set_wb_regs(void *__iomem base_addr, struct vpfe_ipipe_wb *wb); + +#endif /* _DAVINCI_VPFE_DM365_IPIPE_HW_H */ -- cgit v1.2.3 From 45e46b3bbe187a1bfc8f44c8d7ceaf851f0f4219 Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Wed, 28 Nov 2012 02:17:55 -0300 Subject: [media] davinci: vpfe: dm365: resizer driver based on media framework Add the video resizer driver with the v4l2 media controller framework which takes care of resizing the video frames with both up-scaling downscaling facility. The driver supports both continuous and single shot operations.The driver supports resizer as a subdevice and a media entity. It has support for 2 resizers - resizerA and resizerB. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/davinci_vpfe/dm365_resizer.c | 1999 ++++++++++++++++++++ drivers/staging/media/davinci_vpfe/dm365_resizer.h | 244 +++ 2 files changed, 2243 insertions(+) create mode 100644 drivers/staging/media/davinci_vpfe/dm365_resizer.c create mode 100644 drivers/staging/media/davinci_vpfe/dm365_resizer.h (limited to 'drivers') diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.c b/drivers/staging/media/davinci_vpfe/dm365_resizer.c new file mode 100644 index 000000000000..9cb0262b9b33 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.c @@ -0,0 +1,1999 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + * + * + * Resizer allows upscaling or downscaling a image to a desired + * resolution. There are 2 resizer modules. both operating on the + * same input image, but can have different output resolution. + */ + +#include "dm365_ipipe_hw.h" +#include "dm365_resizer.h" + +#define MIN_IN_WIDTH 32 +#define MIN_IN_HEIGHT 32 +#define MAX_IN_WIDTH 4095 +#define MAX_IN_HEIGHT 4095 +#define MIN_OUT_WIDTH 16 +#define MIN_OUT_HEIGHT 2 + +static const unsigned int resizer_input_formats[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_UV8_1X8, + V4L2_MBUS_FMT_SGRBG12_1X12, +}; + +static const unsigned int resizer_output_formats[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_UV8_1X8, + V4L2_MBUS_FMT_YDYUYDYV8_1X16, + V4L2_MBUS_FMT_SGRBG12_1X12, +}; + +/* resizer_calculate_line_length() - This function calculates the line length of + * various image planes at the input and + * output. + */ +static void +resizer_calculate_line_length(enum v4l2_mbus_pixelcode pix, int width, + int height, int *line_len, int *line_len_c) +{ + *line_len = 0; + *line_len_c = 0; + + if (pix == V4L2_MBUS_FMT_UYVY8_2X8 || + pix == V4L2_MBUS_FMT_SGRBG12_1X12) { + *line_len = width << 1; + } else if (pix == V4L2_MBUS_FMT_Y8_1X8 || + pix == V4L2_MBUS_FMT_UV8_1X8) { + *line_len = width; + *line_len_c = width; + } else { + /* YUV 420 */ + /* round width to upper 32 byte boundary */ + *line_len = width; + *line_len_c = width; + } + /* adjust the line len to be a multiple of 32 */ + *line_len += 31; + *line_len &= ~0x1f; + *line_len_c += 31; + *line_len_c &= ~0x1f; +} + +static inline int +resizer_validate_output_image_format(struct device *dev, + struct v4l2_mbus_framefmt *format, + int *in_line_len, int *in_line_len_c) +{ + if (format->code != V4L2_MBUS_FMT_UYVY8_2X8 && + format->code != V4L2_MBUS_FMT_Y8_1X8 && + format->code != V4L2_MBUS_FMT_UV8_1X8 && + format->code != V4L2_MBUS_FMT_YDYUYDYV8_1X16 && + format->code != V4L2_MBUS_FMT_SGRBG12_1X12) { + dev_err(dev, "Invalid Mbus format, %d\n", format->code); + return -EINVAL; + } + if (!format->width || !format->height) { + dev_err(dev, "invalid width or height\n"); + return -EINVAL; + } + resizer_calculate_line_length(format->code, format->width, + format->height, in_line_len, in_line_len_c); + return 0; +} + +static void +resizer_configure_passthru(struct vpfe_resizer_device *resizer, int bypass) +{ + struct resizer_params *param = &resizer->config; + + param->rsz_rsc_param[RSZ_A].cen = DISABLE; + param->rsz_rsc_param[RSZ_A].yen = DISABLE; + param->rsz_rsc_param[RSZ_A].v_phs_y = 0; + param->rsz_rsc_param[RSZ_A].v_phs_c = 0; + param->rsz_rsc_param[RSZ_A].v_dif = 256; + param->rsz_rsc_param[RSZ_A].v_lpf_int_y = 0; + param->rsz_rsc_param[RSZ_A].v_lpf_int_c = 0; + param->rsz_rsc_param[RSZ_A].h_phs = 0; + param->rsz_rsc_param[RSZ_A].h_dif = 256; + param->rsz_rsc_param[RSZ_A].h_lpf_int_y = 0; + param->rsz_rsc_param[RSZ_A].h_lpf_int_c = 0; + param->rsz_rsc_param[RSZ_A].dscale_en = DISABLE; + param->rsz2rgb[RSZ_A].rgb_en = DISABLE; + param->rsz_en[RSZ_A] = ENABLE; + param->rsz_en[RSZ_B] = DISABLE; + if (bypass) { + param->rsz_rsc_param[RSZ_A].i_vps = 0; + param->rsz_rsc_param[RSZ_A].i_hps = 0; + /* Raw Bypass */ + param->rsz_common.passthrough = BYPASS_ON; + } +} + +static void +configure_resizer_out_params(struct vpfe_resizer_device *resizer, int index, + void *output_spec, unsigned char partial, + unsigned flag) +{ + struct resizer_params *param = &resizer->config; + struct v4l2_mbus_framefmt *outformat; + struct vpfe_rsz_output_spec *output; + + if (index == RSZ_A && + resizer->resizer_a.output == RESIZER_OUTPUT_NONE) { + param->rsz_en[index] = DISABLE; + return; + } + if (index == RSZ_B && + resizer->resizer_b.output == RESIZER_OUTPUT_NONE) { + param->rsz_en[index] = DISABLE; + return; + } + output = (struct vpfe_rsz_output_spec *)output_spec; + param->rsz_en[index] = ENABLE; + if (partial) { + param->rsz_rsc_param[index].h_flip = output->h_flip; + param->rsz_rsc_param[index].v_flip = output->v_flip; + param->rsz_rsc_param[index].v_typ_y = output->v_typ_y; + param->rsz_rsc_param[index].v_typ_c = output->v_typ_c; + param->rsz_rsc_param[index].v_lpf_int_y = + output->v_lpf_int_y; + param->rsz_rsc_param[index].v_lpf_int_c = + output->v_lpf_int_c; + param->rsz_rsc_param[index].h_typ_y = output->h_typ_y; + param->rsz_rsc_param[index].h_typ_c = output->h_typ_c; + param->rsz_rsc_param[index].h_lpf_int_y = + output->h_lpf_int_y; + param->rsz_rsc_param[index].h_lpf_int_c = + output->h_lpf_int_c; + param->rsz_rsc_param[index].dscale_en = + output->en_down_scale; + param->rsz_rsc_param[index].h_dscale_ave_sz = + output->h_dscale_ave_sz; + param->rsz_rsc_param[index].v_dscale_ave_sz = + output->v_dscale_ave_sz; + param->ext_mem_param[index].user_y_ofst = + (output->user_y_ofst + 31) & ~0x1f; + param->ext_mem_param[index].user_c_ofst = + (output->user_c_ofst + 31) & ~0x1f; + return; + } + + if (index == RSZ_A) + outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; + else + outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; + param->rsz_rsc_param[index].o_vsz = outformat->height - 1; + param->rsz_rsc_param[index].o_hsz = outformat->width - 1; + param->ext_mem_param[index].rsz_sdr_ptr_s_y = output->vst_y; + param->ext_mem_param[index].rsz_sdr_ptr_e_y = outformat->height; + param->ext_mem_param[index].rsz_sdr_ptr_s_c = output->vst_c; + param->ext_mem_param[index].rsz_sdr_ptr_e_c = outformat->height; + + if (!flag) + return; + /* update common parameters */ + param->rsz_rsc_param[index].h_flip = output->h_flip; + param->rsz_rsc_param[index].v_flip = output->v_flip; + param->rsz_rsc_param[index].v_typ_y = output->v_typ_y; + param->rsz_rsc_param[index].v_typ_c = output->v_typ_c; + param->rsz_rsc_param[index].v_lpf_int_y = output->v_lpf_int_y; + param->rsz_rsc_param[index].v_lpf_int_c = output->v_lpf_int_c; + param->rsz_rsc_param[index].h_typ_y = output->h_typ_y; + param->rsz_rsc_param[index].h_typ_c = output->h_typ_c; + param->rsz_rsc_param[index].h_lpf_int_y = output->h_lpf_int_y; + param->rsz_rsc_param[index].h_lpf_int_c = output->h_lpf_int_c; + param->rsz_rsc_param[index].dscale_en = output->en_down_scale; + param->rsz_rsc_param[index].h_dscale_ave_sz = output->h_dscale_ave_sz; + param->rsz_rsc_param[index].v_dscale_ave_sz = output->h_dscale_ave_sz; + param->ext_mem_param[index].user_y_ofst = + (output->user_y_ofst + 31) & ~0x1f; + param->ext_mem_param[index].user_c_ofst = + (output->user_c_ofst + 31) & ~0x1f; +} + +/* + * resizer_calculate_resize_ratios() - Calculates resize ratio for resizer + * A or B. This is called after setting + * the input size or output size. + * @resizer: Pointer to VPFE resizer subdevice. + * @index: index RSZ_A-resizer-A RSZ_B-resizer-B. + */ +void +resizer_calculate_resize_ratios(struct vpfe_resizer_device *resizer, int index) +{ + struct resizer_params *param = &resizer->config; + struct v4l2_mbus_framefmt *informat, *outformat; + + informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK]; + + if (index == RSZ_A) + outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; + else + outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; + + if (outformat->field != V4L2_FIELD_INTERLACED) + param->rsz_rsc_param[index].v_dif = + ((informat->height) * 256) / (outformat->height); + else + param->rsz_rsc_param[index].v_dif = + ((informat->height >> 1) * 256) / (outformat->height); + param->rsz_rsc_param[index].h_dif = + ((informat->width) * 256) / (outformat->width); +} + +void +static resizer_enable_422_420_conversion(struct resizer_params *param, + int index, bool en) +{ + param->rsz_rsc_param[index].cen = en; + param->rsz_rsc_param[index].yen = en; +} + +/* resizer_calculate_sdram_offsets() - This function calculates the offsets from + * start of buffer for the C plane when + * output format is YUV420SP. It also + * calculates the offsets from the start of + * the buffer when the image is flipped + * vertically or horizontally for ycbcr/y/c + * planes. + * @resizer: Pointer to resizer subdevice. + * @index: index RSZ_A-resizer-A RSZ_B-resizer-B. + */ +static int +resizer_calculate_sdram_offsets(struct vpfe_resizer_device *resizer, int index) +{ + struct resizer_params *param = &resizer->config; + struct v4l2_mbus_framefmt *outformat; + int bytesperpixel = 2; + int image_height; + int image_width; + int yuv_420 = 0; + int offset = 0; + + if (index == RSZ_A) + outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; + else + outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; + + image_height = outformat->height + 1; + image_width = outformat->width + 1; + param->ext_mem_param[index].c_offset = 0; + param->ext_mem_param[index].flip_ofst_y = 0; + param->ext_mem_param[index].flip_ofst_c = 0; + if (outformat->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) { + /* YUV 420 */ + yuv_420 = 1; + bytesperpixel = 1; + } + + if (param->rsz_rsc_param[index].h_flip) + /* width * bytesperpixel - 1 */ + offset = (image_width * bytesperpixel) - 1; + if (param->rsz_rsc_param[index].v_flip) + offset += (image_height - 1) * + param->ext_mem_param[index].rsz_sdr_oft_y; + param->ext_mem_param[index].flip_ofst_y = offset; + if (!yuv_420) + return 0; + offset = 0; + /* half height for c-plane */ + if (param->rsz_rsc_param[index].h_flip) + /* width * bytesperpixel - 1 */ + offset = image_width - 1; + if (param->rsz_rsc_param[index].v_flip) + offset += (((image_height >> 1) - 1) * + param->ext_mem_param[index].rsz_sdr_oft_c); + param->ext_mem_param[index].flip_ofst_c = offset; + param->ext_mem_param[index].c_offset = + param->ext_mem_param[index].rsz_sdr_oft_y * image_height; + return 0; +} + +int resizer_configure_output_win(struct vpfe_resizer_device *resizer) +{ + struct resizer_params *param = &resizer->config; + struct vpfe_rsz_output_spec output_specs; + struct v4l2_mbus_framefmt *outformat; + int line_len_c; + int line_len; + int ret; + + outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; + + output_specs.vst_y = param->user_config.vst; + if (outformat->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) + output_specs.vst_c = param->user_config.vst; + + configure_resizer_out_params(resizer, RSZ_A, &output_specs, 0, 0); + resizer_calculate_line_length(outformat->code, + param->rsz_rsc_param[0].o_hsz + 1, + param->rsz_rsc_param[0].o_vsz + 1, + &line_len, &line_len_c); + param->ext_mem_param[0].rsz_sdr_oft_y = line_len; + param->ext_mem_param[0].rsz_sdr_oft_c = line_len_c; + resizer_calculate_resize_ratios(resizer, RSZ_A); + if (param->rsz_en[RSZ_B]) + resizer_calculate_resize_ratios(resizer, RSZ_B); + + if (outformat->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) + resizer_enable_422_420_conversion(param, RSZ_A, ENABLE); + else + resizer_enable_422_420_conversion(param, RSZ_A, DISABLE); + + ret = resizer_calculate_sdram_offsets(resizer, RSZ_A); + if (!ret && param->rsz_en[RSZ_B]) + ret = resizer_calculate_sdram_offsets(resizer, RSZ_B); + + if (ret) + pr_err("Error in calculating sdram offsets\n"); + return ret; +} + +static int +resizer_calculate_down_scale_f_div_param(struct device *dev, + int input_width, int output_width, + struct resizer_scale_param *param) +{ + /* rsz = R, input_width = H, output width = h in the equation */ + unsigned int two_power; + unsigned int upper_h1; + unsigned int upper_h2; + unsigned int val1; + unsigned int val; + unsigned int rsz; + unsigned int h1; + unsigned int h2; + unsigned int o; + unsigned int n; + + upper_h1 = input_width >> 1; + n = param->h_dscale_ave_sz; + /* 2 ^ (scale+1) */ + two_power = 1 << (n + 1); + upper_h1 = (upper_h1 >> (n + 1)) << (n + 1); + upper_h2 = input_width - upper_h1; + if (upper_h2 % two_power) { + dev_err(dev, "frame halves to be a multiple of 2 power n+1\n"); + return -EINVAL; + } + two_power = 1 << n; + rsz = (input_width << 8) / output_width; + val = rsz * two_power; + val = ((upper_h1 << 8) / val) + 1; + if (!(val % 2)) { + h1 = val; + } else { + val = upper_h1 << 8; + val >>= n + 1; + val -= rsz >> 1; + val /= rsz << 1; + val <<= 1; + val += 2; + h1 = val; + } + o = 10 + (two_power << 2); + if (((input_width << 7) / rsz) % 2) + o += (((CEIL(rsz, 1024)) << 1) << n); + h2 = output_width - h1; + /* phi */ + val = (h1 * rsz) - (((upper_h1 - (o - 10)) / two_power) << 8); + /* skip */ + val1 = ((val - 1024) >> 9) << 1; + param->f_div.num_passes = MAX_PASSES; + param->f_div.pass[0].o_hsz = h1 - 1; + param->f_div.pass[0].i_hps = 0; + param->f_div.pass[0].h_phs = 0; + param->f_div.pass[0].src_hps = 0; + param->f_div.pass[0].src_hsz = upper_h1 + o; + param->f_div.pass[1].o_hsz = h2 - 1; + param->f_div.pass[1].i_hps = 10 + (val1 * two_power); + param->f_div.pass[1].h_phs = (val - (val1 << 8)); + param->f_div.pass[1].src_hps = upper_h1 - o; + param->f_div.pass[1].src_hsz = upper_h2 + o; + + return 0; +} + +static int +resizer_configure_common_in_params(struct vpfe_resizer_device *resizer) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct resizer_params *param = &resizer->config; + struct vpfe_rsz_config_params *user_config; + struct v4l2_mbus_framefmt *informat; + + informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK]; + user_config = &resizer->config.user_config; + param->rsz_common.vps = param->user_config.vst; + param->rsz_common.hps = param->user_config.hst; + + if (vpfe_ipipeif_decimation_enabled(vpfe_dev)) + param->rsz_common.hsz = (((informat->width - 1) * + IPIPEIF_RSZ_CONST) / vpfe_ipipeif_get_rsz(vpfe_dev)); + else + param->rsz_common.hsz = informat->width - 1; + + if (informat->field == V4L2_FIELD_INTERLACED) + param->rsz_common.vsz = (informat->height - 1) >> 1; + else + param->rsz_common.vsz = informat->height - 1; + + param->rsz_common.raw_flip = 0; + + if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_IPIPEIF) + param->rsz_common.source = IPIPEIF_DATA; + else + param->rsz_common.source = IPIPE_DATA; + + switch (informat->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + param->rsz_common.src_img_fmt = RSZ_IMG_422; + param->rsz_common.raw_flip = 0; + break; + + case V4L2_MBUS_FMT_Y8_1X8: + param->rsz_common.src_img_fmt = RSZ_IMG_420; + /* Select y */ + param->rsz_common.y_c = 0; + param->rsz_common.raw_flip = 0; + break; + + case V4L2_MBUS_FMT_UV8_1X8: + param->rsz_common.src_img_fmt = RSZ_IMG_420; + /* Select y */ + param->rsz_common.y_c = 1; + param->rsz_common.raw_flip = 0; + break; + + case V4L2_MBUS_FMT_SGRBG12_1X12: + param->rsz_common.raw_flip = 1; + break; + + default: + param->rsz_common.src_img_fmt = RSZ_IMG_422; + param->rsz_common.source = IPIPE_DATA; + } + + param->rsz_common.yuv_y_min = user_config->yuv_y_min; + param->rsz_common.yuv_y_max = user_config->yuv_y_max; + param->rsz_common.yuv_c_min = user_config->yuv_c_min; + param->rsz_common.yuv_c_max = user_config->yuv_c_max; + param->rsz_common.out_chr_pos = user_config->out_chr_pos; + param->rsz_common.rsz_seq_crv = user_config->chroma_sample_even; + + return 0; +} +static int +resizer_configure_in_continious_mode(struct vpfe_resizer_device *resizer) +{ + struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev; + struct resizer_params *param = &resizer->config; + struct vpfe_rsz_config_params *cont_config; + int line_len_c; + int line_len; + int ret; + + if (resizer->resizer_a.output != RESIZER_OUPUT_MEMORY) { + dev_err(dev, "enable resizer - Resizer-A\n"); + return -EINVAL; + } + + cont_config = &resizer->config.user_config; + param->rsz_en[RSZ_A] = ENABLE; + configure_resizer_out_params(resizer, RSZ_A, + &cont_config->output1, 1, 0); + param->rsz_en[RSZ_B] = DISABLE; + param->oper_mode = RESIZER_MODE_CONTINIOUS; + + if (resizer->resizer_b.output == RESIZER_OUPUT_MEMORY) { + struct v4l2_mbus_framefmt *outformat2; + + param->rsz_en[RSZ_B] = ENABLE; + outformat2 = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; + ret = resizer_validate_output_image_format(dev, outformat2, + &line_len, &line_len_c); + if (ret) + return ret; + param->ext_mem_param[RSZ_B].rsz_sdr_oft_y = line_len; + param->ext_mem_param[RSZ_B].rsz_sdr_oft_c = line_len_c; + configure_resizer_out_params(resizer, RSZ_B, + &cont_config->output2, 0, 1); + if (outformat2->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) + resizer_enable_422_420_conversion(param, + RSZ_B, ENABLE); + else + resizer_enable_422_420_conversion(param, + RSZ_B, DISABLE); + } + resizer_configure_common_in_params(resizer); + ret = resizer_configure_output_win(resizer); + if (ret) + return ret; + + param->rsz_common.passthrough = cont_config->bypass; + if (cont_config->bypass) + resizer_configure_passthru(resizer, 1); + + return 0; +} + +static inline int +resizer_validate_input_image_format(struct device *dev, + enum v4l2_mbus_pixelcode pix, + int width, int height, int *line_len) +{ + int val; + + if (pix != V4L2_MBUS_FMT_UYVY8_2X8 && + pix != V4L2_MBUS_FMT_Y8_1X8 && + pix != V4L2_MBUS_FMT_UV8_1X8 && + pix != V4L2_MBUS_FMT_SGRBG12_1X12) { + dev_err(dev, + "resizer validate output: pix format not supported, %d\n", pix); + return -EINVAL; + } + + if (!width || !height) { + dev_err(dev, + "resizer validate input: invalid width or height\n"); + return -EINVAL; + } + + if (pix == V4L2_MBUS_FMT_UV8_1X8) + resizer_calculate_line_length(pix, width, + height, &val, line_len); + else + resizer_calculate_line_length(pix, width, + height, line_len, &val); + + return 0; +} + +static int +resizer_validate_decimation(struct device *dev, enum ipipeif_decimation dec_en, + unsigned char rsz, unsigned char frame_div_mode_en, + int width) +{ + if (dec_en && frame_div_mode_en) { + dev_err(dev, + "dec_en & frame_div_mode_en can not enabled simultaneously\n"); + return -EINVAL; + } + + if (frame_div_mode_en) { + dev_err(dev, "frame_div_mode mode not supported\n"); + return -EINVAL; + } + + if (!dec_en) + return 0; + + if (width <= VPFE_IPIPE_MAX_INPUT_WIDTH) { + dev_err(dev, + "image width to be more than %d for decimation\n", + VPFE_IPIPE_MAX_INPUT_WIDTH); + return -EINVAL; + } + + if (rsz < IPIPEIF_RSZ_MIN || rsz > IPIPEIF_RSZ_MAX) { + dev_err(dev, "rsz range is %d to %d\n", + IPIPEIF_RSZ_MIN, IPIPEIF_RSZ_MAX); + return -EINVAL; + } + + return 0; +} + +/* resizer_calculate_normal_f_div_param() - Algorithm to calculate the frame + * division parameters for resizer. + * in normal mode. + */ +static int +resizer_calculate_normal_f_div_param(struct device *dev, int input_width, + int output_width, struct resizer_scale_param *param) +{ + /* rsz = R, input_width = H, output width = h in the equation */ + unsigned int val1; + unsigned int rsz; + unsigned int val; + unsigned int h1; + unsigned int h2; + unsigned int o; + + if (output_width > input_width) { + dev_err(dev, "frame div mode is used for scale down only\n"); + return -EINVAL; + } + + rsz = (input_width << 8) / output_width; + val = rsz << 1; + val = ((input_width << 8) / val) + 1; + o = 14; + if (!(val % 2)) { + h1 = val; + } else { + val = (input_width << 7); + val -= rsz >> 1; + val /= rsz << 1; + val <<= 1; + val += 2; + o += ((CEIL(rsz, 1024)) << 1); + h1 = val; + } + h2 = output_width - h1; + /* phi */ + val = (h1 * rsz) - (((input_width >> 1) - o) << 8); + /* skip */ + val1 = ((val - 1024) >> 9) << 1; + param->f_div.num_passes = MAX_PASSES; + param->f_div.pass[0].o_hsz = h1 - 1; + param->f_div.pass[0].i_hps = 0; + param->f_div.pass[0].h_phs = 0; + param->f_div.pass[0].src_hps = 0; + param->f_div.pass[0].src_hsz = (input_width >> 2) + o; + param->f_div.pass[1].o_hsz = h2 - 1; + param->f_div.pass[1].i_hps = val1; + param->f_div.pass[1].h_phs = (val - (val1 << 8)); + param->f_div.pass[1].src_hps = (input_width >> 2) - o; + param->f_div.pass[1].src_hsz = (input_width >> 2) + o; + + return 0; +} + +static int +resizer_configure_in_single_shot_mode(struct vpfe_resizer_device *resizer) +{ + struct vpfe_rsz_config_params *config = &resizer->config.user_config; + struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct v4l2_mbus_framefmt *outformat1, *outformat2; + struct resizer_params *param = &resizer->config; + struct v4l2_mbus_framefmt *informat; + int decimation; + int line_len_c; + int line_len; + int rsz; + int ret; + + informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK]; + outformat1 = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; + outformat2 = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; + + decimation = vpfe_ipipeif_decimation_enabled(vpfe_dev); + rsz = vpfe_ipipeif_get_rsz(vpfe_dev); + if (decimation && param->user_config.frame_div_mode_en) { + dev_err(dev, + "dec_en & frame_div_mode_en cannot enabled simultaneously\n"); + return -EINVAL; + } + + ret = resizer_validate_decimation(dev, decimation, rsz, + param->user_config.frame_div_mode_en, informat->width); + if (ret) + return -EINVAL; + + ret = resizer_validate_input_image_format(dev, informat->code, + informat->width, informat->height, &line_len); + if (ret) + return -EINVAL; + + if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) { + param->rsz_en[RSZ_A] = ENABLE; + ret = resizer_validate_output_image_format(dev, outformat1, + &line_len, &line_len_c); + if (ret) + return ret; + param->ext_mem_param[RSZ_A].rsz_sdr_oft_y = line_len; + param->ext_mem_param[RSZ_A].rsz_sdr_oft_c = line_len_c; + configure_resizer_out_params(resizer, RSZ_A, + ¶m->user_config.output1, 0, 1); + + if (outformat1->code == V4L2_MBUS_FMT_SGRBG12_1X12) + param->rsz_common.raw_flip = 1; + else + param->rsz_common.raw_flip = 0; + + if (outformat1->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) + resizer_enable_422_420_conversion(param, + RSZ_A, ENABLE); + else + resizer_enable_422_420_conversion(param, + RSZ_A, DISABLE); + } + + if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) { + param->rsz_en[RSZ_B] = ENABLE; + ret = resizer_validate_output_image_format(dev, outformat2, + &line_len, &line_len_c); + if (ret) + return ret; + param->ext_mem_param[RSZ_B].rsz_sdr_oft_y = line_len; + param->ext_mem_param[RSZ_B].rsz_sdr_oft_c = line_len_c; + configure_resizer_out_params(resizer, RSZ_B, + ¶m->user_config.output2, 0, 1); + if (outformat2->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) + resizer_enable_422_420_conversion(param, + RSZ_B, ENABLE); + else + resizer_enable_422_420_conversion(param, + RSZ_B, DISABLE); + } + + resizer_configure_common_in_params(resizer); + if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) { + resizer_calculate_resize_ratios(resizer, RSZ_A); + resizer_calculate_sdram_offsets(resizer, RSZ_A); + /* Overriding resize ratio calculation */ + if (informat->code == V4L2_MBUS_FMT_UV8_1X8) { + param->rsz_rsc_param[RSZ_A].v_dif = + (((informat->height + 1) * 2) * 256) / + (param->rsz_rsc_param[RSZ_A].o_vsz + 1); + } + } + + if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) { + resizer_calculate_resize_ratios(resizer, RSZ_B); + resizer_calculate_sdram_offsets(resizer, RSZ_B); + /* Overriding resize ratio calculation */ + if (informat->code == V4L2_MBUS_FMT_UV8_1X8) { + param->rsz_rsc_param[RSZ_B].v_dif = + (((informat->height + 1) * 2) * 256) / + (param->rsz_rsc_param[RSZ_B].o_vsz + 1); + } + } + if (param->user_config.frame_div_mode_en && + param->rsz_en[RSZ_A]) { + if (!param->rsz_rsc_param[RSZ_A].dscale_en) + ret = resizer_calculate_normal_f_div_param(dev, + informat->width, + param->rsz_rsc_param[RSZ_A].o_vsz + 1, + ¶m->rsz_rsc_param[RSZ_A]); + else + ret = resizer_calculate_down_scale_f_div_param(dev, + informat->width, + param->rsz_rsc_param[RSZ_A].o_vsz + 1, + ¶m->rsz_rsc_param[RSZ_A]); + if (ret) + return -EINVAL; + } + if (param->user_config.frame_div_mode_en && + param->rsz_en[RSZ_B]) { + if (!param->rsz_rsc_param[RSZ_B].dscale_en) + ret = resizer_calculate_normal_f_div_param(dev, + informat->width, + param->rsz_rsc_param[RSZ_B].o_vsz + 1, + ¶m->rsz_rsc_param[RSZ_B]); + else + ret = resizer_calculate_down_scale_f_div_param(dev, + informat->width, + param->rsz_rsc_param[RSZ_B].o_vsz + 1, + ¶m->rsz_rsc_param[RSZ_B]); + if (ret) + return -EINVAL; + } + param->rsz_common.passthrough = config->bypass; + if (config->bypass) + resizer_configure_passthru(resizer, 1); + return 0; +} + +static void +resizer_set_defualt_configuration(struct vpfe_resizer_device *resizer) +{ +#define WIDTH_I 640 +#define HEIGHT_I 480 +#define WIDTH_O 640 +#define HEIGHT_O 480 + const struct resizer_params rsz_default_config = { + .oper_mode = RESIZER_MODE_ONE_SHOT, + .rsz_common = { + .vsz = HEIGHT_I - 1, + .hsz = WIDTH_I - 1, + .src_img_fmt = RSZ_IMG_422, + .raw_flip = 1, /* flip preserve Raw format */ + .source = IPIPE_DATA, + .passthrough = BYPASS_OFF, + .yuv_y_max = 255, + .yuv_c_max = 255, + .rsz_seq_crv = DISABLE, + .out_chr_pos = VPFE_IPIPE_YUV422_CHR_POS_COSITE, + }, + .rsz_rsc_param = { + { + .h_flip = DISABLE, + .v_flip = DISABLE, + .cen = DISABLE, + .yen = DISABLE, + .o_vsz = HEIGHT_O - 1, + .o_hsz = WIDTH_O - 1, + .v_dif = 256, + .v_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dif = 256, + .h_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + }, + { + .h_flip = DISABLE, + .v_flip = DISABLE, + .cen = DISABLE, + .yen = DISABLE, + .o_vsz = HEIGHT_O - 1, + .o_hsz = WIDTH_O - 1, + .v_dif = 256, + .v_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dif = 256, + .h_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + }, + }, + .rsz2rgb = { + { + .rgb_en = DISABLE + }, + { + .rgb_en = DISABLE + } + }, + .ext_mem_param = { + { + .rsz_sdr_oft_y = WIDTH_O << 1, + .rsz_sdr_ptr_e_y = HEIGHT_O, + .rsz_sdr_oft_c = WIDTH_O, + .rsz_sdr_ptr_e_c = HEIGHT_O >> 1, + }, + { + .rsz_sdr_oft_y = WIDTH_O << 1, + .rsz_sdr_ptr_e_y = HEIGHT_O, + .rsz_sdr_oft_c = WIDTH_O, + .rsz_sdr_ptr_e_c = HEIGHT_O, + }, + }, + .rsz_en[0] = ENABLE, + .rsz_en[1] = DISABLE, + .user_config = { + .output1 = { + .v_typ_y = VPFE_RSZ_INTP_CUBIC, + .v_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + }, + .output2 = { + .v_typ_y = VPFE_RSZ_INTP_CUBIC, + .v_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + }, + .yuv_y_max = 255, + .yuv_c_max = 255, + .out_chr_pos = VPFE_IPIPE_YUV422_CHR_POS_COSITE, + }, + }; + memset(&resizer->config, 0, sizeof(struct resizer_params)); + memcpy(&resizer->config, &rsz_default_config, + sizeof(struct resizer_params)); +} + +/* + * resizer_set_configuration() - set resizer config + * @resizer: vpfe resizer device pointer. + * @chan_config: resizer channel configuration. + */ +static int +resizer_set_configuration(struct vpfe_resizer_device *resizer, + struct vpfe_rsz_config *chan_config) +{ + if (!chan_config->config) + resizer_set_defualt_configuration(resizer); + else + if (copy_from_user(&resizer->config.user_config, + chan_config->config, sizeof(struct vpfe_rsz_config_params))) + return -EFAULT; + + return 0; +} + +/* + * resizer_get_configuration() - get resizer config + * @resizer: vpfe resizer device pointer. + * @channel: image processor logical channel. + * @chan_config: resizer channel configuration. + */ +static int +resizer_get_configuration(struct vpfe_resizer_device *resizer, + struct vpfe_rsz_config *chan_config) +{ + struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev; + + if (!chan_config->config) { + dev_err(dev, "Resizer channel invalid pointer\n"); + return -EINVAL; + } + + if (copy_to_user((void *)chan_config->config, + (void *)&resizer->config.user_config, + sizeof(struct vpfe_rsz_config_params))) { + dev_err(dev, "resizer_get_configuration: Error in copy to user\n"); + return -EFAULT; + } + + return 0; +} + +/* + * VPFE video operations + */ + +/* + * resizer_a_video_out_queue() - RESIZER-A video out queue + * @vpfe_dev: vpfe device pointer. + * @addr: buffer address. + */ +static int resizer_a_video_out_queue(struct vpfe_device *vpfe_dev, + unsigned long addr) +{ + struct vpfe_resizer_device *resizer = &vpfe_dev->vpfe_resizer; + + return resizer_set_outaddr(resizer->base_addr, + &resizer->config, RSZ_A, addr); +} + +/* + * resizer_b_video_out_queue() - RESIZER-B video out queue + * @vpfe_dev: vpfe device pointer. + * @addr: buffer address. + */ +static int resizer_b_video_out_queue(struct vpfe_device *vpfe_dev, + unsigned long addr) +{ + struct vpfe_resizer_device *resizer = &vpfe_dev->vpfe_resizer; + + return resizer_set_outaddr(resizer->base_addr, + &resizer->config, RSZ_B, addr); +} + +static const struct vpfe_video_operations resizer_a_video_ops = { + .queue = resizer_a_video_out_queue, +}; + +static const struct vpfe_video_operations resizer_b_video_ops = { + .queue = resizer_b_video_out_queue, +}; + +static void resizer_enable(struct vpfe_resizer_device *resizer, int en) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; + unsigned char val; + + if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_NONE) + return; + + if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_IPIPEIF && + ipipeif_sink == IPIPEIF_INPUT_MEMORY) { + do { + val = regr_rsz(resizer->base_addr, RSZ_SRC_EN); + } while (val); + + if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) { + do { + val = regr_rsz(resizer->base_addr, RSZ_A); + } while (val); + } + if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) { + do { + val = regr_rsz(resizer->base_addr, RSZ_B); + } while (val); + } + } + if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) + rsz_enable(resizer->base_addr, RSZ_A, en); + + if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) + rsz_enable(resizer->base_addr, RSZ_B, en); +} + + +/* + * resizer_ss_isr() - resizer module single-shot buffer scheduling isr + * @resizer: vpfe resizer device pointer. + */ +static void resizer_ss_isr(struct vpfe_resizer_device *resizer) +{ + struct vpfe_video_device *video_out = &resizer->resizer_a.video_out; + struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct vpfe_pipeline *pipe = &video_out->pipe; + u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; + u32 val; + + if (ipipeif_sink != IPIPEIF_INPUT_MEMORY) + return; + + if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) { + val = vpss_dma_complete_interrupt(); + if (val != 0 && val != 2) + return; + } + + if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) { + spin_lock(&video_out->dma_queue_lock); + vpfe_video_process_buffer_complete(video_out); + video_out->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; + vpfe_video_schedule_next_buffer(video_out); + spin_unlock(&video_out->dma_queue_lock); + } + + /* If resizer B is enabled */ + if (pipe->output_num > 1 && resizer->resizer_b.output == + RESIZER_OUPUT_MEMORY) { + spin_lock(&video_out->dma_queue_lock); + vpfe_video_process_buffer_complete(video_out2); + video_out2->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; + vpfe_video_schedule_next_buffer(video_out2); + spin_unlock(&video_out2->dma_queue_lock); + } + + /* start HW if buffers are queued */ + if (vpfe_video_is_pipe_ready(pipe) && + resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) { + resizer_enable(resizer, 1); + vpfe_ipipe_enable(vpfe_dev, 1); + vpfe_ipipeif_enable(vpfe_dev); + } +} + +/* + * vpfe_resizer_buffer_isr() - resizer module buffer scheduling isr + * @resizer: vpfe resizer device pointer. + */ +void vpfe_resizer_buffer_isr(struct vpfe_resizer_device *resizer) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct vpfe_video_device *video_out = &resizer->resizer_a.video_out; + struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out; + struct vpfe_pipeline *pipe = &resizer->resizer_a.video_out.pipe; + enum v4l2_field field; + int fid; + + if (!video_out->started) + return; + + if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_NONE) + return; + + field = video_out->fmt.fmt.pix.field; + if (field == V4L2_FIELD_NONE) { + /* handle progressive frame capture */ + if (video_out->cur_frm != video_out->next_frm) { + vpfe_video_process_buffer_complete(video_out); + if (pipe->output_num > 1) + vpfe_video_process_buffer_complete(video_out2); + } + + video_out->skip_frame_count--; + if (!video_out->skip_frame_count) { + video_out->skip_frame_count = + video_out->skip_frame_count_init; + rsz_src_enable(resizer->base_addr, 1); + } else { + rsz_src_enable(resizer->base_addr, 0); + } + return; + } + + /* handle interlaced frame capture */ + fid = vpfe_isif_get_fid(vpfe_dev); + + /* switch the software maintained field id */ + video_out->field_id ^= 1; + if (fid == video_out->field_id) { + /* + * we are in-sync here,continue. + * One frame is just being captured. If the + * next frame is available, release the current + * frame and move on + */ + if (fid == 0 && video_out->cur_frm != video_out->next_frm) { + vpfe_video_process_buffer_complete(video_out); + if (pipe->output_num > 1) + vpfe_video_process_buffer_complete(video_out2); + } + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + video_out->field_id = fid; + } +} + +/* + * vpfe_resizer_dma_isr() - resizer module dma isr + * @resizer: vpfe resizer device pointer. + */ +void vpfe_resizer_dma_isr(struct vpfe_resizer_device *resizer) +{ + struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out; + struct vpfe_video_device *video_out = &resizer->resizer_a.video_out; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct vpfe_pipeline *pipe = &video_out->pipe; + int schedule_capture = 0; + enum v4l2_field field; + int fid; + + if (!video_out->started) + return; + + if (pipe->state == VPFE_PIPELINE_STREAM_SINGLESHOT) { + resizer_ss_isr(resizer); + return; + } + + field = video_out->fmt.fmt.pix.field; + if (field == V4L2_FIELD_NONE) { + if (!list_empty(&video_out->dma_queue) && + video_out->cur_frm == video_out->next_frm) + schedule_capture = 1; + } else { + fid = vpfe_isif_get_fid(vpfe_dev); + if (fid == video_out->field_id) { + /* we are in-sync here,continue */ + if (fid == 1 && !list_empty(&video_out->dma_queue) && + video_out->cur_frm == video_out->next_frm) + schedule_capture = 1; + } + } + + if (!schedule_capture) + return; + + spin_lock(&video_out->dma_queue_lock); + vpfe_video_schedule_next_buffer(video_out); + spin_unlock(&video_out->dma_queue_lock); + if (pipe->output_num > 1) { + spin_lock(&video_out2->dma_queue_lock); + vpfe_video_schedule_next_buffer(video_out2); + spin_unlock(&video_out2->dma_queue_lock); + } +} + +/* + * V4L2 subdev operations + */ + +/* + * resizer_ioctl() - Handle resizer module private ioctl's + * @sd: pointer to v4l2 subdev structure + * @cmd: configuration command + * @arg: configuration argument + */ +static long resizer_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev; + struct vpfe_rsz_config *user_config; + int ret = -ENOIOCTLCMD; + + if (&resizer->crop_resizer.subdev != sd) + return ret; + + switch (cmd) { + case VIDIOC_VPFE_RSZ_S_CONFIG: + user_config = (struct vpfe_rsz_config *)arg; + ret = resizer_set_configuration(resizer, user_config); + break; + + case VIDIOC_VPFE_RSZ_G_CONFIG: + user_config = (struct vpfe_rsz_config *)arg; + if (!user_config->config) { + dev_err(dev, "error in VIDIOC_VPFE_RSZ_G_CONFIG\n"); + return -EINVAL; + } + ret = resizer_get_configuration(resizer, user_config); + break; + } + return ret; +} + +static int resizer_do_hw_setup(struct vpfe_resizer_device *resizer) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; + u16 ipipeif_source = vpfe_dev->vpfe_ipipeif.output; + struct resizer_params *param = &resizer->config; + int ret = 0; + + if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY || + resizer->resizer_b.output == RESIZER_OUPUT_MEMORY) { + if (ipipeif_sink == IPIPEIF_INPUT_MEMORY && + ipipeif_source == IPIPEIF_OUTPUT_RESIZER) + ret = resizer_configure_in_single_shot_mode(resizer); + else + ret = resizer_configure_in_continious_mode(resizer); + if (ret) + return ret; + ret = config_rsz_hw(resizer, param); + } + return ret; +} + +/* + * resizer_set_stream() - Enable/Disable streaming on resizer subdev + * @sd: pointer to v4l2 subdev structure + * @enable: 1 == Enable, 0 == Disable + */ +static int resizer_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + + if (&resizer->crop_resizer.subdev != sd) + return 0; + + if (resizer->resizer_a.output != RESIZER_OUPUT_MEMORY) + return 0; + + switch (enable) { + case 1: + if (resizer_do_hw_setup(resizer) < 0) + return -EINVAL; + resizer_enable(resizer, enable); + break; + + case 0: + resizer_enable(resizer, enable); + break; + } + + return 0; +} + +/* + * __resizer_get_format() - helper function for getting resizer format + * @sd: pointer to subdev. + * @fh: V4L2 subdev file handle. + * @pad: pad number. + * @which: wanted subdev format. + * Retun wanted mbus frame format. + */ +static struct v4l2_mbus_framefmt * +__resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + if (&resizer->crop_resizer.subdev == sd) + return &resizer->crop_resizer.formats[pad]; + if (&resizer->resizer_a.subdev == sd) + return &resizer->resizer_a.formats[pad]; + if (&resizer->resizer_b.subdev == sd) + return &resizer->resizer_b.formats[pad]; + return NULL; +} + +/* + * resizer_try_format() - Handle try format by pad subdev method + * @sd: pointer to subdev. + * @fh: V4L2 subdev file handle. + * @pad: pad num. + * @fmt: pointer to v4l2 format structure. + * @which: wanted subdev format. + */ +static void +resizer_try_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + unsigned int pad, struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + unsigned int max_out_height; + unsigned int max_out_width; + unsigned int i; + + if ((&resizer->resizer_a.subdev == sd && pad == RESIZER_PAD_SINK) || + (&resizer->resizer_b.subdev == sd && pad == RESIZER_PAD_SINK) || + (&resizer->crop_resizer.subdev == sd && + (pad == RESIZER_CROP_PAD_SOURCE || + pad == RESIZER_CROP_PAD_SOURCE2 || pad == RESIZER_CROP_PAD_SINK))) { + for (i = 0; i < ARRAY_SIZE(resizer_input_formats); i++) { + if (fmt->code == resizer_input_formats[i]) + break; + } + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(resizer_input_formats)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, MIN_IN_WIDTH, + MAX_IN_WIDTH); + fmt->height = clamp_t(u32, fmt->height, MIN_IN_HEIGHT, + MAX_IN_HEIGHT); + } else if (&resizer->resizer_a.subdev == sd && + pad == RESIZER_PAD_SOURCE) { + max_out_width = IPIPE_MAX_OUTPUT_WIDTH_A; + max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_A; + + for (i = 0; i < ARRAY_SIZE(resizer_output_formats); i++) { + if (fmt->code == resizer_output_formats[i]) + break; + } + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(resizer_output_formats)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_WIDTH, + max_out_width); + fmt->width &= ~15; + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_HEIGHT, + max_out_height); + } else if (&resizer->resizer_b.subdev == sd && + pad == RESIZER_PAD_SOURCE) { + max_out_width = IPIPE_MAX_OUTPUT_WIDTH_B; + max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_B; + + for (i = 0; i < ARRAY_SIZE(resizer_output_formats); i++) { + if (fmt->code == resizer_output_formats[i]) + break; + } + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(resizer_output_formats)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_WIDTH, + max_out_width); + fmt->width &= ~15; + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_HEIGHT, + max_out_height); + } +} + +/* + * resizer_set_format() - Handle set format by pads subdev method + * @sd: pointer to v4l2 subdev structure + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __resizer_get_format(sd, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + resizer_try_format(sd, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (&resizer->crop_resizer.subdev == sd) { + if (fmt->pad == RESIZER_CROP_PAD_SINK) { + resizer->crop_resizer.formats[fmt->pad] = fmt->format; + } else if (fmt->pad == RESIZER_CROP_PAD_SOURCE && + resizer->crop_resizer.output == RESIZER_A) { + resizer->crop_resizer.formats[fmt->pad] = fmt->format; + resizer->crop_resizer. + formats[RESIZER_CROP_PAD_SOURCE2] = fmt->format; + } else if (fmt->pad == RESIZER_CROP_PAD_SOURCE2 && + resizer->crop_resizer.output2 == RESIZER_B) { + resizer->crop_resizer.formats[fmt->pad] = fmt->format; + resizer->crop_resizer. + formats[RESIZER_CROP_PAD_SOURCE] = fmt->format; + } else { + return -EINVAL; + } + } else if (&resizer->resizer_a.subdev == sd) { + if (fmt->pad == RESIZER_PAD_SINK) + resizer->resizer_a.formats[fmt->pad] = fmt->format; + else if (fmt->pad == RESIZER_PAD_SOURCE) + resizer->resizer_a.formats[fmt->pad] = fmt->format; + else + return -EINVAL; + } else if (&resizer->resizer_b.subdev == sd) { + if (fmt->pad == RESIZER_PAD_SINK) + resizer->resizer_b.formats[fmt->pad] = fmt->format; + else if (fmt->pad == RESIZER_PAD_SOURCE) + resizer->resizer_b.formats[fmt->pad] = fmt->format; + else + return -EINVAL; + } else { + return -EINVAL; + } + + return 0; +} + +/* + * resizer_get_format() - Retrieve the video format on a pad + * @sd: pointer to v4l2 subdev structure. + * @fh: V4L2 subdev file handle. + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *format; + + format = __resizer_get_format(sd, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +/* + * resizer_enum_frame_size() - enum frame sizes on pads + * @sd: Pointer to subdevice. + * @fh: V4L2 subdev file handle. + * @code: pointer to v4l2_subdev_frame_size_enum structure. + */ +static int resizer_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + resizer_try_format(sd, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + resizer_try_format(sd, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * resizer_enum_mbus_code() - enum mbus codes for pads + * @sd: Pointer to subdevice. + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + */ +static int resizer_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad == RESIZER_PAD_SINK) { + if (code->index >= ARRAY_SIZE(resizer_input_formats)) + return -EINVAL; + + code->code = resizer_input_formats[code->index]; + } else if (code->pad == RESIZER_PAD_SOURCE) { + if (code->index >= ARRAY_SIZE(resizer_output_formats)) + return -EINVAL; + + code->code = resizer_output_formats[code->index]; + } + + return 0; +} + +/* + * resizer_init_formats() - Initialize formats on all pads + * @sd: Pointer to subdevice. + * @fh: V4L2 subdev file handle. + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int resizer_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + __u32 which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct v4l2_subdev_format format; + + if (&resizer->crop_resizer.subdev == sd) { + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_CROP_PAD_SINK; + format.which = which; + format.format.code = V4L2_MBUS_FMT_YUYV8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_HEIGHT; + resizer_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_CROP_PAD_SOURCE; + format.which = which; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_WIDTH; + resizer_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_CROP_PAD_SOURCE2; + format.which = which; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_WIDTH; + resizer_set_format(sd, fh, &format); + } else if (&resizer->resizer_a.subdev == sd) { + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SINK; + format.which = which; + format.format.code = V4L2_MBUS_FMT_YUYV8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_HEIGHT; + resizer_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SOURCE; + format.which = which; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; + resizer_set_format(sd, fh, &format); + } else if (&resizer->resizer_b.subdev == sd) { + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SINK; + format.which = which; + format.format.code = V4L2_MBUS_FMT_YUYV8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_HEIGHT; + resizer_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SOURCE; + format.which = which; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_B; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_B; + resizer_set_format(sd, fh, &format); + } + + return 0; +} + +/* subdev core operations */ +static const struct v4l2_subdev_core_ops resizer_v4l2_core_ops = { + .ioctl = resizer_ioctl, +}; + +/* subdev internal operations */ +static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = { + .open = resizer_init_formats, +}; + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = { + .s_stream = resizer_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = { + .enum_mbus_code = resizer_enum_mbus_code, + .enum_frame_size = resizer_enum_frame_size, + .get_fmt = resizer_get_format, + .set_fmt = resizer_set_format, +}; + +/* subdev operations */ +static const struct v4l2_subdev_ops resizer_v4l2_ops = { + .core = &resizer_v4l2_core_ops, + .video = &resizer_v4l2_video_ops, + .pad = &resizer_v4l2_pad_ops, +}; + +/* + * Media entity operations + */ + +/* + * resizer_link_setup() - Setup resizer connections + * @entity: Pointer to media entity structure + * @local: Pointer to local pad array + * @remote: Pointer to remote pad array + * @flags: Link flags + * return -EINVAL or zero on success + */ +static int resizer_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + u16 ipipeif_source = vpfe_dev->vpfe_ipipeif.output; + u16 ipipe_source = vpfe_dev->vpfe_ipipe.output; + + if (&resizer->crop_resizer.subdev == sd) { + switch (local->index | media_entity_type(remote->entity)) { + case RESIZER_CROP_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->crop_resizer.input = + RESIZER_CROP_INPUT_NONE; + break; + } + + if (resizer->crop_resizer.input != + RESIZER_CROP_INPUT_NONE) + return -EBUSY; + if (ipipeif_source == IPIPEIF_OUTPUT_RESIZER) + resizer->crop_resizer.input = + RESIZER_CROP_INPUT_IPIPEIF; + else if (ipipe_source == IPIPE_OUTPUT_RESIZER) + resizer->crop_resizer.input = + RESIZER_CROP_INPUT_IPIPE; + else + return -EINVAL; + break; + + case RESIZER_CROP_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->crop_resizer.output = + RESIZER_CROP_OUTPUT_NONE; + break; + } + if (resizer->crop_resizer.output != + RESIZER_CROP_OUTPUT_NONE) + return -EBUSY; + resizer->crop_resizer.output = RESIZER_A; + break; + + case RESIZER_CROP_PAD_SOURCE2 | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->crop_resizer.output2 = + RESIZER_CROP_OUTPUT_NONE; + break; + } + if (resizer->crop_resizer.output2 != + RESIZER_CROP_OUTPUT_NONE) + return -EBUSY; + resizer->crop_resizer.output2 = RESIZER_B; + break; + + default: + return -EINVAL; + } + } else if (&resizer->resizer_a.subdev == sd) { + switch (local->index | media_entity_type(remote->entity)) { + case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->resizer_a.input = RESIZER_INPUT_NONE; + break; + } + if (resizer->resizer_a.input != RESIZER_INPUT_NONE) + return -EBUSY; + resizer->resizer_a.input = RESIZER_INPUT_CROP_RESIZER; + break; + + case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->resizer_a.output = RESIZER_OUTPUT_NONE; + break; + } + if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) + return -EBUSY; + resizer->resizer_a.output = RESIZER_OUPUT_MEMORY; + break; + + default: + return -EINVAL; + } + } else if (&resizer->resizer_b.subdev == sd) { + switch (local->index | media_entity_type(remote->entity)) { + case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->resizer_b.input = RESIZER_INPUT_NONE; + break; + } + if (resizer->resizer_b.input != RESIZER_INPUT_NONE) + return -EBUSY; + resizer->resizer_b.input = RESIZER_INPUT_CROP_RESIZER; + break; + + case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->resizer_b.output = RESIZER_OUTPUT_NONE; + break; + } + if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) + return -EBUSY; + resizer->resizer_b.output = RESIZER_OUPUT_MEMORY; + break; + + default: + return -EINVAL; + } + } else { + return -EINVAL; + } + + return 0; +} + +static const struct media_entity_operations resizer_media_ops = { + .link_setup = resizer_link_setup, +}; + +/* + * vpfe_resizer_unregister_entities() - Unregister entity + * @vpfe_rsz - pointer to resizer subdevice structure. + */ +void vpfe_resizer_unregister_entities(struct vpfe_resizer_device *vpfe_rsz) +{ + /* unregister video devices */ + vpfe_video_unregister(&vpfe_rsz->resizer_a.video_out); + vpfe_video_unregister(&vpfe_rsz->resizer_b.video_out); + + /* cleanup entity */ + media_entity_cleanup(&vpfe_rsz->crop_resizer.subdev.entity); + media_entity_cleanup(&vpfe_rsz->resizer_a.subdev.entity); + media_entity_cleanup(&vpfe_rsz->resizer_b.subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&vpfe_rsz->crop_resizer.subdev); + v4l2_device_unregister_subdev(&vpfe_rsz->resizer_a.subdev); + v4l2_device_unregister_subdev(&vpfe_rsz->resizer_b.subdev); +} + +/* + * vpfe_resizer_register_entities() - Register entity + * @resizer - pointer to resizer devive. + * @vdev: pointer to v4l2 device structure. + */ +int vpfe_resizer_register_entities(struct vpfe_resizer_device *resizer, + struct v4l2_device *vdev) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + unsigned int flags = 0; + int ret; + + /* Register the crop resizer subdev */ + ret = v4l2_device_register_subdev(vdev, &resizer->crop_resizer.subdev); + if (ret < 0) { + pr_err("Failed to register crop resizer as v4l2-subdev\n"); + return ret; + } + /* Register Resizer-A subdev */ + ret = v4l2_device_register_subdev(vdev, &resizer->resizer_a.subdev); + if (ret < 0) { + pr_err("Failed to register resizer-a as v4l2-subdev\n"); + return ret; + } + /* Register Resizer-B subdev */ + ret = v4l2_device_register_subdev(vdev, &resizer->resizer_b.subdev); + if (ret < 0) { + pr_err("Failed to register resizer-b as v4l2-subdev\n"); + return ret; + } + /* Register video-out device for resizer-a */ + ret = vpfe_video_register(&resizer->resizer_a.video_out, vdev); + if (ret) { + pr_err("Failed to register RSZ-A video-out device\n"); + goto out_video_out2_register; + } + resizer->resizer_a.video_out.vpfe_dev = vpfe_dev; + + /* Register video-out device for resizer-b */ + ret = vpfe_video_register(&resizer->resizer_b.video_out, vdev); + if (ret) { + pr_err("Failed to register RSZ-B video-out device\n"); + goto out_video_out2_register; + } + resizer->resizer_b.video_out.vpfe_dev = vpfe_dev; + + /* create link between Resizer Crop----> Resizer A*/ + ret = media_entity_create_link(&resizer->crop_resizer.subdev.entity, 1, + &resizer->resizer_a.subdev.entity, + 0, flags); + if (ret < 0) + goto out_create_link; + + /* create link between Resizer Crop----> Resizer B*/ + ret = media_entity_create_link(&resizer->crop_resizer.subdev.entity, 2, + &resizer->resizer_b.subdev.entity, + 0, flags); + if (ret < 0) + goto out_create_link; + + /* create link between Resizer A ----> video out */ + ret = media_entity_create_link(&resizer->resizer_a.subdev.entity, 1, + &resizer->resizer_a.video_out.video_dev.entity, 0, flags); + if (ret < 0) + goto out_create_link; + + /* create link between Resizer B ----> video out */ + ret = media_entity_create_link(&resizer->resizer_b.subdev.entity, 1, + &resizer->resizer_b.video_out.video_dev.entity, 0, flags); + if (ret < 0) + goto out_create_link; + + return 0; + +out_create_link: + vpfe_video_unregister(&resizer->resizer_b.video_out); +out_video_out2_register: + vpfe_video_unregister(&resizer->resizer_a.video_out); + media_entity_cleanup(&resizer->crop_resizer.subdev.entity); + media_entity_cleanup(&resizer->resizer_a.subdev.entity); + media_entity_cleanup(&resizer->resizer_b.subdev.entity); + v4l2_device_unregister_subdev(&resizer->crop_resizer.subdev); + v4l2_device_unregister_subdev(&resizer->resizer_a.subdev); + v4l2_device_unregister_subdev(&resizer->resizer_b.subdev); + return ret; +} + +/* + * vpfe_resizer_init() - resizer device initialization. + * @vpfe_rsz - pointer to resizer device + * @pdev: platform device pointer. + */ +int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, + struct platform_device *pdev) +{ + struct v4l2_subdev *sd = &vpfe_rsz->crop_resizer.subdev; + struct media_pad *pads = &vpfe_rsz->crop_resizer.pads[0]; + struct media_entity *me = &sd->entity; + static resource_size_t res_len; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 5); + if (!res) + return -ENOENT; + + res_len = resource_size(res); + res = request_mem_region(res->start, res_len, res->name); + if (!res) + return -EBUSY; + + vpfe_rsz->base_addr = ioremap_nocache(res->start, res_len); + if (!vpfe_rsz->base_addr) + return -EBUSY; + + v4l2_subdev_init(sd, &resizer_v4l2_ops); + sd->internal_ops = &resizer_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI RESIZER CROP", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, vpfe_rsz); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[RESIZER_CROP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[RESIZER_CROP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + pads[RESIZER_CROP_PAD_SOURCE2].flags = MEDIA_PAD_FL_SOURCE; + + vpfe_rsz->crop_resizer.input = RESIZER_CROP_INPUT_NONE; + vpfe_rsz->crop_resizer.output = RESIZER_CROP_OUTPUT_NONE; + vpfe_rsz->crop_resizer.output2 = RESIZER_CROP_OUTPUT_NONE; + vpfe_rsz->crop_resizer.rsz_device = vpfe_rsz; + me->ops = &resizer_media_ops; + ret = media_entity_init(me, RESIZER_CROP_PADS_NUM, pads, 0); + if (ret) + return ret; + + sd = &vpfe_rsz->resizer_a.subdev; + pads = &vpfe_rsz->resizer_a.pads[0]; + me = &sd->entity; + + v4l2_subdev_init(sd, &resizer_v4l2_ops); + sd->internal_ops = &resizer_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI RESIZER A", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, vpfe_rsz); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[RESIZER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + vpfe_rsz->resizer_a.input = RESIZER_INPUT_NONE; + vpfe_rsz->resizer_a.output = RESIZER_OUTPUT_NONE; + vpfe_rsz->resizer_a.rsz_device = vpfe_rsz; + me->ops = &resizer_media_ops; + ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0); + if (ret) + return ret; + + sd = &vpfe_rsz->resizer_b.subdev; + pads = &vpfe_rsz->resizer_b.pads[0]; + me = &sd->entity; + + v4l2_subdev_init(sd, &resizer_v4l2_ops); + sd->internal_ops = &resizer_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI RESIZER B", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, vpfe_rsz); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[RESIZER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + vpfe_rsz->resizer_b.input = RESIZER_INPUT_NONE; + vpfe_rsz->resizer_b.output = RESIZER_OUTPUT_NONE; + vpfe_rsz->resizer_b.rsz_device = vpfe_rsz; + me->ops = &resizer_media_ops; + ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0); + if (ret) + return ret; + + vpfe_rsz->resizer_a.video_out.ops = &resizer_a_video_ops; + vpfe_rsz->resizer_a.video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = vpfe_video_init(&vpfe_rsz->resizer_a.video_out, "RSZ-A"); + if (ret) { + pr_err("Failed to init RSZ video-out device\n"); + return ret; + } + vpfe_rsz->resizer_b.video_out.ops = &resizer_b_video_ops; + vpfe_rsz->resizer_b.video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = vpfe_video_init(&vpfe_rsz->resizer_b.video_out, "RSZ-B"); + if (ret) { + pr_err("Failed to init RSZ video-out2 device\n"); + return ret; + } + memset(&vpfe_rsz->config, 0, sizeof(struct resizer_params)); + + return 0; +} + +void +vpfe_resizer_cleanup(struct vpfe_resizer_device *vpfe_rsz, + struct platform_device *pdev) +{ + struct resource *res; + + iounmap(vpfe_rsz->base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 5); + if (res) + release_mem_region(res->start, + res->end - res->start + 1); +} diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.h b/drivers/staging/media/davinci_vpfe/dm365_resizer.h new file mode 100644 index 000000000000..59a79422b914 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.h @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _DAVINCI_VPFE_DM365_RESIZER_H +#define _DAVINCI_VPFE_DM365_RESIZER_H + +enum resizer_oper_mode { + RESIZER_MODE_CONTINIOUS = 0, + RESIZER_MODE_ONE_SHOT = 1, +}; + +struct f_div_pass { + unsigned int o_hsz; + unsigned int i_hps; + unsigned int h_phs; + unsigned int src_hps; + unsigned int src_hsz; +}; + +#define MAX_PASSES 2 + +struct f_div_param { + unsigned char en; + unsigned int num_passes; + struct f_div_pass pass[MAX_PASSES]; +}; + +/* Resizer Rescale Parameters*/ +struct resizer_scale_param { + bool h_flip; + bool v_flip; + bool cen; + bool yen; + unsigned short i_vps; + unsigned short i_hps; + unsigned short o_vsz; + unsigned short o_hsz; + unsigned short v_phs_y; + unsigned short v_phs_c; + unsigned short v_dif; + /* resize method - Luminance */ + enum vpfe_rsz_intp_t v_typ_y; + /* resize method - Chrominance */ + enum vpfe_rsz_intp_t v_typ_c; + /* vertical lpf intensity - Luminance */ + unsigned char v_lpf_int_y; + /* vertical lpf intensity - Chrominance */ + unsigned char v_lpf_int_c; + unsigned short h_phs; + unsigned short h_dif; + /* resize method - Luminance */ + enum vpfe_rsz_intp_t h_typ_y; + /* resize method - Chrominance */ + enum vpfe_rsz_intp_t h_typ_c; + /* horizontal lpf intensity - Luminance */ + unsigned char h_lpf_int_y; + /* horizontal lpf intensity - Chrominance */ + unsigned char h_lpf_int_c; + bool dscale_en; + enum vpfe_rsz_down_scale_ave_sz h_dscale_ave_sz; + enum vpfe_rsz_down_scale_ave_sz v_dscale_ave_sz; + /* store the calculated frame division parameter */ + struct f_div_param f_div; +}; + +enum resizer_rgb_t { + OUTPUT_32BIT, + OUTPUT_16BIT +}; + +enum resizer_rgb_msk_t { + NOMASK = 0, + MASKLAST2 = 1, +}; + +/* Resizer RGB Conversion Parameters */ +struct resizer_rgb { + bool rgb_en; + enum resizer_rgb_t rgb_typ; + enum resizer_rgb_msk_t rgb_msk0; + enum resizer_rgb_msk_t rgb_msk1; + unsigned int rgb_alpha_val; +}; + +/* Resizer External Memory Parameters */ +struct rsz_ext_mem_param { + unsigned int rsz_sdr_oft_y; + unsigned int rsz_sdr_ptr_s_y; + unsigned int rsz_sdr_ptr_e_y; + unsigned int rsz_sdr_oft_c; + unsigned int rsz_sdr_ptr_s_c; + unsigned int rsz_sdr_ptr_e_c; + /* offset to be added to buffer start when flipping for y/ycbcr */ + unsigned int flip_ofst_y; + /* offset to be added to buffer start when flipping for c */ + unsigned int flip_ofst_c; + /* c offset for YUV 420SP */ + unsigned int c_offset; + /* User Defined Y offset for YUV 420SP or YUV420ILE data */ + unsigned int user_y_ofst; + /* User Defined C offset for YUV 420SP data */ + unsigned int user_c_ofst; +}; + +enum rsz_data_source { + IPIPE_DATA, + IPIPEIF_DATA +}; + +enum rsz_src_img_fmt { + RSZ_IMG_422, + RSZ_IMG_420 +}; + +enum rsz_dpaths_bypass_t { + BYPASS_OFF = 0, + BYPASS_ON = 1, +}; + +struct rsz_common_params { + unsigned int vps; + unsigned int vsz; + unsigned int hps; + unsigned int hsz; + /* 420 or 422 */ + enum rsz_src_img_fmt src_img_fmt; + /* Y or C when src_fmt is 420, 0 - y, 1 - c */ + unsigned char y_c; + /* flip raw or ycbcr */ + unsigned char raw_flip; + /* IPIPE or IPIPEIF data */ + enum rsz_data_source source; + enum rsz_dpaths_bypass_t passthrough; + unsigned char yuv_y_min; + unsigned char yuv_y_max; + unsigned char yuv_c_min; + unsigned char yuv_c_max; + bool rsz_seq_crv; + enum vpfe_chr_pos out_chr_pos; +}; + +struct resizer_params { + enum resizer_oper_mode oper_mode; + struct rsz_common_params rsz_common; + struct resizer_scale_param rsz_rsc_param[2]; + struct resizer_rgb rsz2rgb[2]; + struct rsz_ext_mem_param ext_mem_param[2]; + bool rsz_en[2]; + struct vpfe_rsz_config_params user_config; +}; + +#define ENABLE 1 +#define DISABLE (!ENABLE) + +#define RESIZER_CROP_PAD_SINK 0 +#define RESIZER_CROP_PAD_SOURCE 1 +#define RESIZER_CROP_PAD_SOURCE2 2 + +#define RESIZER_CROP_PADS_NUM 3 + +enum resizer_crop_input_entity { + RESIZER_CROP_INPUT_NONE = 0, + RESIZER_CROP_INPUT_IPIPEIF = 1, + RESIZER_CROP_INPUT_IPIPE = 2, +}; + +enum resizer_crop_output_entity { + RESIZER_CROP_OUTPUT_NONE, + RESIZER_A, + RESIZER_B, +}; + +struct dm365_crop_resizer_device { + struct v4l2_subdev subdev; + struct media_pad pads[RESIZER_CROP_PADS_NUM]; + struct v4l2_mbus_framefmt formats[RESIZER_CROP_PADS_NUM]; + enum resizer_crop_input_entity input; + enum resizer_crop_output_entity output; + enum resizer_crop_output_entity output2; + struct vpfe_resizer_device *rsz_device; +}; + +#define RESIZER_PAD_SINK 0 +#define RESIZER_PAD_SOURCE 1 + +#define RESIZER_PADS_NUM 2 + +enum resizer_input_entity { + RESIZER_INPUT_NONE = 0, + RESIZER_INPUT_CROP_RESIZER = 1, +}; + +enum resizer_output_entity { + RESIZER_OUTPUT_NONE = 0, + RESIZER_OUPUT_MEMORY = 1, +}; + +struct dm365_resizer_device { + struct v4l2_subdev subdev; + struct media_pad pads[RESIZER_PADS_NUM]; + struct v4l2_mbus_framefmt formats[RESIZER_PADS_NUM]; + enum resizer_input_entity input; + enum resizer_output_entity output; + struct vpfe_video_device video_out; + struct vpfe_resizer_device *rsz_device; +}; + +struct vpfe_resizer_device { + struct dm365_crop_resizer_device crop_resizer; + struct dm365_resizer_device resizer_a; + struct dm365_resizer_device resizer_b; + struct resizer_params config; + void *__iomem base_addr; +}; + +int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, + struct platform_device *pdev); +int vpfe_resizer_register_entities(struct vpfe_resizer_device *vpfe_rsz, + struct v4l2_device *v4l2_dev); +void vpfe_resizer_unregister_entities(struct vpfe_resizer_device *vpfe_rsz); +void vpfe_resizer_cleanup(struct vpfe_resizer_device *vpfe_rsz, + struct platform_device *pdev); +void vpfe_resizer_buffer_isr(struct vpfe_resizer_device *resizer); +void vpfe_resizer_dma_isr(struct vpfe_resizer_device *resizer); + +#endif /* _DAVINCI_VPFE_DM365_RESIZER_H */ -- cgit v1.2.3 From 44261e38059385ca1cd9c5018c579b4fc48b5a52 Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Wed, 28 Nov 2012 02:26:17 -0300 Subject: [media] davinci: vpfe: dm365: add build infrastructure for capture driver add build infrastructure for dm365 specific modules for VPFE capture driver. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/Kconfig | 2 ++ drivers/staging/media/Makefile | 1 + drivers/staging/media/davinci_vpfe/Kconfig | 9 +++++++++ drivers/staging/media/davinci_vpfe/Makefile | 3 +++ 4 files changed, 15 insertions(+) create mode 100644 drivers/staging/media/davinci_vpfe/Kconfig create mode 100644 drivers/staging/media/davinci_vpfe/Makefile (limited to 'drivers') diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 427218b8b10f..ae0abc350e34 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -23,6 +23,8 @@ source "drivers/staging/media/as102/Kconfig" source "drivers/staging/media/cxd2099/Kconfig" +source "drivers/staging/media/davinci_vpfe/Kconfig" + source "drivers/staging/media/dt3155v4l/Kconfig" source "drivers/staging/media/go7007/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index aec6eb963940..2b97cae99499 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_SOLO6X10) += solo6x10/ obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ +obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ diff --git a/drivers/staging/media/davinci_vpfe/Kconfig b/drivers/staging/media/davinci_vpfe/Kconfig new file mode 100644 index 000000000000..2e4a28b018e8 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/Kconfig @@ -0,0 +1,9 @@ +config VIDEO_DM365_VPFE + tristate "DM365 VPFE Media Controller Capture Driver" + depends on VIDEO_V4L2 && ARCH_DAVINCI_DM365 && !VIDEO_VPFE_CAPTURE + select VIDEOBUF2_DMA_CONTIG + help + Support for DM365 VPFE based Media Controller Capture driver. + + To compile this driver as a module, choose M here: the + module will be called vpfe-mc-capture. diff --git a/drivers/staging/media/davinci_vpfe/Makefile b/drivers/staging/media/davinci_vpfe/Makefile new file mode 100644 index 000000000000..c64515c644cd --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_VIDEO_DM365_VPFE) += \ + dm365_isif.o dm365_ipipe_hw.o dm365_ipipe.o \ + dm365_resizer.o dm365_ipipeif.o vpfe_mc_capture.o vpfe_video.o -- cgit v1.2.3 From 5a89fac7e90dd75b9783914fa351069d20fd8c54 Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Wed, 28 Nov 2012 02:29:46 -0300 Subject: [media] davinci: vpfe: Add documentation and TODO Add documentation on the Davinci VPFE driver. Document the subdevs, and private IOTCLs the driver implements. This patch also includes the TODO's to fit into drivers/media/ folder. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/davinci_vpfe/TODO | 37 +++++ .../staging/media/davinci_vpfe/davinci-vpfe-mc.txt | 154 +++++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 drivers/staging/media/davinci_vpfe/TODO create mode 100644 drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt (limited to 'drivers') diff --git a/drivers/staging/media/davinci_vpfe/TODO b/drivers/staging/media/davinci_vpfe/TODO new file mode 100644 index 000000000000..7015ab35ded5 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/TODO @@ -0,0 +1,37 @@ +TODO (general): +================================== + +- User space interface refinement + - Controls should be used when possible rather than private ioctl + - No enums should be used + - Use of MC and V4L2 subdev APIs when applicable + - Single interface header might suffice + - Current interface forces to configure everything at once +- Get rid of the dm365_ipipe_hw.[ch] layer +- Active external sub-devices defined by link configuration; no strcmp + needed +- More generic platform data (i2c adapters) +- The driver should have no knowledge of possible external subdevs; see + struct vpfe_subdev_id +- Some of the hardware control should be refactorede +- Check proper serialisation (through mutexes and spinlocks) +- Names that are visible in kernel global namespace should have a common + prefix (or a few) +- While replacing the older driver in media folder, provide a compatibility + layer and compatibility tests that warrants (using the libv4l's LD_PRELOAD + approach) there is no regression for the users using the older driver. + +Building of uImage and Applications: +================================== + +As of now since the interface will undergo few changes all the include +files are present in staging itself, to build for dm365 follow below steps, + +- copy vpfe.h from drivers/staging/media/davinci_vpfe/ to + include/media/davinci/ folder for building the uImage. +- copy davinci_vpfe_user.h from drivers/staging/media/davinci_vpfe/ to + include/uapi/linux/davinci_vpfe.h, and add a entry in Kbuild (required + for building application). +- copy dm365_ipipeif_user.h from drivers/staging/media/davinci_vpfe/ to + include/uapi/linux/dm365_ipipeif.h and a entry in Kbuild (required + for building application). diff --git a/drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt b/drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt new file mode 100644 index 000000000000..1dbd56418704 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt @@ -0,0 +1,154 @@ +Davinci Video processing Front End (VPFE) driver + +Copyright (C) 2012 Texas Instruments Inc + +Contacts: Manjunath Hadli + Prabhakar Lad + + +Introduction +============ + +This file documents the Texas Instruments Davinci Video processing Front End +(VPFE) driver located under drivers/media/platform/davinci. The original driver +exists for Davinci VPFE, which is now being changed to Media Controller +Framework. + +Currently the driver has been successfully used on the following +version of Davinci: + + DM365/DM368 + +The driver implements V4L2, Media controller and v4l2_subdev interfaces. Sensor, +lens and flash drivers using the v4l2_subdev interface in the kernel are +supported. + + +Split to subdevs +================ + +The Davinci VPFE is split into V4L2 subdevs, each of the blocks inside the VPFE +having one subdev to represent it. Each of the subdevs provide a V4L2 subdev +interface to userspace. + + DAVINCI ISIF + DAVINCI IPIPEIF + DAVINCI IPIPE + DAVINCI CROP RESIZER + DAVINCI RESIZER A + DAVINCI RESIZER B + +Each possible link in the VPFE is modeled by a link in the Media controller +interface. For an example program see [1]. + + +ISIF, IPIPE, and RESIZER block IOCTLs +====================================== + +The Davinci Video processing Front End (VPFE) driver supports standard V4L2 +IOCTLs and controls where possible and practical. Much of the functions provided +by the VPFE, however, does not fall under the standard IOCTL's. + +In general, there is a private ioctl for configuring each of the blocks +containing hardware-dependent functions. + +The following private IOCTLs are supported: + + VIDIOC_VPFE_ISIF_[S/G]_RAW_PARAMS + VIDIOC_VPFE_IPIPE_[S/G]_CONFIG + VIDIOC_VPFE_RSZ_[S/G]_CONFIG + +The parameter structures used by these ioctl's are described in +include/uapi/linux/davinci_vpfe.h. + +The VIDIOC_VPFE_ISIF_S_RAW_PARAMS, VIDIOC_VPFE_IPIPE_S_CONFIG and +VIDIOC_VPFE_RSZ_S_CONFIG are used to configure, enable and disable functions in +the isif, ipipe and resizer blocks respectively. These IOCTL's control several +functions in the blocks they control. VIDIOC_VPFE_ISIF_S_RAW_PARAMS IOCTL +accepts a pointer to struct vpfe_isif_raw_config as its argument. Similarly +VIDIOC_VPFE_IPIPE_S_CONFIG accepts a pointer to struct vpfe_ipipe_config. And +VIDIOC_VPFE_RSZ_S_CONFIG accepts a pointer to struct vpfe_rsz_config as its +argument. Similarly VIDIOC_VPFE_ISIF_G_RAW_PARAMS, VIDIOC_VPFE_IPIPE_G_CONFIG +and VIDIOC_VPFE_RSZ_G_CONFIG are used to get the current configuration set in +the isif, ipipe and resizer blocks respectively. + +The detailed functions of the VPFE itself related to a given VPFE block is +described in the Technical Reference Manuals (TRMs) --- see the end of the +document for those. + + +IPIPEIF block IOCTLs +====================================== + +The following private IOCTLs are supported: + + VIDIOC_VPFE_IPIPEIF_[S/G]_CONFIG + +The parameter structures used by these ioctl's are described in +include/uapi/linux/dm365_ipipeif.h + +The VIDIOC_VPFE_IPIPEIF_S_CONFIG is used to configure the ipipeif +hardware block. The VIDIOC_VPFE_IPIPEIF_S_CONFIG and +VIDIOC_VPFE_IPIPEIF_G_CONFIG accepts a pointer to struct ipipeif_params +as its argument. + + +VPFE Operating Modes +========================================== + +a: Continuous Modes +------------------------ + +1: tvp514x/tvp7002/mt9p031---> DAVINCI ISIF---> SDRAM + +2: tvp514x/tvp7002/mt9p031---> DAVINCI ISIF---> DAVINCI IPIPEIF--->| + | + <--------------------<----------------<---------------------<---| + | + V + DAVINCI CROP RESIZER--->DAVINCI RESIZER [A/B]---> SDRAM + +3: tvp514x/tvp7002/mt9p031---> DAVINCI ISIF---> DAVINCI IPIPEIF--->| + | + <--------------------<----------------<---------------------<---| + | + V + DAVINCI IPIPE---> DAVINCI CROP RESIZER--->DAVINCI RESIZER [A/B]---> SDRAM + +a: Single Shot Modes +------------------------ + +1: SDRAM---> DAVINCI IPIPEIF---> DAVINCI IPIPE---> DAVINCI CROP RESIZER--->| + | + <----------------<----------------<------------------<---------------<--| + | + V +DAVINCI RESIZER [A/B]---> SDRAM + +2: SDRAM---> DAVINCI IPIPEIF---> DAVINCI CROP RESIZER--->| + | + <----------------<----------------<---------------<---| + | + V +DAVINCI RESIZER [A/B]---> SDRAM + + +Technical reference manuals (TRMs) and other documentation +========================================================== + +Davinci DM365 TRM: + +Referenced MARCH 2009-REVISED JUNE 2011 + +Davinci DM368 TRM: + +Referenced APRIL 2010-REVISED JUNE 2011 + +Davinci Video Processing Front End (VPFE) DM36x + + + +References +========== + +[1] http://git.ideasonboard.org/?p=media-ctl.git;a=summary -- cgit v1.2.3 From caff80c35f923806b7e5ef312dce41663b5e99b9 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Tue, 20 Nov 2012 07:30:36 -0300 Subject: [media] davinci: vpbe: pass different platform names to handle different ip's The vpbe driver can handle different platforms DM644X, DM36X and DM355. To differentiate between this platforms venc_type/vpbe_type was passed as part of platform data which was incorrect. The correct way to differentiate to handle this case is by passing different platform names. This patch creates platform_device_id[] array supporting different platforms and assigns id_table to the platform driver, and finally in the probe gets the actual device by using platform_get_device_id() and gets the appropriate driver data for that platform. Taking this approach will also make the DT transition easier. Signed-off-by: Lad, Prabhakar Signed-off-by: Manjunath Hadli Acked-by: Sekhar Nori Signed-off-by: Mauro Carvalho Chehab --- arch/arm/mach-davinci/board-dm644x-evm.c | 8 ++-- arch/arm/mach-davinci/dm644x.c | 10 +---- drivers/media/platform/davinci/vpbe.c | 4 +- drivers/media/platform/davinci/vpbe_display.c | 2 +- drivers/media/platform/davinci/vpbe_osd.c | 35 ++++++++++----- drivers/media/platform/davinci/vpbe_venc.c | 65 ++++++++++++++++++--------- include/media/davinci/vpbe_osd.h | 5 ++- include/media/davinci/vpbe_venc.h | 5 ++- 8 files changed, 84 insertions(+), 50 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index f22572cee49d..b00ade498077 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -689,7 +689,7 @@ static struct vpbe_output dm644xevm_vpbe_outputs[] = { .std = VENC_STD_ALL, .capabilities = V4L2_OUT_CAP_STD, }, - .subdev_name = VPBE_VENC_SUBDEV_NAME, + .subdev_name = DM644X_VPBE_VENC_SUBDEV_NAME, .default_mode = "ntsc", .num_modes = ARRAY_SIZE(dm644xevm_enc_std_timing), .modes = dm644xevm_enc_std_timing, @@ -701,7 +701,7 @@ static struct vpbe_output dm644xevm_vpbe_outputs[] = { .type = V4L2_OUTPUT_TYPE_ANALOG, .capabilities = V4L2_OUT_CAP_DV_TIMINGS, }, - .subdev_name = VPBE_VENC_SUBDEV_NAME, + .subdev_name = DM644X_VPBE_VENC_SUBDEV_NAME, .default_mode = "480p59_94", .num_modes = ARRAY_SIZE(dm644xevm_enc_preset_timing), .modes = dm644xevm_enc_preset_timing, @@ -712,10 +712,10 @@ static struct vpbe_config dm644xevm_display_cfg = { .module_name = "dm644x-vpbe-display", .i2c_adapter_id = 1, .osd = { - .module_name = VPBE_OSD_SUBDEV_NAME, + .module_name = DM644X_VPBE_OSD_SUBDEV_NAME, }, .venc = { - .module_name = VPBE_VENC_SUBDEV_NAME, + .module_name = DM644X_VPBE_VENC_SUBDEV_NAME, }, .num_outputs = ARRAY_SIZE(dm644xevm_vpbe_outputs), .outputs = dm644xevm_vpbe_outputs, diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 14e9947bad6e..0849d5768d99 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -669,19 +669,14 @@ static struct resource dm644x_osd_resources[] = { }, }; -static struct osd_platform_data dm644x_osd_data = { - .vpbe_type = VPBE_VERSION_1, -}; - static struct platform_device dm644x_osd_dev = { - .name = VPBE_OSD_SUBDEV_NAME, + .name = DM644X_VPBE_OSD_SUBDEV_NAME, .id = -1, .num_resources = ARRAY_SIZE(dm644x_osd_resources), .resource = dm644x_osd_resources, .dev = { .dma_mask = &dm644x_video_dma_mask, .coherent_dma_mask = DMA_BIT_MASK(32), - .platform_data = &dm644x_osd_data, }, }; @@ -751,12 +746,11 @@ static struct platform_device dm644x_vpbe_display = { }; static struct venc_platform_data dm644x_venc_pdata = { - .venc_type = VPBE_VERSION_1, .setup_clock = dm644x_venc_setup_clock, }; static struct platform_device dm644x_venc_dev = { - .name = VPBE_VENC_SUBDEV_NAME, + .name = DM644X_VPBE_VENC_SUBDEV_NAME, .id = -1, .num_resources = ARRAY_SIZE(dm644x_venc_resources), .resource = dm644x_venc_resources, diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c index 7f5cf9b347b2..dd670cdb2c2a 100644 --- a/drivers/media/platform/davinci/vpbe.c +++ b/drivers/media/platform/davinci/vpbe.c @@ -558,9 +558,9 @@ static int platform_device_get(struct device *dev, void *data) struct platform_device *pdev = to_platform_device(dev); struct vpbe_device *vpbe_dev = data; - if (strcmp("vpbe-osd", pdev->name) == 0) + if (strstr(pdev->name, "vpbe-osd") != NULL) vpbe_dev->osd_device = platform_get_drvdata(pdev); - if (strcmp("vpbe-venc", pdev->name) == 0) + if (strstr(pdev->name, "vpbe-venc") != NULL) vpbe_dev->venc_device = dev_get_platdata(&pdev->dev); return 0; diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 2bfde7958fef..3846890ea707 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -1656,7 +1656,7 @@ static int vpbe_device_get(struct device *dev, void *data) if (strcmp("vpbe_controller", pdev->name) == 0) vpbe_disp->vpbe_dev = platform_get_drvdata(pdev); - if (strcmp("vpbe-osd", pdev->name) == 0) + if (strstr(pdev->name, "vpbe-osd") != NULL) vpbe_disp->osd_device = platform_get_drvdata(pdev); return 0; diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c index 707f243f810d..12ad17c52ef3 100644 --- a/drivers/media/platform/davinci/vpbe_osd.c +++ b/drivers/media/platform/davinci/vpbe_osd.c @@ -39,7 +39,22 @@ #include #include "vpbe_osd_regs.h" -#define MODULE_NAME VPBE_OSD_SUBDEV_NAME +#define MODULE_NAME "davinci-vpbe-osd" + +static struct platform_device_id vpbe_osd_devtype[] = { + { + .name = DM644X_VPBE_OSD_SUBDEV_NAME, + .driver_data = VPBE_VERSION_1, + }, { + .name = DM365_VPBE_OSD_SUBDEV_NAME, + .driver_data = VPBE_VERSION_2, + }, { + .name = DM355_VPBE_OSD_SUBDEV_NAME, + .driver_data = VPBE_VERSION_3, + }, +}; + +MODULE_DEVICE_TABLE(platform, vpbe_osd_devtype); /* register access routines */ static inline u32 osd_read(struct osd_state *sd, u32 offset) @@ -129,7 +144,7 @@ static int _osd_dm6446_vid0_pingpong(struct osd_state *sd, struct osd_platform_data *pdata; pdata = (struct osd_platform_data *)sd->dev->platform_data; - if (pdata->field_inv_wa_enable) { + if (pdata != NULL && pdata->field_inv_wa_enable) { if (!field_inversion || !lconfig->interlaced) { osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR); @@ -1526,7 +1541,7 @@ static const struct vpbe_osd_ops osd_ops = { static int osd_probe(struct platform_device *pdev) { - struct osd_platform_data *pdata; + const struct platform_device_id *pdev_id; struct osd_state *osd; struct resource *res; int ret = 0; @@ -1535,16 +1550,15 @@ static int osd_probe(struct platform_device *pdev) if (osd == NULL) return -ENOMEM; - osd->dev = &pdev->dev; - pdata = (struct osd_platform_data *)pdev->dev.platform_data; - osd->vpbe_type = (enum vpbe_version)pdata->vpbe_type; - if (NULL == pdev->dev.platform_data) { - dev_err(osd->dev, "No platform data defined for OSD" - " sub device\n"); - ret = -ENOENT; + pdev_id = platform_get_device_id(pdev); + if (!pdev_id) { + ret = -EINVAL; goto free_mem; } + osd->dev = &pdev->dev; + osd->vpbe_type = pdev_id->driver_data; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(osd->dev, "Unable to get OSD register address map\n"); @@ -1595,6 +1609,7 @@ static struct platform_driver osd_driver = { .name = MODULE_NAME, .owner = THIS_MODULE, }, + .id_table = vpbe_osd_devtype }; module_platform_driver(osd_driver); diff --git a/drivers/media/platform/davinci/vpbe_venc.c b/drivers/media/platform/davinci/vpbe_venc.c index aed7369b962a..bdbebd59df98 100644 --- a/drivers/media/platform/davinci/vpbe_venc.c +++ b/drivers/media/platform/davinci/vpbe_venc.c @@ -38,7 +38,22 @@ #include "vpbe_venc_regs.h" -#define MODULE_NAME VPBE_VENC_SUBDEV_NAME +#define MODULE_NAME "davinci-vpbe-venc" + +static struct platform_device_id vpbe_venc_devtype[] = { + { + .name = DM644X_VPBE_VENC_SUBDEV_NAME, + .driver_data = VPBE_VERSION_1, + }, { + .name = DM365_VPBE_VENC_SUBDEV_NAME, + .driver_data = VPBE_VERSION_2, + }, { + .name = DM355_VPBE_VENC_SUBDEV_NAME, + .driver_data = VPBE_VERSION_3, + }, +}; + +MODULE_DEVICE_TABLE(platform, vpbe_venc_devtype); static int debug = 2; module_param(debug, int, 0644); @@ -54,6 +69,7 @@ struct venc_state { spinlock_t lock; void __iomem *venc_base; void __iomem *vdaccfg_reg; + enum vpbe_version venc_type; }; static inline struct venc_state *to_state(struct v4l2_subdev *sd) @@ -127,7 +143,7 @@ static int venc_set_dac(struct v4l2_subdev *sd, u32 out_index) static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable) { struct venc_state *venc = to_state(sd); - struct venc_platform_data *pdata = venc->pdata; + v4l2_dbg(debug, 2, sd, "venc_enabledigitaloutput\n"); if (benable) { @@ -159,7 +175,7 @@ static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable) /* Disable LCD output control (accepting default polarity) */ venc_write(sd, VENC_LCDOUT, 0); - if (pdata->venc_type != VPBE_VERSION_3) + if (venc->venc_type != VPBE_VERSION_3) venc_write(sd, VENC_CMPNT, 0x100); venc_write(sd, VENC_HSPLS, 0); venc_write(sd, VENC_HINT, 0); @@ -203,11 +219,11 @@ static int venc_set_ntsc(struct v4l2_subdev *sd) venc_enabledigitaloutput(sd, 0); - if (pdata->venc_type == VPBE_VERSION_3) { + if (venc->venc_type == VPBE_VERSION_3) { venc_write(sd, VENC_CLKCTL, 0x01); venc_write(sd, VENC_VIDCTL, 0); val = vdaccfg_write(sd, VDAC_CONFIG_SD_V3); - } else if (pdata->venc_type == VPBE_VERSION_2) { + } else if (venc->venc_type == VPBE_VERSION_2) { venc_write(sd, VENC_CLKCTL, 0x01); venc_write(sd, VENC_VIDCTL, 0); vdaccfg_write(sd, VDAC_CONFIG_SD_V2); @@ -238,7 +254,6 @@ static int venc_set_ntsc(struct v4l2_subdev *sd) static int venc_set_pal(struct v4l2_subdev *sd) { struct venc_state *venc = to_state(sd); - struct venc_platform_data *pdata = venc->pdata; v4l2_dbg(debug, 2, sd, "venc_set_pal\n"); @@ -249,11 +264,11 @@ static int venc_set_pal(struct v4l2_subdev *sd) venc_enabledigitaloutput(sd, 0); - if (pdata->venc_type == VPBE_VERSION_3) { + if (venc->venc_type == VPBE_VERSION_3) { venc_write(sd, VENC_CLKCTL, 0x1); venc_write(sd, VENC_VIDCTL, 0); vdaccfg_write(sd, VDAC_CONFIG_SD_V3); - } else if (pdata->venc_type == VPBE_VERSION_2) { + } else if (venc->venc_type == VPBE_VERSION_2) { venc_write(sd, VENC_CLKCTL, 0x1); venc_write(sd, VENC_VIDCTL, 0); vdaccfg_write(sd, VDAC_CONFIG_SD_V2); @@ -293,8 +308,8 @@ static int venc_set_480p59_94(struct v4l2_subdev *sd) struct venc_platform_data *pdata = venc->pdata; v4l2_dbg(debug, 2, sd, "venc_set_480p59_94\n"); - if ((pdata->venc_type != VPBE_VERSION_1) && - (pdata->venc_type != VPBE_VERSION_2)) + if (venc->venc_type != VPBE_VERSION_1 && + venc->venc_type != VPBE_VERSION_2) return -EINVAL; /* Setup clock at VPSS & VENC for SD */ @@ -303,12 +318,12 @@ static int venc_set_480p59_94(struct v4l2_subdev *sd) venc_enabledigitaloutput(sd, 0); - if (pdata->venc_type == VPBE_VERSION_2) + if (venc->venc_type == VPBE_VERSION_2) vdaccfg_write(sd, VDAC_CONFIG_HD_V2); venc_write(sd, VENC_OSDCLK0, 0); venc_write(sd, VENC_OSDCLK1, 1); - if (pdata->venc_type == VPBE_VERSION_1) { + if (venc->venc_type == VPBE_VERSION_1) { venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ, VENC_VDPRO_DAFRQ); venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS, @@ -341,8 +356,8 @@ static int venc_set_576p50(struct v4l2_subdev *sd) v4l2_dbg(debug, 2, sd, "venc_set_576p50\n"); - if ((pdata->venc_type != VPBE_VERSION_1) && - (pdata->venc_type != VPBE_VERSION_2)) + if (venc->venc_type != VPBE_VERSION_1 && + venc->venc_type != VPBE_VERSION_2) return -EINVAL; /* Setup clock at VPSS & VENC for SD */ if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 27000000) < 0) @@ -350,13 +365,13 @@ static int venc_set_576p50(struct v4l2_subdev *sd) venc_enabledigitaloutput(sd, 0); - if (pdata->venc_type == VPBE_VERSION_2) + if (venc->venc_type == VPBE_VERSION_2) vdaccfg_write(sd, VDAC_CONFIG_HD_V2); venc_write(sd, VENC_OSDCLK0, 0); venc_write(sd, VENC_OSDCLK1, 1); - if (pdata->venc_type == VPBE_VERSION_1) { + if (venc->venc_type == VPBE_VERSION_1) { venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ, VENC_VDPRO_DAFRQ); venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS, @@ -460,14 +475,14 @@ static int venc_s_dv_timings(struct v4l2_subdev *sd, else if (height == 480) return venc_set_480p59_94(sd); else if ((height == 720) && - (venc->pdata->venc_type == VPBE_VERSION_2)) { + (venc->venc_type == VPBE_VERSION_2)) { /* TBD setup internal 720p mode here */ ret = venc_set_720p60_internal(sd); /* for DM365 VPBE, there is DAC inside */ vdaccfg_write(sd, VDAC_CONFIG_HD_V2); return ret; } else if ((height == 1080) && - (venc->pdata->venc_type == VPBE_VERSION_2)) { + (venc->venc_type == VPBE_VERSION_2)) { /* TBD setup internal 1080i mode here */ ret = venc_set_1080i30_internal(sd); /* for DM365 VPBE, there is DAC inside */ @@ -556,7 +571,7 @@ static int venc_device_get(struct device *dev, void *data) struct platform_device *pdev = to_platform_device(dev); struct venc_state **venc = data; - if (strcmp(MODULE_NAME, pdev->name) == 0) + if (strstr(pdev->name, "vpbe-venc") != NULL) *venc = platform_get_drvdata(pdev); return 0; @@ -593,6 +608,7 @@ EXPORT_SYMBOL(venc_sub_dev_init); static int venc_probe(struct platform_device *pdev) { + const struct platform_device_id *pdev_id; struct venc_state *venc; struct resource *res; int ret; @@ -601,6 +617,12 @@ static int venc_probe(struct platform_device *pdev) if (venc == NULL) return -ENOMEM; + pdev_id = platform_get_device_id(pdev); + if (!pdev_id) { + ret = -EINVAL; + goto free_mem; + } + venc->venc_type = pdev_id->driver_data; venc->pdev = &pdev->dev; venc->pdata = pdev->dev.platform_data; if (NULL == venc->pdata) { @@ -630,7 +652,7 @@ static int venc_probe(struct platform_device *pdev) goto release_venc_mem_region; } - if (venc->pdata->venc_type != VPBE_VERSION_1) { + if (venc->venc_type != VPBE_VERSION_1) { res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) { dev_err(venc->pdev, @@ -681,7 +703,7 @@ static int venc_remove(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); iounmap((void *)venc->venc_base); release_mem_region(res->start, resource_size(res)); - if (venc->pdata->venc_type != VPBE_VERSION_1) { + if (venc->venc_type != VPBE_VERSION_1) { res = platform_get_resource(pdev, IORESOURCE_MEM, 1); iounmap((void *)venc->vdaccfg_reg); release_mem_region(res->start, resource_size(res)); @@ -698,6 +720,7 @@ static struct platform_driver venc_driver = { .name = MODULE_NAME, .owner = THIS_MODULE, }, + .id_table = vpbe_venc_devtype }; module_platform_driver(venc_driver); diff --git a/include/media/davinci/vpbe_osd.h b/include/media/davinci/vpbe_osd.h index 5ab0d8d41f68..42628fcfe1bd 100644 --- a/include/media/davinci/vpbe_osd.h +++ b/include/media/davinci/vpbe_osd.h @@ -26,7 +26,9 @@ #include -#define VPBE_OSD_SUBDEV_NAME "vpbe-osd" +#define DM644X_VPBE_OSD_SUBDEV_NAME "dm644x,vpbe-osd" +#define DM365_VPBE_OSD_SUBDEV_NAME "dm365,vpbe-osd" +#define DM355_VPBE_OSD_SUBDEV_NAME "dm355,vpbe-osd" /** * enum osd_layer @@ -387,7 +389,6 @@ struct osd_state { }; struct osd_platform_data { - enum vpbe_version vpbe_type; int field_inv_wa_enable; }; diff --git a/include/media/davinci/vpbe_venc.h b/include/media/davinci/vpbe_venc.h index cc78c2eb16da..476fafc2f522 100644 --- a/include/media/davinci/vpbe_venc.h +++ b/include/media/davinci/vpbe_venc.h @@ -20,7 +20,9 @@ #include #include -#define VPBE_VENC_SUBDEV_NAME "vpbe-venc" +#define DM644X_VPBE_VENC_SUBDEV_NAME "dm644x,vpbe-venc" +#define DM365_VPBE_VENC_SUBDEV_NAME "dm365,vpbe-venc" +#define DM355_VPBE_VENC_SUBDEV_NAME "dm355,vpbe-venc" /* venc events */ #define VENC_END_OF_FRAME BIT(0) @@ -28,7 +30,6 @@ #define VENC_SECOND_FIELD BIT(2) struct venc_platform_data { - enum vpbe_version venc_type; int (*setup_pinmux)(enum v4l2_mbus_pixelcode if_type, int field); int (*setup_clock)(enum vpbe_enc_timings_type type, -- cgit v1.2.3 From cfe9dbd8a76835abd33ba92060817e2699524b1e Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Wed, 28 Nov 2012 10:58:47 -0300 Subject: [media] media: davinci: vpbe: enable building of vpbe driver for DM355 and DM365 This patch allows enabling building of VPBE display driver for DM365 and DM355. This also removes unnecessary entry VIDEO_DM644X_VPBE in Kconfig, which could have been done with single entry, and appropriate changes in Makefile for building. Signed-off-by: Lad, Prabhakar Signed-off-by: Manjunath Hadli Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/Kconfig | 22 ++++++---------------- drivers/media/platform/davinci/Makefile | 4 ++-- 2 files changed, 8 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig index 3c56037c82fc..ccfde4eb626a 100644 --- a/drivers/media/platform/davinci/Kconfig +++ b/drivers/media/platform/davinci/Kconfig @@ -97,25 +97,15 @@ config VIDEO_ISIF To compile this driver as a module, choose M here: the module will be called vpfe. -config VIDEO_DM644X_VPBE - tristate "DM644X VPBE HW module" - depends on ARCH_DAVINCI_DM644x +config VIDEO_DAVINCI_VPBE_DISPLAY + tristate "DM644X/DM365/DM355 VPBE HW module" + depends on ARCH_DAVINCI_DM644x || ARCH_DAVINCI_DM355 || ARCH_DAVINCI_DM365 select VIDEO_VPSS_SYSTEM select VIDEOBUF2_DMA_CONTIG help - Enables VPBE modules used for display on a DM644x - SoC. + Enables Davinci VPBE module used for display devices. + This module is common for following DM644x/DM365/DM355 + based display devices. To compile this driver as a module, choose M here: the module will be called vpbe. - - -config VIDEO_VPBE_DISPLAY - tristate "VPBE V4L2 Display driver" - depends on ARCH_DAVINCI_DM644x - select VIDEO_DM644X_VPBE - help - Enables VPBE V4L2 Display driver on a DM644x device - - To compile this driver as a module, choose M here: the - module will be called vpbe_display. diff --git a/drivers/media/platform/davinci/Makefile b/drivers/media/platform/davinci/Makefile index 74ed92d09257..f40f5219ca50 100644 --- a/drivers/media/platform/davinci/Makefile +++ b/drivers/media/platform/davinci/Makefile @@ -16,5 +16,5 @@ obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o obj-$(CONFIG_VIDEO_ISIF) += isif.o -obj-$(CONFIG_VIDEO_DM644X_VPBE) += vpbe.o vpbe_osd.o vpbe_venc.o -obj-$(CONFIG_VIDEO_VPBE_DISPLAY) += vpbe_display.o +obj-$(CONFIG_VIDEO_DAVINCI_VPBE_DISPLAY) += vpbe.o vpbe_osd.o \ + vpbe_venc.o vpbe_display.o -- cgit v1.2.3 From 4d22f1086d24f61e271e1e84c0c27db4ad495e8f Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 2 Dec 2012 06:18:35 -0300 Subject: [media] media: davinci: vpbe: fix return value check in vpbe_display_reqbufs() In case of error, the function vb2_dma_contig_init_ctx() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). Signed-off-by: Wei Yongjun Acked-by: Prabhakar Lad Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpbe_display.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 3846890ea707..0723c4694ced 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -1393,9 +1393,9 @@ static int vpbe_display_reqbufs(struct file *file, void *priv, } /* Initialize videobuf queue as per the buffer type */ layer->alloc_ctx = vb2_dma_contig_init_ctx(vpbe_dev->pdev); - if (!layer->alloc_ctx) { + if (IS_ERR(layer->alloc_ctx)) { v4l2_err(&vpbe_dev->v4l2_dev, "Failed to get the context\n"); - return -EINVAL; + return PTR_ERR(layer->alloc_ctx); } q = &layer->buffer_queue; memset(q, 0, sizeof(*q)); -- cgit v1.2.3 From e276f03b4f29fcc54d8e658d5de8dd953e4aef1e Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 2 Dec 2012 22:50:47 -0300 Subject: [media] media: davinci: vpbe: return error code on error in vpbe_display_g_crop() We have assigned error code to 'ret' if crop->type is not V4L2_BUF_TYPE_VIDEO_OUTPUT, but never use it. We'd better return the error code on this error. Signed-off-by: Wei Yongjun Acked-by: Prabhakar Lad Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpbe_display.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 0723c4694ced..d078738b35d6 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -791,7 +791,6 @@ static int vpbe_display_g_crop(struct file *file, void *priv, struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; struct osd_state *osd_device = fh->disp_dev->osd_device; struct v4l2_rect *rect = &crop->c; - int ret; v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_CROP, layer id = %d\n", @@ -799,7 +798,7 @@ static int vpbe_display_g_crop(struct file *file, void *priv, if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); - ret = -EINVAL; + return -EINVAL; } osd_device->ops.get_layer_config(osd_device, layer->layer_info.id, cfg); -- cgit v1.2.3 From cc91de5fad155fdfb40856ac65f29f080b9b42ab Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 2 Dec 2012 22:53:44 -0300 Subject: [media] davinci: vpbe: remove unused variable in vpbe_initialize() The variable 'output_index' is initialized but never used otherwise, so remove the unused variable. Signed-off-by: Wei Yongjun Acked-by: Prabhakar Lad Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpbe.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c index dd670cdb2c2a..fe2b9ce0bce8 100644 --- a/drivers/media/platform/davinci/vpbe.c +++ b/drivers/media/platform/davinci/vpbe.c @@ -584,7 +584,6 @@ static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev) struct v4l2_subdev **enc_subdev; struct osd_state *osd_device; struct i2c_adapter *i2c_adap; - int output_index; int num_encoders; int ret = 0; int err; @@ -731,7 +730,6 @@ static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev) /* set the current encoder and output to that of venc by default */ vpbe_dev->current_sd_index = 0; vpbe_dev->current_out_index = 0; - output_index = 0; mutex_unlock(&vpbe_dev->lock); -- cgit v1.2.3 From 870f31cbf03e0c759900eea2feb276b2fe6558f4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 17 Dec 2012 22:10:00 -0300 Subject: [media] or51211: use %*ph[N] to dump small buffers Signed-off-by: Andy Shevchenko Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/or51211.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/or51211.c b/drivers/media/dvb-frontends/or51211.c index c625b57b4333..1af997eb0578 100644 --- a/drivers/media/dvb-frontends/or51211.c +++ b/drivers/media/dvb-frontends/or51211.c @@ -471,10 +471,7 @@ static int or51211_init(struct dvb_frontend* fe) i--; } } - dprintk("read_fwbits %x %x %x %x %x %x %x %x %x %x\n", - rec_buf[0], rec_buf[1], rec_buf[2], rec_buf[3], - rec_buf[4], rec_buf[5], rec_buf[6], rec_buf[7], - rec_buf[8], rec_buf[9]); + dprintk("read_fwbits %10ph\n", rec_buf); printk(KERN_INFO "or51211: ver TU%02x%02x%02x VSB mode %02x" " Status %02x\n", -- cgit v1.2.3 From aa735ee9dd92118488b30924b4710061b813193f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 17 Dec 2012 22:10:48 -0300 Subject: [media] ix2505v: use %*ph[N] to dump small buffers Signed-off-by: Andy Shevchenko Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/ix2505v.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/ix2505v.c b/drivers/media/dvb-frontends/ix2505v.c index bc5a82082aaa..0e3387e00952 100644 --- a/drivers/media/dvb-frontends/ix2505v.c +++ b/drivers/media/dvb-frontends/ix2505v.c @@ -212,7 +212,7 @@ static int ix2505v_set_params(struct dvb_frontend *fe) lpf = 0xb; deb_info("Osc=%x b_w=%x lpf=%x\n", local_osc, b_w, lpf); - deb_info("Data 0=[%x%x%x%x]\n", data[0], data[1], data[2], data[3]); + deb_info("Data 0=[%4phN]\n", data); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); -- cgit v1.2.3 From bb9e31f3928dd9b1ecb66689890d1f5f3d19227c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 18 Dec 2012 10:20:28 -0300 Subject: [media] or51211: apply pr_fmt and use pr_* macros instead of printk Signed-off-by: Andy Shevchenko Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/or51211.c | 94 ++++++++++++++++------------------- 1 file changed, 43 insertions(+), 51 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/or51211.c b/drivers/media/dvb-frontends/or51211.c index 1af997eb0578..10cfc0579168 100644 --- a/drivers/media/dvb-frontends/or51211.c +++ b/drivers/media/dvb-frontends/or51211.c @@ -22,6 +22,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + /* * This driver needs external firmware. Please use the command * "/Documentation/dvb/get_dvb_firmware or51211" to @@ -44,9 +46,7 @@ static int debug; #define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG "or51211: " args); \ - } while (0) + do { if (debug) pr_debug(args); } while (0) static u8 run_buf[] = {0x7f,0x01}; static u8 cmd_buf[] = {0x04,0x01,0x50,0x80,0x06}; // ATSC @@ -80,8 +80,7 @@ static int i2c_writebytes (struct or51211_state* state, u8 reg, const u8 *buf, msg.buf = (u8 *)buf; if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { - printk(KERN_WARNING "or51211: i2c_writebytes error " - "(addr %02x, err == %i)\n", reg, err); + pr_warn("error (addr %02x, err == %i)\n", reg, err); return -EREMOTEIO; } @@ -98,8 +97,7 @@ static int i2c_readbytes(struct or51211_state *state, u8 reg, u8 *buf, int len) msg.buf = buf; if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { - printk(KERN_WARNING "or51211: i2c_readbytes error " - "(addr %02x, err == %i)\n", reg, err); + pr_warn("error (addr %02x, err == %i)\n", reg, err); return -EREMOTEIO; } @@ -118,11 +116,11 @@ static int or51211_load_firmware (struct dvb_frontend* fe, /* Get eprom data */ tudata[0] = 17; if (i2c_writebytes(state,0x50,tudata,1)) { - printk(KERN_WARNING "or51211:load_firmware error eprom addr\n"); + pr_warn("error eprom addr\n"); return -1; } if (i2c_readbytes(state,0x50,&tudata[145],192)) { - printk(KERN_WARNING "or51211: load_firmware error eprom\n"); + pr_warn("error eprom\n"); return -1; } @@ -136,32 +134,32 @@ static int or51211_load_firmware (struct dvb_frontend* fe, state->config->reset(fe); if (i2c_writebytes(state,state->config->demod_address,tudata,585)) { - printk(KERN_WARNING "or51211: load_firmware error 1\n"); + pr_warn("error 1\n"); return -1; } msleep(1); if (i2c_writebytes(state,state->config->demod_address, &fw->data[393],8125)) { - printk(KERN_WARNING "or51211: load_firmware error 2\n"); + pr_warn("error 2\n"); return -1; } msleep(1); if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { - printk(KERN_WARNING "or51211: load_firmware error 3\n"); + pr_warn("error 3\n"); return -1; } /* Wait at least 5 msec */ msleep(10); if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { - printk(KERN_WARNING "or51211: load_firmware error 4\n"); + pr_warn("error 4\n"); return -1; } msleep(10); - printk("or51211: Done.\n"); + pr_info("Done.\n"); return 0; }; @@ -173,14 +171,14 @@ static int or51211_setmode(struct dvb_frontend* fe, int mode) state->config->setmode(fe, mode); if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { - printk(KERN_WARNING "or51211: setmode error 1\n"); + pr_warn("error 1\n"); return -1; } /* Wait at least 5 msec */ msleep(10); if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { - printk(KERN_WARNING "or51211: setmode error 2\n"); + pr_warn("error 2\n"); return -1; } @@ -196,7 +194,7 @@ static int or51211_setmode(struct dvb_frontend* fe, int mode) * normal +/-150kHz Carrier acquisition range */ if (i2c_writebytes(state,state->config->demod_address,cmd_buf,3)) { - printk(KERN_WARNING "or51211: setmode error 3\n"); + pr_warn("error 3\n"); return -1; } @@ -206,14 +204,14 @@ static int or51211_setmode(struct dvb_frontend* fe, int mode) rec_buf[3] = 0x00; msleep(20); if (i2c_writebytes(state,state->config->demod_address,rec_buf,3)) { - printk(KERN_WARNING "or51211: setmode error 5\n"); + pr_warn("error 5\n"); } msleep(3); if (i2c_readbytes(state,state->config->demod_address,&rec_buf[10],2)) { - printk(KERN_WARNING "or51211: setmode error 6"); + pr_warn("error 6\n"); return -1; } - dprintk("setmode rec status %02x %02x\n",rec_buf[10],rec_buf[11]); + dprintk("rec status %02x %02x\n", rec_buf[10], rec_buf[11]); return 0; } @@ -248,15 +246,15 @@ static int or51211_read_status(struct dvb_frontend* fe, fe_status_t* status) /* Receiver Status */ if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) { - printk(KERN_WARNING "or51132: read_status write error\n"); + pr_warn("write error\n"); return -1; } msleep(3); if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { - printk(KERN_WARNING "or51132: read_status read error\n"); + pr_warn("read error\n"); return -1; } - dprintk("read_status %x %x\n",rec_buf[0],rec_buf[1]); + dprintk("%x %x\n", rec_buf[0], rec_buf[1]); if (rec_buf[0] & 0x01) { /* Receiver Lock */ *status |= FE_HAS_SIGNAL; @@ -306,20 +304,18 @@ static int or51211_read_snr(struct dvb_frontend* fe, u16* snr) snd_buf[2] = 0x04; if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) { - printk(KERN_WARNING "%s: error writing snr reg\n", - __func__); + pr_warn("error writing snr reg\n"); return -1; } if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { - printk(KERN_WARNING "%s: read_status read error\n", - __func__); + pr_warn("read_status read error\n"); return -1; } state->snr = calculate_snr(rec_buf[0], 89599047); *snr = (state->snr) >> 16; - dprintk("%s: noise = 0x%02x, snr = %d.%02d dB\n", __func__, rec_buf[0], + dprintk("noise = 0x%02x, snr = %d.%02d dB\n", rec_buf[0], state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16); return 0; @@ -375,25 +371,24 @@ static int or51211_init(struct dvb_frontend* fe) if (!state->initialized) { /* Request the firmware, this will block until it uploads */ - printk(KERN_INFO "or51211: Waiting for firmware upload " - "(%s)...\n", OR51211_DEFAULT_FIRMWARE); + pr_info("Waiting for firmware upload (%s)...\n", + OR51211_DEFAULT_FIRMWARE); ret = config->request_firmware(fe, &fw, OR51211_DEFAULT_FIRMWARE); - printk(KERN_INFO "or51211:Got Hotplug firmware\n"); + pr_info("Got Hotplug firmware\n"); if (ret) { - printk(KERN_WARNING "or51211: No firmware uploaded " - "(timeout or file not found?)\n"); + pr_warn("No firmware uploaded " + "(timeout or file not found?)\n"); return ret; } ret = or51211_load_firmware(fe, fw); release_firmware(fw); if (ret) { - printk(KERN_WARNING "or51211: Writing firmware to " - "device failed!\n"); + pr_warn("Writing firmware to device failed!\n"); return ret; } - printk(KERN_INFO "or51211: Firmware upload complete.\n"); + pr_info("Firmware upload complete.\n"); /* Set operation mode in Receiver 1 register; * type 1: @@ -406,7 +401,7 @@ static int or51211_init(struct dvb_frontend* fe) */ if (i2c_writebytes(state,state->config->demod_address, cmd_buf,3)) { - printk(KERN_WARNING "or51211: Load DVR Error 5\n"); + pr_warn("Load DVR Error 5\n"); return -1; } @@ -419,13 +414,13 @@ static int or51211_init(struct dvb_frontend* fe) msleep(30); if (i2c_writebytes(state,state->config->demod_address, rec_buf,3)) { - printk(KERN_WARNING "or51211: Load DVR Error A\n"); + pr_warn("Load DVR Error A\n"); return -1; } msleep(3); if (i2c_readbytes(state,state->config->demod_address, &rec_buf[10],2)) { - printk(KERN_WARNING "or51211: Load DVR Error B\n"); + pr_warn("Load DVR Error B\n"); return -1; } @@ -436,13 +431,13 @@ static int or51211_init(struct dvb_frontend* fe) msleep(20); if (i2c_writebytes(state,state->config->demod_address, rec_buf,3)) { - printk(KERN_WARNING "or51211: Load DVR Error C\n"); + pr_warn("Load DVR Error C\n"); return -1; } msleep(3); if (i2c_readbytes(state,state->config->demod_address, &rec_buf[12],2)) { - printk(KERN_WARNING "or51211: Load DVR Error D\n"); + pr_warn("Load DVR Error D\n"); return -1; } @@ -454,16 +449,14 @@ static int or51211_init(struct dvb_frontend* fe) get_ver_buf[4] = i+1; if (i2c_writebytes(state,state->config->demod_address, get_ver_buf,5)) { - printk(KERN_WARNING "or51211:Load DVR Error 6" - " - %d\n",i); + pr_warn("Load DVR Error 6 - %d\n", i); return -1; } msleep(3); if (i2c_readbytes(state,state->config->demod_address, &rec_buf[i*2],2)) { - printk(KERN_WARNING "or51211:Load DVR Error 7" - " - %d\n",i); + pr_warn("Load DVR Error 7 - %d\n", i); return -1; } /* If we didn't receive the right index, try again */ @@ -473,10 +466,9 @@ static int or51211_init(struct dvb_frontend* fe) } dprintk("read_fwbits %10ph\n", rec_buf); - printk(KERN_INFO "or51211: ver TU%02x%02x%02x VSB mode %02x" - " Status %02x\n", - rec_buf[2], rec_buf[4],rec_buf[6], - rec_buf[12],rec_buf[10]); + pr_info("ver TU%02x%02x%02x VSB mode %02x Status %02x\n", + rec_buf[2], rec_buf[4], rec_buf[6], rec_buf[12], + rec_buf[10]); rec_buf[0] = 0x04; rec_buf[1] = 0x00; @@ -485,13 +477,13 @@ static int or51211_init(struct dvb_frontend* fe) msleep(20); if (i2c_writebytes(state,state->config->demod_address, rec_buf,3)) { - printk(KERN_WARNING "or51211: Load DVR Error 8\n"); + pr_warn("Load DVR Error 8\n"); return -1; } msleep(20); if (i2c_readbytes(state,state->config->demod_address, &rec_buf[8],2)) { - printk(KERN_WARNING "or51211: Load DVR Error 9\n"); + pr_warn("Load DVR Error 9\n"); return -1; } state->initialized = 1; -- cgit v1.2.3 From 41f55d57552b7d2236f94fccb5cdd07dbf2e8557 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 16 Dec 2012 19:37:11 -0300 Subject: [media] tda10071: make sure both tuner and demod i2c addresses are specified display an error message if either tuner_i2c_addr or demod_i2c_addr are not specified in the tda10071_config structure Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/tda10071.c | 18 +++++++++++++++--- drivers/media/dvb-frontends/tda10071.h | 4 ++-- drivers/media/pci/cx23885/cx23885-dvb.c | 2 +- drivers/media/usb/em28xx/em28xx-dvb.c | 3 ++- 4 files changed, 20 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 710362916f83..02f9234a5400 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -30,7 +30,7 @@ static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val, u8 buf[len+1]; struct i2c_msg msg[1] = { { - .addr = priv->cfg.i2c_address, + .addr = priv->cfg.demod_i2c_addr, .flags = 0, .len = sizeof(buf), .buf = buf, @@ -59,12 +59,12 @@ static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val, u8 buf[len]; struct i2c_msg msg[2] = { { - .addr = priv->cfg.i2c_address, + .addr = priv->cfg.demod_i2c_addr, .flags = 0, .len = 1, .buf = ®, }, { - .addr = priv->cfg.i2c_address, + .addr = priv->cfg.demod_i2c_addr, .flags = I2C_M_RD, .len = sizeof(buf), .buf = buf, @@ -1202,6 +1202,18 @@ struct dvb_frontend *tda10071_attach(const struct tda10071_config *config, goto error; } + /* make sure demod i2c address is specified */ + if (!config->demod_i2c_addr) { + dev_dbg(&i2c->dev, "%s: invalid demod i2c address!\n", __func__); + goto error; + } + + /* make sure tuner i2c address is specified */ + if (!config->tuner_i2c_addr) { + dev_dbg(&i2c->dev, "%s: invalid tuner i2c address!\n", __func__); + goto error; + } + /* setup the priv */ priv->i2c = i2c; memcpy(&priv->cfg, config, sizeof(struct tda10071_config)); diff --git a/drivers/media/dvb-frontends/tda10071.h b/drivers/media/dvb-frontends/tda10071.h index a20d5c41e048..bff1c38df802 100644 --- a/drivers/media/dvb-frontends/tda10071.h +++ b/drivers/media/dvb-frontends/tda10071.h @@ -28,10 +28,10 @@ struct tda10071_config { * Default: none, must set * Values: 0x55, */ - u8 i2c_address; + u8 demod_i2c_addr; /* Tuner I2C address. - * Default: 0x14 + * Default: none, must set * Values: 0x14, 0x54, ... */ u8 tuner_i2c_addr; diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index cf84c534f005..a1aae5633739 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -662,7 +662,7 @@ static struct mt2063_config terratec_mt2063_config[] = { }; static const struct tda10071_config hauppauge_tda10071_config = { - .i2c_address = 0x05, + .demod_i2c_addr = 0x05, .tuner_i2c_addr = 0x54, .i2c_wr_max = 64, .ts_mode = TDA10071_TS_SERIAL, diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 63f2e7070c00..e8008810b60c 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -714,7 +714,8 @@ static struct tda18271_config em28xx_cxd2820r_tda18271_config = { }; static const struct tda10071_config em28xx_tda10071_config = { - .i2c_address = 0x55, /* (0xaa >> 1) */ + .demod_i2c_addr = 0x55, /* (0xaa >> 1) */ + .tuner_i2c_addr = 0x14, .i2c_wr_max = 64, .ts_mode = TDA10071_TS_SERIAL, .spec_inv = 0, -- cgit v1.2.3 From 1c12bf8de7e1557afeedd55d9bcec6b6a6d7b5d1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 21 Dec 2012 14:43:19 -0200 Subject: [media] tda10071: fix a warning introduced by changeset 41f55d5755 The two new tests don't set the returned value. Cc: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/tda10071.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 02f9234a5400..2521f7e23018 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -1205,12 +1205,14 @@ struct dvb_frontend *tda10071_attach(const struct tda10071_config *config, /* make sure demod i2c address is specified */ if (!config->demod_i2c_addr) { dev_dbg(&i2c->dev, "%s: invalid demod i2c address!\n", __func__); + ret = -EINVAL; goto error; } /* make sure tuner i2c address is specified */ if (!config->tuner_i2c_addr) { dev_dbg(&i2c->dev, "%s: invalid tuner i2c address!\n", __func__); + ret = -EINVAL; goto error; } -- cgit v1.2.3 From e5d85b9ac3133f67460ea5b2d4e33e0473d6eb4b Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Tue, 25 Nov 2008 10:57:30 -0300 Subject: [media] rc: Fix double free in gpio_ir_recv_probe() At the 'err_request_irq' label, rc_unregister_device(rcdev) frees its argument. So when we fall through to the 'err_gpio_request' label further down and call rc_free_device(rcdev) then that's a double free. Fix that by moving 'rcdev = NULL' from after the call to rc_free_device() to after rc_unregister_device(). That fixes the problem since rc_free_device() just does nothing if passed NULL and there's no further use of 'rcdev' after the call to rc_free_device() so it's not needed there. Signed-off-by: Jesper Juhl Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/gpio-ir-recv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c index ba1a1eb356cf..32db5f59fbc3 100644 --- a/drivers/media/rc/gpio-ir-recv.c +++ b/drivers/media/rc/gpio-ir-recv.c @@ -129,12 +129,12 @@ static int __devinit gpio_ir_recv_probe(struct platform_device *pdev) err_request_irq: platform_set_drvdata(pdev, NULL); rc_unregister_device(rcdev); + rcdev = NULL; err_register_rc_device: err_gpio_direction_input: gpio_free(pdata->gpio_nr); err_gpio_request: rc_free_device(rcdev); - rcdev = NULL; err_allocate_device: kfree(gpio_dev); return rc; -- cgit v1.2.3 From bbe2a1d32f40c01ca1a7e7795e20ca06f87ffc9b Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Tue, 25 Nov 2008 10:57:54 -0300 Subject: [media] rc: Fix double free in gpio_ir_recv_remove() Since rc_unregister_device() frees its argument there's no need to subsequently call rc_free_device() on the same variable - in fact it's a double free bug. Easily fixed by just removing the rc_free_device() call. Signed-off-by: Jesper Juhl Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/gpio-ir-recv.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c index 32db5f59fbc3..03e3cf6eb68f 100644 --- a/drivers/media/rc/gpio-ir-recv.c +++ b/drivers/media/rc/gpio-ir-recv.c @@ -148,7 +148,6 @@ static int __devexit gpio_ir_recv_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); rc_unregister_device(gpio_dev->rcdev); gpio_free(gpio_dev->gpio_nr); - rc_free_device(gpio_dev->rcdev); kfree(gpio_dev); return 0; } -- cgit v1.2.3 From 3f3f5c7f63dec5d6413075116cd6beee1e888d7b Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Mon, 29 Oct 2012 05:20:29 -0300 Subject: [media] media: coda: Fix H.264 header alignment Length of H.264 headers is variable and thus it might not be aligned for the coda to append the encoded frame. This causes the first frame to overwrite part of the H.264 PPS. In order to solve that, a filler NAL must be added between the headers and the first frame to preserve alignment. [mchehab@redhat.com: Fix a few CodingStyle issues] Signed-off-by: Javier Martin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 7b8b547f2d51..8b7f5ac7fc11 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -178,6 +178,9 @@ struct coda_ctx { int idx; }; +static u8 coda_filler_nal[] = { 0x00, 0x00, 0x00, 0x01, 0x0c, + 0xff, 0xff, 0xff, 0xff, 0xff}; + static inline void coda_write(struct coda_dev *dev, u32 data, u32 reg) { v4l2_dbg(1, coda_debug, &dev->v4l2_dev, @@ -944,6 +947,29 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_d return 0; } +static int coda_h264_padding(int size, char *p) +{ + int size_align = size & ~0x3; + int filler_size = ARRAY_SIZE(coda_filler_nal); + int nal_size; + int diff; + + diff = size - size_align; + if (diff == 0) + return 0; + + nal_size = filler_size + 2 - diff; + if (nal_size > filler_size) + nal_size -= 4; + + memcpy(p, coda_filler_nal, nal_size); + + /* Add rbsp stop bit and trailing at the end */ + *(p + nal_size - 1) = 0x80; + + return nal_size; +} + static int coda_start_streaming(struct vb2_queue *q, unsigned int count) { struct coda_ctx *ctx = vb2_get_drv_priv(q); @@ -1171,7 +1197,15 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); memcpy(&ctx->vpu_header[1][0], vb2_plane_vaddr(buf, 0), ctx->vpu_header_size[1]); - ctx->vpu_header_size[2] = 0; + /* + * Length of H.264 headers is variable and thus it might not be + * aligned for the coda to append the encoded frame. In that is + * the case a filler NAL must be added to header 2. + */ + ctx->vpu_header_size[2] = coda_h264_padding( + (ctx->vpu_header_size[0] + + ctx->vpu_header_size[1]), + ctx->vpu_header[2]); break; case V4L2_PIX_FMT_MPEG4: /* -- cgit v1.2.3 From 832fbb5aec6ec877ed9273a0b20520e3dc0b23b3 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Mon, 29 Oct 2012 08:34:59 -0300 Subject: [media] media: coda: Fix H.264 header alignment - v2 Length of H.264 headers is variable and thus it might not be aligned for the coda to append the encoded frame. This causes the first frame to overwrite part of the H.264 PPS. In order to solve that, a filler NAL must be added between the headers and the first frame to preserve alignment. [mchehab@redhat.com: applied only v2 diff here, as v1 ended by mistakenly being applied] Signed-off-by: Javier Martin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 8b7f5ac7fc11..2721f839852f 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -178,8 +178,9 @@ struct coda_ctx { int idx; }; -static u8 coda_filler_nal[] = { 0x00, 0x00, 0x00, 0x01, 0x0c, - 0xff, 0xff, 0xff, 0xff, 0xff}; +static const u8 coda_filler_nal[14] = { 0x00, 0x00, 0x00, 0x01, 0x0c, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80 }; +static const u8 coda_filler_size[8] = { 0, 7, 14, 13, 12, 11, 10, 9 }; static inline void coda_write(struct coda_dev *dev, u32 data, u32 reg) { @@ -949,19 +950,14 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_d static int coda_h264_padding(int size, char *p) { - int size_align = size & ~0x3; - int filler_size = ARRAY_SIZE(coda_filler_nal); int nal_size; int diff; - diff = size - size_align; + diff = size - (size & ~0x7); if (diff == 0) return 0; - nal_size = filler_size + 2 - diff; - if (nal_size > filler_size) - nal_size -= 4; - + nal_size = coda_filler_size[diff]; memcpy(p, coda_filler_nal, nal_size); /* Add rbsp stop bit and trailing at the end */ -- cgit v1.2.3 From 37e310edf3f6090eb118b7fcafd00221c290ac6b Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Tue, 30 Oct 2012 11:09:41 -0300 Subject: [media] drivers/media/pci/saa7134/saa7134-dvb.c: Test if videobuf_dvb_get_frontend return NULL Based on commit: e66131cee501ee720b7b58a4b87073b8fbaaaba6 Not testing videobuf_dvb_get_frontend output may cause OOPS if it return NULL. This patch fixes this issue. The semantic patch that found this issue is(http://coccinelle.lip6.fr/): // @@ identifier i,a,b; statement S, S2; @@ i = videobuf_dvb_get_frontend(...); ... when != if (!i) S * if (i->a.b) S2 // Signed-off-by: Peter Senna Tschudin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/saa7134/saa7134-dvb.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/media/pci/saa7134/saa7134-dvb.c b/drivers/media/pci/saa7134/saa7134-dvb.c index b209de40a4f8..27915e501db9 100644 --- a/drivers/media/pci/saa7134/saa7134-dvb.c +++ b/drivers/media/pci/saa7134/saa7134-dvb.c @@ -607,6 +607,9 @@ static int configure_tda827x_fe(struct saa7134_dev *dev, /* Get the first frontend */ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); + if (!fe0) + return -EINVAL; + fe0->dvb.frontend = dvb_attach(tda10046_attach, cdec_conf, &dev->i2c_adap); if (fe0->dvb.frontend) { if (cdec_conf->i2c_gate) -- cgit v1.2.3 From 202724096816ebf5c6557719f6a2d6faf6371f9a Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 30 Oct 2012 11:12:32 -0300 Subject: [media] media: m2m-deinterlace: Do not set debugging flag to true Default value should be 'debugging disabled'. Signed-off-by: Javier Martin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/m2m-deinterlace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 05c560f2ef06..ed77a645e992 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -28,7 +28,7 @@ MODULE_AUTHOR("Javier Martin Date: Tue, 30 Oct 2012 12:04:23 -0300 Subject: [media] media: ov7670: Allow 32x maximum gain for yuv422 4x gain ceiling is not enough to capture a decent image in conditions of total darkness and only a LED light source. Allow a maximum gain of 32x instead. This doesn't have any drawback since the image quality in 'normal' light conditions is the same. Signed-off-by: Javier Martin Acked-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index e7c82b297514..882ddf6f66d3 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -353,7 +353,7 @@ static struct regval_list ov7670_fmt_yuv422[] = { { REG_RGB444, 0 }, /* No RGB444 please */ { REG_COM1, 0 }, /* CCIR601 */ { REG_COM15, COM15_R00FF }, - { REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */ + { REG_COM9, 0x48 }, /* 32x gain ceiling; 0x8 is reserved bit */ { 0x4f, 0x80 }, /* "matrix coefficient 1" */ { 0x50, 0x80 }, /* "matrix coefficient 2" */ { 0x51, 0 }, /* vb */ -- cgit v1.2.3 From 6dc8f3823e52f3b9a127d79ca7b8bc782a5cfd7e Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Wed, 31 Oct 2012 11:52:45 -0300 Subject: [media] staging: media: Fix minor typo in staging/media Correct spelling typo in comment witin staging/media. Signed-off-by: Masanari Iida Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/as102/as10x_cmd_cfg.c | 2 +- drivers/staging/media/dt3155v4l/dt3155v4l.c | 2 +- drivers/staging/media/lirc/lirc_sasem.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/as102/as10x_cmd_cfg.c b/drivers/staging/media/as102/as10x_cmd_cfg.c index d2a4bce89623..4a2bbd766655 100644 --- a/drivers/staging/media/as102/as10x_cmd_cfg.c +++ b/drivers/staging/media/as102/as10x_cmd_cfg.c @@ -197,7 +197,7 @@ out: * @prsp: pointer to AS10x command response buffer * @proc_id: id of the command * - * Since the contex command reponse does not follow the common + * Since the contex command response does not follow the common * response, a specific parse function is required. * Return 0 on success or negative value in case of error. */ diff --git a/drivers/staging/media/dt3155v4l/dt3155v4l.c b/drivers/staging/media/dt3155v4l/dt3155v4l.c index 54f1813aa1ef..0af8917ebc57 100644 --- a/drivers/staging/media/dt3155v4l/dt3155v4l.c +++ b/drivers/staging/media/dt3155v4l/dt3155v4l.c @@ -785,7 +785,7 @@ dt3155_init_board(struct pci_dev *pdev) } write_i2c_reg(pd->regs, CONFIG, pd->config); /* ACQ_MODE_EVEN */ - /* select chanel 1 for input and set sync level */ + /* select channel 1 for input and set sync level */ write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG); write_i2c_reg(pd->regs, AD_CMD, VIDEO_CNL_1 | SYNC_CNL_1 | SYNC_LVL_3); diff --git a/drivers/staging/media/lirc/lirc_sasem.c b/drivers/staging/media/lirc/lirc_sasem.c index f4e4d9003f38..101ac12eb381 100644 --- a/drivers/staging/media/lirc/lirc_sasem.c +++ b/drivers/staging/media/lirc/lirc_sasem.c @@ -891,7 +891,7 @@ exit: } /** - * Callback function for USB core API: disonnect + * Callback function for USB core API: disconnect */ static void sasem_disconnect(struct usb_interface *interface) { -- cgit v1.2.3 From cb31c7487580a0cfc6eb253e604c1e51ac8eb3c8 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Thu, 1 Nov 2012 16:24:30 -0300 Subject: [media] budget-av: only use t_state if initialized MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Building budget-av.o triggers this GCC warning: In file included from drivers/media/pci/ttpci/budget-av.c:44:0: drivers/media/dvb-frontends/tda8261_cfg.h: In function ‘tda8261_get_bandwidth’: drivers/media/dvb-frontends/tda8261_cfg.h:68:21: warning: ‘t_state.bandwidth’ may be used uninitialized in this function [-Wuninitialized] Move the printk() that uses t_state.bandwith to the location where it should be initialized to fix this. Signed-off-by: Paul Bolle Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/tda8261_cfg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/tda8261_cfg.h b/drivers/media/dvb-frontends/tda8261_cfg.h index 1af1ee49b542..46710744173b 100644 --- a/drivers/media/dvb-frontends/tda8261_cfg.h +++ b/drivers/media/dvb-frontends/tda8261_cfg.h @@ -78,7 +78,7 @@ static int tda8261_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) return err; } *bandwidth = t_state.bandwidth; + printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth); } - printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth); return 0; } -- cgit v1.2.3 From ed2e33011451f655052ff1d3aa9ee936057d508b Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Thu, 1 Nov 2012 17:00:09 -0300 Subject: [media] tda18212: tda18218: use 'val' if initialized MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commits e666a44fa313cb9329c0381ad02fc6ee1e21cb31 ("[media] tda18212: silence compiler warning") and e0e52d4e9f5bce7ea887027c127473eb654a5a04 ("[media] tda18218: silence compiler warning") silenced warnings equivalent to these: drivers/media/tuners/tda18212.c: In function ‘tda18212_attach’: drivers/media/tuners/tda18212.c:299:2: warning: ‘val’ may be used uninitialized in this function [-Wmaybe-uninitialized] drivers/media/tuners/tda18218.c: In function ‘tda18218_attach’: drivers/media/tuners/tda18218.c:305:2: warning: ‘val’ may be used uninitialized in this function [-Wmaybe-uninitialized] But in both cases 'val' will still be used uninitialized if the calls of tda18212_rd_reg() or tda18218_rd_reg() fail. Fix this by only printing the "chip id" if the calls of those functions were successful. This allows to drop the uninitialized_var() stopgap measure. Also stop printing the return values of tda18212_rd_reg() or tda18218_rd_reg(), as these are not interesting. Signed-off-by: Paul Bolle Acked-by: Antti Palosaari Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/tda18212.c | 6 +++--- drivers/media/tuners/tda18218.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/media/tuners/tda18212.c b/drivers/media/tuners/tda18212.c index 5d9f02842501..e4a84ee231cf 100644 --- a/drivers/media/tuners/tda18212.c +++ b/drivers/media/tuners/tda18212.c @@ -277,7 +277,7 @@ struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe, { struct tda18212_priv *priv = NULL; int ret; - u8 uninitialized_var(val); + u8 val; priv = kzalloc(sizeof(struct tda18212_priv), GFP_KERNEL); if (priv == NULL) @@ -296,8 +296,8 @@ struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe, if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ - dev_dbg(&priv->i2c->dev, "%s: ret=%d chip id=%02x\n", __func__, ret, - val); + if (!ret) + dev_dbg(&priv->i2c->dev, "%s: chip id=%02x\n", __func__, val); if (ret || val != 0xc7) { kfree(priv); return NULL; diff --git a/drivers/media/tuners/tda18218.c b/drivers/media/tuners/tda18218.c index 18198537be9f..2d31aeb6b088 100644 --- a/drivers/media/tuners/tda18218.c +++ b/drivers/media/tuners/tda18218.c @@ -277,7 +277,7 @@ struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct tda18218_config *cfg) { struct tda18218_priv *priv = NULL; - u8 uninitialized_var(val); + u8 val; int ret; /* chip default registers values */ static u8 def_regs[] = { @@ -302,8 +302,8 @@ struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe, /* check if the tuner is there */ ret = tda18218_rd_reg(priv, R00_ID, &val); - dev_dbg(&priv->i2c->dev, "%s: ret=%d chip id=%02x\n", __func__, ret, - val); + if (!ret) + dev_dbg(&priv->i2c->dev, "%s: chip id=%02x\n", __func__, val); if (ret || val != def_regs[R00_ID]) { kfree(priv); return NULL; -- cgit v1.2.3 From 011b2aad587a770e758711f465312ac843440d2a Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Fri, 2 Nov 2012 04:48:48 -0300 Subject: [media] staging/media: Use dev_ printks in cxd2099/cxd2099.[ch] fixed below checkpatch warnings. - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... - WARNING: Prefer netdev_warn(netdev, ... then dev_warn(dev, ... then pr_warn(... to printk(KERN_WARNING ... Signed-off-by: YAMANE Toshiaki Tested-by: Peter Senna Tschudin Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/cxd2099/cxd2099.c | 29 +++++++++++++++-------------- drivers/staging/media/cxd2099/cxd2099.h | 2 +- 2 files changed, 16 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/cxd2099/cxd2099.c b/drivers/staging/media/cxd2099/cxd2099.c index 0ff19724992f..822c487592a4 100644 --- a/drivers/staging/media/cxd2099/cxd2099.c +++ b/drivers/staging/media/cxd2099/cxd2099.c @@ -66,8 +66,9 @@ static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr, struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = 2}; if (i2c_transfer(adapter, &msg, 1) != 1) { - printk(KERN_ERR "Failed to write to I2C register %02x@%02x!\n", - reg, adr); + dev_err(&adapter->dev, + "Failed to write to I2C register %02x@%02x!\n", + reg, adr); return -1; } return 0; @@ -79,7 +80,7 @@ static int i2c_write(struct i2c_adapter *adapter, u8 adr, struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len}; if (i2c_transfer(adapter, &msg, 1) != 1) { - printk(KERN_ERR "Failed to write to I2C!\n"); + dev_err(&adapter->dev, "Failed to write to I2C!\n"); return -1; } return 0; @@ -94,7 +95,7 @@ static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, .buf = val, .len = 1} }; if (i2c_transfer(adapter, msgs, 2) != 2) { - printk(KERN_ERR "error in i2c_read_reg\n"); + dev_err(&adapter->dev, "error in i2c_read_reg\n"); return -1; } return 0; @@ -109,7 +110,7 @@ static int i2c_read(struct i2c_adapter *adapter, u8 adr, .buf = data, .len = n} }; if (i2c_transfer(adapter, msgs, 2) != 2) { - printk(KERN_ERR "error in i2c_read\n"); + dev_err(&adapter->dev, "error in i2c_read\n"); return -1; } return 0; @@ -277,7 +278,7 @@ static void cam_mode(struct cxd *ci, int mode) #ifdef BUFFER_MODE if (!ci->en.read_data) return; - printk(KERN_INFO "enable cam buffer mode\n"); + dev_info(&ci->i2c->dev, "enable cam buffer mode\n"); /* write_reg(ci, 0x0d, 0x00); */ /* write_reg(ci, 0x0e, 0x01); */ write_regm(ci, 0x08, 0x40, 0x40); @@ -524,7 +525,7 @@ static int slot_reset(struct dvb_ca_en50221 *ca, int slot) msleep(10); #if 0 read_reg(ci, 0x06, &val); - printk(KERN_INFO "%d:%02x\n", i, val); + dev_info(&ci->i2c->dev, "%d:%02x\n", i, val); if (!(val&0x10)) break; #else @@ -542,7 +543,7 @@ static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot) { struct cxd *ci = ca->data; - printk(KERN_INFO "slot_shutdown\n"); + dev_info(&ci->i2c->dev, "slot_shutdown\n"); mutex_lock(&ci->lock); write_regm(ci, 0x09, 0x08, 0x08); write_regm(ci, 0x20, 0x80, 0x80); /* Reset CAM Mode */ @@ -578,10 +579,10 @@ static int campoll(struct cxd *ci) if (istat&0x40) { ci->dr = 1; - printk(KERN_INFO "DR\n"); + dev_info(&ci->i2c->dev, "DR\n"); } if (istat&0x20) - printk(KERN_INFO "WC\n"); + dev_info(&ci->i2c->dev, "WC\n"); if (istat&2) { u8 slotstat; @@ -597,7 +598,7 @@ static int campoll(struct cxd *ci) if (ci->slot_stat) { ci->slot_stat = 0; write_regm(ci, 0x03, 0x00, 0x08); - printk(KERN_INFO "NO CAM\n"); + dev_info(&ci->i2c->dev, "NO CAM\n"); ci->ready = 0; } } @@ -634,7 +635,7 @@ static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) campoll(ci); mutex_unlock(&ci->lock); - printk(KERN_INFO "read_data\n"); + dev_info(&ci->i2c->dev, "read_data\n"); if (!ci->dr) return 0; @@ -687,7 +688,7 @@ struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, u8 val; if (i2c_read_reg(i2c, cfg->adr, 0, &val) < 0) { - printk(KERN_INFO "No CXD2099 detected at %02x\n", cfg->adr); + dev_info(&i2c->dev, "No CXD2099 detected at %02x\n", cfg->adr); return NULL; } @@ -705,7 +706,7 @@ struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, ci->en = en_templ; ci->en.data = ci; init(ci); - printk(KERN_INFO "Attached CXD2099AR at %02x\n", ci->cfg.adr); + dev_info(&i2c->dev, "Attached CXD2099AR at %02x\n", ci->cfg.adr); return &ci->en; } EXPORT_SYMBOL(cxd2099_attach); diff --git a/drivers/staging/media/cxd2099/cxd2099.h b/drivers/staging/media/cxd2099/cxd2099.h index 19c588a59588..0eb607c5b423 100644 --- a/drivers/staging/media/cxd2099/cxd2099.h +++ b/drivers/staging/media/cxd2099/cxd2099.h @@ -43,7 +43,7 @@ struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, static inline struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, void *priv, struct i2c_adapter *i2c) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__); return NULL; } #endif -- cgit v1.2.3 From e3a8b4d22b47b3ee17baccdc5b351465f890671c Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Fri, 2 Nov 2012 09:10:30 -0300 Subject: [media] vivi: Optimize gen_text() I've noticed that vivi takes a lot of CPU to produce its frames. For example for 8 devices and 8 simple programs running, where each captures YUY2 640x480 and displays it to X via SDL, profile timing is as follows: # cmdline : /home/kirr/local/perf/bin/perf record -g -a sleep 20 # Samples: 82K of event 'cycles' # Event count (approx.): 31551930117 # # Overhead Command Shared Object Symbol # ........ ............... .................... # 49.48% vivi-* [vivi] [k] gen_twopix 10.79% vivi-* [kernel.kallsyms] [k] memcpy 10.02% rawv libc-2.13.so [.] __memcpy_ssse3 8.35% vivi-* [vivi] [k] gen_text.constprop.6 5.06% Xorg [unknown] [.] 0xa73015f8 2.32% rawv [vivi] [k] gen_twopix 1.22% rawv [vivi] [k] precalculate_line 1.20% vivi-* [vivi] [k] vivi_fillbuff (rawv is display program, vivi-* is a combination of vivi-000 through vivi-007) so a lot of time is spent in gen_twopix() which as the follwing call-graph profile shows ... 49.48% vivi-* [vivi] [k] gen_twopix | --- gen_twopix | |--96.30%-- gen_text.constprop.6 | vivi_fillbuff | vivi_thread | kthread | ret_from_kernel_thread | --3.70%-- vivi_fillbuff vivi_thread kthread ret_from_kernel_thread ... is called mostly from gen_text(). If we'll look at gen_text(), in the inner loop, we'll see if (chr & (1 << (7 - i))) gen_twopix(dev, pos + j * dev->pixelsize, WHITE, (x+y) & 1); else gen_twopix(dev, pos + j * dev->pixelsize, TEXT_BLACK, (x+y) & 1); which calls gen_twopix() for every character pixel, and that is very expensive, because gen_twopix() branches several times. Now, let's note, that we operate on only two colors - WHITE and TEXT_BLACK, and that pixel for that colors could be precomputed and gen_twopix() moved out of the inner loop. Also note, that for black and white colors even/odd does not make a difference for all supported pixel formats, so we could stop doing that `odd` gen_twopix() parameter game. So the first thing we are doing here is 1) moving gen_twopix() calls out of gen_text() into vivi_fillbuff(), to pregenerate black and white colors, just before printing starts. what we have next is that gen_text's font rendering loop, even with gen_twopix() calls moved out, was inefficient and branchy, so let's 2) rewrite gen_text() loop so it uses less variables + unroll char horizontal-rendering loop + instantiate 3 code paths for pixelsizes 2,3 and 4 so that in all inner loops we don't have to branch or make indirections (*). Done all above reworks, for gen_text() we get nice, non-branchy streamlined code (showing loop for pixelsize=2): ? cmp $0x2,%eax ? ? jne 26 ? mov -0x18(%ebp),%eax ? mov -0x20(%ebp),%edi ? imul -0x20(%ebp),%eax ? movzwl 0x3ffc(%ebx),%esi 0,08 ? movzwl 0x4000(%ebx),%ecx 0,04 ? add %edi,%edi ? mov 0x0,%ebx 0,51 ? mov %edi,-0x1c(%ebp) ? mov %ebx,-0x14(%ebp) ? movl $0x0,-0x10(%ebp) ? lea 0x20(%edx,%eax,2),%eax ? mov %eax,-0x18(%ebp) ? xchg %ax,%ax 0,04 ? a0: mov 0x8(%ebp),%ebx ? mov -0x18(%ebp),%eax 0,04 ? movzbl (%ebx),%edx 0,16 ? test %dl,%dl 0,04 ? ? je 128 0,08 ? lea 0x0(%esi),%esi 1,61 ? b0:???shl $0x4,%edx 1,02 ? ? mov -0x14(%ebp),%edi 2,04 ? ? add -0x10(%ebp),%edx 2,24 ? ? lea 0x1(%ebx),%ebx 0,27 ? ? movzbl (%edi,%edx,1),%edx 9,92 ? ? mov %esi,%edi 0,39 ? ? test %dl,%dl 2,04 ? ? cmovns %ecx,%edi 4,63 ? ? test $0x40,%dl 0,55 ? ? mov %di,(%eax) 3,76 ? ? mov %esi,%edi 0,71 ? ? cmove %ecx,%edi 3,41 ? ? test $0x20,%dl 0,75 ? ? mov %di,0x2(%eax) 2,43 ? ? mov %esi,%edi 0,59 ? ? cmove %ecx,%edi 4,59 ? ? test $0x10,%dl 0,67 ? ? mov %di,0x4(%eax) 2,55 ? ? mov %esi,%edi 0,78 ? ? cmove %ecx,%edi 4,31 ? ? test $0x8,%dl 0,67 ? ? mov %di,0x6(%eax) 5,76 ? ? mov %esi,%edi 1,80 ? ? cmove %ecx,%edi 4,20 ? ? test $0x4,%dl 0,86 ? ? mov %di,0x8(%eax) 2,98 ? ? mov %esi,%edi 1,37 ? ? cmove %ecx,%edi 4,67 ? ? test $0x2,%dl 0,20 ? ? mov %di,0xa(%eax) 2,78 ? ? mov %esi,%edi 0,75 ? ? cmove %ecx,%edi 3,92 ? ? and $0x1,%edx 0,75 ? ? mov %esi,%edx 2,59 ? ? mov %di,0xc(%eax) 0,59 ? ? cmove %ecx,%edx 3,10 ? ? mov %dx,0xe(%eax) 2,39 ? ? add $0x10,%eax 0,51 ? ? movzbl (%ebx),%edx 2,86 ? ? test %dl,%dl 2,31 ? ???jne b0 0,04 ?128: addl $0x1,-0x10(%ebp) 4,00 ? mov -0x1c(%ebp),%eax 0,04 ? add %eax,-0x18(%ebp) 0,08 ? cmpl $0x10,-0x10(%ebp) ? ? jne a0 which almost goes away from the profile: # cmdline : /home/kirr/local/perf/bin/perf record -g -a sleep 20 # Samples: 49K of event 'cycles' # Event count (approx.): 16799780016 # # Overhead Command Shared Object Symbol # ........ ............... .................... # 27.51% rawv libc-2.13.so [.] __memcpy_ssse3 23.77% vivi-* [kernel.kallsyms] [k] memcpy 9.96% Xorg [unknown] [.] 0xa76f5e12 4.94% vivi-* [vivi] [k] gen_text.constprop.6 4.44% rawv [vivi] [k] gen_twopix 3.17% vivi-* [vivi] [k] vivi_fillbuff 2.45% rawv [vivi] [k] precalculate_line 1.20% swapper [kernel.kallsyms] [k] read_hpet i.e. gen_twopix() overhead dropped from 49% to 4% and gen_text() loops from ~8% to ~4%, and overal cycles count dropped from 31551930117 to 16799780016 which is ~1.9x whole workload speedup. (*) for RGB24 rendering I've introduced x24, which could be thought as synthetic u24 for simplifying the code. That's done because for memcpy used for conditional assignment, gcc generates suboptimal code with more indirections. Fortunately, in C struct assignment is builtin and that's all we need from pixeltype for font rendering. Signed-off-by: Kirill Smelkov Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivi.c | 61 ++++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index c2f424f32450..3801a8d00946 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -240,6 +240,7 @@ struct vivi_dev { u8 line[MAX_WIDTH * 8]; unsigned int pixelsize; u8 alpha_component; + u32 textfg, textbg; }; /* ------------------------------------------------------------------ @@ -520,33 +521,54 @@ static void precalculate_line(struct vivi_dev *dev) } } +/* need this to do rgb24 rendering */ +typedef struct { u16 __; u8 _; } __attribute__((packed)) x24; + static void gen_text(struct vivi_dev *dev, char *basep, int y, int x, char *text) { int line; + unsigned int width = dev->width; /* Checks if it is possible to show string */ - if (y + 16 >= dev->height || x + strlen(text) * 8 >= dev->width) + if (y + 16 >= dev->height || x + strlen(text) * 8 >= width) return; /* Print stream time */ - for (line = y; line < y + 16; line++) { - int j = 0; - char *pos = basep + line * dev->width * dev->pixelsize + x * dev->pixelsize; - char *s; - - for (s = text; *s; s++) { - u8 chr = font8x16[*s * 16 + line - y]; - int i; - - for (i = 0; i < 7; i++, j++) { - /* Draw white font on black background */ - if (chr & (1 << (7 - i))) - gen_twopix(dev, pos + j * dev->pixelsize, WHITE, (x+y) & 1); - else - gen_twopix(dev, pos + j * dev->pixelsize, TEXT_BLACK, (x+y) & 1); - } - } +#define PRINTSTR(PIXTYPE) do { \ + PIXTYPE fg; \ + PIXTYPE bg; \ + memcpy(&fg, &dev->textfg, sizeof(PIXTYPE)); \ + memcpy(&bg, &dev->textbg, sizeof(PIXTYPE)); \ + \ + for (line = 0; line < 16; line++) { \ + PIXTYPE *pos = (PIXTYPE *)( basep + ((y + line) * width + x) * sizeof(PIXTYPE) ); \ + u8 *s; \ + \ + for (s = text; *s; s++) { \ + u8 chr = font8x16[*s * 16 + line]; \ + \ + pos[0] = (chr & (0x01 << 7) ? fg : bg); \ + pos[1] = (chr & (0x01 << 6) ? fg : bg); \ + pos[2] = (chr & (0x01 << 5) ? fg : bg); \ + pos[3] = (chr & (0x01 << 4) ? fg : bg); \ + pos[4] = (chr & (0x01 << 3) ? fg : bg); \ + pos[5] = (chr & (0x01 << 2) ? fg : bg); \ + pos[6] = (chr & (0x01 << 1) ? fg : bg); \ + pos[7] = (chr & (0x01 << 0) ? fg : bg); \ + \ + pos += 8; \ + } \ + } \ +} while (0) + + switch (dev->pixelsize) { + case 2: + PRINTSTR(u16); break; + case 4: + PRINTSTR(u32); break; + case 3: + PRINTSTR(x24); break; } } @@ -570,6 +592,9 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) /* Updates stream time */ + gen_twopix(dev, (u8 *)&dev->textbg, TEXT_BLACK, /*odd=*/ 0); + gen_twopix(dev, (u8 *)&dev->textfg, WHITE, /*odd=*/ 0); + dev->ms += jiffies_to_msecs(jiffies - dev->jiffies); dev->jiffies = jiffies; ms = dev->ms; -- cgit v1.2.3 From 10ce84415836d3fbf907367b9eb595a0dfd6ccb1 Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Fri, 2 Nov 2012 09:10:31 -0300 Subject: [media] vivi: vivi_dev->line[] was not aligned Though dev->line[] is u8 array we work with it as with u16, u24 or u32 pixels, and also pass it to memcpy() and it's better to align it to at least 4. Before the patch, on x86 offsetof(vivi_dev, line) was 1003 and after patch it is 1004. There is slight performance increase, but I think is is slight, only because we start copying not from line[0]: ---- 8< ---- drivers/media/platform/vivi.c static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) { ... for (h = 0; h < hmax; h++) memcpy(vbuf + h * wmax * dev->pixelsize, dev->line + (dev->mv_count % wmax) * dev->pixelsize, wmax * dev->pixelsize); before: # cmdline : /home/kirr/local/perf/bin/perf record -g -a sleep 20 # # Samples: 49K of event 'cycles' # Event count (approx.): 16799780016 # # Overhead Command Shared Object # ........ ............... .................... # 27.51% rawv libc-2.13.so [.] __memcpy_ssse3 23.77% vivi-* [kernel.kallsyms] [k] memcpy 9.96% Xorg [unknown] [.] 0xa76f5e12 4.94% vivi-* [vivi] [k] gen_text.constprop.6 4.44% rawv [vivi] [k] gen_twopix 3.17% vivi-* [vivi] [k] vivi_fillbuff 2.45% rawv [vivi] [k] precalculate_line 1.20% swapper [kernel.kallsyms] [k] read_hpet 23.77% vivi-* [kernel.kallsyms] [k] memcpy | --- memcpy | |--99.28%-- vivi_fillbuff | vivi_thread | kthread | ret_from_kernel_thread --0.72%-- [...] after: # cmdline : /home/kirr/local/perf/bin/perf record -g -a sleep 20 # # Samples: 49K of event 'cycles' # Event count (approx.): 16475832370 # # Overhead Command Shared Object # ........ ............... ...................... # 29.07% rawv libc-2.13.so [.] __memcpy_ssse3 20.57% vivi-* [kernel.kallsyms] [k] memcpy 10.20% Xorg [unknown] [.] 0xa7301494 5.16% vivi-* [vivi] [k] gen_text.constprop.6 4.43% rawv [vivi] [k] gen_twopix 4.36% vivi-* [vivi] [k] vivi_fillbuff 2.42% rawv [vivi] [k] precalculate_line 1.33% swapper [kernel.kallsyms] [k] read_hpet Signed-off-by: Kirill Smelkov Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index 3801a8d00946..a3090f0312c1 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -237,7 +237,7 @@ struct vivi_dev { unsigned int field_count; u8 bars[9][3]; - u8 line[MAX_WIDTH * 8]; + u8 line[MAX_WIDTH * 8] __attribute__((__aligned__(4))); unsigned int pixelsize; u8 alpha_component; u32 textfg, textbg; -- cgit v1.2.3 From 13908f330f91996b6d59355aa7860553dd1093d5 Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Fri, 2 Nov 2012 09:10:32 -0300 Subject: [media] vivi: Move computations out of vivi_fillbuf linecopy loop The "dev->mvcount % wmax" thing was showing high in profiles (we do it for each line which ~ 500 per frame) ? 000010c0 : ... 0,39 ? 70:???mov 0x3ff4(%edi),%esi 0,22 ? 76:? mov 0x2a0(%edi),%eax 0,30 ? ? mov -0x84(%ebp),%ebx 0,35 ? ? mov %eax,%edx 0,04 ? ? mov -0x7c(%ebp),%ecx 0,35 ? ? sar $0x1f,%edx 0,44 ? ? idivl -0x7c(%ebp) 21,68 ? ? imul %esi,%ecx 0,70 ? ? imul %esi,%ebx 0,52 ? ? add -0x88(%ebp),%ebx 1,65 ? ? mov %ebx,%eax 0,22 ? ? imul %edx,%esi 0,04 ? ? lea 0x3f4(%edi,%esi,1),%edx 2,18 ? ?? call vivi_fillbuff+0xa6 0,74 ? ? addl $0x1,-0x80(%ebp) 62,69 ? ? mov -0x7c(%ebp),%edx 1,18 ? ? mov -0x80(%ebp),%ecx 0,35 ? ? add %edx,-0x84(%ebp) 0,61 ? ? cmp %ecx,-0x8c(%ebp) 0,22 ? ???jne 70 so since all variables stay the same for all iterations let's move computations out of the loop: the abovementioned division and "width*pixelsize" too before: # cmdline : /home/kirr/local/perf/bin/perf record -g -a sleep 20 # # Samples: 49K of event 'cycles' # Event count (approx.): 16475832370 # # Overhead Command Shared Object # ........ ............... ...................... # 29.07% rawv libc-2.13.so [.] __memcpy_ssse3 20.57% vivi-* [kernel.kallsyms] [k] memcpy 10.20% Xorg [unknown] [.] 0xa7301494 5.16% vivi-* [vivi] [k] gen_text.constprop.6 4.43% rawv [vivi] [k] gen_twopix 4.36% vivi-* [vivi] [k] vivi_fillbuff 2.42% rawv [vivi] [k] precalculate_line 1.33% swapper [kernel.kallsyms] [k] read_hpet after: # cmdline : /home/kirr/local/perf/bin/perf record -g -a sleep 20 # # Samples: 46K of event 'cycles' # Event count (approx.): 15574200568 # # Overhead Command Shared Object # ........ ............... .................... # 27.99% rawv libc-2.13.so [.] __memcpy_ssse3 23.29% vivi-* [kernel.kallsyms] [k] memcpy 10.30% Xorg [unknown] [.] 0xa75c98f8 5.34% vivi-* [vivi] [k] gen_text.constprop.6 4.61% rawv [vivi] [k] gen_twopix 2.64% rawv [vivi] [k] precalculate_line 1.37% swapper [kernel.kallsyms] [k] read_hpet 0.79% Xorg [kernel.kallsyms] [k] read_hpet 0.64% Xorg [kernel.kallsyms] [k] unix_poll 0.45% Xorg [kernel.kallsyms] [k] fget_light 0.43% rawv libxcb.so.1.1.0 [.] 0x0000aae9 0.40% runsv [kernel.kallsyms] [k] ext2_try_to_allocate 0.36% Xorg [kernel.kallsyms] [k] _raw_spin_lock_irqsave 0.31% vivi-* [vivi] [k] vivi_fillbuff (i.e. vivi_fillbuff own overhead is almost gone) Signed-off-by: Kirill Smelkov Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivi.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index a3090f0312c1..ec832e9461a8 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -574,21 +574,22 @@ static void gen_text(struct vivi_dev *dev, char *basep, static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) { - int wmax = dev->width; + int stride = dev->width * dev->pixelsize; int hmax = dev->height; void *vbuf = vb2_plane_vaddr(&buf->vb, 0); unsigned ms; char str[100]; int h, line = 1; + u8 *linestart; s32 gain; if (!vbuf) return; + linestart = dev->line + (dev->mv_count % dev->width) * dev->pixelsize; + for (h = 0; h < hmax; h++) - memcpy(vbuf + h * wmax * dev->pixelsize, - dev->line + (dev->mv_count % wmax) * dev->pixelsize, - wmax * dev->pixelsize); + memcpy(vbuf + h * stride, linestart, stride); /* Updates stream time */ -- cgit v1.2.3 From d40fbf8d52ae6c9b7fe9d76eeab624afc3a3f1ea Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Fri, 2 Nov 2012 09:10:33 -0300 Subject: [media] vivi: Optimize precalculate_line() precalculate_line() is not very high on profile, but it calls expensive gen_twopix(), so let's polish it too: call gen_twopix() only once for every color bar and then distribute the result. before: # cmdline : /home/kirr/local/perf/bin/perf record -g -a sleep 20 # # Samples: 46K of event 'cycles' # Event count (approx.): 15574200568 # # Overhead Command Shared Object # ........ ............... .................... # 27.99% rawv libc-2.13.so [.] __memcpy_ssse3 23.29% vivi-* [kernel.kallsyms] [k] memcpy 10.30% Xorg [unknown] [.] 0xa75c98f8 5.34% vivi-* [vivi] [k] gen_text.constprop.6 4.61% rawv [vivi] [k] gen_twopix 2.64% rawv [vivi] [k] precalculate_line 1.37% swapper [kernel.kallsyms] [k] read_hpet after: # cmdline : /home/kirr/local/perf/bin/perf record -g -a sleep 20 # # Samples: 45K of event 'cycles' # Event count (approx.): 15561769214 # # Overhead Command Shared Object # ........ ............... .................... # 30.73% rawv libc-2.13.so [.] __memcpy_ssse3 26.78% vivi-* [kernel.kallsyms] [k] memcpy 10.68% Xorg [unknown] [.] 0xa73015e9 5.55% vivi-* [vivi] [k] gen_text.constprop.6 1.36% swapper [kernel.kallsyms] [k] read_hpet 0.96% Xorg [kernel.kallsyms] [k] read_hpet ... 0.16% rawv [vivi] [k] precalculate_line ... 0.14% rawv [vivi] [k] gen_twopix (i.e. gen_twopix and precalculate_line overheads are almost gone) Signed-off-by: Kirill Smelkov Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivi.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index ec832e9461a8..7abb046e0fa5 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -512,12 +512,22 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos, bool odd) static void precalculate_line(struct vivi_dev *dev) { - int w; - - for (w = 0; w < dev->width * 2; w++) { - int colorpos = w / (dev->width / 8) % 8; - - gen_twopix(dev, dev->line + w * dev->pixelsize, colorpos, w & 1); + unsigned pixsize = dev->pixelsize; + unsigned pixsize2 = 2*pixsize; + int colorpos; + u8 *pos; + + for (colorpos = 0; colorpos < 16; ++colorpos) { + u8 pix[8]; + int wstart = colorpos * dev->width / 8; + int wend = (colorpos+1) * dev->width / 8; + int w; + + gen_twopix(dev, &pix[0], colorpos % 8, 0); + gen_twopix(dev, &pix[pixsize], colorpos % 8, 1); + + for (w = wstart/2*2, pos = dev->line + w*pixsize; w < wend; w += 2, pos += pixsize2) + memcpy(pos, pix, pixsize2); } } -- cgit v1.2.3 From 70ef69915b1fba4ad85aebe530caf156a144c2e5 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 2 Nov 2012 09:13:54 -0300 Subject: [media] rc: Make probe cleanup goto labels more verbose Before, labels were simply numbered. Now, the labels are named after the cleanup action they'll perform (first), based on how the winbond-cir driver does it. This makes the code a bit more clear and makes changes in the ordering of labels easier to review. This change is applied only to the rc drivers that do significant cleanup in their probe functions: ati-remote, ene-ir, fintek-cir, gpio-ir-recv, ite-cir, nuvoton-cir. This commit should not change any code, it just renames goto labels. [mchehab@redhat.com: removed changes at gpio-ir-recv.c, due to merge conflicts] Signed-off-by: Matthijs Kooijman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ati_remote.c | 27 ++++++++++++++++----------- drivers/media/rc/ene_ir.c | 20 ++++++++++---------- drivers/media/rc/fintek-cir.c | 20 ++++++++++---------- drivers/media/rc/ite-cir.c | 18 +++++++++--------- drivers/media/rc/nuvoton-cir.c | 30 +++++++++++++++--------------- 5 files changed, 60 insertions(+), 55 deletions(-) (limited to 'drivers') diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index 2d6fb26a0170..4d6a63fe6c5e 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -872,11 +872,11 @@ static int ati_remote_probe(struct usb_interface *interface, ati_remote = kzalloc(sizeof (struct ati_remote), GFP_KERNEL); rc_dev = rc_allocate_device(); if (!ati_remote || !rc_dev) - goto fail1; + goto exit_free_dev_rdev; /* Allocate URB buffers, URBs */ if (ati_remote_alloc_buffers(udev, ati_remote)) - goto fail2; + goto exit_free_buffers; ati_remote->endpoint_in = endpoint_in; ati_remote->endpoint_out = endpoint_out; @@ -924,12 +924,12 @@ static int ati_remote_probe(struct usb_interface *interface, /* Device Hardware Initialization - fills in ati_remote->idev from udev. */ err = ati_remote_initialize(ati_remote); if (err) - goto fail3; + goto exit_kill_urbs; /* Set up and register rc device */ err = rc_register_device(ati_remote->rdev); if (err) - goto fail3; + goto exit_kill_urbs; /* use our delay for rc_dev */ ati_remote->rdev->input_dev->rep[REP_DELAY] = repeat_delay; @@ -939,7 +939,7 @@ static int ati_remote_probe(struct usb_interface *interface, input_dev = input_allocate_device(); if (!input_dev) { err = -ENOMEM; - goto fail4; + goto exit_unregister_device; } ati_remote->idev = input_dev; @@ -947,19 +947,24 @@ static int ati_remote_probe(struct usb_interface *interface, err = input_register_device(input_dev); if (err) - goto fail5; + goto exit_free_input_device; } usb_set_intfdata(interface, ati_remote); return 0; - fail5: input_free_device(input_dev); - fail4: rc_unregister_device(rc_dev); + exit_free_input_device: + input_free_device(input_dev); + exit_unregister_device: + rc_unregister_device(rc_dev); rc_dev = NULL; - fail3: usb_kill_urb(ati_remote->irq_urb); + exit_kill_urbs: + usb_kill_urb(ati_remote->irq_urb); usb_kill_urb(ati_remote->out_urb); - fail2: ati_remote_free_buffers(ati_remote); - fail1: rc_free_device(rc_dev); + exit_free_buffers: + ati_remote_free_buffers(ati_remote); + exit_free_dev_rdev: + rc_free_device(rc_dev); kfree(ati_remote); return err; } diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index 22231dd4f62b..f7fdfea49ab1 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -1003,7 +1003,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL); rdev = rc_allocate_device(); if (!dev || !rdev) - goto failure; + goto exit_free_dev_rdev; /* validate resources */ error = -ENODEV; @@ -1014,10 +1014,10 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) if (!pnp_port_valid(pnp_dev, 0) || pnp_port_len(pnp_dev, 0) < ENE_IO_SIZE) - goto failure; + goto exit_free_dev_rdev; if (!pnp_irq_valid(pnp_dev, 0)) - goto failure; + goto exit_free_dev_rdev; spin_lock_init(&dev->hw_lock); @@ -1033,7 +1033,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) /* detect hardware version and features */ error = ene_hw_detect(dev); if (error) - goto failure; + goto exit_free_dev_rdev; if (!dev->hw_learning_and_tx_capable && txsim) { dev->hw_learning_and_tx_capable = true; @@ -1078,27 +1078,27 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) /* claim the resources */ error = -EBUSY; if (!request_region(dev->hw_io, ENE_IO_SIZE, ENE_DRIVER_NAME)) { - goto failure; + goto exit_free_dev_rdev; } dev->irq = pnp_irq(pnp_dev, 0); if (request_irq(dev->irq, ene_isr, IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev)) { - goto failure2; + goto exit_release_hw_io; } error = rc_register_device(rdev); if (error < 0) - goto failure3; + goto exit_free_irq; pr_notice("driver has been successfully loaded\n"); return 0; -failure3: +exit_free_irq: free_irq(dev->irq, dev); -failure2: +exit_release_hw_io: release_region(dev->hw_io, ENE_IO_SIZE); -failure: +exit_free_dev_rdev: rc_free_device(rdev); kfree(dev); return error; diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index 936c3f79b62c..3d5e57cacf31 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -500,18 +500,18 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id /* input device for IR remote (and tx) */ rdev = rc_allocate_device(); if (!rdev) - goto failure; + goto exit_free_dev_rdev; ret = -ENODEV; /* validate pnp resources */ if (!pnp_port_valid(pdev, 0)) { dev_err(&pdev->dev, "IR PNP Port not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } if (!pnp_irq_valid(pdev, 0)) { dev_err(&pdev->dev, "IR PNP IRQ not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } fintek->cir_addr = pnp_port_start(pdev, 0); @@ -528,7 +528,7 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id ret = fintek_hw_detect(fintek); if (ret) - goto failure; + goto exit_free_dev_rdev; /* Initialize CIR & CIR Wake Logical Devices */ fintek_config_mode_enable(fintek); @@ -561,15 +561,15 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id /* now claim resources */ if (!request_region(fintek->cir_addr, fintek->cir_port_len, FINTEK_DRIVER_NAME)) - goto failure; + goto exit_free_dev_rdev; if (request_irq(fintek->cir_irq, fintek_cir_isr, IRQF_SHARED, FINTEK_DRIVER_NAME, (void *)fintek)) - goto failure2; + goto exit_free_cir_addr; ret = rc_register_device(rdev); if (ret) - goto failure3; + goto exit_free_irq; device_init_wakeup(&pdev->dev, true); fintek->rdev = rdev; @@ -579,11 +579,11 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id return 0; -failure3: +exit_free_irq: free_irq(fintek->cir_irq, fintek); -failure2: +exit_free_cir_addr: release_region(fintek->cir_addr, fintek->cir_port_len); -failure: +exit_free_dev_rdev: rc_free_device(rdev); kfree(fintek); diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index 5e5a7f2b8184..8e0e661b5ef9 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -1472,7 +1472,7 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id /* input device for IR remote (and tx) */ rdev = rc_allocate_device(); if (!rdev) - goto failure; + goto exit_free_dev_rdev; itdev->rdev = rdev; ret = -ENODEV; @@ -1498,12 +1498,12 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id if (!pnp_port_valid(pdev, io_rsrc_no) || pnp_port_len(pdev, io_rsrc_no) != dev_desc->io_region_size) { dev_err(&pdev->dev, "IR PNP Port not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } if (!pnp_irq_valid(pdev, 0)) { dev_err(&pdev->dev, "PNP IRQ not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } /* store resource values */ @@ -1595,25 +1595,25 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id /* now claim resources */ if (!request_region(itdev->cir_addr, dev_desc->io_region_size, ITE_DRIVER_NAME)) - goto failure; + goto exit_free_dev_rdev; if (request_irq(itdev->cir_irq, ite_cir_isr, IRQF_SHARED, ITE_DRIVER_NAME, (void *)itdev)) - goto failure2; + goto exit_release_cir_addr; ret = rc_register_device(rdev); if (ret) - goto failure3; + goto exit_free_irq; ite_pr(KERN_NOTICE, "driver has been successfully loaded\n"); return 0; -failure3: +exit_free_irq: free_irq(itdev->cir_irq, itdev); -failure2: +exit_release_cir_addr: release_region(itdev->cir_addr, itdev->params.io_region_size); -failure: +exit_free_dev_rdev: rc_free_device(rdev); kfree(itdev); diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index e4ea89a11eed..3477e231c182 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -986,25 +986,25 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) /* input device for IR remote (and tx) */ rdev = rc_allocate_device(); if (!rdev) - goto failure; + goto exit_free_dev_rdev; ret = -ENODEV; /* validate pnp resources */ if (!pnp_port_valid(pdev, 0) || pnp_port_len(pdev, 0) < CIR_IOREG_LENGTH) { dev_err(&pdev->dev, "IR PNP Port not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } if (!pnp_irq_valid(pdev, 0)) { dev_err(&pdev->dev, "PNP IRQ not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } if (!pnp_port_valid(pdev, 1) || pnp_port_len(pdev, 1) < CIR_IOREG_LENGTH) { dev_err(&pdev->dev, "Wake PNP Port not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } nvt->cir_addr = pnp_port_start(pdev, 0); @@ -1027,7 +1027,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) ret = nvt_hw_detect(nvt); if (ret) - goto failure; + goto exit_free_dev_rdev; /* Initialize CIR & CIR Wake Logical Devices */ nvt_efm_enable(nvt); @@ -1070,23 +1070,23 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) /* now claim resources */ if (!request_region(nvt->cir_addr, CIR_IOREG_LENGTH, NVT_DRIVER_NAME)) - goto failure; + goto exit_free_dev_rdev; if (request_irq(nvt->cir_irq, nvt_cir_isr, IRQF_SHARED, NVT_DRIVER_NAME, (void *)nvt)) - goto failure2; + goto exit_release_cir_addr; if (!request_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH, NVT_DRIVER_NAME)) - goto failure3; + goto exit_free_irq; if (request_irq(nvt->cir_wake_irq, nvt_cir_wake_isr, IRQF_SHARED, NVT_DRIVER_NAME, (void *)nvt)) - goto failure4; + goto exit_release_cir_wake_addr; ret = rc_register_device(rdev); if (ret) - goto failure5; + goto exit_free_wake_irq; device_init_wakeup(&pdev->dev, true); nvt->rdev = rdev; @@ -1098,15 +1098,15 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) return 0; -failure5: +exit_free_wake_irq: free_irq(nvt->cir_wake_irq, nvt); -failure4: +exit_release_cir_wake_addr: release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH); -failure3: +exit_free_irq: free_irq(nvt->cir_irq, nvt); -failure2: +exit_release_cir_addr: release_region(nvt->cir_addr, CIR_IOREG_LENGTH); -failure: +exit_free_dev_rdev: rc_free_device(rdev); kfree(nvt); -- cgit v1.2.3 From d62b6818477704683d00c680335eff5833bd3906 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 2 Nov 2012 09:13:55 -0300 Subject: [media] rc: Set rdev before irq setup This fixes a problem in fintek-cir and nuvoton-cir where the irq handler would trigger during module load before the rdev member was set, causing a NULL pointer crash. It seems this crash is very reproducible (just bombard the receiver with IR signals during module load), probably because when request_irq is called, any pending intterupt is handled immediately, before request_irq returns and rdev can be set. This same crash was supposed to be fixed by commit 9ef449c6b31bb6a8e6dedc24de475a3b8c79be20 ("[media] rc: Postpone ISR registration"), but the crash was still observed on the nuvoton-cir driver. This commit was tested on nuvoton-cir only. Cc: Jarod Wilson Signed-off-by: Matthijs Kooijman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/fintek-cir.c | 4 +++- drivers/media/rc/nuvoton-cir.c | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index 3d5e57cacf31..5eefe65760a4 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -557,6 +557,8 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */ rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD); + fintek->rdev = rdev; + ret = -EBUSY; /* now claim resources */ if (!request_region(fintek->cir_addr, @@ -572,7 +574,7 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id goto exit_free_irq; device_init_wakeup(&pdev->dev, true); - fintek->rdev = rdev; + fit_pr(KERN_NOTICE, "driver has been successfully loaded\n"); if (debug) cir_dump_regs(fintek); diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 3477e231c182..c6441e690dd4 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -1065,6 +1065,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) /* tx bits */ rdev->tx_resolution = XYZ; #endif + nvt->rdev = rdev; ret = -EBUSY; /* now claim resources */ @@ -1089,7 +1090,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) goto exit_free_wake_irq; device_init_wakeup(&pdev->dev, true); - nvt->rdev = rdev; + nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n"); if (debug) { cir_dump_regs(nvt); -- cgit v1.2.3 From 9fa35204dd19eb0e96ee870b7128a8f5da51dbfa Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 2 Nov 2012 09:13:56 -0300 Subject: [media] rc: Call rc_register_device before irq setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should fix a potential race condition, when the irq handler triggers while rc_register_device is still setting up the rdev->raw device. This crash has not been observed in practice, but there should be a very small window where it could occur. Since ir_raw_event_store_with_filter checks if rdev->raw is not NULL before using it, this bug is not triggered if the request_irq triggers a pending irq directly (since rdev->raw will still be NULL then). This commit was tested on nuvoton-cir only. Cc: Jarod Wilson Cc: Maxim Levitsky Cc: David Härdeman Signed-off-by: Matthijs Kooijman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ene_ir.c | 14 +++++++------- drivers/media/rc/ite-cir.c | 14 +++++++------- drivers/media/rc/nuvoton-cir.c | 14 +++++++------- drivers/media/rc/winbond-cir.c | 14 +++++++------- 4 files changed, 28 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index f7fdfea49ab1..e601166c1edb 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -1075,10 +1075,14 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) device_set_wakeup_capable(&pnp_dev->dev, true); device_set_wakeup_enable(&pnp_dev->dev, true); + error = rc_register_device(rdev); + if (error < 0) + goto exit_free_dev_rdev; + /* claim the resources */ error = -EBUSY; if (!request_region(dev->hw_io, ENE_IO_SIZE, ENE_DRIVER_NAME)) { - goto exit_free_dev_rdev; + goto exit_unregister_device; } dev->irq = pnp_irq(pnp_dev, 0); @@ -1087,17 +1091,13 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) goto exit_release_hw_io; } - error = rc_register_device(rdev); - if (error < 0) - goto exit_free_irq; - pr_notice("driver has been successfully loaded\n"); return 0; -exit_free_irq: - free_irq(dev->irq, dev); exit_release_hw_io: release_region(dev->hw_io, ENE_IO_SIZE); +exit_unregister_device: + rc_unregister_device(rdev); exit_free_dev_rdev: rc_free_device(rdev); kfree(dev); diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index 8e0e661b5ef9..e810846fada4 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -1591,28 +1591,28 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id rdev->driver_name = ITE_DRIVER_NAME; rdev->map_name = RC_MAP_RC6_MCE; + ret = rc_register_device(rdev); + if (ret) + goto exit_free_dev_rdev; + ret = -EBUSY; /* now claim resources */ if (!request_region(itdev->cir_addr, dev_desc->io_region_size, ITE_DRIVER_NAME)) - goto exit_free_dev_rdev; + goto exit_unregister_device; if (request_irq(itdev->cir_irq, ite_cir_isr, IRQF_SHARED, ITE_DRIVER_NAME, (void *)itdev)) goto exit_release_cir_addr; - ret = rc_register_device(rdev); - if (ret) - goto exit_free_irq; - ite_pr(KERN_NOTICE, "driver has been successfully loaded\n"); return 0; -exit_free_irq: - free_irq(itdev->cir_irq, itdev); exit_release_cir_addr: release_region(itdev->cir_addr, itdev->params.io_region_size); +exit_unregister_device: + rc_unregister_device(rdev); exit_free_dev_rdev: rc_free_device(rdev); kfree(itdev); diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index c6441e690dd4..6cf43cc237eb 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -1067,11 +1067,15 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) #endif nvt->rdev = rdev; + ret = rc_register_device(rdev); + if (ret) + goto exit_free_dev_rdev; + ret = -EBUSY; /* now claim resources */ if (!request_region(nvt->cir_addr, CIR_IOREG_LENGTH, NVT_DRIVER_NAME)) - goto exit_free_dev_rdev; + goto exit_unregister_device; if (request_irq(nvt->cir_irq, nvt_cir_isr, IRQF_SHARED, NVT_DRIVER_NAME, (void *)nvt)) @@ -1085,10 +1089,6 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) NVT_DRIVER_NAME, (void *)nvt)) goto exit_release_cir_wake_addr; - ret = rc_register_device(rdev); - if (ret) - goto exit_free_wake_irq; - device_init_wakeup(&pdev->dev, true); nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n"); @@ -1099,14 +1099,14 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) return 0; -exit_free_wake_irq: - free_irq(nvt->cir_wake_irq, nvt); exit_release_cir_wake_addr: release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH); exit_free_irq: free_irq(nvt->cir_irq, nvt); exit_release_cir_addr: release_region(nvt->cir_addr, CIR_IOREG_LENGTH); +exit_unregister_device: + rc_unregister_device(rdev); exit_free_dev_rdev: rc_free_device(rdev); kfree(nvt); diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index 7f3c476dde05..553d1cdc439f 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -1093,11 +1093,15 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) data->dev->rx_resolution = US_TO_NS(2); data->dev->allowed_protos = RC_BIT_ALL; + err = rc_register_device(data->dev); + if (err) + goto exit_free_rc; + if (!request_region(data->wbase, WAKEUP_IOMEM_LEN, DRVNAME)) { dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", data->wbase, data->wbase + WAKEUP_IOMEM_LEN - 1); err = -EBUSY; - goto exit_free_rc; + goto exit_unregister_device; } if (!request_region(data->ebase, EHFUNC_IOMEM_LEN, DRVNAME)) { @@ -1122,24 +1126,20 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) goto exit_release_sbase; } - err = rc_register_device(data->dev); - if (err) - goto exit_free_irq; - device_init_wakeup(&device->dev, 1); wbcir_init_hw(data); return 0; -exit_free_irq: - free_irq(data->irq, device); exit_release_sbase: release_region(data->sbase, SP_IOMEM_LEN); exit_release_ebase: release_region(data->ebase, EHFUNC_IOMEM_LEN); exit_release_wbase: release_region(data->wbase, WAKEUP_IOMEM_LEN); +exit_unregister_device: + rc_unregister_device(data->dev); exit_free_rc: rc_free_device(data->dev); exit_unregister_led: -- cgit v1.2.3 From 6d569502b49849ae392a4453bfe3a2a973cc95d2 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Sun, 4 Nov 2012 16:40:22 -0300 Subject: [media] staging/media: Use dev_ printks in go7007/go7007-driver.c fixed below checkpatch warning. - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/go7007-driver.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c index ece2dd146487..c9dfc75d1d2c 100644 --- a/drivers/staging/media/go7007/go7007-driver.c +++ b/drivers/staging/media/go7007/go7007-driver.c @@ -201,7 +201,8 @@ static int init_i2c_module(struct i2c_adapter *adapter, const char *type, if (v4l2_i2c_new_subdev(v4l2_dev, adapter, type, addr, NULL)) return 0; - printk(KERN_INFO "go7007: probing for module i2c:%s failed\n", type); + dev_info(&adapter->dev, + "go7007: probing for module i2c:%s failed\n", type); return -1; } @@ -217,7 +218,7 @@ int go7007_register_encoder(struct go7007 *go) { int i, ret; - printk(KERN_INFO "go7007: registering new %s\n", go->name); + dev_info(go->dev, "go7007: registering new %s\n", go->name); mutex_lock(&go->hw_lock); ret = go7007_init_encoder(go); -- cgit v1.2.3 From 6c629edcde1610635a7af93dbb181d5081afc194 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Sun, 4 Nov 2012 16:40:51 -0300 Subject: [media] staging/media: Use dev_ printks in go7007/wis-sony-tuner.c fixed below checkpatch warning. - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... - WARNING: Prefer netdev_dbg(netdev, ... then dev_dbg(dev, ... then pr_debug(... to printk(KERN_DEBUG ... - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/wis-sony-tuner.c | 86 +++++++++++++-------------- 1 file changed, 42 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/go7007/wis-sony-tuner.c b/drivers/staging/media/go7007/wis-sony-tuner.c index 1291ab79d2af..5d7ff8c81d6d 100644 --- a/drivers/staging/media/go7007/wis-sony-tuner.c +++ b/drivers/staging/media/go7007/wis-sony-tuner.c @@ -95,8 +95,8 @@ static int set_freq(struct i2c_client *client, int freq) band_name = "UHF"; band_select = tun->UHF; } - printk(KERN_DEBUG "wis-sony-tuner: tuning to frequency %d.%04d (%s)\n", - freq / 16, (freq % 16) * 625, band_name); + dev_dbg(&client->dev, "tuning to frequency %d.%04d (%s)\n", + freq / 16, (freq % 16) * 625, band_name); n = freq + tun->IFPCoff; buffer[0] = n >> 8; @@ -288,16 +288,16 @@ static int mpx_setup(struct i2c_client *client) u8 buf1[3], buf2[2]; struct i2c_msg msgs[2]; - printk(KERN_DEBUG "wis-sony-tuner: MPX registers: %04x %04x " - "%04x %04x %04x %04x %04x %04x\n", - mpx_audio_modes[t->mpxmode].modus, - source, - mpx_audio_modes[t->mpxmode].acb, - mpx_audio_modes[t->mpxmode].fm_prescale, - mpx_audio_modes[t->mpxmode].nicam_prescale, - mpx_audio_modes[t->mpxmode].scart_prescale, - mpx_audio_modes[t->mpxmode].system, - mpx_audio_modes[t->mpxmode].volume); + dev_dbg(&client->dev, + "MPX registers: %04x %04x %04x %04x %04x %04x %04x %04x\n", + mpx_audio_modes[t->mpxmode].modus, + source, + mpx_audio_modes[t->mpxmode].acb, + mpx_audio_modes[t->mpxmode].fm_prescale, + mpx_audio_modes[t->mpxmode].nicam_prescale, + mpx_audio_modes[t->mpxmode].scart_prescale, + mpx_audio_modes[t->mpxmode].system, + mpx_audio_modes[t->mpxmode].volume); buf1[0] = 0x11; buf1[1] = 0x00; buf1[2] = 0x7e; @@ -310,14 +310,14 @@ static int mpx_setup(struct i2c_client *client) msgs[1].len = 2; msgs[1].buf = buf2; i2c_transfer(client->adapter, msgs, 2); - printk(KERN_DEBUG "wis-sony-tuner: MPX system: %02x%02x\n", - buf2[0], buf2[1]); + dev_dbg(&client->dev, "MPX system: %02x%02x\n", + buf2[0], buf2[1]); buf1[0] = 0x11; buf1[1] = 0x02; buf1[2] = 0x00; i2c_transfer(client->adapter, msgs, 2); - printk(KERN_DEBUG "wis-sony-tuner: MPX status: %02x%02x\n", - buf2[0], buf2[1]); + dev_dbg(&client->dev, "MPX status: %02x%02x\n", + buf2[0], buf2[1]); } #endif return 0; @@ -375,8 +375,7 @@ static int set_if(struct i2c_client *client) t->mpxmode = force_mpx_mode; else t->mpxmode = default_mpx_mode; - printk(KERN_DEBUG "wis-sony-tuner: setting MPX to mode %d\n", - t->mpxmode); + dev_dbg(&client->dev, "setting MPX to mode %d\n", t->mpxmode); mpx_setup(client); return 0; @@ -401,8 +400,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) if (t->type >= 0) { if (t->type != *type) - printk(KERN_ERR "wis-sony-tuner: type already " - "set to %d, ignoring request for %d\n", + dev_err(&client->dev, + "type already set to %d, ignoring request for %d\n", t->type, *type); break; } @@ -414,28 +413,28 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) case 'B': case 'g': case 'G': - printk(KERN_INFO "wis-sony-tuner: forcing " - "tuner to PAL-B/G bands\n"); + dev_info(&client->dev, + "forcing tuner to PAL-B/G bands\n"); force_band = V4L2_STD_PAL_BG; break; case 'i': case 'I': - printk(KERN_INFO "wis-sony-tuner: forcing " - "tuner to PAL-I band\n"); + dev_info(&client->dev, + "forcing tuner to PAL-I band\n"); force_band = V4L2_STD_PAL_I; break; case 'd': case 'D': case 'k': case 'K': - printk(KERN_INFO "wis-sony-tuner: forcing " - "tuner to PAL-D/K bands\n"); + dev_info(&client->dev, + "forcing tuner to PAL-D/K bands\n"); force_band = V4L2_STD_PAL_I; break; case 'l': case 'L': - printk(KERN_INFO "wis-sony-tuner: forcing " - "tuner to SECAM-L band\n"); + dev_info(&client->dev, + "forcing tuner to SECAM-L band\n"); force_band = V4L2_STD_SECAM_L; break; default: @@ -455,14 +454,15 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) t->std = V4L2_STD_NTSC_M; break; default: - printk(KERN_ERR "wis-sony-tuner: tuner type %d is not " - "supported by this module\n", *type); + dev_err(&client->dev, + "tuner type %d is not supported by this module\n", + *type); break; } if (type >= 0) - printk(KERN_INFO - "wis-sony-tuner: type set to %d (%s)\n", - t->type, sony_tuners[t->type - 200].name); + dev_info(&clinet->dev, + "type set to %d (%s)\n", + t->type, sony_tuners[t->type - 200].name); break; } #endif @@ -544,9 +544,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) if (force_band && (*std & force_band) != *std && *std != V4L2_STD_PAL && *std != V4L2_STD_SECAM) { - printk(KERN_DEBUG "wis-sony-tuner: ignoring " - "requested TV standard in " - "favor of force_band value\n"); + dev_dbg(&client->dev, + "ignoring requested TV standard in favor of force_band value\n"); t->std = force_band; } else if (*std & V4L2_STD_PAL_BG) { /* default */ t->std = V4L2_STD_PAL_BG; @@ -557,8 +556,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) } else if (*std & V4L2_STD_SECAM_L) { t->std = V4L2_STD_SECAM_L; } else { - printk(KERN_ERR "wis-sony-tuner: TV standard " - "not supported\n"); + dev_err(&client->dev, + "TV standard not supported\n"); *std = 0; /* hack to indicate EINVAL */ break; } @@ -567,15 +566,15 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) break; case TUNER_SONY_BTF_PK467Z: if (!(*std & V4L2_STD_NTSC_M_JP)) { - printk(KERN_ERR "wis-sony-tuner: TV standard " - "not supported\n"); + dev_err(&client->dev, + "TV standard not supported\n"); *std = 0; /* hack to indicate EINVAL */ } break; case TUNER_SONY_BTF_PB463Z: if (!(*std & V4L2_STD_NTSC_M)) { - printk(KERN_ERR "wis-sony-tuner: TV standard " - "not supported\n"); + dev_err(&client->dev, + "TV standard not supported\n"); *std = 0; /* hack to indicate EINVAL */ } break; @@ -673,8 +672,7 @@ static int wis_sony_tuner_probe(struct i2c_client *client, t->audmode = V4L2_TUNER_MODE_STEREO; i2c_set_clientdata(client, t); - printk(KERN_DEBUG - "wis-sony-tuner: initializing tuner at address %d on %s\n", + dev_dbg(&client->dev, "initializing tuner at address %d on %s\n", client->addr, adapter->name); return 0; -- cgit v1.2.3 From b11558a3026bd01436d08b7b8f92da301116c669 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Mon, 5 Nov 2012 07:34:42 -0300 Subject: [media] staging/media: Use dev_ printks in go7007/s2250-loader.c fixed below checkpatch warnings. - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/s2250-loader.c | 35 +++++++++++++++-------------- 1 file changed, 18 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/go7007/s2250-loader.c b/drivers/staging/media/go7007/s2250-loader.c index f1bd159ac195..f57eb3beb341 100644 --- a/drivers/staging/media/go7007/s2250-loader.c +++ b/drivers/staging/media/go7007/s2250-loader.c @@ -55,16 +55,16 @@ static int s2250loader_probe(struct usb_interface *interface, usbdev = usb_get_dev(interface_to_usbdev(interface)); if (!usbdev) { - printk(KERN_ERR "Enter s2250loader_probe failed\n"); + dev_err(&interface->dev, "Enter s2250loader_probe failed\n"); return -1; } - printk(KERN_INFO "Enter s2250loader_probe 2.6 kernel\n"); - printk(KERN_INFO "vendor id 0x%x, device id 0x%x devnum:%d\n", - usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, - usbdev->devnum); + dev_info(&interface->dev, "Enter s2250loader_probe 2.6 kernel\n"); + dev_info(&interface->dev, "vendor id 0x%x, device id 0x%x devnum:%d\n", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, + usbdev->devnum); if (usbdev->descriptor.bNumConfigurations != 1) { - printk(KERN_ERR "can't handle multiple config\n"); + dev_err(&interface->dev, "can't handle multiple config\n"); return -1; } mutex_lock(&s2250_dev_table_mutex); @@ -75,31 +75,32 @@ static int s2250loader_probe(struct usb_interface *interface, } if (minor < 0 || minor >= MAX_DEVICES) { - printk(KERN_ERR "Invalid minor: %d\n", minor); + dev_err(&interface->dev, "Invalid minor: %d\n", minor); goto failed; } /* Allocate dev data structure */ s = kmalloc(sizeof(device_extension_t), GFP_KERNEL); if (s == NULL) { - printk(KERN_ERR "Out of memory\n"); + dev_err(&interface->dev, "Out of memory\n"); goto failed; } s2250_dev_table[minor] = s; - printk(KERN_INFO "s2250loader_probe: Device %d on Bus %d Minor %d\n", - usbdev->devnum, usbdev->bus->busnum, minor); + dev_info(&interface->dev, + "s2250loader_probe: Device %d on Bus %d Minor %d\n", + usbdev->devnum, usbdev->bus->busnum, minor); memset(s, 0, sizeof(device_extension_t)); s->usbdev = usbdev; - printk(KERN_INFO "loading 2250 loader\n"); + dev_info(&interface->dev, "loading 2250 loader\n"); kref_init(&(s->kref)); mutex_unlock(&s2250_dev_table_mutex); if (request_firmware(&fw, S2250_LOADER_FIRMWARE, &usbdev->dev)) { - printk(KERN_ERR + dev_err(&interface->dev, "s2250: unable to load firmware from file \"%s\"\n", S2250_LOADER_FIRMWARE); goto failed2; @@ -107,12 +108,12 @@ static int s2250loader_probe(struct usb_interface *interface, ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2); release_firmware(fw); if (0 != ret) { - printk(KERN_ERR "loader download failed\n"); + dev_err(&interface->dev, "loader download failed\n"); goto failed2; } if (request_firmware(&fw, S2250_FIRMWARE, &usbdev->dev)) { - printk(KERN_ERR + dev_err(&interface->dev, "s2250: unable to load firmware from file \"%s\"\n", S2250_FIRMWARE); goto failed2; @@ -120,7 +121,7 @@ static int s2250loader_probe(struct usb_interface *interface, ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2); release_firmware(fw); if (0 != ret) { - printk(KERN_ERR "firmware_s2250 download failed\n"); + dev_err(&interface->dev, "firmware_s2250 download failed\n"); goto failed2; } @@ -133,14 +134,14 @@ failed2: if (s) kref_put(&(s->kref), s2250loader_delete); - printk(KERN_ERR "probe failed\n"); + dev_err(&interface->dev, "probe failed\n"); return -1; } static void s2250loader_disconnect(struct usb_interface *interface) { pdevice_extension_t s; - printk(KERN_INFO "s2250: disconnect\n"); + dev_info(&interface->dev, "s2250: disconnect\n"); s = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); kref_put(&(s->kref), s2250loader_delete); -- cgit v1.2.3 From afc2e8a037885a8ce3a87684d742b9afb18e295c Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Mon, 5 Nov 2012 07:35:06 -0300 Subject: [media] staging/media: Use dev_ or pr_ printks in go7007/go7007-i2c.c fixed below checkpatch warnings. - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... - WARNING: Prefer netdev_dbg(netdev, ... then dev_dbg(dev, ... then pr_debug(... to printk(KERN_DEBUG ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/go7007-i2c.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/go7007/go7007-i2c.c b/drivers/staging/media/go7007/go7007-i2c.c index 6bc82aaeef11..39456a36b2c6 100644 --- a/drivers/staging/media/go7007/go7007-i2c.c +++ b/drivers/staging/media/go7007/go7007-i2c.c @@ -60,10 +60,10 @@ static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, #ifdef GO7007_I2C_DEBUG if (read) - printk(KERN_DEBUG "go7007-i2c: reading 0x%02x on 0x%02x\n", + dev_dbg(go->dev, "go7007-i2c: reading 0x%02x on 0x%02x\n", command, addr); else - printk(KERN_DEBUG + dev_dbg(go->dev, "go7007-i2c: writing 0x%02x to 0x%02x on 0x%02x\n", *data, command, addr); #endif @@ -85,7 +85,7 @@ static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, msleep(100); } if (i == 10) { - printk(KERN_ERR "go7007-i2c: I2C adapter is hung\n"); + dev_err(go->dev, "go7007-i2c: I2C adapter is hung\n"); goto i2c_done; } @@ -119,7 +119,7 @@ static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, msleep(100); } if (i == 10) { - printk(KERN_ERR "go7007-i2c: I2C adapter is hung\n"); + dev_err(go->dev, "go7007-i2c: I2C adapter is hung\n"); goto i2c_done; } @@ -216,7 +216,7 @@ int go7007_i2c_init(struct go7007 *go) go->i2c_adapter.dev.parent = go->dev; i2c_set_adapdata(&go->i2c_adapter, go); if (i2c_add_adapter(&go->i2c_adapter) < 0) { - printk(KERN_ERR + dev_err(go->dev, "go7007-i2c: error: i2c_add_adapter failed\n"); return -1; } -- cgit v1.2.3 From afca99a2bf2cef3a6e5e574cb7bc0e0bdf5b3ffa Mon Sep 17 00:00:00 2001 From: Julian Scheel Date: Mon, 5 Nov 2012 11:51:05 -0300 Subject: [media] tm6000-dvb: Fix module unload dvb_unregister_frontend has to be called before detach. Otherwise the unregister call will segfault. This made tm6000-dvb module unload unusable. Signed-off-by: Julian Scheel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-dvb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/tm6000/tm6000-dvb.c b/drivers/media/usb/tm6000/tm6000-dvb.c index e1f3f66e1e63..9fc1e940a82b 100644 --- a/drivers/media/usb/tm6000/tm6000-dvb.c +++ b/drivers/media/usb/tm6000/tm6000-dvb.c @@ -360,8 +360,8 @@ dvb_dmx_err: dvb_dmx_release(&dvb->demux); frontend_err: if (dvb->frontend) { - dvb_frontend_detach(dvb->frontend); dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); } adapter_err: dvb_unregister_adapter(&dvb->adapter); @@ -384,8 +384,8 @@ static void unregister_dvb(struct tm6000_core *dev) /* mutex_lock(&tm6000_driver.open_close_mutex); */ if (dvb->frontend) { - dvb_frontend_detach(dvb->frontend); dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); } dvb_dmxdev_release(&dvb->dmxdev); -- cgit v1.2.3 From 8ce21ecdf500edfa0123238f40d3fb592a44d9f3 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Tue, 6 Nov 2012 08:33:26 -0300 Subject: [media] Staging/media: fixed spacing coding style in go7007/wis-tw9903.c fixed below checkpatch error. - ERROR: that open brace { should be on the previous line Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/wis-tw9903.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/go7007/wis-tw9903.c b/drivers/staging/media/go7007/wis-tw9903.c index 94071def3bb4..854919e81547 100644 --- a/drivers/staging/media/go7007/wis-tw9903.c +++ b/drivers/staging/media/go7007/wis-tw9903.c @@ -31,8 +31,7 @@ struct wis_tw9903 { int hue; }; -static u8 initial_registers[] = -{ +static u8 initial_registers[] = { 0x02, 0x44, /* input 1, composite */ 0x03, 0x92, /* correct digital format */ 0x04, 0x00, -- cgit v1.2.3 From b2704e1540b297c06efa96851949ed060bd0d2bb Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Tue, 6 Nov 2012 08:34:02 -0300 Subject: [media] Staging/media: Use dev_ printks in go7007/wis-tw9903.c fixed below checkpatch warning. - WARNING: Prefer netdev_dbg(netdev, ... then dev_dbg(dev, ... then pr_debug(... to printk(KERN_DEBUG ... - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/wis-tw9903.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/go7007/wis-tw9903.c b/drivers/staging/media/go7007/wis-tw9903.c index 854919e81547..684ca37f0382 100644 --- a/drivers/staging/media/go7007/wis-tw9903.c +++ b/drivers/staging/media/go7007/wis-tw9903.c @@ -127,8 +127,8 @@ static int wis_tw9903_command(struct i2c_client *client, 0x06, 0xc0, /* reset device */ 0, 0, }; - printk(KERN_DEBUG "vscale is %04x, hscale is %04x\n", - vscale, hscale); + dev_dbg(&client->dev, "vscale is %04x, hscale is %04x\n", + vscale, hscale); /*write_regs(client, regs);*/ break; } @@ -287,12 +287,11 @@ static int wis_tw9903_probe(struct i2c_client *client, dec->hue = 0; i2c_set_clientdata(client, dec); - printk(KERN_DEBUG - "wis-tw9903: initializing TW9903 at address %d on %s\n", + dev_dbg(&client->dev, "initializing TW9903 at address %d on %s\n", client->addr, adapter->name); if (write_regs(client, initial_registers) < 0) { - printk(KERN_ERR "wis-tw9903: error initializing TW9903\n"); + dev_err(&client->dev, "error initializing TW9903\n"); kfree(dec); return -ENODEV; } -- cgit v1.2.3 From 34047bac17471d47989b96450b784f22eaf96223 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Tue, 6 Nov 2012 08:34:20 -0300 Subject: [media] Staging/media: Use dev_ printks in go7007/go7007-v4l2.c fixed below checkpatch warning. - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/go7007-v4l2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index a78133b67de2..0d3713dceba5 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -1811,8 +1811,8 @@ int go7007_v4l2_init(struct go7007 *go) } video_set_drvdata(go->video_dev, go); ++go->ref_count; - printk(KERN_INFO "%s: registered device %s [v4l2]\n", - go->video_dev->name, video_device_node_name(go->video_dev)); + dev_info(go->dev, "registered device %s [v4l2]\n", + video_device_node_name(go->video_dev)); return 0; } -- cgit v1.2.3 From dd442d4d799155b370593b3737956b3709fb8cbb Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Tue, 6 Nov 2012 15:39:54 -0300 Subject: [media] Staging/media: Use dev_ printks in go7007/wis-uda1342.c fixed below checkpatch warning. - WARNING: Prefer netdev_dbg(netdev, ... then dev_dbg(dev, ... then pr_debug(... to printk(KERN_DEBUG ... - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/wis-uda1342.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/go7007/wis-uda1342.c b/drivers/staging/media/go7007/wis-uda1342.c index 05ac798f35f7..582ea120a531 100644 --- a/drivers/staging/media/go7007/wis-uda1342.c +++ b/drivers/staging/media/go7007/wis-uda1342.c @@ -47,8 +47,8 @@ static int wis_uda1342_command(struct i2c_client *client, write_reg(client, 0x00, 0x1241); /* select input 1 */ break; default: - printk(KERN_ERR "wis-uda1342: input %d not supported\n", - *inp); + dev_err(&client->dev, "input %d not supported\n", + *inp); break; } break; @@ -67,8 +67,7 @@ static int wis_uda1342_probe(struct i2c_client *client, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; - printk(KERN_DEBUG - "wis-uda1342: initializing UDA1342 at address %d on %s\n", + dev_dbg(&client->dev, "initializing UDA1342 at address %d on %s\n", client->addr, adapter->name); write_reg(client, 0x00, 0x8000); /* reset registers */ -- cgit v1.2.3 From b91c78e354711ffc8fe26b53be85706dc90918fa Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Tue, 6 Nov 2012 15:40:16 -0300 Subject: [media] Staging/media: fixed spacing coding style in go7007/wis-uda1342.c fixed below checkpatch error. - ERROR: that open brace { should be on the previous line Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/s2250-board.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/go7007/s2250-board.c b/drivers/staging/media/go7007/s2250-board.c index b3974100c6cd..9ba61bd382ac 100644 --- a/drivers/staging/media/go7007/s2250-board.c +++ b/drivers/staging/media/go7007/s2250-board.c @@ -103,8 +103,7 @@ static u16 vid_regs_fp[] = { }; /* PAL specific values */ -static u16 vid_regs_fp_pal[] = -{ +static u16 vid_regs_fp_pal[] = { 0x120, 0x017, 0x121, 0xd22, 0x122, 0x122, -- cgit v1.2.3 From b441d03331cabcf14352465b7b0951f0c8d69481 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Tue, 6 Nov 2012 15:41:26 -0300 Subject: [media] Staging/media: Use dev_ printks in go7007/wis-tw2804.c fixed below checkpatch warning. - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... - WARNING: Prefer netdev_dbg(netdev, ... then dev_dbg(dev, ... then pr_debug(... to printk(KERN_DEBUG ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/wis-tw2804.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/go7007/wis-tw2804.c b/drivers/staging/media/go7007/wis-tw2804.c index d6410ee01be8..290fd8c7bfef 100644 --- a/drivers/staging/media/go7007/wis-tw2804.c +++ b/drivers/staging/media/go7007/wis-tw2804.c @@ -128,30 +128,32 @@ static int wis_tw2804_command(struct i2c_client *client, int *input = arg; if (*input < 0 || *input > 3) { - printk(KERN_ERR "wis-tw2804: channel %d is not " - "between 0 and 3!\n", *input); + dev_err(&client->dev, + "channel %d is not between 0 and 3!\n", *input); return 0; } dec->channel = *input; - printk(KERN_DEBUG "wis-tw2804: initializing TW2804 " - "channel %d\n", dec->channel); + dev_dbg(&client->dev, "initializing TW2804 channel %d\n", + dec->channel); if (dec->channel == 0 && write_regs(client, global_registers, 0) < 0) { - printk(KERN_ERR "wis-tw2804: error initializing " - "TW2804 global registers\n"); + dev_err(&client->dev, + "error initializing TW2804 global registers\n"); return 0; } if (write_regs(client, channel_registers, dec->channel) < 0) { - printk(KERN_ERR "wis-tw2804: error initializing " - "TW2804 channel %d\n", dec->channel); + dev_err(&client->dev, + "error initializing TW2804 channel %d\n", + dec->channel); return 0; } return 0; } if (dec->channel < 0) { - printk(KERN_DEBUG "wis-tw2804: ignoring command %08x until " - "channel number is set\n", cmd); + dev_dbg(&client->dev, + "ignoring command %08x until channel number is set\n", + cmd); return 0; } @@ -311,7 +313,7 @@ static int wis_tw2804_probe(struct i2c_client *client, dec->hue = 128; i2c_set_clientdata(client, dec); - printk(KERN_DEBUG "wis-tw2804: creating TW2804 at address %d on %s\n", + dev_dbg(&client->dev, "creating TW2804 at address %d on %s\n", client->addr, adapter->name); return 0; -- cgit v1.2.3 From 5820debccb530a8a5456f358e92d8272aa2902ae Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Tue, 6 Nov 2012 15:41:03 -0300 Subject: [media] Staging/media: Use dev_ printks in go7007/s2250-board.c fixed below checkpatch warning. - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/s2250-board.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/go7007/s2250-board.c b/drivers/staging/media/go7007/s2250-board.c index 9ba61bd382ac..d60e06596375 100644 --- a/drivers/staging/media/go7007/s2250-board.c +++ b/drivers/staging/media/go7007/s2250-board.c @@ -173,7 +173,7 @@ static int write_reg(struct i2c_client *client, u8 reg, u8 value) usb = go->hpi_context; if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { - printk(KERN_INFO "i2c lock failed\n"); + dev_info(&client->dev, "i2c lock failed\n"); kfree(buf); return -EINTR; } @@ -212,7 +212,7 @@ static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val) usb = go->hpi_context; if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { - printk(KERN_INFO "i2c lock failed\n"); + dev_info(&client->dev, "i2c lock failed\n"); kfree(buf); return -EINTR; } @@ -230,13 +230,13 @@ static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val) val_read = (buf[2] << 8) + buf[3]; kfree(buf); if (val_read != val) { - printk(KERN_INFO "invalid fp write %x %x\n", - val_read, val); + dev_info(&client->dev, "invalid fp write %x %x\n", + val_read, val); return -EFAULT; } if (subaddr != addr) { - printk(KERN_INFO "invalid fp write addr %x %x\n", - subaddr, addr); + dev_info(&client->dev, "invalid fp write addr %x %x\n", + subaddr, addr); return -EFAULT; } } else { @@ -274,7 +274,7 @@ static int read_reg_fp(struct i2c_client *client, u16 addr, u16 *val) memset(buf, 0xcd, 6); usb = go->hpi_context; if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { - printk(KERN_INFO "i2c lock failed\n"); + dev_info(&client->dev, "i2c lock failed\n"); kfree(buf); return -EINTR; } @@ -298,7 +298,7 @@ static int write_regs(struct i2c_client *client, u8 *regs) for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) { if (write_reg(client, regs[i], regs[i+1]) < 0) { - printk(KERN_INFO "s2250: failed\n"); + dev_info(&client->dev, "failed\n"); return -1; } } @@ -311,7 +311,7 @@ static int write_regs_fp(struct i2c_client *client, u16 *regs) for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) { if (write_reg_fp(client, regs[i], regs[i+1]) < 0) { - printk(KERN_INFO "s2250: failed fp\n"); + dev_info(&client->dev, "failed fp\n"); return -1; } } @@ -605,23 +605,20 @@ static int s2250_probe(struct i2c_client *client, /* initialize the audio */ if (write_regs(audio, aud_regs) < 0) { - printk(KERN_ERR - "s2250: error initializing audio\n"); + dev_err(&client->dev, "error initializing audio\n"); i2c_unregister_device(audio); kfree(state); return 0; } if (write_regs(client, vid_regs) < 0) { - printk(KERN_ERR - "s2250: error initializing decoder\n"); + dev_err(&client->dev, "error initializing decoder\n"); i2c_unregister_device(audio); kfree(state); return 0; } if (write_regs_fp(client, vid_regs_fp) < 0) { - printk(KERN_ERR - "s2250: error initializing decoder\n"); + dev_err(&client->dev, "error initializing decoder\n"); i2c_unregister_device(audio); kfree(state); return 0; -- cgit v1.2.3 From e6da76617fe79ab76ad8f78606c423e093a284f3 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Wed, 7 Nov 2012 15:26:19 -0300 Subject: [media] Staging/media: Use dev_ printks in solo6x10/p2m.c fixed below checkpatch warning. - WARNING: Prefer netdev_warn(netdev, ... then dev_warn(dev, ... then pr_warn(... to printk(KERN_WARNING ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/solo6x10/p2m.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/solo6x10/p2m.c b/drivers/staging/media/solo6x10/p2m.c index 56210f0fc5ec..58ab61b1f1d9 100644 --- a/drivers/staging/media/solo6x10/p2m.c +++ b/drivers/staging/media/solo6x10/p2m.c @@ -231,15 +231,15 @@ static void run_p2m_test(struct solo_dev *solo_dev) u32 size = SOLO_JPEG_EXT_ADDR(solo_dev) + SOLO_JPEG_EXT_SIZE(solo_dev); int i, d; - printk(KERN_WARNING "%s: Testing %u bytes of external ram\n", - SOLO6X10_NAME, size); + dev_warn(&solo_dev->pdev->dev, "Testing %u bytes of external ram\n", + size); for (i = 0; i < size; i += TEST_CHUNK_SIZE) for (d = 0; d < 4; d++) errs += p2m_test(solo_dev, d, i, TEST_CHUNK_SIZE); - printk(KERN_WARNING "%s: Found %llu errors during p2m test\n", - SOLO6X10_NAME, errs); + dev_warn(&solo_dev->pdev->dev, "Found %llu errors during p2m test\n", + errs); return; } -- cgit v1.2.3 From e174e6cac62419ab136506e48790ccd36854ed53 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Thu, 8 Nov 2012 15:52:49 -0300 Subject: [media] staging/media: Use dev_ or pr_ printks in lirc/lirc_sasem.c fixed below checkpatch warnings. - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... - WARNING: Prefer netdev_warn(netdev, ... then dev_warn(dev, ... then pr_warn(... to printk(KERN_WARNING ... - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... and add pr_fmt. Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_sasem.c | 65 +++++++++++++++++---------------- 1 file changed, 33 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/lirc/lirc_sasem.c b/drivers/staging/media/lirc/lirc_sasem.c index 101ac12eb381..b3fe21e7a90d 100644 --- a/drivers/staging/media/lirc/lirc_sasem.c +++ b/drivers/staging/media/lirc/lirc_sasem.c @@ -34,6 +34,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -171,7 +173,7 @@ static void delete_context(struct sasem_context *context) kfree(context); if (debug) - printk(KERN_INFO "%s: context deleted\n", __func__); + pr_info("%s: context deleted\n", __func__); } static void deregister_from_lirc(struct sasem_context *context) @@ -181,11 +183,10 @@ static void deregister_from_lirc(struct sasem_context *context) retval = lirc_unregister_driver(minor); if (retval) - printk(KERN_ERR "%s: unable to deregister from lirc (%d)\n", - __func__, retval); + pr_err("%s: unable to deregister from lirc (%d)\n", + __func__, retval); else - printk(KERN_INFO "Deregistered Sasem driver (minor:%d)\n", - minor); + pr_info("Deregistered Sasem driver (minor:%d)\n", minor); } @@ -206,8 +207,7 @@ static int vfd_open(struct inode *inode, struct file *file) subminor = iminor(inode); interface = usb_find_interface(&sasem_driver, subminor); if (!interface) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: could not find interface for minor %d\n", + pr_err("%s: could not find interface for minor %d\n", __func__, subminor); retval = -ENODEV; goto exit; @@ -252,8 +252,7 @@ static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg) context = (struct sasem_context *) file->private_data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return -ENODEV; } @@ -266,7 +265,7 @@ static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg) context->vfd_contrast = (unsigned int)arg; break; default: - printk(KERN_INFO "Unknown IOCTL command\n"); + pr_info("Unknown IOCTL command\n"); mutex_unlock(&context->ctx_lock); return -ENOIOCTLCMD; /* not supported */ } @@ -287,8 +286,7 @@ static int vfd_close(struct inode *inode, struct file *file) context = (struct sasem_context *) file->private_data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return -ENODEV; } @@ -299,7 +297,7 @@ static int vfd_close(struct inode *inode, struct file *file) retval = -EIO; } else { context->vfd_isopen = 0; - printk(KERN_INFO "VFD port closed\n"); + dev_info(&context->dev->dev, "VFD port closed\n"); if (!context->dev_present && !context->ir_isopen) { /* Device disconnected before close and IR port is @@ -373,16 +371,14 @@ static ssize_t vfd_write(struct file *file, const char *buf, context = (struct sasem_context *) file->private_data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return -ENODEV; } mutex_lock(&context->ctx_lock); if (!context->dev_present) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: no Sasem device present\n", __func__); + pr_err("%s: no Sasem device present\n", __func__); retval = -ENODEV; goto exit; } @@ -519,7 +515,7 @@ static int ir_open(void *data) __func__, retval); else { context->ir_isopen = 1; - printk(KERN_INFO "IR port opened\n"); + dev_info(&context->dev->dev, "IR port opened\n"); } exit: @@ -538,8 +534,7 @@ static void ir_close(void *data) context = (struct sasem_context *)data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return; } @@ -547,7 +542,7 @@ static void ir_close(void *data) usb_kill_urb(context->rx_urb); context->ir_isopen = 0; - printk(KERN_INFO "IR port closed\n"); + pr_info("IR port closed\n"); if (!context->dev_present) { @@ -584,8 +579,9 @@ static void incoming_packet(struct sasem_context *context, int i; if (len != 8) { - printk(KERN_WARNING "%s: invalid incoming packet size (%d)\n", - __func__, len); + dev_warn(&context->dev->dev, + "%s: invalid incoming packet size (%d)\n", + __func__, len); return; } @@ -663,7 +659,7 @@ static void usb_rx_callback(struct urb *urb) break; default: - printk(KERN_WARNING "%s: status (%d): ignored", + dev_warn(&urb->dev->dev, "%s: status (%d): ignored", __func__, urb->status); break; } @@ -830,8 +826,9 @@ static int sasem_probe(struct usb_interface *interface, retval = lirc_minor; goto unlock; } else - printk(KERN_INFO "%s: Registered Sasem driver (minor:%d)\n", - __func__, lirc_minor); + dev_info(&interface->dev, + "%s: Registered Sasem driver (minor:%d)\n", + __func__, lirc_minor); /* Needed while unregistering! */ driver->minor = lirc_minor; @@ -852,15 +849,18 @@ static int sasem_probe(struct usb_interface *interface, if (vfd_ep_found) { if (debug) - printk(KERN_INFO "Registering VFD with sysfs\n"); + dev_info(&interface->dev, + "Registering VFD with sysfs\n"); if (usb_register_dev(interface, &sasem_class)) /* Not a fatal error, so ignore */ - printk(KERN_INFO "%s: could not get a minor number " - "for VFD\n", __func__); + dev_info(&interface->dev, + "%s: could not get a minor number for VFD\n", + __func__); } - printk(KERN_INFO "%s: Sasem device on usb<%d:%d> initialized\n", - __func__, dev->bus->busnum, dev->devnum); + dev_info(&interface->dev, + "%s: Sasem device on usb<%d:%d> initialized\n", + __func__, dev->bus->busnum, dev->devnum); unlock: mutex_unlock(&context->ctx_lock); @@ -903,7 +903,8 @@ static void sasem_disconnect(struct usb_interface *interface) context = usb_get_intfdata(interface); mutex_lock(&context->ctx_lock); - printk(KERN_INFO "%s: Sasem device disconnected\n", __func__); + dev_info(&interface->dev, "%s: Sasem device disconnected\n", + __func__); usb_set_intfdata(interface, NULL); context->dev_present = 0; -- cgit v1.2.3 From 014f00664267904585f58c3478e9a7bfc96d0f03 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Thu, 8 Nov 2012 15:53:37 -0300 Subject: [media] staging/media: Use pr_ printks in lirc/lirc_sir.c fixed below checkpatch warnings. - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... and add pr_fmt. Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_sir.c | 36 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/lirc/lirc_sir.c b/drivers/staging/media/lirc/lirc_sir.c index 4afc3b419738..9a88f057c0bd 100644 --- a/drivers/staging/media/lirc/lirc_sir.c +++ b/drivers/staging/media/lirc/lirc_sir.c @@ -33,6 +33,8 @@ * parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -495,7 +497,7 @@ static int init_chrdev(void) driver.dev = &lirc_sir_dev->dev; driver.minor = lirc_register_driver(&driver); if (driver.minor < 0) { - printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n"); + pr_err("init_chrdev() failed.\n"); return -EIO; } return 0; @@ -604,7 +606,7 @@ static irqreturn_t sir_interrupt(int irq, void *dev_id) } if (status & UTSR0_TFS) - printk(KERN_ERR "transmit fifo not full, shouldn't happen\n"); + pr_err("transmit fifo not full, shouldn't happen\n"); /* We must clear certain bits. */ status &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); @@ -787,7 +789,7 @@ static int init_hardware(void) #ifdef LIRC_ON_SA1100 #ifdef CONFIG_SA1100_BITSY if (machine_is_bitsy()) { - printk(KERN_INFO "Power on IR module\n"); + pr_info("Power on IR module\n"); set_bitsy_egpio(EGPIO_BITSY_IR_ON); } #endif @@ -885,8 +887,7 @@ static int init_hardware(void) udelay(1500); /* read previous control byte */ - printk(KERN_INFO LIRC_DRIVER_NAME - ": 0x%02x\n", sinp(UART_RX)); + pr_info("0x%02x\n", sinp(UART_RX)); /* Set DLAB 1. */ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB); @@ -964,8 +965,7 @@ static int init_port(void) /* get I/O port access and IRQ line */ #ifndef LIRC_ON_SA1100 if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) { - printk(KERN_ERR LIRC_DRIVER_NAME - ": i/o port 0x%.4x already in use.\n", io); + pr_err("i/o port 0x%.4x already in use.\n", io); return -EBUSY; } #endif @@ -975,15 +975,11 @@ static int init_port(void) # ifndef LIRC_ON_SA1100 release_region(io, 8); # endif - printk(KERN_ERR LIRC_DRIVER_NAME - ": IRQ %d already in use.\n", - irq); + pr_err("IRQ %d already in use.\n", irq); return retval; } #ifndef LIRC_ON_SA1100 - printk(KERN_INFO LIRC_DRIVER_NAME - ": I/O port 0x%.4x, IRQ %d.\n", - io, irq); + pr_info("I/O port 0x%.4x, IRQ %d.\n", io, irq); #endif init_timer(&timerlist); @@ -1213,8 +1209,7 @@ static int init_lirc_sir(void) if (retval < 0) return retval; init_hardware(); - printk(KERN_INFO LIRC_DRIVER_NAME - ": Installed.\n"); + pr_info("Installed.\n"); return 0; } @@ -1243,23 +1238,20 @@ static int __init lirc_sir_init(void) retval = platform_driver_register(&lirc_sir_driver); if (retval) { - printk(KERN_ERR LIRC_DRIVER_NAME ": Platform driver register " - "failed!\n"); + pr_err("Platform driver register failed!\n"); return -ENODEV; } lirc_sir_dev = platform_device_alloc("lirc_dev", 0); if (!lirc_sir_dev) { - printk(KERN_ERR LIRC_DRIVER_NAME ": Platform device alloc " - "failed!\n"); + pr_err("Platform device alloc failed!\n"); retval = -ENOMEM; goto pdev_alloc_fail; } retval = platform_device_add(lirc_sir_dev); if (retval) { - printk(KERN_ERR LIRC_DRIVER_NAME ": Platform device add " - "failed!\n"); + pr_err("Platform device add failed!\n"); retval = -ENODEV; goto pdev_add_fail; } @@ -1292,7 +1284,7 @@ static void __exit lirc_sir_exit(void) drop_port(); platform_device_unregister(lirc_sir_dev); platform_driver_unregister(&lirc_sir_driver); - printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n"); + pr_info("Uninstalled.\n"); } module_init(lirc_sir_init); -- cgit v1.2.3 From f8a7df00210145d54f11855bac4023f59866efc4 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Thu, 8 Nov 2012 15:54:09 -0300 Subject: [media] staging/media: Use pr_ printks in lirc/lirc_bt829.c fixed below checkpatch warnings. - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... and add pr_fmt. Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_bt829.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/lirc/lirc_bt829.c b/drivers/staging/media/lirc/lirc_bt829.c index 951007a3fc96..fa31ee7dd6a9 100644 --- a/drivers/staging/media/lirc/lirc_bt829.c +++ b/drivers/staging/media/lirc/lirc_bt829.c @@ -18,6 +18,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -72,20 +74,19 @@ static struct pci_dev *do_pci_probe(void) my_dev = pci_get_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_264VT, NULL); if (my_dev) { - printk(KERN_ERR DRIVER_NAME ": Using device: %s\n", - pci_name(my_dev)); + pr_err("Using device: %s\n", pci_name(my_dev)); pci_addr_phys = 0; if (my_dev->resource[0].flags & IORESOURCE_MEM) { pci_addr_phys = my_dev->resource[0].start; - printk(KERN_INFO DRIVER_NAME ": memory at 0x%08X\n", + pr_info("memory at 0x%08X\n", (unsigned int)pci_addr_phys); } if (pci_addr_phys == 0) { - printk(KERN_ERR DRIVER_NAME ": no memory resource ?\n"); + pr_err("no memory resource ?\n"); return NULL; } } else { - printk(KERN_ERR DRIVER_NAME ": pci_probe failed\n"); + pr_err("pci_probe failed\n"); return NULL; } return my_dev; @@ -140,7 +141,7 @@ int init_module(void) atir_minor = lirc_register_driver(&atir_driver); if (atir_minor < 0) { - printk(KERN_ERR DRIVER_NAME ": failed to register driver!\n"); + pr_err("failed to register driver!\n"); return atir_minor; } dprintk("driver is registered on minor %d\n", atir_minor); @@ -159,7 +160,7 @@ static int atir_init_start(void) { pci_addr_lin = ioremap(pci_addr_phys + DATA_PCI_OFF, 0x400); if (pci_addr_lin == 0) { - printk(KERN_INFO DRIVER_NAME ": pci mem must be mapped\n"); + pr_info("pci mem must be mapped\n"); return 0; } return 1; -- cgit v1.2.3 From cc38b8e9f595c37677b5d5a3d72c985a6ee085c9 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Thu, 8 Nov 2012 15:54:33 -0300 Subject: [media] staging/media: Use pr_ printks in lirc/lirc_parallel.c fixed below checkpatch warnings. - WARNING: Prefer netdev_warn(netdev, ... then dev_warn(dev, ... then pr_warn(... to printk(KERN_WARNING ... - WARNING: Prefer netdev_notice(netdev, ... then dev_notice(dev, ... then pr_notice(... to printk(KERN_NOTICE ... - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... and add pr_fmt. Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_parallel.c | 49 ++++++++++++------------------ 1 file changed, 19 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/lirc/lirc_parallel.c b/drivers/staging/media/lirc/lirc_parallel.c index dd2bca7b56fa..139920c290ca 100644 --- a/drivers/staging/media/lirc/lirc_parallel.c +++ b/drivers/staging/media/lirc/lirc_parallel.c @@ -22,6 +22,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + /*** Includes ***/ #include @@ -115,8 +117,7 @@ static void out(int offset, int value) parport_write_control(pport, value); break; case LIRC_LP_STATUS: - printk(KERN_INFO "%s: attempt to write to status register\n", - LIRC_DRIVER_NAME); + pr_info("attempt to write to status register\n"); break; } } @@ -166,27 +167,23 @@ static unsigned int init_lirc_timer(void) if (default_timer == 0) { /* autodetect timer */ newtimer = (1000000*count)/timeelapsed; - printk(KERN_INFO "%s: %u Hz timer detected\n", - LIRC_DRIVER_NAME, newtimer); + pr_info("%u Hz timer detected\n", newtimer); return newtimer; } else { newtimer = (1000000*count)/timeelapsed; if (abs(newtimer - default_timer) > default_timer/10) { /* bad timer */ - printk(KERN_NOTICE "%s: bad timer: %u Hz\n", - LIRC_DRIVER_NAME, newtimer); - printk(KERN_NOTICE "%s: using default timer: " - "%u Hz\n", - LIRC_DRIVER_NAME, default_timer); + pr_notice("bad timer: %u Hz\n", newtimer); + pr_notice("using default timer: %u Hz\n", + default_timer); return default_timer; } else { - printk(KERN_INFO "%s: %u Hz timer detected\n", - LIRC_DRIVER_NAME, newtimer); + pr_info("%u Hz timer detected\n", newtimer); return newtimer; /* use detected value */ } } } else { - printk(KERN_NOTICE "%s: no timer detected\n", LIRC_DRIVER_NAME); + pr_notice("no timer detected\n"); return 0; } } @@ -194,13 +191,10 @@ static unsigned int init_lirc_timer(void) static int lirc_claim(void) { if (parport_claim(ppdevice) != 0) { - printk(KERN_WARNING "%s: could not claim port\n", - LIRC_DRIVER_NAME); - printk(KERN_WARNING "%s: waiting for port becoming available" - "\n", LIRC_DRIVER_NAME); + pr_warn("could not claim port\n"); + pr_warn("waiting for port becoming available\n"); if (parport_claim_or_block(ppdevice) < 0) { - printk(KERN_NOTICE "%s: could not claim port, giving" - " up\n", LIRC_DRIVER_NAME); + pr_notice("could not claim port, giving up\n"); return 0; } } @@ -219,7 +213,7 @@ static void rbuf_write(int signal) if (nwptr == rptr) { /* no new signals will be accepted */ lost_irqs++; - printk(KERN_NOTICE "%s: buffer overrun\n", LIRC_DRIVER_NAME); + pr_notice("buffer overrun\n"); return; } rbuf[wptr] = signal; @@ -290,7 +284,7 @@ static void irq_handler(void *blah) if (signal > timeout || (check_pselecd && (in(1) & LP_PSELECD))) { signal = 0; - printk(KERN_NOTICE "%s: timeout\n", LIRC_DRIVER_NAME); + pr_notice("timeout\n"); break; } } while (lirc_get_signal()); @@ -644,8 +638,7 @@ static int __init lirc_parallel_init(void) result = platform_driver_register(&lirc_parallel_driver); if (result) { - printk(KERN_NOTICE "platform_driver_register" - " returned %d\n", result); + pr_notice("platform_driver_register returned %d\n", result); return result; } @@ -661,8 +654,7 @@ static int __init lirc_parallel_init(void) pport = parport_find_base(io); if (pport == NULL) { - printk(KERN_NOTICE "%s: no port at %x found\n", - LIRC_DRIVER_NAME, io); + pr_notice("no port at %x found\n", io); result = -ENXIO; goto exit_device_put; } @@ -670,8 +662,7 @@ static int __init lirc_parallel_init(void) pf, kf, irq_handler, 0, NULL); parport_put_port(pport); if (ppdevice == NULL) { - printk(KERN_NOTICE "%s: parport_register_device() failed\n", - LIRC_DRIVER_NAME); + pr_notice("parport_register_device() failed\n"); result = -ENXIO; goto exit_device_put; } @@ -706,14 +697,12 @@ static int __init lirc_parallel_init(void) driver.dev = &lirc_parallel_dev->dev; driver.minor = lirc_register_driver(&driver); if (driver.minor < 0) { - printk(KERN_NOTICE "%s: register_chrdev() failed\n", - LIRC_DRIVER_NAME); + pr_notice("register_chrdev() failed\n"); parport_unregister_device(ppdevice); result = -EIO; goto exit_device_put; } - printk(KERN_INFO "%s: installed using port 0x%04x irq %d\n", - LIRC_DRIVER_NAME, io, irq); + pr_info("installed using port 0x%04x irq %d\n", io, irq); return 0; exit_device_put: -- cgit v1.2.3 From df4f07b5066f3db4270e5b3240fd8292fd28aebb Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Thu, 8 Nov 2012 15:55:09 -0300 Subject: [media] staging/media: Use pr_ printks in lirc/lirc_serial.c fixed below checkpatch warnings. - WARNING: Prefer netdev_warn(netdev, ... then dev_warn(dev, ... then pr_warn(... to printk(KERN_WARNING ... - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... and add pr_fmt. Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_serial.c | 70 +++++++++++++------------------- 1 file changed, 29 insertions(+), 41 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/lirc/lirc_serial.c b/drivers/staging/media/lirc/lirc_serial.c index 08cfaf6fb399..513b75ef5179 100644 --- a/drivers/staging/media/lirc/lirc_serial.c +++ b/drivers/staging/media/lirc/lirc_serial.c @@ -48,6 +48,8 @@ * Steve Davies July 2001 */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -667,8 +669,7 @@ static irqreturn_t irq_handler(int i, void *blah) counter++; status = sinp(UART_MSR); if (counter > RS_ISR_PASS_LIMIT) { - printk(KERN_WARNING LIRC_DRIVER_NAME ": AIEEEE: " - "We're caught!\n"); + pr_warn("AIEEEE: We're caught!\n"); break; } if ((status & hardware[type].signal_pin_change) @@ -703,11 +704,10 @@ static irqreturn_t irq_handler(int i, void *blah) dcd = (status & hardware[type].signal_pin) ? 1 : 0; if (dcd == last_dcd) { - printk(KERN_WARNING LIRC_DRIVER_NAME - ": ignoring spike: %d %d %lx %lx %lx %lx\n", - dcd, sense, - tv.tv_sec, lasttv.tv_sec, - tv.tv_usec, lasttv.tv_usec); + pr_warn("ignoring spike: %d %d %lx %lx %lx %lx\n", + dcd, sense, + tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); continue; } @@ -715,25 +715,20 @@ static irqreturn_t irq_handler(int i, void *blah) if (tv.tv_sec < lasttv.tv_sec || (tv.tv_sec == lasttv.tv_sec && tv.tv_usec < lasttv.tv_usec)) { - printk(KERN_WARNING LIRC_DRIVER_NAME - ": AIEEEE: your clock just jumped " - "backwards\n"); - printk(KERN_WARNING LIRC_DRIVER_NAME - ": %d %d %lx %lx %lx %lx\n", - dcd, sense, - tv.tv_sec, lasttv.tv_sec, - tv.tv_usec, lasttv.tv_usec); + pr_warn("AIEEEE: your clock just jumped backwards\n"); + pr_warn("%d %d %lx %lx %lx %lx\n", + dcd, sense, + tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); data = PULSE_MASK; } else if (deltv > 15) { data = PULSE_MASK; /* really long time */ if (!(dcd^sense)) { /* sanity check */ - printk(KERN_WARNING LIRC_DRIVER_NAME - ": AIEEEE: " - "%d %d %lx %lx %lx %lx\n", - dcd, sense, - tv.tv_sec, lasttv.tv_sec, - tv.tv_usec, lasttv.tv_usec); + pr_warn("AIEEEE: %d %d %lx %lx %lx %lx\n", + dcd, sense, + tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); /* * detecting pulse while this * MUST be a space! @@ -776,8 +771,7 @@ static int hardware_init_port(void) soutp(UART_IER, scratch); if (scratch2 != 0 || scratch3 != 0x0f) { /* we fail, there's nothing here */ - printk(KERN_ERR LIRC_DRIVER_NAME ": port existence test " - "failed, cannot continue\n"); + pr_err("port existence test failed, cannot continue\n"); return -ENODEV; } @@ -850,11 +844,9 @@ static int __devinit lirc_serial_probe(struct platform_device *dev) LIRC_DRIVER_NAME, (void *)&hardware); if (result < 0) { if (result == -EBUSY) - printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", - irq); + dev_err(&dev->dev, "IRQ %d busy\n", irq); else if (result == -EINVAL) - printk(KERN_ERR LIRC_DRIVER_NAME - ": Bad irq number or handler\n"); + dev_err(&dev->dev, "Bad irq number or handler\n"); return result; } @@ -869,14 +861,11 @@ static int __devinit lirc_serial_probe(struct platform_device *dev) LIRC_DRIVER_NAME) == NULL)) || ((iommap == 0) && (request_region(io, 8, LIRC_DRIVER_NAME) == NULL))) { - printk(KERN_ERR LIRC_DRIVER_NAME - ": port %04x already in use\n", io); - printk(KERN_WARNING LIRC_DRIVER_NAME - ": use 'setserial /dev/ttySX uart none'\n"); - printk(KERN_WARNING LIRC_DRIVER_NAME - ": or compile the serial port driver as module and\n"); - printk(KERN_WARNING LIRC_DRIVER_NAME - ": make sure this module is loaded first\n"); + dev_err(&dev->dev, "port %04x already in use\n", io); + dev_warn(&dev->dev, "use 'setserial /dev/ttySX uart none'\n"); + dev_warn(&dev->dev, + "or compile the serial port driver as module and\n"); + dev_warn(&dev->dev, "make sure this module is loaded first\n"); result = -EBUSY; goto exit_free_irq; } @@ -907,11 +896,11 @@ static int __devinit lirc_serial_probe(struct platform_device *dev) msleep(40); } sense = (nlow >= nhigh ? 1 : 0); - printk(KERN_INFO LIRC_DRIVER_NAME ": auto-detected active " - "%s receiver\n", sense ? "low" : "high"); + dev_info(&dev->dev, "auto-detected active %s receiver\n", + sense ? "low" : "high"); } else - printk(KERN_INFO LIRC_DRIVER_NAME ": Manually using active " - "%s receiver\n", sense ? "low" : "high"); + dev_info(&dev->dev, "Manually using active %s receiver\n", + sense ? "low" : "high"); dprintk("Interrupt %d, port %04x obtained\n", irq, io); return 0; @@ -1251,8 +1240,7 @@ static int __init lirc_serial_init_module(void) driver.dev = &lirc_serial_dev->dev; driver.minor = lirc_register_driver(&driver); if (driver.minor < 0) { - printk(KERN_ERR LIRC_DRIVER_NAME - ": register_chrdev failed!\n"); + pr_err("register_chrdev failed!\n"); lirc_serial_exit(); return driver.minor; } -- cgit v1.2.3 From 5c77dc40c9fe957220b30c1334d8959be554e1d8 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Thu, 8 Nov 2012 15:54:54 -0300 Subject: [media] staging/media: Use dev_ or pr_ printks in lirc/lirc_imon.c fixed below checkpatch warnings. - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... and add pr_fmt. Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_imon.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c index 2944fde89f44..343c622f9387 100644 --- a/drivers/staging/media/lirc/lirc_imon.c +++ b/drivers/staging/media/lirc/lirc_imon.c @@ -20,6 +20,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -205,12 +207,12 @@ static void deregister_from_lirc(struct imon_context *context) retval = lirc_unregister_driver(minor); if (retval) - printk(KERN_ERR KBUILD_MODNAME - ": %s: unable to deregister from lirc(%d)", - __func__, retval); + dev_err(&context->usbdev->dev, + ": %s: unable to deregister from lirc(%d)", + __func__, retval); else - printk(KERN_INFO MOD_NAME ": Deregistered iMON driver " - "(minor:%d)\n", minor); + dev_info(&context->usbdev->dev, + "Deregistered iMON driver (minor:%d)\n", minor); } @@ -231,8 +233,7 @@ static int display_open(struct inode *inode, struct file *file) subminor = iminor(inode); interface = usb_find_interface(&imon_driver, subminor); if (!interface) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: could not find interface for minor %d\n", + pr_err("%s: could not find interface for minor %d\n", __func__, subminor); retval = -ENODEV; goto exit; @@ -282,8 +283,7 @@ static int display_close(struct inode *inode, struct file *file) context = file->private_data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - "%s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return -ENODEV; } @@ -391,8 +391,7 @@ static ssize_t vfd_write(struct file *file, const char __user *buf, context = file->private_data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - "%s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return -ENODEV; } @@ -521,8 +520,7 @@ static void ir_close(void *data) context = (struct imon_context *)data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - "%s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return; } @@ -1009,8 +1007,8 @@ static void imon_disconnect(struct usb_interface *interface) mutex_unlock(&driver_lock); - printk(KERN_INFO "%s: iMON device (intf%d) disconnected\n", - __func__, ifnum); + dev_info(&interface->dev, "%s: iMON device (intf%d) disconnected\n", + __func__, ifnum); } static int imon_suspend(struct usb_interface *intf, pm_message_t message) -- cgit v1.2.3 From ce24c25b97b26c3f5a1634b2919b50a053eabc01 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Thu, 8 Nov 2012 15:53:53 -0300 Subject: [media] staging/media: Use dev_ printks in lirc/igorplugusb.c fixed below checkpatch warnings. - WARNING: Prefer netdev_warn(netdev, ... then dev_warn(dev, ... then pr_warn(... to printk(KERN_WARNING ... - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_igorplugusb.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/lirc/lirc_igorplugusb.c b/drivers/staging/media/lirc/lirc_igorplugusb.c index 939a801c23e4..2faa391006db 100644 --- a/drivers/staging/media/lirc/lirc_igorplugusb.c +++ b/drivers/staging/media/lirc/lirc_igorplugusb.c @@ -223,8 +223,8 @@ static int unregister_from_lirc(struct igorplug *ir) int devnum; if (!ir) { - printk(KERN_ERR "%s: called with NULL device struct!\n", - __func__); + dev_err(&ir->usbdev->dev, + "%s: called with NULL device struct!\n", __func__); return -EINVAL; } @@ -232,8 +232,8 @@ static int unregister_from_lirc(struct igorplug *ir) d = ir->d; if (!d) { - printk(KERN_ERR "%s: called with NULL lirc driver struct!\n", - __func__); + dev_err(&ir->usbdev->dev, + "%s: called with NULL lirc driver struct!\n", __func__); return -EINVAL; } @@ -347,8 +347,8 @@ static int igorplugusb_remote_poll(void *data, struct lirc_buffer *buf) if (ir->buf_in[2] == 0) send_fragment(ir, buf, DEVICE_HEADERLEN, ret); else { - printk(KERN_WARNING DRIVER_NAME - "[%d]: Device buffer overrun.\n", ir->devnum); + dev_warn(&ir->usbdev->dev, + "[%d]: Device buffer overrun.\n", ir->devnum); /* HHHNNNNNNNNNNNOOOOOOOO H = header <---[2]---> N = newer <---------ret--------> O = older */ -- cgit v1.2.3 From fc09931e10d08c2d3eb82c4992cb21fd98682cd8 Mon Sep 17 00:00:00 2001 From: Jonathan McDowell Date: Thu, 15 Nov 2012 21:55:12 -0300 Subject: [media] Autoselect more relevant frontends for EM28XX DVB stick I noticed that the EM28XX DVB driver doesn't auto select all of the appropriate DVB tuner modules required. In particular I needed DVB_LGDT3305 for my a340, but it looks like DVB_MT352 + DVB_S5H1409 were missing as well. Signed-Off-by: Jonathan McDowell Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/Kconfig | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index 7a5bd61bd3bb..617c6e4cce49 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -34,6 +34,7 @@ config VIDEO_EM28XX_DVB tristate "DVB/ATSC Support for em28xx based TV cards" depends on VIDEO_EM28XX && DVB_CORE select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT select DVB_S921 if MEDIA_SUBDRV_AUTOSELECT @@ -43,6 +44,8 @@ config VIDEO_EM28XX_DVB select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA10071 if MEDIA_SUBDRV_AUTOSELECT select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT select VIDEOBUF_DVB ---help--- This adds support for DVB cards based on the -- cgit v1.2.3 From 37285bf2a516a808f1282540badcaf847340a154 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 21 Dec 2012 21:14:41 -0200 Subject: em28xx: add two missing tuners at the Kconfig file Those two tuners may also be needed. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/Kconfig | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index 617c6e4cce49..094c4ecf086d 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -46,6 +46,8 @@ config VIDEO_EM28XX_DVB select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT select VIDEOBUF_DVB ---help--- This adds support for DVB cards based on the -- cgit v1.2.3 From 105e3687ada4ebe6dfbda7abc3b16106f86a787d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 15 Dec 2012 08:29:11 -0300 Subject: [media] em28xx: add support for NEC proto variants on em2874 and upper By disabling the NEC parity check, it is possible to handle all 3 NEC protocol variants (32, 24 or 16 bits). Change the driver in order to handle all of them. Unfortunately, em2860/em2863 provide only 16 bits for the IR scancode, even when NEC parity is disabled. So, this change should affect only em2874 and newer devices, with provides up to 32 bits for the scancode. Tested with one NEC-16, one NEC-24 and one RC5 IR. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 58 +++++++++++++++++++++------------ drivers/media/usb/em28xx/em28xx-reg.h | 1 + 2 files changed, 39 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 660bf803c9e4..507370c5217d 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -57,8 +57,8 @@ MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); struct em28xx_ir_poll_result { unsigned int toggle_bit:1; unsigned int read_count:7; - u8 rc_address; - u8 rc_data[4]; /* 1 byte on em2860/2880, 4 on em2874 */ + + u32 scancode; }; struct em28xx_IR { @@ -72,6 +72,7 @@ struct em28xx_IR { struct delayed_work work; unsigned int full_code:1; unsigned int last_readcount; + u64 rc_type; int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); }; @@ -236,11 +237,8 @@ static int default_polling_getkey(struct em28xx_IR *ir, /* Infrared read count (Reg 0x45[6:0] */ poll_result->read_count = (msg[0] & 0x7f); - /* Remote Control Address (Reg 0x46) */ - poll_result->rc_address = msg[1]; - - /* Remote Control Data (Reg 0x47) */ - poll_result->rc_data[0] = msg[2]; + /* Remote Control Address/Data (Regs 0x46/0x47) */ + poll_result->scancode = msg[1] << 8 | msg[2]; return 0; } @@ -266,13 +264,32 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, /* Infrared read count (Reg 0x51[6:0] */ poll_result->read_count = (msg[0] & 0x7f); - /* Remote Control Address (Reg 0x52) */ - poll_result->rc_address = msg[1]; - - /* Remote Control Data (Reg 0x53-55) */ - poll_result->rc_data[0] = msg[2]; - poll_result->rc_data[1] = msg[3]; - poll_result->rc_data[2] = msg[4]; + /* + * Remote Control Address (Reg 0x52) + * Remote Control Data (Reg 0x53-0x55) + */ + switch (ir->rc_type) { + case RC_BIT_RC5: + poll_result->scancode = msg[1] << 8 | msg[2]; + break; + case RC_BIT_NEC: + if ((msg[3] ^ msg[4]) != 0xff) /* 32 bits NEC */ + poll_result->scancode = (msg[1] << 24) | + (msg[2] << 16) | + (msg[3] << 8) | + msg[4]; + else if ((msg[1] ^ msg[2]) != 0xff) /* 24 bits NEC */ + poll_result->scancode = (msg[1] << 16) | + (msg[2] << 8) | + msg[3]; + else /* Normal NEC */ + poll_result->scancode = msg[1] << 8 | msg[3]; + break; + default: + poll_result->scancode = (msg[1] << 24) | (msg[2] << 16) | + (msg[3] << 8) | msg[4]; + break; + } return 0; } @@ -294,17 +311,16 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) } if (unlikely(poll_result.read_count != ir->last_readcount)) { - dprintk("%s: toggle: %d, count: %d, key 0x%02x%02x\n", __func__, + dprintk("%s: toggle: %d, count: %d, key 0x%04x\n", __func__, poll_result.toggle_bit, poll_result.read_count, - poll_result.rc_address, poll_result.rc_data[0]); + poll_result.scancode); if (ir->full_code) rc_keydown(ir->rc, - poll_result.rc_address << 8 | - poll_result.rc_data[0], + poll_result.scancode, poll_result.toggle_bit); else rc_keydown(ir->rc, - poll_result.rc_data[0], + poll_result.scancode & 0xff, poll_result.toggle_bit); if (ir->dev->chip_id == CHIP_ID_EM2874 || @@ -360,12 +376,14 @@ static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) *rc_type = RC_BIT_RC5; } else if (*rc_type & RC_BIT_NEC) { dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE; - ir_config = EM2874_IR_NEC; + ir_config = EM2874_IR_NEC | EM2874_IR_NEC_NO_PARITY; ir->full_code = 1; *rc_type = RC_BIT_NEC; } else if (*rc_type != RC_BIT_UNKNOWN) rc = -EINVAL; + ir->rc_type = *rc_type; + em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, EM28XX_XCLK_IR_RC5_MODE); diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h index 6ff368297f6e..2ad357354d81 100644 --- a/drivers/media/usb/em28xx/em28xx-reg.h +++ b/drivers/media/usb/em28xx/em28xx-reg.h @@ -177,6 +177,7 @@ /* em2874 IR config register (0x50) */ #define EM2874_IR_NEC 0x00 +#define EM2874_IR_NEC_NO_PARITY 0x01 #define EM2874_IR_RC5 0x04 #define EM2874_IR_RC6_MODE_0 0x08 #define EM2874_IR_RC6_MODE_6A 0x0b -- cgit v1.2.3 From 0dae88392395e228e67436cd08f084d395b39df5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 15 Dec 2012 08:29:12 -0300 Subject: [media] em28xx: add support for RC6 mode 0 on devices that support it Newer em28xx chipsets (em2874 and upper) are capable of supporting RC6 codes, on both mode 0 (command mode, 16 bits payload size, similar to RC5, also called "Philips mode") and mode 6a (OEM command mode, with offers a few alternatives with regards to the payload size). I don't have any mode 6a control ATM to test it, so, I opted to add support only to mode 0. After this patch, adding support to mode 6a should not be hard. Tested with a Philips television remote controller. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 93 +++++++++++++++++++++++++++------ 1 file changed, 76 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 507370c5217d..3899ea823336 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -285,6 +285,9 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, else /* Normal NEC */ poll_result->scancode = msg[1] << 8 | msg[3]; break; + case RC_BIT_RC6_0: + poll_result->scancode = msg[1] << 8 | msg[2]; + break; default: poll_result->scancode = (msg[1] << 24) | (msg[2] << 16) | (msg[3] << 8) | msg[4]; @@ -361,15 +364,42 @@ static void em28xx_ir_stop(struct rc_dev *rc) cancel_delayed_work_sync(&ir->work); } -static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) +static int em2860_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) { - int rc = 0; struct em28xx_IR *ir = rc_dev->priv; struct em28xx *dev = ir->dev; - u8 ir_config = EM2874_IR_RC5; - /* Adjust xclk based o IR table for RC5/NEC tables */ + /* Adjust xclk based on IR table for RC5/NEC tables */ + if (*rc_type & RC_BIT_RC5) { + dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE; + ir->full_code = 1; + *rc_type = RC_BIT_RC5; + } else if (*rc_type & RC_BIT_NEC) { + dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE; + ir->full_code = 1; + *rc_type = RC_BIT_NEC; + } else if (*rc_type & RC_BIT_UNKNOWN) { + *rc_type = RC_BIT_UNKNOWN; + } else { + *rc_type = ir->rc_type; + return -EINVAL; + } + ir->get_key = default_polling_getkey; + em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, + EM28XX_XCLK_IR_RC5_MODE); + + ir->rc_type = *rc_type; + return 0; +} + +static int em2874_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) +{ + struct em28xx_IR *ir = rc_dev->priv; + struct em28xx *dev = ir->dev; + u8 ir_config = EM2874_IR_RC5; + + /* Adjust xclk and set type based on IR table for RC5/NEC/RC6 tables */ if (*rc_type & RC_BIT_RC5) { dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE; ir->full_code = 1; @@ -379,33 +409,47 @@ static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) ir_config = EM2874_IR_NEC | EM2874_IR_NEC_NO_PARITY; ir->full_code = 1; *rc_type = RC_BIT_NEC; - } else if (*rc_type != RC_BIT_UNKNOWN) - rc = -EINVAL; + } else if (*rc_type & RC_BIT_RC6_0) { + dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE; + ir_config = EM2874_IR_RC6_MODE_0; + ir->full_code = 1; + *rc_type = RC_BIT_RC6_0; + } else if (*rc_type & RC_BIT_UNKNOWN) { + *rc_type = RC_BIT_UNKNOWN; + } else { + *rc_type = ir->rc_type; + return -EINVAL; + } - ir->rc_type = *rc_type; + ir->get_key = em2874_polling_getkey; + em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1); em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, EM28XX_XCLK_IR_RC5_MODE); + ir->rc_type = *rc_type; + + return 0; +} +static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) +{ + struct em28xx_IR *ir = rc_dev->priv; + struct em28xx *dev = ir->dev; + /* Setup the proper handler based on the chip */ switch (dev->chip_id) { case CHIP_ID_EM2860: case CHIP_ID_EM2883: - ir->get_key = default_polling_getkey; - break; + return em2860_ir_change_protocol(rc_dev, rc_type); case CHIP_ID_EM2884: case CHIP_ID_EM2874: case CHIP_ID_EM28174: - ir->get_key = em2874_polling_getkey; - em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1); - break; + return em2874_ir_change_protocol(rc_dev, rc_type); default: printk("Unrecognized em28xx chip id 0x%02x: IR not supported\n", dev->chip_id); - rc = -EINVAL; + return -EINVAL; } - - return rc; } static void em28xx_register_i2c_ir(struct em28xx *dev) @@ -573,6 +617,21 @@ static int em28xx_ir_init(struct em28xx *dev) rc->open = em28xx_ir_start; rc->close = em28xx_ir_stop; + switch (dev->chip_id) { + case CHIP_ID_EM2860: + case CHIP_ID_EM2883: + rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC; + break; + case CHIP_ID_EM2884: + case CHIP_ID_EM2874: + case CHIP_ID_EM28174: + rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC | RC_BIT_RC6_0; + break; + default: + err = -ENODEV; + goto err_out_free; + } + /* By default, keep protocol field untouched */ rc_type = RC_BIT_UNKNOWN; err = em28xx_ir_change_protocol(rc, &rc_type); @@ -615,9 +674,9 @@ static int em28xx_ir_init(struct em28xx *dev) return 0; - err_out_stop: +err_out_stop: dev->ir = NULL; - err_out_free: +err_out_free: rc_free_device(rc); kfree(ir); return err; -- cgit v1.2.3 From 2f5741aa6a71aea6bc8f186e8753f270ae8742f1 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 22 Dec 2012 10:13:38 -0300 Subject: [media] em28xx: input: fix oops on device removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When em28xx_ir_init() fails due to an configuration error, it frees the memory of struct em28xx_IR *ir, but doesn't set the corresponding pointer in the device struct to NULL. On device removal, em28xx_ir_fini() gets called, which then calls rc_unregister_device() with a pointer to freed memory. Fixes bug 26572 (http://bugzilla.kernel.org/show_bug.cgi?id=26572) Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 3899ea823336..3598221378ac 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -600,7 +600,7 @@ static int em28xx_ir_init(struct em28xx *dev) ir = kzalloc(sizeof(*ir), GFP_KERNEL); rc = rc_allocate_device(); if (!ir || !rc) - goto err_out_free; + goto error; /* record handles to ourself */ ir->dev = dev; @@ -629,14 +629,14 @@ static int em28xx_ir_init(struct em28xx *dev) break; default: err = -ENODEV; - goto err_out_free; + goto error; } /* By default, keep protocol field untouched */ rc_type = RC_BIT_UNKNOWN; err = em28xx_ir_change_protocol(rc, &rc_type); if (err) - goto err_out_free; + goto error; /* This is how often we ask the chip for IR information */ ir->polling = 100; /* ms */ @@ -661,7 +661,7 @@ static int em28xx_ir_init(struct em28xx *dev) /* all done */ err = rc_register_device(rc); if (err) - goto err_out_stop; + goto error; em28xx_register_i2c_ir(dev); @@ -674,9 +674,8 @@ static int em28xx_ir_init(struct em28xx *dev) return 0; -err_out_stop: +error: dev->ir = NULL; -err_out_free: rc_free_device(rc); kfree(ir); return err; -- cgit v1.2.3 From c02ec71b014ea7bc6f50deb9db765bf57d593c53 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:33 -0300 Subject: [media] em28xx: fix wrong data offset for non-interlaced mode in em28xx_copy_video MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit em28xx_copy_video uses a wrong offset for the target buffer when copying the data from an USB isoc packet. This happens only for the second and all following lines in the packet. The reason why this bug doesn't cause image corruption with my test device (SilverCrest Webcam 1.3 MPix) is, that this device never sends any packets that cross the end of a line. I don't know if all devices behave like this, so this patch should be considered for stable. With the upcoming patches to add support for USB bulk transfers, em28xx_copy_video will be called once per URB, which will always trigger this bug. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 766ad125fc08..202e00bb2b6e 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -207,15 +207,10 @@ static void em28xx_copy_video(struct em28xx *dev, startread = p; remain = len; - if (dev->progressive) + if (dev->progressive || buf->top_field) fieldstart = outp; - else { - /* Interlaces two half frames */ - if (buf->top_field) - fieldstart = outp; - else - fieldstart = outp + bytesperline; - } + else /* interlaced mode, even nr. of lines */ + fieldstart = outp + bytesperline; linesdone = dma_q->pos / bytesperline; currlinedone = dma_q->pos % bytesperline; @@ -243,7 +238,10 @@ static void em28xx_copy_video(struct em28xx *dev, remain -= lencopy; while (remain > 0) { - startwrite += lencopy + bytesperline; + if (dev->progressive) + startwrite += lencopy; + else + startwrite += lencopy + bytesperline; startread += lencopy; if (bytesperline > remain) lencopy = remain; -- cgit v1.2.3 From 8c3015676f64289577c79c3b231b12acd0c2c62b Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:34 -0300 Subject: [media] em28xx: clarify meaning of field 'progressive' in struct em28xx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 86e90d86da6d..ad9eec063275 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -497,7 +497,7 @@ struct em28xx { int sensor_xres, sensor_yres; int sensor_xtal; - /* Allows progressive (e. g. non-interlaced) mode */ + /* Progressive (non-interlaced) mode */ int progressive; /* Vinmode/Vinctl used at the driver */ -- cgit v1.2.3 From 515688a8985c023ba47cc89eb6a22564fab76694 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:35 -0300 Subject: [media] em28xx: rename isoc packet number constants and parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename EM28XX_NUM_PACKETS to EM28XX_NUM_ISOC_PACKETS and EM28XX_DVB_MAX_PACKETS to EM28XX_DVB_NUM_ISOC_PACKETS to clarify that these values are used only for isoc usb transfers. Also use the term num_packets instead of max_packets, as this is how these values are used and called in struct urb. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 2 +- drivers/media/usb/em28xx/em28xx-core.c | 8 ++++---- drivers/media/usb/em28xx/em28xx-dvb.c | 5 +++-- drivers/media/usb/em28xx/em28xx-video.c | 4 ++-- drivers/media/usb/em28xx/em28xx.h | 10 +++++----- 5 files changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 619bffbab3bc..7cd2faf610b9 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3323,7 +3323,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, if (has_dvb) { /* pre-allocate DVB isoc transfer buffers */ retval = em28xx_alloc_isoc(dev, EM28XX_DIGITAL_MODE, - EM28XX_DVB_MAX_PACKETS, + EM28XX_DVB_NUM_ISOC_PACKETS, EM28XX_DVB_NUM_BUFS, dev->dvb_max_pkt_size); if (retval) { diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index bed07a6c33f8..2520a164bfa9 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -1034,7 +1034,7 @@ EXPORT_SYMBOL_GPL(em28xx_stop_urbs); * Allocate URBs */ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, - int max_packets, int num_bufs, int max_pkt_size) + int num_packets, int num_bufs, int max_pkt_size) { struct em28xx_usb_isoc_bufs *isoc_bufs; int i; @@ -1069,7 +1069,7 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, } isoc_bufs->max_pkt_size = max_pkt_size; - isoc_bufs->num_packets = max_packets; + isoc_bufs->num_packets = num_packets; dev->isoc_ctl.vid_buf = NULL; dev->isoc_ctl.vbi_buf = NULL; @@ -1129,7 +1129,7 @@ EXPORT_SYMBOL_GPL(em28xx_alloc_isoc); * Allocate URBs and start IRQ */ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, - int max_packets, int num_bufs, int max_pkt_size, + int num_packets, int num_bufs, int max_pkt_size, int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) { struct em28xx_dmaqueue *dma_q = &dev->vidq; @@ -1153,7 +1153,7 @@ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, } if (alloc) { - rc = em28xx_alloc_isoc(dev, mode, max_packets, + rc = em28xx_alloc_isoc(dev, mode, num_packets, num_bufs, max_pkt_size); if (rc) return rc; diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index e8008810b60c..581578f39133 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -173,11 +173,12 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) return max_dvb_packet_size; dprintk(1, "Using %d buffers each with %d x %d bytes\n", EM28XX_DVB_NUM_BUFS, - EM28XX_DVB_MAX_PACKETS, + EM28XX_DVB_NUM_ISOC_PACKETS, max_dvb_packet_size); return em28xx_init_isoc(dev, EM28XX_DIGITAL_MODE, - EM28XX_DVB_MAX_PACKETS, EM28XX_DVB_NUM_BUFS, + EM28XX_DVB_NUM_ISOC_PACKETS, + EM28XX_DVB_NUM_BUFS, max_dvb_packet_size, em28xx_dvb_isoc_copy); } diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 202e00bb2b6e..94b51da01af0 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -764,13 +764,13 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, if (urb_init) { if (em28xx_vbi_supported(dev) == 1) rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, - EM28XX_NUM_PACKETS, + EM28XX_NUM_ISOC_PACKETS, EM28XX_NUM_BUFS, dev->max_pkt_size, em28xx_isoc_copy_vbi); else rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, - EM28XX_NUM_PACKETS, + EM28XX_NUM_ISOC_PACKETS, EM28XX_NUM_BUFS, dev->max_pkt_size, em28xx_isoc_copy); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index ad9eec063275..36a786460e18 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -157,12 +157,12 @@ #define EM28XX_NUM_BUFS 5 #define EM28XX_DVB_NUM_BUFS 5 -/* number of packets for each buffer +/* isoc transfers: number of packets for each buffer windows requests only 64 packets .. so we better do the same this is what I found out for all alternate numbers there! */ -#define EM28XX_NUM_PACKETS 64 -#define EM28XX_DVB_MAX_PACKETS 64 +#define EM28XX_NUM_ISOC_PACKETS 64 +#define EM28XX_DVB_NUM_ISOC_PACKETS 64 #define EM28XX_INTERLACED_DEFAULT 1 @@ -667,9 +667,9 @@ int em28xx_set_outfmt(struct em28xx *dev); int em28xx_resolution_set(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, - int max_packets, int num_bufs, int max_pkt_size); + int num_packets, int num_bufs, int max_pkt_size); int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, - int max_packets, int num_bufs, int max_pkt_size, + int num_packets, int num_bufs, int max_pkt_size, int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode); void em28xx_stop_urbs(struct em28xx *dev); -- cgit v1.2.3 From f0fa9936f577597dabd4a0140095bb3b02988814 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:36 -0300 Subject: [media] em28xx: rename struct em28xx_usb_isoc_bufs to em28xx_usb_bufs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It will be used for USB bulk transfers, too. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 8 ++++---- drivers/media/usb/em28xx/em28xx.h | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 2520a164bfa9..b250a63827d3 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -964,7 +964,7 @@ static void em28xx_irq_callback(struct urb *urb) void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) { struct urb *urb; - struct em28xx_usb_isoc_bufs *isoc_bufs; + struct em28xx_usb_bufs *isoc_bufs; int i; em28xx_isocdbg("em28xx: called em28xx_uninit_isoc in mode %d\n", mode); @@ -1012,7 +1012,7 @@ void em28xx_stop_urbs(struct em28xx *dev) { int i; struct urb *urb; - struct em28xx_usb_isoc_bufs *isoc_bufs = &dev->isoc_ctl.digital_bufs; + struct em28xx_usb_bufs *isoc_bufs = &dev->isoc_ctl.digital_bufs; em28xx_isocdbg("em28xx: called em28xx_stop_urbs\n"); @@ -1036,7 +1036,7 @@ EXPORT_SYMBOL_GPL(em28xx_stop_urbs); int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, int num_packets, int num_bufs, int max_pkt_size) { - struct em28xx_usb_isoc_bufs *isoc_bufs; + struct em28xx_usb_bufs *isoc_bufs; int i; int sb_size, pipe; struct urb *urb; @@ -1134,7 +1134,7 @@ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, { struct em28xx_dmaqueue *dma_q = &dev->vidq; struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; - struct em28xx_usb_isoc_bufs *isoc_bufs; + struct em28xx_usb_bufs *isoc_bufs; int i; int rc; int alloc; diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 36a786460e18..e062a2767d2f 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -203,7 +203,7 @@ enum em28xx_mode { struct em28xx; -struct em28xx_usb_isoc_bufs { +struct em28xx_usb_bufs { /* max packet size of isoc transaction */ int max_pkt_size; @@ -213,19 +213,19 @@ struct em28xx_usb_isoc_bufs { /* number of allocated urbs */ int num_bufs; - /* urb for isoc transfers */ + /* urb for isoc/bulk transfers */ struct urb **urb; - /* transfer buffers for isoc transfer */ + /* transfer buffers for isoc/bulk transfer */ char **transfer_buffer; }; struct em28xx_usb_isoc_ctl { /* isoc transfer buffers for analog mode */ - struct em28xx_usb_isoc_bufs analog_bufs; + struct em28xx_usb_bufs analog_bufs; /* isoc transfer buffers for digital mode */ - struct em28xx_usb_isoc_bufs digital_bufs; + struct em28xx_usb_bufs digital_bufs; /* Stores already requested buffers */ struct em28xx_buffer *vid_buf; -- cgit v1.2.3 From 74209dc06a7c27401de637cc371f54920d628ba8 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:37 -0300 Subject: [media] em28xx: rename struct em28xx_usb_isoc_ctl to em28xx_usb_ctl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also rename the corresponding field isoc_ctl in struct em28xx to usb_ctl. We will use this struct for USB bulk transfers, too. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 24 ++++++++++++------------ drivers/media/usb/em28xx/em28xx-vbi.c | 4 ++-- drivers/media/usb/em28xx/em28xx-video.c | 24 ++++++++++++------------ drivers/media/usb/em28xx/em28xx.h | 12 ++++++------ 4 files changed, 32 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index b250a63827d3..0892d9225d6a 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -941,7 +941,7 @@ static void em28xx_irq_callback(struct urb *urb) /* Copy data from URB */ spin_lock(&dev->slock); - dev->isoc_ctl.isoc_copy(dev, urb); + dev->usb_ctl.urb_data_copy(dev, urb); spin_unlock(&dev->slock); /* Reset urb buffers */ @@ -970,9 +970,9 @@ void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) em28xx_isocdbg("em28xx: called em28xx_uninit_isoc in mode %d\n", mode); if (mode == EM28XX_DIGITAL_MODE) - isoc_bufs = &dev->isoc_ctl.digital_bufs; + isoc_bufs = &dev->usb_ctl.digital_bufs; else - isoc_bufs = &dev->isoc_ctl.analog_bufs; + isoc_bufs = &dev->usb_ctl.analog_bufs; for (i = 0; i < isoc_bufs->num_bufs; i++) { urb = isoc_bufs->urb[i]; @@ -1012,7 +1012,7 @@ void em28xx_stop_urbs(struct em28xx *dev) { int i; struct urb *urb; - struct em28xx_usb_bufs *isoc_bufs = &dev->isoc_ctl.digital_bufs; + struct em28xx_usb_bufs *isoc_bufs = &dev->usb_ctl.digital_bufs; em28xx_isocdbg("em28xx: called em28xx_stop_urbs\n"); @@ -1045,9 +1045,9 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode); if (mode == EM28XX_DIGITAL_MODE) - isoc_bufs = &dev->isoc_ctl.digital_bufs; + isoc_bufs = &dev->usb_ctl.digital_bufs; else - isoc_bufs = &dev->isoc_ctl.analog_bufs; + isoc_bufs = &dev->usb_ctl.analog_bufs; /* De-allocates all pending stuff */ em28xx_uninit_isoc(dev, mode); @@ -1070,8 +1070,8 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, isoc_bufs->max_pkt_size = max_pkt_size; isoc_bufs->num_packets = num_packets; - dev->isoc_ctl.vid_buf = NULL; - dev->isoc_ctl.vbi_buf = NULL; + dev->usb_ctl.vid_buf = NULL; + dev->usb_ctl.vbi_buf = NULL; sb_size = isoc_bufs->num_packets * isoc_bufs->max_pkt_size; @@ -1079,7 +1079,7 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, for (i = 0; i < isoc_bufs->num_bufs; i++) { urb = usb_alloc_urb(isoc_bufs->num_packets, GFP_KERNEL); if (!urb) { - em28xx_err("cannot alloc isoc_ctl.urb %i\n", i); + em28xx_err("cannot alloc usb_ctl.urb %i\n", i); em28xx_uninit_isoc(dev, mode); return -ENOMEM; } @@ -1141,14 +1141,14 @@ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, em28xx_isocdbg("em28xx: called em28xx_init_isoc in mode %d\n", mode); - dev->isoc_ctl.isoc_copy = isoc_copy; + dev->usb_ctl.urb_data_copy = isoc_copy; if (mode == EM28XX_DIGITAL_MODE) { - isoc_bufs = &dev->isoc_ctl.digital_bufs; + isoc_bufs = &dev->usb_ctl.digital_bufs; /* no need to free/alloc isoc buffers in digital mode */ alloc = 0; } else { - isoc_bufs = &dev->isoc_ctl.analog_bufs; + isoc_bufs = &dev->usb_ctl.analog_bufs; alloc = 1; } diff --git a/drivers/media/usb/em28xx/em28xx-vbi.c b/drivers/media/usb/em28xx/em28xx-vbi.c index 2b4c9cba2d67..d74713bd1d18 100644 --- a/drivers/media/usb/em28xx/em28xx-vbi.c +++ b/drivers/media/usb/em28xx/em28xx-vbi.c @@ -60,8 +60,8 @@ free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) VIDEOBUF_ACTIVE, it won't be, though. */ spin_lock_irqsave(&dev->slock, flags); - if (dev->isoc_ctl.vbi_buf == buf) - dev->isoc_ctl.vbi_buf = NULL; + if (dev->usb_ctl.vbi_buf == buf) + dev->usb_ctl.vbi_buf = NULL; spin_unlock_irqrestore(&dev->slock, flags); videobuf_vmalloc_free(&buf->vb); diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 94b51da01af0..4326b9b0e8c7 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -165,7 +165,7 @@ static inline void buffer_filled(struct em28xx *dev, buf->vb.field_count++; v4l2_get_timestamp(&buf->vb.ts); - dev->isoc_ctl.vid_buf = NULL; + dev->usb_ctl.vid_buf = NULL; list_del(&buf->vb.queue); wake_up(&buf->vb.done); @@ -182,7 +182,7 @@ static inline void vbi_buffer_filled(struct em28xx *dev, buf->vb.field_count++; v4l2_get_timestamp(&buf->vb.ts); - dev->isoc_ctl.vbi_buf = NULL; + dev->usb_ctl.vbi_buf = NULL; list_del(&buf->vb.queue); wake_up(&buf->vb.done); @@ -368,7 +368,7 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, if (list_empty(&dma_q->active)) { em28xx_isocdbg("No active queue to serve\n"); - dev->isoc_ctl.vid_buf = NULL; + dev->usb_ctl.vid_buf = NULL; *buf = NULL; return; } @@ -380,7 +380,7 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, outp = videobuf_to_vmalloc(&(*buf)->vb); memset(outp, 0, (*buf)->vb.size); - dev->isoc_ctl.vid_buf = *buf; + dev->usb_ctl.vid_buf = *buf; return; } @@ -396,7 +396,7 @@ static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q, if (list_empty(&dma_q->active)) { em28xx_isocdbg("No active queue to serve\n"); - dev->isoc_ctl.vbi_buf = NULL; + dev->usb_ctl.vbi_buf = NULL; *buf = NULL; return; } @@ -407,7 +407,7 @@ static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q, outp = videobuf_to_vmalloc(&(*buf)->vb); memset(outp, 0x00, (*buf)->vb.size); - dev->isoc_ctl.vbi_buf = *buf; + dev->usb_ctl.vbi_buf = *buf; return; } @@ -435,7 +435,7 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) return 0; } - buf = dev->isoc_ctl.vid_buf; + buf = dev->usb_ctl.vid_buf; if (buf != NULL) outp = videobuf_to_vmalloc(&buf->vb); @@ -531,11 +531,11 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) return 0; } - buf = dev->isoc_ctl.vid_buf; + buf = dev->usb_ctl.vid_buf; if (buf != NULL) outp = videobuf_to_vmalloc(&buf->vb); - vbi_buf = dev->isoc_ctl.vbi_buf; + vbi_buf = dev->usb_ctl.vbi_buf; if (vbi_buf != NULL) vbioutp = videobuf_to_vmalloc(&vbi_buf->vb); @@ -725,8 +725,8 @@ static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) VIDEOBUF_ACTIVE, it won't be, though. */ spin_lock_irqsave(&dev->slock, flags); - if (dev->isoc_ctl.vid_buf == buf) - dev->isoc_ctl.vid_buf = NULL; + if (dev->usb_ctl.vid_buf == buf) + dev->usb_ctl.vid_buf = NULL; spin_unlock_irqrestore(&dev->slock, flags); videobuf_vmalloc_free(&buf->vb); @@ -758,7 +758,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, goto fail; } - if (!dev->isoc_ctl.analog_bufs.num_bufs) + if (!dev->usb_ctl.analog_bufs.num_bufs) urb_init = 1; if (urb_init) { diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index e062a2767d2f..17310e6a51e2 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -220,19 +220,19 @@ struct em28xx_usb_bufs { char **transfer_buffer; }; -struct em28xx_usb_isoc_ctl { - /* isoc transfer buffers for analog mode */ +struct em28xx_usb_ctl { + /* isoc/bulk transfer buffers for analog mode */ struct em28xx_usb_bufs analog_bufs; - /* isoc transfer buffers for digital mode */ + /* isoc/bulk transfer buffers for digital mode */ struct em28xx_usb_bufs digital_bufs; /* Stores already requested buffers */ struct em28xx_buffer *vid_buf; struct em28xx_buffer *vbi_buf; - /* isoc urb callback */ - int (*isoc_copy) (struct em28xx *dev, struct urb *urb); + /* copy data from URB */ + int (*urb_data_copy) (struct em28xx *dev, struct urb *urb); }; @@ -582,7 +582,7 @@ struct em28xx { /* Isoc control struct */ struct em28xx_dmaqueue vidq; struct em28xx_dmaqueue vbiq; - struct em28xx_usb_isoc_ctl isoc_ctl; + struct em28xx_usb_ctl usb_ctl; spinlock_t slock; /* usb transfer */ -- cgit v1.2.3 From 89f84b9c2057fbfc85796d29d53d5b8e01002315 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:38 -0300 Subject: [media] em28xx: remove obsolete #define EM28XX_URB_TIMEOUT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It isn't used anymore and uses constants which no longer exist. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 17310e6a51e2..6773ca8d3647 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -187,10 +187,6 @@ Interval: 125us */ -/* time to wait when stopping the isoc transfer */ -#define EM28XX_URB_TIMEOUT \ - msecs_to_jiffies(EM28XX_NUM_BUFS * EM28XX_NUM_PACKETS) - /* time in msecs to wait for i2c writes to finish */ #define EM2800_I2C_WRITE_TIMEOUT 20 -- cgit v1.2.3 From 836e93bf6a7f0e5385f850f51d66cd1612e815aa Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:39 -0300 Subject: [media] em28xx: update description of em28xx_irq_callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit em28xx_irq_callback can be used for isoc and bulk transfers. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 0892d9225d6a..8f50f5c8d069 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -919,7 +919,7 @@ EXPORT_SYMBOL_GPL(em28xx_set_mode); ------------------------------------------------------------------*/ /* - * IRQ callback, called by URB callback + * URB completion handler for isoc/bulk transfers */ static void em28xx_irq_callback(struct urb *urb) { @@ -946,6 +946,7 @@ static void em28xx_irq_callback(struct urb *urb) /* Reset urb buffers */ for (i = 0; i < urb->number_of_packets; i++) { + /* isoc only (bulk: number_of_packets = 0) */ urb->iso_frame_desc[i].status = 0; urb->iso_frame_desc[i].actual_length = 0; } -- cgit v1.2.3 From afb177e06563861bfe4d7795a9d4d3b52851813b Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:40 -0300 Subject: [media] em28xx: rename function em28xx_uninit_isoc to em28xx_uninit_usb_xfer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function will be used to uninitialize USB bulk transfers, too. Also rename the local variable isoc_bufs to usb_bufs. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 4 +-- drivers/media/usb/em28xx/em28xx-core.c | 43 +++++++++++++++++---------------- drivers/media/usb/em28xx/em28xx-video.c | 2 +- drivers/media/usb/em28xx/em28xx.h | 2 +- 4 files changed, 26 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 7cd2faf610b9..e474ccf07832 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3394,7 +3394,7 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) video_device_node_name(dev->vdev)); dev->state |= DEV_MISCONFIGURED; - em28xx_uninit_isoc(dev, dev->mode); + em28xx_uninit_usb_xfer(dev, dev->mode); dev->state |= DEV_DISCONNECTED; } else { dev->state |= DEV_DISCONNECTED; @@ -3402,7 +3402,7 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) } /* free DVB isoc buffers */ - em28xx_uninit_isoc(dev, EM28XX_DIGITAL_MODE); + em28xx_uninit_usb_xfer(dev, EM28XX_DIGITAL_MODE); mutex_unlock(&dev->lock); diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 8f50f5c8d069..a1ebd08c5bcd 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -962,49 +962,50 @@ static void em28xx_irq_callback(struct urb *urb) /* * Stop and Deallocate URBs */ -void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) +void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode) { struct urb *urb; - struct em28xx_usb_bufs *isoc_bufs; + struct em28xx_usb_bufs *usb_bufs; int i; - em28xx_isocdbg("em28xx: called em28xx_uninit_isoc in mode %d\n", mode); + em28xx_isocdbg("em28xx: called em28xx_uninit_usb_xfer in mode %d\n", + mode); if (mode == EM28XX_DIGITAL_MODE) - isoc_bufs = &dev->usb_ctl.digital_bufs; + usb_bufs = &dev->usb_ctl.digital_bufs; else - isoc_bufs = &dev->usb_ctl.analog_bufs; + usb_bufs = &dev->usb_ctl.analog_bufs; - for (i = 0; i < isoc_bufs->num_bufs; i++) { - urb = isoc_bufs->urb[i]; + for (i = 0; i < usb_bufs->num_bufs; i++) { + urb = usb_bufs->urb[i]; if (urb) { if (!irqs_disabled()) usb_kill_urb(urb); else usb_unlink_urb(urb); - if (isoc_bufs->transfer_buffer[i]) { + if (usb_bufs->transfer_buffer[i]) { usb_free_coherent(dev->udev, urb->transfer_buffer_length, - isoc_bufs->transfer_buffer[i], + usb_bufs->transfer_buffer[i], urb->transfer_dma); } usb_free_urb(urb); - isoc_bufs->urb[i] = NULL; + usb_bufs->urb[i] = NULL; } - isoc_bufs->transfer_buffer[i] = NULL; + usb_bufs->transfer_buffer[i] = NULL; } - kfree(isoc_bufs->urb); - kfree(isoc_bufs->transfer_buffer); + kfree(usb_bufs->urb); + kfree(usb_bufs->transfer_buffer); - isoc_bufs->urb = NULL; - isoc_bufs->transfer_buffer = NULL; - isoc_bufs->num_bufs = 0; + usb_bufs->urb = NULL; + usb_bufs->transfer_buffer = NULL; + usb_bufs->num_bufs = 0; em28xx_capture_start(dev, 0); } -EXPORT_SYMBOL_GPL(em28xx_uninit_isoc); +EXPORT_SYMBOL_GPL(em28xx_uninit_usb_xfer); /* * Stop URBs @@ -1051,7 +1052,7 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, isoc_bufs = &dev->usb_ctl.analog_bufs; /* De-allocates all pending stuff */ - em28xx_uninit_isoc(dev, mode); + em28xx_uninit_usb_xfer(dev, mode); isoc_bufs->num_bufs = num_bufs; @@ -1081,7 +1082,7 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, urb = usb_alloc_urb(isoc_bufs->num_packets, GFP_KERNEL); if (!urb) { em28xx_err("cannot alloc usb_ctl.urb %i\n", i); - em28xx_uninit_isoc(dev, mode); + em28xx_uninit_usb_xfer(dev, mode); return -ENOMEM; } isoc_bufs->urb[i] = urb; @@ -1093,7 +1094,7 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, " buffer %i%s\n", sb_size, i, in_interrupt() ? " while in int" : ""); - em28xx_uninit_isoc(dev, mode); + em28xx_uninit_usb_xfer(dev, mode); return -ENOMEM; } memset(isoc_bufs->transfer_buffer[i], 0, sb_size); @@ -1171,7 +1172,7 @@ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, if (rc) { em28xx_err("submit of urb %i failed (error=%i)\n", i, rc); - em28xx_uninit_isoc(dev, mode); + em28xx_uninit_usb_xfer(dev, mode); return rc; } } diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 4326b9b0e8c7..d61d4dcca626 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -2272,7 +2272,7 @@ static int em28xx_v4l2_close(struct file *filp) v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); /* do this before setting alternate! */ - em28xx_uninit_isoc(dev, EM28XX_ANALOG_MODE); + em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); em28xx_set_mode(dev, EM28XX_SUSPEND); /* set alternate 0 */ diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 6773ca8d3647..8fb350479e9e 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -667,7 +667,7 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, int num_packets, int num_bufs, int max_pkt_size, int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); -void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode); +void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode); void em28xx_stop_urbs(struct em28xx *dev); int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev); int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode); -- cgit v1.2.3 From 6ddd89d0c90ec384d5a8058cb38679beb03c7eb7 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:41 -0300 Subject: [media] em28xx: create a common function for isoc and bulk URB allocation and setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the existing function for isoc transfers em28xx_init_isoc to em28xx_init_usb_xfer and extend it. URB allocation and setup is now done depending on the USB transfer type, which is selected with a new function parameter. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 6 +- drivers/media/usb/em28xx/em28xx-core.c | 101 ++++++++++++++++++-------------- drivers/media/usb/em28xx/em28xx.h | 4 +- 3 files changed, 61 insertions(+), 50 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index e474ccf07832..bfce34d491c0 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3322,10 +3322,10 @@ static int em28xx_usb_probe(struct usb_interface *interface, if (has_dvb) { /* pre-allocate DVB isoc transfer buffers */ - retval = em28xx_alloc_isoc(dev, EM28XX_DIGITAL_MODE, - EM28XX_DVB_NUM_ISOC_PACKETS, + retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, 0, EM28XX_DVB_NUM_BUFS, - dev->dvb_max_pkt_size); + dev->dvb_max_pkt_size, + EM28XX_DVB_NUM_ISOC_PACKETS); if (retval) { goto unlock_and_free; } diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index a1ebd08c5bcd..42388dec5f32 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -5,6 +5,7 @@ Markus Rechberger Mauro Carvalho Chehab Sascha Sommer + Copyright (C) 2012 Frank Schäfer 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 @@ -1035,10 +1036,10 @@ EXPORT_SYMBOL_GPL(em28xx_stop_urbs); /* * Allocate URBs */ -int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, - int num_packets, int num_bufs, int max_pkt_size) +int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, + int num_bufs, int max_pkt_size, int packet_multiplier) { - struct em28xx_usb_bufs *isoc_bufs; + struct em28xx_usb_bufs *usb_bufs; int i; int sb_size, pipe; struct urb *urb; @@ -1047,49 +1048,52 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode); if (mode == EM28XX_DIGITAL_MODE) - isoc_bufs = &dev->usb_ctl.digital_bufs; + usb_bufs = &dev->usb_ctl.digital_bufs; else - isoc_bufs = &dev->usb_ctl.analog_bufs; + usb_bufs = &dev->usb_ctl.analog_bufs; /* De-allocates all pending stuff */ em28xx_uninit_usb_xfer(dev, mode); - isoc_bufs->num_bufs = num_bufs; + usb_bufs->num_bufs = num_bufs; - isoc_bufs->urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); - if (!isoc_bufs->urb) { + usb_bufs->urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!usb_bufs->urb) { em28xx_errdev("cannot alloc memory for usb buffers\n"); return -ENOMEM; } - isoc_bufs->transfer_buffer = kzalloc(sizeof(void *)*num_bufs, + usb_bufs->transfer_buffer = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); - if (!isoc_bufs->transfer_buffer) { + if (!usb_bufs->transfer_buffer) { em28xx_errdev("cannot allocate memory for usb transfer\n"); - kfree(isoc_bufs->urb); + kfree(usb_bufs->urb); return -ENOMEM; } - isoc_bufs->max_pkt_size = max_pkt_size; - isoc_bufs->num_packets = num_packets; + usb_bufs->max_pkt_size = max_pkt_size; + if (xfer_bulk) + usb_bufs->num_packets = 0; + else + usb_bufs->num_packets = packet_multiplier; dev->usb_ctl.vid_buf = NULL; dev->usb_ctl.vbi_buf = NULL; - sb_size = isoc_bufs->num_packets * isoc_bufs->max_pkt_size; + sb_size = packet_multiplier * usb_bufs->max_pkt_size; /* allocate urbs and transfer buffers */ - for (i = 0; i < isoc_bufs->num_bufs; i++) { - urb = usb_alloc_urb(isoc_bufs->num_packets, GFP_KERNEL); + for (i = 0; i < usb_bufs->num_bufs; i++) { + urb = usb_alloc_urb(usb_bufs->num_packets, GFP_KERNEL); if (!urb) { em28xx_err("cannot alloc usb_ctl.urb %i\n", i); em28xx_uninit_usb_xfer(dev, mode); return -ENOMEM; } - isoc_bufs->urb[i] = urb; + usb_bufs->urb[i] = urb; - isoc_bufs->transfer_buffer[i] = usb_alloc_coherent(dev->udev, + usb_bufs->transfer_buffer[i] = usb_alloc_coherent(dev->udev, sb_size, GFP_KERNEL, &urb->transfer_dma); - if (!isoc_bufs->transfer_buffer[i]) { + if (!usb_bufs->transfer_buffer[i]) { em28xx_err("unable to allocate %i bytes for transfer" " buffer %i%s\n", sb_size, i, @@ -1097,35 +1101,42 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, em28xx_uninit_usb_xfer(dev, mode); return -ENOMEM; } - memset(isoc_bufs->transfer_buffer[i], 0, sb_size); - - /* FIXME: this is a hack - should be - 'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK' - should also be using 'desc.bInterval' - */ - pipe = usb_rcvisocpipe(dev->udev, - mode == EM28XX_ANALOG_MODE ? - EM28XX_EP_ANALOG : EM28XX_EP_DIGITAL); - - usb_fill_int_urb(urb, dev->udev, pipe, - isoc_bufs->transfer_buffer[i], sb_size, - em28xx_irq_callback, dev, 1); - - urb->number_of_packets = isoc_bufs->num_packets; - urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - - k = 0; - for (j = 0; j < isoc_bufs->num_packets; j++) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = - isoc_bufs->max_pkt_size; - k += isoc_bufs->max_pkt_size; + memset(usb_bufs->transfer_buffer[i], 0, sb_size); + + if (xfer_bulk) { /* bulk */ + pipe = usb_rcvbulkpipe(dev->udev, + mode == EM28XX_ANALOG_MODE ? + EM28XX_EP_ANALOG : + EM28XX_EP_DIGITAL); + usb_fill_bulk_urb(urb, dev->udev, pipe, + usb_bufs->transfer_buffer[i], sb_size, + em28xx_irq_callback, dev); + urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + } else { /* isoc */ + pipe = usb_rcvisocpipe(dev->udev, + mode == EM28XX_ANALOG_MODE ? + EM28XX_EP_ANALOG : + EM28XX_EP_DIGITAL); + usb_fill_int_urb(urb, dev->udev, pipe, + usb_bufs->transfer_buffer[i], sb_size, + em28xx_irq_callback, dev, 1); + urb->transfer_flags = URB_ISO_ASAP | + URB_NO_TRANSFER_DMA_MAP; + k = 0; + for (j = 0; j < usb_bufs->num_packets; j++) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = + usb_bufs->max_pkt_size; + k += usb_bufs->max_pkt_size; + } } + + urb->number_of_packets = usb_bufs->num_packets; } return 0; } -EXPORT_SYMBOL_GPL(em28xx_alloc_isoc); +EXPORT_SYMBOL_GPL(em28xx_alloc_urbs); /* * Allocate URBs and start IRQ @@ -1155,8 +1166,8 @@ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, } if (alloc) { - rc = em28xx_alloc_isoc(dev, mode, num_packets, - num_bufs, max_pkt_size); + rc = em28xx_alloc_urbs(dev, mode, 0, num_bufs, + max_pkt_size, num_packets); if (rc) return rc; } diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 8fb350479e9e..7bc2ddd4dc0c 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -662,8 +662,8 @@ int em28xx_vbi_supported(struct em28xx *dev); int em28xx_set_outfmt(struct em28xx *dev); int em28xx_resolution_set(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); -int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, - int num_packets, int num_bufs, int max_pkt_size); +int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, + int num_bufs, int max_pkt_size, int packet_multiplier); int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, int num_packets, int num_bufs, int max_pkt_size, int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); -- cgit v1.2.3 From 057ca0da067c8c0c734088eba229ab06e21bc88c Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:42 -0300 Subject: [media] em28xx: create a common function for isoc and bulk USB transfer initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - rename em28xx_init_isoc to em28xx_init_usb_xfer - add parameter for isoc/bulk transfer selection which is passed to em28xx_alloc_urbs - rename local variable isoc_buf to usb_bufs Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 30 ++++++++++++++++-------------- drivers/media/usb/em28xx/em28xx-dvb.c | 9 +++++---- drivers/media/usb/em28xx/em28xx-video.c | 20 ++++++++++---------- drivers/media/usb/em28xx/em28xx.h | 8 +++++--- 4 files changed, 36 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 42388dec5f32..d8a8e8bbb870 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -1141,33 +1141,35 @@ EXPORT_SYMBOL_GPL(em28xx_alloc_urbs); /* * Allocate URBs and start IRQ */ -int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, - int num_packets, int num_bufs, int max_pkt_size, - int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) +int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode, + int xfer_bulk, int num_bufs, int max_pkt_size, + int packet_multiplier, + int (*urb_data_copy) (struct em28xx *dev, struct urb *urb)) { struct em28xx_dmaqueue *dma_q = &dev->vidq; struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; - struct em28xx_usb_bufs *isoc_bufs; + struct em28xx_usb_bufs *usb_bufs; int i; int rc; int alloc; - em28xx_isocdbg("em28xx: called em28xx_init_isoc in mode %d\n", mode); + em28xx_isocdbg("em28xx: called em28xx_init_usb_xfer in mode %d\n", + mode); - dev->usb_ctl.urb_data_copy = isoc_copy; + dev->usb_ctl.urb_data_copy = urb_data_copy; if (mode == EM28XX_DIGITAL_MODE) { - isoc_bufs = &dev->usb_ctl.digital_bufs; - /* no need to free/alloc isoc buffers in digital mode */ + usb_bufs = &dev->usb_ctl.digital_bufs; + /* no need to free/alloc usb buffers in digital mode */ alloc = 0; } else { - isoc_bufs = &dev->usb_ctl.analog_bufs; + usb_bufs = &dev->usb_ctl.analog_bufs; alloc = 1; } if (alloc) { - rc = em28xx_alloc_urbs(dev, mode, 0, num_bufs, - max_pkt_size, num_packets); + rc = em28xx_alloc_urbs(dev, mode, xfer_bulk, num_bufs, + max_pkt_size, packet_multiplier); if (rc) return rc; } @@ -1178,8 +1180,8 @@ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, em28xx_capture_start(dev, 1); /* submit urbs and enables IRQ */ - for (i = 0; i < isoc_bufs->num_bufs; i++) { - rc = usb_submit_urb(isoc_bufs->urb[i], GFP_ATOMIC); + for (i = 0; i < usb_bufs->num_bufs; i++) { + rc = usb_submit_urb(usb_bufs->urb[i], GFP_ATOMIC); if (rc) { em28xx_err("submit of urb %i failed (error=%i)\n", i, rc); @@ -1190,7 +1192,7 @@ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, return 0; } -EXPORT_SYMBOL_GPL(em28xx_init_isoc); +EXPORT_SYMBOL_GPL(em28xx_init_usb_xfer); /* * em28xx_wake_i2c() diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 581578f39133..fe4d11c67a4e 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -176,10 +176,11 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) EM28XX_DVB_NUM_ISOC_PACKETS, max_dvb_packet_size); - return em28xx_init_isoc(dev, EM28XX_DIGITAL_MODE, - EM28XX_DVB_NUM_ISOC_PACKETS, - EM28XX_DVB_NUM_BUFS, - max_dvb_packet_size, em28xx_dvb_isoc_copy); + return em28xx_init_usb_xfer(dev, EM28XX_DIGITAL_MODE, 0, + EM28XX_DVB_NUM_BUFS, + max_dvb_packet_size, + EM28XX_DVB_NUM_ISOC_PACKETS, + em28xx_dvb_isoc_copy); } static int em28xx_stop_streaming(struct em28xx_dvb *dvb) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index d61d4dcca626..a5c1a42f3923 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -763,17 +763,17 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, if (urb_init) { if (em28xx_vbi_supported(dev) == 1) - rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, - EM28XX_NUM_ISOC_PACKETS, - EM28XX_NUM_BUFS, - dev->max_pkt_size, - em28xx_isoc_copy_vbi); + rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, 0, + EM28XX_NUM_BUFS, + dev->max_pkt_size, + EM28XX_NUM_ISOC_PACKETS, + em28xx_isoc_copy_vbi); else - rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, - EM28XX_NUM_ISOC_PACKETS, - EM28XX_NUM_BUFS, - dev->max_pkt_size, - em28xx_isoc_copy); + rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, 0, + EM28XX_NUM_BUFS, + dev->max_pkt_size, + EM28XX_NUM_ISOC_PACKETS, + em28xx_isoc_copy); if (rc < 0) goto fail; } diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 7bc2ddd4dc0c..950a71786a14 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -664,9 +664,11 @@ int em28xx_resolution_set(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, int num_bufs, int max_pkt_size, int packet_multiplier); -int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, - int num_packets, int num_bufs, int max_pkt_size, - int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); +int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode, + int xfer_bulk, + int num_bufs, int max_pkt_size, int packet_multiplier, + int (*urb_data_copy) + (struct em28xx *dev, struct urb *urb)); void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode); void em28xx_stop_urbs(struct em28xx *dev); int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev); -- cgit v1.2.3 From 337fe8dad58692ac468f4139ea19624ce464d953 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:43 -0300 Subject: [media] em28xx: clear USB halt/stall condition in em28xx_init_usb_xfer when using bulk transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [mchehab@redhat.com: Fix a CodingStyle issue: don't break strings into separate lines] Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index d8a8e8bbb870..6b3485d2266a 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -1174,6 +1174,16 @@ int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode, return rc; } + if (xfer_bulk) { + rc = usb_clear_halt(dev->udev, usb_bufs->urb[0]->pipe); + if (rc < 0) { + em28xx_err("failed to clear USB bulk endpoint stall/halt condition (error=%i)\n", + rc); + em28xx_uninit_usb_xfer(dev, mode); + return rc; + } + } + init_waitqueue_head(&dma_q->wq); init_waitqueue_head(&vbi_dma_q->wq); -- cgit v1.2.3 From 1653cb0cb27fba2577933a5a2dd8df78a5bca906 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:44 -0300 Subject: [media] em28xx: remove double checks for urb->status == -ENOENT in urb_data_copy functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This check is already done in the URB handler em28xx_irq_callback before calling these functions. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-dvb.c | 5 +---- drivers/media/usb/em28xx/em28xx-video.c | 10 ++-------- 2 files changed, 3 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index fe4d11c67a4e..7583cb74d53d 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -134,11 +134,8 @@ static inline int em28xx_dvb_isoc_copy(struct em28xx *dev, struct urb *urb) if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) return 0; - if (urb->status < 0) { + if (urb->status < 0) print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } for (i = 0; i < urb->number_of_packets; i++) { int status = urb->iso_frame_desc[i].status; diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index a5c1a42f3923..6bb0b1d74e36 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -429,11 +429,8 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) return 0; - if (urb->status < 0) { + if (urb->status < 0) print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } buf = dev->usb_ctl.vid_buf; if (buf != NULL) @@ -525,11 +522,8 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) return 0; - if (urb->status < 0) { + if (urb->status < 0) print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } buf = dev->usb_ctl.vid_buf; if (buf != NULL) -- cgit v1.2.3 From 0fa4a4029025507feb6c0322cd4c4693fbad9aac Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:45 -0300 Subject: [media] em28xx: rename function em28xx_isoc_copy and extend for USB bulk transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The URB data processing for bulk transfers is very similar to what is done with isoc transfers, so create a common function that works with both transfer types based on the existing isoc function. [mchehab@redhat.com: Fix a CodingStyle issue: don't break strings into separate lines] Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 65 ++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 6bb0b1d74e36..87161eea257e 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -6,6 +6,7 @@ Markus Rechberger Mauro Carvalho Chehab Sascha Sommer + Copyright (C) 2012 Frank Schäfer Some parts based on SN9C10x PC Camera Controllers GPL driver made by Luca Risolia @@ -412,16 +413,14 @@ static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q, return; } -/* - * Controls the isoc copy of each urb packet - */ -static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) +/* Processes and copies the URB data content to a frame buffer queue */ +static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) { struct em28xx_buffer *buf; struct em28xx_dmaqueue *dma_q = &dev->vidq; - unsigned char *outp = NULL; - int i, len = 0, rc = 1; - unsigned char *p; + int xfer_bulk, num_packets, i, rc = 1; + unsigned int actual_length, len = 0; + unsigned char *p, *outp = NULL; if (!dev) return 0; @@ -432,33 +431,46 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) if (urb->status < 0) print_err_status(dev, -1, urb->status); + xfer_bulk = usb_pipebulk(urb->pipe); + buf = dev->usb_ctl.vid_buf; if (buf != NULL) outp = videobuf_to_vmalloc(&buf->vb); - for (i = 0; i < urb->number_of_packets; i++) { - int status = urb->iso_frame_desc[i].status; + if (xfer_bulk) /* bulk */ + num_packets = 1; + else /* isoc */ + num_packets = urb->number_of_packets; + + for (i = 0; i < num_packets; i++) { + if (xfer_bulk) { /* bulk */ + actual_length = urb->actual_length; + + p = urb->transfer_buffer; + } else { /* isoc */ + if (urb->iso_frame_desc[i].status < 0) { + print_err_status(dev, i, + urb->iso_frame_desc[i].status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } - if (status < 0) { - print_err_status(dev, i, status); - if (urb->iso_frame_desc[i].status != -EPROTO) + actual_length = urb->iso_frame_desc[i].actual_length; + if (actual_length > dev->max_pkt_size) { + em28xx_isocdbg("packet bigger than packet size"); continue; - } - - len = urb->iso_frame_desc[i].actual_length - 4; + } - if (urb->iso_frame_desc[i].actual_length <= 0) { - /* em28xx_isocdbg("packet %d is empty",i); - spammy */ - continue; + p = urb->transfer_buffer + + urb->iso_frame_desc[i].offset; } - if (urb->iso_frame_desc[i].actual_length > - dev->max_pkt_size) { - em28xx_isocdbg("packet bigger than packet size"); + + if (actual_length <= 0) { + /* NOTE: happens very often with isoc transfers */ + /* em28xx_usbdbg("packet %d is empty",i); - spammy */ continue; } - p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - /* FIXME: incomplete buffer checks where removed to make logic simpler. Impacts of those changes should be evaluated */ @@ -492,9 +504,12 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) } if (buf != NULL) { if (p[0] != 0x88 && p[0] != 0x22) { + /* NOTE: no intermediate data packet header + * 88 88 88 88 when using bulk transfers */ em28xx_isocdbg("frame is not complete\n"); - len += 4; + len = actual_length; } else { + len = actual_length - 4; p += 4; } em28xx_copy_video(dev, dma_q, buf, p, outp, len); @@ -767,7 +782,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, EM28XX_NUM_BUFS, dev->max_pkt_size, EM28XX_NUM_ISOC_PACKETS, - em28xx_isoc_copy); + em28xx_urb_data_copy); if (rc < 0) goto fail; } -- cgit v1.2.3 From 4601cc39773b33a336eda2010ea5a551aaf6d7f0 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:46 -0300 Subject: [media] em28xx: rename function em28xx_isoc_copy_vbi and extend for USB bulk transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The URB data processing for bulk transfers is very similar to what is done with isoc transfers, so create a common function that works with both transfer types based on the existing isoc function. [mchehab@redhat.com: Fix a CodingStyle issue: don't break strings into separate lines] Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 70 ++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 87161eea257e..df294f3858ea 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -518,18 +518,16 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) return rc; } -/* Version of isoc handler that takes into account a mixture of video and - VBI data */ -static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) +/* Version of the urb data handler that takes into account a mixture of + video and VBI data */ +static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) { struct em28xx_buffer *buf, *vbi_buf; struct em28xx_dmaqueue *dma_q = &dev->vidq; struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; - unsigned char *outp = NULL; - unsigned char *vbioutp = NULL; - int i, len = 0, rc = 1; - unsigned char *p; - int vbi_size; + int xfer_bulk, vbi_size, num_packets, i, rc = 1; + unsigned int actual_length, len = 0; + unsigned char *p, *outp = NULL, *vbioutp = NULL; if (!dev) return 0; @@ -540,6 +538,8 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) if (urb->status < 0) print_err_status(dev, -1, urb->status); + xfer_bulk = usb_pipebulk(urb->pipe); + buf = dev->usb_ctl.vid_buf; if (buf != NULL) outp = videobuf_to_vmalloc(&buf->vb); @@ -548,28 +548,40 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) if (vbi_buf != NULL) vbioutp = videobuf_to_vmalloc(&vbi_buf->vb); - for (i = 0; i < urb->number_of_packets; i++) { - int status = urb->iso_frame_desc[i].status; + if (xfer_bulk) /* bulk */ + num_packets = 1; + else /* isoc */ + num_packets = urb->number_of_packets; - if (status < 0) { - print_err_status(dev, i, status); - if (urb->iso_frame_desc[i].status != -EPROTO) + for (i = 0; i < num_packets; i++) { + if (xfer_bulk) { /* bulk */ + actual_length = urb->actual_length; + + p = urb->transfer_buffer; + } else { /* isoc */ + if (urb->iso_frame_desc[i].status < 0) { + print_err_status(dev, i, + urb->iso_frame_desc[i].status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + actual_length = urb->iso_frame_desc[i].actual_length; + if (actual_length > dev->max_pkt_size) { + em28xx_isocdbg("packet bigger than packet size"); continue; - } + } - len = urb->iso_frame_desc[i].actual_length; - if (urb->iso_frame_desc[i].actual_length <= 0) { - /* em28xx_isocdbg("packet %d is empty",i); - spammy */ - continue; + p = urb->transfer_buffer + + urb->iso_frame_desc[i].offset; } - if (urb->iso_frame_desc[i].actual_length > - dev->max_pkt_size) { - em28xx_isocdbg("packet bigger than packet size"); + + if (actual_length <= 0) { + /* NOTE: happens very often with isoc transfers */ + /* em28xx_usbdbg("packet %d is empty",i); - spammy */ continue; } - p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - /* capture type 0 = vbi start capture type 1 = video start capture type 2 = video in progress */ @@ -579,16 +591,20 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) em28xx_isocdbg("VBI START HEADER!!!\n"); dev->cur_field = p[2]; p += 4; - len -= 4; + len = actual_length - 4; } else if (p[0] == 0x88 && p[1] == 0x88 && p[2] == 0x88 && p[3] == 0x88) { /* continuation */ p += 4; - len -= 4; + len = actual_length - 4; } else if (p[0] == 0x22 && p[1] == 0x5a) { /* start video */ p += 4; - len -= 4; + len = actual_length - 4; + } else { + /* NOTE: With bulk transfers, intermediate data packets + * have no continuation header */ + len = actual_length; } vbi_size = dev->vbi_width * dev->vbi_height; @@ -776,7 +792,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, EM28XX_NUM_BUFS, dev->max_pkt_size, EM28XX_NUM_ISOC_PACKETS, - em28xx_isoc_copy_vbi); + em28xx_urb_data_copy_vbi); else rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, 0, EM28XX_NUM_BUFS, -- cgit v1.2.3 From a950e4a75ea498f2f43c90a41173fdb4235752c9 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:47 -0300 Subject: [media] em28xx: rename function em28xx_dvb_isoc_copy and extend for USB bulk transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The URB data processing for DVB bulk transfers is very similar to what is done with isoc transfers, so create a common function that works with both transfer types based on the existing isoc function. Tested with device Hauppauge HVR-930c. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-dvb.c | 44 ++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 7583cb74d53d..22ef6dd128e2 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -10,6 +10,8 @@ (c) 2008 Aidan Thornton + (c) 2012 Frank Schäfer + Based on cx88-dvb, saa7134-dvb and videobuf-dvb originally written by: (c) 2004, 2005 Chris Pascoe (c) 2004 Gerd Knorr [SuSE Labs] @@ -124,9 +126,9 @@ static inline void print_err_status(struct em28xx *dev, } } -static inline int em28xx_dvb_isoc_copy(struct em28xx *dev, struct urb *urb) +static inline int em28xx_dvb_urb_data_copy(struct em28xx *dev, struct urb *urb) { - int i; + int xfer_bulk, num_packets, i; if (!dev) return 0; @@ -137,18 +139,34 @@ static inline int em28xx_dvb_isoc_copy(struct em28xx *dev, struct urb *urb) if (urb->status < 0) print_err_status(dev, -1, urb->status); - for (i = 0; i < urb->number_of_packets; i++) { - int status = urb->iso_frame_desc[i].status; + xfer_bulk = usb_pipebulk(urb->pipe); - if (status < 0) { - print_err_status(dev, i, status); - if (urb->iso_frame_desc[i].status != -EPROTO) - continue; - } + if (xfer_bulk) /* bulk */ + num_packets = 1; + else /* isoc */ + num_packets = urb->number_of_packets; - dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer + - urb->iso_frame_desc[i].offset, - urb->iso_frame_desc[i].actual_length); + for (i = 0; i < num_packets; i++) { + if (xfer_bulk) { + if (urb->status < 0) { + print_err_status(dev, i, urb->status); + if (urb->status != -EPROTO) + continue; + } + dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer, + urb->actual_length); + } else { + if (urb->iso_frame_desc[i].status < 0) { + print_err_status(dev, i, + urb->iso_frame_desc[i].status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + dvb_dmx_swfilter(&dev->dvb->demux, + urb->transfer_buffer + + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + } } return 0; @@ -177,7 +195,7 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) EM28XX_DVB_NUM_BUFS, max_dvb_packet_size, EM28XX_DVB_NUM_ISOC_PACKETS, - em28xx_dvb_isoc_copy); + em28xx_dvb_urb_data_copy); } static int em28xx_stop_streaming(struct em28xx_dvb *dvb) -- cgit v1.2.3 From 0cf544a6cc66b493852d48517ce4833dfade5809 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:49 -0300 Subject: [media] em28xx: rename some USB parameter fields in struct em28xx to clarify their role MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also improve the comments. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 20 +++++++++++--------- drivers/media/usb/em28xx/em28xx-core.c | 8 ++++---- drivers/media/usb/em28xx/em28xx-dvb.c | 4 ++-- drivers/media/usb/em28xx/em28xx-video.c | 2 +- drivers/media/usb/em28xx/em28xx.h | 14 ++++++++------ 5 files changed, 26 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index bfce34d491c0..873b52f71ab2 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3183,9 +3183,10 @@ static int em28xx_usb_probe(struct usb_interface *interface, } /* compute alternate max packet sizes */ - dev->alt_max_pkt_size = kmalloc(sizeof(dev->alt_max_pkt_size[0]) * + dev->alt_max_pkt_size_isoc = + kmalloc(sizeof(dev->alt_max_pkt_size_isoc[0]) * interface->num_altsetting, GFP_KERNEL); - if (dev->alt_max_pkt_size == NULL) { + if (dev->alt_max_pkt_size_isoc == NULL) { em28xx_errdev("out of memory!\n"); kfree(dev); retval = -ENOMEM; @@ -3216,13 +3217,14 @@ static int em28xx_usb_probe(struct usb_interface *interface, break; case EM28XX_EP_ANALOG: has_video = true; - dev->alt_max_pkt_size[i] = size; + dev->alt_max_pkt_size_isoc[i] = size; break; case EM28XX_EP_DIGITAL: has_dvb = true; - if (size > dev->dvb_max_pkt_size) { - dev->dvb_max_pkt_size = size; - dev->dvb_alt = i; + if (size > dev->dvb_max_pkt_size_isoc) { + dev->dvb_max_pkt_size_isoc = + size; + dev->dvb_alt_isoc = i; } break; } @@ -3324,7 +3326,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* pre-allocate DVB isoc transfer buffers */ retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, 0, EM28XX_DVB_NUM_BUFS, - dev->dvb_max_pkt_size, + dev->dvb_max_pkt_size_isoc, EM28XX_DVB_NUM_ISOC_PACKETS); if (retval) { goto unlock_and_free; @@ -3344,7 +3346,7 @@ unlock_and_free: mutex_unlock(&dev->lock); err_free: - kfree(dev->alt_max_pkt_size); + kfree(dev->alt_max_pkt_size_isoc); kfree(dev); err: @@ -3409,7 +3411,7 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) em28xx_close_extension(dev); if (!dev->users) { - kfree(dev->alt_max_pkt_size); + kfree(dev->alt_max_pkt_size_isoc); kfree(dev); } } diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 6b3485d2266a..3c40a1da69b6 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -830,14 +830,14 @@ int em28xx_set_alternate(struct em28xx *dev) for (i = 0; i < dev->num_alt; i++) { /* stop when the selected alt setting offers enough bandwidth */ - if (dev->alt_max_pkt_size[i] >= min_pkt_size) { + if (dev->alt_max_pkt_size_isoc[i] >= min_pkt_size) { dev->alt = i; break; /* otherwise make sure that we end up with the maximum bandwidth because the min_pkt_size equation might be wrong... */ - } else if (dev->alt_max_pkt_size[i] > - dev->alt_max_pkt_size[dev->alt]) + } else if (dev->alt_max_pkt_size_isoc[i] > + dev->alt_max_pkt_size_isoc[dev->alt]) dev->alt = i; } @@ -845,7 +845,7 @@ set_alt: if (dev->alt != prev_alt) { em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", min_pkt_size, dev->alt); - dev->max_pkt_size = dev->alt_max_pkt_size[dev->alt]; + dev->max_pkt_size = dev->alt_max_pkt_size_isoc[dev->alt]; em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", dev->alt, dev->max_pkt_size); errCode = usb_set_interface(dev->udev, 0, dev->alt); diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 22ef6dd128e2..24962520df80 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -178,12 +178,12 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) struct em28xx *dev = dvb->adapter.priv; int max_dvb_packet_size; - usb_set_interface(dev->udev, 0, dev->dvb_alt); + usb_set_interface(dev->udev, 0, dev->dvb_alt_isoc); rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); if (rc < 0) return rc; - max_dvb_packet_size = dev->dvb_max_pkt_size; + max_dvb_packet_size = dev->dvb_max_pkt_size_isoc; if (max_dvb_packet_size < 0) return max_dvb_packet_size; dprintk(1, "Using %d buffers each with %d x %d bytes\n", diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index df294f3858ea..d9c15b6da1af 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -2286,7 +2286,7 @@ static int em28xx_v4l2_close(struct file *filp) free the remaining resources */ if (dev->state & DEV_DISCONNECTED) { em28xx_release_resources(dev); - kfree(dev->alt_max_pkt_size); + kfree(dev->alt_max_pkt_size_isoc); mutex_unlock(&dev->lock); kfree(dev); kfree(fh); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 950a71786a14..6b8d3e6b6920 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -4,6 +4,7 @@ Copyright (C) 2005 Markus Rechberger Ludovico Cavedon Mauro Carvalho Chehab + Copyright (C) 2012 Frank Schäfer Based on the em2800 driver from Sascha Sommer @@ -583,12 +584,13 @@ struct em28xx { /* usb transfer */ struct usb_device *udev; /* the usb device */ - int alt; /* alternate */ - int max_pkt_size; /* max packet size of isoc transaction */ - int num_alt; /* Number of alternative settings */ - unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ - int dvb_alt; /* alternate for DVB */ - unsigned int dvb_max_pkt_size; /* wMaxPacketSize for DVB */ + int alt; /* alternate setting */ + int max_pkt_size; /* max packet size of the selected ep at alt */ + int num_alt; /* number of alternative settings */ + unsigned int *alt_max_pkt_size_isoc; /* array of isoc wMaxPacketSize */ + int dvb_alt_isoc; /* alternate setting for DVB isoc transfers */ + unsigned int dvb_max_pkt_size_isoc; /* isoc max packet size of the + selected DVB ep at dvb_alt */ char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */ /* helper funcs that call usb_control_msg */ -- cgit v1.2.3 From 7312f2c9fa22614acc787c064a0865840888d662 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:50 -0300 Subject: [media] em28xx: add fields for analog and DVB USB transfer type selection to struct em28xx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 6b8d3e6b6920..f5be5229f304 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -588,9 +588,13 @@ struct em28xx { int max_pkt_size; /* max packet size of the selected ep at alt */ int num_alt; /* number of alternative settings */ unsigned int *alt_max_pkt_size_isoc; /* array of isoc wMaxPacketSize */ + unsigned int analog_xfer_bulk:1; /* use bulk instead of isoc + transfers for analog */ int dvb_alt_isoc; /* alternate setting for DVB isoc transfers */ unsigned int dvb_max_pkt_size_isoc; /* isoc max packet size of the selected DVB ep at dvb_alt */ + unsigned int dvb_xfer_bulk:1; /* use bulk instead of isoc + transfers for DVB */ char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */ /* helper funcs that call usb_control_msg */ -- cgit v1.2.3 From c8e9d95b41f2a441b2af0a1899448dd45ad7632d Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:51 -0300 Subject: [media] em28xx: set USB alternate settings for analog video bulk transfers properly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend function em28xx_set_alternate: - use alternate setting 0 for bulk transfers as default - respect module parameter 'alt'=0 for bulk transfers - set max_packet_size to 512 bytes for bulk transfers [mchehab@redhat.com: Fix a CodingStyle issue: don't break strings into separate lines] Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 3c40a1da69b6..cdf4cd24007d 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -805,21 +805,23 @@ int em28xx_resolution_set(struct em28xx *dev) return em28xx_scaler_set(dev, dev->hscale, dev->vscale); } +/* Set USB alternate setting for analog video */ int em28xx_set_alternate(struct em28xx *dev) { int errCode, prev_alt = dev->alt; int i; unsigned int min_pkt_size = dev->width * 2 + 4; - /* - * alt = 0 is used only for control messages, so, only values - * greater than 0 can be used for streaming. - */ - if (alt && alt < dev->num_alt) { + /* NOTE: for isoc transfers, only alt settings > 0 are allowed + for bulk transfers, use alt=0 as default value */ + dev->alt = 0; + if ((alt > 0) && (alt < dev->num_alt)) { em28xx_coredbg("alternate forced to %d\n", dev->alt); dev->alt = alt; goto set_alt; } + if (dev->analog_xfer_bulk) + goto set_alt; /* When image size is bigger than a certain value, the frame size should be increased, otherwise, only @@ -843,9 +845,14 @@ int em28xx_set_alternate(struct em28xx *dev) set_alt: if (dev->alt != prev_alt) { - em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", - min_pkt_size, dev->alt); - dev->max_pkt_size = dev->alt_max_pkt_size_isoc[dev->alt]; + if (dev->analog_xfer_bulk) { + dev->max_pkt_size = 512; /* USB 2.0 spec */ + } else { /* isoc */ + em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", + min_pkt_size, dev->alt); + dev->max_pkt_size = + dev->alt_max_pkt_size_isoc[dev->alt]; + } em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", dev->alt, dev->max_pkt_size); errCode = usb_set_interface(dev->udev, 0, dev->alt); -- cgit v1.2.3 From c647a91a2558c4031eddd013e5860ca5a41363a7 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:52 -0300 Subject: [media] em28xx: improve USB endpoint logic, also use bulk transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current enpoint logic ignores all bulk endpoints and uses a fixed mapping between endpint addresses and the supported data stream types (analog/audio/DVB): Ep 0x82, isoc => analog Ep 0x83, isoc => audio Ep 0x84, isoc => DVB Now that the code can also do bulk transfers, the endpoint logic has to be extended to also consider bulk endpoints. The new logic preserves backwards compatibility and reflects the endpoint configurations we have seen so far: Ep 0x82, isoc => analog Ep 0x82, bulk => analog Ep 0x83, isoc* => audio Ep 0x84, isoc => digital Ep 0x84, bulk => analog or digital** (*: audio should always be isoc) (**: analog, if ep 0x82 is isoc, otherwise digital) [mchehab@redhat.com: Fix a CodingStyle issue: don't break strings into separate lines] Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 95 ++++++++++++++++++++++++++------- drivers/media/usb/em28xx/em28xx-core.c | 32 ++++++++--- drivers/media/usb/em28xx/em28xx-dvb.c | 34 ++++++++---- drivers/media/usb/em28xx/em28xx-reg.h | 4 +- drivers/media/usb/em28xx/em28xx-video.c | 10 ++-- drivers/media/usb/em28xx/em28xx.h | 12 +++++ 6 files changed, 147 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 873b52f71ab2..a6f00181c246 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -6,6 +6,7 @@ Markus Rechberger Mauro Carvalho Chehab Sascha Sommer + Copyright (C) 2012 Frank Schäfer 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 @@ -3209,26 +3210,67 @@ static int em28xx_usb_probe(struct usb_interface *interface, if (udev->speed == USB_SPEED_HIGH) size = size * hb_mult(sizedescr); - if (usb_endpoint_xfer_isoc(e) && - usb_endpoint_dir_in(e)) { + if (usb_endpoint_dir_in(e)) { switch (e->bEndpointAddress) { - case EM28XX_EP_AUDIO: - has_audio = true; - break; - case EM28XX_EP_ANALOG: + case 0x82: has_video = true; - dev->alt_max_pkt_size_isoc[i] = size; + if (usb_endpoint_xfer_isoc(e)) { + dev->analog_ep_isoc = + e->bEndpointAddress; + dev->alt_max_pkt_size_isoc[i] = size; + } else if (usb_endpoint_xfer_bulk(e)) { + dev->analog_ep_bulk = + e->bEndpointAddress; + } + break; + case 0x83: + if (usb_endpoint_xfer_isoc(e)) { + has_audio = true; + } else { + printk(KERN_INFO DRIVER_NAME + ": error: skipping audio endpoint 0x83, because it uses bulk transfers !\n"); + } break; - case EM28XX_EP_DIGITAL: - has_dvb = true; - if (size > dev->dvb_max_pkt_size_isoc) { - dev->dvb_max_pkt_size_isoc = - size; - dev->dvb_alt_isoc = i; + case 0x84: + if (has_video && + (usb_endpoint_xfer_bulk(e))) { + dev->analog_ep_bulk = + e->bEndpointAddress; + } else { + has_dvb = true; + if (usb_endpoint_xfer_isoc(e)) { + dev->dvb_ep_isoc = e->bEndpointAddress; + if (size > dev->dvb_max_pkt_size_isoc) { + dev->dvb_max_pkt_size_isoc = size; + dev->dvb_alt_isoc = i; + } + } else { + dev->dvb_ep_bulk = e->bEndpointAddress; + } } break; } } + /* NOTE: + * Old logic with support for isoc transfers only was: + * 0x82 isoc => analog + * 0x83 isoc => audio + * 0x84 isoc => digital + * + * New logic with support for bulk transfers + * 0x82 isoc => analog + * 0x82 bulk => analog + * 0x83 isoc* => audio + * 0x84 isoc => digital + * 0x84 bulk => analog or digital** + * (*: audio should always be isoc) + * (**: analog, if ep 0x82 is isoc, otherwise digital) + * + * The new logic preserves backwards compatibility and + * reflects the endpoint configurations we have seen + * so far. But there might be devices for which this + * logic is not sufficient... + */ } } @@ -3289,6 +3331,12 @@ static int em28xx_usb_probe(struct usb_interface *interface, goto err_free; } + /* Select USB transfer types to use */ + if (has_video && !dev->analog_ep_isoc) + dev->analog_xfer_bulk = 1; + if (has_dvb && !dev->dvb_ep_isoc) + dev->dvb_xfer_bulk = 1; + snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr); dev->devno = nr; dev->model = id->driver_info; @@ -3323,12 +3371,23 @@ static int em28xx_usb_probe(struct usb_interface *interface, } if (has_dvb) { - /* pre-allocate DVB isoc transfer buffers */ - retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, 0, - EM28XX_DVB_NUM_BUFS, - dev->dvb_max_pkt_size_isoc, - EM28XX_DVB_NUM_ISOC_PACKETS); + /* pre-allocate DVB usb transfer buffers */ + if (dev->dvb_xfer_bulk) { + retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, + dev->dvb_xfer_bulk, + EM28XX_DVB_NUM_BUFS, + 512, + EM28XX_DVB_BULK_PACKET_MULTIPLIER); + } else { + retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, + dev->dvb_xfer_bulk, + EM28XX_DVB_NUM_BUFS, + dev->dvb_max_pkt_size_isoc, + EM28XX_DVB_NUM_ISOC_PACKETS); + } if (retval) { + printk(DRIVER_NAME + ": Failed to pre-allocate USB transfer buffers for DVB.\n"); goto unlock_and_free; } } diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index cdf4cd24007d..b10d959fefe5 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -847,11 +847,13 @@ set_alt: if (dev->alt != prev_alt) { if (dev->analog_xfer_bulk) { dev->max_pkt_size = 512; /* USB 2.0 spec */ + dev->packet_multiplier = EM28XX_BULK_PACKET_MULTIPLIER; } else { /* isoc */ em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", min_pkt_size, dev->alt); dev->max_pkt_size = dev->alt_max_pkt_size_isoc[dev->alt]; + dev->packet_multiplier = EM28XX_NUM_ISOC_PACKETS; } em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", dev->alt, dev->max_pkt_size); @@ -1054,10 +1056,28 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode); - if (mode == EM28XX_DIGITAL_MODE) + /* Check mode and if we have an endpoint for the selected + transfer type, select buffer */ + if (mode == EM28XX_DIGITAL_MODE) { + if ((xfer_bulk && !dev->dvb_ep_bulk) || + (!xfer_bulk && !dev->dvb_ep_isoc)) { + em28xx_errdev("no endpoint for DVB mode and transfer type %d\n", + xfer_bulk > 0); + return -EINVAL; + } usb_bufs = &dev->usb_ctl.digital_bufs; - else + } else if (mode == EM28XX_ANALOG_MODE) { + if ((xfer_bulk && !dev->analog_ep_bulk) || + (!xfer_bulk && !dev->analog_ep_isoc)) { + em28xx_errdev("no endpoint for analog mode and transfer type %d\n", + xfer_bulk > 0); + return -EINVAL; + } usb_bufs = &dev->usb_ctl.analog_bufs; + } else { + em28xx_errdev("invalid mode selected\n"); + return -EINVAL; + } /* De-allocates all pending stuff */ em28xx_uninit_usb_xfer(dev, mode); @@ -1113,8 +1133,8 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, if (xfer_bulk) { /* bulk */ pipe = usb_rcvbulkpipe(dev->udev, mode == EM28XX_ANALOG_MODE ? - EM28XX_EP_ANALOG : - EM28XX_EP_DIGITAL); + dev->analog_ep_bulk : + dev->dvb_ep_bulk); usb_fill_bulk_urb(urb, dev->udev, pipe, usb_bufs->transfer_buffer[i], sb_size, em28xx_irq_callback, dev); @@ -1122,8 +1142,8 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, } else { /* isoc */ pipe = usb_rcvisocpipe(dev->udev, mode == EM28XX_ANALOG_MODE ? - EM28XX_EP_ANALOG : - EM28XX_EP_DIGITAL); + dev->analog_ep_isoc : + dev->dvb_ep_isoc); usb_fill_int_urb(urb, dev->udev, pipe, usb_bufs->transfer_buffer[i], sb_size, em28xx_irq_callback, dev, 1); diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 24962520df80..a70b19e07e37 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -176,25 +176,39 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) { int rc; struct em28xx *dev = dvb->adapter.priv; - int max_dvb_packet_size; + int dvb_max_packet_size, packet_multiplier, dvb_alt; + + if (dev->dvb_xfer_bulk) { + if (!dev->dvb_ep_bulk) + return -ENODEV; + dvb_max_packet_size = 512; /* USB 2.0 spec */ + packet_multiplier = EM28XX_DVB_BULK_PACKET_MULTIPLIER; + dvb_alt = 0; + } else { /* isoc */ + if (!dev->dvb_ep_isoc) + return -ENODEV; + dvb_max_packet_size = dev->dvb_max_pkt_size_isoc; + if (dvb_max_packet_size < 0) + return dvb_max_packet_size; + packet_multiplier = EM28XX_DVB_NUM_ISOC_PACKETS; + dvb_alt = dev->dvb_alt_isoc; + } - usb_set_interface(dev->udev, 0, dev->dvb_alt_isoc); + usb_set_interface(dev->udev, 0, dvb_alt); rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); if (rc < 0) return rc; - max_dvb_packet_size = dev->dvb_max_pkt_size_isoc; - if (max_dvb_packet_size < 0) - return max_dvb_packet_size; dprintk(1, "Using %d buffers each with %d x %d bytes\n", EM28XX_DVB_NUM_BUFS, - EM28XX_DVB_NUM_ISOC_PACKETS, - max_dvb_packet_size); + packet_multiplier, + dvb_max_packet_size); - return em28xx_init_usb_xfer(dev, EM28XX_DIGITAL_MODE, 0, + return em28xx_init_usb_xfer(dev, EM28XX_DIGITAL_MODE, + dev->dvb_xfer_bulk, EM28XX_DVB_NUM_BUFS, - max_dvb_packet_size, - EM28XX_DVB_NUM_ISOC_PACKETS, + dvb_max_packet_size, + packet_multiplier, em28xx_dvb_urb_data_copy); } diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h index 2ad357354d81..885089e22bcd 100644 --- a/drivers/media/usb/em28xx/em28xx-reg.h +++ b/drivers/media/usb/em28xx/em28xx-reg.h @@ -13,9 +13,9 @@ #define EM_GPO_3 (1 << 3) /* em28xx endpoints */ -#define EM28XX_EP_ANALOG 0x82 +/* 0x82: (always ?) analog */ #define EM28XX_EP_AUDIO 0x83 -#define EM28XX_EP_DIGITAL 0x84 +/* 0x84: digital or analog */ /* em2800 registers */ #define EM2800_R08_AUDIOSRC 0x08 diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index d9c15b6da1af..c7e23dd73b88 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -788,16 +788,18 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, if (urb_init) { if (em28xx_vbi_supported(dev) == 1) - rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, 0, + rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, + dev->analog_xfer_bulk, EM28XX_NUM_BUFS, dev->max_pkt_size, - EM28XX_NUM_ISOC_PACKETS, + dev->packet_multiplier, em28xx_urb_data_copy_vbi); else - rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, 0, + rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, + dev->analog_xfer_bulk, EM28XX_NUM_BUFS, dev->max_pkt_size, - EM28XX_NUM_ISOC_PACKETS, + dev->packet_multiplier, em28xx_urb_data_copy); if (rc < 0) goto fail; diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index f5be5229f304..aa413bd76dec 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -165,6 +165,12 @@ #define EM28XX_NUM_ISOC_PACKETS 64 #define EM28XX_DVB_NUM_ISOC_PACKETS 64 +/* bulk transfers: transfer buffer size = packet size * packet multiplier + USB 2.0 spec says bulk packet size is always 512 bytes + */ +#define EM28XX_BULK_PACKET_MULTIPLIER 384 +#define EM28XX_DVB_BULK_PACKET_MULTIPLIER 384 + #define EM28XX_INTERLACED_DEFAULT 1 /* @@ -584,8 +590,14 @@ struct em28xx { /* usb transfer */ struct usb_device *udev; /* the usb device */ + u8 analog_ep_isoc; /* address of isoc endpoint for analog */ + u8 analog_ep_bulk; /* address of bulk endpoint for analog */ + u8 dvb_ep_isoc; /* address of isoc endpoint for DVB */ + u8 dvb_ep_bulk; /* address of bulk endpoint for DVC */ int alt; /* alternate setting */ int max_pkt_size; /* max packet size of the selected ep at alt */ + int packet_multiplier; /* multiplier for wMaxPacketSize, used for + URB buffer size definition */ int num_alt; /* number of alternative settings */ unsigned int *alt_max_pkt_size_isoc; /* array of isoc wMaxPacketSize */ unsigned int analog_xfer_bulk:1; /* use bulk instead of isoc -- cgit v1.2.3 From 454fe92f01f8d669669619a9b301d133eda9d173 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:53 -0300 Subject: [media] em28xx: add module parameter for selection of the preferred USB transfer type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By default, isoc transfers are used if possible. With the new module parameter, bulk can be selected as the preferred USB transfer type. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index a6f00181c246..bc63b1cee808 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -61,6 +61,11 @@ static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); +static unsigned int prefer_bulk; +module_param(prefer_bulk, int, 0644); +MODULE_PARM_DESC(prefer_bulk, "prefer USB bulk transfers"); + + /* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */ static unsigned long em28xx_devused; @@ -3332,9 +3337,11 @@ static int em28xx_usb_probe(struct usb_interface *interface, } /* Select USB transfer types to use */ - if (has_video && !dev->analog_ep_isoc) + if (has_video && + (!dev->analog_ep_isoc || (prefer_bulk && dev->analog_ep_bulk))) dev->analog_xfer_bulk = 1; - if (has_dvb && !dev->dvb_ep_isoc) + if (has_dvb && + (!dev->dvb_ep_isoc || (prefer_bulk && dev->dvb_ep_bulk))) dev->dvb_xfer_bulk = 1; snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr); -- cgit v1.2.3 From b77e0c088f07817ecfc4e0a34d4d6a3f99a3ecaf Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 25 Nov 2012 06:37:32 -0300 Subject: [media] em28xx: fix video data start position calculation in em28xx_urb_data_copy_vbi() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The header check/removal code at the end of function em28xx_urb_data_copy_vbi() is obsolete, because this is already done earlier in this function. In fact it is incomplete (doesn't check for vbi header) and causes trouble when the first data bytes are the same as header bytes (which is fortunately very unlikely). Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index c7e23dd73b88..fec8847e84d8 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -678,24 +678,8 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) dma_q->pos = 0; } - if (buf != NULL && dev->capture_type == 2) { - if (len >= 4 && p[0] == 0x88 && p[1] == 0x88 && - p[2] == 0x88 && p[3] == 0x88) { - p += 4; - len -= 4; - } - if (len >= 4 && p[0] == 0x22 && p[1] == 0x5a) { - em28xx_isocdbg("Video frame %d, len=%i, %s\n", - p[2], len, (p[2] & 1) ? - "odd" : "even"); - p += 4; - len -= 4; - } - - if (len > 0) - em28xx_copy_video(dev, dma_q, buf, p, outp, - len); - } + if (buf != NULL && dev->capture_type == 2 && len > 0) + em28xx_copy_video(dev, dma_q, buf, p, outp, len); } return rc; } -- cgit v1.2.3 From 3610f58bb1d545fe3f3767ec8ca3251383e9c728 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 25 Nov 2012 06:37:33 -0300 Subject: [media] em28xx: make sure the packet size is >= 4 before checking for headers in em28xx_urb_data_copy_vbi() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 45 ++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index fec8847e84d8..da31fd495466 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -576,7 +576,7 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) urb->iso_frame_desc[i].offset; } - if (actual_length <= 0) { + if (actual_length == 0) { /* NOTE: happens very often with isoc transfers */ /* em28xx_usbdbg("packet %d is empty",i); - spammy */ continue; @@ -585,27 +585,30 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) /* capture type 0 = vbi start capture type 1 = video start capture type 2 = video in progress */ - if (p[0] == 0x33 && p[1] == 0x95) { - dev->capture_type = 0; - dev->vbi_read = 0; - em28xx_isocdbg("VBI START HEADER!!!\n"); - dev->cur_field = p[2]; - p += 4; - len = actual_length - 4; - } else if (p[0] == 0x88 && p[1] == 0x88 && - p[2] == 0x88 && p[3] == 0x88) { - /* continuation */ - p += 4; - len = actual_length - 4; - } else if (p[0] == 0x22 && p[1] == 0x5a) { - /* start video */ - p += 4; - len = actual_length - 4; - } else { - /* NOTE: With bulk transfers, intermediate data packets - * have no continuation header */ - len = actual_length; + len = actual_length; + if (len >= 4) { + /* NOTE: headers are always 4 bytes and + * never split across packets */ + if (p[0] == 0x33 && p[1] == 0x95) { + dev->capture_type = 0; + dev->vbi_read = 0; + em28xx_isocdbg("VBI START HEADER!!!\n"); + dev->cur_field = p[2]; + p += 4; + len -= 4; + } else if (p[0] == 0x88 && p[1] == 0x88 && + p[2] == 0x88 && p[3] == 0x88) { + /* continuation */ + p += 4; + len -= 4; + } else if (p[0] == 0x22 && p[1] == 0x5a) { + /* start video */ + p += 4; + len -= 4; + } } + /* NOTE: with bulk transfers, intermediate data packets + * have no continuation header */ vbi_size = dev->vbi_width * dev->vbi_height; -- cgit v1.2.3 From 0455eebfbd6c286552f9d98bdc6614dfbdd63682 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 25 Nov 2012 06:37:34 -0300 Subject: [media] em28xx: fix capture type setting in em28xx_urb_data_copy_vbi() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set capture type to 1 (video start) when the video frame start header is detected. This bug didn't cause any trouble, because this type of header is never received in vbi mode. Fix it, because we want to use this function with disabled vbi in the future. Also start with capture type -1 to avoid processing of corrupted/incomplete frame data which is usually received at streaming start (especially when USB bulk transfers are used). Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 26 ++++++++++---------------- drivers/media/usb/em28xx/em28xx.h | 2 +- 2 files changed, 11 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index da31fd495466..d4f230002747 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -593,7 +593,7 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) dev->capture_type = 0; dev->vbi_read = 0; em28xx_isocdbg("VBI START HEADER!!!\n"); - dev->cur_field = p[2]; + dev->top_field = !(p[2] & 1); p += 4; len -= 4; } else if (p[0] == 0x88 && p[1] == 0x88 && @@ -603,6 +603,8 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) len -= 4; } else if (p[0] == 0x22 && p[1] == 0x5a) { /* start video */ + dev->capture_type = 1; + dev->top_field = !(p[2] & 1); p += 4; len -= 4; } @@ -619,8 +621,7 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) em28xx_isocdbg("dev->vbi_read > vbi_size\n"); } else if ((dev->vbi_read + len) < vbi_size) { /* This entire frame is VBI data */ - if (dev->vbi_read == 0 && - (!(dev->cur_field & 1))) { + if (dev->vbi_read == 0 && dev->top_field) { /* Brand new frame */ if (vbi_buf != NULL) vbi_buffer_filled(dev, @@ -636,12 +637,8 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) if (dev->vbi_read == 0) { vbi_dma_q->pos = 0; - if (vbi_buf != NULL) { - if (dev->cur_field & 1) - vbi_buf->top_field = 0; - else - vbi_buf->top_field = 1; - } + if (vbi_buf != NULL) + vbi_buf->top_field = dev->top_field; } dev->vbi_read += len; @@ -662,7 +659,7 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) if (dev->capture_type == 1) { dev->capture_type = 2; - if (dev->progressive || !(dev->cur_field & 1)) { + if (dev->progressive || dev->top_field) { if (buf != NULL) buffer_filled(dev, dma_q, buf); get_next_buf(dma_q, &buf); @@ -671,12 +668,8 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) else outp = videobuf_to_vmalloc(&buf->vb); } - if (buf != NULL) { - if (dev->cur_field & 1) - buf->top_field = 0; - else - buf->top_field = 1; - } + if (buf != NULL) + buf->top_field = dev->top_field; dma_q->pos = 0; } @@ -774,6 +767,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, urb_init = 1; if (urb_init) { + dev->capture_type = -1; if (em28xx_vbi_supported(dev) == 1) rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, dev->analog_xfer_bulk, diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index aa413bd76dec..09df56a4570c 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -563,7 +563,7 @@ struct em28xx { /* vbi related state tracking */ int capture_type; int vbi_read; - unsigned char cur_field; + unsigned char top_field:1; unsigned int vbi_width; unsigned int vbi_height; /* lines per field */ -- cgit v1.2.3 From 79ff8697e98295606a55f7426930affe1322f9eb Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 25 Nov 2012 06:37:36 -0300 Subject: [media] em28xx: em28xx_urb_data_copy_vbi(): calculate vbi_size only if needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index d4f230002747..c397aa213601 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -525,7 +525,7 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) struct em28xx_buffer *buf, *vbi_buf; struct em28xx_dmaqueue *dma_q = &dev->vidq; struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; - int xfer_bulk, vbi_size, num_packets, i, rc = 1; + int xfer_bulk, num_packets, i, rc = 1; unsigned int actual_length, len = 0; unsigned char *p, *outp = NULL, *vbioutp = NULL; @@ -612,9 +612,8 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) /* NOTE: with bulk transfers, intermediate data packets * have no continuation header */ - vbi_size = dev->vbi_width * dev->vbi_height; - if (dev->capture_type == 0) { + int vbi_size = dev->vbi_width * dev->vbi_height; if (dev->vbi_read >= vbi_size) { /* We've already read all the VBI data, so treat the rest as video */ -- cgit v1.2.3 From 960da93ba56f281261038e85c57ee3ec942dc734 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 25 Nov 2012 06:37:37 -0300 Subject: [media] em28xx: use common urb data copying function for vbi and non-vbi data streams MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit em28xx_urb_data_copy_vbi() is actually an extended version of em28xx_urb_data_copy(). With the preceding fixes and improvements, it works fine with both, vbi and non-vbi data streams without performance impacts. So rename em28xx_urb_data_copy_vbi() to em28xx_urb_data_copy(), delete the the old implementation of em28xx_urb_data_copy() and change the code to use this function for both data stream types. Tested with "SilverCrest 1.3 MPix webcam" (progressive, non-vbi) and "Hauppauge HVR-900 (65008/A1C0)" (interlaced, vbi enabled and disabled). Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 129 ++------------------------------ drivers/media/usb/em28xx/em28xx.h | 4 +- 2 files changed, 9 insertions(+), 124 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index c397aa213601..a1436a453bc8 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -376,7 +376,6 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, /* Get the next buffer */ *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); - /* Cleans up buffer - Useful for testing for frame/URB loss */ outp = videobuf_to_vmalloc(&(*buf)->vb); memset(outp, 0, (*buf)->vb.size); @@ -413,114 +412,8 @@ static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q, return; } -/* Processes and copies the URB data content to a frame buffer queue */ +/* Processes and copies the URB data content (video and VBI data) */ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) -{ - struct em28xx_buffer *buf; - struct em28xx_dmaqueue *dma_q = &dev->vidq; - int xfer_bulk, num_packets, i, rc = 1; - unsigned int actual_length, len = 0; - unsigned char *p, *outp = NULL; - - if (!dev) - return 0; - - if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) - return 0; - - if (urb->status < 0) - print_err_status(dev, -1, urb->status); - - xfer_bulk = usb_pipebulk(urb->pipe); - - buf = dev->usb_ctl.vid_buf; - if (buf != NULL) - outp = videobuf_to_vmalloc(&buf->vb); - - if (xfer_bulk) /* bulk */ - num_packets = 1; - else /* isoc */ - num_packets = urb->number_of_packets; - - for (i = 0; i < num_packets; i++) { - if (xfer_bulk) { /* bulk */ - actual_length = urb->actual_length; - - p = urb->transfer_buffer; - } else { /* isoc */ - if (urb->iso_frame_desc[i].status < 0) { - print_err_status(dev, i, - urb->iso_frame_desc[i].status); - if (urb->iso_frame_desc[i].status != -EPROTO) - continue; - } - - actual_length = urb->iso_frame_desc[i].actual_length; - if (actual_length > dev->max_pkt_size) { - em28xx_isocdbg("packet bigger than packet size"); - continue; - } - - p = urb->transfer_buffer + - urb->iso_frame_desc[i].offset; - } - - if (actual_length <= 0) { - /* NOTE: happens very often with isoc transfers */ - /* em28xx_usbdbg("packet %d is empty",i); - spammy */ - continue; - } - - /* FIXME: incomplete buffer checks where removed to make - logic simpler. Impacts of those changes should be evaluated - */ - if (p[0] == 0x33 && p[1] == 0x95 && p[2] == 0x00) { - em28xx_isocdbg("VBI HEADER!!!\n"); - /* FIXME: Should add vbi copy */ - continue; - } - if (p[0] == 0x22 && p[1] == 0x5a) { - em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], - len, (p[2] & 1) ? "odd" : "even"); - - if (dev->progressive || !(p[2] & 1)) { - if (buf != NULL) - buffer_filled(dev, dma_q, buf); - get_next_buf(dma_q, &buf); - if (buf == NULL) - outp = NULL; - else - outp = videobuf_to_vmalloc(&buf->vb); - } - - if (buf != NULL) { - if (p[2] & 1) - buf->top_field = 0; - else - buf->top_field = 1; - } - - dma_q->pos = 0; - } - if (buf != NULL) { - if (p[0] != 0x88 && p[0] != 0x22) { - /* NOTE: no intermediate data packet header - * 88 88 88 88 when using bulk transfers */ - em28xx_isocdbg("frame is not complete\n"); - len = actual_length; - } else { - len = actual_length - 4; - p += 4; - } - em28xx_copy_video(dev, dma_q, buf, p, outp, len); - } - } - return rc; -} - -/* Version of the urb data handler that takes into account a mixture of - video and VBI data */ -static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) { struct em28xx_buffer *buf, *vbi_buf; struct em28xx_dmaqueue *dma_q = &dev->vidq; @@ -767,20 +660,12 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, if (urb_init) { dev->capture_type = -1; - if (em28xx_vbi_supported(dev) == 1) - rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, - dev->analog_xfer_bulk, - EM28XX_NUM_BUFS, - dev->max_pkt_size, - dev->packet_multiplier, - em28xx_urb_data_copy_vbi); - else - rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, - dev->analog_xfer_bulk, - EM28XX_NUM_BUFS, - dev->max_pkt_size, - dev->packet_multiplier, - em28xx_urb_data_copy); + rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, + dev->analog_xfer_bulk, + EM28XX_NUM_BUFS, + dev->max_pkt_size, + dev->packet_multiplier, + em28xx_urb_data_copy); if (rc < 0) goto fail; } diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 09df56a4570c..304896de4879 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -560,10 +560,10 @@ struct em28xx { /* states */ enum em28xx_dev_state state; - /* vbi related state tracking */ + /* capture state tracking */ int capture_type; - int vbi_read; unsigned char top_field:1; + int vbi_read; unsigned int vbi_width; unsigned int vbi_height; /* lines per field */ -- cgit v1.2.3 From 24a6d8497f7e64a8870018ed1ed561755b2075ec Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 8 Dec 2012 11:31:24 -0300 Subject: [media] em28xx: refactor get_next_buf() and use it for vbi data, too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit get_next_buf() and vbi_get_next_buf() do exactly the same just with a different dma queue and buffer. Saving the new buffer pointer back to the device struct in em28xx_urb_data_copy() instead of doing this from inside these functions makes it possible to get rid of one of them. Also refactor the function parameters and return type: - pass a pointer to struct em28xx as parameter (instead of obtaining the pointer from the dma queue pointer with the container_of macro) like we do it in all other functions - instead of using a pointer-pointer, return the pointer to the new buffer as return value of the function Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 58 +++++++++------------------------ 1 file changed, 15 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index a1436a453bc8..db27499ecaa2 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -359,57 +359,26 @@ static inline void print_err_status(struct em28xx *dev, } /* - * video-buf generic routine to get the next available buffer + * get the next available buffer from dma queue */ -static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer **buf) +static inline struct em28xx_buffer *get_next_buf(struct em28xx *dev, + struct em28xx_dmaqueue *dma_q) { - struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); + struct em28xx_buffer *buf; char *outp; if (list_empty(&dma_q->active)) { em28xx_isocdbg("No active queue to serve\n"); - dev->usb_ctl.vid_buf = NULL; - *buf = NULL; - return; - } - - /* Get the next buffer */ - *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); - /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&(*buf)->vb); - memset(outp, 0, (*buf)->vb.size); - - dev->usb_ctl.vid_buf = *buf; - - return; -} - -/* - * video-buf generic routine to get the next available VBI buffer - */ -static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer **buf) -{ - struct em28xx *dev = container_of(dma_q, struct em28xx, vbiq); - char *outp; - - if (list_empty(&dma_q->active)) { - em28xx_isocdbg("No active queue to serve\n"); - dev->usb_ctl.vbi_buf = NULL; - *buf = NULL; - return; + return NULL; } /* Get the next buffer */ - *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); + buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&(*buf)->vb); - memset(outp, 0x00, (*buf)->vb.size); - - dev->usb_ctl.vbi_buf = *buf; + outp = videobuf_to_vmalloc(&buf->vb); + memset(outp, 0, buf->vb.size); - return; + return buf; } /* Processes and copies the URB data content (video and VBI data) */ @@ -519,7 +488,8 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) vbi_buffer_filled(dev, vbi_dma_q, vbi_buf); - vbi_get_next_buf(vbi_dma_q, &vbi_buf); + vbi_buf = get_next_buf(dev, vbi_dma_q); + dev->usb_ctl.vbi_buf = vbi_buf; if (vbi_buf == NULL) vbioutp = NULL; else @@ -530,7 +500,8 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) if (dev->vbi_read == 0) { vbi_dma_q->pos = 0; if (vbi_buf != NULL) - vbi_buf->top_field = dev->top_field; + vbi_buf->top_field + = dev->top_field; } dev->vbi_read += len; @@ -554,7 +525,8 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) if (dev->progressive || dev->top_field) { if (buf != NULL) buffer_filled(dev, dma_q, buf); - get_next_buf(dma_q, &buf); + buf = get_next_buf(dev, dma_q); + dev->usb_ctl.vid_buf = buf; if (buf == NULL) outp = NULL; else -- cgit v1.2.3 From 948a49aa692e12cc33558e407898c467b22bf9b4 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 8 Dec 2012 11:31:25 -0300 Subject: [media] em28xx: use common function for video and vbi buffer completion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 34 +++++---------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index db27499ecaa2..f9f24215b56c 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -154,37 +154,15 @@ static struct v4l2_queryctrl ac97_qctrl[] = { ------------------------------------------------------------------*/ /* - * Announces that a buffer were filled and request the next + * Finish the current buffer */ -static inline void buffer_filled(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer *buf) +static inline void finish_buffer(struct em28xx *dev, + struct em28xx_buffer *buf) { - /* Advice that buffer was filled */ em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; v4l2_get_timestamp(&buf->vb.ts); - - dev->usb_ctl.vid_buf = NULL; - - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); -} - -static inline void vbi_buffer_filled(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer *buf) -{ - /* Advice that buffer was filled */ - em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); - - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - v4l2_get_timestamp(&buf->vb.ts); - - dev->usb_ctl.vbi_buf = NULL; - list_del(&buf->vb.queue); wake_up(&buf->vb.done); } @@ -485,9 +463,7 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) if (dev->vbi_read == 0 && dev->top_field) { /* Brand new frame */ if (vbi_buf != NULL) - vbi_buffer_filled(dev, - vbi_dma_q, - vbi_buf); + finish_buffer(dev, vbi_buf); vbi_buf = get_next_buf(dev, vbi_dma_q); dev->usb_ctl.vbi_buf = vbi_buf; if (vbi_buf == NULL) @@ -524,7 +500,7 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) dev->capture_type = 2; if (dev->progressive || dev->top_field) { if (buf != NULL) - buffer_filled(dev, dma_q, buf); + finish_buffer(dev, buf); buf = get_next_buf(dev, dma_q); dev->usb_ctl.vid_buf = buf; if (buf == NULL) -- cgit v1.2.3 From cbe7f8a030f2056d5cee8c2729d5edd23ae61589 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 8 Dec 2012 11:31:26 -0300 Subject: [media] em28xx: remove obsolete field 'frame' from struct em28xx_buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 304896de4879..b3d72a92dd9b 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -252,7 +252,6 @@ struct em28xx_buffer { /* common v4l buffer stuff -- must be first */ struct videobuf_buffer vb; - struct list_head frame; int top_field; }; -- cgit v1.2.3 From 8732533b3284ca078e3ea4a4721e43627ff7fa8e Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 8 Dec 2012 11:31:27 -0300 Subject: [media] em28xx: move field 'pos' from struct em28xx_dmaqueue to struct em28xx_buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This field is used to keep track of the current memory position in the buffer, not in the dma queue, so move it to right place. This also allows us to get rid of the struct em28xx_dmaqueue pointer parameter in functions em28xx_copy_video() and em28xx_copy_vbi(). Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 53 +++++++++++++++------------------ drivers/media/usb/em28xx/em28xx.h | 8 +++-- 2 files changed, 29 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index f9f24215b56c..565b6462f152 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -171,7 +171,6 @@ static inline void finish_buffer(struct em28xx *dev, * Identify the buffer header type and properly handles */ static void em28xx_copy_video(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, struct em28xx_buffer *buf, unsigned char *p, unsigned char *outp, unsigned long len) @@ -180,8 +179,8 @@ static void em28xx_copy_video(struct em28xx *dev, int linesdone, currlinedone, offset, lencopy, remain; int bytesperline = dev->width << 1; - if (dma_q->pos + len > buf->vb.size) - len = buf->vb.size - dma_q->pos; + if (buf->pos + len > buf->vb.size) + len = buf->vb.size - buf->pos; startread = p; remain = len; @@ -191,8 +190,8 @@ static void em28xx_copy_video(struct em28xx *dev, else /* interlaced mode, even nr. of lines */ fieldstart = outp + bytesperline; - linesdone = dma_q->pos / bytesperline; - currlinedone = dma_q->pos % bytesperline; + linesdone = buf->pos / bytesperline; + currlinedone = buf->pos % bytesperline; if (dev->progressive) offset = linesdone * bytesperline + currlinedone; @@ -244,14 +243,13 @@ static void em28xx_copy_video(struct em28xx *dev, remain -= lencopy; } - dma_q->pos += len; + buf->pos += len; } static void em28xx_copy_vbi(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer *buf, - unsigned char *p, - unsigned char *outp, unsigned long len) + struct em28xx_buffer *buf, + unsigned char *p, + unsigned char *outp, unsigned long len) { void *startwrite, *startread; int offset; @@ -263,10 +261,6 @@ static void em28xx_copy_vbi(struct em28xx *dev, } bytesperline = dev->vbi_width; - if (dma_q == NULL) { - em28xx_isocdbg("dma_q is null\n"); - return; - } if (buf == NULL) { return; } @@ -279,13 +273,13 @@ static void em28xx_copy_vbi(struct em28xx *dev, return; } - if (dma_q->pos + len > buf->vb.size) - len = buf->vb.size - dma_q->pos; + if (buf->pos + len > buf->vb.size) + len = buf->vb.size - buf->pos; startread = p; - startwrite = outp + dma_q->pos; - offset = dma_q->pos; + startwrite = outp + buf->pos; + offset = buf->pos; /* Make sure the bottom field populates the second half of the frame */ if (buf->top_field == 0) { @@ -294,7 +288,7 @@ static void em28xx_copy_vbi(struct em28xx *dev, } memcpy(startwrite, startread, len); - dma_q->pos += len; + buf->pos += len; } static inline void print_err_status(struct em28xx *dev, @@ -355,6 +349,7 @@ static inline struct em28xx_buffer *get_next_buf(struct em28xx *dev, /* Cleans up buffer - Useful for testing for frame/URB loss */ outp = videobuf_to_vmalloc(&buf->vb); memset(outp, 0, buf->vb.size); + buf->pos = 0; return buf; } @@ -474,22 +469,22 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) } if (dev->vbi_read == 0) { - vbi_dma_q->pos = 0; - if (vbi_buf != NULL) + if (vbi_buf != NULL) { vbi_buf->top_field = dev->top_field; + vbi_buf->pos = 0; + } } dev->vbi_read += len; - em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p, - vbioutp, len); + em28xx_copy_vbi(dev, vbi_buf, p, vbioutp, len); } else { /* Some of this frame is VBI data and some is video data */ int vbi_data_len = vbi_size - dev->vbi_read; dev->vbi_read += vbi_data_len; - em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p, - vbioutp, vbi_data_len); + em28xx_copy_vbi(dev, vbi_buf, p, vbioutp, + vbi_data_len); dev->capture_type = 1; p += vbi_data_len; len -= vbi_data_len; @@ -508,14 +503,14 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) else outp = videobuf_to_vmalloc(&buf->vb); } - if (buf != NULL) + if (buf != NULL) { buf->top_field = dev->top_field; - - dma_q->pos = 0; + buf->pos = 0; + } } if (buf != NULL && dev->capture_type == 2 && len > 0) - em28xx_copy_video(dev, dma_q, buf, p, outp, len); + em28xx_copy_video(dev, buf, p, outp, len); } return rc; } diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index b3d72a92dd9b..7507aa6580d0 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -253,15 +253,17 @@ struct em28xx_buffer { struct videobuf_buffer vb; int top_field; + + /* counter to control buffer fill */ + unsigned int pos; + /* NOTE; in interlaced mode, this value is reset to zero at + * the start of each new field (not frame !) */ }; struct em28xx_dmaqueue { struct list_head active; wait_queue_head_t wq; - - /* Counters to control buffer fill */ - int pos; }; /* inputs */ -- cgit v1.2.3 From a48370158d134807f5b02655287b91cc000c45ca Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 8 Dec 2012 11:31:28 -0300 Subject: [media] em28xx: refactor VBI data processing code in em28xx_urb_data_copy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a new frame header is detected in em28xx_urb_data_copy() and the data packet contains both, VBI data and video data, the prevoius VBI buffer doesn't get finished and is overwritten with the new VBI data. This bug is not triggered with isochronous USB transfers, because the data packetes are much smaller than the VBI data size. But when using USB bulk transfers, the whole data of an URB is treated as single packet, which is usually much larger then the VBI data size. Refactor the VBI data processing code to fix this bug, but also to simplify the code and make it similar to the video data processing code part (which allows further code abstraction/unification in the future). The changes have been tested with device "Hauppauge HVR-900". Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 77 +++++++++++++++------------------ 1 file changed, 36 insertions(+), 41 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 565b6462f152..3a13a7138e73 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -418,8 +418,9 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) } /* capture type 0 = vbi start - capture type 1 = video start - capture type 2 = video in progress */ + capture type 1 = vbi in progress + capture type 2 = video start + capture type 3 = video in progress */ len = actual_length; if (len >= 4) { /* NOTE: headers are always 4 bytes and @@ -438,7 +439,7 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) len -= 4; } else if (p[0] == 0x22 && p[1] == 0x5a) { /* start video */ - dev->capture_type = 1; + dev->capture_type = 2; dev->top_field = !(p[2] & 1); p += 4; len -= 4; @@ -448,51 +449,45 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) * have no continuation header */ if (dev->capture_type == 0) { + dev->capture_type = 1; + if (dev->top_field) { /* Brand new frame */ + if (vbi_buf != NULL) + finish_buffer(dev, vbi_buf); + vbi_buf = get_next_buf(dev, vbi_dma_q); + dev->usb_ctl.vbi_buf = vbi_buf; + if (vbi_buf == NULL) + vbioutp = NULL; + else + vbioutp = + videobuf_to_vmalloc(&vbi_buf->vb); + } + if (vbi_buf != NULL) { + vbi_buf->top_field = dev->top_field; + vbi_buf->pos = 0; + } + } + + if (dev->capture_type == 1) { int vbi_size = dev->vbi_width * dev->vbi_height; - if (dev->vbi_read >= vbi_size) { - /* We've already read all the VBI data, so - treat the rest as video */ - em28xx_isocdbg("dev->vbi_read > vbi_size\n"); - } else if ((dev->vbi_read + len) < vbi_size) { - /* This entire frame is VBI data */ - if (dev->vbi_read == 0 && dev->top_field) { - /* Brand new frame */ - if (vbi_buf != NULL) - finish_buffer(dev, vbi_buf); - vbi_buf = get_next_buf(dev, vbi_dma_q); - dev->usb_ctl.vbi_buf = vbi_buf; - if (vbi_buf == NULL) - vbioutp = NULL; - else - vbioutp = videobuf_to_vmalloc( - &vbi_buf->vb); - } - - if (dev->vbi_read == 0) { - if (vbi_buf != NULL) { - vbi_buf->top_field - = dev->top_field; - vbi_buf->pos = 0; - } - } - - dev->vbi_read += len; - em28xx_copy_vbi(dev, vbi_buf, p, vbioutp, len); - } else { - /* Some of this frame is VBI data and some is - video data */ - int vbi_data_len = vbi_size - dev->vbi_read; - dev->vbi_read += vbi_data_len; + int vbi_data_len = ((dev->vbi_read + len) > vbi_size) ? + (vbi_size - dev->vbi_read) : len; + + /* Copy VBI data */ + if (vbi_buf != NULL) em28xx_copy_vbi(dev, vbi_buf, p, vbioutp, vbi_data_len); - dev->capture_type = 1; + dev->vbi_read += vbi_data_len; + + if (vbi_data_len < len) { + /* Continue with copying video data */ + dev->capture_type = 2; p += vbi_data_len; len -= vbi_data_len; } } - if (dev->capture_type == 1) { - dev->capture_type = 2; + if (dev->capture_type == 2) { + dev->capture_type = 3; if (dev->progressive || dev->top_field) { if (buf != NULL) finish_buffer(dev, buf); @@ -509,7 +504,7 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) } } - if (buf != NULL && dev->capture_type == 2 && len > 0) + if (buf != NULL && dev->capture_type == 3 && len > 0) em28xx_copy_video(dev, buf, p, outp, len); } return rc; -- cgit v1.2.3 From 4078d625c9610a362f571f7e5ff2521adadfff2b Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 8 Dec 2012 11:31:29 -0300 Subject: [media] em28xx: move caching of pointer to vmalloc memory in videobuf to struct em28xx_buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the current code em28xx_urb_data_copy() caches the pointer to the vmalloc memory in videobuf locally. The alternative would be to call videobuf_to_vmalloc() for each processed USB data packet (isoc USB transfers => 64 times per URB) in the em28xx_copy_*() functions. With the next commits, the data processing code will be split into functions for serveral reasons: - em28xx_urb_data_copy() is generally way to long, making it less readable - there is code duplication between VBI and video data processing - support for em25xx data processing (uses a different header and frame end signaling mechanism) will be added This would require extensive usage of pointer-pointers, which usually makes the code less readable and prone to bugs. The better solution is to cache the pointer in struct em28xx_buffer. This also improves consistency, because we already track the buffer fill count there. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 29 +++++++++-------------------- drivers/media/usb/em28xx/em28xx.h | 3 +++ 2 files changed, 12 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 3a13a7138e73..da59442d40a5 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -173,11 +173,12 @@ static inline void finish_buffer(struct em28xx *dev, static void em28xx_copy_video(struct em28xx *dev, struct em28xx_buffer *buf, unsigned char *p, - unsigned char *outp, unsigned long len) + unsigned long len) { void *fieldstart, *startwrite, *startread; int linesdone, currlinedone, offset, lencopy, remain; int bytesperline = dev->width << 1; + unsigned char *outp = buf->vb_buf; if (buf->pos + len > buf->vb.size) len = buf->vb.size - buf->pos; @@ -249,11 +250,12 @@ static void em28xx_copy_video(struct em28xx *dev, static void em28xx_copy_vbi(struct em28xx *dev, struct em28xx_buffer *buf, unsigned char *p, - unsigned char *outp, unsigned long len) + unsigned long len) { void *startwrite, *startread; int offset; int bytesperline; + unsigned char *outp; if (dev == NULL) { em28xx_isocdbg("dev is null\n"); @@ -268,6 +270,7 @@ static void em28xx_copy_vbi(struct em28xx *dev, em28xx_isocdbg("p is null\n"); return; } + outp = buf->vb_buf; if (outp == NULL) { em28xx_isocdbg("outp is null\n"); return; @@ -350,6 +353,7 @@ static inline struct em28xx_buffer *get_next_buf(struct em28xx *dev, outp = videobuf_to_vmalloc(&buf->vb); memset(outp, 0, buf->vb.size); buf->pos = 0; + buf->vb_buf = outp; return buf; } @@ -362,7 +366,7 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; int xfer_bulk, num_packets, i, rc = 1; unsigned int actual_length, len = 0; - unsigned char *p, *outp = NULL, *vbioutp = NULL; + unsigned char *p; if (!dev) return 0; @@ -376,12 +380,7 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) xfer_bulk = usb_pipebulk(urb->pipe); buf = dev->usb_ctl.vid_buf; - if (buf != NULL) - outp = videobuf_to_vmalloc(&buf->vb); - vbi_buf = dev->usb_ctl.vbi_buf; - if (vbi_buf != NULL) - vbioutp = videobuf_to_vmalloc(&vbi_buf->vb); if (xfer_bulk) /* bulk */ num_packets = 1; @@ -455,11 +454,6 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) finish_buffer(dev, vbi_buf); vbi_buf = get_next_buf(dev, vbi_dma_q); dev->usb_ctl.vbi_buf = vbi_buf; - if (vbi_buf == NULL) - vbioutp = NULL; - else - vbioutp = - videobuf_to_vmalloc(&vbi_buf->vb); } if (vbi_buf != NULL) { vbi_buf->top_field = dev->top_field; @@ -474,8 +468,7 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) /* Copy VBI data */ if (vbi_buf != NULL) - em28xx_copy_vbi(dev, vbi_buf, p, vbioutp, - vbi_data_len); + em28xx_copy_vbi(dev, vbi_buf, p, vbi_data_len); dev->vbi_read += vbi_data_len; if (vbi_data_len < len) { @@ -493,10 +486,6 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) finish_buffer(dev, buf); buf = get_next_buf(dev, dma_q); dev->usb_ctl.vid_buf = buf; - if (buf == NULL) - outp = NULL; - else - outp = videobuf_to_vmalloc(&buf->vb); } if (buf != NULL) { buf->top_field = dev->top_field; @@ -505,7 +494,7 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) } if (buf != NULL && dev->capture_type == 3 && len > 0) - em28xx_copy_video(dev, buf, p, outp, len); + em28xx_copy_video(dev, buf, p, len); } return rc; } diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 7507aa6580d0..062841e50722 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -258,6 +258,9 @@ struct em28xx_buffer { unsigned int pos; /* NOTE; in interlaced mode, this value is reset to zero at * the start of each new field (not frame !) */ + + /* pointer to vmalloc memory address in vb */ + char *vb_buf; }; struct em28xx_dmaqueue { -- cgit v1.2.3 From e04c00d985c62a6e1cc6c8048308f3216442f708 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 8 Dec 2012 11:31:30 -0300 Subject: [media] em28xx: em28xx_urb_data_copy(): move duplicate code for capture_type=0 and capture_type=2 to a function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce code duplication by moving the duplicate code for dev->capture_type=0 (vbi start) and dev->capture_type=2 (video start) to a function. The same function will also be called by the (not yet existing) em25xx frame data processing code. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 45 ++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index da59442d40a5..a1f6de0f1b3b 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -358,6 +358,27 @@ static inline struct em28xx_buffer *get_next_buf(struct em28xx *dev, return buf; } +/* + * Finish the current buffer if completed and prepare for the next field + */ +static struct em28xx_buffer * +finish_field_prepare_next(struct em28xx *dev, + struct em28xx_buffer *buf, + struct em28xx_dmaqueue *dma_q) +{ + if (dev->progressive || dev->top_field) { /* Brand new frame */ + if (buf != NULL) + finish_buffer(dev, buf); + buf = get_next_buf(dev, dma_q); + } + if (buf != NULL) { + buf->top_field = dev->top_field; + buf->pos = 0; + } + + return buf; +} + /* Processes and copies the URB data content (video and VBI data) */ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) { @@ -448,17 +469,9 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) * have no continuation header */ if (dev->capture_type == 0) { + vbi_buf = finish_field_prepare_next(dev, vbi_buf, vbi_dma_q); + dev->usb_ctl.vbi_buf = vbi_buf; dev->capture_type = 1; - if (dev->top_field) { /* Brand new frame */ - if (vbi_buf != NULL) - finish_buffer(dev, vbi_buf); - vbi_buf = get_next_buf(dev, vbi_dma_q); - dev->usb_ctl.vbi_buf = vbi_buf; - } - if (vbi_buf != NULL) { - vbi_buf->top_field = dev->top_field; - vbi_buf->pos = 0; - } } if (dev->capture_type == 1) { @@ -480,17 +493,9 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) } if (dev->capture_type == 2) { + buf = finish_field_prepare_next(dev, buf, dma_q); + dev->usb_ctl.vid_buf = buf; dev->capture_type = 3; - if (dev->progressive || dev->top_field) { - if (buf != NULL) - finish_buffer(dev, buf); - buf = get_next_buf(dev, dma_q); - dev->usb_ctl.vid_buf = buf; - } - if (buf != NULL) { - buf->top_field = dev->top_field; - buf->pos = 0; - } } if (buf != NULL && dev->capture_type == 3 && len > 0) -- cgit v1.2.3 From 227b7c90671624e0d143e324a3015726282981df Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 8 Dec 2012 11:31:31 -0300 Subject: [media] em28xx: move the em2710/em2750/em28xx specific frame data processing code to a separate function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit em28xx_urb_data_copy() actually consists of two parts: USB urb processing (checks, data extraction) and frame data packet processing. Move the latter to a separate function and call it from em28xx_urb_data_copy() for each data packet. The em25xx, em2760, em2765 (and likely em277x) chip variants are using a different frame data format, for which support will be added later with another function. This reduces the size of em28xx_urb_data_copy() and makes the code much more readable. While we're at it, clean up the code a bit (rename some variables to something more meaningful, improve some comments etc.) Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 170 +++++++++++++++++--------------- 1 file changed, 90 insertions(+), 80 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index a1f6de0f1b3b..133e6b0a5c7d 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -379,15 +379,90 @@ finish_field_prepare_next(struct em28xx *dev, return buf; } -/* Processes and copies the URB data content (video and VBI data) */ -static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) +/* + * Process data packet according to the em2710/em2750/em28xx frame data format + */ +static inline void process_frame_data_em28xx(struct em28xx *dev, + unsigned char *data_pkt, + unsigned int data_len) { - struct em28xx_buffer *buf, *vbi_buf; + struct em28xx_buffer *buf = dev->usb_ctl.vid_buf; + struct em28xx_buffer *vbi_buf = dev->usb_ctl.vbi_buf; struct em28xx_dmaqueue *dma_q = &dev->vidq; struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; - int xfer_bulk, num_packets, i, rc = 1; - unsigned int actual_length, len = 0; - unsigned char *p; + + /* capture type 0 = vbi start + capture type 1 = vbi in progress + capture type 2 = video start + capture type 3 = video in progress */ + if (data_len >= 4) { + /* NOTE: Headers are always 4 bytes and + * never split across packets */ + if (data_pkt[0] == 0x88 && data_pkt[1] == 0x88 && + data_pkt[2] == 0x88 && data_pkt[3] == 0x88) { + /* Continuation */ + data_pkt += 4; + data_len -= 4; + } else if (data_pkt[0] == 0x33 && data_pkt[1] == 0x95) { + /* Field start (VBI mode) */ + dev->capture_type = 0; + dev->vbi_read = 0; + em28xx_isocdbg("VBI START HEADER !!!\n"); + dev->top_field = !(data_pkt[2] & 1); + data_pkt += 4; + data_len -= 4; + } else if (data_pkt[0] == 0x22 && data_pkt[1] == 0x5a) { + /* Field start (VBI disabled) */ + dev->capture_type = 2; + em28xx_isocdbg("VIDEO START HEADER !!!\n"); + dev->top_field = !(data_pkt[2] & 1); + data_pkt += 4; + data_len -= 4; + } + } + /* NOTE: With bulk transfers, intermediate data packets + * have no continuation header */ + + if (dev->capture_type == 0) { + vbi_buf = finish_field_prepare_next(dev, vbi_buf, vbi_dma_q); + dev->usb_ctl.vbi_buf = vbi_buf; + dev->capture_type = 1; + } + + if (dev->capture_type == 1) { + int vbi_size = dev->vbi_width * dev->vbi_height; + int vbi_data_len = ((dev->vbi_read + data_len) > vbi_size) ? + (vbi_size - dev->vbi_read) : data_len; + + /* Copy VBI data */ + if (vbi_buf != NULL) + em28xx_copy_vbi(dev, vbi_buf, data_pkt, vbi_data_len); + dev->vbi_read += vbi_data_len; + + if (vbi_data_len < data_len) { + /* Continue with copying video data */ + dev->capture_type = 2; + data_pkt += vbi_data_len; + data_len -= vbi_data_len; + } + } + + if (dev->capture_type == 2) { + buf = finish_field_prepare_next(dev, buf, dma_q); + dev->usb_ctl.vid_buf = buf; + dev->capture_type = 3; + } + + if (dev->capture_type == 3 && buf != NULL && data_len > 0) + em28xx_copy_video(dev, buf, data_pkt, data_len); +} + +/* Processes and copies the URB data content (video and VBI data) */ +static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) +{ + int xfer_bulk, num_packets, i; + unsigned char *usb_data_pkt; + unsigned int usb_data_len; if (!dev) return 0; @@ -400,9 +475,6 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) xfer_bulk = usb_pipebulk(urb->pipe); - buf = dev->usb_ctl.vid_buf; - vbi_buf = dev->usb_ctl.vbi_buf; - if (xfer_bulk) /* bulk */ num_packets = 1; else /* isoc */ @@ -410,9 +482,9 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) for (i = 0; i < num_packets; i++) { if (xfer_bulk) { /* bulk */ - actual_length = urb->actual_length; + usb_data_len = urb->actual_length; - p = urb->transfer_buffer; + usb_data_pkt = urb->transfer_buffer; } else { /* isoc */ if (urb->iso_frame_desc[i].status < 0) { print_err_status(dev, i, @@ -421,87 +493,25 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) continue; } - actual_length = urb->iso_frame_desc[i].actual_length; - if (actual_length > dev->max_pkt_size) { + usb_data_len = urb->iso_frame_desc[i].actual_length; + if (usb_data_len > dev->max_pkt_size) { em28xx_isocdbg("packet bigger than packet size"); continue; } - p = urb->transfer_buffer + - urb->iso_frame_desc[i].offset; + usb_data_pkt = urb->transfer_buffer + + urb->iso_frame_desc[i].offset; } - if (actual_length == 0) { + if (usb_data_len == 0) { /* NOTE: happens very often with isoc transfers */ /* em28xx_usbdbg("packet %d is empty",i); - spammy */ continue; } - /* capture type 0 = vbi start - capture type 1 = vbi in progress - capture type 2 = video start - capture type 3 = video in progress */ - len = actual_length; - if (len >= 4) { - /* NOTE: headers are always 4 bytes and - * never split across packets */ - if (p[0] == 0x33 && p[1] == 0x95) { - dev->capture_type = 0; - dev->vbi_read = 0; - em28xx_isocdbg("VBI START HEADER!!!\n"); - dev->top_field = !(p[2] & 1); - p += 4; - len -= 4; - } else if (p[0] == 0x88 && p[1] == 0x88 && - p[2] == 0x88 && p[3] == 0x88) { - /* continuation */ - p += 4; - len -= 4; - } else if (p[0] == 0x22 && p[1] == 0x5a) { - /* start video */ - dev->capture_type = 2; - dev->top_field = !(p[2] & 1); - p += 4; - len -= 4; - } - } - /* NOTE: with bulk transfers, intermediate data packets - * have no continuation header */ - - if (dev->capture_type == 0) { - vbi_buf = finish_field_prepare_next(dev, vbi_buf, vbi_dma_q); - dev->usb_ctl.vbi_buf = vbi_buf; - dev->capture_type = 1; - } - - if (dev->capture_type == 1) { - int vbi_size = dev->vbi_width * dev->vbi_height; - int vbi_data_len = ((dev->vbi_read + len) > vbi_size) ? - (vbi_size - dev->vbi_read) : len; - - /* Copy VBI data */ - if (vbi_buf != NULL) - em28xx_copy_vbi(dev, vbi_buf, p, vbi_data_len); - dev->vbi_read += vbi_data_len; - - if (vbi_data_len < len) { - /* Continue with copying video data */ - dev->capture_type = 2; - p += vbi_data_len; - len -= vbi_data_len; - } - } - - if (dev->capture_type == 2) { - buf = finish_field_prepare_next(dev, buf, dma_q); - dev->usb_ctl.vid_buf = buf; - dev->capture_type = 3; - } - - if (buf != NULL && dev->capture_type == 3 && len > 0) - em28xx_copy_video(dev, buf, p, len); + process_frame_data_em28xx(dev, usb_data_pkt, usb_data_len); } - return rc; + return 1; } -- cgit v1.2.3 From 36016a351d6245b2753138dff3022efba822e5e2 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 8 Dec 2012 11:31:32 -0300 Subject: [media] em28xx: clean up and unify functions em28xx_copy_vbi() em28xx_copy_video() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code in em28xx_vbi_copy can be simplified a lot. Also rename some variables to something more meaningful and fix+add the function descriptions. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 67 +++++++++++---------------------- 1 file changed, 21 insertions(+), 46 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 133e6b0a5c7d..4c1726d43374 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -168,28 +168,27 @@ static inline void finish_buffer(struct em28xx *dev, } /* - * Identify the buffer header type and properly handles + * Copy picture data from USB buffer to videobuf buffer */ static void em28xx_copy_video(struct em28xx *dev, struct em28xx_buffer *buf, - unsigned char *p, + unsigned char *usb_buf, unsigned long len) { void *fieldstart, *startwrite, *startread; int linesdone, currlinedone, offset, lencopy, remain; int bytesperline = dev->width << 1; - unsigned char *outp = buf->vb_buf; if (buf->pos + len > buf->vb.size) len = buf->vb.size - buf->pos; - startread = p; + startread = usb_buf; remain = len; if (dev->progressive || buf->top_field) - fieldstart = outp; + fieldstart = buf->vb_buf; else /* interlaced mode, even nr. of lines */ - fieldstart = outp + bytesperline; + fieldstart = buf->vb_buf + bytesperline; linesdone = buf->pos / bytesperline; currlinedone = buf->pos % bytesperline; @@ -203,11 +202,12 @@ static void em28xx_copy_video(struct em28xx *dev, lencopy = bytesperline - currlinedone; lencopy = lencopy > remain ? remain : lencopy; - if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) { + if ((char *)startwrite + lencopy > (char *)buf->vb_buf + buf->vb.size) { em28xx_isocdbg("Overflow of %zi bytes past buffer end (1)\n", - ((char *)startwrite + lencopy) - - ((char *)outp + buf->vb.size)); - remain = (char *)outp + buf->vb.size - (char *)startwrite; + ((char *)startwrite + lencopy) - + ((char *)buf->vb_buf + buf->vb.size)); + remain = (char *)buf->vb_buf + buf->vb.size - + (char *)startwrite; lencopy = remain; } if (lencopy <= 0) @@ -227,13 +227,13 @@ static void em28xx_copy_video(struct em28xx *dev, else lencopy = bytesperline; - if ((char *)startwrite + lencopy > (char *)outp + + if ((char *)startwrite + lencopy > (char *)buf->vb_buf + buf->vb.size) { em28xx_isocdbg("Overflow of %zi bytes past buffer end" "(2)\n", ((char *)startwrite + lencopy) - - ((char *)outp + buf->vb.size)); - lencopy = remain = (char *)outp + buf->vb.size - + ((char *)buf->vb_buf + buf->vb.size)); + lencopy = remain = (char *)buf->vb_buf + buf->vb.size - (char *)startwrite; } if (lencopy <= 0) @@ -247,50 +247,25 @@ static void em28xx_copy_video(struct em28xx *dev, buf->pos += len; } +/* + * Copy VBI data from USB buffer to videobuf buffer + */ static void em28xx_copy_vbi(struct em28xx *dev, struct em28xx_buffer *buf, - unsigned char *p, + unsigned char *usb_buf, unsigned long len) { - void *startwrite, *startread; - int offset; - int bytesperline; - unsigned char *outp; - - if (dev == NULL) { - em28xx_isocdbg("dev is null\n"); - return; - } - bytesperline = dev->vbi_width; - - if (buf == NULL) { - return; - } - if (p == NULL) { - em28xx_isocdbg("p is null\n"); - return; - } - outp = buf->vb_buf; - if (outp == NULL) { - em28xx_isocdbg("outp is null\n"); - return; - } + unsigned int offset; if (buf->pos + len > buf->vb.size) len = buf->vb.size - buf->pos; - startread = p; - - startwrite = outp + buf->pos; offset = buf->pos; - /* Make sure the bottom field populates the second half of the frame */ - if (buf->top_field == 0) { - startwrite += bytesperline * dev->vbi_height; - offset += bytesperline * dev->vbi_height; - } + if (buf->top_field == 0) + offset += dev->vbi_width * dev->vbi_height; - memcpy(startwrite, startread, len); + memcpy(buf->vb_buf + offset, usb_buf, len); buf->pos += len; } -- cgit v1.2.3 From a6bad040dacb7c0c59173feba98001ae1d4811e4 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 16 Dec 2012 14:23:27 -0300 Subject: [media] em28xx: clean up the data type mess of the i2c transfer function parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-i2c.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 1683bd9d51ee..44533e4574ff 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -53,12 +53,11 @@ do { \ * em2800_i2c_send_max4() * send up to 4 bytes to the i2c device */ -static int em2800_i2c_send_max4(struct em28xx *dev, unsigned char addr, - char *buf, int len) +static int em2800_i2c_send_max4(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { int ret; int write_timeout; - unsigned char b2[6]; + u8 b2[6]; BUG_ON(len < 1 || len > 4); b2[5] = 0x80 + len - 1; b2[4] = addr; @@ -89,15 +88,13 @@ static int em2800_i2c_send_max4(struct em28xx *dev, unsigned char addr, /* * em2800_i2c_send_bytes() */ -static int em2800_i2c_send_bytes(void *data, unsigned char addr, char *buf, - short len) +static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { - char *bufPtr = buf; + u8 *bufPtr = buf; int ret; int wrcount = 0; int count; int maxLen = 4; - struct em28xx *dev = (struct em28xx *)data; while (len > 0) { count = (len > maxLen) ? maxLen : len; ret = em2800_i2c_send_max4(dev, addr, bufPtr, count); @@ -115,9 +112,9 @@ static int em2800_i2c_send_bytes(void *data, unsigned char addr, char *buf, * em2800_i2c_check_for_device() * check if there is a i2c_device at the supplied address */ -static int em2800_i2c_check_for_device(struct em28xx *dev, unsigned char addr) +static int em2800_i2c_check_for_device(struct em28xx *dev, u8 addr) { - char msg; + u8 msg; int ret; int write_timeout; msg = addr; @@ -150,8 +147,7 @@ static int em2800_i2c_check_for_device(struct em28xx *dev, unsigned char addr) * em2800_i2c_recv_bytes() * read from the i2c device */ -static int em2800_i2c_recv_bytes(struct em28xx *dev, unsigned char addr, - char *buf, int len) +static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { int ret; /* check for the device and set i2c read address */ @@ -174,11 +170,10 @@ static int em2800_i2c_recv_bytes(struct em28xx *dev, unsigned char addr, /* * em28xx_i2c_send_bytes() */ -static int em28xx_i2c_send_bytes(void *data, unsigned char addr, char *buf, - short len, int stop) +static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, + u16 len, int stop) { int wrcount = 0; - struct em28xx *dev = (struct em28xx *)data; int write_timeout, ret; wrcount = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len); @@ -199,8 +194,7 @@ static int em28xx_i2c_send_bytes(void *data, unsigned char addr, char *buf, * em28xx_i2c_recv_bytes() * read a byte from the i2c device */ -static int em28xx_i2c_recv_bytes(struct em28xx *dev, unsigned char addr, - char *buf, int len) +static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len) { int ret; ret = dev->em28xx_read_reg_req_len(dev, 2, addr, buf, len); @@ -217,7 +211,7 @@ static int em28xx_i2c_recv_bytes(struct em28xx *dev, unsigned char addr, * em28xx_i2c_check_for_device() * check if there is a i2c_device at the supplied address */ -static int em28xx_i2c_check_for_device(struct em28xx *dev, unsigned char addr) +static int em28xx_i2c_check_for_device(struct em28xx *dev, u16 addr) { int ret; -- cgit v1.2.3 From b5cff595bd005dba8051e9125f0ed28b5ff05c89 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 23 Dec 2012 13:14:20 -0200 Subject: [media] em28xx: prefer_bulk parameter is read-only As the bulk mode is set at device's probe, it is not possible to change it later. So, change the parameter to be read only after modprobing. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index bc63b1cee808..2129560a7823 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -62,7 +62,7 @@ module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); static unsigned int prefer_bulk; -module_param(prefer_bulk, int, 0644); +module_param(prefer_bulk, int, 0444); MODULE_PARM_DESC(prefer_bulk, "prefer USB bulk transfers"); -- cgit v1.2.3 From aa51496b21542855e779a78bf33384002f01acb6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 23 Dec 2012 13:15:47 -0200 Subject: [media] em28xx: display the isoc/bulk mode As both bulk and isoc modes can be available, display what it was found for both DVB and analog. While here, also displays if audio is provided via USB Audio Class or via vendor's extension. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 2129560a7823..9a2cb6136728 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3310,19 +3310,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, ifnum, interface->altsetting->desc.bInterfaceNumber); - if (has_audio) - printk(KERN_INFO DRIVER_NAME - ": Audio Vendor Class interface %i found\n", - ifnum); - if (has_video) - printk(KERN_INFO DRIVER_NAME - ": Video interface %i found\n", - ifnum); - if (has_dvb) - printk(KERN_INFO DRIVER_NAME - ": DVB interface %i found\n", - ifnum); - /* * Make sure we have 480 Mbps of bandwidth, otherwise things like * video stream wouldn't likely work, since 12 Mbps is generally @@ -3361,6 +3348,24 @@ static int em28xx_usb_probe(struct usb_interface *interface, } } + if (has_audio) + printk(KERN_INFO DRIVER_NAME + ": Audio interface %i found %s\n", + ifnum, + dev->has_audio_class ? "(USB Audio Class)" : "(Vendor Class)"); + if (has_video) + printk(KERN_INFO DRIVER_NAME + ": Video interface %i found:%s%s\n", + ifnum, + dev->analog_ep_bulk ? " bulk" : "", + dev->analog_ep_isoc ? " isoc" : ""); + if (has_dvb) + printk(KERN_INFO DRIVER_NAME + ": DVB interface %i found:%s%s\n", + ifnum, + dev->dvb_ep_bulk ? " bulk" : "", + dev->dvb_ep_isoc ? " isoc" : ""); + dev->num_alt = interface->num_altsetting; if ((unsigned)card[nr] < em28xx_bcount) -- cgit v1.2.3 From a3efa1cc0e067675ffa2d2c357cbe1da0db4653b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 23 Dec 2012 16:32:03 -0200 Subject: [media] em28xx: make the logs reflect the specific chip name In order to make easier to analize the logs when multiple devices are plugged, change the device name accordingly with the chip version. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 46 +++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 9a2cb6136728..1f90a8a8181b 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2957,6 +2957,8 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, int minor) { int retval; + static const char *default_chip_name = "em28xx"; + const char *chip_name = default_chip_name; dev->udev = udev; mutex_init(&dev->ctrl_urb_lock); @@ -2984,51 +2986,62 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, switch (dev->chip_id) { case CHIP_ID_EM2800: - em28xx_info("chip ID is em2800\n"); + chip_name = "em2800"; break; case CHIP_ID_EM2710: - em28xx_info("chip ID is em2710\n"); + chip_name = "em2710"; break; case CHIP_ID_EM2750: - em28xx_info("chip ID is em2750\n"); + chip_name = "em2750"; break; case CHIP_ID_EM2820: - em28xx_info("chip ID is em2820 (or em2710)\n"); + chip_name = "em2710/2820"; break; case CHIP_ID_EM2840: - em28xx_info("chip ID is em2840\n"); + chip_name = "em2840"; break; case CHIP_ID_EM2860: - em28xx_info("chip ID is em2860\n"); + chip_name = "em2860"; break; case CHIP_ID_EM2870: - em28xx_info("chip ID is em2870\n"); + chip_name = "em2870"; dev->wait_after_write = 0; break; case CHIP_ID_EM2874: - em28xx_info("chip ID is em2874\n"); + chip_name = "em2874"; dev->reg_gpio_num = EM2874_R80_GPIO; dev->wait_after_write = 0; break; case CHIP_ID_EM28174: - em28xx_info("chip ID is em28174\n"); + chip_name = "em28174"; dev->reg_gpio_num = EM2874_R80_GPIO; dev->wait_after_write = 0; break; case CHIP_ID_EM2883: - em28xx_info("chip ID is em2882/em2883\n"); + chip_name = "em2882/3"; dev->wait_after_write = 0; break; case CHIP_ID_EM2884: - em28xx_info("chip ID is em2884\n"); + chip_name = "em2884"; dev->reg_gpio_num = EM2874_R80_GPIO; dev->wait_after_write = 0; break; default: - em28xx_info("em28xx chip ID = %d\n", dev->chip_id); + printk(KERN_INFO DRIVER_NAME + ": unknown em28xx chip ID (%d)\n", dev->chip_id); } } + if (chip_name != default_chip_name) + printk(KERN_INFO DRIVER_NAME + ": chip ID is %s\n", chip_name); + + /* + * For em2820/em2710, the name may change latter, after checking + * if the device has a sensor (so, it is em2710) or not. + */ + snprintf(dev->name, sizeof(dev->name), "%s #%d", chip_name, dev->devno); + if (dev->is_audio_only) { retval = em28xx_audio_setup(dev); if (retval) @@ -3045,6 +3058,14 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, em28xx_pre_card_setup(dev); + if (dev->chip_id == CHIP_ID_EM2820) { + if (dev->board.is_webcam) + chip_name = "em2710"; + else + chip_name = "em2820"; + snprintf(dev->name, sizeof(dev->name), "%s #%d", chip_name, dev->devno); + } + if (!dev->board.is_em2800) { /* Resets I2C speed */ retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed); @@ -3331,7 +3352,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, (!dev->dvb_ep_isoc || (prefer_bulk && dev->dvb_ep_bulk))) dev->dvb_xfer_bulk = 1; - snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr); dev->devno = nr; dev->model = id->driver_info; dev->alt = -1; -- cgit v1.2.3 From 8b2aea7878f64814544d0527c659011949d52358 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 23 Dec 2012 13:25:38 -0200 Subject: [media] em28xx: prefer bulk mode on webcams Using bulk mode allows more than one webcam, as the maximum fps is low at 640x480 resolution. So, prefer it, if the device is a webcam. Tested with Silvercrest 1.3 Mpixel webcam (em2710) on both bulk and isoc modes. Tested analog with HVR-950 model 65201/A1C0 (em2883), where only ISOC endpoints are available for both DVB and Analog. Tested on Hauppauge WinTV USB 2 (em2840) on both bulk and isoc modes. It should be noticed that enabling bulk mode by default with TV boards is a bad idea; what happens is that, while with ISOC the USB logic will prevent the concurrent usage of two devices that spends more than 100% of the USB2 traffic, it doesn't care with bulk transfers. On my tests, I started two streams, one with a WinTV at 640x480x30fps and the other one with a Silvercrest webcam at 640x480, on a lower fps) both on bulk mode. One of the streams always silently failed. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 36 +++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 1f90a8a8181b..ad6c800d9a40 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -61,9 +61,9 @@ static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); -static unsigned int prefer_bulk; +static int prefer_bulk = -1; module_param(prefer_bulk, int, 0444); -MODULE_PARM_DESC(prefer_bulk, "prefer USB bulk transfers"); +MODULE_PARM_DESC(prefer_bulk, "prefer USB bulk transfers (-1 = auto, 0 = isoc, 1 = bulk)"); /* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */ @@ -3170,7 +3170,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, struct em28xx *dev = NULL; int retval; bool has_audio = false, has_video = false, has_dvb = false; - int i, nr; + int i, nr, try_bulk; const int ifnum = interface->altsetting[0].desc.bInterfaceNumber; char *speed; @@ -3344,14 +3344,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, goto err_free; } - /* Select USB transfer types to use */ - if (has_video && - (!dev->analog_ep_isoc || (prefer_bulk && dev->analog_ep_bulk))) - dev->analog_xfer_bulk = 1; - if (has_dvb && - (!dev->dvb_ep_isoc || (prefer_bulk && dev->dvb_ep_bulk))) - dev->dvb_xfer_bulk = 1; - dev->devno = nr; dev->model = id->driver_info; dev->alt = -1; @@ -3402,7 +3394,29 @@ static int em28xx_usb_probe(struct usb_interface *interface, goto unlock_and_free; } + if (prefer_bulk < 0) { + if (dev->board.is_webcam) + try_bulk = 1; + else + try_bulk = 0; + } else { + try_bulk = prefer_bulk > 0; + } + + /* Select USB transfer types to use */ + if (has_video) { + if (!dev->analog_ep_isoc || (try_bulk && dev->analog_ep_bulk)) + dev->analog_xfer_bulk = 1; + em28xx_info("analog set to %s mode.\n", + dev->analog_xfer_bulk ? "bulk" : "isoc"); + } if (has_dvb) { + if (!dev->dvb_ep_isoc || (try_bulk && dev->dvb_ep_bulk)) + dev->dvb_xfer_bulk = 1; + + em28xx_info("dvb set to %s mode.\n", + dev->dvb_xfer_bulk ? "bulk" : "isoc"); + /* pre-allocate DVB usb transfer buffers */ if (dev->dvb_xfer_bulk) { retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, -- cgit v1.2.3 From 36cb26a4a67cdc186a2a5ec5e49063ea635969ee Mon Sep 17 00:00:00 2001 From: Alfredo Jesús Delaiti Date: Thu, 8 Nov 2012 16:14:50 -0300 Subject: [media] rc/keymaps: add RC keytable for MyGica X8507 Add RC-5 remote keytable definition for MyGica X8507. [mchehab@redhat.com: fixed whitespacing - it seems that Alfredo's emailer mangled it] Signed-off-by: Alfredo J. Delaiti Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/keymaps/Makefile | 1 + .../media/rc/keymaps/rc-total-media-in-hand-02.c | 86 ++++++++++++++++++++++ include/media/rc-map.h | 1 + 3 files changed, 88 insertions(+) create mode 100644 drivers/media/rc/keymaps/rc-total-media-in-hand-02.c (limited to 'drivers') diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index ab84d66c67c1..778661971aed 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -88,6 +88,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-tevii-nec.o \ rc-tivo.o \ rc-total-media-in-hand.o \ + rc-total-media-in-hand-02.o \ rc-trekstor.o \ rc-tt-1500.o \ rc-twinhan1027.o \ diff --git a/drivers/media/rc/keymaps/rc-total-media-in-hand-02.c b/drivers/media/rc/keymaps/rc-total-media-in-hand-02.c new file mode 100644 index 000000000000..47270f72ebf0 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-total-media-in-hand-02.c @@ -0,0 +1,86 @@ +/* + * Total Media In Hand_02 remote controller keytable for Mygica X8507 + * + * Copyright (C) 2012 Alfredo J. Delaiti + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + + +static struct rc_map_table total_media_in_hand_02[] = { + { 0x0000, KEY_0 }, + { 0x0001, KEY_1 }, + { 0x0002, KEY_2 }, + { 0x0003, KEY_3 }, + { 0x0004, KEY_4 }, + { 0x0005, KEY_5 }, + { 0x0006, KEY_6 }, + { 0x0007, KEY_7 }, + { 0x0008, KEY_8 }, + { 0x0009, KEY_9 }, + { 0x000a, KEY_MUTE }, + { 0x000b, KEY_STOP }, /* Stop */ + { 0x000c, KEY_POWER2 }, /* Turn on/off application */ + { 0x000d, KEY_OK }, /* OK */ + { 0x000e, KEY_CAMERA }, /* Snapshot */ + { 0x000f, KEY_ZOOM }, /* Full Screen/Restore */ + { 0x0010, KEY_RIGHT }, /* Right arrow */ + { 0x0011, KEY_LEFT }, /* Left arrow */ + { 0x0012, KEY_CHANNELUP }, + { 0x0013, KEY_CHANNELDOWN }, + { 0x0014, KEY_SHUFFLE }, + { 0x0016, KEY_PAUSE }, + { 0x0017, KEY_PLAY }, /* Play */ + { 0x001e, KEY_TIME }, /* Time Shift */ + { 0x001f, KEY_RECORD }, + { 0x0020, KEY_UP }, + { 0x0021, KEY_DOWN }, + { 0x0025, KEY_POWER }, /* Turn off computer */ + { 0x0026, KEY_REWIND }, /* FR << */ + { 0x0027, KEY_FASTFORWARD }, /* FF >> */ + { 0x0029, KEY_ESC }, + { 0x002b, KEY_VOLUMEUP }, + { 0x002c, KEY_VOLUMEDOWN }, + { 0x002d, KEY_CHANNEL }, /* CH Surfing */ + { 0x0038, KEY_VIDEO }, /* TV/AV/S-Video/YPbPr */ +}; + +static struct rc_map_list total_media_in_hand_02_map = { + .map = { + .scan = total_media_in_hand_02, + .size = ARRAY_SIZE(total_media_in_hand_02), + .rc_type = RC_TYPE_RC5, + .name = RC_MAP_TOTAL_MEDIA_IN_HAND_02, + } +}; + +static int __init init_rc_map_total_media_in_hand_02(void) +{ + return rc_map_register(&total_media_in_hand_02_map); +} + +static void __exit exit_rc_map_total_media_in_hand_02(void) +{ + rc_map_unregister(&total_media_in_hand_02_map); +} + +module_init(init_rc_map_total_media_in_hand_02) +module_exit(exit_rc_map_total_media_in_hand_02) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR(" Alfredo J. Delaiti "); diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 74f55a3f14eb..f74ee6f89711 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -182,6 +182,7 @@ void rc_map_init(void); #define RC_MAP_TEVII_NEC "rc-tevii-nec" #define RC_MAP_TIVO "rc-tivo" #define RC_MAP_TOTAL_MEDIA_IN_HAND "rc-total-media-in-hand" +#define RC_MAP_TOTAL_MEDIA_IN_HAND_02 "rc-total-media-in-hand-02" #define RC_MAP_TREKSTOR "rc-trekstor" #define RC_MAP_TT_1500 "rc-tt-1500" #define RC_MAP_TWINHAN_VP1027_DVBS "rc-twinhan1027" -- cgit v1.2.3 From e5f670b7f9685a850e94ba263948c676b1b06eef Mon Sep 17 00:00:00 2001 From: Alfredo Jesús Delaiti Date: Thu, 8 Nov 2012 15:50:25 -0300 Subject: [media] cx23885: add RC support for MyGica X8507 This series add remote control support for MyGica X8507. I test for 2 month under OpenSuse(X64) 11.4 and 12.2 with kernel 3.4, 3.5, 3.6 also 3.7-rc2 and rc3. [mchehab@redhat.com: fixed whitespacing - it seems that Alfredo's emailer mangled it] Signed-off-by: Alfredo J. Delaiti Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx23885/cx23885-cards.c | 3 +++ drivers/media/pci/cx23885/cx23885-input.c | 9 +++++++++ 2 files changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index 7a79a17105c4..8d96ae4ebbdd 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -1408,6 +1408,7 @@ int cx23885_ir_init(struct cx23885_dev *dev) break; case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: case CX23885_BOARD_TEVII_S470: + case CX23885_BOARD_MYGICA_X8507: if (!enable_885_ir) break; dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE); @@ -1450,6 +1451,7 @@ void cx23885_ir_fini(struct cx23885_dev *dev) case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: case CX23885_BOARD_TEVII_S470: case CX23885_BOARD_HAUPPAUGE_HVR1250: + case CX23885_BOARD_MYGICA_X8507: cx23885_irq_remove(dev, PCI_MSK_AV_CORE); /* sd_ir is a duplicate pointer to the AV Core, just clear it */ dev->sd_ir = NULL; @@ -1494,6 +1496,7 @@ void cx23885_ir_pci_int_enable(struct cx23885_dev *dev) case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: case CX23885_BOARD_TEVII_S470: case CX23885_BOARD_HAUPPAUGE_HVR1250: + case CX23885_BOARD_MYGICA_X8507: if (dev->sd_ir) cx23885_irq_add_enable(dev, PCI_MSK_AV_CORE); break; diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c index 4f1055a194b5..7875dfbe09ff 100644 --- a/drivers/media/pci/cx23885/cx23885-input.c +++ b/drivers/media/pci/cx23885/cx23885-input.c @@ -89,6 +89,7 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events) case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: case CX23885_BOARD_TEVII_S470: case CX23885_BOARD_HAUPPAUGE_HVR1250: + case CX23885_BOARD_MYGICA_X8507: /* * The only boards we handle right now. However other boards * using the CX2388x integrated IR controller should be similar @@ -140,6 +141,7 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: case CX23885_BOARD_HAUPPAUGE_HVR1250: + case CX23885_BOARD_MYGICA_X8507: /* * The IR controller on this board only returns pulse widths. * Any other mode setting will fail to set up the device. @@ -289,6 +291,13 @@ int cx23885_input_init(struct cx23885_dev *dev) /* A guess at the remote */ rc_map = RC_MAP_TEVII_NEC; break; + case CX23885_BOARD_MYGICA_X8507: + /* Integrated CX23885 IR controller */ + driver_type = RC_DRIVER_IR_RAW; + allowed_protos = RC_BIT_ALL; + /* A guess at the remote */ + rc_map = RC_MAP_TOTAL_MEDIA_IN_HAND_02; + break; default: return -ENODEV; } -- cgit v1.2.3 From 341068fcf8cc10145d38abe927fa9b9f0c461c66 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Thu, 8 Nov 2012 17:30:21 -0300 Subject: [media] it913x: fix correct endpoint size when pid filter on I kept the count as the hardware default with dvb-usb-v2, with 5, users can still run in to trouble with Video PIDs. I have traced it to an incorrect endpoint size when the PID filter is enabled. It also affected USB 2.0 with the filter on. Reported-by: Antti Palosaari Tested-by: Antti Palosaari Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/it913x.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/it913x.c b/drivers/media/usb/dvb-usb-v2/it913x.c index 47204280b8b3..fbc0a84465eb 100644 --- a/drivers/media/usb/dvb-usb-v2/it913x.c +++ b/drivers/media/usb/dvb-usb-v2/it913x.c @@ -643,7 +643,8 @@ static int it913x_frontend_attach(struct dvb_usb_adapter *adap) struct it913x_state *st = d->priv; int ret = 0; u8 adap_addr = I2C_BASE_ADDR + (adap->id << 5); - u16 ep_size = adap->stream.buf_size / 4; + u16 ep_size = (adap->pid_filtering) ? TS_BUFFER_SIZE_PID / 4 : + TS_BUFFER_SIZE_MAX / 4; u8 pkt_size = 0x80; if (d->udev->speed != USB_SPEED_HIGH) -- cgit v1.2.3 From 899a179dbf5878fd01c0bd3b0e884ec526916afb Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 16 Nov 2012 00:55:28 -0300 Subject: [media] s5p-tv: Use devm_gpio_request in sii9234_drv.c devm_gpio_request is a device managed function and will make error handling and cleanup a bit simpler. Signed-off-by: Sachin Kamat Acked-by: Tomasz Stanislawski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/sii9234_drv.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-tv/sii9234_drv.c b/drivers/media/platform/s5p-tv/sii9234_drv.c index 716d4846f8bd..4597342cdfbe 100644 --- a/drivers/media/platform/s5p-tv/sii9234_drv.c +++ b/drivers/media/platform/s5p-tv/sii9234_drv.c @@ -338,7 +338,7 @@ static int __devinit sii9234_probe(struct i2c_client *client, } ctx->gpio_n_reset = pdata->gpio_n_reset; - ret = gpio_request(ctx->gpio_n_reset, "MHL_RST"); + ret = devm_gpio_request(dev, ctx->gpio_n_reset, "MHL_RST"); if (ret) { dev_err(dev, "failed to acquire MHL_RST gpio\n"); return ret; @@ -370,7 +370,6 @@ fail_pm_get: fail_pm: pm_runtime_disable(dev); - gpio_free(ctx->gpio_n_reset); fail: dev_err(dev, "probe failed\n"); @@ -381,11 +380,8 @@ fail: static int __devexit sii9234_remove(struct i2c_client *client) { struct device *dev = &client->dev; - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct sii9234_context *ctx = sd_to_context(sd); pm_runtime_disable(dev); - gpio_free(ctx->gpio_n_reset); dev_info(dev, "remove successful\n"); -- cgit v1.2.3 From cb412a8da82233af001d13c28fc54f25a2001aef Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Fri, 16 Nov 2012 18:17:01 -0300 Subject: [media] staging/media/solo6x10/v4l2-enc.c: fix error-handling The return values of copy_to_user() and copy_from_user() cannot be negative. Found using the following semantich patch: @exists@ identifier ret; statement S; expression E; @@ ( * ret = copy_to_user(...); | * ret = copy_from_user(...); ) ... when != ret = E when != if (ret) { <+... ret = E; ...+> } * if (ret < 0) S Signed-off-by: Cyril Roelandt Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/solo6x10/v4l2-enc.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c index f8f0da952288..4977e869d5b7 100644 --- a/drivers/staging/media/solo6x10/v4l2-enc.c +++ b/drivers/staging/media/solo6x10/v4l2-enc.c @@ -1619,6 +1619,8 @@ static int solo_s_ext_ctrls(struct file *file, void *priv, solo_enc->osd_text[OSD_TEXT_MAX] = '\0'; if (!err) err = solo_osd_print(solo_enc); + else + err = -EFAULT; } break; default: @@ -1654,6 +1656,8 @@ static int solo_g_ext_ctrls(struct file *file, void *priv, err = copy_to_user(ctrl->string, solo_enc->osd_text, OSD_TEXT_MAX); + if (err) + err = -EFAULT; } break; default: -- cgit v1.2.3 From fe0e990b22c24d53793c8cf246f0e535f31a4406 Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Tue, 23 Oct 2012 09:56:59 -0300 Subject: [media] vivi: Teach it to tune FPS I was testing my video-over-ethernet subsystem recently, and vivi seemed to be perfect video source for testing when one don't have lots of capture boards and cameras. Only its framerate was hardcoded to NTSC's 30fps, while in my country we usually use PAL (25 fps) and I needed that to precisely simulate bandwidth. That's why here is this patch with ->enum_frameintervals() and ->{g,s}_parm() implemented as suggested by Hans Verkuil which passes v4l2-compliance and manual testing through v4l2-ctl -P / -p . Regarding newly introduced __get_format(u32 pixelformat) I decided not to convert original get_format() to operate on fourcc codes, since >= 3 places in driver need to deal with v4l2_format and otherwise it won't be handy. [mchehab@redhat.com: Some CodingStyle fixes] Signed-off-by: Kirill Smelkov Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivi.c | 104 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 96 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index 7abb046e0fa5..ec6508909f02 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -36,9 +36,17 @@ #define VIVI_MODULE_NAME "vivi" -/* Wake up at about 30 fps */ -#define WAKE_NUMERATOR 30 -#define WAKE_DENOMINATOR 1001 +/* Maximum allowed frame rate + * + * Vivi will allow setting timeperframe in [1/FPS_MAX - FPS_MAX/1] range. + * + * Ideally FPS_MAX should be infinity, i.e. practically UINT_MAX, but that + * might hit application errors when they manipulate these values. + * + * Besides, for tpf < 1ms image-generation logic should be changed, to avoid + * producing frames with equal content. + */ +#define FPS_MAX 1000 #define MAX_WIDTH 1920 #define MAX_HEIGHT 1200 @@ -69,6 +77,12 @@ MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); /* Global font descriptor */ static const u8 *font8x16; +/* timeperframe: min/max and default */ +static const struct v4l2_fract + tpf_min = {.numerator = 1, .denominator = FPS_MAX}, + tpf_max = {.numerator = FPS_MAX, .denominator = 1}, + tpf_default = {.numerator = 1001, .denominator = 30000}; /* NTSC */ + #define dprintk(dev, level, fmt, arg...) \ v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg) @@ -150,14 +164,14 @@ static struct vivi_fmt formats[] = { }, }; -static struct vivi_fmt *get_format(struct v4l2_format *f) +static struct vivi_fmt *__get_format(u32 pixelformat) { struct vivi_fmt *fmt; unsigned int k; for (k = 0; k < ARRAY_SIZE(formats); k++) { fmt = &formats[k]; - if (fmt->fourcc == f->fmt.pix.pixelformat) + if (fmt->fourcc == pixelformat) break; } @@ -167,6 +181,11 @@ static struct vivi_fmt *get_format(struct v4l2_format *f) return &formats[k]; } +static struct vivi_fmt *get_format(struct v4l2_format *f) +{ + return __get_format(f->fmt.pix.pixelformat); +} + /* buffer for one video frame */ struct vivi_buffer { /* common v4l buffer stuff -- must be first */ @@ -232,6 +251,7 @@ struct vivi_dev { /* video capture */ struct vivi_fmt *fmt; + struct v4l2_fract timeperframe; unsigned int width, height; struct vb2_queue vb_vidq; unsigned int field_count; @@ -689,8 +709,8 @@ static void vivi_thread_tick(struct vivi_dev *dev) dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index); } -#define frames_to_ms(frames) \ - ((frames * WAKE_NUMERATOR * 1000) / WAKE_DENOMINATOR) +#define frames_to_ms(dev, frames) \ + ((frames * dev->timeperframe.numerator * 1000) / dev->timeperframe.denominator) static void vivi_sleep(struct vivi_dev *dev) { @@ -706,7 +726,7 @@ static void vivi_sleep(struct vivi_dev *dev) goto stop_task; /* Calculate time to wake up */ - timeout = msecs_to_jiffies(frames_to_ms(1)); + timeout = msecs_to_jiffies(frames_to_ms(dev, 1)); vivi_thread_tick(dev); @@ -1078,6 +1098,70 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) return 0; } +/* timeperframe is arbitrary and continous */ +static int vidioc_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct vivi_fmt *fmt; + + if (fival->index) + return -EINVAL; + + fmt = __get_format(fival->pixel_format); + if (!fmt) + return -EINVAL; + + /* regarding width & height - we support any */ + + fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; + + /* fill in stepwise (step=1.0 is requred by V4L2 spec) */ + fival->stepwise.min = tpf_min; + fival->stepwise.max = tpf_max; + fival->stepwise.step = (struct v4l2_fract) {1, 1}; + + return 0; +} + +static int vidioc_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + struct vivi_dev *dev = video_drvdata(file); + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.timeperframe = dev->timeperframe; + parm->parm.capture.readbuffers = 1; + return 0; +} + +#define FRACT_CMP(a, OP, b) \ + ((u64)(a).numerator * (b).denominator OP (u64)(b).numerator * (a).denominator) + +static int vidioc_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + struct vivi_dev *dev = video_drvdata(file); + struct v4l2_fract tpf; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + tpf = parm->parm.capture.timeperframe; + + /* tpf: {*, 0} resets timing; clip to [min, max]*/ + tpf = tpf.denominator ? tpf : tpf_default; + tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf; + tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf; + + dev->timeperframe = tpf; + parm->parm.capture.timeperframe = tpf; + parm->parm.capture.readbuffers = 1; + return 0; +} + /* --- controls ---------------------------------------------- */ static int vivi_g_volatile_ctrl(struct v4l2_ctrl *ctrl) @@ -1236,6 +1320,9 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, + .vidioc_enum_frameintervals = vidioc_enum_frameintervals, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_log_status = v4l2_ctrl_log_status, @@ -1294,6 +1381,7 @@ static int __init vivi_create_instance(int inst) goto free_dev; dev->fmt = &formats[0]; + dev->timeperframe = tpf_default; dev->width = 640; dev->height = 480; dev->pixelsize = dev->fmt->depth / 8; -- cgit v1.2.3 From fab0e8fa432e42d7b5c91a3d4c8af053f291a65a Mon Sep 17 00:00:00 2001 From: Scott Jiang Date: Tue, 20 Nov 2012 15:49:35 -0300 Subject: [media] v4l2: blackfin: convert ppi driver to a module Other drivers can make use of it. Signed-off-by: Scott Jiang Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/blackfin/Kconfig | 6 +++++- drivers/media/platform/blackfin/Makefile | 4 ++-- drivers/media/platform/blackfin/ppi.c | 7 +++++++ 3 files changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/blackfin/Kconfig b/drivers/media/platform/blackfin/Kconfig index ecd5323768b7..519990e17122 100644 --- a/drivers/media/platform/blackfin/Kconfig +++ b/drivers/media/platform/blackfin/Kconfig @@ -2,9 +2,13 @@ config VIDEO_BLACKFIN_CAPTURE tristate "Blackfin Video Capture Driver" depends on VIDEO_V4L2 && BLACKFIN && I2C select VIDEOBUF2_DMA_CONTIG + select VIDEO_BLACKFIN_PPI help V4L2 bridge driver for Blackfin video capture device. Choose PPI or EPPI as its interface. To compile this driver as a module, choose M here: the - module will be called bfin_video_capture. + module will be called bfin_capture. + +config VIDEO_BLACKFIN_PPI + tristate diff --git a/drivers/media/platform/blackfin/Makefile b/drivers/media/platform/blackfin/Makefile index aa3a0a216387..30421bc23080 100644 --- a/drivers/media/platform/blackfin/Makefile +++ b/drivers/media/platform/blackfin/Makefile @@ -1,2 +1,2 @@ -bfin_video_capture-objs := bfin_capture.o ppi.o -obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_video_capture.o +obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_capture.o +obj-$(CONFIG_VIDEO_BLACKFIN_PPI) += ppi.o diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c index d29592186b02..9374d676f63d 100644 --- a/drivers/media/platform/blackfin/ppi.c +++ b/drivers/media/platform/blackfin/ppi.c @@ -17,6 +17,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include #include #include @@ -263,9 +264,15 @@ struct ppi_if *ppi_create_instance(const struct ppi_info *info) pr_info("ppi probe success\n"); return ppi; } +EXPORT_SYMBOL(ppi_create_instance); void ppi_delete_instance(struct ppi_if *ppi) { peripheral_free_list(ppi->info->pin_req); kfree(ppi); } +EXPORT_SYMBOL(ppi_delete_instance); + +MODULE_DESCRIPTION("Analog Devices PPI driver"); +MODULE_AUTHOR("Scott Jiang "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 45b82596be0214f161c8176bd3e18f779e36eccd Mon Sep 17 00:00:00 2001 From: Scott Jiang Date: Tue, 20 Nov 2012 15:49:36 -0300 Subject: [media] v4l2: blackfin: add EPPI3 support Bf60x soc has a new PPI called Enhanced PPI version 3. HD video is supported now. To achieve this, we redesign ppi params and add dv timings feature. Signed-off-by: Scott Jiang Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/blackfin/bfin_capture.c | 148 ++++++++++++++++++++++--- drivers/media/platform/blackfin/ppi.c | 72 +++++++++--- include/media/blackfin/bfin_capture.h | 5 +- include/media/blackfin/ppi.h | 33 +++++- 4 files changed, 222 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index d422d3c379e4..aa9f846a667e 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -52,6 +52,7 @@ struct bcap_format { u32 pixelformat; enum v4l2_mbus_pixelcode mbus_code; int bpp; /* bits per pixel */ + int dlen; /* data length for ppi in bits */ }; struct bcap_buffer { @@ -76,10 +77,14 @@ struct bcap_device { unsigned int cur_input; /* current selected standard */ v4l2_std_id std; + /* current selected dv_timings */ + struct v4l2_dv_timings dv_timings; /* used to store pixel format */ struct v4l2_pix_format fmt; /* bits per pixel*/ int bpp; + /* data length for ppi in bits */ + int dlen; /* used to store sensor supported format */ struct bcap_format *sensor_formats; /* number of sensor formats array */ @@ -116,24 +121,35 @@ static const struct bcap_format bcap_formats[] = { .pixelformat = V4L2_PIX_FMT_UYVY, .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, .bpp = 16, + .dlen = 8, }, { .desc = "YCbCr 4:2:2 Interleaved YUYV", .pixelformat = V4L2_PIX_FMT_YUYV, .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, .bpp = 16, + .dlen = 8, + }, + { + .desc = "YCbCr 4:2:2 Interleaved UYVY", + .pixelformat = V4L2_PIX_FMT_UYVY, + .mbus_code = V4L2_MBUS_FMT_UYVY8_1X16, + .bpp = 16, + .dlen = 16, }, { .desc = "RGB 565", .pixelformat = V4L2_PIX_FMT_RGB565, .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, .bpp = 16, + .dlen = 8, }, { .desc = "RGB 444", .pixelformat = V4L2_PIX_FMT_RGB444, .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, .bpp = 16, + .dlen = 8, }, }; @@ -366,9 +382,39 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count) params.width = bcap_dev->fmt.width; params.height = bcap_dev->fmt.height; params.bpp = bcap_dev->bpp; + params.dlen = bcap_dev->dlen; params.ppi_control = bcap_dev->cfg->ppi_control; params.int_mask = bcap_dev->cfg->int_mask; - params.blank_clocks = bcap_dev->cfg->blank_clocks; + if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities + & V4L2_IN_CAP_CUSTOM_TIMINGS) { + struct v4l2_bt_timings *bt = &bcap_dev->dv_timings.bt; + + params.hdelay = bt->hsync + bt->hbackporch; + params.vdelay = bt->vsync + bt->vbackporch; + params.line = bt->hfrontporch + bt->hsync + + bt->hbackporch + bt->width; + params.frame = bt->vfrontporch + bt->vsync + + bt->vbackporch + bt->height; + if (bt->interlaced) + params.frame += bt->il_vfrontporch + bt->il_vsync + + bt->il_vbackporch; + } else if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities + & V4L2_IN_CAP_STD) { + params.hdelay = 0; + params.vdelay = 0; + if (bcap_dev->std & V4L2_STD_525_60) { + params.line = 858; + params.frame = 525; + } else { + params.line = 864; + params.frame = 625; + } + } else { + params.hdelay = 0; + params.vdelay = 0; + params.line = params.width + bcap_dev->cfg->blank_pixels; + params.frame = params.height; + } ret = ppi->ops->set_params(ppi, ¶ms); if (ret < 0) { v4l2_err(&bcap_dev->v4l2_dev, @@ -600,6 +646,37 @@ static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std) return 0; } +static int bcap_g_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + int ret; + + ret = v4l2_subdev_call(bcap_dev->sd, video, + g_dv_timings, timings); + if (ret < 0) + return ret; + + bcap_dev->dv_timings = *timings; + return 0; +} + +static int bcap_s_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + int ret; + if (vb2_is_busy(&bcap_dev->buffer_queue)) + return -EBUSY; + + ret = v4l2_subdev_call(bcap_dev->sd, video, s_dv_timings, timings); + if (ret < 0) + return ret; + + bcap_dev->dv_timings = *timings; + return 0; +} + static int bcap_enum_input(struct file *file, void *priv, struct v4l2_input *input) { @@ -648,13 +725,15 @@ static int bcap_s_input(struct file *file, void *priv, unsigned int index) return ret; } bcap_dev->cur_input = index; + /* if this route has specific config, update ppi control */ + if (route->ppi_control) + config->ppi_control = route->ppi_control; return 0; } static int bcap_try_format(struct bcap_device *bcap, struct v4l2_pix_format *pixfmt, - enum v4l2_mbus_pixelcode *mbus_code, - int *bpp) + struct bcap_format *bcap_fmt) { struct bcap_format *sf = bcap->sensor_formats; struct bcap_format *fmt = NULL; @@ -669,16 +748,20 @@ static int bcap_try_format(struct bcap_device *bcap, if (i == bcap->num_sensor_formats) fmt = &sf[0]; - if (mbus_code) - *mbus_code = fmt->mbus_code; - if (bpp) - *bpp = fmt->bpp; v4l2_fill_mbus_format(&mbus_fmt, pixfmt, fmt->mbus_code); ret = v4l2_subdev_call(bcap->sd, video, try_mbus_fmt, &mbus_fmt); if (ret < 0) return ret; v4l2_fill_pix_format(pixfmt, &mbus_fmt); + if (bcap_fmt) { + for (i = 0; i < bcap->num_sensor_formats; i++) { + fmt = &sf[i]; + if (mbus_fmt.code == fmt->mbus_code) + break; + } + *bcap_fmt = *fmt; + } pixfmt->bytesperline = pixfmt->width * fmt->bpp / 8; pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; return 0; @@ -707,7 +790,7 @@ static int bcap_try_fmt_vid_cap(struct file *file, void *priv, struct bcap_device *bcap_dev = video_drvdata(file); struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; - return bcap_try_format(bcap_dev, pixfmt, NULL, NULL); + return bcap_try_format(bcap_dev, pixfmt, NULL); } static int bcap_g_fmt_vid_cap(struct file *file, void *priv, @@ -724,24 +807,25 @@ static int bcap_s_fmt_vid_cap(struct file *file, void *priv, { struct bcap_device *bcap_dev = video_drvdata(file); struct v4l2_mbus_framefmt mbus_fmt; - enum v4l2_mbus_pixelcode mbus_code; + struct bcap_format bcap_fmt; struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; - int ret, bpp; + int ret; if (vb2_is_busy(&bcap_dev->buffer_queue)) return -EBUSY; /* see if format works */ - ret = bcap_try_format(bcap_dev, pixfmt, &mbus_code, &bpp); + ret = bcap_try_format(bcap_dev, pixfmt, &bcap_fmt); if (ret < 0) return ret; - v4l2_fill_mbus_format(&mbus_fmt, pixfmt, mbus_code); + v4l2_fill_mbus_format(&mbus_fmt, pixfmt, bcap_fmt.mbus_code); ret = v4l2_subdev_call(bcap_dev->sd, video, s_mbus_fmt, &mbus_fmt); if (ret < 0) return ret; bcap_dev->fmt = *pixfmt; - bcap_dev->bpp = bpp; + bcap_dev->bpp = bcap_fmt.bpp; + bcap_dev->dlen = bcap_fmt.dlen; return 0; } @@ -832,6 +916,8 @@ static const struct v4l2_ioctl_ops bcap_ioctl_ops = { .vidioc_querystd = bcap_querystd, .vidioc_s_std = bcap_s_std, .vidioc_g_std = bcap_g_std, + .vidioc_s_dv_timings = bcap_s_dv_timings, + .vidioc_g_dv_timings = bcap_g_dv_timings, .vidioc_reqbufs = bcap_reqbufs, .vidioc_querybuf = bcap_querybuf, .vidioc_qbuf = bcap_qbuf, @@ -867,6 +953,7 @@ static int __devinit bcap_probe(struct platform_device *pdev) struct i2c_adapter *i2c_adap; struct bfin_capture_config *config; struct vb2_queue *q; + struct bcap_route *route; int ret; config = pdev->dev.platform_data; @@ -976,6 +1063,12 @@ static int __devinit bcap_probe(struct platform_device *pdev) NULL); if (bcap_dev->sd) { int i; + if (!config->num_inputs) { + v4l2_err(&bcap_dev->v4l2_dev, + "Unable to work without input\n"); + goto err_unreg_vdev; + } + /* update tvnorms from the sub devices */ for (i = 0; i < config->num_inputs; i++) vfd->tvnorms |= config->inputs[i].std; @@ -987,8 +1080,24 @@ static int __devinit bcap_probe(struct platform_device *pdev) v4l2_info(&bcap_dev->v4l2_dev, "v4l2 sub device registered\n"); + /* + * explicitly set input, otherwise some boards + * may not work at the state as we expected + */ + route = &config->routes[0]; + ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing, + route->input, route->output, 0); + if ((ret < 0) && (ret != -ENOIOCTLCMD)) { + v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n"); + goto err_unreg_vdev; + } + bcap_dev->cur_input = 0; + /* if this route has specific config, update ppi control */ + if (route->ppi_control) + config->ppi_control = route->ppi_control; + /* now we can probe the default state */ - if (vfd->tvnorms) { + if (config->inputs[0].capabilities & V4L2_IN_CAP_STD) { v4l2_std_id std; ret = v4l2_subdev_call(bcap_dev->sd, core, g_std, &std); if (ret) { @@ -998,6 +1107,17 @@ static int __devinit bcap_probe(struct platform_device *pdev) } bcap_dev->std = std; } + if (config->inputs[0].capabilities & V4L2_IN_CAP_CUSTOM_TIMINGS) { + struct v4l2_dv_timings dv_timings; + ret = v4l2_subdev_call(bcap_dev->sd, video, + g_dv_timings, &dv_timings); + if (ret) { + v4l2_err(&bcap_dev->v4l2_dev, + "Unable to get dv timings\n"); + goto err_unreg_vdev; + } + bcap_dev->dv_timings = dv_timings; + } ret = bcap_init_sensor_formats(bcap_dev); if (ret) { v4l2_err(&bcap_dev->v4l2_dev, diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c index 9374d676f63d..1e24584605f2 100644 --- a/drivers/media/platform/blackfin/ppi.c +++ b/drivers/media/platform/blackfin/ppi.c @@ -68,6 +68,13 @@ static irqreturn_t ppi_irq_err(int irq, void *dev_id) bfin_write16(®->status, 0xffff); break; } + case PPI_TYPE_EPPI3: + { + struct bfin_eppi3_regs *reg = info->base; + + bfin_write32(®->stat, 0xc0ff); + break; + } default: break; } @@ -129,6 +136,12 @@ static int ppi_start(struct ppi_if *ppi) bfin_write32(®->control, ppi->ppi_control); break; } + case PPI_TYPE_EPPI3: + { + struct bfin_eppi3_regs *reg = info->base; + bfin_write32(®->ctl, ppi->ppi_control); + break; + } default: return -EINVAL; } @@ -156,6 +169,12 @@ static int ppi_stop(struct ppi_if *ppi) bfin_write32(®->control, ppi->ppi_control); break; } + case PPI_TYPE_EPPI3: + { + struct bfin_eppi3_regs *reg = info->base; + bfin_write32(®->ctl, ppi->ppi_control); + break; + } default: return -EINVAL; } @@ -172,17 +191,23 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) { const struct ppi_info *info = ppi->info; int dma32 = 0; - int dma_config, bytes_per_line, lines_per_frame; + int dma_config, bytes_per_line; + int hcount, hdelay, samples_per_line; bytes_per_line = params->width * params->bpp / 8; - lines_per_frame = params->height; + /* convert parameters unit from pixels to samples */ + hcount = params->width * params->bpp / params->dlen; + hdelay = params->hdelay * params->bpp / params->dlen; + samples_per_line = params->line * params->bpp / params->dlen; if (params->int_mask == 0xFFFFFFFF) ppi->err_int = false; else ppi->err_int = true; - dma_config = (DMA_FLOW_STOP | WNR | RESTART | DMA2D | DI_EN); + dma_config = (DMA_FLOW_STOP | RESTART | DMA2D | DI_EN_Y); ppi->ppi_control = params->ppi_control & ~PORT_EN; + if (!(ppi->ppi_control & PORT_DIR)) + dma_config |= WNR; switch (info->type) { case PPI_TYPE_PPI: { @@ -192,8 +217,8 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) dma32 = 1; bfin_write16(®->control, ppi->ppi_control); - bfin_write16(®->count, bytes_per_line - 1); - bfin_write16(®->frame, lines_per_frame); + bfin_write16(®->count, samples_per_line - 1); + bfin_write16(®->frame, params->frame); break; } case PPI_TYPE_EPPI: @@ -205,12 +230,31 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) dma32 = 1; bfin_write32(®->control, ppi->ppi_control); - bfin_write16(®->line, bytes_per_line + params->blank_clocks); - bfin_write16(®->frame, lines_per_frame); - bfin_write16(®->hdelay, 0); - bfin_write16(®->vdelay, 0); - bfin_write16(®->hcount, bytes_per_line); - bfin_write16(®->vcount, lines_per_frame); + bfin_write16(®->line, samples_per_line); + bfin_write16(®->frame, params->frame); + bfin_write16(®->hdelay, hdelay); + bfin_write16(®->vdelay, params->vdelay); + bfin_write16(®->hcount, hcount); + bfin_write16(®->vcount, params->height); + break; + } + case PPI_TYPE_EPPI3: + { + struct bfin_eppi3_regs *reg = info->base; + + if ((params->ppi_control & PACK_EN) + || (params->ppi_control & 0x70000) > DLEN_16) + dma32 = 1; + + bfin_write32(®->ctl, ppi->ppi_control); + bfin_write32(®->line, samples_per_line); + bfin_write32(®->frame, params->frame); + bfin_write32(®->hdly, hdelay); + bfin_write32(®->vdly, params->vdelay); + bfin_write32(®->hcnt, hcount); + bfin_write32(®->vcnt, params->height); + if (params->int_mask) + bfin_write32(®->imsk, params->int_mask & 0xFF); break; } default: @@ -218,17 +262,17 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) } if (dma32) { - dma_config |= WDSIZE_32; + dma_config |= WDSIZE_32 | PSIZE_32; set_dma_x_count(info->dma_ch, bytes_per_line >> 2); set_dma_x_modify(info->dma_ch, 4); set_dma_y_modify(info->dma_ch, 4); } else { - dma_config |= WDSIZE_16; + dma_config |= WDSIZE_16 | PSIZE_16; set_dma_x_count(info->dma_ch, bytes_per_line >> 1); set_dma_x_modify(info->dma_ch, 2); set_dma_y_modify(info->dma_ch, 2); } - set_dma_y_count(info->dma_ch, lines_per_frame); + set_dma_y_count(info->dma_ch, params->height); set_dma_config(info->dma_ch, dma_config); SSYNC(); diff --git a/include/media/blackfin/bfin_capture.h b/include/media/blackfin/bfin_capture.h index 2038a8a3f8aa..56b9ce4472fc 100644 --- a/include/media/blackfin/bfin_capture.h +++ b/include/media/blackfin/bfin_capture.h @@ -9,6 +9,7 @@ struct ppi_info; struct bcap_route { u32 input; u32 output; + u32 ppi_control; }; struct bfin_capture_config { @@ -30,8 +31,8 @@ struct bfin_capture_config { unsigned long ppi_control; /* ppi interrupt mask */ u32 int_mask; - /* horizontal blanking clocks */ - int blank_clocks; + /* horizontal blanking pixels */ + int blank_pixels; }; #endif diff --git a/include/media/blackfin/ppi.h b/include/media/blackfin/ppi.h index 8f72f8a0b3d0..65c467576b31 100644 --- a/include/media/blackfin/ppi.h +++ b/include/media/blackfin/ppi.h @@ -21,22 +21,42 @@ #define _PPI_H_ #include +#include +#include +/* EPPI */ #ifdef EPPI_EN #define PORT_EN EPPI_EN +#define PORT_DIR EPPI_DIR #define DMA32 0 #define PACK_EN PACKEN #endif +/* EPPI3 */ +#ifdef EPPI0_CTL2 +#define PORT_EN EPPI_CTL_EN +#define PORT_DIR EPPI_CTL_DIR +#define PACK_EN EPPI_CTL_PACKEN +#define DMA32 0 +#define DLEN_8 EPPI_CTL_DLEN08 +#define DLEN_16 EPPI_CTL_DLEN16 +#endif + struct ppi_if; struct ppi_params { - int width; - int height; - int bpp; - unsigned long ppi_control; - u32 int_mask; - int blank_clocks; + u32 width; /* width in pixels */ + u32 height; /* height in lines */ + u32 hdelay; /* delay after the HSYNC in pixels */ + u32 vdelay; /* delay after the VSYNC in lines */ + u32 line; /* total pixels per line */ + u32 frame; /* total lines per frame */ + u32 hsync; /* HSYNC length in pixels */ + u32 vsync; /* VSYNC length in lines */ + int bpp; /* bits per pixel */ + int dlen; /* data length for ppi in bits */ + u32 ppi_control; /* ppi configuration */ + u32 int_mask; /* interrupt mask */ }; struct ppi_ops { @@ -51,6 +71,7 @@ struct ppi_ops { enum ppi_type { PPI_TYPE_PPI, PPI_TYPE_EPPI, + PPI_TYPE_EPPI3, }; struct ppi_info { -- cgit v1.2.3 From 30ebc5e44d057a1619ad63fe32c8c1670c37c4b8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 27 Nov 2012 13:35:09 -0300 Subject: [media] rc: unlock on error in show_protocols() We recently introduced a new return -ENODEV in this function but we need to unlock before returning. [mchehab@redhat.com: found two patches with the same fix. Merged SOB's/acks into one patch] Acked-by: Herton R. Krzesinski Signed-off-by: Dan Carpenter Cc: stable@vger.kernel.org Signed-off-by: Douglas Bagnall Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/rc-main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 601d1ac1c688..d593bc65b4ca 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -789,8 +789,10 @@ static ssize_t show_protocols(struct device *device, } else if (dev->raw) { enabled = dev->raw->enabled_protocols; allowed = ir_raw_get_allowed_protocols(); - } else + } else { + mutex_unlock(&dev->lock); return -ENODEV; + } IR_dprintk(1, "allowed - 0x%llx, enabled - 0x%llx\n", (long long)allowed, -- cgit v1.2.3 From afe5624b142279c6072ce1872811e309ad7e94be Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 27 Nov 2012 13:35:30 -0300 Subject: [media] rc: unlock on error in store_protocols() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This error path is missing the unlock. [mchehab@redhat.com: Merged two equal patches into one] Signed-off-by: Sasha Levin Acked-by: David Härdeman Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/rc-main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index d593bc65b4ca..759a40a42eaa 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -892,7 +892,8 @@ static ssize_t store_protocols(struct device *device, if (i == ARRAY_SIZE(proto_names)) { IR_dprintk(1, "Unknown protocol: '%s'\n", tmp); - return -EINVAL; + ret = -EINVAL; + goto out; } count++; -- cgit v1.2.3 From 68c97bf39ad853063876f4a8449009c1620d972a Mon Sep 17 00:00:00 2001 From: Alf Høgemark Date: Wed, 28 Nov 2012 14:29:16 -0300 Subject: [media] cx231xx : Add support for Elgato Video Capture V2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds support for the Elgato Video Capture, version 2, device. The device is added based on the code for CX231XX_BOARD_HAUPPAUGE_USBLIVE2, it is simply a copy of the code for that board, with the proper USB device info for the Elgato Video Capture V2 device. Signed-off-by: Alf Høgemark Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/cx231xx-cards.c | 29 +++++++++++++++++++++++++++++ drivers/media/usb/cx231xx/cx231xx.h | 1 + 2 files changed, 30 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index bbed1e40eeda..94b573ad96d5 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -603,6 +603,33 @@ struct cx231xx_board cx231xx_boards[] = { .gpio = NULL, } }, }, + [CX231XX_BOARD_ELGATO_VIDEO_CAPTURE_V2] = { + .name = "Elgato Video Capture V2", + .tuner_type = TUNER_ABSENT, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .norm = V4L2_STD_NTSC, + .no_alt_vanc = 1, + .external_av = 1, + .dont_use_port_3 = 1, + .input = {{ + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } }, + }, }; const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards); @@ -642,6 +669,8 @@ struct usb_device_id cx231xx_id_table[] = { .driver_info = CX231XX_BOARD_KWORLD_UB430_USB_HYBRID}, {USB_DEVICE(0x1f4d, 0x0237), .driver_info = CX231XX_BOARD_ICONBIT_U100}, + {USB_DEVICE(0x0fd9, 0x0037), + .driver_info = CX231XX_BOARD_ELGATO_VIDEO_CAPTURE_V2}, {}, }; diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h index a89d020de948..3e11462be0d0 100644 --- a/drivers/media/usb/cx231xx/cx231xx.h +++ b/drivers/media/usb/cx231xx/cx231xx.h @@ -68,6 +68,7 @@ #define CX231XX_BOARD_ICONBIT_U100 13 #define CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL 14 #define CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC 15 +#define CX231XX_BOARD_ELGATO_VIDEO_CAPTURE_V2 16 /* Limits minimum and default number of buffers */ #define CX231XX_MIN_BUF 4 -- cgit v1.2.3 From 5d92bbe634cc9d768db2b88ca7c303e6799ed5c0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Dec 2012 14:43:41 -0200 Subject: [media] ttpci: Fix a missing Kconfig dependency Fix a Kconfig warning that appears with allmodconfig on arm: warning: (DVB_USB_PCTV452E) selects TTPCI_EEPROM which has unmet direct dependencies (MEDIA_SUPPORT && MEDIA_PCI_SUPPORT && MEDIA_DIGITAL_TV_SUPPORT && I2C) Signed-off-by: Mauro Carvalho Chehab --- drivers/media/Kconfig | 6 ++++++ drivers/media/pci/ttpci/Kconfig | 5 ----- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 4ef0d80b57f4..4d9b4c21c199 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -135,6 +135,12 @@ config DVB_NET You may want to disable the network support on embedded devices. If unsure say Y. +# This Kconfig option is used by both PCI and USB drivers +config TTPCI_EEPROM + tristate + depends on I2C + default n + source "drivers/media/dvb-core/Kconfig" comment "Media drivers" diff --git a/drivers/media/pci/ttpci/Kconfig b/drivers/media/pci/ttpci/Kconfig index 314e417addae..0dcb8cd77676 100644 --- a/drivers/media/pci/ttpci/Kconfig +++ b/drivers/media/pci/ttpci/Kconfig @@ -1,8 +1,3 @@ -config TTPCI_EEPROM - tristate - depends on I2C - default n - config DVB_AV7110 tristate "AV7110 cards" depends on DVB_CORE && PCI && I2C -- cgit v1.2.3 From 26711113c4e4e72a72d347bfc33590595f248df7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Dec 2012 14:46:55 -0200 Subject: [media] omap: Fix Kconfig dependencies on OMAP2 Resolves the following warning that appears with allmodconfig on -arm: warning: (VIDEO_OMAP2_VOUT && DRM_OMAP) selects OMAP2_DSS which has unmet direct dependencies (HAS_IOMEM && ARCH_OMAP2PLUS) Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/omap/Kconfig b/drivers/media/platform/omap/Kconfig index 390ab094f9f2..37ad446b35b3 100644 --- a/drivers/media/platform/omap/Kconfig +++ b/drivers/media/platform/omap/Kconfig @@ -6,7 +6,7 @@ config VIDEO_OMAP2_VOUT depends on ARCH_OMAP2 || ARCH_OMAP3 select VIDEOBUF_GEN select VIDEOBUF_DMA_CONTIG - select OMAP2_DSS + select OMAP2_DSS if HAS_IOMEM && ARCH_OMAP2PLUS select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 select VIDEO_OMAP2_VOUT_VRFB if VIDEO_OMAP2_VOUT && OMAP2_VRFB default n -- cgit v1.2.3 From 9d193b758edaad192d05ebcb8dc4cb72711bf618 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 29 Nov 2012 16:45:07 -0300 Subject: [media] s5p-fimc: convert struct spinlock to spinlock_t spinlock_t should always be used. Could not get this to build with allmodconfig: mcgrof@frijol ~/linux-next (git::(no branch))$ make C=1 M=drivers/media/platform/s5p-fimc/ WARNING: Symbol version dump /home/mcgrof/linux-next/Module.symvers is missing; modules will have no dependencies and modversions. LD drivers/media/platform/s5p-fimc/built-in.o Building modules, stage 2. MODPOST 0 modules Reported-by: Hauke Mehrtens Signed-off-by: Luis R. Rodriguez Cc: Kyungmin Park Cc: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/mipi-csis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index 8a06f1402f37..076c29bd41c8 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -187,7 +187,7 @@ struct csis_state { const struct csis_pix_format *csis_fmt; struct v4l2_mbus_framefmt format; - struct spinlock slock; + spinlock_t slock; struct csis_pktbuf pkt_buf; struct s5pcsis_event events[S5PCSIS_NUM_EVENTS]; }; -- cgit v1.2.3 From a75831f3600c479054fc3f70cd11257ab07886e2 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 29 Nov 2012 16:45:08 -0300 Subject: [media] s5p-jpeg: convert struct spinlock to spinlock_t spinlock_t should always be used. Could not get this to build with allmodconfig: mcgrof@frijol ~/linux-next (git::(no branch))$ make C=1 M=drivers/media/platform/s5p-jpeg WARNING: Symbol version dump /home/mcgrof/linux-next/Module.symvers is missing; modules will have no dependencies and modversions. Building modules, stage 2. MODPOST 0 modules Reported-by: Hauke Mehrtens Signed-off-by: Luis R. Rodriguez Cc: Kyungmin Park Cc: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-jpeg/jpeg-core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h index 022b9b9baff9..8a4013e3aee7 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.h @@ -62,7 +62,7 @@ */ struct s5p_jpeg { struct mutex lock; - struct spinlock slock; + spinlock_t slock; struct v4l2_device v4l2_dev; struct video_device *vfd_encoder; -- cgit v1.2.3 From 6ae23224557d797439d02f6ce5d10a82ab544b21 Mon Sep 17 00:00:00 2001 From: Juergen Lock Date: Sun, 23 Dec 2012 17:23:06 -0300 Subject: [media] dvb_frontend: fix ioctls failing if frontend open/closed too fast That likely fixes this MythTV ticket: http://code.mythtv.org/trac/ticket/10830 (which btw affects all usb tuners I tested as well, pctv452e, dib0700, af9015) pctv452e is still possibly broken with MythTV even after this fix; it does work with VDR here tho despite I2C errors. Reduced testcase: http://people.freebsd.org/~nox/tmp/ioctltst.c Thanx to devinheitmueller and crope from #linuxtv for helping with this fix! :) Signed-off-by: Juergen Lock Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_frontend.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 49d95040096a..9b4a47c104a1 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -603,6 +603,7 @@ static int dvb_frontend_thread(void *data) enum dvbfe_algo algo; bool re_tune = false; + bool semheld = false; dev_dbg(fe->dvb->device, "%s:\n", __func__); @@ -626,6 +627,8 @@ restart: if (kthread_should_stop() || dvb_frontend_is_exiting(fe)) { /* got signal or quitting */ + if (!down_interruptible(&fepriv->sem)) + semheld = true; fepriv->exit = DVB_FE_NORMAL_EXIT; break; } @@ -741,6 +744,8 @@ restart: fepriv->exit = DVB_FE_NO_EXIT; mb(); + if (semheld) + up(&fepriv->sem); dvb_frontend_wakeup(fe); return 0; } @@ -1823,16 +1828,20 @@ static int dvb_frontend_ioctl(struct file *file, int err = -ENOTTY; dev_dbg(fe->dvb->device, "%s: (%d)\n", __func__, _IOC_NR(cmd)); - if (fepriv->exit != DVB_FE_NO_EXIT) + if (down_interruptible(&fepriv->sem)) + return -ERESTARTSYS; + + if (fepriv->exit != DVB_FE_NO_EXIT) { + up(&fepriv->sem); return -ENODEV; + } if ((file->f_flags & O_ACCMODE) == O_RDONLY && (_IOC_DIR(cmd) != _IOC_READ || cmd == FE_GET_EVENT || - cmd == FE_DISEQC_RECV_SLAVE_REPLY)) + cmd == FE_DISEQC_RECV_SLAVE_REPLY)) { + up(&fepriv->sem); return -EPERM; - - if (down_interruptible (&fepriv->sem)) - return -ERESTARTSYS; + } if ((cmd == FE_SET_PROPERTY) || (cmd == FE_GET_PROPERTY)) err = dvb_frontend_ioctl_properties(file, cmd, parg); -- cgit v1.2.3 From 30ad64b8ac539459f8975aa186421ef3db0bb5cb Mon Sep 17 00:00:00 2001 From: Nikolaus Schulz Date: Sun, 23 Dec 2012 18:49:07 -0300 Subject: [media] dvb: push down ioctl lock in dvb_usercopy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since most dvb ioctls wrap their real work with dvb_usercopy, the static mutex used in dvb_usercopy effectively is a global lock for dvb ioctls. Unfortunately, frontend ioctls can be blocked by the frontend thread for several seconds; this leads to unacceptable lock contention. Mitigate that by pushing the mutex from dvb_usercopy down to the individual, device specific ioctls. There are 10 such ioctl functions using dvb_usercopy, either calling it directly, or via the trivial wrapper dvb_generic_ioctl. The following already employ their own locking and look safe: • dvb_demux_ioctl (as per dvb_demux_do_ioctl) • dvb_dvr_ioctl (as per dvb_dvr_do_ioctl) • dvb_osd_ioctl (as per single non-trivial callee) • fdtv_ca_ioctl (as per callees) • dvb_frontend_ioctl The following functions do not, and are thus changed to use a device specific mutex: • dvb_net_ioctl (as per dvb_net_do_ioctl) • dvb_ca_en50221_io_ioctl (as per dvb_ca_en50221_io_do_ioctl) • dvb_video_ioctl • dvb_audio_ioctl • dvb_ca_ioctl Signed-off-by: Nikolaus Schulz Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_ca_en50221.c | 9 +++++ drivers/media/dvb-core/dvb_net.c | 71 ++++++++++++++++++++++----------- drivers/media/dvb-core/dvb_net.h | 1 + drivers/media/dvb-core/dvbdev.c | 2 - drivers/media/pci/ttpci/av7110.c | 2 + drivers/media/pci/ttpci/av7110.h | 2 + drivers/media/pci/ttpci/av7110_av.c | 8 ++++ drivers/media/pci/ttpci/av7110_ca.c | 24 +++++++---- 8 files changed, 87 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index 9be65a3b931f..190e5e0f48c7 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -156,6 +156,9 @@ struct dvb_ca_private { /* Slot to start looking for data to read from in the next user-space read operation */ int next_read_slot; + + /* mutex serializing ioctls */ + struct mutex ioctl_mutex; }; static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca); @@ -1191,6 +1194,9 @@ static int dvb_ca_en50221_io_do_ioctl(struct file *file, dprintk("%s\n", __func__); + if (mutex_lock_interruptible(&ca->ioctl_mutex)) + return -ERESTARTSYS; + switch (cmd) { case CA_RESET: for (slot = 0; slot < ca->slot_count; slot++) { @@ -1241,6 +1247,7 @@ static int dvb_ca_en50221_io_do_ioctl(struct file *file, break; } + mutex_unlock(&ca->ioctl_mutex); return err; } @@ -1695,6 +1702,8 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, mutex_init(&ca->slot_info[i].slot_lock); } + mutex_init(&ca->ioctl_mutex); + if (signal_pending(current)) { ret = -EINTR; goto error; diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c index c2117688aa23..44225b186f6d 100644 --- a/drivers/media/dvb-core/dvb_net.c +++ b/drivers/media/dvb-core/dvb_net.c @@ -1345,26 +1345,35 @@ static int dvb_net_do_ioctl(struct file *file, { struct dvb_device *dvbdev = file->private_data; struct dvb_net *dvbnet = dvbdev->priv; + int ret = 0; if (((file->f_flags&O_ACCMODE)==O_RDONLY)) return -EPERM; + if (mutex_lock_interruptible(&dvbnet->ioctl_mutex)) + return -ERESTARTSYS; + switch (cmd) { case NET_ADD_IF: { struct dvb_net_if *dvbnetif = parg; int result; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; + if (!capable(CAP_SYS_ADMIN)) { + ret = -EPERM; + goto ioctl_error; + } - if (!try_module_get(dvbdev->adapter->module)) - return -EPERM; + if (!try_module_get(dvbdev->adapter->module)) { + ret = -EPERM; + goto ioctl_error; + } result=dvb_net_add_if(dvbnet, dvbnetif->pid, dvbnetif->feedtype); if (result<0) { module_put(dvbdev->adapter->module); - return result; + ret = result; + goto ioctl_error; } dvbnetif->if_num=result; break; @@ -1376,8 +1385,10 @@ static int dvb_net_do_ioctl(struct file *file, struct dvb_net_if *dvbnetif = parg; if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX || - !dvbnet->state[dvbnetif->if_num]) - return -EINVAL; + !dvbnet->state[dvbnetif->if_num]) { + ret = -EINVAL; + goto ioctl_error; + } netdev = dvbnet->device[dvbnetif->if_num]; @@ -1388,16 +1399,18 @@ static int dvb_net_do_ioctl(struct file *file, } case NET_REMOVE_IF: { - int ret; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if ((unsigned long) parg >= DVB_NET_DEVICES_MAX) - return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) { + ret = -EPERM; + goto ioctl_error; + } + if ((unsigned long) parg >= DVB_NET_DEVICES_MAX) { + ret = -EINVAL; + goto ioctl_error; + } ret = dvb_net_remove_if(dvbnet, (unsigned long) parg); if (!ret) module_put(dvbdev->adapter->module); - return ret; + break; } /* binary compatibility cruft */ @@ -1406,16 +1419,21 @@ static int dvb_net_do_ioctl(struct file *file, struct __dvb_net_if_old *dvbnetif = parg; int result; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; + if (!capable(CAP_SYS_ADMIN)) { + ret = -EPERM; + goto ioctl_error; + } - if (!try_module_get(dvbdev->adapter->module)) - return -EPERM; + if (!try_module_get(dvbdev->adapter->module)) { + ret = -EPERM; + goto ioctl_error; + } result=dvb_net_add_if(dvbnet, dvbnetif->pid, DVB_NET_FEEDTYPE_MPE); if (result<0) { module_put(dvbdev->adapter->module); - return result; + ret = result; + goto ioctl_error; } dvbnetif->if_num=result; break; @@ -1427,8 +1445,10 @@ static int dvb_net_do_ioctl(struct file *file, struct __dvb_net_if_old *dvbnetif = parg; if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX || - !dvbnet->state[dvbnetif->if_num]) - return -EINVAL; + !dvbnet->state[dvbnetif->if_num]) { + ret = -EINVAL; + goto ioctl_error; + } netdev = dvbnet->device[dvbnetif->if_num]; @@ -1437,9 +1457,13 @@ static int dvb_net_do_ioctl(struct file *file, break; } default: - return -ENOTTY; + ret = -ENOTTY; + break; } - return 0; + +ioctl_error: + mutex_unlock(&dvbnet->ioctl_mutex); + return ret; } static long dvb_net_ioctl(struct file *file, @@ -1505,6 +1529,7 @@ int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet, { int i; + mutex_init(&dvbnet->ioctl_mutex); dvbnet->demux = dmx; for (i=0; iioctl_mutex); + #if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) av7110_ir_init(av7110); #endif diff --git a/drivers/media/pci/ttpci/av7110.h b/drivers/media/pci/ttpci/av7110.h index a378662b1dcf..ef3d9606b269 100644 --- a/drivers/media/pci/ttpci/av7110.h +++ b/drivers/media/pci/ttpci/av7110.h @@ -271,6 +271,8 @@ struct av7110 { struct dvb_frontend* fe; fe_status_t fe_status; + struct mutex ioctl_mutex; + /* crash recovery */ void (*recover)(struct av7110* av7110); fe_sec_voltage_t saved_voltage; diff --git a/drivers/media/pci/ttpci/av7110_av.c b/drivers/media/pci/ttpci/av7110_av.c index 952b33dbac4f..301029ca4535 100644 --- a/drivers/media/pci/ttpci/av7110_av.c +++ b/drivers/media/pci/ttpci/av7110_av.c @@ -1109,6 +1109,9 @@ static int dvb_video_ioctl(struct file *file, } } + if (mutex_lock_interruptible(&av7110->ioctl_mutex)) + return -ERESTARTSYS; + switch (cmd) { case VIDEO_STOP: av7110->videostate.play_state = VIDEO_STOPPED; @@ -1297,6 +1300,7 @@ static int dvb_video_ioctl(struct file *file, break; } + mutex_unlock(&av7110->ioctl_mutex); return ret; } @@ -1314,6 +1318,9 @@ static int dvb_audio_ioctl(struct file *file, (cmd != AUDIO_GET_STATUS)) return -EPERM; + if (mutex_lock_interruptible(&av7110->ioctl_mutex)) + return -ERESTARTSYS; + switch (cmd) { case AUDIO_STOP: if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) @@ -1442,6 +1449,7 @@ static int dvb_audio_ioctl(struct file *file, ret = -ENOIOCTLCMD; } + mutex_unlock(&av7110->ioctl_mutex); return ret; } diff --git a/drivers/media/pci/ttpci/av7110_ca.c b/drivers/media/pci/ttpci/av7110_ca.c index 9fc1dd0ba4c3..a6079b90252a 100644 --- a/drivers/media/pci/ttpci/av7110_ca.c +++ b/drivers/media/pci/ttpci/av7110_ca.c @@ -253,12 +253,17 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) struct dvb_device *dvbdev = file->private_data; struct av7110 *av7110 = dvbdev->priv; unsigned long arg = (unsigned long) parg; + int ret = 0; dprintk(8, "av7110:%p\n",av7110); + if (mutex_lock_interruptible(&av7110->ioctl_mutex)) + return -ERESTARTSYS; + switch (cmd) { case CA_RESET: - return ci_ll_reset(&av7110->ci_wbuffer, file, arg, &av7110->ci_slot[0]); + ret = ci_ll_reset(&av7110->ci_wbuffer, file, arg, + &av7110->ci_slot[0]); break; case CA_GET_CAP: { @@ -277,8 +282,10 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) { ca_slot_info_t *info=(ca_slot_info_t *)parg; - if (info->num < 0 || info->num > 1) + if (info->num < 0 || info->num > 1) { + mutex_unlock(&av7110->ioctl_mutex); return -EINVAL; + } av7110->ci_slot[info->num].num = info->num; av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? CA_CI_LINK : CA_CI; @@ -306,10 +313,10 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) { ca_descr_t *descr = (ca_descr_t*) parg; - if (descr->index >= 16) - return -EINVAL; - if (descr->parity > 1) + if (descr->index >= 16 || descr->parity > 1) { + mutex_unlock(&av7110->ioctl_mutex); return -EINVAL; + } av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetDescr, 5, (descr->index<<8)|descr->parity, (descr->cw[0]<<8)|descr->cw[1], @@ -320,9 +327,12 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) } default: - return -EINVAL; + ret = -EINVAL; + break; } - return 0; + + mutex_unlock(&av7110->ioctl_mutex); + return ret; } static ssize_t dvb_ca_write(struct file *file, const char __user *buf, -- cgit v1.2.3 From e8d4237325a475b02594d1fd85bb67983f7d57b9 Mon Sep 17 00:00:00 2001 From: Oleh Kravchenko Date: Sat, 8 Dec 2012 18:20:59 -0300 Subject: [media] Added support for AVerTV Hybrid Express Slim HC81R This patch provide only analog support. The device is based on AF9013 demodulator, XC3028 tuner and CX23885 chipset; subsystem id: 1461:d939 Signed-off-by: Oleh Kravchenko Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx23885/cx23885-cards.c | 75 +++++++++++++++++++++++++++++++ drivers/media/pci/cx23885/cx23885-video.c | 15 ++++++- drivers/media/pci/cx23885/cx23885.h | 1 + 3 files changed, 90 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index 8d96ae4ebbdd..7e923f8dd2f5 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -577,6 +577,35 @@ struct cx23885_board cx23885_boards[] = { .name = "Hauppauge WinTV-HVR4400", .portb = CX23885_MPEG_DVB, }, + [CX23885_BOARD_AVERMEDIA_HC81R] = { + .name = "AVerTV Hybrid Express Slim HC81R", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, /* 0xc2 >> 1 */ + .tuner_bus = 1, + .porta = CX23885_ANALOG_VIDEO, + .input = {{ + .type = CX23885_VMUX_TELEVISION, + .vmux = CX25840_VIN2_CH1 | + CX25840_VIN5_CH2 | + CX25840_NONE0_CH3 | + CX25840_NONE1_CH3, + .amux = CX25840_AUDIO8, + }, { + .type = CX23885_VMUX_SVIDEO, + .vmux = CX25840_VIN8_CH1 | + CX25840_NONE_CH2 | + CX25840_VIN7_CH3 | + CX25840_SVIDEO_ON, + .amux = CX25840_AUDIO6, + }, { + .type = CX23885_VMUX_COMPONENT, + .vmux = CX25840_VIN1_CH1 | + CX25840_NONE_CH2 | + CX25840_NONE0_CH3 | + CX25840_NONE1_CH3, + .amux = CX25840_AUDIO6, + } }, + } }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -808,6 +837,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x0070, .subdevice = 0xc1f8, .card = CX23885_BOARD_HAUPPAUGE_HVR4400, + }, { + .subvendor = 0x1461, + .subdevice = 0xd939, + .card = CX23885_BOARD_AVERMEDIA_HC81R, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -1032,6 +1065,10 @@ int cx23885_tuner_callback(void *priv, int component, int command, int arg) case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: altera_ci_tuner_reset(dev, port->nr); break; + case CX23885_BOARD_AVERMEDIA_HC81R: + /* XC3028L Reset Command */ + bitmask = 1 << 2; + break; } if (bitmask) { @@ -1331,6 +1368,32 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx23885_gpio_set(dev, GPIO_8); mdelay(100); break; + case CX23885_BOARD_AVERMEDIA_HC81R: + cx_clear(MC417_CTL, 1); + /* GPIO-0,1,2 setup direction as output */ + cx_set(GP0_IO, 0x00070000); + mdelay(10); + /* AF9013 demod reset */ + cx_set(GP0_IO, 0x00010001); + mdelay(10); + cx_clear(GP0_IO, 0x00010001); + mdelay(10); + cx_set(GP0_IO, 0x00010001); + mdelay(10); + /* demod tune? */ + cx_clear(GP0_IO, 0x00030003); + mdelay(10); + cx_set(GP0_IO, 0x00020002); + mdelay(10); + cx_set(GP0_IO, 0x00010001); + mdelay(10); + cx_clear(GP0_IO, 0x00020002); + /* XC3028L tuner reset */ + cx_set(GP0_IO, 0x00040004); + cx_clear(GP0_IO, 0x00040004); + cx_set(GP0_IO, 0x00040004); + mdelay(60); + break; } } @@ -1549,6 +1612,17 @@ void cx23885_card_setup(struct cx23885_dev *dev) } switch (dev->board) { + case CX23885_BOARD_AVERMEDIA_HC81R: + /* Defaults for VID B */ + ts1->gen_ctrl_val = 0x4; /* Parallel */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + /* Defaults for VID C */ + /* DREQ_POL, SMODE, PUNC_CLK, MCLK_POL Serial bus + punc clk */ + ts2->gen_ctrl_val = 0x10e; + ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP: case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ @@ -1675,6 +1749,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_MPX885: case CX23885_BOARD_MYGICA_X8507: case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: + case CX23885_BOARD_AVERMEDIA_HC81R: dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_bus[2].i2c_adap, "cx25840", 0x88 >> 1, NULL); diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 83975313e0f2..0e80ba4ca4dd 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -509,7 +509,8 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) || - (dev->board == CX23885_BOARD_MYGICA_X8507)) { + (dev->board == CX23885_BOARD_MYGICA_X8507) || + (dev->board == CX23885_BOARD_AVERMEDIA_HC81R)) { /* Configure audio routing */ v4l2_subdev_call(dev->sd_cx25840, audio, s_routing, INPUT(input)->amux, 0, 0); @@ -1878,6 +1879,18 @@ int cx23885_video_register(struct cx23885_dev *dev) }; v4l2_subdev_call(sd, tuner, s_config, &cfg); } + + if (dev->board == CX23885_BOARD_AVERMEDIA_HC81R) { + struct xc2028_ctrl ctrl = { + .fname = "xc3028L-v36.fw", + .max_len = 64 + }; + struct v4l2_priv_tun_config cfg = { + .tuner = dev->tuner_type, + .priv = &ctrl + }; + v4l2_subdev_call(sd, tuner, s_config, &cfg); + } } } diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h index 61889b25a2af..59c322d870f2 100644 --- a/drivers/media/pci/cx23885/cx23885.h +++ b/drivers/media/pci/cx23885/cx23885.h @@ -92,6 +92,7 @@ #define CX23885_BOARD_HAUPPAUGE_HVR1255_22111 36 #define CX23885_BOARD_PROF_8000 37 #define CX23885_BOARD_HAUPPAUGE_HVR4400 38 +#define CX23885_BOARD_AVERMEDIA_HC81R 39 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 -- cgit v1.2.3 From 87739868944919beb4e6b3860c74355a114a54a1 Mon Sep 17 00:00:00 2001 From: Simon Farnsworth Date: Mon, 10 Dec 2012 08:35:09 -0300 Subject: [media] saa7134: Add pm_qos_request to fix video corruption The SAA7134 appears to have trouble buffering more than one line of video when doing DMA. Rather than try to fix the driver to cope (as has been done by Andy Walls for the cx18 driver), put in a pm_qos_request to limit deep sleep exit latencies. The visible effect of not having this is that seemingly random lines are only partly transferred - if you feed in a static image, you see a portion of the image "flicker" into place. [mchehab@redhat.com: Fix ABI breakage due to some renames at pm_qos] Signed-off-by: Simon Farnsworth Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/saa7134/saa7134-video.c | 13 +++++++++++++ drivers/media/pci/saa7134/saa7134.h | 2 ++ 2 files changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 3abf52711e13..7c503fb68526 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -2248,6 +2248,17 @@ static int saa7134_streamon(struct file *file, void *priv, if (!res_get(dev, fh, res)) return -EBUSY; + /* The SAA7134 has a 1K FIFO; the datasheet suggests that when + * configured conservatively, there's 22 usec of buffering for video. + * We therefore request a DMA latency of 20 usec, giving us 2 usec of + * margin in case the FIFO is configured differently to the datasheet. + * Unfortunately, I lack register-level documentation to check the + * Linux FIFO setup and confirm the perfect value. + */ + pm_qos_add_request(&fh->qos_request, + PM_QOS_CPU_DMA_LATENCY, + 20); + return videobuf_streamon(saa7134_queue(fh)); } @@ -2259,6 +2270,8 @@ static int saa7134_streamoff(struct file *file, void *priv, struct saa7134_dev *dev = fh->dev; int res = saa7134_resource(fh); + pm_qos_remove_request(&fh->qos_request); + err = videobuf_streamoff(saa7134_queue(fh)); if (err < 0) return err; diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index c24b6512bd8f..0a3feaa8589b 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -29,6 +29,7 @@ #include #include #include +#include #include @@ -469,6 +470,7 @@ struct saa7134_fh { enum v4l2_buf_type type; unsigned int resources; enum v4l2_priority prio; + struct pm_qos_request qos_request; /* video overlay */ struct v4l2_window win; -- cgit v1.2.3 From a214c55121e6746f21a32eea2a06a78a7a2d12ca Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 10 Dec 2012 17:37:09 -0300 Subject: [media] dvb-usb: fix indentation of a for loop Signed-off-by: Antonio Ospite Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/dvb-usb-init.c | 60 ++++++++++++++++---------------- 1 file changed, 30 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb/dvb-usb-init.c b/drivers/media/usb/dvb-usb/dvb-usb-init.c index 169196ec2d4e..1adf325012f7 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-init.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-init.c @@ -38,41 +38,41 @@ static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs) memcpy(&adap->props, &d->props.adapter[n], sizeof(struct dvb_usb_adapter_properties)); - for (o = 0; o < adap->props.num_frontends; o++) { - struct dvb_usb_adapter_fe_properties *props = &adap->props.fe[o]; - /* speed - when running at FULL speed we need a HW PID filter */ - if (d->udev->speed == USB_SPEED_FULL && !(props->caps & DVB_USB_ADAP_HAS_PID_FILTER)) { - err("This USB2.0 device cannot be run on a USB1.1 port. (it lacks a hardware PID filter)"); - return -ENODEV; - } + for (o = 0; o < adap->props.num_frontends; o++) { + struct dvb_usb_adapter_fe_properties *props = &adap->props.fe[o]; + /* speed - when running at FULL speed we need a HW PID filter */ + if (d->udev->speed == USB_SPEED_FULL && !(props->caps & DVB_USB_ADAP_HAS_PID_FILTER)) { + err("This USB2.0 device cannot be run on a USB1.1 port. (it lacks a hardware PID filter)"); + return -ENODEV; + } - if ((d->udev->speed == USB_SPEED_FULL && props->caps & DVB_USB_ADAP_HAS_PID_FILTER) || - (props->caps & DVB_USB_ADAP_NEED_PID_FILTERING)) { - info("will use the device's hardware PID filter (table count: %d).", props->pid_filter_count); - adap->fe_adap[o].pid_filtering = 1; - adap->fe_adap[o].max_feed_count = props->pid_filter_count; - } else { - info("will pass the complete MPEG2 transport stream to the software demuxer."); - adap->fe_adap[o].pid_filtering = 0; - adap->fe_adap[o].max_feed_count = 255; - } + if ((d->udev->speed == USB_SPEED_FULL && props->caps & DVB_USB_ADAP_HAS_PID_FILTER) || + (props->caps & DVB_USB_ADAP_NEED_PID_FILTERING)) { + info("will use the device's hardware PID filter (table count: %d).", props->pid_filter_count); + adap->fe_adap[o].pid_filtering = 1; + adap->fe_adap[o].max_feed_count = props->pid_filter_count; + } else { + info("will pass the complete MPEG2 transport stream to the software demuxer."); + adap->fe_adap[o].pid_filtering = 0; + adap->fe_adap[o].max_feed_count = 255; + } - if (!adap->fe_adap[o].pid_filtering && - dvb_usb_force_pid_filter_usage && - props->caps & DVB_USB_ADAP_HAS_PID_FILTER) { - info("pid filter enabled by module option."); - adap->fe_adap[o].pid_filtering = 1; - adap->fe_adap[o].max_feed_count = props->pid_filter_count; - } + if (!adap->fe_adap[o].pid_filtering && + dvb_usb_force_pid_filter_usage && + props->caps & DVB_USB_ADAP_HAS_PID_FILTER) { + info("pid filter enabled by module option."); + adap->fe_adap[o].pid_filtering = 1; + adap->fe_adap[o].max_feed_count = props->pid_filter_count; + } - if (props->size_of_priv > 0) { - adap->fe_adap[o].priv = kzalloc(props->size_of_priv, GFP_KERNEL); - if (adap->fe_adap[o].priv == NULL) { - err("no memory for priv for adapter %d fe %d.", n, o); - return -ENOMEM; + if (props->size_of_priv > 0) { + adap->fe_adap[o].priv = kzalloc(props->size_of_priv, GFP_KERNEL); + if (adap->fe_adap[o].priv == NULL) { + err("no memory for priv for adapter %d fe %d.", n, o); + return -ENOMEM; + } } } - } if (adap->props.size_of_priv > 0) { adap->priv = kzalloc(adap->props.size_of_priv, GFP_KERNEL); -- cgit v1.2.3 From 908498349562ff6614d570b40e904a20d397dafa Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 10 Dec 2012 17:37:10 -0300 Subject: [media] m920x: fix a typo in a comment Signed-off-by: Antonio Ospite Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/m920x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index 661bb75be955..433696d14f4b 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -591,7 +591,7 @@ static struct m920x_inits tvwalkertwin_rc_init [] = { }; static struct m920x_inits pinnacle310e_init[] = { - /* without these the tuner don't work */ + /* without these the tuner doesn't work */ { 0xff20, 0x9b }, { 0xff22, 0x70 }, -- cgit v1.2.3 From 7543f344e9b06afe86b55a2620f5c11b38bd5642 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 10 Dec 2012 17:37:11 -0300 Subject: [media] m920x: factor out a m920x_write_seq() function This is in preparation for the vp7049 frontend attach function which is going to set a sequence of registers as well. Signed-off-by: Antonio Ospite Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/m920x.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index 433696d14f4b..23416fbb9f8c 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -63,6 +63,21 @@ static inline int m920x_write(struct usb_device *udev, u8 request, return ret; } +static inline int m920x_write_seq(struct usb_device *udev, u8 request, + struct m920x_inits *seq) +{ + int ret; + while (seq->address) { + ret = m920x_write(udev, request, seq->data, seq->address); + if (ret != 0) + return ret; + + seq++; + } + + return ret; +} + static int m920x_init(struct dvb_usb_device *d, struct m920x_inits *rc_seq) { int ret = 0, i, epi, flags = 0; @@ -71,15 +86,10 @@ static int m920x_init(struct dvb_usb_device *d, struct m920x_inits *rc_seq) /* Remote controller init. */ if (d->props.rc.legacy.rc_query) { deb("Initialising remote control\n"); - while (rc_seq->address) { - if ((ret = m920x_write(d->udev, M9206_CORE, - rc_seq->data, - rc_seq->address)) != 0) { - deb("Initialising remote control failed\n"); - return ret; - } - - rc_seq++; + ret = m920x_write_seq(d->udev, M9206_CORE, rc_seq); + if (ret != 0) { + deb("Initialising remote control failed\n"); + return ret; } deb("Initialising remote control success\n"); -- cgit v1.2.3 From f526e9e1dcb65c8967c61bbfa72933f4553958ee Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 10 Dec 2012 17:37:12 -0300 Subject: [media] m920x: factor out a m920x_parse_rc_state() function This is in preparation to using RC core infrastructure for some devices, the RC button state parsing logic can be shared berween rc.legacy and rc.core callbacks as it is independent from the mechanism used for RC handling. Signed-off-by: Antonio Ospite Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/m920x.c | 81 +++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index 23416fbb9f8c..581c5deffb6e 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -140,9 +140,50 @@ static int m920x_init_ep(struct usb_interface *intf) alt->desc.bAlternateSetting); } -static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +static inline void m920x_parse_rc_state(struct dvb_usb_device *d, u8 rc_state, + int *state) { struct m920x_state *m = d->priv; + + switch (rc_state) { + case 0x80: + *state = REMOTE_NO_KEY_PRESSED; + break; + + case 0x88: /* framing error or "invalid code" */ + case 0x99: + case 0xc0: + case 0xd8: + *state = REMOTE_NO_KEY_PRESSED; + m->rep_count = 0; + break; + + case 0x93: + case 0x92: + case 0x83: /* pinnacle PCTV310e */ + case 0x82: + m->rep_count = 0; + *state = REMOTE_KEY_PRESSED; + break; + + case 0x91: + case 0x81: /* pinnacle PCTV310e */ + /* prevent immediate auto-repeat */ + if (++m->rep_count > 2) + *state = REMOTE_KEY_REPEAT; + else + *state = REMOTE_NO_KEY_PRESSED; + break; + + default: + deb("Unexpected rc state %02x\n", rc_state); + *state = REMOTE_NO_KEY_PRESSED; + break; + } +} + +static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ int i, ret = 0; u8 *rc_state; @@ -159,42 +200,8 @@ static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) if (rc5_data(&d->props.rc.legacy.rc_map_table[i]) == rc_state[1]) { *event = d->props.rc.legacy.rc_map_table[i].keycode; - - switch(rc_state[0]) { - case 0x80: - *state = REMOTE_NO_KEY_PRESSED; - goto out; - - case 0x88: /* framing error or "invalid code" */ - case 0x99: - case 0xc0: - case 0xd8: - *state = REMOTE_NO_KEY_PRESSED; - m->rep_count = 0; - goto out; - - case 0x93: - case 0x92: - case 0x83: /* pinnacle PCTV310e */ - case 0x82: - m->rep_count = 0; - *state = REMOTE_KEY_PRESSED; - goto out; - - case 0x91: - case 0x81: /* pinnacle PCTV310e */ - /* prevent immediate auto-repeat */ - if (++m->rep_count > 2) - *state = REMOTE_KEY_REPEAT; - else - *state = REMOTE_NO_KEY_PRESSED; - goto out; - - default: - deb("Unexpected rc state %02x\n", rc_state[0]); - *state = REMOTE_NO_KEY_PRESSED; - goto out; - } + m920x_parse_rc_state(d, rc_state[0], state); + goto out; } if (rc_state[1] != 0) -- cgit v1.2.3 From 7a7ef4657e84b5038eace08a1db5b480854c893e Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 10 Dec 2012 17:37:13 -0300 Subject: [media] m920x: avoid repeating RC state parsing at each keycode Parsing the RC press state is invariant wrt. the keycode, take it out of the keycode scanning loop. Signed-off-by: Antonio Ospite Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/m920x.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index 581c5deffb6e..5f6ca753e293 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -197,10 +197,11 @@ static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) if ((ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_KEY, rc_state + 1, 1)) != 0) goto out; + m920x_parse_rc_state(d, rc_state[0], state); + for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) if (rc5_data(&d->props.rc.legacy.rc_map_table[i]) == rc_state[1]) { *event = d->props.rc.legacy.rc_map_table[i].keycode; - m920x_parse_rc_state(d, rc_state[0], state); goto out; } -- cgit v1.2.3 From b677757cef208203426aa8486a91214809135e01 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 10 Dec 2012 17:37:14 -0300 Subject: [media] m920x: introduce m920x_rc_core_query() Add an m920x_rc_core_query() function for drivers which want to use the linux RC core infrastructure. Signed-off-by: Antonio Ospite Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/m920x.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index 5f6ca753e293..bddd7637fdaa 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -215,6 +215,38 @@ static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) return ret; } +static int m920x_rc_core_query(struct dvb_usb_device *d) +{ + int ret = 0; + u8 *rc_state; + int state; + + rc_state = kmalloc(2, GFP_KERNEL); + if (!rc_state) + return -ENOMEM; + + if ((ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_STATE, &rc_state[0], 1)) != 0) + goto out; + + if ((ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_KEY, &rc_state[1], 1)) != 0) + goto out; + + deb("state=0x%02x keycode=0x%02x\n", rc_state[0], rc_state[1]); + + m920x_parse_rc_state(d, rc_state[0], &state); + + if (state == REMOTE_NO_KEY_PRESSED) + rc_keyup(d->rc_dev); + else if (state == REMOTE_KEY_REPEAT) + rc_repeat(d->rc_dev); + else + rc_keydown(d->rc_dev, rc_state[1], 0); + +out: + kfree(rc_state); + return ret; +} + /* I2C */ static int m920x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { -- cgit v1.2.3 From 9d5394c2a19f8a74cb55ce83027bad1808b373b5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Dec 2012 16:32:58 -0200 Subject: [media] m920x: Fix CodingStyle issues Fix CodingStyle issues introduced by the last patch Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/m920x.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index bddd7637fdaa..a70c5eaf0600 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -191,10 +191,14 @@ static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) if (!rc_state) return -ENOMEM; - if ((ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_STATE, rc_state, 1)) != 0) + ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_STATE, + rc_state, 1); + if (ret != 0) goto out; - if ((ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_KEY, rc_state + 1, 1)) != 0) + ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_KEY, + rc_state + 1, 1); + if (ret != 0) goto out; m920x_parse_rc_state(d, rc_state[0], state); -- cgit v1.2.3 From 68511a7553b773a29808b5f9efe26e89df152c3d Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 10 Dec 2012 17:37:15 -0300 Subject: [media] m920x: send the RC init sequence also when rc.core is used Signed-off-by: Antonio Ospite Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/m920x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index a70c5eaf0600..8888b8c2f8e5 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -84,7 +84,7 @@ static int m920x_init(struct dvb_usb_device *d, struct m920x_inits *rc_seq) int adap_enabled[M9206_MAX_ADAPTERS] = { 0 }; /* Remote controller init. */ - if (d->props.rc.legacy.rc_query) { + if (d->props.rc.legacy.rc_query || d->props.rc.core.rc_query) { deb("Initialising remote control\n"); ret = m920x_write_seq(d->udev, M9206_CORE, rc_seq); if (ret != 0) { -- cgit v1.2.3 From de8ed820fd594a95582562d8f9f68148c972d1a4 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 10 Dec 2012 17:37:17 -0300 Subject: [media] m920x: add support for the VP-7049 Twinhan DVB-T USB Stick This device was originally made by Twinhan/Azurewave[1] and sometimes named DTV-DVB UDTT7049, it could be also found in Italy under the name of Digicom Digitune-S[2], or Think Xtra Hollywood DVB-T USB2.0[3]. Components: Usb bridge: ULi M9206 Frontend: MT352CG Tuner: MT2060F The firmware can be downloaded with: $ ./Documentation/dvb/get_dvb_firmware vp7049 [1] http://www.azurewave.com/Support_Utility_Driver.asp [2] http://www.digicom.it/digisit/driver_link.nsf/driverprodotto?openform&prodotto=DigiTuneS [3] http://www.txitalia.it/prodotto.asp?prodotto=txhollywooddvttv Signed-off-by: Antonio Ospite Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb-usb-ids.h | 1 + drivers/media/usb/dvb-usb/m920x.c | 123 +++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) (limited to 'drivers') diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index 388c2eb4d747..c6720f2991ac 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -172,6 +172,7 @@ #define USB_PID_TWINHAN_VP7045_WARM 0x3206 #define USB_PID_TWINHAN_VP7021_COLD 0x3207 #define USB_PID_TWINHAN_VP7021_WARM 0x3208 +#define USB_PID_TWINHAN_VP7049 0x3219 #define USB_PID_TINYTWIN 0x3226 #define USB_PID_TINYTWIN_2 0xe402 #define USB_PID_TINYTWIN_3 0x9016 diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index 8888b8c2f8e5..92afeb20650f 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -16,6 +16,7 @@ #include "qt1010.h" #include "tda1004x.h" #include "tda827x.h" +#include "mt2060.h" #include #include "tuner-simple.h" @@ -550,6 +551,12 @@ static struct qt1010_config m920x_qt1010_config = { .i2c_address = 0x62 }; +static struct mt2060_config m920x_mt2060_config = { + .i2c_address = 0x60, /* 0xc0 */ + .clock_out = 0, +}; + + /* Callbacks for DVB USB */ static int m920x_mt352_frontend_attach(struct dvb_usb_adapter *adap) { @@ -564,6 +571,37 @@ static int m920x_mt352_frontend_attach(struct dvb_usb_adapter *adap) return 0; } +static int m920x_mt352_frontend_attach_vp7049(struct dvb_usb_adapter *adap) +{ + struct m920x_inits vp7049_fe_init_seq[] = { + /* XXX without these commands the frontend cannot be detected, + * they must be sent BEFORE the frontend is attached */ + { 0xff28, 0x00 }, + { 0xff23, 0x00 }, + { 0xff28, 0x00 }, + { 0xff23, 0x00 }, + { 0xff21, 0x20 }, + { 0xff21, 0x60 }, + { 0xff28, 0x00 }, + { 0xff22, 0x00 }, + { 0xff20, 0x30 }, + { 0xff20, 0x20 }, + { 0xff20, 0x30 }, + { } /* terminating entry */ + }; + int ret; + + deb("%s\n", __func__); + + ret = m920x_write_seq(adap->dev->udev, M9206_CORE, vp7049_fe_init_seq); + if (ret != 0) { + deb("Initialization of vp7049 frontend failed."); + return ret; + } + + return m920x_mt352_frontend_attach(adap); +} + static int m920x_tda10046_08_frontend_attach(struct dvb_usb_adapter *adap) { deb("%s\n",__func__); @@ -628,6 +666,18 @@ static int m920x_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap) return 0; } +static int m920x_mt2060_tuner_attach(struct dvb_usb_adapter *adap) +{ + deb("%s\n", __func__); + + if (dvb_attach(mt2060_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, + &m920x_mt2060_config, 1220) == NULL) + return -ENODEV; + + return 0; +} + + /* device-specific initialization */ static struct m920x_inits megasky_rc_init [] = { { M9206_RC_INIT2, 0xa8 }, @@ -656,6 +706,15 @@ static struct m920x_inits pinnacle310e_init[] = { { } /* terminating entry */ }; +static struct m920x_inits vp7049_rc_init[] = { + { 0xff28, 0x00 }, + { 0xff23, 0x00 }, + { 0xff21, 0x70 }, + { M9206_RC_INIT2, 0x00 }, + { M9206_RC_INIT1, 0xff }, + { } /* terminating entry */ +}; + /* ir keymaps */ static struct rc_map_table rc_map_megasky_table[] = { { 0x0012, KEY_POWER }, @@ -758,6 +817,7 @@ static struct dvb_usb_device_properties digivox_mini_ii_properties; static struct dvb_usb_device_properties tvwalkertwin_properties; static struct dvb_usb_device_properties dposh_properties; static struct dvb_usb_device_properties pinnacle_pctv310e_properties; +static struct dvb_usb_device_properties vp7049_properties; static int m920x_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -810,6 +870,13 @@ static int m920x_probe(struct usb_interface *intf, goto found; } + ret = dvb_usb_device_init(intf, &vp7049_properties, + THIS_MODULE, &d, adapter_nr); + if (ret == 0) { + rc_init_seq = vp7049_rc_init; + goto found; + } + return ret; } else { /* Another interface on a multi-tuner device */ @@ -841,6 +908,7 @@ static struct usb_device_id m920x_table [] = { { USB_DEVICE(USB_VID_DPOSH, USB_PID_DPOSH_M9206_COLD) }, { USB_DEVICE(USB_VID_DPOSH, USB_PID_DPOSH_M9206_WARM) }, { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_PINNACLE_PCTV310E) }, + { USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_TWINHAN_VP7049) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, m920x_table); @@ -1133,6 +1201,61 @@ static struct dvb_usb_device_properties pinnacle_pctv310e_properties = { } }; +static struct dvb_usb_device_properties vp7049_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-vp7049-0.95.fw", + .download_firmware = m920x_firmware_download, + + .rc.core = { + .rc_interval = 150, + .rc_codes = RC_MAP_TWINHAN_VP1027_DVBS, + .rc_query = m920x_rc_core_query, + .allowed_protos = RC_TYPE_UNKNOWN, + }, + + .size_of_priv = sizeof(struct m920x_state), + + .identify_state = m920x_identify_state, + .num_adapters = 1, + .adapter = {{ + .num_frontends = 1, + .fe = {{ + + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + + .pid_filter_count = 8, + .pid_filter = m920x_pid_filter, + .pid_filter_ctrl = m920x_pid_filter_ctrl, + + .frontend_attach = m920x_mt352_frontend_attach_vp7049, + .tuner_attach = m920x_mt2060_tuner_attach, + + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x81, + .u = { + .bulk = { + .buffersize = 512, + } + } + }, + } }, + } }, + .i2c_algo = &m920x_i2c_algo, + + .num_device_descs = 1, + .devices = { + { "DTV-DVB UDTT7049", + { &m920x_table[7], NULL }, + { NULL }, + } + } +}; + static struct usb_driver m920x_driver = { .name = "dvb_usb_m920x", .probe = m920x_probe, -- cgit v1.2.3 From 21a73397c0cea688a37c532d1029fc8ecbd88fc6 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Mon, 10 Dec 2012 23:05:28 -0300 Subject: [media] media: saa7146: don't use mutex_lock_interruptible() in device_release() Use uninterruptible mutex_lock in the release() file op to make sure all resources are properly freed when a process is being terminated. Returning -ERESTARTSYS has no effect for a terminating process and this may cause driver resources not to be released. This was found using the following semantic patch (http://coccinelle.lip6.fr/): @r@ identifier fops; identifier release_func; @@ static const struct v4l2_file_operations fops = { .release = release_func }; @depends on r@ identifier r.release_func; expression E; @@ static int release_func(...) { ... - if (mutex_lock_interruptible(E)) return -ERESTARTSYS; + mutex_lock(E); ... } Signed-off-by: Cyril Roelandt Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146/saa7146_fops.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c index 2652f9155c34..eda01bc68ab2 100644 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -265,8 +265,7 @@ static int fops_release(struct file *file) DEB_EE("file:%p\n", file); - if (mutex_lock_interruptible(vdev->lock)) - return -ERESTARTSYS; + mutex_lock(vdev->lock); if (vdev->vfl_type == VFL_TYPE_VBI) { if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) -- cgit v1.2.3 From f7c3f5ce17a135610b114a17e917b5a53c4d07c4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 11 Dec 2012 09:11:52 -0300 Subject: [media] omap3isp: csiphy: Fix an uninitialized variable compiler warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/media/platform/omap3isp/ispcsiphy.c: In function ‘csiphy_routing_cfg’: drivers/media/platform/omap3isp/ispcsiphy.c:71:57: warning: ‘shift’ may be used uninitialized in this function [-Wuninitialized] drivers/media/platform/omap3isp/ispcsiphy.c:40:6: note: ‘shift’ was declared here The warning is a false positive but the compiler is right in complaining. Fix it by using the correct enum data type for the iface argument and adding a default case in the switch statement. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/ispcsiphy.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/omap3isp/ispcsiphy.c b/drivers/media/platform/omap3isp/ispcsiphy.c index 3d56b33f85e8..c09de32f986a 100644 --- a/drivers/media/platform/omap3isp/ispcsiphy.c +++ b/drivers/media/platform/omap3isp/ispcsiphy.c @@ -32,7 +32,8 @@ #include "ispreg.h" #include "ispcsiphy.h" -static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, u32 iface, +static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, + enum isp_interface_type iface, bool ccp2_strobe) { u32 reg = isp_reg_readl( @@ -40,6 +41,8 @@ static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, u32 iface, u32 shift, mode; switch (iface) { + default: + /* Should not happen in practice, but let's keep the compiler happy. */ case ISP_INTERFACE_CCP2B_PHY1: reg &= ~OMAP3630_CONTROL_CAMERA_PHY_CTRL_CSI1_RX_SEL_PHY2; shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY1_SHIFT; @@ -59,9 +62,8 @@ static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, u32 iface, } /* Select data/clock or data/strobe mode for CCP2 */ - switch (iface) { - case ISP_INTERFACE_CCP2B_PHY1: - case ISP_INTERFACE_CCP2B_PHY2: + if (iface == ISP_INTERFACE_CCP2B_PHY1 || + iface == ISP_INTERFACE_CCP2B_PHY2) { if (ccp2_strobe) mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_CCP2_DATA_STROBE; else @@ -110,7 +112,8 @@ static void csiphy_routing_cfg_3430(struct isp_csiphy *phy, u32 iface, bool on, * and 3630, so they will not hold their contents in off-mode. This isn't an * issue since the MPU power domain is forced on whilst the ISP is in use. */ -static void csiphy_routing_cfg(struct isp_csiphy *phy, u32 iface, bool on, +static void csiphy_routing_cfg(struct isp_csiphy *phy, + enum isp_interface_type iface, bool on, bool ccp2_strobe) { if (phy->isp->mmio_base[OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL] -- cgit v1.2.3 From a4bb6f353e287f51a52a347670fd60938a566c25 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Fri, 14 Dec 2012 07:02:48 -0300 Subject: [media] media/rc: fix oops on unloading module rc-core During modiles initialization rc-core schedules work which calls request_module() several times to load ir-*-decoder modules, but it does not wait or cancel this work on module unloading. rc-core should use request_module_nowait() instead, because it anyway cannot load modules synchronously or cancel/wait pending work on unloading, because this leads to deadlock on modules_mutex between several "modprobe" processes. Signed-off-by: Konstantin Khlebnikov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ir-raw.c | 17 +---------------- drivers/media/rc/rc-core-priv.h | 16 ++++++++-------- 2 files changed, 9 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c index 97dc8d13b06b..17c94be9f24c 100644 --- a/drivers/media/rc/ir-raw.c +++ b/drivers/media/rc/ir-raw.c @@ -31,11 +31,6 @@ static DEFINE_MUTEX(ir_raw_handler_lock); static LIST_HEAD(ir_raw_handler_list); static u64 available_protocols; -#ifdef MODULE -/* Used to load the decoders */ -static struct work_struct wq_load; -#endif - static int ir_raw_event_thread(void *data) { struct ir_raw_event ev; @@ -347,8 +342,7 @@ void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler) } EXPORT_SYMBOL(ir_raw_handler_unregister); -#ifdef MODULE -static void init_decoders(struct work_struct *work) +void ir_raw_init(void) { /* Load the decoder modules */ @@ -365,12 +359,3 @@ static void init_decoders(struct work_struct *work) it is needed to change the CONFIG_MODULE test at rc-core.h */ } -#endif - -void ir_raw_init(void) -{ -#ifdef MODULE - INIT_WORK(&wq_load, init_decoders); - schedule_work(&wq_load); -#endif -} diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index 96f0a8bb39ea..5d87287ed372 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -165,56 +165,56 @@ void ir_raw_init(void); /* from ir-nec-decoder.c */ #ifdef CONFIG_IR_NEC_DECODER_MODULE -#define load_nec_decode() request_module("ir-nec-decoder") +#define load_nec_decode() request_module_nowait("ir-nec-decoder") #else static inline void load_nec_decode(void) { } #endif /* from ir-rc5-decoder.c */ #ifdef CONFIG_IR_RC5_DECODER_MODULE -#define load_rc5_decode() request_module("ir-rc5-decoder") +#define load_rc5_decode() request_module_nowait("ir-rc5-decoder") #else static inline void load_rc5_decode(void) { } #endif /* from ir-rc6-decoder.c */ #ifdef CONFIG_IR_RC6_DECODER_MODULE -#define load_rc6_decode() request_module("ir-rc6-decoder") +#define load_rc6_decode() request_module_nowait("ir-rc6-decoder") #else static inline void load_rc6_decode(void) { } #endif /* from ir-jvc-decoder.c */ #ifdef CONFIG_IR_JVC_DECODER_MODULE -#define load_jvc_decode() request_module("ir-jvc-decoder") +#define load_jvc_decode() request_module_nowait("ir-jvc-decoder") #else static inline void load_jvc_decode(void) { } #endif /* from ir-sony-decoder.c */ #ifdef CONFIG_IR_SONY_DECODER_MODULE -#define load_sony_decode() request_module("ir-sony-decoder") +#define load_sony_decode() request_module_nowait("ir-sony-decoder") #else static inline void load_sony_decode(void) { } #endif /* from ir-sanyo-decoder.c */ #ifdef CONFIG_IR_SANYO_DECODER_MODULE -#define load_sanyo_decode() request_module("ir-sanyo-decoder") +#define load_sanyo_decode() request_module_nowait("ir-sanyo-decoder") #else static inline void load_sanyo_decode(void) { } #endif /* from ir-mce_kbd-decoder.c */ #ifdef CONFIG_IR_MCE_KBD_DECODER_MODULE -#define load_mce_kbd_decode() request_module("ir-mce_kbd-decoder") +#define load_mce_kbd_decode() request_module_nowait("ir-mce_kbd-decoder") #else static inline void load_mce_kbd_decode(void) { } #endif /* from ir-lirc-codec.c */ #ifdef CONFIG_IR_LIRC_CODEC_MODULE -#define load_lirc_codec() request_module("ir-lirc-codec") +#define load_lirc_codec() request_module_nowait("ir-lirc-codec") #else static inline void load_lirc_codec(void) { } #endif -- cgit v1.2.3 From f698957aeaf3a711c2aa630a845b43426c02f339 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Sat, 15 Dec 2012 05:57:50 -0300 Subject: [media] marvell-ccic: use internal variable replace global frame stats variable This patch replaces the global frame stats variables by using internal variables in mcam_camera structure. Signed-off-by: Albert Wang Signed-off-by: Libin Yang Acked-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/marvell-ccic/mcam-core.c | 30 +++++++++++-------------- drivers/media/platform/marvell-ccic/mcam-core.h | 9 ++++++++ 2 files changed, 22 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index ce2b7b4788d6..7012913f34a2 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -30,13 +30,6 @@ #include "mcam-core.h" -/* - * Basic frame stats - to be deleted shortly - */ -static int frames; -static int singles; -static int delivered; - #ifdef MCAM_MODE_VMALLOC /* * Internal DMA buffer management. Since the controller cannot do S/G I/O, @@ -367,10 +360,10 @@ static void mcam_frame_tasklet(unsigned long data) if (!test_bit(bufno, &cam->flags)) continue; if (list_empty(&cam->buffers)) { - singles++; + cam->frame_state.singles++; break; /* Leave it valid, hope for better later */ } - delivered++; + cam->frame_state.delivered++; clear_bit(bufno, &cam->flags); buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue); @@ -452,7 +445,7 @@ static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame) mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR, vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0)); set_bit(CF_SINGLE_BUFFER, &cam->flags); - singles++; + cam->frame_state.singles++; return; } /* @@ -485,7 +478,7 @@ static void mcam_dma_contig_done(struct mcam_camera *cam, int frame) struct mcam_vb_buffer *buf = cam->vb_bufs[frame]; if (!test_bit(CF_SINGLE_BUFFER, &cam->flags)) { - delivered++; + cam->frame_state.delivered++; mcam_buffer_done(cam, frame, &buf->vb_buf); } mcam_set_contig_buffer(cam, frame); @@ -578,13 +571,13 @@ static void mcam_dma_sg_done(struct mcam_camera *cam, int frame) */ } else { set_bit(CF_SG_RESTART, &cam->flags); - singles++; + cam->frame_state.singles++; cam->vb_bufs[0] = NULL; } /* * Now we can give the completed frame back to user space. */ - delivered++; + cam->frame_state.delivered++; mcam_buffer_done(cam, frame, &buf->vb_buf); } @@ -1545,7 +1538,9 @@ static int mcam_v4l_open(struct file *filp) filp->private_data = cam; - frames = singles = delivered = 0; + cam->frame_state.frames = 0; + cam->frame_state.singles = 0; + cam->frame_state.delivered = 0; mutex_lock(&cam->s_mutex); if (cam->users == 0) { ret = mcam_setup_vb2(cam); @@ -1566,8 +1561,9 @@ static int mcam_v4l_release(struct file *filp) { struct mcam_camera *cam = filp->private_data; - cam_dbg(cam, "Release, %d frames, %d singles, %d delivered\n", frames, - singles, delivered); + cam_dbg(cam, "Release, %d frames, %d singles, %d delivered\n", + cam->frame_state.frames, cam->frame_state.singles, + cam->frame_state.delivered); mutex_lock(&cam->s_mutex); (cam->users)--; if (cam->users == 0) { @@ -1660,7 +1656,7 @@ static void mcam_frame_complete(struct mcam_camera *cam, int frame) clear_bit(CF_DMA_ACTIVE, &cam->flags); cam->next_buf = frame; cam->buf_seq[frame] = ++(cam->sequence); - frames++; + cam->frame_state.frames++; /* * "This should never happen" */ diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index bd6acba9fb37..5e802c6b398e 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -73,6 +73,14 @@ static inline int mcam_buffer_mode_supported(enum mcam_buffer_mode mode) } } +/* + * Basic frame states + */ +struct mcam_frame_state { + unsigned int frames; + unsigned int singles; + unsigned int delivered; +}; /* * A description of one of our devices. @@ -108,6 +116,7 @@ struct mcam_camera { unsigned long flags; /* Buffer status, mainly (dev_lock) */ int users; /* How many open FDs */ + struct mcam_frame_state frame_state; /* Frame state counter */ /* * Subsystem structures. */ -- cgit v1.2.3 From e7c953d280cea9a79018ae36e2bc7cedc3678de3 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Sat, 15 Dec 2012 19:11:28 -0300 Subject: [media] drxd: allow functional gate control after, attach Previously, gate control didn't work until drxd_init() execution. Migrate necessary set of commands in drxd_attach to allow gate control to be used by tuner which are accessible through i2c gate. Reported-by: frederic.mantegazza@gbiloba.org Signed-off-by: Patrice Chotard Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/drxd_hard.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c index e71cc60851e7..487c53b69bf3 100644 --- a/drivers/media/dvb-frontends/drxd_hard.c +++ b/drivers/media/dvb-frontends/drxd_hard.c @@ -2980,6 +2980,10 @@ struct dvb_frontend *drxd_attach(const struct drxd_config *config, sizeof(struct dvb_frontend_ops)); state->frontend.demodulator_priv = state; ConfigureMPEGOutput(state, 0); + /* add few initialization to allow gate control */ + CDRXD(state, state->config.IF ? state->config.IF : 36000000); + InitHI(state); + return &state->frontend; error: -- cgit v1.2.3 From 36a495a336c3fbbb2f4eeed2a94ab6d5be19d186 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Sat, 15 Dec 2012 19:11:43 -0300 Subject: [media] ngene: separate demodulator and tuner attach Previously, demodulator and tuner attach was done in the demod_attach callback. Migrate the tuner part in the tuner_attach callback in ngene_info to do thing in right place. Signed-off-by: Patrice Chotard Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ngene/ngene-cards.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/media/pci/ngene/ngene-cards.c b/drivers/media/pci/ngene/ngene-cards.c index b38bce529566..2a4895b0706b 100644 --- a/drivers/media/pci/ngene/ngene-cards.c +++ b/drivers/media/pci/ngene/ngene-cards.c @@ -327,6 +327,14 @@ static int demod_attach_drxd(struct ngene_channel *chan) pr_err("No DRXD found!\n"); return -ENODEV; } + return 0; +} + +static int tuner_attach_dtt7520x(struct ngene_channel *chan) +{ + struct drxd_config *feconf; + + feconf = chan->dev->card_info->fe_config[chan->number]; if (!dvb_attach(dvb_pll_attach, chan->fe, feconf->pll_address, &chan->i2c_adapter, -- cgit v1.2.3 From e19bc863f1f45223a85935f070177eda6b422f8f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 17 Dec 2012 04:52:48 -0300 Subject: [media] omap3isp: ispqueue: Fix uninitialized variable compiler warnings drivers/media/platform/omap3isp/ispqueue.c:399:18: warning: 'pa' may be used uninitialized in this function [-Wuninitialized] This is a false positive but the compiler has no way to know about it, so initialize the variable to 0. drivers/media/platform/omap3isp/ispqueue.c:445:6: warning: 'vm_page_prot' may be used uninitialized in this function [-Wuninitialized] This is a false positive and the compiler should know better. Use uninitialized_var(). Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/ispqueue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/omap3isp/ispqueue.c b/drivers/media/platform/omap3isp/ispqueue.c index 6599963cdd9b..e15f01342058 100644 --- a/drivers/media/platform/omap3isp/ispqueue.c +++ b/drivers/media/platform/omap3isp/ispqueue.c @@ -366,7 +366,7 @@ static int isp_video_buffer_prepare_pfnmap(struct isp_video_buffer *buf) unsigned long this_pfn; unsigned long start; unsigned long end; - dma_addr_t pa; + dma_addr_t pa = 0; int ret = -EFAULT; start = buf->vbuf.m.userptr; @@ -419,7 +419,7 @@ done: static int isp_video_buffer_prepare_vm_flags(struct isp_video_buffer *buf) { struct vm_area_struct *vma; - pgprot_t vm_page_prot; + pgprot_t uninitialized_var(vm_page_prot); unsigned long start; unsigned long end; int ret = -EFAULT; -- cgit v1.2.3 From 1ca6ae8de8a563f69eebe114d023855b4f0bcb1b Mon Sep 17 00:00:00 2001 From: John Törnblom Date: Mon, 17 Dec 2012 08:53:54 -0300 Subject: [media] bttv: avoid flooding the kernel log when i2c debugging is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the bttv driver is running without i2c_debug being set, the kernel log is being flooded with the string ">". This string is really a part of a debug message that is logged using several substrings protected by a conditional check. This patch adds the same conditional check to the leaked substring. Signed-off-by: John Törnblom Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/bt8xx/bttv-i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/pci/bt8xx/bttv-i2c.c b/drivers/media/pci/bt8xx/bttv-i2c.c index 580c8e682392..da400dbe4a10 100644 --- a/drivers/media/pci/bt8xx/bttv-i2c.c +++ b/drivers/media/pci/bt8xx/bttv-i2c.c @@ -173,7 +173,7 @@ bttv_i2c_sendbytes(struct bttv *btv, const struct i2c_msg *msg, int last) if (i2c_debug) pr_cont(" %02x", msg->buf[cnt]); } - if (!(xmit & BT878_I2C_NOSTOP)) + if (i2c_debug && !(xmit & BT878_I2C_NOSTOP)) pr_cont(">\n"); return msg->len; -- cgit v1.2.3 From 5344fe6e041c1ff867cde87d8088abf845645655 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 22 Dec 2012 07:48:58 -0300 Subject: [media] Improve media Kconfig menu I've always found it very confusing that the "Media ancillary drivers (tuners, sensors, i2c, frontends)" comment came after the "Autoselect" option. This patch moves it up and changes the "Autoselect" text to correspond more closely to the "Media ancillary drivers" comment. It also fixes two typos. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/Kconfig | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 4d9b4c21c199..84d85b921d6d 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -164,17 +164,20 @@ source "drivers/media/firewire/Kconfig" # Common driver options source "drivers/media/common/Kconfig" +comment "Media ancillary drivers (tuners, sensors, i2c, frontends)" + # # Ancillary drivers (tuners, i2c, frontends) # config MEDIA_SUBDRV_AUTOSELECT - bool "Autoselect tuners and i2c modules to build" + bool "Autoselect ancillary drivers (tuners, sensors, i2c, frontends)" depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_CAMERA_SUPPORT default y help - By default, a media driver auto-selects all possible i2c - devices that are used by any of the supported devices. + By default, a media driver auto-selects all possible ancillary + devices such as tuners, sensors, video encoders/decoders and + frontends, that are used by any of the supported devices. This is generally the right thing to do, except when there are strict constraints with regards to the kernel size, @@ -183,12 +186,10 @@ config MEDIA_SUBDRV_AUTOSELECT Use this option with care, as deselecting ancillary drivers which are, in fact, necessary will result in the lack of the needed functionality for your device (it may not tune or may not have - the need demodulers). + the needed demodulators). If unsure say Y. -comment "Media ancillary drivers (tuners, sensors, i2c, frontends)" - source "drivers/media/i2c/Kconfig" source "drivers/media/tuners/Kconfig" source "drivers/media/dvb-frontends/Kconfig" -- cgit v1.2.3 From 3724dde9c8c9f55c31ce8c7f8f2645733d6a59ac Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:05 -0300 Subject: [media] cx231xx: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/cx231xx-cards.c | 2 +- drivers/media/usb/cx231xx/cx231xx-video.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 94b573ad96d5..8d529565f163 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -736,7 +736,7 @@ static void cx231xx_sleep_s5h1432(struct cx231xx *dev) static inline void cx231xx_set_model(struct cx231xx *dev) { - memcpy(&dev->board, &cx231xx_boards[dev->model], sizeof(dev->board)); + dev->board = cx231xx_boards[dev->model]; } /* Since cx231xx_pre_card_setup() requires a proper dev->model, diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 239cb913be5c..93dfc182ec51 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -2627,8 +2627,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) dev->name, video_device_node_name(dev->vdev)); /* Initialize VBI template */ - memcpy(&cx231xx_vbi_template, &cx231xx_video_template, - sizeof(cx231xx_vbi_template)); + cx231xx_vbi_template = cx231xx_video_template; strcpy(cx231xx_vbi_template.name, "cx231xx-vbi"); /* Allocate and fill vbi video_device struct */ -- cgit v1.2.3 From 8fe392b8fdb93ced86e1661bff91af95ee234cf9 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:06 -0300 Subject: [media] usbvision: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/usbvision/usbvision-i2c.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/usbvision/usbvision-i2c.c b/drivers/media/usb/usbvision/usbvision-i2c.c index 89fec029e924..ba262a32bd3a 100644 --- a/drivers/media/usb/usbvision/usbvision-i2c.c +++ b/drivers/media/usb/usbvision/usbvision-i2c.c @@ -189,8 +189,7 @@ int usbvision_i2c_register(struct usb_usbvision *usbvision) if (usbvision->registered_i2c) return 0; - memcpy(&usbvision->i2c_adap, &i2c_adap_template, - sizeof(struct i2c_adapter)); + usbvision->i2c_adap = i2c_adap_template; sprintf(usbvision->i2c_adap.name, "%s-%d-%s", i2c_adap_template.name, usbvision->dev->bus->busnum, usbvision->dev->devpath); -- cgit v1.2.3 From 5869bb39f8e1f671746dcb5f070e3b1c38b23e5c Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:07 -0300 Subject: [media] sn9c102: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/sn9c102/sn9c102_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/sn9c102/sn9c102_core.c b/drivers/media/usb/sn9c102/sn9c102_core.c index 6bda81aebf87..c957e9aa6077 100644 --- a/drivers/media/usb/sn9c102/sn9c102_core.c +++ b/drivers/media/usb/sn9c102/sn9c102_core.c @@ -2827,7 +2827,7 @@ sn9c102_vidioc_querybuf(struct sn9c102_device* cam, void __user * arg) b.index >= cam->nbuffers || cam->io != IO_MMAP) return -EINVAL; - memcpy(&b, &cam->frame[b.index].buf, sizeof(b)); + b = cam->frame[b.index].buf; if (cam->frame[b.index].vma_use_count) b.flags |= V4L2_BUF_FLAG_MAPPED; @@ -2930,7 +2930,7 @@ sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp, f->state = F_UNUSED; - memcpy(&b, &f->buf, sizeof(b)); + b = f->buf; if (f->vma_use_count) b.flags |= V4L2_BUF_FLAG_MAPPED; -- cgit v1.2.3 From 5c2edefed74fb29b35634bce1b4c38ce1fdb2ce6 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:08 -0300 Subject: [media] pwc: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/pwc/pwc-if.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 21c15233c6ae..5ec15cb1ed26 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -1008,7 +1008,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id } /* Init video_device structure */ - memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template)); + pdev->vdev = pwc_template; strcpy(pdev->vdev.name, name); pdev->vdev.queue = &pdev->vb_queue; pdev->vdev.queue->lock = &pdev->vb_queue_lock; -- cgit v1.2.3 From 5338c16905ed7e8145861e1dacdd4eadce18f2b9 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:09 -0300 Subject: [media] pvrusb2: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/pvrusb2/pvrusb2-encoder.c | 3 +-- drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c | 4 ++-- drivers/media/usb/pvrusb2/pvrusb2-v4l2.c | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/pvrusb2/pvrusb2-encoder.c b/drivers/media/usb/pvrusb2/pvrusb2-encoder.c index e046fdaec5ae..f7702aeeda3f 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-encoder.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-encoder.c @@ -422,8 +422,7 @@ int pvr2_encoder_adjust(struct pvr2_hdw *hdw) pvr2_trace(PVR2_TRACE_ERROR_LEGS, "Error from cx2341x module code=%d",ret); } else { - memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state, - sizeof(struct cx2341x_mpeg_params)); + hdw->enc_cur_state = hdw->enc_ctl_state; hdw->enc_cur_valid = !0; } return ret; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c index 9ab596c78a4e..b5e929f1bf82 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c @@ -649,8 +649,8 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw) } // Configure the adapter and set up everything else related to it. - memcpy(&hdw->i2c_adap,&pvr2_i2c_adap_template,sizeof(hdw->i2c_adap)); - memcpy(&hdw->i2c_algo,&pvr2_i2c_algo_template,sizeof(hdw->i2c_algo)); + hdw->i2c_adap = pvr2_i2c_adap_template; + hdw->i2c_algo = pvr2_i2c_algo_template; strlcpy(hdw->i2c_adap.name,hdw->name,sizeof(hdw->i2c_adap.name)); hdw->i2c_adap.dev.parent = &hdw->usb_dev->dev; hdw->i2c_adap.algo = &hdw->i2c_algo; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c index 6930676051e7..34c3b6e80e86 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c @@ -1339,7 +1339,7 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, return; } - memcpy(&dip->devbase,&vdev_template,sizeof(vdev_template)); + dip->devbase = vdev_template; dip->devbase.release = pvr2_video_device_release; dip->devbase.ioctl_ops = &pvr2_ioctl_ops; { -- cgit v1.2.3 From d486b94b2636ce169f7f2bb1a4a7973843cc72e3 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:10 -0300 Subject: [media] hdpvr: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/hdpvr/hdpvr-i2c.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/hdpvr/hdpvr-i2c.c b/drivers/media/usb/hdpvr/hdpvr-i2c.c index 031cf024304c..6c5054f3f674 100644 --- a/drivers/media/usb/hdpvr/hdpvr-i2c.c +++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c @@ -217,8 +217,7 @@ int hdpvr_register_i2c_adapter(struct hdpvr_device *dev) hdpvr_activate_ir(dev); - memcpy(&dev->i2c_adapter, &hdpvr_i2c_adapter_template, - sizeof(struct i2c_adapter)); + dev->i2c_adapter = hdpvr_i2c_adapter_template; dev->i2c_adapter.dev.parent = &dev->udev->dev; i2c_set_adapdata(&dev->i2c_adapter, dev); -- cgit v1.2.3 From 8ba6220fe3b1ffbb3106faae48eee3bcbd27b553 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:11 -0300 Subject: [media] cx25840: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/cx25840/cx25840-ir.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/cx25840/cx25840-ir.c b/drivers/media/i2c/cx25840/cx25840-ir.c index 38ce76ed1924..9ae977b5983a 100644 --- a/drivers/media/i2c/cx25840/cx25840-ir.c +++ b/drivers/media/i2c/cx25840/cx25840-ir.c @@ -1251,13 +1251,11 @@ int cx25840_ir_probe(struct v4l2_subdev *sd) cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, 0); mutex_init(&ir_state->rx_params_lock); - memcpy(&default_params, &default_rx_params, - sizeof(struct v4l2_subdev_ir_parameters)); + default_params = default_rx_params; v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params); mutex_init(&ir_state->tx_params_lock); - memcpy(&default_params, &default_tx_params, - sizeof(struct v4l2_subdev_ir_parameters)); + default_params = default_tx_params; v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params); return 0; -- cgit v1.2.3 From 37320d7bc0180979d49de21b90f30a97f57b3ee1 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:12 -0300 Subject: [media] zr36067: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/zoran/zoran_card.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c index fffc54b452c8..cea325d3e530 100644 --- a/drivers/media/pci/zoran/zoran_card.c +++ b/drivers/media/pci/zoran/zoran_card.c @@ -708,8 +708,7 @@ static const struct i2c_algo_bit_data zoran_i2c_bit_data_template = { static int zoran_register_i2c (struct zoran *zr) { - memcpy(&zr->i2c_algo, &zoran_i2c_bit_data_template, - sizeof(struct i2c_algo_bit_data)); + zr->i2c_algo = zoran_i2c_bit_data_template; zr->i2c_algo.data = zr; strlcpy(zr->i2c_adapter.name, ZR_DEVNAME(zr), sizeof(zr->i2c_adapter.name)); -- cgit v1.2.3 From d3a950918446e201f0f9048995badc4fe8ba4e20 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:13 -0300 Subject: [media] dvb-usb/friio-fe: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/friio-fe.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb/friio-fe.c b/drivers/media/usb/dvb-usb/friio-fe.c index 90a70c66a96e..d56f927fc31a 100644 --- a/drivers/media/usb/dvb-usb/friio-fe.c +++ b/drivers/media/usb/dvb-usb/friio-fe.c @@ -421,11 +421,10 @@ struct dvb_frontend *jdvbt90502_attach(struct dvb_usb_device *d) /* setup the state */ state->i2c = &d->i2c_adap; - memcpy(&state->config, &friio_fe_config, sizeof(friio_fe_config)); + state->config = friio_fe_config; /* create dvb_frontend */ - memcpy(&state->frontend.ops, &jdvbt90502_ops, - sizeof(jdvbt90502_ops)); + state->frontend.ops = jdvbt90502_ops; state->frontend.demodulator_priv = state; if (jdvbt90502_init(&state->frontend) < 0) -- cgit v1.2.3 From f01e0ffd01eb0a8c6df71ac80234354ed716b488 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:14 -0300 Subject: [media] au0828: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/au0828/au0828-cards.c | 2 +- drivers/media/usb/au0828/au0828-i2c.c | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c index 88e35dfa33d4..dd32decb237d 100644 --- a/drivers/media/usb/au0828/au0828-cards.c +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -193,7 +193,7 @@ void au0828_card_setup(struct au0828_dev *dev) dprintk(1, "%s()\n", __func__); - memcpy(&dev->board, &au0828_boards[dev->boardnr], sizeof(dev->board)); + dev->board = au0828_boards[dev->boardnr]; if (dev->i2c_rc == 0) { dev->i2c_client.addr = 0xa0 >> 1; diff --git a/drivers/media/usb/au0828/au0828-i2c.c b/drivers/media/usb/au0828/au0828-i2c.c index 20d69b565255..17ec3651b10e 100644 --- a/drivers/media/usb/au0828/au0828-i2c.c +++ b/drivers/media/usb/au0828/au0828-i2c.c @@ -364,12 +364,9 @@ int au0828_i2c_register(struct au0828_dev *dev) { dprintk(1, "%s()\n", __func__); - memcpy(&dev->i2c_adap, &au0828_i2c_adap_template, - sizeof(dev->i2c_adap)); - memcpy(&dev->i2c_algo, &au0828_i2c_algo_template, - sizeof(dev->i2c_algo)); - memcpy(&dev->i2c_client, &au0828_i2c_client_template, - sizeof(dev->i2c_client)); + dev->i2c_adap = au0828_i2c_adap_template; + dev->i2c_algo = au0828_i2c_algo_template; + dev->i2c_client = au0828_i2c_client_template; dev->i2c_adap.dev.parent = &dev->usbdev->dev; -- cgit v1.2.3 From 36628731ec9ef46393d25d2fbdac40cc70c4d27a Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:15 -0300 Subject: [media] tuners/xc4000: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/xc4000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c index 5c0fd787cc8f..2018befabb5a 100644 --- a/drivers/media/tuners/xc4000.c +++ b/drivers/media/tuners/xc4000.c @@ -1066,7 +1066,7 @@ check_device: goto fail; } - memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw)); + priv->cur_fw = new_fw; /* * By setting BASE in cur_fw.type only after successfully loading all -- cgit v1.2.3 From 03c420010f4c5ded38bd0fc909ccadc25c82d080 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:16 -0300 Subject: [media] tuners/xc2028: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/tuner-xc2028.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c index 7bcb6b0ff1df..09451737c77e 100644 --- a/drivers/media/tuners/tuner-xc2028.c +++ b/drivers/media/tuners/tuner-xc2028.c @@ -870,7 +870,7 @@ check_device: } read_not_reliable: - memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw)); + priv->cur_fw = new_fw; /* * By setting BASE in cur_fw.type only after successfully loading all -- cgit v1.2.3 From 7f05b24536f068c0a5072929fb6c0fb2099d273c Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:17 -0300 Subject: [media] tuners/tda18271: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/tda18271-maps.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/tuners/tda18271-maps.c b/drivers/media/tuners/tda18271-maps.c index fb881c667c94..b62e925f643f 100644 --- a/drivers/media/tuners/tda18271-maps.c +++ b/drivers/media/tuners/tda18271-maps.c @@ -1290,13 +1290,11 @@ int tda18271_assign_map_layout(struct dvb_frontend *fe) switch (priv->id) { case TDA18271HDC1: priv->maps = &tda18271c1_map_layout; - memcpy(&priv->std, &tda18271c1_std_map, - sizeof(struct tda18271_std_map)); + priv->std = tda18271c1_std_map; break; case TDA18271HDC2: priv->maps = &tda18271c2_map_layout; - memcpy(&priv->std, &tda18271c2_std_map, - sizeof(struct tda18271_std_map)); + priv->std = tda18271c2_std_map; break; default: ret = -EINVAL; -- cgit v1.2.3 From 01a5cbebce7bca910f50dff19b05177c2c8a8a76 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:18 -0300 Subject: [media] ivtv: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ivtv/ivtv-i2c.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/ivtv/ivtv-i2c.c b/drivers/media/pci/ivtv/ivtv-i2c.c index 46e262becb67..a1811054fde4 100644 --- a/drivers/media/pci/ivtv/ivtv-i2c.c +++ b/drivers/media/pci/ivtv/ivtv-i2c.c @@ -719,13 +719,10 @@ int init_ivtv_i2c(struct ivtv *itv) return -ENODEV; } if (itv->options.newi2c > 0) { - memcpy(&itv->i2c_adap, &ivtv_i2c_adap_hw_template, - sizeof(struct i2c_adapter)); + itv->i2c_adap = ivtv_i2c_adap_hw_template; } else { - memcpy(&itv->i2c_adap, &ivtv_i2c_adap_template, - sizeof(struct i2c_adapter)); - memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template, - sizeof(struct i2c_algo_bit_data)); + itv->i2c_adap = ivtv_i2c_adap_template; + itv->i2c_algo = ivtv_i2c_algo_template; } itv->i2c_algo.udelay = itv->options.i2c_clock_period / 2; itv->i2c_algo.data = itv; @@ -735,8 +732,7 @@ int init_ivtv_i2c(struct ivtv *itv) itv->instance); i2c_set_adapdata(&itv->i2c_adap, &itv->v4l2_dev); - memcpy(&itv->i2c_client, &ivtv_i2c_client_template, - sizeof(struct i2c_client)); + itv->i2c_client = ivtv_i2c_client_template; itv->i2c_client.adapter = &itv->i2c_adap; itv->i2c_adap.dev.parent = &itv->pdev->dev; -- cgit v1.2.3 From b52377475ae9dca21bcb1ae8ec0936fbae8595b2 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:19 -0300 Subject: [media] cx88: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx88/cx88-cards.c | 2 +- drivers/media/pci/cx88/cx88-i2c.c | 3 +-- drivers/media/pci/cx88/cx88-vp3054-i2c.c | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c index 0c255248cbcd..e2e0b8faf7a4 100644 --- a/drivers/media/pci/cx88/cx88-cards.c +++ b/drivers/media/pci/cx88/cx88-cards.c @@ -3743,7 +3743,7 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) cx88_card_list(core, pci); } - memcpy(&core->board, &cx88_boards[core->boardnr], sizeof(core->board)); + core->board = cx88_boards[core->boardnr]; if (!core->board.num_frontends && (core->board.mpeg & CX88_MPEG_DVB)) core->board.num_frontends = 1; diff --git a/drivers/media/pci/cx88/cx88-i2c.c b/drivers/media/pci/cx88/cx88-i2c.c index de0f1af74e41..cf2d69615838 100644 --- a/drivers/media/pci/cx88/cx88-i2c.c +++ b/drivers/media/pci/cx88/cx88-i2c.c @@ -139,8 +139,7 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci) if (i2c_udelay<5) i2c_udelay=5; - memcpy(&core->i2c_algo, &cx8800_i2c_algo_template, - sizeof(core->i2c_algo)); + core->i2c_algo = cx8800_i2c_algo_template; core->i2c_adap.dev.parent = &pci->dev; diff --git a/drivers/media/pci/cx88/cx88-vp3054-i2c.c b/drivers/media/pci/cx88/cx88-vp3054-i2c.c index d77f8ecab9d7..deede6e25d94 100644 --- a/drivers/media/pci/cx88/cx88-vp3054-i2c.c +++ b/drivers/media/pci/cx88/cx88-vp3054-i2c.c @@ -118,8 +118,7 @@ int vp3054_i2c_probe(struct cx8802_dev *dev) return -ENOMEM; dev->vp3054 = vp3054_i2c; - memcpy(&vp3054_i2c->algo, &vp3054_i2c_algo_template, - sizeof(vp3054_i2c->algo)); + vp3054_i2c->algo = vp3054_i2c_algo_template; vp3054_i2c->adap.dev.parent = &dev->pci->dev; strlcpy(vp3054_i2c->adap.name, core->name, -- cgit v1.2.3 From 3618acab2ccefe292a3b1a1d7295f1368023b71a Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:20 -0300 Subject: [media] cx23885: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Reviewed-by: Andy Walls Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx23885/cx23885-video.c | 3 +-- drivers/media/pci/cx23885/cx23888-ir.c | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 0e80ba4ca4dd..5991bc8dc158 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -1819,8 +1819,7 @@ int cx23885_video_register(struct cx23885_dev *dev) spin_lock_init(&dev->slock); /* Initialize VBI template */ - memcpy(&cx23885_vbi_template, &cx23885_video_template, - sizeof(cx23885_vbi_template)); + cx23885_vbi_template = cx23885_video_template; strcpy(cx23885_vbi_template.name, "cx23885-vbi"); dev->tvnorm = cx23885_video_template.current_norm; diff --git a/drivers/media/pci/cx23885/cx23888-ir.c b/drivers/media/pci/cx23885/cx23888-ir.c index c4bd1e95d33f..d51eed051d59 100644 --- a/drivers/media/pci/cx23885/cx23888-ir.c +++ b/drivers/media/pci/cx23885/cx23888-ir.c @@ -1237,13 +1237,11 @@ int cx23888_ir_probe(struct cx23885_dev *dev) cx23888_ir_write4(dev, CX23888_IR_IRQEN_REG, 0); mutex_init(&state->rx_params_lock); - memcpy(&default_params, &default_rx_params, - sizeof(struct v4l2_subdev_ir_parameters)); + default_params = default_rx_params; v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params); mutex_init(&state->tx_params_lock); - memcpy(&default_params, &default_tx_params, - sizeof(struct v4l2_subdev_ir_parameters)); + default_params = default_tx_params; v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params); } else { kfifo_free(&state->rx_kfifo); -- cgit v1.2.3 From 2e814af502e0fc5983cbb96fc8c0c64fe49a9340 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:21 -0300 Subject: [media] cx18: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx18/cx18-i2c.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/cx18/cx18-i2c.c b/drivers/media/pci/cx18/cx18-i2c.c index 4908eb7bcf6c..d61ac6393e7e 100644 --- a/drivers/media/pci/cx18/cx18-i2c.c +++ b/drivers/media/pci/cx18/cx18-i2c.c @@ -240,15 +240,13 @@ int init_cx18_i2c(struct cx18 *cx) for (i = 0; i < 2; i++) { /* Setup algorithm for adapter */ - memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template, - sizeof(struct i2c_algo_bit_data)); + cx->i2c_algo[i] = cx18_i2c_algo_template; cx->i2c_algo_cb_data[i].cx = cx; cx->i2c_algo_cb_data[i].bus_index = i; cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i]; /* Setup adapter */ - memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template, - sizeof(struct i2c_adapter)); + cx->i2c_adap[i] = cx18_i2c_adap_template; cx->i2c_adap[i].algo_data = &cx->i2c_algo[i]; sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name), " #%d-%d", cx->instance, i); -- cgit v1.2.3 From b4b1d04006540ebeb5c85222292cdcb2f95f217b Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:22 -0300 Subject: [media] bttv: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/bt8xx/bttv-i2c.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/bt8xx/bttv-i2c.c b/drivers/media/pci/bt8xx/bttv-i2c.c index da400dbe4a10..99865f531199 100644 --- a/drivers/media/pci/bt8xx/bttv-i2c.c +++ b/drivers/media/pci/bt8xx/bttv-i2c.c @@ -366,8 +366,7 @@ int __devinit init_bttv_i2c(struct bttv *btv) strlcpy(btv->c.i2c_adap.name, "bttv", sizeof(btv->c.i2c_adap.name)); - memcpy(&btv->i2c_algo, &bttv_i2c_algo_bit_template, - sizeof(bttv_i2c_algo_bit_template)); + btv->i2c_algo = bttv_i2c_algo_bit_template; btv->i2c_algo.udelay = i2c_udelay; btv->i2c_algo.data = btv; btv->c.i2c_adap.algo_data = &btv->i2c_algo; -- cgit v1.2.3 From b9b1b3a8f7b76035140912bc9e3a325e58fc6d58 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:23 -0300 Subject: [media] dvb-core: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_frontend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 9b4a47c104a1..dd35fa972067 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -2255,7 +2255,7 @@ static int dvb_frontend_ioctl_legacy(struct file *file, printk("%s switch command: 0x%04lx\n", __func__, swcmd); do_gettimeofday(&nexttime); if (dvb_frontend_debug) - memcpy(&tv[0], &nexttime, sizeof(struct timeval)); + tv[0] = nexttime; /* before sending a command, initialize by sending * a 32ms 18V to the switch */ -- cgit v1.2.3 From ee45ddc1e03afc221afad273503b6c2fc0683008 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:24 -0300 Subject: [media] dvb-frontends: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/cx24116.c | 2 +- drivers/media/dvb-frontends/drxd_hard.c | 5 ++--- drivers/media/dvb-frontends/stv0299.c | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/cx24116.c b/drivers/media/dvb-frontends/cx24116.c index b48879186537..2916d7c74a1d 100644 --- a/drivers/media/dvb-frontends/cx24116.c +++ b/drivers/media/dvb-frontends/cx24116.c @@ -819,7 +819,7 @@ static int cx24116_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) static void cx24116_clone_params(struct dvb_frontend *fe) { struct cx24116_state *state = fe->demodulator_priv; - memcpy(&state->dcur, &state->dnxt, sizeof(state->dcur)); + state->dcur = state->dnxt; } /* Wait for LNB */ diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c index 487c53b69bf3..9a2134792cfa 100644 --- a/drivers/media/dvb-frontends/drxd_hard.c +++ b/drivers/media/dvb-frontends/drxd_hard.c @@ -2965,7 +2965,7 @@ struct dvb_frontend *drxd_attach(const struct drxd_config *config, return NULL; memset(state, 0, sizeof(*state)); - memcpy(&state->ops, &drxd_ops, sizeof(struct dvb_frontend_ops)); + state->ops = drxd_ops; state->dev = dev; state->config = *config; state->i2c = i2c; @@ -2976,8 +2976,7 @@ struct dvb_frontend *drxd_attach(const struct drxd_config *config, if (Read16(state, 0, 0, 0) < 0) goto error; - memcpy(&state->frontend.ops, &drxd_ops, - sizeof(struct dvb_frontend_ops)); + state->frontend.ops = drxd_ops; state->frontend.demodulator_priv = state; ConfigureMPEGOutput(state, 0); /* add few initialization to allow gate control */ diff --git a/drivers/media/dvb-frontends/stv0299.c b/drivers/media/dvb-frontends/stv0299.c index 92a6075cd82f..b57ecf42e75a 100644 --- a/drivers/media/dvb-frontends/stv0299.c +++ b/drivers/media/dvb-frontends/stv0299.c @@ -420,7 +420,7 @@ static int stv0299_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long do_gettimeofday (&nexttime); if (debug_legacy_dish_switch) - memcpy (&tv[0], &nexttime, sizeof (struct timeval)); + tv[0] = nexttime; stv0299_writeregI (state, 0x0c, reg0x0c | 0x50); /* set LNB to 18V */ dvb_frontend_sleep_until(&nexttime, 32000); -- cgit v1.2.3 From 2b9b325d73dc092e1b888c381b98dde57d394194 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:25 -0300 Subject: [media] radio-wl1273: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-wl1273.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index 9b0c9fa0beb8..6e55e93c0cf6 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -2084,8 +2084,7 @@ static int __devinit wl1273_fm_radio_probe(struct platform_device *pdev) } /* V4L2 configuration */ - memcpy(&radio->videodev, &wl1273_viddev_template, - sizeof(wl1273_viddev_template)); + radio->videodev = wl1273_viddev_template; radio->videodev.v4l2_dev = &radio->v4l2dev; -- cgit v1.2.3 From c84401c200423e4855bc990d8460cc0c3a2bb664 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:26 -0300 Subject: [media] wl128x: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/wl128x/fmdrv_common.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c index 602ef7ac8c24..a002234ed5de 100644 --- a/drivers/media/radio/wl128x/fmdrv_common.c +++ b/drivers/media/radio/wl128x/fmdrv_common.c @@ -1563,8 +1563,7 @@ int fmc_prepare(struct fmdev *fmdev) fmdev->irq_info.mask = FM_MAL_EVENT; /* Region info */ - memcpy(&fmdev->rx.region, ®ion_configs[default_radio_region], - sizeof(struct region_info)); + fmdev->rx.region = region_configs[default_radio_region]; fmdev->rx.mute_mode = FM_MUTE_OFF; fmdev->rx.rf_depend_mute = FM_RX_RF_DEPENDENT_MUTE_OFF; -- cgit v1.2.3 From 9a3323aef4e32585a76ace5f53973404e7e5afee Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Thu, 20 Dec 2012 15:11:18 -0300 Subject: [media] m2m-deinterlace: use correct check for kzalloc failure There is no point in PTR_ERR()ing a NULL pointer, use a real error instead. Signed-off-by: Sasha Levin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/m2m-deinterlace.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index ed77a645e992..6c4db9b98989 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -917,10 +917,8 @@ static int deinterlace_open(struct file *file) ctx->xt = kzalloc(sizeof(struct dma_async_tx_descriptor) + sizeof(struct data_chunk), GFP_KERNEL); if (!ctx->xt) { - int ret = PTR_ERR(ctx->xt); - kfree(ctx); - return ret; + return -ENOMEM; } ctx->colorspace = V4L2_COLORSPACE_REC709; -- cgit v1.2.3 From 10a5c9148ee6841004cb7e6f4b09022eba94c7be Mon Sep 17 00:00:00 2001 From: Eddi De Pieri Date: Sat, 22 Dec 2012 09:41:49 -0300 Subject: [media] it913x: add support for Avermedia A835B Add support for Avermedia A835B and variants. Signed-off-by: Eddi De Pieri Cc: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb-usb-ids.h | 4 ++++ drivers/media/usb/dvb-usb-v2/it913x.c | 12 ++++++++++++ 2 files changed, 16 insertions(+) (limited to 'drivers') diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index c6720f2991ac..7e1597dad568 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -234,6 +234,10 @@ #define USB_PID_AVERMEDIA_A815M 0x815a #define USB_PID_AVERMEDIA_A835 0xa835 #define USB_PID_AVERMEDIA_B835 0xb835 +#define USB_PID_AVERMEDIA_A835B_1835 0x1835 +#define USB_PID_AVERMEDIA_A835B_2835 0x2835 +#define USB_PID_AVERMEDIA_A835B_3835 0x3835 +#define USB_PID_AVERMEDIA_A835B_4835 0x4835 #define USB_PID_AVERMEDIA_1867 0x1867 #define USB_PID_AVERMEDIA_A867 0xa867 #define USB_PID_AVERMEDIA_TWINSTAR 0x0825 diff --git a/drivers/media/usb/dvb-usb-v2/it913x.c b/drivers/media/usb/dvb-usb-v2/it913x.c index fbc0a84465eb..744c9f9f7680 100644 --- a/drivers/media/usb/dvb-usb-v2/it913x.c +++ b/drivers/media/usb/dvb-usb-v2/it913x.c @@ -780,6 +780,18 @@ static const struct usb_device_id it913x_id_table[] = { { DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135_9006, &it913x_properties, "ITE 9135(9006) Generic", RC_MAP_IT913X_V1) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_1835, + &it913x_properties, "Avermedia A835B(1835)", + RC_MAP_IT913X_V2) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_2835, + &it913x_properties, "Avermedia A835B(2835)", + RC_MAP_IT913X_V2) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_3835, + &it913x_properties, "Avermedia A835B(3835)", + RC_MAP_IT913X_V2) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_4835, + &it913x_properties, "Avermedia A835B(4835)", + RC_MAP_IT913X_V2) }, {} /* Terminating entry */ }; -- cgit v1.2.3 From c1965eae65f0db2eee574f72aab4e8b34ecf8f9c Mon Sep 17 00:00:00 2001 From: Konstantin Dimitrov Date: Sun, 23 Dec 2012 19:25:09 -0300 Subject: [media] ds3000: remove ts2020 tuner related code remove ts2020 tuner related code from ds3000 driver prepare ds3000 driver for using external tuner driver Signed-off-by: Konstantin Dimitrov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/ds3000.c | 242 ++++------------------------------- drivers/media/dvb-frontends/ds3000.h | 8 +- 2 files changed, 27 insertions(+), 223 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c index 60a529e3833f..cd84fbd78a5b 100644 --- a/drivers/media/dvb-frontends/ds3000.c +++ b/drivers/media/dvb-frontends/ds3000.c @@ -1,8 +1,8 @@ /* - Montage Technology DS3000/TS2020 - DVBS/S2 Demodulator/Tuner driver - Copyright (C) 2009 Konstantin Dimitrov + Montage Technology DS3000 - DVBS/S2 Demodulator driver + Copyright (C) 2009-2012 Konstantin Dimitrov - Copyright (C) 2009 TurboSight.com + Copyright (C) 2009-2012 TurboSight.com 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 @@ -42,7 +42,6 @@ static int debug; #define DS3000_DEFAULT_FIRMWARE "dvb-fe-ds3000.fw" #define DS3000_SAMPLE_RATE 96000 /* in kHz */ -#define DS3000_XTAL_FREQ 27000 /* in kHz */ /* Register values to initialise the demod in DVB-S mode */ static u8 ds3000_dvbs_init_tab[] = { @@ -256,22 +255,14 @@ static int ds3000_writereg(struct ds3000_state *state, int reg, int data) return 0; } -static int ds3000_tuner_writereg(struct ds3000_state *state, int reg, int data) +static int ds3000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) { - u8 buf[] = { reg, data }; - struct i2c_msg msg = { .addr = 0x60, - .flags = 0, .buf = buf, .len = 2 }; - int err; - - dprintk("%s: write reg 0x%02x, value 0x%02x\n", __func__, reg, data); + struct ds3000_state *state = fe->demodulator_priv; - ds3000_writereg(state, 0x03, 0x11); - err = i2c_transfer(state->i2c, &msg, 1); - if (err != 1) { - printk("%s: writereg error(err == %i, reg == 0x%02x," - " value == 0x%02x)\n", __func__, err, reg, data); - return -EREMOTEIO; - } + if (enable) + ds3000_writereg(state, 0x03, 0x12); + else + ds3000_writereg(state, 0x03, 0x02); return 0; } @@ -348,38 +339,6 @@ static int ds3000_readreg(struct ds3000_state *state, u8 reg) return b1[0]; } -static int ds3000_tuner_readreg(struct ds3000_state *state, u8 reg) -{ - int ret; - u8 b0[] = { reg }; - u8 b1[] = { 0 }; - struct i2c_msg msg[] = { - { - .addr = 0x60, - .flags = 0, - .buf = b0, - .len = 1 - }, { - .addr = 0x60, - .flags = I2C_M_RD, - .buf = b1, - .len = 1 - } - }; - - ds3000_writereg(state, 0x03, 0x12); - ret = i2c_transfer(state->i2c, msg, 2); - - if (ret != 2) { - printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret); - return ret; - } - - dprintk("%s: read reg 0x%02x, value 0x%02x\n", __func__, reg, b1[0]); - - return b1[0]; -} - static int ds3000_load_firmware(struct dvb_frontend *fe, const struct firmware *fw); @@ -568,37 +527,6 @@ static int ds3000_read_ber(struct dvb_frontend *fe, u32* ber) return 0; } -/* read TS2020 signal strength */ -static int ds3000_read_signal_strength(struct dvb_frontend *fe, - u16 *signal_strength) -{ - struct ds3000_state *state = fe->demodulator_priv; - u16 sig_reading, sig_strength; - u8 rfgain, bbgain; - - dprintk("%s()\n", __func__); - - rfgain = ds3000_tuner_readreg(state, 0x3d) & 0x1f; - bbgain = ds3000_tuner_readreg(state, 0x21) & 0x1f; - - if (rfgain > 15) - rfgain = 15; - if (bbgain > 13) - bbgain = 13; - - sig_reading = rfgain * 2 + bbgain * 3; - - sig_strength = 40 + (64 - sig_reading) * 50 / 64 ; - - /* cook the value to be suitable for szap-s2 human readable output */ - *signal_strength = sig_strength * 1000; - - dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", __func__, - sig_reading, *signal_strength); - - return 0; -} - /* calculate DS3000 snr value in dB */ static int ds3000_read_snr(struct dvb_frontend *fe, u16 *snr) { @@ -952,133 +880,17 @@ static int ds3000_set_frontend(struct dvb_frontend *fe) int i; fe_status_t status; - u8 mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf, div4; s32 offset_khz; - u16 value, ndiv; - u32 f3db; + u32 frequency; + u16 value; dprintk("%s() ", __func__); if (state->config->set_ts_params) state->config->set_ts_params(fe, 0); /* Tune */ - /* unknown */ - ds3000_tuner_writereg(state, 0x07, 0x02); - ds3000_tuner_writereg(state, 0x10, 0x00); - ds3000_tuner_writereg(state, 0x60, 0x79); - ds3000_tuner_writereg(state, 0x08, 0x01); - ds3000_tuner_writereg(state, 0x00, 0x01); - div4 = 0; - - /* calculate and set freq divider */ - if (c->frequency < 1146000) { - ds3000_tuner_writereg(state, 0x10, 0x11); - div4 = 1; - ndiv = ((c->frequency * (6 + 8) * 4) + - (DS3000_XTAL_FREQ / 2)) / - DS3000_XTAL_FREQ - 1024; - } else { - ds3000_tuner_writereg(state, 0x10, 0x01); - ndiv = ((c->frequency * (6 + 8) * 2) + - (DS3000_XTAL_FREQ / 2)) / - DS3000_XTAL_FREQ - 1024; - } - - ds3000_tuner_writereg(state, 0x01, (ndiv & 0x0f00) >> 8); - ds3000_tuner_writereg(state, 0x02, ndiv & 0x00ff); - - /* set pll */ - ds3000_tuner_writereg(state, 0x03, 0x06); - ds3000_tuner_writereg(state, 0x51, 0x0f); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x10); - ds3000_tuner_writereg(state, 0x50, 0x00); - msleep(5); - - /* unknown */ - ds3000_tuner_writereg(state, 0x51, 0x17); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x08); - ds3000_tuner_writereg(state, 0x50, 0x00); - msleep(5); - - value = ds3000_tuner_readreg(state, 0x3d); - value &= 0x0f; - if ((value > 4) && (value < 15)) { - value -= 3; - if (value < 4) - value = 4; - value = ((value << 3) | 0x01) & 0x79; - } - - ds3000_tuner_writereg(state, 0x60, value); - ds3000_tuner_writereg(state, 0x51, 0x17); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x08); - ds3000_tuner_writereg(state, 0x50, 0x00); - - /* set low-pass filter period */ - ds3000_tuner_writereg(state, 0x04, 0x2e); - ds3000_tuner_writereg(state, 0x51, 0x1b); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x04); - ds3000_tuner_writereg(state, 0x50, 0x00); - msleep(5); - - f3db = ((c->symbol_rate / 1000) << 2) / 5 + 2000; - if ((c->symbol_rate / 1000) < 5000) - f3db += 3000; - if (f3db < 7000) - f3db = 7000; - if (f3db > 40000) - f3db = 40000; - - /* set low-pass filter baseband */ - value = ds3000_tuner_readreg(state, 0x26); - mlpf = 0x2e * 207 / ((value << 1) + 151); - mlpf_max = mlpf * 135 / 100; - mlpf_min = mlpf * 78 / 100; - if (mlpf_max > 63) - mlpf_max = 63; - - /* rounded to the closest integer */ - nlpf = ((mlpf * f3db * 1000) + (2766 * DS3000_XTAL_FREQ / 2)) - / (2766 * DS3000_XTAL_FREQ); - if (nlpf > 23) - nlpf = 23; - if (nlpf < 1) - nlpf = 1; - - /* rounded to the closest integer */ - mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) + - (1000 * f3db / 2)) / (1000 * f3db); - - if (mlpf_new < mlpf_min) { - nlpf++; - mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) + - (1000 * f3db / 2)) / (1000 * f3db); - } - - if (mlpf_new > mlpf_max) - mlpf_new = mlpf_max; - - ds3000_tuner_writereg(state, 0x04, mlpf_new); - ds3000_tuner_writereg(state, 0x06, nlpf); - ds3000_tuner_writereg(state, 0x51, 0x1b); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x04); - ds3000_tuner_writereg(state, 0x50, 0x00); - msleep(5); - - /* unknown */ - ds3000_tuner_writereg(state, 0x51, 0x1e); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x01); - ds3000_tuner_writereg(state, 0x50, 0x00); - msleep(60); - - offset_khz = (ndiv - ndiv % 2 + 1024) * DS3000_XTAL_FREQ - / (6 + 8) / (div4 + 1) / 2 - c->frequency; + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); /* ds3000 global reset */ ds3000_writereg(state, 0x07, 0x80); @@ -1186,7 +998,11 @@ static int ds3000_set_frontend(struct dvb_frontend *fe) /* start ds3000 build-in uC */ ds3000_writereg(state, 0xb2, 0x00); - ds3000_set_carrier_offset(fe, offset_khz); + if (fe->ops.tuner_ops.get_frequency) { + fe->ops.tuner_ops.get_frequency(fe, &frequency); + offset_khz = frequency - c->frequency; + ds3000_set_carrier_offset(fe, offset_khz); + } for (i = 0; i < 30 ; i++) { ds3000_read_status(fe, &status); @@ -1237,10 +1053,6 @@ static int ds3000_initfe(struct dvb_frontend *fe) ds3000_writereg(state, 0x08, 0x01 | ds3000_readreg(state, 0x08)); msleep(1); - /* TS2020 init */ - ds3000_tuner_writereg(state, 0x42, 0x73); - ds3000_tuner_writereg(state, 0x05, 0x01); - ds3000_tuner_writereg(state, 0x62, 0xf5); /* Load the firmware if required */ ret = ds3000_firmware_ondemand(fe); if (ret != 0) { @@ -1251,17 +1063,10 @@ static int ds3000_initfe(struct dvb_frontend *fe) return 0; } -/* Put device to sleep */ -static int ds3000_sleep(struct dvb_frontend *fe) -{ - dprintk("%s()\n", __func__); - return 0; -} - static struct dvb_frontend_ops ds3000_ops = { - .delsys = { SYS_DVBS, SYS_DVBS2}, + .delsys = { SYS_DVBS, SYS_DVBS2 }, .info = { - .name = "Montage Technology DS3000/TS2020", + .name = "Montage Technology DS3000", .frequency_min = 950000, .frequency_max = 2150000, .frequency_stepsize = 1011, /* kHz for QPSK frontends */ @@ -1279,10 +1084,9 @@ static struct dvb_frontend_ops ds3000_ops = { .release = ds3000_release, .init = ds3000_initfe, - .sleep = ds3000_sleep, + .i2c_gate_ctrl = ds3000_i2c_gate_ctrl, .read_status = ds3000_read_status, .read_ber = ds3000_read_ber, - .read_signal_strength = ds3000_read_signal_strength, .read_snr = ds3000_read_snr, .read_ucblocks = ds3000_read_ucblocks, .set_voltage = ds3000_set_voltage, @@ -1299,7 +1103,7 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); MODULE_DESCRIPTION("DVB Frontend module for Montage Technology " - "DS3000/TS2020 hardware"); -MODULE_AUTHOR("Konstantin Dimitrov"); + "DS3000 hardware"); +MODULE_AUTHOR("Konstantin Dimitrov "); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(DS3000_DEFAULT_FIRMWARE); diff --git a/drivers/media/dvb-frontends/ds3000.h b/drivers/media/dvb-frontends/ds3000.h index 1b736888ea37..67eeaf9bdbce 100644 --- a/drivers/media/dvb-frontends/ds3000.h +++ b/drivers/media/dvb-frontends/ds3000.h @@ -1,8 +1,8 @@ /* - Montage Technology DS3000/TS2020 - DVBS/S2 Satellite demod/tuner driver - Copyright (C) 2009 Konstantin Dimitrov + Montage Technology DS3000 - DVBS/S2 Demodulator driver + Copyright (C) 2009-2012 Konstantin Dimitrov - Copyright (C) 2009 TurboSight.com + Copyright (C) 2009-2012 TurboSight.com 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 @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ + */ #ifndef DS3000_H #define DS3000_H -- cgit v1.2.3 From 6fef4fc71e79282b673d7613cfc63da6bdeec5bd Mon Sep 17 00:00:00 2001 From: Konstantin Dimitrov Date: Sun, 23 Dec 2012 19:25:27 -0300 Subject: [media] ts2020: add ts2020 tuner driver add separate ts2020 tuner driver Signed-off-by: Konstantin Dimitrov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/Kconfig | 7 + drivers/media/dvb-frontends/Makefile | 1 + drivers/media/dvb-frontends/ts2020.c | 323 +++++++++++++++++++++++++++++++++++ drivers/media/dvb-frontends/ts2020.h | 49 ++++++ 4 files changed, 380 insertions(+) create mode 100644 drivers/media/dvb-frontends/ts2020.c create mode 100644 drivers/media/dvb-frontends/ts2020.h (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 5efec73a32d2..6f809a70c78e 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -207,6 +207,13 @@ config DVB_SI21XX help A DVB-S tuner module. Say Y when you want to support this frontend. +config DVB_TS2020 + tristate "Montage Tehnology TS2020 based tuners" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-S/S2 silicon tuner. Say Y when you want to support this tuner. + config DVB_DS3000 tristate "Montage Tehnology DS3000 based" depends on DVB_CORE && I2C diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index 7eb73bbd2e26..cebc0faffab5 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -88,6 +88,7 @@ obj-$(CONFIG_DVB_ISL6423) += isl6423.o obj-$(CONFIG_DVB_EC100) += ec100.o obj-$(CONFIG_DVB_HD29L2) += hd29l2.o obj-$(CONFIG_DVB_DS3000) += ds3000.o +obj-$(CONFIG_DVB_TS2020) += ts2020.o obj-$(CONFIG_DVB_MB86A16) += mb86a16.o obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o obj-$(CONFIG_DVB_IX2505V) += ix2505v.o diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c new file mode 100644 index 000000000000..8dce4ae55f09 --- /dev/null +++ b/drivers/media/dvb-frontends/ts2020.c @@ -0,0 +1,323 @@ +/* + Montage Technology TS2020 - Silicon Tuner driver + Copyright (C) 2009-2012 Konstantin Dimitrov + + Copyright (C) 2009-2012 TurboSight.com + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dvb_frontend.h" +#include "ts2020.h" + +#define TS2020_XTAL_FREQ 27000 /* in kHz */ + +struct ts2020_state { + u8 tuner_address; + struct i2c_adapter *i2c; +}; + +static int ts2020_readreg(struct dvb_frontend *fe, u8 reg) +{ + struct ts2020_state *state = fe->tuner_priv; + + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { + .addr = state->tuner_address, + .flags = 0, + .buf = b0, + .len = 1 + }, { + .addr = state->tuner_address, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + } + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = i2c_transfer(state->i2c, msg, 2); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (ret != 2) { + printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret); + return ret; + } + + return b1[0]; +} + +static int ts2020_writereg(struct dvb_frontend *fe, int reg, int data) +{ + struct ts2020_state *state = fe->tuner_priv; + + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .addr = state->tuner_address, + .flags = 0, .buf = buf, .len = 2 }; + int err; + + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + err = i2c_transfer(state->i2c, &msg, 1); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (err != 1) { + printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x," + " value == 0x%02x)\n", __func__, err, reg, data); + return -EREMOTEIO; + } + + return 0; +} + +static int ts2020_init(struct dvb_frontend *fe) +{ + ts2020_writereg(fe, 0x42, 0x73); + ts2020_writereg(fe, 0x05, 0x01); + ts2020_writereg(fe, 0x62, 0xf5); + return 0; +} + +static int ts2020_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + u16 ndiv, div4; + + div4 = (ts2020_readreg(fe, 0x10) & 0x10) >> 4; + + ndiv = ts2020_readreg(fe, 0x01); + ndiv &= 0x0f; + ndiv <<= 8; + ndiv |= ts2020_readreg(fe, 0x02); + + /* actual tuned frequency, i.e. including the offset */ + *frequency = (ndiv - ndiv % 2 + 1024) * TS2020_XTAL_FREQ + / (6 + 8) / (div4 + 1) / 2; + + return 0; +} + +static int ts2020_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + u8 mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf, div4; + u16 value, ndiv; + u32 srate = 0, f3db; + + ts2020_init(fe); + + /* unknown */ + ts2020_writereg(fe, 0x07, 0x02); + ts2020_writereg(fe, 0x10, 0x00); + ts2020_writereg(fe, 0x60, 0x79); + ts2020_writereg(fe, 0x08, 0x01); + ts2020_writereg(fe, 0x00, 0x01); + div4 = 0; + + /* calculate and set freq divider */ + if (c->frequency < 1146000) { + ts2020_writereg(fe, 0x10, 0x11); + div4 = 1; + ndiv = ((c->frequency * (6 + 8) * 4) + + (TS2020_XTAL_FREQ / 2)) / + TS2020_XTAL_FREQ - 1024; + } else { + ts2020_writereg(fe, 0x10, 0x01); + ndiv = ((c->frequency * (6 + 8) * 2) + + (TS2020_XTAL_FREQ / 2)) / + TS2020_XTAL_FREQ - 1024; + } + + ts2020_writereg(fe, 0x01, (ndiv & 0x0f00) >> 8); + ts2020_writereg(fe, 0x02, ndiv & 0x00ff); + + /* set pll */ + ts2020_writereg(fe, 0x03, 0x06); + ts2020_writereg(fe, 0x51, 0x0f); + ts2020_writereg(fe, 0x51, 0x1f); + ts2020_writereg(fe, 0x50, 0x10); + ts2020_writereg(fe, 0x50, 0x00); + msleep(5); + + /* unknown */ + ts2020_writereg(fe, 0x51, 0x17); + ts2020_writereg(fe, 0x51, 0x1f); + ts2020_writereg(fe, 0x50, 0x08); + ts2020_writereg(fe, 0x50, 0x00); + msleep(5); + + value = ts2020_readreg(fe, 0x3d); + value &= 0x0f; + if ((value > 4) && (value < 15)) { + value -= 3; + if (value < 4) + value = 4; + value = ((value << 3) | 0x01) & 0x79; + } + + ts2020_writereg(fe, 0x60, value); + ts2020_writereg(fe, 0x51, 0x17); + ts2020_writereg(fe, 0x51, 0x1f); + ts2020_writereg(fe, 0x50, 0x08); + ts2020_writereg(fe, 0x50, 0x00); + + /* set low-pass filter period */ + ts2020_writereg(fe, 0x04, 0x2e); + ts2020_writereg(fe, 0x51, 0x1b); + ts2020_writereg(fe, 0x51, 0x1f); + ts2020_writereg(fe, 0x50, 0x04); + ts2020_writereg(fe, 0x50, 0x00); + msleep(5); + + srate = c->symbol_rate / 1000; + + f3db = (srate << 2) / 5 + 2000; + if (srate < 5000) + f3db += 3000; + if (f3db < 7000) + f3db = 7000; + if (f3db > 40000) + f3db = 40000; + + /* set low-pass filter baseband */ + value = ts2020_readreg(fe, 0x26); + mlpf = 0x2e * 207 / ((value << 1) + 151); + mlpf_max = mlpf * 135 / 100; + mlpf_min = mlpf * 78 / 100; + if (mlpf_max > 63) + mlpf_max = 63; + + /* rounded to the closest integer */ + nlpf = ((mlpf * f3db * 1000) + (2766 * TS2020_XTAL_FREQ / 2)) + / (2766 * TS2020_XTAL_FREQ); + if (nlpf > 23) + nlpf = 23; + if (nlpf < 1) + nlpf = 1; + + /* rounded to the closest integer */ + mlpf_new = ((TS2020_XTAL_FREQ * nlpf * 2766) + + (1000 * f3db / 2)) / (1000 * f3db); + + if (mlpf_new < mlpf_min) { + nlpf++; + mlpf_new = ((TS2020_XTAL_FREQ * nlpf * 2766) + + (1000 * f3db / 2)) / (1000 * f3db); + } + + if (mlpf_new > mlpf_max) + mlpf_new = mlpf_max; + + ts2020_writereg(fe, 0x04, mlpf_new); + ts2020_writereg(fe, 0x06, nlpf); + ts2020_writereg(fe, 0x51, 0x1b); + ts2020_writereg(fe, 0x51, 0x1f); + ts2020_writereg(fe, 0x50, 0x04); + ts2020_writereg(fe, 0x50, 0x00); + msleep(5); + + /* unknown */ + ts2020_writereg(fe, 0x51, 0x1e); + ts2020_writereg(fe, 0x51, 0x1f); + ts2020_writereg(fe, 0x50, 0x01); + ts2020_writereg(fe, 0x50, 0x00); + msleep(60); + + return 0; +} + +static int ts2020_release(struct dvb_frontend *fe) +{ + struct ts2020_state *state = fe->tuner_priv; + + fe->tuner_priv = NULL; + kfree(state); + + return 0; +} + +int ts2020_get_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + u16 sig_reading, sig_strength; + u8 rfgain, bbgain; + + rfgain = ts2020_readreg(fe, 0x3d) & 0x1f; + bbgain = ts2020_readreg(fe, 0x21) & 0x1f; + + if (rfgain > 15) + rfgain = 15; + if (bbgain > 13) + bbgain = 13; + + sig_reading = rfgain * 2 + bbgain * 3; + + sig_strength = 40 + (64 - sig_reading) * 50 / 64 ; + + /* cook the value to be suitable for szap-s2 human readable output */ + *signal_strength = sig_strength * 1000; + + return 0; +} + +static struct dvb_tuner_ops ts2020_ops = { + .info = { + .name = "Montage Technology TS2020 Silicon Tuner", + .frequency_min = 950000, + .frequency_max = 2150000, + }, + + .init = ts2020_init, + .release = ts2020_release, + .set_params = ts2020_set_params, + .get_frequency = ts2020_get_frequency, + .get_rf_strength = ts2020_get_signal_strength +}; + +struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe, + const struct ts2020_config *config, struct i2c_adapter *i2c) +{ + struct ts2020_state *state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct ts2020_state), GFP_KERNEL); + if (!state) + return NULL; + + /* setup the state */ + state->tuner_address = config->tuner_address; + state->i2c = i2c; + fe->tuner_priv = state; + fe->ops.tuner_ops = ts2020_ops; + fe->ops.read_signal_strength = fe->ops.tuner_ops.get_rf_strength; + + return fe; +} +EXPORT_SYMBOL(ts2020_attach); + +MODULE_AUTHOR("Konstantin Dimitrov "); +MODULE_DESCRIPTION("Montage Technology TS2020 - Silicon tuner driver module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/ts2020.h b/drivers/media/dvb-frontends/ts2020.h new file mode 100644 index 000000000000..13a172dfd582 --- /dev/null +++ b/drivers/media/dvb-frontends/ts2020.h @@ -0,0 +1,49 @@ +/* + Montage Technology TS2020 - Silicon Tuner driver + Copyright (C) 2009-2012 Konstantin Dimitrov + + Copyright (C) 2009-2012 TurboSight.com + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef TS2020_H +#define TS2020_H + +#include + +struct ts2020_config { + u8 tuner_address; +}; + +#if defined(CONFIG_DVB_TS2020) || \ + (defined(CONFIG_DVB_TS2020_MODULE) && defined(MODULE)) + +extern struct dvb_frontend *ts2020_attach( + struct dvb_frontend *fe, + const struct ts2020_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *ts2020_attach( + struct dvb_frontend *fe, + const struct ts2020_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* TS2020_H */ -- cgit v1.2.3 From 73f0af44a9137cc2ab18e181f68f59d2ad3fe3f7 Mon Sep 17 00:00:00 2001 From: Konstantin Dimitrov Date: Sun, 23 Dec 2012 19:25:38 -0300 Subject: [media] make the other drivers take use of the new ts2020 driver make the other drivers take use of the separate ts2020 driver Signed-off-by: Konstantin Dimitrov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/ds3000.c | 1 + drivers/media/pci/cx23885/Kconfig | 1 + drivers/media/pci/cx23885/cx23885-dvb.c | 10 +++++++++- drivers/media/pci/cx88/Kconfig | 2 ++ drivers/media/pci/cx88/cx88-dvb.c | 10 +++++++++- drivers/media/pci/dm1105/Kconfig | 1 + drivers/media/pci/dm1105/dm1105.c | 10 +++++++++- drivers/media/usb/dvb-usb/Kconfig | 1 + drivers/media/usb/dvb-usb/dw2102.c | 17 +++++++++++++++++ 9 files changed, 50 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c index cd84fbd78a5b..bc17e29b54d1 100644 --- a/drivers/media/dvb-frontends/ds3000.c +++ b/drivers/media/dvb-frontends/ds3000.c @@ -27,6 +27,7 @@ #include #include "dvb_frontend.h" +#include "ts2020.h" #include "ds3000.h" static int debug; diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig index 733d6c8ebe4c..b3688aa8acc3 100644 --- a/drivers/media/pci/cx23885/Kconfig +++ b/drivers/media/pci/cx23885/Kconfig @@ -25,6 +25,7 @@ config VIDEO_CX23885 select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA10071 if MEDIA_SUBDRV_AUTOSELECT select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index a1aae5633739..a2ed0f759b0a 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -57,6 +57,7 @@ #include "netup-init.h" #include "lgdt3305.h" #include "atbm8830.h" +#include "ts2020.h" #include "ds3000.h" #include "cx23885-f300.h" #include "altera-ci.h" @@ -471,6 +472,10 @@ static struct ds3000_config tevii_ds3000_config = { .demod_address = 0x68, }; +static struct ts2020_config tevii_ts2020_config = { + .tuner_address = 0x60, +}; + static struct cx24116_config dvbworld_cx24116_config = { .demod_address = 0x05, }; @@ -1027,8 +1032,11 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend = dvb_attach(ds3000_attach, &tevii_ds3000_config, &i2c_bus->i2c_adap); - if (fe0->dvb.frontend != NULL) + if (fe0->dvb.frontend != NULL) { + dvb_attach(ts2020_attach, fe0->dvb.frontend, + &tevii_ts2020_config, &i2c_bus->i2c_adap); fe0->dvb.frontend->ops.set_voltage = f300_set_voltage; + } break; case CX23885_BOARD_DVBWORLD_2005: diff --git a/drivers/media/pci/cx88/Kconfig b/drivers/media/pci/cx88/Kconfig index d27fccbf03c4..bb05eca2da29 100644 --- a/drivers/media/pci/cx88/Kconfig +++ b/drivers/media/pci/cx88/Kconfig @@ -62,6 +62,8 @@ config VIDEO_CX88_DVB select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT + select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT ---help--- This adds support for DVB/ATSC cards based on the diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c index 666f83b2f3c0..e085851d6872 100644 --- a/drivers/media/pci/cx88/cx88-dvb.c +++ b/drivers/media/pci/cx88/cx88-dvb.c @@ -58,6 +58,7 @@ #include "stb6100.h" #include "stb6100_proc.h" #include "mb86a16.h" +#include "ts2020.h" #include "ds3000.h" MODULE_DESCRIPTION("driver for cx2388x based DVB cards"); @@ -700,6 +701,10 @@ static struct ds3000_config tevii_ds3000_config = { .set_ts_params = ds3000_set_ts_param, }; +static struct ts2020_config tevii_ts2020_config = { + .tuner_address = 0x60, +}; + static const struct stv0900_config prof_7301_stv0900_config = { .demod_address = 0x6a, /* demod_mode = 0,*/ @@ -1466,9 +1471,12 @@ static int dvb_register(struct cx8802_dev *dev) fe0->dvb.frontend = dvb_attach(ds3000_attach, &tevii_ds3000_config, &core->i2c_adap); - if (fe0->dvb.frontend != NULL) + if (fe0->dvb.frontend != NULL) { + dvb_attach(ts2020_attach, fe0->dvb.frontend, + &tevii_ts2020_config, &core->i2c_adap); fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage; + } break; case CX88_BOARD_OMICOM_SS4_PCI: case CX88_BOARD_TBS_8920: diff --git a/drivers/media/pci/dm1105/Kconfig b/drivers/media/pci/dm1105/Kconfig index 013df4e015cd..173daf0c0847 100644 --- a/drivers/media/pci/dm1105/Kconfig +++ b/drivers/media/pci/dm1105/Kconfig @@ -8,6 +8,7 @@ config DVB_DM1105 select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT select DVB_SI21XX if MEDIA_SUBDRV_AUTOSELECT select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT depends on RC_CORE help Support for cards based on the SDMC DM1105 PCI chip like diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index f288ffcc4b6b..79fe74aae1f9 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -45,6 +45,7 @@ #include "si21xx.h" #include "cx24116.h" #include "z0194a.h" +#include "ts2020.h" #include "ds3000.h" #define MODULE_NAME "dm1105" @@ -849,6 +850,10 @@ static struct ds3000_config dvbworld_ds3000_config = { .demod_address = 0x68, }; +static struct ts2020_config dvbworld_ts2020_config = { + .tuner_address = 0x60, +}; + static int __devinit frontend_init(struct dm1105_dev *dev) { int ret; @@ -898,8 +903,11 @@ static int __devinit frontend_init(struct dm1105_dev *dev) dev->fe = dvb_attach( ds3000_attach, &dvbworld_ds3000_config, &dev->i2c_adap); - if (dev->fe) + if (dev->fe) { + dvb_attach(ts2020_attach, dev->fe, + &dvbworld_ts2020_config, &dev->i2c_adap); dev->fe->ops.set_voltage = dm1105_set_voltage; + } break; case DM1105_BOARD_DVBWORLD_2002: diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index fa0b2931d305..dac7204c8020 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -267,6 +267,7 @@ config DVB_USB_DW2102 select DVB_MT312 if MEDIA_SUBDRV_AUTOSELECT select DVB_ZL10039 if MEDIA_SUBDRV_AUTOSELECT select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 937c744217c8..f61c5e367ea1 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -22,6 +22,7 @@ #include "tda1002x.h" #include "mt312.h" #include "zl10039.h" +#include "ts2020.h" #include "ds3000.h" #include "stv0900.h" #include "stv6110.h" @@ -941,6 +942,10 @@ static struct ds3000_config dw2104_ds3000_config = { .demod_address = 0x68, }; +static struct ts2020_config dw2104_ts2020_config = { + .tuner_address = 0x60, +}; + static struct stv0900_config dw2104a_stv0900_config = { .demod_address = 0x6a, .demod_mode = 0, @@ -992,6 +997,10 @@ static struct ds3000_config su3000_ds3000_config = { .ci_mode = 1, }; +static struct ts2020_config su3000_ts2020_config = { + .tuner_address = 0x60, +}; + static int dw2104_frontend_attach(struct dvb_usb_adapter *d) { struct dvb_tuner_ops *tuner_ops = NULL; @@ -1042,6 +1051,8 @@ static int dw2104_frontend_attach(struct dvb_usb_adapter *d) d->fe_adap[0].fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config, &d->dev->i2c_adap); if (d->fe_adap[0].fe != NULL) { + dvb_attach(ts2020_attach, d->fe_adap[0].fe, + &dw2104_ts2020_config, &d->dev->i2c_adap); d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; info("Attached DS3000!\n"); return 0; @@ -1154,6 +1165,9 @@ static int ds3000_frontend_attach(struct dvb_usb_adapter *d) if (d->fe_adap[0].fe == NULL) return -EIO; + dvb_attach(ts2020_attach, d->fe_adap[0].fe, &dw2104_ts2020_config, + &d->dev->i2c_adap); + st->old_set_voltage = d->fe_adap[0].fe->ops.set_voltage; d->fe_adap[0].fe->ops.set_voltage = s660_set_voltage; @@ -1214,6 +1228,9 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *d) if (d->fe_adap[0].fe == NULL) return -EIO; + dvb_attach(ts2020_attach, d->fe_adap[0].fe, &su3000_ts2020_config, + &d->dev->i2c_adap); + info("Attached DS3000!\n"); return 0; -- cgit v1.2.3 From f8c30b6f3bbea7eb8b25db7df61f8a7c24072480 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Dec 2012 19:35:44 -0200 Subject: [media] ts2020: fix two warnings added by changeset 73f0af4 drivers/media/dvb-frontends/ts2020.c: In function 'ts2020_set_params': drivers/media/dvb-frontends/ts2020.c:126:47: warning: variable 'div4' set but not used [-Wunused-but-set-variable] drivers/media/dvb-frontends/ts2020.c: At top level: drivers/media/dvb-frontends/ts2020.c:262:5: warning: no previous prototype for 'ts2020_get_signal_strength' [-Wmissing-prototypes] Cc: Konstantin Dimitrov Cc: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/ts2020.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index 8dce4ae55f09..73010ecb9866 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c @@ -123,7 +123,7 @@ static int ts2020_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; - u8 mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf, div4; + u8 mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf; u16 value, ndiv; u32 srate = 0, f3db; @@ -135,12 +135,10 @@ static int ts2020_set_params(struct dvb_frontend *fe) ts2020_writereg(fe, 0x60, 0x79); ts2020_writereg(fe, 0x08, 0x01); ts2020_writereg(fe, 0x00, 0x01); - div4 = 0; /* calculate and set freq divider */ if (c->frequency < 1146000) { ts2020_writereg(fe, 0x10, 0x11); - div4 = 1; ndiv = ((c->frequency * (6 + 8) * 4) + (TS2020_XTAL_FREQ / 2)) / TS2020_XTAL_FREQ - 1024; @@ -259,7 +257,7 @@ static int ts2020_release(struct dvb_frontend *fe) return 0; } -int ts2020_get_signal_strength(struct dvb_frontend *fe, +static int ts2020_get_signal_strength(struct dvb_frontend *fe, u16 *signal_strength) { u16 sig_reading, sig_strength; -- cgit v1.2.3 From 7e5d74ee116d9622fc4ef9f5100692485ae286cf Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Wed, 26 Dec 2012 15:12:37 -0300 Subject: [media] em28xx: rename module parameter prefer_bulk to usb_xfer_mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we have now 3 modes (auto/isoc/bulk), usb_xfer_mode is more suitable than prefer_bulk. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index ad6c800d9a40..f5cac47d099a 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -61,9 +61,10 @@ static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); -static int prefer_bulk = -1; -module_param(prefer_bulk, int, 0444); -MODULE_PARM_DESC(prefer_bulk, "prefer USB bulk transfers (-1 = auto, 0 = isoc, 1 = bulk)"); +static int usb_xfer_mode = -1; +module_param(usb_xfer_mode, int, 0444); +MODULE_PARM_DESC(usb_xfer_mode, + "USB transfer mode for frame data (-1 = auto, 0 = prefer isoc, 1 = prefer bulk)"); /* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */ @@ -3394,13 +3395,13 @@ static int em28xx_usb_probe(struct usb_interface *interface, goto unlock_and_free; } - if (prefer_bulk < 0) { + if (usb_xfer_mode < 0) { if (dev->board.is_webcam) try_bulk = 1; else try_bulk = 0; } else { - try_bulk = prefer_bulk > 0; + try_bulk = usb_xfer_mode > 0; } /* Select USB transfer types to use */ -- cgit v1.2.3 From d58f4f27282e10b25daee53045a7e839bd4178a5 Mon Sep 17 00:00:00 2001 From: Rémi Cardona Date: Fri, 28 Sep 2012 08:59:29 -0300 Subject: [media] ds3000: bail out early on i2c failures during firmware load MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - if kmalloc() returns NULL, we can return immediately without trying to kfree() a NULL pointer. - if i2c_transfer() fails, error out immediately instead of trying to upload the remaining bytes of the firmware. - the error code is then properly propagated down to ds3000_initfe(). Signed-off-by: Rémi Cardona Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/ds3000.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c index bc17e29b54d1..fded9b67456c 100644 --- a/drivers/media/dvb-frontends/ds3000.c +++ b/drivers/media/dvb-frontends/ds3000.c @@ -272,15 +272,14 @@ static int ds3000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) static int ds3000_writeFW(struct ds3000_state *state, int reg, const u8 *data, u16 len) { - int i, ret = -EREMOTEIO; + int i, ret = 0; struct i2c_msg msg; u8 *buf; buf = kmalloc(33, GFP_KERNEL); if (buf == NULL) { printk(KERN_ERR "Unable to kmalloc\n"); - ret = -ENOMEM; - goto error; + return -ENOMEM; } *(buf) = reg; @@ -300,8 +299,10 @@ static int ds3000_writeFW(struct ds3000_state *state, int reg, printk(KERN_ERR "%s: write error(err == %i, " "reg == 0x%02x\n", __func__, ret, reg); ret = -EREMOTEIO; + goto error; } } + ret = 0; error: kfree(buf); @@ -384,6 +385,7 @@ static int ds3000_load_firmware(struct dvb_frontend *fe, const struct firmware *fw) { struct ds3000_state *state = fe->demodulator_priv; + int ret = 0; dprintk("%s\n", __func__); dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n", @@ -396,10 +398,10 @@ static int ds3000_load_firmware(struct dvb_frontend *fe, /* Begin the firmware load process */ ds3000_writereg(state, 0xb2, 0x01); /* write the entire firmware */ - ds3000_writeFW(state, 0xb0, fw->data, fw->size); + ret = ds3000_writeFW(state, 0xb0, fw->data, fw->size); ds3000_writereg(state, 0xb2, 0x00); - return 0; + return ret; } static int ds3000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) -- cgit v1.2.3 From 955d00ac7a193e9c29a897cd5d731a84e3850217 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 8 May 2012 03:53:17 -0300 Subject: [media] TeVii DVB-S s421 and s632 cards support DVB-S chip is Montage m88rs2000, so initial patch is simple. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/dw2102.c | 106 ++++++++++++++++++++++++++++++++++--- 1 file changed, 99 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index f61c5e367ea1..5ae3529f81ad 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -1,9 +1,9 @@ /* DVB USB framework compliant Linux driver for the * DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101, - * TeVii S600, S630, S650, S660, S480, + * TeVii S600, S630, S650, S660, S480, S421, S632 * Prof 1100, 7500, * Geniatech SU3000 Cards - * Copyright (C) 2008-2011 Igor M. Liplianin (liplianin@me.by) + * Copyright (C) 2008-2012 Igor M. Liplianin (liplianin@me.by) * * 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 @@ -28,6 +28,7 @@ #include "stv6110.h" #include "stb6100.h" #include "stb6100_proc.h" +#include "m88rs2000.h" #ifndef USB_PID_DW2102 #define USB_PID_DW2102 0x2102 @@ -69,6 +70,14 @@ #define USB_PID_PROF_1100 0xb012 #endif +#ifndef USB_PID_TEVII_S421 +#define USB_PID_TEVII_S421 0xd421 +#endif + +#ifndef USB_PID_TEVII_S632 +#define USB_PID_TEVII_S632 0xd632 +#endif + #define DW210X_READ_MSG 0 #define DW210X_WRITE_MSG 1 @@ -544,7 +553,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], } /*case 0x55: cx24116 case 0x6a: stv0903 - case 0x68: ds3000, stv0903 + case 0x68: ds3000, stv0903, rs2000 case 0x60: ts2020, stv6110, stb6100 case 0xa0: eeprom */ default: { @@ -1001,6 +1010,38 @@ static struct ts2020_config su3000_ts2020_config = { .tuner_address = 0x60, }; +static u8 m88rs2000_inittab[] = { + DEMOD_WRITE, 0x9a, 0x30, + DEMOD_WRITE, 0x00, 0x01, + WRITE_DELAY, 0x19, 0x00, + DEMOD_WRITE, 0x00, 0x00, + DEMOD_WRITE, 0x9a, 0xb0, + DEMOD_WRITE, 0x81, 0xc1, + TUNER_WRITE, 0x42, 0x73, + TUNER_WRITE, 0x05, 0x07, + TUNER_WRITE, 0x20, 0x27, + TUNER_WRITE, 0x07, 0x02, + TUNER_WRITE, 0x11, 0xff, + TUNER_WRITE, 0x60, 0xf9, + TUNER_WRITE, 0x08, 0x01, + TUNER_WRITE, 0x00, 0x41, + DEMOD_WRITE, 0x81, 0x81, + DEMOD_WRITE, 0x86, 0xc6, + DEMOD_WRITE, 0x9a, 0x30, + DEMOD_WRITE, 0xf0, 0x80, + DEMOD_WRITE, 0xf1, 0xbf, + DEMOD_WRITE, 0xb0, 0x45, + DEMOD_WRITE, 0xb2, 0x01, + DEMOD_WRITE, 0x9a, 0xb0, + 0xff, 0xaa, 0xff +}; + +static struct m88rs2000_config s421_m88rs2000_config = { + .demod_addr = 0x68, + .tuner_addr = 0x60, + .inittab = m88rs2000_inittab, +}; + static int dw2104_frontend_attach(struct dvb_usb_adapter *d) { struct dvb_tuner_ops *tuner_ops = NULL; @@ -1236,6 +1277,24 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *d) return 0; } +static int m88rs2000_frontend_attach(struct dvb_usb_adapter *d) +{ + u8 obuf[] = { 0x51 }; + u8 ibuf[] = { 0 }; + + if (dvb_usb_generic_rw(d->dev, obuf, 1, ibuf, 1, 0) < 0) + err("command 0x51 transfer failed."); + + d->fe_adap[0].fe = dvb_attach(m88rs2000_attach, &s421_m88rs2000_config, + &d->dev->i2c_adap); + if (d->fe_adap[0].fe == NULL) + return -EIO; + + info("Attached m88rs2000!\n"); + + return 0; +} + static int dw2102_tuner_attach(struct dvb_usb_adapter *adap) { dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, @@ -1473,6 +1532,8 @@ enum dw2102_table_entry { TEVII_S480_1, TEVII_S480_2, X3M_SPC1400HD, + TEVII_S421, + TEVII_S632, }; static struct usb_device_id dw2102_table[] = { @@ -1491,6 +1552,8 @@ static struct usb_device_id dw2102_table[] = { [TEVII_S480_1] = {USB_DEVICE(0x9022, USB_PID_TEVII_S480_1)}, [TEVII_S480_2] = {USB_DEVICE(0x9022, USB_PID_TEVII_S480_2)}, [X3M_SPC1400HD] = {USB_DEVICE(0x1f4d, 0x3100)}, + [TEVII_S421] = {USB_DEVICE(0x9022, USB_PID_TEVII_S421)}, + [TEVII_S632] = {USB_DEVICE(0x9022, USB_PID_TEVII_S632)}, { } }; @@ -1839,6 +1902,19 @@ static struct dvb_usb_device_description d7500 = { {NULL}, }; +struct dvb_usb_device_properties *s421; +static struct dvb_usb_device_description d421 = { + "TeVii S421 PCI", + {&dw2102_table[TEVII_S421], NULL}, + {NULL}, +}; + +static struct dvb_usb_device_description d632 = { + "TeVii S632 USB", + {&dw2102_table[TEVII_S632], NULL}, + {NULL}, +}; + static struct dvb_usb_device_properties su3000_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, @@ -1936,6 +2012,20 @@ static int dw2102_probe(struct usb_interface *intf, p7500->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table); p7500->adapter->fe[0].frontend_attach = prof_7500_frontend_attach; + + s421 = kmemdup(&su3000_properties, + sizeof(struct dvb_usb_device_properties), GFP_KERNEL); + if (!s421) { + kfree(p1100); + kfree(s660); + kfree(p7500); + return -ENOMEM; + } + s421->num_device_descs = 2; + s421->devices[0] = d421; + s421->devices[1] = d632; + s421->adapter->fe[0].frontend_attach = m88rs2000_frontend_attach; + if (0 == dvb_usb_device_init(intf, &dw2102_properties, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &dw2104_properties, @@ -1950,6 +2040,8 @@ static int dw2102_probe(struct usb_interface *intf, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, p7500, THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, s421, + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &su3000_properties, THIS_MODULE, NULL, adapter_nr)) return 0; @@ -1968,10 +2060,10 @@ module_usb_driver(dw2102_driver); MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by"); MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104," - " DVB-C 3101 USB2.0," - " TeVii S600, S630, S650, S660, S480," - " Prof 1100, 7500 USB2.0," - " Geniatech SU3000 devices"); + " DVB-C 3101 USB2.0," + " TeVii S600, S630, S650, S660, S480, S421, S632" + " Prof 1100, 7500 USB2.0," + " Geniatech SU3000 devices"); MODULE_VERSION("0.1"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(DW2101_FIRMWARE); -- cgit v1.2.3 From 081416e62d516a6412225751c9c4a3807b2374b9 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 8 May 2012 04:08:04 -0300 Subject: [media] TeVii DVB-S s421 and s632 cards support, rs2000 part One register needs to be changed to TS to work. So we use separate inittab. Signed-off-by: Igor M. Liplianin Acked-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/m88rs2000.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index 633815ed90ca..5d3d5dd62daa 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -458,7 +458,11 @@ static int m88rs2000_init(struct dvb_frontend *fe) deb_info("m88rs2000: init chip\n"); /* Setup frontend from shutdown/cold */ - ret = m88rs2000_tab_set(state, m88rs2000_setup); + if (state->config->inittab) + ret = m88rs2000_tab_set(state, + (struct inittab *)state->config->inittab); + else + ret = m88rs2000_tab_set(state, m88rs2000_setup); return ret; } -- cgit v1.2.3 From 1d6ca29db8b1f213de880b10ac28aed0a19c1d4a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sun, 2 Dec 2012 06:43:13 -0300 Subject: [media] mantis: cleanup NULL checking in mantis_ca_exit() Smatch complainst that the call to mantis_evmgr_exit() dereferences "ca" but then we check it for NULL on the next line. I've moved the NULL check forward to avoid that. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/mantis/mantis_ca.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/mantis/mantis_ca.c b/drivers/media/pci/mantis/mantis_ca.c index 3d7046909009..60c6c2f24066 100644 --- a/drivers/media/pci/mantis/mantis_ca.c +++ b/drivers/media/pci/mantis/mantis_ca.c @@ -198,11 +198,12 @@ void mantis_ca_exit(struct mantis_pci *mantis) struct mantis_ca *ca = mantis->mantis_ca; dprintk(MANTIS_DEBUG, 1, "Mantis CA exit"); + if (!ca) + return; mantis_evmgr_exit(ca); dprintk(MANTIS_ERROR, 1, "Unregistering EN50221 device"); - if (ca) - dvb_ca_en50221_release(&ca->en50221); + dvb_ca_en50221_release(&ca->en50221); kfree(ca); } -- cgit v1.2.3 From 250539a36f69aa51e4c68a1f92c4ed2bcc042a1a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 3 Dec 2012 02:44:31 -0300 Subject: [media] s3c-camif: Add missing version.h header file versioncheck script complains about missing linux/version.h header file. Signed-off-by: Sachin Kamat Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s3c-camif/camif-core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c index 0dd65376c067..37c9b6faae26 100644 --- a/drivers/media/platform/s3c-camif/camif-core.c +++ b/drivers/media/platform/s3c-camif/camif-core.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include -- cgit v1.2.3 From e669c8d379567d42d62989f890f926f7b1b8f2a1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Dec 2012 21:21:14 -0200 Subject: [media] blackfin Kconfig: select is evil; use, instead depends on Select is evil as it has issues with dependencies. Better to convert it to use depends on. That fixes a breakage with out-of-tree compilation of the media tree. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/blackfin/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/blackfin/Kconfig b/drivers/media/platform/blackfin/Kconfig index 519990e17122..cc239972fa2c 100644 --- a/drivers/media/platform/blackfin/Kconfig +++ b/drivers/media/platform/blackfin/Kconfig @@ -2,7 +2,6 @@ config VIDEO_BLACKFIN_CAPTURE tristate "Blackfin Video Capture Driver" depends on VIDEO_V4L2 && BLACKFIN && I2C select VIDEOBUF2_DMA_CONTIG - select VIDEO_BLACKFIN_PPI help V4L2 bridge driver for Blackfin video capture device. Choose PPI or EPPI as its interface. @@ -12,3 +11,5 @@ config VIDEO_BLACKFIN_CAPTURE config VIDEO_BLACKFIN_PPI tristate + depends on VIDEO_BLACKFIN_CAPTURE + default VIDEO_BLACKFIN_CAPTURE -- cgit v1.2.3 From 4834f4d1ff1dc574024e1a6de920ea99571090ff Mon Sep 17 00:00:00 2001 From: Alexey Klimov Date: Mon, 12 Nov 2012 02:56:37 -0300 Subject: [media] media: add driver for Masterkit MA901 usb radio This patch creates a new usb-radio driver, radio-ma901.c, that supports Masterkit MA 901 USB FM radio devices. This device plugs into both the USB and an analog audio input or headphones, so this thing only deals with initialization and frequency setting. Signed-off-by: Alexey Klimov Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/Kconfig | 12 + drivers/media/radio/Makefile | 1 + drivers/media/radio/radio-ma901.c | 460 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 473 insertions(+) create mode 100644 drivers/media/radio/radio-ma901.c (limited to 'drivers') diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 8090b87b3066..ead992853730 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -124,6 +124,18 @@ config USB_KEENE To compile this driver as a module, choose M here: the module will be called radio-keene. +config USB_MA901 + tristate "Masterkit MA901 USB FM radio support" + depends on USB && VIDEO_V4L2 + ---help--- + Say Y here if you want to connect this type of radio to your + computer's USB port. Note that the audio is not digital, and + you must connect the line out connector to a sound card or a + set of speakers or headphones. + + To compile this driver as a module, choose M here: the + module will be called radio-ma901. + config RADIO_TEA5764 tristate "TEA5764 I2C FM radio support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index c03ce4fe74e9..303eaebdb85a 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_RADIO_SI470X) += si470x/ obj-$(CONFIG_USB_MR800) += radio-mr800.o obj-$(CONFIG_USB_KEENE) += radio-keene.o +obj-$(CONFIG_USB_MA901) += radio-ma901.o obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o obj-$(CONFIG_RADIO_SAA7706H) += saa7706h.o obj-$(CONFIG_RADIO_TEF6862) += tef6862.o diff --git a/drivers/media/radio/radio-ma901.c b/drivers/media/radio/radio-ma901.c new file mode 100644 index 000000000000..c61f590029ad --- /dev/null +++ b/drivers/media/radio/radio-ma901.c @@ -0,0 +1,460 @@ +/* + * Driver for the MasterKit MA901 USB FM radio. This device plugs + * into the USB port and an analog audio input or headphones, so this thing + * only deals with initialization, frequency setting, volume. + * + * Copyright (c) 2012 Alexey Klimov + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_AUTHOR "Alexey Klimov " +#define DRIVER_DESC "Masterkit MA901 USB FM radio driver" +#define DRIVER_VERSION "0.0.1" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); + +#define USB_MA901_VENDOR 0x16c0 +#define USB_MA901_PRODUCT 0x05df + +/* dev_warn macro with driver name */ +#define MA901_DRIVER_NAME "radio-ma901" +#define ma901radio_dev_warn(dev, fmt, arg...) \ + dev_warn(dev, MA901_DRIVER_NAME " - " fmt, ##arg) + +#define ma901radio_dev_err(dev, fmt, arg...) \ + dev_err(dev, MA901_DRIVER_NAME " - " fmt, ##arg) + +/* Probably USB_TIMEOUT should be modified in module parameter */ +#define BUFFER_LENGTH 8 +#define USB_TIMEOUT 500 + +#define FREQ_MIN 87.5 +#define FREQ_MAX 108.0 +#define FREQ_MUL 16000 + +#define MA901_VOLUME_MAX 16 +#define MA901_VOLUME_MIN 0 + +/* Commands that device should understand + * List isn't full and will be updated with implementation of new functions + */ +#define MA901_RADIO_SET_FREQ 0x03 +#define MA901_RADIO_SET_VOLUME 0x04 +#define MA901_RADIO_SET_MONO_STEREO 0x05 + +/* Comfortable defines for ma901radio_set_stereo */ +#define MA901_WANT_STEREO 0x50 +#define MA901_WANT_MONO 0xd0 + +/* module parameter */ +static int radio_nr = -1; +module_param(radio_nr, int, 0); +MODULE_PARM_DESC(radio_nr, "Radio file number"); + +/* Data for one (physical) device */ +struct ma901radio_device { + /* reference to USB and video device */ + struct usb_device *usbdev; + struct usb_interface *intf; + struct video_device vdev; + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; + + u8 *buffer; + struct mutex lock; /* buffer locking */ + int curfreq; + u16 volume; + int stereo; + bool muted; +}; + +static inline struct ma901radio_device *to_ma901radio_dev(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct ma901radio_device, v4l2_dev); +} + +/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ +static int ma901radio_set_freq(struct ma901radio_device *radio, int freq) +{ + unsigned int freq_send = 0x300 + (freq >> 5) / 25; + int retval; + + radio->buffer[0] = 0x0a; + radio->buffer[1] = MA901_RADIO_SET_FREQ; + radio->buffer[2] = ((freq_send >> 8) & 0xff) + 0x80; + radio->buffer[3] = freq_send & 0xff; + radio->buffer[4] = 0x00; + radio->buffer[5] = 0x00; + radio->buffer[6] = 0x00; + radio->buffer[7] = 0x00; + + retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), + 9, 0x21, 0x0300, 0, + radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); + if (retval < 0) + return retval; + + radio->curfreq = freq; + return 0; +} + +static int ma901radio_set_volume(struct ma901radio_device *radio, u16 vol_to_set) +{ + int retval; + + radio->buffer[0] = 0x0a; + radio->buffer[1] = MA901_RADIO_SET_VOLUME; + radio->buffer[2] = 0xc2; + radio->buffer[3] = vol_to_set + 0x20; + radio->buffer[4] = 0x00; + radio->buffer[5] = 0x00; + radio->buffer[6] = 0x00; + radio->buffer[7] = 0x00; + + retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), + 9, 0x21, 0x0300, 0, + radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); + if (retval < 0) + return retval; + + radio->volume = vol_to_set; + return retval; +} + +static int ma901_set_stereo(struct ma901radio_device *radio, u8 stereo) +{ + int retval; + + radio->buffer[0] = 0x0a; + radio->buffer[1] = MA901_RADIO_SET_MONO_STEREO; + radio->buffer[2] = stereo; + radio->buffer[3] = 0x00; + radio->buffer[4] = 0x00; + radio->buffer[5] = 0x00; + radio->buffer[6] = 0x00; + radio->buffer[7] = 0x00; + + retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), + 9, 0x21, 0x0300, 0, + radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); + + if (retval < 0) + return retval; + + if (stereo == MA901_WANT_STEREO) + radio->stereo = V4L2_TUNER_MODE_STEREO; + else + radio->stereo = V4L2_TUNER_MODE_MONO; + + return retval; +} + +/* Handle unplugging the device. + * We call video_unregister_device in any case. + * The last function called in this procedure is + * usb_ma901radio_device_release. + */ +static void usb_ma901radio_disconnect(struct usb_interface *intf) +{ + struct ma901radio_device *radio = to_ma901radio_dev(usb_get_intfdata(intf)); + + mutex_lock(&radio->lock); + video_unregister_device(&radio->vdev); + usb_set_intfdata(intf, NULL); + v4l2_device_disconnect(&radio->v4l2_dev); + mutex_unlock(&radio->lock); + v4l2_device_put(&radio->v4l2_dev); +} + +/* vidioc_querycap - query device capabilities */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + struct ma901radio_device *radio = video_drvdata(file); + + strlcpy(v->driver, "radio-ma901", sizeof(v->driver)); + strlcpy(v->card, "Masterkit MA901 USB FM Radio", sizeof(v->card)); + usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); + v->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; + v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +/* vidioc_g_tuner - get tuner attributes */ +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + struct ma901radio_device *radio = video_drvdata(file); + + if (v->index > 0) + return -EINVAL; + + v->signal = 0; + + /* TODO: the same words like in _probe() goes here. + * When receiving of stats will be implemented then we can call + * ma901radio_get_stat(). + * retval = ma901radio_get_stat(radio, &is_stereo, &v->signal); + */ + + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = FREQ_MIN * FREQ_MUL; + v->rangehigh = FREQ_MAX * FREQ_MUL; + v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; + /* v->rxsubchans = is_stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; */ + v->audmode = radio->stereo ? + V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO; + return 0; +} + +/* vidioc_s_tuner - set tuner attributes */ +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + struct ma901radio_device *radio = video_drvdata(file); + + if (v->index > 0) + return -EINVAL; + + /* mono/stereo selector */ + switch (v->audmode) { + case V4L2_TUNER_MODE_MONO: + return ma901_set_stereo(radio, MA901_WANT_MONO); + default: + return ma901_set_stereo(radio, MA901_WANT_STEREO); + } +} + +/* vidioc_s_frequency - set tuner radio frequency */ +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct ma901radio_device *radio = video_drvdata(file); + + if (f->tuner != 0) + return -EINVAL; + + return ma901radio_set_freq(radio, clamp_t(unsigned, f->frequency, + FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL)); +} + +/* vidioc_g_frequency - get tuner radio frequency */ +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct ma901radio_device *radio = video_drvdata(file); + + if (f->tuner != 0) + return -EINVAL; + f->frequency = radio->curfreq; + + return 0; +} + +static int usb_ma901radio_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ma901radio_device *radio = + container_of(ctrl->handler, struct ma901radio_device, hdl); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: /* set volume */ + return ma901radio_set_volume(radio, (u16)ctrl->val); + } + + return -EINVAL; +} + +/* TODO: Should we really need to implement suspend and resume functions? + * Radio has it's own memory and will continue playing if power is present + * on usb port and on resume it will start to play again based on freq, volume + * values in device memory. + */ +static int usb_ma901radio_suspend(struct usb_interface *intf, pm_message_t message) +{ + return 0; +} + +static int usb_ma901radio_resume(struct usb_interface *intf) +{ + return 0; +} + +static const struct v4l2_ctrl_ops usb_ma901radio_ctrl_ops = { + .s_ctrl = usb_ma901radio_s_ctrl, +}; + +/* File system interface */ +static const struct v4l2_file_operations usb_ma901radio_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops usb_ma901radio_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static void usb_ma901radio_release(struct v4l2_device *v4l2_dev) +{ + struct ma901radio_device *radio = to_ma901radio_dev(v4l2_dev); + + v4l2_ctrl_handler_free(&radio->hdl); + v4l2_device_unregister(&radio->v4l2_dev); + kfree(radio->buffer); + kfree(radio); +} + +/* check if the device is present and register with v4l and usb if it is */ +static int usb_ma901radio_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct ma901radio_device *radio; + int retval = 0; + + radio = kzalloc(sizeof(struct ma901radio_device), GFP_KERNEL); + if (!radio) { + dev_err(&intf->dev, "kzalloc for ma901radio_device failed\n"); + retval = -ENOMEM; + goto err; + } + + radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL); + if (!radio->buffer) { + dev_err(&intf->dev, "kmalloc for radio->buffer failed\n"); + retval = -ENOMEM; + goto err_nobuf; + } + + retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); + if (retval < 0) { + dev_err(&intf->dev, "couldn't register v4l2_device\n"); + goto err_v4l2; + } + + v4l2_ctrl_handler_init(&radio->hdl, 1); + + /* TODO:It looks like this radio doesn't have mute/unmute control + * and windows program just emulate it using volume control. + * Let's plan to do the same in this driver. + * + * v4l2_ctrl_new_std(&radio->hdl, &usb_ma901radio_ctrl_ops, + * V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + */ + + v4l2_ctrl_new_std(&radio->hdl, &usb_ma901radio_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, MA901_VOLUME_MIN, + MA901_VOLUME_MAX, 1, MA901_VOLUME_MAX); + + if (radio->hdl.error) { + retval = radio->hdl.error; + dev_err(&intf->dev, "couldn't register control\n"); + goto err_ctrl; + } + mutex_init(&radio->lock); + + radio->v4l2_dev.ctrl_handler = &radio->hdl; + radio->v4l2_dev.release = usb_ma901radio_release; + strlcpy(radio->vdev.name, radio->v4l2_dev.name, + sizeof(radio->vdev.name)); + radio->vdev.v4l2_dev = &radio->v4l2_dev; + radio->vdev.fops = &usb_ma901radio_fops; + radio->vdev.ioctl_ops = &usb_ma901radio_ioctl_ops; + radio->vdev.release = video_device_release_empty; + radio->vdev.lock = &radio->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags); + + radio->usbdev = interface_to_usbdev(intf); + radio->intf = intf; + usb_set_intfdata(intf, &radio->v4l2_dev); + radio->curfreq = 95.21 * FREQ_MUL; + + video_set_drvdata(&radio->vdev, radio); + + /* TODO: we can get some statistics (freq, volume) from device + * but it's not implemented yet. After insertion in usb-port radio + * setups frequency and starts playing without any initialization. + * So we don't call usb_ma901radio_init/get_stat() here. + * retval = usb_ma901radio_init(radio); + */ + + retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, + radio_nr); + if (retval < 0) { + dev_err(&intf->dev, "could not register video device\n"); + goto err_vdev; + } + + return 0; + +err_vdev: + v4l2_ctrl_handler_free(&radio->hdl); +err_ctrl: + v4l2_device_unregister(&radio->v4l2_dev); +err_v4l2: + kfree(radio->buffer); +err_nobuf: + kfree(radio); +err: + return retval; +} + +/* USB Device ID List */ +static struct usb_device_id usb_ma901radio_device_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(USB_MA901_VENDOR, USB_MA901_PRODUCT, + USB_CLASS_HID, 0, 0) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, usb_ma901radio_device_table); + +/* USB subsystem interface */ +static struct usb_driver usb_ma901radio_driver = { + .name = MA901_DRIVER_NAME, + .probe = usb_ma901radio_probe, + .disconnect = usb_ma901radio_disconnect, + .suspend = usb_ma901radio_suspend, + .resume = usb_ma901radio_resume, + .reset_resume = usb_ma901radio_resume, + .id_table = usb_ma901radio_device_table, +}; + +module_usb_driver(usb_ma901radio_driver); -- cgit v1.2.3 From 0322bd3980b3ebf7dde8474e22614cb443d6479a Mon Sep 17 00:00:00 2001 From: Alexey Klimov Date: Mon, 12 Nov 2012 02:57:03 -0300 Subject: [hid] usb hid quirks for Masterkit MA901 usb radio Don't let Masterkit MA901 USB radio be handled by usb hid drivers. This device will be handled by radio-ma901.c driver. Signed-off-by: Alexey Klimov Acked-by: Hans Verkuil Acked-by: Jiri Kosina Signed-off-by: Mauro Carvalho Chehab --- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 3 +++ 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index eb2ee11b6412..ccca9ac34dca 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2070,6 +2070,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HYBRID) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HEATCONTROL) }, { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_BEATPAD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MASTERKIT, USB_DEVICE_ID_MASTERKIT_MA901RADIO) }, { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS) }, { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 4dfa605e2d14..f39be08c1358 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -551,6 +551,9 @@ #define USB_VENDOR_ID_MADCATZ 0x0738 #define USB_DEVICE_ID_MADCATZ_BEATPAD 0x4540 +#define USB_VENDOR_ID_MASTERKIT 0x16c0 +#define USB_DEVICE_ID_MASTERKIT_MA901RADIO 0x05df + #define USB_VENDOR_ID_MCC 0x09db #define USB_DEVICE_ID_MCC_PMD1024LS 0x0076 #define USB_DEVICE_ID_MCC_PMD1208LS 0x007a -- cgit v1.2.3 From c19bec500168108bf28710fae304523679ffb40f Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Wed, 26 Dec 2012 12:23:26 -0300 Subject: [media] vivi: Constify structures Most of *_ops and other structures in vivi.c were already declared const but some have not. Constify and code/data will take less space: $ size drivers/media/platform/vivi.o text data bss dec hex filename before: 12569 248 8 12825 3219 drivers/media/platform/vivi.o after: 12308 20 8 12336 3030 drivers/media/platform/vivi.o i.e. vivi.o is now ~500 bytes less. Signed-off-by: Kirill Smelkov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivi.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index ec6508909f02..8a33a712f480 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -91,13 +91,13 @@ static const struct v4l2_fract ------------------------------------------------------------------*/ struct vivi_fmt { - char *name; + const char *name; u32 fourcc; /* v4l2 format id */ u8 depth; bool is_yuv; }; -static struct vivi_fmt formats[] = { +static const struct vivi_fmt formats[] = { { .name = "4:2:2, packed, YUYV", .fourcc = V4L2_PIX_FMT_YUYV, @@ -164,9 +164,9 @@ static struct vivi_fmt formats[] = { }, }; -static struct vivi_fmt *__get_format(u32 pixelformat) +static const struct vivi_fmt *__get_format(u32 pixelformat) { - struct vivi_fmt *fmt; + const struct vivi_fmt *fmt; unsigned int k; for (k = 0; k < ARRAY_SIZE(formats); k++) { @@ -181,7 +181,7 @@ static struct vivi_fmt *__get_format(u32 pixelformat) return &formats[k]; } -static struct vivi_fmt *get_format(struct v4l2_format *f) +static const struct vivi_fmt *get_format(struct v4l2_format *f) { return __get_format(f->fmt.pix.pixelformat); } @@ -191,7 +191,7 @@ struct vivi_buffer { /* common v4l buffer stuff -- must be first */ struct vb2_buffer vb; struct list_head list; - struct vivi_fmt *fmt; + const struct vivi_fmt *fmt; }; struct vivi_dmaqueue { @@ -250,7 +250,7 @@ struct vivi_dev { int input; /* video capture */ - struct vivi_fmt *fmt; + const struct vivi_fmt *fmt; struct v4l2_fract timeperframe; unsigned int width, height; struct vb2_queue vb_vidq; @@ -297,7 +297,7 @@ struct bar_std { /* Maximum number of bars are 10 - otherwise, the input print code should be modified */ -static struct bar_std bars[] = { +static const struct bar_std bars[] = { { /* Standard ITU-R color bar sequence */ { COLOR_WHITE, COLOR_AMBER, COLOR_CYAN, COLOR_GREEN, COLOR_MAGENTA, COLOR_RED, COLOR_BLUE, COLOR_BLACK, COLOR_BLACK } @@ -926,7 +926,7 @@ static void vivi_unlock(struct vb2_queue *vq) } -static struct vb2_ops vivi_video_qops = { +static const struct vb2_ops vivi_video_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, @@ -957,7 +957,7 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - struct vivi_fmt *fmt; + const struct vivi_fmt *fmt; if (f->index >= ARRAY_SIZE(formats)) return -EINVAL; @@ -993,7 +993,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct vivi_dev *dev = video_drvdata(file); - struct vivi_fmt *fmt; + const struct vivi_fmt *fmt; fmt = get_format(f); if (!fmt) { @@ -1102,7 +1102,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) static int vidioc_enum_frameintervals(struct file *file, void *priv, struct v4l2_frmivalenum *fival) { - struct vivi_fmt *fmt; + const struct vivi_fmt *fmt; if (fival->index) return -EINVAL; @@ -1330,7 +1330,7 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = { .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; -static struct video_device vivi_template = { +static const struct video_device vivi_template = { .name = "vivi", .fops = &vivi_fops, .ioctl_ops = &vivi_ioctl_ops, -- cgit v1.2.3 From a3e7ad256cae84dfbb8d016a5838c72f3b695883 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 28 Dec 2012 19:40:04 -0300 Subject: [media] dw2102: autoselect DVB_M88RS2000 Patch to select rs2000 module to compile automatically for TeVii S421 and S632 cards. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index dac7204c8020..c423eb80c3a5 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -271,6 +271,7 @@ config DVB_USB_DW2102 select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT + select DVB_M88RS2000 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the DvbWorld, TeVii, Prof DVB-S/S2 USB2.0 receivers. -- cgit v1.2.3 From 38f7889cea9d5754493fa601a2d466ba33f13f55 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 28 Dec 2012 19:40:16 -0300 Subject: [media] m88rs2000: SNR, BER implemented Trivial patch to implement SNR, BER, UCB counter for Montage rs2000 demod. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/m88rs2000.c | 44 +++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index 5d3d5dd62daa..df9e7dd6fe74 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -492,12 +492,29 @@ static int m88rs2000_read_status(struct dvb_frontend *fe, fe_status_t *status) return 0; } -/* Extact code for these unknown but lmedm04 driver uses interupt callbacks */ - static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber) { - deb_info("m88rs2000_read_ber %d\n", *ber); - *ber = 0; + struct m88rs2000_state *state = fe->demodulator_priv; + u8 tmp0, tmp1; + + m88rs2000_demod_write(state, 0x9a, 0x30); + tmp0 = m88rs2000_demod_read(state, 0xd8); + if ((tmp0 & 0x10) != 0) { + m88rs2000_demod_write(state, 0x9a, 0xb0); + *ber = 0xffffffff; + return 0; + } + + *ber = (m88rs2000_demod_read(state, 0xd7) << 8) | + m88rs2000_demod_read(state, 0xd6); + + tmp1 = m88rs2000_demod_read(state, 0xd9); + m88rs2000_demod_write(state, 0xd9, (tmp1 & ~7) | 4); + /* needs twice */ + m88rs2000_demod_write(state, 0xd8, (tmp0 & ~8) | 0x30); + m88rs2000_demod_write(state, 0xd8, (tmp0 & ~8) | 0x30); + m88rs2000_demod_write(state, 0x9a, 0xb0); + return 0; } @@ -510,15 +527,26 @@ static int m88rs2000_read_signal_strength(struct dvb_frontend *fe, static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr) { - deb_info("m88rs2000_read_snr %d\n", *snr); - *snr = 0; + struct m88rs2000_state *state = fe->demodulator_priv; + + *snr = 512 * m88rs2000_demod_read(state, 0x65); + return 0; } static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { - deb_info("m88rs2000_read_ber %d\n", *ucblocks); - *ucblocks = 0; + struct m88rs2000_state *state = fe->demodulator_priv; + u8 tmp; + + *ucblocks = (m88rs2000_demod_read(state, 0xd5) << 8) | + m88rs2000_demod_read(state, 0xd4); + tmp = m88rs2000_demod_read(state, 0xd8); + m88rs2000_demod_write(state, 0xd8, tmp & ~0x20); + /* needs two times */ + m88rs2000_demod_write(state, 0xd8, tmp | 0x20); + m88rs2000_demod_write(state, 0xd8, tmp | 0x20); + return 0; } -- cgit v1.2.3 From 43385c8a645a25ddef7a45df8786ff26806f7e5d Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 28 Dec 2012 19:40:24 -0300 Subject: [media] ds3000: lock led procedure added TeVii s660 and others have LED for lock indication. Let's use it in right order. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/ds3000.c | 12 ++++++++++++ drivers/media/dvb-frontends/ds3000.h | 2 ++ drivers/media/usb/dvb-usb/dw2102.c | 7 ++++++- 3 files changed, 20 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c index fded9b67456c..d128f85844e7 100644 --- a/drivers/media/dvb-frontends/ds3000.c +++ b/drivers/media/dvb-frontends/ds3000.c @@ -460,6 +460,9 @@ static int ds3000_read_status(struct dvb_frontend *fe, fe_status_t* status) return 1; } + if (state->config->set_lock_led) + state->config->set_lock_led(fe, *status == 0 ? 0 : 1); + dprintk("%s: status = 0x%02x\n", __func__, lock); return 0; @@ -809,6 +812,10 @@ static int ds3000_diseqc_send_burst(struct dvb_frontend *fe, static void ds3000_release(struct dvb_frontend *fe) { struct ds3000_state *state = fe->demodulator_priv; + + if (state->config->set_lock_led) + state->config->set_lock_led(fe, 0); + dprintk("%s\n", __func__); kfree(state); } @@ -1037,6 +1044,11 @@ static int ds3000_tune(struct dvb_frontend *fe, static enum dvbfe_algo ds3000_get_algo(struct dvb_frontend *fe) { + struct ds3000_state *state = fe->demodulator_priv; + + if (state->config->set_lock_led) + state->config->set_lock_led(fe, 0); + dprintk("%s()\n", __func__); return DVBFE_ALGO_HW; } diff --git a/drivers/media/dvb-frontends/ds3000.h b/drivers/media/dvb-frontends/ds3000.h index 67eeaf9bdbce..478ad66c63d7 100644 --- a/drivers/media/dvb-frontends/ds3000.h +++ b/drivers/media/dvb-frontends/ds3000.h @@ -30,6 +30,8 @@ struct ds3000_config { u8 ci_mode; /* Set device param to start dma */ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); + /* Hook for Lock LED */ + void (*set_lock_led)(struct dvb_frontend *fe, int offon); }; #if defined(CONFIG_DVB_DS3000) || \ diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 5ae3529f81ad..d8a5ebb43626 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -955,6 +955,11 @@ static struct ts2020_config dw2104_ts2020_config = { .tuner_address = 0x60, }; +static struct ds3000_config s660_ds3000_config = { + .demod_address = 0x68, + .set_lock_led = dw210x_led_ctrl, +}; + static struct stv0900_config dw2104a_stv0900_config = { .demod_address = 0x6a, .demod_mode = 0, @@ -1200,7 +1205,7 @@ static int ds3000_frontend_attach(struct dvb_usb_adapter *d) struct s6x0_state *st = (struct s6x0_state *)d->dev->priv; u8 obuf[] = {7, 1}; - d->fe_adap[0].fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config, + d->fe_adap[0].fe = dvb_attach(ds3000_attach, &s660_ds3000_config, &d->dev->i2c_adap); if (d->fe_adap[0].fe == NULL) -- cgit v1.2.3 From b858c331cdf402853be2c48c8f4f77173ef04da8 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 28 Dec 2012 19:40:33 -0300 Subject: [media] m88rs2000: make use ts2020 Tuner part of Montage rs2000 chip is similar to ts2020 tuner. Patch to use ts2020 code. [mchehab@redhat.com: a few CodingStyle fixes] Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/m88rs2000.c | 408 ++++++++------------------------ drivers/media/dvb-frontends/m88rs2000.h | 6 - drivers/media/dvb-frontends/ts2020.c | 381 ++++++++++++++++------------- drivers/media/dvb-frontends/ts2020.h | 1 + drivers/media/pci/cx23885/cx23885-dvb.c | 29 +-- drivers/media/pci/cx88/cx88-dvb.c | 1 + drivers/media/pci/dm1105/dm1105.c | 1 + drivers/media/usb/dvb-usb-v2/Kconfig | 1 + drivers/media/usb/dvb-usb-v2/lmedm04.c | 9 +- drivers/media/usb/dvb-usb/dw2102.c | 49 ++-- 10 files changed, 373 insertions(+), 513 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index df9e7dd6fe74..283c90fee374 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -60,15 +60,13 @@ MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); #define info(format, arg...) \ printk(KERN_INFO "m88rs2000-fe: " format "\n" , ## arg) -static int m88rs2000_writereg(struct m88rs2000_state *state, u8 tuner, +static int m88rs2000_writereg(struct m88rs2000_state *state, u8 reg, u8 data) { int ret; - u8 addr = (tuner == 0) ? state->config->tuner_addr : - state->config->demod_addr; u8 buf[] = { reg, data }; struct i2c_msg msg = { - .addr = addr, + .addr = state->config->demod_addr, .flags = 0, .buf = buf, .len = 2 @@ -83,44 +81,20 @@ static int m88rs2000_writereg(struct m88rs2000_state *state, u8 tuner, return (ret != 1) ? -EREMOTEIO : 0; } -static int m88rs2000_demod_write(struct m88rs2000_state *state, u8 reg, u8 data) -{ - return m88rs2000_writereg(state, 1, reg, data); -} - -static int m88rs2000_tuner_write(struct m88rs2000_state *state, u8 reg, u8 data) -{ - m88rs2000_demod_write(state, 0x81, 0x84); - udelay(10); - return m88rs2000_writereg(state, 0, reg, data); - -} - -static int m88rs2000_write(struct dvb_frontend *fe, const u8 buf[], int len) -{ - struct m88rs2000_state *state = fe->demodulator_priv; - - if (len != 2) - return -EINVAL; - - return m88rs2000_writereg(state, 1, buf[0], buf[1]); -} - -static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 tuner, u8 reg) +static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 reg) { int ret; u8 b0[] = { reg }; u8 b1[] = { 0 }; - u8 addr = (tuner == 0) ? state->config->tuner_addr : - state->config->demod_addr; + struct i2c_msg msg[] = { { - .addr = addr, + .addr = state->config->demod_addr, .flags = 0, .buf = b0, .len = 1 }, { - .addr = addr, + .addr = state->config->demod_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 @@ -136,18 +110,6 @@ static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 tuner, u8 reg) return b1[0]; } -static u8 m88rs2000_demod_read(struct m88rs2000_state *state, u8 reg) -{ - return m88rs2000_readreg(state, 1, reg); -} - -static u8 m88rs2000_tuner_read(struct m88rs2000_state *state, u8 reg) -{ - m88rs2000_demod_write(state, 0x81, 0x85); - udelay(10); - return m88rs2000_readreg(state, 0, reg); -} - static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate) { struct m88rs2000_state *state = fe->demodulator_priv; @@ -166,9 +128,9 @@ static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate) b[0] = (u8) (temp >> 16) & 0xff; b[1] = (u8) (temp >> 8) & 0xff; b[2] = (u8) temp & 0xff; - ret = m88rs2000_demod_write(state, 0x93, b[2]); - ret |= m88rs2000_demod_write(state, 0x94, b[1]); - ret |= m88rs2000_demod_write(state, 0x95, b[0]); + ret = m88rs2000_writereg(state, 0x93, b[2]); + ret |= m88rs2000_writereg(state, 0x94, b[1]); + ret |= m88rs2000_writereg(state, 0x95, b[0]); deb_info("m88rs2000: m88rs2000_set_symbolrate\n"); return ret; @@ -182,37 +144,37 @@ static int m88rs2000_send_diseqc_msg(struct dvb_frontend *fe, int i; u8 reg; deb_info("%s\n", __func__); - m88rs2000_demod_write(state, 0x9a, 0x30); - reg = m88rs2000_demod_read(state, 0xb2); + m88rs2000_writereg(state, 0x9a, 0x30); + reg = m88rs2000_readreg(state, 0xb2); reg &= 0x3f; - m88rs2000_demod_write(state, 0xb2, reg); + m88rs2000_writereg(state, 0xb2, reg); for (i = 0; i < m->msg_len; i++) - m88rs2000_demod_write(state, 0xb3 + i, m->msg[i]); + m88rs2000_writereg(state, 0xb3 + i, m->msg[i]); - reg = m88rs2000_demod_read(state, 0xb1); + reg = m88rs2000_readreg(state, 0xb1); reg &= 0x87; reg |= ((m->msg_len - 1) << 3) | 0x07; reg &= 0x7f; - m88rs2000_demod_write(state, 0xb1, reg); + m88rs2000_writereg(state, 0xb1, reg); for (i = 0; i < 15; i++) { - if ((m88rs2000_demod_read(state, 0xb1) & 0x40) == 0x0) + if ((m88rs2000_readreg(state, 0xb1) & 0x40) == 0x0) break; msleep(20); } - reg = m88rs2000_demod_read(state, 0xb1); + reg = m88rs2000_readreg(state, 0xb1); if ((reg & 0x40) > 0x0) { reg &= 0x7f; reg |= 0x40; - m88rs2000_demod_write(state, 0xb1, reg); + m88rs2000_writereg(state, 0xb1, reg); } - reg = m88rs2000_demod_read(state, 0xb2); + reg = m88rs2000_readreg(state, 0xb2); reg &= 0x3f; reg |= 0x80; - m88rs2000_demod_write(state, 0xb2, reg); - m88rs2000_demod_write(state, 0x9a, 0xb0); + m88rs2000_writereg(state, 0xb2, reg); + m88rs2000_writereg(state, 0x9a, 0xb0); return 0; @@ -224,14 +186,14 @@ static int m88rs2000_send_diseqc_burst(struct dvb_frontend *fe, struct m88rs2000_state *state = fe->demodulator_priv; u8 reg0, reg1; deb_info("%s\n", __func__); - m88rs2000_demod_write(state, 0x9a, 0x30); + m88rs2000_writereg(state, 0x9a, 0x30); msleep(50); - reg0 = m88rs2000_demod_read(state, 0xb1); - reg1 = m88rs2000_demod_read(state, 0xb2); + reg0 = m88rs2000_readreg(state, 0xb1); + reg1 = m88rs2000_readreg(state, 0xb2); /* TODO complete this section */ - m88rs2000_demod_write(state, 0xb2, reg1); - m88rs2000_demod_write(state, 0xb1, reg0); - m88rs2000_demod_write(state, 0x9a, 0xb0); + m88rs2000_writereg(state, 0xb2, reg1); + m88rs2000_writereg(state, 0xb1, reg0); + m88rs2000_writereg(state, 0x9a, 0xb0); return 0; } @@ -240,9 +202,9 @@ static int m88rs2000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) { struct m88rs2000_state *state = fe->demodulator_priv; u8 reg0, reg1; - m88rs2000_demod_write(state, 0x9a, 0x30); - reg0 = m88rs2000_demod_read(state, 0xb1); - reg1 = m88rs2000_demod_read(state, 0xb2); + m88rs2000_writereg(state, 0x9a, 0x30); + reg0 = m88rs2000_readreg(state, 0xb1); + reg1 = m88rs2000_readreg(state, 0xb2); reg1 &= 0x3f; @@ -257,9 +219,9 @@ static int m88rs2000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) default: break; } - m88rs2000_demod_write(state, 0xb2, reg1); - m88rs2000_demod_write(state, 0xb1, reg0); - m88rs2000_demod_write(state, 0x9a, 0xb0); + m88rs2000_writereg(state, 0xb2, reg1); + m88rs2000_writereg(state, 0xb1, reg0); + m88rs2000_writereg(state, 0x9a, 0xb0); return 0; } @@ -276,14 +238,6 @@ struct inittab m88rs2000_setup[] = { {DEMOD_WRITE, 0x00, 0x00}, {DEMOD_WRITE, 0x9a, 0xb0}, {DEMOD_WRITE, 0x81, 0xc1}, - {TUNER_WRITE, 0x42, 0x73}, - {TUNER_WRITE, 0x05, 0x07}, - {TUNER_WRITE, 0x20, 0x27}, - {TUNER_WRITE, 0x07, 0x02}, - {TUNER_WRITE, 0x11, 0xff}, - {TUNER_WRITE, 0x60, 0xf9}, - {TUNER_WRITE, 0x08, 0x01}, - {TUNER_WRITE, 0x00, 0x41}, {DEMOD_WRITE, 0x81, 0x81}, {DEMOD_WRITE, 0x86, 0xc6}, {DEMOD_WRITE, 0x9a, 0x30}, @@ -301,23 +255,10 @@ struct inittab m88rs2000_shutdown[] = { {DEMOD_WRITE, 0xf1, 0x89}, {DEMOD_WRITE, 0x00, 0x01}, {DEMOD_WRITE, 0x9a, 0xb0}, - {TUNER_WRITE, 0x00, 0x40}, {DEMOD_WRITE, 0x81, 0x81}, {0xff, 0xaa, 0xff} }; -struct inittab tuner_reset[] = { - {TUNER_WRITE, 0x42, 0x73}, - {TUNER_WRITE, 0x05, 0x07}, - {TUNER_WRITE, 0x20, 0x27}, - {TUNER_WRITE, 0x07, 0x02}, - {TUNER_WRITE, 0x11, 0xff}, - {TUNER_WRITE, 0x60, 0xf9}, - {TUNER_WRITE, 0x08, 0x01}, - {TUNER_WRITE, 0x00, 0x41}, - {0xff, 0xaa, 0xff} -}; - struct inittab fe_reset[] = { {DEMOD_WRITE, 0x00, 0x01}, {DEMOD_WRITE, 0xf1, 0xbf}, @@ -389,11 +330,7 @@ static int m88rs2000_tab_set(struct m88rs2000_state *state, for (i = 0; i < 255; i++) { switch (tab[i].cmd) { case 0x01: - ret = m88rs2000_demod_write(state, tab[i].reg, - tab[i].val); - break; - case 0x02: - ret = m88rs2000_tuner_write(state, tab[i].reg, + ret = m88rs2000_writereg(state, tab[i].reg, tab[i].val); break; case 0x10: @@ -419,7 +356,7 @@ static int m88rs2000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) struct m88rs2000_state *state = fe->demodulator_priv; u8 data; - data = m88rs2000_demod_read(state, 0xb2); + data = m88rs2000_readreg(state, 0xb2); data |= 0x03; /* bit0 V/H, bit1 off/on */ switch (volt) { @@ -434,23 +371,11 @@ static int m88rs2000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) break; } - m88rs2000_demod_write(state, 0xb2, data); + m88rs2000_writereg(state, 0xb2, data); return 0; } -static int m88rs2000_startup(struct m88rs2000_state *state) -{ - int ret = 0; - u8 reg; - - reg = m88rs2000_tuner_read(state, 0x00); - if ((reg & 0x40) == 0) - ret = -ENODEV; - - return ret; -} - static int m88rs2000_init(struct dvb_frontend *fe) { struct m88rs2000_state *state = fe->demodulator_priv; @@ -479,7 +404,7 @@ static int m88rs2000_sleep(struct dvb_frontend *fe) static int m88rs2000_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct m88rs2000_state *state = fe->demodulator_priv; - u8 reg = m88rs2000_demod_read(state, 0x8c); + u8 reg = m88rs2000_readreg(state, 0x8c); *status = 0; @@ -497,23 +422,23 @@ static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber) struct m88rs2000_state *state = fe->demodulator_priv; u8 tmp0, tmp1; - m88rs2000_demod_write(state, 0x9a, 0x30); - tmp0 = m88rs2000_demod_read(state, 0xd8); + m88rs2000_writereg(state, 0x9a, 0x30); + tmp0 = m88rs2000_readreg(state, 0xd8); if ((tmp0 & 0x10) != 0) { - m88rs2000_demod_write(state, 0x9a, 0xb0); + m88rs2000_writereg(state, 0x9a, 0xb0); *ber = 0xffffffff; return 0; } - *ber = (m88rs2000_demod_read(state, 0xd7) << 8) | - m88rs2000_demod_read(state, 0xd6); + *ber = (m88rs2000_readreg(state, 0xd7) << 8) | + m88rs2000_readreg(state, 0xd6); - tmp1 = m88rs2000_demod_read(state, 0xd9); - m88rs2000_demod_write(state, 0xd9, (tmp1 & ~7) | 4); + tmp1 = m88rs2000_readreg(state, 0xd9); + m88rs2000_writereg(state, 0xd9, (tmp1 & ~7) | 4); /* needs twice */ - m88rs2000_demod_write(state, 0xd8, (tmp0 & ~8) | 0x30); - m88rs2000_demod_write(state, 0xd8, (tmp0 & ~8) | 0x30); - m88rs2000_demod_write(state, 0x9a, 0xb0); + m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30); + m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30); + m88rs2000_writereg(state, 0x9a, 0xb0); return 0; } @@ -529,7 +454,7 @@ static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr) { struct m88rs2000_state *state = fe->demodulator_priv; - *snr = 512 * m88rs2000_demod_read(state, 0x65); + *snr = 512 * m88rs2000_readreg(state, 0x65); return 0; } @@ -539,166 +464,17 @@ static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) struct m88rs2000_state *state = fe->demodulator_priv; u8 tmp; - *ucblocks = (m88rs2000_demod_read(state, 0xd5) << 8) | - m88rs2000_demod_read(state, 0xd4); - tmp = m88rs2000_demod_read(state, 0xd8); - m88rs2000_demod_write(state, 0xd8, tmp & ~0x20); + *ucblocks = (m88rs2000_readreg(state, 0xd5) << 8) | + m88rs2000_readreg(state, 0xd4); + tmp = m88rs2000_readreg(state, 0xd8); + m88rs2000_writereg(state, 0xd8, tmp & ~0x20); /* needs two times */ - m88rs2000_demod_write(state, 0xd8, tmp | 0x20); - m88rs2000_demod_write(state, 0xd8, tmp | 0x20); + m88rs2000_writereg(state, 0xd8, tmp | 0x20); + m88rs2000_writereg(state, 0xd8, tmp | 0x20); return 0; } -static int m88rs2000_tuner_gate_ctrl(struct m88rs2000_state *state, u8 offset) -{ - int ret; - ret = m88rs2000_tuner_write(state, 0x51, 0x1f - offset); - ret |= m88rs2000_tuner_write(state, 0x51, 0x1f); - ret |= m88rs2000_tuner_write(state, 0x50, offset); - ret |= m88rs2000_tuner_write(state, 0x50, 0x00); - msleep(20); - return ret; -} - -static int m88rs2000_set_tuner_rf(struct dvb_frontend *fe) -{ - struct m88rs2000_state *state = fe->demodulator_priv; - int reg; - reg = m88rs2000_tuner_read(state, 0x3d); - reg &= 0x7f; - if (reg < 0x16) - reg = 0xa1; - else if (reg == 0x16) - reg = 0x99; - else - reg = 0xf9; - - m88rs2000_tuner_write(state, 0x60, reg); - reg = m88rs2000_tuner_gate_ctrl(state, 0x08); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - return reg; -} - -static int m88rs2000_set_tuner(struct dvb_frontend *fe, u16 *offset) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct m88rs2000_state *state = fe->demodulator_priv; - int ret; - u32 frequency = c->frequency; - s32 offset_khz; - s32 tmp; - u32 symbol_rate = (c->symbol_rate / 1000); - u32 f3db, gdiv28; - u16 value, ndiv, lpf_coeff; - u8 lpf_mxdiv, mlpf_max, mlpf_min, nlpf; - u8 lo = 0x01, div4 = 0x0; - - /* Reset Tuner */ - ret = m88rs2000_tab_set(state, tuner_reset); - - /* Calculate frequency divider */ - if (frequency < 1060000) { - lo |= 0x10; - div4 = 0x1; - ndiv = (frequency * 14 * 4) / FE_CRYSTAL_KHZ; - } else - ndiv = (frequency * 14 * 2) / FE_CRYSTAL_KHZ; - ndiv = ndiv + ndiv % 2; - ndiv = ndiv - 1024; - - ret = m88rs2000_tuner_write(state, 0x10, 0x80 | lo); - - /* Set frequency divider */ - ret |= m88rs2000_tuner_write(state, 0x01, (ndiv >> 8) & 0xf); - ret |= m88rs2000_tuner_write(state, 0x02, ndiv & 0xff); - - ret |= m88rs2000_tuner_write(state, 0x03, 0x06); - ret |= m88rs2000_tuner_gate_ctrl(state, 0x10); - if (ret < 0) - return -ENODEV; - - /* Tuner Frequency Range */ - ret = m88rs2000_tuner_write(state, 0x10, lo); - - ret |= m88rs2000_tuner_gate_ctrl(state, 0x08); - - /* Tuner RF */ - ret |= m88rs2000_set_tuner_rf(fe); - - gdiv28 = (FE_CRYSTAL_KHZ / 1000 * 1694 + 500) / 1000; - ret |= m88rs2000_tuner_write(state, 0x04, gdiv28 & 0xff); - ret |= m88rs2000_tuner_gate_ctrl(state, 0x04); - if (ret < 0) - return -ENODEV; - - value = m88rs2000_tuner_read(state, 0x26); - - f3db = (symbol_rate * 135) / 200 + 2000; - f3db += FREQ_OFFSET_LOW_SYM_RATE; - if (f3db < 7000) - f3db = 7000; - if (f3db > 40000) - f3db = 40000; - - gdiv28 = gdiv28 * 207 / (value * 2 + 151); - mlpf_max = gdiv28 * 135 / 100; - mlpf_min = gdiv28 * 78 / 100; - if (mlpf_max > 63) - mlpf_max = 63; - - lpf_coeff = 2766; - - nlpf = (f3db * gdiv28 * 2 / lpf_coeff / - (FE_CRYSTAL_KHZ / 1000) + 1) / 2; - if (nlpf > 23) - nlpf = 23; - if (nlpf < 1) - nlpf = 1; - - lpf_mxdiv = (nlpf * (FE_CRYSTAL_KHZ / 1000) - * lpf_coeff * 2 / f3db + 1) / 2; - - if (lpf_mxdiv < mlpf_min) { - nlpf++; - lpf_mxdiv = (nlpf * (FE_CRYSTAL_KHZ / 1000) - * lpf_coeff * 2 / f3db + 1) / 2; - } - - if (lpf_mxdiv > mlpf_max) - lpf_mxdiv = mlpf_max; - - ret = m88rs2000_tuner_write(state, 0x04, lpf_mxdiv); - ret |= m88rs2000_tuner_write(state, 0x06, nlpf); - - ret |= m88rs2000_tuner_gate_ctrl(state, 0x04); - - ret |= m88rs2000_tuner_gate_ctrl(state, 0x01); - - msleep(80); - /* calculate offset assuming 96000kHz*/ - offset_khz = (ndiv - ndiv % 2 + 1024) * FE_CRYSTAL_KHZ - / 14 / (div4 + 1) / 2; - - offset_khz -= frequency; - - tmp = offset_khz; - tmp *= 65536; - - tmp = (2 * tmp + 96000) / (2 * 96000); - if (tmp < 0) - tmp += 65536; - - *offset = tmp & 0xffff; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - return (ret < 0) ? -EINVAL : 0; -} - static int m88rs2000_set_fec(struct m88rs2000_state *state, fe_code_rate_t fec) { @@ -724,7 +500,7 @@ static int m88rs2000_set_fec(struct m88rs2000_state *state, default: fec_set = 0x08; } - m88rs2000_demod_write(state, 0x76, fec_set); + m88rs2000_writereg(state, 0x76, fec_set); return 0; } @@ -733,9 +509,9 @@ static int m88rs2000_set_fec(struct m88rs2000_state *state, static fe_code_rate_t m88rs2000_get_fec(struct m88rs2000_state *state) { u8 reg; - m88rs2000_demod_write(state, 0x9a, 0x30); - reg = m88rs2000_demod_read(state, 0x76); - m88rs2000_demod_write(state, 0x9a, 0xb0); + m88rs2000_writereg(state, 0x9a, 0x30); + reg = m88rs2000_readreg(state, 0x76); + m88rs2000_writereg(state, 0x9a, 0xb0); switch (reg) { case 0x88: @@ -761,7 +537,9 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) struct m88rs2000_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; fe_status_t status; - int i, ret; + int i, ret = 0; + s32 tmp; + u32 tuner_freq; u16 offset = 0; u8 reg; @@ -775,17 +553,37 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) } /* Set Tuner */ - ret = m88rs2000_set_tuner(fe, &offset); + if (fe->ops.tuner_ops.set_params) + ret = fe->ops.tuner_ops.set_params(fe); + + if (ret < 0) + return -ENODEV; + + if (fe->ops.tuner_ops.get_frequency) + ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_freq); + if (ret < 0) return -ENODEV; - ret = m88rs2000_demod_write(state, 0x9a, 0x30); + offset = tuner_freq - c->frequency; + + /* calculate offset assuming 96000kHz*/ + tmp = offset; + tmp *= 65536; + + tmp = (2 * tmp + 96000) / (2 * 96000); + if (tmp < 0) + tmp += 65536; + + offset = tmp & 0xffff; + + ret = m88rs2000_writereg(state, 0x9a, 0x30); /* Unknown usually 0xc6 sometimes 0xc1 */ - reg = m88rs2000_demod_read(state, 0x86); - ret |= m88rs2000_demod_write(state, 0x86, reg); + reg = m88rs2000_readreg(state, 0x86); + ret |= m88rs2000_writereg(state, 0x86, reg); /* Offset lower nibble always 0 */ - ret |= m88rs2000_demod_write(state, 0x9c, (offset >> 8)); - ret |= m88rs2000_demod_write(state, 0x9d, offset & 0xf0); + ret |= m88rs2000_writereg(state, 0x9c, (offset >> 8)); + ret |= m88rs2000_writereg(state, 0x9d, offset & 0xf0); /* Reset Demod */ @@ -794,16 +592,16 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) return -ENODEV; /* Unknown */ - reg = m88rs2000_demod_read(state, 0x70); - ret = m88rs2000_demod_write(state, 0x70, reg); + reg = m88rs2000_readreg(state, 0x70); + ret = m88rs2000_writereg(state, 0x70, reg); /* Set FEC */ ret |= m88rs2000_set_fec(state, c->fec_inner); - ret |= m88rs2000_demod_write(state, 0x85, 0x1); - ret |= m88rs2000_demod_write(state, 0x8a, 0xbf); - ret |= m88rs2000_demod_write(state, 0x8d, 0x1e); - ret |= m88rs2000_demod_write(state, 0x90, 0xf1); - ret |= m88rs2000_demod_write(state, 0x91, 0x08); + ret |= m88rs2000_writereg(state, 0x85, 0x1); + ret |= m88rs2000_writereg(state, 0x8a, 0xbf); + ret |= m88rs2000_writereg(state, 0x8d, 0x1e); + ret |= m88rs2000_writereg(state, 0x90, 0xf1); + ret |= m88rs2000_writereg(state, 0x91, 0x08); if (ret < 0) return -ENODEV; @@ -819,27 +617,25 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) return -ENODEV; for (i = 0; i < 25; i++) { - reg = m88rs2000_demod_read(state, 0x8c); + reg = m88rs2000_readreg(state, 0x8c); if ((reg & 0x7) == 0x7) { status = FE_HAS_LOCK; break; } state->no_lock_count++; if (state->no_lock_count == 15) { - reg = m88rs2000_demod_read(state, 0x70); + reg = m88rs2000_readreg(state, 0x70); reg ^= 0x4; - m88rs2000_demod_write(state, 0x70, reg); + m88rs2000_writereg(state, 0x70, reg); state->no_lock_count = 0; } - if (state->no_lock_count == 20) - m88rs2000_set_tuner_rf(fe); msleep(20); } if (status & FE_HAS_LOCK) { state->fec_inner = m88rs2000_get_fec(state); /* Uknown suspect SNR level */ - reg = m88rs2000_demod_read(state, 0x65); + reg = m88rs2000_readreg(state, 0x65); } state->tuner_frequency = c->frequency; @@ -862,9 +658,9 @@ static int m88rs2000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) struct m88rs2000_state *state = fe->demodulator_priv; if (enable) - m88rs2000_demod_write(state, 0x81, 0x84); + m88rs2000_writereg(state, 0x81, 0x84); else - m88rs2000_demod_write(state, 0x81, 0x81); + m88rs2000_writereg(state, 0x81, 0x81); udelay(10); return 0; } @@ -895,7 +691,6 @@ static struct dvb_frontend_ops m88rs2000_ops = { .release = m88rs2000_release, .init = m88rs2000_init, .sleep = m88rs2000_sleep, - .write = m88rs2000_write, .i2c_gate_ctrl = m88rs2000_i2c_gate_ctrl, .read_status = m88rs2000_read_status, .read_ber = m88rs2000_read_ber, @@ -928,9 +723,6 @@ struct dvb_frontend *m88rs2000_attach(const struct m88rs2000_config *config, state->symbol_rate = 0; state->fec_inner = 0; - if (m88rs2000_startup(state) < 0) - goto error; - /* create dvb_frontend */ memcpy(&state->frontend.ops, &m88rs2000_ops, sizeof(struct dvb_frontend_ops)); diff --git a/drivers/media/dvb-frontends/m88rs2000.h b/drivers/media/dvb-frontends/m88rs2000.h index 59acdb696873..5a8023e5a4b8 100644 --- a/drivers/media/dvb-frontends/m88rs2000.h +++ b/drivers/media/dvb-frontends/m88rs2000.h @@ -26,8 +26,6 @@ struct m88rs2000_config { /* Demodulator i2c address */ u8 demod_addr; - /* Tuner address */ - u8 tuner_addr; u8 *inittab; @@ -55,12 +53,8 @@ static inline struct dvb_frontend *m88rs2000_attach( } #endif /* CONFIG_DVB_M88RS2000 */ -#define FE_CRYSTAL_KHZ 27000 -#define FREQ_OFFSET_LOW_SYM_RATE 3000 - enum { DEMOD_WRITE = 0x1, - TUNER_WRITE, WRITE_DELAY = 0x10, }; #endif /* M88RS2000_H */ diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index 73010ecb9866..94e3fe21eefb 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c @@ -23,27 +23,68 @@ #include "ts2020.h" #define TS2020_XTAL_FREQ 27000 /* in kHz */ +#define FREQ_OFFSET_LOW_SYM_RATE 3000 -struct ts2020_state { - u8 tuner_address; +struct ts2020_priv { + /* i2c details */ + int i2c_address; struct i2c_adapter *i2c; + u8 clk_out_div; + u32 frequency; }; -static int ts2020_readreg(struct dvb_frontend *fe, u8 reg) +static int ts2020_release(struct dvb_frontend *fe) { - struct ts2020_state *state = fe->tuner_priv; + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int ts2020_writereg(struct dvb_frontend *fe, int reg, int data) +{ + struct ts2020_priv *priv = fe->tuner_priv; + u8 buf[] = { reg, data }; + struct i2c_msg msg[] = { + { + .addr = priv->i2c_address, + .flags = 0, + .buf = buf, + .len = 2 + } + }; + int err; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + err = i2c_transfer(priv->i2c, msg, 1); + if (err != 1) { + printk(KERN_ERR + "%s: writereg error(err == %i, reg == 0x%02x, value == 0x%02x)\n", + __func__, err, reg, data); + return -EREMOTEIO; + } + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int ts2020_readreg(struct dvb_frontend *fe, u8 reg) +{ + struct ts2020_priv *priv = fe->tuner_priv; int ret; u8 b0[] = { reg }; u8 b1[] = { 0 }; struct i2c_msg msg[] = { { - .addr = state->tuner_address, + .addr = priv->i2c_address, .flags = 0, .buf = b0, .len = 1 }, { - .addr = state->tuner_address, + .addr = priv->i2c_address, .flags = I2C_M_RD, .buf = b1, .len = 1 @@ -53,212 +94,202 @@ static int ts2020_readreg(struct dvb_frontend *fe, u8 reg) if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - ret = i2c_transfer(state->i2c, msg, 2); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); + ret = i2c_transfer(priv->i2c, msg, 2); if (ret != 2) { - printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret); + printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", + __func__, reg, ret); return ret; } + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + return b1[0]; } -static int ts2020_writereg(struct dvb_frontend *fe, int reg, int data) +static int ts2020_sleep(struct dvb_frontend *fe) { - struct ts2020_state *state = fe->tuner_priv; - - u8 buf[] = { reg, data }; - struct i2c_msg msg = { .addr = state->tuner_address, - .flags = 0, .buf = buf, .len = 2 }; - int err; - + struct ts2020_priv *priv = fe->tuner_priv; + int ret; + u8 buf[] = { 10, 0 }; + struct i2c_msg msg = { + .addr = priv->i2c_address, + .flags = 0, + .buf = buf, + .len = 2 + }; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - err = i2c_transfer(state->i2c, &msg, 1); + ret = i2c_transfer(priv->i2c, &msg, 1); + if (ret != 1) + printk(KERN_ERR "%s: i2c error\n", __func__); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); - if (err != 1) { - printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x," - " value == 0x%02x)\n", __func__, err, reg, data); - return -EREMOTEIO; - } - - return 0; + return (ret == 1) ? 0 : ret; } static int ts2020_init(struct dvb_frontend *fe) { + struct ts2020_priv *priv = fe->tuner_priv; + ts2020_writereg(fe, 0x42, 0x73); - ts2020_writereg(fe, 0x05, 0x01); - ts2020_writereg(fe, 0x62, 0xf5); + ts2020_writereg(fe, 0x05, priv->clk_out_div); + ts2020_writereg(fe, 0x20, 0x27); + ts2020_writereg(fe, 0x07, 0x02); + ts2020_writereg(fe, 0x11, 0xff); + ts2020_writereg(fe, 0x60, 0xf9); + ts2020_writereg(fe, 0x08, 0x01); + ts2020_writereg(fe, 0x00, 0x41); + return 0; } -static int ts2020_get_frequency(struct dvb_frontend *fe, u32 *frequency) +static int ts2020_tuner_gate_ctrl(struct dvb_frontend *fe, u8 offset) { - u16 ndiv, div4; + int ret; + ret = ts2020_writereg(fe, 0x51, 0x1f - offset); + ret |= ts2020_writereg(fe, 0x51, 0x1f); + ret |= ts2020_writereg(fe, 0x50, offset); + ret |= ts2020_writereg(fe, 0x50, 0x00); + msleep(20); + return ret; +} - div4 = (ts2020_readreg(fe, 0x10) & 0x10) >> 4; +static int ts2020_set_tuner_rf(struct dvb_frontend *fe) +{ + int reg; - ndiv = ts2020_readreg(fe, 0x01); - ndiv &= 0x0f; - ndiv <<= 8; - ndiv |= ts2020_readreg(fe, 0x02); + reg = ts2020_readreg(fe, 0x3d); + reg &= 0x7f; + if (reg < 0x16) + reg = 0xa1; + else if (reg == 0x16) + reg = 0x99; + else + reg = 0xf9; - /* actual tuned frequency, i.e. including the offset */ - *frequency = (ndiv - ndiv % 2 + 1024) * TS2020_XTAL_FREQ - / (6 + 8) / (div4 + 1) / 2; + ts2020_writereg(fe, 0x60, reg); + reg = ts2020_tuner_gate_ctrl(fe, 0x08); - return 0; + return reg; } static int ts2020_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct ts2020_priv *priv = fe->demodulator_priv; + int ret; + u32 frequency = c->frequency; + s32 offset_khz; + u32 symbol_rate = (c->symbol_rate / 1000); + u32 f3db, gdiv28; + u16 value, ndiv, lpf_coeff; + u8 lpf_mxdiv, mlpf_max, mlpf_min, nlpf; + u8 lo = 0x01, div4 = 0x0; + + /* Calculate frequency divider */ + if (frequency < 1060000) { + lo |= 0x10; + div4 = 0x1; + ndiv = (frequency * 14 * 4) / TS2020_XTAL_FREQ; + } else + ndiv = (frequency * 14 * 2) / TS2020_XTAL_FREQ; + ndiv = ndiv + ndiv % 2; + ndiv = ndiv - 1024; + + ret = ts2020_writereg(fe, 0x10, 0x80 | lo); + + /* Set frequency divider */ + ret |= ts2020_writereg(fe, 0x01, (ndiv >> 8) & 0xf); + ret |= ts2020_writereg(fe, 0x02, ndiv & 0xff); + + ret |= ts2020_writereg(fe, 0x03, 0x06); + ret |= ts2020_tuner_gate_ctrl(fe, 0x10); + if (ret < 0) + return -ENODEV; + + /* Tuner Frequency Range */ + ret = ts2020_writereg(fe, 0x10, lo); + + ret |= ts2020_tuner_gate_ctrl(fe, 0x08); + + /* Tuner RF */ + ret |= ts2020_set_tuner_rf(fe); + + gdiv28 = (TS2020_XTAL_FREQ / 1000 * 1694 + 500) / 1000; + ret |= ts2020_writereg(fe, 0x04, gdiv28 & 0xff); + ret |= ts2020_tuner_gate_ctrl(fe, 0x04); + if (ret < 0) + return -ENODEV; - u8 mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf; - u16 value, ndiv; - u32 srate = 0, f3db; - - ts2020_init(fe); - - /* unknown */ - ts2020_writereg(fe, 0x07, 0x02); - ts2020_writereg(fe, 0x10, 0x00); - ts2020_writereg(fe, 0x60, 0x79); - ts2020_writereg(fe, 0x08, 0x01); - ts2020_writereg(fe, 0x00, 0x01); - - /* calculate and set freq divider */ - if (c->frequency < 1146000) { - ts2020_writereg(fe, 0x10, 0x11); - ndiv = ((c->frequency * (6 + 8) * 4) + - (TS2020_XTAL_FREQ / 2)) / - TS2020_XTAL_FREQ - 1024; - } else { - ts2020_writereg(fe, 0x10, 0x01); - ndiv = ((c->frequency * (6 + 8) * 2) + - (TS2020_XTAL_FREQ / 2)) / - TS2020_XTAL_FREQ - 1024; - } - - ts2020_writereg(fe, 0x01, (ndiv & 0x0f00) >> 8); - ts2020_writereg(fe, 0x02, ndiv & 0x00ff); - - /* set pll */ - ts2020_writereg(fe, 0x03, 0x06); - ts2020_writereg(fe, 0x51, 0x0f); - ts2020_writereg(fe, 0x51, 0x1f); - ts2020_writereg(fe, 0x50, 0x10); - ts2020_writereg(fe, 0x50, 0x00); - msleep(5); - - /* unknown */ - ts2020_writereg(fe, 0x51, 0x17); - ts2020_writereg(fe, 0x51, 0x1f); - ts2020_writereg(fe, 0x50, 0x08); - ts2020_writereg(fe, 0x50, 0x00); - msleep(5); - - value = ts2020_readreg(fe, 0x3d); - value &= 0x0f; - if ((value > 4) && (value < 15)) { - value -= 3; - if (value < 4) - value = 4; - value = ((value << 3) | 0x01) & 0x79; - } + value = ts2020_readreg(fe, 0x26); - ts2020_writereg(fe, 0x60, value); - ts2020_writereg(fe, 0x51, 0x17); - ts2020_writereg(fe, 0x51, 0x1f); - ts2020_writereg(fe, 0x50, 0x08); - ts2020_writereg(fe, 0x50, 0x00); - - /* set low-pass filter period */ - ts2020_writereg(fe, 0x04, 0x2e); - ts2020_writereg(fe, 0x51, 0x1b); - ts2020_writereg(fe, 0x51, 0x1f); - ts2020_writereg(fe, 0x50, 0x04); - ts2020_writereg(fe, 0x50, 0x00); - msleep(5); - - srate = c->symbol_rate / 1000; - - f3db = (srate << 2) / 5 + 2000; - if (srate < 5000) - f3db += 3000; + f3db = (symbol_rate * 135) / 200 + 2000; + f3db += FREQ_OFFSET_LOW_SYM_RATE; if (f3db < 7000) f3db = 7000; if (f3db > 40000) f3db = 40000; - /* set low-pass filter baseband */ - value = ts2020_readreg(fe, 0x26); - mlpf = 0x2e * 207 / ((value << 1) + 151); - mlpf_max = mlpf * 135 / 100; - mlpf_min = mlpf * 78 / 100; + gdiv28 = gdiv28 * 207 / (value * 2 + 151); + mlpf_max = gdiv28 * 135 / 100; + mlpf_min = gdiv28 * 78 / 100; if (mlpf_max > 63) mlpf_max = 63; - /* rounded to the closest integer */ - nlpf = ((mlpf * f3db * 1000) + (2766 * TS2020_XTAL_FREQ / 2)) - / (2766 * TS2020_XTAL_FREQ); + lpf_coeff = 2766; + + nlpf = (f3db * gdiv28 * 2 / lpf_coeff / + (TS2020_XTAL_FREQ / 1000) + 1) / 2; if (nlpf > 23) nlpf = 23; if (nlpf < 1) nlpf = 1; - /* rounded to the closest integer */ - mlpf_new = ((TS2020_XTAL_FREQ * nlpf * 2766) + - (1000 * f3db / 2)) / (1000 * f3db); + lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000) + * lpf_coeff * 2 / f3db + 1) / 2; - if (mlpf_new < mlpf_min) { + if (lpf_mxdiv < mlpf_min) { nlpf++; - mlpf_new = ((TS2020_XTAL_FREQ * nlpf * 2766) + - (1000 * f3db / 2)) / (1000 * f3db); + lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000) + * lpf_coeff * 2 / f3db + 1) / 2; } - if (mlpf_new > mlpf_max) - mlpf_new = mlpf_max; + if (lpf_mxdiv > mlpf_max) + lpf_mxdiv = mlpf_max; - ts2020_writereg(fe, 0x04, mlpf_new); - ts2020_writereg(fe, 0x06, nlpf); - ts2020_writereg(fe, 0x51, 0x1b); - ts2020_writereg(fe, 0x51, 0x1f); - ts2020_writereg(fe, 0x50, 0x04); - ts2020_writereg(fe, 0x50, 0x00); - msleep(5); + ret = ts2020_writereg(fe, 0x04, lpf_mxdiv); + ret |= ts2020_writereg(fe, 0x06, nlpf); - /* unknown */ - ts2020_writereg(fe, 0x51, 0x1e); - ts2020_writereg(fe, 0x51, 0x1f); - ts2020_writereg(fe, 0x50, 0x01); - ts2020_writereg(fe, 0x50, 0x00); - msleep(60); + ret |= ts2020_tuner_gate_ctrl(fe, 0x04); - return 0; -} + ret |= ts2020_tuner_gate_ctrl(fe, 0x01); -static int ts2020_release(struct dvb_frontend *fe) -{ - struct ts2020_state *state = fe->tuner_priv; + msleep(80); + /* calculate offset assuming 96000kHz*/ + offset_khz = (ndiv - ndiv % 2 + 1024) * TS2020_XTAL_FREQ + / (6 + 8) / (div4 + 1) / 2; - fe->tuner_priv = NULL; - kfree(state); + priv->frequency = offset_khz; + + return (ret < 0) ? -EINVAL : 0; +} +static int ts2020_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct ts2020_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; return 0; } -static int ts2020_get_signal_strength(struct dvb_frontend *fe, - u16 *signal_strength) +/* read TS2020 signal strength */ +static int ts2020_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) { u16 sig_reading, sig_strength; u8 rfgain, bbgain; @@ -281,35 +312,57 @@ static int ts2020_get_signal_strength(struct dvb_frontend *fe, return 0; } -static struct dvb_tuner_ops ts2020_ops = { +static struct dvb_tuner_ops ts2020_tuner_ops = { .info = { - .name = "Montage Technology TS2020 Silicon Tuner", + .name = "TS2020", .frequency_min = 950000, - .frequency_max = 2150000, + .frequency_max = 2150000 }, - .init = ts2020_init, .release = ts2020_release, + .sleep = ts2020_sleep, .set_params = ts2020_set_params, .get_frequency = ts2020_get_frequency, - .get_rf_strength = ts2020_get_signal_strength + .get_rf_strength = ts2020_read_signal_strength, }; struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe, - const struct ts2020_config *config, struct i2c_adapter *i2c) + const struct ts2020_config *config, + struct i2c_adapter *i2c) { - struct ts2020_state *state = NULL; + struct ts2020_priv *priv = NULL; + u8 buf; + + priv = kzalloc(sizeof(struct ts2020_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; - /* allocate memory for the internal state */ - state = kzalloc(sizeof(struct ts2020_state), GFP_KERNEL); - if (!state) + priv->i2c_address = config->tuner_address; + priv->i2c = i2c; + priv->clk_out_div = config->clk_out_div; + fe->tuner_priv = priv; + + /* Wake Up the tuner */ + if ((0x03 & ts2020_readreg(fe, 0x00)) == 0x00) { + ts2020_writereg(fe, 0x00, 0x01); + msleep(2); + } + + ts2020_writereg(fe, 0x00, 0x03); + msleep(2); + + /* Check the tuner version */ + buf = ts2020_readreg(fe, 0x00); + if ((buf == 0x01) || (buf == 0x41) || (buf == 0x81)) + printk(KERN_INFO "%s: Find tuner TS2020!\n", __func__); + else { + printk(KERN_ERR "%s: Read tuner reg[0] = %d\n", __func__, buf); + kfree(priv); return NULL; + } - /* setup the state */ - state->tuner_address = config->tuner_address; - state->i2c = i2c; - fe->tuner_priv = state; - fe->ops.tuner_ops = ts2020_ops; + memcpy(&fe->ops.tuner_ops, &ts2020_tuner_ops, + sizeof(struct dvb_tuner_ops)); fe->ops.read_signal_strength = fe->ops.tuner_ops.get_rf_strength; return fe; diff --git a/drivers/media/dvb-frontends/ts2020.h b/drivers/media/dvb-frontends/ts2020.h index 13a172dfd582..c7e64afa614a 100644 --- a/drivers/media/dvb-frontends/ts2020.h +++ b/drivers/media/dvb-frontends/ts2020.h @@ -26,6 +26,7 @@ struct ts2020_config { u8 tuner_address; + u8 clk_out_div; }; #if defined(CONFIG_DVB_TS2020) || \ diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index a2ed0f759b0a..9c5ed10b2c5e 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -474,6 +474,7 @@ static struct ds3000_config tevii_ds3000_config = { static struct ts2020_config tevii_ts2020_config = { .tuner_address = 0x60, + .clk_out_div = 1, }; static struct cx24116_config dvbworld_cx24116_config = { @@ -500,20 +501,20 @@ static struct xc5000_config mygica_x8506_xc5000_config = { }; static struct stv090x_config prof_8000_stv090x_config = { - .device = STV0903, - .demod_mode = STV090x_SINGLE, - .clk_mode = STV090x_CLK_EXT, - .xtal = 27000000, - .address = 0x6A, - .ts1_mode = STV090x_TSMODE_PARALLEL_PUNCTURED, - .repeater_level = STV090x_RPTLEVEL_64, - .adc1_range = STV090x_ADC_2Vpp, - .diseqc_envelope_mode = false, - - .tuner_get_frequency = stb6100_get_frequency, - .tuner_set_frequency = stb6100_set_frequency, - .tuner_set_bandwidth = stb6100_set_bandwidth, - .tuner_get_bandwidth = stb6100_get_bandwidth, + .device = STV0903, + .demod_mode = STV090x_SINGLE, + .clk_mode = STV090x_CLK_EXT, + .xtal = 27000000, + .address = 0x6A, + .ts1_mode = STV090x_TSMODE_PARALLEL_PUNCTURED, + .repeater_level = STV090x_RPTLEVEL_64, + .adc1_range = STV090x_ADC_2Vpp, + .diseqc_envelope_mode = false, + + .tuner_get_frequency = stb6100_get_frequency, + .tuner_set_frequency = stb6100_set_frequency, + .tuner_set_bandwidth = stb6100_set_bandwidth, + .tuner_get_bandwidth = stb6100_get_bandwidth, }; static struct stb6100_config prof_8000_stb6100_config = { diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c index e085851d6872..50b5ac5b3983 100644 --- a/drivers/media/pci/cx88/cx88-dvb.c +++ b/drivers/media/pci/cx88/cx88-dvb.c @@ -703,6 +703,7 @@ static struct ds3000_config tevii_ds3000_config = { static struct ts2020_config tevii_ts2020_config = { .tuner_address = 0x60, + .clk_out_div = 1, }; static const struct stv0900_config prof_7301_stv0900_config = { diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index 79fe74aae1f9..c789e7c67e4e 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -852,6 +852,7 @@ static struct ds3000_config dvbworld_ds3000_config = { static struct ts2020_config dvbworld_ts2020_config = { .tuner_address = 0x60, + .clk_out_div = 1, }; static int __devinit frontend_init(struct dm1105_dev *dev) diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 834bfecbed73..3240d559ef7b 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -120,6 +120,7 @@ config DVB_USB_LME2510 select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT select DVB_M88RS2000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the LME DM04/QQBOX DVB-S USB2.0 diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index 6427ac359f21..b5e1f736eb7e 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -81,6 +81,7 @@ #include "dvb-pll.h" #include "z0194a.h" #include "m88rs2000.h" +#include "ts2020.h" #define LME2510_C_S7395 "dvb-usb-lme2510c-s7395.fw"; @@ -944,10 +945,14 @@ static int dm04_rs2000_set_ts_param(struct dvb_frontend *fe, static struct m88rs2000_config m88rs2000_config = { .demod_addr = 0xd0, - .tuner_addr = 0xc0, .set_ts_params = dm04_rs2000_set_ts_param, }; +static struct ts2020_config ts2020_config = { + .tuner_address = 0x60, + .clk_out_div = 7, +}; + static int dm04_lme2510_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { @@ -1097,6 +1102,8 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) if (adap->fe[0]) { info("FE Found M88RS2000"); + dvb_attach(ts2020_attach, adap->fe[0], &ts2020_config, + &d->i2c_adap); st->i2c_tuner_gate_w = 5; st->i2c_tuner_gate_r = 5; st->i2c_tuner_addr = 0xc0; diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index d8a5ebb43626..9578a6761f1b 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -29,6 +29,7 @@ #include "stb6100.h" #include "stb6100_proc.h" #include "m88rs2000.h" +#include "ts2020.h" #ifndef USB_PID_DW2102 #define USB_PID_DW2102 0x2102 @@ -953,10 +954,12 @@ static struct ds3000_config dw2104_ds3000_config = { static struct ts2020_config dw2104_ts2020_config = { .tuner_address = 0x60, + .clk_out_div = 1, }; static struct ds3000_config s660_ds3000_config = { .demod_address = 0x68, + .ci_mode = 1, .set_lock_led = dw210x_led_ctrl, }; @@ -1009,10 +1012,7 @@ static struct stv0900_config prof_7500_stv0900_config = { static struct ds3000_config su3000_ds3000_config = { .demod_address = 0x68, .ci_mode = 1, -}; - -static struct ts2020_config su3000_ts2020_config = { - .tuner_address = 0x60, + .set_lock_led = dw210x_led_ctrl, }; static u8 m88rs2000_inittab[] = { @@ -1022,14 +1022,6 @@ static u8 m88rs2000_inittab[] = { DEMOD_WRITE, 0x00, 0x00, DEMOD_WRITE, 0x9a, 0xb0, DEMOD_WRITE, 0x81, 0xc1, - TUNER_WRITE, 0x42, 0x73, - TUNER_WRITE, 0x05, 0x07, - TUNER_WRITE, 0x20, 0x27, - TUNER_WRITE, 0x07, 0x02, - TUNER_WRITE, 0x11, 0xff, - TUNER_WRITE, 0x60, 0xf9, - TUNER_WRITE, 0x08, 0x01, - TUNER_WRITE, 0x00, 0x41, DEMOD_WRITE, 0x81, 0x81, DEMOD_WRITE, 0x86, 0xc6, DEMOD_WRITE, 0x9a, 0x30, @@ -1043,7 +1035,6 @@ static u8 m88rs2000_inittab[] = { static struct m88rs2000_config s421_m88rs2000_config = { .demod_addr = 0x68, - .tuner_addr = 0x60, .inittab = m88rs2000_inittab, }; @@ -1250,6 +1241,14 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *d) if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) err("command 0x0e transfer failed."); + obuf[0] = 0xe; + obuf[1] = 0x02; + obuf[2] = 1; + + if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) + err("command 0x0e transfer failed."); + msleep(300); + obuf[0] = 0xe; obuf[1] = 0x83; obuf[2] = 0; @@ -1274,12 +1273,15 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *d) if (d->fe_adap[0].fe == NULL) return -EIO; - dvb_attach(ts2020_attach, d->fe_adap[0].fe, &su3000_ts2020_config, - &d->dev->i2c_adap); - - info("Attached DS3000!\n"); + if (dvb_attach(ts2020_attach, d->fe_adap[0].fe, + &dw2104_ts2020_config, + &d->dev->i2c_adap)) { + info("Attached DS3000/TS2020!\n"); + return 0; + } - return 0; + info("Failed to attach DS3000/TS2020!\n"); + return -EIO; } static int m88rs2000_frontend_attach(struct dvb_usb_adapter *d) @@ -1292,12 +1294,19 @@ static int m88rs2000_frontend_attach(struct dvb_usb_adapter *d) d->fe_adap[0].fe = dvb_attach(m88rs2000_attach, &s421_m88rs2000_config, &d->dev->i2c_adap); + if (d->fe_adap[0].fe == NULL) return -EIO; - info("Attached m88rs2000!\n"); + if (dvb_attach(ts2020_attach, d->fe_adap[0].fe, + &dw2104_ts2020_config, + &d->dev->i2c_adap)) { + info("Attached RS2000/TS2020!\n"); + return 0; + } - return 0; + info("Failed to attach RS2000/TS2020!\n"); + return -EIO; } static int dw2102_tuner_attach(struct dvb_usb_adapter *adap) -- cgit v1.2.3 From e003ae399c160e00c1a882dc6dd4f0ef855ae616 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 23 Nov 2012 08:12:35 -0300 Subject: [media] stk1160: Replace BUG_ON with WARN_ON This situation is not even an error condition so it's stupid to BUG_ON. Learn the lesson: http://permalink.gmane.org/gmane.linux.kernel/1347333 Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/stk1160/stk1160-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c index 0a4ee85f4399..39f1aae209bc 100644 --- a/drivers/media/usb/stk1160/stk1160-video.c +++ b/drivers/media/usb/stk1160/stk1160-video.c @@ -78,7 +78,7 @@ struct stk1160_buffer *stk1160_next_buffer(struct stk1160 *dev) unsigned long flags = 0; /* Current buffer must be NULL when this functions gets called */ - BUG_ON(dev->isoc_ctl.buf); + WARN_ON(dev->isoc_ctl.buf); spin_lock_irqsave(&dev->buf_lock, flags); if (!list_empty(&dev->avail_bufs)) { -- cgit v1.2.3 From 71dc98becc3ddc9775f6e54485929927dd106b6e Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sat, 29 Dec 2012 07:34:24 -0300 Subject: [media] lmedm04: correct I2C values to 7 bit addressing The separation the lmedm04 fails on the ts2020 portion because the correct I2C addressing. So, it's time to correct the addressing in the remainder of lmedm04. Tested all tuners. Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/lmedm04.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index b5e1f736eb7e..f30c58cecbba 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -627,8 +627,8 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], gate = 5; for (i = 0; i < num; i++) { - read_o = 1 & (msg[i].flags & I2C_M_RD); - read = i+1 < num && (msg[i+1].flags & I2C_M_RD); + read_o = msg[i].flags & I2C_M_RD; + read = i + 1 < num && msg[i + 1].flags & I2C_M_RD; read |= read_o; gate = (msg[i].addr == st->i2c_tuner_addr) ? (read) ? st->i2c_tuner_gate_r @@ -641,7 +641,8 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], else obuf[1] = msg[i].len + read + 1; - obuf[2] = msg[i].addr; + obuf[2] = msg[i].addr << 1; + if (read) { if (read_o) len = 3; @@ -895,27 +896,27 @@ static int lme2510_kill_urb(struct usb_data_stream *stream) } static struct tda10086_config tda10086_config = { - .demod_address = 0x1c, + .demod_address = 0x0e, .invert = 0, .diseqc_tone = 1, .xtal_freq = TDA10086_XTAL_16M, }; static struct stv0288_config lme_config = { - .demod_address = 0xd0, + .demod_address = 0x68, .min_delay_ms = 15, .inittab = s7395_inittab, }; static struct ix2505v_config lme_tuner = { - .tuner_address = 0xc0, + .tuner_address = 0x60, .min_delay_ms = 100, .tuner_gain = 0x0, .tuner_chargepump = 0x3, }; static struct stv0299_config sharp_z0194_config = { - .demod_address = 0xd0, + .demod_address = 0x68, .inittab = sharp_z0194a_inittab, .mclk = 88000000UL, .invert = 0, @@ -944,7 +945,7 @@ static int dm04_rs2000_set_ts_param(struct dvb_frontend *fe, } static struct m88rs2000_config m88rs2000_config = { - .demod_addr = 0xd0, + .demod_addr = 0x68, .set_ts_params = dm04_rs2000_set_ts_param, }; @@ -1054,7 +1055,7 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) info("TUN Found Frontend TDA10086"); st->i2c_tuner_gate_w = 4; st->i2c_tuner_gate_r = 4; - st->i2c_tuner_addr = 0xc0; + st->i2c_tuner_addr = 0x60; st->tuner_config = TUNER_LG; if (st->dvb_usb_lme2510_firmware != TUNER_LG) { st->dvb_usb_lme2510_firmware = TUNER_LG; @@ -1070,7 +1071,7 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) info("FE Found Stv0299"); st->i2c_tuner_gate_w = 4; st->i2c_tuner_gate_r = 5; - st->i2c_tuner_addr = 0xc0; + st->i2c_tuner_addr = 0x60; st->tuner_config = TUNER_S0194; if (st->dvb_usb_lme2510_firmware != TUNER_S0194) { st->dvb_usb_lme2510_firmware = TUNER_S0194; @@ -1087,7 +1088,7 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) info("FE Found Stv0288"); st->i2c_tuner_gate_w = 4; st->i2c_tuner_gate_r = 5; - st->i2c_tuner_addr = 0xc0; + st->i2c_tuner_addr = 0x60; st->tuner_config = TUNER_S7395; if (st->dvb_usb_lme2510_firmware != TUNER_S7395) { st->dvb_usb_lme2510_firmware = TUNER_S7395; @@ -1106,7 +1107,7 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) &d->i2c_adap); st->i2c_tuner_gate_w = 5; st->i2c_tuner_gate_r = 5; - st->i2c_tuner_addr = 0xc0; + st->i2c_tuner_addr = 0x60; st->tuner_config = TUNER_RS2000; st->fe_set_voltage = adap->fe[0]->ops.set_voltage; @@ -1151,7 +1152,7 @@ static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap) switch (st->tuner_config) { case TUNER_LG: - if (dvb_attach(tda826x_attach, adap->fe[0], 0xc0, + if (dvb_attach(tda826x_attach, adap->fe[0], 0x60, &d->i2c_adap, 1)) ret = st->tuner_config; break; @@ -1161,7 +1162,7 @@ static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap) ret = st->tuner_config; break; case TUNER_S0194: - if (dvb_attach(dvb_pll_attach , adap->fe[0], 0xc0, + if (dvb_attach(dvb_pll_attach , adap->fe[0], 0x60, &d->i2c_adap, DVB_PLL_OPERA1)) ret = st->tuner_config; break; -- cgit v1.2.3 From 9a32299394d8cce79ca7d0098dc32c4f14032dcd Mon Sep 17 00:00:00 2001 From: Philippe De Muyter Date: Fri, 12 Oct 2012 17:52:45 +0200 Subject: powerpc, dma: move bestcomm driver from arch/powerpc/sysdev to drivers/dma The bestcomm dma hardware, and some of its users like the FEC ethernet component, is used in different FreeScale parts, including non-powerpc parts like the ColdFire MCF547x & MCF548x families. Don't keep the driver hidden in arch/powerpc where it is inaccessible for other arches. .c files are moved to drivers/dma/bestcomm, while .h files are moved to include/linux/fsl/bestcomm. Makefiles, Kconfigs and #include directives are updated for the new file locations. Tested by recompiling for MPC5200 with all bestcomm users enabled. Signed-off-by: Philippe De Muyter Signed-off-by: Anatolij Gustschin --- arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c | 6 +- arch/powerpc/platforms/Kconfig | 2 - arch/powerpc/sysdev/Makefile | 1 - arch/powerpc/sysdev/bestcomm/Kconfig | 36 -- arch/powerpc/sysdev/bestcomm/Makefile | 14 - arch/powerpc/sysdev/bestcomm/ata.c | 157 ------ arch/powerpc/sysdev/bestcomm/ata.h | 30 -- arch/powerpc/sysdev/bestcomm/bcom_ata_task.c | 67 --- arch/powerpc/sysdev/bestcomm/bcom_fec_rx_task.c | 78 --- arch/powerpc/sysdev/bestcomm/bcom_fec_tx_task.c | 91 ---- arch/powerpc/sysdev/bestcomm/bcom_gen_bd_rx_task.c | 63 --- arch/powerpc/sysdev/bestcomm/bcom_gen_bd_tx_task.c | 69 --- arch/powerpc/sysdev/bestcomm/bestcomm.c | 532 --------------------- arch/powerpc/sysdev/bestcomm/bestcomm.h | 213 --------- arch/powerpc/sysdev/bestcomm/bestcomm_priv.h | 350 -------------- arch/powerpc/sysdev/bestcomm/fec.c | 270 ----------- arch/powerpc/sysdev/bestcomm/fec.h | 61 --- arch/powerpc/sysdev/bestcomm/gen_bd.c | 354 -------------- arch/powerpc/sysdev/bestcomm/gen_bd.h | 53 -- arch/powerpc/sysdev/bestcomm/sram.c | 178 ------- arch/powerpc/sysdev/bestcomm/sram.h | 54 --- drivers/Makefile | 2 +- drivers/ata/pata_mpc52xx.c | 6 +- drivers/dma/Kconfig | 2 + drivers/dma/Makefile | 1 + drivers/dma/bestcomm/Kconfig | 36 ++ drivers/dma/bestcomm/Makefile | 14 + drivers/dma/bestcomm/ata.c | 157 ++++++ drivers/dma/bestcomm/bcom_ata_task.c | 67 +++ drivers/dma/bestcomm/bcom_fec_rx_task.c | 78 +++ drivers/dma/bestcomm/bcom_fec_tx_task.c | 91 ++++ drivers/dma/bestcomm/bcom_gen_bd_rx_task.c | 63 +++ drivers/dma/bestcomm/bcom_gen_bd_tx_task.c | 69 +++ drivers/dma/bestcomm/bestcomm.c | 532 +++++++++++++++++++++ drivers/dma/bestcomm/fec.c | 270 +++++++++++ drivers/dma/bestcomm/gen_bd.c | 354 ++++++++++++++ drivers/dma/bestcomm/sram.c | 178 +++++++ drivers/net/ethernet/freescale/fec_mpc52xx.c | 4 +- include/linux/fsl/bestcomm/ata.h | 30 ++ include/linux/fsl/bestcomm/bestcomm.h | 213 +++++++++ include/linux/fsl/bestcomm/bestcomm_priv.h | 350 ++++++++++++++ include/linux/fsl/bestcomm/fec.h | 61 +++ include/linux/fsl/bestcomm/gen_bd.h | 53 ++ include/linux/fsl/bestcomm/sram.h | 54 +++ sound/soc/fsl/mpc5200_dma.c | 4 +- 45 files changed, 2684 insertions(+), 2684 deletions(-) delete mode 100644 arch/powerpc/sysdev/bestcomm/Kconfig delete mode 100644 arch/powerpc/sysdev/bestcomm/Makefile delete mode 100644 arch/powerpc/sysdev/bestcomm/ata.c delete mode 100644 arch/powerpc/sysdev/bestcomm/ata.h delete mode 100644 arch/powerpc/sysdev/bestcomm/bcom_ata_task.c delete mode 100644 arch/powerpc/sysdev/bestcomm/bcom_fec_rx_task.c delete mode 100644 arch/powerpc/sysdev/bestcomm/bcom_fec_tx_task.c delete mode 100644 arch/powerpc/sysdev/bestcomm/bcom_gen_bd_rx_task.c delete mode 100644 arch/powerpc/sysdev/bestcomm/bcom_gen_bd_tx_task.c delete mode 100644 arch/powerpc/sysdev/bestcomm/bestcomm.c delete mode 100644 arch/powerpc/sysdev/bestcomm/bestcomm.h delete mode 100644 arch/powerpc/sysdev/bestcomm/bestcomm_priv.h delete mode 100644 arch/powerpc/sysdev/bestcomm/fec.c delete mode 100644 arch/powerpc/sysdev/bestcomm/fec.h delete mode 100644 arch/powerpc/sysdev/bestcomm/gen_bd.c delete mode 100644 arch/powerpc/sysdev/bestcomm/gen_bd.h delete mode 100644 arch/powerpc/sysdev/bestcomm/sram.c delete mode 100644 arch/powerpc/sysdev/bestcomm/sram.h create mode 100644 drivers/dma/bestcomm/Kconfig create mode 100644 drivers/dma/bestcomm/Makefile create mode 100644 drivers/dma/bestcomm/ata.c create mode 100644 drivers/dma/bestcomm/bcom_ata_task.c create mode 100644 drivers/dma/bestcomm/bcom_fec_rx_task.c create mode 100644 drivers/dma/bestcomm/bcom_fec_tx_task.c create mode 100644 drivers/dma/bestcomm/bcom_gen_bd_rx_task.c create mode 100644 drivers/dma/bestcomm/bcom_gen_bd_tx_task.c create mode 100644 drivers/dma/bestcomm/bestcomm.c create mode 100644 drivers/dma/bestcomm/fec.c create mode 100644 drivers/dma/bestcomm/gen_bd.c create mode 100644 drivers/dma/bestcomm/sram.c create mode 100644 include/linux/fsl/bestcomm/ata.h create mode 100644 include/linux/fsl/bestcomm/bestcomm.h create mode 100644 include/linux/fsl/bestcomm/bestcomm_priv.h create mode 100644 include/linux/fsl/bestcomm/fec.h create mode 100644 include/linux/fsl/bestcomm/gen_bd.h create mode 100644 include/linux/fsl/bestcomm/sram.h (limited to 'drivers') diff --git a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c index 16150fa430f9..93fa645904e6 100644 --- a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c +++ b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c @@ -20,9 +20,9 @@ #include #include -#include -#include -#include +#include +#include +#include MODULE_AUTHOR("Grant Likely "); MODULE_DESCRIPTION("MPC5200 LocalPlus FIFO device driver"); diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index 48a920d51489..52de8bccfb30 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -352,8 +352,6 @@ config OF_RTC Uses information from the OF or flattened device tree to instantiate platform devices for direct mapped RTC chips like the DS1742 or DS1743. -source "arch/powerpc/sysdev/bestcomm/Kconfig" - config SIMPLE_GPIO bool "Support for simple, memory-mapped GPIO controllers" depends on PPC diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index a57600b3a4e3..3884776600fd 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -26,7 +26,6 @@ obj-$(CONFIG_SIMPLE_GPIO) += simple_gpio.o obj-$(CONFIG_FSL_RIO) += fsl_rio.o fsl_rmu.o obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o obj-$(CONFIG_QUICC_ENGINE) += qe_lib/ -obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/ mv64x60-$(CONFIG_PCI) += mv64x60_pci.o obj-$(CONFIG_MV64X60) += $(mv64x60-y) mv64x60_pic.o mv64x60_dev.o \ mv64x60_udbg.o diff --git a/arch/powerpc/sysdev/bestcomm/Kconfig b/arch/powerpc/sysdev/bestcomm/Kconfig deleted file mode 100644 index 29e427085efb..000000000000 --- a/arch/powerpc/sysdev/bestcomm/Kconfig +++ /dev/null @@ -1,36 +0,0 @@ -# -# Kconfig options for Bestcomm -# - -config PPC_BESTCOMM - tristate "Bestcomm DMA engine support" - depends on PPC_MPC52xx - default n - select PPC_LIB_RHEAP - help - BestComm is the name of the communication coprocessor found - on the Freescale MPC5200 family of processor. Its usage is - optional for some drivers (like ATA), but required for - others (like FEC). - - If you want to use drivers that require DMA operations, - answer Y or M. Otherwise say N. - -config PPC_BESTCOMM_ATA - tristate - depends on PPC_BESTCOMM - help - This option enables the support for the ATA task. - -config PPC_BESTCOMM_FEC - tristate - depends on PPC_BESTCOMM - help - This option enables the support for the FEC tasks. - -config PPC_BESTCOMM_GEN_BD - tristate - depends on PPC_BESTCOMM - help - This option enables the support for the GenBD tasks. - diff --git a/arch/powerpc/sysdev/bestcomm/Makefile b/arch/powerpc/sysdev/bestcomm/Makefile deleted file mode 100644 index aed2df2a6580..000000000000 --- a/arch/powerpc/sysdev/bestcomm/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# -# Makefile for BestComm & co -# - -bestcomm-core-objs := bestcomm.o sram.o -bestcomm-ata-objs := ata.o bcom_ata_task.o -bestcomm-fec-objs := fec.o bcom_fec_rx_task.o bcom_fec_tx_task.o -bestcomm-gen-bd-objs := gen_bd.o bcom_gen_bd_rx_task.o bcom_gen_bd_tx_task.o - -obj-$(CONFIG_PPC_BESTCOMM) += bestcomm-core.o -obj-$(CONFIG_PPC_BESTCOMM_ATA) += bestcomm-ata.o -obj-$(CONFIG_PPC_BESTCOMM_FEC) += bestcomm-fec.o -obj-$(CONFIG_PPC_BESTCOMM_GEN_BD) += bestcomm-gen-bd.o - diff --git a/arch/powerpc/sysdev/bestcomm/ata.c b/arch/powerpc/sysdev/bestcomm/ata.c deleted file mode 100644 index 901c9f91e5dd..000000000000 --- a/arch/powerpc/sysdev/bestcomm/ata.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Bestcomm ATA task driver - * - * - * Patterned after bestcomm/fec.c by Dale Farnsworth - * 2003-2004 (c) MontaVista, Software, Inc. - * - * Copyright (C) 2006-2007 Sylvain Munaut - * Copyright (C) 2006 Freescale - John Rigby - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -#include -#include -#include -#include - -#include "bestcomm.h" -#include "bestcomm_priv.h" -#include "ata.h" - - -/* ======================================================================== */ -/* Task image/var/inc */ -/* ======================================================================== */ - -/* ata task image */ -extern u32 bcom_ata_task[]; - -/* ata task vars that need to be set before enabling the task */ -struct bcom_ata_var { - u32 enable; /* (u16*) address of task's control register */ - u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */ - u32 bd_last; /* (struct bcom_bd*) end of ring buffer */ - u32 bd_start; /* (struct bcom_bd*) current bd */ - u32 buffer_size; /* size of receive buffer */ -}; - -/* ata task incs that need to be set before enabling the task */ -struct bcom_ata_inc { - u16 pad0; - s16 incr_bytes; - u16 pad1; - s16 incr_dst; - u16 pad2; - s16 incr_src; -}; - - -/* ======================================================================== */ -/* Task support code */ -/* ======================================================================== */ - -struct bcom_task * -bcom_ata_init(int queue_len, int maxbufsize) -{ - struct bcom_task *tsk; - struct bcom_ata_var *var; - struct bcom_ata_inc *inc; - - /* Prefetch breaks ATA DMA. Turn it off for ATA DMA */ - bcom_disable_prefetch(); - - tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_ata_bd), 0); - if (!tsk) - return NULL; - - tsk->flags = BCOM_FLAGS_NONE; - - bcom_ata_reset_bd(tsk); - - var = (struct bcom_ata_var *) bcom_task_var(tsk->tasknum); - inc = (struct bcom_ata_inc *) bcom_task_inc(tsk->tasknum); - - if (bcom_load_image(tsk->tasknum, bcom_ata_task)) { - bcom_task_free(tsk); - return NULL; - } - - var->enable = bcom_eng->regs_base + - offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]); - var->bd_base = tsk->bd_pa; - var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size); - var->bd_start = tsk->bd_pa; - var->buffer_size = maxbufsize; - - /* Configure some stuff */ - bcom_set_task_pragma(tsk->tasknum, BCOM_ATA_PRAGMA); - bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum); - - out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ATA_RX], BCOM_IPR_ATA_RX); - out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ATA_TX], BCOM_IPR_ATA_TX); - - out_be32(&bcom_eng->regs->IntPend, 1<tasknum); /* Clear ints */ - - return tsk; -} -EXPORT_SYMBOL_GPL(bcom_ata_init); - -void bcom_ata_rx_prepare(struct bcom_task *tsk) -{ - struct bcom_ata_inc *inc; - - inc = (struct bcom_ata_inc *) bcom_task_inc(tsk->tasknum); - - inc->incr_bytes = -(s16)sizeof(u32); - inc->incr_src = 0; - inc->incr_dst = sizeof(u32); - - bcom_set_initiator(tsk->tasknum, BCOM_INITIATOR_ATA_RX); -} -EXPORT_SYMBOL_GPL(bcom_ata_rx_prepare); - -void bcom_ata_tx_prepare(struct bcom_task *tsk) -{ - struct bcom_ata_inc *inc; - - inc = (struct bcom_ata_inc *) bcom_task_inc(tsk->tasknum); - - inc->incr_bytes = -(s16)sizeof(u32); - inc->incr_src = sizeof(u32); - inc->incr_dst = 0; - - bcom_set_initiator(tsk->tasknum, BCOM_INITIATOR_ATA_TX); -} -EXPORT_SYMBOL_GPL(bcom_ata_tx_prepare); - -void bcom_ata_reset_bd(struct bcom_task *tsk) -{ - struct bcom_ata_var *var; - - /* Reset all BD */ - memset(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size); - - tsk->index = 0; - tsk->outdex = 0; - - var = (struct bcom_ata_var *) bcom_task_var(tsk->tasknum); - var->bd_start = var->bd_base; -} -EXPORT_SYMBOL_GPL(bcom_ata_reset_bd); - -void bcom_ata_release(struct bcom_task *tsk) -{ - /* Nothing special for the ATA tasks */ - bcom_task_free(tsk); -} -EXPORT_SYMBOL_GPL(bcom_ata_release); - - -MODULE_DESCRIPTION("BestComm ATA task driver"); -MODULE_AUTHOR("John Rigby"); -MODULE_LICENSE("GPL v2"); - diff --git a/arch/powerpc/sysdev/bestcomm/ata.h b/arch/powerpc/sysdev/bestcomm/ata.h deleted file mode 100644 index 0b2371811334..000000000000 --- a/arch/powerpc/sysdev/bestcomm/ata.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Header for Bestcomm ATA task driver - * - * - * Copyright (C) 2006 Freescale - John Rigby - * Copyright (C) 2006 Sylvain Munaut - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -#ifndef __BESTCOMM_ATA_H__ -#define __BESTCOMM_ATA_H__ - - -struct bcom_ata_bd { - u32 status; - u32 src_pa; - u32 dst_pa; -}; - -extern struct bcom_task * bcom_ata_init(int queue_len, int maxbufsize); -extern void bcom_ata_rx_prepare(struct bcom_task *tsk); -extern void bcom_ata_tx_prepare(struct bcom_task *tsk); -extern void bcom_ata_reset_bd(struct bcom_task *tsk); -extern void bcom_ata_release(struct bcom_task *tsk); - -#endif /* __BESTCOMM_ATA_H__ */ - diff --git a/arch/powerpc/sysdev/bestcomm/bcom_ata_task.c b/arch/powerpc/sysdev/bestcomm/bcom_ata_task.c deleted file mode 100644 index cc6049a4e469..000000000000 --- a/arch/powerpc/sysdev/bestcomm/bcom_ata_task.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Bestcomm ATA task microcode - * - * Copyright (c) 2004 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - * Created based on bestcom/code_dma/image_rtos1/dma_image.hex - */ - -#include - -/* - * The header consists of the following fields: - * u32 magic; - * u8 desc_size; - * u8 var_size; - * u8 inc_size; - * u8 first_var; - * u8 reserved[8]; - * - * The size fields contain the number of 32-bit words. - */ - -u32 bcom_ata_task[] = { - /* header */ - 0x4243544b, - 0x0e060709, - 0x00000000, - 0x00000000, - - /* Task descriptors */ - 0x8198009b, /* LCD: idx0 = var3; idx0 <= var2; idx0 += inc3 */ - 0x13e00c08, /* DRD1A: var3 = var1; FN=0 MORE init=31 WS=0 RS=0 */ - 0xb8000264, /* LCD: idx1 = *idx0, idx2 = var0; idx1 < var9; idx1 += inc4, idx2 += inc4 */ - 0x10000f00, /* DRD1A: var3 = idx0; FN=0 MORE init=0 WS=0 RS=0 */ - 0x60140002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */ - 0x0c8cfc8a, /* DRD2B1: *idx2 = EU3(); EU3(*idx2,var10) */ - 0xd8988240, /* LCDEXT: idx1 = idx1; idx1 > var9; idx1 += inc0 */ - 0xf845e011, /* LCDEXT: idx2 = *(idx0 + var00000015); ; idx2 += inc2 */ - 0xb845e00a, /* LCD: idx3 = *(idx0 + var00000019); ; idx3 += inc1 */ - 0x0bfecf90, /* DRD1A: *idx3 = *idx2; FN=0 TFD init=31 WS=3 RS=3 */ - 0x9898802d, /* LCD: idx1 = idx1; idx1 once var0; idx1 += inc5 */ - 0x64000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 INT EXT init=0 WS=0 RS=0 */ - 0x0c0cf849, /* DRD2B1: *idx0 = EU3(); EU3(idx1,var9) */ - 0x000001f8, /* NOP */ - - /* VAR[9]-VAR[14] */ - 0x40000000, - 0x7fff7fff, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - - /* INC[0]-INC[6] */ - 0x40000000, - 0xe0000000, - 0xe0000000, - 0xa000000c, - 0x20000000, - 0x00000000, - 0x00000000, -}; - diff --git a/arch/powerpc/sysdev/bestcomm/bcom_fec_rx_task.c b/arch/powerpc/sysdev/bestcomm/bcom_fec_rx_task.c deleted file mode 100644 index a1ad6a02fcef..000000000000 --- a/arch/powerpc/sysdev/bestcomm/bcom_fec_rx_task.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Bestcomm FEC RX task microcode - * - * Copyright (c) 2004 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - * Automatically created based on BestCommAPI-2.2/code_dma/image_rtos1/dma_image.hex - * on Tue Mar 22 11:19:38 2005 GMT - */ - -#include - -/* - * The header consists of the following fields: - * u32 magic; - * u8 desc_size; - * u8 var_size; - * u8 inc_size; - * u8 first_var; - * u8 reserved[8]; - * - * The size fields contain the number of 32-bit words. - */ - -u32 bcom_fec_rx_task[] = { - /* header */ - 0x4243544b, - 0x18060709, - 0x00000000, - 0x00000000, - - /* Task descriptors */ - 0x808220e3, /* LCD: idx0 = var1, idx1 = var4; idx1 <= var3; idx0 += inc4, idx1 += inc3 */ - 0x10601010, /* DRD1A: var4 = var2; FN=0 MORE init=3 WS=0 RS=0 */ - 0xb8800264, /* LCD: idx2 = *idx1, idx3 = var0; idx2 < var9; idx2 += inc4, idx3 += inc4 */ - 0x10001308, /* DRD1A: var4 = idx1; FN=0 MORE init=0 WS=0 RS=0 */ - 0x60140002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */ - 0x0cccfcca, /* DRD2B1: *idx3 = EU3(); EU3(*idx3,var10) */ - 0x80004000, /* LCDEXT: idx2 = 0x00000000; ; */ - 0xb8c58029, /* LCD: idx3 = *(idx1 + var00000015); idx3 once var0; idx3 += inc5 */ - 0x60000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=0 RS=0 */ - 0x088cf8cc, /* DRD2B1: idx2 = EU3(); EU3(idx3,var12) */ - 0x991982f2, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var11; idx2 += inc6, idx3 += inc2 */ - 0x006acf80, /* DRD1A: *idx3 = *idx0; FN=0 init=3 WS=1 RS=1 */ - 0x80004000, /* LCDEXT: idx2 = 0x00000000; ; */ - 0x9999802d, /* LCD: idx3 = idx3; idx3 once var0; idx3 += inc5 */ - 0x70000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */ - 0x034cfc4e, /* DRD2B1: var13 = EU3(); EU3(*idx1,var14) */ - 0x00008868, /* DRD1A: idx2 = var13; FN=0 init=0 WS=0 RS=0 */ - 0x99198341, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var13; idx2 += inc0, idx3 += inc1 */ - 0x007ecf80, /* DRD1A: *idx3 = *idx0; FN=0 init=3 WS=3 RS=3 */ - 0x99198272, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var9; idx2 += inc6, idx3 += inc2 */ - 0x046acf80, /* DRD1A: *idx3 = *idx0; FN=0 INT init=3 WS=1 RS=1 */ - 0x9819002d, /* LCD: idx2 = idx0; idx2 once var0; idx2 += inc5 */ - 0x0060c790, /* DRD1A: *idx1 = *idx2; FN=0 init=3 WS=0 RS=0 */ - 0x000001f8, /* NOP */ - - /* VAR[9]-VAR[14] */ - 0x40000000, - 0x7fff7fff, - 0x00000000, - 0x00000003, - 0x40000008, - 0x43ffffff, - - /* INC[0]-INC[6] */ - 0x40000000, - 0xe0000000, - 0xe0000000, - 0xa0000008, - 0x20000000, - 0x00000000, - 0x4000ffff, -}; - diff --git a/arch/powerpc/sysdev/bestcomm/bcom_fec_tx_task.c b/arch/powerpc/sysdev/bestcomm/bcom_fec_tx_task.c deleted file mode 100644 index b1c495c3a65a..000000000000 --- a/arch/powerpc/sysdev/bestcomm/bcom_fec_tx_task.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Bestcomm FEC TX task microcode - * - * Copyright (c) 2004 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - * Automatically created based on BestCommAPI-2.2/code_dma/image_rtos1/dma_image.hex - * on Tue Mar 22 11:19:29 2005 GMT - */ - -#include - -/* - * The header consists of the following fields: - * u32 magic; - * u8 desc_size; - * u8 var_size; - * u8 inc_size; - * u8 first_var; - * u8 reserved[8]; - * - * The size fields contain the number of 32-bit words. - */ - -u32 bcom_fec_tx_task[] = { - /* header */ - 0x4243544b, - 0x2407070d, - 0x00000000, - 0x00000000, - - /* Task descriptors */ - 0x8018001b, /* LCD: idx0 = var0; idx0 <= var0; idx0 += inc3 */ - 0x60000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */ - 0x01ccfc0d, /* DRD2B1: var7 = EU3(); EU3(*idx0,var13) */ - 0x8082a123, /* LCD: idx0 = var1, idx1 = var5; idx1 <= var4; idx0 += inc4, idx1 += inc3 */ - 0x10801418, /* DRD1A: var5 = var3; FN=0 MORE init=4 WS=0 RS=0 */ - 0xf88103a4, /* LCDEXT: idx2 = *idx1, idx3 = var2; idx2 < var14; idx2 += inc4, idx3 += inc4 */ - 0x801a6024, /* LCD: idx4 = var0; ; idx4 += inc4 */ - 0x10001708, /* DRD1A: var5 = idx1; FN=0 MORE init=0 WS=0 RS=0 */ - 0x60140002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */ - 0x0cccfccf, /* DRD2B1: *idx3 = EU3(); EU3(*idx3,var15) */ - 0x991a002c, /* LCD: idx2 = idx2, idx3 = idx4; idx2 once var0; idx2 += inc5, idx3 += inc4 */ - 0x70000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */ - 0x024cfc4d, /* DRD2B1: var9 = EU3(); EU3(*idx1,var13) */ - 0x60000003, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=3 EXT init=0 WS=0 RS=0 */ - 0x0cccf247, /* DRD2B1: *idx3 = EU3(); EU3(var9,var7) */ - 0x80004000, /* LCDEXT: idx2 = 0x00000000; ; */ - 0xb8c80029, /* LCD: idx3 = *(idx1 + var0000001a); idx3 once var0; idx3 += inc5 */ - 0x70000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */ - 0x088cf8d1, /* DRD2B1: idx2 = EU3(); EU3(idx3,var17) */ - 0x00002f10, /* DRD1A: var11 = idx2; FN=0 init=0 WS=0 RS=0 */ - 0x99198432, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var16; idx2 += inc6, idx3 += inc2 */ - 0x008ac398, /* DRD1A: *idx0 = *idx3; FN=0 init=4 WS=1 RS=1 */ - 0x80004000, /* LCDEXT: idx2 = 0x00000000; ; */ - 0x9999802d, /* LCD: idx3 = idx3; idx3 once var0; idx3 += inc5 */ - 0x70000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */ - 0x048cfc53, /* DRD2B1: var18 = EU3(); EU3(*idx1,var19) */ - 0x60000008, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=8 EXT init=0 WS=0 RS=0 */ - 0x088cf48b, /* DRD2B1: idx2 = EU3(); EU3(var18,var11) */ - 0x99198481, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var18; idx2 += inc0, idx3 += inc1 */ - 0x009ec398, /* DRD1A: *idx0 = *idx3; FN=0 init=4 WS=3 RS=3 */ - 0x991983b2, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var14; idx2 += inc6, idx3 += inc2 */ - 0x088ac398, /* DRD1A: *idx0 = *idx3; FN=0 TFD init=4 WS=1 RS=1 */ - 0x9919002d, /* LCD: idx2 = idx2; idx2 once var0; idx2 += inc5 */ - 0x60000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */ - 0x0c4cf88e, /* DRD2B1: *idx1 = EU3(); EU3(idx2,var14) */ - 0x000001f8, /* NOP */ - - /* VAR[13]-VAR[19] */ - 0x0c000000, - 0x40000000, - 0x7fff7fff, - 0x00000000, - 0x00000003, - 0x40000004, - 0x43ffffff, - - /* INC[0]-INC[6] */ - 0x40000000, - 0xe0000000, - 0xe0000000, - 0xa0000008, - 0x20000000, - 0x00000000, - 0x4000ffff, -}; - diff --git a/arch/powerpc/sysdev/bestcomm/bcom_gen_bd_rx_task.c b/arch/powerpc/sysdev/bestcomm/bcom_gen_bd_rx_task.c deleted file mode 100644 index efee022b0256..000000000000 --- a/arch/powerpc/sysdev/bestcomm/bcom_gen_bd_rx_task.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Bestcomm GenBD RX task microcode - * - * Copyright (C) 2006 AppSpec Computer Technologies Corp. - * Jeff Gibbons - * Copyright (c) 2004 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - * Based on BestCommAPI-2.2/code_dma/image_rtos1/dma_image.hex - * on Tue Mar 4 10:14:12 2006 GMT - * - */ - -#include - -/* - * The header consists of the following fields: - * u32 magic; - * u8 desc_size; - * u8 var_size; - * u8 inc_size; - * u8 first_var; - * u8 reserved[8]; - * - * The size fields contain the number of 32-bit words. - */ - -u32 bcom_gen_bd_rx_task[] = { - /* header */ - 0x4243544b, - 0x0d020409, - 0x00000000, - 0x00000000, - - /* Task descriptors */ - 0x808220da, /* LCD: idx0 = var1, idx1 = var4; idx1 <= var3; idx0 += inc3, idx1 += inc2 */ - 0x13e01010, /* DRD1A: var4 = var2; FN=0 MORE init=31 WS=0 RS=0 */ - 0xb880025b, /* LCD: idx2 = *idx1, idx3 = var0; idx2 < var9; idx2 += inc3, idx3 += inc3 */ - 0x10001308, /* DRD1A: var4 = idx1; FN=0 MORE init=0 WS=0 RS=0 */ - 0x60140002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */ - 0x0cccfcca, /* DRD2B1: *idx3 = EU3(); EU3(*idx3,var10) */ - 0xd9190240, /* LCDEXT: idx2 = idx2; idx2 > var9; idx2 += inc0 */ - 0xb8c5e009, /* LCD: idx3 = *(idx1 + var00000015); ; idx3 += inc1 */ - 0x07fecf80, /* DRD1A: *idx3 = *idx0; FN=0 INT init=31 WS=3 RS=3 */ - 0x99190024, /* LCD: idx2 = idx2; idx2 once var0; idx2 += inc4 */ - 0x60000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */ - 0x0c4cf889, /* DRD2B1: *idx1 = EU3(); EU3(idx2,var9) */ - 0x000001f8, /* NOP */ - - /* VAR[9]-VAR[10] */ - 0x40000000, - 0x7fff7fff, - - /* INC[0]-INC[3] */ - 0x40000000, - 0xe0000000, - 0xa0000008, - 0x20000000, -}; - diff --git a/arch/powerpc/sysdev/bestcomm/bcom_gen_bd_tx_task.c b/arch/powerpc/sysdev/bestcomm/bcom_gen_bd_tx_task.c deleted file mode 100644 index c605aa42ecbb..000000000000 --- a/arch/powerpc/sysdev/bestcomm/bcom_gen_bd_tx_task.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Bestcomm GenBD TX task microcode - * - * Copyright (C) 2006 AppSpec Computer Technologies Corp. - * Jeff Gibbons - * Copyright (c) 2004 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - * Based on BestCommAPI-2.2/code_dma/image_rtos1/dma_image.hex - * on Tue Mar 4 10:14:12 2006 GMT - * - */ - -#include - -/* - * The header consists of the following fields: - * u32 magic; - * u8 desc_size; - * u8 var_size; - * u8 inc_size; - * u8 first_var; - * u8 reserved[8]; - * - * The size fields contain the number of 32-bit words. - */ - -u32 bcom_gen_bd_tx_task[] = { - /* header */ - 0x4243544b, - 0x0f040609, - 0x00000000, - 0x00000000, - - /* Task descriptors */ - 0x800220e3, /* LCD: idx0 = var0, idx1 = var4; idx1 <= var3; idx0 += inc4, idx1 += inc3 */ - 0x13e01010, /* DRD1A: var4 = var2; FN=0 MORE init=31 WS=0 RS=0 */ - 0xb8808264, /* LCD: idx2 = *idx1, idx3 = var1; idx2 < var9; idx2 += inc4, idx3 += inc4 */ - 0x10001308, /* DRD1A: var4 = idx1; FN=0 MORE init=0 WS=0 RS=0 */ - 0x60140002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */ - 0x0cccfcca, /* DRD2B1: *idx3 = EU3(); EU3(*idx3,var10) */ - 0xd9190300, /* LCDEXT: idx2 = idx2; idx2 > var12; idx2 += inc0 */ - 0xb8c5e009, /* LCD: idx3 = *(idx1 + var00000015); ; idx3 += inc1 */ - 0x03fec398, /* DRD1A: *idx0 = *idx3; FN=0 init=31 WS=3 RS=3 */ - 0x9919826a, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var9; idx2 += inc5, idx3 += inc2 */ - 0x0feac398, /* DRD1A: *idx0 = *idx3; FN=0 TFD INT init=31 WS=1 RS=1 */ - 0x99190036, /* LCD: idx2 = idx2; idx2 once var0; idx2 += inc6 */ - 0x60000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */ - 0x0c4cf889, /* DRD2B1: *idx1 = EU3(); EU3(idx2,var9) */ - 0x000001f8, /* NOP */ - - /* VAR[9]-VAR[12] */ - 0x40000000, - 0x7fff7fff, - 0x00000000, - 0x40000004, - - /* INC[0]-INC[5] */ - 0x40000000, - 0xe0000000, - 0xe0000000, - 0xa0000008, - 0x20000000, - 0x4000ffff, -}; - diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm.c b/arch/powerpc/sysdev/bestcomm/bestcomm.c deleted file mode 100644 index b3fbb271be87..000000000000 --- a/arch/powerpc/sysdev/bestcomm/bestcomm.c +++ /dev/null @@ -1,532 +0,0 @@ -/* - * Driver for MPC52xx processor BestComm peripheral controller - * - * - * Copyright (C) 2006-2007 Sylvain Munaut - * Copyright (C) 2005 Varma Electronics Oy, - * ( by Andrey Volkov ) - * Copyright (C) 2003-2004 MontaVista, Software, Inc. - * ( by Dale Farnsworth ) - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sram.h" -#include "bestcomm_priv.h" -#include "bestcomm.h" - -#define DRIVER_NAME "bestcomm-core" - -/* MPC5200 device tree match tables */ -static struct of_device_id mpc52xx_sram_ids[] __devinitdata = { - { .compatible = "fsl,mpc5200-sram", }, - { .compatible = "mpc5200-sram", }, - {} -}; - - -struct bcom_engine *bcom_eng = NULL; -EXPORT_SYMBOL_GPL(bcom_eng); /* needed for inline functions */ - -/* ======================================================================== */ -/* Public and private API */ -/* ======================================================================== */ - -/* Private API */ - -struct bcom_task * -bcom_task_alloc(int bd_count, int bd_size, int priv_size) -{ - int i, tasknum = -1; - struct bcom_task *tsk; - - /* Don't try to do anything if bestcomm init failed */ - if (!bcom_eng) - return NULL; - - /* Get and reserve a task num */ - spin_lock(&bcom_eng->lock); - - for (i=0; itdt[i].stop) { /* we use stop as a marker */ - bcom_eng->tdt[i].stop = 0xfffffffful; /* dummy addr */ - tasknum = i; - break; - } - - spin_unlock(&bcom_eng->lock); - - if (tasknum < 0) - return NULL; - - /* Allocate our structure */ - tsk = kzalloc(sizeof(struct bcom_task) + priv_size, GFP_KERNEL); - if (!tsk) - goto error; - - tsk->tasknum = tasknum; - if (priv_size) - tsk->priv = (void*)tsk + sizeof(struct bcom_task); - - /* Get IRQ of that task */ - tsk->irq = irq_of_parse_and_map(bcom_eng->ofnode, tsk->tasknum); - if (tsk->irq == NO_IRQ) - goto error; - - /* Init the BDs, if needed */ - if (bd_count) { - tsk->cookie = kmalloc(sizeof(void*) * bd_count, GFP_KERNEL); - if (!tsk->cookie) - goto error; - - tsk->bd = bcom_sram_alloc(bd_count * bd_size, 4, &tsk->bd_pa); - if (!tsk->bd) - goto error; - memset(tsk->bd, 0x00, bd_count * bd_size); - - tsk->num_bd = bd_count; - tsk->bd_size = bd_size; - } - - return tsk; - -error: - if (tsk) { - if (tsk->irq != NO_IRQ) - irq_dispose_mapping(tsk->irq); - bcom_sram_free(tsk->bd); - kfree(tsk->cookie); - kfree(tsk); - } - - bcom_eng->tdt[tasknum].stop = 0; - - return NULL; -} -EXPORT_SYMBOL_GPL(bcom_task_alloc); - -void -bcom_task_free(struct bcom_task *tsk) -{ - /* Stop the task */ - bcom_disable_task(tsk->tasknum); - - /* Clear TDT */ - bcom_eng->tdt[tsk->tasknum].start = 0; - bcom_eng->tdt[tsk->tasknum].stop = 0; - - /* Free everything */ - irq_dispose_mapping(tsk->irq); - bcom_sram_free(tsk->bd); - kfree(tsk->cookie); - kfree(tsk); -} -EXPORT_SYMBOL_GPL(bcom_task_free); - -int -bcom_load_image(int task, u32 *task_image) -{ - struct bcom_task_header *hdr = (struct bcom_task_header *)task_image; - struct bcom_tdt *tdt; - u32 *desc, *var, *inc; - u32 *desc_src, *var_src, *inc_src; - - /* Safety checks */ - if (hdr->magic != BCOM_TASK_MAGIC) { - printk(KERN_ERR DRIVER_NAME - ": Trying to load invalid microcode\n"); - return -EINVAL; - } - - if ((task < 0) || (task >= BCOM_MAX_TASKS)) { - printk(KERN_ERR DRIVER_NAME - ": Trying to load invalid task %d\n", task); - return -EINVAL; - } - - /* Initial load or reload */ - tdt = &bcom_eng->tdt[task]; - - if (tdt->start) { - desc = bcom_task_desc(task); - if (hdr->desc_size != bcom_task_num_descs(task)) { - printk(KERN_ERR DRIVER_NAME - ": Trying to reload wrong task image " - "(%d size %d/%d)!\n", - task, - hdr->desc_size, - bcom_task_num_descs(task)); - return -EINVAL; - } - } else { - phys_addr_t start_pa; - - desc = bcom_sram_alloc(hdr->desc_size * sizeof(u32), 4, &start_pa); - if (!desc) - return -ENOMEM; - - tdt->start = start_pa; - tdt->stop = start_pa + ((hdr->desc_size-1) * sizeof(u32)); - } - - var = bcom_task_var(task); - inc = bcom_task_inc(task); - - /* Clear & copy */ - memset(var, 0x00, BCOM_VAR_SIZE); - memset(inc, 0x00, BCOM_INC_SIZE); - - desc_src = (u32 *)(hdr + 1); - var_src = desc_src + hdr->desc_size; - inc_src = var_src + hdr->var_size; - - memcpy(desc, desc_src, hdr->desc_size * sizeof(u32)); - memcpy(var + hdr->first_var, var_src, hdr->var_size * sizeof(u32)); - memcpy(inc, inc_src, hdr->inc_size * sizeof(u32)); - - return 0; -} -EXPORT_SYMBOL_GPL(bcom_load_image); - -void -bcom_set_initiator(int task, int initiator) -{ - int i; - int num_descs; - u32 *desc; - int next_drd_has_initiator; - - bcom_set_tcr_initiator(task, initiator); - - /* Just setting tcr is apparently not enough due to some problem */ - /* with it. So we just go thru all the microcode and replace in */ - /* the DRD directly */ - - desc = bcom_task_desc(task); - next_drd_has_initiator = 1; - num_descs = bcom_task_num_descs(task); - - for (i=0; itasknum); -} -EXPORT_SYMBOL_GPL(bcom_enable); - -void -bcom_disable(struct bcom_task *tsk) -{ - bcom_disable_task(tsk->tasknum); -} -EXPORT_SYMBOL_GPL(bcom_disable); - - -/* ======================================================================== */ -/* Engine init/cleanup */ -/* ======================================================================== */ - -/* Function Descriptor table */ -/* this will need to be updated if Freescale changes their task code FDT */ -static u32 fdt_ops[] = { - 0xa0045670, /* FDT[48] - load_acc() */ - 0x80045670, /* FDT[49] - unload_acc() */ - 0x21800000, /* FDT[50] - and() */ - 0x21e00000, /* FDT[51] - or() */ - 0x21500000, /* FDT[52] - xor() */ - 0x21400000, /* FDT[53] - andn() */ - 0x21500000, /* FDT[54] - not() */ - 0x20400000, /* FDT[55] - add() */ - 0x20500000, /* FDT[56] - sub() */ - 0x20800000, /* FDT[57] - lsh() */ - 0x20a00000, /* FDT[58] - rsh() */ - 0xc0170000, /* FDT[59] - crc8() */ - 0xc0145670, /* FDT[60] - crc16() */ - 0xc0345670, /* FDT[61] - crc32() */ - 0xa0076540, /* FDT[62] - endian32() */ - 0xa0000760, /* FDT[63] - endian16() */ -}; - - -static int __devinit -bcom_engine_init(void) -{ - int task; - phys_addr_t tdt_pa, ctx_pa, var_pa, fdt_pa; - unsigned int tdt_size, ctx_size, var_size, fdt_size; - - /* Allocate & clear SRAM zones for FDT, TDTs, contexts and vars/incs */ - tdt_size = BCOM_MAX_TASKS * sizeof(struct bcom_tdt); - ctx_size = BCOM_MAX_TASKS * BCOM_CTX_SIZE; - var_size = BCOM_MAX_TASKS * (BCOM_VAR_SIZE + BCOM_INC_SIZE); - fdt_size = BCOM_FDT_SIZE; - - bcom_eng->tdt = bcom_sram_alloc(tdt_size, sizeof(u32), &tdt_pa); - bcom_eng->ctx = bcom_sram_alloc(ctx_size, BCOM_CTX_ALIGN, &ctx_pa); - bcom_eng->var = bcom_sram_alloc(var_size, BCOM_VAR_ALIGN, &var_pa); - bcom_eng->fdt = bcom_sram_alloc(fdt_size, BCOM_FDT_ALIGN, &fdt_pa); - - if (!bcom_eng->tdt || !bcom_eng->ctx || !bcom_eng->var || !bcom_eng->fdt) { - printk(KERN_ERR "DMA: SRAM alloc failed in engine init !\n"); - - bcom_sram_free(bcom_eng->tdt); - bcom_sram_free(bcom_eng->ctx); - bcom_sram_free(bcom_eng->var); - bcom_sram_free(bcom_eng->fdt); - - return -ENOMEM; - } - - memset(bcom_eng->tdt, 0x00, tdt_size); - memset(bcom_eng->ctx, 0x00, ctx_size); - memset(bcom_eng->var, 0x00, var_size); - memset(bcom_eng->fdt, 0x00, fdt_size); - - /* Copy the FDT for the EU#3 */ - memcpy(&bcom_eng->fdt[48], fdt_ops, sizeof(fdt_ops)); - - /* Initialize Task base structure */ - for (task=0; taskregs->tcr[task], 0); - out_8(&bcom_eng->regs->ipr[task], 0); - - bcom_eng->tdt[task].context = ctx_pa; - bcom_eng->tdt[task].var = var_pa; - bcom_eng->tdt[task].fdt = fdt_pa; - - var_pa += BCOM_VAR_SIZE + BCOM_INC_SIZE; - ctx_pa += BCOM_CTX_SIZE; - } - - out_be32(&bcom_eng->regs->taskBar, tdt_pa); - - /* Init 'always' initiator */ - out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ALWAYS], BCOM_IPR_ALWAYS); - - /* Disable COMM Bus Prefetch on the original 5200; it's broken */ - if ((mfspr(SPRN_SVR) & MPC5200_SVR_MASK) == MPC5200_SVR) - bcom_disable_prefetch(); - - /* Init lock */ - spin_lock_init(&bcom_eng->lock); - - return 0; -} - -static void -bcom_engine_cleanup(void) -{ - int task; - - /* Stop all tasks */ - for (task=0; taskregs->tcr[task], 0); - out_8(&bcom_eng->regs->ipr[task], 0); - } - - out_be32(&bcom_eng->regs->taskBar, 0ul); - - /* Release the SRAM zones */ - bcom_sram_free(bcom_eng->tdt); - bcom_sram_free(bcom_eng->ctx); - bcom_sram_free(bcom_eng->var); - bcom_sram_free(bcom_eng->fdt); -} - - -/* ======================================================================== */ -/* OF platform driver */ -/* ======================================================================== */ - -static int __devinit mpc52xx_bcom_probe(struct platform_device *op) -{ - struct device_node *ofn_sram; - struct resource res_bcom; - - int rv; - - /* Inform user we're ok so far */ - printk(KERN_INFO "DMA: MPC52xx BestComm driver\n"); - - /* Get the bestcomm node */ - of_node_get(op->dev.of_node); - - /* Prepare SRAM */ - ofn_sram = of_find_matching_node(NULL, mpc52xx_sram_ids); - if (!ofn_sram) { - printk(KERN_ERR DRIVER_NAME ": " - "No SRAM found in device tree\n"); - rv = -ENODEV; - goto error_ofput; - } - rv = bcom_sram_init(ofn_sram, DRIVER_NAME); - of_node_put(ofn_sram); - - if (rv) { - printk(KERN_ERR DRIVER_NAME ": " - "Error in SRAM init\n"); - goto error_ofput; - } - - /* Get a clean struct */ - bcom_eng = kzalloc(sizeof(struct bcom_engine), GFP_KERNEL); - if (!bcom_eng) { - printk(KERN_ERR DRIVER_NAME ": " - "Can't allocate state structure\n"); - rv = -ENOMEM; - goto error_sramclean; - } - - /* Save the node */ - bcom_eng->ofnode = op->dev.of_node; - - /* Get, reserve & map io */ - if (of_address_to_resource(op->dev.of_node, 0, &res_bcom)) { - printk(KERN_ERR DRIVER_NAME ": " - "Can't get resource\n"); - rv = -EINVAL; - goto error_sramclean; - } - - if (!request_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma), - DRIVER_NAME)) { - printk(KERN_ERR DRIVER_NAME ": " - "Can't request registers region\n"); - rv = -EBUSY; - goto error_sramclean; - } - - bcom_eng->regs_base = res_bcom.start; - bcom_eng->regs = ioremap(res_bcom.start, sizeof(struct mpc52xx_sdma)); - if (!bcom_eng->regs) { - printk(KERN_ERR DRIVER_NAME ": " - "Can't map registers\n"); - rv = -ENOMEM; - goto error_release; - } - - /* Now, do the real init */ - rv = bcom_engine_init(); - if (rv) - goto error_unmap; - - /* Done ! */ - printk(KERN_INFO "DMA: MPC52xx BestComm engine @%08lx ok !\n", - (long)bcom_eng->regs_base); - - return 0; - - /* Error path */ -error_unmap: - iounmap(bcom_eng->regs); -error_release: - release_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma)); -error_sramclean: - kfree(bcom_eng); - bcom_sram_cleanup(); -error_ofput: - of_node_put(op->dev.of_node); - - printk(KERN_ERR "DMA: MPC52xx BestComm init failed !\n"); - - return rv; -} - - -static int mpc52xx_bcom_remove(struct platform_device *op) -{ - /* Clean up the engine */ - bcom_engine_cleanup(); - - /* Cleanup SRAM */ - bcom_sram_cleanup(); - - /* Release regs */ - iounmap(bcom_eng->regs); - release_mem_region(bcom_eng->regs_base, sizeof(struct mpc52xx_sdma)); - - /* Release the node */ - of_node_put(bcom_eng->ofnode); - - /* Release memory */ - kfree(bcom_eng); - bcom_eng = NULL; - - return 0; -} - -static struct of_device_id mpc52xx_bcom_of_match[] = { - { .compatible = "fsl,mpc5200-bestcomm", }, - { .compatible = "mpc5200-bestcomm", }, - {}, -}; - -MODULE_DEVICE_TABLE(of, mpc52xx_bcom_of_match); - - -static struct platform_driver mpc52xx_bcom_of_platform_driver = { - .probe = mpc52xx_bcom_probe, - .remove = mpc52xx_bcom_remove, - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - .of_match_table = mpc52xx_bcom_of_match, - }, -}; - - -/* ======================================================================== */ -/* Module */ -/* ======================================================================== */ - -static int __init -mpc52xx_bcom_init(void) -{ - return platform_driver_register(&mpc52xx_bcom_of_platform_driver); -} - -static void __exit -mpc52xx_bcom_exit(void) -{ - platform_driver_unregister(&mpc52xx_bcom_of_platform_driver); -} - -/* If we're not a module, we must make sure everything is setup before */ -/* anyone tries to use us ... that's why we use subsys_initcall instead */ -/* of module_init. */ -subsys_initcall(mpc52xx_bcom_init); -module_exit(mpc52xx_bcom_exit); - -MODULE_DESCRIPTION("Freescale MPC52xx BestComm DMA"); -MODULE_AUTHOR("Sylvain Munaut "); -MODULE_AUTHOR("Andrey Volkov "); -MODULE_AUTHOR("Dale Farnsworth "); -MODULE_LICENSE("GPL v2"); - diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm.h b/arch/powerpc/sysdev/bestcomm/bestcomm.h deleted file mode 100644 index a0e2e6b19b57..000000000000 --- a/arch/powerpc/sysdev/bestcomm/bestcomm.h +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Public header for the MPC52xx processor BestComm driver - * - * - * Copyright (C) 2006 Sylvain Munaut - * Copyright (C) 2005 Varma Electronics Oy, - * ( by Andrey Volkov ) - * Copyright (C) 2003-2004 MontaVista, Software, Inc. - * ( by Dale Farnsworth ) - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -#ifndef __BESTCOMM_H__ -#define __BESTCOMM_H__ - -/** - * struct bcom_bd - Structure describing a generic BestComm buffer descriptor - * @status: The current status of this buffer. Exact meaning depends on the - * task type - * @data: An array of u32 extra data. Size of array is task dependent. - * - * Note: Don't dereference a bcom_bd pointer as an array. The size of the - * bcom_bd is variable. Use bcom_get_bd() instead. - */ -struct bcom_bd { - u32 status; - u32 data[0]; /* variable payload size */ -}; - -/* ======================================================================== */ -/* Generic task management */ -/* ======================================================================== */ - -/** - * struct bcom_task - Structure describing a loaded BestComm task - * - * This structure is never built by the driver it self. It's built and - * filled the intermediate layer of the BestComm API, the task dependent - * support code. - * - * Most likely you don't need to poke around inside this structure. The - * fields are exposed in the header just for the sake of inline functions - */ -struct bcom_task { - unsigned int tasknum; - unsigned int flags; - int irq; - - struct bcom_bd *bd; - phys_addr_t bd_pa; - void **cookie; - unsigned short index; - unsigned short outdex; - unsigned int num_bd; - unsigned int bd_size; - - void* priv; -}; - -#define BCOM_FLAGS_NONE 0x00000000ul -#define BCOM_FLAGS_ENABLE_TASK (1ul << 0) - -/** - * bcom_enable - Enable a BestComm task - * @tsk: The BestComm task structure - * - * This function makes sure the given task is enabled and can be run - * by the BestComm engine as needed - */ -extern void bcom_enable(struct bcom_task *tsk); - -/** - * bcom_disable - Disable a BestComm task - * @tsk: The BestComm task structure - * - * This function disable a given task, making sure it's not executed - * by the BestComm engine. - */ -extern void bcom_disable(struct bcom_task *tsk); - - -/** - * bcom_get_task_irq - Returns the irq number of a BestComm task - * @tsk: The BestComm task structure - */ -static inline int -bcom_get_task_irq(struct bcom_task *tsk) { - return tsk->irq; -} - -/* ======================================================================== */ -/* BD based tasks helpers */ -/* ======================================================================== */ - -#define BCOM_BD_READY 0x40000000ul - -/** _bcom_next_index - Get next input index. - * @tsk: pointer to task structure - * - * Support function; Device drivers should not call this - */ -static inline int -_bcom_next_index(struct bcom_task *tsk) -{ - return ((tsk->index + 1) == tsk->num_bd) ? 0 : tsk->index + 1; -} - -/** _bcom_next_outdex - Get next output index. - * @tsk: pointer to task structure - * - * Support function; Device drivers should not call this - */ -static inline int -_bcom_next_outdex(struct bcom_task *tsk) -{ - return ((tsk->outdex + 1) == tsk->num_bd) ? 0 : tsk->outdex + 1; -} - -/** - * bcom_queue_empty - Checks if a BestComm task BD queue is empty - * @tsk: The BestComm task structure - */ -static inline int -bcom_queue_empty(struct bcom_task *tsk) -{ - return tsk->index == tsk->outdex; -} - -/** - * bcom_queue_full - Checks if a BestComm task BD queue is full - * @tsk: The BestComm task structure - */ -static inline int -bcom_queue_full(struct bcom_task *tsk) -{ - return tsk->outdex == _bcom_next_index(tsk); -} - -/** - * bcom_get_bd - Get a BD from the queue - * @tsk: The BestComm task structure - * index: Index of the BD to fetch - */ -static inline struct bcom_bd -*bcom_get_bd(struct bcom_task *tsk, unsigned int index) -{ - /* A cast to (void*) so the address can be incremented by the - * real size instead of by sizeof(struct bcom_bd) */ - return ((void *)tsk->bd) + (index * tsk->bd_size); -} - -/** - * bcom_buffer_done - Checks if a BestComm - * @tsk: The BestComm task structure - */ -static inline int -bcom_buffer_done(struct bcom_task *tsk) -{ - struct bcom_bd *bd; - if (bcom_queue_empty(tsk)) - return 0; - - bd = bcom_get_bd(tsk, tsk->outdex); - return !(bd->status & BCOM_BD_READY); -} - -/** - * bcom_prepare_next_buffer - clear status of next available buffer. - * @tsk: The BestComm task structure - * - * Returns pointer to next buffer descriptor - */ -static inline struct bcom_bd * -bcom_prepare_next_buffer(struct bcom_task *tsk) -{ - struct bcom_bd *bd; - - bd = bcom_get_bd(tsk, tsk->index); - bd->status = 0; /* cleanup last status */ - return bd; -} - -static inline void -bcom_submit_next_buffer(struct bcom_task *tsk, void *cookie) -{ - struct bcom_bd *bd = bcom_get_bd(tsk, tsk->index); - - tsk->cookie[tsk->index] = cookie; - mb(); /* ensure the bd is really up-to-date */ - bd->status |= BCOM_BD_READY; - tsk->index = _bcom_next_index(tsk); - if (tsk->flags & BCOM_FLAGS_ENABLE_TASK) - bcom_enable(tsk); -} - -static inline void * -bcom_retrieve_buffer(struct bcom_task *tsk, u32 *p_status, struct bcom_bd **p_bd) -{ - void *cookie = tsk->cookie[tsk->outdex]; - struct bcom_bd *bd = bcom_get_bd(tsk, tsk->outdex); - - if (p_status) - *p_status = bd->status; - if (p_bd) - *p_bd = bd; - tsk->outdex = _bcom_next_outdex(tsk); - return cookie; -} - -#endif /* __BESTCOMM_H__ */ diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h b/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h deleted file mode 100644 index 3b52f3ffbdf8..000000000000 --- a/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Private header for the MPC52xx processor BestComm driver - * - * By private, we mean that driver should not use it directly. It's meant - * to be used by the BestComm engine driver itself and by the intermediate - * layer between the core and the drivers. - * - * Copyright (C) 2006 Sylvain Munaut - * Copyright (C) 2005 Varma Electronics Oy, - * ( by Andrey Volkov ) - * Copyright (C) 2003-2004 MontaVista, Software, Inc. - * ( by Dale Farnsworth ) - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -#ifndef __BESTCOMM_PRIV_H__ -#define __BESTCOMM_PRIV_H__ - -#include -#include -#include -#include - -#include "sram.h" - - -/* ======================================================================== */ -/* Engine related stuff */ -/* ======================================================================== */ - -/* Zones sizes and needed alignments */ -#define BCOM_MAX_TASKS 16 -#define BCOM_MAX_VAR 24 -#define BCOM_MAX_INC 8 -#define BCOM_MAX_FDT 64 -#define BCOM_MAX_CTX 20 -#define BCOM_CTX_SIZE (BCOM_MAX_CTX * sizeof(u32)) -#define BCOM_CTX_ALIGN 0x100 -#define BCOM_VAR_SIZE (BCOM_MAX_VAR * sizeof(u32)) -#define BCOM_INC_SIZE (BCOM_MAX_INC * sizeof(u32)) -#define BCOM_VAR_ALIGN 0x80 -#define BCOM_FDT_SIZE (BCOM_MAX_FDT * sizeof(u32)) -#define BCOM_FDT_ALIGN 0x100 - -/** - * struct bcom_tdt - Task Descriptor Table Entry - * - */ -struct bcom_tdt { - u32 start; - u32 stop; - u32 var; - u32 fdt; - u32 exec_status; /* used internally by BestComm engine */ - u32 mvtp; /* used internally by BestComm engine */ - u32 context; - u32 litbase; -}; - -/** - * struct bcom_engine - * - * This holds all info needed globaly to handle the engine - */ -struct bcom_engine { - struct device_node *ofnode; - struct mpc52xx_sdma __iomem *regs; - phys_addr_t regs_base; - - struct bcom_tdt *tdt; - u32 *ctx; - u32 *var; - u32 *fdt; - - spinlock_t lock; -}; - -extern struct bcom_engine *bcom_eng; - - -/* ======================================================================== */ -/* Tasks related stuff */ -/* ======================================================================== */ - -/* Tasks image header */ -#define BCOM_TASK_MAGIC 0x4243544B /* 'BCTK' */ - -struct bcom_task_header { - u32 magic; - u8 desc_size; /* the size fields */ - u8 var_size; /* are given in number */ - u8 inc_size; /* of 32-bits words */ - u8 first_var; - u8 reserved[8]; -}; - -/* Descriptors structure & co */ -#define BCOM_DESC_NOP 0x000001f8 -#define BCOM_LCD_MASK 0x80000000 -#define BCOM_DRD_EXTENDED 0x40000000 -#define BCOM_DRD_INITIATOR_SHIFT 21 - -/* Tasks pragma */ -#define BCOM_PRAGMA_BIT_RSV 7 /* reserved pragma bit */ -#define BCOM_PRAGMA_BIT_PRECISE_INC 6 /* increment 0=when possible, */ - /* 1=iter end */ -#define BCOM_PRAGMA_BIT_RST_ERROR_NO 5 /* don't reset errors on */ - /* task enable */ -#define BCOM_PRAGMA_BIT_PACK 4 /* pack data enable */ -#define BCOM_PRAGMA_BIT_INTEGER 3 /* data alignment */ - /* 0=frac(msb), 1=int(lsb) */ -#define BCOM_PRAGMA_BIT_SPECREAD 2 /* XLB speculative read */ -#define BCOM_PRAGMA_BIT_CW 1 /* write line buffer enable */ -#define BCOM_PRAGMA_BIT_RL 0 /* read line buffer enable */ - - /* Looks like XLB speculative read generates XLB errors when a buffer - * is at the end of the physical memory. i.e. when accessing the - * lasts words, the engine tries to prefetch the next but there is no - * next ... - */ -#define BCOM_STD_PRAGMA ((0 << BCOM_PRAGMA_BIT_RSV) | \ - (0 << BCOM_PRAGMA_BIT_PRECISE_INC) | \ - (0 << BCOM_PRAGMA_BIT_RST_ERROR_NO) | \ - (0 << BCOM_PRAGMA_BIT_PACK) | \ - (0 << BCOM_PRAGMA_BIT_INTEGER) | \ - (0 << BCOM_PRAGMA_BIT_SPECREAD) | \ - (1 << BCOM_PRAGMA_BIT_CW) | \ - (1 << BCOM_PRAGMA_BIT_RL)) - -#define BCOM_PCI_PRAGMA ((0 << BCOM_PRAGMA_BIT_RSV) | \ - (0 << BCOM_PRAGMA_BIT_PRECISE_INC) | \ - (0 << BCOM_PRAGMA_BIT_RST_ERROR_NO) | \ - (0 << BCOM_PRAGMA_BIT_PACK) | \ - (1 << BCOM_PRAGMA_BIT_INTEGER) | \ - (0 << BCOM_PRAGMA_BIT_SPECREAD) | \ - (1 << BCOM_PRAGMA_BIT_CW) | \ - (1 << BCOM_PRAGMA_BIT_RL)) - -#define BCOM_ATA_PRAGMA BCOM_STD_PRAGMA -#define BCOM_CRC16_DP_0_PRAGMA BCOM_STD_PRAGMA -#define BCOM_CRC16_DP_1_PRAGMA BCOM_STD_PRAGMA -#define BCOM_FEC_RX_BD_PRAGMA BCOM_STD_PRAGMA -#define BCOM_FEC_TX_BD_PRAGMA BCOM_STD_PRAGMA -#define BCOM_GEN_DP_0_PRAGMA BCOM_STD_PRAGMA -#define BCOM_GEN_DP_1_PRAGMA BCOM_STD_PRAGMA -#define BCOM_GEN_DP_2_PRAGMA BCOM_STD_PRAGMA -#define BCOM_GEN_DP_3_PRAGMA BCOM_STD_PRAGMA -#define BCOM_GEN_DP_BD_0_PRAGMA BCOM_STD_PRAGMA -#define BCOM_GEN_DP_BD_1_PRAGMA BCOM_STD_PRAGMA -#define BCOM_GEN_RX_BD_PRAGMA BCOM_STD_PRAGMA -#define BCOM_GEN_TX_BD_PRAGMA BCOM_STD_PRAGMA -#define BCOM_GEN_LPC_PRAGMA BCOM_STD_PRAGMA -#define BCOM_PCI_RX_PRAGMA BCOM_PCI_PRAGMA -#define BCOM_PCI_TX_PRAGMA BCOM_PCI_PRAGMA - -/* Initiators number */ -#define BCOM_INITIATOR_ALWAYS 0 -#define BCOM_INITIATOR_SCTMR_0 1 -#define BCOM_INITIATOR_SCTMR_1 2 -#define BCOM_INITIATOR_FEC_RX 3 -#define BCOM_INITIATOR_FEC_TX 4 -#define BCOM_INITIATOR_ATA_RX 5 -#define BCOM_INITIATOR_ATA_TX 6 -#define BCOM_INITIATOR_SCPCI_RX 7 -#define BCOM_INITIATOR_SCPCI_TX 8 -#define BCOM_INITIATOR_PSC3_RX 9 -#define BCOM_INITIATOR_PSC3_TX 10 -#define BCOM_INITIATOR_PSC2_RX 11 -#define BCOM_INITIATOR_PSC2_TX 12 -#define BCOM_INITIATOR_PSC1_RX 13 -#define BCOM_INITIATOR_PSC1_TX 14 -#define BCOM_INITIATOR_SCTMR_2 15 -#define BCOM_INITIATOR_SCLPC 16 -#define BCOM_INITIATOR_PSC5_RX 17 -#define BCOM_INITIATOR_PSC5_TX 18 -#define BCOM_INITIATOR_PSC4_RX 19 -#define BCOM_INITIATOR_PSC4_TX 20 -#define BCOM_INITIATOR_I2C2_RX 21 -#define BCOM_INITIATOR_I2C2_TX 22 -#define BCOM_INITIATOR_I2C1_RX 23 -#define BCOM_INITIATOR_I2C1_TX 24 -#define BCOM_INITIATOR_PSC6_RX 25 -#define BCOM_INITIATOR_PSC6_TX 26 -#define BCOM_INITIATOR_IRDA_RX 25 -#define BCOM_INITIATOR_IRDA_TX 26 -#define BCOM_INITIATOR_SCTMR_3 27 -#define BCOM_INITIATOR_SCTMR_4 28 -#define BCOM_INITIATOR_SCTMR_5 29 -#define BCOM_INITIATOR_SCTMR_6 30 -#define BCOM_INITIATOR_SCTMR_7 31 - -/* Initiators priorities */ -#define BCOM_IPR_ALWAYS 7 -#define BCOM_IPR_SCTMR_0 2 -#define BCOM_IPR_SCTMR_1 2 -#define BCOM_IPR_FEC_RX 6 -#define BCOM_IPR_FEC_TX 5 -#define BCOM_IPR_ATA_RX 7 -#define BCOM_IPR_ATA_TX 7 -#define BCOM_IPR_SCPCI_RX 2 -#define BCOM_IPR_SCPCI_TX 2 -#define BCOM_IPR_PSC3_RX 2 -#define BCOM_IPR_PSC3_TX 2 -#define BCOM_IPR_PSC2_RX 2 -#define BCOM_IPR_PSC2_TX 2 -#define BCOM_IPR_PSC1_RX 2 -#define BCOM_IPR_PSC1_TX 2 -#define BCOM_IPR_SCTMR_2 2 -#define BCOM_IPR_SCLPC 2 -#define BCOM_IPR_PSC5_RX 2 -#define BCOM_IPR_PSC5_TX 2 -#define BCOM_IPR_PSC4_RX 2 -#define BCOM_IPR_PSC4_TX 2 -#define BCOM_IPR_I2C2_RX 2 -#define BCOM_IPR_I2C2_TX 2 -#define BCOM_IPR_I2C1_RX 2 -#define BCOM_IPR_I2C1_TX 2 -#define BCOM_IPR_PSC6_RX 2 -#define BCOM_IPR_PSC6_TX 2 -#define BCOM_IPR_IRDA_RX 2 -#define BCOM_IPR_IRDA_TX 2 -#define BCOM_IPR_SCTMR_3 2 -#define BCOM_IPR_SCTMR_4 2 -#define BCOM_IPR_SCTMR_5 2 -#define BCOM_IPR_SCTMR_6 2 -#define BCOM_IPR_SCTMR_7 2 - - -/* ======================================================================== */ -/* API */ -/* ======================================================================== */ - -extern struct bcom_task *bcom_task_alloc(int bd_count, int bd_size, int priv_size); -extern void bcom_task_free(struct bcom_task *tsk); -extern int bcom_load_image(int task, u32 *task_image); -extern void bcom_set_initiator(int task, int initiator); - - -#define TASK_ENABLE 0x8000 - -/** - * bcom_disable_prefetch - Hook to disable bus prefetching - * - * ATA DMA and the original MPC5200 need this due to silicon bugs. At the - * moment disabling prefetch is a one-way street. There is no mechanism - * in place to turn prefetch back on after it has been disabled. There is - * no reason it couldn't be done, it would just be more complex to implement. - */ -static inline void bcom_disable_prefetch(void) -{ - u16 regval; - - regval = in_be16(&bcom_eng->regs->PtdCntrl); - out_be16(&bcom_eng->regs->PtdCntrl, regval | 1); -}; - -static inline void -bcom_enable_task(int task) -{ - u16 reg; - reg = in_be16(&bcom_eng->regs->tcr[task]); - out_be16(&bcom_eng->regs->tcr[task], reg | TASK_ENABLE); -} - -static inline void -bcom_disable_task(int task) -{ - u16 reg = in_be16(&bcom_eng->regs->tcr[task]); - out_be16(&bcom_eng->regs->tcr[task], reg & ~TASK_ENABLE); -} - - -static inline u32 * -bcom_task_desc(int task) -{ - return bcom_sram_pa2va(bcom_eng->tdt[task].start); -} - -static inline int -bcom_task_num_descs(int task) -{ - return (bcom_eng->tdt[task].stop - bcom_eng->tdt[task].start)/sizeof(u32) + 1; -} - -static inline u32 * -bcom_task_var(int task) -{ - return bcom_sram_pa2va(bcom_eng->tdt[task].var); -} - -static inline u32 * -bcom_task_inc(int task) -{ - return &bcom_task_var(task)[BCOM_MAX_VAR]; -} - - -static inline int -bcom_drd_is_extended(u32 desc) -{ - return (desc) & BCOM_DRD_EXTENDED; -} - -static inline int -bcom_desc_is_drd(u32 desc) -{ - return !(desc & BCOM_LCD_MASK) && desc != BCOM_DESC_NOP; -} - -static inline int -bcom_desc_initiator(u32 desc) -{ - return (desc >> BCOM_DRD_INITIATOR_SHIFT) & 0x1f; -} - -static inline void -bcom_set_desc_initiator(u32 *desc, int initiator) -{ - *desc = (*desc & ~(0x1f << BCOM_DRD_INITIATOR_SHIFT)) | - ((initiator & 0x1f) << BCOM_DRD_INITIATOR_SHIFT); -} - - -static inline void -bcom_set_task_pragma(int task, int pragma) -{ - u32 *fdt = &bcom_eng->tdt[task].fdt; - *fdt = (*fdt & ~0xff) | pragma; -} - -static inline void -bcom_set_task_auto_start(int task, int next_task) -{ - u16 __iomem *tcr = &bcom_eng->regs->tcr[task]; - out_be16(tcr, (in_be16(tcr) & ~0xff) | 0x00c0 | next_task); -} - -static inline void -bcom_set_tcr_initiator(int task, int initiator) -{ - u16 __iomem *tcr = &bcom_eng->regs->tcr[task]; - out_be16(tcr, (in_be16(tcr) & ~0x1f00) | ((initiator & 0x1f) << 8)); -} - - -#endif /* __BESTCOMM_PRIV_H__ */ - diff --git a/arch/powerpc/sysdev/bestcomm/fec.c b/arch/powerpc/sysdev/bestcomm/fec.c deleted file mode 100644 index 957a988d23ea..000000000000 --- a/arch/powerpc/sysdev/bestcomm/fec.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Bestcomm FEC tasks driver - * - * - * Copyright (C) 2006-2007 Sylvain Munaut - * Copyright (C) 2003-2004 MontaVista, Software, Inc. - * ( by Dale Farnsworth ) - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -#include -#include -#include -#include - -#include "bestcomm.h" -#include "bestcomm_priv.h" -#include "fec.h" - - -/* ======================================================================== */ -/* Task image/var/inc */ -/* ======================================================================== */ - -/* fec tasks images */ -extern u32 bcom_fec_rx_task[]; -extern u32 bcom_fec_tx_task[]; - -/* rx task vars that need to be set before enabling the task */ -struct bcom_fec_rx_var { - u32 enable; /* (u16*) address of task's control register */ - u32 fifo; /* (u32*) address of fec's fifo */ - u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */ - u32 bd_last; /* (struct bcom_bd*) end of ring buffer */ - u32 bd_start; /* (struct bcom_bd*) current bd */ - u32 buffer_size; /* size of receive buffer */ -}; - -/* rx task incs that need to be set before enabling the task */ -struct bcom_fec_rx_inc { - u16 pad0; - s16 incr_bytes; - u16 pad1; - s16 incr_dst; - u16 pad2; - s16 incr_dst_ma; -}; - -/* tx task vars that need to be set before enabling the task */ -struct bcom_fec_tx_var { - u32 DRD; /* (u32*) address of self-modified DRD */ - u32 fifo; /* (u32*) address of fec's fifo */ - u32 enable; /* (u16*) address of task's control register */ - u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */ - u32 bd_last; /* (struct bcom_bd*) end of ring buffer */ - u32 bd_start; /* (struct bcom_bd*) current bd */ - u32 buffer_size; /* set by uCode for each packet */ -}; - -/* tx task incs that need to be set before enabling the task */ -struct bcom_fec_tx_inc { - u16 pad0; - s16 incr_bytes; - u16 pad1; - s16 incr_src; - u16 pad2; - s16 incr_src_ma; -}; - -/* private structure in the task */ -struct bcom_fec_priv { - phys_addr_t fifo; - int maxbufsize; -}; - - -/* ======================================================================== */ -/* Task support code */ -/* ======================================================================== */ - -struct bcom_task * -bcom_fec_rx_init(int queue_len, phys_addr_t fifo, int maxbufsize) -{ - struct bcom_task *tsk; - struct bcom_fec_priv *priv; - - tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_fec_bd), - sizeof(struct bcom_fec_priv)); - if (!tsk) - return NULL; - - tsk->flags = BCOM_FLAGS_NONE; - - priv = tsk->priv; - priv->fifo = fifo; - priv->maxbufsize = maxbufsize; - - if (bcom_fec_rx_reset(tsk)) { - bcom_task_free(tsk); - return NULL; - } - - return tsk; -} -EXPORT_SYMBOL_GPL(bcom_fec_rx_init); - -int -bcom_fec_rx_reset(struct bcom_task *tsk) -{ - struct bcom_fec_priv *priv = tsk->priv; - struct bcom_fec_rx_var *var; - struct bcom_fec_rx_inc *inc; - - /* Shutdown the task */ - bcom_disable_task(tsk->tasknum); - - /* Reset the microcode */ - var = (struct bcom_fec_rx_var *) bcom_task_var(tsk->tasknum); - inc = (struct bcom_fec_rx_inc *) bcom_task_inc(tsk->tasknum); - - if (bcom_load_image(tsk->tasknum, bcom_fec_rx_task)) - return -1; - - var->enable = bcom_eng->regs_base + - offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]); - var->fifo = (u32) priv->fifo; - var->bd_base = tsk->bd_pa; - var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size); - var->bd_start = tsk->bd_pa; - var->buffer_size = priv->maxbufsize; - - inc->incr_bytes = -(s16)sizeof(u32); /* These should be in the */ - inc->incr_dst = sizeof(u32); /* task image, but we stick */ - inc->incr_dst_ma= sizeof(u8); /* to the official ones */ - - /* Reset the BDs */ - tsk->index = 0; - tsk->outdex = 0; - - memset(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size); - - /* Configure some stuff */ - bcom_set_task_pragma(tsk->tasknum, BCOM_FEC_RX_BD_PRAGMA); - bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum); - - out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_FEC_RX], BCOM_IPR_FEC_RX); - - out_be32(&bcom_eng->regs->IntPend, 1<tasknum); /* Clear ints */ - - return 0; -} -EXPORT_SYMBOL_GPL(bcom_fec_rx_reset); - -void -bcom_fec_rx_release(struct bcom_task *tsk) -{ - /* Nothing special for the FEC tasks */ - bcom_task_free(tsk); -} -EXPORT_SYMBOL_GPL(bcom_fec_rx_release); - - - - /* Return 2nd to last DRD */ - /* This is an ugly hack, but at least it's only done - once at initialization */ -static u32 *self_modified_drd(int tasknum) -{ - u32 *desc; - int num_descs; - int drd_count; - int i; - - num_descs = bcom_task_num_descs(tasknum); - desc = bcom_task_desc(tasknum) + num_descs - 1; - drd_count = 0; - for (i=0; iflags = BCOM_FLAGS_ENABLE_TASK; - - priv = tsk->priv; - priv->fifo = fifo; - - if (bcom_fec_tx_reset(tsk)) { - bcom_task_free(tsk); - return NULL; - } - - return tsk; -} -EXPORT_SYMBOL_GPL(bcom_fec_tx_init); - -int -bcom_fec_tx_reset(struct bcom_task *tsk) -{ - struct bcom_fec_priv *priv = tsk->priv; - struct bcom_fec_tx_var *var; - struct bcom_fec_tx_inc *inc; - - /* Shutdown the task */ - bcom_disable_task(tsk->tasknum); - - /* Reset the microcode */ - var = (struct bcom_fec_tx_var *) bcom_task_var(tsk->tasknum); - inc = (struct bcom_fec_tx_inc *) bcom_task_inc(tsk->tasknum); - - if (bcom_load_image(tsk->tasknum, bcom_fec_tx_task)) - return -1; - - var->enable = bcom_eng->regs_base + - offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]); - var->fifo = (u32) priv->fifo; - var->DRD = bcom_sram_va2pa(self_modified_drd(tsk->tasknum)); - var->bd_base = tsk->bd_pa; - var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size); - var->bd_start = tsk->bd_pa; - - inc->incr_bytes = -(s16)sizeof(u32); /* These should be in the */ - inc->incr_src = sizeof(u32); /* task image, but we stick */ - inc->incr_src_ma= sizeof(u8); /* to the official ones */ - - /* Reset the BDs */ - tsk->index = 0; - tsk->outdex = 0; - - memset(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size); - - /* Configure some stuff */ - bcom_set_task_pragma(tsk->tasknum, BCOM_FEC_TX_BD_PRAGMA); - bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum); - - out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_FEC_TX], BCOM_IPR_FEC_TX); - - out_be32(&bcom_eng->regs->IntPend, 1<tasknum); /* Clear ints */ - - return 0; -} -EXPORT_SYMBOL_GPL(bcom_fec_tx_reset); - -void -bcom_fec_tx_release(struct bcom_task *tsk) -{ - /* Nothing special for the FEC tasks */ - bcom_task_free(tsk); -} -EXPORT_SYMBOL_GPL(bcom_fec_tx_release); - - -MODULE_DESCRIPTION("BestComm FEC tasks driver"); -MODULE_AUTHOR("Dale Farnsworth "); -MODULE_LICENSE("GPL v2"); - diff --git a/arch/powerpc/sysdev/bestcomm/fec.h b/arch/powerpc/sysdev/bestcomm/fec.h deleted file mode 100644 index ee565d94d503..000000000000 --- a/arch/powerpc/sysdev/bestcomm/fec.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Header for Bestcomm FEC tasks driver - * - * - * Copyright (C) 2006-2007 Sylvain Munaut - * Copyright (C) 2003-2004 MontaVista, Software, Inc. - * ( by Dale Farnsworth ) - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -#ifndef __BESTCOMM_FEC_H__ -#define __BESTCOMM_FEC_H__ - - -struct bcom_fec_bd { - u32 status; - u32 skb_pa; -}; - -#define BCOM_FEC_TX_BD_TFD 0x08000000ul /* transmit frame done */ -#define BCOM_FEC_TX_BD_TC 0x04000000ul /* transmit CRC */ -#define BCOM_FEC_TX_BD_ABC 0x02000000ul /* append bad CRC */ - -#define BCOM_FEC_RX_BD_L 0x08000000ul /* buffer is last in frame */ -#define BCOM_FEC_RX_BD_BC 0x00800000ul /* DA is broadcast */ -#define BCOM_FEC_RX_BD_MC 0x00400000ul /* DA is multicast and not broadcast */ -#define BCOM_FEC_RX_BD_LG 0x00200000ul /* Rx frame length violation */ -#define BCOM_FEC_RX_BD_NO 0x00100000ul /* Rx non-octet aligned frame */ -#define BCOM_FEC_RX_BD_CR 0x00040000ul /* Rx CRC error */ -#define BCOM_FEC_RX_BD_OV 0x00020000ul /* overrun */ -#define BCOM_FEC_RX_BD_TR 0x00010000ul /* Rx frame truncated */ -#define BCOM_FEC_RX_BD_LEN_MASK 0x000007fful /* mask for length of received frame */ -#define BCOM_FEC_RX_BD_ERRORS (BCOM_FEC_RX_BD_LG | BCOM_FEC_RX_BD_NO | \ - BCOM_FEC_RX_BD_CR | BCOM_FEC_RX_BD_OV | BCOM_FEC_RX_BD_TR) - - -extern struct bcom_task * -bcom_fec_rx_init(int queue_len, phys_addr_t fifo, int maxbufsize); - -extern int -bcom_fec_rx_reset(struct bcom_task *tsk); - -extern void -bcom_fec_rx_release(struct bcom_task *tsk); - - -extern struct bcom_task * -bcom_fec_tx_init(int queue_len, phys_addr_t fifo); - -extern int -bcom_fec_tx_reset(struct bcom_task *tsk); - -extern void -bcom_fec_tx_release(struct bcom_task *tsk); - - -#endif /* __BESTCOMM_FEC_H__ */ - diff --git a/arch/powerpc/sysdev/bestcomm/gen_bd.c b/arch/powerpc/sysdev/bestcomm/gen_bd.c deleted file mode 100644 index e0a53e3147b2..000000000000 --- a/arch/powerpc/sysdev/bestcomm/gen_bd.c +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Driver for MPC52xx processor BestComm General Buffer Descriptor - * - * Copyright (C) 2007 Sylvain Munaut - * Copyright (C) 2006 AppSpec Computer Technologies Corp. - * Jeff Gibbons - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "bestcomm.h" -#include "bestcomm_priv.h" -#include "gen_bd.h" - - -/* ======================================================================== */ -/* Task image/var/inc */ -/* ======================================================================== */ - -/* gen_bd tasks images */ -extern u32 bcom_gen_bd_rx_task[]; -extern u32 bcom_gen_bd_tx_task[]; - -/* rx task vars that need to be set before enabling the task */ -struct bcom_gen_bd_rx_var { - u32 enable; /* (u16*) address of task's control register */ - u32 fifo; /* (u32*) address of gen_bd's fifo */ - u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */ - u32 bd_last; /* (struct bcom_bd*) end of ring buffer */ - u32 bd_start; /* (struct bcom_bd*) current bd */ - u32 buffer_size; /* size of receive buffer */ -}; - -/* rx task incs that need to be set before enabling the task */ -struct bcom_gen_bd_rx_inc { - u16 pad0; - s16 incr_bytes; - u16 pad1; - s16 incr_dst; -}; - -/* tx task vars that need to be set before enabling the task */ -struct bcom_gen_bd_tx_var { - u32 fifo; /* (u32*) address of gen_bd's fifo */ - u32 enable; /* (u16*) address of task's control register */ - u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */ - u32 bd_last; /* (struct bcom_bd*) end of ring buffer */ - u32 bd_start; /* (struct bcom_bd*) current bd */ - u32 buffer_size; /* set by uCode for each packet */ -}; - -/* tx task incs that need to be set before enabling the task */ -struct bcom_gen_bd_tx_inc { - u16 pad0; - s16 incr_bytes; - u16 pad1; - s16 incr_src; - u16 pad2; - s16 incr_src_ma; -}; - -/* private structure */ -struct bcom_gen_bd_priv { - phys_addr_t fifo; - int initiator; - int ipr; - int maxbufsize; -}; - - -/* ======================================================================== */ -/* Task support code */ -/* ======================================================================== */ - -struct bcom_task * -bcom_gen_bd_rx_init(int queue_len, phys_addr_t fifo, - int initiator, int ipr, int maxbufsize) -{ - struct bcom_task *tsk; - struct bcom_gen_bd_priv *priv; - - tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_gen_bd), - sizeof(struct bcom_gen_bd_priv)); - if (!tsk) - return NULL; - - tsk->flags = BCOM_FLAGS_NONE; - - priv = tsk->priv; - priv->fifo = fifo; - priv->initiator = initiator; - priv->ipr = ipr; - priv->maxbufsize = maxbufsize; - - if (bcom_gen_bd_rx_reset(tsk)) { - bcom_task_free(tsk); - return NULL; - } - - return tsk; -} -EXPORT_SYMBOL_GPL(bcom_gen_bd_rx_init); - -int -bcom_gen_bd_rx_reset(struct bcom_task *tsk) -{ - struct bcom_gen_bd_priv *priv = tsk->priv; - struct bcom_gen_bd_rx_var *var; - struct bcom_gen_bd_rx_inc *inc; - - /* Shutdown the task */ - bcom_disable_task(tsk->tasknum); - - /* Reset the microcode */ - var = (struct bcom_gen_bd_rx_var *) bcom_task_var(tsk->tasknum); - inc = (struct bcom_gen_bd_rx_inc *) bcom_task_inc(tsk->tasknum); - - if (bcom_load_image(tsk->tasknum, bcom_gen_bd_rx_task)) - return -1; - - var->enable = bcom_eng->regs_base + - offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]); - var->fifo = (u32) priv->fifo; - var->bd_base = tsk->bd_pa; - var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size); - var->bd_start = tsk->bd_pa; - var->buffer_size = priv->maxbufsize; - - inc->incr_bytes = -(s16)sizeof(u32); - inc->incr_dst = sizeof(u32); - - /* Reset the BDs */ - tsk->index = 0; - tsk->outdex = 0; - - memset(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size); - - /* Configure some stuff */ - bcom_set_task_pragma(tsk->tasknum, BCOM_GEN_RX_BD_PRAGMA); - bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum); - - out_8(&bcom_eng->regs->ipr[priv->initiator], priv->ipr); - bcom_set_initiator(tsk->tasknum, priv->initiator); - - out_be32(&bcom_eng->regs->IntPend, 1<tasknum); /* Clear ints */ - - return 0; -} -EXPORT_SYMBOL_GPL(bcom_gen_bd_rx_reset); - -void -bcom_gen_bd_rx_release(struct bcom_task *tsk) -{ - /* Nothing special for the GenBD tasks */ - bcom_task_free(tsk); -} -EXPORT_SYMBOL_GPL(bcom_gen_bd_rx_release); - - -extern struct bcom_task * -bcom_gen_bd_tx_init(int queue_len, phys_addr_t fifo, - int initiator, int ipr) -{ - struct bcom_task *tsk; - struct bcom_gen_bd_priv *priv; - - tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_gen_bd), - sizeof(struct bcom_gen_bd_priv)); - if (!tsk) - return NULL; - - tsk->flags = BCOM_FLAGS_NONE; - - priv = tsk->priv; - priv->fifo = fifo; - priv->initiator = initiator; - priv->ipr = ipr; - - if (bcom_gen_bd_tx_reset(tsk)) { - bcom_task_free(tsk); - return NULL; - } - - return tsk; -} -EXPORT_SYMBOL_GPL(bcom_gen_bd_tx_init); - -int -bcom_gen_bd_tx_reset(struct bcom_task *tsk) -{ - struct bcom_gen_bd_priv *priv = tsk->priv; - struct bcom_gen_bd_tx_var *var; - struct bcom_gen_bd_tx_inc *inc; - - /* Shutdown the task */ - bcom_disable_task(tsk->tasknum); - - /* Reset the microcode */ - var = (struct bcom_gen_bd_tx_var *) bcom_task_var(tsk->tasknum); - inc = (struct bcom_gen_bd_tx_inc *) bcom_task_inc(tsk->tasknum); - - if (bcom_load_image(tsk->tasknum, bcom_gen_bd_tx_task)) - return -1; - - var->enable = bcom_eng->regs_base + - offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]); - var->fifo = (u32) priv->fifo; - var->bd_base = tsk->bd_pa; - var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size); - var->bd_start = tsk->bd_pa; - - inc->incr_bytes = -(s16)sizeof(u32); - inc->incr_src = sizeof(u32); - inc->incr_src_ma = sizeof(u8); - - /* Reset the BDs */ - tsk->index = 0; - tsk->outdex = 0; - - memset(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size); - - /* Configure some stuff */ - bcom_set_task_pragma(tsk->tasknum, BCOM_GEN_TX_BD_PRAGMA); - bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum); - - out_8(&bcom_eng->regs->ipr[priv->initiator], priv->ipr); - bcom_set_initiator(tsk->tasknum, priv->initiator); - - out_be32(&bcom_eng->regs->IntPend, 1<tasknum); /* Clear ints */ - - return 0; -} -EXPORT_SYMBOL_GPL(bcom_gen_bd_tx_reset); - -void -bcom_gen_bd_tx_release(struct bcom_task *tsk) -{ - /* Nothing special for the GenBD tasks */ - bcom_task_free(tsk); -} -EXPORT_SYMBOL_GPL(bcom_gen_bd_tx_release); - -/* --------------------------------------------------------------------- - * PSC support code - */ - -/** - * bcom_psc_parameters - Bestcomm initialization value table for PSC devices - * - * This structure is only used internally. It is a lookup table for PSC - * specific parameters to bestcomm tasks. - */ -static struct bcom_psc_params { - int rx_initiator; - int rx_ipr; - int tx_initiator; - int tx_ipr; -} bcom_psc_params[] = { - [0] = { - .rx_initiator = BCOM_INITIATOR_PSC1_RX, - .rx_ipr = BCOM_IPR_PSC1_RX, - .tx_initiator = BCOM_INITIATOR_PSC1_TX, - .tx_ipr = BCOM_IPR_PSC1_TX, - }, - [1] = { - .rx_initiator = BCOM_INITIATOR_PSC2_RX, - .rx_ipr = BCOM_IPR_PSC2_RX, - .tx_initiator = BCOM_INITIATOR_PSC2_TX, - .tx_ipr = BCOM_IPR_PSC2_TX, - }, - [2] = { - .rx_initiator = BCOM_INITIATOR_PSC3_RX, - .rx_ipr = BCOM_IPR_PSC3_RX, - .tx_initiator = BCOM_INITIATOR_PSC3_TX, - .tx_ipr = BCOM_IPR_PSC3_TX, - }, - [3] = { - .rx_initiator = BCOM_INITIATOR_PSC4_RX, - .rx_ipr = BCOM_IPR_PSC4_RX, - .tx_initiator = BCOM_INITIATOR_PSC4_TX, - .tx_ipr = BCOM_IPR_PSC4_TX, - }, - [4] = { - .rx_initiator = BCOM_INITIATOR_PSC5_RX, - .rx_ipr = BCOM_IPR_PSC5_RX, - .tx_initiator = BCOM_INITIATOR_PSC5_TX, - .tx_ipr = BCOM_IPR_PSC5_TX, - }, - [5] = { - .rx_initiator = BCOM_INITIATOR_PSC6_RX, - .rx_ipr = BCOM_IPR_PSC6_RX, - .tx_initiator = BCOM_INITIATOR_PSC6_TX, - .tx_ipr = BCOM_IPR_PSC6_TX, - }, -}; - -/** - * bcom_psc_gen_bd_rx_init - Allocate a receive bcom_task for a PSC port - * @psc_num: Number of the PSC to allocate a task for - * @queue_len: number of buffer descriptors to allocate for the task - * @fifo: physical address of FIFO register - * @maxbufsize: Maximum receive data size in bytes. - * - * Allocate a bestcomm task structure for receiving data from a PSC. - */ -struct bcom_task * bcom_psc_gen_bd_rx_init(unsigned psc_num, int queue_len, - phys_addr_t fifo, int maxbufsize) -{ - if (psc_num >= MPC52xx_PSC_MAXNUM) - return NULL; - - return bcom_gen_bd_rx_init(queue_len, fifo, - bcom_psc_params[psc_num].rx_initiator, - bcom_psc_params[psc_num].rx_ipr, - maxbufsize); -} -EXPORT_SYMBOL_GPL(bcom_psc_gen_bd_rx_init); - -/** - * bcom_psc_gen_bd_tx_init - Allocate a transmit bcom_task for a PSC port - * @psc_num: Number of the PSC to allocate a task for - * @queue_len: number of buffer descriptors to allocate for the task - * @fifo: physical address of FIFO register - * - * Allocate a bestcomm task structure for transmitting data to a PSC. - */ -struct bcom_task * -bcom_psc_gen_bd_tx_init(unsigned psc_num, int queue_len, phys_addr_t fifo) -{ - struct psc; - return bcom_gen_bd_tx_init(queue_len, fifo, - bcom_psc_params[psc_num].tx_initiator, - bcom_psc_params[psc_num].tx_ipr); -} -EXPORT_SYMBOL_GPL(bcom_psc_gen_bd_tx_init); - - -MODULE_DESCRIPTION("BestComm General Buffer Descriptor tasks driver"); -MODULE_AUTHOR("Jeff Gibbons "); -MODULE_LICENSE("GPL v2"); - diff --git a/arch/powerpc/sysdev/bestcomm/gen_bd.h b/arch/powerpc/sysdev/bestcomm/gen_bd.h deleted file mode 100644 index de47260e69da..000000000000 --- a/arch/powerpc/sysdev/bestcomm/gen_bd.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Header for Bestcomm General Buffer Descriptor tasks driver - * - * - * Copyright (C) 2007 Sylvain Munaut - * Copyright (C) 2006 AppSpec Computer Technologies Corp. - * Jeff Gibbons - * - * 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 __BESTCOMM_GEN_BD_H__ -#define __BESTCOMM_GEN_BD_H__ - -struct bcom_gen_bd { - u32 status; - u32 buf_pa; -}; - - -extern struct bcom_task * -bcom_gen_bd_rx_init(int queue_len, phys_addr_t fifo, - int initiator, int ipr, int maxbufsize); - -extern int -bcom_gen_bd_rx_reset(struct bcom_task *tsk); - -extern void -bcom_gen_bd_rx_release(struct bcom_task *tsk); - - -extern struct bcom_task * -bcom_gen_bd_tx_init(int queue_len, phys_addr_t fifo, - int initiator, int ipr); - -extern int -bcom_gen_bd_tx_reset(struct bcom_task *tsk); - -extern void -bcom_gen_bd_tx_release(struct bcom_task *tsk); - - -/* PSC support utility wrappers */ -struct bcom_task * bcom_psc_gen_bd_rx_init(unsigned psc_num, int queue_len, - phys_addr_t fifo, int maxbufsize); -struct bcom_task * bcom_psc_gen_bd_tx_init(unsigned psc_num, int queue_len, - phys_addr_t fifo); -#endif /* __BESTCOMM_GEN_BD_H__ */ - diff --git a/arch/powerpc/sysdev/bestcomm/sram.c b/arch/powerpc/sysdev/bestcomm/sram.c deleted file mode 100644 index b6db23e085fb..000000000000 --- a/arch/powerpc/sysdev/bestcomm/sram.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Simple memory allocator for on-board SRAM - * - * - * Maintainer : Sylvain Munaut - * - * Copyright (C) 2005 Sylvain Munaut - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "sram.h" - - -/* Struct keeping our 'state' */ -struct bcom_sram *bcom_sram = NULL; -EXPORT_SYMBOL_GPL(bcom_sram); /* needed for inline functions */ - - -/* ======================================================================== */ -/* Public API */ -/* ======================================================================== */ -/* DO NOT USE in interrupts, if needed in irq handler, we should use the - _irqsave version of the spin_locks */ - -int bcom_sram_init(struct device_node *sram_node, char *owner) -{ - int rv; - const u32 *regaddr_p; - u64 regaddr64, size64; - unsigned int psize; - - /* Create our state struct */ - if (bcom_sram) { - printk(KERN_ERR "%s: bcom_sram_init: " - "Already initialized !\n", owner); - return -EBUSY; - } - - bcom_sram = kmalloc(sizeof(struct bcom_sram), GFP_KERNEL); - if (!bcom_sram) { - printk(KERN_ERR "%s: bcom_sram_init: " - "Couldn't allocate internal state !\n", owner); - return -ENOMEM; - } - - /* Get address and size of the sram */ - regaddr_p = of_get_address(sram_node, 0, &size64, NULL); - if (!regaddr_p) { - printk(KERN_ERR "%s: bcom_sram_init: " - "Invalid device node !\n", owner); - rv = -EINVAL; - goto error_free; - } - - regaddr64 = of_translate_address(sram_node, regaddr_p); - - bcom_sram->base_phys = (phys_addr_t) regaddr64; - bcom_sram->size = (unsigned int) size64; - - /* Request region */ - if (!request_mem_region(bcom_sram->base_phys, bcom_sram->size, owner)) { - printk(KERN_ERR "%s: bcom_sram_init: " - "Couldn't request region !\n", owner); - rv = -EBUSY; - goto error_free; - } - - /* Map SRAM */ - /* sram is not really __iomem */ - bcom_sram->base_virt = (void*) ioremap(bcom_sram->base_phys, bcom_sram->size); - - if (!bcom_sram->base_virt) { - printk(KERN_ERR "%s: bcom_sram_init: " - "Map error SRAM zone 0x%08lx (0x%0x)!\n", - owner, (long)bcom_sram->base_phys, bcom_sram->size ); - rv = -ENOMEM; - goto error_release; - } - - /* Create an rheap (defaults to 32 bits word alignment) */ - bcom_sram->rh = rh_create(4); - - /* Attach the free zones */ -#if 0 - /* Currently disabled ... for future use only */ - reg_addr_p = of_get_property(sram_node, "available", &psize); -#else - regaddr_p = NULL; - psize = 0; -#endif - - if (!regaddr_p || !psize) { - /* Attach the whole zone */ - rh_attach_region(bcom_sram->rh, 0, bcom_sram->size); - } else { - /* Attach each zone independently */ - while (psize >= 2 * sizeof(u32)) { - phys_addr_t zbase = of_translate_address(sram_node, regaddr_p); - rh_attach_region(bcom_sram->rh, zbase - bcom_sram->base_phys, regaddr_p[1]); - regaddr_p += 2; - psize -= 2 * sizeof(u32); - } - } - - /* Init our spinlock */ - spin_lock_init(&bcom_sram->lock); - - return 0; - -error_release: - release_mem_region(bcom_sram->base_phys, bcom_sram->size); -error_free: - kfree(bcom_sram); - bcom_sram = NULL; - - return rv; -} -EXPORT_SYMBOL_GPL(bcom_sram_init); - -void bcom_sram_cleanup(void) -{ - /* Free resources */ - if (bcom_sram) { - rh_destroy(bcom_sram->rh); - iounmap((void __iomem *)bcom_sram->base_virt); - release_mem_region(bcom_sram->base_phys, bcom_sram->size); - kfree(bcom_sram); - bcom_sram = NULL; - } -} -EXPORT_SYMBOL_GPL(bcom_sram_cleanup); - -void* bcom_sram_alloc(int size, int align, phys_addr_t *phys) -{ - unsigned long offset; - - spin_lock(&bcom_sram->lock); - offset = rh_alloc_align(bcom_sram->rh, size, align, NULL); - spin_unlock(&bcom_sram->lock); - - if (IS_ERR_VALUE(offset)) - return NULL; - - *phys = bcom_sram->base_phys + offset; - return bcom_sram->base_virt + offset; -} -EXPORT_SYMBOL_GPL(bcom_sram_alloc); - -void bcom_sram_free(void *ptr) -{ - unsigned long offset; - - if (!ptr) - return; - - offset = ptr - bcom_sram->base_virt; - - spin_lock(&bcom_sram->lock); - rh_free(bcom_sram->rh, offset); - spin_unlock(&bcom_sram->lock); -} -EXPORT_SYMBOL_GPL(bcom_sram_free); - diff --git a/arch/powerpc/sysdev/bestcomm/sram.h b/arch/powerpc/sysdev/bestcomm/sram.h deleted file mode 100644 index b6d668963cce..000000000000 --- a/arch/powerpc/sysdev/bestcomm/sram.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Handling of a sram zone for bestcomm - * - * - * Copyright (C) 2007 Sylvain Munaut - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -#ifndef __BESTCOMM_SRAM_H__ -#define __BESTCOMM_SRAM_H__ - -#include -#include -#include - - -/* Structure used internally */ - /* The internals are here for the inline functions - * sake, certainly not for the user to mess with ! - */ -struct bcom_sram { - phys_addr_t base_phys; - void *base_virt; - unsigned int size; - rh_info_t *rh; - spinlock_t lock; -}; - -extern struct bcom_sram *bcom_sram; - - -/* Public API */ -extern int bcom_sram_init(struct device_node *sram_node, char *owner); -extern void bcom_sram_cleanup(void); - -extern void* bcom_sram_alloc(int size, int align, phys_addr_t *phys); -extern void bcom_sram_free(void *ptr); - -static inline phys_addr_t bcom_sram_va2pa(void *va) { - return bcom_sram->base_phys + - (unsigned long)(va - bcom_sram->base_virt); -} - -static inline void *bcom_sram_pa2va(phys_addr_t pa) { - return bcom_sram->base_virt + - (unsigned long)(pa - bcom_sram->base_phys); -} - - -#endif /* __BESTCOMM_SRAM_H__ */ - diff --git a/drivers/Makefile b/drivers/Makefile index 7863b9fee50b..d8372ab2e4cf 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -29,7 +29,7 @@ obj-$(CONFIG_PNP) += pnp/ obj-y += amba/ # Many drivers will want to use DMA so this has to be made available # really early. -obj-$(CONFIG_DMA_ENGINE) += dma/ +obj-$(CONFIG_DMADEVICES) += dma/ obj-$(CONFIG_VIRTIO) += virtio/ obj-$(CONFIG_XEN) += xen/ diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c index ec67f54dc56f..0b363e9d42a5 100644 --- a/drivers/ata/pata_mpc52xx.c +++ b/drivers/ata/pata_mpc52xx.c @@ -26,9 +26,9 @@ #include #include -#include -#include -#include +#include +#include +#include #define DRV_NAME "mpc52xx_ata" diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index d4c12180c654..40179e749f08 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -125,6 +125,8 @@ config MPC512X_DMA ---help--- Enable support for the Freescale MPC512x built-in DMA engine. +source "drivers/dma/bestcomm/Kconfig" + config MV_XOR bool "Marvell XOR engine support" depends on PLAT_ORION diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 7428feaa8705..642d96736cf5 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_INTEL_IOATDMA) += ioat/ obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o obj-$(CONFIG_FSL_DMA) += fsldma.o obj-$(CONFIG_MPC512X_DMA) += mpc512x_dma.o +obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/ obj-$(CONFIG_MV_XOR) += mv_xor.o obj-$(CONFIG_DW_DMAC) += dw_dmac.o obj-$(CONFIG_AT_HDMAC) += at_hdmac.o diff --git a/drivers/dma/bestcomm/Kconfig b/drivers/dma/bestcomm/Kconfig new file mode 100644 index 000000000000..29e427085efb --- /dev/null +++ b/drivers/dma/bestcomm/Kconfig @@ -0,0 +1,36 @@ +# +# Kconfig options for Bestcomm +# + +config PPC_BESTCOMM + tristate "Bestcomm DMA engine support" + depends on PPC_MPC52xx + default n + select PPC_LIB_RHEAP + help + BestComm is the name of the communication coprocessor found + on the Freescale MPC5200 family of processor. Its usage is + optional for some drivers (like ATA), but required for + others (like FEC). + + If you want to use drivers that require DMA operations, + answer Y or M. Otherwise say N. + +config PPC_BESTCOMM_ATA + tristate + depends on PPC_BESTCOMM + help + This option enables the support for the ATA task. + +config PPC_BESTCOMM_FEC + tristate + depends on PPC_BESTCOMM + help + This option enables the support for the FEC tasks. + +config PPC_BESTCOMM_GEN_BD + tristate + depends on PPC_BESTCOMM + help + This option enables the support for the GenBD tasks. + diff --git a/drivers/dma/bestcomm/Makefile b/drivers/dma/bestcomm/Makefile new file mode 100644 index 000000000000..aed2df2a6580 --- /dev/null +++ b/drivers/dma/bestcomm/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for BestComm & co +# + +bestcomm-core-objs := bestcomm.o sram.o +bestcomm-ata-objs := ata.o bcom_ata_task.o +bestcomm-fec-objs := fec.o bcom_fec_rx_task.o bcom_fec_tx_task.o +bestcomm-gen-bd-objs := gen_bd.o bcom_gen_bd_rx_task.o bcom_gen_bd_tx_task.o + +obj-$(CONFIG_PPC_BESTCOMM) += bestcomm-core.o +obj-$(CONFIG_PPC_BESTCOMM_ATA) += bestcomm-ata.o +obj-$(CONFIG_PPC_BESTCOMM_FEC) += bestcomm-fec.o +obj-$(CONFIG_PPC_BESTCOMM_GEN_BD) += bestcomm-gen-bd.o + diff --git a/drivers/dma/bestcomm/ata.c b/drivers/dma/bestcomm/ata.c new file mode 100644 index 000000000000..2fd87f83cf90 --- /dev/null +++ b/drivers/dma/bestcomm/ata.c @@ -0,0 +1,157 @@ +/* + * Bestcomm ATA task driver + * + * + * Patterned after bestcomm/fec.c by Dale Farnsworth + * 2003-2004 (c) MontaVista, Software, Inc. + * + * Copyright (C) 2006-2007 Sylvain Munaut + * Copyright (C) 2006 Freescale - John Rigby + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include + +#include +#include +#include + + +/* ======================================================================== */ +/* Task image/var/inc */ +/* ======================================================================== */ + +/* ata task image */ +extern u32 bcom_ata_task[]; + +/* ata task vars that need to be set before enabling the task */ +struct bcom_ata_var { + u32 enable; /* (u16*) address of task's control register */ + u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */ + u32 bd_last; /* (struct bcom_bd*) end of ring buffer */ + u32 bd_start; /* (struct bcom_bd*) current bd */ + u32 buffer_size; /* size of receive buffer */ +}; + +/* ata task incs that need to be set before enabling the task */ +struct bcom_ata_inc { + u16 pad0; + s16 incr_bytes; + u16 pad1; + s16 incr_dst; + u16 pad2; + s16 incr_src; +}; + + +/* ======================================================================== */ +/* Task support code */ +/* ======================================================================== */ + +struct bcom_task * +bcom_ata_init(int queue_len, int maxbufsize) +{ + struct bcom_task *tsk; + struct bcom_ata_var *var; + struct bcom_ata_inc *inc; + + /* Prefetch breaks ATA DMA. Turn it off for ATA DMA */ + bcom_disable_prefetch(); + + tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_ata_bd), 0); + if (!tsk) + return NULL; + + tsk->flags = BCOM_FLAGS_NONE; + + bcom_ata_reset_bd(tsk); + + var = (struct bcom_ata_var *) bcom_task_var(tsk->tasknum); + inc = (struct bcom_ata_inc *) bcom_task_inc(tsk->tasknum); + + if (bcom_load_image(tsk->tasknum, bcom_ata_task)) { + bcom_task_free(tsk); + return NULL; + } + + var->enable = bcom_eng->regs_base + + offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]); + var->bd_base = tsk->bd_pa; + var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size); + var->bd_start = tsk->bd_pa; + var->buffer_size = maxbufsize; + + /* Configure some stuff */ + bcom_set_task_pragma(tsk->tasknum, BCOM_ATA_PRAGMA); + bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum); + + out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ATA_RX], BCOM_IPR_ATA_RX); + out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ATA_TX], BCOM_IPR_ATA_TX); + + out_be32(&bcom_eng->regs->IntPend, 1<tasknum); /* Clear ints */ + + return tsk; +} +EXPORT_SYMBOL_GPL(bcom_ata_init); + +void bcom_ata_rx_prepare(struct bcom_task *tsk) +{ + struct bcom_ata_inc *inc; + + inc = (struct bcom_ata_inc *) bcom_task_inc(tsk->tasknum); + + inc->incr_bytes = -(s16)sizeof(u32); + inc->incr_src = 0; + inc->incr_dst = sizeof(u32); + + bcom_set_initiator(tsk->tasknum, BCOM_INITIATOR_ATA_RX); +} +EXPORT_SYMBOL_GPL(bcom_ata_rx_prepare); + +void bcom_ata_tx_prepare(struct bcom_task *tsk) +{ + struct bcom_ata_inc *inc; + + inc = (struct bcom_ata_inc *) bcom_task_inc(tsk->tasknum); + + inc->incr_bytes = -(s16)sizeof(u32); + inc->incr_src = sizeof(u32); + inc->incr_dst = 0; + + bcom_set_initiator(tsk->tasknum, BCOM_INITIATOR_ATA_TX); +} +EXPORT_SYMBOL_GPL(bcom_ata_tx_prepare); + +void bcom_ata_reset_bd(struct bcom_task *tsk) +{ + struct bcom_ata_var *var; + + /* Reset all BD */ + memset(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size); + + tsk->index = 0; + tsk->outdex = 0; + + var = (struct bcom_ata_var *) bcom_task_var(tsk->tasknum); + var->bd_start = var->bd_base; +} +EXPORT_SYMBOL_GPL(bcom_ata_reset_bd); + +void bcom_ata_release(struct bcom_task *tsk) +{ + /* Nothing special for the ATA tasks */ + bcom_task_free(tsk); +} +EXPORT_SYMBOL_GPL(bcom_ata_release); + + +MODULE_DESCRIPTION("BestComm ATA task driver"); +MODULE_AUTHOR("John Rigby"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/dma/bestcomm/bcom_ata_task.c b/drivers/dma/bestcomm/bcom_ata_task.c new file mode 100644 index 000000000000..cc6049a4e469 --- /dev/null +++ b/drivers/dma/bestcomm/bcom_ata_task.c @@ -0,0 +1,67 @@ +/* + * Bestcomm ATA task microcode + * + * Copyright (c) 2004 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * Created based on bestcom/code_dma/image_rtos1/dma_image.hex + */ + +#include + +/* + * The header consists of the following fields: + * u32 magic; + * u8 desc_size; + * u8 var_size; + * u8 inc_size; + * u8 first_var; + * u8 reserved[8]; + * + * The size fields contain the number of 32-bit words. + */ + +u32 bcom_ata_task[] = { + /* header */ + 0x4243544b, + 0x0e060709, + 0x00000000, + 0x00000000, + + /* Task descriptors */ + 0x8198009b, /* LCD: idx0 = var3; idx0 <= var2; idx0 += inc3 */ + 0x13e00c08, /* DRD1A: var3 = var1; FN=0 MORE init=31 WS=0 RS=0 */ + 0xb8000264, /* LCD: idx1 = *idx0, idx2 = var0; idx1 < var9; idx1 += inc4, idx2 += inc4 */ + 0x10000f00, /* DRD1A: var3 = idx0; FN=0 MORE init=0 WS=0 RS=0 */ + 0x60140002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */ + 0x0c8cfc8a, /* DRD2B1: *idx2 = EU3(); EU3(*idx2,var10) */ + 0xd8988240, /* LCDEXT: idx1 = idx1; idx1 > var9; idx1 += inc0 */ + 0xf845e011, /* LCDEXT: idx2 = *(idx0 + var00000015); ; idx2 += inc2 */ + 0xb845e00a, /* LCD: idx3 = *(idx0 + var00000019); ; idx3 += inc1 */ + 0x0bfecf90, /* DRD1A: *idx3 = *idx2; FN=0 TFD init=31 WS=3 RS=3 */ + 0x9898802d, /* LCD: idx1 = idx1; idx1 once var0; idx1 += inc5 */ + 0x64000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 INT EXT init=0 WS=0 RS=0 */ + 0x0c0cf849, /* DRD2B1: *idx0 = EU3(); EU3(idx1,var9) */ + 0x000001f8, /* NOP */ + + /* VAR[9]-VAR[14] */ + 0x40000000, + 0x7fff7fff, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + + /* INC[0]-INC[6] */ + 0x40000000, + 0xe0000000, + 0xe0000000, + 0xa000000c, + 0x20000000, + 0x00000000, + 0x00000000, +}; + diff --git a/drivers/dma/bestcomm/bcom_fec_rx_task.c b/drivers/dma/bestcomm/bcom_fec_rx_task.c new file mode 100644 index 000000000000..a1ad6a02fcef --- /dev/null +++ b/drivers/dma/bestcomm/bcom_fec_rx_task.c @@ -0,0 +1,78 @@ +/* + * Bestcomm FEC RX task microcode + * + * Copyright (c) 2004 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * Automatically created based on BestCommAPI-2.2/code_dma/image_rtos1/dma_image.hex + * on Tue Mar 22 11:19:38 2005 GMT + */ + +#include + +/* + * The header consists of the following fields: + * u32 magic; + * u8 desc_size; + * u8 var_size; + * u8 inc_size; + * u8 first_var; + * u8 reserved[8]; + * + * The size fields contain the number of 32-bit words. + */ + +u32 bcom_fec_rx_task[] = { + /* header */ + 0x4243544b, + 0x18060709, + 0x00000000, + 0x00000000, + + /* Task descriptors */ + 0x808220e3, /* LCD: idx0 = var1, idx1 = var4; idx1 <= var3; idx0 += inc4, idx1 += inc3 */ + 0x10601010, /* DRD1A: var4 = var2; FN=0 MORE init=3 WS=0 RS=0 */ + 0xb8800264, /* LCD: idx2 = *idx1, idx3 = var0; idx2 < var9; idx2 += inc4, idx3 += inc4 */ + 0x10001308, /* DRD1A: var4 = idx1; FN=0 MORE init=0 WS=0 RS=0 */ + 0x60140002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */ + 0x0cccfcca, /* DRD2B1: *idx3 = EU3(); EU3(*idx3,var10) */ + 0x80004000, /* LCDEXT: idx2 = 0x00000000; ; */ + 0xb8c58029, /* LCD: idx3 = *(idx1 + var00000015); idx3 once var0; idx3 += inc5 */ + 0x60000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=0 RS=0 */ + 0x088cf8cc, /* DRD2B1: idx2 = EU3(); EU3(idx3,var12) */ + 0x991982f2, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var11; idx2 += inc6, idx3 += inc2 */ + 0x006acf80, /* DRD1A: *idx3 = *idx0; FN=0 init=3 WS=1 RS=1 */ + 0x80004000, /* LCDEXT: idx2 = 0x00000000; ; */ + 0x9999802d, /* LCD: idx3 = idx3; idx3 once var0; idx3 += inc5 */ + 0x70000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */ + 0x034cfc4e, /* DRD2B1: var13 = EU3(); EU3(*idx1,var14) */ + 0x00008868, /* DRD1A: idx2 = var13; FN=0 init=0 WS=0 RS=0 */ + 0x99198341, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var13; idx2 += inc0, idx3 += inc1 */ + 0x007ecf80, /* DRD1A: *idx3 = *idx0; FN=0 init=3 WS=3 RS=3 */ + 0x99198272, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var9; idx2 += inc6, idx3 += inc2 */ + 0x046acf80, /* DRD1A: *idx3 = *idx0; FN=0 INT init=3 WS=1 RS=1 */ + 0x9819002d, /* LCD: idx2 = idx0; idx2 once var0; idx2 += inc5 */ + 0x0060c790, /* DRD1A: *idx1 = *idx2; FN=0 init=3 WS=0 RS=0 */ + 0x000001f8, /* NOP */ + + /* VAR[9]-VAR[14] */ + 0x40000000, + 0x7fff7fff, + 0x00000000, + 0x00000003, + 0x40000008, + 0x43ffffff, + + /* INC[0]-INC[6] */ + 0x40000000, + 0xe0000000, + 0xe0000000, + 0xa0000008, + 0x20000000, + 0x00000000, + 0x4000ffff, +}; + diff --git a/drivers/dma/bestcomm/bcom_fec_tx_task.c b/drivers/dma/bestcomm/bcom_fec_tx_task.c new file mode 100644 index 000000000000..b1c495c3a65a --- /dev/null +++ b/drivers/dma/bestcomm/bcom_fec_tx_task.c @@ -0,0 +1,91 @@ +/* + * Bestcomm FEC TX task microcode + * + * Copyright (c) 2004 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * Automatically created based on BestCommAPI-2.2/code_dma/image_rtos1/dma_image.hex + * on Tue Mar 22 11:19:29 2005 GMT + */ + +#include + +/* + * The header consists of the following fields: + * u32 magic; + * u8 desc_size; + * u8 var_size; + * u8 inc_size; + * u8 first_var; + * u8 reserved[8]; + * + * The size fields contain the number of 32-bit words. + */ + +u32 bcom_fec_tx_task[] = { + /* header */ + 0x4243544b, + 0x2407070d, + 0x00000000, + 0x00000000, + + /* Task descriptors */ + 0x8018001b, /* LCD: idx0 = var0; idx0 <= var0; idx0 += inc3 */ + 0x60000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */ + 0x01ccfc0d, /* DRD2B1: var7 = EU3(); EU3(*idx0,var13) */ + 0x8082a123, /* LCD: idx0 = var1, idx1 = var5; idx1 <= var4; idx0 += inc4, idx1 += inc3 */ + 0x10801418, /* DRD1A: var5 = var3; FN=0 MORE init=4 WS=0 RS=0 */ + 0xf88103a4, /* LCDEXT: idx2 = *idx1, idx3 = var2; idx2 < var14; idx2 += inc4, idx3 += inc4 */ + 0x801a6024, /* LCD: idx4 = var0; ; idx4 += inc4 */ + 0x10001708, /* DRD1A: var5 = idx1; FN=0 MORE init=0 WS=0 RS=0 */ + 0x60140002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */ + 0x0cccfccf, /* DRD2B1: *idx3 = EU3(); EU3(*idx3,var15) */ + 0x991a002c, /* LCD: idx2 = idx2, idx3 = idx4; idx2 once var0; idx2 += inc5, idx3 += inc4 */ + 0x70000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */ + 0x024cfc4d, /* DRD2B1: var9 = EU3(); EU3(*idx1,var13) */ + 0x60000003, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=3 EXT init=0 WS=0 RS=0 */ + 0x0cccf247, /* DRD2B1: *idx3 = EU3(); EU3(var9,var7) */ + 0x80004000, /* LCDEXT: idx2 = 0x00000000; ; */ + 0xb8c80029, /* LCD: idx3 = *(idx1 + var0000001a); idx3 once var0; idx3 += inc5 */ + 0x70000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */ + 0x088cf8d1, /* DRD2B1: idx2 = EU3(); EU3(idx3,var17) */ + 0x00002f10, /* DRD1A: var11 = idx2; FN=0 init=0 WS=0 RS=0 */ + 0x99198432, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var16; idx2 += inc6, idx3 += inc2 */ + 0x008ac398, /* DRD1A: *idx0 = *idx3; FN=0 init=4 WS=1 RS=1 */ + 0x80004000, /* LCDEXT: idx2 = 0x00000000; ; */ + 0x9999802d, /* LCD: idx3 = idx3; idx3 once var0; idx3 += inc5 */ + 0x70000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */ + 0x048cfc53, /* DRD2B1: var18 = EU3(); EU3(*idx1,var19) */ + 0x60000008, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=8 EXT init=0 WS=0 RS=0 */ + 0x088cf48b, /* DRD2B1: idx2 = EU3(); EU3(var18,var11) */ + 0x99198481, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var18; idx2 += inc0, idx3 += inc1 */ + 0x009ec398, /* DRD1A: *idx0 = *idx3; FN=0 init=4 WS=3 RS=3 */ + 0x991983b2, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var14; idx2 += inc6, idx3 += inc2 */ + 0x088ac398, /* DRD1A: *idx0 = *idx3; FN=0 TFD init=4 WS=1 RS=1 */ + 0x9919002d, /* LCD: idx2 = idx2; idx2 once var0; idx2 += inc5 */ + 0x60000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */ + 0x0c4cf88e, /* DRD2B1: *idx1 = EU3(); EU3(idx2,var14) */ + 0x000001f8, /* NOP */ + + /* VAR[13]-VAR[19] */ + 0x0c000000, + 0x40000000, + 0x7fff7fff, + 0x00000000, + 0x00000003, + 0x40000004, + 0x43ffffff, + + /* INC[0]-INC[6] */ + 0x40000000, + 0xe0000000, + 0xe0000000, + 0xa0000008, + 0x20000000, + 0x00000000, + 0x4000ffff, +}; + diff --git a/drivers/dma/bestcomm/bcom_gen_bd_rx_task.c b/drivers/dma/bestcomm/bcom_gen_bd_rx_task.c new file mode 100644 index 000000000000..efee022b0256 --- /dev/null +++ b/drivers/dma/bestcomm/bcom_gen_bd_rx_task.c @@ -0,0 +1,63 @@ +/* + * Bestcomm GenBD RX task microcode + * + * Copyright (C) 2006 AppSpec Computer Technologies Corp. + * Jeff Gibbons + * Copyright (c) 2004 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * Based on BestCommAPI-2.2/code_dma/image_rtos1/dma_image.hex + * on Tue Mar 4 10:14:12 2006 GMT + * + */ + +#include + +/* + * The header consists of the following fields: + * u32 magic; + * u8 desc_size; + * u8 var_size; + * u8 inc_size; + * u8 first_var; + * u8 reserved[8]; + * + * The size fields contain the number of 32-bit words. + */ + +u32 bcom_gen_bd_rx_task[] = { + /* header */ + 0x4243544b, + 0x0d020409, + 0x00000000, + 0x00000000, + + /* Task descriptors */ + 0x808220da, /* LCD: idx0 = var1, idx1 = var4; idx1 <= var3; idx0 += inc3, idx1 += inc2 */ + 0x13e01010, /* DRD1A: var4 = var2; FN=0 MORE init=31 WS=0 RS=0 */ + 0xb880025b, /* LCD: idx2 = *idx1, idx3 = var0; idx2 < var9; idx2 += inc3, idx3 += inc3 */ + 0x10001308, /* DRD1A: var4 = idx1; FN=0 MORE init=0 WS=0 RS=0 */ + 0x60140002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */ + 0x0cccfcca, /* DRD2B1: *idx3 = EU3(); EU3(*idx3,var10) */ + 0xd9190240, /* LCDEXT: idx2 = idx2; idx2 > var9; idx2 += inc0 */ + 0xb8c5e009, /* LCD: idx3 = *(idx1 + var00000015); ; idx3 += inc1 */ + 0x07fecf80, /* DRD1A: *idx3 = *idx0; FN=0 INT init=31 WS=3 RS=3 */ + 0x99190024, /* LCD: idx2 = idx2; idx2 once var0; idx2 += inc4 */ + 0x60000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */ + 0x0c4cf889, /* DRD2B1: *idx1 = EU3(); EU3(idx2,var9) */ + 0x000001f8, /* NOP */ + + /* VAR[9]-VAR[10] */ + 0x40000000, + 0x7fff7fff, + + /* INC[0]-INC[3] */ + 0x40000000, + 0xe0000000, + 0xa0000008, + 0x20000000, +}; + diff --git a/drivers/dma/bestcomm/bcom_gen_bd_tx_task.c b/drivers/dma/bestcomm/bcom_gen_bd_tx_task.c new file mode 100644 index 000000000000..c605aa42ecbb --- /dev/null +++ b/drivers/dma/bestcomm/bcom_gen_bd_tx_task.c @@ -0,0 +1,69 @@ +/* + * Bestcomm GenBD TX task microcode + * + * Copyright (C) 2006 AppSpec Computer Technologies Corp. + * Jeff Gibbons + * Copyright (c) 2004 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * Based on BestCommAPI-2.2/code_dma/image_rtos1/dma_image.hex + * on Tue Mar 4 10:14:12 2006 GMT + * + */ + +#include + +/* + * The header consists of the following fields: + * u32 magic; + * u8 desc_size; + * u8 var_size; + * u8 inc_size; + * u8 first_var; + * u8 reserved[8]; + * + * The size fields contain the number of 32-bit words. + */ + +u32 bcom_gen_bd_tx_task[] = { + /* header */ + 0x4243544b, + 0x0f040609, + 0x00000000, + 0x00000000, + + /* Task descriptors */ + 0x800220e3, /* LCD: idx0 = var0, idx1 = var4; idx1 <= var3; idx0 += inc4, idx1 += inc3 */ + 0x13e01010, /* DRD1A: var4 = var2; FN=0 MORE init=31 WS=0 RS=0 */ + 0xb8808264, /* LCD: idx2 = *idx1, idx3 = var1; idx2 < var9; idx2 += inc4, idx3 += inc4 */ + 0x10001308, /* DRD1A: var4 = idx1; FN=0 MORE init=0 WS=0 RS=0 */ + 0x60140002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */ + 0x0cccfcca, /* DRD2B1: *idx3 = EU3(); EU3(*idx3,var10) */ + 0xd9190300, /* LCDEXT: idx2 = idx2; idx2 > var12; idx2 += inc0 */ + 0xb8c5e009, /* LCD: idx3 = *(idx1 + var00000015); ; idx3 += inc1 */ + 0x03fec398, /* DRD1A: *idx0 = *idx3; FN=0 init=31 WS=3 RS=3 */ + 0x9919826a, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var9; idx2 += inc5, idx3 += inc2 */ + 0x0feac398, /* DRD1A: *idx0 = *idx3; FN=0 TFD INT init=31 WS=1 RS=1 */ + 0x99190036, /* LCD: idx2 = idx2; idx2 once var0; idx2 += inc6 */ + 0x60000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */ + 0x0c4cf889, /* DRD2B1: *idx1 = EU3(); EU3(idx2,var9) */ + 0x000001f8, /* NOP */ + + /* VAR[9]-VAR[12] */ + 0x40000000, + 0x7fff7fff, + 0x00000000, + 0x40000004, + + /* INC[0]-INC[5] */ + 0x40000000, + 0xe0000000, + 0xe0000000, + 0xa0000008, + 0x20000000, + 0x4000ffff, +}; + diff --git a/drivers/dma/bestcomm/bestcomm.c b/drivers/dma/bestcomm/bestcomm.c new file mode 100644 index 000000000000..67371fb820d3 --- /dev/null +++ b/drivers/dma/bestcomm/bestcomm.c @@ -0,0 +1,532 @@ +/* + * Driver for MPC52xx processor BestComm peripheral controller + * + * + * Copyright (C) 2006-2007 Sylvain Munaut + * Copyright (C) 2005 Varma Electronics Oy, + * ( by Andrey Volkov ) + * Copyright (C) 2003-2004 MontaVista, Software, Inc. + * ( by Dale Farnsworth ) + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "linux/fsl/bestcomm/bestcomm.h" + +#define DRIVER_NAME "bestcomm-core" + +/* MPC5200 device tree match tables */ +static struct of_device_id mpc52xx_sram_ids[] __devinitdata = { + { .compatible = "fsl,mpc5200-sram", }, + { .compatible = "mpc5200-sram", }, + {} +}; + + +struct bcom_engine *bcom_eng = NULL; +EXPORT_SYMBOL_GPL(bcom_eng); /* needed for inline functions */ + +/* ======================================================================== */ +/* Public and private API */ +/* ======================================================================== */ + +/* Private API */ + +struct bcom_task * +bcom_task_alloc(int bd_count, int bd_size, int priv_size) +{ + int i, tasknum = -1; + struct bcom_task *tsk; + + /* Don't try to do anything if bestcomm init failed */ + if (!bcom_eng) + return NULL; + + /* Get and reserve a task num */ + spin_lock(&bcom_eng->lock); + + for (i=0; itdt[i].stop) { /* we use stop as a marker */ + bcom_eng->tdt[i].stop = 0xfffffffful; /* dummy addr */ + tasknum = i; + break; + } + + spin_unlock(&bcom_eng->lock); + + if (tasknum < 0) + return NULL; + + /* Allocate our structure */ + tsk = kzalloc(sizeof(struct bcom_task) + priv_size, GFP_KERNEL); + if (!tsk) + goto error; + + tsk->tasknum = tasknum; + if (priv_size) + tsk->priv = (void*)tsk + sizeof(struct bcom_task); + + /* Get IRQ of that task */ + tsk->irq = irq_of_parse_and_map(bcom_eng->ofnode, tsk->tasknum); + if (tsk->irq == NO_IRQ) + goto error; + + /* Init the BDs, if needed */ + if (bd_count) { + tsk->cookie = kmalloc(sizeof(void*) * bd_count, GFP_KERNEL); + if (!tsk->cookie) + goto error; + + tsk->bd = bcom_sram_alloc(bd_count * bd_size, 4, &tsk->bd_pa); + if (!tsk->bd) + goto error; + memset(tsk->bd, 0x00, bd_count * bd_size); + + tsk->num_bd = bd_count; + tsk->bd_size = bd_size; + } + + return tsk; + +error: + if (tsk) { + if (tsk->irq != NO_IRQ) + irq_dispose_mapping(tsk->irq); + bcom_sram_free(tsk->bd); + kfree(tsk->cookie); + kfree(tsk); + } + + bcom_eng->tdt[tasknum].stop = 0; + + return NULL; +} +EXPORT_SYMBOL_GPL(bcom_task_alloc); + +void +bcom_task_free(struct bcom_task *tsk) +{ + /* Stop the task */ + bcom_disable_task(tsk->tasknum); + + /* Clear TDT */ + bcom_eng->tdt[tsk->tasknum].start = 0; + bcom_eng->tdt[tsk->tasknum].stop = 0; + + /* Free everything */ + irq_dispose_mapping(tsk->irq); + bcom_sram_free(tsk->bd); + kfree(tsk->cookie); + kfree(tsk); +} +EXPORT_SYMBOL_GPL(bcom_task_free); + +int +bcom_load_image(int task, u32 *task_image) +{ + struct bcom_task_header *hdr = (struct bcom_task_header *)task_image; + struct bcom_tdt *tdt; + u32 *desc, *var, *inc; + u32 *desc_src, *var_src, *inc_src; + + /* Safety checks */ + if (hdr->magic != BCOM_TASK_MAGIC) { + printk(KERN_ERR DRIVER_NAME + ": Trying to load invalid microcode\n"); + return -EINVAL; + } + + if ((task < 0) || (task >= BCOM_MAX_TASKS)) { + printk(KERN_ERR DRIVER_NAME + ": Trying to load invalid task %d\n", task); + return -EINVAL; + } + + /* Initial load or reload */ + tdt = &bcom_eng->tdt[task]; + + if (tdt->start) { + desc = bcom_task_desc(task); + if (hdr->desc_size != bcom_task_num_descs(task)) { + printk(KERN_ERR DRIVER_NAME + ": Trying to reload wrong task image " + "(%d size %d/%d)!\n", + task, + hdr->desc_size, + bcom_task_num_descs(task)); + return -EINVAL; + } + } else { + phys_addr_t start_pa; + + desc = bcom_sram_alloc(hdr->desc_size * sizeof(u32), 4, &start_pa); + if (!desc) + return -ENOMEM; + + tdt->start = start_pa; + tdt->stop = start_pa + ((hdr->desc_size-1) * sizeof(u32)); + } + + var = bcom_task_var(task); + inc = bcom_task_inc(task); + + /* Clear & copy */ + memset(var, 0x00, BCOM_VAR_SIZE); + memset(inc, 0x00, BCOM_INC_SIZE); + + desc_src = (u32 *)(hdr + 1); + var_src = desc_src + hdr->desc_size; + inc_src = var_src + hdr->var_size; + + memcpy(desc, desc_src, hdr->desc_size * sizeof(u32)); + memcpy(var + hdr->first_var, var_src, hdr->var_size * sizeof(u32)); + memcpy(inc, inc_src, hdr->inc_size * sizeof(u32)); + + return 0; +} +EXPORT_SYMBOL_GPL(bcom_load_image); + +void +bcom_set_initiator(int task, int initiator) +{ + int i; + int num_descs; + u32 *desc; + int next_drd_has_initiator; + + bcom_set_tcr_initiator(task, initiator); + + /* Just setting tcr is apparently not enough due to some problem */ + /* with it. So we just go thru all the microcode and replace in */ + /* the DRD directly */ + + desc = bcom_task_desc(task); + next_drd_has_initiator = 1; + num_descs = bcom_task_num_descs(task); + + for (i=0; itasknum); +} +EXPORT_SYMBOL_GPL(bcom_enable); + +void +bcom_disable(struct bcom_task *tsk) +{ + bcom_disable_task(tsk->tasknum); +} +EXPORT_SYMBOL_GPL(bcom_disable); + + +/* ======================================================================== */ +/* Engine init/cleanup */ +/* ======================================================================== */ + +/* Function Descriptor table */ +/* this will need to be updated if Freescale changes their task code FDT */ +static u32 fdt_ops[] = { + 0xa0045670, /* FDT[48] - load_acc() */ + 0x80045670, /* FDT[49] - unload_acc() */ + 0x21800000, /* FDT[50] - and() */ + 0x21e00000, /* FDT[51] - or() */ + 0x21500000, /* FDT[52] - xor() */ + 0x21400000, /* FDT[53] - andn() */ + 0x21500000, /* FDT[54] - not() */ + 0x20400000, /* FDT[55] - add() */ + 0x20500000, /* FDT[56] - sub() */ + 0x20800000, /* FDT[57] - lsh() */ + 0x20a00000, /* FDT[58] - rsh() */ + 0xc0170000, /* FDT[59] - crc8() */ + 0xc0145670, /* FDT[60] - crc16() */ + 0xc0345670, /* FDT[61] - crc32() */ + 0xa0076540, /* FDT[62] - endian32() */ + 0xa0000760, /* FDT[63] - endian16() */ +}; + + +static int __devinit +bcom_engine_init(void) +{ + int task; + phys_addr_t tdt_pa, ctx_pa, var_pa, fdt_pa; + unsigned int tdt_size, ctx_size, var_size, fdt_size; + + /* Allocate & clear SRAM zones for FDT, TDTs, contexts and vars/incs */ + tdt_size = BCOM_MAX_TASKS * sizeof(struct bcom_tdt); + ctx_size = BCOM_MAX_TASKS * BCOM_CTX_SIZE; + var_size = BCOM_MAX_TASKS * (BCOM_VAR_SIZE + BCOM_INC_SIZE); + fdt_size = BCOM_FDT_SIZE; + + bcom_eng->tdt = bcom_sram_alloc(tdt_size, sizeof(u32), &tdt_pa); + bcom_eng->ctx = bcom_sram_alloc(ctx_size, BCOM_CTX_ALIGN, &ctx_pa); + bcom_eng->var = bcom_sram_alloc(var_size, BCOM_VAR_ALIGN, &var_pa); + bcom_eng->fdt = bcom_sram_alloc(fdt_size, BCOM_FDT_ALIGN, &fdt_pa); + + if (!bcom_eng->tdt || !bcom_eng->ctx || !bcom_eng->var || !bcom_eng->fdt) { + printk(KERN_ERR "DMA: SRAM alloc failed in engine init !\n"); + + bcom_sram_free(bcom_eng->tdt); + bcom_sram_free(bcom_eng->ctx); + bcom_sram_free(bcom_eng->var); + bcom_sram_free(bcom_eng->fdt); + + return -ENOMEM; + } + + memset(bcom_eng->tdt, 0x00, tdt_size); + memset(bcom_eng->ctx, 0x00, ctx_size); + memset(bcom_eng->var, 0x00, var_size); + memset(bcom_eng->fdt, 0x00, fdt_size); + + /* Copy the FDT for the EU#3 */ + memcpy(&bcom_eng->fdt[48], fdt_ops, sizeof(fdt_ops)); + + /* Initialize Task base structure */ + for (task=0; taskregs->tcr[task], 0); + out_8(&bcom_eng->regs->ipr[task], 0); + + bcom_eng->tdt[task].context = ctx_pa; + bcom_eng->tdt[task].var = var_pa; + bcom_eng->tdt[task].fdt = fdt_pa; + + var_pa += BCOM_VAR_SIZE + BCOM_INC_SIZE; + ctx_pa += BCOM_CTX_SIZE; + } + + out_be32(&bcom_eng->regs->taskBar, tdt_pa); + + /* Init 'always' initiator */ + out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ALWAYS], BCOM_IPR_ALWAYS); + + /* Disable COMM Bus Prefetch on the original 5200; it's broken */ + if ((mfspr(SPRN_SVR) & MPC5200_SVR_MASK) == MPC5200_SVR) + bcom_disable_prefetch(); + + /* Init lock */ + spin_lock_init(&bcom_eng->lock); + + return 0; +} + +static void +bcom_engine_cleanup(void) +{ + int task; + + /* Stop all tasks */ + for (task=0; taskregs->tcr[task], 0); + out_8(&bcom_eng->regs->ipr[task], 0); + } + + out_be32(&bcom_eng->regs->taskBar, 0ul); + + /* Release the SRAM zones */ + bcom_sram_free(bcom_eng->tdt); + bcom_sram_free(bcom_eng->ctx); + bcom_sram_free(bcom_eng->var); + bcom_sram_free(bcom_eng->fdt); +} + + +/* ======================================================================== */ +/* OF platform driver */ +/* ======================================================================== */ + +static int __devinit mpc52xx_bcom_probe(struct platform_device *op) +{ + struct device_node *ofn_sram; + struct resource res_bcom; + + int rv; + + /* Inform user we're ok so far */ + printk(KERN_INFO "DMA: MPC52xx BestComm driver\n"); + + /* Get the bestcomm node */ + of_node_get(op->dev.of_node); + + /* Prepare SRAM */ + ofn_sram = of_find_matching_node(NULL, mpc52xx_sram_ids); + if (!ofn_sram) { + printk(KERN_ERR DRIVER_NAME ": " + "No SRAM found in device tree\n"); + rv = -ENODEV; + goto error_ofput; + } + rv = bcom_sram_init(ofn_sram, DRIVER_NAME); + of_node_put(ofn_sram); + + if (rv) { + printk(KERN_ERR DRIVER_NAME ": " + "Error in SRAM init\n"); + goto error_ofput; + } + + /* Get a clean struct */ + bcom_eng = kzalloc(sizeof(struct bcom_engine), GFP_KERNEL); + if (!bcom_eng) { + printk(KERN_ERR DRIVER_NAME ": " + "Can't allocate state structure\n"); + rv = -ENOMEM; + goto error_sramclean; + } + + /* Save the node */ + bcom_eng->ofnode = op->dev.of_node; + + /* Get, reserve & map io */ + if (of_address_to_resource(op->dev.of_node, 0, &res_bcom)) { + printk(KERN_ERR DRIVER_NAME ": " + "Can't get resource\n"); + rv = -EINVAL; + goto error_sramclean; + } + + if (!request_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma), + DRIVER_NAME)) { + printk(KERN_ERR DRIVER_NAME ": " + "Can't request registers region\n"); + rv = -EBUSY; + goto error_sramclean; + } + + bcom_eng->regs_base = res_bcom.start; + bcom_eng->regs = ioremap(res_bcom.start, sizeof(struct mpc52xx_sdma)); + if (!bcom_eng->regs) { + printk(KERN_ERR DRIVER_NAME ": " + "Can't map registers\n"); + rv = -ENOMEM; + goto error_release; + } + + /* Now, do the real init */ + rv = bcom_engine_init(); + if (rv) + goto error_unmap; + + /* Done ! */ + printk(KERN_INFO "DMA: MPC52xx BestComm engine @%08lx ok !\n", + (long)bcom_eng->regs_base); + + return 0; + + /* Error path */ +error_unmap: + iounmap(bcom_eng->regs); +error_release: + release_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma)); +error_sramclean: + kfree(bcom_eng); + bcom_sram_cleanup(); +error_ofput: + of_node_put(op->dev.of_node); + + printk(KERN_ERR "DMA: MPC52xx BestComm init failed !\n"); + + return rv; +} + + +static int mpc52xx_bcom_remove(struct platform_device *op) +{ + /* Clean up the engine */ + bcom_engine_cleanup(); + + /* Cleanup SRAM */ + bcom_sram_cleanup(); + + /* Release regs */ + iounmap(bcom_eng->regs); + release_mem_region(bcom_eng->regs_base, sizeof(struct mpc52xx_sdma)); + + /* Release the node */ + of_node_put(bcom_eng->ofnode); + + /* Release memory */ + kfree(bcom_eng); + bcom_eng = NULL; + + return 0; +} + +static struct of_device_id mpc52xx_bcom_of_match[] = { + { .compatible = "fsl,mpc5200-bestcomm", }, + { .compatible = "mpc5200-bestcomm", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, mpc52xx_bcom_of_match); + + +static struct platform_driver mpc52xx_bcom_of_platform_driver = { + .probe = mpc52xx_bcom_probe, + .remove = mpc52xx_bcom_remove, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = mpc52xx_bcom_of_match, + }, +}; + + +/* ======================================================================== */ +/* Module */ +/* ======================================================================== */ + +static int __init +mpc52xx_bcom_init(void) +{ + return platform_driver_register(&mpc52xx_bcom_of_platform_driver); +} + +static void __exit +mpc52xx_bcom_exit(void) +{ + platform_driver_unregister(&mpc52xx_bcom_of_platform_driver); +} + +/* If we're not a module, we must make sure everything is setup before */ +/* anyone tries to use us ... that's why we use subsys_initcall instead */ +/* of module_init. */ +subsys_initcall(mpc52xx_bcom_init); +module_exit(mpc52xx_bcom_exit); + +MODULE_DESCRIPTION("Freescale MPC52xx BestComm DMA"); +MODULE_AUTHOR("Sylvain Munaut "); +MODULE_AUTHOR("Andrey Volkov "); +MODULE_AUTHOR("Dale Farnsworth "); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/dma/bestcomm/fec.c b/drivers/dma/bestcomm/fec.c new file mode 100644 index 000000000000..7f1fb1c999e4 --- /dev/null +++ b/drivers/dma/bestcomm/fec.c @@ -0,0 +1,270 @@ +/* + * Bestcomm FEC tasks driver + * + * + * Copyright (C) 2006-2007 Sylvain Munaut + * Copyright (C) 2003-2004 MontaVista, Software, Inc. + * ( by Dale Farnsworth ) + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include + +#include +#include +#include + + +/* ======================================================================== */ +/* Task image/var/inc */ +/* ======================================================================== */ + +/* fec tasks images */ +extern u32 bcom_fec_rx_task[]; +extern u32 bcom_fec_tx_task[]; + +/* rx task vars that need to be set before enabling the task */ +struct bcom_fec_rx_var { + u32 enable; /* (u16*) address of task's control register */ + u32 fifo; /* (u32*) address of fec's fifo */ + u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */ + u32 bd_last; /* (struct bcom_bd*) end of ring buffer */ + u32 bd_start; /* (struct bcom_bd*) current bd */ + u32 buffer_size; /* size of receive buffer */ +}; + +/* rx task incs that need to be set before enabling the task */ +struct bcom_fec_rx_inc { + u16 pad0; + s16 incr_bytes; + u16 pad1; + s16 incr_dst; + u16 pad2; + s16 incr_dst_ma; +}; + +/* tx task vars that need to be set before enabling the task */ +struct bcom_fec_tx_var { + u32 DRD; /* (u32*) address of self-modified DRD */ + u32 fifo; /* (u32*) address of fec's fifo */ + u32 enable; /* (u16*) address of task's control register */ + u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */ + u32 bd_last; /* (struct bcom_bd*) end of ring buffer */ + u32 bd_start; /* (struct bcom_bd*) current bd */ + u32 buffer_size; /* set by uCode for each packet */ +}; + +/* tx task incs that need to be set before enabling the task */ +struct bcom_fec_tx_inc { + u16 pad0; + s16 incr_bytes; + u16 pad1; + s16 incr_src; + u16 pad2; + s16 incr_src_ma; +}; + +/* private structure in the task */ +struct bcom_fec_priv { + phys_addr_t fifo; + int maxbufsize; +}; + + +/* ======================================================================== */ +/* Task support code */ +/* ======================================================================== */ + +struct bcom_task * +bcom_fec_rx_init(int queue_len, phys_addr_t fifo, int maxbufsize) +{ + struct bcom_task *tsk; + struct bcom_fec_priv *priv; + + tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_fec_bd), + sizeof(struct bcom_fec_priv)); + if (!tsk) + return NULL; + + tsk->flags = BCOM_FLAGS_NONE; + + priv = tsk->priv; + priv->fifo = fifo; + priv->maxbufsize = maxbufsize; + + if (bcom_fec_rx_reset(tsk)) { + bcom_task_free(tsk); + return NULL; + } + + return tsk; +} +EXPORT_SYMBOL_GPL(bcom_fec_rx_init); + +int +bcom_fec_rx_reset(struct bcom_task *tsk) +{ + struct bcom_fec_priv *priv = tsk->priv; + struct bcom_fec_rx_var *var; + struct bcom_fec_rx_inc *inc; + + /* Shutdown the task */ + bcom_disable_task(tsk->tasknum); + + /* Reset the microcode */ + var = (struct bcom_fec_rx_var *) bcom_task_var(tsk->tasknum); + inc = (struct bcom_fec_rx_inc *) bcom_task_inc(tsk->tasknum); + + if (bcom_load_image(tsk->tasknum, bcom_fec_rx_task)) + return -1; + + var->enable = bcom_eng->regs_base + + offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]); + var->fifo = (u32) priv->fifo; + var->bd_base = tsk->bd_pa; + var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size); + var->bd_start = tsk->bd_pa; + var->buffer_size = priv->maxbufsize; + + inc->incr_bytes = -(s16)sizeof(u32); /* These should be in the */ + inc->incr_dst = sizeof(u32); /* task image, but we stick */ + inc->incr_dst_ma= sizeof(u8); /* to the official ones */ + + /* Reset the BDs */ + tsk->index = 0; + tsk->outdex = 0; + + memset(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size); + + /* Configure some stuff */ + bcom_set_task_pragma(tsk->tasknum, BCOM_FEC_RX_BD_PRAGMA); + bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum); + + out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_FEC_RX], BCOM_IPR_FEC_RX); + + out_be32(&bcom_eng->regs->IntPend, 1<tasknum); /* Clear ints */ + + return 0; +} +EXPORT_SYMBOL_GPL(bcom_fec_rx_reset); + +void +bcom_fec_rx_release(struct bcom_task *tsk) +{ + /* Nothing special for the FEC tasks */ + bcom_task_free(tsk); +} +EXPORT_SYMBOL_GPL(bcom_fec_rx_release); + + + + /* Return 2nd to last DRD */ + /* This is an ugly hack, but at least it's only done + once at initialization */ +static u32 *self_modified_drd(int tasknum) +{ + u32 *desc; + int num_descs; + int drd_count; + int i; + + num_descs = bcom_task_num_descs(tasknum); + desc = bcom_task_desc(tasknum) + num_descs - 1; + drd_count = 0; + for (i=0; iflags = BCOM_FLAGS_ENABLE_TASK; + + priv = tsk->priv; + priv->fifo = fifo; + + if (bcom_fec_tx_reset(tsk)) { + bcom_task_free(tsk); + return NULL; + } + + return tsk; +} +EXPORT_SYMBOL_GPL(bcom_fec_tx_init); + +int +bcom_fec_tx_reset(struct bcom_task *tsk) +{ + struct bcom_fec_priv *priv = tsk->priv; + struct bcom_fec_tx_var *var; + struct bcom_fec_tx_inc *inc; + + /* Shutdown the task */ + bcom_disable_task(tsk->tasknum); + + /* Reset the microcode */ + var = (struct bcom_fec_tx_var *) bcom_task_var(tsk->tasknum); + inc = (struct bcom_fec_tx_inc *) bcom_task_inc(tsk->tasknum); + + if (bcom_load_image(tsk->tasknum, bcom_fec_tx_task)) + return -1; + + var->enable = bcom_eng->regs_base + + offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]); + var->fifo = (u32) priv->fifo; + var->DRD = bcom_sram_va2pa(self_modified_drd(tsk->tasknum)); + var->bd_base = tsk->bd_pa; + var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size); + var->bd_start = tsk->bd_pa; + + inc->incr_bytes = -(s16)sizeof(u32); /* These should be in the */ + inc->incr_src = sizeof(u32); /* task image, but we stick */ + inc->incr_src_ma= sizeof(u8); /* to the official ones */ + + /* Reset the BDs */ + tsk->index = 0; + tsk->outdex = 0; + + memset(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size); + + /* Configure some stuff */ + bcom_set_task_pragma(tsk->tasknum, BCOM_FEC_TX_BD_PRAGMA); + bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum); + + out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_FEC_TX], BCOM_IPR_FEC_TX); + + out_be32(&bcom_eng->regs->IntPend, 1<tasknum); /* Clear ints */ + + return 0; +} +EXPORT_SYMBOL_GPL(bcom_fec_tx_reset); + +void +bcom_fec_tx_release(struct bcom_task *tsk) +{ + /* Nothing special for the FEC tasks */ + bcom_task_free(tsk); +} +EXPORT_SYMBOL_GPL(bcom_fec_tx_release); + + +MODULE_DESCRIPTION("BestComm FEC tasks driver"); +MODULE_AUTHOR("Dale Farnsworth "); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/dma/bestcomm/gen_bd.c b/drivers/dma/bestcomm/gen_bd.c new file mode 100644 index 000000000000..1a5b22d88127 --- /dev/null +++ b/drivers/dma/bestcomm/gen_bd.c @@ -0,0 +1,354 @@ +/* + * Driver for MPC52xx processor BestComm General Buffer Descriptor + * + * Copyright (C) 2007 Sylvain Munaut + * Copyright (C) 2006 AppSpec Computer Technologies Corp. + * Jeff Gibbons + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + + +/* ======================================================================== */ +/* Task image/var/inc */ +/* ======================================================================== */ + +/* gen_bd tasks images */ +extern u32 bcom_gen_bd_rx_task[]; +extern u32 bcom_gen_bd_tx_task[]; + +/* rx task vars that need to be set before enabling the task */ +struct bcom_gen_bd_rx_var { + u32 enable; /* (u16*) address of task's control register */ + u32 fifo; /* (u32*) address of gen_bd's fifo */ + u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */ + u32 bd_last; /* (struct bcom_bd*) end of ring buffer */ + u32 bd_start; /* (struct bcom_bd*) current bd */ + u32 buffer_size; /* size of receive buffer */ +}; + +/* rx task incs that need to be set before enabling the task */ +struct bcom_gen_bd_rx_inc { + u16 pad0; + s16 incr_bytes; + u16 pad1; + s16 incr_dst; +}; + +/* tx task vars that need to be set before enabling the task */ +struct bcom_gen_bd_tx_var { + u32 fifo; /* (u32*) address of gen_bd's fifo */ + u32 enable; /* (u16*) address of task's control register */ + u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */ + u32 bd_last; /* (struct bcom_bd*) end of ring buffer */ + u32 bd_start; /* (struct bcom_bd*) current bd */ + u32 buffer_size; /* set by uCode for each packet */ +}; + +/* tx task incs that need to be set before enabling the task */ +struct bcom_gen_bd_tx_inc { + u16 pad0; + s16 incr_bytes; + u16 pad1; + s16 incr_src; + u16 pad2; + s16 incr_src_ma; +}; + +/* private structure */ +struct bcom_gen_bd_priv { + phys_addr_t fifo; + int initiator; + int ipr; + int maxbufsize; +}; + + +/* ======================================================================== */ +/* Task support code */ +/* ======================================================================== */ + +struct bcom_task * +bcom_gen_bd_rx_init(int queue_len, phys_addr_t fifo, + int initiator, int ipr, int maxbufsize) +{ + struct bcom_task *tsk; + struct bcom_gen_bd_priv *priv; + + tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_gen_bd), + sizeof(struct bcom_gen_bd_priv)); + if (!tsk) + return NULL; + + tsk->flags = BCOM_FLAGS_NONE; + + priv = tsk->priv; + priv->fifo = fifo; + priv->initiator = initiator; + priv->ipr = ipr; + priv->maxbufsize = maxbufsize; + + if (bcom_gen_bd_rx_reset(tsk)) { + bcom_task_free(tsk); + return NULL; + } + + return tsk; +} +EXPORT_SYMBOL_GPL(bcom_gen_bd_rx_init); + +int +bcom_gen_bd_rx_reset(struct bcom_task *tsk) +{ + struct bcom_gen_bd_priv *priv = tsk->priv; + struct bcom_gen_bd_rx_var *var; + struct bcom_gen_bd_rx_inc *inc; + + /* Shutdown the task */ + bcom_disable_task(tsk->tasknum); + + /* Reset the microcode */ + var = (struct bcom_gen_bd_rx_var *) bcom_task_var(tsk->tasknum); + inc = (struct bcom_gen_bd_rx_inc *) bcom_task_inc(tsk->tasknum); + + if (bcom_load_image(tsk->tasknum, bcom_gen_bd_rx_task)) + return -1; + + var->enable = bcom_eng->regs_base + + offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]); + var->fifo = (u32) priv->fifo; + var->bd_base = tsk->bd_pa; + var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size); + var->bd_start = tsk->bd_pa; + var->buffer_size = priv->maxbufsize; + + inc->incr_bytes = -(s16)sizeof(u32); + inc->incr_dst = sizeof(u32); + + /* Reset the BDs */ + tsk->index = 0; + tsk->outdex = 0; + + memset(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size); + + /* Configure some stuff */ + bcom_set_task_pragma(tsk->tasknum, BCOM_GEN_RX_BD_PRAGMA); + bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum); + + out_8(&bcom_eng->regs->ipr[priv->initiator], priv->ipr); + bcom_set_initiator(tsk->tasknum, priv->initiator); + + out_be32(&bcom_eng->regs->IntPend, 1<tasknum); /* Clear ints */ + + return 0; +} +EXPORT_SYMBOL_GPL(bcom_gen_bd_rx_reset); + +void +bcom_gen_bd_rx_release(struct bcom_task *tsk) +{ + /* Nothing special for the GenBD tasks */ + bcom_task_free(tsk); +} +EXPORT_SYMBOL_GPL(bcom_gen_bd_rx_release); + + +extern struct bcom_task * +bcom_gen_bd_tx_init(int queue_len, phys_addr_t fifo, + int initiator, int ipr) +{ + struct bcom_task *tsk; + struct bcom_gen_bd_priv *priv; + + tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_gen_bd), + sizeof(struct bcom_gen_bd_priv)); + if (!tsk) + return NULL; + + tsk->flags = BCOM_FLAGS_NONE; + + priv = tsk->priv; + priv->fifo = fifo; + priv->initiator = initiator; + priv->ipr = ipr; + + if (bcom_gen_bd_tx_reset(tsk)) { + bcom_task_free(tsk); + return NULL; + } + + return tsk; +} +EXPORT_SYMBOL_GPL(bcom_gen_bd_tx_init); + +int +bcom_gen_bd_tx_reset(struct bcom_task *tsk) +{ + struct bcom_gen_bd_priv *priv = tsk->priv; + struct bcom_gen_bd_tx_var *var; + struct bcom_gen_bd_tx_inc *inc; + + /* Shutdown the task */ + bcom_disable_task(tsk->tasknum); + + /* Reset the microcode */ + var = (struct bcom_gen_bd_tx_var *) bcom_task_var(tsk->tasknum); + inc = (struct bcom_gen_bd_tx_inc *) bcom_task_inc(tsk->tasknum); + + if (bcom_load_image(tsk->tasknum, bcom_gen_bd_tx_task)) + return -1; + + var->enable = bcom_eng->regs_base + + offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]); + var->fifo = (u32) priv->fifo; + var->bd_base = tsk->bd_pa; + var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size); + var->bd_start = tsk->bd_pa; + + inc->incr_bytes = -(s16)sizeof(u32); + inc->incr_src = sizeof(u32); + inc->incr_src_ma = sizeof(u8); + + /* Reset the BDs */ + tsk->index = 0; + tsk->outdex = 0; + + memset(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size); + + /* Configure some stuff */ + bcom_set_task_pragma(tsk->tasknum, BCOM_GEN_TX_BD_PRAGMA); + bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum); + + out_8(&bcom_eng->regs->ipr[priv->initiator], priv->ipr); + bcom_set_initiator(tsk->tasknum, priv->initiator); + + out_be32(&bcom_eng->regs->IntPend, 1<tasknum); /* Clear ints */ + + return 0; +} +EXPORT_SYMBOL_GPL(bcom_gen_bd_tx_reset); + +void +bcom_gen_bd_tx_release(struct bcom_task *tsk) +{ + /* Nothing special for the GenBD tasks */ + bcom_task_free(tsk); +} +EXPORT_SYMBOL_GPL(bcom_gen_bd_tx_release); + +/* --------------------------------------------------------------------- + * PSC support code + */ + +/** + * bcom_psc_parameters - Bestcomm initialization value table for PSC devices + * + * This structure is only used internally. It is a lookup table for PSC + * specific parameters to bestcomm tasks. + */ +static struct bcom_psc_params { + int rx_initiator; + int rx_ipr; + int tx_initiator; + int tx_ipr; +} bcom_psc_params[] = { + [0] = { + .rx_initiator = BCOM_INITIATOR_PSC1_RX, + .rx_ipr = BCOM_IPR_PSC1_RX, + .tx_initiator = BCOM_INITIATOR_PSC1_TX, + .tx_ipr = BCOM_IPR_PSC1_TX, + }, + [1] = { + .rx_initiator = BCOM_INITIATOR_PSC2_RX, + .rx_ipr = BCOM_IPR_PSC2_RX, + .tx_initiator = BCOM_INITIATOR_PSC2_TX, + .tx_ipr = BCOM_IPR_PSC2_TX, + }, + [2] = { + .rx_initiator = BCOM_INITIATOR_PSC3_RX, + .rx_ipr = BCOM_IPR_PSC3_RX, + .tx_initiator = BCOM_INITIATOR_PSC3_TX, + .tx_ipr = BCOM_IPR_PSC3_TX, + }, + [3] = { + .rx_initiator = BCOM_INITIATOR_PSC4_RX, + .rx_ipr = BCOM_IPR_PSC4_RX, + .tx_initiator = BCOM_INITIATOR_PSC4_TX, + .tx_ipr = BCOM_IPR_PSC4_TX, + }, + [4] = { + .rx_initiator = BCOM_INITIATOR_PSC5_RX, + .rx_ipr = BCOM_IPR_PSC5_RX, + .tx_initiator = BCOM_INITIATOR_PSC5_TX, + .tx_ipr = BCOM_IPR_PSC5_TX, + }, + [5] = { + .rx_initiator = BCOM_INITIATOR_PSC6_RX, + .rx_ipr = BCOM_IPR_PSC6_RX, + .tx_initiator = BCOM_INITIATOR_PSC6_TX, + .tx_ipr = BCOM_IPR_PSC6_TX, + }, +}; + +/** + * bcom_psc_gen_bd_rx_init - Allocate a receive bcom_task for a PSC port + * @psc_num: Number of the PSC to allocate a task for + * @queue_len: number of buffer descriptors to allocate for the task + * @fifo: physical address of FIFO register + * @maxbufsize: Maximum receive data size in bytes. + * + * Allocate a bestcomm task structure for receiving data from a PSC. + */ +struct bcom_task * bcom_psc_gen_bd_rx_init(unsigned psc_num, int queue_len, + phys_addr_t fifo, int maxbufsize) +{ + if (psc_num >= MPC52xx_PSC_MAXNUM) + return NULL; + + return bcom_gen_bd_rx_init(queue_len, fifo, + bcom_psc_params[psc_num].rx_initiator, + bcom_psc_params[psc_num].rx_ipr, + maxbufsize); +} +EXPORT_SYMBOL_GPL(bcom_psc_gen_bd_rx_init); + +/** + * bcom_psc_gen_bd_tx_init - Allocate a transmit bcom_task for a PSC port + * @psc_num: Number of the PSC to allocate a task for + * @queue_len: number of buffer descriptors to allocate for the task + * @fifo: physical address of FIFO register + * + * Allocate a bestcomm task structure for transmitting data to a PSC. + */ +struct bcom_task * +bcom_psc_gen_bd_tx_init(unsigned psc_num, int queue_len, phys_addr_t fifo) +{ + struct psc; + return bcom_gen_bd_tx_init(queue_len, fifo, + bcom_psc_params[psc_num].tx_initiator, + bcom_psc_params[psc_num].tx_ipr); +} +EXPORT_SYMBOL_GPL(bcom_psc_gen_bd_tx_init); + + +MODULE_DESCRIPTION("BestComm General Buffer Descriptor tasks driver"); +MODULE_AUTHOR("Jeff Gibbons "); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/dma/bestcomm/sram.c b/drivers/dma/bestcomm/sram.c new file mode 100644 index 000000000000..5e2ed30ba2c4 --- /dev/null +++ b/drivers/dma/bestcomm/sram.c @@ -0,0 +1,178 @@ +/* + * Simple memory allocator for on-board SRAM + * + * + * Maintainer : Sylvain Munaut + * + * Copyright (C) 2005 Sylvain Munaut + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + + +/* Struct keeping our 'state' */ +struct bcom_sram *bcom_sram = NULL; +EXPORT_SYMBOL_GPL(bcom_sram); /* needed for inline functions */ + + +/* ======================================================================== */ +/* Public API */ +/* ======================================================================== */ +/* DO NOT USE in interrupts, if needed in irq handler, we should use the + _irqsave version of the spin_locks */ + +int bcom_sram_init(struct device_node *sram_node, char *owner) +{ + int rv; + const u32 *regaddr_p; + u64 regaddr64, size64; + unsigned int psize; + + /* Create our state struct */ + if (bcom_sram) { + printk(KERN_ERR "%s: bcom_sram_init: " + "Already initialized !\n", owner); + return -EBUSY; + } + + bcom_sram = kmalloc(sizeof(struct bcom_sram), GFP_KERNEL); + if (!bcom_sram) { + printk(KERN_ERR "%s: bcom_sram_init: " + "Couldn't allocate internal state !\n", owner); + return -ENOMEM; + } + + /* Get address and size of the sram */ + regaddr_p = of_get_address(sram_node, 0, &size64, NULL); + if (!regaddr_p) { + printk(KERN_ERR "%s: bcom_sram_init: " + "Invalid device node !\n", owner); + rv = -EINVAL; + goto error_free; + } + + regaddr64 = of_translate_address(sram_node, regaddr_p); + + bcom_sram->base_phys = (phys_addr_t) regaddr64; + bcom_sram->size = (unsigned int) size64; + + /* Request region */ + if (!request_mem_region(bcom_sram->base_phys, bcom_sram->size, owner)) { + printk(KERN_ERR "%s: bcom_sram_init: " + "Couldn't request region !\n", owner); + rv = -EBUSY; + goto error_free; + } + + /* Map SRAM */ + /* sram is not really __iomem */ + bcom_sram->base_virt = (void*) ioremap(bcom_sram->base_phys, bcom_sram->size); + + if (!bcom_sram->base_virt) { + printk(KERN_ERR "%s: bcom_sram_init: " + "Map error SRAM zone 0x%08lx (0x%0x)!\n", + owner, (long)bcom_sram->base_phys, bcom_sram->size ); + rv = -ENOMEM; + goto error_release; + } + + /* Create an rheap (defaults to 32 bits word alignment) */ + bcom_sram->rh = rh_create(4); + + /* Attach the free zones */ +#if 0 + /* Currently disabled ... for future use only */ + reg_addr_p = of_get_property(sram_node, "available", &psize); +#else + regaddr_p = NULL; + psize = 0; +#endif + + if (!regaddr_p || !psize) { + /* Attach the whole zone */ + rh_attach_region(bcom_sram->rh, 0, bcom_sram->size); + } else { + /* Attach each zone independently */ + while (psize >= 2 * sizeof(u32)) { + phys_addr_t zbase = of_translate_address(sram_node, regaddr_p); + rh_attach_region(bcom_sram->rh, zbase - bcom_sram->base_phys, regaddr_p[1]); + regaddr_p += 2; + psize -= 2 * sizeof(u32); + } + } + + /* Init our spinlock */ + spin_lock_init(&bcom_sram->lock); + + return 0; + +error_release: + release_mem_region(bcom_sram->base_phys, bcom_sram->size); +error_free: + kfree(bcom_sram); + bcom_sram = NULL; + + return rv; +} +EXPORT_SYMBOL_GPL(bcom_sram_init); + +void bcom_sram_cleanup(void) +{ + /* Free resources */ + if (bcom_sram) { + rh_destroy(bcom_sram->rh); + iounmap((void __iomem *)bcom_sram->base_virt); + release_mem_region(bcom_sram->base_phys, bcom_sram->size); + kfree(bcom_sram); + bcom_sram = NULL; + } +} +EXPORT_SYMBOL_GPL(bcom_sram_cleanup); + +void* bcom_sram_alloc(int size, int align, phys_addr_t *phys) +{ + unsigned long offset; + + spin_lock(&bcom_sram->lock); + offset = rh_alloc_align(bcom_sram->rh, size, align, NULL); + spin_unlock(&bcom_sram->lock); + + if (IS_ERR_VALUE(offset)) + return NULL; + + *phys = bcom_sram->base_phys + offset; + return bcom_sram->base_virt + offset; +} +EXPORT_SYMBOL_GPL(bcom_sram_alloc); + +void bcom_sram_free(void *ptr) +{ + unsigned long offset; + + if (!ptr) + return; + + offset = ptr - bcom_sram->base_virt; + + spin_lock(&bcom_sram->lock); + rh_free(bcom_sram->rh, offset); + spin_unlock(&bcom_sram->lock); +} +EXPORT_SYMBOL_GPL(bcom_sram_free); + diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx.c b/drivers/net/ethernet/freescale/fec_mpc52xx.c index 817d081d2cd8..85e776d500a6 100644 --- a/drivers/net/ethernet/freescale/fec_mpc52xx.c +++ b/drivers/net/ethernet/freescale/fec_mpc52xx.c @@ -40,8 +40,8 @@ #include #include -#include -#include +#include +#include #include "fec_mpc52xx.h" diff --git a/include/linux/fsl/bestcomm/ata.h b/include/linux/fsl/bestcomm/ata.h new file mode 100644 index 000000000000..0b2371811334 --- /dev/null +++ b/include/linux/fsl/bestcomm/ata.h @@ -0,0 +1,30 @@ +/* + * Header for Bestcomm ATA task driver + * + * + * Copyright (C) 2006 Freescale - John Rigby + * Copyright (C) 2006 Sylvain Munaut + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef __BESTCOMM_ATA_H__ +#define __BESTCOMM_ATA_H__ + + +struct bcom_ata_bd { + u32 status; + u32 src_pa; + u32 dst_pa; +}; + +extern struct bcom_task * bcom_ata_init(int queue_len, int maxbufsize); +extern void bcom_ata_rx_prepare(struct bcom_task *tsk); +extern void bcom_ata_tx_prepare(struct bcom_task *tsk); +extern void bcom_ata_reset_bd(struct bcom_task *tsk); +extern void bcom_ata_release(struct bcom_task *tsk); + +#endif /* __BESTCOMM_ATA_H__ */ + diff --git a/include/linux/fsl/bestcomm/bestcomm.h b/include/linux/fsl/bestcomm/bestcomm.h new file mode 100644 index 000000000000..a0e2e6b19b57 --- /dev/null +++ b/include/linux/fsl/bestcomm/bestcomm.h @@ -0,0 +1,213 @@ +/* + * Public header for the MPC52xx processor BestComm driver + * + * + * Copyright (C) 2006 Sylvain Munaut + * Copyright (C) 2005 Varma Electronics Oy, + * ( by Andrey Volkov ) + * Copyright (C) 2003-2004 MontaVista, Software, Inc. + * ( by Dale Farnsworth ) + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef __BESTCOMM_H__ +#define __BESTCOMM_H__ + +/** + * struct bcom_bd - Structure describing a generic BestComm buffer descriptor + * @status: The current status of this buffer. Exact meaning depends on the + * task type + * @data: An array of u32 extra data. Size of array is task dependent. + * + * Note: Don't dereference a bcom_bd pointer as an array. The size of the + * bcom_bd is variable. Use bcom_get_bd() instead. + */ +struct bcom_bd { + u32 status; + u32 data[0]; /* variable payload size */ +}; + +/* ======================================================================== */ +/* Generic task management */ +/* ======================================================================== */ + +/** + * struct bcom_task - Structure describing a loaded BestComm task + * + * This structure is never built by the driver it self. It's built and + * filled the intermediate layer of the BestComm API, the task dependent + * support code. + * + * Most likely you don't need to poke around inside this structure. The + * fields are exposed in the header just for the sake of inline functions + */ +struct bcom_task { + unsigned int tasknum; + unsigned int flags; + int irq; + + struct bcom_bd *bd; + phys_addr_t bd_pa; + void **cookie; + unsigned short index; + unsigned short outdex; + unsigned int num_bd; + unsigned int bd_size; + + void* priv; +}; + +#define BCOM_FLAGS_NONE 0x00000000ul +#define BCOM_FLAGS_ENABLE_TASK (1ul << 0) + +/** + * bcom_enable - Enable a BestComm task + * @tsk: The BestComm task structure + * + * This function makes sure the given task is enabled and can be run + * by the BestComm engine as needed + */ +extern void bcom_enable(struct bcom_task *tsk); + +/** + * bcom_disable - Disable a BestComm task + * @tsk: The BestComm task structure + * + * This function disable a given task, making sure it's not executed + * by the BestComm engine. + */ +extern void bcom_disable(struct bcom_task *tsk); + + +/** + * bcom_get_task_irq - Returns the irq number of a BestComm task + * @tsk: The BestComm task structure + */ +static inline int +bcom_get_task_irq(struct bcom_task *tsk) { + return tsk->irq; +} + +/* ======================================================================== */ +/* BD based tasks helpers */ +/* ======================================================================== */ + +#define BCOM_BD_READY 0x40000000ul + +/** _bcom_next_index - Get next input index. + * @tsk: pointer to task structure + * + * Support function; Device drivers should not call this + */ +static inline int +_bcom_next_index(struct bcom_task *tsk) +{ + return ((tsk->index + 1) == tsk->num_bd) ? 0 : tsk->index + 1; +} + +/** _bcom_next_outdex - Get next output index. + * @tsk: pointer to task structure + * + * Support function; Device drivers should not call this + */ +static inline int +_bcom_next_outdex(struct bcom_task *tsk) +{ + return ((tsk->outdex + 1) == tsk->num_bd) ? 0 : tsk->outdex + 1; +} + +/** + * bcom_queue_empty - Checks if a BestComm task BD queue is empty + * @tsk: The BestComm task structure + */ +static inline int +bcom_queue_empty(struct bcom_task *tsk) +{ + return tsk->index == tsk->outdex; +} + +/** + * bcom_queue_full - Checks if a BestComm task BD queue is full + * @tsk: The BestComm task structure + */ +static inline int +bcom_queue_full(struct bcom_task *tsk) +{ + return tsk->outdex == _bcom_next_index(tsk); +} + +/** + * bcom_get_bd - Get a BD from the queue + * @tsk: The BestComm task structure + * index: Index of the BD to fetch + */ +static inline struct bcom_bd +*bcom_get_bd(struct bcom_task *tsk, unsigned int index) +{ + /* A cast to (void*) so the address can be incremented by the + * real size instead of by sizeof(struct bcom_bd) */ + return ((void *)tsk->bd) + (index * tsk->bd_size); +} + +/** + * bcom_buffer_done - Checks if a BestComm + * @tsk: The BestComm task structure + */ +static inline int +bcom_buffer_done(struct bcom_task *tsk) +{ + struct bcom_bd *bd; + if (bcom_queue_empty(tsk)) + return 0; + + bd = bcom_get_bd(tsk, tsk->outdex); + return !(bd->status & BCOM_BD_READY); +} + +/** + * bcom_prepare_next_buffer - clear status of next available buffer. + * @tsk: The BestComm task structure + * + * Returns pointer to next buffer descriptor + */ +static inline struct bcom_bd * +bcom_prepare_next_buffer(struct bcom_task *tsk) +{ + struct bcom_bd *bd; + + bd = bcom_get_bd(tsk, tsk->index); + bd->status = 0; /* cleanup last status */ + return bd; +} + +static inline void +bcom_submit_next_buffer(struct bcom_task *tsk, void *cookie) +{ + struct bcom_bd *bd = bcom_get_bd(tsk, tsk->index); + + tsk->cookie[tsk->index] = cookie; + mb(); /* ensure the bd is really up-to-date */ + bd->status |= BCOM_BD_READY; + tsk->index = _bcom_next_index(tsk); + if (tsk->flags & BCOM_FLAGS_ENABLE_TASK) + bcom_enable(tsk); +} + +static inline void * +bcom_retrieve_buffer(struct bcom_task *tsk, u32 *p_status, struct bcom_bd **p_bd) +{ + void *cookie = tsk->cookie[tsk->outdex]; + struct bcom_bd *bd = bcom_get_bd(tsk, tsk->outdex); + + if (p_status) + *p_status = bd->status; + if (p_bd) + *p_bd = bd; + tsk->outdex = _bcom_next_outdex(tsk); + return cookie; +} + +#endif /* __BESTCOMM_H__ */ diff --git a/include/linux/fsl/bestcomm/bestcomm_priv.h b/include/linux/fsl/bestcomm/bestcomm_priv.h new file mode 100644 index 000000000000..3b52f3ffbdf8 --- /dev/null +++ b/include/linux/fsl/bestcomm/bestcomm_priv.h @@ -0,0 +1,350 @@ +/* + * Private header for the MPC52xx processor BestComm driver + * + * By private, we mean that driver should not use it directly. It's meant + * to be used by the BestComm engine driver itself and by the intermediate + * layer between the core and the drivers. + * + * Copyright (C) 2006 Sylvain Munaut + * Copyright (C) 2005 Varma Electronics Oy, + * ( by Andrey Volkov ) + * Copyright (C) 2003-2004 MontaVista, Software, Inc. + * ( by Dale Farnsworth ) + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef __BESTCOMM_PRIV_H__ +#define __BESTCOMM_PRIV_H__ + +#include +#include +#include +#include + +#include "sram.h" + + +/* ======================================================================== */ +/* Engine related stuff */ +/* ======================================================================== */ + +/* Zones sizes and needed alignments */ +#define BCOM_MAX_TASKS 16 +#define BCOM_MAX_VAR 24 +#define BCOM_MAX_INC 8 +#define BCOM_MAX_FDT 64 +#define BCOM_MAX_CTX 20 +#define BCOM_CTX_SIZE (BCOM_MAX_CTX * sizeof(u32)) +#define BCOM_CTX_ALIGN 0x100 +#define BCOM_VAR_SIZE (BCOM_MAX_VAR * sizeof(u32)) +#define BCOM_INC_SIZE (BCOM_MAX_INC * sizeof(u32)) +#define BCOM_VAR_ALIGN 0x80 +#define BCOM_FDT_SIZE (BCOM_MAX_FDT * sizeof(u32)) +#define BCOM_FDT_ALIGN 0x100 + +/** + * struct bcom_tdt - Task Descriptor Table Entry + * + */ +struct bcom_tdt { + u32 start; + u32 stop; + u32 var; + u32 fdt; + u32 exec_status; /* used internally by BestComm engine */ + u32 mvtp; /* used internally by BestComm engine */ + u32 context; + u32 litbase; +}; + +/** + * struct bcom_engine + * + * This holds all info needed globaly to handle the engine + */ +struct bcom_engine { + struct device_node *ofnode; + struct mpc52xx_sdma __iomem *regs; + phys_addr_t regs_base; + + struct bcom_tdt *tdt; + u32 *ctx; + u32 *var; + u32 *fdt; + + spinlock_t lock; +}; + +extern struct bcom_engine *bcom_eng; + + +/* ======================================================================== */ +/* Tasks related stuff */ +/* ======================================================================== */ + +/* Tasks image header */ +#define BCOM_TASK_MAGIC 0x4243544B /* 'BCTK' */ + +struct bcom_task_header { + u32 magic; + u8 desc_size; /* the size fields */ + u8 var_size; /* are given in number */ + u8 inc_size; /* of 32-bits words */ + u8 first_var; + u8 reserved[8]; +}; + +/* Descriptors structure & co */ +#define BCOM_DESC_NOP 0x000001f8 +#define BCOM_LCD_MASK 0x80000000 +#define BCOM_DRD_EXTENDED 0x40000000 +#define BCOM_DRD_INITIATOR_SHIFT 21 + +/* Tasks pragma */ +#define BCOM_PRAGMA_BIT_RSV 7 /* reserved pragma bit */ +#define BCOM_PRAGMA_BIT_PRECISE_INC 6 /* increment 0=when possible, */ + /* 1=iter end */ +#define BCOM_PRAGMA_BIT_RST_ERROR_NO 5 /* don't reset errors on */ + /* task enable */ +#define BCOM_PRAGMA_BIT_PACK 4 /* pack data enable */ +#define BCOM_PRAGMA_BIT_INTEGER 3 /* data alignment */ + /* 0=frac(msb), 1=int(lsb) */ +#define BCOM_PRAGMA_BIT_SPECREAD 2 /* XLB speculative read */ +#define BCOM_PRAGMA_BIT_CW 1 /* write line buffer enable */ +#define BCOM_PRAGMA_BIT_RL 0 /* read line buffer enable */ + + /* Looks like XLB speculative read generates XLB errors when a buffer + * is at the end of the physical memory. i.e. when accessing the + * lasts words, the engine tries to prefetch the next but there is no + * next ... + */ +#define BCOM_STD_PRAGMA ((0 << BCOM_PRAGMA_BIT_RSV) | \ + (0 << BCOM_PRAGMA_BIT_PRECISE_INC) | \ + (0 << BCOM_PRAGMA_BIT_RST_ERROR_NO) | \ + (0 << BCOM_PRAGMA_BIT_PACK) | \ + (0 << BCOM_PRAGMA_BIT_INTEGER) | \ + (0 << BCOM_PRAGMA_BIT_SPECREAD) | \ + (1 << BCOM_PRAGMA_BIT_CW) | \ + (1 << BCOM_PRAGMA_BIT_RL)) + +#define BCOM_PCI_PRAGMA ((0 << BCOM_PRAGMA_BIT_RSV) | \ + (0 << BCOM_PRAGMA_BIT_PRECISE_INC) | \ + (0 << BCOM_PRAGMA_BIT_RST_ERROR_NO) | \ + (0 << BCOM_PRAGMA_BIT_PACK) | \ + (1 << BCOM_PRAGMA_BIT_INTEGER) | \ + (0 << BCOM_PRAGMA_BIT_SPECREAD) | \ + (1 << BCOM_PRAGMA_BIT_CW) | \ + (1 << BCOM_PRAGMA_BIT_RL)) + +#define BCOM_ATA_PRAGMA BCOM_STD_PRAGMA +#define BCOM_CRC16_DP_0_PRAGMA BCOM_STD_PRAGMA +#define BCOM_CRC16_DP_1_PRAGMA BCOM_STD_PRAGMA +#define BCOM_FEC_RX_BD_PRAGMA BCOM_STD_PRAGMA +#define BCOM_FEC_TX_BD_PRAGMA BCOM_STD_PRAGMA +#define BCOM_GEN_DP_0_PRAGMA BCOM_STD_PRAGMA +#define BCOM_GEN_DP_1_PRAGMA BCOM_STD_PRAGMA +#define BCOM_GEN_DP_2_PRAGMA BCOM_STD_PRAGMA +#define BCOM_GEN_DP_3_PRAGMA BCOM_STD_PRAGMA +#define BCOM_GEN_DP_BD_0_PRAGMA BCOM_STD_PRAGMA +#define BCOM_GEN_DP_BD_1_PRAGMA BCOM_STD_PRAGMA +#define BCOM_GEN_RX_BD_PRAGMA BCOM_STD_PRAGMA +#define BCOM_GEN_TX_BD_PRAGMA BCOM_STD_PRAGMA +#define BCOM_GEN_LPC_PRAGMA BCOM_STD_PRAGMA +#define BCOM_PCI_RX_PRAGMA BCOM_PCI_PRAGMA +#define BCOM_PCI_TX_PRAGMA BCOM_PCI_PRAGMA + +/* Initiators number */ +#define BCOM_INITIATOR_ALWAYS 0 +#define BCOM_INITIATOR_SCTMR_0 1 +#define BCOM_INITIATOR_SCTMR_1 2 +#define BCOM_INITIATOR_FEC_RX 3 +#define BCOM_INITIATOR_FEC_TX 4 +#define BCOM_INITIATOR_ATA_RX 5 +#define BCOM_INITIATOR_ATA_TX 6 +#define BCOM_INITIATOR_SCPCI_RX 7 +#define BCOM_INITIATOR_SCPCI_TX 8 +#define BCOM_INITIATOR_PSC3_RX 9 +#define BCOM_INITIATOR_PSC3_TX 10 +#define BCOM_INITIATOR_PSC2_RX 11 +#define BCOM_INITIATOR_PSC2_TX 12 +#define BCOM_INITIATOR_PSC1_RX 13 +#define BCOM_INITIATOR_PSC1_TX 14 +#define BCOM_INITIATOR_SCTMR_2 15 +#define BCOM_INITIATOR_SCLPC 16 +#define BCOM_INITIATOR_PSC5_RX 17 +#define BCOM_INITIATOR_PSC5_TX 18 +#define BCOM_INITIATOR_PSC4_RX 19 +#define BCOM_INITIATOR_PSC4_TX 20 +#define BCOM_INITIATOR_I2C2_RX 21 +#define BCOM_INITIATOR_I2C2_TX 22 +#define BCOM_INITIATOR_I2C1_RX 23 +#define BCOM_INITIATOR_I2C1_TX 24 +#define BCOM_INITIATOR_PSC6_RX 25 +#define BCOM_INITIATOR_PSC6_TX 26 +#define BCOM_INITIATOR_IRDA_RX 25 +#define BCOM_INITIATOR_IRDA_TX 26 +#define BCOM_INITIATOR_SCTMR_3 27 +#define BCOM_INITIATOR_SCTMR_4 28 +#define BCOM_INITIATOR_SCTMR_5 29 +#define BCOM_INITIATOR_SCTMR_6 30 +#define BCOM_INITIATOR_SCTMR_7 31 + +/* Initiators priorities */ +#define BCOM_IPR_ALWAYS 7 +#define BCOM_IPR_SCTMR_0 2 +#define BCOM_IPR_SCTMR_1 2 +#define BCOM_IPR_FEC_RX 6 +#define BCOM_IPR_FEC_TX 5 +#define BCOM_IPR_ATA_RX 7 +#define BCOM_IPR_ATA_TX 7 +#define BCOM_IPR_SCPCI_RX 2 +#define BCOM_IPR_SCPCI_TX 2 +#define BCOM_IPR_PSC3_RX 2 +#define BCOM_IPR_PSC3_TX 2 +#define BCOM_IPR_PSC2_RX 2 +#define BCOM_IPR_PSC2_TX 2 +#define BCOM_IPR_PSC1_RX 2 +#define BCOM_IPR_PSC1_TX 2 +#define BCOM_IPR_SCTMR_2 2 +#define BCOM_IPR_SCLPC 2 +#define BCOM_IPR_PSC5_RX 2 +#define BCOM_IPR_PSC5_TX 2 +#define BCOM_IPR_PSC4_RX 2 +#define BCOM_IPR_PSC4_TX 2 +#define BCOM_IPR_I2C2_RX 2 +#define BCOM_IPR_I2C2_TX 2 +#define BCOM_IPR_I2C1_RX 2 +#define BCOM_IPR_I2C1_TX 2 +#define BCOM_IPR_PSC6_RX 2 +#define BCOM_IPR_PSC6_TX 2 +#define BCOM_IPR_IRDA_RX 2 +#define BCOM_IPR_IRDA_TX 2 +#define BCOM_IPR_SCTMR_3 2 +#define BCOM_IPR_SCTMR_4 2 +#define BCOM_IPR_SCTMR_5 2 +#define BCOM_IPR_SCTMR_6 2 +#define BCOM_IPR_SCTMR_7 2 + + +/* ======================================================================== */ +/* API */ +/* ======================================================================== */ + +extern struct bcom_task *bcom_task_alloc(int bd_count, int bd_size, int priv_size); +extern void bcom_task_free(struct bcom_task *tsk); +extern int bcom_load_image(int task, u32 *task_image); +extern void bcom_set_initiator(int task, int initiator); + + +#define TASK_ENABLE 0x8000 + +/** + * bcom_disable_prefetch - Hook to disable bus prefetching + * + * ATA DMA and the original MPC5200 need this due to silicon bugs. At the + * moment disabling prefetch is a one-way street. There is no mechanism + * in place to turn prefetch back on after it has been disabled. There is + * no reason it couldn't be done, it would just be more complex to implement. + */ +static inline void bcom_disable_prefetch(void) +{ + u16 regval; + + regval = in_be16(&bcom_eng->regs->PtdCntrl); + out_be16(&bcom_eng->regs->PtdCntrl, regval | 1); +}; + +static inline void +bcom_enable_task(int task) +{ + u16 reg; + reg = in_be16(&bcom_eng->regs->tcr[task]); + out_be16(&bcom_eng->regs->tcr[task], reg | TASK_ENABLE); +} + +static inline void +bcom_disable_task(int task) +{ + u16 reg = in_be16(&bcom_eng->regs->tcr[task]); + out_be16(&bcom_eng->regs->tcr[task], reg & ~TASK_ENABLE); +} + + +static inline u32 * +bcom_task_desc(int task) +{ + return bcom_sram_pa2va(bcom_eng->tdt[task].start); +} + +static inline int +bcom_task_num_descs(int task) +{ + return (bcom_eng->tdt[task].stop - bcom_eng->tdt[task].start)/sizeof(u32) + 1; +} + +static inline u32 * +bcom_task_var(int task) +{ + return bcom_sram_pa2va(bcom_eng->tdt[task].var); +} + +static inline u32 * +bcom_task_inc(int task) +{ + return &bcom_task_var(task)[BCOM_MAX_VAR]; +} + + +static inline int +bcom_drd_is_extended(u32 desc) +{ + return (desc) & BCOM_DRD_EXTENDED; +} + +static inline int +bcom_desc_is_drd(u32 desc) +{ + return !(desc & BCOM_LCD_MASK) && desc != BCOM_DESC_NOP; +} + +static inline int +bcom_desc_initiator(u32 desc) +{ + return (desc >> BCOM_DRD_INITIATOR_SHIFT) & 0x1f; +} + +static inline void +bcom_set_desc_initiator(u32 *desc, int initiator) +{ + *desc = (*desc & ~(0x1f << BCOM_DRD_INITIATOR_SHIFT)) | + ((initiator & 0x1f) << BCOM_DRD_INITIATOR_SHIFT); +} + + +static inline void +bcom_set_task_pragma(int task, int pragma) +{ + u32 *fdt = &bcom_eng->tdt[task].fdt; + *fdt = (*fdt & ~0xff) | pragma; +} + +static inline void +bcom_set_task_auto_start(int task, int next_task) +{ + u16 __iomem *tcr = &bcom_eng->regs->tcr[task]; + out_be16(tcr, (in_be16(tcr) & ~0xff) | 0x00c0 | next_task); +} + +static inline void +bcom_set_tcr_initiator(int task, int initiator) +{ + u16 __iomem *tcr = &bcom_eng->regs->tcr[task]; + out_be16(tcr, (in_be16(tcr) & ~0x1f00) | ((initiator & 0x1f) << 8)); +} + + +#endif /* __BESTCOMM_PRIV_H__ */ + diff --git a/include/linux/fsl/bestcomm/fec.h b/include/linux/fsl/bestcomm/fec.h new file mode 100644 index 000000000000..ee565d94d503 --- /dev/null +++ b/include/linux/fsl/bestcomm/fec.h @@ -0,0 +1,61 @@ +/* + * Header for Bestcomm FEC tasks driver + * + * + * Copyright (C) 2006-2007 Sylvain Munaut + * Copyright (C) 2003-2004 MontaVista, Software, Inc. + * ( by Dale Farnsworth ) + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef __BESTCOMM_FEC_H__ +#define __BESTCOMM_FEC_H__ + + +struct bcom_fec_bd { + u32 status; + u32 skb_pa; +}; + +#define BCOM_FEC_TX_BD_TFD 0x08000000ul /* transmit frame done */ +#define BCOM_FEC_TX_BD_TC 0x04000000ul /* transmit CRC */ +#define BCOM_FEC_TX_BD_ABC 0x02000000ul /* append bad CRC */ + +#define BCOM_FEC_RX_BD_L 0x08000000ul /* buffer is last in frame */ +#define BCOM_FEC_RX_BD_BC 0x00800000ul /* DA is broadcast */ +#define BCOM_FEC_RX_BD_MC 0x00400000ul /* DA is multicast and not broadcast */ +#define BCOM_FEC_RX_BD_LG 0x00200000ul /* Rx frame length violation */ +#define BCOM_FEC_RX_BD_NO 0x00100000ul /* Rx non-octet aligned frame */ +#define BCOM_FEC_RX_BD_CR 0x00040000ul /* Rx CRC error */ +#define BCOM_FEC_RX_BD_OV 0x00020000ul /* overrun */ +#define BCOM_FEC_RX_BD_TR 0x00010000ul /* Rx frame truncated */ +#define BCOM_FEC_RX_BD_LEN_MASK 0x000007fful /* mask for length of received frame */ +#define BCOM_FEC_RX_BD_ERRORS (BCOM_FEC_RX_BD_LG | BCOM_FEC_RX_BD_NO | \ + BCOM_FEC_RX_BD_CR | BCOM_FEC_RX_BD_OV | BCOM_FEC_RX_BD_TR) + + +extern struct bcom_task * +bcom_fec_rx_init(int queue_len, phys_addr_t fifo, int maxbufsize); + +extern int +bcom_fec_rx_reset(struct bcom_task *tsk); + +extern void +bcom_fec_rx_release(struct bcom_task *tsk); + + +extern struct bcom_task * +bcom_fec_tx_init(int queue_len, phys_addr_t fifo); + +extern int +bcom_fec_tx_reset(struct bcom_task *tsk); + +extern void +bcom_fec_tx_release(struct bcom_task *tsk); + + +#endif /* __BESTCOMM_FEC_H__ */ + diff --git a/include/linux/fsl/bestcomm/gen_bd.h b/include/linux/fsl/bestcomm/gen_bd.h new file mode 100644 index 000000000000..de47260e69da --- /dev/null +++ b/include/linux/fsl/bestcomm/gen_bd.h @@ -0,0 +1,53 @@ +/* + * Header for Bestcomm General Buffer Descriptor tasks driver + * + * + * Copyright (C) 2007 Sylvain Munaut + * Copyright (C) 2006 AppSpec Computer Technologies Corp. + * Jeff Gibbons + * + * 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 __BESTCOMM_GEN_BD_H__ +#define __BESTCOMM_GEN_BD_H__ + +struct bcom_gen_bd { + u32 status; + u32 buf_pa; +}; + + +extern struct bcom_task * +bcom_gen_bd_rx_init(int queue_len, phys_addr_t fifo, + int initiator, int ipr, int maxbufsize); + +extern int +bcom_gen_bd_rx_reset(struct bcom_task *tsk); + +extern void +bcom_gen_bd_rx_release(struct bcom_task *tsk); + + +extern struct bcom_task * +bcom_gen_bd_tx_init(int queue_len, phys_addr_t fifo, + int initiator, int ipr); + +extern int +bcom_gen_bd_tx_reset(struct bcom_task *tsk); + +extern void +bcom_gen_bd_tx_release(struct bcom_task *tsk); + + +/* PSC support utility wrappers */ +struct bcom_task * bcom_psc_gen_bd_rx_init(unsigned psc_num, int queue_len, + phys_addr_t fifo, int maxbufsize); +struct bcom_task * bcom_psc_gen_bd_tx_init(unsigned psc_num, int queue_len, + phys_addr_t fifo); +#endif /* __BESTCOMM_GEN_BD_H__ */ + diff --git a/include/linux/fsl/bestcomm/sram.h b/include/linux/fsl/bestcomm/sram.h new file mode 100644 index 000000000000..b6d668963cce --- /dev/null +++ b/include/linux/fsl/bestcomm/sram.h @@ -0,0 +1,54 @@ +/* + * Handling of a sram zone for bestcomm + * + * + * Copyright (C) 2007 Sylvain Munaut + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef __BESTCOMM_SRAM_H__ +#define __BESTCOMM_SRAM_H__ + +#include +#include +#include + + +/* Structure used internally */ + /* The internals are here for the inline functions + * sake, certainly not for the user to mess with ! + */ +struct bcom_sram { + phys_addr_t base_phys; + void *base_virt; + unsigned int size; + rh_info_t *rh; + spinlock_t lock; +}; + +extern struct bcom_sram *bcom_sram; + + +/* Public API */ +extern int bcom_sram_init(struct device_node *sram_node, char *owner); +extern void bcom_sram_cleanup(void); + +extern void* bcom_sram_alloc(int size, int align, phys_addr_t *phys); +extern void bcom_sram_free(void *ptr); + +static inline phys_addr_t bcom_sram_va2pa(void *va) { + return bcom_sram->base_phys + + (unsigned long)(va - bcom_sram->base_virt); +} + +static inline void *bcom_sram_pa2va(phys_addr_t pa) { + return bcom_sram->base_virt + + (unsigned long)(pa - bcom_sram->base_phys); +} + + +#endif /* __BESTCOMM_SRAM_H__ */ + diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index 9997c039bb24..2a847ca494b5 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -14,8 +14,8 @@ #include -#include -#include +#include +#include #include #include "mpc5200_dma.h" -- cgit v1.2.3 From 3dbfff3dfe6714aeefb615c65bec0800dc5a4c51 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 19 Nov 2012 16:10:20 +0800 Subject: Introduce THERMAL_TREND_RAISE/DROP_FULL support for step_wise governor step_wise governor should set the device cooling state to upper/lower limit directly when THERMAL_TREND_RAISE/DROP_FULL. Signed-off-by: Zhang Rui --- drivers/thermal/step_wise.c | 64 ++++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c index 0cd5e9fbab1c..f45dd0f768f0 100644 --- a/drivers/thermal/step_wise.c +++ b/drivers/thermal/step_wise.c @@ -35,21 +35,54 @@ * state for this trip point * b. if the trend is THERMAL_TREND_DROPPING, use lower cooling * state for this trip point + * c. if the trend is THERMAL_TREND_RAISE_FULL, use upper limit + * for this trip point + * d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit + * for this trip point + * If the temperature is lower than a trip point, + * a. if the trend is THERMAL_TREND_RAISING, do nothing + * b. if the trend is THERMAL_TREND_DROPPING, use lower cooling + * state for this trip point, if the cooling state already + * equals lower limit, deactivate the thermal instance + * c. if the trend is THERMAL_TREND_RAISE_FULL, do nothing + * d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit, + * if the cooling state already equals lower limit, + * deactive the thermal instance */ static unsigned long get_target_state(struct thermal_instance *instance, - enum thermal_trend trend) + enum thermal_trend trend, bool throttle) { struct thermal_cooling_device *cdev = instance->cdev; unsigned long cur_state; cdev->ops->get_cur_state(cdev, &cur_state); - if (trend == THERMAL_TREND_RAISING) { - cur_state = cur_state < instance->upper ? - (cur_state + 1) : instance->upper; - } else if (trend == THERMAL_TREND_DROPPING) { - cur_state = cur_state > instance->lower ? - (cur_state - 1) : instance->lower; + switch (trend) { + case THERMAL_TREND_RAISING: + if (throttle) + cur_state = cur_state < instance->upper ? + (cur_state + 1) : instance->upper; + break; + case THERMAL_TREND_RAISE_FULL: + if (throttle) + cur_state = instance->upper; + break; + case THERMAL_TREND_DROPPING: + if (cur_state == instance->lower) { + if (!throttle) + cur_state = -1; + } else + cur_state -= 1; + break; + case THERMAL_TREND_DROP_FULL: + if (cur_state == instance->lower) { + if (!throttle) + cur_state = -1; + } else + cur_state = instance->lower; + break; + default: + break; } return cur_state; @@ -76,7 +109,7 @@ static void update_instance_for_throttle(struct thermal_zone_device *tz, if (instance->trip != trip) continue; - instance->target = get_target_state(instance, trend); + instance->target = get_target_state(instance, trend, true); /* Activate a passive thermal instance */ if (instance->target == THERMAL_NO_TARGET) @@ -87,28 +120,23 @@ static void update_instance_for_throttle(struct thermal_zone_device *tz, } static void update_instance_for_dethrottle(struct thermal_zone_device *tz, - int trip, enum thermal_trip_type trip_type) + int trip, enum thermal_trip_type trip_type, + enum thermal_trend trend) { struct thermal_instance *instance; - struct thermal_cooling_device *cdev; - unsigned long cur_state; list_for_each_entry(instance, &tz->thermal_instances, tz_node) { if (instance->trip != trip || instance->target == THERMAL_NO_TARGET) continue; - cdev = instance->cdev; - cdev->ops->get_cur_state(cdev, &cur_state); - - instance->target = cur_state > instance->lower ? - (cur_state - 1) : THERMAL_NO_TARGET; + instance->target = get_target_state(instance, trend, false); /* Deactivate a passive thermal instance */ if (instance->target == THERMAL_NO_TARGET) update_passive_instance(tz, trip_type, -1); - cdev->updated = false; /* cdev needs update */ + instance->cdev->updated = false; /* cdev needs update */ } } @@ -133,7 +161,7 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) if (tz->temperature >= trip_temp) update_instance_for_throttle(tz, trip, trip_type, trend); else - update_instance_for_dethrottle(tz, trip, trip_type); + update_instance_for_dethrottle(tz, trip, trip_type, trend); mutex_unlock(&tz->lock); } -- cgit v1.2.3 From b8bb6cb999858043489c1ddef08eed2127559169 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 22 Nov 2012 15:45:02 +0800 Subject: step_wise: Unify the code for both throttle and dethrottle Signed-off-by: Zhang Rui --- drivers/thermal/step_wise.c | 70 ++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 45 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c index f45dd0f768f0..407cde3211c1 100644 --- a/drivers/thermal/step_wise.c +++ b/drivers/thermal/step_wise.c @@ -99,52 +99,14 @@ static void update_passive_instance(struct thermal_zone_device *tz, tz->passive += value; } -static void update_instance_for_throttle(struct thermal_zone_device *tz, - int trip, enum thermal_trip_type trip_type, - enum thermal_trend trend) -{ - struct thermal_instance *instance; - - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { - if (instance->trip != trip) - continue; - - instance->target = get_target_state(instance, trend, true); - - /* Activate a passive thermal instance */ - if (instance->target == THERMAL_NO_TARGET) - update_passive_instance(tz, trip_type, 1); - - instance->cdev->updated = false; /* cdev needs update */ - } -} - -static void update_instance_for_dethrottle(struct thermal_zone_device *tz, - int trip, enum thermal_trip_type trip_type, - enum thermal_trend trend) -{ - struct thermal_instance *instance; - - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { - if (instance->trip != trip || - instance->target == THERMAL_NO_TARGET) - continue; - - instance->target = get_target_state(instance, trend, false); - - /* Deactivate a passive thermal instance */ - if (instance->target == THERMAL_NO_TARGET) - update_passive_instance(tz, trip_type, -1); - - instance->cdev->updated = false; /* cdev needs update */ - } -} - static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) { long trip_temp; enum thermal_trip_type trip_type; enum thermal_trend trend; + struct thermal_instance *instance; + bool throttle = false; + int old_target; if (trip == THERMAL_TRIPS_NONE) { trip_temp = tz->forced_passive; @@ -156,12 +118,30 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) trend = get_tz_trend(tz, trip); + if (tz->temperature >= trip_temp) + throttle = true; + mutex_lock(&tz->lock); - if (tz->temperature >= trip_temp) - update_instance_for_throttle(tz, trip, trip_type, trend); - else - update_instance_for_dethrottle(tz, trip, trip_type, trend); + list_for_each_entry(instance, &tz->thermal_instances, tz_node) { + if (instance->trip != trip) + continue; + + old_target = instance->target; + instance->target = get_target_state(instance, trend, throttle); + + /* Activate a passive thermal instance */ + if (old_target == THERMAL_NO_TARGET && + instance->target != THERMAL_NO_TARGET) + update_passive_instance(tz, trip_type, 1); + /* Deactivate a passive thermal instance */ + else if (old_target != THERMAL_NO_TARGET && + instance->target == THERMAL_NO_TARGET) + update_passive_instance(tz, trip_type, -1); + + + instance->cdev->updated = false; /* cdev needs update */ + } mutex_unlock(&tz->lock); } -- cgit v1.2.3 From bbf63be4f331358173da26b888a10583fcc92ec0 Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Wed, 21 Nov 2012 13:31:01 +0900 Subject: Thermal: exynos: Add sysfs node supporting exynos's emulation mode. This patch supports exynos's emulation mode with newly created sysfs node. Exynos 4x12 (4212, 4412) and 5 series provide emulation mode for thermal management unit. Thermal emulation mode supports software debug for TMU's operation. User can set temperature manually with software code and TMU will read current temperature from user value not from sensor's value. This patch includes also documentary placed under Documentation/thermal/. Signed-off-by: Jonghwa Lee Signed-off-by: Zhang Rui --- Documentation/thermal/exynos_thermal_emulation | 53 +++++++++++++ drivers/thermal/Kconfig | 9 +++ drivers/thermal/exynos_thermal.c | 103 +++++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 Documentation/thermal/exynos_thermal_emulation (limited to 'drivers') diff --git a/Documentation/thermal/exynos_thermal_emulation b/Documentation/thermal/exynos_thermal_emulation new file mode 100644 index 000000000000..b73bbfb697bb --- /dev/null +++ b/Documentation/thermal/exynos_thermal_emulation @@ -0,0 +1,53 @@ +EXYNOS EMULATION MODE +======================== + +Copyright (C) 2012 Samsung Electronics + +Written by Jonghwa Lee + +Description +----------- + +Exynos 4x12 (4212, 4412) and 5 series provide emulation mode for thermal management unit. +Thermal emulation mode supports software debug for TMU's operation. User can set temperature +manually with software code and TMU will read current temperature from user value not from +sensor's value. + +Enabling CONFIG_EXYNOS_THERMAL_EMUL option will make this support in available. +When it's enabled, sysfs node will be created under +/sys/bus/platform/devices/'exynos device name'/ with name of 'emulation'. + +The sysfs node, 'emulation', will contain value 0 for the initial state. When you input any +temperature you want to update to sysfs node, it automatically enable emulation mode and +current temperature will be changed into it. +(Exynos also supports user changable delay time which would be used to delay of + changing temperature. However, this node only uses same delay of real sensing time, 938us.) + +Exynos emulation mode requires synchronous of value changing and enabling. It means when you +want to update the any value of delay or next temperature, then you have to enable emulation +mode at the same time. (Or you have to keep the mode enabling.) If you don't, it fails to +change the value to updated one and just use last succeessful value repeatedly. That's why +this node gives users the right to change termerpature only. Just one interface makes it more +simply to use. + +Disabling emulation mode only requires writing value 0 to sysfs node. + + +TEMP 120 | + | + 100 | + | + 80 | + | +----------- + 60 | | | + | +-------------| | + 40 | | | | + | | | | + 20 | | | +---------- + | | | | | + 0 |______________|_____________|__________|__________|_________ + A A A A TIME + |<----->| |<----->| |<----->| | + | 938us | | | | | | +emulation : 0 50 | 70 | 20 | 0 +current temp : sensor 50 70 20 sensor diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index c2c77d1ac499..c31b9e4451a3 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -101,6 +101,15 @@ config EXYNOS_THERMAL If you say yes here you get support for TMU (Thermal Management Unit) on SAMSUNG EXYNOS series of SoC. +config EXYNOS_THERMAL_EMUL + bool "EXYNOS TMU emulation mode support" + depends on EXYNOS_THERMAL + help + Exynos 4412 and 4414 and 5 series has emulation mode on TMU. + Enable this option will be make sysfs node in exynos thermal platform + device directory to support emulation mode. With emulation mode sysfs + node, you can manually input temperature to TMU for simulation purpose. + config DB8500_THERMAL bool "DB8500 thermal management" depends on ARCH_U8500 diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c index 7772d1603769..e2ef8a3531ac 100644 --- a/drivers/thermal/exynos_thermal.c +++ b/drivers/thermal/exynos_thermal.c @@ -99,6 +99,14 @@ #define IDLE_INTERVAL 10000 #define MCELSIUS 1000 +#ifdef CONFIG_EXYNOS_THERMAL_EMUL +#define EXYNOS_EMUL_TIME 0x57F0 +#define EXYNOS_EMUL_TIME_SHIFT 16 +#define EXYNOS_EMUL_DATA_SHIFT 8 +#define EXYNOS_EMUL_DATA_MASK 0xFF +#define EXYNOS_EMUL_ENABLE 0x1 +#endif /* CONFIG_EXYNOS_THERMAL_EMUL */ + /* CPU Zone information */ #define PANIC_ZONE 4 #define WARN_ZONE 3 @@ -832,6 +840,94 @@ static inline struct exynos_tmu_platform_data *exynos_get_driver_data( return (struct exynos_tmu_platform_data *) platform_get_device_id(pdev)->driver_data; } + +#ifdef CONFIG_EXYNOS_THERMAL_EMUL +static ssize_t exynos_tmu_emulation_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = container_of(dev, + struct platform_device, dev); + struct exynos_tmu_data *data = platform_get_drvdata(pdev); + unsigned int reg; + u8 temp_code; + int temp = 0; + + if (data->soc == SOC_ARCH_EXYNOS4210) + goto out; + + mutex_lock(&data->lock); + clk_enable(data->clk); + reg = readl(data->base + EXYNOS_EMUL_CON); + clk_disable(data->clk); + mutex_unlock(&data->lock); + + if (reg & EXYNOS_EMUL_ENABLE) { + reg >>= EXYNOS_EMUL_DATA_SHIFT; + temp_code = reg & EXYNOS_EMUL_DATA_MASK; + temp = code_to_temp(data, temp_code); + } +out: + return sprintf(buf, "%d\n", temp * MCELSIUS); +} + +static ssize_t exynos_tmu_emulation_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = container_of(dev, + struct platform_device, dev); + struct exynos_tmu_data *data = platform_get_drvdata(pdev); + unsigned int reg; + int temp; + + if (data->soc == SOC_ARCH_EXYNOS4210) + goto out; + + if (!sscanf(buf, "%d\n", &temp) || temp < 0) + return -EINVAL; + + mutex_lock(&data->lock); + clk_enable(data->clk); + + reg = readl(data->base + EXYNOS_EMUL_CON); + + if (temp) { + /* Both CELSIUS and MCELSIUS type are available for input */ + if (temp > MCELSIUS) + temp /= MCELSIUS; + + reg = (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT) | + (temp_to_code(data, (temp / MCELSIUS)) + << EXYNOS_EMUL_DATA_SHIFT) | EXYNOS_EMUL_ENABLE; + } else { + reg &= ~EXYNOS_EMUL_ENABLE; + } + + writel(reg, data->base + EXYNOS_EMUL_CON); + + clk_disable(data->clk); + mutex_unlock(&data->lock); + +out: + return count; +} + +static DEVICE_ATTR(emulation, 0644, exynos_tmu_emulation_show, + exynos_tmu_emulation_store); +static int create_emulation_sysfs(struct device *dev) +{ + return device_create_file(dev, &dev_attr_emulation); +} +static void remove_emulation_sysfs(struct device *dev) +{ + device_remove_file(dev, &dev_attr_emulation); +} +#else +static inline int create_emulation_sysfs(struct device *dev) { return 0; } +static inline void remove_emulation_sysfs(struct device *dev) {} +#endif + static int __devinit exynos_tmu_probe(struct platform_device *pdev) { struct exynos_tmu_data *data; @@ -930,6 +1026,11 @@ static int __devinit exynos_tmu_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to register thermal interface\n"); goto err_clk; } + + ret = create_emulation_sysfs(&pdev->dev); + if (ret) + dev_err(&pdev->dev, "Failed to create emulation mode sysfs node\n"); + return 0; err_clk: platform_set_drvdata(pdev, NULL); @@ -941,6 +1042,8 @@ static int __devexit exynos_tmu_remove(struct platform_device *pdev) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); + remove_emulation_sysfs(&pdev->dev); + exynos_tmu_control(pdev, false); exynos_unregister_thermal(); -- cgit v1.2.3 From d2a73e225d113fdccd80373ad9aeb2b58b32a30b Mon Sep 17 00:00:00 2001 From: "kuninori.morimoto.gx@renesas.com" Date: Sun, 2 Dec 2012 18:48:41 -0800 Subject: thermal: rcar: add .get_trip_type/temp and .notify support This patch adds .get_trip_type(), .get_trip_temp(), and .notify() on rcar_thermal_zone_ops. Driver will try platform power OFF if it reached to critical temperature. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 68 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 90db951725da..89979ff10e27 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -22,10 +22,13 @@ #include #include #include +#include #include #include #include +#define IDLE_INTERVAL 5000 + #define THSCR 0x2c #define THSSR 0x30 @@ -176,8 +179,66 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, return 0; } +static int rcar_thermal_get_trip_type(struct thermal_zone_device *zone, + int trip, enum thermal_trip_type *type) +{ + struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + + /* see rcar_thermal_get_temp() */ + switch (trip) { + case 0: /* +90 <= temp */ + *type = THERMAL_TRIP_CRITICAL; + break; + default: + dev_err(priv->dev, "rcar driver trip error\n"); + return -EINVAL; + } + + return 0; +} + +static int rcar_thermal_get_trip_temp(struct thermal_zone_device *zone, + int trip, unsigned long *temp) +{ + struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + + /* see rcar_thermal_get_temp() */ + switch (trip) { + case 0: /* +90 <= temp */ + *temp = MCELSIUS(90); + break; + default: + dev_err(priv->dev, "rcar driver trip error\n"); + return -EINVAL; + } + + return 0; +} + +static int rcar_thermal_notify(struct thermal_zone_device *zone, + int trip, enum thermal_trip_type type) +{ + struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + + switch (type) { + case THERMAL_TRIP_CRITICAL: + /* FIXME */ + dev_warn(priv->dev, + "Thermal reached to critical temperature\n"); + machine_power_off(); + break; + default: + break; + } + + return 0; +} + static struct thermal_zone_device_ops rcar_thermal_zone_ops = { - .get_temp = rcar_thermal_get_temp, + .get_temp = rcar_thermal_get_temp, + .get_trip_type = rcar_thermal_get_trip_type, + .get_trip_temp = rcar_thermal_get_trip_temp, + .notify = rcar_thermal_notify, }; /* @@ -211,8 +272,9 @@ static int rcar_thermal_probe(struct platform_device *pdev) return -ENOMEM; } - zone = thermal_zone_device_register("rcar_thermal", 0, 0, priv, - &rcar_thermal_zone_ops, NULL, 0, 0); + zone = thermal_zone_device_register("rcar_thermal", 1, 0, priv, + &rcar_thermal_zone_ops, NULL, 0, + IDLE_INTERVAL); if (IS_ERR(zone)) { dev_err(&pdev->dev, "thermal zone device is NULL\n"); return PTR_ERR(zone); -- cgit v1.2.3 From 03b79bda8d061d80a6106e257c072a2754141bab Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 7 Dec 2012 11:29:32 +0100 Subject: drivers/thermal/spear_thermal.c: use devm_clk_get devm_clk_get allocates a resource that is released when a driver detaches. This patch uses devm_clk_get for data that is allocated in the probe function of a platform device and is only released in the remove function. Signed-off-by: Julia Lawall Signed-off-by: Zhang Rui --- drivers/thermal/spear_thermal.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c index 6b2d8b21aaee..3c5ee5607977 100644 --- a/drivers/thermal/spear_thermal.c +++ b/drivers/thermal/spear_thermal.c @@ -131,7 +131,7 @@ static int spear_thermal_probe(struct platform_device *pdev) return -ENOMEM; } - stdev->clk = clk_get(&pdev->dev, NULL); + stdev->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(stdev->clk)) { dev_err(&pdev->dev, "Can't get clock\n"); return PTR_ERR(stdev->clk); @@ -140,7 +140,7 @@ static int spear_thermal_probe(struct platform_device *pdev) ret = clk_enable(stdev->clk); if (ret) { dev_err(&pdev->dev, "Can't enable clock\n"); - goto put_clk; + return ret; } stdev->flags = val; @@ -163,8 +163,6 @@ static int spear_thermal_probe(struct platform_device *pdev) disable_clk: clk_disable(stdev->clk); -put_clk: - clk_put(stdev->clk); return ret; } @@ -183,7 +181,6 @@ static int spear_thermal_exit(struct platform_device *pdev) writel_relaxed(actual_mask & ~stdev->flags, stdev->thermal_base); clk_disable(stdev->clk); - clk_put(stdev->clk); return 0; } -- cgit v1.2.3 From caa5cbd5a18a72bb6f5a5211ddeade71fdb2282d Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 12 Dec 2012 15:54:24 +0530 Subject: thermal: exynos: Use of_match_ptr() macro This eliminates having an #ifdef returning NULL for the case when OF is disabled. Signed-off-by: Sachin Kamat Signed-off-by: Zhang Rui --- drivers/thermal/exynos_thermal.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c index e2ef8a3531ac..327102a4596a 100644 --- a/drivers/thermal/exynos_thermal.c +++ b/drivers/thermal/exynos_thermal.c @@ -808,8 +808,6 @@ static const struct of_device_id exynos_tmu_match[] = { {}, }; MODULE_DEVICE_TABLE(of, exynos_tmu_match); -#else -#define exynos_tmu_match NULL #endif static struct platform_device_id exynos_tmu_driver_ids[] = { @@ -1085,7 +1083,7 @@ static struct platform_driver exynos_tmu_driver = { .name = "exynos-tmu", .owner = THIS_MODULE, .pm = EXYNOS_TMU_PM, - .of_match_table = exynos_tmu_match, + .of_match_table = of_match_ptr(exynos_tmu_match), }, .probe = exynos_tmu_probe, .remove = __devexit_p(exynos_tmu_remove), -- cgit v1.2.3 From c313637641c3e33388903e16cfde97b2e67adb9e Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 19 Dec 2012 14:20:58 +0530 Subject: thermal: db8500: Use of_match_ptr() macro in db8500_thermal.c This eliminates having an #ifdef returning NULL for the case when OF is disabled. Signed-off-by: Sachin Kamat Reviewed-by: Hongbo Zhang Signed-off-by: Zhang Rui --- drivers/thermal/db8500_thermal.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/db8500_thermal.c b/drivers/thermal/db8500_thermal.c index ec71ade3e317..61ce60a35921 100644 --- a/drivers/thermal/db8500_thermal.c +++ b/drivers/thermal/db8500_thermal.c @@ -508,15 +508,13 @@ static const struct of_device_id db8500_thermal_match[] = { { .compatible = "stericsson,db8500-thermal" }, {}, }; -#else -#define db8500_thermal_match NULL #endif static struct platform_driver db8500_thermal_driver = { .driver = { .owner = THIS_MODULE, .name = "db8500-thermal", - .of_match_table = db8500_thermal_match, + .of_match_table = of_match_ptr(db8500_thermal_match), }, .probe = db8500_thermal_probe, .suspend = db8500_thermal_suspend, -- cgit v1.2.3 From c076fc42a4d6c51d6422aaf9f6626bf1adf983e7 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 19 Dec 2012 14:20:59 +0530 Subject: thermal: db8500: Use of_match_ptr() macro in db8500_cpufreq_cooling.c This eliminates having an #ifdef returning NULL for the case when OF is disabled. Signed-off-by: Sachin Kamat Reviewed-by: Hongbo Zhang Signed-off-by: Zhang Rui --- drivers/thermal/db8500_cpufreq_cooling.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/db8500_cpufreq_cooling.c b/drivers/thermal/db8500_cpufreq_cooling.c index 4cf8e72af90a..21419851fc02 100644 --- a/drivers/thermal/db8500_cpufreq_cooling.c +++ b/drivers/thermal/db8500_cpufreq_cooling.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -73,15 +74,13 @@ static const struct of_device_id db8500_cpufreq_cooling_match[] = { { .compatible = "stericsson,db8500-cpufreq-cooling" }, {}, }; -#else -#define db8500_cpufreq_cooling_match NULL #endif static struct platform_driver db8500_cpufreq_cooling_driver = { .driver = { .owner = THIS_MODULE, .name = "db8500-cpufreq-cooling", - .of_match_table = db8500_cpufreq_cooling_match, + .of_match_table = of_match_ptr(db8500_cpufreq_cooling_match), }, .probe = db8500_cpufreq_cooling_probe, .suspend = db8500_cpufreq_cooling_suspend, -- cgit v1.2.3 From f7ffe19a6fab14df70bd0acca9a4836aa80b15f5 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 4 Jan 2013 12:13:15 -0700 Subject: PCI: Use "unsigned long" for __pci_enable_device_flags to match ioport.h __pci_enable_device_flags() takes values like IORESOURCE_IO and IORESOURCE_MEM, which are values for struct resource.flags, which is "unsigned long", not "resource_size_t". Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 5cb5820fae40..d574fbdbf4b0 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1157,7 +1157,7 @@ int pci_reenable_device(struct pci_dev *dev) } static int __pci_enable_device_flags(struct pci_dev *dev, - resource_size_t flags) + unsigned long flags) { int err; int i, bars = 0; -- cgit v1.2.3 From b4b4fbba46a7a638878e58f303317e7904edfa13 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 4 Jan 2013 12:12:55 -0700 Subject: PCI: Drop "__" prefix on __pci_enable_device_flags() Drop the useless "__" prefix on __pci_enable_device_flags(). Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d574fbdbf4b0..a2f30394c091 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1156,8 +1156,7 @@ int pci_reenable_device(struct pci_dev *dev) return 0; } -static int __pci_enable_device_flags(struct pci_dev *dev, - unsigned long flags) +static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags) { int err; int i, bars = 0; @@ -1201,7 +1200,7 @@ static int __pci_enable_device_flags(struct pci_dev *dev, */ int pci_enable_device_io(struct pci_dev *dev) { - return __pci_enable_device_flags(dev, IORESOURCE_IO); + return pci_enable_device_flags(dev, IORESOURCE_IO); } /** @@ -1214,7 +1213,7 @@ int pci_enable_device_io(struct pci_dev *dev) */ int pci_enable_device_mem(struct pci_dev *dev) { - return __pci_enable_device_flags(dev, IORESOURCE_MEM); + return pci_enable_device_flags(dev, IORESOURCE_MEM); } /** @@ -1230,7 +1229,7 @@ int pci_enable_device_mem(struct pci_dev *dev) */ int pci_enable_device(struct pci_dev *dev) { - return __pci_enable_device_flags(dev, IORESOURCE_MEM | IORESOURCE_IO); + return pci_enable_device_flags(dev, IORESOURCE_MEM | IORESOURCE_IO); } /* -- cgit v1.2.3 From 8303dc9952758ab3060a3ee9a19ecb6fec83c600 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jan 2013 17:27:47 -0300 Subject: [media] em28xx: initialize button/I2C IR earlier The em28xx-input is used by 3 different types of input devices: - devices with buttons (like cameras and grabber devices); - devices with I2C remotes; - em2860 or latter chips with RC support embedded. When the device has an I2C remote, all it needs to do is to call the proper I2C driver (ir-i2c-kbd), passing the proper data to it, and just leave the code. Also, button devices have its own init code that doesn't depend on having an IR or not (as a general rule, they don't have). So, move its init code to fix bugs introduced by earlier patches that prevent them to work. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 3598221378ac..2a1b3d277db1 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -590,6 +590,17 @@ static int em28xx_ir_init(struct em28xx *dev) int err = -ENOMEM; u64 rc_type; + if (dev->board.has_snapshot_button) + em28xx_register_snapshot_button(dev); + + if (dev->board.has_ir_i2c) { + em28xx_register_i2c_ir(dev); +#if defined(CONFIG_MODULES) && defined(MODULE) + request_module("ir-kbd-i2c"); +#endif + return 0; + } + if (dev->board.ir_codes == NULL) { /* No remote control support */ em28xx_warn("Remote control support is not available for " @@ -663,15 +674,6 @@ static int em28xx_ir_init(struct em28xx *dev) if (err) goto error; - em28xx_register_i2c_ir(dev); - -#if defined(CONFIG_MODULES) && defined(MODULE) - if (dev->board.has_ir_i2c) - request_module("ir-kbd-i2c"); -#endif - if (dev->board.has_snapshot_button) - em28xx_register_snapshot_button(dev); - return 0; error: -- cgit v1.2.3 From 728f9778e273a11a65926ac21574e6ca8d911ebf Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jan 2013 17:32:58 -0300 Subject: [media] em28xx: autoload em28xx-rc if the device has an I2C IR If the device has an I2C IR, em28xx-rc should be loaded by default, except if the user explicitly requested to not load, via modprobe option. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index f5cac47d099a..e17be073c0c3 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2912,7 +2912,7 @@ static void request_module_async(struct work_struct *work) if (dev->board.has_dvb) request_module("em28xx-dvb"); - if (dev->board.ir_codes && !disable_ir) + if ((dev->board.ir_codes || dev->board.has_ir_i2c) && !disable_ir) request_module("em28xx-rc"); #endif /* CONFIG_MODULES */ } -- cgit v1.2.3 From d40580e7c043cdfbf472e76052a4606fea16642d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jan 2013 17:37:26 -0300 Subject: [media] em28xx: simplify IR names on I2C devices The ir-i2c-kbd already adds I2C IR before the name. The way it is, the devices are named as: "i2c IR (i2c IR (EM2840 Hauppaug" With is ugly and incorrect. After this patch, it is now properly displayed as: "i2c IR (WinTV USB2)" Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 2a1b3d277db1..ebbb0aa51dfa 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -472,22 +472,22 @@ static void em28xx_register_i2c_ir(struct em28xx *dev) case EM2820_BOARD_TERRATEC_CINERGY_250: dev->init_data.ir_codes = RC_MAP_EM_TERRATEC; dev->init_data.get_key = em28xx_get_key_terratec; - dev->init_data.name = "i2c IR (EM28XX Terratec)"; + dev->init_data.name = "Terratec Cinergy 200/250"; break; case EM2820_BOARD_PINNACLE_USB_2: dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; - dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; + dev->init_data.name = "Pinnacle USB2"; break; case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; dev->init_data.get_key = em28xx_get_key_em_haup; - dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; + dev->init_data.name = "WinTV USB2"; break; case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE; dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; - dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)"; + dev->init_data.name = "Winfast TV USBII Deluxe"; break; } -- cgit v1.2.3 From 3ac693c40a08703f3c456d8f45940a48c1f8d93f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jan 2013 18:01:59 -0300 Subject: [media] em28xx: tell ir-kbd-i2c that WinTV uses an RC5 protocol Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index ebbb0aa51dfa..5b3292c8310b 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -483,6 +483,7 @@ static void em28xx_register_i2c_ir(struct em28xx *dev) dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; dev->init_data.get_key = em28xx_get_key_em_haup; dev->init_data.name = "WinTV USB2"; + dev->init_data.type = RC_BIT_RC5; break; case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE; -- cgit v1.2.3 From d1a6f4f19728d6e90480e53601a90fc9f6a348ad Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Fri, 4 Jan 2013 19:49:31 -0500 Subject: block: delete super ancient PC-XT driver for 1980's hardware This driver was for the 8 bit ISA cards that were installed in the PC-XT machines of 1980 vintage. They supported the dual ribbon cable MFM drives of 10-20MB capacity, and ran at a 3:1 interleave, giving performance on the order of 128kB/s. By the introduction of the PC-AT (286) these controllers were already scrapped in favour of 16 bit controllers with some onboard RAM that could support a 1:1 interleave. The git history doesn't show any evidence of runtime fixes that would reflect active usage; instead just the usual tree-wide API type changes/cleanups. Going back to in-source changelogs, the last "runtime" fix that is evident is something I did over a dozen years ago[1] -- and even back then, the hardware was long since unavailable, so that ancient fix was also not runtime tested. The time is long overdue for this to get flushed, so lets get rid of it before anyone wastes more time doing builds and sparse checks etc. on long since dead code. [1] http://lkml.indiana.edu/hypermail/linux/kernel/0102.2/0027.html Signed-off-by: Paul Gortmaker --- drivers/block/Kconfig | 13 - drivers/block/Makefile | 1 - drivers/block/xd.c | 1123 ------------------------------------------------ drivers/block/xd.h | 134 ------ 4 files changed, 1271 deletions(-) delete mode 100644 drivers/block/xd.c delete mode 100644 drivers/block/xd.h (limited to 'drivers') diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 824e09c4d0d7..e29a44e78e15 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -63,19 +63,6 @@ config AMIGA_Z2RAM To compile this driver as a module, choose M here: the module will be called z2ram. -config BLK_DEV_XD - tristate "XT hard disk support" - depends on ISA && ISA_DMA_API - select CHECK_SIGNATURE - help - Very old 8 bit hard disk controllers used in the IBM XT computer - will be supported if you say Y here. - - To compile this driver as a module, choose M here: the - module will be called xd. - - It's pretty unlikely that you have one of these: say N. - config GDROM tristate "SEGA Dreamcast GD-ROM drive" depends on SH_DREAMCAST diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 17e82df3df74..5195c1f61b2a 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o obj-$(CONFIG_AMIGA_Z2RAM) += z2ram.o obj-$(CONFIG_BLK_DEV_RAM) += brd.o obj-$(CONFIG_BLK_DEV_LOOP) += loop.o -obj-$(CONFIG_BLK_DEV_XD) += xd.o obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o diff --git a/drivers/block/xd.c b/drivers/block/xd.c deleted file mode 100644 index ff540520bada..000000000000 --- a/drivers/block/xd.c +++ /dev/null @@ -1,1123 +0,0 @@ -/* - * This file contains the driver for an XT hard disk controller - * (at least the DTC 5150X) for Linux. - * - * Author: Pat Mackinlay, pat@it.com.au - * Date: 29/09/92 - * - * Revised: 01/01/93, ... - * - * Ref: DTC 5150X Controller Specification (thanks to Kevin Fowler, - * kevinf@agora.rain.com) - * Also thanks to: Salvador Abreu, Dave Thaler, Risto Kankkunen and - * Wim Van Dorst. - * - * Revised: 04/04/94 by Risto Kankkunen - * Moved the detection code from xd_init() to xd_geninit() as it needed - * interrupts enabled and Linus didn't want to enable them in that first - * phase. xd_geninit() is the place to do these kinds of things anyway, - * he says. - * - * Modularized: 04/10/96 by Todd Fries, tfries@umr.edu - * - * Revised: 13/12/97 by Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl - * Fixed some problems with disk initialization and module initiation. - * Added support for manual geometry setting (except Seagate controllers) - * in form: - * xd_geo=,,[,,,] - * Recovered DMA access. Abridged messages. Added support for DTC5051CX, - * WD1002-27X & XEBEC controllers. Driver uses now some jumper settings. - * Extended ioctl() support. - * - * Bugfix: 15/02/01, Paul G. - inform queue layer of tiny xd_maxsect. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "xd.h" - -static DEFINE_MUTEX(xd_mutex); -static void __init do_xd_setup (int *integers); -#ifdef MODULE -static int xd[5] = { -1,-1,-1,-1, }; -#endif - -#define XD_DONT_USE_DMA 0 /* Initial value. may be overriden using - "nodma" module option */ -#define XD_INIT_DISK_DELAY (30) /* 30 ms delay during disk initialization */ - -/* Above may need to be increased if a problem with the 2nd drive detection - (ST11M controller) or resetting a controller (WD) appears */ - -static XD_INFO xd_info[XD_MAXDRIVES]; - -/* If you try this driver and find that your card is not detected by the driver at bootup, you need to add your BIOS - signature and details to the following list of signatures. A BIOS signature is a string embedded into the first - few bytes of your controller's on-board ROM BIOS. To find out what yours is, use something like MS-DOS's DEBUG - command. Run DEBUG, and then you can examine your BIOS signature with: - - d xxxx:0000 - - where xxxx is the segment of your controller (like C800 or D000 or something). On the ASCII dump at the right, you should - be able to see a string mentioning the manufacturer's copyright etc. Add this string into the table below. The parameters - in the table are, in order: - - offset ; this is the offset (in bytes) from the start of your ROM where the signature starts - signature ; this is the actual text of the signature - xd_?_init_controller ; this is the controller init routine used by your controller - xd_?_init_drive ; this is the drive init routine used by your controller - - The controllers directly supported at the moment are: DTC 5150x, WD 1004A27X, ST11M/R and override. If your controller is - made by the same manufacturer as one of these, try using the same init routines as they do. If that doesn't work, your - best bet is to use the "override" routines. These routines use a "portable" method of getting the disk's geometry, and - may work with your card. If none of these seem to work, try sending me some email and I'll see what I can do . - - NOTE: You can now specify your XT controller's parameters from the command line in the form xd=TYPE,IRQ,IO,DMA. The driver - should be able to detect your drive's geometry from this info. (eg: xd=0,5,0x320,3 is the "standard"). */ - -#include -#define xd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL,get_order(size)) -#define xd_dma_mem_free(addr, size) free_pages(addr, get_order(size)) -static char *xd_dma_buffer; - -static XD_SIGNATURE xd_sigs[] __initdata = { - { 0x0000,"Override geometry handler",NULL,xd_override_init_drive,"n unknown" }, /* Pat Mackinlay, pat@it.com.au */ - { 0x0008,"[BXD06 (C) DTC 17-MAY-1985]",xd_dtc_init_controller,xd_dtc5150cx_init_drive," DTC 5150CX" }, /* Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl */ - { 0x000B,"CRD18A Not an IBM rom. (C) Copyright Data Technology Corp. 05/31/88",xd_dtc_init_controller,xd_dtc_init_drive," DTC 5150X" }, /* Todd Fries, tfries@umr.edu */ - { 0x000B,"CXD23A Not an IBM ROM (C)Copyright Data Technology Corp 12/03/88",xd_dtc_init_controller,xd_dtc_init_drive," DTC 5150X" }, /* Pat Mackinlay, pat@it.com.au */ - { 0x0008,"07/15/86(C) Copyright 1986 Western Digital Corp.",xd_wd_init_controller,xd_wd_init_drive," Western Dig. 1002-27X" }, /* Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl */ - { 0x0008,"06/24/88(C) Copyright 1988 Western Digital Corp.",xd_wd_init_controller,xd_wd_init_drive," Western Dig. WDXT-GEN2" }, /* Dan Newcombe, newcombe@aa.csc.peachnet.edu */ - { 0x0015,"SEAGATE ST11 BIOS REVISION",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11M/R" }, /* Salvador Abreu, spa@fct.unl.pt */ - { 0x0010,"ST11R BIOS",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11M/R" }, /* Risto Kankkunen, risto.kankkunen@cs.helsinki.fi */ - { 0x0010,"ST11 BIOS v1.7",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11R" }, /* Alan Hourihane, alanh@fairlite.demon.co.uk */ - { 0x1000,"(c)Copyright 1987 SMS",xd_omti_init_controller,xd_omti_init_drive,"n OMTI 5520" }, /* Dirk Melchers, dirk@merlin.nbg.sub.org */ - { 0x0006,"COPYRIGHT XEBEC (C) 1984",xd_xebec_init_controller,xd_xebec_init_drive," XEBEC" }, /* Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl */ - { 0x0008,"(C) Copyright 1984 Western Digital Corp", xd_wd_init_controller, xd_wd_init_drive," Western Dig. 1002s-wx2" }, - { 0x0008,"(C) Copyright 1986 Western Digital Corporation", xd_wd_init_controller, xd_wd_init_drive," 1986 Western Digital" }, /* jfree@sovereign.org */ -}; - -static unsigned int xd_bases[] __initdata = -{ - 0xC8000, 0xCA000, 0xCC000, - 0xCE000, 0xD0000, 0xD2000, - 0xD4000, 0xD6000, 0xD8000, - 0xDA000, 0xDC000, 0xDE000, - 0xE0000 -}; - -static DEFINE_SPINLOCK(xd_lock); - -static struct gendisk *xd_gendisk[2]; - -static int xd_getgeo(struct block_device *bdev, struct hd_geometry *geo); - -static const struct block_device_operations xd_fops = { - .owner = THIS_MODULE, - .ioctl = xd_ioctl, - .getgeo = xd_getgeo, -}; -static DECLARE_WAIT_QUEUE_HEAD(xd_wait_int); -static u_char xd_drives, xd_irq = 5, xd_dma = 3, xd_maxsectors; -static u_char xd_override __initdata = 0, xd_type __initdata = 0; -static u_short xd_iobase = 0x320; -static int xd_geo[XD_MAXDRIVES*3] __initdata = { 0, }; - -static volatile int xdc_busy; -static struct timer_list xd_watchdog_int; - -static volatile u_char xd_error; -static bool nodma = XD_DONT_USE_DMA; - -static struct request_queue *xd_queue; - -/* xd_init: register the block device number and set up pointer tables */ -static int __init xd_init(void) -{ - u_char i,controller; - unsigned int address; - int err; - -#ifdef MODULE - { - u_char count = 0; - for (i = 4; i > 0; i--) - if (((xd[i] = xd[i-1]) >= 0) && !count) - count = i; - if ((xd[0] = count)) - do_xd_setup(xd); - } -#endif - - init_timer (&xd_watchdog_int); xd_watchdog_int.function = xd_watchdog; - - err = -EBUSY; - if (register_blkdev(XT_DISK_MAJOR, "xd")) - goto out1; - - err = -ENOMEM; - xd_queue = blk_init_queue(do_xd_request, &xd_lock); - if (!xd_queue) - goto out1a; - - if (xd_detect(&controller,&address)) { - - printk("Detected a%s controller (type %d) at address %06x\n", - xd_sigs[controller].name,controller,address); - if (!request_region(xd_iobase,4,"xd")) { - printk("xd: Ports at 0x%x are not available\n", - xd_iobase); - goto out2; - } - if (controller) - xd_sigs[controller].init_controller(address); - xd_drives = xd_initdrives(xd_sigs[controller].init_drive); - - printk("Detected %d hard drive%s (using IRQ%d & DMA%d)\n", - xd_drives,xd_drives == 1 ? "" : "s",xd_irq,xd_dma); - } - - /* - * With the drive detected, xd_maxsectors should now be known. - * If xd_maxsectors is 0, nothing was detected and we fall through - * to return -ENODEV - */ - if (!xd_dma_buffer && xd_maxsectors) { - xd_dma_buffer = (char *)xd_dma_mem_alloc(xd_maxsectors * 0x200); - if (!xd_dma_buffer) { - printk(KERN_ERR "xd: Out of memory.\n"); - goto out3; - } - } - - err = -ENODEV; - if (!xd_drives) - goto out3; - - for (i = 0; i < xd_drives; i++) { - XD_INFO *p = &xd_info[i]; - struct gendisk *disk = alloc_disk(64); - if (!disk) - goto Enomem; - p->unit = i; - disk->major = XT_DISK_MAJOR; - disk->first_minor = i<<6; - sprintf(disk->disk_name, "xd%c", i+'a'); - disk->fops = &xd_fops; - disk->private_data = p; - disk->queue = xd_queue; - set_capacity(disk, p->heads * p->cylinders * p->sectors); - printk(" %s: CHS=%d/%d/%d\n", disk->disk_name, - p->cylinders, p->heads, p->sectors); - xd_gendisk[i] = disk; - } - - err = -EBUSY; - if (request_irq(xd_irq,xd_interrupt_handler, 0, "XT hard disk", NULL)) { - printk("xd: unable to get IRQ%d\n",xd_irq); - goto out4; - } - - if (request_dma(xd_dma,"xd")) { - printk("xd: unable to get DMA%d\n",xd_dma); - goto out5; - } - - /* xd_maxsectors depends on controller - so set after detection */ - blk_queue_max_hw_sectors(xd_queue, xd_maxsectors); - - for (i = 0; i < xd_drives; i++) - add_disk(xd_gendisk[i]); - - return 0; - -out5: - free_irq(xd_irq, NULL); -out4: - for (i = 0; i < xd_drives; i++) - put_disk(xd_gendisk[i]); -out3: - if (xd_maxsectors) - release_region(xd_iobase,4); - - if (xd_dma_buffer) - xd_dma_mem_free((unsigned long)xd_dma_buffer, - xd_maxsectors * 0x200); -out2: - blk_cleanup_queue(xd_queue); -out1a: - unregister_blkdev(XT_DISK_MAJOR, "xd"); -out1: - return err; -Enomem: - err = -ENOMEM; - while (i--) - put_disk(xd_gendisk[i]); - goto out3; -} - -/* xd_detect: scan the possible BIOS ROM locations for the signature strings */ -static u_char __init xd_detect (u_char *controller, unsigned int *address) -{ - int i, j; - - if (xd_override) - { - *controller = xd_type; - *address = 0; - return(1); - } - - for (i = 0; i < ARRAY_SIZE(xd_bases); i++) { - void __iomem *p = ioremap(xd_bases[i], 0x2000); - if (!p) - continue; - for (j = 1; j < ARRAY_SIZE(xd_sigs); j++) { - const char *s = xd_sigs[j].string; - if (check_signature(p + xd_sigs[j].offset, s, strlen(s))) { - *controller = j; - xd_type = j; - *address = xd_bases[i]; - iounmap(p); - return 1; - } - } - iounmap(p); - } - return 0; -} - -/* do_xd_request: handle an incoming request */ -static void do_xd_request (struct request_queue * q) -{ - struct request *req; - - if (xdc_busy) - return; - - req = blk_fetch_request(q); - while (req) { - unsigned block = blk_rq_pos(req); - unsigned count = blk_rq_cur_sectors(req); - XD_INFO *disk = req->rq_disk->private_data; - int res = -EIO; - int retry; - - if (req->cmd_type != REQ_TYPE_FS) - goto done; - if (block + count > get_capacity(req->rq_disk)) - goto done; - for (retry = 0; (retry < XD_RETRIES) && !res; retry++) - res = xd_readwrite(rq_data_dir(req), disk, req->buffer, - block, count); - done: - /* wrap up, 0 = success, -errno = fail */ - if (!__blk_end_request_cur(req, res)) - req = blk_fetch_request(q); - } -} - -static int xd_getgeo(struct block_device *bdev, struct hd_geometry *geo) -{ - XD_INFO *p = bdev->bd_disk->private_data; - - geo->heads = p->heads; - geo->sectors = p->sectors; - geo->cylinders = p->cylinders; - return 0; -} - -/* xd_ioctl: handle device ioctl's */ -static int xd_locked_ioctl(struct block_device *bdev, fmode_t mode, u_int cmd, u_long arg) -{ - switch (cmd) { - case HDIO_SET_DMA: - if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (xdc_busy) return -EBUSY; - nodma = !arg; - if (nodma && xd_dma_buffer) { - xd_dma_mem_free((unsigned long)xd_dma_buffer, - xd_maxsectors * 0x200); - xd_dma_buffer = NULL; - } else if (!nodma && !xd_dma_buffer) { - xd_dma_buffer = (char *)xd_dma_mem_alloc(xd_maxsectors * 0x200); - if (!xd_dma_buffer) { - nodma = XD_DONT_USE_DMA; - return -ENOMEM; - } - } - return 0; - case HDIO_GET_DMA: - return put_user(!nodma, (long __user *) arg); - case HDIO_GET_MULTCOUNT: - return put_user(xd_maxsectors, (long __user *) arg); - default: - return -EINVAL; - } -} - -static int xd_ioctl(struct block_device *bdev, fmode_t mode, - unsigned int cmd, unsigned long param) -{ - int ret; - - mutex_lock(&xd_mutex); - ret = xd_locked_ioctl(bdev, mode, cmd, param); - mutex_unlock(&xd_mutex); - - return ret; -} - -/* xd_readwrite: handle a read/write request */ -static int xd_readwrite (u_char operation,XD_INFO *p,char *buffer,u_int block,u_int count) -{ - int drive = p->unit; - u_char cmdblk[6],sense[4]; - u_short track,cylinder; - u_char head,sector,control,mode = PIO_MODE,temp; - char **real_buffer; - register int i; - -#ifdef DEBUG_READWRITE - printk("xd_readwrite: operation = %s, drive = %d, buffer = 0x%X, block = %d, count = %d\n",operation == READ ? "read" : "write",drive,buffer,block,count); -#endif /* DEBUG_READWRITE */ - - spin_unlock_irq(&xd_lock); - - control = p->control; - if (!xd_dma_buffer) - xd_dma_buffer = (char *)xd_dma_mem_alloc(xd_maxsectors * 0x200); - while (count) { - temp = count < xd_maxsectors ? count : xd_maxsectors; - - track = block / p->sectors; - head = track % p->heads; - cylinder = track / p->heads; - sector = block % p->sectors; - -#ifdef DEBUG_READWRITE - printk("xd_readwrite: drive = %d, head = %d, cylinder = %d, sector = %d, count = %d\n",drive,head,cylinder,sector,temp); -#endif /* DEBUG_READWRITE */ - - if (xd_dma_buffer) { - mode = xd_setup_dma(operation == READ ? DMA_MODE_READ : DMA_MODE_WRITE,(u_char *)(xd_dma_buffer),temp * 0x200); - real_buffer = &xd_dma_buffer; - for (i=0; i < (temp * 0x200); i++) - xd_dma_buffer[i] = buffer[i]; - } - else - real_buffer = &buffer; - - xd_build(cmdblk,operation == READ ? CMD_READ : CMD_WRITE,drive,head,cylinder,sector,temp & 0xFF,control); - - switch (xd_command(cmdblk,mode,(u_char *)(*real_buffer),(u_char *)(*real_buffer),sense,XD_TIMEOUT)) { - case 1: - printk("xd%c: %s timeout, recalibrating drive\n",'a'+drive,(operation == READ ? "read" : "write")); - xd_recalibrate(drive); - spin_lock_irq(&xd_lock); - return -EIO; - case 2: - if (sense[0] & 0x30) { - printk("xd%c: %s - ",'a'+drive,(operation == READ ? "reading" : "writing")); - switch ((sense[0] & 0x30) >> 4) { - case 0: printk("drive error, code = 0x%X",sense[0] & 0x0F); - break; - case 1: printk("controller error, code = 0x%X",sense[0] & 0x0F); - break; - case 2: printk("command error, code = 0x%X",sense[0] & 0x0F); - break; - case 3: printk("miscellaneous error, code = 0x%X",sense[0] & 0x0F); - break; - } - } - if (sense[0] & 0x80) - printk(" - CHS = %d/%d/%d\n",((sense[2] & 0xC0) << 2) | sense[3],sense[1] & 0x1F,sense[2] & 0x3F); - /* reported drive number = (sense[1] & 0xE0) >> 5 */ - else - printk(" - no valid disk address\n"); - spin_lock_irq(&xd_lock); - return -EIO; - } - if (xd_dma_buffer) - for (i=0; i < (temp * 0x200); i++) - buffer[i] = xd_dma_buffer[i]; - - count -= temp, buffer += temp * 0x200, block += temp; - } - spin_lock_irq(&xd_lock); - return 0; -} - -/* xd_recalibrate: recalibrate a given drive and reset controller if necessary */ -static void xd_recalibrate (u_char drive) -{ - u_char cmdblk[6]; - - xd_build(cmdblk,CMD_RECALIBRATE,drive,0,0,0,0,0); - if (xd_command(cmdblk,PIO_MODE,NULL,NULL,NULL,XD_TIMEOUT * 8)) - printk("xd%c: warning! error recalibrating, controller may be unstable\n", 'a'+drive); -} - -/* xd_interrupt_handler: interrupt service routine */ -static irqreturn_t xd_interrupt_handler(int irq, void *dev_id) -{ - if (inb(XD_STATUS) & STAT_INTERRUPT) { /* check if it was our device */ -#ifdef DEBUG_OTHER - printk("xd_interrupt_handler: interrupt detected\n"); -#endif /* DEBUG_OTHER */ - outb(0,XD_CONTROL); /* acknowledge interrupt */ - wake_up(&xd_wait_int); /* and wake up sleeping processes */ - return IRQ_HANDLED; - } - else - printk("xd: unexpected interrupt\n"); - return IRQ_NONE; -} - -/* xd_setup_dma: set up the DMA controller for a data transfer */ -static u_char xd_setup_dma (u_char mode,u_char *buffer,u_int count) -{ - unsigned long f; - - if (nodma) - return (PIO_MODE); - if (((unsigned long) buffer & 0xFFFF0000) != (((unsigned long) buffer + count) & 0xFFFF0000)) { -#ifdef DEBUG_OTHER - printk("xd_setup_dma: using PIO, transfer overlaps 64k boundary\n"); -#endif /* DEBUG_OTHER */ - return (PIO_MODE); - } - - f=claim_dma_lock(); - disable_dma(xd_dma); - clear_dma_ff(xd_dma); - set_dma_mode(xd_dma,mode); - set_dma_addr(xd_dma, (unsigned long) buffer); - set_dma_count(xd_dma,count); - - release_dma_lock(f); - - return (DMA_MODE); /* use DMA and INT */ -} - -/* xd_build: put stuff into an array in a format suitable for the controller */ -static u_char *xd_build (u_char *cmdblk,u_char command,u_char drive,u_char head,u_short cylinder,u_char sector,u_char count,u_char control) -{ - cmdblk[0] = command; - cmdblk[1] = ((drive & 0x07) << 5) | (head & 0x1F); - cmdblk[2] = ((cylinder & 0x300) >> 2) | (sector & 0x3F); - cmdblk[3] = cylinder & 0xFF; - cmdblk[4] = count; - cmdblk[5] = control; - - return (cmdblk); -} - -static void xd_watchdog (unsigned long unused) -{ - xd_error = 1; - wake_up(&xd_wait_int); -} - -/* xd_waitport: waits until port & mask == flags or a timeout occurs. return 1 for a timeout */ -static inline u_char xd_waitport (u_short port,u_char flags,u_char mask,u_long timeout) -{ - u_long expiry = jiffies + timeout; - int success; - - xdc_busy = 1; - while ((success = ((inb(port) & mask) != flags)) && time_before(jiffies, expiry)) - schedule_timeout_uninterruptible(1); - xdc_busy = 0; - return (success); -} - -static inline u_int xd_wait_for_IRQ (void) -{ - unsigned long flags; - xd_watchdog_int.expires = jiffies + 8 * HZ; - add_timer(&xd_watchdog_int); - - flags=claim_dma_lock(); - enable_dma(xd_dma); - release_dma_lock(flags); - - sleep_on(&xd_wait_int); - del_timer(&xd_watchdog_int); - xdc_busy = 0; - - flags=claim_dma_lock(); - disable_dma(xd_dma); - release_dma_lock(flags); - - if (xd_error) { - printk("xd: missed IRQ - command aborted\n"); - xd_error = 0; - return (1); - } - return (0); -} - -/* xd_command: handle all data transfers necessary for a single command */ -static u_int xd_command (u_char *command,u_char mode,u_char *indata,u_char *outdata,u_char *sense,u_long timeout) -{ - u_char cmdblk[6],csb,complete = 0; - -#ifdef DEBUG_COMMAND - printk("xd_command: command = 0x%X, mode = 0x%X, indata = 0x%X, outdata = 0x%X, sense = 0x%X\n",command,mode,indata,outdata,sense); -#endif /* DEBUG_COMMAND */ - - outb(0,XD_SELECT); - outb(mode,XD_CONTROL); - - if (xd_waitport(XD_STATUS,STAT_SELECT,STAT_SELECT,timeout)) - return (1); - - while (!complete) { - if (xd_waitport(XD_STATUS,STAT_READY,STAT_READY,timeout)) - return (1); - - switch (inb(XD_STATUS) & (STAT_COMMAND | STAT_INPUT)) { - case 0: - if (mode == DMA_MODE) { - if (xd_wait_for_IRQ()) - return (1); - } else - outb(outdata ? *outdata++ : 0,XD_DATA); - break; - case STAT_INPUT: - if (mode == DMA_MODE) { - if (xd_wait_for_IRQ()) - return (1); - } else - if (indata) - *indata++ = inb(XD_DATA); - else - inb(XD_DATA); - break; - case STAT_COMMAND: - outb(command ? *command++ : 0,XD_DATA); - break; - case STAT_COMMAND | STAT_INPUT: - complete = 1; - break; - } - } - csb = inb(XD_DATA); - - if (xd_waitport(XD_STATUS,0,STAT_SELECT,timeout)) /* wait until deselected */ - return (1); - - if (csb & CSB_ERROR) { /* read sense data if error */ - xd_build(cmdblk,CMD_SENSE,(csb & CSB_LUN) >> 5,0,0,0,0,0); - if (xd_command(cmdblk,0,sense,NULL,NULL,XD_TIMEOUT)) - printk("xd: warning! sense command failed!\n"); - } - -#ifdef DEBUG_COMMAND - printk("xd_command: completed with csb = 0x%X\n",csb); -#endif /* DEBUG_COMMAND */ - - return (csb & CSB_ERROR); -} - -static u_char __init xd_initdrives (void (*init_drive)(u_char drive)) -{ - u_char cmdblk[6],i,count = 0; - - for (i = 0; i < XD_MAXDRIVES; i++) { - xd_build(cmdblk,CMD_TESTREADY,i,0,0,0,0,0); - if (!xd_command(cmdblk,PIO_MODE,NULL,NULL,NULL,XD_TIMEOUT*8)) { - msleep_interruptible(XD_INIT_DISK_DELAY); - - init_drive(count); - count++; - - msleep_interruptible(XD_INIT_DISK_DELAY); - } - } - return (count); -} - -static void __init xd_manual_geo_set (u_char drive) -{ - xd_info[drive].heads = (u_char)(xd_geo[3 * drive + 1]); - xd_info[drive].cylinders = (u_short)(xd_geo[3 * drive]); - xd_info[drive].sectors = (u_char)(xd_geo[3 * drive + 2]); -} - -static void __init xd_dtc_init_controller (unsigned int address) -{ - switch (address) { - case 0x00000: - case 0xC8000: break; /*initial: 0x320 */ - case 0xCA000: xd_iobase = 0x324; - case 0xD0000: /*5150CX*/ - case 0xD8000: break; /*5150CX & 5150XL*/ - default: printk("xd_dtc_init_controller: unsupported BIOS address %06x\n",address); - break; - } - xd_maxsectors = 0x01; /* my card seems to have trouble doing multi-block transfers? */ - - outb(0,XD_RESET); /* reset the controller */ -} - - -static void __init xd_dtc5150cx_init_drive (u_char drive) -{ - /* values from controller's BIOS - BIOS chip may be removed */ - static u_short geometry_table[][4] = { - {0x200,8,0x200,0x100}, - {0x267,2,0x267,0x267}, - {0x264,4,0x264,0x80}, - {0x132,4,0x132,0x0}, - {0x132,2,0x80, 0x132}, - {0x177,8,0x177,0x0}, - {0x132,8,0x84, 0x0}, - {}, /* not used */ - {0x132,6,0x80, 0x100}, - {0x200,6,0x100,0x100}, - {0x264,2,0x264,0x80}, - {0x280,4,0x280,0x100}, - {0x2B9,3,0x2B9,0x2B9}, - {0x2B9,5,0x2B9,0x2B9}, - {0x280,6,0x280,0x100}, - {0x132,4,0x132,0x0}}; - u_char n; - - n = inb(XD_JUMPER); - n = (drive ? n : (n >> 2)) & 0x33; - n = (n | (n >> 2)) & 0x0F; - if (xd_geo[3*drive]) - xd_manual_geo_set(drive); - else - if (n != 7) { - xd_info[drive].heads = (u_char)(geometry_table[n][1]); /* heads */ - xd_info[drive].cylinders = geometry_table[n][0]; /* cylinders */ - xd_info[drive].sectors = 17; /* sectors */ -#if 0 - xd_info[drive].rwrite = geometry_table[n][2]; /* reduced write */ - xd_info[drive].precomp = geometry_table[n][3] /* write precomp */ - xd_info[drive].ecc = 0x0B; /* ecc length */ -#endif /* 0 */ - } - else { - printk("xd%c: undetermined drive geometry\n",'a'+drive); - return; - } - xd_info[drive].control = 5; /* control byte */ - xd_setparam(CMD_DTCSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,geometry_table[n][2],geometry_table[n][3],0x0B); - xd_recalibrate(drive); -} - -static void __init xd_dtc_init_drive (u_char drive) -{ - u_char cmdblk[6],buf[64]; - - xd_build(cmdblk,CMD_DTCGETGEOM,drive,0,0,0,0,0); - if (!xd_command(cmdblk,PIO_MODE,buf,NULL,NULL,XD_TIMEOUT * 2)) { - xd_info[drive].heads = buf[0x0A]; /* heads */ - xd_info[drive].cylinders = ((u_short *) (buf))[0x04]; /* cylinders */ - xd_info[drive].sectors = 17; /* sectors */ - if (xd_geo[3*drive]) - xd_manual_geo_set(drive); -#if 0 - xd_info[drive].rwrite = ((u_short *) (buf + 1))[0x05]; /* reduced write */ - xd_info[drive].precomp = ((u_short *) (buf + 1))[0x06]; /* write precomp */ - xd_info[drive].ecc = buf[0x0F]; /* ecc length */ -#endif /* 0 */ - xd_info[drive].control = 0; /* control byte */ - - xd_setparam(CMD_DTCSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,((u_short *) (buf + 1))[0x05],((u_short *) (buf + 1))[0x06],buf[0x0F]); - xd_build(cmdblk,CMD_DTCSETSTEP,drive,0,0,0,0,7); - if (xd_command(cmdblk,PIO_MODE,NULL,NULL,NULL,XD_TIMEOUT * 2)) - printk("xd_dtc_init_drive: error setting step rate for xd%c\n", 'a'+drive); - } - else - printk("xd_dtc_init_drive: error reading geometry for xd%c\n", 'a'+drive); -} - -static void __init xd_wd_init_controller (unsigned int address) -{ - switch (address) { - case 0x00000: - case 0xC8000: break; /*initial: 0x320 */ - case 0xCA000: xd_iobase = 0x324; break; - case 0xCC000: xd_iobase = 0x328; break; - case 0xCE000: xd_iobase = 0x32C; break; - case 0xD0000: xd_iobase = 0x328; break; /* ? */ - case 0xD8000: xd_iobase = 0x32C; break; /* ? */ - default: printk("xd_wd_init_controller: unsupported BIOS address %06x\n",address); - break; - } - xd_maxsectors = 0x01; /* this one doesn't wrap properly either... */ - - outb(0,XD_RESET); /* reset the controller */ - - msleep(XD_INIT_DISK_DELAY); -} - -static void __init xd_wd_init_drive (u_char drive) -{ - /* values from controller's BIOS - BIOS may be disabled */ - static u_short geometry_table[][4] = { - {0x264,4,0x1C2,0x1C2}, /* common part */ - {0x132,4,0x099,0x0}, - {0x267,2,0x1C2,0x1C2}, - {0x267,4,0x1C2,0x1C2}, - - {0x334,6,0x335,0x335}, /* 1004 series RLL */ - {0x30E,4,0x30F,0x3DC}, - {0x30E,2,0x30F,0x30F}, - {0x267,4,0x268,0x268}, - - {0x3D5,5,0x3D6,0x3D6}, /* 1002 series RLL */ - {0x3DB,7,0x3DC,0x3DC}, - {0x264,4,0x265,0x265}, - {0x267,4,0x268,0x268}}; - - u_char cmdblk[6],buf[0x200]; - u_char n = 0,rll,jumper_state,use_jumper_geo; - u_char wd_1002 = (xd_sigs[xd_type].string[7] == '6'); - - jumper_state = ~(inb(0x322)); - if (jumper_state & 0x40) - xd_irq = 9; - rll = (jumper_state & 0x30) ? (0x04 << wd_1002) : 0; - xd_build(cmdblk,CMD_READ,drive,0,0,0,1,0); - if (!xd_command(cmdblk,PIO_MODE,buf,NULL,NULL,XD_TIMEOUT * 2)) { - xd_info[drive].heads = buf[0x1AF]; /* heads */ - xd_info[drive].cylinders = ((u_short *) (buf + 1))[0xD6]; /* cylinders */ - xd_info[drive].sectors = 17; /* sectors */ - if (xd_geo[3*drive]) - xd_manual_geo_set(drive); -#if 0 - xd_info[drive].rwrite = ((u_short *) (buf))[0xD8]; /* reduced write */ - xd_info[drive].wprecomp = ((u_short *) (buf))[0xDA]; /* write precomp */ - xd_info[drive].ecc = buf[0x1B4]; /* ecc length */ -#endif /* 0 */ - xd_info[drive].control = buf[0x1B5]; /* control byte */ - use_jumper_geo = !(xd_info[drive].heads) || !(xd_info[drive].cylinders); - if (xd_geo[3*drive]) { - xd_manual_geo_set(drive); - xd_info[drive].control = rll ? 7 : 5; - } - else if (use_jumper_geo) { - n = (((jumper_state & 0x0F) >> (drive << 1)) & 0x03) | rll; - xd_info[drive].cylinders = geometry_table[n][0]; - xd_info[drive].heads = (u_char)(geometry_table[n][1]); - xd_info[drive].control = rll ? 7 : 5; -#if 0 - xd_info[drive].rwrite = geometry_table[n][2]; - xd_info[drive].wprecomp = geometry_table[n][3]; - xd_info[drive].ecc = 0x0B; -#endif /* 0 */ - } - if (!wd_1002) { - if (use_jumper_geo) - xd_setparam(CMD_WDSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders, - geometry_table[n][2],geometry_table[n][3],0x0B); - else - xd_setparam(CMD_WDSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders, - ((u_short *) (buf))[0xD8],((u_short *) (buf))[0xDA],buf[0x1B4]); - } - /* 1002 based RLL controller requests converted addressing, but reports physical - (physical 26 sec., logical 17 sec.) - 1004 based ???? */ - if (rll & wd_1002) { - if ((xd_info[drive].cylinders *= 26, - xd_info[drive].cylinders /= 17) > 1023) - xd_info[drive].cylinders = 1023; /* 1024 ? */ -#if 0 - xd_info[drive].rwrite *= 26; - xd_info[drive].rwrite /= 17; - xd_info[drive].wprecomp *= 26 - xd_info[drive].wprecomp /= 17; -#endif /* 0 */ - } - } - else - printk("xd_wd_init_drive: error reading geometry for xd%c\n",'a'+drive); - -} - -static void __init xd_seagate_init_controller (unsigned int address) -{ - switch (address) { - case 0x00000: - case 0xC8000: break; /*initial: 0x320 */ - case 0xD0000: xd_iobase = 0x324; break; - case 0xD8000: xd_iobase = 0x328; break; - case 0xE0000: xd_iobase = 0x32C; break; - default: printk("xd_seagate_init_controller: unsupported BIOS address %06x\n",address); - break; - } - xd_maxsectors = 0x40; - - outb(0,XD_RESET); /* reset the controller */ -} - -static void __init xd_seagate_init_drive (u_char drive) -{ - u_char cmdblk[6],buf[0x200]; - - xd_build(cmdblk,CMD_ST11GETGEOM,drive,0,0,0,1,0); - if (!xd_command(cmdblk,PIO_MODE,buf,NULL,NULL,XD_TIMEOUT * 2)) { - xd_info[drive].heads = buf[0x04]; /* heads */ - xd_info[drive].cylinders = (buf[0x02] << 8) | buf[0x03]; /* cylinders */ - xd_info[drive].sectors = buf[0x05]; /* sectors */ - xd_info[drive].control = 0; /* control byte */ - } - else - printk("xd_seagate_init_drive: error reading geometry from xd%c\n", 'a'+drive); -} - -/* Omti support courtesy Dirk Melchers */ -static void __init xd_omti_init_controller (unsigned int address) -{ - switch (address) { - case 0x00000: - case 0xC8000: break; /*initial: 0x320 */ - case 0xD0000: xd_iobase = 0x324; break; - case 0xD8000: xd_iobase = 0x328; break; - case 0xE0000: xd_iobase = 0x32C; break; - default: printk("xd_omti_init_controller: unsupported BIOS address %06x\n",address); - break; - } - - xd_maxsectors = 0x40; - - outb(0,XD_RESET); /* reset the controller */ -} - -static void __init xd_omti_init_drive (u_char drive) -{ - /* gets infos from drive */ - xd_override_init_drive(drive); - - /* set other parameters, Hardcoded, not that nice :-) */ - xd_info[drive].control = 2; -} - -/* Xebec support (AK) */ -static void __init xd_xebec_init_controller (unsigned int address) -{ -/* iobase may be set manually in range 0x300 - 0x33C - irq may be set manually to 2(9),3,4,5,6,7 - dma may be set manually to 1,2,3 - (How to detect them ???) -BIOS address may be set manually in range 0x0 - 0xF8000 -If you need non-standard settings use the xd=... command */ - - switch (address) { - case 0x00000: - case 0xC8000: /* initially: xd_iobase==0x320 */ - case 0xD0000: - case 0xD2000: - case 0xD4000: - case 0xD6000: - case 0xD8000: - case 0xDA000: - case 0xDC000: - case 0xDE000: - case 0xE0000: break; - default: printk("xd_xebec_init_controller: unsupported BIOS address %06x\n",address); - break; - } - - xd_maxsectors = 0x01; - outb(0,XD_RESET); /* reset the controller */ - - msleep(XD_INIT_DISK_DELAY); -} - -static void __init xd_xebec_init_drive (u_char drive) -{ - /* values from controller's BIOS - BIOS chip may be removed */ - static u_short geometry_table[][5] = { - {0x132,4,0x080,0x080,0x7}, - {0x132,4,0x080,0x080,0x17}, - {0x264,2,0x100,0x100,0x7}, - {0x264,2,0x100,0x100,0x17}, - {0x132,8,0x080,0x080,0x7}, - {0x132,8,0x080,0x080,0x17}, - {0x264,4,0x100,0x100,0x6}, - {0x264,4,0x100,0x100,0x17}, - {0x2BC,5,0x2BC,0x12C,0x6}, - {0x3A5,4,0x3A5,0x3A5,0x7}, - {0x26C,6,0x26C,0x26C,0x7}, - {0x200,8,0x200,0x100,0x17}, - {0x400,5,0x400,0x400,0x7}, - {0x400,6,0x400,0x400,0x7}, - {0x264,8,0x264,0x200,0x17}, - {0x33E,7,0x33E,0x200,0x7}}; - u_char n; - - n = inb(XD_JUMPER) & 0x0F; /* BIOS's drive number: same geometry - is assumed for BOTH drives */ - if (xd_geo[3*drive]) - xd_manual_geo_set(drive); - else { - xd_info[drive].heads = (u_char)(geometry_table[n][1]); /* heads */ - xd_info[drive].cylinders = geometry_table[n][0]; /* cylinders */ - xd_info[drive].sectors = 17; /* sectors */ -#if 0 - xd_info[drive].rwrite = geometry_table[n][2]; /* reduced write */ - xd_info[drive].precomp = geometry_table[n][3] /* write precomp */ - xd_info[drive].ecc = 0x0B; /* ecc length */ -#endif /* 0 */ - } - xd_info[drive].control = geometry_table[n][4]; /* control byte */ - xd_setparam(CMD_XBSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,geometry_table[n][2],geometry_table[n][3],0x0B); - xd_recalibrate(drive); -} - -/* xd_override_init_drive: this finds disk geometry in a "binary search" style, narrowing in on the "correct" number of heads - etc. by trying values until it gets the highest successful value. Idea courtesy Salvador Abreu (spa@fct.unl.pt). */ -static void __init xd_override_init_drive (u_char drive) -{ - u_short min[] = { 0,0,0 },max[] = { 16,1024,64 },test[] = { 0,0,0 }; - u_char cmdblk[6],i; - - if (xd_geo[3*drive]) - xd_manual_geo_set(drive); - else { - for (i = 0; i < 3; i++) { - while (min[i] != max[i] - 1) { - test[i] = (min[i] + max[i]) / 2; - xd_build(cmdblk,CMD_SEEK,drive,(u_char) test[0],(u_short) test[1],(u_char) test[2],0,0); - if (!xd_command(cmdblk,PIO_MODE,NULL,NULL,NULL,XD_TIMEOUT * 2)) - min[i] = test[i]; - else - max[i] = test[i]; - } - test[i] = min[i]; - } - xd_info[drive].heads = (u_char) min[0] + 1; - xd_info[drive].cylinders = (u_short) min[1] + 1; - xd_info[drive].sectors = (u_char) min[2] + 1; - } - xd_info[drive].control = 0; -} - -/* xd_setup: initialise controller from command line parameters */ -static void __init do_xd_setup (int *integers) -{ - switch (integers[0]) { - case 4: if (integers[4] < 0) - nodma = 1; - else if (integers[4] < 8) - xd_dma = integers[4]; - case 3: if ((integers[3] > 0) && (integers[3] <= 0x3FC)) - xd_iobase = integers[3]; - case 2: if ((integers[2] > 0) && (integers[2] < 16)) - xd_irq = integers[2]; - case 1: xd_override = 1; - if ((integers[1] >= 0) && (integers[1] < ARRAY_SIZE(xd_sigs))) - xd_type = integers[1]; - case 0: break; - default:printk("xd: too many parameters for xd\n"); - } - xd_maxsectors = 0x01; -} - -/* xd_setparam: set the drive characteristics */ -static void __init xd_setparam (u_char command,u_char drive,u_char heads,u_short cylinders,u_short rwrite,u_short wprecomp,u_char ecc) -{ - u_char cmdblk[14]; - - xd_build(cmdblk,command,drive,0,0,0,0,0); - cmdblk[6] = (u_char) (cylinders >> 8) & 0x03; - cmdblk[7] = (u_char) (cylinders & 0xFF); - cmdblk[8] = heads & 0x1F; - cmdblk[9] = (u_char) (rwrite >> 8) & 0x03; - cmdblk[10] = (u_char) (rwrite & 0xFF); - cmdblk[11] = (u_char) (wprecomp >> 8) & 0x03; - cmdblk[12] = (u_char) (wprecomp & 0xFF); - cmdblk[13] = ecc; - - /* Some controllers require geometry info as data, not command */ - - if (xd_command(cmdblk,PIO_MODE,NULL,&cmdblk[6],NULL,XD_TIMEOUT * 2)) - printk("xd: error setting characteristics for xd%c\n", 'a'+drive); -} - - -#ifdef MODULE - -module_param_array(xd, int, NULL, 0); -module_param_array(xd_geo, int, NULL, 0); -module_param(nodma, bool, 0); - -MODULE_LICENSE("GPL"); - -void cleanup_module(void) -{ - int i; - unregister_blkdev(XT_DISK_MAJOR, "xd"); - for (i = 0; i < xd_drives; i++) { - del_gendisk(xd_gendisk[i]); - put_disk(xd_gendisk[i]); - } - blk_cleanup_queue(xd_queue); - release_region(xd_iobase,4); - if (xd_drives) { - free_irq(xd_irq, NULL); - free_dma(xd_dma); - if (xd_dma_buffer) - xd_dma_mem_free((unsigned long)xd_dma_buffer, xd_maxsectors * 0x200); - } -} -#else - -static int __init xd_setup (char *str) -{ - int ints[5]; - get_options (str, ARRAY_SIZE (ints), ints); - do_xd_setup (ints); - return 1; -} - -/* xd_manual_geo_init: initialise drive geometry from command line parameters - (used only for WD drives) */ -static int __init xd_manual_geo_init (char *str) -{ - int i, integers[1 + 3*XD_MAXDRIVES]; - - get_options (str, ARRAY_SIZE (integers), integers); - if (integers[0]%3 != 0) { - printk("xd: incorrect number of parameters for xd_geo\n"); - return 1; - } - for (i = 0; (i < integers[0]) && (i < 3*XD_MAXDRIVES); i++) - xd_geo[i] = integers[i+1]; - return 1; -} - -__setup ("xd=", xd_setup); -__setup ("xd_geo=", xd_manual_geo_init); - -#endif /* MODULE */ - -module_init(xd_init); -MODULE_ALIAS_BLOCKDEV_MAJOR(XT_DISK_MAJOR); diff --git a/drivers/block/xd.h b/drivers/block/xd.h deleted file mode 100644 index 37cacef16e93..000000000000 --- a/drivers/block/xd.h +++ /dev/null @@ -1,134 +0,0 @@ -#ifndef _LINUX_XD_H -#define _LINUX_XD_H - -/* - * This file contains the definitions for the IO ports and errors etc. for XT hard disk controllers (at least the DTC 5150X). - * - * Author: Pat Mackinlay, pat@it.com.au - * Date: 29/09/92 - * - * Revised: 01/01/93, ... - * - * Ref: DTC 5150X Controller Specification (thanks to Kevin Fowler, kevinf@agora.rain.com) - * Also thanks to: Salvador Abreu, Dave Thaler, Risto Kankkunen and Wim Van Dorst. - */ - -#include - -/* XT hard disk controller registers */ -#define XD_DATA (xd_iobase + 0x00) /* data RW register */ -#define XD_RESET (xd_iobase + 0x01) /* reset WO register */ -#define XD_STATUS (xd_iobase + 0x01) /* status RO register */ -#define XD_SELECT (xd_iobase + 0x02) /* select WO register */ -#define XD_JUMPER (xd_iobase + 0x02) /* jumper RO register */ -#define XD_CONTROL (xd_iobase + 0x03) /* DMAE/INTE WO register */ -#define XD_RESERVED (xd_iobase + 0x03) /* reserved */ - -/* XT hard disk controller commands (incomplete list) */ -#define CMD_TESTREADY 0x00 /* test drive ready */ -#define CMD_RECALIBRATE 0x01 /* recalibrate drive */ -#define CMD_SENSE 0x03 /* request sense */ -#define CMD_FORMATDRV 0x04 /* format drive */ -#define CMD_VERIFY 0x05 /* read verify */ -#define CMD_FORMATTRK 0x06 /* format track */ -#define CMD_FORMATBAD 0x07 /* format bad track */ -#define CMD_READ 0x08 /* read */ -#define CMD_WRITE 0x0A /* write */ -#define CMD_SEEK 0x0B /* seek */ - -/* Controller specific commands */ -#define CMD_DTCSETPARAM 0x0C /* set drive parameters (DTC 5150X & CX only?) */ -#define CMD_DTCGETECC 0x0D /* get ecc error length (DTC 5150X only?) */ -#define CMD_DTCREADBUF 0x0E /* read sector buffer (DTC 5150X only?) */ -#define CMD_DTCWRITEBUF 0x0F /* write sector buffer (DTC 5150X only?) */ -#define CMD_DTCREMAPTRK 0x11 /* assign alternate track (DTC 5150X only?) */ -#define CMD_DTCGETPARAM 0xFB /* get drive parameters (DTC 5150X only?) */ -#define CMD_DTCSETSTEP 0xFC /* set step rate (DTC 5150X only?) */ -#define CMD_DTCSETGEOM 0xFE /* set geometry data (DTC 5150X only?) */ -#define CMD_DTCGETGEOM 0xFF /* get geometry data (DTC 5150X only?) */ -#define CMD_ST11GETGEOM 0xF8 /* get geometry data (Seagate ST11R/M only?) */ -#define CMD_WDSETPARAM 0x0C /* set drive parameters (WD 1004A27X only?) */ -#define CMD_XBSETPARAM 0x0C /* set drive parameters (XEBEC only?) */ - -/* Bits for command status byte */ -#define CSB_ERROR 0x02 /* error */ -#define CSB_LUN 0x20 /* logical Unit Number */ - -/* XT hard disk controller status bits */ -#define STAT_READY 0x01 /* controller is ready */ -#define STAT_INPUT 0x02 /* data flowing from controller to host */ -#define STAT_COMMAND 0x04 /* controller in command phase */ -#define STAT_SELECT 0x08 /* controller is selected */ -#define STAT_REQUEST 0x10 /* controller requesting data */ -#define STAT_INTERRUPT 0x20 /* controller requesting interrupt */ - -/* XT hard disk controller control bits */ -#define PIO_MODE 0x00 /* control bits to set for PIO */ -#define DMA_MODE 0x03 /* control bits to set for DMA & interrupt */ - -#define XD_MAXDRIVES 2 /* maximum 2 drives */ -#define XD_TIMEOUT HZ /* 1 second timeout */ -#define XD_RETRIES 4 /* maximum 4 retries */ - -#undef DEBUG /* define for debugging output */ - -#ifdef DEBUG - #define DEBUG_STARTUP /* debug driver initialisation */ - #define DEBUG_OVERRIDE /* debug override geometry detection */ - #define DEBUG_READWRITE /* debug each read/write command */ - #define DEBUG_OTHER /* debug misc. interrupt/DMA stuff */ - #define DEBUG_COMMAND /* debug each controller command */ -#endif /* DEBUG */ - -/* this structure defines the XT drives and their types */ -typedef struct { - u_char heads; - u_short cylinders; - u_char sectors; - u_char control; - int unit; -} XD_INFO; - -/* this structure defines a ROM BIOS signature */ -typedef struct { - unsigned int offset; - const char *string; - void (*init_controller)(unsigned int address); - void (*init_drive)(u_char drive); - const char *name; -} XD_SIGNATURE; - -#ifndef MODULE -static int xd_manual_geo_init (char *command); -#endif /* MODULE */ -static u_char xd_detect (u_char *controller, unsigned int *address); -static u_char xd_initdrives (void (*init_drive)(u_char drive)); - -static void do_xd_request (struct request_queue * q); -static int xd_ioctl (struct block_device *bdev,fmode_t mode,unsigned int cmd,unsigned long arg); -static int xd_readwrite (u_char operation,XD_INFO *disk,char *buffer,u_int block,u_int count); -static void xd_recalibrate (u_char drive); - -static irqreturn_t xd_interrupt_handler(int irq, void *dev_id); -static u_char xd_setup_dma (u_char opcode,u_char *buffer,u_int count); -static u_char *xd_build (u_char *cmdblk,u_char command,u_char drive,u_char head,u_short cylinder,u_char sector,u_char count,u_char control); -static void xd_watchdog (unsigned long unused); -static inline u_char xd_waitport (u_short port,u_char flags,u_char mask,u_long timeout); -static u_int xd_command (u_char *command,u_char mode,u_char *indata,u_char *outdata,u_char *sense,u_long timeout); - -/* card specific setup and geometry gathering code */ -static void xd_dtc_init_controller (unsigned int address); -static void xd_dtc5150cx_init_drive (u_char drive); -static void xd_dtc_init_drive (u_char drive); -static void xd_wd_init_controller (unsigned int address); -static void xd_wd_init_drive (u_char drive); -static void xd_seagate_init_controller (unsigned int address); -static void xd_seagate_init_drive (u_char drive); -static void xd_omti_init_controller (unsigned int address); -static void xd_omti_init_drive (u_char drive); -static void xd_xebec_init_controller (unsigned int address); -static void xd_xebec_init_drive (u_char drive); -static void xd_setparam (u_char command,u_char drive,u_char heads,u_short cylinders,u_short rwrite,u_short wprecomp,u_char ecc); -static void xd_override_init_drive (u_char drive); - -#endif /* _LINUX_XD_H */ -- cgit v1.2.3 From a9d79fe581f4019209ddc121b114dc28b88cdab0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 6 Sep 2012 07:31:04 -0300 Subject: [media] em28xx: fix querycap Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 42 +++++++++++++++------------------ 1 file changed, 19 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 4c1726d43374..fb9ee4677130 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1577,6 +1577,7 @@ static int vidioc_streamoff(struct file *file, void *priv, static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { + struct video_device *vdev = video_devdata(file); struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; @@ -1584,20 +1585,28 @@ static int vidioc_querycap(struct file *file, void *priv, strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->capabilities = - V4L2_CAP_SLICED_VBI_CAPTURE | - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - - if (dev->vbi_dev) - cap->capabilities |= V4L2_CAP_VBI_CAPTURE; + if (vdev->vfl_type == VFL_TYPE_GRABBER) + cap->device_caps = V4L2_CAP_READWRITE | + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + else if (vdev->vfl_type == VFL_TYPE_RADIO) + cap->device_caps = V4L2_CAP_RADIO; + else + cap->device_caps = V4L2_CAP_READWRITE | + V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE; if (dev->audio_mode.has_audio) - cap->capabilities |= V4L2_CAP_AUDIO; + cap->device_caps |= V4L2_CAP_AUDIO; if (dev->tuner_type != TUNER_ABSENT) - cap->capabilities |= V4L2_CAP_TUNER; + cap->device_caps |= V4L2_CAP_TUNER; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS | + V4L2_CAP_READWRITE | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + if (dev->vbi_dev) + cap->capabilities |= + V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE; + if (dev->radio_dev) + cap->capabilities |= V4L2_CAP_RADIO; return 0; } @@ -1831,19 +1840,6 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) /* RADIO ESPECIFIC IOCTLS */ /* ----------------------------------------------------------- */ -static int radio_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct em28xx *dev = ((struct em28xx_fh *)priv)->dev; - - strlcpy(cap->driver, "em28xx", sizeof(cap->driver)); - strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); - usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - - cap->capabilities = V4L2_CAP_TUNER; - return 0; -} - static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { @@ -2281,7 +2277,7 @@ static const struct v4l2_file_operations radio_fops = { }; static const struct v4l2_ioctl_ops radio_ioctl_ops = { - .vidioc_querycap = radio_querycap, + .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = radio_g_tuner, .vidioc_enum_input = radio_enum_input, .vidioc_g_audio = radio_g_audio, -- cgit v1.2.3 From dd5a4363224614489f6fef25272328ba949a4121 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 6 Sep 2012 09:23:03 -0300 Subject: [media] em28xx: remove bogus input/audio ioctls for the radio device Radio devices should not implement those ioctls. Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 35 --------------------------------- 1 file changed, 35 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index fb9ee4677130..f025440bf953 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1856,26 +1856,6 @@ static int radio_g_tuner(struct file *file, void *priv, return 0; } -static int radio_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - if (i->index != 0) - return -EINVAL; - strcpy(i->name, "Radio"); - i->type = V4L2_INPUT_TYPE_TUNER; - - return 0; -} - -static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - if (unlikely(a->index)) - return -EINVAL; - - strcpy(a->name, "Radio"); - return 0; -} - static int radio_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { @@ -1889,17 +1869,6 @@ static int radio_s_tuner(struct file *file, void *priv, return 0; } -static int radio_s_audio(struct file *file, void *fh, - const struct v4l2_audio *a) -{ - return 0; -} - -static int radio_s_input(struct file *file, void *fh, unsigned int i) -{ - return 0; -} - static int radio_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *qc) { @@ -2279,11 +2248,7 @@ static const struct v4l2_file_operations radio_fops = { static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = radio_g_tuner, - .vidioc_enum_input = radio_enum_input, - .vidioc_g_audio = radio_g_audio, .vidioc_s_tuner = radio_s_tuner, - .vidioc_s_audio = radio_s_audio, - .vidioc_s_input = radio_s_input, .vidioc_queryctrl = radio_queryctrl, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, -- cgit v1.2.3 From 319a55fbe4c1d3bbe8abe3900e6dc91440ec9b0b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 6 Sep 2012 09:53:08 -0300 Subject: [media] em28xx: fix VIDIOC_DBG_G_CHIP_IDENT compliance errors Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index f025440bf953..b71df42aa7bb 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1403,6 +1403,14 @@ static int vidioc_g_chip_ident(struct file *file, void *priv, chip->ident = V4L2_IDENT_NONE; chip->revision = 0; + if (chip->match.type == V4L2_CHIP_MATCH_HOST) { + if (v4l2_chip_match_host(&chip->match)) + chip->ident = V4L2_IDENT_NONE; + return 0; + } + if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && + chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip); -- cgit v1.2.3 From 20deebfe17b20ded00ba404adbcd014eb2b024c1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 6 Sep 2012 10:07:25 -0300 Subject: [media] em28xx: fix tuner/frequency handling v4l2-compliance found problems with frequency clamping that wasn't reported correctly and missing tuner index checks. Also removed unnecessary tuner type checks (these are now done by the v4l2 core). Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index b71df42aa7bb..89cbfaf17bda 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1322,7 +1322,6 @@ static int vidioc_g_tuner(struct file *file, void *priv, return -EINVAL; strcpy(t->name, "Tuner"); - t->type = V4L2_TUNER_ANALOG_TV; v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); return 0; @@ -1352,7 +1351,9 @@ static int vidioc_g_frequency(struct file *file, void *priv, struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + if (0 != f->tuner) + return -EINVAL; + f->frequency = dev->ctl_freq; return 0; } @@ -1371,13 +1372,9 @@ static int vidioc_s_frequency(struct file *file, void *priv, if (0 != f->tuner) return -EINVAL; - if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV)) - return -EINVAL; - if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO)) - return -EINVAL; - - dev->ctl_freq = f->frequency; v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, f); + dev->ctl_freq = f->frequency; return 0; } -- cgit v1.2.3 From 8ac7a9493a4380a8a886fbfe311ab00bc424ca0f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Sep 2012 04:46:39 -0300 Subject: [media] v4l2-ctrls: add a notify callback Sometimes platform/bridge drivers need to be notified when a control from a sub-device changes value. In order to support this a notify callback was added. [dheitmueller@kernellabs.com: fix merge conflict in v4l2-ctrls.c] Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-controls.txt | 22 ++++++++++++++-------- drivers/media/v4l2-core/v4l2-ctrls.c | 18 ++++++++++++++++++ include/media/v4l2-ctrls.h | 25 +++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/Documentation/video4linux/v4l2-controls.txt b/Documentation/video4linux/v4l2-controls.txt index cfe52c798d74..676f87366025 100644 --- a/Documentation/video4linux/v4l2-controls.txt +++ b/Documentation/video4linux/v4l2-controls.txt @@ -715,14 +715,20 @@ a control of this type whenever the first control belonging to a new control class is added. -Proposals for Extensions -======================== +Adding Notify Callbacks +======================= + +Sometimes the platform or bridge driver needs to be notified when a control +from a sub-device driver changes. You can set a notify callback by calling +this function: -Some ideas for future extensions to the spec: +void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, + void (*notify)(struct v4l2_ctrl *ctrl, void *priv), void *priv); -1) Add a V4L2_CTRL_FLAG_HEX to have values shown as hexadecimal instead of -decimal. Useful for e.g. video_mute_yuv. +Whenever the give control changes value the notify callback will be called +with a pointer to the control and the priv pointer that was passed with +v4l2_ctrl_notify. Note that the control's handler lock is held when the +notify function is called. -2) It is possible to mark in the controls array which controls have been -successfully written and which failed by for example adding a bit to the -control ID. Not sure if it is worth the effort, though. +There can be only one notify function per control handler. Any attempt +to set another notify function will cause a WARN_ON. diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index f6ee201d9347..fa02363e7db4 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1204,6 +1204,8 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, send_event(fh, ctrl, (changed ? V4L2_EVENT_CTRL_CH_VALUE : 0) | (update_inactive ? V4L2_EVENT_CTRL_CH_FLAGS : 0)); + if (ctrl->call_notify && changed && ctrl->handler->notify) + ctrl->handler->notify(ctrl, ctrl->handler->notify_priv); } } @@ -2725,6 +2727,22 @@ int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) } EXPORT_SYMBOL(v4l2_ctrl_s_ctrl_int64); +void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv) +{ + if (ctrl == NULL) + return; + if (notify == NULL) { + ctrl->call_notify = 0; + return; + } + if (WARN_ON(ctrl->handler->notify && ctrl->handler->notify != notify)) + return; + ctrl->handler->notify = notify; + ctrl->handler->notify_priv = priv; + ctrl->call_notify = 1; +} +EXPORT_SYMBOL(v4l2_ctrl_notify); + static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems) { struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id); diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 96509119f28f..c4cc04136074 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -53,6 +53,8 @@ struct v4l2_ctrl_ops { int (*s_ctrl)(struct v4l2_ctrl *ctrl); }; +typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); + /** struct v4l2_ctrl - The control structure. * @node: The list node. * @ev_subs: The list of control event subscriptions. @@ -72,6 +74,8 @@ struct v4l2_ctrl_ops { * set this flag directly. * @has_volatiles: If set, then one or more members of the cluster are volatile. * Drivers should never touch this flag. + * @call_notify: If set, then call the handler's notify function whenever the + * control's value changes. * @manual_mode_value: If the is_auto flag is set, then this is the value * of the auto control that determines if that control is in * manual mode. So if the value of the auto control equals this @@ -119,6 +123,7 @@ struct v4l2_ctrl { unsigned int is_private:1; unsigned int is_auto:1; unsigned int has_volatiles:1; + unsigned int call_notify:1; unsigned int manual_mode_value:8; const struct v4l2_ctrl_ops *ops; @@ -177,6 +182,10 @@ struct v4l2_ctrl_ref { * control is needed multiple times, so this is a simple * optimization. * @buckets: Buckets for the hashing. Allows for quick control lookup. + * @notify: A notify callback that is called whenever the control changes value. + * Note that the handler's lock is held when the notify function + * is called! + * @notify_priv: Passed as argument to the v4l2_ctrl notify callback. * @nr_of_buckets: Total number of buckets in the array. * @error: The error code of the first failed control addition. */ @@ -187,6 +196,8 @@ struct v4l2_ctrl_handler { struct list_head ctrl_refs; struct v4l2_ctrl_ref *cached; struct v4l2_ctrl_ref **buckets; + v4l2_ctrl_notify_fnc notify; + void *notify_priv; u16 nr_of_buckets; int error; }; @@ -525,6 +536,20 @@ static inline void v4l2_ctrl_unlock(struct v4l2_ctrl *ctrl) mutex_unlock(ctrl->handler->lock); } +/** v4l2_ctrl_notify() - Function to set a notify callback for a control. + * @ctrl: The control. + * @notify: The callback function. + * @priv: The callback private handle, passed as argument to the callback. + * + * This function sets a callback function for the control. If @ctrl is NULL, + * then it will do nothing. If @notify is NULL, then the notify callback will + * be removed. + * + * There can be only one notify. If another already exists, then a WARN_ON + * will be issued and the function will do nothing. + */ +void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv); + /** v4l2_ctrl_g_ctrl() - Helper function to get the control's value from within a driver. * @ctrl: The control. * -- cgit v1.2.3 From 081b945ed74c9bd37da2ee928f9ad281222a6477 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Sep 2012 05:43:59 -0300 Subject: [media] em28xx: convert to the control framework Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 24 ++++ drivers/media/usb/em28xx/em28xx-video.c | 248 +++----------------------------- drivers/media/usb/em28xx/em28xx.h | 6 + 3 files changed, 53 insertions(+), 225 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index e17be073c0c3..1aca98f30a8d 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2941,6 +2941,8 @@ void em28xx_release_resources(struct em28xx *dev) em28xx_i2c_unregister(dev); + v4l2_ctrl_handler_free(&dev->ctrl_handler); + v4l2_device_unregister(&dev->v4l2_dev); usb_put_dev(dev->udev); @@ -2957,6 +2959,7 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, struct usb_interface *interface, int minor) { + struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; int retval; static const char *default_chip_name = "em28xx"; const char *chip_name = default_chip_name; @@ -3084,6 +3087,9 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, return retval; } + v4l2_ctrl_handler_init(hdl, 4); + dev->v4l2_dev.ctrl_handler = hdl; + /* register i2c bus */ retval = em28xx_i2c_register(dev); if (retval < 0) { @@ -3109,6 +3115,18 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, __func__, retval); goto fail; } + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 0x1f, 1, 0x1f); + } else { + /* install the em28xx notify callback */ + v4l2_ctrl_notify(v4l2_ctrl_find(hdl, V4L2_CID_AUDIO_MUTE), + em28xx_ctrl_notify, dev); + v4l2_ctrl_notify(v4l2_ctrl_find(hdl, V4L2_CID_AUDIO_VOLUME), + em28xx_ctrl_notify, dev); + } /* wake i2c devices */ em28xx_wake_i2c(dev); @@ -3138,6 +3156,11 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, msleep(3); } + v4l2_ctrl_handler_setup(&dev->ctrl_handler); + retval = dev->ctrl_handler.error; + if (retval) + goto fail; + retval = em28xx_register_analog_devices(dev); if (retval < 0) { goto fail; @@ -3150,6 +3173,7 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, fail: em28xx_i2c_unregister(dev); + v4l2_ctrl_handler_free(&dev->ctrl_handler); unregister_dev: v4l2_device_unregister(&dev->v4l2_dev); diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 89cbfaf17bda..ebbf775fcaba 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -125,30 +125,6 @@ static struct em28xx_fmt format[] = { }, }; -/* supported controls */ -/* Common to all boards */ -static struct v4l2_queryctrl ac97_qctrl[] = { - { - .id = V4L2_CID_AUDIO_VOLUME, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Volume", - .minimum = 0x0, - .maximum = 0x1f, - .step = 0x1, - .default_value = 0x1f, - .flags = V4L2_CTRL_FLAG_SLIDER, - }, { - .id = V4L2_CID_AUDIO_MUTE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - .flags = 0, - } -}; - /* ------------------------------------------------------------------ DMA and thread functions ------------------------------------------------------------------*/ @@ -718,76 +694,48 @@ static int get_ressource(struct em28xx_fh *fh) } } -/* - * ac97_queryctrl() - * return the ac97 supported controls - */ -static int ac97_queryctrl(struct v4l2_queryctrl *qc) +void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv) { - int i; - - for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { - if (qc->id && qc->id == ac97_qctrl[i].id) { - memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); - return 0; - } - } - - /* Control is not ac97 related */ - return 1; -} + struct em28xx *dev = priv; -/* - * ac97_get_ctrl() - * return the current values for ac97 mute and volume - */ -static int ac97_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) -{ + /* + * In the case of non-AC97 volume controls, we still need + * to do some setups at em28xx, in order to mute/unmute + * and to adjust audio volume. However, the value ranges + * should be checked by the corresponding V4L subdriver. + */ switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - ctrl->value = dev->mute; - return 0; + dev->mute = ctrl->val; + em28xx_audio_analog_set(dev); + break; case V4L2_CID_AUDIO_VOLUME: - ctrl->value = dev->volume; - return 0; - default: - /* Control is not ac97 related */ - return 1; + dev->volume = ctrl->val; + em28xx_audio_analog_set(dev); + break; } } -/* - * ac97_set_ctrl() - * set values for ac97 mute and volume - */ -static int ac97_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) +static int em28xx_s_ctrl(struct v4l2_ctrl *ctrl) { - int i; - - for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) - if (ctrl->id == ac97_qctrl[i].id) - goto handle; - - /* Announce that hasn't handle it */ - return 1; - -handle: - if (ctrl->value < ac97_qctrl[i].minimum || - ctrl->value > ac97_qctrl[i].maximum) - return -ERANGE; + struct em28xx *dev = container_of(ctrl->handler, struct em28xx, ctrl_handler); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - dev->mute = ctrl->value; + dev->mute = ctrl->val; break; case V4L2_CID_AUDIO_VOLUME: - dev->volume = ctrl->value; + dev->volume = ctrl->val; break; } return em28xx_audio_analog_set(dev); } +const struct v4l2_ctrl_ops em28xx_ctrl_ops = { + .s_ctrl = em28xx_s_ctrl, +}; + static int check_dev(struct em28xx *dev) { if (dev->state & DEV_DISCONNECTED) { @@ -1182,131 +1130,6 @@ static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio return 0; } -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int id = qc->id; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - memset(qc, 0, sizeof(*qc)); - - qc->id = id; - - /* enumerate AC97 controls */ - if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { - rc = ac97_queryctrl(qc); - if (!rc) - return 0; - } - - /* enumerate V4L2 device controls */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc); - - if (qc->type) - return 0; - else - return -EINVAL; -} - -/* - * FIXME: This is an indirect way to check if a control exists at a - * subdev. Instead of that hack, maybe the better would be to change all - * subdevs to return -ENOIOCTLCMD, if an ioctl is not supported. - */ -static int check_subdev_ctrl(struct em28xx *dev, int id) -{ - struct v4l2_queryctrl qc; - - memset(&qc, 0, sizeof(qc)); - qc.id = id; - - /* enumerate V4L2 device controls */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, &qc); - - if (qc.type) - return 0; - else - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - rc = 0; - - /* Set an AC97 control */ - if (dev->audio_mode.ac97 != EM28XX_NO_AC97) - rc = ac97_get_ctrl(dev, ctrl); - else - rc = 1; - - /* It were not an AC97 control. Sends it to the v4l2 dev interface */ - if (rc == 1) { - if (check_subdev_ctrl(dev, ctrl->id)) - return -EINVAL; - - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); - rc = 0; - } - - return rc; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - /* Set an AC97 control */ - if (dev->audio_mode.ac97 != EM28XX_NO_AC97) - rc = ac97_set_ctrl(dev, ctrl); - else - rc = 1; - - /* It isn't an AC97 control. Sends it to the v4l2 dev interface */ - if (rc == 1) { - rc = check_subdev_ctrl(dev, ctrl->id); - if (!rc) - v4l2_device_call_all(&dev->v4l2_dev, 0, - core, s_ctrl, ctrl); - /* - * In the case of non-AC97 volume controls, we still need - * to do some setups at em28xx, in order to mute/unmute - * and to adjust audio volume. However, the value ranges - * should be checked by the corresponding V4L subdriver. - */ - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - dev->mute = ctrl->value; - rc = em28xx_audio_analog_set(dev); - break; - case V4L2_CID_AUDIO_VOLUME: - dev->volume = ctrl->value; - rc = em28xx_audio_analog_set(dev); - } - } - return (rc < 0) ? rc : 0; -} - static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { @@ -1874,25 +1697,6 @@ static int radio_s_tuner(struct file *file, void *priv, return 0; } -static int radio_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - int i; - - if (qc->id < V4L2_CID_BASE || - qc->id >= V4L2_CID_LASTP1) - return -EINVAL; - - for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { - if (qc->id && qc->id == ac97_qctrl[i].id) { - memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); - return 0; - } - } - - return -EINVAL; -} - /* * em28xx_v4l2_open() * inits the device and starts isoc transfer @@ -2218,9 +2022,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_g_tuner = vidioc_g_tuner, @@ -2254,9 +2055,6 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = radio_g_tuner, .vidioc_s_tuner = radio_s_tuner, - .vidioc_queryctrl = radio_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -2300,7 +2098,7 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, int em28xx_register_analog_devices(struct em28xx *dev) { - u8 val; + u8 val; int ret; unsigned int maxw; diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 062841e50722..707319eabe2d 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -33,6 +33,7 @@ #include #include +#include #include #include #if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE) @@ -497,6 +498,9 @@ struct em28xx { int audio_ifnum; struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; + /* provides ac97 mute and volume overrides */ + struct v4l2_ctrl_handler ac97_ctrl_handler; struct em28xx_board board; /* Webcam specific fields */ @@ -705,6 +709,8 @@ void em28xx_close_extension(struct em28xx *dev); /* Provided by em28xx-video.c */ int em28xx_register_analog_devices(struct em28xx *dev); void em28xx_release_analog_resources(struct em28xx *dev); +void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv); +extern const struct v4l2_ctrl_ops em28xx_ctrl_ops; /* Provided by em28xx-cards.c */ extern int em2800_variant_detect(struct usb_device *udev, int model); -- cgit v1.2.3 From 69a61642ac60e84647394b4cf0f322579701d218 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Sep 2012 05:52:40 -0300 Subject: [media] em28xx: convert to v4l2_fh, fix priority handling Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 5 +++++ drivers/media/usb/em28xx/em28xx.h | 2 ++ 2 files changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index ebbf775fcaba..c67ff8d96d22 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1735,6 +1735,7 @@ static int em28xx_v4l2_open(struct file *filp) mutex_unlock(&dev->lock); return -ENOMEM; } + v4l2_fh_init(&fh->fh, vdev); fh->dev = dev; fh->radio = radio; fh->type = fh_type; @@ -1774,6 +1775,7 @@ static int em28xx_v4l2_open(struct file *filp) V4L2_FIELD_SEQ_TB, sizeof(struct em28xx_buffer), fh, &dev->lock); mutex_unlock(&dev->lock); + v4l2_fh_add(&fh->fh); return errCode; } @@ -1867,6 +1869,8 @@ static int em28xx_v4l2_close(struct file *filp) "0 (error=%i)\n", errCode); } } + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); videobuf_mmap_free(&fh->vb_vidq); videobuf_mmap_free(&fh->vb_vbiq); @@ -2088,6 +2092,7 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, vfd->release = video_device_release; vfd->debug = video_debug; vfd->lock = &dev->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 707319eabe2d..7432be483c22 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE) @@ -477,6 +478,7 @@ struct em28xx_audio { struct em28xx; struct em28xx_fh { + struct v4l2_fh fh; struct em28xx *dev; int radio; unsigned int resources; -- cgit v1.2.3 From 50fdf40f696106e0e3c9fa0ee2f6f1457209e81e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Sep 2012 06:10:12 -0300 Subject: [media] em28xx: add support for control events Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 38 ++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index c67ff8d96d22..acdb4340399f 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -40,6 +40,7 @@ #include "em28xx.h" #include #include +#include #include #include #include @@ -1927,24 +1928,33 @@ em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count, static unsigned int em28xx_poll(struct file *filp, poll_table *wait) { struct em28xx_fh *fh = filp->private_data; + unsigned long req_events = poll_requested_events(wait); struct em28xx *dev = fh->dev; + unsigned int res = 0; int rc; rc = check_dev(dev); if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (!res_get(fh, EM28XX_RESOURCE_VIDEO)) - return POLLERR; - return videobuf_poll_stream(filp, &fh->vb_vidq, wait); - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (!res_get(fh, EM28XX_RESOURCE_VBI)) - return POLLERR; - return videobuf_poll_stream(filp, &fh->vb_vbiq, wait); - } else { - return POLLERR; + return DEFAULT_POLLMASK; + + if (v4l2_event_pending(&fh->fh)) + res = POLLPRI; + else if (req_events & POLLPRI) + poll_wait(filp, &fh->fh.wait, wait); + + if (req_events & (POLLIN | POLLRDNORM)) { + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (!res_get(fh, EM28XX_RESOURCE_VIDEO)) + return res | POLLERR; + return videobuf_poll_stream(filp, &fh->vb_vidq, wait); + } + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (!res_get(fh, EM28XX_RESOURCE_VBI)) + return res | POLLERR; + return res | videobuf_poll_stream(filp, &fh->vb_vbiq, wait); + } } + return res; } static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait) @@ -2032,6 +2042,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, @@ -2061,6 +2073,8 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_s_tuner = radio_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, -- cgit v1.2.3 From 86ff7f1d4be99080d740fd88495154717cd39e2b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Sep 2012 06:16:03 -0300 Subject: [media] em28xx: fill in readbuffers and fix incorrect return code g/s_parm should fill in readbuffers. For non-webcams s_parm should return -ENOTTY instead of -EINVAL. Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index acdb4340399f..a91a2484bc98 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -972,6 +972,7 @@ static int vidioc_g_parm(struct file *file, void *priv, if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + p->parm.capture.readbuffers = EM28XX_MIN_BUF; if (dev->board.is_webcam) rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, g_parm, p); @@ -989,11 +990,12 @@ static int vidioc_s_parm(struct file *file, void *priv, struct em28xx *dev = fh->dev; if (!dev->board.is_webcam) - return -EINVAL; + return -ENOTTY; if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + p->parm.capture.readbuffers = EM28XX_MIN_BUF; return v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, s_parm, p); } -- cgit v1.2.3 From 7f529794847bc2ff509967e5ad54fce7d885de93 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Sep 2012 06:34:26 -0300 Subject: [media] tvp5150: remove compat control ops No longer needed now that em28xx has been converted to the control framework. Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvp5150.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 31104a960652..5967e1a0c809 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1096,13 +1096,6 @@ static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = { static const struct v4l2_subdev_core_ops tvp5150_core_ops = { .log_status = tvp5150_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, .s_std = tvp5150_s_std, .reset = tvp5150_reset, .g_chip_ident = tvp5150_g_chip_ident, -- cgit v1.2.3 From d8c95c08ef1127c8777dc3a1177143cf8a5b86ef Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Sep 2012 07:31:54 -0300 Subject: [media] em28xx: std fixes: don't implement in webcam mode, and fix std changes When in webcam mode the STD API shouldn't be implemented. When changing the standard the resolution wasn't updated, and there was no check against streaming-in-progress. Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index a91a2484bc98..7000e22e11a8 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -909,6 +909,8 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) struct em28xx *dev = fh->dev; int rc; + if (dev->board.is_webcam) + return -ENOTTY; rc = check_dev(dev); if (rc < 0) return rc; @@ -924,6 +926,8 @@ static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm) struct em28xx *dev = fh->dev; int rc; + if (dev->board.is_webcam) + return -ENOTTY; rc = check_dev(dev); if (rc < 0) return rc; @@ -940,15 +944,24 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) struct v4l2_format f; int rc; + if (dev->board.is_webcam) + return -ENOTTY; + if (*norm == dev->norm) + return 0; rc = check_dev(dev); if (rc < 0) return rc; + if (videobuf_queue_is_busy(&fh->vb_vidq)) { + em28xx_errdev("%s queue busy\n", __func__); + return -EBUSY; + } + dev->norm = *norm; /* Adjusts width/height, if needed */ - f.fmt.pix.width = dev->width; - f.fmt.pix.height = dev->height; + f.fmt.pix.width = 720; + f.fmt.pix.height = (*norm & V4L2_STD_525_60) ? 480 : 576; vidioc_try_fmt_vid_cap(file, priv, &f); /* set new image size */ @@ -1034,6 +1047,9 @@ static int vidioc_enum_input(struct file *file, void *priv, i->type = V4L2_INPUT_TYPE_TUNER; i->std = dev->vdev->tvnorms; + /* webcams do not have the STD API */ + if (dev->board.is_webcam) + i->capabilities = 0; return 0; } @@ -2059,7 +2075,6 @@ static const struct video_device em28xx_video_template = { .ioctl_ops = &video_ioctl_ops, .tvnorms = V4L2_STD_ALL, - .current_norm = V4L2_STD_PAL, }; static const struct v4l2_file_operations radio_fops = { @@ -2109,6 +2124,8 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, vfd->debug = video_debug; vfd->lock = &dev->lock; set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); + if (dev->board.is_webcam) + vfd->tvnorms = 0; snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); @@ -2127,7 +2144,7 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->name, EM28XX_VERSION); /* set default norm */ - dev->norm = em28xx_video_template.current_norm; + dev->norm = V4L2_STD_PAL; v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); dev->interlaced = EM28XX_INTERLACED_DEFAULT; -- cgit v1.2.3 From 1d179eeedc8cb48712bc236ec82ec6c63af42008 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Sep 2012 08:45:10 -0300 Subject: [media] em28xx: remove sliced VBI support The sliced VBI support in the tvp5150 is completely broken. And there is no support for the saa7115 sliced VBI implementation in the em28xx driver. So we remove the sliced VBI support completely. It should be possible to get it to work with the tvp5150, but that will require someone to really dig into that driver. Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 49 ++------------------------------- 1 file changed, 2 insertions(+), 47 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 7000e22e11a8..971046861f7f 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1438,8 +1438,7 @@ static int vidioc_querycap(struct file *file, void *priv, else if (vdev->vfl_type == VFL_TYPE_RADIO) cap->device_caps = V4L2_CAP_RADIO; else - cap->device_caps = V4L2_CAP_READWRITE | - V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE; + cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE; if (dev->audio_mode.has_audio) cap->device_caps |= V4L2_CAP_AUDIO; @@ -1450,8 +1449,7 @@ static int vidioc_querycap(struct file *file, void *priv, cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS | V4L2_CAP_READWRITE | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; if (dev->vbi_dev) - cap->capabilities |= - V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE; + cap->capabilities |= V4L2_CAP_VBI_CAPTURE; if (dev->radio_dev) cap->capabilities |= V4L2_CAP_RADIO; return 0; @@ -1508,46 +1506,6 @@ static int vidioc_enum_framesizes(struct file *file, void *priv, return 0; } -/* Sliced VBI ioctls */ -static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - f->fmt.sliced.service_set = 0; - v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced); - - if (f->fmt.sliced.service_set == 0) - rc = -EINVAL; - - return rc; -} - -static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced); - - if (f->fmt.sliced.service_set == 0) - return -EINVAL; - - return 0; -} - /* RAW VBI ioctls */ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, @@ -2038,9 +1996,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, .vidioc_cropcap = vidioc_cropcap, - .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap, - .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, - .vidioc_s_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, -- cgit v1.2.3 From 2a221d34b646c7e1f44a1b1d8af4eee18015fbda Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Sep 2012 08:51:32 -0300 Subject: [media] em28xx: zero vbi_format reserved array and add try_vbi_fmt Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 971046861f7f..e26e01422a2d 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1521,6 +1521,7 @@ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; format->fmt.vbi.count[0] = dev->vbi_height; format->fmt.vbi.count[1] = dev->vbi_height; + memset(format->fmt.vbi.reserved, 0, sizeof(format->fmt.vbi.reserved)); /* Varies by video standard (NTSC, PAL, etc.) */ if (dev->norm & V4L2_STD_525_60) { @@ -1549,6 +1550,7 @@ static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv, format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; format->fmt.vbi.count[0] = dev->vbi_height; format->fmt.vbi.count[1] = dev->vbi_height; + memset(format->fmt.vbi.reserved, 0, sizeof(format->fmt.vbi.reserved)); /* Varies by video standard (NTSC, PAL, etc.) */ if (dev->norm & V4L2_STD_525_60) { @@ -1991,6 +1993,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_try_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_g_audio = vidioc_g_audio, -- cgit v1.2.3 From d3829fadc4611e96aa360b8ead5adefdf61f45ea Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Fri, 4 Jan 2013 16:16:24 -0300 Subject: [media] em28xx: convert to videobuf2 This patch converts the em28xx driver over to videobuf2. It is likely that em28xx_fh can go away entirely, but that will come in a separate patch. [mchehab@redhat.com: fix a non-trivial merge conflict with some VBI patches; CodingStyle fixes] Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/Kconfig | 3 +- drivers/media/usb/em28xx/em28xx-cards.c | 7 +- drivers/media/usb/em28xx/em28xx-dvb.c | 4 +- drivers/media/usb/em28xx/em28xx-vbi.c | 123 ++---- drivers/media/usb/em28xx/em28xx-video.c | 760 +++++++++++--------------------- drivers/media/usb/em28xx/em28xx.h | 30 +- 6 files changed, 338 insertions(+), 589 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index 094c4ecf086d..c754a80a8d8b 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -3,7 +3,7 @@ config VIDEO_EM28XX depends on VIDEO_DEV && I2C select VIDEO_TUNER select VIDEO_TVEEPROM - select VIDEOBUF_VMALLOC + select VIDEOBUF2_VMALLOC select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT select VIDEO_TVP5150 if MEDIA_SUBDRV_AUTOSELECT select VIDEO_MSP3400 if MEDIA_SUBDRV_AUTOSELECT @@ -48,7 +48,6 @@ config VIDEO_EM28XX_DVB select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT - select VIDEOBUF_DVB ---help--- This adds support for DVB cards based on the Empiatech em28xx chips. diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 1aca98f30a8d..a4d11a46fd4f 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -57,7 +57,7 @@ module_param(disable_usb_speed_check, int, 0444); MODULE_PARM_DESC(disable_usb_speed_check, "override min bandwidth requirement of 480M bps"); -static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); @@ -2965,6 +2965,8 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, const char *chip_name = default_chip_name; dev->udev = udev; + mutex_init(&dev->vb_queue_lock); + mutex_init(&dev->vb_vbi_queue_lock); mutex_init(&dev->ctrl_urb_lock); spin_lock_init(&dev->slock); @@ -3411,6 +3413,9 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); + /* initialize videobuf2 stuff */ + em28xx_vb2_setup(dev); + /* allocate device struct */ mutex_init(&dev->lock); mutex_lock(&dev->lock); diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index a70b19e07e37..01bb8006f659 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -27,7 +27,9 @@ #include "em28xx.h" #include -#include +#include +#include +#include #include #include "tuner-simple.h" #include diff --git a/drivers/media/usb/em28xx/em28xx-vbi.c b/drivers/media/usb/em28xx/em28xx-vbi.c index d74713bd1d18..39f39c527c13 100644 --- a/drivers/media/usb/em28xx/em28xx-vbi.c +++ b/drivers/media/usb/em28xx/em28xx-vbi.c @@ -41,105 +41,72 @@ MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]"); /* ------------------------------------------------------------------ */ -static void -free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) +static int vbi_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - unsigned long flags = 0; - if (in_interrupt()) - BUG(); - - /* We used to wait for the buffer to finish here, but this didn't work - because, as we were keeping the state as VIDEOBUF_QUEUED, - videobuf_queue_cancel marked it as finished for us. - (Also, it could wedge forever if the hardware was misconfigured.) - - This should be safe; by the time we get here, the buffer isn't - queued anymore. If we ever start marking the buffers as - VIDEOBUF_ACTIVE, it won't be, though. - */ - spin_lock_irqsave(&dev->slock, flags); - if (dev->usb_ctl.vbi_buf == buf) - dev->usb_ctl.vbi_buf = NULL; - spin_unlock_irqrestore(&dev->slock, flags); + struct em28xx *dev = vb2_get_drv_priv(vq); + unsigned long size; - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} + if (fmt) + size = fmt->fmt.pix.sizeimage; + else + size = dev->vbi_width * dev->vbi_height * 2; -static int -vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) -{ - struct em28xx_fh *fh = q->priv_data; - struct em28xx *dev = fh->dev; + if (0 == *nbuffers) + *nbuffers = 32; + if (*nbuffers < 2) + *nbuffers = 2; + if (*nbuffers > 32) + *nbuffers = 32; - *size = dev->vbi_width * dev->vbi_height * 2; + *nplanes = 1; + sizes[0] = size; - if (0 == *count) - *count = vbibufs; - if (*count < 2) - *count = 2; - if (*count > 32) - *count = 32; return 0; } -static int -vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, - enum v4l2_field field) +static int vbi_buffer_prepare(struct vb2_buffer *vb) { - struct em28xx_fh *fh = q->priv_data; - struct em28xx *dev = fh->dev; + struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); - int rc = 0; + unsigned long size; - buf->vb.size = dev->vbi_width * dev->vbi_height * 2; + size = dev->vbi_width * dev->vbi_height * 2; - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + if (vb2_plane_size(vb, 0) < size) { + printk(KERN_INFO "%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), size); return -EINVAL; - - buf->vb.width = dev->vbi_width; - buf->vb.height = dev->vbi_height; - buf->vb.field = field; - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(q, &buf->vb, NULL); - if (rc < 0) - goto fail; } + vb2_set_plane_payload(&buf->vb, 0, size); - buf->vb.state = VIDEOBUF_PREPARED; return 0; - -fail: - free_buffer(q, buf); - return rc; } static void -vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct em28xx_buffer *buf = container_of(vb, - struct em28xx_buffer, - vb); - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - struct em28xx_dmaqueue *vbiq = &dev->vbiq; - - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vbiq->active); -} - -static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +vbi_buffer_queue(struct vb2_buffer *vb) { + struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); - free_buffer(q, buf); + struct em28xx_dmaqueue *vbiq = &dev->vbiq; + unsigned long flags = 0; + + buf->mem = vb2_plane_vaddr(vb, 0); + buf->length = vb2_plane_size(vb, 0); + + spin_lock_irqsave(&dev->slock, flags); + list_add_tail(&buf->list, &vbiq->active); + spin_unlock_irqrestore(&dev->slock, flags); } -struct videobuf_queue_ops em28xx_vbi_qops = { - .buf_setup = vbi_setup, - .buf_prepare = vbi_prepare, - .buf_queue = vbi_queue, - .buf_release = vbi_release, + +struct vb2_ops em28xx_vbi_qops = { + .queue_setup = vbi_queue_setup, + .buf_prepare = vbi_buffer_prepare, + .buf_queue = vbi_buffer_queue, + .start_streaming = em28xx_start_analog_streaming, + .stop_streaming = em28xx_stop_vbi_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index e26e01422a2d..57510c0d383c 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -76,9 +76,9 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); MODULE_VERSION(EM28XX_VERSION); -static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; -static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; -static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; +static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; +static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; module_param_array(video_nr, int, NULL, 0444); module_param_array(vbi_nr, int, NULL, 0444); @@ -136,12 +136,13 @@ static struct em28xx_fmt format[] = { static inline void finish_buffer(struct em28xx *dev, struct em28xx_buffer *buf) { - em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - v4l2_get_timestamp(&buf->vb.ts); - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); + em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->top_field); + + buf->vb.v4l2_buf.sequence = dev->field_count++; + buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); } /* @@ -156,8 +157,8 @@ static void em28xx_copy_video(struct em28xx *dev, int linesdone, currlinedone, offset, lencopy, remain; int bytesperline = dev->width << 1; - if (buf->pos + len > buf->vb.size) - len = buf->vb.size - buf->pos; + if (buf->pos + len > buf->length) + len = buf->length - buf->pos; startread = usb_buf; remain = len; @@ -179,11 +180,11 @@ static void em28xx_copy_video(struct em28xx *dev, lencopy = bytesperline - currlinedone; lencopy = lencopy > remain ? remain : lencopy; - if ((char *)startwrite + lencopy > (char *)buf->vb_buf + buf->vb.size) { + if ((char *)startwrite + lencopy > (char *)buf->vb_buf + buf->length) { em28xx_isocdbg("Overflow of %zi bytes past buffer end (1)\n", ((char *)startwrite + lencopy) - - ((char *)buf->vb_buf + buf->vb.size)); - remain = (char *)buf->vb_buf + buf->vb.size - + ((char *)buf->vb_buf + buf->length)); + remain = (char *)buf->vb_buf + buf->length - (char *)startwrite; lencopy = remain; } @@ -205,13 +206,13 @@ static void em28xx_copy_video(struct em28xx *dev, lencopy = bytesperline; if ((char *)startwrite + lencopy > (char *)buf->vb_buf + - buf->vb.size) { + buf->length) { em28xx_isocdbg("Overflow of %zi bytes past buffer end" "(2)\n", ((char *)startwrite + lencopy) - - ((char *)buf->vb_buf + buf->vb.size)); - lencopy = remain = (char *)buf->vb_buf + buf->vb.size - - (char *)startwrite; + ((char *)buf->vb_buf + buf->length)); + lencopy = remain = (char *)buf->vb_buf + buf->length - + (char *)startwrite; } if (lencopy <= 0) break; @@ -234,8 +235,8 @@ static void em28xx_copy_vbi(struct em28xx *dev, { unsigned int offset; - if (buf->pos + len > buf->vb.size) - len = buf->vb.size - buf->pos; + if (buf->pos + len > buf->length) + len = buf->length - buf->pos; offset = buf->pos; /* Make sure the bottom field populates the second half of the frame */ @@ -292,7 +293,6 @@ static inline struct em28xx_buffer *get_next_buf(struct em28xx *dev, struct em28xx_dmaqueue *dma_q) { struct em28xx_buffer *buf; - char *outp; if (list_empty(&dma_q->active)) { em28xx_isocdbg("No active queue to serve\n"); @@ -300,12 +300,11 @@ static inline struct em28xx_buffer *get_next_buf(struct em28xx *dev, } /* Get the next buffer */ - buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); + buf = list_entry(dma_q->active.next, struct em28xx_buffer, list); /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&buf->vb); - memset(outp, 0, buf->vb.size); + list_del(&buf->list); buf->pos = 0; - buf->vb_buf = outp; + buf->vb_buf = buf->mem; return buf; } @@ -467,92 +466,118 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) } +static int get_ressource(enum v4l2_buf_type f_type) +{ + switch (f_type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return EM28XX_RESOURCE_VIDEO; + case V4L2_BUF_TYPE_VBI_CAPTURE: + return EM28XX_RESOURCE_VBI; + default: + BUG(); + return 0; + } +} + +/* Usage lock check functions */ +static int res_get(struct em28xx *dev, enum v4l2_buf_type f_type) +{ + int res_type = get_ressource(f_type); + + /* is it free? */ + if (dev->resources & res_type) { + /* no, someone else uses it */ + return -EBUSY; + } + + /* it's free, grab it */ + dev->resources |= res_type; + em28xx_videodbg("res: get %d\n", res_type); + return 0; +} + +static void res_free(struct em28xx *dev, enum v4l2_buf_type f_type) +{ + int res_type = get_ressource(f_type); + + dev->resources &= ~res_type; + em28xx_videodbg("res: put %d\n", res_type); +} + /* ------------------------------------------------------------------ - Videobuf operations + Videobuf2 operations ------------------------------------------------------------------*/ -static int -buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - struct v4l2_frequency f; - - *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) - >> 3; + struct em28xx *dev = vb2_get_drv_priv(vq); + unsigned long size; - if (0 == *count) - *count = EM28XX_DEF_BUF; + if (fmt) + size = fmt->fmt.pix.sizeimage; + else + size = (dev->width * dev->height * dev->format->depth + 7) >> 3; - if (*count < EM28XX_MIN_BUF) - *count = EM28XX_MIN_BUF; + if (size == 0) + return -EINVAL; - /* Ask tuner to go to analog or radio mode */ - memset(&f, 0, sizeof(f)); - f.frequency = dev->ctl_freq; - f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + if (0 == *nbuffers) + *nbuffers = 32; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + *nplanes = 1; + sizes[0] = size; return 0; } -/* This is called *without* dev->slock held; please keep it that way */ -static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) +static int +buffer_prepare(struct vb2_buffer *vb) { - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - unsigned long flags = 0; - if (in_interrupt()) - BUG(); + struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + unsigned long size; - /* We used to wait for the buffer to finish here, but this didn't work - because, as we were keeping the state as VIDEOBUF_QUEUED, - videobuf_queue_cancel marked it as finished for us. - (Also, it could wedge forever if the hardware was misconfigured.) + em28xx_videodbg("%s, field=%d\n", __func__, vb->v4l2_buf.field); - This should be safe; by the time we get here, the buffer isn't - queued anymore. If we ever start marking the buffers as - VIDEOBUF_ACTIVE, it won't be, though. - */ - spin_lock_irqsave(&dev->slock, flags); - if (dev->usb_ctl.vid_buf == buf) - dev->usb_ctl.vid_buf = NULL; - spin_unlock_irqrestore(&dev->slock, flags); + size = (dev->width * dev->height * dev->format->depth + 7) >> 3; - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; + if (vb2_plane_size(vb, 0) < size) { + em28xx_videodbg("%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), size); + return -EINVAL; + } + vb2_set_plane_payload(&buf->vb, 0, size); + + return 0; } -static int -buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) +int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) { - struct em28xx_fh *fh = vq->priv_data; - struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); - struct em28xx *dev = fh->dev; - int rc = 0, urb_init = 0; + struct em28xx *dev = vb2_get_drv_priv(vq); + struct v4l2_frequency f; + int rc = 0; - buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth - + 7) >> 3; + em28xx_videodbg("%s\n", __func__); - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) - return -EINVAL; + /* Make sure streaming is not already in progress for this type + of filehandle (e.g. video, vbi) */ + rc = res_get(dev, vq->type); + if (rc) + return rc; - buf->vb.width = dev->width; - buf->vb.height = dev->height; - buf->vb.field = field; + if (dev->streaming_users++ == 0) { + /* First active streaming user, so allocate all the URBs */ - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(vq, &buf->vb, NULL); - if (rc < 0) - goto fail; - } + /* Allocate the USB bandwidth */ + em28xx_set_alternate(dev); - if (!dev->usb_ctl.analog_bufs.num_bufs) - urb_init = 1; + /* Needed, since GPIO might have disabled power of + some i2c device + */ + em28xx_wake_i2c(dev); - if (urb_init) { dev->capture_type = -1; rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, dev->analog_xfer_bulk, @@ -562,52 +587,144 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, em28xx_urb_data_copy); if (rc < 0) goto fail; - } - buf->vb.state = VIDEOBUF_PREPARED; - return 0; + /* + * djh: it's not clear whether this code is still needed. I'm + * leaving it in here for now entirely out of concern for + * backward compatibility (the old code did it) + */ + + /* Ask tuner to go to analog or radio mode */ + memset(&f, 0, sizeof(f)); + f.frequency = dev->ctl_freq; + if (vq->owner && vq->owner->vdev->vfl_type == VFL_TYPE_RADIO) + f.type = V4L2_TUNER_RADIO; + else + f.type = V4L2_TUNER_ANALOG_TV; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + } fail: - free_buffer(vq, buf); return rc; } -static void -buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +int em28xx_stop_streaming(struct vb2_queue *vq) +{ + struct em28xx *dev = vb2_get_drv_priv(vq); + struct em28xx_dmaqueue *vidq = &dev->vidq; + unsigned long flags = 0; + + em28xx_videodbg("%s\n", __func__); + + res_free(dev, vq->type); + + if (dev->streaming_users-- == 1) { + /* Last active user, so shutdown all the URBS */ + em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); + } + + spin_lock_irqsave(&dev->slock, flags); + while (!list_empty(&vidq->active)) { + struct em28xx_buffer *buf; + buf = list_entry(vidq->active.next, struct em28xx_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + dev->usb_ctl.vid_buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + return 0; +} + +int em28xx_stop_vbi_streaming(struct vb2_queue *vq) { - struct em28xx_buffer *buf = container_of(vb, - struct em28xx_buffer, - vb); - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - struct em28xx_dmaqueue *vidq = &dev->vidq; + struct em28xx *dev = vb2_get_drv_priv(vq); + struct em28xx_dmaqueue *vbiq = &dev->vbiq; + unsigned long flags = 0; + + em28xx_videodbg("%s\n", __func__); + + res_free(dev, vq->type); - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vidq->active); + if (dev->streaming_users-- == 1) { + /* Last active user, so shutdown all the URBS */ + em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); + } + + spin_lock_irqsave(&dev->slock, flags); + while (!list_empty(&vbiq->active)) { + struct em28xx_buffer *buf; + buf = list_entry(vbiq->active.next, struct em28xx_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + dev->usb_ctl.vbi_buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + return 0; } -static void buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void +buffer_queue(struct vb2_buffer *vb) { - struct em28xx_buffer *buf = container_of(vb, - struct em28xx_buffer, - vb); - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = (struct em28xx *)fh->dev; + struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + struct em28xx_dmaqueue *vidq = &dev->vidq; + unsigned long flags = 0; - em28xx_isocdbg("em28xx: called buffer_release\n"); + em28xx_videodbg("%s\n", __func__); + buf->mem = vb2_plane_vaddr(vb, 0); + buf->length = vb2_plane_size(vb, 0); - free_buffer(vq, buf); + spin_lock_irqsave(&dev->slock, flags); + list_add_tail(&buf->list, &vidq->active); + spin_unlock_irqrestore(&dev->slock, flags); } -static struct videobuf_queue_ops em28xx_video_qops = { - .buf_setup = buffer_setup, +static struct vb2_ops em28xx_video_qops = { + .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, - .buf_release = buffer_release, + .start_streaming = em28xx_start_analog_streaming, + .stop_streaming = em28xx_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; +int em28xx_vb2_setup(struct em28xx *dev) +{ + int rc; + struct vb2_queue *q; + + /* Setup Videobuf2 for Video capture */ + q = &dev->vb_vidq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct em28xx_buffer); + q->ops = &em28xx_video_qops; + q->mem_ops = &vb2_vmalloc_memops; + + rc = vb2_queue_init(q); + if (rc < 0) + return rc; + + /* Setup Videobuf2 for VBI capture */ + q = &dev->vb_vbiq; + q->type = V4L2_BUF_TYPE_VBI_CAPTURE; + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct em28xx_buffer); + q->ops = &em28xx_vbi_qops; + q->mem_ops = &vb2_vmalloc_memops; + + rc = vb2_queue_init(q); + if (rc < 0) + return rc; + + return 0; +} + /********************* v4l2 interface **************************************/ static void video_mux(struct em28xx *dev, int index) @@ -640,61 +757,6 @@ static void video_mux(struct em28xx *dev, int index) em28xx_audio_analog_set(dev); } -/* Usage lock check functions */ -static int res_get(struct em28xx_fh *fh, unsigned int bit) -{ - struct em28xx *dev = fh->dev; - - if (fh->resources & bit) - /* have it already allocated */ - return 1; - - /* is it free? */ - if (dev->resources & bit) { - /* no, someone else uses it */ - return 0; - } - /* it's free, grab it */ - fh->resources |= bit; - dev->resources |= bit; - em28xx_videodbg("res: get %d\n", bit); - return 1; -} - -static int res_check(struct em28xx_fh *fh, unsigned int bit) -{ - return fh->resources & bit; -} - -static int res_locked(struct em28xx *dev, unsigned int bit) -{ - return dev->resources & bit; -} - -static void res_free(struct em28xx_fh *fh, unsigned int bits) -{ - struct em28xx *dev = fh->dev; - - BUG_ON((fh->resources & bits) != bits); - - fh->resources &= ~bits; - dev->resources &= ~bits; - em28xx_videodbg("res: put %d\n", bits); -} - -static int get_ressource(struct em28xx_fh *fh) -{ - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - return EM28XX_RESOURCE_VIDEO; - case V4L2_BUF_TYPE_VBI_CAPTURE: - return EM28XX_RESOURCE_VBI; - default: - BUG(); - return 0; - } -} - void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv) { struct em28xx *dev = priv; @@ -828,8 +890,11 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, /* the em2800 can only scale down to 50% */ height = height > (3 * maxh / 4) ? maxh : maxh / 2; width = width > (3 * maxw / 4) ? maxw : maxw / 2; - /* MaxPacketSize for em2800 is too small to capture at full resolution - * use half of maxw as the scaler can only scale to 50% */ + /* + * MaxPacketSize for em2800 is too small to capture at full + * resolution use half of maxw as the scaler can only scale + * to 50% + */ if (width == maxw && height == maxh) width /= 2; } else { @@ -875,7 +940,6 @@ static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc, /* set new image size */ get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); - em28xx_set_alternate(dev); em28xx_resolution_set(dev); return 0; @@ -884,21 +948,13 @@ static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc, static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; + struct em28xx *dev = video_drvdata(file); - rc = check_dev(dev); - if (rc < 0) - return rc; + if (dev->streaming_users > 0) + return -EBUSY; vidioc_try_fmt_vid_cap(file, priv, f); - if (videobuf_queue_is_busy(&fh->vb_vidq)) { - em28xx_errdev("%s queue busy\n", __func__); - return -EBUSY; - } - return em28xx_set_video_format(dev, f->fmt.pix.pixelformat, f->fmt.pix.width, f->fmt.pix.height); } @@ -952,10 +1008,8 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) if (rc < 0) return rc; - if (videobuf_queue_is_busy(&fh->vb_vidq)) { - em28xx_errdev("%s queue busy\n", __func__); + if (dev->streaming_users > 0) return -EBUSY; - } dev->norm = *norm; @@ -1358,69 +1412,6 @@ static int vidioc_cropcap(struct file *file, void *priv, return 0; } -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc = -EINVAL; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (unlikely(type != fh->type)) - return -EINVAL; - - em28xx_videodbg("vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n", - fh, type, fh->resources, dev->resources); - - if (unlikely(!res_get(fh, get_ressource(fh)))) - return -EBUSY; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - rc = videobuf_streamon(&fh->vb_vidq); - else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) - rc = videobuf_streamon(&fh->vb_vbiq); - - return rc; -} - -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) - return -EINVAL; - if (type != fh->type) - return -EINVAL; - - em28xx_videodbg("vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n", - fh, type, fh->resources, dev->resources); - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (res_check(fh, EM28XX_RESOURCE_VIDEO)) { - videobuf_streamoff(&fh->vb_vidq); - res_free(fh, EM28XX_RESOURCE_VIDEO); - } - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (res_check(fh, EM28XX_RESOURCE_VBI)) { - videobuf_streamoff(&fh->vb_vbiq); - res_free(fh, EM28XX_RESOURCE_VBI); - } - } - - return 0; -} - static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -1566,83 +1557,6 @@ static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv, return 0; } -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *rb) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_reqbufs(&fh->vb_vidq, rb); - else - return videobuf_reqbufs(&fh->vb_vbiq, rb); -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_querybuf(&fh->vb_vidq, b); - else { - /* FIXME: I'm not sure yet whether this is a bug in zvbi or - the videobuf framework, but we probably shouldn't be - returning a buffer larger than that which was asked for. - At a minimum, it causes a crash in zvbi since it does - a memcpy based on the source buffer length */ - int result = videobuf_querybuf(&fh->vb_vbiq, b); - b->length = dev->vbi_width * dev->vbi_height * 2; - - return result; - } -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_qbuf(&fh->vb_vidq, b); - else - return videobuf_qbuf(&fh->vb_vbiq, b); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & - O_NONBLOCK); - else - return videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags & - O_NONBLOCK); -} - /* ----------------------------------------------------------- */ /* RADIO ESPECIFIC IOCTLS */ /* ----------------------------------------------------------- */ @@ -1682,12 +1596,10 @@ static int radio_s_tuner(struct file *file, void *priv, */ static int em28xx_v4l2_open(struct file *filp) { - int errCode = 0, radio = 0; struct video_device *vdev = video_devdata(filp); struct em28xx *dev = video_drvdata(filp); enum v4l2_buf_type fh_type = 0; struct em28xx_fh *fh; - enum v4l2_field field; switch (vdev->vfl_type) { case VFL_TYPE_GRABBER: @@ -1696,9 +1608,6 @@ static int em28xx_v4l2_open(struct file *filp) case VFL_TYPE_VBI: fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; break; - case VFL_TYPE_RADIO: - radio = 1; - break; } em28xx_videodbg("open dev=%s type=%s users=%d\n", @@ -1716,13 +1625,11 @@ static int em28xx_v4l2_open(struct file *filp) } v4l2_fh_init(&fh->fh, vdev); fh->dev = dev; - fh->radio = radio; fh->type = fh_type; filp->private_data = fh; if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { em28xx_set_mode(dev, EM28XX_ANALOG_MODE); - em28xx_set_alternate(dev); em28xx_resolution_set(dev); /* Needed, since GPIO might have disabled power of @@ -1731,32 +1638,18 @@ static int em28xx_v4l2_open(struct file *filp) em28xx_wake_i2c(dev); } - if (fh->radio) { + + if (vdev->vfl_type == VFL_TYPE_RADIO) { em28xx_videodbg("video_open: setting radio device\n"); v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); } dev->users++; - if (dev->progressive) - field = V4L2_FIELD_NONE; - else - field = V4L2_FIELD_INTERLACED; - - videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops, - NULL, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, field, - sizeof(struct em28xx_buffer), fh, &dev->lock); - - videobuf_queue_vmalloc_init(&fh->vb_vbiq, &em28xx_vbi_qops, - NULL, &dev->slock, - V4L2_BUF_TYPE_VBI_CAPTURE, - V4L2_FIELD_SEQ_TB, - sizeof(struct em28xx_buffer), fh, &dev->lock); mutex_unlock(&dev->lock); v4l2_fh_add(&fh->fh); - return errCode; + return 0; } /* @@ -1810,15 +1703,7 @@ static int em28xx_v4l2_close(struct file *filp) em28xx_videodbg("users=%d\n", dev->users); mutex_lock(&dev->lock); - if (res_check(fh, EM28XX_RESOURCE_VIDEO)) { - videobuf_stop(&fh->vb_vidq); - res_free(fh, EM28XX_RESOURCE_VIDEO); - } - - if (res_check(fh, EM28XX_RESOURCE_VBI)) { - videobuf_stop(&fh->vb_vbiq); - res_free(fh, EM28XX_RESOURCE_VBI); - } + vb2_fop_release(filp); if (dev->users == 1) { /* the device is already disconnect, @@ -1828,7 +1713,6 @@ static int em28xx_v4l2_close(struct file *filp) kfree(dev->alt_max_pkt_size_isoc); mutex_unlock(&dev->lock); kfree(dev); - kfree(fh); return 0; } @@ -1836,7 +1720,6 @@ static int em28xx_v4l2_close(struct file *filp) v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); /* do this before setting alternate! */ - em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); em28xx_set_mode(dev, EM28XX_SUSPEND); /* set alternate 0 */ @@ -1848,141 +1731,19 @@ static int em28xx_v4l2_close(struct file *filp) "0 (error=%i)\n", errCode); } } - v4l2_fh_del(&fh->fh); - v4l2_fh_exit(&fh->fh); - videobuf_mmap_free(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vbiq); - kfree(fh); dev->users--; mutex_unlock(&dev->lock); return 0; } -/* - * em28xx_v4l2_read() - * will allocate buffers when called for the first time - */ -static ssize_t -em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count, - loff_t *pos) -{ - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - /* FIXME: read() is not prepared to allow changing the video - resolution while streaming. Seems a bug at em28xx_set_fmt - */ - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (res_locked(dev, EM28XX_RESOURCE_VIDEO)) - rc = -EBUSY; - else - rc = videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, - filp->f_flags & O_NONBLOCK); - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (!res_get(fh, EM28XX_RESOURCE_VBI)) - rc = -EBUSY; - else - rc = videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0, - filp->f_flags & O_NONBLOCK); - } - mutex_unlock(&dev->lock); - - return rc; -} - -/* - * em28xx_poll() - * will allocate buffers when called for the first time - */ -static unsigned int em28xx_poll(struct file *filp, poll_table *wait) -{ - struct em28xx_fh *fh = filp->private_data; - unsigned long req_events = poll_requested_events(wait); - struct em28xx *dev = fh->dev; - unsigned int res = 0; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return DEFAULT_POLLMASK; - - if (v4l2_event_pending(&fh->fh)) - res = POLLPRI; - else if (req_events & POLLPRI) - poll_wait(filp, &fh->fh.wait, wait); - - if (req_events & (POLLIN | POLLRDNORM)) { - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (!res_get(fh, EM28XX_RESOURCE_VIDEO)) - return res | POLLERR; - return videobuf_poll_stream(filp, &fh->vb_vidq, wait); - } - if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (!res_get(fh, EM28XX_RESOURCE_VBI)) - return res | POLLERR; - return res | videobuf_poll_stream(filp, &fh->vb_vbiq, wait); - } - } - return res; -} - -static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait) -{ - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - unsigned int res; - - mutex_lock(&dev->lock); - res = em28xx_poll(filp, wait); - mutex_unlock(&dev->lock); - return res; -} - -/* - * em28xx_v4l2_mmap() - */ -static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); - else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) - rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma); - mutex_unlock(&dev->lock); - - em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n", - (unsigned long)vma->vm_start, - (unsigned long)vma->vm_end-(unsigned long)vma->vm_start, - rc); - - return rc; -} - static const struct v4l2_file_operations em28xx_v4l_fops = { .owner = THIS_MODULE, .open = em28xx_v4l2_open, .release = em28xx_v4l2_close, - .read = em28xx_v4l2_read, - .poll = em28xx_v4l2_poll, - .mmap = em28xx_v4l2_mmap, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, .unlocked_ioctl = video_ioctl2, }; @@ -2000,10 +1761,13 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_s_audio = vidioc_s_audio, .vidioc_cropcap = vidioc_cropcap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_g_std = vidioc_g_std, .vidioc_querystd = vidioc_querystd, .vidioc_s_std = vidioc_s_std, @@ -2012,8 +1776,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, @@ -2029,7 +1793,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { static const struct video_device em28xx_video_template = { .fops = &em28xx_v4l_fops, - .release = video_device_release, + .release = video_device_release_empty, .ioctl_ops = &video_ioctl_ops, .tvnorms = V4L2_STD_ALL, @@ -2078,7 +1842,6 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, *vfd = *template; vfd->v4l2_dev = &dev->v4l2_dev; - vfd->release = video_device_release; vfd->debug = video_debug; vfd->lock = &dev->lock; set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); @@ -2110,10 +1873,10 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->format = &format[0]; maxw = norm_maxw(dev); - /* MaxPacketSize for em2800 is too small to capture at full resolution - * use half of maxw as the scaler can only scale to 50% */ - if (dev->board.is_em2800) - maxw /= 2; + /* MaxPacketSize for em2800 is too small to capture at full resolution + * use half of maxw as the scaler can only scale to 50% */ + if (dev->board.is_em2800) + maxw /= 2; em28xx_set_video_format(dev, format[0].fourcc, maxw, norm_maxh(dev)); @@ -2139,6 +1902,8 @@ int em28xx_register_analog_devices(struct em28xx *dev) em28xx_errdev("cannot allocate video_device.\n"); return -ENODEV; } + dev->vdev->queue = &dev->vb_vidq; + dev->vdev->queue->lock = &dev->vb_queue_lock; /* register v4l2 video video_device */ ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER, @@ -2154,6 +1919,9 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, "vbi"); + dev->vbi_dev->queue = &dev->vb_vbiq; + dev->vbi_dev->queue->lock = &dev->vb_vbi_queue_lock; + /* register v4l2 vbi video_device */ ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, vbi_nr[dev->devno]); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 7432be483c22..bf0a790f7eb9 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -31,15 +31,12 @@ #include #include -#include +#include #include #include #include #include #include -#if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE) -#include -#endif #include "tuner-xc2028.h" #include "xc5000.h" #include "em28xx-reg.h" @@ -252,8 +249,11 @@ struct em28xx_fmt { /* buffer for one video frame */ struct em28xx_buffer { /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; + struct vb2_buffer vb; + struct list_head list; + void *mem; + unsigned int length; int top_field; /* counter to control buffer fill */ @@ -480,11 +480,6 @@ struct em28xx; struct em28xx_fh { struct v4l2_fh fh; struct em28xx *dev; - int radio; - unsigned int resources; - - struct videobuf_queue vb_vidq; - struct videobuf_queue vb_vbiq; enum v4l2_buf_type type; }; @@ -545,6 +540,7 @@ struct em28xx { struct i2c_client i2c_client; /* video for linux */ int users; /* user count for exclusive use */ + int streaming_users; /* Number of actively streaming users */ struct video_device *vdev; /* video for linux device struct */ v4l2_std_id norm; /* selected tv norm */ int ctl_freq; /* selected frequency */ @@ -587,6 +583,12 @@ struct em28xx { struct video_device *vbi_dev; struct video_device *radio_dev; + /* Videobuf2 */ + struct vb2_queue vb_vidq; + struct vb2_queue vb_vbiq; + struct mutex vb_queue_lock; + struct mutex vb_vbi_queue_lock; + /* resources in use */ unsigned int resources; @@ -598,6 +600,9 @@ struct em28xx { struct em28xx_usb_ctl usb_ctl; spinlock_t slock; + unsigned int field_count; + unsigned int vbi_field_count; + /* usb transfer */ struct usb_device *udev; /* the usb device */ u8 analog_ep_isoc; /* address of isoc endpoint for analog */ @@ -709,9 +714,12 @@ void em28xx_init_extension(struct em28xx *dev); void em28xx_close_extension(struct em28xx *dev); /* Provided by em28xx-video.c */ +int em28xx_vb2_setup(struct em28xx *dev); int em28xx_register_analog_devices(struct em28xx *dev); void em28xx_release_analog_resources(struct em28xx *dev); void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv); +int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count); +int em28xx_stop_vbi_streaming(struct vb2_queue *vq); extern const struct v4l2_ctrl_ops em28xx_ctrl_ops; /* Provided by em28xx-cards.c */ @@ -723,7 +731,7 @@ int em28xx_tuner_callback(void *ptr, int component, int command, int arg); void em28xx_release_resources(struct em28xx *dev); /* Provided by em28xx-vbi.c */ -extern struct videobuf_queue_ops em28xx_vbi_qops; +extern struct vb2_ops em28xx_vbi_qops; /* printk macros */ -- cgit v1.2.3 From 2665c2995d6a6026cfc9ec118908dfccb74fb5e0 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 27 Dec 2012 19:02:43 -0300 Subject: [media] em28xx: simplify device state tracking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DEV_INITIALIZED of enum em28xx_dev_state state is used nowhere and there is no need for DEV_MISCONFIGURED, so remove this enum and use a boolean field 'disconnected' in the device struct instead. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 5 ++--- drivers/media/usb/em28xx/em28xx-core.c | 4 ++-- drivers/media/usb/em28xx/em28xx-dvb.c | 4 ++-- drivers/media/usb/em28xx/em28xx-video.c | 12 +++--------- drivers/media/usb/em28xx/em28xx.h | 12 ++---------- 5 files changed, 11 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index a4d11a46fd4f..99f2da661c32 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3530,11 +3530,10 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) "deallocation are deferred on close.\n", video_device_node_name(dev->vdev)); - dev->state |= DEV_MISCONFIGURED; em28xx_uninit_usb_xfer(dev, dev->mode); - dev->state |= DEV_DISCONNECTED; + dev->disconnected = 1; } else { - dev->state |= DEV_DISCONNECTED; + dev->disconnected = 1; em28xx_release_resources(dev); } diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index b10d959fefe5..6916e87bc624 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -77,7 +77,7 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, int ret; int pipe = usb_rcvctrlpipe(dev->udev, 0); - if (dev->state & DEV_DISCONNECTED) + if (dev->disconnected) return -ENODEV; if (len > URB_MAX_CTRL_SIZE) @@ -153,7 +153,7 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, int ret; int pipe = usb_sndctrlpipe(dev->udev, 0); - if (dev->state & DEV_DISCONNECTED) + if (dev->disconnected) return -ENODEV; if ((len < 1) || (len > URB_MAX_CTRL_SIZE)) diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 01bb8006f659..a81ec2e8cc9b 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -135,7 +135,7 @@ static inline int em28xx_dvb_urb_data_copy(struct em28xx *dev, struct urb *urb) if (!dev) return 0; - if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + if (dev->disconnected) return 0; if (urb->status < 0) @@ -1322,7 +1322,7 @@ static int em28xx_dvb_fini(struct em28xx *dev) if (dev->dvb) { struct em28xx_dvb *dvb = dev->dvb; - if (dev->state & DEV_DISCONNECTED) { + if (dev->disconnected) { /* We cannot tell the device to sleep * once it has been unplugged. */ if (dvb->fe[0]) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 57510c0d383c..40b96d17ccf8 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -418,7 +418,7 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) if (!dev) return 0; - if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + if (dev->disconnected) return 0; if (urb->status < 0) @@ -801,16 +801,10 @@ const struct v4l2_ctrl_ops em28xx_ctrl_ops = { static int check_dev(struct em28xx *dev) { - if (dev->state & DEV_DISCONNECTED) { + if (dev->disconnected) { em28xx_errdev("v4l2 ioctl: device not present\n"); return -ENODEV; } - - if (dev->state & DEV_MISCONFIGURED) { - em28xx_errdev("v4l2 ioctl: device is misconfigured; " - "close and open it again\n"); - return -EIO; - } return 0; } @@ -1708,7 +1702,7 @@ static int em28xx_v4l2_close(struct file *filp) if (dev->users == 1) { /* the device is already disconnect, free the remaining resources */ - if (dev->state & DEV_DISCONNECTED) { + if (dev->disconnected) { em28xx_release_resources(dev); kfree(dev->alt_max_pkt_size_isoc); mutex_unlock(&dev->lock); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index bf0a790f7eb9..f891a28706f5 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -439,13 +439,6 @@ struct em28xx_eeprom { u8 string_idx_table; }; -/* device states */ -enum em28xx_dev_state { - DEV_INITIALIZED = 0x01, - DEV_DISCONNECTED = 0x02, - DEV_MISCONFIGURED = 0x04, -}; - #define EM28XX_AUDIO_BUFS 5 #define EM28XX_NUM_AUDIO_PACKETS 64 #define EM28XX_AUDIO_MAX_PACKET_SIZE 196 /* static value */ @@ -492,6 +485,8 @@ struct em28xx { int devno; /* marks the number of this device */ enum em28xx_chip_id chip_id; + unsigned char disconnected:1; /* device has been diconnected */ + int audio_ifnum; struct v4l2_device v4l2_dev; @@ -563,9 +558,6 @@ struct em28xx { struct em28xx_audio adev; - /* states */ - enum em28xx_dev_state state; - /* capture state tracking */ int capture_type; unsigned char top_field:1; -- cgit v1.2.3 From 05fe2175cf87da8a5475aed422bd636475ab0412 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 27 Dec 2012 19:02:44 -0300 Subject: [media] em28xx: refactor the code in em28xx_usb_disconnect() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The main purpose of this patch is to move the call of em28xx_release_resources() after the call of em28xx_close_extension(). This is necessary, because some resources might be needed/used by the extensions fini() functions when they get closed. Also mark the device as disconnected earlier in this function and unify the em28xx_uninit_usb_xfer() calls for analog and digital mode. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 99f2da661c32..4d849bfa2db3 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3507,6 +3507,8 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) if (!dev) return; + dev->disconnected = 1; + if (dev->is_audio_only) { mutex_lock(&dev->lock); em28xx_close_extension(dev); @@ -3518,32 +3520,26 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) flush_request_modules(dev); - /* wait until all current v4l2 io is finished then deallocate - resources */ mutex_lock(&dev->lock); v4l2_device_disconnect(&dev->v4l2_dev); if (dev->users) { - em28xx_warn - ("device %s is open! Deregistration and memory " - "deallocation are deferred on close.\n", - video_device_node_name(dev->vdev)); + em28xx_warn("device %s is open! Deregistration and memory deallocation are deferred on close.\n", + video_device_node_name(dev->vdev)); - em28xx_uninit_usb_xfer(dev, dev->mode); - dev->disconnected = 1; - } else { - dev->disconnected = 1; - em28xx_release_resources(dev); + em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); + em28xx_uninit_usb_xfer(dev, EM28XX_DIGITAL_MODE); } - /* free DVB isoc buffers */ - em28xx_uninit_usb_xfer(dev, EM28XX_DIGITAL_MODE); + em28xx_close_extension(dev); + /* NOTE: must be called BEFORE the resources are released */ + + if (!dev->users) + em28xx_release_resources(dev); mutex_unlock(&dev->lock); - em28xx_close_extension(dev); - if (!dev->users) { kfree(dev->alt_max_pkt_size_isoc); kfree(dev); -- cgit v1.2.3 From 6ea887efadec30ec830ed9466073715b7d339d2b Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 27 Dec 2012 19:02:47 -0300 Subject: [media] em28xx: IR RC: move assignment of get_key functions from *_change_protocol() functions to em28xx_ir_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The get_key functions are independent from the selected protocol, so assign them once only at device initialization. [mchehab@redhat.com: fix a merge conflict] Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 5b3292c8310b..07f6030d7455 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -384,7 +384,6 @@ static int em2860_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) *rc_type = ir->rc_type; return -EINVAL; } - ir->get_key = default_polling_getkey; em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, EM28XX_XCLK_IR_RC5_MODE); @@ -420,10 +419,7 @@ static int em2874_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) *rc_type = ir->rc_type; return -EINVAL; } - - ir->get_key = em2874_polling_getkey; em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1); - em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, EM28XX_XCLK_IR_RC5_MODE); @@ -633,10 +629,12 @@ static int em28xx_ir_init(struct em28xx *dev) case CHIP_ID_EM2860: case CHIP_ID_EM2883: rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC; + ir->get_key = default_polling_getkey; break; case CHIP_ID_EM2884: case CHIP_ID_EM2874: case CHIP_ID_EM28174: + ir->get_key = em2874_polling_getkey; rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC | RC_BIT_RC6_0; break; default: -- cgit v1.2.3 From f5ae371aca34bd0660a75f8838198466e9d5166c Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 3 Jan 2013 14:27:02 -0300 Subject: [media] em28xx: respect the message size constraints for i2c transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The em2800 can transfer up to 4 bytes per i2c message. All other em25xx/em27xx/28xx chips can transfer at least 64 bytes per message. I2C adapters should never split messages transferred via the I2C subsystem into multiple message transfers, because the result will almost always NOT be the same as when the whole data is transferred to the I2C client in a single message. If the message size exceeds the capabilities of the I2C adapter, -EOPNOTSUPP should be returned. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-i2c.c | 44 ++++++++++++++--------------------- 1 file changed, 18 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 44533e4574ff..c508c1297a26 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -50,14 +50,18 @@ do { \ } while (0) /* - * em2800_i2c_send_max4() - * send up to 4 bytes to the i2c device + * em2800_i2c_send_bytes() + * send up to 4 bytes to the em2800 i2c device */ -static int em2800_i2c_send_max4(struct em28xx *dev, u8 addr, u8 *buf, u16 len) +static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { int ret; int write_timeout; u8 b2[6]; + + if (len < 1 || len > 4) + return -EOPNOTSUPP; + BUG_ON(len < 1 || len > 4); b2[5] = 0x80 + len - 1; b2[4] = addr; @@ -85,29 +89,6 @@ static int em2800_i2c_send_max4(struct em28xx *dev, u8 addr, u8 *buf, u16 len) return -EIO; } -/* - * em2800_i2c_send_bytes() - */ -static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) -{ - u8 *bufPtr = buf; - int ret; - int wrcount = 0; - int count; - int maxLen = 4; - while (len > 0) { - count = (len > maxLen) ? maxLen : len; - ret = em2800_i2c_send_max4(dev, addr, bufPtr, count); - if (ret > 0) { - len -= count; - bufPtr += count; - wrcount += count; - } else - return (ret < 0) ? ret : -EFAULT; - } - return wrcount; -} - /* * em2800_i2c_check_for_device() * check if there is a i2c_device at the supplied address @@ -150,6 +131,10 @@ static int em2800_i2c_check_for_device(struct em28xx *dev, u8 addr) static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { int ret; + + if (len < 1 || len > 4) + return -EOPNOTSUPP; + /* check for the device and set i2c read address */ ret = em2800_i2c_check_for_device(dev, addr); if (ret) { @@ -176,6 +161,9 @@ static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, int wrcount = 0; int write_timeout, ret; + if (len < 1 || len > 64) + return -EOPNOTSUPP; + wrcount = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len); /* Seems to be required after a write */ @@ -197,6 +185,10 @@ static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len) { int ret; + + if (len < 1 || len > 64) + return -EOPNOTSUPP; + ret = dev->em28xx_read_reg_req_len(dev, 2, addr, buf, len); if (ret < 0) { em28xx_warn("reading i2c device failed (error=%i)\n", ret); -- cgit v1.2.3 From 2fcc82d8831a74afd55c3cb898beb9fde5f2a1fd Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 3 Jan 2013 14:27:03 -0300 Subject: [media] em28xx: fix two severe bugs in function em2800_i2c_recv_bytes() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Function em2800_i2c_recv_bytes() has 2 severe bugs: 1) It does not wait for the i2c read to complete before reading the received message content from the bridge registers. 2) Reading more than 1 byte doesn't work The former can result in data corruption, the latter always does. The rewritten code also superseds the content of function em2800_i2c_check_for_device(). Tested with device "Terratec Cinergy 200 USB". [mchehab@redhat.com: Fix CodingStyle issues] Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-i2c.c | 104 +++++++++++++++++++--------------- drivers/media/usb/em28xx/em28xx.h | 2 +- 2 files changed, 58 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index c508c1297a26..67a8e623dd8c 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -73,12 +73,14 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) if (len > 3) b2[0] = buf[3]; + /* trigger write */ ret = dev->em28xx_write_regs(dev, 4 - len, &b2[4 - len], 2 + len); if (ret != 2 + len) { em28xx_warn("writing to i2c device failed (error=%i)\n", ret); return -EIO; } - for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; + /* wait for completion */ + for (write_timeout = EM2800_I2C_XFER_TIMEOUT; write_timeout > 0; write_timeout -= 5) { ret = dev->em28xx_read_reg(dev, 0x05); if (ret == 0x80 + len - 1) @@ -90,66 +92,74 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) } /* - * em2800_i2c_check_for_device() - * check if there is a i2c_device at the supplied address + * em2800_i2c_recv_bytes() + * read up to 4 bytes from the em2800 i2c device */ -static int em2800_i2c_check_for_device(struct em28xx *dev, u8 addr) +static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { - u8 msg; + u8 buf2[4]; int ret; - int write_timeout; - msg = addr; - ret = dev->em28xx_write_regs(dev, 0x04, &msg, 1); - if (ret < 0) { - em28xx_warn("setting i2c device address failed (error=%i)\n", - ret); - return ret; - } - msg = 0x84; - ret = dev->em28xx_write_regs(dev, 0x05, &msg, 1); - if (ret < 0) { - em28xx_warn("preparing i2c read failed (error=%i)\n", ret); - return ret; + int read_timeout; + int i; + + if (len < 1 || len > 4) + return -EOPNOTSUPP; + + /* trigger read */ + buf2[1] = 0x84 + len - 1; + buf2[0] = addr; + ret = dev->em28xx_write_regs(dev, 0x04, buf2, 2); + if (ret != 2) { + em28xx_warn("failed to trigger read from i2c address 0x%x " + "(error=%i)\n", addr, ret); + return (ret < 0) ? ret : -EIO; } - for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; - write_timeout -= 5) { - unsigned reg = dev->em28xx_read_reg(dev, 0x5); - if (reg == 0x94) + /* wait for completion */ + for (read_timeout = EM2800_I2C_XFER_TIMEOUT; read_timeout > 0; + read_timeout -= 5) { + ret = dev->em28xx_read_reg(dev, 0x05); + if (ret == 0x84 + len - 1) { + break; + } else if (ret == 0x94 + len - 1) { return -ENODEV; - else if (reg == 0x84) - return 0; + } else if (ret < 0) { + em28xx_warn("failed to get i2c transfer status from " + "bridge register (error=%i)\n", ret); + return ret; + } msleep(5); } - return -ENODEV; + if (ret != 0x84 + len - 1) + em28xx_warn("read from i2c device at 0x%x timed out\n", addr); + + /* get the received message */ + ret = dev->em28xx_read_reg_req_len(dev, 0x00, 4-len, buf2, len); + if (ret != len) { + em28xx_warn("reading from i2c device at 0x%x failed: " + "couldn't get the received message from the bridge " + "(error=%i)\n", addr, ret); + return (ret < 0) ? ret : -EIO; + } + for (i = 0; i < len; i++) + buf[i] = buf2[len - 1 - i]; + + return ret; } /* - * em2800_i2c_recv_bytes() - * read from the i2c device + * em2800_i2c_check_for_device() + * check if there is an i2c device at the supplied address */ -static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) +static int em2800_i2c_check_for_device(struct em28xx *dev, u8 addr) { + u8 buf; int ret; - if (len < 1 || len > 4) - return -EOPNOTSUPP; - - /* check for the device and set i2c read address */ - ret = em2800_i2c_check_for_device(dev, addr); - if (ret) { - em28xx_warn - ("preparing read at i2c address 0x%x failed (error=%i)\n", - addr, ret); - return ret; - } - ret = dev->em28xx_read_reg_req_len(dev, 0x0, 0x3, buf, len); - if (ret < 0) { - em28xx_warn("reading from i2c device at 0x%x failed (error=%i)", - addr, ret); - return ret; - } - return ret; + ret = em2800_i2c_recv_bytes(dev, addr, &buf, 1); + if (ret == 1) + return 0; + return (ret < 0) ? ret : -EIO; } /* @@ -167,7 +177,7 @@ static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, wrcount = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len); /* Seems to be required after a write */ - for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; + for (write_timeout = EM2800_I2C_XFER_TIMEOUT; write_timeout > 0; write_timeout -= 5) { ret = dev->em28xx_read_reg(dev, 0x05); if (!ret) diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index f891a28706f5..2aa4b8472a62 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -194,7 +194,7 @@ */ /* time in msecs to wait for i2c writes to finish */ -#define EM2800_I2C_WRITE_TIMEOUT 20 +#define EM2800_I2C_XFER_TIMEOUT 20 enum em28xx_mode { EM28XX_SUSPEND, -- cgit v1.2.3 From eaf33c404cd60ba4b442324766abbb5da8c94381 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 3 Jan 2013 14:27:04 -0300 Subject: [media] em28xx: fix the i2c adapter functionality flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I2C_FUNC_SMBUS_EMUL includes flag I2C_FUNC_SMBUS_WRITE_BLOCK_DATA which signals that up to 31 data bytes can be written to the ic2 client. But the EM2800 supports only i2c messages with max. 4 data bytes. I2C_FUNC_IC2 should be set if a master_xfer function pointer is provided in struct i2c_algorithm. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-i2c.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 67a8e623dd8c..4a24ed08e46a 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -445,7 +445,11 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) */ static u32 functionality(struct i2c_adapter *adap) { - return I2C_FUNC_SMBUS_EMUL; + struct em28xx *dev = adap->algo_data; + u32 func_flags = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; + if (dev->board.is_em2800) + func_flags &= ~I2C_FUNC_SMBUS_WRITE_BLOCK_DATA; + return func_flags; } static struct i2c_algorithm em28xx_algo = { -- cgit v1.2.3 From 45f04e82d035006afe5023850393e9b3b74b85c2 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 3 Jan 2013 14:27:05 -0300 Subject: [media] em28xx: fix+improve+unify i2c error handling, debug messages and code comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - do not pass USB specific error codes to userspace/i2c-subsystem - unify the returned error codes and make them compliant with the i2c subsystem spec - check number of actually transferred bytes (via USB) everywehere - fix/improve debug messages - improve code comments Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 5 +- drivers/media/usb/em28xx/em28xx-i2c.c | 116 ++++++++++++++++++++++++--------- 2 files changed, 89 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 6916e87bc624..80f87bbbd554 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -101,7 +101,7 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, if (reg_debug) printk(" failed!\n"); mutex_unlock(&dev->ctrl_urb_lock); - return ret; + return usb_translate_errors(ret); } if (len) @@ -182,6 +182,9 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, 0x0000, reg, dev->urb_buf, len, HZ); mutex_unlock(&dev->ctrl_urb_lock); + if (ret < 0) + return usb_translate_errors(ret); + if (dev->wait_after_write) msleep(dev->wait_after_write); diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 4a24ed08e46a..f784b516ae1c 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -76,18 +76,26 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) /* trigger write */ ret = dev->em28xx_write_regs(dev, 4 - len, &b2[4 - len], 2 + len); if (ret != 2 + len) { - em28xx_warn("writing to i2c device failed (error=%i)\n", ret); - return -EIO; + em28xx_warn("failed to trigger write to i2c address 0x%x " + "(error=%i)\n", addr, ret); + return (ret < 0) ? ret : -EIO; } /* wait for completion */ for (write_timeout = EM2800_I2C_XFER_TIMEOUT; write_timeout > 0; write_timeout -= 5) { ret = dev->em28xx_read_reg(dev, 0x05); - if (ret == 0x80 + len - 1) + if (ret == 0x80 + len - 1) { return len; + } else if (ret == 0x94 + len - 1) { + return -ENODEV; + } else if (ret < 0) { + em28xx_warn("failed to get i2c transfer status from " + "bridge register (error=%i)\n", ret); + return ret; + } msleep(5); } - em28xx_warn("i2c write timed out\n"); + em28xx_warn("write to i2c device at 0x%x timed out\n", addr); return -EIO; } @@ -168,24 +176,48 @@ static int em2800_i2c_check_for_device(struct em28xx *dev, u8 addr) static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len, int stop) { - int wrcount = 0; int write_timeout, ret; if (len < 1 || len > 64) return -EOPNOTSUPP; + /* NOTE: limited by the USB ctrl message constraints + * Zero length reads always succeed, even if no device is connected */ - wrcount = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len); + /* Write to i2c device */ + ret = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len); + if (ret != len) { + if (ret < 0) { + em28xx_warn("writing to i2c device at 0x%x failed " + "(error=%i)\n", addr, ret); + return ret; + } else { + em28xx_warn("%i bytes write to i2c device at 0x%x " + "requested, but %i bytes written\n", + len, addr, ret); + return -EIO; + } + } - /* Seems to be required after a write */ + /* Check success of the i2c operation */ for (write_timeout = EM2800_I2C_XFER_TIMEOUT; write_timeout > 0; write_timeout -= 5) { ret = dev->em28xx_read_reg(dev, 0x05); - if (!ret) - break; + if (ret == 0) { /* success */ + return len; + } else if (ret == 0x10) { + return -ENODEV; + } else if (ret < 0) { + em28xx_warn("failed to read i2c transfer status from " + "bridge (error=%i)\n", ret); + return ret; + } msleep(5); + /* NOTE: do we really have to wait for success ? + Never seen anything else than 0x00 or 0x10 + (even with high payload) ... */ } - - return wrcount; + em28xx_warn("write to i2c device at 0x%x timed out\n", addr); + return -EIO; } /* @@ -198,15 +230,40 @@ static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len) if (len < 1 || len > 64) return -EOPNOTSUPP; + /* NOTE: limited by the USB ctrl message constraints + * Zero length reads always succeed, even if no device is connected */ + /* Read data from i2c device */ ret = dev->em28xx_read_reg_req_len(dev, 2, addr, buf, len); + if (ret != len) { + if (ret < 0) { + em28xx_warn("reading from i2c device at 0x%x failed " + "(error=%i)\n", addr, ret); + return ret; + } else { + em28xx_warn("%i bytes requested from i2c device at " + "0x%x, but %i bytes received\n", + len, addr, ret); + return -EIO; + } + } + + /* Check success of the i2c operation */ + ret = dev->em28xx_read_reg(dev, 0x05); if (ret < 0) { - em28xx_warn("reading i2c device failed (error=%i)\n", ret); + em28xx_warn("failed to read i2c transfer status from " + "bridge (error=%i)\n", ret); return ret; } - if (dev->em28xx_read_reg(dev, 0x5) != 0) - return -ENODEV; - return ret; + if (ret > 0) { + if (ret == 0x10) { + return -ENODEV; + } else { + em28xx_warn("unknown i2c error (status=%i)\n", ret); + return -EIO; + } + } + return len; } /* @@ -216,15 +273,12 @@ static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len) static int em28xx_i2c_check_for_device(struct em28xx *dev, u16 addr) { int ret; + u8 buf; - ret = dev->em28xx_read_reg_req(dev, 2, addr); - if (ret < 0) { - em28xx_warn("reading from i2c device failed (error=%i)\n", ret); - return ret; - } - if (dev->em28xx_read_reg(dev, 0x5) != 0) - return -ENODEV; - return 0; + ret = em28xx_i2c_recv_bytes(dev, addr, &buf, 1); + if (ret == 1) + return 0; + return (ret < 0) ? ret : -EIO; } /* @@ -249,11 +303,11 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, rc = em2800_i2c_check_for_device(dev, addr); else rc = em28xx_i2c_check_for_device(dev, addr); - if (rc < 0) { - dprintk2(2, " no device\n"); + if (rc == -ENODEV) { + if (i2c_debug >= 2) + printk(" no device\n"); return rc; } - } else if (msgs[i].flags & I2C_M_RD) { /* read bytes */ if (dev->board.is_em2800) @@ -284,16 +338,16 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, msgs[i].len, i == num - 1); } - if (rc < 0) - goto err; + if (rc < 0) { + if (i2c_debug >= 2) + printk(" ERROR: %i\n", rc); + return rc; + } if (i2c_debug >= 2) printk("\n"); } return num; -err: - dprintk2(2, " ERROR: %i\n", rc); - return rc; } /* based on linux/sunrpc/svcauth.h and linux/hash.h -- cgit v1.2.3 From 90271964c96f89862b3e06e184e770e16cca7c8f Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 3 Jan 2013 14:27:06 -0300 Subject: [media] em28xx: consider the message length limitation of the i2c adapter when reading the eeprom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EEPROMs are currently read in blocks of 16 bytes, but the em2800 is limited to 4 bytes per read. All other chip variants support reading of max. 64 bytes at once (according to the em258x datasheet; also verified with em2710, em2882, and em28174). Since em2800_i2c_recv_bytes() has been fixed to return with -EOPNOTSUPP when more than 4 bytes are requested, EEPROM reading with this chip is broken. It was actually broken before that change, too, it just didn't throw an error because the i2c adapter silently returned trash data (for all reads >1 byte !). Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-i2c.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index f784b516ae1c..9ae8f6051d8e 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -379,7 +379,7 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) { unsigned char buf, *p = eedata; struct em28xx_eeprom *em_eeprom = (void *)eedata; - int i, err, size = len, block; + int i, err, size = len, block, block_max; if (dev->chip_id == CHIP_ID_EM2874 || dev->chip_id == CHIP_ID_EM28174 || @@ -412,9 +412,15 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) dev->name, err); return err; } + + if (dev->board.is_em2800) + block_max = 4; + else + block_max = 64; + while (size > 0) { - if (size > 16) - block = 16; + if (size > block_max) + block = block_max; else block = size; -- cgit v1.2.3 From 317efce991620adc589b3005b9baed433dcb2a56 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 24 Nov 2012 21:35:48 -0300 Subject: [media] v4l: Reset subdev v4l2_dev field to NULL if registration fails When subdev registration fails the subdev v4l2_dev field is left to a non-NULL value. Later calls to v4l2_device_unregister_subdev() will consider the subdev as registered and will module_put() the subdev module without any matching module_get(). Fix this by setting the subdev v4l2_dev field to NULL in v4l2_device_register_subdev() when the function fails. Signed-off-by: Laurent Pinchart Cc: stable@vger.kernel.org Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-device.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c index 513969fa695d..98a7f5e9cb1b 100644 --- a/drivers/media/v4l2-core/v4l2-device.c +++ b/drivers/media/v4l2-core/v4l2-device.c @@ -159,31 +159,21 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, sd->v4l2_dev = v4l2_dev; if (sd->internal_ops && sd->internal_ops->registered) { err = sd->internal_ops->registered(sd); - if (err) { - module_put(sd->owner); - return err; - } + if (err) + goto error_module; } /* This just returns 0 if either of the two args is NULL */ err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler, NULL); - if (err) { - if (sd->internal_ops && sd->internal_ops->unregistered) - sd->internal_ops->unregistered(sd); - module_put(sd->owner); - return err; - } + if (err) + goto error_unregister; #if defined(CONFIG_MEDIA_CONTROLLER) /* Register the entity. */ if (v4l2_dev->mdev) { err = media_device_register_entity(v4l2_dev->mdev, entity); - if (err < 0) { - if (sd->internal_ops && sd->internal_ops->unregistered) - sd->internal_ops->unregistered(sd); - module_put(sd->owner); - return err; - } + if (err < 0) + goto error_unregister; } #endif @@ -192,6 +182,14 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, spin_unlock(&v4l2_dev->lock); return 0; + +error_unregister: + if (sd->internal_ops && sd->internal_ops->unregistered) + sd->internal_ops->unregistered(sd); +error_module: + module_put(sd->owner); + sd->v4l2_dev = NULL; + return err; } EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); -- cgit v1.2.3 From 3a799c27b3faebaf5a7a6149dfe8f705451b241f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Jan 2013 01:27:21 -0200 Subject: [media] em28xx: declare em28xx_stop_streaming as static That fixes the following warning: drivers/media/usb/em28xx/em28xx-video.c:611:5: warning: no previous prototype for 'em28xx_stop_streaming' [-Wmissing-prototypes] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 40b96d17ccf8..75027e390738 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -608,7 +608,7 @@ fail: return rc; } -int em28xx_stop_streaming(struct vb2_queue *vq) +static int em28xx_stop_streaming(struct vb2_queue *vq) { struct em28xx *dev = vb2_get_drv_priv(vq); struct em28xx_dmaqueue *vidq = &dev->vidq; -- cgit v1.2.3 From daf16bab1eaf5a82217697bfb91eb7d9c9745d0d Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 28 Nov 2012 23:56:15 -0300 Subject: [media] mt9v022: fix potential NULL pointer dereference in mt9v022_probe() The dereference to 'icl' should be moved below the NULL test. Reported-by: Fengguang Wu Signed-off-by: Wei Yongjun Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/mt9v022.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c index d40a8858be01..75098024d477 100644 --- a/drivers/media/i2c/soc_camera/mt9v022.c +++ b/drivers/media/i2c/soc_camera/mt9v022.c @@ -875,7 +875,7 @@ static int mt9v022_probe(struct i2c_client *client, struct mt9v022 *mt9v022; struct soc_camera_link *icl = soc_camera_i2c_to_link(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct mt9v022_platform_data *pdata = icl->priv; + struct mt9v022_platform_data *pdata; int ret; if (!icl) { @@ -893,6 +893,7 @@ static int mt9v022_probe(struct i2c_client *client, if (!mt9v022) return -ENOMEM; + pdata = icl->priv; v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops); v4l2_ctrl_handler_init(&mt9v022->hdl, 6); v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, -- cgit v1.2.3 From 7d051b35d5196ad6011a17e751dbd3d180abb046 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 20 Dec 2012 13:02:51 -0300 Subject: [media] soc-camera: properly fix camera probing races The recently introduced host_lock causes lockdep warnings, besides, list enumeration in scan_add_host() must be protected by holdint the list_lock. OTOH, holding .video_lock in soc_camera_open() isn't enough to protect the host during its building of the pipeline, because .video_lock is per soc-camera device. If, e.g. more than one sensor can be attached to a host and the user tries to open both device nodes simultaneously, host's .add() method can be called simultaneously for both sensors. Fix these problems by holding list_lock instead of .host_lock in scan_add_host() and taking it shortly at the beginning of soc_camera_open(), and using .host_lock to protect host's .add() and .remove() operations only. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/soc_camera.c | 22 +++++++++++++++++++--- include/media/soc_camera.h | 2 +- 2 files changed, 20 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index a8ca956e7a40..1070a0c496d8 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -517,7 +517,14 @@ static int soc_camera_open(struct file *file) /* No device driver attached */ return -ENODEV; + /* + * Don't mess with the host during probe: wait until the loop in + * scan_add_host() completes + */ + if (mutex_lock_interruptible(&list_lock)) + return -ERESTARTSYS; ici = to_soc_camera_host(icd->parent); + mutex_unlock(&list_lock); if (mutex_lock_interruptible(&icd->video_lock)) return -ERESTARTSYS; @@ -548,7 +555,6 @@ static int soc_camera_open(struct file *file) if (icl->reset) icl->reset(icd->pdev); - /* Don't mess with the host during probe */ mutex_lock(&ici->host_lock); ret = ici->ops->add(icd); mutex_unlock(&ici->host_lock); @@ -602,7 +608,9 @@ esfmt: eresume: __soc_camera_power_off(icd); epower: + mutex_lock(&ici->host_lock); ici->ops->remove(icd); + mutex_unlock(&ici->host_lock); eiciadd: icd->use_count--; module_put(ici->ops->owner); @@ -625,7 +633,9 @@ static int soc_camera_close(struct file *file) if (ici->ops->init_videobuf2) vb2_queue_release(&icd->vb2_vidq); + mutex_lock(&ici->host_lock); ici->ops->remove(icd); + mutex_unlock(&ici->host_lock); __soc_camera_power_off(icd); } @@ -1052,7 +1062,7 @@ static void scan_add_host(struct soc_camera_host *ici) { struct soc_camera_device *icd; - mutex_lock(&ici->host_lock); + mutex_lock(&list_lock); list_for_each_entry(icd, &devices, list) { if (icd->iface == ici->nr) { @@ -1061,7 +1071,7 @@ static void scan_add_host(struct soc_camera_host *ici) } } - mutex_unlock(&ici->host_lock); + mutex_unlock(&list_lock); } #ifdef CONFIG_I2C_BOARDINFO @@ -1148,7 +1158,9 @@ static int soc_camera_probe(struct soc_camera_device *icd) if (icl->reset) icl->reset(icd->pdev); + mutex_lock(&ici->host_lock); ret = ici->ops->add(icd); + mutex_unlock(&ici->host_lock); if (ret < 0) goto eadd; @@ -1220,7 +1232,9 @@ static int soc_camera_probe(struct soc_camera_device *icd) icd->field = mf.field; } + mutex_lock(&ici->host_lock); ici->ops->remove(icd); + mutex_unlock(&ici->host_lock); mutex_unlock(&icd->video_lock); @@ -1242,7 +1256,9 @@ eadddev: video_device_release(icd->vdev); icd->vdev = NULL; evdc: + mutex_lock(&ici->host_lock); ici->ops->remove(icd); + mutex_unlock(&ici->host_lock); eadd: ereg: v4l2_ctrl_handler_free(&icd->ctrl_handler); diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 6442edc2a151..0370a9517282 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -62,7 +62,7 @@ struct soc_camera_device { struct soc_camera_host { struct v4l2_device v4l2_dev; struct list_head list; - struct mutex host_lock; /* Protect during probing */ + struct mutex host_lock; /* Protect pipeline modifications */ unsigned char nr; /* Host number */ u32 capabilities; void *priv; -- cgit v1.2.3 From 8a97d4c11756ab6bab8582126d0f1b5c00b067ad Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 3 Jan 2013 11:21:02 -0300 Subject: [media] soc-camera: fix repeated regulator requesting Currently devm_regulator_bulk_get() is called by soc-camera during host driver probing, but regulators are attached to the camera platform device, that is staying, independent whether the host probed successfully or not. This can lead to repeated regulator requesting, if the host driver is re-probed. Move the call to platform device probing to avoid this. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/soc_camera.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 1070a0c496d8..6552856ea59b 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -1149,11 +1149,6 @@ static int soc_camera_probe(struct soc_camera_device *icd) if (ret < 0) return ret; - ret = devm_regulator_bulk_get(icd->pdev, icl->num_regulators, - icl->regulators); - if (ret < 0) - goto ereg; - /* The camera could have been already on, try to reset */ if (icl->reset) icl->reset(icd->pdev); @@ -1260,7 +1255,6 @@ evdc: ici->ops->remove(icd); mutex_unlock(&ici->host_lock); eadd: -ereg: v4l2_ctrl_handler_free(&icd->ctrl_handler); return ret; } @@ -1549,6 +1543,7 @@ static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev) { struct soc_camera_link *icl = pdev->dev.platform_data; struct soc_camera_device *icd; + int ret; if (!icl) return -EINVAL; @@ -1557,6 +1552,11 @@ static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev) if (!icd) return -ENOMEM; + ret = devm_regulator_bulk_get(&pdev->dev, icl->num_regulators, + icl->regulators); + if (ret < 0) + return ret; + icd->iface = icl->bus_id; icd->link = icl; icd->pdev = &pdev->dev; -- cgit v1.2.3 From dd669e907cbe1cf33f9cbbff79af2b5c271cdd89 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 24 Dec 2012 09:31:33 -0300 Subject: [media] soc-camera: remove struct soc_camera_device::video_lock Currently soc-camera has a per-device node lock, used for video operations and a per-host lock for code paths, modifying host's pipeline. Manipulating the two locks increases complexity and doesn't bring any advantages. This patch removes the per-device lock and uses the per-host lock for all operations. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/atmel-isi.c | 4 +- drivers/media/platform/soc_camera/mx1_camera.c | 3 +- drivers/media/platform/soc_camera/mx2_camera.c | 1 - drivers/media/platform/soc_camera/mx3_camera.c | 4 +- drivers/media/platform/soc_camera/omap1_camera.c | 4 +- drivers/media/platform/soc_camera/pxa_camera.c | 6 +-- .../platform/soc_camera/sh_mobile_ceu_camera.c | 4 +- drivers/media/platform/soc_camera/soc_camera.c | 51 ++++++++++------------ include/media/soc_camera.h | 3 +- 9 files changed, 35 insertions(+), 45 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index c8d748a31944..eba1f6b2b0fb 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -745,7 +745,7 @@ static int isi_camera_get_formats(struct soc_camera_device *icd, return formats; } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static int isi_camera_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -770,7 +770,7 @@ static int isi_camera_add_device(struct soc_camera_device *icd) icd->devnum); return 0; } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static void isi_camera_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); diff --git a/drivers/media/platform/soc_camera/mx1_camera.c b/drivers/media/platform/soc_camera/mx1_camera.c index 674ded646b66..4b661e87d8b1 100644 --- a/drivers/media/platform/soc_camera/mx1_camera.c +++ b/drivers/media/platform/soc_camera/mx1_camera.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -373,7 +372,7 @@ static void mx1_camera_init_videobuf(struct videobuf_queue *q, videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, icd->parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct mx1_buffer), icd, &icd->video_lock); + sizeof(struct mx1_buffer), icd, &icd->host_lock); } static int mclk_get_divisor(struct mx1_camera_dev *pcdev) diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 28d5c84eef82..bf2740f7c4d1 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c index 574d12522f95..37d0d0ef8adf 100644 --- a/drivers/media/platform/soc_camera/mx3_camera.c +++ b/drivers/media/platform/soc_camera/mx3_camera.c @@ -510,7 +510,7 @@ static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam, clk_set_rate(mx3_cam->clk, rate); } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static int mx3_camera_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -530,7 +530,7 @@ static int mx3_camera_add_device(struct soc_camera_device *icd) return 0; } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static void mx3_camera_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c index 8f9c1f44544c..dcf7be814776 100644 --- a/drivers/media/platform/soc_camera/omap1_camera.c +++ b/drivers/media/platform/soc_camera/omap1_camera.c @@ -1383,12 +1383,12 @@ static void omap1_cam_init_videobuf(struct videobuf_queue *q, videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops, icd->parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct omap1_cam_buf), icd, &icd->video_lock); + sizeof(struct omap1_cam_buf), icd, &icd->host_lock); else videobuf_queue_sg_init(q, &omap1_videobuf_ops, icd->parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct omap1_cam_buf), icd, &icd->video_lock); + sizeof(struct omap1_cam_buf), icd, &icd->host_lock); /* use videobuf mode (auto)selected with the module parameter */ pcdev->vb_mode = sg_mode ? OMAP1_CAM_DMA_SG : OMAP1_CAM_DMA_CONTIG; diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c index 8ff961eec39d..f3c1b6202425 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/soc_camera/pxa_camera.c @@ -842,7 +842,7 @@ static void pxa_camera_init_videobuf(struct videobuf_queue *q, */ videobuf_queue_sg_init(q, &pxa_videobuf_ops, NULL, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct pxa_buffer), icd, &icd->video_lock); + sizeof(struct pxa_buffer), icd, &icd->host_lock); } static u32 mclk_get_divisor(struct platform_device *pdev, @@ -958,7 +958,7 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) /* * The following two functions absolutely depend on the fact, that * there can be only one camera on PXA quick capture interface - * Called with .video_lock held + * Called with .host_lock held */ static int pxa_camera_add_device(struct soc_camera_device *icd) { @@ -978,7 +978,7 @@ static int pxa_camera_add_device(struct soc_camera_device *icd) return 0; } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static void pxa_camera_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 9f021043cfe6..cdf173bbcc93 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -543,7 +543,7 @@ static struct v4l2_subdev *find_csi2(struct sh_mobile_ceu_dev *pcdev) return NULL; } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -587,7 +587,7 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) return 0; } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 6552856ea59b..ae89f980e82b 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -383,7 +383,7 @@ static int soc_camera_prepare_buf(struct file *file, void *priv, return vb2_prepare_buf(&icd->vb2_vidq, b); } -/* Always entered with .video_lock held */ +/* Always entered with .host_lock held */ static int soc_camera_init_user_formats(struct soc_camera_device *icd) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); @@ -450,7 +450,7 @@ egfmt: return ret; } -/* Always entered with .video_lock held */ +/* Always entered with .host_lock held */ static void soc_camera_free_user_formats(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -526,7 +526,7 @@ static int soc_camera_open(struct file *file) ici = to_soc_camera_host(icd->parent); mutex_unlock(&list_lock); - if (mutex_lock_interruptible(&icd->video_lock)) + if (mutex_lock_interruptible(&ici->host_lock)) return -ERESTARTSYS; if (!try_module_get(ici->ops->owner)) { dev_err(icd->pdev, "Couldn't lock capture bus driver.\n"); @@ -555,9 +555,7 @@ static int soc_camera_open(struct file *file) if (icl->reset) icl->reset(icd->pdev); - mutex_lock(&ici->host_lock); ret = ici->ops->add(icd); - mutex_unlock(&ici->host_lock); if (ret < 0) { dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret); goto eiciadd; @@ -576,7 +574,7 @@ static int soc_camera_open(struct file *file) * Try to configure with default parameters. Notice: this is the * very first open, so, we cannot race against other calls, * apart from someone else calling open() simultaneously, but - * .video_lock is protecting us against it. + * .host_lock is protecting us against it. */ ret = soc_camera_set_fmt(icd, &f); if (ret < 0) @@ -591,7 +589,7 @@ static int soc_camera_open(struct file *file) } v4l2_ctrl_handler_setup(&icd->ctrl_handler); } - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); file->private_data = icd; dev_dbg(icd->pdev, "camera device open\n"); @@ -599,7 +597,7 @@ static int soc_camera_open(struct file *file) return 0; /* - * First four errors are entered with the .video_lock held + * First four errors are entered with the .host_lock held * and use_count == 1 */ einitvb: @@ -608,14 +606,12 @@ esfmt: eresume: __soc_camera_power_off(icd); epower: - mutex_lock(&ici->host_lock); ici->ops->remove(icd); - mutex_unlock(&ici->host_lock); eiciadd: icd->use_count--; module_put(ici->ops->owner); emodule: - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); return ret; } @@ -625,7 +621,7 @@ static int soc_camera_close(struct file *file) struct soc_camera_device *icd = file->private_data; struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - mutex_lock(&icd->video_lock); + mutex_lock(&ici->host_lock); icd->use_count--; if (!icd->use_count) { pm_runtime_suspend(&icd->vdev->dev); @@ -633,16 +629,14 @@ static int soc_camera_close(struct file *file) if (ici->ops->init_videobuf2) vb2_queue_release(&icd->vb2_vidq); - mutex_lock(&ici->host_lock); ici->ops->remove(icd); - mutex_unlock(&ici->host_lock); __soc_camera_power_off(icd); } if (icd->streamer == file) icd->streamer = NULL; - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); module_put(ici->ops->owner); @@ -679,13 +673,13 @@ static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma) if (icd->streamer != file) return -EBUSY; - if (mutex_lock_interruptible(&icd->video_lock)) + if (mutex_lock_interruptible(&ici->host_lock)) return -ERESTARTSYS; if (ici->ops->init_videobuf) err = videobuf_mmap_mapper(&icd->vb_vidq, vma); else err = vb2_mmap(&icd->vb2_vidq, vma); - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); dev_dbg(icd->pdev, "vma start=0x%08lx, size=%ld, ret=%d\n", (unsigned long)vma->vm_start, @@ -704,26 +698,28 @@ static unsigned int soc_camera_poll(struct file *file, poll_table *pt) if (icd->streamer != file) return POLLERR; - mutex_lock(&icd->video_lock); + mutex_lock(&ici->host_lock); if (ici->ops->init_videobuf && list_empty(&icd->vb_vidq.stream)) dev_err(icd->pdev, "Trying to poll with no queued buffers!\n"); else res = ici->ops->poll(file, pt); - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); return res; } void soc_camera_lock(struct vb2_queue *vq) { struct soc_camera_device *icd = vb2_get_drv_priv(vq); - mutex_lock(&icd->video_lock); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + mutex_lock(&ici->host_lock); } EXPORT_SYMBOL(soc_camera_lock); void soc_camera_unlock(struct vb2_queue *vq) { struct soc_camera_device *icd = vb2_get_drv_priv(vq); - mutex_unlock(&icd->video_lock); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + mutex_unlock(&ici->host_lock); } EXPORT_SYMBOL(soc_camera_unlock); @@ -1213,7 +1209,7 @@ static int soc_camera_probe(struct soc_camera_device *icd) * itself is protected against concurrent open() calls, but we also have * to protect our data. */ - mutex_lock(&icd->video_lock); + mutex_lock(&ici->host_lock); ret = soc_camera_video_start(icd); if (ret < 0) @@ -1227,16 +1223,14 @@ static int soc_camera_probe(struct soc_camera_device *icd) icd->field = mf.field; } - mutex_lock(&ici->host_lock); ici->ops->remove(icd); - mutex_unlock(&ici->host_lock); - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); return 0; evidstart: - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); soc_camera_free_user_formats(icd); eiufmt: ectrl: @@ -1451,7 +1445,6 @@ static int soc_camera_device_register(struct soc_camera_device *icd) icd->devnum = num; icd->use_count = 0; icd->host_priv = NULL; - mutex_init(&icd->video_lock); list_add_tail(&icd->list, &devices); @@ -1509,7 +1502,7 @@ static int video_dev_create(struct soc_camera_device *icd) vdev->release = video_device_release; vdev->tvnorms = V4L2_STD_UNKNOWN; vdev->ctrl_handler = &icd->ctrl_handler; - vdev->lock = &icd->video_lock; + vdev->lock = &ici->host_lock; icd->vdev = vdev; @@ -1517,7 +1510,7 @@ static int video_dev_create(struct soc_camera_device *icd) } /* - * Called from soc_camera_probe() above (with .video_lock held???) + * Called from soc_camera_probe() above with .host_lock held */ static int soc_camera_video_start(struct soc_camera_device *icd) { diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 0370a9517282..5a662c981484 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -46,9 +46,8 @@ struct soc_camera_device { int num_user_formats; enum v4l2_field field; /* Preserve field over close() */ void *host_priv; /* Per-device host private data */ - /* soc_camera.c private count. Only accessed with .video_lock held */ + /* soc_camera.c private count. Only accessed with .host_lock held */ int use_count; - struct mutex video_lock; /* Protects device data */ struct file *streamer; /* stream owner */ union { struct videobuf_queue vb_vidq; -- cgit v1.2.3 From 25a348110078cefa99b0b079938dd930cfc3a0be Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 21 Dec 2012 08:11:48 -0300 Subject: [media] soc-camera: split struct soc_camera_link into host and subdevice parts struct soc_camera_link currently contains fields, used both by sensor and bridge drivers. To make subdevice driver re-use simpler, split it into a host and a subdevice parts. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/imx074.c | 14 +-- drivers/media/i2c/soc_camera/mt9m001.c | 38 ++++---- drivers/media/i2c/soc_camera/mt9m111.c | 21 +++-- drivers/media/i2c/soc_camera/mt9t031.c | 22 ++--- drivers/media/i2c/soc_camera/mt9t112.c | 18 ++-- drivers/media/i2c/soc_camera/mt9v022.c | 36 ++++---- drivers/media/i2c/soc_camera/ov2640.c | 12 +-- drivers/media/i2c/soc_camera/ov5642.c | 16 ++-- drivers/media/i2c/soc_camera/ov6650.c | 16 ++-- drivers/media/i2c/soc_camera/ov772x.c | 14 +-- drivers/media/i2c/soc_camera/ov9640.c | 12 +-- drivers/media/i2c/soc_camera/ov9740.c | 14 +-- drivers/media/i2c/soc_camera/rj54n1cb0c.c | 24 ++--- drivers/media/i2c/soc_camera/tw9910.c | 18 ++-- drivers/media/platform/soc_camera/soc_camera.c | 98 ++++++++++---------- .../platform/soc_camera/soc_camera_platform.c | 2 +- include/media/soc_camera.h | 102 +++++++++++++++++---- include/media/soc_camera_platform.h | 10 +- 18 files changed, 279 insertions(+), 208 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/soc_camera/imx074.c b/drivers/media/i2c/soc_camera/imx074.c index f8534eec9de9..1c065717cdd1 100644 --- a/drivers/media/i2c/soc_camera/imx074.c +++ b/drivers/media/i2c/soc_camera/imx074.c @@ -271,9 +271,9 @@ static int imx074_g_chip_ident(struct v4l2_subdev *sd, static int imx074_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static int imx074_g_mbus_config(struct v4l2_subdev *sd, @@ -430,10 +430,10 @@ static int imx074_probe(struct i2c_client *client, { struct imx074 *priv; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "IMX074: missing platform data!\n"); return -EINVAL; } @@ -464,10 +464,10 @@ static int imx074_probe(struct i2c_client *client, static int imx074_remove(struct i2c_client *client) { struct imx074 *priv = to_imx074(client); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - if (icl->free_bus) - icl->free_bus(icl); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); kfree(priv); return 0; diff --git a/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/mt9m001.c index 19f8a07764f9..9ae7066798eb 100644 --- a/drivers/media/i2c/soc_camera/mt9m001.c +++ b/drivers/media/i2c/soc_camera/mt9m001.c @@ -23,7 +23,7 @@ /* * mt9m001 i2c address 0x5d * The platform has to define struct i2c_board_info objects and link to them - * from struct soc_camera_link + * from struct soc_camera_host_desc */ /* mt9m001 selected register addresses */ @@ -380,9 +380,9 @@ static int mt9m001_s_register(struct v4l2_subdev *sd, static int mt9m001_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl) @@ -482,7 +482,7 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one */ -static int mt9m001_video_probe(struct soc_camera_link *icl, +static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd, struct i2c_client *client) { struct mt9m001 *mt9m001 = to_mt9m001(client); @@ -526,8 +526,8 @@ static int mt9m001_video_probe(struct soc_camera_link *icl, * The platform may support different bus widths due to * different routing of the data lines. */ - if (icl->query_bus_param) - flags = icl->query_bus_param(icl); + if (ssdd->query_bus_param) + flags = ssdd->query_bus_param(ssdd); else flags = SOCAM_DATAWIDTH_10; @@ -558,10 +558,10 @@ done: return ret; } -static void mt9m001_video_remove(struct soc_camera_link *icl) +static void mt9m001_video_remove(struct soc_camera_subdev_desc *ssdd) { - if (icl->free_bus) - icl->free_bus(icl); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); } static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) @@ -605,14 +605,14 @@ static int mt9m001_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); /* MT9M001 has all capture_format parameters fixed */ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -621,12 +621,12 @@ static int mt9m001_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { const struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct mt9m001 *mt9m001 = to_mt9m001(client); unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample; - if (icl->set_bus_param) - return icl->set_bus_param(icl, 1 << (bps - 1)); + if (ssdd->set_bus_param) + return ssdd->set_bus_param(ssdd, 1 << (bps - 1)); /* * Without board specific bus width settings we only support the @@ -663,10 +663,10 @@ static int mt9m001_probe(struct i2c_client *client, { struct mt9m001 *mt9m001; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "MT9M001 driver needs platform data\n"); return -EINVAL; } @@ -713,7 +713,7 @@ static int mt9m001_probe(struct i2c_client *client, mt9m001->rect.width = MT9M001_MAX_WIDTH; mt9m001->rect.height = MT9M001_MAX_HEIGHT; - ret = mt9m001_video_probe(icl, client); + ret = mt9m001_video_probe(ssdd, client); if (ret) { v4l2_ctrl_handler_free(&mt9m001->hdl); kfree(mt9m001); @@ -725,11 +725,11 @@ static int mt9m001_probe(struct i2c_client *client, static int mt9m001_remove(struct i2c_client *client) { struct mt9m001 *mt9m001 = to_mt9m001(client); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); v4l2_device_unregister_subdev(&mt9m001->subdev); v4l2_ctrl_handler_free(&mt9m001->hdl); - mt9m001_video_remove(icl); + mt9m001_video_remove(ssdd); kfree(mt9m001); return 0; diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c index 62fd94af599b..78511669bab0 100644 --- a/drivers/media/i2c/soc_camera/mt9m111.c +++ b/drivers/media/i2c/soc_camera/mt9m111.c @@ -24,7 +24,8 @@ /* * MT9M111, MT9M112 and MT9M131: * i2c address is 0x48 or 0x5d (depending on SADDR pin) - * The platform has to define i2c_board_info and call i2c_register_board_info() + * The platform has to define struct i2c_board_info objects and link to them + * from struct soc_camera_host_desc */ /* @@ -799,17 +800,17 @@ static int mt9m111_init(struct mt9m111 *mt9m111) static int mt9m111_power_on(struct mt9m111 *mt9m111) { struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - ret = soc_camera_power_on(&client->dev, icl); + ret = soc_camera_power_on(&client->dev, ssdd); if (ret < 0) return ret; ret = mt9m111_resume(mt9m111); if (ret < 0) { dev_err(&client->dev, "Failed to resume the sensor: %d\n", ret); - soc_camera_power_off(&client->dev, icl); + soc_camera_power_off(&client->dev, ssdd); } return ret; @@ -818,10 +819,10 @@ static int mt9m111_power_on(struct mt9m111 *mt9m111) static void mt9m111_power_off(struct mt9m111 *mt9m111) { struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); mt9m111_suspend(mt9m111); - soc_camera_power_off(&client->dev, icl); + soc_camera_power_off(&client->dev, ssdd); } static int mt9m111_s_power(struct v4l2_subdev *sd, int on) @@ -879,13 +880,13 @@ static int mt9m111_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -956,10 +957,10 @@ static int mt9m111_probe(struct i2c_client *client, { struct mt9m111 *mt9m111; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "mt9m111: driver needs platform data\n"); return -EINVAL; } diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c index 40800b10a080..9ca6d65cefaf 100644 --- a/drivers/media/i2c/soc_camera/mt9t031.c +++ b/drivers/media/i2c/soc_camera/mt9t031.c @@ -31,8 +31,8 @@ /* * mt9t031 i2c address 0x5d - * The platform has to define i2c_board_info and link to it from - * struct soc_camera_link + * The platform has to define struct i2c_board_info objects and link to them + * from struct soc_camera_host_desc */ /* mt9t031 selected register addresses */ @@ -608,18 +608,18 @@ static struct device_type mt9t031_dev_type = { static int mt9t031_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct video_device *vdev = soc_camera_i2c_to_vdev(client); int ret; if (on) { - ret = soc_camera_power_on(&client->dev, icl); + ret = soc_camera_power_on(&client->dev, ssdd); if (ret < 0) return ret; vdev->dev.type = &mt9t031_dev_type; } else { vdev->dev.type = NULL; - soc_camera_power_off(&client->dev, icl); + soc_camera_power_off(&client->dev, ssdd); } return 0; @@ -707,13 +707,13 @@ static int mt9t031_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -722,9 +722,9 @@ static int mt9t031_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - if (soc_camera_apply_board_flags(icl, cfg) & + if (soc_camera_apply_board_flags(ssdd, cfg) & V4L2_MBUS_PCLK_SAMPLE_FALLING) return reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); else @@ -758,11 +758,11 @@ static int mt9t031_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9t031 *mt9t031; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "MT9T031 driver needs platform data\n"); return -EINVAL; } diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c index de7cd836b0a2..92bb65de6cd9 100644 --- a/drivers/media/i2c/soc_camera/mt9t112.c +++ b/drivers/media/i2c/soc_camera/mt9t112.c @@ -779,9 +779,9 @@ static int mt9t112_s_register(struct v4l2_subdev *sd, static int mt9t112_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = { @@ -991,13 +991,13 @@ static int mt9t112_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -1006,10 +1006,10 @@ static int mt9t112_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct mt9t112_priv *priv = to_mt9t112(client); - if (soc_camera_apply_board_flags(icl, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING) + if (soc_camera_apply_board_flags(ssdd, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING) priv->flags |= PCLK_RISING; return 0; @@ -1078,7 +1078,7 @@ static int mt9t112_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9t112_priv *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct v4l2_rect rect = { .width = VGA_WIDTH, .height = VGA_HEIGHT, @@ -1087,7 +1087,7 @@ static int mt9t112_probe(struct i2c_client *client, }; int ret; - if (!icl || !icl->priv) { + if (!ssdd || !ssdd->drv_priv) { dev_err(&client->dev, "mt9t112: missing platform data!\n"); return -EINVAL; } @@ -1096,7 +1096,7 @@ static int mt9t112_probe(struct i2c_client *client, if (!priv) return -ENOMEM; - priv->info = icl->priv; + priv->info = ssdd->drv_priv; v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops); diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c index 75098024d477..33fb5c3b52b4 100644 --- a/drivers/media/i2c/soc_camera/mt9v022.c +++ b/drivers/media/i2c/soc_camera/mt9v022.c @@ -25,7 +25,7 @@ /* * mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c * The platform has to define struct i2c_board_info objects and link to them - * from struct soc_camera_link + * from struct soc_camera_host_desc */ static char *sensor_type; @@ -508,9 +508,9 @@ static int mt9v022_s_register(struct v4l2_subdev *sd, static int mt9v022_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl) @@ -655,7 +655,7 @@ static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl) static int mt9v022_video_probe(struct i2c_client *client) { struct mt9v022 *mt9v022 = to_mt9v022(client); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); s32 data; int ret; unsigned long flags; @@ -715,8 +715,8 @@ static int mt9v022_video_probe(struct i2c_client *client) * The platform may support different bus widths due to * different routing of the data lines. */ - if (icl->query_bus_param) - flags = icl->query_bus_param(icl); + if (ssdd->query_bus_param) + flags = ssdd->query_bus_param(ssdd); else flags = SOCAM_DATAWIDTH_10; @@ -784,7 +784,7 @@ static int mt9v022_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | @@ -792,7 +792,7 @@ static int mt9v022_g_mbus_config(struct v4l2_subdev *sd, V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -801,15 +801,15 @@ static int mt9v022_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct mt9v022 *mt9v022 = to_mt9v022(client); - unsigned long flags = soc_camera_apply_board_flags(icl, cfg); + unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg); unsigned int bps = soc_mbus_get_fmtdesc(mt9v022->fmt->code)->bits_per_sample; int ret; u16 pixclk = 0; - if (icl->set_bus_param) { - ret = icl->set_bus_param(icl, 1 << (bps - 1)); + if (ssdd->set_bus_param) { + ret = ssdd->set_bus_param(ssdd, 1 << (bps - 1)); if (ret) return ret; } else if (bps != 10) { @@ -873,12 +873,12 @@ static int mt9v022_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9v022 *mt9v022; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct mt9v022_platform_data *pdata; int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "MT9V022 driver needs platform data\n"); return -EINVAL; } @@ -893,7 +893,7 @@ static int mt9v022_probe(struct i2c_client *client, if (!mt9v022) return -ENOMEM; - pdata = icl->priv; + pdata = ssdd->drv_priv; v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops); v4l2_ctrl_handler_init(&mt9v022->hdl, 6); v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, @@ -961,11 +961,11 @@ static int mt9v022_probe(struct i2c_client *client, static int mt9v022_remove(struct i2c_client *client) { struct mt9v022 *mt9v022 = to_mt9v022(client); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); v4l2_device_unregister_subdev(&mt9v022->subdev); - if (icl->free_bus) - icl->free_bus(icl); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); v4l2_ctrl_handler_free(&mt9v022->hdl); kfree(mt9v022); diff --git a/drivers/media/i2c/soc_camera/ov2640.c b/drivers/media/i2c/soc_camera/ov2640.c index 66698a83bda2..c57a5095edef 100644 --- a/drivers/media/i2c/soc_camera/ov2640.c +++ b/drivers/media/i2c/soc_camera/ov2640.c @@ -771,9 +771,9 @@ static int ov2640_s_register(struct v4l2_subdev *sd, static int ov2640_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } /* Select the nearest higher resolution for capture */ @@ -1046,13 +1046,13 @@ static int ov2640_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -1080,11 +1080,11 @@ static int ov2640_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov2640_priv *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); int ret; - if (!icl) { + if (!ssdd) { dev_err(&adapter->dev, "OV2640: Missing platform_data for driver\n"); return -EINVAL; diff --git a/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/ov5642.c index 8577e0cfb7fe..b892d01e57ae 100644 --- a/drivers/media/i2c/soc_camera/ov5642.c +++ b/drivers/media/i2c/soc_camera/ov5642.c @@ -934,13 +934,13 @@ static int ov5642_g_mbus_config(struct v4l2_subdev *sd, static int ov5642_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; if (!on) - return soc_camera_power_off(&client->dev, icl); + return soc_camera_power_off(&client->dev, ssdd); - ret = soc_camera_power_on(&client->dev, icl); + ret = soc_camera_power_on(&client->dev, ssdd); if (ret < 0) return ret; @@ -1020,10 +1020,10 @@ static int ov5642_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov5642 *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "OV5642: missing platform data!\n"); return -EINVAL; } @@ -1057,10 +1057,10 @@ error: static int ov5642_remove(struct i2c_client *client) { struct ov5642 *priv = to_ov5642(client); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - if (icl->free_bus) - icl->free_bus(icl); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); kfree(priv); return 0; diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c index e87feb0881e3..1ae8b8dd268b 100644 --- a/drivers/media/i2c/soc_camera/ov6650.c +++ b/drivers/media/i2c/soc_camera/ov6650.c @@ -435,9 +435,9 @@ static int ov6650_set_register(struct v4l2_subdev *sd, static int ov6650_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static int ov6650_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) @@ -892,7 +892,7 @@ static int ov6650_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | @@ -900,7 +900,7 @@ static int ov6650_g_mbus_config(struct v4l2_subdev *sd, V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -910,8 +910,8 @@ static int ov6650_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); - unsigned long flags = soc_camera_apply_board_flags(icl, cfg); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg); int ret; if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING) @@ -963,10 +963,10 @@ static int ov6650_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov6650 *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "Missing platform_data for driver\n"); return -EINVAL; } diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c index e4a10751894d..5172ce9d8de1 100644 --- a/drivers/media/i2c/soc_camera/ov772x.c +++ b/drivers/media/i2c/soc_camera/ov772x.c @@ -667,9 +667,9 @@ static int ov772x_s_register(struct v4l2_subdev *sd, static int ov772x_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height) @@ -1019,13 +1019,13 @@ static int ov772x_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -1054,11 +1054,11 @@ static int ov772x_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov772x_priv *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); int ret; - if (!icl || !icl->priv) { + if (!ssdd || !ssdd->drv_priv) { dev_err(&client->dev, "OV772X: missing platform data!\n"); return -EINVAL; } @@ -1074,7 +1074,7 @@ static int ov772x_probe(struct i2c_client *client, if (!priv) return -ENOMEM; - priv->info = icl->priv; + priv->info = ssdd->drv_priv; v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops); v4l2_ctrl_handler_init(&priv->hdl, 3); diff --git a/drivers/media/i2c/soc_camera/ov9640.c b/drivers/media/i2c/soc_camera/ov9640.c index b323684eaf77..0ce212437bc1 100644 --- a/drivers/media/i2c/soc_camera/ov9640.c +++ b/drivers/media/i2c/soc_camera/ov9640.c @@ -336,9 +336,9 @@ static int ov9640_set_register(struct v4l2_subdev *sd, static int ov9640_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } /* select nearest higher resolution for capture */ @@ -657,13 +657,13 @@ static int ov9640_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -690,10 +690,10 @@ static int ov9640_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov9640_priv *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "Missing platform_data for driver\n"); return -EINVAL; } diff --git a/drivers/media/i2c/soc_camera/ov9740.c b/drivers/media/i2c/soc_camera/ov9740.c index 7a55889e397b..cdaaf5e140f8 100644 --- a/drivers/media/i2c/soc_camera/ov9740.c +++ b/drivers/media/i2c/soc_camera/ov9740.c @@ -787,12 +787,12 @@ static int ov9740_g_chip_ident(struct v4l2_subdev *sd, static int ov9740_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct ov9740_priv *priv = to_ov9740(sd); int ret; if (on) { - ret = soc_camera_power_on(&client->dev, icl); + ret = soc_camera_power_on(&client->dev, ssdd); if (ret < 0) return ret; @@ -806,7 +806,7 @@ static int ov9740_s_power(struct v4l2_subdev *sd, int on) priv->current_enable = true; } - soc_camera_power_off(&client->dev, icl); + soc_camera_power_off(&client->dev, ssdd); } return 0; @@ -905,13 +905,13 @@ static int ov9740_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -951,10 +951,10 @@ static int ov9740_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov9740_priv *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "Missing platform_data for driver\n"); return -EINVAL; } diff --git a/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/drivers/media/i2c/soc_camera/rj54n1cb0c.c index 02f0400051d9..297e28817e45 100644 --- a/drivers/media/i2c/soc_camera/rj54n1cb0c.c +++ b/drivers/media/i2c/soc_camera/rj54n1cb0c.c @@ -1183,9 +1183,9 @@ static int rj54n1_s_register(struct v4l2_subdev *sd, static int rj54n1_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl) @@ -1245,14 +1245,14 @@ static int rj54n1_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -1261,10 +1261,10 @@ static int rj54n1_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */ - if (soc_camera_apply_board_flags(icl, cfg) & + if (soc_camera_apply_board_flags(ssdd, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING) return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4); else @@ -1334,17 +1334,17 @@ static int rj54n1_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct rj54n1 *rj54n1; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct rj54n1_pdata *rj54n1_priv; int ret; - if (!icl || !icl->priv) { + if (!ssdd || !ssdd->drv_priv) { dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n"); return -EINVAL; } - rj54n1_priv = icl->priv; + rj54n1_priv = ssdd->drv_priv; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_warn(&adapter->dev, @@ -1398,11 +1398,11 @@ static int rj54n1_probe(struct i2c_client *client, static int rj54n1_remove(struct i2c_client *client) { struct rj54n1 *rj54n1 = to_rj54n1(client); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); v4l2_device_unregister_subdev(&rj54n1->subdev); - if (icl->free_bus) - icl->free_bus(icl); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); v4l2_ctrl_handler_free(&rj54n1->hdl); kfree(rj54n1); diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c index 140716e71a15..cc34c5956419 100644 --- a/drivers/media/i2c/soc_camera/tw9910.c +++ b/drivers/media/i2c/soc_camera/tw9910.c @@ -569,9 +569,9 @@ static int tw9910_s_register(struct v4l2_subdev *sd, static int tw9910_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height) @@ -847,14 +847,14 @@ static int tw9910_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -863,9 +863,9 @@ static int tw9910_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); u8 val = VSSL_VVALID | HSSL_DVALID; - unsigned long flags = soc_camera_apply_board_flags(icl, cfg); + unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg); /* * set OUTCTR1 @@ -911,15 +911,15 @@ static int tw9910_probe(struct i2c_client *client, struct tw9910_video_info *info; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl || !icl->priv) { + if (!ssdd || !ssdd->drv_priv) { dev_err(&client->dev, "TW9910: missing platform data!\n"); return -EINVAL; } - info = icl->priv; + info = ssdd->drv_priv; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&client->dev, diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index ae89f980e82b..dd057f19dbc3 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -50,22 +50,22 @@ static LIST_HEAD(hosts); static LIST_HEAD(devices); static DEFINE_MUTEX(list_lock); /* Protects the list of hosts */ -int soc_camera_power_on(struct device *dev, struct soc_camera_link *icl) +int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd) { - int ret = regulator_bulk_enable(icl->num_regulators, - icl->regulators); + int ret = regulator_bulk_enable(ssdd->num_regulators, + ssdd->regulators); if (ret < 0) { dev_err(dev, "Cannot enable regulators\n"); return ret; } - if (icl->power) { - ret = icl->power(dev, 1); + if (ssdd->power) { + ret = ssdd->power(dev, 1); if (ret < 0) { dev_err(dev, "Platform failed to power-on the camera.\n"); - regulator_bulk_disable(icl->num_regulators, - icl->regulators); + regulator_bulk_disable(ssdd->num_regulators, + ssdd->regulators); } } @@ -73,13 +73,13 @@ int soc_camera_power_on(struct device *dev, struct soc_camera_link *icl) } EXPORT_SYMBOL(soc_camera_power_on); -int soc_camera_power_off(struct device *dev, struct soc_camera_link *icl) +int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd) { int ret = 0; int err; - if (icl->power) { - err = icl->power(dev, 0); + if (ssdd->power) { + err = ssdd->power(dev, 0); if (err < 0) { dev_err(dev, "Platform failed to power-off the camera.\n"); @@ -87,8 +87,8 @@ int soc_camera_power_off(struct device *dev, struct soc_camera_link *icl) } } - err = regulator_bulk_disable(icl->num_regulators, - icl->regulators); + err = regulator_bulk_disable(ssdd->num_regulators, + ssdd->regulators); if (err < 0) { dev_err(dev, "Cannot disable regulators\n"); ret = ret ? : err; @@ -136,29 +136,29 @@ EXPORT_SYMBOL(soc_camera_xlate_by_fourcc); /** * soc_camera_apply_board_flags() - apply platform SOCAM_SENSOR_INVERT_* flags - * @icl: camera platform parameters + * @ssdd: camera platform parameters * @cfg: media bus configuration * @return: resulting flags */ -unsigned long soc_camera_apply_board_flags(struct soc_camera_link *icl, +unsigned long soc_camera_apply_board_flags(struct soc_camera_subdev_desc *ssdd, const struct v4l2_mbus_config *cfg) { unsigned long f, flags = cfg->flags; /* If only one of the two polarities is supported, switch to the opposite */ - if (icl->flags & SOCAM_SENSOR_INVERT_HSYNC) { + if (ssdd->flags & SOCAM_SENSOR_INVERT_HSYNC) { f = flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW); if (f == V4L2_MBUS_HSYNC_ACTIVE_HIGH || f == V4L2_MBUS_HSYNC_ACTIVE_LOW) flags ^= V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW; } - if (icl->flags & SOCAM_SENSOR_INVERT_VSYNC) { + if (ssdd->flags & SOCAM_SENSOR_INVERT_VSYNC) { f = flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW); if (f == V4L2_MBUS_VSYNC_ACTIVE_HIGH || f == V4L2_MBUS_VSYNC_ACTIVE_LOW) flags ^= V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW; } - if (icl->flags & SOCAM_SENSOR_INVERT_PCLK) { + if (ssdd->flags & SOCAM_SENSOR_INVERT_PCLK) { f = flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING); if (f == V4L2_MBUS_PCLK_SAMPLE_RISING || f == V4L2_MBUS_PCLK_SAMPLE_FALLING) flags ^= V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING; @@ -509,7 +509,7 @@ static int soc_camera_open(struct file *file) { struct video_device *vdev = video_devdata(file); struct soc_camera_device *icd = dev_get_drvdata(vdev->parent); - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); struct soc_camera_host *ici; int ret; @@ -552,8 +552,8 @@ static int soc_camera_open(struct file *file) }; /* The camera could have been already on, try to reset */ - if (icl->reset) - icl->reset(icd->pdev); + if (sdesc->subdev_desc.reset) + sdesc->subdev_desc.reset(icd->pdev); ret = ici->ops->add(icd); if (ret < 0) { @@ -1072,23 +1072,24 @@ static void scan_add_host(struct soc_camera_host *ici) #ifdef CONFIG_I2C_BOARDINFO static int soc_camera_init_i2c(struct soc_camera_device *icd, - struct soc_camera_link *icl) + struct soc_camera_desc *sdesc) { struct i2c_client *client; struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct i2c_adapter *adap = i2c_get_adapter(icl->i2c_adapter_id); + struct soc_camera_host_desc *shd = &sdesc->host_desc; + struct i2c_adapter *adap = i2c_get_adapter(shd->i2c_adapter_id); struct v4l2_subdev *subdev; if (!adap) { dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n", - icl->i2c_adapter_id); + shd->i2c_adapter_id); goto ei2cga; } - icl->board_info->platform_data = icl; + shd->board_info->platform_data = &sdesc->subdev_desc; subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap, - icl->board_info, NULL); + shd->board_info, NULL); if (!subdev) goto ei2cnd; @@ -1116,7 +1117,7 @@ static void soc_camera_free_i2c(struct soc_camera_device *icd) i2c_put_adapter(adap); } #else -#define soc_camera_init_i2c(icd, icl) (-ENODEV) +#define soc_camera_init_i2c(icd, sdesc) (-ENODEV) #define soc_camera_free_i2c(icd) do {} while (0) #endif @@ -1126,7 +1127,9 @@ static int video_dev_create(struct soc_camera_device *icd); static int soc_camera_probe(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); + struct soc_camera_host_desc *shd = &sdesc->host_desc; + struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc; struct device *control = NULL; struct v4l2_subdev *sd; struct v4l2_mbus_framefmt mf; @@ -1146,8 +1149,8 @@ static int soc_camera_probe(struct soc_camera_device *icd) return ret; /* The camera could have been already on, try to reset */ - if (icl->reset) - icl->reset(icd->pdev); + if (ssdd->reset) + ssdd->reset(icd->pdev); mutex_lock(&ici->host_lock); ret = ici->ops->add(icd); @@ -1161,18 +1164,18 @@ static int soc_camera_probe(struct soc_camera_device *icd) goto evdc; /* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */ - if (icl->board_info) { - ret = soc_camera_init_i2c(icd, icl); + if (shd->board_info) { + ret = soc_camera_init_i2c(icd, sdesc); if (ret < 0) goto eadddev; - } else if (!icl->add_device || !icl->del_device) { + } else if (!shd->add_device || !shd->del_device) { ret = -EINVAL; goto eadddev; } else { - if (icl->module_name) - ret = request_module(icl->module_name); + if (shd->module_name) + ret = request_module(shd->module_name); - ret = icl->add_device(icd); + ret = shd->add_device(icd); if (ret < 0) goto eadddev; @@ -1183,7 +1186,7 @@ static int soc_camera_probe(struct soc_camera_device *icd) control = to_soc_camera_control(icd); if (!control || !control->driver || !dev_get_drvdata(control) || !try_module_get(control->driver->owner)) { - icl->del_device(icd); + shd->del_device(icd); ret = -ENODEV; goto enodrv; } @@ -1234,10 +1237,10 @@ evidstart: soc_camera_free_user_formats(icd); eiufmt: ectrl: - if (icl->board_info) { + if (shd->board_info) { soc_camera_free_i2c(icd); } else { - icl->del_device(icd); + shd->del_device(icd); module_put(control->driver->owner); } enodrv: @@ -1259,7 +1262,7 @@ eadd: */ static int soc_camera_remove(struct soc_camera_device *icd) { - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); struct video_device *vdev = icd->vdev; BUG_ON(!icd->parent); @@ -1270,12 +1273,12 @@ static int soc_camera_remove(struct soc_camera_device *icd) icd->vdev = NULL; } - if (icl->board_info) { + if (sdesc->host_desc.board_info) { soc_camera_free_i2c(icd); } else { struct device_driver *drv = to_soc_camera_control(icd)->driver; if (drv) { - icl->del_device(icd); + sdesc->host_desc.del_device(icd); module_put(drv->owner); } } @@ -1534,24 +1537,25 @@ static int soc_camera_video_start(struct soc_camera_device *icd) static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev) { - struct soc_camera_link *icl = pdev->dev.platform_data; + struct soc_camera_desc *sdesc = pdev->dev.platform_data; + struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc; struct soc_camera_device *icd; int ret; - if (!icl) + if (!sdesc) return -EINVAL; icd = devm_kzalloc(&pdev->dev, sizeof(*icd), GFP_KERNEL); if (!icd) return -ENOMEM; - ret = devm_regulator_bulk_get(&pdev->dev, icl->num_regulators, - icl->regulators); + ret = devm_regulator_bulk_get(&pdev->dev, ssdd->num_regulators, + ssdd->regulators); if (ret < 0) return ret; - icd->iface = icl->bus_id; - icd->link = icl; + icd->iface = sdesc->host_desc.bus_id; + icd->sdesc = sdesc; icd->pdev = &pdev->dev; platform_set_drvdata(pdev, icd); diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c index 7cf7fd16481f..51e29d19b1b4 100644 --- a/drivers/media/platform/soc_camera/soc_camera_platform.c +++ b/drivers/media/platform/soc_camera/soc_camera_platform.c @@ -54,7 +54,7 @@ static int soc_camera_platform_s_power(struct v4l2_subdev *sd, int on) { struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - return soc_camera_set_power(p->icd->control, p->icd->link, on); + return soc_camera_set_power(p->icd->control, &p->icd->sdesc->subdev_desc, on); } static struct v4l2_subdev_core_ops platform_subdev_core_ops = { diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 5a662c981484..2cc70cf318bf 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -23,11 +23,11 @@ #include struct file; -struct soc_camera_link; +struct soc_camera_desc; struct soc_camera_device { struct list_head list; /* list of all registered devices */ - struct soc_camera_link *link; + struct soc_camera_desc *sdesc; struct device *pdev; /* Platform device */ struct device *parent; /* Camera host device */ struct device *control; /* E.g., the i2c client */ @@ -116,26 +116,72 @@ struct soc_camera_host_ops { struct i2c_board_info; struct regulator_bulk_data; -struct soc_camera_link { - /* Camera bus id, used to match a camera and a bus */ - int bus_id; +struct soc_camera_subdev_desc { /* Per camera SOCAM_SENSOR_* bus flags */ unsigned long flags; - int i2c_adapter_id; - struct i2c_board_info *board_info; - const char *module_name; - void *priv; + + /* sensor driver private platform data */ + void *drv_priv; /* Optional regulators that have to be managed on power on/off events */ struct regulator_bulk_data *regulators; int num_regulators; + /* Optional callbacks to power on or off and reset the sensor */ + int (*power)(struct device *, int); + int (*reset)(struct device *); + + /* + * some platforms may support different data widths than the sensors + * native ones due to different data line routing. Let the board code + * overwrite the width flags. + */ + int (*set_bus_param)(struct soc_camera_subdev_desc *, unsigned long flags); + unsigned long (*query_bus_param)(struct soc_camera_subdev_desc *); + void (*free_bus)(struct soc_camera_subdev_desc *); +}; + +struct soc_camera_host_desc { + /* Camera bus id, used to match a camera and a bus */ + int bus_id; + int i2c_adapter_id; + struct i2c_board_info *board_info; + const char *module_name; + /* * For non-I2C devices platform has to provide methods to add a device * to the system and to remove it */ int (*add_device)(struct soc_camera_device *); void (*del_device)(struct soc_camera_device *); +}; + +/* + * This MUST be kept binary-identical to struct soc_camera_link below, until + * it is completely replaced by this one, after which we can split it into its + * two components. + */ +struct soc_camera_desc { + struct soc_camera_subdev_desc subdev_desc; + struct soc_camera_host_desc host_desc; +}; + +/* Prepare to replace this struct: don't change its layout any more! */ +struct soc_camera_link { + /* + * Subdevice part - keep at top and compatible to + * struct soc_camera_subdev_desc + */ + + /* Per camera SOCAM_SENSOR_* bus flags */ + unsigned long flags; + + void *priv; + + /* Optional regulators that have to be managed on power on/off events */ + struct regulator_bulk_data *regulators; + int num_regulators; + /* Optional callbacks to power on or off and reset the sensor */ int (*power)(struct device *, int); int (*reset)(struct device *); @@ -147,6 +193,24 @@ struct soc_camera_link { int (*set_bus_param)(struct soc_camera_link *, unsigned long flags); unsigned long (*query_bus_param)(struct soc_camera_link *); void (*free_bus)(struct soc_camera_link *); + + /* + * Host part - keep at bottom and compatible to + * struct soc_camera_host_desc + */ + + /* Camera bus id, used to match a camera and a bus */ + int bus_id; + int i2c_adapter_id; + struct i2c_board_info *board_info; + const char *module_name; + + /* + * For non-I2C devices platform has to provide methods to add a device + * to the system and to remove it + */ + int (*add_device)(struct soc_camera_device *); + void (*del_device)(struct soc_camera_device *); }; static inline struct soc_camera_host *to_soc_camera_host( @@ -157,10 +221,10 @@ static inline struct soc_camera_host *to_soc_camera_host( return container_of(v4l2_dev, struct soc_camera_host, v4l2_dev); } -static inline struct soc_camera_link *to_soc_camera_link( +static inline struct soc_camera_desc *to_soc_camera_desc( const struct soc_camera_device *icd) { - return icd->link; + return icd->sdesc; } static inline struct device *to_soc_camera_control( @@ -250,19 +314,17 @@ static inline void soc_camera_limit_side(int *start, int *length, *start = start_min + length_max - *length; } -unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl, - unsigned long flags); -unsigned long soc_camera_apply_board_flags(struct soc_camera_link *icl, +unsigned long soc_camera_apply_board_flags(struct soc_camera_subdev_desc *ssdd, const struct v4l2_mbus_config *cfg); -int soc_camera_power_on(struct device *dev, struct soc_camera_link *icl); -int soc_camera_power_off(struct device *dev, struct soc_camera_link *icl); +int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd); +int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd); static inline int soc_camera_set_power(struct device *dev, - struct soc_camera_link *icl, bool on) + struct soc_camera_subdev_desc *ssdd, bool on) { - return on ? soc_camera_power_on(dev, icl) - : soc_camera_power_off(dev, icl); + return on ? soc_camera_power_on(dev, ssdd) + : soc_camera_power_off(dev, ssdd); } /* This is only temporary here - until v4l2-subdev begins to link to video_device */ @@ -274,7 +336,7 @@ static inline struct video_device *soc_camera_i2c_to_vdev(const struct i2c_clien return icd ? icd->vdev : NULL; } -static inline struct soc_camera_link *soc_camera_i2c_to_link(const struct i2c_client *client) +static inline struct soc_camera_subdev_desc *soc_camera_i2c_to_desc(const struct i2c_client *client) { return client->dev.platform_data; } diff --git a/include/media/soc_camera_platform.h b/include/media/soc_camera_platform.h index 8aa4200a0b1d..1e5065dab430 100644 --- a/include/media/soc_camera_platform.h +++ b/include/media/soc_camera_platform.h @@ -38,10 +38,12 @@ static inline int soc_camera_platform_add(struct soc_camera_device *icd, void (*release)(struct device *dev), int id) { - struct soc_camera_platform_info *info = plink->priv; + struct soc_camera_subdev_desc *ssdd = + (struct soc_camera_subdev_desc *)plink; + struct soc_camera_platform_info *info = ssdd->drv_priv; int ret; - if (icd->link != plink) + if (&icd->sdesc->subdev_desc != ssdd) return -ENODEV; if (*pdev) @@ -70,7 +72,9 @@ static inline void soc_camera_platform_del(const struct soc_camera_device *icd, struct platform_device *pdev, const struct soc_camera_link *plink) { - if (icd->link != plink || !pdev) + const struct soc_camera_subdev_desc *ssdd = + (const struct soc_camera_subdev_desc *)plink; + if (&icd->sdesc->subdev_desc != ssdd || !pdev) return; platform_device_unregister(pdev); -- cgit v1.2.3 From 70e176a5a9839ea22f0fbcfa21d1c8ae952a0dd2 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 21 Dec 2012 10:28:43 -0300 Subject: [media] soc-camera: use devm_kzalloc in subdevice drivers I2C drivers can use devm_kzalloc() too in their .probe() methods. Doing so simplifies their clean up paths. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/imx074.c | 13 ++----------- drivers/media/i2c/soc_camera/mt9m001.c | 14 ++++---------- drivers/media/i2c/soc_camera/mt9m111.c | 15 ++++----------- drivers/media/i2c/soc_camera/mt9t031.c | 14 ++++---------- drivers/media/i2c/soc_camera/mt9t112.c | 9 ++------- drivers/media/i2c/soc_camera/mt9v022.c | 8 ++------ drivers/media/i2c/soc_camera/ov2640.c | 17 +++++------------ drivers/media/i2c/soc_camera/ov5642.c | 15 ++------------- drivers/media/i2c/soc_camera/ov6650.c | 14 ++++---------- drivers/media/i2c/soc_camera/ov772x.c | 22 +++++++--------------- drivers/media/i2c/soc_camera/ov9640.c | 15 ++++----------- drivers/media/i2c/soc_camera/ov9740.c | 15 ++++----------- drivers/media/i2c/soc_camera/rj54n1cb0c.c | 15 ++++----------- drivers/media/i2c/soc_camera/tw9910.c | 12 ++---------- .../platform/soc_camera/soc_camera_platform.c | 4 +--- 15 files changed, 51 insertions(+), 151 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/soc_camera/imx074.c b/drivers/media/i2c/soc_camera/imx074.c index 1c065717cdd1..a2a5cbbdbe28 100644 --- a/drivers/media/i2c/soc_camera/imx074.c +++ b/drivers/media/i2c/soc_camera/imx074.c @@ -431,7 +431,6 @@ static int imx074_probe(struct i2c_client *client, struct imx074 *priv; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; if (!ssdd) { dev_err(&client->dev, "IMX074: missing platform data!\n"); @@ -444,7 +443,7 @@ static int imx074_probe(struct i2c_client *client, return -EIO; } - priv = kzalloc(sizeof(struct imx074), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(struct imx074), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -452,23 +451,15 @@ static int imx074_probe(struct i2c_client *client, priv->fmt = &imx074_colour_fmts[0]; - ret = imx074_video_probe(client); - if (ret < 0) { - kfree(priv); - return ret; - } - - return ret; + return imx074_video_probe(client); } static int imx074_remove(struct i2c_client *client) { - struct imx074 *priv = to_imx074(client); struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); if (ssdd->free_bus) ssdd->free_bus(ssdd); - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/mt9m001.c index 9ae7066798eb..bcdc86175549 100644 --- a/drivers/media/i2c/soc_camera/mt9m001.c +++ b/drivers/media/i2c/soc_camera/mt9m001.c @@ -677,7 +677,7 @@ static int mt9m001_probe(struct i2c_client *client, return -EIO; } - mt9m001 = kzalloc(sizeof(struct mt9m001), GFP_KERNEL); + mt9m001 = devm_kzalloc(&client->dev, sizeof(struct mt9m001), GFP_KERNEL); if (!mt9m001) return -ENOMEM; @@ -697,12 +697,9 @@ static int mt9m001_probe(struct i2c_client *client, &mt9m001_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_AUTO); mt9m001->subdev.ctrl_handler = &mt9m001->hdl; - if (mt9m001->hdl.error) { - int err = mt9m001->hdl.error; + if (mt9m001->hdl.error) + return mt9m001->hdl.error; - kfree(mt9m001); - return err; - } v4l2_ctrl_auto_cluster(2, &mt9m001->autoexposure, V4L2_EXPOSURE_MANUAL, true); @@ -714,10 +711,8 @@ static int mt9m001_probe(struct i2c_client *client, mt9m001->rect.height = MT9M001_MAX_HEIGHT; ret = mt9m001_video_probe(ssdd, client); - if (ret) { + if (ret) v4l2_ctrl_handler_free(&mt9m001->hdl); - kfree(mt9m001); - } return ret; } @@ -730,7 +725,6 @@ static int mt9m001_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&mt9m001->subdev); v4l2_ctrl_handler_free(&mt9m001->hdl); mt9m001_video_remove(ssdd); - kfree(mt9m001); return 0; } diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c index 78511669bab0..bbc4ff99603c 100644 --- a/drivers/media/i2c/soc_camera/mt9m111.c +++ b/drivers/media/i2c/soc_camera/mt9m111.c @@ -971,7 +971,7 @@ static int mt9m111_probe(struct i2c_client *client, return -EIO; } - mt9m111 = kzalloc(sizeof(struct mt9m111), GFP_KERNEL); + mt9m111 = devm_kzalloc(&client->dev, sizeof(struct mt9m111), GFP_KERNEL); if (!mt9m111) return -ENOMEM; @@ -989,12 +989,8 @@ static int mt9m111_probe(struct i2c_client *client, &mt9m111_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_AUTO); mt9m111->subdev.ctrl_handler = &mt9m111->hdl; - if (mt9m111->hdl.error) { - int err = mt9m111->hdl.error; - - kfree(mt9m111); - return err; - } + if (mt9m111->hdl.error) + return mt9m111->hdl.error; /* Second stage probe - when a capture adapter is there */ mt9m111->rect.left = MT9M111_MIN_DARK_COLS; @@ -1006,10 +1002,8 @@ static int mt9m111_probe(struct i2c_client *client, mutex_init(&mt9m111->power_lock); ret = mt9m111_video_probe(client); - if (ret) { + if (ret) v4l2_ctrl_handler_free(&mt9m111->hdl); - kfree(mt9m111); - } return ret; } @@ -1020,7 +1014,6 @@ static int mt9m111_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&mt9m111->subdev); v4l2_ctrl_handler_free(&mt9m111->hdl); - kfree(mt9m111); return 0; } diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c index 9ca6d65cefaf..d80d044ebf15 100644 --- a/drivers/media/i2c/soc_camera/mt9t031.c +++ b/drivers/media/i2c/soc_camera/mt9t031.c @@ -773,7 +773,7 @@ static int mt9t031_probe(struct i2c_client *client, return -EIO; } - mt9t031 = kzalloc(sizeof(struct mt9t031), GFP_KERNEL); + mt9t031 = devm_kzalloc(&client->dev, sizeof(struct mt9t031), GFP_KERNEL); if (!mt9t031) return -ENOMEM; @@ -797,12 +797,9 @@ static int mt9t031_probe(struct i2c_client *client, V4L2_CID_EXPOSURE, 1, 255, 1, 255); mt9t031->subdev.ctrl_handler = &mt9t031->hdl; - if (mt9t031->hdl.error) { - int err = mt9t031->hdl.error; + if (mt9t031->hdl.error) + return mt9t031->hdl.error; - kfree(mt9t031); - return err; - } v4l2_ctrl_auto_cluster(2, &mt9t031->autoexposure, V4L2_EXPOSURE_MANUAL, true); @@ -816,10 +813,8 @@ static int mt9t031_probe(struct i2c_client *client, mt9t031->yskip = 1; ret = mt9t031_video_probe(client); - if (ret) { + if (ret) v4l2_ctrl_handler_free(&mt9t031->hdl); - kfree(mt9t031); - } return ret; } @@ -830,7 +825,6 @@ static int mt9t031_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&mt9t031->subdev); v4l2_ctrl_handler_free(&mt9t031->hdl); - kfree(mt9t031); return 0; } diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c index 92bb65de6cd9..c75d83168700 100644 --- a/drivers/media/i2c/soc_camera/mt9t112.c +++ b/drivers/media/i2c/soc_camera/mt9t112.c @@ -1092,7 +1092,7 @@ static int mt9t112_probe(struct i2c_client *client, return -EINVAL; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -1101,10 +1101,8 @@ static int mt9t112_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops); ret = mt9t112_camera_probe(client); - if (ret) { - kfree(priv); + if (ret) return ret; - } /* Cannot fail: using the default supported pixel code */ mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8); @@ -1114,9 +1112,6 @@ static int mt9t112_probe(struct i2c_client *client, static int mt9t112_remove(struct i2c_client *client) { - struct mt9t112_priv *priv = to_mt9t112(client); - - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c index 33fb5c3b52b4..a5e65d6a0781 100644 --- a/drivers/media/i2c/soc_camera/mt9v022.c +++ b/drivers/media/i2c/soc_camera/mt9v022.c @@ -889,7 +889,7 @@ static int mt9v022_probe(struct i2c_client *client, return -EIO; } - mt9v022 = kzalloc(sizeof(struct mt9v022), GFP_KERNEL); + mt9v022 = devm_kzalloc(&client->dev, sizeof(struct mt9v022), GFP_KERNEL); if (!mt9v022) return -ENOMEM; @@ -930,7 +930,6 @@ static int mt9v022_probe(struct i2c_client *client, int err = mt9v022->hdl.error; dev_err(&client->dev, "control initialisation err %d\n", err); - kfree(mt9v022); return err; } v4l2_ctrl_auto_cluster(2, &mt9v022->autoexposure, @@ -950,10 +949,8 @@ static int mt9v022_probe(struct i2c_client *client, mt9v022->rect.height = MT9V022_MAX_HEIGHT; ret = mt9v022_video_probe(client); - if (ret) { + if (ret) v4l2_ctrl_handler_free(&mt9v022->hdl); - kfree(mt9v022); - } return ret; } @@ -967,7 +964,6 @@ static int mt9v022_remove(struct i2c_client *client) if (ssdd->free_bus) ssdd->free_bus(ssdd); v4l2_ctrl_handler_free(&mt9v022->hdl); - kfree(mt9v022); return 0; } diff --git a/drivers/media/i2c/soc_camera/ov2640.c b/drivers/media/i2c/soc_camera/ov2640.c index c57a5095edef..0f520f693b6e 100644 --- a/drivers/media/i2c/soc_camera/ov2640.c +++ b/drivers/media/i2c/soc_camera/ov2640.c @@ -1096,7 +1096,7 @@ static int ov2640_probe(struct i2c_client *client, return -EIO; } - priv = kzalloc(sizeof(struct ov2640_priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(struct ov2640_priv), GFP_KERNEL); if (!priv) { dev_err(&adapter->dev, "Failed to allocate memory for private data!\n"); @@ -1110,20 +1110,14 @@ static int ov2640_probe(struct i2c_client *client, v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) { - int err = priv->hdl.error; - - kfree(priv); - return err; - } + if (priv->hdl.error) + return priv->hdl.error; ret = ov2640_video_probe(client); - if (ret) { + if (ret) v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); - } else { + else dev_info(&adapter->dev, "OV2640 Probed\n"); - } return ret; } @@ -1134,7 +1128,6 @@ static int ov2640_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/ov5642.c index b892d01e57ae..9d53309619d2 100644 --- a/drivers/media/i2c/soc_camera/ov5642.c +++ b/drivers/media/i2c/soc_camera/ov5642.c @@ -1021,14 +1021,13 @@ static int ov5642_probe(struct i2c_client *client, { struct ov5642 *priv; struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; if (!ssdd) { dev_err(&client->dev, "OV5642: missing platform data!\n"); return -EINVAL; } - priv = kzalloc(sizeof(struct ov5642), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(struct ov5642), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -1043,25 +1042,15 @@ static int ov5642_probe(struct i2c_client *client, priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH; priv->total_height = BLANKING_MIN_HEIGHT; - ret = ov5642_video_probe(client); - if (ret < 0) - goto error; - - return 0; - -error: - kfree(priv); - return ret; + return ov5642_video_probe(client); } static int ov5642_remove(struct i2c_client *client) { - struct ov5642 *priv = to_ov5642(client); struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); if (ssdd->free_bus) ssdd->free_bus(ssdd); - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c index 1ae8b8dd268b..dbe4f564e6b2 100644 --- a/drivers/media/i2c/soc_camera/ov6650.c +++ b/drivers/media/i2c/soc_camera/ov6650.c @@ -971,7 +971,7 @@ static int ov6650_probe(struct i2c_client *client, return -EINVAL; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { dev_err(&client->dev, "Failed to allocate memory for private data!\n"); @@ -1009,12 +1009,9 @@ static int ov6650_probe(struct i2c_client *client, V4L2_CID_GAMMA, 0, 0xff, 1, 0x12); priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) { - int err = priv->hdl.error; + if (priv->hdl.error) + return priv->hdl.error; - kfree(priv); - return err; - } v4l2_ctrl_auto_cluster(2, &priv->autogain, 0, true); v4l2_ctrl_auto_cluster(3, &priv->autowb, 0, true); v4l2_ctrl_auto_cluster(2, &priv->autoexposure, @@ -1029,10 +1026,8 @@ static int ov6650_probe(struct i2c_client *client, priv->colorspace = V4L2_COLORSPACE_JPEG; ret = ov6650_video_probe(client); - if (ret) { + if (ret) v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); - } return ret; } @@ -1043,7 +1038,6 @@ static int ov6650_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c index 5172ce9d8de1..fbeb5b2f3ae5 100644 --- a/drivers/media/i2c/soc_camera/ov772x.c +++ b/drivers/media/i2c/soc_camera/ov772x.c @@ -1070,7 +1070,7 @@ static int ov772x_probe(struct i2c_client *client, return -EIO; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -1085,22 +1085,15 @@ static int ov772x_probe(struct i2c_client *client, v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0); priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) { - ret = priv->hdl.error; - goto done; - } + if (priv->hdl.error) + return priv->hdl.error; ret = ov772x_video_probe(priv); - if (ret < 0) - goto done; - - priv->cfmt = &ov772x_cfmts[0]; - priv->win = &ov772x_win_sizes[0]; - -done: - if (ret) { + if (ret < 0) { v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); + } else { + priv->cfmt = &ov772x_cfmts[0]; + priv->win = &ov772x_win_sizes[0]; } return ret; } @@ -1111,7 +1104,6 @@ static int ov772x_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/ov9640.c b/drivers/media/i2c/soc_camera/ov9640.c index 0ce212437bc1..05993041be31 100644 --- a/drivers/media/i2c/soc_camera/ov9640.c +++ b/drivers/media/i2c/soc_camera/ov9640.c @@ -698,7 +698,7 @@ static int ov9640_probe(struct i2c_client *client, return -EINVAL; } - priv = kzalloc(sizeof(struct ov9640_priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(struct ov9640_priv), GFP_KERNEL); if (!priv) { dev_err(&client->dev, "Failed to allocate memory for private data!\n"); @@ -713,19 +713,13 @@ static int ov9640_probe(struct i2c_client *client, v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) { - int err = priv->hdl.error; - - kfree(priv); - return err; - } + if (priv->hdl.error) + return priv->hdl.error; ret = ov9640_video_probe(client); - if (ret) { + if (ret) v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); - } return ret; } @@ -737,7 +731,6 @@ static int ov9640_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/ov9740.c b/drivers/media/i2c/soc_camera/ov9740.c index cdaaf5e140f8..2f236da80165 100644 --- a/drivers/media/i2c/soc_camera/ov9740.c +++ b/drivers/media/i2c/soc_camera/ov9740.c @@ -959,7 +959,7 @@ static int ov9740_probe(struct i2c_client *client, return -EINVAL; } - priv = kzalloc(sizeof(struct ov9740_priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(struct ov9740_priv), GFP_KERNEL); if (!priv) { dev_err(&client->dev, "Failed to allocate private data!\n"); return -ENOMEM; @@ -972,18 +972,12 @@ static int ov9740_probe(struct i2c_client *client, v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) { - int err = priv->hdl.error; - - kfree(priv); - return err; - } + if (priv->hdl.error) + return priv->hdl.error; ret = ov9740_video_probe(client); - if (ret < 0) { + if (ret < 0) v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); - } return ret; } @@ -994,7 +988,6 @@ static int ov9740_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/drivers/media/i2c/soc_camera/rj54n1cb0c.c index 297e28817e45..5c92679bfefb 100644 --- a/drivers/media/i2c/soc_camera/rj54n1cb0c.c +++ b/drivers/media/i2c/soc_camera/rj54n1cb0c.c @@ -1352,7 +1352,7 @@ static int rj54n1_probe(struct i2c_client *client, return -EIO; } - rj54n1 = kzalloc(sizeof(struct rj54n1), GFP_KERNEL); + rj54n1 = devm_kzalloc(&client->dev, sizeof(struct rj54n1), GFP_KERNEL); if (!rj54n1) return -ENOMEM; @@ -1367,12 +1367,8 @@ static int rj54n1_probe(struct i2c_client *client, v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); rj54n1->subdev.ctrl_handler = &rj54n1->hdl; - if (rj54n1->hdl.error) { - int err = rj54n1->hdl.error; - - kfree(rj54n1); - return err; - } + if (rj54n1->hdl.error) + return rj54n1->hdl.error; rj54n1->clk_div = clk_div; rj54n1->rect.left = RJ54N1_COLUMN_SKIP; @@ -1387,10 +1383,8 @@ static int rj54n1_probe(struct i2c_client *client, (clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1); ret = rj54n1_video_probe(client, rj54n1_priv); - if (ret < 0) { + if (ret < 0) v4l2_ctrl_handler_free(&rj54n1->hdl); - kfree(rj54n1); - } return ret; } @@ -1404,7 +1398,6 @@ static int rj54n1_remove(struct i2c_client *client) if (ssdd->free_bus) ssdd->free_bus(ssdd); v4l2_ctrl_handler_free(&rj54n1->hdl); - kfree(rj54n1); return 0; } diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c index cc34c5956419..7d2074601881 100644 --- a/drivers/media/i2c/soc_camera/tw9910.c +++ b/drivers/media/i2c/soc_camera/tw9910.c @@ -912,7 +912,6 @@ static int tw9910_probe(struct i2c_client *client, struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; if (!ssdd || !ssdd->drv_priv) { dev_err(&client->dev, "TW9910: missing platform data!\n"); @@ -928,7 +927,7 @@ static int tw9910_probe(struct i2c_client *client, return -EIO; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -936,18 +935,11 @@ static int tw9910_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops); - ret = tw9910_video_probe(client); - if (ret) - kfree(priv); - - return ret; + return tw9910_video_probe(client); } static int tw9910_remove(struct i2c_client *client) { - struct tw9910_priv *priv = to_tw9910(client); - - kfree(priv); return 0; } diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c index 51e29d19b1b4..ce3b1d6a4734 100644 --- a/drivers/media/platform/soc_camera/soc_camera_platform.c +++ b/drivers/media/platform/soc_camera/soc_camera_platform.c @@ -148,7 +148,7 @@ static int soc_camera_platform_probe(struct platform_device *pdev) return -EINVAL; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -173,7 +173,6 @@ static int soc_camera_platform_probe(struct platform_device *pdev) evdrs: platform_set_drvdata(pdev, NULL); - kfree(priv); return ret; } @@ -185,7 +184,6 @@ static int soc_camera_platform_remove(struct platform_device *pdev) p->icd->control = NULL; v4l2_device_unregister_subdev(&priv->subdev); platform_set_drvdata(pdev, NULL); - kfree(priv); return 0; } -- cgit v1.2.3 From 9b556953224080d81754bfe764ab1899fc069edc Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 30 Oct 2012 11:28:59 -0300 Subject: [media] mx2_camera: Remove i.mx25 support i.MX25 support has been broken for several releases now and nobody seems to care about it. Signed-off-by: Javier Martin [g.liakhovetski@gmx.de: rebased on top of cpu_is_mx27() removal] Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/Kconfig | 7 +- drivers/media/platform/soc_camera/mx2_camera.c | 476 ++++++------------------- 2 files changed, 115 insertions(+), 368 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index cb6791e62bd4..b139b525bb16 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -70,13 +70,12 @@ config VIDEO_MX2_HOSTSUPPORT bool config VIDEO_MX2 - tristate "i.MX27/i.MX25 Camera Sensor Interface driver" - depends on VIDEO_DEV && SOC_CAMERA && (MACH_MX27 || (ARCH_MX25 && BROKEN)) + tristate "i.MX27 Camera Sensor Interface driver" + depends on VIDEO_DEV && SOC_CAMERA && MACH_MX27 select VIDEOBUF2_DMA_CONTIG select VIDEO_MX2_HOSTSUPPORT ---help--- - This is a v4l2 driver for the i.MX27 and the i.MX25 Camera Sensor - Interface + This is a v4l2 driver for the i.MX27 Camera Sensor Interface config VIDEO_ATMEL_ISI tristate "ATMEL Image Sensor Interface (ISI) support" diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index bf2740f7c4d1..c0511c001be1 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -1,5 +1,5 @@ /* - * V4L2 Driver for i.MX27/i.MX25 camera host + * V4L2 Driver for i.MX27 camera host * * Copyright (C) 2008, Sascha Hauer, Pengutronix * Copyright (C) 2010, Baruch Siach, Orex Computed Radiography @@ -63,9 +63,7 @@ #define CSICR1_RF_OR_INTEN (1 << 24) #define CSICR1_STATFF_LEVEL (3 << 22) #define CSICR1_STATFF_INTEN (1 << 21) -#define CSICR1_RXFF_LEVEL(l) (((l) & 3) << 19) /* MX27 */ -#define CSICR1_FB2_DMA_INTEN (1 << 20) /* MX25 */ -#define CSICR1_FB1_DMA_INTEN (1 << 19) /* MX25 */ +#define CSICR1_RXFF_LEVEL(l) (((l) & 3) << 19) #define CSICR1_RXFF_INTEN (1 << 18) #define CSICR1_SOF_POL (1 << 17) #define CSICR1_SOF_INTEN (1 << 16) @@ -87,45 +85,15 @@ #define SHIFT_RXFF_LEVEL 19 #define SHIFT_MCLKDIV 12 -/* control reg 3 */ -#define CSICR3_FRMCNT (0xFFFF << 16) -#define CSICR3_FRMCNT_RST (1 << 15) -#define CSICR3_DMA_REFLASH_RFF (1 << 14) -#define CSICR3_DMA_REFLASH_SFF (1 << 13) -#define CSICR3_DMA_REQ_EN_RFF (1 << 12) -#define CSICR3_DMA_REQ_EN_SFF (1 << 11) -#define CSICR3_RXFF_LEVEL(l) (((l) & 7) << 4) /* MX25 */ -#define CSICR3_CSI_SUP (1 << 3) -#define CSICR3_ZERO_PACK_EN (1 << 2) -#define CSICR3_ECC_INT_EN (1 << 1) -#define CSICR3_ECC_AUTO_EN (1 << 0) - #define SHIFT_FRMCNT 16 -/* csi status reg */ -#define CSISR_SFF_OR_INT (1 << 25) -#define CSISR_RFF_OR_INT (1 << 24) -#define CSISR_STATFF_INT (1 << 21) -#define CSISR_DMA_TSF_FB2_INT (1 << 20) /* MX25 */ -#define CSISR_DMA_TSF_FB1_INT (1 << 19) /* MX25 */ -#define CSISR_RXFF_INT (1 << 18) -#define CSISR_EOF_INT (1 << 17) -#define CSISR_SOF_INT (1 << 16) -#define CSISR_F2_INT (1 << 15) -#define CSISR_F1_INT (1 << 14) -#define CSISR_COF_INT (1 << 13) -#define CSISR_ECC_INT (1 << 1) -#define CSISR_DRDY (1 << 0) - #define CSICR1 0x00 #define CSICR2 0x04 -#define CSISR_IMX25 0x18 -#define CSISR_IMX27 0x08 +#define CSISR 0x08 #define CSISTATFIFO 0x0c #define CSIRFIFO 0x10 #define CSIRXCNT 0x14 -#define CSICR3_IMX25 0x08 -#define CSICR3_IMX27 0x1c +#define CSICR3 0x1c #define CSIDMASA_STATFIFO 0x20 #define CSIDMATA_STATFIFO 0x24 #define CSIDMASA_FB1 0x28 @@ -269,7 +237,6 @@ struct mx2_buffer { }; enum mx2_camera_type { - IMX25_CAMERA, IMX27_CAMERA, }; @@ -297,8 +264,6 @@ struct mx2_camera_dev { struct mx2_buffer *fb2_active; u32 csicr1; - u32 reg_csisr; - u32 reg_csicr3; enum mx2_camera_type devtype; struct mx2_buf_internal buf_discard[2]; @@ -314,9 +279,6 @@ struct mx2_camera_dev { static struct platform_device_id mx2_camera_devtype[] = { { - .name = "imx25-camera", - .driver_data = IMX25_CAMERA, - }, { .name = "imx27-camera", .driver_data = IMX27_CAMERA, }, { @@ -325,16 +287,6 @@ static struct platform_device_id mx2_camera_devtype[] = { }; MODULE_DEVICE_TABLE(platform, mx2_camera_devtype); -static inline int is_imx25_camera(struct mx2_camera_dev *pcdev) -{ - return pcdev->devtype == IMX25_CAMERA; -} - -static inline int is_imx27_camera(struct mx2_camera_dev *pcdev) -{ - return pcdev->devtype == IMX27_CAMERA; -} - static struct mx2_buffer *mx2_ibuf_to_buf(struct mx2_buf_internal *int_buf) { return container_of(int_buf, struct mx2_buffer, internal); @@ -462,21 +414,10 @@ static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev, static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev) { - unsigned long flags; - clk_disable_unprepare(pcdev->clk_csi_ahb); clk_disable_unprepare(pcdev->clk_csi_per); writel(0, pcdev->base_csi + CSICR1); - if (is_imx27_camera(pcdev)) { - writel(0, pcdev->base_emma + PRP_CNTL); - } else if (is_imx25_camera(pcdev)) { - spin_lock_irqsave(&pcdev->lock, flags); - pcdev->fb1_active = NULL; - pcdev->fb2_active = NULL; - writel(0, pcdev->base_csi + CSIDMASA_FB1); - writel(0, pcdev->base_csi + CSIDMASA_FB2); - spin_unlock_irqrestore(&pcdev->lock, flags); - } + writel(0, pcdev->base_emma + PRP_CNTL); } /* @@ -501,11 +442,8 @@ static int mx2_camera_add_device(struct soc_camera_device *icd) if (ret < 0) goto exit_csi_ahb; - csicr1 = CSICR1_MCLKEN; - - if (is_imx27_camera(pcdev)) - csicr1 |= CSICR1_PRP_IF_EN | CSICR1_FCC | - CSICR1_RXFF_LEVEL(0); + csicr1 = CSICR1_MCLKEN | CSICR1_PRP_IF_EN | CSICR1_FCC | + CSICR1_RXFF_LEVEL(0); pcdev->csicr1 = csicr1; writel(pcdev->csicr1, pcdev->base_csi + CSICR1); @@ -539,65 +477,6 @@ static void mx2_camera_remove_device(struct soc_camera_device *icd) pcdev->icd = NULL; } -static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb, - int state) -{ - struct vb2_buffer *vb; - struct mx2_buffer *buf; - struct mx2_buffer **fb_active = fb == 1 ? &pcdev->fb1_active : - &pcdev->fb2_active; - u32 fb_reg = fb == 1 ? CSIDMASA_FB1 : CSIDMASA_FB2; - unsigned long flags; - - spin_lock_irqsave(&pcdev->lock, flags); - - if (*fb_active == NULL) - goto out; - - vb = &(*fb_active)->vb; - dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, - vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - - v4l2_get_timestamp(&vb->v4l2_buf.timestamp); - vb->v4l2_buf.sequence++; - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); - - if (list_empty(&pcdev->capture)) { - buf = NULL; - writel(0, pcdev->base_csi + fb_reg); - } else { - buf = list_first_entry(&pcdev->capture, struct mx2_buffer, - internal.queue); - vb = &buf->vb; - list_del(&buf->internal.queue); - buf->state = MX2_STATE_ACTIVE; - writel(vb2_dma_contig_plane_dma_addr(vb, 0), - pcdev->base_csi + fb_reg); - } - - *fb_active = buf; - -out: - spin_unlock_irqrestore(&pcdev->lock, flags); -} - -static irqreturn_t mx25_camera_irq(int irq_csi, void *data) -{ - struct mx2_camera_dev *pcdev = data; - u32 status = readl(pcdev->base_csi + pcdev->reg_csisr); - - if (status & CSISR_DMA_TSF_FB1_INT) - mx25_camera_frame_done(pcdev, 1, MX2_STATE_DONE); - else if (status & CSISR_DMA_TSF_FB2_INT) - mx25_camera_frame_done(pcdev, 2, MX2_STATE_DONE); - - /* FIXME: handle CSISR_RFF_OR_INT */ - - writel(status, pcdev->base_csi + pcdev->reg_csisr); - - return IRQ_HANDLED; -} - /* * Videobuf operations */ @@ -678,54 +557,17 @@ static void mx2_videobuf_queue(struct vb2_buffer *vb) buf->state = MX2_STATE_QUEUED; list_add_tail(&buf->internal.queue, &pcdev->capture); - if (is_imx25_camera(pcdev)) { - u32 csicr3, dma_inten = 0; - - if (pcdev->fb1_active == NULL) { - writel(vb2_dma_contig_plane_dma_addr(vb, 0), - pcdev->base_csi + CSIDMASA_FB1); - pcdev->fb1_active = buf; - dma_inten = CSICR1_FB1_DMA_INTEN; - } else if (pcdev->fb2_active == NULL) { - writel(vb2_dma_contig_plane_dma_addr(vb, 0), - pcdev->base_csi + CSIDMASA_FB2); - pcdev->fb2_active = buf; - dma_inten = CSICR1_FB2_DMA_INTEN; - } - - if (dma_inten) { - list_del(&buf->internal.queue); - buf->state = MX2_STATE_ACTIVE; - - csicr3 = readl(pcdev->base_csi + pcdev->reg_csicr3); - - /* Reflash DMA */ - writel(csicr3 | CSICR3_DMA_REFLASH_RFF, - pcdev->base_csi + pcdev->reg_csicr3); - - /* clear & enable interrupts */ - writel(dma_inten, pcdev->base_csi + pcdev->reg_csisr); - pcdev->csicr1 |= dma_inten; - writel(pcdev->csicr1, pcdev->base_csi + CSICR1); - - /* enable DMA */ - csicr3 |= CSICR3_DMA_REQ_EN_RFF | CSICR3_RXFF_LEVEL(1); - writel(csicr3, pcdev->base_csi + pcdev->reg_csicr3); - } - } - spin_unlock_irqrestore(&pcdev->lock, flags); } static void mx2_videobuf_release(struct vb2_buffer *vb) { +#ifdef DEBUG struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); - unsigned long flags; -#ifdef DEBUG dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); @@ -744,29 +586,11 @@ static void mx2_videobuf_release(struct vb2_buffer *vb) #endif /* - * Terminate only queued but inactive buffers. Active buffers are - * released when they become inactive after videobuf_waiton(). - * * FIXME: implement forced termination of active buffers for mx27 and * mx27 eMMA, so that the user won't get stuck in an uninterruptible * state. This requires a specific handling for each of the these DMA * types. */ - - spin_lock_irqsave(&pcdev->lock, flags); - if (is_imx25_camera(pcdev) && buf->state == MX2_STATE_ACTIVE) { - if (pcdev->fb1_active == buf) { - pcdev->csicr1 &= ~CSICR1_FB1_DMA_INTEN; - writel(0, pcdev->base_csi + CSIDMASA_FB1); - pcdev->fb1_active = NULL; - } else if (pcdev->fb2_active == buf) { - pcdev->csicr1 &= ~CSICR1_FB2_DMA_INTEN; - writel(0, pcdev->base_csi + CSIDMASA_FB2); - pcdev->fb2_active = NULL; - } - writel(pcdev->csicr1, pcdev->base_csi + CSICR1); - } - spin_unlock_irqrestore(&pcdev->lock, flags); } static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, @@ -876,91 +700,89 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) struct mx2_buffer *buf; unsigned long phys; int bytesperline; + unsigned long flags; - if (is_imx27_camera(pcdev)) { - unsigned long flags; - if (count < 2) - return -EINVAL; + if (count < 2) + return -EINVAL; - spin_lock_irqsave(&pcdev->lock, flags); + spin_lock_irqsave(&pcdev->lock, flags); - buf = list_first_entry(&pcdev->capture, struct mx2_buffer, - internal.queue); - buf->internal.bufnum = 0; - vb = &buf->vb; - buf->state = MX2_STATE_ACTIVE; + buf = list_first_entry(&pcdev->capture, struct mx2_buffer, + internal.queue); + buf->internal.bufnum = 0; + vb = &buf->vb; + buf->state = MX2_STATE_ACTIVE; - phys = vb2_dma_contig_plane_dma_addr(vb, 0); - mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); - list_move_tail(pcdev->capture.next, &pcdev->active_bufs); + phys = vb2_dma_contig_plane_dma_addr(vb, 0); + mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); + list_move_tail(pcdev->capture.next, &pcdev->active_bufs); - buf = list_first_entry(&pcdev->capture, struct mx2_buffer, - internal.queue); - buf->internal.bufnum = 1; - vb = &buf->vb; - buf->state = MX2_STATE_ACTIVE; + buf = list_first_entry(&pcdev->capture, struct mx2_buffer, + internal.queue); + buf->internal.bufnum = 1; + vb = &buf->vb; + buf->state = MX2_STATE_ACTIVE; - phys = vb2_dma_contig_plane_dma_addr(vb, 0); - mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); - list_move_tail(pcdev->capture.next, &pcdev->active_bufs); - - bytesperline = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - if (bytesperline < 0) { - spin_unlock_irqrestore(&pcdev->lock, flags); - return bytesperline; - } + phys = vb2_dma_contig_plane_dma_addr(vb, 0); + mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); + list_move_tail(pcdev->capture.next, &pcdev->active_bufs); - /* - * I didn't manage to properly enable/disable the prp - * on a per frame basis during running transfers, - * thus we allocate a buffer here and use it to - * discard frames when no buffer is available. - * Feel free to work on this ;) - */ - pcdev->discard_size = icd->user_height * bytesperline; - pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, - pcdev->discard_size, &pcdev->discard_buffer_dma, - GFP_ATOMIC); - if (!pcdev->discard_buffer) { - spin_unlock_irqrestore(&pcdev->lock, flags); - return -ENOMEM; - } + bytesperline = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + if (bytesperline < 0) { + spin_unlock_irqrestore(&pcdev->lock, flags); + return bytesperline; + } - pcdev->buf_discard[0].discard = true; - list_add_tail(&pcdev->buf_discard[0].queue, - &pcdev->discard); + /* + * I didn't manage to properly enable/disable the prp + * on a per frame basis during running transfers, + * thus we allocate a buffer here and use it to + * discard frames when no buffer is available. + * Feel free to work on this ;) + */ + pcdev->discard_size = icd->user_height * bytesperline; + pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, + pcdev->discard_size, + &pcdev->discard_buffer_dma, GFP_ATOMIC); + if (!pcdev->discard_buffer) { + spin_unlock_irqrestore(&pcdev->lock, flags); + return -ENOMEM; + } - pcdev->buf_discard[1].discard = true; - list_add_tail(&pcdev->buf_discard[1].queue, - &pcdev->discard); + pcdev->buf_discard[0].discard = true; + list_add_tail(&pcdev->buf_discard[0].queue, + &pcdev->discard); - mx2_prp_resize_commit(pcdev); + pcdev->buf_discard[1].discard = true; + list_add_tail(&pcdev->buf_discard[1].queue, + &pcdev->discard); - mx27_camera_emma_buf_init(icd, bytesperline); + mx2_prp_resize_commit(pcdev); - if (prp->cfg.channel == 1) { - writel(PRP_CNTL_CH1EN | - PRP_CNTL_CSIEN | - prp->cfg.in_fmt | - prp->cfg.out_fmt | - PRP_CNTL_CH1_LEN | - PRP_CNTL_CH1BYP | - PRP_CNTL_CH1_TSKIP(0) | - PRP_CNTL_IN_TSKIP(0), - pcdev->base_emma + PRP_CNTL); - } else { - writel(PRP_CNTL_CH2EN | - PRP_CNTL_CSIEN | - prp->cfg.in_fmt | - prp->cfg.out_fmt | - PRP_CNTL_CH2_LEN | - PRP_CNTL_CH2_TSKIP(0) | - PRP_CNTL_IN_TSKIP(0), - pcdev->base_emma + PRP_CNTL); - } - spin_unlock_irqrestore(&pcdev->lock, flags); + mx27_camera_emma_buf_init(icd, bytesperline); + + if (prp->cfg.channel == 1) { + writel(PRP_CNTL_CH1EN | + PRP_CNTL_CSIEN | + prp->cfg.in_fmt | + prp->cfg.out_fmt | + PRP_CNTL_CH1_LEN | + PRP_CNTL_CH1BYP | + PRP_CNTL_CH1_TSKIP(0) | + PRP_CNTL_IN_TSKIP(0), + pcdev->base_emma + PRP_CNTL); + } else { + writel(PRP_CNTL_CH2EN | + PRP_CNTL_CSIEN | + prp->cfg.in_fmt | + prp->cfg.out_fmt | + PRP_CNTL_CH2_LEN | + PRP_CNTL_CH2_TSKIP(0) | + PRP_CNTL_IN_TSKIP(0), + pcdev->base_emma + PRP_CNTL); } + spin_unlock_irqrestore(&pcdev->lock, flags); return 0; } @@ -976,29 +798,27 @@ static int mx2_stop_streaming(struct vb2_queue *q) void *b; u32 cntl; - if (is_imx27_camera(pcdev)) { - spin_lock_irqsave(&pcdev->lock, flags); + spin_lock_irqsave(&pcdev->lock, flags); - cntl = readl(pcdev->base_emma + PRP_CNTL); - if (prp->cfg.channel == 1) { - writel(cntl & ~PRP_CNTL_CH1EN, - pcdev->base_emma + PRP_CNTL); - } else { - writel(cntl & ~PRP_CNTL_CH2EN, - pcdev->base_emma + PRP_CNTL); - } - INIT_LIST_HEAD(&pcdev->capture); - INIT_LIST_HEAD(&pcdev->active_bufs); - INIT_LIST_HEAD(&pcdev->discard); + cntl = readl(pcdev->base_emma + PRP_CNTL); + if (prp->cfg.channel == 1) { + writel(cntl & ~PRP_CNTL_CH1EN, + pcdev->base_emma + PRP_CNTL); + } else { + writel(cntl & ~PRP_CNTL_CH2EN, + pcdev->base_emma + PRP_CNTL); + } + INIT_LIST_HEAD(&pcdev->capture); + INIT_LIST_HEAD(&pcdev->active_bufs); + INIT_LIST_HEAD(&pcdev->discard); - b = pcdev->discard_buffer; - pcdev->discard_buffer = NULL; + b = pcdev->discard_buffer; + pcdev->discard_buffer = NULL; - spin_unlock_irqrestore(&pcdev->lock, flags); + spin_unlock_irqrestore(&pcdev->lock, flags); - dma_free_coherent(ici->v4l2_dev.dev, - pcdev->discard_size, b, pcdev->discard_buffer_dma); - } + dma_free_coherent(ici->v4l2_dev.dev, + pcdev->discard_size, b, pcdev->discard_buffer_dma); return 0; } @@ -1128,16 +948,9 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd) if (bytesperline < 0) return bytesperline; - if (is_imx27_camera(pcdev)) { - ret = mx27_camera_emma_prp_reset(pcdev); - if (ret) - return ret; - } else if (is_imx25_camera(pcdev)) { - writel((bytesperline * icd->user_height) >> 2, - pcdev->base_csi + CSIRXCNT); - writel((bytesperline << 16) | icd->user_height, - pcdev->base_csi + CSIIMAG_PARA); - } + ret = mx27_camera_emma_prp_reset(pcdev); + if (ret) + return ret; writel(pcdev->csicr1, pcdev->base_csi + CSICR1); @@ -1424,7 +1237,6 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; struct mx2_fmt_cfg *emma_prp; - unsigned int width_limit; int ret; dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", @@ -1436,44 +1248,11 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, return -EINVAL; } - /* limit to MX25 hardware capabilities */ - if (is_imx25_camera(pcdev)) { - if (xlate->host_fmt->bits_per_sample <= 8) - width_limit = 0xffff * 4; - else - width_limit = 0xffff * 2; - /* CSIIMAG_PARA limit */ - if (pix->width > width_limit) - pix->width = width_limit; - if (pix->height > 0xffff) - pix->height = 0xffff; - - pix->bytesperline = soc_mbus_bytes_per_line(pix->width, - xlate->host_fmt); - if (pix->bytesperline < 0) - return pix->bytesperline; - pix->sizeimage = soc_mbus_image_size(xlate->host_fmt, - pix->bytesperline, pix->height); - /* Check against the CSIRXCNT limit */ - if (pix->sizeimage > 4 * 0x3ffff) { - /* Adjust geometry, preserve aspect ratio */ - unsigned int new_height = int_sqrt(div_u64(0x3ffffULL * - 4 * pix->height, pix->bytesperline)); - pix->width = new_height * pix->width / pix->height; - pix->height = new_height; - pix->bytesperline = soc_mbus_bytes_per_line(pix->width, - xlate->host_fmt); - BUG_ON(pix->bytesperline < 0); - pix->sizeimage = soc_mbus_image_size(xlate->host_fmt, - pix->bytesperline, pix->height); - } - } else { - /* - * Width must be a multiple of 8 as requested by the CSI. - * (Table 39-2 in the i.MX27 Reference Manual). - */ - pix->width &= ~0x7; - } + /* + * limit to MX27 hardware capabilities: width must be a multiple of 8 as + * requested by the CSI. (Table 39-2 in the i.MX27 Reference Manual). + */ + pix->width &= ~0x7; /* limit to sensor capabilities */ mf.width = pix->width; @@ -1491,7 +1270,7 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, /* If the sensor does not support image size try PrP resizing */ emma_prp = mx27_emma_prp_get_format(xlate->code, - xlate->host_fmt->fourcc); + xlate->host_fmt->fourcc); if ((mf.width != pix->width || mf.height != pix->height) && emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { @@ -1777,20 +1556,6 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) goto exit; } - pcdev->devtype = pdev->id_entry->driver_data; - switch (pcdev->devtype) { - case IMX25_CAMERA: - pcdev->reg_csisr = CSISR_IMX25; - pcdev->reg_csicr3 = CSICR3_IMX25; - break; - case IMX27_CAMERA: - pcdev->reg_csisr = CSISR_IMX27; - pcdev->reg_csicr3 = CSICR3_IMX27; - break; - default: - break; - } - pcdev->clk_csi_ahb = devm_clk_get(&pdev->dev, "ahb"); if (IS_ERR(pcdev->clk_csi_ahb)) { dev_err(&pdev->dev, "Could not get csi ahb clock\n"); @@ -1836,20 +1601,9 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) pcdev->dev = &pdev->dev; platform_set_drvdata(pdev, pcdev); - if (is_imx25_camera(pcdev)) { - err = devm_request_irq(&pdev->dev, irq_csi, mx25_camera_irq, 0, - MX2_CAM_DRV_NAME, pcdev); - if (err) { - dev_err(pcdev->dev, "Camera interrupt register failed \n"); - goto exit; - } - } - - if (is_imx27_camera(pcdev)) { - err = mx27_camera_emma_init(pdev); - if (err) - goto exit; - } + err = mx27_camera_emma_init(pdev); + if (err) + goto exit; /* * We're done with drvdata here. Clear the pointer so that @@ -1862,8 +1616,6 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) pcdev->soc_host.priv = pcdev; pcdev->soc_host.v4l2_dev.dev = &pdev->dev; pcdev->soc_host.nr = pdev->id; - if (is_imx25_camera(pcdev)) - pcdev->soc_host.capabilities = SOCAM_HOST_CAP_STRIDE; pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); if (IS_ERR(pcdev->alloc_ctx)) { @@ -1882,10 +1634,8 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) exit_free_emma: vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); eallocctx: - if (is_imx27_camera(pcdev)) { - clk_disable_unprepare(pcdev->clk_emma_ipg); - clk_disable_unprepare(pcdev->clk_emma_ahb); - } + clk_disable_unprepare(pcdev->clk_emma_ipg); + clk_disable_unprepare(pcdev->clk_emma_ahb); exit: return err; } @@ -1900,10 +1650,8 @@ static int __devexit mx2_camera_remove(struct platform_device *pdev) vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); - if (is_imx27_camera(pcdev)) { - clk_disable_unprepare(pcdev->clk_emma_ipg); - clk_disable_unprepare(pcdev->clk_emma_ahb); - } + clk_disable_unprepare(pcdev->clk_emma_ipg); + clk_disable_unprepare(pcdev->clk_emma_ahb); dev_info(&pdev->dev, "MX2 Camera driver unloaded\n"); @@ -1932,7 +1680,7 @@ static void __exit mx2_camera_exit(void) module_init(mx2_camera_init); module_exit(mx2_camera_exit); -MODULE_DESCRIPTION("i.MX27/i.MX25 SoC Camera Host driver"); +MODULE_DESCRIPTION("i.MX27 SoC Camera Host driver"); MODULE_AUTHOR("Sascha Hauer "); MODULE_LICENSE("GPL"); MODULE_VERSION(MX2_CAM_VERSION); -- cgit v1.2.3 From 22f9a477a23b2e4fe85017920aaf4fd86a8a6660 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 30 Oct 2012 11:29:01 -0300 Subject: [media] mx2_camera: Remove 'buf_cleanup' callback All necessary tasks to end the streaming properly are already implemented in mx2_stop_streaming() and nothing remains to be done in this callback. Furthermore, it only included debug messages so it can be removed. Signed-off-by: Javier Martin Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/mx2_camera.c | 34 -------------------------- 1 file changed, 34 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index c0511c001be1..c93e790594ef 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -560,39 +560,6 @@ static void mx2_videobuf_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&pcdev->lock, flags); } -static void mx2_videobuf_release(struct vb2_buffer *vb) -{ -#ifdef DEBUG - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); - - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, - vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - - switch (buf->state) { - case MX2_STATE_ACTIVE: - dev_info(icd->parent, "%s (active)\n", __func__); - break; - case MX2_STATE_QUEUED: - dev_info(icd->parent, "%s (queued)\n", __func__); - break; - default: - dev_info(icd->parent, "%s (unknown) %d\n", __func__, - buf->state); - break; - } -#endif - - /* - * FIXME: implement forced termination of active buffers for mx27 and - * mx27 eMMA, so that the user won't get stuck in an uninterruptible - * state. This requires a specific handling for each of the these DMA - * types. - */ -} - static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, int bytesperline) { @@ -827,7 +794,6 @@ static struct vb2_ops mx2_videobuf_ops = { .queue_setup = mx2_videobuf_setup, .buf_prepare = mx2_videobuf_prepare, .buf_queue = mx2_videobuf_queue, - .buf_cleanup = mx2_videobuf_release, .start_streaming = mx2_start_streaming, .stop_streaming = mx2_stop_streaming, }; -- cgit v1.2.3 From 3fdd97973b357255c291beaad5cdf6255d3ff936 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 30 Oct 2012 11:29:02 -0300 Subject: [media] mx2_camera: Remove buffer states After removing i.mx25 support and buf_cleanup() callback, buffer states are not used in the code any longer. Signed-off-by: Javier Martin Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/mx2_camera.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index c93e790594ef..188541ac355a 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -216,12 +216,6 @@ struct mx2_fmt_cfg { struct mx2_prp_cfg cfg; }; -enum mx2_buffer_state { - MX2_STATE_QUEUED, - MX2_STATE_ACTIVE, - MX2_STATE_DONE, -}; - struct mx2_buf_internal { struct list_head queue; int bufnum; @@ -232,7 +226,6 @@ struct mx2_buf_internal { struct mx2_buffer { /* common v4l buffer stuff -- must be first */ struct vb2_buffer vb; - enum mx2_buffer_state state; struct mx2_buf_internal internal; }; @@ -554,7 +547,6 @@ static void mx2_videobuf_queue(struct vb2_buffer *vb) spin_lock_irqsave(&pcdev->lock, flags); - buf->state = MX2_STATE_QUEUED; list_add_tail(&buf->internal.queue, &pcdev->capture); spin_unlock_irqrestore(&pcdev->lock, flags); @@ -678,7 +670,6 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) internal.queue); buf->internal.bufnum = 0; vb = &buf->vb; - buf->state = MX2_STATE_ACTIVE; phys = vb2_dma_contig_plane_dma_addr(vb, 0); mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); @@ -688,7 +679,6 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) internal.queue); buf->internal.bufnum = 1; vb = &buf->vb; - buf->state = MX2_STATE_ACTIVE; phys = vb2_dma_contig_plane_dma_addr(vb, 0); mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); @@ -1382,7 +1372,6 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev, list_move_tail(pcdev->capture.next, &pcdev->active_bufs); vb = &buf->vb; - buf->state = MX2_STATE_ACTIVE; phys = vb2_dma_contig_plane_dma_addr(vb, 0); mx27_update_emma_buf(pcdev, phys, bufnum); -- cgit v1.2.3 From 39793c6900b89c22cf30e0c83207df8c0a5458df Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 30 Nov 2012 12:06:21 -0300 Subject: [media] mx2_camera: Convert it to platform driver Converting it to platform code can make the code smaller. Signed-off-by: Fabio Estevam Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/mx2_camera.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 188541ac355a..168e062164e9 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -1619,21 +1619,10 @@ static struct platform_driver mx2_camera_driver = { }, .id_table = mx2_camera_devtype, .remove = __devexit_p(mx2_camera_remove), + .probe = mx2_camera_probe, }; - -static int __init mx2_camera_init(void) -{ - return platform_driver_probe(&mx2_camera_driver, &mx2_camera_probe); -} - -static void __exit mx2_camera_exit(void) -{ - return platform_driver_unregister(&mx2_camera_driver); -} - -module_init(mx2_camera_init); -module_exit(mx2_camera_exit); +module_platform_driver(mx2_camera_driver); MODULE_DESCRIPTION("i.MX27 SoC Camera Host driver"); MODULE_AUTHOR("Sascha Hauer "); -- cgit v1.2.3 From 2c46bb119f13a3ebc62461dac498a7057f5b4a94 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Jan 2013 02:03:14 -0200 Subject: [media] ngene: fix commit 36a495a336c3fbbb2f4eeed2a94ab6d5be19d186 The above commit were applied only partially; it broke tuner and demod attach, but the part that added it to ngene_info was missing. Not sure what happened there, but, without this patch, a regression would be happening. Also, gcc complains about a defined but not used symbol. So, apply manually the missing part. Cc: Patrice Chotard Cc: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ngene/ngene-cards.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/pci/ngene/ngene-cards.c b/drivers/media/pci/ngene/ngene-cards.c index 2a4895b0706b..82318d11696f 100644 --- a/drivers/media/pci/ngene/ngene-cards.c +++ b/drivers/media/pci/ngene/ngene-cards.c @@ -732,6 +732,7 @@ static struct ngene_info ngene_info_terratec = { .name = "Terratec Integra/Cinergy2400i Dual DVB-T", .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, .demod_attach = {demod_attach_drxd, demod_attach_drxd}, + .tuner_attach = {tuner_attach_dtt7520x, tuner_attach_dtt7520x}, .fe_config = {&fe_terratec_dvbt_0, &fe_terratec_dvbt_1}, .i2c_access = 1, }; -- cgit v1.2.3 From 3ff59bcee7be7796ce566dfef0604eda013cc8c2 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Fri, 21 Dec 2012 10:04:01 -0700 Subject: crypto: omap-sham - Remove unnecessary pr_info noise Remove the unnecessary pr_info() call in omap_sham_mod_init(). CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-sham.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index 1d75e6f95a58..2df7a54fc3b9 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -1286,8 +1286,6 @@ static struct platform_driver omap_sham_driver = { static int __init omap_sham_mod_init(void) { - pr_info("loading %s driver\n", "omap-sham"); - return platform_driver_register(&omap_sham_driver); } -- cgit v1.2.3 From b359f034c8bf6c6ae4785c1172786ce73eccf9f2 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Fri, 21 Dec 2012 10:04:02 -0700 Subject: crypto: omap-sham - Convert to use pm_runtime API Convert the omap-sham crypto driver to use the pm_runtime API instead of the clk API. CC: Kevin Hilman CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-sham.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index 2df7a54fc3b9..777eb9fdd096 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -22,12 +22,12 @@ #include #include #include -#include #include #include #include #include #include +#include #include #include #include @@ -140,7 +140,6 @@ struct omap_sham_dev { struct device *dev; void __iomem *io_base; int irq; - struct clk *iclk; spinlock_t lock; int err; int dma; @@ -237,7 +236,7 @@ static void omap_sham_copy_ready_hash(struct ahash_request *req) static int omap_sham_hw_init(struct omap_sham_dev *dd) { - clk_enable(dd->iclk); + pm_runtime_get_sync(dd->dev); if (!test_bit(FLAGS_INIT, &dd->flags)) { omap_sham_write_mask(dd, SHA_REG_MASK, @@ -652,7 +651,8 @@ static void omap_sham_finish_req(struct ahash_request *req, int err) /* atomic operation is not needed here */ dd->flags &= ~(BIT(FLAGS_BUSY) | BIT(FLAGS_FINAL) | BIT(FLAGS_CPU) | BIT(FLAGS_DMA_READY) | BIT(FLAGS_OUTPUT_READY)); - clk_disable(dd->iclk); + + pm_runtime_put_sync(dd->dev); if (req->base.complete) req->base.complete(&req->base, err); @@ -1197,14 +1197,6 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) if (err) goto dma_err; - /* Initializing the clock */ - dd->iclk = clk_get(dev, "ick"); - if (IS_ERR(dd->iclk)) { - dev_err(dev, "clock intialization failed.\n"); - err = PTR_ERR(dd->iclk); - goto clk_err; - } - dd->io_base = ioremap(dd->phys_base, SZ_4K); if (!dd->io_base) { dev_err(dev, "can't ioremap\n"); @@ -1212,11 +1204,14 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) goto io_err; } - clk_enable(dd->iclk); + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + dev_info(dev, "hw accel on OMAP rev %u.%u\n", (omap_sham_read(dd, SHA_REG_REV) & SHA_REG_REV_MAJOR) >> 4, omap_sham_read(dd, SHA_REG_REV) & SHA_REG_REV_MINOR); - clk_disable(dd->iclk); + + pm_runtime_put_sync(&pdev->dev); spin_lock(&sham.lock); list_add_tail(&dd->list, &sham.dev_list); @@ -1234,9 +1229,8 @@ err_algs: for (j = 0; j < i; j++) crypto_unregister_ahash(&algs[j]); iounmap(dd->io_base); + pm_runtime_disable(dev); io_err: - clk_put(dd->iclk); -clk_err: omap_sham_dma_cleanup(dd); dma_err: if (dd->irq >= 0) @@ -1265,7 +1259,7 @@ static int __devexit omap_sham_remove(struct platform_device *pdev) crypto_unregister_ahash(&algs[i]); tasklet_kill(&dd->done_task); iounmap(dd->io_base); - clk_put(dd->iclk); + pm_runtime_disable(&pdev->dev); omap_sham_dma_cleanup(dd); if (dd->irq >= 0) free_irq(dd->irq, dd); -- cgit v1.2.3 From 3b3f440023b3809c8eabec681768a4bcee15f2b4 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Fri, 21 Dec 2012 10:04:03 -0700 Subject: crypto: omap-sham - Add suspend/resume support Add suspend/resume support to the OMAP SHAM driver. CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-sham.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers') diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index 777eb9fdd096..9c3b096e15e8 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -1269,12 +1269,31 @@ static int __devexit omap_sham_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int omap_sham_suspend(struct device *dev) +{ + pm_runtime_put_sync(dev); + return 0; +} + +static int omap_sham_resume(struct device *dev) +{ + pm_runtime_get_sync(dev); + return 0; +} +#endif + +static const struct dev_pm_ops omap_sham_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(omap_sham_suspend, omap_sham_resume) +}; + static struct platform_driver omap_sham_driver = { .probe = omap_sham_probe, .remove = omap_sham_remove, .driver = { .name = "omap-sham", .owner = THIS_MODULE, + .pm = &omap_sham_pm_ops, }, }; -- cgit v1.2.3 From dfd061d5a8f5a6d89c77d23693a63038ff8cbcd8 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Fri, 21 Dec 2012 10:04:04 -0700 Subject: crypto: omap-sham - Add code to use dmaengine API Add code to use the new dmaengine API alongside the existing DMA code that uses the private OMAP DMA API. The API to use is chosen by defining or undefining 'OMAP_SHAM_DMA_PRIVATE'. This is a transitional change and the code that uses the private DMA API will be removed in an upcoming commit. CC: Russell King CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-sham.c | 154 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 145 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index 9c3b096e15e8..f54ceb8f5b24 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -13,6 +13,8 @@ * Some ideas are from old omap-sha1-md5.c driver. */ +#define OMAP_SHAM_DMA_PRIVATE + #define pr_fmt(fmt) "%s: " fmt, __func__ #include @@ -27,6 +29,8 @@ #include #include #include +#include +#include #include #include #include @@ -37,15 +41,15 @@ #include #include -#include -#include - #define SHA_REG_DIGEST(x) (0x00 + ((x) * 0x04)) #define SHA_REG_DIN(x) (0x1C + ((x) * 0x04)) #define SHA1_MD5_BLOCK_SIZE SHA1_BLOCK_SIZE #define MD5_DIGEST_SIZE 16 +#define DST_MAXBURST 16 +#define DMA_MIN (DST_MAXBURST * sizeof(u32)) + #define SHA_REG_DIGCNT 0x14 #define SHA_REG_CTRL 0x18 @@ -109,6 +113,9 @@ struct omap_sham_reqctx { /* walk state */ struct scatterlist *sg; +#ifndef OMAP_SHAM_DMA_PRIVATE + struct scatterlist sgl; +#endif unsigned int offset; /* offset in current sg */ unsigned int total; /* total request */ @@ -142,8 +149,12 @@ struct omap_sham_dev { int irq; spinlock_t lock; int err; +#ifdef OMAP_SHAM_DMA_PRIVATE int dma; int dma_lch; +#else + struct dma_chan *dma_lch; +#endif struct tasklet_struct done_task; unsigned long flags; @@ -313,15 +324,32 @@ static int omap_sham_xmit_cpu(struct omap_sham_dev *dd, const u8 *buf, return -EINPROGRESS; } +#ifndef OMAP_SHAM_DMA_PRIVATE +static void omap_sham_dma_callback(void *param) +{ + struct omap_sham_dev *dd = param; + + set_bit(FLAGS_DMA_READY, &dd->flags); + tasklet_schedule(&dd->done_task); +} +#endif + static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, - size_t length, int final) + size_t length, int final, int is_sg) { struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req); +#ifdef OMAP_SHAM_DMA_PRIVATE int len32; +#else + struct dma_async_tx_descriptor *tx; + struct dma_slave_config cfg; + int len32, ret; +#endif dev_dbg(dd->dev, "xmit_dma: digcnt: %d, length: %d, final: %d\n", ctx->digcnt, length, final); +#ifdef OMAP_SHAM_DMA_PRIVATE len32 = DIV_ROUND_UP(length, sizeof(u32)); omap_set_dma_transfer_params(dd->dma_lch, OMAP_DMA_DATA_TYPE_S32, len32, @@ -331,6 +359,50 @@ static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, omap_set_dma_src_params(dd->dma_lch, 0, OMAP_DMA_AMODE_POST_INC, dma_addr, 0, 0); +#else + memset(&cfg, 0, sizeof(cfg)); + + cfg.dst_addr = dd->phys_base + SHA_REG_DIN(0); + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.dst_maxburst = DST_MAXBURST; + + ret = dmaengine_slave_config(dd->dma_lch, &cfg); + if (ret) { + pr_err("omap-sham: can't configure dmaengine slave: %d\n", ret); + return ret; + } + + len32 = DIV_ROUND_UP(length, DMA_MIN) * DMA_MIN; + + if (is_sg) { + /* + * The SG entry passed in may not have the 'length' member + * set correctly so use a local SG entry (sgl) with the + * proper value for 'length' instead. If this is not done, + * the dmaengine may try to DMA the incorrect amount of data. + */ + sg_init_table(&ctx->sgl, 1); + ctx->sgl.page_link = ctx->sg->page_link; + ctx->sgl.offset = ctx->sg->offset; + sg_dma_len(&ctx->sgl) = len32; + sg_dma_address(&ctx->sgl) = sg_dma_address(ctx->sg); + + tx = dmaengine_prep_slave_sg(dd->dma_lch, &ctx->sgl, 1, + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + } else { + tx = dmaengine_prep_slave_single(dd->dma_lch, dma_addr, len32, + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + } + + if (!tx) { + dev_err(dd->dev, "prep_slave_sg/single() failed\n"); + return -EINVAL; + } + + tx->callback = omap_sham_dma_callback; + tx->callback_param = dd; +#endif + omap_sham_write_ctrl(dd, length, final, 1); ctx->digcnt += length; @@ -340,7 +412,12 @@ static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, set_bit(FLAGS_DMA_ACTIVE, &dd->flags); +#ifdef OMAP_SHAM_DMA_PRIVATE omap_start_dma(dd->dma_lch); +#else + dmaengine_submit(tx); + dma_async_issue_pending(dd->dma_lch); +#endif return -EINPROGRESS; } @@ -387,6 +464,8 @@ static int omap_sham_xmit_dma_map(struct omap_sham_dev *dd, struct omap_sham_reqctx *ctx, size_t length, int final) { + int ret; + ctx->dma_addr = dma_map_single(dd->dev, ctx->buffer, ctx->buflen, DMA_TO_DEVICE); if (dma_mapping_error(dd->dev, ctx->dma_addr)) { @@ -396,8 +475,12 @@ static int omap_sham_xmit_dma_map(struct omap_sham_dev *dd, ctx->flags &= ~BIT(FLAGS_SG); - /* next call does not fail... so no unmap in the case of error */ - return omap_sham_xmit_dma(dd, ctx->dma_addr, length, final); + ret = omap_sham_xmit_dma(dd, ctx->dma_addr, length, final, 0); + if (ret) + dma_unmap_single(dd->dev, ctx->dma_addr, ctx->buflen, + DMA_TO_DEVICE); + + return ret; } static int omap_sham_update_dma_slow(struct omap_sham_dev *dd) @@ -432,6 +515,7 @@ static int omap_sham_update_dma_start(struct omap_sham_dev *dd) struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req); unsigned int length, final, tail; struct scatterlist *sg; + int ret; if (!ctx->total) return 0; @@ -439,6 +523,17 @@ static int omap_sham_update_dma_start(struct omap_sham_dev *dd) if (ctx->bufcnt || ctx->offset) return omap_sham_update_dma_slow(dd); +#ifndef OMAP_SHAM_DMA_PRIVATE + /* + * Don't use the sg interface when the transfer size is less + * than the number of elements in a DMA frame. Otherwise, + * the dmaengine infrastructure will calculate that it needs + * to transfer 0 frames which ultimately fails. + */ + if (ctx->total < (DST_MAXBURST * sizeof(u32))) + return omap_sham_update_dma_slow(dd); +#endif + dev_dbg(dd->dev, "fast: digcnt: %d, bufcnt: %u, total: %u\n", ctx->digcnt, ctx->bufcnt, ctx->total); @@ -476,8 +571,11 @@ static int omap_sham_update_dma_start(struct omap_sham_dev *dd) final = (ctx->flags & BIT(FLAGS_FINUP)) && !ctx->total; - /* next call does not fail... so no unmap in the case of error */ - return omap_sham_xmit_dma(dd, sg_dma_address(ctx->sg), length, final); + ret = omap_sham_xmit_dma(dd, sg_dma_address(ctx->sg), length, final, 1); + if (ret) + dma_unmap_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE); + + return ret; } static int omap_sham_update_cpu(struct omap_sham_dev *dd) @@ -496,7 +594,12 @@ static int omap_sham_update_dma_stop(struct omap_sham_dev *dd) { struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req); +#ifdef OMAP_SHAM_DMA_PRIVATE omap_stop_dma(dd->dma_lch); +#else + dmaengine_terminate_all(dd->dma_lch); +#endif + if (ctx->flags & BIT(FLAGS_SG)) { dma_unmap_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE); if (ctx->sg->length == ctx->offset) { @@ -583,7 +686,7 @@ static int omap_sham_final_req(struct omap_sham_dev *dd) struct omap_sham_reqctx *ctx = ahash_request_ctx(req); int err = 0, use_dma = 1; - if (ctx->bufcnt <= 64) + if (ctx->bufcnt <= DMA_MIN) /* faster to handle last block with cpu */ use_dma = 0; @@ -699,6 +802,7 @@ static int omap_sham_handle_queue(struct omap_sham_dev *dd, if (err) goto err1; +#ifdef OMAP_SHAM_DMA_PRIVATE omap_set_dma_dest_params(dd->dma_lch, 0, OMAP_DMA_AMODE_CONSTANT, dd->phys_base + SHA_REG_DIN(0), 0, 16); @@ -708,6 +812,7 @@ static int omap_sham_handle_queue(struct omap_sham_dev *dd, omap_set_dma_src_burst_mode(dd->dma_lch, OMAP_DMA_DATA_BURST_4); +#endif if (ctx->digcnt) /* request has changed - restore hash */ @@ -1099,6 +1204,7 @@ static irqreturn_t omap_sham_irq(int irq, void *dev_id) return IRQ_HANDLED; } +#ifdef OMAP_SHAM_DMA_PRIVATE static void omap_sham_dma_callback(int lch, u16 ch_status, void *data) { struct omap_sham_dev *dd = data; @@ -1136,12 +1242,17 @@ static void omap_sham_dma_cleanup(struct omap_sham_dev *dd) dd->dma_lch = -1; } } +#endif static int __devinit omap_sham_probe(struct platform_device *pdev) { struct omap_sham_dev *dd; struct device *dev = &pdev->dev; struct resource *res; +#ifndef OMAP_SHAM_DMA_PRIVATE + dma_cap_mask_t mask; + unsigned dma_chan; +#endif int err, i, j; dd = kzalloc(sizeof(struct omap_sham_dev), GFP_KERNEL); @@ -1176,7 +1287,11 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) err = -ENODEV; goto res_err; } +#ifdef OMAP_SHAM_DMA_PRIVATE dd->dma = res->start; +#else + dma_chan = res->start; +#endif /* Get the IRQ */ dd->irq = platform_get_irq(pdev, 0); @@ -1193,9 +1308,22 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) goto res_err; } +#ifdef OMAP_SHAM_DMA_PRIVATE err = omap_sham_dma_init(dd); if (err) goto dma_err; +#else + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + dd->dma_lch = dma_request_channel(mask, omap_dma_filter_fn, &dma_chan); + if (!dd->dma_lch) { + dev_err(dev, "unable to obtain RX DMA engine channel %u\n", + dma_chan); + err = -ENXIO; + goto dma_err; + } +#endif dd->io_base = ioremap(dd->phys_base, SZ_4K); if (!dd->io_base) { @@ -1231,7 +1359,11 @@ err_algs: iounmap(dd->io_base); pm_runtime_disable(dev); io_err: +#ifdef OMAP_SHAM_DMA_PRIVATE omap_sham_dma_cleanup(dd); +#else + dma_release_channel(dd->dma_lch); +#endif dma_err: if (dd->irq >= 0) free_irq(dd->irq, dd); @@ -1260,7 +1392,11 @@ static int __devexit omap_sham_remove(struct platform_device *pdev) tasklet_kill(&dd->done_task); iounmap(dd->io_base); pm_runtime_disable(&pdev->dev); +#ifdef OMAP_SHAM_DMA_PRIVATE omap_sham_dma_cleanup(dd); +#else + dma_release_channel(dd->dma_lch); +#endif if (dd->irq >= 0) free_irq(dd->irq, dd); kfree(dd); -- cgit v1.2.3 From dd49a69e8eb1423e4d434081a7785bc1b8b8948a Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Fri, 21 Dec 2012 10:04:05 -0700 Subject: crypto: omap-sham - Remove usage of private DMA API Remove usage of the private OMAP DMA API. The dmaengine API will be used instead. CC: Russell King CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-sham.c | 109 --------------------------------------------- 1 file changed, 109 deletions(-) (limited to 'drivers') diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index f54ceb8f5b24..f6b270ed7d62 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -13,8 +13,6 @@ * Some ideas are from old omap-sha1-md5.c driver. */ -#define OMAP_SHAM_DMA_PRIVATE - #define pr_fmt(fmt) "%s: " fmt, __func__ #include @@ -113,9 +111,7 @@ struct omap_sham_reqctx { /* walk state */ struct scatterlist *sg; -#ifndef OMAP_SHAM_DMA_PRIVATE struct scatterlist sgl; -#endif unsigned int offset; /* offset in current sg */ unsigned int total; /* total request */ @@ -149,12 +145,7 @@ struct omap_sham_dev { int irq; spinlock_t lock; int err; -#ifdef OMAP_SHAM_DMA_PRIVATE - int dma; - int dma_lch; -#else struct dma_chan *dma_lch; -#endif struct tasklet_struct done_task; unsigned long flags; @@ -324,7 +315,6 @@ static int omap_sham_xmit_cpu(struct omap_sham_dev *dd, const u8 *buf, return -EINPROGRESS; } -#ifndef OMAP_SHAM_DMA_PRIVATE static void omap_sham_dma_callback(void *param) { struct omap_sham_dev *dd = param; @@ -332,34 +322,18 @@ static void omap_sham_dma_callback(void *param) set_bit(FLAGS_DMA_READY, &dd->flags); tasklet_schedule(&dd->done_task); } -#endif static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, size_t length, int final, int is_sg) { struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req); -#ifdef OMAP_SHAM_DMA_PRIVATE - int len32; -#else struct dma_async_tx_descriptor *tx; struct dma_slave_config cfg; int len32, ret; -#endif dev_dbg(dd->dev, "xmit_dma: digcnt: %d, length: %d, final: %d\n", ctx->digcnt, length, final); -#ifdef OMAP_SHAM_DMA_PRIVATE - len32 = DIV_ROUND_UP(length, sizeof(u32)); - - omap_set_dma_transfer_params(dd->dma_lch, OMAP_DMA_DATA_TYPE_S32, len32, - 1, OMAP_DMA_SYNC_PACKET, dd->dma, - OMAP_DMA_DST_SYNC_PREFETCH); - - omap_set_dma_src_params(dd->dma_lch, 0, OMAP_DMA_AMODE_POST_INC, - dma_addr, 0, 0); - -#else memset(&cfg, 0, sizeof(cfg)); cfg.dst_addr = dd->phys_base + SHA_REG_DIN(0); @@ -401,7 +375,6 @@ static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, tx->callback = omap_sham_dma_callback; tx->callback_param = dd; -#endif omap_sham_write_ctrl(dd, length, final, 1); @@ -412,12 +385,8 @@ static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, set_bit(FLAGS_DMA_ACTIVE, &dd->flags); -#ifdef OMAP_SHAM_DMA_PRIVATE - omap_start_dma(dd->dma_lch); -#else dmaengine_submit(tx); dma_async_issue_pending(dd->dma_lch); -#endif return -EINPROGRESS; } @@ -523,7 +492,6 @@ static int omap_sham_update_dma_start(struct omap_sham_dev *dd) if (ctx->bufcnt || ctx->offset) return omap_sham_update_dma_slow(dd); -#ifndef OMAP_SHAM_DMA_PRIVATE /* * Don't use the sg interface when the transfer size is less * than the number of elements in a DMA frame. Otherwise, @@ -532,7 +500,6 @@ static int omap_sham_update_dma_start(struct omap_sham_dev *dd) */ if (ctx->total < (DST_MAXBURST * sizeof(u32))) return omap_sham_update_dma_slow(dd); -#endif dev_dbg(dd->dev, "fast: digcnt: %d, bufcnt: %u, total: %u\n", ctx->digcnt, ctx->bufcnt, ctx->total); @@ -594,11 +561,7 @@ static int omap_sham_update_dma_stop(struct omap_sham_dev *dd) { struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req); -#ifdef OMAP_SHAM_DMA_PRIVATE - omap_stop_dma(dd->dma_lch); -#else dmaengine_terminate_all(dd->dma_lch); -#endif if (ctx->flags & BIT(FLAGS_SG)) { dma_unmap_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE); @@ -802,18 +765,6 @@ static int omap_sham_handle_queue(struct omap_sham_dev *dd, if (err) goto err1; -#ifdef OMAP_SHAM_DMA_PRIVATE - omap_set_dma_dest_params(dd->dma_lch, 0, - OMAP_DMA_AMODE_CONSTANT, - dd->phys_base + SHA_REG_DIN(0), 0, 16); - - omap_set_dma_dest_burst_mode(dd->dma_lch, - OMAP_DMA_DATA_BURST_16); - - omap_set_dma_src_burst_mode(dd->dma_lch, - OMAP_DMA_DATA_BURST_4); -#endif - if (ctx->digcnt) /* request has changed - restore hash */ omap_sham_copy_hash(req, 0); @@ -1204,55 +1155,13 @@ static irqreturn_t omap_sham_irq(int irq, void *dev_id) return IRQ_HANDLED; } -#ifdef OMAP_SHAM_DMA_PRIVATE -static void omap_sham_dma_callback(int lch, u16 ch_status, void *data) -{ - struct omap_sham_dev *dd = data; - - if (ch_status != OMAP_DMA_BLOCK_IRQ) { - pr_err("omap-sham DMA error status: 0x%hx\n", ch_status); - dd->err = -EIO; - clear_bit(FLAGS_INIT, &dd->flags);/* request to re-initialize */ - } - - set_bit(FLAGS_DMA_READY, &dd->flags); - tasklet_schedule(&dd->done_task); -} - -static int omap_sham_dma_init(struct omap_sham_dev *dd) -{ - int err; - - dd->dma_lch = -1; - - err = omap_request_dma(dd->dma, dev_name(dd->dev), - omap_sham_dma_callback, dd, &dd->dma_lch); - if (err) { - dev_err(dd->dev, "Unable to request DMA channel\n"); - return err; - } - - return 0; -} - -static void omap_sham_dma_cleanup(struct omap_sham_dev *dd) -{ - if (dd->dma_lch >= 0) { - omap_free_dma(dd->dma_lch); - dd->dma_lch = -1; - } -} -#endif - static int __devinit omap_sham_probe(struct platform_device *pdev) { struct omap_sham_dev *dd; struct device *dev = &pdev->dev; struct resource *res; -#ifndef OMAP_SHAM_DMA_PRIVATE dma_cap_mask_t mask; unsigned dma_chan; -#endif int err, i, j; dd = kzalloc(sizeof(struct omap_sham_dev), GFP_KERNEL); @@ -1287,11 +1196,7 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) err = -ENODEV; goto res_err; } -#ifdef OMAP_SHAM_DMA_PRIVATE - dd->dma = res->start; -#else dma_chan = res->start; -#endif /* Get the IRQ */ dd->irq = platform_get_irq(pdev, 0); @@ -1308,11 +1213,6 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) goto res_err; } -#ifdef OMAP_SHAM_DMA_PRIVATE - err = omap_sham_dma_init(dd); - if (err) - goto dma_err; -#else dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); @@ -1323,7 +1223,6 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) err = -ENXIO; goto dma_err; } -#endif dd->io_base = ioremap(dd->phys_base, SZ_4K); if (!dd->io_base) { @@ -1359,11 +1258,7 @@ err_algs: iounmap(dd->io_base); pm_runtime_disable(dev); io_err: -#ifdef OMAP_SHAM_DMA_PRIVATE - omap_sham_dma_cleanup(dd); -#else dma_release_channel(dd->dma_lch); -#endif dma_err: if (dd->irq >= 0) free_irq(dd->irq, dd); @@ -1392,11 +1287,7 @@ static int __devexit omap_sham_remove(struct platform_device *pdev) tasklet_kill(&dd->done_task); iounmap(dd->io_base); pm_runtime_disable(&pdev->dev); -#ifdef OMAP_SHAM_DMA_PRIVATE - omap_sham_dma_cleanup(dd); -#else dma_release_channel(dd->dma_lch); -#endif if (dd->irq >= 0) free_irq(dd->irq, dd); kfree(dd); -- cgit v1.2.3 From 03feec9cc67eaa21e9aa0d3aede0dfed0629f468 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Fri, 21 Dec 2012 10:04:06 -0700 Subject: crypto: omap-sham - Add Device Tree Support Add Device Tree suport to the omap-sham crypto driver. Currently, only support for OMAP2 and OMAP3 is being added but support for OMAP4 will be added in a subsequent patch. CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-sham.c | 139 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 106 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index f6b270ed7d62..860cad866a36 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -30,6 +30,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -145,6 +149,7 @@ struct omap_sham_dev { int irq; spinlock_t lock; int err; + unsigned int dma; struct dma_chan *dma_lch; struct tasklet_struct done_task; @@ -1155,13 +1160,99 @@ static irqreturn_t omap_sham_irq(int irq, void *dev_id) return IRQ_HANDLED; } +#ifdef CONFIG_OF +static const struct of_device_id omap_sham_of_match[] = { + { + .compatible = "ti,omap2-sham", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, omap_sham_of_match); + +static int omap_sham_get_res_of(struct omap_sham_dev *dd, + struct device *dev, struct resource *res) +{ + struct device_node *node = dev->of_node; + const struct of_device_id *match; + int err = 0; + + match = of_match_device(of_match_ptr(omap_sham_of_match), dev); + if (!match) { + dev_err(dev, "no compatible OF match\n"); + err = -EINVAL; + goto err; + } + + err = of_address_to_resource(node, 0, res); + if (err < 0) { + dev_err(dev, "can't translate OF node address\n"); + err = -EINVAL; + goto err; + } + + dd->irq = of_irq_to_resource(node, 0, NULL); + if (!dd->irq) { + dev_err(dev, "can't translate OF irq value\n"); + err = -EINVAL; + goto err; + } + + dd->dma = -1; /* Dummy value that's unused */ + +err: + return err; +} +#else +static int omap_sham_get_res_dev(struct omap_sham_dev *dd, + struct device *dev, struct resource *res) +{ + return -EINVAL; +} +#endif + +static int omap_sham_get_res_pdev(struct omap_sham_dev *dd, + struct platform_device *pdev, struct resource *res) +{ + struct device *dev = &pdev->dev; + struct resource *r; + int err = 0; + + /* Get the base address */ + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(dev, "no MEM resource info\n"); + err = -ENODEV; + goto err; + } + memcpy(res, r, sizeof(*res)); + + /* Get the IRQ */ + dd->irq = platform_get_irq(pdev, 0); + if (dd->irq < 0) { + dev_err(dev, "no IRQ resource info\n"); + err = dd->irq; + goto err; + } + + /* Get the DMA */ + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!r) { + dev_err(dev, "no DMA resource info\n"); + err = -ENODEV; + goto err; + } + dd->dma = r->start; + +err: + return err; +} + static int __devinit omap_sham_probe(struct platform_device *pdev) { struct omap_sham_dev *dd; struct device *dev = &pdev->dev; - struct resource *res; + struct resource res; dma_cap_mask_t mask; - unsigned dma_chan; int err, i, j; dd = kzalloc(sizeof(struct omap_sham_dev), GFP_KERNEL); @@ -1178,33 +1269,18 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) tasklet_init(&dd->done_task, omap_sham_done_task, (unsigned long)dd); crypto_init_queue(&dd->queue, OMAP_SHAM_QUEUE_LENGTH); - dd->irq = -1; - - /* Get the base address */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "no MEM resource info\n"); - err = -ENODEV; - goto res_err; - } - dd->phys_base = res->start; - - /* Get the DMA */ - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!res) { - dev_err(dev, "no DMA resource info\n"); - err = -ENODEV; + err = (dev->of_node) ? omap_sham_get_res_of(dd, dev, &res) : + omap_sham_get_res_pdev(dd, pdev, &res); + if (err) goto res_err; - } - dma_chan = res->start; - /* Get the IRQ */ - dd->irq = platform_get_irq(pdev, 0); - if (dd->irq < 0) { - dev_err(dev, "no IRQ resource info\n"); - err = dd->irq; + dd->io_base = devm_request_and_ioremap(dev, &res); + if (!dd->io_base) { + dev_err(dev, "can't ioremap\n"); + err = -ENOMEM; goto res_err; } + dd->phys_base = res.start; err = request_irq(dd->irq, omap_sham_irq, IRQF_TRIGGER_LOW, dev_name(dev), dd); @@ -1216,10 +1292,10 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - dd->dma_lch = dma_request_channel(mask, omap_dma_filter_fn, &dma_chan); + dd->dma_lch = dma_request_channel(mask, omap_dma_filter_fn, &dd->dma); if (!dd->dma_lch) { dev_err(dev, "unable to obtain RX DMA engine channel %u\n", - dma_chan); + dd->dma); err = -ENXIO; goto dma_err; } @@ -1255,13 +1331,11 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) err_algs: for (j = 0; j < i; j++) crypto_unregister_ahash(&algs[j]); - iounmap(dd->io_base); pm_runtime_disable(dev); io_err: dma_release_channel(dd->dma_lch); dma_err: - if (dd->irq >= 0) - free_irq(dd->irq, dd); + free_irq(dd->irq, dd); res_err: kfree(dd); dd = NULL; @@ -1285,11 +1359,9 @@ static int __devexit omap_sham_remove(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(algs); i++) crypto_unregister_ahash(&algs[i]); tasklet_kill(&dd->done_task); - iounmap(dd->io_base); pm_runtime_disable(&pdev->dev); dma_release_channel(dd->dma_lch); - if (dd->irq >= 0) - free_irq(dd->irq, dd); + free_irq(dd->irq, dd); kfree(dd); dd = NULL; @@ -1321,6 +1393,7 @@ static struct platform_driver omap_sham_driver = { .name = "omap-sham", .owner = THIS_MODULE, .pm = &omap_sham_pm_ops, + .of_match_table = omap_sham_of_match, }, }; -- cgit v1.2.3 From 0e87e73f4abe1ada69cf780fe2550c6361a1b53b Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Fri, 21 Dec 2012 10:04:07 -0700 Subject: crypto: omap-sham - Convert to dma_request_slave_channel_compat() Use the dma_request_slave_channel_compat() call instead of the dma_request_channel() call to request a DMA channel. This allows the omap-sham driver use different DMA engines. CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-sham.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index 860cad866a36..8074bd9947d1 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -1292,7 +1292,8 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - dd->dma_lch = dma_request_channel(mask, omap_dma_filter_fn, &dd->dma); + dd->dma_lch = dma_request_slave_channel_compat(mask, omap_dma_filter_fn, + &dd->dma, dev, "rx"); if (!dd->dma_lch) { dev_err(dev, "unable to obtain RX DMA engine channel %u\n", dd->dma); -- cgit v1.2.3 From 0d373d603202b8bfecc87b9b3602e6ffbf9e4feb Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Fri, 21 Dec 2012 10:04:08 -0700 Subject: crypto: omap-sham - Add OMAP4/AM33XX SHAM Support Add support for the OMAP4 version of the SHAM module that is present on OMAP4 and AM33xx SoCs. The modules have several differences including register offsets, hardware XORing, and how DMA is triggered. To handle these differences, a platform_data structure is defined and contains routine pointers, register offsets, bit shifts within registers, and flags to indicate whether the hardware supports XORing and provides SHA1 results in big or little endian. OMAP2/OMAP3-specific routines are suffixed with '_omap2' and OMAP4/AM33xx routines are suffixed with '_omap4'. Note: The code being integrated is from the TI AM33xx SDK and was written by Greg Turner and Herman Schuurman (current email unknown) while at TI. CC: Greg Turner CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-sham.c | 416 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 332 insertions(+), 84 deletions(-) (limited to 'drivers') diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index 8074bd9947d1..fab0af488b83 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -5,6 +5,7 @@ * * Copyright (c) 2010 Nokia Corporation * Author: Dmitry Kasatkin + * Copyright (c) 2011 Texas Instruments Incorporated * * 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 @@ -43,16 +44,17 @@ #include #include -#define SHA_REG_DIGEST(x) (0x00 + ((x) * 0x04)) -#define SHA_REG_DIN(x) (0x1C + ((x) * 0x04)) - #define SHA1_MD5_BLOCK_SIZE SHA1_BLOCK_SIZE #define MD5_DIGEST_SIZE 16 #define DST_MAXBURST 16 #define DMA_MIN (DST_MAXBURST * sizeof(u32)) -#define SHA_REG_DIGCNT 0x14 +#define SHA_REG_IDIGEST(dd, x) ((dd)->pdata->idigest_ofs + ((x)*0x04)) +#define SHA_REG_DIN(dd, x) ((dd)->pdata->din_ofs + ((x) * 0x04)) +#define SHA_REG_DIGCNT(dd) ((dd)->pdata->digcnt_ofs) + +#define SHA_REG_ODIGEST(x) (0x00 + ((x) * 0x04)) #define SHA_REG_CTRL 0x18 #define SHA_REG_CTRL_LENGTH (0xFFFFFFFF << 5) @@ -62,19 +64,40 @@ #define SHA_REG_CTRL_INPUT_READY (1 << 1) #define SHA_REG_CTRL_OUTPUT_READY (1 << 0) -#define SHA_REG_REV 0x5C -#define SHA_REG_REV_MAJOR 0xF0 -#define SHA_REG_REV_MINOR 0x0F +#define SHA_REG_REV(dd) ((dd)->pdata->rev_ofs) -#define SHA_REG_MASK 0x60 +#define SHA_REG_MASK(dd) ((dd)->pdata->mask_ofs) #define SHA_REG_MASK_DMA_EN (1 << 3) #define SHA_REG_MASK_IT_EN (1 << 2) #define SHA_REG_MASK_SOFTRESET (1 << 1) #define SHA_REG_AUTOIDLE (1 << 0) -#define SHA_REG_SYSSTATUS 0x64 +#define SHA_REG_SYSSTATUS(dd) ((dd)->pdata->sysstatus_ofs) #define SHA_REG_SYSSTATUS_RESETDONE (1 << 0) +#define SHA_REG_MODE 0x44 +#define SHA_REG_MODE_HMAC_OUTER_HASH (1 << 7) +#define SHA_REG_MODE_HMAC_KEY_PROC (1 << 5) +#define SHA_REG_MODE_CLOSE_HASH (1 << 4) +#define SHA_REG_MODE_ALGO_CONSTANT (1 << 3) +#define SHA_REG_MODE_ALGO_MASK (3 << 1) +#define SHA_REG_MODE_ALGO_MD5_128 (0 << 1) +#define SHA_REG_MODE_ALGO_SHA1_160 (1 << 1) + +#define SHA_REG_LENGTH 0x48 + +#define SHA_REG_IRQSTATUS 0x118 +#define SHA_REG_IRQSTATUS_CTX_RDY (1 << 3) +#define SHA_REG_IRQSTATUS_PARTHASH_RDY (1 << 2) +#define SHA_REG_IRQSTATUS_INPUT_RDY (1 << 1) +#define SHA_REG_IRQSTATUS_OUTPUT_RDY (1 << 0) + +#define SHA_REG_IRQENA 0x11C +#define SHA_REG_IRQENA_CTX_RDY (1 << 3) +#define SHA_REG_IRQENA_PARTHASH_RDY (1 << 2) +#define SHA_REG_IRQENA_INPUT_RDY (1 << 1) +#define SHA_REG_IRQENA_OUTPUT_RDY (1 << 0) + #define DEFAULT_TIMEOUT_INTERVAL HZ /* mostly device flags */ @@ -85,20 +108,29 @@ #define FLAGS_INIT 4 #define FLAGS_CPU 5 #define FLAGS_DMA_READY 6 +#define FLAGS_AUTO_XOR 7 +#define FLAGS_BE32_SHA1 8 /* context flags */ #define FLAGS_FINUP 16 #define FLAGS_SG 17 -#define FLAGS_SHA1 18 -#define FLAGS_HMAC 19 -#define FLAGS_ERROR 20 -#define OP_UPDATE 1 -#define OP_FINAL 2 +#define FLAGS_MODE_SHIFT 18 +#define FLAGS_MODE_MASK (SHA_REG_MODE_ALGO_MASK \ + << (FLAGS_MODE_SHIFT - 1)) +#define FLAGS_MODE_MD5 (SHA_REG_MODE_ALGO_MD5_128 \ + << (FLAGS_MODE_SHIFT - 1)) +#define FLAGS_MODE_SHA1 (SHA_REG_MODE_ALGO_SHA1_160 \ + << (FLAGS_MODE_SHIFT - 1)) +#define FLAGS_HMAC 20 +#define FLAGS_ERROR 21 + +#define OP_UPDATE 1 +#define OP_FINAL 2 #define OMAP_ALIGN_MASK (sizeof(u32)-1) #define OMAP_ALIGNED __attribute__((aligned(sizeof(u32)))) -#define BUFLEN PAGE_SIZE +#define BUFLEN PAGE_SIZE struct omap_sham_dev; @@ -107,7 +139,7 @@ struct omap_sham_reqctx { unsigned long flags; unsigned long op; - u8 digest[SHA1_DIGEST_SIZE] OMAP_ALIGNED; + u8 digest[SHA256_DIGEST_SIZE] OMAP_ALIGNED; size_t digcnt; size_t bufcnt; size_t buflen; @@ -124,8 +156,8 @@ struct omap_sham_reqctx { struct omap_sham_hmac_ctx { struct crypto_shash *shash; - u8 ipad[SHA1_MD5_BLOCK_SIZE]; - u8 opad[SHA1_MD5_BLOCK_SIZE]; + u8 ipad[SHA1_MD5_BLOCK_SIZE] OMAP_ALIGNED; + u8 opad[SHA1_MD5_BLOCK_SIZE] OMAP_ALIGNED; }; struct omap_sham_ctx { @@ -141,6 +173,31 @@ struct omap_sham_ctx { #define OMAP_SHAM_QUEUE_LENGTH 1 +struct omap_sham_pdata { + unsigned long flags; + int digest_size; + + void (*copy_hash)(struct ahash_request *req, int out); + void (*write_ctrl)(struct omap_sham_dev *dd, size_t length, + int final, int dma); + void (*trigger)(struct omap_sham_dev *dd, size_t length); + int (*poll_irq)(struct omap_sham_dev *dd); + irqreturn_t (*intr_hdlr)(int irq, void *dev_id); + + u32 odigest_ofs; + u32 idigest_ofs; + u32 din_ofs; + u32 digcnt_ofs; + u32 rev_ofs; + u32 mask_ofs; + u32 sysstatus_ofs; + + u32 major_mask; + u32 major_shift; + u32 minor_mask; + u32 minor_shift; +}; + struct omap_sham_dev { struct list_head list; unsigned long phys_base; @@ -156,6 +213,8 @@ struct omap_sham_dev { unsigned long flags; struct crypto_queue queue; struct ahash_request *req; + + const struct omap_sham_pdata *pdata; }; struct omap_sham_drv { @@ -203,21 +262,44 @@ static inline int omap_sham_wait(struct omap_sham_dev *dd, u32 offset, u32 bit) return 0; } -static void omap_sham_copy_hash(struct ahash_request *req, int out) +static void omap_sham_copy_hash_omap2(struct ahash_request *req, int out) { struct omap_sham_reqctx *ctx = ahash_request_ctx(req); + struct omap_sham_dev *dd = ctx->dd; u32 *hash = (u32 *)ctx->digest; int i; - /* MD5 is almost unused. So copy sha1 size to reduce code */ - for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(u32); i++) { + for (i = 0; i < dd->pdata->digest_size / sizeof(u32); i++) { if (out) - hash[i] = omap_sham_read(ctx->dd, - SHA_REG_DIGEST(i)); + hash[i] = omap_sham_read(dd, SHA_REG_IDIGEST(dd, i)); else - omap_sham_write(ctx->dd, - SHA_REG_DIGEST(i), hash[i]); + omap_sham_write(dd, SHA_REG_IDIGEST(dd, i), hash[i]); + } +} + +static void omap_sham_copy_hash_omap4(struct ahash_request *req, int out) +{ + struct omap_sham_reqctx *ctx = ahash_request_ctx(req); + struct omap_sham_dev *dd = ctx->dd; + int i; + + if (ctx->flags & BIT(FLAGS_HMAC)) { + struct crypto_ahash *tfm = crypto_ahash_reqtfm(dd->req); + struct omap_sham_ctx *tctx = crypto_ahash_ctx(tfm); + struct omap_sham_hmac_ctx *bctx = tctx->base; + u32 *opad = (u32 *)bctx->opad; + + for (i = 0; i < dd->pdata->digest_size / sizeof(u32); i++) { + if (out) + opad[i] = omap_sham_read(dd, + SHA_REG_ODIGEST(i)); + else + omap_sham_write(dd, SHA_REG_ODIGEST(i), + opad[i]); + } } + + omap_sham_copy_hash_omap2(req, out); } static void omap_sham_copy_ready_hash(struct ahash_request *req) @@ -225,20 +307,31 @@ static void omap_sham_copy_ready_hash(struct ahash_request *req) struct omap_sham_reqctx *ctx = ahash_request_ctx(req); u32 *in = (u32 *)ctx->digest; u32 *hash = (u32 *)req->result; - int i; + int i, d, big_endian = 0; if (!hash) return; - if (likely(ctx->flags & BIT(FLAGS_SHA1))) { - /* SHA1 results are in big endian */ - for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(u32); i++) + switch (ctx->flags & FLAGS_MODE_MASK) { + case FLAGS_MODE_MD5: + d = MD5_DIGEST_SIZE / sizeof(u32); + break; + case FLAGS_MODE_SHA1: + /* OMAP2 SHA1 is big endian */ + if (test_bit(FLAGS_BE32_SHA1, &ctx->dd->flags)) + big_endian = 1; + d = SHA1_DIGEST_SIZE / sizeof(u32); + break; + default: + d = 0; + } + + if (big_endian) + for (i = 0; i < d; i++) hash[i] = be32_to_cpu(in[i]); - } else { - /* MD5 results are in little endian */ - for (i = 0; i < MD5_DIGEST_SIZE / sizeof(u32); i++) + else + for (i = 0; i < d; i++) hash[i] = le32_to_cpu(in[i]); - } } static int omap_sham_hw_init(struct omap_sham_dev *dd) @@ -246,13 +339,6 @@ static int omap_sham_hw_init(struct omap_sham_dev *dd) pm_runtime_get_sync(dd->dev); if (!test_bit(FLAGS_INIT, &dd->flags)) { - omap_sham_write_mask(dd, SHA_REG_MASK, - SHA_REG_MASK_SOFTRESET, SHA_REG_MASK_SOFTRESET); - - if (omap_sham_wait(dd, SHA_REG_SYSSTATUS, - SHA_REG_SYSSTATUS_RESETDONE)) - return -ETIMEDOUT; - set_bit(FLAGS_INIT, &dd->flags); dd->err = 0; } @@ -260,23 +346,23 @@ static int omap_sham_hw_init(struct omap_sham_dev *dd) return 0; } -static void omap_sham_write_ctrl(struct omap_sham_dev *dd, size_t length, +static void omap_sham_write_ctrl_omap2(struct omap_sham_dev *dd, size_t length, int final, int dma) { struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req); u32 val = length << 5, mask; if (likely(ctx->digcnt)) - omap_sham_write(dd, SHA_REG_DIGCNT, ctx->digcnt); + omap_sham_write(dd, SHA_REG_DIGCNT(dd), ctx->digcnt); - omap_sham_write_mask(dd, SHA_REG_MASK, + omap_sham_write_mask(dd, SHA_REG_MASK(dd), SHA_REG_MASK_IT_EN | (dma ? SHA_REG_MASK_DMA_EN : 0), SHA_REG_MASK_IT_EN | SHA_REG_MASK_DMA_EN); /* * Setting ALGO_CONST only for the first iteration * and CLOSE_HASH only for the last one. */ - if (ctx->flags & BIT(FLAGS_SHA1)) + if ((ctx->flags & FLAGS_MODE_MASK) == FLAGS_MODE_SHA1) val |= SHA_REG_CTRL_ALGO; if (!ctx->digcnt) val |= SHA_REG_CTRL_ALGO_CONST; @@ -289,6 +375,81 @@ static void omap_sham_write_ctrl(struct omap_sham_dev *dd, size_t length, omap_sham_write_mask(dd, SHA_REG_CTRL, val, mask); } +static void omap_sham_trigger_omap2(struct omap_sham_dev *dd, size_t length) +{ +} + +static int omap_sham_poll_irq_omap2(struct omap_sham_dev *dd) +{ + return omap_sham_wait(dd, SHA_REG_CTRL, SHA_REG_CTRL_INPUT_READY); +} + +static void omap_sham_write_n(struct omap_sham_dev *dd, u32 offset, + u32 *value, int count) +{ + for (; count--; value++, offset += 4) + omap_sham_write(dd, offset, *value); +} + +static void omap_sham_write_ctrl_omap4(struct omap_sham_dev *dd, size_t length, + int final, int dma) +{ + struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req); + u32 val, mask; + + /* + * Setting ALGO_CONST only for the first iteration and + * CLOSE_HASH only for the last one. Note that flags mode bits + * correspond to algorithm encoding in mode register. + */ + val = (ctx->flags & FLAGS_MODE_MASK) >> (FLAGS_MODE_SHIFT - 1); + if (!ctx->digcnt) { + struct crypto_ahash *tfm = crypto_ahash_reqtfm(dd->req); + struct omap_sham_ctx *tctx = crypto_ahash_ctx(tfm); + struct omap_sham_hmac_ctx *bctx = tctx->base; + + val |= SHA_REG_MODE_ALGO_CONSTANT; + + if (ctx->flags & BIT(FLAGS_HMAC)) { + val |= SHA_REG_MODE_HMAC_KEY_PROC; + omap_sham_write_n(dd, SHA_REG_ODIGEST(0), + (u32 *)bctx->ipad, + SHA1_BLOCK_SIZE / sizeof(u32)); + ctx->digcnt += SHA1_BLOCK_SIZE; + } + } + + if (final) { + val |= SHA_REG_MODE_CLOSE_HASH; + + if (ctx->flags & BIT(FLAGS_HMAC)) + val |= SHA_REG_MODE_HMAC_OUTER_HASH; + } + + mask = SHA_REG_MODE_ALGO_CONSTANT | SHA_REG_MODE_CLOSE_HASH | + SHA_REG_MODE_ALGO_MASK | SHA_REG_MODE_HMAC_OUTER_HASH | + SHA_REG_MODE_HMAC_KEY_PROC; + + dev_dbg(dd->dev, "ctrl: %08x, flags: %08lx\n", val, ctx->flags); + omap_sham_write_mask(dd, SHA_REG_MODE, val, mask); + omap_sham_write(dd, SHA_REG_IRQENA, SHA_REG_IRQENA_OUTPUT_RDY); + omap_sham_write_mask(dd, SHA_REG_MASK(dd), + SHA_REG_MASK_IT_EN | + (dma ? SHA_REG_MASK_DMA_EN : 0), + SHA_REG_MASK_IT_EN | SHA_REG_MASK_DMA_EN); +} + +static void omap_sham_trigger_omap4(struct omap_sham_dev *dd, size_t length) +{ + omap_sham_write(dd, SHA_REG_LENGTH, length); +} + +static int omap_sham_poll_irq_omap4(struct omap_sham_dev *dd) +{ + return omap_sham_wait(dd, SHA_REG_IRQSTATUS, + SHA_REG_IRQSTATUS_INPUT_RDY); +} + static int omap_sham_xmit_cpu(struct omap_sham_dev *dd, const u8 *buf, size_t length, int final) { @@ -299,12 +460,13 @@ static int omap_sham_xmit_cpu(struct omap_sham_dev *dd, const u8 *buf, dev_dbg(dd->dev, "xmit_cpu: digcnt: %d, length: %d, final: %d\n", ctx->digcnt, length, final); - omap_sham_write_ctrl(dd, length, final, 0); + dd->pdata->write_ctrl(dd, length, final, 0); + dd->pdata->trigger(dd, length); /* should be non-zero before next lines to disable clocks later */ ctx->digcnt += length; - if (omap_sham_wait(dd, SHA_REG_CTRL, SHA_REG_CTRL_INPUT_READY)) + if (dd->pdata->poll_irq(dd)) return -ETIMEDOUT; if (final) @@ -315,7 +477,7 @@ static int omap_sham_xmit_cpu(struct omap_sham_dev *dd, const u8 *buf, len32 = DIV_ROUND_UP(length, sizeof(u32)); for (count = 0; count < len32; count++) - omap_sham_write(dd, SHA_REG_DIN(count), buffer[count]); + omap_sham_write(dd, SHA_REG_DIN(dd, count), buffer[count]); return -EINPROGRESS; } @@ -341,7 +503,7 @@ static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, memset(&cfg, 0, sizeof(cfg)); - cfg.dst_addr = dd->phys_base + SHA_REG_DIN(0); + cfg.dst_addr = dd->phys_base + SHA_REG_DIN(dd, 0); cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; cfg.dst_maxburst = DST_MAXBURST; @@ -381,7 +543,7 @@ static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, tx->callback = omap_sham_dma_callback; tx->callback_param = dd; - omap_sham_write_ctrl(dd, length, final, 1); + dd->pdata->write_ctrl(dd, length, final, 1); ctx->digcnt += length; @@ -393,6 +555,8 @@ static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, dmaengine_submit(tx); dma_async_issue_pending(dd->dma_lch); + dd->pdata->trigger(dd, length); + return -EINPROGRESS; } @@ -450,7 +614,7 @@ static int omap_sham_xmit_dma_map(struct omap_sham_dev *dd, ctx->flags &= ~BIT(FLAGS_SG); ret = omap_sham_xmit_dma(dd, ctx->dma_addr, length, final, 0); - if (ret) + if (ret != -EINPROGRESS) dma_unmap_single(dd->dev, ctx->dma_addr, ctx->buflen, DMA_TO_DEVICE); @@ -544,7 +708,7 @@ static int omap_sham_update_dma_start(struct omap_sham_dev *dd) final = (ctx->flags & BIT(FLAGS_FINUP)) && !ctx->total; ret = omap_sham_xmit_dma(dd, sg_dma_address(ctx->sg), length, final, 1); - if (ret) + if (ret != -EINPROGRESS) dma_unmap_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE); return ret; @@ -609,18 +773,27 @@ static int omap_sham_init(struct ahash_request *req) dev_dbg(dd->dev, "init: digest size: %d\n", crypto_ahash_digestsize(tfm)); - if (crypto_ahash_digestsize(tfm) == SHA1_DIGEST_SIZE) - ctx->flags |= BIT(FLAGS_SHA1); + switch (crypto_ahash_digestsize(tfm)) { + case MD5_DIGEST_SIZE: + ctx->flags |= FLAGS_MODE_MD5; + break; + case SHA1_DIGEST_SIZE: + ctx->flags |= FLAGS_MODE_SHA1; + break; + } ctx->bufcnt = 0; ctx->digcnt = 0; ctx->buflen = BUFLEN; if (tctx->flags & BIT(FLAGS_HMAC)) { - struct omap_sham_hmac_ctx *bctx = tctx->base; + if (!test_bit(FLAGS_AUTO_XOR, &dd->flags)) { + struct omap_sham_hmac_ctx *bctx = tctx->base; + + memcpy(ctx->buffer, bctx->ipad, SHA1_MD5_BLOCK_SIZE); + ctx->bufcnt = SHA1_MD5_BLOCK_SIZE; + } - memcpy(ctx->buffer, bctx->ipad, SHA1_MD5_BLOCK_SIZE); - ctx->bufcnt = SHA1_MD5_BLOCK_SIZE; ctx->flags |= BIT(FLAGS_HMAC); } @@ -697,7 +870,8 @@ static int omap_sham_finish(struct ahash_request *req) if (ctx->digcnt) { omap_sham_copy_ready_hash(req); - if (ctx->flags & BIT(FLAGS_HMAC)) + if ((ctx->flags & BIT(FLAGS_HMAC)) && + !test_bit(FLAGS_AUTO_XOR, &dd->flags)) err = omap_sham_finish_hmac(req); } @@ -712,7 +886,7 @@ static void omap_sham_finish_req(struct ahash_request *req, int err) struct omap_sham_dev *dd = ctx->dd; if (!err) { - omap_sham_copy_hash(req, 1); + dd->pdata->copy_hash(req, 1); if (test_bit(FLAGS_FINAL, &dd->flags)) err = omap_sham_finish(req); } else { @@ -772,7 +946,7 @@ static int omap_sham_handle_queue(struct omap_sham_dev *dd, if (ctx->digcnt) /* request has changed - restore hash */ - omap_sham_copy_hash(req, 0); + dd->pdata->copy_hash(req, 0); if (ctx->op == OP_UPDATE) { err = omap_sham_update_req(dd); @@ -911,7 +1085,21 @@ static int omap_sham_setkey(struct crypto_ahash *tfm, const u8 *key, struct omap_sham_hmac_ctx *bctx = tctx->base; int bs = crypto_shash_blocksize(bctx->shash); int ds = crypto_shash_digestsize(bctx->shash); + struct omap_sham_dev *dd = NULL, *tmp; int err, i; + + spin_lock_bh(&sham.lock); + if (!tctx->dd) { + list_for_each_entry(tmp, &sham.dev_list, list) { + dd = tmp; + break; + } + tctx->dd = dd; + } else { + dd = tctx->dd; + } + spin_unlock_bh(&sham.lock); + err = crypto_shash_setkey(tctx->fallback, key, keylen); if (err) return err; @@ -928,11 +1116,14 @@ static int omap_sham_setkey(struct crypto_ahash *tfm, const u8 *key, } memset(bctx->ipad + keylen, 0, bs - keylen); - memcpy(bctx->opad, bctx->ipad, bs); - for (i = 0; i < bs; i++) { - bctx->ipad[i] ^= 0x36; - bctx->opad[i] ^= 0x5c; + if (!test_bit(FLAGS_AUTO_XOR, &dd->flags)) { + memcpy(bctx->opad, bctx->ipad, bs); + + for (i = 0; i < bs; i++) { + bctx->ipad[i] ^= 0x36; + bctx->opad[i] ^= 0x5c; + } } return err; @@ -1137,7 +1328,19 @@ finish: omap_sham_finish_req(dd->req, err); } -static irqreturn_t omap_sham_irq(int irq, void *dev_id) +static irqreturn_t omap_sham_irq_common(struct omap_sham_dev *dd) +{ + if (!test_bit(FLAGS_BUSY, &dd->flags)) { + dev_warn(dd->dev, "Interrupt when no active requests.\n"); + } else { + set_bit(FLAGS_OUTPUT_READY, &dd->flags); + tasklet_schedule(&dd->done_task); + } + + return IRQ_HANDLED; +} + +static irqreturn_t omap_sham_irq_omap2(int irq, void *dev_id) { struct omap_sham_dev *dd = dev_id; @@ -1149,21 +1352,67 @@ static irqreturn_t omap_sham_irq(int irq, void *dev_id) SHA_REG_CTRL_OUTPUT_READY); omap_sham_read(dd, SHA_REG_CTRL); - if (!test_bit(FLAGS_BUSY, &dd->flags)) { - dev_warn(dd->dev, "Interrupt when no active requests.\n"); - return IRQ_HANDLED; - } + return omap_sham_irq_common(dd); +} - set_bit(FLAGS_OUTPUT_READY, &dd->flags); - tasklet_schedule(&dd->done_task); +static irqreturn_t omap_sham_irq_omap4(int irq, void *dev_id) +{ + struct omap_sham_dev *dd = dev_id; - return IRQ_HANDLED; + omap_sham_write_mask(dd, SHA_REG_MASK(dd), 0, SHA_REG_MASK_IT_EN); + + return omap_sham_irq_common(dd); } +static const struct omap_sham_pdata omap_sham_pdata_omap2 = { + .flags = BIT(FLAGS_BE32_SHA1), + .digest_size = SHA1_DIGEST_SIZE, + .copy_hash = omap_sham_copy_hash_omap2, + .write_ctrl = omap_sham_write_ctrl_omap2, + .trigger = omap_sham_trigger_omap2, + .poll_irq = omap_sham_poll_irq_omap2, + .intr_hdlr = omap_sham_irq_omap2, + .idigest_ofs = 0x00, + .din_ofs = 0x1c, + .digcnt_ofs = 0x14, + .rev_ofs = 0x5c, + .mask_ofs = 0x60, + .sysstatus_ofs = 0x64, + .major_mask = 0xf0, + .major_shift = 4, + .minor_mask = 0x0f, + .minor_shift = 0, +}; + #ifdef CONFIG_OF +static const struct omap_sham_pdata omap_sham_pdata_omap4 = { + .flags = BIT(FLAGS_AUTO_XOR), + .digest_size = SHA256_DIGEST_SIZE, + .copy_hash = omap_sham_copy_hash_omap4, + .write_ctrl = omap_sham_write_ctrl_omap4, + .trigger = omap_sham_trigger_omap4, + .poll_irq = omap_sham_poll_irq_omap4, + .intr_hdlr = omap_sham_irq_omap4, + .idigest_ofs = 0x020, + .din_ofs = 0x080, + .digcnt_ofs = 0x040, + .rev_ofs = 0x100, + .mask_ofs = 0x110, + .sysstatus_ofs = 0x114, + .major_mask = 0x0700, + .major_shift = 8, + .minor_mask = 0x003f, + .minor_shift = 0, +}; + static const struct of_device_id omap_sham_of_match[] = { { .compatible = "ti,omap2-sham", + .data = &omap_sham_pdata_omap2, + }, + { + .compatible = "ti,omap4-sham", + .data = &omap_sham_pdata_omap4, }, {}, }; @@ -1198,6 +1447,7 @@ static int omap_sham_get_res_of(struct omap_sham_dev *dd, } dd->dma = -1; /* Dummy value that's unused */ + dd->pdata = match->data; err: return err; @@ -1243,6 +1493,9 @@ static int omap_sham_get_res_pdev(struct omap_sham_dev *dd, } dd->dma = r->start; + /* Only OMAP2/3 can be non-DT */ + dd->pdata = &omap_sham_pdata_omap2; + err: return err; } @@ -1254,6 +1507,7 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) struct resource res; dma_cap_mask_t mask; int err, i, j; + u32 rev; dd = kzalloc(sizeof(struct omap_sham_dev), GFP_KERNEL); if (dd == NULL) { @@ -1282,8 +1536,8 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) } dd->phys_base = res.start; - err = request_irq(dd->irq, omap_sham_irq, - IRQF_TRIGGER_LOW, dev_name(dev), dd); + err = request_irq(dd->irq, dd->pdata->intr_hdlr, IRQF_TRIGGER_LOW, + dev_name(dev), dd); if (err) { dev_err(dev, "unable to request irq.\n"); goto res_err; @@ -1301,21 +1555,16 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) goto dma_err; } - dd->io_base = ioremap(dd->phys_base, SZ_4K); - if (!dd->io_base) { - dev_err(dev, "can't ioremap\n"); - err = -ENOMEM; - goto io_err; - } + dd->flags |= dd->pdata->flags; pm_runtime_enable(dev); pm_runtime_get_sync(dev); + rev = omap_sham_read(dd, SHA_REG_REV(dd)); + pm_runtime_put_sync(&pdev->dev); dev_info(dev, "hw accel on OMAP rev %u.%u\n", - (omap_sham_read(dd, SHA_REG_REV) & SHA_REG_REV_MAJOR) >> 4, - omap_sham_read(dd, SHA_REG_REV) & SHA_REG_REV_MINOR); - - pm_runtime_put_sync(&pdev->dev); + (rev & dd->pdata->major_mask) >> dd->pdata->major_shift, + (rev & dd->pdata->minor_mask) >> dd->pdata->minor_shift); spin_lock(&sham.lock); list_add_tail(&dd->list, &sham.dev_list); @@ -1333,7 +1582,6 @@ err_algs: for (j = 0; j < i; j++) crypto_unregister_ahash(&algs[j]); pm_runtime_disable(dev); -io_err: dma_release_channel(dd->dma_lch); dma_err: free_irq(dd->irq, dd); -- cgit v1.2.3 From d20fb18be246d196225ed151c126832b2dab6506 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Fri, 21 Dec 2012 10:04:09 -0700 Subject: crypto: omap-sham - Add SHA224 and SHA256 Support The OMAP4/AM33xx version of the SHAM crypto module supports SHA224 and SHA256 in addition to MD5 and SHA1 that the OMAP2 version of the module supports. To add this support, use the platform_data introduced in an ealier commit to hold the list of algorithms supported by the current module. The probe routine will use that list to register the correct algorithms. Note: The code being integrated is from the TI AM33xx SDK and was written by Greg Turner and Herman Schuurman (current email unknown) while at TI. CC: Greg Turner CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-sham.c | 183 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 173 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index fab0af488b83..edff981edfb1 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -83,6 +83,8 @@ #define SHA_REG_MODE_ALGO_MASK (3 << 1) #define SHA_REG_MODE_ALGO_MD5_128 (0 << 1) #define SHA_REG_MODE_ALGO_SHA1_160 (1 << 1) +#define SHA_REG_MODE_ALGO_SHA2_224 (2 << 1) +#define SHA_REG_MODE_ALGO_SHA2_256 (3 << 1) #define SHA_REG_LENGTH 0x48 @@ -121,6 +123,10 @@ << (FLAGS_MODE_SHIFT - 1)) #define FLAGS_MODE_SHA1 (SHA_REG_MODE_ALGO_SHA1_160 \ << (FLAGS_MODE_SHIFT - 1)) +#define FLAGS_MODE_SHA224 (SHA_REG_MODE_ALGO_SHA2_224 \ + << (FLAGS_MODE_SHIFT - 1)) +#define FLAGS_MODE_SHA256 (SHA_REG_MODE_ALGO_SHA2_256 \ + << (FLAGS_MODE_SHIFT - 1)) #define FLAGS_HMAC 20 #define FLAGS_ERROR 21 @@ -173,7 +179,15 @@ struct omap_sham_ctx { #define OMAP_SHAM_QUEUE_LENGTH 1 +struct omap_sham_algs_info { + struct ahash_alg *algs_list; + unsigned int size; + unsigned int registered; +}; + struct omap_sham_pdata { + struct omap_sham_algs_info *algs_info; + unsigned int algs_info_size; unsigned long flags; int digest_size; @@ -322,6 +336,12 @@ static void omap_sham_copy_ready_hash(struct ahash_request *req) big_endian = 1; d = SHA1_DIGEST_SIZE / sizeof(u32); break; + case FLAGS_MODE_SHA224: + d = SHA224_DIGEST_SIZE / sizeof(u32); + break; + case FLAGS_MODE_SHA256: + d = SHA256_DIGEST_SIZE / sizeof(u32); + break; default: d = 0; } @@ -780,6 +800,12 @@ static int omap_sham_init(struct ahash_request *req) case SHA1_DIGEST_SIZE: ctx->flags |= FLAGS_MODE_SHA1; break; + case SHA224_DIGEST_SIZE: + ctx->flags |= FLAGS_MODE_SHA224; + break; + case SHA256_DIGEST_SIZE: + ctx->flags |= FLAGS_MODE_SHA256; + break; } ctx->bufcnt = 0; @@ -1173,6 +1199,16 @@ static int omap_sham_cra_sha1_init(struct crypto_tfm *tfm) return omap_sham_cra_init_alg(tfm, "sha1"); } +static int omap_sham_cra_sha224_init(struct crypto_tfm *tfm) +{ + return omap_sham_cra_init_alg(tfm, "sha224"); +} + +static int omap_sham_cra_sha256_init(struct crypto_tfm *tfm) +{ + return omap_sham_cra_init_alg(tfm, "sha256"); +} + static int omap_sham_cra_md5_init(struct crypto_tfm *tfm) { return omap_sham_cra_init_alg(tfm, "md5"); @@ -1191,7 +1227,7 @@ static void omap_sham_cra_exit(struct crypto_tfm *tfm) } } -static struct ahash_alg algs[] = { +static struct ahash_alg algs_sha1_md5[] = { { .init = omap_sham_init, .update = omap_sham_update, @@ -1290,6 +1326,102 @@ static struct ahash_alg algs[] = { } }; +/* OMAP4 has some algs in addition to what OMAP2 has */ +static struct ahash_alg algs_sha224_sha256[] = { +{ + .init = omap_sham_init, + .update = omap_sham_update, + .final = omap_sham_final, + .finup = omap_sham_finup, + .digest = omap_sham_digest, + .halg.digestsize = SHA224_DIGEST_SIZE, + .halg.base = { + .cra_name = "sha224", + .cra_driver_name = "omap-sha224", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct omap_sham_ctx), + .cra_alignmask = 0, + .cra_module = THIS_MODULE, + .cra_init = omap_sham_cra_init, + .cra_exit = omap_sham_cra_exit, + } +}, +{ + .init = omap_sham_init, + .update = omap_sham_update, + .final = omap_sham_final, + .finup = omap_sham_finup, + .digest = omap_sham_digest, + .halg.digestsize = SHA256_DIGEST_SIZE, + .halg.base = { + .cra_name = "sha256", + .cra_driver_name = "omap-sha256", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct omap_sham_ctx), + .cra_alignmask = 0, + .cra_module = THIS_MODULE, + .cra_init = omap_sham_cra_init, + .cra_exit = omap_sham_cra_exit, + } +}, +{ + .init = omap_sham_init, + .update = omap_sham_update, + .final = omap_sham_final, + .finup = omap_sham_finup, + .digest = omap_sham_digest, + .setkey = omap_sham_setkey, + .halg.digestsize = SHA224_DIGEST_SIZE, + .halg.base = { + .cra_name = "hmac(sha224)", + .cra_driver_name = "omap-hmac-sha224", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct omap_sham_ctx) + + sizeof(struct omap_sham_hmac_ctx), + .cra_alignmask = OMAP_ALIGN_MASK, + .cra_module = THIS_MODULE, + .cra_init = omap_sham_cra_sha224_init, + .cra_exit = omap_sham_cra_exit, + } +}, +{ + .init = omap_sham_init, + .update = omap_sham_update, + .final = omap_sham_final, + .finup = omap_sham_finup, + .digest = omap_sham_digest, + .setkey = omap_sham_setkey, + .halg.digestsize = SHA256_DIGEST_SIZE, + .halg.base = { + .cra_name = "hmac(sha256)", + .cra_driver_name = "omap-hmac-sha256", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct omap_sham_ctx) + + sizeof(struct omap_sham_hmac_ctx), + .cra_alignmask = OMAP_ALIGN_MASK, + .cra_module = THIS_MODULE, + .cra_init = omap_sham_cra_sha256_init, + .cra_exit = omap_sham_cra_exit, + } +}, +}; + static void omap_sham_done_task(unsigned long data) { struct omap_sham_dev *dd = (struct omap_sham_dev *)data; @@ -1364,7 +1496,16 @@ static irqreturn_t omap_sham_irq_omap4(int irq, void *dev_id) return omap_sham_irq_common(dd); } +static struct omap_sham_algs_info omap_sham_algs_info_omap2[] = { + { + .algs_list = algs_sha1_md5, + .size = ARRAY_SIZE(algs_sha1_md5), + }, +}; + static const struct omap_sham_pdata omap_sham_pdata_omap2 = { + .algs_info = omap_sham_algs_info_omap2, + .algs_info_size = ARRAY_SIZE(omap_sham_algs_info_omap2), .flags = BIT(FLAGS_BE32_SHA1), .digest_size = SHA1_DIGEST_SIZE, .copy_hash = omap_sham_copy_hash_omap2, @@ -1385,7 +1526,20 @@ static const struct omap_sham_pdata omap_sham_pdata_omap2 = { }; #ifdef CONFIG_OF +static struct omap_sham_algs_info omap_sham_algs_info_omap4[] = { + { + .algs_list = algs_sha1_md5, + .size = ARRAY_SIZE(algs_sha1_md5), + }, + { + .algs_list = algs_sha224_sha256, + .size = ARRAY_SIZE(algs_sha224_sha256), + }, +}; + static const struct omap_sham_pdata omap_sham_pdata_omap4 = { + .algs_info = omap_sham_algs_info_omap4, + .algs_info_size = ARRAY_SIZE(omap_sham_algs_info_omap4), .flags = BIT(FLAGS_AUTO_XOR), .digest_size = SHA256_DIGEST_SIZE, .copy_hash = omap_sham_copy_hash_omap4, @@ -1570,17 +1724,24 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) list_add_tail(&dd->list, &sham.dev_list); spin_unlock(&sham.lock); - for (i = 0; i < ARRAY_SIZE(algs); i++) { - err = crypto_register_ahash(&algs[i]); - if (err) - goto err_algs; + for (i = 0; i < dd->pdata->algs_info_size; i++) { + for (j = 0; j < dd->pdata->algs_info[i].size; j++) { + err = crypto_register_ahash( + &dd->pdata->algs_info[i].algs_list[j]); + if (err) + goto err_algs; + + dd->pdata->algs_info[i].registered++; + } } return 0; err_algs: - for (j = 0; j < i; j++) - crypto_unregister_ahash(&algs[j]); + for (i = dd->pdata->algs_info_size - 1; i >= 0; i--) + for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--) + crypto_unregister_ahash( + &dd->pdata->algs_info[i].algs_list[j]); pm_runtime_disable(dev); dma_release_channel(dd->dma_lch); dma_err: @@ -1597,7 +1758,7 @@ data_err: static int __devexit omap_sham_remove(struct platform_device *pdev) { static struct omap_sham_dev *dd; - int i; + int i, j; dd = platform_get_drvdata(pdev); if (!dd) @@ -1605,8 +1766,10 @@ static int __devexit omap_sham_remove(struct platform_device *pdev) spin_lock(&sham.lock); list_del(&dd->list); spin_unlock(&sham.lock); - for (i = 0; i < ARRAY_SIZE(algs); i++) - crypto_unregister_ahash(&algs[i]); + for (i = dd->pdata->algs_info_size - 1; i >= 0; i--) + for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--) + crypto_unregister_ahash( + &dd->pdata->algs_info[i].algs_list[j]); tasklet_kill(&dd->done_task); pm_runtime_disable(&pdev->dev); dma_release_channel(dd->dma_lch); -- cgit v1.2.3 From ef85cd9cd5474e94638248955d035598789fc737 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 6 Jan 2013 00:34:22 -0200 Subject: [media] em28xx: enable DMABUF Now that it uses videobuf2, em28xx can support DMABUF. Tested with an HVR-950 on analog mode and a 2gen i5core machine with an i915 graphics adapter. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 75027e390738..2eabf2a94752 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -699,7 +699,7 @@ int em28xx_vb2_setup(struct em28xx *dev) /* Setup Videobuf2 for Video capture */ q = &dev->vb_vidq; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR; + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR | VB2_DMABUF; q->drv_priv = dev; q->buf_struct_size = sizeof(struct em28xx_buffer); q->ops = &em28xx_video_qops; -- cgit v1.2.3 From e713ad1549209c10a8440d943a05056874a96015 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 2 Dec 2012 18:47:00 -0300 Subject: [media] af9033: add support for Fitipower FC0012 tuner Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/af9033.c | 4 +++ drivers/media/dvb-frontends/af9033.h | 1 + drivers/media/dvb-frontends/af9033_priv.h | 43 +++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c index 464ad878490b..27638a9bb4a6 100644 --- a/drivers/media/dvb-frontends/af9033.c +++ b/drivers/media/dvb-frontends/af9033.c @@ -318,6 +318,10 @@ static int af9033_init(struct dvb_frontend *fe) len = ARRAY_SIZE(tuner_init_fc2580); init = tuner_init_fc2580; break; + case AF9033_TUNER_FC0012: + len = ARRAY_SIZE(tuner_init_fc0012); + init = tuner_init_fc0012; + break; default: dev_dbg(&state->i2c->dev, "%s: unsupported tuner ID=%d\n", __func__, state->cfg.tuner); diff --git a/drivers/media/dvb-frontends/af9033.h b/drivers/media/dvb-frontends/af9033.h index bfa4313fde21..82bd8c1513b6 100644 --- a/drivers/media/dvb-frontends/af9033.h +++ b/drivers/media/dvb-frontends/af9033.h @@ -40,6 +40,7 @@ struct af9033_config { */ #define AF9033_TUNER_TUA9001 0x27 /* Infineon TUA 9001 */ #define AF9033_TUNER_FC0011 0x28 /* Fitipower FC0011 */ +#define AF9033_TUNER_FC0012 0x2e /* Fitipower FC0012 */ #define AF9033_TUNER_MXL5007T 0xa0 /* MaxLinear MxL5007T */ #define AF9033_TUNER_TDA18218 0xa1 /* NXP TDA 18218HN */ #define AF9033_TUNER_FC2580 0x32 /* FCI FC2580 */ diff --git a/drivers/media/dvb-frontends/af9033_priv.h b/drivers/media/dvb-frontends/af9033_priv.h index 34dddcd77538..288cd4524547 100644 --- a/drivers/media/dvb-frontends/af9033_priv.h +++ b/drivers/media/dvb-frontends/af9033_priv.h @@ -397,6 +397,49 @@ static const struct reg_val tuner_init_fc0011[] = { { 0x80F1E6, 0x00 }, }; +/* Fitipower FC0012 tuner init + AF9033_TUNER_FC0012 = 0x2e */ +static const struct reg_val tuner_init_fc0012[] = { + { 0x800046, 0x2e }, + { 0x800057, 0x00 }, + { 0x800058, 0x01 }, + { 0x800059, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x80006d, 0x00 }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800074, 0x01 }, + { 0x800075, 0x03 }, + { 0x800076, 0x02 }, + { 0x800077, 0x01 }, + { 0x800078, 0x00 }, + { 0x800079, 0x00 }, + { 0x80007a, 0x90 }, + { 0x80007b, 0x90 }, + { 0x800093, 0x00 }, + { 0x800094, 0x01 }, + { 0x800095, 0x02 }, + { 0x800096, 0x01 }, + { 0x800098, 0x0a }, + { 0x80009b, 0x05 }, + { 0x80009c, 0x80 }, + { 0x8000b3, 0x00 }, + { 0x8000c5, 0x01 }, + { 0x8000c6, 0x00 }, + { 0x8000c9, 0x5d }, + { 0x80f007, 0x00 }, + { 0x80f01f, 0xa0 }, + { 0x80f020, 0x00 }, + { 0x80f029, 0x82 }, + { 0x80f02a, 0x00 }, + { 0x80f047, 0x00 }, + { 0x80f054, 0x00 }, + { 0x80f055, 0x00 }, + { 0x80f077, 0x01 }, + { 0x80f1e6, 0x00 }, +}; + /* MaxLinear MxL5007T tuner init AF9033_TUNER_MXL5007T = 0xa0 */ static const struct reg_val tuner_init_mxl5007t[] = { -- cgit v1.2.3 From 7e0bc2960397c43019757aadc76c89da27120bea Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 2 Dec 2012 20:12:29 -0300 Subject: [media] af9035: support for Fitipower FC0012 tuner devices Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9035.c | 26 ++++++++++++++++++++++++++ drivers/media/usb/dvb-usb-v2/af9035.h | 1 + 2 files changed, 27 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 61ae7f9d0b27..c1ec18ccb0ba 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -514,6 +514,7 @@ static int af9035_read_config(struct dvb_usb_device *d) case AF9033_TUNER_MXL5007T: case AF9033_TUNER_TDA18218: case AF9033_TUNER_FC2580: + case AF9033_TUNER_FC0012: state->af9033_config[i].spec_inv = 1; break; default: @@ -907,6 +908,31 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) fe = dvb_attach(fc2580_attach, adap->fe[0], &d->i2c_adap, &af9035_fc2580_config); break; + case AF9033_TUNER_FC0012: + /* + * AF9035 gpiot2 = FC0012 enable + * XXX: there seems to be something on gpioh8 too, but on my + * my test I didn't find any difference. + */ + + /* configure gpiot2 as output and high */ + ret = af9035_wr_reg_mask(d, 0xd8eb, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8ec, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8ed, 0x01, 0x01); + if (ret < 0) + goto err; + + usleep_range(10000, 50000); + + fe = dvb_attach(fc0012_attach, adap->fe[0], &d->i2c_adap, 0x63, + 1, FC_XTAL_36_MHZ); + break; default: fe = NULL; } diff --git a/drivers/media/usb/dvb-usb-v2/af9035.h b/drivers/media/usb/dvb-usb-v2/af9035.h index 75ef1ec13fbf..f509d35a3b00 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.h +++ b/drivers/media/usb/dvb-usb-v2/af9035.h @@ -26,6 +26,7 @@ #include "af9033.h" #include "tua9001.h" #include "fc0011.h" +#include "fc0012.h" #include "mxl5007t.h" #include "tda18218.h" #include "fc2580.h" -- cgit v1.2.3 From 9805992ffc59ec0271ec037bfb02fe8111691284 Mon Sep 17 00:00:00 2001 From: Jose Alberto Reguero Date: Sun, 23 Sep 2012 16:48:47 -0300 Subject: [media] af9035: dual mode support Adds initial support for af9035 dual mode designs. Signed-off-by: Jose Alberto Reguero [crope@iki.fi: fix merge conflict] Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/af9033.c | 12 +++ drivers/media/usb/dvb-usb-v2/af9035.c | 155 +++++++++++++++++++++++----------- drivers/media/usb/dvb-usb-v2/af9035.h | 3 + 3 files changed, 123 insertions(+), 47 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c index 27638a9bb4a6..745d2fa3fbdc 100644 --- a/drivers/media/dvb-frontends/af9033.c +++ b/drivers/media/dvb-frontends/af9033.c @@ -335,6 +335,18 @@ static int af9033_init(struct dvb_frontend *fe) goto err; } + if (state->cfg.ts_mode == AF9033_TS_MODE_SERIAL) { + ret = af9033_wr_reg_mask(state, 0x00d91c, 0x01, 0x01); + if (ret < 0) + goto err; + ret = af9033_wr_reg_mask(state, 0x00d917, 0x00, 0x01); + if (ret < 0) + goto err; + ret = af9033_wr_reg_mask(state, 0x00d916, 0x00, 0x01); + if (ret < 0) + goto err; + } + state->bandwidth_hz = 0; /* force to program all parameters */ return 0; diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index c1ec18ccb0ba..15625eb28b56 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -209,10 +209,14 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, if (msg[0].len > 40 || msg[1].len > 40) { /* TODO: correct limits > 40 */ ret = -EOPNOTSUPP; - } else if (msg[0].addr == state->af9033_config[0].i2c_addr) { + } else if ((msg[0].addr == state->af9033_config[0].i2c_addr) || + (msg[0].addr == state->af9033_config[1].i2c_addr)) { /* integrated demod */ u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | msg[0].buf[2]; + if (state->af9033_config[1].i2c_addr && + (msg[0].addr == state->af9033_config[1].i2c_addr)) + reg |= 0x100000; ret = af9035_rd_regs(d, reg, &msg[1].buf[0], msg[1].len); } else { @@ -220,8 +224,9 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, u8 buf[5 + msg[0].len]; struct usb_req req = { CMD_I2C_RD, 0, sizeof(buf), buf, msg[1].len, msg[1].buf }; + req.mbox |= ((msg[0].addr & 0x80) >> 3); buf[0] = msg[1].len; - buf[1] = msg[0].addr << 1; + buf[1] = (u8)(msg[0].addr << 1); buf[2] = 0x00; /* reg addr len */ buf[3] = 0x00; /* reg addr MSB */ buf[4] = 0x00; /* reg addr LSB */ @@ -232,10 +237,14 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, if (msg[0].len > 40) { /* TODO: correct limits > 40 */ ret = -EOPNOTSUPP; - } else if (msg[0].addr == state->af9033_config[0].i2c_addr) { + } else if ((msg[0].addr == state->af9033_config[0].i2c_addr) || + (msg[0].addr == state->af9033_config[1].i2c_addr)) { /* integrated demod */ u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | msg[0].buf[2]; + if (state->af9033_config[1].i2c_addr && + (msg[0].addr == state->af9033_config[1].i2c_addr)) + reg |= 0x100000; ret = af9035_wr_regs(d, reg, &msg[0].buf[3], msg[0].len - 3); } else { @@ -243,8 +252,9 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, u8 buf[5 + msg[0].len]; struct usb_req req = { CMD_I2C_WR, 0, sizeof(buf), buf, 0, NULL }; + req.mbox |= ((msg[0].addr & 0x80) >> 3); buf[0] = msg[0].len; - buf[1] = msg[0].addr << 1; + buf[1] = (u8)(msg[0].addr << 1); buf[2] = 0x00; /* reg addr len */ buf[3] = 0x00; /* reg addr MSB */ buf[4] = 0x00; /* reg addr LSB */ @@ -283,9 +293,30 @@ static int af9035_identify_state(struct dvb_usb_device *d, const char **name) int ret; u8 wbuf[1] = { 1 }; u8 rbuf[4]; + u8 tmp; struct usb_req req = { CMD_FW_QUERYINFO, 0, sizeof(wbuf), wbuf, sizeof(rbuf), rbuf }; + /* check if there is dual tuners */ + ret = af9035_rd_reg(d, EEPROM_DUAL_MODE, &tmp); + if (ret < 0) + goto err; + + if (tmp) { + /* read 2nd demodulator I2C address */ + ret = af9035_rd_reg(d, EEPROM_2WIREADDR, &tmp); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0x00417f, tmp); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0x00d81a, 1); + if (ret < 0) + goto err; + } + ret = af9035_ctrl_msg(d, &req); if (ret < 0) goto err; @@ -498,6 +529,15 @@ static int af9035_read_config(struct dvb_usb_device *d) dev_dbg(&d->udev->dev, "%s: dual mode=%d\n", __func__, state->dual_mode); + if (state->dual_mode) { + /* read 2nd demodulator I2C address */ + ret = af9035_rd_reg(d, EEPROM_2WIREADDR, &tmp); + if (ret < 0) + goto err; + state->af9033_config[1].i2c_addr = tmp; + pr_debug("%s: 2nd demod I2C addr:%02x\n", __func__, tmp); + } + for (i = 0; i < state->dual_mode + 1; i++) { /* tuner */ ret = af9035_rd_reg(d, EEPROM_1_TUNER_ID + eeprom_shift, &tmp); @@ -731,6 +771,12 @@ static int af9035_frontend_callback(void *adapter_priv, int component, return 0; } +static int af9035_get_adapter_count(struct dvb_usb_device *d) +{ + struct state *state = d_to_priv(d); + return state->dual_mode + 1; +} + static int af9035_frontend_attach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); @@ -786,13 +832,22 @@ static const struct fc0011_config af9035_fc0011_config = { .i2c_address = 0x60, }; -static struct mxl5007t_config af9035_mxl5007t_config = { - .xtal_freq_hz = MxL_XTAL_24_MHZ, - .if_freq_hz = MxL_IF_4_57_MHZ, - .invert_if = 0, - .loop_thru_enable = 0, - .clk_out_enable = 0, - .clk_out_amp = MxL_CLKOUT_AMP_0_94V, +static struct mxl5007t_config af9035_mxl5007t_config[] = { + { + .xtal_freq_hz = MxL_XTAL_24_MHZ, + .if_freq_hz = MxL_IF_4_57_MHZ, + .invert_if = 0, + .loop_thru_enable = 0, + .clk_out_enable = 0, + .clk_out_amp = MxL_CLKOUT_AMP_0_94V, + }, { + .xtal_freq_hz = MxL_XTAL_24_MHZ, + .if_freq_hz = MxL_IF_4_57_MHZ, + .invert_if = 0, + .loop_thru_enable = 1, + .clk_out_enable = 1, + .clk_out_amp = MxL_CLKOUT_AMP_0_94V, + } }; static struct tda18218_config af9035_tda18218_config = { @@ -843,46 +898,52 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) &d->i2c_adap, &af9035_fc0011_config); break; case AF9033_TUNER_MXL5007T: - ret = af9035_wr_reg(d, 0x00d8e0, 1); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8e1, 1); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8df, 0); - if (ret < 0) - goto err; + state->tuner_address[adap->id] = 0x60; + /* hack, use b[7] to carry used I2C-bus */ + state->tuner_address[adap->id] |= (adap->id << 7); + if (adap->id == 0) { + ret = af9035_wr_reg(d, 0x00d8e0, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8e1, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8df, 0); + if (ret < 0) + goto err; - msleep(30); + msleep(30); - ret = af9035_wr_reg(d, 0x00d8df, 1); - if (ret < 0) - goto err; + ret = af9035_wr_reg(d, 0x00d8df, 1); + if (ret < 0) + goto err; - msleep(300); + msleep(300); - ret = af9035_wr_reg(d, 0x00d8c0, 1); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8c1, 1); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8bf, 0); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8b4, 1); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8b5, 1); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8b3, 1); - if (ret < 0) - goto err; + ret = af9035_wr_reg(d, 0x00d8c0, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8c1, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8bf, 0); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8b4, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8b5, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8b3, 1); + if (ret < 0) + goto err; + } /* attach tuner */ fe = dvb_attach(mxl5007t_attach, adap->fe[0], - &d->i2c_adap, 0x60, &af9035_mxl5007t_config); + &d->i2c_adap, state->tuner_address[adap->id], + &af9035_mxl5007t_config[adap->id]); break; case AF9033_TUNER_TDA18218: /* attach tuner */ @@ -971,8 +1032,8 @@ static int af9035_init(struct dvb_usb_device *d) { 0x00dd8a, (frame_size >> 0) & 0xff, 0xff}, { 0x00dd8b, (frame_size >> 8) & 0xff, 0xff}, { 0x00dd0d, packet_size, 0xff }, - { 0x80f9a3, 0x00, 0x01 }, - { 0x80f9cd, 0x00, 0x01 }, + { 0x80f9a3, state->dual_mode, 0x01 }, + { 0x80f9cd, state->dual_mode, 0x01 }, { 0x80f99d, 0x00, 0x01 }, { 0x80f9a4, 0x00, 0x01 }, }; @@ -1094,7 +1155,7 @@ static const struct dvb_usb_device_properties af9035_props = { .init = af9035_init, .get_rc_config = af9035_get_rc_config, - .num_adapters = 1, + .get_adapter_count = af9035_get_adapter_count, .adapter = { { .stream = DVB_USB_STREAM_BULK(0x84, 6, 87 * 188), diff --git a/drivers/media/usb/dvb-usb-v2/af9035.h b/drivers/media/usb/dvb-usb-v2/af9035.h index f509d35a3b00..e26e04d076aa 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.h +++ b/drivers/media/usb/dvb-usb-v2/af9035.h @@ -56,6 +56,8 @@ struct state { bool dual_mode; struct af9033_config af9033_config[2]; + + u8 tuner_address[2]; }; u32 clock_lut[] = { @@ -92,6 +94,7 @@ u32 clock_lut_it9135[] = { /* EEPROM locations */ #define EEPROM_IR_MODE 0x430d #define EEPROM_DUAL_MODE 0x4326 +#define EEPROM_2WIREADDR 0x4327 #define EEPROM_IR_TYPE 0x4329 #define EEPROM_1_IFFREQ_L 0x432d #define EEPROM_1_IFFREQ_H 0x432e -- cgit v1.2.3 From bf97b6373bb10bbde7c0b485b8fc829fec5a4bcf Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 8 Dec 2012 22:51:19 -0300 Subject: [media] af9035: dual mode related changes Various small changes and fixes releated to dual mode. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/af9033.c | 2 + drivers/media/usb/dvb-usb-v2/af9035.c | 140 +++++++++++++++++++++++----------- drivers/media/usb/dvb-usb-v2/af9035.h | 5 +- 3 files changed, 99 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c index 745d2fa3fbdc..c9cad989b8b9 100644 --- a/drivers/media/dvb-frontends/af9033.c +++ b/drivers/media/dvb-frontends/af9033.c @@ -339,9 +339,11 @@ static int af9033_init(struct dvb_frontend *fe) ret = af9033_wr_reg_mask(state, 0x00d91c, 0x01, 0x01); if (ret < 0) goto err; + ret = af9033_wr_reg_mask(state, 0x00d917, 0x00, 0x01); if (ret < 0) goto err; + ret = af9033_wr_reg_mask(state, 0x00d916, 0x00, 0x01); if (ret < 0) goto err; diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 15625eb28b56..d1beb7ffe72c 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -211,12 +211,13 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, ret = -EOPNOTSUPP; } else if ((msg[0].addr == state->af9033_config[0].i2c_addr) || (msg[0].addr == state->af9033_config[1].i2c_addr)) { - /* integrated demod */ + /* demod access via firmware interface */ u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | msg[0].buf[2]; - if (state->af9033_config[1].i2c_addr && - (msg[0].addr == state->af9033_config[1].i2c_addr)) + + if (msg[0].addr == state->af9033_config[1].i2c_addr) reg |= 0x100000; + ret = af9035_rd_regs(d, reg, &msg[1].buf[0], msg[1].len); } else { @@ -226,7 +227,7 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, buf, msg[1].len, msg[1].buf }; req.mbox |= ((msg[0].addr & 0x80) >> 3); buf[0] = msg[1].len; - buf[1] = (u8)(msg[0].addr << 1); + buf[1] = msg[0].addr << 1; buf[2] = 0x00; /* reg addr len */ buf[3] = 0x00; /* reg addr MSB */ buf[4] = 0x00; /* reg addr LSB */ @@ -239,12 +240,13 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, ret = -EOPNOTSUPP; } else if ((msg[0].addr == state->af9033_config[0].i2c_addr) || (msg[0].addr == state->af9033_config[1].i2c_addr)) { - /* integrated demod */ + /* demod access via firmware interface */ u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | msg[0].buf[2]; - if (state->af9033_config[1].i2c_addr && - (msg[0].addr == state->af9033_config[1].i2c_addr)) + + if (msg[0].addr == state->af9033_config[1].i2c_addr) reg |= 0x100000; + ret = af9035_wr_regs(d, reg, &msg[0].buf[3], msg[0].len - 3); } else { @@ -254,7 +256,7 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, 0, NULL }; req.mbox |= ((msg[0].addr & 0x80) >> 3); buf[0] = msg[0].len; - buf[1] = (u8)(msg[0].addr << 1); + buf[1] = msg[0].addr << 1; buf[2] = 0x00; /* reg addr len */ buf[3] = 0x00; /* reg addr MSB */ buf[4] = 0x00; /* reg addr LSB */ @@ -293,30 +295,9 @@ static int af9035_identify_state(struct dvb_usb_device *d, const char **name) int ret; u8 wbuf[1] = { 1 }; u8 rbuf[4]; - u8 tmp; struct usb_req req = { CMD_FW_QUERYINFO, 0, sizeof(wbuf), wbuf, sizeof(rbuf), rbuf }; - /* check if there is dual tuners */ - ret = af9035_rd_reg(d, EEPROM_DUAL_MODE, &tmp); - if (ret < 0) - goto err; - - if (tmp) { - /* read 2nd demodulator I2C address */ - ret = af9035_rd_reg(d, EEPROM_2WIREADDR, &tmp); - if (ret < 0) - goto err; - - ret = af9035_wr_reg(d, 0x00417f, tmp); - if (ret < 0) - goto err; - - ret = af9035_wr_reg(d, 0x00d81a, 1); - if (ret < 0) - goto err; - } - ret = af9035_ctrl_msg(d, &req); if (ret < 0) goto err; @@ -344,11 +325,56 @@ static int af9035_download_firmware(struct dvb_usb_device *d, struct usb_req req = { 0, 0, 0, NULL, 0, NULL }; struct usb_req req_fw_dl = { CMD_FW_DL, 0, 0, wbuf, 0, NULL }; struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ; - u8 hdr_core; + u8 hdr_core, tmp; u16 hdr_addr, hdr_data_len, hdr_checksum; #define MAX_DATA 58 #define HDR_SIZE 7 + /* + * In case of dual tuner configuration we need to do some extra + * initialization in order to download firmware to slave demod too, + * which is done by master demod. + * Master feeds also clock and controls power via GPIO. + */ + ret = af9035_rd_reg(d, EEPROM_DUAL_MODE, &tmp); + if (ret < 0) + goto err; + + if (tmp) { + /* configure gpioh1, reset & power slave demod */ + ret = af9035_wr_reg_mask(d, 0x00d8b0, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0x00d8b1, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0x00d8af, 0x00, 0x01); + if (ret < 0) + goto err; + + usleep_range(10000, 50000); + + ret = af9035_wr_reg_mask(d, 0x00d8af, 0x01, 0x01); + if (ret < 0) + goto err; + + /* tell the slave I2C address */ + ret = af9035_rd_reg(d, EEPROM_2ND_DEMOD_ADDR, &tmp); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0x00417f, tmp); + if (ret < 0) + goto err; + + /* enable clock out */ + ret = af9035_wr_reg_mask(d, 0x00d81a, 0x01, 0x01); + if (ret < 0) + goto err; + } + /* * Thanks to Daniel Glöckner about that info! * @@ -520,22 +546,27 @@ static int af9035_read_config(struct dvb_usb_device *d) u8 tmp; u16 tmp16; + /* demod I2C "address" */ + state->af9033_config[0].i2c_addr = 0x38; + /* check if there is dual tuners */ ret = af9035_rd_reg(d, EEPROM_DUAL_MODE, &tmp); if (ret < 0) goto err; state->dual_mode = tmp; - dev_dbg(&d->udev->dev, "%s: dual mode=%d\n", - __func__, state->dual_mode); + dev_dbg(&d->udev->dev, "%s: dual mode=%d\n", __func__, + state->dual_mode); if (state->dual_mode) { /* read 2nd demodulator I2C address */ - ret = af9035_rd_reg(d, EEPROM_2WIREADDR, &tmp); + ret = af9035_rd_reg(d, EEPROM_2ND_DEMOD_ADDR, &tmp); if (ret < 0) goto err; + state->af9033_config[1].i2c_addr = tmp; - pr_debug("%s: 2nd demod I2C addr:%02x\n", __func__, tmp); + dev_dbg(&d->udev->dev, "%s: 2nd demod I2C addr=%02x\n", + __func__, tmp); } for (i = 0; i < state->dual_mode + 1; i++) { @@ -563,6 +594,16 @@ static int af9035_read_config(struct dvb_usb_device *d) KBUILD_MODNAME, tmp); } + /* disable dual mode if driver does not support it */ + if (i == 1) + switch (tmp) { + default: + state->dual_mode = false; + dev_info(&d->udev->dev, "%s: driver does not " \ + "support 2nd tuner and will " \ + "disable it", KBUILD_MODNAME); + } + /* tuner IF frequency */ ret = af9035_rd_reg(d, EEPROM_1_IFFREQ_L + eeprom_shift, &tmp); if (ret < 0) @@ -798,15 +839,14 @@ static int af9035_frontend_attach(struct dvb_usb_adapter *adap) if (ret < 0) goto err; - ret = af9035_wr_reg(d, 0x00d81a, - state->dual_mode); + ret = af9035_wr_reg(d, 0x00d81a, state->dual_mode); if (ret < 0) goto err; } /* attach demodulator */ - adap->fe[0] = dvb_attach(af9033_attach, - &state->af9033_config[adap->id], &d->i2c_adap); + adap->fe[0] = dvb_attach(af9033_attach, &state->af9033_config[adap->id], + &d->i2c_adap); if (adap->fe[0] == NULL) { ret = -ENODEV; goto err; @@ -866,6 +906,11 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) struct dvb_usb_device *d = adap_to_d(adap); int ret; struct dvb_frontend *fe; + u8 tuner_addr; + /* + * XXX: Hack used in that function: we abuse unused I2C address bit [7] + * to carry info about used I2C bus for dual tuner configuration. + */ switch (state->af9033_config[adap->id].tuner) { case AF9033_TUNER_TUA9001: @@ -898,16 +943,15 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) &d->i2c_adap, &af9035_fc0011_config); break; case AF9033_TUNER_MXL5007T: - state->tuner_address[adap->id] = 0x60; - /* hack, use b[7] to carry used I2C-bus */ - state->tuner_address[adap->id] |= (adap->id << 7); if (adap->id == 0) { ret = af9035_wr_reg(d, 0x00d8e0, 1); if (ret < 0) goto err; + ret = af9035_wr_reg(d, 0x00d8e1, 1); if (ret < 0) goto err; + ret = af9035_wr_reg(d, 0x00d8df, 0); if (ret < 0) goto err; @@ -923,27 +967,35 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) ret = af9035_wr_reg(d, 0x00d8c0, 1); if (ret < 0) goto err; + ret = af9035_wr_reg(d, 0x00d8c1, 1); if (ret < 0) goto err; + ret = af9035_wr_reg(d, 0x00d8bf, 0); if (ret < 0) goto err; + ret = af9035_wr_reg(d, 0x00d8b4, 1); if (ret < 0) goto err; + ret = af9035_wr_reg(d, 0x00d8b5, 1); if (ret < 0) goto err; + ret = af9035_wr_reg(d, 0x00d8b3, 1); if (ret < 0) goto err; + + tuner_addr = 0x60; + } else { + tuner_addr = 0x60 | 0x80; /* I2C bus hack */ } /* attach tuner */ - fe = dvb_attach(mxl5007t_attach, adap->fe[0], - &d->i2c_adap, state->tuner_address[adap->id], - &af9035_mxl5007t_config[adap->id]); + fe = dvb_attach(mxl5007t_attach, adap->fe[0], &d->i2c_adap, + tuner_addr, &af9035_mxl5007t_config[adap->id]); break; case AF9033_TUNER_TDA18218: /* attach tuner */ diff --git a/drivers/media/usb/dvb-usb-v2/af9035.h b/drivers/media/usb/dvb-usb-v2/af9035.h index e26e04d076aa..29f3eec22c2c 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.h +++ b/drivers/media/usb/dvb-usb-v2/af9035.h @@ -54,10 +54,7 @@ struct usb_req { struct state { u8 seq; /* packet sequence number */ bool dual_mode; - struct af9033_config af9033_config[2]; - - u8 tuner_address[2]; }; u32 clock_lut[] = { @@ -94,7 +91,7 @@ u32 clock_lut_it9135[] = { /* EEPROM locations */ #define EEPROM_IR_MODE 0x430d #define EEPROM_DUAL_MODE 0x4326 -#define EEPROM_2WIREADDR 0x4327 +#define EEPROM_2ND_DEMOD_ADDR 0x4327 #define EEPROM_IR_TYPE 0x4329 #define EEPROM_1_IFFREQ_L 0x432d #define EEPROM_1_IFFREQ_H 0x432e -- cgit v1.2.3 From ad3a758bb30ab7c71b67930ae7dcc794d517dd6b Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 8 Dec 2012 23:27:49 -0300 Subject: [media] fc0012: use struct for driver config I need even more configuration options and overloading dvb_attach() for all those sounds quite stupid. Due to that switch struct and make room for new options. Signed-off-by: Antti Palosaari Acked-by: Hans-Frieder Vogt Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0012.c | 9 ++++----- drivers/media/tuners/fc0012.h | 20 ++++++++++++++++---- drivers/media/usb/dvb-usb-v2/af9035.c | 10 ++++++++-- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 7 ++++++- 4 files changed, 34 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/media/tuners/fc0012.c b/drivers/media/tuners/fc0012.c index 308135abd54c..5ede0c049bc0 100644 --- a/drivers/media/tuners/fc0012.c +++ b/drivers/media/tuners/fc0012.c @@ -436,8 +436,7 @@ static const struct dvb_tuner_ops fc0012_tuner_ops = { }; struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, u8 i2c_address, int dual_master, - enum fc001x_xtal_freq xtal_freq) + struct i2c_adapter *i2c, const struct fc0012_config *cfg) { struct fc0012_priv *priv = NULL; @@ -446,9 +445,9 @@ struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, return NULL; priv->i2c = i2c; - priv->dual_master = dual_master; - priv->addr = i2c_address; - priv->xtal_freq = xtal_freq; + priv->dual_master = cfg->dual_master; + priv->addr = cfg->i2c_address; + priv->xtal_freq = cfg->xtal_freq; info("Fitipower FC0012 successfully attached."); diff --git a/drivers/media/tuners/fc0012.h b/drivers/media/tuners/fc0012.h index 4dbd5efe8845..41946f82d02b 100644 --- a/drivers/media/tuners/fc0012.h +++ b/drivers/media/tuners/fc0012.h @@ -24,17 +24,29 @@ #include "dvb_frontend.h" #include "fc001x-common.h" +struct fc0012_config { + /* + * I2C address + */ + u8 i2c_address; + + /* + * clock + */ + enum fc001x_xtal_freq xtal_freq; + + int dual_master; +}; + #if defined(CONFIG_MEDIA_TUNER_FC0012) || \ (defined(CONFIG_MEDIA_TUNER_FC0012_MODULE) && defined(MODULE)) extern struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, - u8 i2c_address, int dual_master, - enum fc001x_xtal_freq xtal_freq); + const struct fc0012_config *cfg); #else static inline struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, - u8 i2c_address, int dual_master, - enum fc001x_xtal_freq xtal_freq) + const struct fc0012_config *cfg) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index d1beb7ffe72c..6cf9ad5ef8c1 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -900,6 +900,12 @@ static const struct fc2580_config af9035_fc2580_config = { .clock = 16384000, }; +static const struct fc0012_config af9035_fc0012_config = { + .i2c_address = 0x63, + .xtal_freq = FC_XTAL_36_MHZ, + .dual_master = 1, +}; + static int af9035_tuner_attach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); @@ -1043,8 +1049,8 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) usleep_range(10000, 50000); - fe = dvb_attach(fc0012_attach, adap->fe[0], &d->i2c_adap, 0x63, - 1, FC_XTAL_36_MHZ); + fe = dvb_attach(fc0012_attach, adap->fe[0], &d->i2c_adap, + &af9035_fc0012_config); break; default: fe = NULL; diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index a4c302d0aa37..eddda6958c19 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -835,6 +835,11 @@ static struct tua9001_config rtl2832u_tua9001_config = { .i2c_addr = 0x60, }; +static const struct fc0012_config rtl2832u_fc0012_config = { + .i2c_address = 0x63, /* 0xc6 >> 1 */ + .xtal_freq = FC_XTAL_28_8_MHZ, +}; + static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) { int ret; @@ -847,7 +852,7 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) switch (priv->tuner) { case TUNER_RTL2832_FC0012: fe = dvb_attach(fc0012_attach, adap->fe[0], - &d->i2c_adap, 0xc6>>1, 0, FC_XTAL_28_8_MHZ); + &d->i2c_adap, &rtl2832u_fc0012_config); /* since fc0012 includs reading the signal strength delegate * that to the tuner driver */ -- cgit v1.2.3 From 71b1e82794bbae7b23409e013f7249dd2f382160 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 00:30:08 -0300 Subject: [media] fc0012: add RF loop through Signed-off-by: Antti Palosaari Acked-by: Hans-Frieder Vogt Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0012-priv.h | 1 + drivers/media/tuners/fc0012.c | 7 +++++++ drivers/media/tuners/fc0012.h | 5 +++++ 3 files changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/media/tuners/fc0012-priv.h b/drivers/media/tuners/fc0012-priv.h index 4577c917e616..1195ee9b9cf7 100644 --- a/drivers/media/tuners/fc0012-priv.h +++ b/drivers/media/tuners/fc0012-priv.h @@ -32,6 +32,7 @@ struct fc0012_priv { struct i2c_adapter *i2c; + const struct fc0012_config *cfg; u8 addr; u8 dual_master; u8 xtal_freq; diff --git a/drivers/media/tuners/fc0012.c b/drivers/media/tuners/fc0012.c index 5ede0c049bc0..636f951219db 100644 --- a/drivers/media/tuners/fc0012.c +++ b/drivers/media/tuners/fc0012.c @@ -101,6 +101,9 @@ static int fc0012_init(struct dvb_frontend *fe) if (priv->dual_master) reg[0x0c] |= 0x02; + if (priv->cfg->loop_through) + reg[0x09] |= 0x01; + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ @@ -445,6 +448,7 @@ struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, return NULL; priv->i2c = i2c; + priv->cfg = cfg; priv->dual_master = cfg->dual_master; priv->addr = cfg->i2c_address; priv->xtal_freq = cfg->xtal_freq; @@ -453,6 +457,9 @@ struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, fe->tuner_priv = priv; + if (priv->cfg->loop_through) + fc0012_writereg(priv, 0x09, 0x6f); + memcpy(&fe->ops.tuner_ops, &fc0012_tuner_ops, sizeof(struct dvb_tuner_ops)); diff --git a/drivers/media/tuners/fc0012.h b/drivers/media/tuners/fc0012.h index 41946f82d02b..891d66d5af3b 100644 --- a/drivers/media/tuners/fc0012.h +++ b/drivers/media/tuners/fc0012.h @@ -36,6 +36,11 @@ struct fc0012_config { enum fc001x_xtal_freq xtal_freq; int dual_master; + + /* + * RF loop-through + */ + bool loop_through; }; #if defined(CONFIG_MEDIA_TUNER_FC0012) || \ -- cgit v1.2.3 From 3b0d51afa026b87784d81e6a88522271a69ca7b9 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 11:46:40 -0300 Subject: [media] fc0012: enable clock output on attach() We need feed clock to slave demodulator at the very beginning in case of dual tuner configuration. I am not sure if that configuration changes clock output divider or enable clock output itself... Signed-off-by: Antti Palosaari Acked-by: Hans-Frieder Vogt Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0012.c | 7 +++++++ drivers/media/tuners/fc0012.h | 5 +++++ 2 files changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/media/tuners/fc0012.c b/drivers/media/tuners/fc0012.c index 636f951219db..1a52b766360f 100644 --- a/drivers/media/tuners/fc0012.c +++ b/drivers/media/tuners/fc0012.c @@ -460,6 +460,13 @@ struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, if (priv->cfg->loop_through) fc0012_writereg(priv, 0x09, 0x6f); + /* + * TODO: Clock out en or div? + * For dual tuner configuration clearing bit [0] is required. + */ + if (priv->cfg->clock_out) + fc0012_writereg(priv, 0x0b, 0x82); + memcpy(&fe->ops.tuner_ops, &fc0012_tuner_ops, sizeof(struct dvb_tuner_ops)); diff --git a/drivers/media/tuners/fc0012.h b/drivers/media/tuners/fc0012.h index 891d66d5af3b..83a98e732502 100644 --- a/drivers/media/tuners/fc0012.h +++ b/drivers/media/tuners/fc0012.h @@ -41,6 +41,11 @@ struct fc0012_config { * RF loop-through */ bool loop_through; + + /* + * clock output + */ + bool clock_out; }; #if defined(CONFIG_MEDIA_TUNER_FC0012) || \ -- cgit v1.2.3 From 0bb3d8ac87188a106f98e5d257f56f3ffe066147 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 12:01:41 -0300 Subject: [media] af9035: add support for fc0012 dual tuner configuration That adds support for AF9035 dual devices having FC0012 tuners. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9035.c | 56 +++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 6cf9ad5ef8c1..1c7fe5ab5b9b 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -597,6 +597,8 @@ static int af9035_read_config(struct dvb_usb_device *d) /* disable dual mode if driver does not support it */ if (i == 1) switch (tmp) { + case AF9033_TUNER_FC0012: + break; default: state->dual_mode = false; dev_info(&d->udev->dev, "%s: driver does not " \ @@ -900,10 +902,18 @@ static const struct fc2580_config af9035_fc2580_config = { .clock = 16384000, }; -static const struct fc0012_config af9035_fc0012_config = { - .i2c_address = 0x63, - .xtal_freq = FC_XTAL_36_MHZ, - .dual_master = 1, +static const struct fc0012_config af9035_fc0012_config[] = { + { + .i2c_address = 0x63, + .xtal_freq = FC_XTAL_36_MHZ, + .dual_master = 1, + .loop_through = true, + .clock_out = true, + }, { + .i2c_address = 0x63 | 0x80, /* I2C bus select hack */ + .xtal_freq = FC_XTAL_36_MHZ, + .dual_master = 1, + } }; static int af9035_tuner_attach(struct dvb_usb_adapter *adap) @@ -912,6 +922,7 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) struct dvb_usb_device *d = adap_to_d(adap); int ret; struct dvb_frontend *fe; + struct i2c_msg msg[1]; u8 tuner_addr; /* * XXX: Hack used in that function: we abuse unused I2C address bit [7] @@ -1034,23 +1045,38 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) * my test I didn't find any difference. */ - /* configure gpiot2 as output and high */ - ret = af9035_wr_reg_mask(d, 0xd8eb, 0x01, 0x01); - if (ret < 0) - goto err; + if (adap->id == 0) { + /* configure gpiot2 as output and high */ + ret = af9035_wr_reg_mask(d, 0xd8eb, 0x01, 0x01); + if (ret < 0) + goto err; - ret = af9035_wr_reg_mask(d, 0xd8ec, 0x01, 0x01); - if (ret < 0) - goto err; + ret = af9035_wr_reg_mask(d, 0xd8ec, 0x01, 0x01); + if (ret < 0) + goto err; - ret = af9035_wr_reg_mask(d, 0xd8ed, 0x01, 0x01); - if (ret < 0) - goto err; + ret = af9035_wr_reg_mask(d, 0xd8ed, 0x01, 0x01); + if (ret < 0) + goto err; + } else { + /* + * FIXME: That belongs for the FC0012 driver. + * Write 02 to FC0012 master tuner register 0d directly + * in order to make slave tuner working. + */ + msg[0].addr = 0x63; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = "\x0d\x02"; + ret = i2c_transfer(&d->i2c_adap, msg, 1); + if (ret < 0) + goto err; + } usleep_range(10000, 50000); fe = dvb_attach(fc0012_attach, adap->fe[0], &d->i2c_adap, - &af9035_fc0012_config); + &af9035_fc0012_config[adap->id]); break; default: fe = NULL; -- cgit v1.2.3 From 3a98477200b44328e50a5c0830f92fd5cdc1ea9b Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 12:33:04 -0300 Subject: [media] fc0012: use config directly from the config struct No need to copy config to the driver state. Those are coming from the const struct and could be used directly. Signed-off-by: Antti Palosaari Acked-by: Hans-Frieder Vogt Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0012-priv.h | 3 --- drivers/media/tuners/fc0012.c | 17 ++++++++--------- drivers/media/tuners/fc0012.h | 2 +- drivers/media/usb/dvb-usb-v2/af9035.c | 4 ++-- 4 files changed, 11 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/media/tuners/fc0012-priv.h b/drivers/media/tuners/fc0012-priv.h index 1195ee9b9cf7..3b98bf971fb3 100644 --- a/drivers/media/tuners/fc0012-priv.h +++ b/drivers/media/tuners/fc0012-priv.h @@ -33,9 +33,6 @@ struct fc0012_priv { struct i2c_adapter *i2c; const struct fc0012_config *cfg; - u8 addr; - u8 dual_master; - u8 xtal_freq; u32 frequency; u32 bandwidth; diff --git a/drivers/media/tuners/fc0012.c b/drivers/media/tuners/fc0012.c index 1a52b766360f..01f5e406282c 100644 --- a/drivers/media/tuners/fc0012.c +++ b/drivers/media/tuners/fc0012.c @@ -25,7 +25,7 @@ static int fc0012_writereg(struct fc0012_priv *priv, u8 reg, u8 val) { u8 buf[2] = {reg, val}; struct i2c_msg msg = { - .addr = priv->addr, .flags = 0, .buf = buf, .len = 2 + .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2 }; if (i2c_transfer(priv->i2c, &msg, 1) != 1) { @@ -38,8 +38,10 @@ static int fc0012_writereg(struct fc0012_priv *priv, u8 reg, u8 val) static int fc0012_readreg(struct fc0012_priv *priv, u8 reg, u8 *val) { struct i2c_msg msg[2] = { - { .addr = priv->addr, .flags = 0, .buf = ®, .len = 1 }, - { .addr = priv->addr, .flags = I2C_M_RD, .buf = val, .len = 1 }, + { .addr = priv->cfg->i2c_address, .flags = 0, + .buf = ®, .len = 1 }, + { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, + .buf = val, .len = 1 }, }; if (i2c_transfer(priv->i2c, msg, 2) != 2) { @@ -88,7 +90,7 @@ static int fc0012_init(struct dvb_frontend *fe) 0x04, /* reg. 0x15: Enable LNA COMPS */ }; - switch (priv->xtal_freq) { + switch (priv->cfg->xtal_freq) { case FC_XTAL_27_MHZ: case FC_XTAL_28_8_MHZ: reg[0x07] |= 0x20; @@ -98,7 +100,7 @@ static int fc0012_init(struct dvb_frontend *fe) break; } - if (priv->dual_master) + if (priv->cfg->dual_master) reg[0x0c] |= 0x02; if (priv->cfg->loop_through) @@ -147,7 +149,7 @@ static int fc0012_set_params(struct dvb_frontend *fe) goto exit; } - switch (priv->xtal_freq) { + switch (priv->cfg->xtal_freq) { case FC_XTAL_27_MHZ: xtal_freq_khz_2 = 27000 / 2; break; @@ -449,9 +451,6 @@ struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, priv->i2c = i2c; priv->cfg = cfg; - priv->dual_master = cfg->dual_master; - priv->addr = cfg->i2c_address; - priv->xtal_freq = cfg->xtal_freq; info("Fitipower FC0012 successfully attached."); diff --git a/drivers/media/tuners/fc0012.h b/drivers/media/tuners/fc0012.h index 83a98e732502..3fb53b86813a 100644 --- a/drivers/media/tuners/fc0012.h +++ b/drivers/media/tuners/fc0012.h @@ -35,7 +35,7 @@ struct fc0012_config { */ enum fc001x_xtal_freq xtal_freq; - int dual_master; + bool dual_master; /* * RF loop-through diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 1c7fe5ab5b9b..68e0e80416aa 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -906,13 +906,13 @@ static const struct fc0012_config af9035_fc0012_config[] = { { .i2c_address = 0x63, .xtal_freq = FC_XTAL_36_MHZ, - .dual_master = 1, + .dual_master = true, .loop_through = true, .clock_out = true, }, { .i2c_address = 0x63 | 0x80, /* I2C bus select hack */ .xtal_freq = FC_XTAL_36_MHZ, - .dual_master = 1, + .dual_master = true, } }; -- cgit v1.2.3 From 44ff69cd95308c115134f0546317b584fe1bf5b2 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 13:28:45 -0300 Subject: [media] fc0012: rework attach() to check chip id and I/O errors Signed-off-by: Antti Palosaari Acked-by: Hans-Frieder Vogt Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0012.c | 59 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/media/tuners/fc0012.c b/drivers/media/tuners/fc0012.c index 01f5e406282c..feb15941cc5b 100644 --- a/drivers/media/tuners/fc0012.c +++ b/drivers/media/tuners/fc0012.c @@ -443,32 +443,71 @@ static const struct dvb_tuner_ops fc0012_tuner_ops = { struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct fc0012_config *cfg) { - struct fc0012_priv *priv = NULL; + struct fc0012_priv *priv; + int ret; + u8 chip_id; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); priv = kzalloc(sizeof(struct fc0012_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; + if (!priv) { + ret = -ENOMEM; + dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); + goto err; + } - priv->i2c = i2c; priv->cfg = cfg; + priv->i2c = i2c; - info("Fitipower FC0012 successfully attached."); + /* check if the tuner is there */ + ret = fc0012_readreg(priv, 0x00, &chip_id); + if (ret < 0) + goto err; - fe->tuner_priv = priv; + dev_dbg(&i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id); - if (priv->cfg->loop_through) - fc0012_writereg(priv, 0x09, 0x6f); + switch (chip_id) { + case 0xa1: + break; + default: + ret = -ENODEV; + goto err; + } + + dev_info(&i2c->dev, "%s: Fitipower FC0012 successfully identified\n", + KBUILD_MODNAME); + + if (priv->cfg->loop_through) { + ret = fc0012_writereg(priv, 0x09, 0x6f); + if (ret < 0) + goto err; + } /* * TODO: Clock out en or div? * For dual tuner configuration clearing bit [0] is required. */ - if (priv->cfg->clock_out) - fc0012_writereg(priv, 0x0b, 0x82); + if (priv->cfg->clock_out) { + ret = fc0012_writereg(priv, 0x0b, 0x82); + if (ret < 0) + goto err; + } + fe->tuner_priv = priv; memcpy(&fe->ops.tuner_ops, &fc0012_tuner_ops, sizeof(struct dvb_tuner_ops)); +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (ret) { + dev_dbg(&i2c->dev, "%s: failed: %d\n", __func__, ret); + kfree(priv); + return NULL; + } + return fe; } EXPORT_SYMBOL(fc0012_attach); -- cgit v1.2.3 From 4562620159c6c0c3c11913d518138a27e6ecd957 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 13:42:25 -0300 Subject: [media] fc0012: use Kernel dev_foo() logging Signed-off-by: Antti Palosaari Acked-by: Hans-Frieder Vogt Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0012-priv.h | 9 --------- drivers/media/tuners/fc0012.c | 20 ++++++++++++++------ drivers/media/tuners/fc0012.h | 2 +- 3 files changed, 15 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/media/tuners/fc0012-priv.h b/drivers/media/tuners/fc0012-priv.h index 3b98bf971fb3..1a86ce1d3fcf 100644 --- a/drivers/media/tuners/fc0012-priv.h +++ b/drivers/media/tuners/fc0012-priv.h @@ -21,15 +21,6 @@ #ifndef _FC0012_PRIV_H_ #define _FC0012_PRIV_H_ -#define LOG_PREFIX "fc0012" - -#undef err -#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) -#undef info -#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) -#undef warn -#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) - struct fc0012_priv { struct i2c_adapter *i2c; const struct fc0012_config *cfg; diff --git a/drivers/media/tuners/fc0012.c b/drivers/media/tuners/fc0012.c index feb15941cc5b..4491f063e211 100644 --- a/drivers/media/tuners/fc0012.c +++ b/drivers/media/tuners/fc0012.c @@ -29,7 +29,9 @@ static int fc0012_writereg(struct fc0012_priv *priv, u8 reg, u8 val) }; if (i2c_transfer(priv->i2c, &msg, 1) != 1) { - err("I2C write reg failed, reg: %02x, val: %02x", reg, val); + dev_err(&priv->i2c->dev, + "%s: I2C write reg failed, reg: %02x, val: %02x\n", + KBUILD_MODNAME, reg, val); return -EREMOTEIO; } return 0; @@ -45,7 +47,9 @@ static int fc0012_readreg(struct fc0012_priv *priv, u8 reg, u8 *val) }; if (i2c_transfer(priv->i2c, msg, 2) != 2) { - err("I2C read reg failed, reg: %02x", reg); + dev_err(&priv->i2c->dev, + "%s: I2C read reg failed, reg: %02x\n", + KBUILD_MODNAME, reg); return -EREMOTEIO; } return 0; @@ -119,7 +123,8 @@ static int fc0012_init(struct dvb_frontend *fe) fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ if (ret) - err("fc0012_writereg failed: %d", ret); + dev_err(&priv->i2c->dev, "%s: fc0012_writereg failed: %d\n", + KBUILD_MODNAME, ret); return ret; } @@ -261,7 +266,8 @@ static int fc0012_set_params(struct dvb_frontend *fe) break; } } else { - err("%s: modulation type not supported!", __func__); + dev_err(&priv->i2c->dev, "%s: modulation type not supported!\n", + KBUILD_MODNAME); return -EINVAL; } @@ -323,7 +329,8 @@ exit: if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ if (ret) - warn("%s: failed: %d", __func__, ret); + dev_warn(&priv->i2c->dev, "%s: %s failed: %d\n", + KBUILD_MODNAME, __func__, ret); return ret; } @@ -413,7 +420,8 @@ err: fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ exit: if (ret) - warn("%s: failed: %d", __func__, ret); + dev_warn(&priv->i2c->dev, "%s: %s failed: %d\n", + KBUILD_MODNAME, __func__, ret); return ret; } diff --git a/drivers/media/tuners/fc0012.h b/drivers/media/tuners/fc0012.h index 3fb53b86813a..54508fcc3469 100644 --- a/drivers/media/tuners/fc0012.h +++ b/drivers/media/tuners/fc0012.h @@ -58,7 +58,7 @@ static inline struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct fc0012_config *cfg) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + pr_warn("%s: driver disabled by Kconfig\n", __func__); return NULL; } #endif -- cgit v1.2.3 From c5dc3b98fffaf183bb3d5bf690bfab9f6705c5e1 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 13:45:42 -0300 Subject: [media] fc0012: remove unused callback and correct one comment There is no need to keep dummy sleep() callback implementation as DVB-core checks existence of it before calls callback. Due to that we can remove it. FC0012 is based of direct-conversion receiver architecture (aka Zero-IF) where is no IF used. Due to that IF is always 0 Hz. Fix comment to point that. Signed-off-by: Antti Palosaari Acked-by: Hans-Frieder Vogt Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0012.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/media/tuners/fc0012.c b/drivers/media/tuners/fc0012.c index 4491f063e211..f4d0e797a6cc 100644 --- a/drivers/media/tuners/fc0012.c +++ b/drivers/media/tuners/fc0012.c @@ -129,12 +129,6 @@ static int fc0012_init(struct dvb_frontend *fe) return ret; } -static int fc0012_sleep(struct dvb_frontend *fe) -{ - /* nothing to do here */ - return 0; -} - static int fc0012_set_params(struct dvb_frontend *fe) { struct fc0012_priv *priv = fe->tuner_priv; @@ -343,8 +337,7 @@ static int fc0012_get_frequency(struct dvb_frontend *fe, u32 *frequency) static int fc0012_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) { - /* CHECK: always ? */ - *frequency = 0; + *frequency = 0; /* Zero-IF */ return 0; } @@ -437,7 +430,6 @@ static const struct dvb_tuner_ops fc0012_tuner_ops = { .release = fc0012_release, .init = fc0012_init, - .sleep = fc0012_sleep, .set_params = fc0012_set_params, -- cgit v1.2.3 From d267d2709196b2a2ef27850abd9189c9ed5e537a Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 14:46:45 -0300 Subject: [media] af9033: update demod init sequence Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/af9033_priv.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/af9033_priv.h b/drivers/media/dvb-frontends/af9033_priv.h index 288cd4524547..d96d128622a1 100644 --- a/drivers/media/dvb-frontends/af9033_priv.h +++ b/drivers/media/dvb-frontends/af9033_priv.h @@ -199,10 +199,9 @@ static const struct reg_val ofsm_init[] = { { 0x8000a6, 0x01 }, { 0x8000a9, 0x00 }, { 0x8000aa, 0x01 }, - { 0x8000ab, 0x01 }, { 0x8000b0, 0x01 }, - { 0x8000c0, 0x05 }, - { 0x8000c4, 0x19 }, + { 0x8000c4, 0x05 }, + { 0x8000c8, 0x19 }, { 0x80f000, 0x0f }, { 0x80f016, 0x10 }, { 0x80f017, 0x04 }, -- cgit v1.2.3 From 2c37d37fc635a562753e93628217c578d298609e Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 15:38:03 -0300 Subject: [media] af9033: update tua9001 init sequence Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/af9033_priv.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/af9033_priv.h b/drivers/media/dvb-frontends/af9033_priv.h index d96d128622a1..e0be0409435e 100644 --- a/drivers/media/dvb-frontends/af9033_priv.h +++ b/drivers/media/dvb-frontends/af9033_priv.h @@ -321,8 +321,9 @@ static const struct reg_val tuner_init_tua9001[] = { { 0x80009b, 0x05 }, { 0x80009c, 0x80 }, { 0x8000b3, 0x00 }, - { 0x8000c1, 0x01 }, - { 0x8000c2, 0x00 }, + { 0x8000c5, 0x01 }, + { 0x8000c6, 0x00 }, + { 0x8000c9, 0x5d }, { 0x80f007, 0x00 }, { 0x80f01f, 0x82 }, { 0x80f020, 0x00 }, -- cgit v1.2.3 From 0353d6b1cd23acf88327fa6bdb28a48f29c080a3 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 15:45:26 -0300 Subject: [media] af9033: update fc0011 init sequence Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/af9033_priv.h | 72 +++++++++++++++---------------- 1 file changed, 36 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/af9033_priv.h b/drivers/media/dvb-frontends/af9033_priv.h index e0be0409435e..1fb84a230b46 100644 --- a/drivers/media/dvb-frontends/af9033_priv.h +++ b/drivers/media/dvb-frontends/af9033_priv.h @@ -339,14 +339,14 @@ static const struct reg_val tuner_init_tua9001[] = { /* Fitipower fc0011 tuner init AF9033_TUNER_FC0011 = 0x28 */ static const struct reg_val tuner_init_fc0011[] = { - { 0x800046, AF9033_TUNER_FC0011 }, + { 0x800046, 0x28 }, { 0x800057, 0x00 }, { 0x800058, 0x01 }, { 0x80005f, 0x00 }, { 0x800060, 0x00 }, { 0x800068, 0xa5 }, { 0x80006e, 0x01 }, - { 0x800071, 0x0A }, + { 0x800071, 0x0a }, { 0x800072, 0x02 }, { 0x800074, 0x01 }, { 0x800079, 0x01 }, @@ -354,7 +354,7 @@ static const struct reg_val tuner_init_fc0011[] = { { 0x800094, 0x00 }, { 0x800095, 0x00 }, { 0x800096, 0x00 }, - { 0x80009b, 0x2D }, + { 0x80009b, 0x2d }, { 0x80009c, 0x60 }, { 0x80009d, 0x23 }, { 0x8000a4, 0x50 }, @@ -362,39 +362,39 @@ static const struct reg_val tuner_init_fc0011[] = { { 0x8000b3, 0x01 }, { 0x8000b7, 0x88 }, { 0x8000b8, 0xa6 }, - { 0x8000c3, 0x01 }, - { 0x8000c4, 0x01 }, - { 0x8000c7, 0x69 }, - { 0x80F007, 0x00 }, - { 0x80F00A, 0x1B }, - { 0x80F00B, 0x1B }, - { 0x80F00C, 0x1B }, - { 0x80F00D, 0x1B }, - { 0x80F00E, 0xFF }, - { 0x80F00F, 0x01 }, - { 0x80F010, 0x00 }, - { 0x80F011, 0x02 }, - { 0x80F012, 0xFF }, - { 0x80F013, 0x01 }, - { 0x80F014, 0x00 }, - { 0x80F015, 0x02 }, - { 0x80F01B, 0xEF }, - { 0x80F01C, 0x01 }, - { 0x80F01D, 0x0f }, - { 0x80F01E, 0x02 }, - { 0x80F01F, 0x6E }, - { 0x80F020, 0x00 }, - { 0x80F025, 0xDE }, - { 0x80F026, 0x00 }, - { 0x80F027, 0x0A }, - { 0x80F028, 0x03 }, - { 0x80F029, 0x6E }, - { 0x80F02A, 0x00 }, - { 0x80F047, 0x00 }, - { 0x80F054, 0x00 }, - { 0x80F055, 0x00 }, - { 0x80F077, 0x01 }, - { 0x80F1E6, 0x00 }, + { 0x8000c5, 0x01 }, + { 0x8000c6, 0x01 }, + { 0x8000c9, 0x69 }, + { 0x80f007, 0x00 }, + { 0x80f00a, 0x1b }, + { 0x80f00b, 0x1b }, + { 0x80f00c, 0x1b }, + { 0x80f00d, 0x1b }, + { 0x80f00e, 0xff }, + { 0x80f00f, 0x01 }, + { 0x80f010, 0x00 }, + { 0x80f011, 0x02 }, + { 0x80f012, 0xff }, + { 0x80f013, 0x01 }, + { 0x80f014, 0x00 }, + { 0x80f015, 0x02 }, + { 0x80f01b, 0xef }, + { 0x80f01c, 0x01 }, + { 0x80f01d, 0x0f }, + { 0x80f01e, 0x02 }, + { 0x80f01f, 0x6e }, + { 0x80f020, 0x00 }, + { 0x80f025, 0xde }, + { 0x80f026, 0x00 }, + { 0x80f027, 0x0a }, + { 0x80f028, 0x03 }, + { 0x80f029, 0x6e }, + { 0x80f02a, 0x00 }, + { 0x80f047, 0x00 }, + { 0x80f054, 0x00 }, + { 0x80f055, 0x00 }, + { 0x80f077, 0x01 }, + { 0x80f1e6, 0x00 }, }; /* Fitipower FC0012 tuner init -- cgit v1.2.3 From 864c714392ee966b8d50af0f480f5855ca3b5334 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 16:07:05 -0300 Subject: [media] af9033: update fc2580 init sequence Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/af9033_priv.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/af9033_priv.h b/drivers/media/dvb-frontends/af9033_priv.h index 1fb84a230b46..e9bd78265543 100644 --- a/drivers/media/dvb-frontends/af9033_priv.h +++ b/drivers/media/dvb-frontends/af9033_priv.h @@ -525,11 +525,12 @@ static const struct reg_val tuner_init_fc2580[] = { { 0x800095, 0x00 }, { 0x800096, 0x05 }, { 0x8000b3, 0x01 }, - { 0x8000c3, 0x01 }, - { 0x8000c4, 0x00 }, + { 0x8000c5, 0x01 }, + { 0x8000c6, 0x00 }, + { 0x8000d1, 0x01 }, { 0x80f007, 0x00 }, { 0x80f00c, 0x19 }, - { 0x80f00d, 0x1A }, + { 0x80f00d, 0x1a }, { 0x80f00e, 0x00 }, { 0x80f00f, 0x02 }, { 0x80f010, 0x00 }, -- cgit v1.2.3 From ff4e3fe86f6c5e8c746f07e232a89330cd3cf1a9 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 16:25:08 -0300 Subject: [media] af9035: print warning when firmware is bad Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9035.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 68e0e80416aa..ea37b5c3c33f 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -437,6 +437,10 @@ static int af9035_download_firmware(struct dvb_usb_device *d, __func__, fw->size - i); } + /* print warn if firmware is bad, continue and see what happens */ + if (i) + dev_warn(&d->udev->dev, "%s: bad firmware\n", KBUILD_MODNAME); + /* firmware loaded, request boot */ req.cmd = CMD_FW_BOOT; ret = af9035_ctrl_msg(d, &req); -- cgit v1.2.3 From 6612545ffb3c14ccb5fa265992cc1b40db3ff463 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 5 Dec 2012 13:52:00 -0300 Subject: [media] s5p-fimc: Avoid possible NULL pointer dereference in set_fmt op This fixes following issue found with a static analysis tool: Pointer 'ffmt' returned from call to function 'fimc_capture_try_format' at line 1522 may be NULL and may be dereferenced at line 1535. Although it shouldn't happen in practice, add the NULL pointer check to be on the safe side. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-capture.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index 95e6a7820b5e..aad0850d0c01 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -1561,6 +1561,10 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, *mf = fmt->format; return 0; } + /* There must be a bug in the driver if this happens */ + if (WARN_ON(ffmt == NULL)) + return -EINVAL; + /* Update RGB Alpha control state and value range */ fimc_alpha_ctrl_update(ctx); -- cgit v1.2.3 From a8697ec8c7f3ab7331bc3210c3b89563356f8de5 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 5 Dec 2012 13:40:04 -0300 Subject: [media] s5p-fimc: Prevent potential buffer overflow Replace the hard coded csi_sensors[] array size with a relevant constant to make sure we don't iterate beyond the actual array. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-mdevice.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 8b43f982c12d..e77dba7a97b8 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -627,7 +627,7 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) */ static int fimc_md_create_links(struct fimc_md *fmd) { - struct v4l2_subdev *csi_sensors[2] = { NULL }; + struct v4l2_subdev *csi_sensors[CSIS_MAX_ENTITIES] = { NULL }; struct v4l2_subdev *sensor, *csis; struct s5p_fimc_isp_info *pdata; struct fimc_sensor_info *s_info; @@ -692,7 +692,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) pad, link_mask); } - for (i = 0; i < ARRAY_SIZE(fmd->csis); i++) { + for (i = 0; i < CSIS_MAX_ENTITIES; i++) { if (fmd->csis[i].sd == NULL) continue; source = &fmd->csis[i].sd->entity; -- cgit v1.2.3 From dc3ae328799bfc5c352174e95162dc5716e209ff Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Tue, 18 Dec 2012 05:28:40 -0300 Subject: [media] s5p-fimc: Fix incorrect usage of IS_ERR_OR_NULL Replace IS_ERR_OR_NULL with IS_ERR on clk_get results. Signed-off-by: Tony Prisk Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-mdevice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index e77dba7a97b8..25055cb43993 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -732,7 +732,7 @@ static int fimc_md_get_clocks(struct fimc_md *fmd) for (i = 0; i < FIMC_MAX_CAMCLKS; i++) { snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i); clock = clk_get(NULL, clk_name); - if (IS_ERR_OR_NULL(clock)) { + if (IS_ERR(clock)) { v4l2_err(&fmd->v4l2_dev, "Failed to get clock: %s", clk_name); return -ENXIO; -- cgit v1.2.3 From 740ad921f8a72ed76d20c88225a2fa71e8290904 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 6 Dec 2012 10:26:19 -0300 Subject: [media] s5p-fimc: Prevent AB-BA deadlock during links reconfiguration This patch patch eliminates potential AB-BA deadlock when one process calls open(), or VIDIOC_S/TRY_FMT ioctl on the FIMC capture video node, while other thread is reconfiguring media links via media device node: /dev/video? open() /dev/media? MEDIA_IOC_SETUP_LINK ioctl mutex_lock(video_lock) mutex_lock(graph_lock) fimc_pipeline_open() fimc_md_link_notify() mutex_lock(graph_lock) mutex_lock(video_lock) ... ... The deadlock is avoided by always taking the graph mutex first in video node open() or an ioctl, before the video lock is acquired. Reversed order seems impossible, since media device driver's link_notify callback is called with media graph mutex already held. To ensure proper locking order VIDIOC_S_FMT and VIDIOC_TRY_FMT ioctls are not serialized in the v4l2-core and the driver takes care of it itself. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-capture.c | 54 +++++++++++---- drivers/media/platform/s5p-fimc/fimc-lite.c | 6 +- drivers/media/platform/s5p-fimc/fimc-mdevice.c | 94 +++++++++----------------- 3 files changed, 78 insertions(+), 76 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index aad0850d0c01..18a70e4a0b0f 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -510,8 +510,8 @@ static int fimc_capture_open(struct file *file) dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state); - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; + fimc_md_graph_lock(fimc); + mutex_lock(&fimc->lock); if (fimc_m2m_active(fimc)) goto unlock; @@ -546,6 +546,7 @@ static int fimc_capture_open(struct file *file) } unlock: mutex_unlock(&fimc->lock); + fimc_md_graph_unlock(fimc); return ret; } @@ -962,6 +963,10 @@ static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, struct fimc_ctx *ctx = fimc->vid_cap.ctx; struct v4l2_mbus_framefmt mf; struct fimc_fmt *ffmt = NULL; + int ret = 0; + + fimc_md_graph_lock(fimc); + mutex_lock(&fimc->lock); if (fimc_jpeg_fourcc(pix->pixelformat)) { fimc_capture_try_format(ctx, &pix->width, &pix->height, @@ -973,16 +978,16 @@ static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, ffmt = fimc_capture_try_format(ctx, &pix->width, &pix->height, NULL, &pix->pixelformat, FIMC_SD_PAD_SOURCE); - if (!ffmt) - return -EINVAL; + if (!ffmt) { + ret = -EINVAL; + goto unlock; + } if (!fimc->vid_cap.user_subdev_api) { mf.width = pix->width; mf.height = pix->height; mf.code = ffmt->mbus_code; - fimc_md_graph_lock(fimc); fimc_pipeline_try_format(ctx, &mf, &ffmt, false); - fimc_md_graph_unlock(fimc); pix->width = mf.width; pix->height = mf.height; if (ffmt) @@ -994,8 +999,11 @@ static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, if (ffmt->flags & FMT_FLAGS_COMPRESSED) fimc_get_sensor_frame_desc(fimc->pipeline.subdevs[IDX_SENSOR], pix->plane_fmt, ffmt->memplanes, true); +unlock: + mutex_unlock(&fimc->lock); + fimc_md_graph_unlock(fimc); - return 0; + return ret; } static void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx, @@ -1012,7 +1020,8 @@ static void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx, clear_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state); } -static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f) +static int __fimc_capture_set_format(struct fimc_dev *fimc, + struct v4l2_format *f) { struct fimc_ctx *ctx = fimc->vid_cap.ctx; struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; @@ -1047,12 +1056,10 @@ static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f) mf->code = ff->fmt->mbus_code; mf->width = pix->width; mf->height = pix->height; - - fimc_md_graph_lock(fimc); ret = fimc_pipeline_try_format(ctx, mf, &s_fmt, true); - fimc_md_graph_unlock(fimc); if (ret) return ret; + pix->width = mf->width; pix->height = mf->height; } @@ -1091,8 +1098,23 @@ static int fimc_cap_s_fmt_mplane(struct file *file, void *priv, struct v4l2_format *f) { struct fimc_dev *fimc = video_drvdata(file); + int ret; + + fimc_md_graph_lock(fimc); + mutex_lock(&fimc->lock); + /* + * The graph is walked within __fimc_capture_set_format() to set + * the format at subdevs thus the graph mutex needs to be held at + * this point and acquired before the video mutex, to avoid AB-BA + * deadlock when fimc_md_link_notify() is called by other thread. + * Ideally the graph walking and setting format at the whole pipeline + * should be removed from this driver and handled in userspace only. + */ + ret = __fimc_capture_set_format(fimc, f); - return fimc_capture_set_format(fimc, f); + mutex_unlock(&fimc->lock); + fimc_md_graph_unlock(fimc); + return ret; } static int fimc_cap_enum_input(struct file *file, void *priv, @@ -1727,7 +1749,7 @@ static int fimc_capture_set_default_format(struct fimc_dev *fimc) }, }; - return fimc_capture_set_format(fimc, &fmt); + return __fimc_capture_set_format(fimc, &fmt); } /* fimc->lock must be already initialized */ @@ -1789,6 +1811,12 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, ret = media_entity_init(&vfd->entity, 1, &vid_cap->vd_pad, 0); if (ret) goto err_ent; + /* + * For proper order of acquiring/releasing the video + * and the graph mutex. + */ + v4l2_disable_ioctl_locking(vfd, VIDIOC_TRY_FMT); + v4l2_disable_ioctl_locking(vfd, VIDIOC_S_FMT); ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); if (ret) diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/s5p-fimc/fimc-lite.c index 765b8e4cbf4e..ef31c3919fe9 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite.c @@ -459,11 +459,12 @@ static void fimc_lite_clear_event_counters(struct fimc_lite *fimc) static int fimc_lite_open(struct file *file) { struct fimc_lite *fimc = video_drvdata(file); + struct media_entity *me = &fimc->vfd.entity; int ret; - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; + mutex_lock(&me->parent->graph_mutex); + mutex_lock(&fimc->lock); if (fimc->out_path != FIMC_IO_DMA) { ret = -EBUSY; goto done; @@ -492,6 +493,7 @@ static int fimc_lite_open(struct file *file) } done: mutex_unlock(&fimc->lock); + mutex_unlock(&me->parent->graph_mutex); return ret; } diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 25055cb43993..62f3a71c4f6d 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -142,7 +142,7 @@ static int fimc_pipeline_s_power(struct fimc_pipeline *p, bool state) * @me: media entity to start graph walk with * @prep: true to acquire sensor (and csis) subdevs * - * This function must be called with the graph mutex held. + * Called with the graph mutex held. */ static int __fimc_pipeline_open(struct fimc_pipeline *p, struct media_entity *me, bool prep) @@ -162,30 +162,19 @@ static int __fimc_pipeline_open(struct fimc_pipeline *p, return fimc_pipeline_s_power(p, 1); } -static int fimc_pipeline_open(struct fimc_pipeline *p, - struct media_entity *me, bool prep) -{ - int ret; - - mutex_lock(&me->parent->graph_mutex); - ret = __fimc_pipeline_open(p, me, prep); - mutex_unlock(&me->parent->graph_mutex); - - return ret; -} - /** * __fimc_pipeline_close - disable the sensor clock and pipeline power * @fimc: fimc device terminating the pipeline * - * Disable power of all subdevs in the pipeline and turn off the external - * sensor clock. - * Called with the graph mutex held. + * Disable power of all subdevs and turn the external sensor clock off. */ static int __fimc_pipeline_close(struct fimc_pipeline *p) { int ret = 0; + if (!p || !p->subdevs[IDX_SENSOR]) + return -EINVAL; + if (p->subdevs[IDX_SENSOR]) { ret = fimc_pipeline_s_power(p, 0); fimc_md_set_camclk(p->subdevs[IDX_SENSOR], false); @@ -193,28 +182,12 @@ static int __fimc_pipeline_close(struct fimc_pipeline *p) return ret == -ENXIO ? 0 : ret; } -static int fimc_pipeline_close(struct fimc_pipeline *p) -{ - struct media_entity *me; - int ret; - - if (!p || !p->subdevs[IDX_SENSOR]) - return -EINVAL; - - me = &p->subdevs[IDX_SENSOR]->entity; - mutex_lock(&me->parent->graph_mutex); - ret = __fimc_pipeline_close(p); - mutex_unlock(&me->parent->graph_mutex); - - return ret; -} - /** - * fimc_pipeline_s_stream - invoke s_stream on pipeline subdevs + * __fimc_pipeline_s_stream - invoke s_stream on pipeline subdevs * @pipeline: video pipeline structure * @on: passed as the s_stream call argument */ -static int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on) +static int __fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on) { int i, ret; @@ -236,9 +209,9 @@ static int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on) /* Media pipeline operations for the FIMC/FIMC-LITE video device driver */ static const struct fimc_pipeline_ops fimc_pipeline_ops = { - .open = fimc_pipeline_open, - .close = fimc_pipeline_close, - .set_stream = fimc_pipeline_s_stream, + .open = __fimc_pipeline_open, + .close = __fimc_pipeline_close, + .set_stream = __fimc_pipeline_s_stream, }; /* @@ -822,7 +795,9 @@ static int fimc_md_link_notify(struct media_pad *source, struct fimc_dev *fimc = NULL; struct fimc_pipeline *pipeline; struct v4l2_subdev *sd; + struct mutex *lock; int ret = 0; + int ref_count; if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV) return 0; @@ -832,26 +807,31 @@ static int fimc_md_link_notify(struct media_pad *source, switch (sd->grp_id) { case GRP_ID_FLITE: fimc_lite = v4l2_get_subdevdata(sd); + if (WARN_ON(fimc_lite == NULL)) + return 0; pipeline = &fimc_lite->pipeline; + lock = &fimc_lite->lock; break; case GRP_ID_FIMC: fimc = v4l2_get_subdevdata(sd); + if (WARN_ON(fimc == NULL)) + return 0; pipeline = &fimc->pipeline; + lock = &fimc->lock; break; default: return 0; } if (!(flags & MEDIA_LNK_FL_ENABLED)) { + int i; + mutex_lock(lock); ret = __fimc_pipeline_close(pipeline); - pipeline->subdevs[IDX_SENSOR] = NULL; - pipeline->subdevs[IDX_CSIS] = NULL; - - if (fimc) { - mutex_lock(&fimc->lock); + for (i = 0; i < IDX_MAX; i++) + pipeline->subdevs[i] = NULL; + if (fimc) fimc_ctrls_delete(fimc->vid_cap.ctx); - mutex_unlock(&fimc->lock); - } + mutex_unlock(lock); return ret; } /* @@ -859,23 +839,15 @@ static int fimc_md_link_notify(struct media_pad *source, * pipeline is already in use, i.e. its video node is opened. * Recreate the controls destroyed during the link deactivation. */ - if (fimc) { - mutex_lock(&fimc->lock); - if (fimc->vid_cap.refcnt > 0) { - ret = __fimc_pipeline_open(pipeline, - source->entity, true); - if (!ret) - ret = fimc_capture_ctrls_create(fimc); - } - mutex_unlock(&fimc->lock); - } else { - mutex_lock(&fimc_lite->lock); - if (fimc_lite->ref_count > 0) { - ret = __fimc_pipeline_open(pipeline, - source->entity, true); - } - mutex_unlock(&fimc_lite->lock); - } + mutex_lock(lock); + + ref_count = fimc ? fimc->vid_cap.refcnt : fimc_lite->ref_count; + if (ref_count > 0) + ret = __fimc_pipeline_open(pipeline, source->entity, true); + if (!ret && fimc) + ret = fimc_capture_ctrls_create(fimc); + + mutex_unlock(lock); return ret ? -EPIPE : ret; } -- cgit v1.2.3 From cf48f56c27ecfe94fbea363db6e8e0bacbd525ed Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 24 Sep 2012 06:00:37 -0300 Subject: [media] s5p-tv: Fix return value in sdo_probe() on error paths Use proper return value test for clk_get() and devm_regulator_get() functions and propagate any errors from the clock and the regulator subsystem to the driver core. In two cases a proper error code is now returned rather than 0. Reported-by: Peter Senna Tschudin Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/sdo_drv.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-tv/sdo_drv.c b/drivers/media/platform/s5p-tv/sdo_drv.c index ad68bbed014e..2d1a65488f2d 100644 --- a/drivers/media/platform/s5p-tv/sdo_drv.c +++ b/drivers/media/platform/s5p-tv/sdo_drv.c @@ -341,47 +341,50 @@ static int __devinit sdo_probe(struct platform_device *pdev) /* acquire clocks */ sdev->sclk_dac = clk_get(dev, "sclk_dac"); - if (IS_ERR_OR_NULL(sdev->sclk_dac)) { + if (IS_ERR(sdev->sclk_dac)) { dev_err(dev, "failed to get clock 'sclk_dac'\n"); - ret = -ENXIO; + ret = PTR_ERR(sdev->sclk_dac); goto fail; } sdev->dac = clk_get(dev, "dac"); - if (IS_ERR_OR_NULL(sdev->dac)) { + if (IS_ERR(sdev->dac)) { dev_err(dev, "failed to get clock 'dac'\n"); - ret = -ENXIO; + ret = PTR_ERR(sdev->dac); goto fail_sclk_dac; } sdev->dacphy = clk_get(dev, "dacphy"); - if (IS_ERR_OR_NULL(sdev->dacphy)) { + if (IS_ERR(sdev->dacphy)) { dev_err(dev, "failed to get clock 'dacphy'\n"); - ret = -ENXIO; + ret = PTR_ERR(sdev->dacphy); goto fail_dac; } sclk_vpll = clk_get(dev, "sclk_vpll"); - if (IS_ERR_OR_NULL(sclk_vpll)) { + if (IS_ERR(sclk_vpll)) { dev_err(dev, "failed to get clock 'sclk_vpll'\n"); - ret = -ENXIO; + ret = PTR_ERR(sclk_vpll); goto fail_dacphy; } clk_set_parent(sdev->sclk_dac, sclk_vpll); clk_put(sclk_vpll); sdev->fout_vpll = clk_get(dev, "fout_vpll"); - if (IS_ERR_OR_NULL(sdev->fout_vpll)) { + if (IS_ERR(sdev->fout_vpll)) { dev_err(dev, "failed to get clock 'fout_vpll'\n"); + ret = PTR_ERR(sdev->fout_vpll); goto fail_dacphy; } dev_info(dev, "fout_vpll.rate = %lu\n", clk_get_rate(sclk_vpll)); /* acquire regulator */ sdev->vdac = devm_regulator_get(dev, "vdd33a_dac"); - if (IS_ERR_OR_NULL(sdev->vdac)) { + if (IS_ERR(sdev->vdac)) { dev_err(dev, "failed to get regulator 'vdac'\n"); + ret = PTR_ERR(sdev->vdac); goto fail_fout_vpll; } sdev->vdet = devm_regulator_get(dev, "vdet"); - if (IS_ERR_OR_NULL(sdev->vdet)) { + if (IS_ERR(sdev->vdet)) { dev_err(dev, "failed to get regulator 'vdet'\n"); + ret = PTR_ERR(sdev->vdet); goto fail_fout_vpll; } -- cgit v1.2.3 From aeae3db1c267759661609c76d6c7791231ae438a Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Tue, 18 Dec 2012 04:28:39 -0300 Subject: [media] s5p-tv: Fix incorrect usage of IS_ERR_OR_NULL Replace IS_ERR_OR_NULL with IS_ERR on clk_get results. Signed-off-by: Tony Prisk Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/hdmi_drv.c | 10 +++++----- drivers/media/platform/s5p-tv/mixer_drv.c | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c index 8a9cf43018f6..1c48ca5e419f 100644 --- a/drivers/media/platform/s5p-tv/hdmi_drv.c +++ b/drivers/media/platform/s5p-tv/hdmi_drv.c @@ -781,27 +781,27 @@ static int hdmi_resources_init(struct hdmi_device *hdev) /* get clocks, power */ res->hdmi = clk_get(dev, "hdmi"); - if (IS_ERR_OR_NULL(res->hdmi)) { + if (IS_ERR(res->hdmi)) { dev_err(dev, "failed to get clock 'hdmi'\n"); goto fail; } res->sclk_hdmi = clk_get(dev, "sclk_hdmi"); - if (IS_ERR_OR_NULL(res->sclk_hdmi)) { + if (IS_ERR(res->sclk_hdmi)) { dev_err(dev, "failed to get clock 'sclk_hdmi'\n"); goto fail; } res->sclk_pixel = clk_get(dev, "sclk_pixel"); - if (IS_ERR_OR_NULL(res->sclk_pixel)) { + if (IS_ERR(res->sclk_pixel)) { dev_err(dev, "failed to get clock 'sclk_pixel'\n"); goto fail; } res->sclk_hdmiphy = clk_get(dev, "sclk_hdmiphy"); - if (IS_ERR_OR_NULL(res->sclk_hdmiphy)) { + if (IS_ERR(res->sclk_hdmiphy)) { dev_err(dev, "failed to get clock 'sclk_hdmiphy'\n"); goto fail; } res->hdmiphy = clk_get(dev, "hdmiphy"); - if (IS_ERR_OR_NULL(res->hdmiphy)) { + if (IS_ERR(res->hdmiphy)) { dev_err(dev, "failed to get clock 'hdmiphy'\n"); goto fail; } diff --git a/drivers/media/platform/s5p-tv/mixer_drv.c b/drivers/media/platform/s5p-tv/mixer_drv.c index ca0f29717448..c1b2e0ed20d2 100644 --- a/drivers/media/platform/s5p-tv/mixer_drv.c +++ b/drivers/media/platform/s5p-tv/mixer_drv.c @@ -240,27 +240,27 @@ static int mxr_acquire_clocks(struct mxr_device *mdev) struct device *dev = mdev->dev; res->mixer = clk_get(dev, "mixer"); - if (IS_ERR_OR_NULL(res->mixer)) { + if (IS_ERR(res->mixer)) { mxr_err(mdev, "failed to get clock 'mixer'\n"); goto fail; } res->vp = clk_get(dev, "vp"); - if (IS_ERR_OR_NULL(res->vp)) { + if (IS_ERR(res->vp)) { mxr_err(mdev, "failed to get clock 'vp'\n"); goto fail; } res->sclk_mixer = clk_get(dev, "sclk_mixer"); - if (IS_ERR_OR_NULL(res->sclk_mixer)) { + if (IS_ERR(res->sclk_mixer)) { mxr_err(mdev, "failed to get clock 'sclk_mixer'\n"); goto fail; } res->sclk_hdmi = clk_get(dev, "sclk_hdmi"); - if (IS_ERR_OR_NULL(res->sclk_hdmi)) { + if (IS_ERR(res->sclk_hdmi)) { mxr_err(mdev, "failed to get clock 'sclk_hdmi'\n"); goto fail; } res->sclk_dac = clk_get(dev, "sclk_dac"); - if (IS_ERR_OR_NULL(res->sclk_dac)) { + if (IS_ERR(res->sclk_dac)) { mxr_err(mdev, "failed to get clock 'sclk_dac'\n"); goto fail; } -- cgit v1.2.3 From 80f0dee21c7ec39f76f90548c9f08a0e7ec9b3fa Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 26 Nov 2012 01:49:00 -0300 Subject: [media] s5p-tv: Add missing braces around sizeof in sdo_drv.c Silences the following checkpatch warnings: WARNING: sizeof *sdev should be sizeof(*sdev) FILE: media/platform/s5p-tv/sdo_drv.c:304: sdev = devm_kzalloc(&pdev->dev, sizeof *sdev, GFP_KERNEL); WARNING: sizeof sdev->sd.name should be sizeof(sdev->sd.name) FILE: media/platform/s5p-tv/sdo_drv.c:394: strlcpy(sdev->sd.name, "s5p-sdo", sizeof sdev->sd.name); Signed-off-by: Sachin Kamat Acked-by: Tomasz Stanislawski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/sdo_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-tv/sdo_drv.c b/drivers/media/platform/s5p-tv/sdo_drv.c index 2d1a65488f2d..35320f08c399 100644 --- a/drivers/media/platform/s5p-tv/sdo_drv.c +++ b/drivers/media/platform/s5p-tv/sdo_drv.c @@ -301,7 +301,7 @@ static int __devinit sdo_probe(struct platform_device *pdev) struct clk *sclk_vpll; dev_info(dev, "probe start\n"); - sdev = devm_kzalloc(&pdev->dev, sizeof *sdev, GFP_KERNEL); + sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL); if (!sdev) { dev_err(dev, "not enough memory.\n"); ret = -ENOMEM; @@ -397,7 +397,7 @@ static int __devinit sdo_probe(struct platform_device *pdev) /* configuration of interface subdevice */ v4l2_subdev_init(&sdev->sd, &sdo_sd_ops); sdev->sd.owner = THIS_MODULE; - strlcpy(sdev->sd.name, "s5p-sdo", sizeof sdev->sd.name); + strlcpy(sdev->sd.name, "s5p-sdo", sizeof(sdev->sd.name)); /* set default format */ sdev->fmt = sdo_find_format(SDO_DEFAULT_STD); -- cgit v1.2.3 From c0d5120429959de122ddab338fbac838a053387a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 26 Nov 2012 01:49:01 -0300 Subject: [media] s5p-tv: Add missing braces around sizeof in mixer_video.c Silences several checkpatch warnings of the type: WARNING: sizeof *out should be sizeof(*out) FILE: media/platform/s5p-tv/mixer_video.c:98: out = kzalloc(sizeof *out, GFP_KERNEL); Signed-off-by: Sachin Kamat Acked-by: Tomasz Stanislawski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/mixer_video.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index 7379e77bf4e1..80ca14b7c4d3 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -95,7 +95,7 @@ int __devinit mxr_acquire_video(struct mxr_device *mdev, /* trying to register next output */ if (sd == NULL) continue; - out = kzalloc(sizeof *out, GFP_KERNEL); + out = kzalloc(sizeof(*out), GFP_KERNEL); if (out == NULL) { mxr_err(mdev, "no memory for '%s'\n", conf->output_name); @@ -127,7 +127,7 @@ fail_output: /* kfree is NULL-safe */ for (i = 0; i < mdev->output_cnt; ++i) kfree(mdev->output[i]); - memset(mdev->output, 0, sizeof mdev->output); + memset(mdev->output, 0, sizeof(mdev->output)); fail_vb2_allocator: /* freeing allocator context */ @@ -160,8 +160,8 @@ static int mxr_querycap(struct file *file, void *priv, mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - strlcpy(cap->driver, MXR_DRIVER_NAME, sizeof cap->driver); - strlcpy(cap->card, layer->vfd.name, sizeof cap->card); + strlcpy(cap->driver, MXR_DRIVER_NAME, sizeof(cap->driver)); + strlcpy(cap->card, layer->vfd.name, sizeof(cap->card)); sprintf(cap->bus_info, "%d", layer->idx); cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT_MPLANE; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; @@ -192,7 +192,7 @@ static void mxr_layer_default_geo(struct mxr_layer *layer) struct mxr_device *mdev = layer->mdev; struct v4l2_mbus_framefmt mbus_fmt; - memset(&layer->geo, 0, sizeof layer->geo); + memset(&layer->geo, 0, sizeof(layer->geo)); mxr_get_mbus_fmt(mdev, &mbus_fmt); @@ -425,7 +425,7 @@ static int mxr_s_selection(struct file *file, void *fh, struct mxr_geometry tmp; struct v4l2_rect res; - memset(&res, 0, sizeof res); + memset(&res, 0, sizeof(res)); mxr_dbg(layer->mdev, "%s: rect: %dx%d@%d,%d\n", __func__, s->r.width, s->r.height, s->r.left, s->r.top); @@ -464,7 +464,7 @@ static int mxr_s_selection(struct file *file, void *fh, /* apply change and update geometry if needed */ if (target) { /* backup current geometry if setup fails */ - memcpy(&tmp, geo, sizeof tmp); + memcpy(&tmp, geo, sizeof(tmp)); /* apply requested selection */ target->x_offset = s->r.left; @@ -496,7 +496,7 @@ static int mxr_s_selection(struct file *file, void *fh, fail: /* restore old geometry, which is not touched if target is NULL */ if (target) - memcpy(geo, &tmp, sizeof tmp); + memcpy(geo, &tmp, sizeof(tmp)); return -ERANGE; } @@ -1071,7 +1071,7 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, { struct mxr_layer *layer; - layer = kzalloc(sizeof *layer, GFP_KERNEL); + layer = kzalloc(sizeof(*layer), GFP_KERNEL); if (layer == NULL) { mxr_err(mdev, "not enough memory for layer.\n"); goto fail; -- cgit v1.2.3 From ad84291c6af11e07d8c55eb4ea4d64ade054e2a5 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 26 Nov 2012 01:49:02 -0300 Subject: [media] s5p-tv: Add missing braces around sizeof in mixer_reg.c Silences checkpatch warnings of the type: WARNING: sizeof filter_y_horiz_tap8 should be sizeof(filter_y_horiz_tap8) FILE: media/platform/s5p-tv/mixer_reg.c:473: filter_y_horiz_tap8, sizeof filter_y_horiz_tap8); Signed-off-by: Sachin Kamat Acked-by: Tomasz Stanislawski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/mixer_reg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-tv/mixer_reg.c b/drivers/media/platform/s5p-tv/mixer_reg.c index 3b1670a045f4..b713403024ef 100644 --- a/drivers/media/platform/s5p-tv/mixer_reg.c +++ b/drivers/media/platform/s5p-tv/mixer_reg.c @@ -470,11 +470,11 @@ static inline void mxr_reg_vp_filter_set(struct mxr_device *mdev, static void mxr_reg_vp_default_filter(struct mxr_device *mdev) { mxr_reg_vp_filter_set(mdev, VP_POLY8_Y0_LL, - filter_y_horiz_tap8, sizeof filter_y_horiz_tap8); + filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8)); mxr_reg_vp_filter_set(mdev, VP_POLY4_Y0_LL, - filter_y_vert_tap4, sizeof filter_y_vert_tap4); + filter_y_vert_tap4, sizeof(filter_y_vert_tap4)); mxr_reg_vp_filter_set(mdev, VP_POLY4_C0_LL, - filter_cr_horiz_tap4, sizeof filter_cr_horiz_tap4); + filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4)); } static void mxr_reg_mxr_dump(struct mxr_device *mdev) -- cgit v1.2.3 From dc03398528c83357e1e95481cd447f6bf1036b76 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 26 Nov 2012 01:49:03 -0300 Subject: [media] s5p-tv: Add missing braces around sizeof in mixer_drv.c Silences checkpatch warnings of type: WARNING: sizeof mdev->res should be sizeof(mdev->res) FILE: media/platform/s5p-tv/mixer_drv.c:301: memset(&mdev->res, 0, sizeof mdev->res); WARNING: sizeof *mdev should be sizeof(*mdev) FILE: media/platform/s5p-tv/mixer_drv.c:385: mdev = kzalloc(sizeof *mdev, GFP_KERNEL); Signed-off-by: Sachin Kamat Acked-by: Tomasz Stanislawski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/mixer_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-tv/mixer_drv.c b/drivers/media/platform/s5p-tv/mixer_drv.c index c1b2e0ed20d2..d5cf603551b2 100644 --- a/drivers/media/platform/s5p-tv/mixer_drv.c +++ b/drivers/media/platform/s5p-tv/mixer_drv.c @@ -298,7 +298,7 @@ static void mxr_release_resources(struct mxr_device *mdev) { mxr_release_clocks(mdev); mxr_release_plat_resources(mdev); - memset(&mdev->res, 0, sizeof mdev->res); + memset(&mdev->res, 0, sizeof(mdev->res)); } static void mxr_release_layers(struct mxr_device *mdev) @@ -382,7 +382,7 @@ static int __devinit mxr_probe(struct platform_device *pdev) /* mdev does not exist yet so no mxr_dbg is used */ dev_info(dev, "probe start\n"); - mdev = kzalloc(sizeof *mdev, GFP_KERNEL); + mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); if (!mdev) { dev_err(dev, "not enough memory.\n"); ret = -ENOMEM; -- cgit v1.2.3 From 45b56d572cd8b4f118ed3a006aa33527fd966389 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 26 Nov 2012 01:49:04 -0300 Subject: [media] s5p-tv: Add missing braces around sizeof in hdmiphy_drv.c Fixes the following checkpatch warning: WARNING: sizeof *ctx should be sizeof(*ctx) FILE: media/platform/s5p-tv/hdmiphy_drv.c:287: ctx = kzalloc(sizeof *ctx, GFP_KERNEL); Signed-off-by: Sachin Kamat Acked-by: Tomasz Stanislawski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/hdmiphy_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-tv/hdmiphy_drv.c b/drivers/media/platform/s5p-tv/hdmiphy_drv.c index f67b38631801..94c2a13c3b3a 100644 --- a/drivers/media/platform/s5p-tv/hdmiphy_drv.c +++ b/drivers/media/platform/s5p-tv/hdmiphy_drv.c @@ -284,7 +284,7 @@ static int __devinit hdmiphy_probe(struct i2c_client *client, { struct hdmiphy_ctx *ctx; - ctx = kzalloc(sizeof *ctx, GFP_KERNEL); + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; -- cgit v1.2.3 From d322bb915425b65a51c48b90e912af55762cc745 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 26 Nov 2012 01:49:05 -0300 Subject: [media] s5p-tv: Add missing braces around sizeof in hdmi_drv.c Fixes the following checkpatch warnings: WARNING: sizeof *fmt should be sizeof(*fmt) WARNING: sizeof *res should be sizeof(*res) WARNING: sizeof *res should be sizeof(*res) WARNING: sizeof sd->name should be sizeof(sd->name) Signed-off-by: Sachin Kamat Acked-by: Tomasz Stanislawski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/hdmi_drv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c index 1c48ca5e419f..c0d0f84e5b86 100644 --- a/drivers/media/platform/s5p-tv/hdmi_drv.c +++ b/drivers/media/platform/s5p-tv/hdmi_drv.c @@ -656,7 +656,7 @@ static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd, dev_dbg(hdev->dev, "%s\n", __func__); if (!hdev->cur_conf) return -EINVAL; - memset(fmt, 0, sizeof *fmt); + memset(fmt, 0, sizeof(*fmt)); fmt->width = t->hact.end - t->hact.beg; fmt->height = t->vact[0].end - t->vact[0].beg; fmt->code = V4L2_MBUS_FMT_FIXED; /* means RGB888 */ @@ -760,7 +760,7 @@ static void hdmi_resources_cleanup(struct hdmi_device *hdev) clk_put(res->sclk_hdmi); if (!IS_ERR_OR_NULL(res->hdmi)) clk_put(res->hdmi); - memset(res, 0, sizeof *res); + memset(res, 0, sizeof(*res)); } static int hdmi_resources_init(struct hdmi_device *hdev) @@ -777,7 +777,7 @@ static int hdmi_resources_init(struct hdmi_device *hdev) dev_dbg(dev, "HDMI resource init\n"); - memset(res, 0, sizeof *res); + memset(res, 0, sizeof(*res)); /* get clocks, power */ res->hdmi = clk_get(dev, "hdmi"); @@ -955,7 +955,7 @@ static int __devinit hdmi_probe(struct platform_device *pdev) v4l2_subdev_init(sd, &hdmi_sd_ops); sd->owner = THIS_MODULE; - strlcpy(sd->name, "s5p-hdmi", sizeof sd->name); + strlcpy(sd->name, "s5p-hdmi", sizeof(sd->name)); hdmi_dev->cur_preset = HDMI_DEFAULT_PRESET; /* FIXME: missing fail preset is not supported */ hdmi_dev->cur_conf = hdmi_preset2timings(hdmi_dev->cur_preset); -- cgit v1.2.3 From 15514fb567728b236dfbedeb360f55791302bf6e Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Tue, 18 Dec 2012 05:28:41 -0300 Subject: [media] s5p-g2d: Fix incorrect usage of IS_ERR_OR_NULL Replace IS_ERR_OR_NULL with IS_ERR on clk_get results. Signed-off-by: Tony Prisk Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-g2d/g2d.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 1bfbc325836b..dcd53357704d 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -715,7 +715,7 @@ static int g2d_probe(struct platform_device *pdev) } dev->clk = clk_get(&pdev->dev, "sclk_fimg2d"); - if (IS_ERR_OR_NULL(dev->clk)) { + if (IS_ERR(dev->clk)) { dev_err(&pdev->dev, "failed to get g2d clock\n"); return -ENXIO; } @@ -727,7 +727,7 @@ static int g2d_probe(struct platform_device *pdev) } dev->gate = clk_get(&pdev->dev, "fimg2d"); - if (IS_ERR_OR_NULL(dev->gate)) { + if (IS_ERR(dev->gate)) { dev_err(&pdev->dev, "failed to get g2d clock gate\n"); ret = -ENXIO; goto unprep_clk; -- cgit v1.2.3 From 371a664eea4e2c0d2acc1df082f7e08693506f89 Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Fri, 7 Dec 2012 08:28:55 -0300 Subject: [media] exynos-gsc: Support dmabuf export buffer This patch adds the dmabuf export buffer feature to the Exynos G-Scaler driver. Signed-off-by: Shaik Ameer Basha Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-m2m.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index 0d06d6c6f373..386c0a7a3a52 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -373,6 +373,13 @@ static int gsc_m2m_reqbufs(struct file *file, void *fh, return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); } +static int gsc_m2m_expbuf(struct file *file, void *fh, + struct v4l2_exportbuffer *eb) +{ + struct gsc_ctx *ctx = fh_to_ctx(fh); + return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb); +} + static int gsc_m2m_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) { @@ -554,6 +561,7 @@ static const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = { .vidioc_s_fmt_vid_cap_mplane = gsc_m2m_s_fmt_mplane, .vidioc_s_fmt_vid_out_mplane = gsc_m2m_s_fmt_mplane, .vidioc_reqbufs = gsc_m2m_reqbufs, + .vidioc_expbuf = gsc_m2m_expbuf, .vidioc_querybuf = gsc_m2m_querybuf, .vidioc_qbuf = gsc_m2m_qbuf, .vidioc_dqbuf = gsc_m2m_dqbuf, @@ -571,7 +579,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, memset(src_vq, 0, sizeof(*src_vq)); src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - src_vq->io_modes = VB2_MMAP | VB2_USERPTR; + src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; src_vq->drv_priv = ctx; src_vq->ops = &gsc_m2m_qops; src_vq->mem_ops = &vb2_dma_contig_memops; @@ -583,7 +591,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, memset(dst_vq, 0, sizeof(*dst_vq)); dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; dst_vq->drv_priv = ctx; dst_vq->ops = &gsc_m2m_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; -- cgit v1.2.3 From b27a23be0d1de44ab2cc01495b4f9149f4f762d5 Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Thu, 25 Oct 2012 05:24:14 -0300 Subject: [media] s5p-mfc: Add device tree support This patch will add the device tree support for MFC driver. Signed-off-by: Arun Kumar K Acked-by: Kamil Debski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 100 ++++++++++++++++++++++++++----- 1 file changed, 84 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 3afe879d54d7..e2fe64efb756 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "s5p_mfc_common.h" #include "s5p_mfc_ctrl.h" @@ -1027,6 +1028,8 @@ static int match_child(struct device *dev, void *data) return !strcmp(dev_name(dev), (char *)data); } +static void *mfc_get_drv_data(struct platform_device *pdev); + /* MFC probe function */ static int s5p_mfc_probe(struct platform_device *pdev) { @@ -1034,6 +1037,7 @@ static int s5p_mfc_probe(struct platform_device *pdev) struct video_device *vfd; struct resource *res; int ret; + unsigned int mem_info[2]; pr_debug("%s++\n", __func__); dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); @@ -1050,8 +1054,7 @@ static int s5p_mfc_probe(struct platform_device *pdev) return -ENODEV; } - dev->variant = (struct s5p_mfc_variant *) - platform_get_device_id(pdev)->driver_data; + dev->variant = mfc_get_drv_data(pdev); ret = s5p_mfc_init_pm(dev); if (ret < 0) { @@ -1081,20 +1084,55 @@ static int s5p_mfc_probe(struct platform_device *pdev) goto err_res; } - dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, "s5p-mfc-l", - match_child); - if (!dev->mem_dev_l) { - mfc_err("Mem child (L) device get failed\n"); - ret = -ENODEV; - goto err_res; - } + if (pdev->dev.of_node) { + dev->mem_dev_l = kzalloc(sizeof(struct device), GFP_KERNEL); + if (!dev->mem_dev_l) { + mfc_err("Not enough memory\n"); + ret = -ENOMEM; + goto err_res; + } + of_property_read_u32_array(pdev->dev.of_node, "samsung,mfc-l", + mem_info, 2); + if (dma_declare_coherent_memory(dev->mem_dev_l, mem_info[0], + mem_info[0], mem_info[1], + DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { + mfc_err("Failed to declare coherent memory for\n" + "MFC device\n"); + ret = -ENOMEM; + goto err_res; + } - dev->mem_dev_r = device_find_child(&dev->plat_dev->dev, "s5p-mfc-r", - match_child); - if (!dev->mem_dev_r) { - mfc_err("Mem child (R) device get failed\n"); - ret = -ENODEV; - goto err_res; + dev->mem_dev_r = kzalloc(sizeof(struct device), GFP_KERNEL); + if (!dev->mem_dev_r) { + mfc_err("Not enough memory\n"); + ret = -ENOMEM; + goto err_res; + } + of_property_read_u32_array(pdev->dev.of_node, "samsung,mfc-r", + mem_info, 2); + if (dma_declare_coherent_memory(dev->mem_dev_r, mem_info[0], + mem_info[0], mem_info[1], + DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { + pr_err("Failed to declare coherent memory for\n" + "MFC device\n"); + ret = -ENOMEM; + goto err_res; + } + } else { + dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, + "s5p-mfc-l", match_child); + if (!dev->mem_dev_l) { + mfc_err("Mem child (L) device get failed\n"); + ret = -ENODEV; + goto err_res; + } + dev->mem_dev_r = device_find_child(&dev->plat_dev->dev, + "s5p-mfc-r", match_child); + if (!dev->mem_dev_r) { + mfc_err("Mem child (R) device get failed\n"); + ret = -ENODEV; + goto err_res; + } } dev->alloc_ctx[0] = vb2_dma_contig_init_ctx(dev->mem_dev_l); @@ -1366,6 +1404,35 @@ static struct platform_device_id mfc_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, mfc_driver_ids); +static const struct of_device_id exynos_mfc_match[] = { + { + .compatible = "samsung,mfc-v5", + .data = &mfc_drvdata_v5, + }, { + .compatible = "samsung,mfc-v6", + .data = &mfc_drvdata_v6, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_mfc_match); + +static void *mfc_get_drv_data(struct platform_device *pdev) +{ + struct s5p_mfc_variant *driver_data = NULL; + + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(of_match_ptr(exynos_mfc_match), + pdev->dev.of_node); + if (match) + driver_data = (struct s5p_mfc_variant *)match->data; + } else { + driver_data = (struct s5p_mfc_variant *) + platform_get_device_id(pdev)->driver_data; + } + return driver_data; +} + static struct platform_driver s5p_mfc_driver = { .probe = s5p_mfc_probe, .remove = __devexit_p(s5p_mfc_remove), @@ -1373,7 +1440,8 @@ static struct platform_driver s5p_mfc_driver = { .driver = { .name = S5P_MFC_NAME, .owner = THIS_MODULE, - .pm = &s5p_mfc_pm_ops + .pm = &s5p_mfc_pm_ops, + .of_match_table = exynos_mfc_match, }, }; -- cgit v1.2.3 From 20fe1cf081ae861e66611b3a8f289fabd8d56a8f Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 2 Dec 2012 08:17:08 -0300 Subject: [media] s5p-mfc: remove unused variable The variable index is initialized but never used otherwise, so remove the unused variable. Signed-off-by: Wei Yongjun Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 5 ----- drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c | 6 ------ 2 files changed, 11 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index e2fe64efb756..a49d0e5db93a 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -274,7 +274,6 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err) struct s5p_mfc_buf *dst_buf; size_t dspl_y_addr; unsigned int frame_type; - unsigned int index; dspl_y_addr = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_y_adr, dev); frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_dec_frame_type, dev); @@ -311,7 +310,6 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err) vb2_buffer_done(dst_buf->b, err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); - index = dst_buf->b->v4l2_buf.index; break; } } @@ -327,8 +325,6 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx, unsigned long flags; unsigned int res_change; - unsigned int index; - dst_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev) & S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK; res_change = (s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev) @@ -388,7 +384,6 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx, mfc_debug(2, "Running again the same buffer\n"); ctx->after_packed_pb = 1; } else { - index = src_buf->b->v4l2_buf.index; mfc_debug(2, "MFC needs next buffer\n"); ctx->consumed_stream = 0; list_del(&src_buf->list); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index 3a8cfd9fc1bd..bf4d2f44f009 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -1408,7 +1408,6 @@ static inline int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx) struct s5p_mfc_buf *temp_vb; unsigned long flags; int last_frame = 0; - unsigned int index; spin_lock_irqsave(&dev->irqlock, flags); @@ -1427,8 +1426,6 @@ static inline int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx) temp_vb->b->v4l2_planes[0].bytesused); spin_unlock_irqrestore(&dev->irqlock, flags); - index = temp_vb->b->v4l2_buf.index; - dev->curr_ctx = ctx->num; s5p_mfc_clean_ctx_int_flags(ctx); if (temp_vb->b->v4l2_planes[0].bytesused == 0) { @@ -1452,7 +1449,6 @@ static inline int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) unsigned int src_y_size, src_c_size; */ unsigned int dst_size; - unsigned int index; spin_lock_irqsave(&dev->irqlock, flags); @@ -1487,8 +1483,6 @@ static inline int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) spin_unlock_irqrestore(&dev->irqlock, flags); - index = src_mb->b->v4l2_buf.index; - dev->curr_ctx = ctx->num; s5p_mfc_clean_ctx_int_flags(ctx); s5p_mfc_encode_one_frame_v6(ctx); -- cgit v1.2.3 From 8f23cc0222a9fe9c43f679dcb3d38604b30cf7c8 Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Thu, 22 Nov 2012 06:15:55 -0300 Subject: [media] s5p-mfc: Flush DPB buffers during stream off Flushing of delay DPB buffers have to be done during stream off. In MFC v6, it is done with a risc to host command. Signed-off-by: Arun Kumar K Signed-off-by: Arun Mankuzhi Acked-by: Kamil Debski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 6 ++++++ drivers/media/platform/s5p-mfc/s5p_mfc_common.h | 1 + drivers/media/platform/s5p-mfc/s5p_mfc_dec.c | 15 +++++++++++++-- drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c | 17 +++++++++++------ 4 files changed, 31 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index a49d0e5db93a..3930177db125 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -686,6 +686,12 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv) s5p_mfc_handle_stream_complete(ctx, reason, err); break; + case S5P_MFC_R2H_CMD_DPB_FLUSH_RET: + clear_work_bit(ctx); + ctx->state = MFCINST_RUNNING; + wake_up(&ctx->queue); + goto irq_cleanup_hw; + default: mfc_debug(2, "Unknown int reason\n"); s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index f02e0497ca98..3b9b600a6182 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -145,6 +145,7 @@ enum s5p_mfc_inst_state { MFCINST_RETURN_INST, MFCINST_ERROR, MFCINST_ABORT, + MFCINST_FLUSH, MFCINST_RES_CHANGE_INIT, MFCINST_RES_CHANGE_FLUSH, MFCINST_RES_CHANGE_END, diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index 6dad9a74f61c..4582473978ca 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -991,24 +991,35 @@ static int s5p_mfc_stop_streaming(struct vb2_queue *q) S5P_MFC_R2H_CMD_FRAME_DONE_RET, 0); aborted = 1; } - spin_lock_irqsave(&dev->irqlock, flags); if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + spin_lock_irqsave(&dev->irqlock, flags); s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->dst_queue, &ctx->vq_dst); INIT_LIST_HEAD(&ctx->dst_queue); ctx->dst_queue_cnt = 0; ctx->dpb_flush_flag = 1; ctx->dec_dst_flag = 0; + spin_unlock_irqrestore(&dev->irqlock, flags); + if (IS_MFCV6(dev) && (ctx->state == MFCINST_RUNNING)) { + ctx->state = MFCINST_FLUSH; + set_work_bit_irqsave(ctx); + s5p_mfc_clean_ctx_int_flags(ctx); + s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + if (s5p_mfc_wait_for_done_ctx(ctx, + S5P_MFC_R2H_CMD_DPB_FLUSH_RET, 0)) + mfc_err("Err flushing buffers\n"); + } } if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + spin_lock_irqsave(&dev->irqlock, flags); s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->src_queue, &ctx->vq_src); INIT_LIST_HEAD(&ctx->src_queue); ctx->src_queue_cnt = 0; + spin_unlock_irqrestore(&dev->irqlock, flags); } if (aborted) ctx->state = MFCINST_RUNNING; - spin_unlock_irqrestore(&dev->irqlock, flags); return 0; } diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index bf4d2f44f009..5f9a5e073456 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -1253,12 +1253,14 @@ int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) static inline void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush) { struct s5p_mfc_dev *dev = ctx->dev; - unsigned int dpb; - if (flush) - dpb = READL(S5P_FIMV_SI_CH0_DPB_CONF_CTRL) | (1 << 14); - else - dpb = READL(S5P_FIMV_SI_CH0_DPB_CONF_CTRL) & ~(1 << 14); - WRITEL(dpb, S5P_FIMV_SI_CH0_DPB_CONF_CTRL); + + if (flush) { + dev->curr_ctx = ctx->num; + s5p_mfc_clean_ctx_int_flags(ctx); + WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6); + s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev, + S5P_FIMV_H2R_CMD_FLUSH_V6, NULL); + } } /* Decode a single frame */ @@ -1650,6 +1652,9 @@ void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev) case MFCINST_HEAD_PARSED: ret = s5p_mfc_run_init_dec_buffers(ctx); break; + case MFCINST_FLUSH: + s5p_mfc_set_flush(ctx, ctx->dpb_flush_flag); + break; case MFCINST_RES_CHANGE_INIT: s5p_mfc_run_dec_last_frames(ctx); break; -- cgit v1.2.3 From 55e2cf34ba290a18782c64786f0e1842b8394c45 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 28 Dec 2012 06:18:27 -0300 Subject: [media] s5p-mfc: Remove redundant 'break' The code returns before this statement. Hence not required. Silences the following smatch message: drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c:525 s5p_mfc_set_dec_frame_buffer_v5() info: ignoring unreachable code. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c index bf7d010a4107..2f099c44cb54 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c @@ -523,7 +523,6 @@ int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx) mfc_err("Unknown codec for decoding (%x)\n", ctx->codec_mode); return -EINVAL; - break; } frame_size = ctx->luma_size; frame_size_ch = ctx->chroma_size; -- cgit v1.2.3 From b311ea4bf18153b78d26e62304a30434447dbbf0 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 2 Jan 2013 05:13:33 -0300 Subject: [media] s5p-mfc: Fix a typo in error message in s5p_mfc_pm.c Fixed a trivial typo. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc_pm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c index 2895333866fc..6aa38a56aaf2 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c @@ -46,7 +46,7 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *dev) ret = clk_prepare(pm->clock_gate); if (ret) { - mfc_err("Failed to preapre clock-gating control\n"); + mfc_err("Failed to prepare clock-gating control\n"); goto err_p_ip_clk; } -- cgit v1.2.3 From 4a9c85aa495a35593eb7f3fd3dc259fd020308b4 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 2 Jan 2013 06:45:49 -0300 Subject: [media] s5p-mfc: Fix an error check Checking unsigned variable for negative value always returns false. Hence make this value signed as we expect it to be negative too. Fixes the following smatch warning: drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c:572 s5p_mfc_set_enc_ref_buffer_v6() warn: unsigned 'buf_size1' is never less than zero. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index 5f9a5e073456..91d508729d43 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -535,8 +535,8 @@ void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; - size_t buf_addr1, buf_size1; - int i; + size_t buf_addr1; + int i, buf_size1; mfc_debug_enter(); -- cgit v1.2.3 From 2e731e443fcc8e4553201ad0573c1d5571e906ac Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Thu, 3 Jan 2013 11:02:07 -0300 Subject: [media] s5p-mfc: Move firmware allocation point to avoid allocation problems Move firmware allocation from open to probe to avoid problems when using CMA for allocation. In certain circumstances CMA may allocate buffer that is not in the beginning of the MFC memory area. Signed-off-by: Kamil Debski Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 29 +++-- drivers/media/platform/s5p-mfc/s5p_mfc_common.h | 10 +- drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c | 149 +++++++++++------------- drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h | 3 +- 4 files changed, 94 insertions(+), 97 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 3930177db125..9a679fab73e2 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -793,14 +793,16 @@ static int s5p_mfc_open(struct file *file) goto err_pwr_enable; } s5p_mfc_clock_on(); - ret = s5p_mfc_alloc_and_load_firmware(dev); - if (ret) - goto err_alloc_fw; + ret = s5p_mfc_load_firmware(dev); + if (ret) { + s5p_mfc_clock_off(); + goto err_load_fw; + } /* Init the FW */ ret = s5p_mfc_init_hw(dev); + s5p_mfc_clock_off(); if (ret) goto err_init_hw; - s5p_mfc_clock_off(); } /* Init videobuf2 queue for CAPTURE */ q = &ctx->vq_dst; @@ -849,17 +851,16 @@ static int s5p_mfc_open(struct file *file) return ret; /* Deinit when failure occured */ err_queue_init: + if (dev->num_inst == 1) + s5p_mfc_deinit_hw(dev); err_init_hw: - s5p_mfc_release_firmware(dev); -err_alloc_fw: +err_load_fw: dev->ctx[ctx->num] = NULL; del_timer_sync(&dev->watchdog_timer); - s5p_mfc_clock_off(); err_pwr_enable: if (dev->num_inst == 1) { if (s5p_mfc_power_off() < 0) mfc_err("power off failed\n"); - s5p_mfc_release_firmware(dev); } err_ctrls_setup: s5p_mfc_dec_ctrls_delete(ctx); @@ -917,11 +918,8 @@ static int s5p_mfc_release(struct file *file) clear_bit(0, &dev->hw_lock); dev->num_inst--; if (dev->num_inst == 0) { - mfc_debug(2, "Last instance - release firmware\n"); - /* reset <-> F/W release */ - s5p_mfc_reset(dev); + mfc_debug(2, "Last instance\n"); s5p_mfc_deinit_hw(dev); - s5p_mfc_release_firmware(dev); del_timer_sync(&dev->watchdog_timer); if (s5p_mfc_power_off() < 0) mfc_err("Power off failed\n"); @@ -1149,6 +1147,10 @@ static int s5p_mfc_probe(struct platform_device *pdev) mutex_init(&dev->mfc_mutex); + ret = s5p_mfc_alloc_firmware(dev); + if (ret) + goto err_alloc_fw; + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) goto err_v4l2_dev_reg; @@ -1230,6 +1232,8 @@ err_dec_reg: err_dec_alloc: v4l2_device_unregister(&dev->v4l2_dev); err_v4l2_dev_reg: + s5p_mfc_release_firmware(dev); +err_alloc_fw: vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]); err_mem_init_ctx_1: vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]); @@ -1255,6 +1259,7 @@ static int __devexit s5p_mfc_remove(struct platform_device *pdev) video_unregister_device(dev->vfd_enc); video_unregister_device(dev->vfd_dec); v4l2_device_unregister(&dev->v4l2_dev); + s5p_mfc_release_firmware(dev); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 3b9b600a6182..0df6454e407e 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -278,8 +278,9 @@ struct s5p_mfc_priv_buf { * @int_err: error number for last interrupt * @queue: waitqueue for waiting for completion of device commands * @fw_size: size of firmware - * @bank1: address of the beggining of bank 1 memory - * @bank2: address of the beggining of bank 2 memory + * @fw_virt_addr: virtual firmware address + * @bank1: address of the beginning of bank 1 memory + * @bank2: address of the beginning of bank 2 memory * @hw_lock: used for hardware locking * @ctx: array of driver contexts * @curr_ctx: number of the currently running context @@ -318,8 +319,9 @@ struct s5p_mfc_dev { unsigned int int_err; wait_queue_head_t queue; size_t fw_size; - size_t bank1; - size_t bank2; + void *fw_virt_addr; + dma_addr_t bank1; + dma_addr_t bank2; unsigned long hw_lock; struct s5p_mfc_ctx *ctx[MFC_NUM_CONTEXTS]; int curr_ctx; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c index 585b7b0ed8ec..1682271c2453 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c @@ -22,16 +22,64 @@ #include "s5p_mfc_opr.h" #include "s5p_mfc_pm.h" -static void *s5p_mfc_bitproc_buf; -static size_t s5p_mfc_bitproc_phys; -static unsigned char *s5p_mfc_bitproc_virt; +/* Allocate memory for firmware */ +int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev) +{ + void *bank2_virt; + dma_addr_t bank2_dma_addr; + + dev->fw_size = dev->variant->buf_size->fw; + + if (dev->fw_virt_addr) { + mfc_err("Attempting to allocate firmware when it seems that it is already loaded\n"); + return -ENOMEM; + } + + dev->fw_virt_addr = dma_alloc_coherent(dev->mem_dev_l, dev->fw_size, + &dev->bank1, GFP_KERNEL); + + if (IS_ERR(dev->fw_virt_addr)) { + dev->fw_virt_addr = NULL; + mfc_err("Allocating bitprocessor buffer failed\n"); + return -ENOMEM; + } + + dev->bank1 = dev->bank1; + + if (HAS_PORTNUM(dev) && IS_TWOPORT(dev)) { + bank2_virt = dma_alloc_coherent(dev->mem_dev_r, 1 << MFC_BASE_ALIGN_ORDER, + &bank2_dma_addr, GFP_KERNEL); + + if (IS_ERR(dev->fw_virt_addr)) { + mfc_err("Allocating bank2 base failed\n"); + dma_free_coherent(dev->mem_dev_l, dev->fw_size, + dev->fw_virt_addr, dev->bank1); + dev->fw_virt_addr = NULL; + return -ENOMEM; + } + + /* Valid buffers passed to MFC encoder with LAST_FRAME command + * should not have address of bank2 - MFC will treat it as a null frame. + * To avoid such situation we set bank2 address below the pool address. + */ + dev->bank2 = bank2_dma_addr - (1 << MFC_BASE_ALIGN_ORDER); + + dma_free_coherent(dev->mem_dev_r, 1 << MFC_BASE_ALIGN_ORDER, + bank2_virt, bank2_dma_addr); + + } else { + /* In this case bank2 can point to the same address as bank1. + * Firmware will always occupy the beggining of this area so it is + * impossible having a video frame buffer with zero address. */ + dev->bank2 = dev->bank1; + } + return 0; +} -/* Allocate and load firmware */ -int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) +/* Load firmware */ +int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev) { struct firmware *fw_blob; - size_t bank2_base_phys; - void *b_base; int err; /* Firmare has to be present as a separate file or compiled @@ -44,77 +92,17 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n"); return -EINVAL; } - dev->fw_size = dev->variant->buf_size->fw; if (fw_blob->size > dev->fw_size) { mfc_err("MFC firmware is too big to be loaded\n"); release_firmware(fw_blob); return -ENOMEM; } - if (s5p_mfc_bitproc_buf) { - mfc_err("Attempting to allocate firmware when it seems that it is already loaded\n"); - release_firmware(fw_blob); - return -ENOMEM; - } - s5p_mfc_bitproc_buf = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], dev->fw_size); - if (IS_ERR(s5p_mfc_bitproc_buf)) { - s5p_mfc_bitproc_buf = NULL; - mfc_err("Allocating bitprocessor buffer failed\n"); + if (!dev->fw_virt_addr) { + mfc_err("MFC firmware is not allocated\n"); release_firmware(fw_blob); - return -ENOMEM; - } - s5p_mfc_bitproc_phys = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], s5p_mfc_bitproc_buf); - if (s5p_mfc_bitproc_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) { - mfc_err("The base memory for bank 1 is not aligned to 128KB\n"); - vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); - s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = NULL; - release_firmware(fw_blob); - return -EIO; - } - s5p_mfc_bitproc_virt = vb2_dma_contig_memops.vaddr(s5p_mfc_bitproc_buf); - if (!s5p_mfc_bitproc_virt) { - mfc_err("Bitprocessor memory remap failed\n"); - vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); - s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = NULL; - release_firmware(fw_blob); - return -EIO; - } - dev->bank1 = s5p_mfc_bitproc_phys; - if (HAS_PORTNUM(dev) && IS_TWOPORT(dev)) { - b_base = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], - 1 << MFC_BASE_ALIGN_ORDER); - if (IS_ERR(b_base)) { - vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); - s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = NULL; - mfc_err("Allocating bank2 base failed\n"); - release_firmware(fw_blob); - return -ENOMEM; - } - bank2_base_phys = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], b_base); - vb2_dma_contig_memops.put(b_base); - if (bank2_base_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) { - mfc_err("The base memory for bank 2 is not aligned to 128KB\n"); - vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); - s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = NULL; - release_firmware(fw_blob); - return -EIO; - } - /* Valid buffers passed to MFC encoder with LAST_FRAME command - * should not have address of bank2 - MFC will treat it as a null frame. - * To avoid such situation we set bank2 address below the pool address. - */ - dev->bank2 = bank2_base_phys - (1 << MFC_BASE_ALIGN_ORDER); - } else { - dev->bank2 = dev->bank1; + return -EINVAL; } - memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size); + memcpy(dev->fw_virt_addr, fw_blob->data, fw_blob->size); wmb(); release_firmware(fw_blob); mfc_debug_leave(); @@ -142,12 +130,12 @@ int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev) release_firmware(fw_blob); return -ENOMEM; } - if (s5p_mfc_bitproc_buf == NULL || s5p_mfc_bitproc_phys == 0) { - mfc_err("MFC firmware is not allocated or was not mapped correctly\n"); + if (dev->fw_virt_addr) { + mfc_err("MFC firmware is not allocated\n"); release_firmware(fw_blob); return -EINVAL; } - memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size); + memcpy(dev->fw_virt_addr, fw_blob->data, fw_blob->size); wmb(); release_firmware(fw_blob); mfc_debug_leave(); @@ -159,12 +147,11 @@ int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev) { /* Before calling this function one has to make sure * that MFC is no longer processing */ - if (!s5p_mfc_bitproc_buf) + if (!dev->fw_virt_addr) return -EINVAL; - vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); - s5p_mfc_bitproc_virt = NULL; - s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = NULL; + dma_free_coherent(dev->mem_dev_l, dev->fw_size, dev->fw_virt_addr, + dev->bank1); + dev->fw_virt_addr = NULL; return 0; } @@ -257,8 +244,10 @@ int s5p_mfc_init_hw(struct s5p_mfc_dev *dev) int ret; mfc_debug_enter(); - if (!s5p_mfc_bitproc_buf) + if (!dev->fw_virt_addr) { + mfc_err("Firmware memory is not allocated.\n"); return -EINVAL; + } /* 0. MFC reset */ mfc_debug(2, "MFC reset..\n"); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h index 90aa9b9886d5..6a9b6f8606bb 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h @@ -16,7 +16,8 @@ #include "s5p_mfc_common.h" int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev); -int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev); +int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev); +int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev); int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev); int s5p_mfc_init_hw(struct s5p_mfc_dev *dev); -- cgit v1.2.3 From ef89fff874758c0f08501bdc2932b7e77421cfc6 Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Thu, 3 Jan 2013 07:06:03 -0300 Subject: [media] s5p-mfc: Correct check of vb2_dma_contig_init_ctx return value vb2_dma_contig_init_ctx returns an error if failed, NULL check is not necessary. Signed-off-by: Kamil Debski Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 9a679fab73e2..4fcd075e8699 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1135,12 +1135,12 @@ static int s5p_mfc_probe(struct platform_device *pdev) } dev->alloc_ctx[0] = vb2_dma_contig_init_ctx(dev->mem_dev_l); - if (IS_ERR_OR_NULL(dev->alloc_ctx[0])) { + if (IS_ERR(dev->alloc_ctx[0])) { ret = PTR_ERR(dev->alloc_ctx[0]); goto err_res; } dev->alloc_ctx[1] = vb2_dma_contig_init_ctx(dev->mem_dev_r); - if (IS_ERR_OR_NULL(dev->alloc_ctx[1])) { + if (IS_ERR(dev->alloc_ctx[1])) { ret = PTR_ERR(dev->alloc_ctx[1]); goto err_mem_init_ctx_1; } -- cgit v1.2.3 From 317b4ca4982ea2429b75d0acd10445ec9475aa86 Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Thu, 3 Jan 2013 07:06:04 -0300 Subject: [media] s5p-mfc: Change internal buffer allocation from vb2 ops to dma_alloc_coherent Change internal buffer allocation from vb2 memory ops call to direct calls of dma_alloc_coherent. This change shortens the code and makes it much more readable. Signed-off-by: Kamil Debski Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc_common.h | 20 +-- drivers/media/platform/s5p-mfc/s5p_mfc_opr.c | 30 ++++ drivers/media/platform/s5p-mfc/s5p_mfc_opr.h | 5 + drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c | 196 ++++++++---------------- drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c | 121 +++++---------- 5 files changed, 143 insertions(+), 229 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 0df6454e407e..202d1d7a37a8 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -496,15 +496,9 @@ struct s5p_mfc_codec_ops { * flushed * @head_processed: flag mentioning whether the header data is processed * completely or not - * @bank1_buf: handle to memory allocated for temporary buffers from + * @bank1: handle to memory allocated for temporary buffers from * memory bank 1 - * @bank1_phys: address of the temporary buffers from memory bank 1 - * @bank1_size: size of the memory allocated for temporary buffers from - * memory bank 1 - * @bank2_buf: handle to memory allocated for temporary buffers from - * memory bank 2 - * @bank2_phys: address of the temporary buffers from memory bank 2 - * @bank2_size: size of the memory allocated for temporary buffers from + * @bank2: handle to memory allocated for temporary buffers from * memory bank 2 * @capture_state: state of the capture buffers queue * @output_state: state of the output buffers queue @@ -584,14 +578,8 @@ struct s5p_mfc_ctx { unsigned int dpb_flush_flag; unsigned int head_processed; - /* Buffers */ - void *bank1_buf; - size_t bank1_phys; - size_t bank1_size; - - void *bank2_buf; - size_t bank2_phys; - size_t bank2_size; + struct s5p_mfc_priv_buf bank1; + struct s5p_mfc_priv_buf bank2; enum s5p_mfc_queue_state capture_state; enum s5p_mfc_queue_state output_state; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c index 6932e90d4065..b4c194331d8c 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c @@ -12,6 +12,7 @@ * published by the Free Software Foundation. */ +#include "s5p_mfc_debug.h" #include "s5p_mfc_opr.h" #include "s5p_mfc_opr_v5.h" #include "s5p_mfc_opr_v6.h" @@ -29,3 +30,32 @@ void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev) } dev->mfc_ops = s5p_mfc_ops; } + +int s5p_mfc_alloc_priv_buf(struct device *dev, + struct s5p_mfc_priv_buf *b) +{ + + mfc_debug(3, "Allocating priv: %d\n", b->size); + + b->virt = dma_alloc_coherent(dev, b->size, &b->dma, GFP_KERNEL); + + if (!b->virt) { + mfc_err("Allocating private buffer failed\n"); + return -ENOMEM; + } + + mfc_debug(3, "Allocated addr %p %08x\n", b->virt, b->dma); + return 0; +} + +void s5p_mfc_release_priv_buf(struct device *dev, + struct s5p_mfc_priv_buf *b) +{ + if (b->virt) { + dma_free_coherent(dev, b->size, b->virt, b->dma); + b->virt = 0; + b->dma = 0; + b->size = 0; + } +} + diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h index 420abecafec0..754c540e7a7e 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h @@ -80,5 +80,10 @@ struct s5p_mfc_hw_ops { }; void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev); +int s5p_mfc_alloc_priv_buf(struct device *dev, + struct s5p_mfc_priv_buf *b); +void s5p_mfc_release_priv_buf(struct device *dev, + struct s5p_mfc_priv_buf *b); + #endif /* S5P_MFC_OPR_H_ */ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c index 2f099c44cb54..f61dba837899 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c @@ -38,39 +38,26 @@ int s5p_mfc_alloc_dec_temp_buffers_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf_size_v5 *buf_size = dev->variant->buf_size->priv; + int ret; - ctx->dsc.alloc = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], - buf_size->dsc); - if (IS_ERR_VALUE((int)ctx->dsc.alloc)) { - ctx->dsc.alloc = NULL; - mfc_err("Allocating DESC buffer failed\n"); - return -ENOMEM; + ctx->dsc.size = buf_size->dsc; + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->dsc); + if (ret) { + mfc_err("Failed to allocate temporary buffer\n"); + return ret; } - ctx->dsc.dma = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->dsc.alloc); + BUG_ON(ctx->dsc.dma & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); - ctx->dsc.virt = vb2_dma_contig_memops.vaddr(ctx->dsc.alloc); - if (ctx->dsc.virt == NULL) { - vb2_dma_contig_memops.put(ctx->dsc.alloc); - ctx->dsc.dma = 0; - ctx->dsc.alloc = NULL; - mfc_err("Remapping DESC buffer failed\n"); - return -ENOMEM; - } - memset(ctx->dsc.virt, 0, buf_size->dsc); + memset(ctx->dsc.virt, 0, ctx->dsc.size); wmb(); return 0; } + /* Release temporary buffers for decoding */ void s5p_mfc_release_dec_desc_buffer_v5(struct s5p_mfc_ctx *ctx) { - if (ctx->dsc.dma) { - vb2_dma_contig_memops.put(ctx->dsc.alloc); - ctx->dsc.alloc = NULL; - ctx->dsc.dma = 0; - } + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->dsc); } /* Allocate codec buffers */ @@ -80,6 +67,7 @@ int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx) unsigned int enc_ref_y_size = 0; unsigned int enc_ref_c_size = 0; unsigned int guard_width, guard_height; + int ret; if (ctx->type == MFCINST_DECODER) { mfc_debug(2, "Luma size:%d Chroma size:%d MV size:%d\n", @@ -113,100 +101,93 @@ int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx) /* Codecs have different memory requirements */ switch (ctx->codec_mode) { case S5P_MFC_CODEC_H264_DEC: - ctx->bank1_size = + ctx->bank1.size = ALIGN(S5P_FIMV_DEC_NB_IP_SIZE + S5P_FIMV_DEC_VERT_NB_MV_SIZE, S5P_FIMV_DEC_BUF_ALIGN); - ctx->bank2_size = ctx->total_dpb_count * ctx->mv_size; + ctx->bank2.size = ctx->total_dpb_count * ctx->mv_size; break; case S5P_MFC_CODEC_MPEG4_DEC: - ctx->bank1_size = + ctx->bank1.size = ALIGN(S5P_FIMV_DEC_NB_DCAC_SIZE + S5P_FIMV_DEC_UPNB_MV_SIZE + S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE + S5P_FIMV_DEC_STX_PARSER_SIZE + S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE, S5P_FIMV_DEC_BUF_ALIGN); - ctx->bank2_size = 0; + ctx->bank2.size = 0; break; case S5P_MFC_CODEC_VC1RCV_DEC: case S5P_MFC_CODEC_VC1_DEC: - ctx->bank1_size = + ctx->bank1.size = ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE + S5P_FIMV_DEC_UPNB_MV_SIZE + S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE + S5P_FIMV_DEC_NB_DCAC_SIZE + 3 * S5P_FIMV_DEC_VC1_BITPLANE_SIZE, S5P_FIMV_DEC_BUF_ALIGN); - ctx->bank2_size = 0; + ctx->bank2.size = 0; break; case S5P_MFC_CODEC_MPEG2_DEC: - ctx->bank1_size = 0; - ctx->bank2_size = 0; + ctx->bank1.size = 0; + ctx->bank2.size = 0; break; case S5P_MFC_CODEC_H263_DEC: - ctx->bank1_size = + ctx->bank1.size = ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE + S5P_FIMV_DEC_UPNB_MV_SIZE + S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE + S5P_FIMV_DEC_NB_DCAC_SIZE, S5P_FIMV_DEC_BUF_ALIGN); - ctx->bank2_size = 0; + ctx->bank2.size = 0; break; case S5P_MFC_CODEC_H264_ENC: - ctx->bank1_size = (enc_ref_y_size * 2) + + ctx->bank1.size = (enc_ref_y_size * 2) + S5P_FIMV_ENC_UPMV_SIZE + S5P_FIMV_ENC_COLFLG_SIZE + S5P_FIMV_ENC_INTRAMD_SIZE + S5P_FIMV_ENC_NBORINFO_SIZE; - ctx->bank2_size = (enc_ref_y_size * 2) + + ctx->bank2.size = (enc_ref_y_size * 2) + (enc_ref_c_size * 4) + S5P_FIMV_ENC_INTRAPRED_SIZE; break; case S5P_MFC_CODEC_MPEG4_ENC: - ctx->bank1_size = (enc_ref_y_size * 2) + + ctx->bank1.size = (enc_ref_y_size * 2) + S5P_FIMV_ENC_UPMV_SIZE + S5P_FIMV_ENC_COLFLG_SIZE + S5P_FIMV_ENC_ACDCCOEF_SIZE; - ctx->bank2_size = (enc_ref_y_size * 2) + + ctx->bank2.size = (enc_ref_y_size * 2) + (enc_ref_c_size * 4); break; case S5P_MFC_CODEC_H263_ENC: - ctx->bank1_size = (enc_ref_y_size * 2) + + ctx->bank1.size = (enc_ref_y_size * 2) + S5P_FIMV_ENC_UPMV_SIZE + S5P_FIMV_ENC_ACDCCOEF_SIZE; - ctx->bank2_size = (enc_ref_y_size * 2) + + ctx->bank2.size = (enc_ref_y_size * 2) + (enc_ref_c_size * 4); break; default: break; } /* Allocate only if memory from bank 1 is necessary */ - if (ctx->bank1_size > 0) { - ctx->bank1_buf = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_size); - if (IS_ERR(ctx->bank1_buf)) { - ctx->bank1_buf = NULL; - printk(KERN_ERR - "Buf alloc for decoding failed (port A)\n"); - return -ENOMEM; + if (ctx->bank1.size > 0) { + + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->bank1); + if (ret) { + mfc_err("Failed to allocate Bank1 temporary buffer\n"); + return ret; } - ctx->bank1_phys = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_buf); - BUG_ON(ctx->bank1_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); + BUG_ON(ctx->bank1.dma & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); } /* Allocate only if memory from bank 2 is necessary */ - if (ctx->bank2_size > 0) { - ctx->bank2_buf = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_size); - if (IS_ERR(ctx->bank2_buf)) { - ctx->bank2_buf = NULL; - mfc_err("Buf alloc for decoding failed (port B)\n"); - return -ENOMEM; + if (ctx->bank2.size > 0) { + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_r, &ctx->bank2); + if (ret) { + mfc_err("Failed to allocate Bank2 temporary buffer\n"); + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->bank1); + return ret; } - ctx->bank2_phys = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_buf); - BUG_ON(ctx->bank2_phys & ((1 << MFC_BANK2_ALIGN_ORDER) - 1)); + BUG_ON(ctx->bank2.dma & ((1 << MFC_BANK2_ALIGN_ORDER) - 1)); } return 0; } @@ -214,18 +195,8 @@ int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx) /* Release buffers allocated for codec */ void s5p_mfc_release_codec_buffers_v5(struct s5p_mfc_ctx *ctx) { - if (ctx->bank1_buf) { - vb2_dma_contig_memops.put(ctx->bank1_buf); - ctx->bank1_buf = NULL; - ctx->bank1_phys = 0; - ctx->bank1_size = 0; - } - if (ctx->bank2_buf) { - vb2_dma_contig_memops.put(ctx->bank2_buf); - ctx->bank2_buf = NULL; - ctx->bank2_phys = 0; - ctx->bank2_size = 0; - } + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->bank1); + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_r, &ctx->bank2); } /* Allocate memory for instance data buffer */ @@ -233,58 +204,38 @@ int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf_size_v5 *buf_size = dev->variant->buf_size->priv; + int ret; if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC || ctx->codec_mode == S5P_MFC_CODEC_H264_ENC) ctx->ctx.size = buf_size->h264_ctx; else ctx->ctx.size = buf_size->non_h264_ctx; - ctx->ctx.alloc = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx.size); - if (IS_ERR(ctx->ctx.alloc)) { - mfc_err("Allocating context buffer failed\n"); - ctx->ctx.alloc = NULL; - return -ENOMEM; + + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->ctx); + if (ret) { + mfc_err("Failed to allocate instance buffer\n"); + return ret; } - ctx->ctx.dma = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx.alloc); - BUG_ON(ctx->ctx.dma & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); ctx->ctx.ofs = OFFSETA(ctx->ctx.dma); - ctx->ctx.virt = vb2_dma_contig_memops.vaddr(ctx->ctx.alloc); - if (!ctx->ctx.virt) { - mfc_err("Remapping instance buffer failed\n"); - vb2_dma_contig_memops.put(ctx->ctx.alloc); - ctx->ctx.alloc = NULL; - ctx->ctx.ofs = 0; - ctx->ctx.dma = 0; - return -ENOMEM; - } + /* Zero content of the allocated memory */ memset(ctx->ctx.virt, 0, ctx->ctx.size); wmb(); /* Initialize shared memory */ - ctx->shm.alloc = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], buf_size->shm); - if (IS_ERR(ctx->shm.alloc)) { - mfc_err("failed to allocate shared memory\n"); - return PTR_ERR(ctx->shm.alloc); + ctx->shm.size = buf_size->shm; + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->shm); + if (ret) { + mfc_err("Failed to allocate shared memory buffer\n"); + return ret; } + /* shared memory offset only keeps the offset from base (port a) */ - ctx->shm.ofs = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->shm.alloc) - - dev->bank1; + ctx->shm.ofs = ctx->shm.dma - dev->bank1; BUG_ON(ctx->shm.ofs & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); - ctx->shm.virt = vb2_dma_contig_memops.vaddr(ctx->shm.alloc); - if (!ctx->shm.virt) { - vb2_dma_contig_memops.put(ctx->shm.alloc); - ctx->shm.alloc = NULL; - ctx->shm.ofs = 0; - mfc_err("failed to virt addr of shared memory\n"); - return -ENOMEM; - } - memset((void *)ctx->shm.virt, 0, buf_size->shm); + memset(ctx->shm.virt, 0, buf_size->shm); wmb(); return 0; } @@ -292,19 +243,8 @@ int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx) /* Release instance buffer */ void s5p_mfc_release_instance_buffer_v5(struct s5p_mfc_ctx *ctx) { - if (ctx->ctx.alloc) { - vb2_dma_contig_memops.put(ctx->ctx.alloc); - ctx->ctx.alloc = NULL; - ctx->ctx.ofs = 0; - ctx->ctx.virt = NULL; - ctx->ctx.dma = 0; - } - if (ctx->shm.alloc) { - vb2_dma_contig_memops.put(ctx->shm.alloc); - ctx->shm.alloc = NULL; - ctx->shm.ofs = 0; - ctx->shm.virt = NULL; - } + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->ctx); + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->shm); } int s5p_mfc_alloc_dev_context_buffer_v5(struct s5p_mfc_dev *dev) @@ -443,10 +383,10 @@ int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx) size_t buf_addr1, buf_addr2; int buf_size1, buf_size2; - buf_addr1 = ctx->bank1_phys; - buf_size1 = ctx->bank1_size; - buf_addr2 = ctx->bank2_phys; - buf_size2 = ctx->bank2_size; + buf_addr1 = ctx->bank1.dma; + buf_size1 = ctx->bank1.size; + buf_addr2 = ctx->bank2.dma; + buf_size2 = ctx->bank2.size; dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) & ~S5P_FIMV_DPB_COUNT_MASK; mfc_write(dev, ctx->total_dpb_count | dpb, @@ -606,10 +546,10 @@ int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx) unsigned int guard_width, guard_height; int i; - buf_addr1 = ctx->bank1_phys; - buf_size1 = ctx->bank1_size; - buf_addr2 = ctx->bank2_phys; - buf_size2 = ctx->bank2_size; + buf_addr1 = ctx->bank1.dma; + buf_size1 = ctx->bank1.size; + buf_addr2 = ctx->bank2.dma; + buf_size2 = ctx->bank2.size; enc_ref_y_size = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN) * ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN); enc_ref_y_size = ALIGN(enc_ref_y_size, S5P_FIMV_NV12MT_SALIGN); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index 91d508729d43..beb6dbacebd9 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -73,6 +73,7 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; unsigned int mb_width, mb_height; + int ret; mb_width = MB_WIDTH(ctx->img_width); mb_height = MB_HEIGHT(ctx->img_height); @@ -112,7 +113,7 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = + ctx->bank1.size = ctx->scratch_buf_size + (ctx->mv_count * ctx->mv_size); break; @@ -123,7 +124,7 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = ctx->scratch_buf_size; + ctx->bank1.size = ctx->scratch_buf_size; break; case S5P_MFC_CODEC_VC1RCV_DEC: case S5P_MFC_CODEC_VC1_DEC: @@ -133,11 +134,11 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = ctx->scratch_buf_size; + ctx->bank1.size = ctx->scratch_buf_size; break; case S5P_MFC_CODEC_MPEG2_DEC: - ctx->bank1_size = 0; - ctx->bank2_size = 0; + ctx->bank1.size = 0; + ctx->bank2.size = 0; break; case S5P_MFC_CODEC_H263_DEC: ctx->scratch_buf_size = @@ -146,7 +147,7 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = ctx->scratch_buf_size; + ctx->bank1.size = ctx->scratch_buf_size; break; case S5P_MFC_CODEC_VP8_DEC: ctx->scratch_buf_size = @@ -155,7 +156,7 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = ctx->scratch_buf_size; + ctx->bank1.size = ctx->scratch_buf_size; break; case S5P_MFC_CODEC_H264_ENC: ctx->scratch_buf_size = @@ -164,11 +165,11 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = + ctx->bank1.size = ctx->scratch_buf_size + ctx->tmv_buffer_size + (ctx->dpb_count * (ctx->luma_dpb_size + ctx->chroma_dpb_size + ctx->me_buffer_size)); - ctx->bank2_size = 0; + ctx->bank2.size = 0; break; case S5P_MFC_CODEC_MPEG4_ENC: case S5P_MFC_CODEC_H263_ENC: @@ -178,28 +179,24 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = + ctx->bank1.size = ctx->scratch_buf_size + ctx->tmv_buffer_size + (ctx->dpb_count * (ctx->luma_dpb_size + ctx->chroma_dpb_size + ctx->me_buffer_size)); - ctx->bank2_size = 0; + ctx->bank2.size = 0; break; default: break; } /* Allocate only if memory from bank 1 is necessary */ - if (ctx->bank1_size > 0) { - ctx->bank1_buf = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_size); - if (IS_ERR(ctx->bank1_buf)) { - ctx->bank1_buf = 0; - pr_err("Buf alloc for decoding failed (port A)\n"); - return -ENOMEM; + if (ctx->bank1.size > 0) { + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->bank1); + if (ret) { + mfc_err("Failed to allocate Bank1 memory\n"); + return ret; } - ctx->bank1_phys = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_buf); - BUG_ON(ctx->bank1_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); + BUG_ON(ctx->bank1.dma & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); } return 0; @@ -208,12 +205,7 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) /* Release buffers allocated for codec */ void s5p_mfc_release_codec_buffers_v6(struct s5p_mfc_ctx *ctx) { - if (ctx->bank1_buf) { - vb2_dma_contig_memops.put(ctx->bank1_buf); - ctx->bank1_buf = 0; - ctx->bank1_phys = 0; - ctx->bank1_size = 0; - } + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->bank1); } /* Allocate memory for instance data buffer */ @@ -221,6 +213,7 @@ int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->priv; + int ret; mfc_debug_enter(); @@ -250,25 +243,10 @@ int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx) break; } - ctx->ctx.alloc = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx.size); - if (IS_ERR(ctx->ctx.alloc)) { - mfc_err("Allocating context buffer failed.\n"); - return PTR_ERR(ctx->ctx.alloc); - } - - ctx->ctx.dma = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx.alloc); - - ctx->ctx.virt = vb2_dma_contig_memops.vaddr(ctx->ctx.alloc); - if (!ctx->ctx.virt) { - vb2_dma_contig_memops.put(ctx->ctx.alloc); - ctx->ctx.alloc = NULL; - ctx->ctx.dma = 0; - ctx->ctx.virt = NULL; - - mfc_err("Remapping context buffer failed.\n"); - return -ENOMEM; + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->ctx); + if (ret) { + mfc_err("Failed to allocate instance buffer\n"); + return ret; } memset(ctx->ctx.virt, 0, ctx->ctx.size); @@ -282,44 +260,22 @@ int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx) /* Release instance buffer */ void s5p_mfc_release_instance_buffer_v6(struct s5p_mfc_ctx *ctx) { - mfc_debug_enter(); - - if (ctx->ctx.alloc) { - vb2_dma_contig_memops.put(ctx->ctx.alloc); - ctx->ctx.alloc = NULL; - ctx->ctx.dma = 0; - ctx->ctx.virt = NULL; - } - - mfc_debug_leave(); + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->ctx); } /* Allocate context buffers for SYS_INIT */ int s5p_mfc_alloc_dev_context_buffer_v6(struct s5p_mfc_dev *dev) { struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->priv; + int ret; mfc_debug_enter(); - dev->ctx_buf.alloc = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], buf_size->dev_ctx); - if (IS_ERR(dev->ctx_buf.alloc)) { - mfc_err("Allocating DESC buffer failed.\n"); - return PTR_ERR(dev->ctx_buf.alloc); - } - - dev->ctx_buf.dma = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], - dev->ctx_buf.alloc); - - dev->ctx_buf.virt = vb2_dma_contig_memops.vaddr(dev->ctx_buf.alloc); - if (!dev->ctx_buf.virt) { - vb2_dma_contig_memops.put(dev->ctx_buf.alloc); - dev->ctx_buf.alloc = NULL; - dev->ctx_buf.dma = 0; - - mfc_err("Remapping DESC buffer failed.\n"); - return -ENOMEM; + dev->ctx_buf.size = buf_size->dev_ctx; + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &dev->ctx_buf); + if (ret) { + mfc_err("Failed to allocate device context buffer\n"); + return ret; } memset(dev->ctx_buf.virt, 0, buf_size->dev_ctx); @@ -333,12 +289,7 @@ int s5p_mfc_alloc_dev_context_buffer_v6(struct s5p_mfc_dev *dev) /* Release context buffers for SYS_INIT */ void s5p_mfc_release_dev_context_buffer_v6(struct s5p_mfc_dev *dev) { - if (dev->ctx_buf.alloc) { - vb2_dma_contig_memops.put(dev->ctx_buf.alloc); - dev->ctx_buf.alloc = NULL; - dev->ctx_buf.dma = 0; - dev->ctx_buf.virt = NULL; - } + s5p_mfc_release_priv_buf(dev->mem_dev_l, &dev->ctx_buf); } static int calc_plane(int width, int height) @@ -417,8 +368,8 @@ int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) int buf_size1; int align_gap; - buf_addr1 = ctx->bank1_phys; - buf_size1 = ctx->bank1_size; + buf_addr1 = ctx->bank1.dma; + buf_size1 = ctx->bank1.size; mfc_debug(2, "Buf1: %p (%d)\n", (void *)buf_addr1, buf_size1); mfc_debug(2, "Total DPB COUNT: %d\n", ctx->total_dpb_count); @@ -540,8 +491,8 @@ int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx) mfc_debug_enter(); - buf_addr1 = ctx->bank1_phys; - buf_size1 = ctx->bank1_size; + buf_addr1 = ctx->bank1.dma; + buf_size1 = ctx->bank1.size; mfc_debug(2, "Buf1: %p (%d)\n", (void *)buf_addr1, buf_size1); -- cgit v1.2.3 From 1b73ba0be4aad87a0d195a6d433bb59ffe81e99a Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Thu, 22 Nov 2012 10:00:28 -0300 Subject: [media] s5p-mfc: Context handling in open() bugfix Signed-off-by: Kamil Debski Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 4fcd075e8699..b1d7f9a9b996 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -855,16 +855,16 @@ err_queue_init: s5p_mfc_deinit_hw(dev); err_init_hw: err_load_fw: - dev->ctx[ctx->num] = NULL; - del_timer_sync(&dev->watchdog_timer); err_pwr_enable: if (dev->num_inst == 1) { if (s5p_mfc_power_off() < 0) mfc_err("power off failed\n"); + del_timer_sync(&dev->watchdog_timer); } err_ctrls_setup: s5p_mfc_dec_ctrls_delete(ctx); err_bad_node: + dev->ctx[ctx->num] = NULL; err_no_ctx: v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); -- cgit v1.2.3 From 24b9f50170f55a3179c6f6d51022eb7d50502d05 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 3 Jan 2013 12:30:30 -0300 Subject: [media] V4L: Remove deprecated image centering controls It has been over 3 years since the V4L2_CID_[HV]CENTER were deprecated. Clean up the DocBook and remove the V4L2_CID_VCENTER_DEPRECATED, V4L2_CID_VCENTER_DEPRECATED control related paragraphs. Remove the V4L2_CID_[HV]CENTER controls definitions from v4l2-controls.h, these controls are not used by any driver in the mainline now. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 23 ----------------------- drivers/media/v4l2-core/v4l2-ctrls.c | 2 -- include/uapi/linux/v4l2-controls.h | 4 ---- 3 files changed, 29 deletions(-) (limited to 'drivers') diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 7fe5be1d3bbb..9e8f85498678 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -203,29 +203,6 @@ and should not be used in new drivers and applications. boolean Mirror the picture vertically. - - V4L2_CID_HCENTER_DEPRECATED (formerly V4L2_CID_HCENTER) - integer - Horizontal image centering. This control is -deprecated. New drivers and applications should use the Camera class controls -V4L2_CID_PAN_ABSOLUTE, -V4L2_CID_PAN_RELATIVE and -V4L2_CID_PAN_RESET instead. - - - V4L2_CID_VCENTER_DEPRECATED - (formerly V4L2_CID_VCENTER) - integer - Vertical image centering. Centering is intended to -physically adjust cameras. For image cropping see -, for clipping . This -control is deprecated. New drivers and applications should use the -Camera class controls -V4L2_CID_TILT_ABSOLUTE, -V4L2_CID_TILT_RELATIVE and -V4L2_CID_TILT_RESET instead. - V4L2_CID_POWER_LINE_FREQUENCY enum diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index fa02363e7db4..7b486ac3f4d9 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -577,8 +577,6 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_GAIN: return "Gain"; case V4L2_CID_HFLIP: return "Horizontal Flip"; case V4L2_CID_VFLIP: return "Vertical Flip"; - case V4L2_CID_HCENTER: return "Horizontal Center"; - case V4L2_CID_VCENTER: return "Vertical Center"; case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency"; case V4L2_CID_HUE_AUTO: return "Hue, Automatic"; case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature"; diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index f56c945cecd4..4dc0822700fe 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -88,10 +88,6 @@ #define V4L2_CID_HFLIP (V4L2_CID_BASE+20) #define V4L2_CID_VFLIP (V4L2_CID_BASE+21) -/* Deprecated; use V4L2_CID_PAN_RESET and V4L2_CID_TILT_RESET */ -#define V4L2_CID_HCENTER (V4L2_CID_BASE+22) -#define V4L2_CID_VCENTER (V4L2_CID_BASE+23) - #define V4L2_CID_POWER_LINE_FREQUENCY (V4L2_CID_BASE+24) enum v4l2_power_line_frequency { V4L2_CID_POWER_LINE_FREQUENCY_DISABLED = 0, -- cgit v1.2.3 From 0c87c66aa383b045c437e7cf456eef28a8aa7b66 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 29 Nov 2012 00:05:35 -0300 Subject: [media] dvb_usb_v2: make remote controller optional Make it possible to compile dvb_usb_v2 driver without the remote controller (RC-core). Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/Kconfig | 3 ++- drivers/media/usb/dvb-usb-v2/dvb_usb.h | 9 +++++++++ drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 12 ++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 3240d559ef7b..7b5773fe6367 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -1,6 +1,6 @@ config DVB_USB_V2 tristate "Support for various USB DVB devices v2" - depends on DVB_CORE && USB && I2C && RC_CORE + depends on DVB_CORE && USB && I2C help By enabling this you will be able to choose the various supported USB1.1 and USB2.0 DVB devices. @@ -113,6 +113,7 @@ config DVB_USB_IT913X config DVB_USB_LME2510 tristate "LME DM04/QQBOX DVB-S USB2.0 support" depends on DVB_USB_V2 + depends on RC_CORE select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h index 059291b892b8..e2678a78db4c 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h @@ -400,4 +400,13 @@ extern int dvb_usbv2_reset_resume(struct usb_interface *); extern int dvb_usbv2_generic_rw(struct dvb_usb_device *, u8 *, u16, u8 *, u16); extern int dvb_usbv2_generic_write(struct dvb_usb_device *, u8 *, u16); +/* stub implementations that will be never called when RC-core is disabled */ +#if !defined(CONFIG_RC_CORE) && !defined(CONFIG_RC_CORE_MODULE) +#define rc_repeat(args...) +#define rc_keydown(args...) +#define rc_keydown_notimeout(args...) +#define rc_keyup(args...) +#define rc_g_keycode_from_table(args...) 0 +#endif + #endif diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index 671b4fa232b4..94f134c4e942 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -102,6 +102,7 @@ static int dvb_usbv2_i2c_exit(struct dvb_usb_device *d) return 0; } +#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) static void dvb_usb_read_remote_control(struct work_struct *work) { struct dvb_usb_device *d = container_of(work, @@ -202,6 +203,17 @@ static int dvb_usbv2_remote_exit(struct dvb_usb_device *d) return 0; } +#else +static int dvb_usbv2_remote_init(struct dvb_usb_device *d) +{ + return 0; +} + +static int dvb_usbv2_remote_exit(struct dvb_usb_device *d) +{ + return 0; +} +#endif static void dvb_usb_data_complete(struct usb_data_stream *stream, u8 *buf, size_t len) -- cgit v1.2.3 From cedda37015e90e58458fd91635ff8ef5d144d3f5 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 20:06:00 -0300 Subject: [media] rtl28xxu: make remote controller optional Do not compile remote controller when RC-core is disabled by Kconfig. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index eddda6958c19..9a6890392e90 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1125,7 +1125,7 @@ err: return ret; } - +#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) static int rtl2831u_rc_query(struct dvb_usb_device *d) { int ret, i; @@ -1208,7 +1208,11 @@ static int rtl2831u_get_rc_config(struct dvb_usb_device *d, return 0; } +#else + #define rtl2831u_get_rc_config NULL +#endif +#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) static int rtl2832u_rc_query(struct dvb_usb_device *d) { int ret, i; @@ -1280,6 +1284,9 @@ static int rtl2832u_get_rc_config(struct dvb_usb_device *d, return 0; } +#else + #define rtl2832u_get_rc_config NULL +#endif static const struct dvb_usb_device_properties rtl2831u_props = { .driver_name = KBUILD_MODNAME, -- cgit v1.2.3 From d5c6209068aeca3d464f4161006e637a6087008c Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 20:12:07 -0300 Subject: [media] anysee: make remote controller optional Do not compile remote controller when RC-core is disabled by Kconfig. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/anysee.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c index d05c5b563dac..5f450374cd03 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.c +++ b/drivers/media/usb/dvb-usb-v2/anysee.c @@ -1019,6 +1019,7 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap) return ret; } +#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) static int anysee_rc_query(struct dvb_usb_device *d) { u8 buf[] = {CMD_GET_IR_CODE}; @@ -1054,6 +1055,9 @@ static int anysee_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) return 0; } +#else + #define anysee_get_rc_config NULL +#endif static int anysee_ci_read_attribute_mem(struct dvb_ca_en50221 *ci, int slot, int addr) -- cgit v1.2.3 From b6215596b5912c205ed48c11deb1cbdf0aa3ac25 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 20:15:47 -0300 Subject: [media] af9015: make remote controller optional Do not compile remote controller when RC-core is disabled by Kconfig. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9015.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index 943d93423705..51505d198f44 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -1156,6 +1156,7 @@ error: return ret; } +#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) struct af9015_rc_setup { unsigned int id; char *rc_codes; @@ -1312,6 +1313,9 @@ static int af9015_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) return 0; } +#else + #define af9015_get_rc_config NULL +#endif /* interface 0 is used by DVB-T receiver and interface 1 is for remote controller (HID) */ -- cgit v1.2.3 From eed5670a31c511e196fd50fc591689ffdab61f80 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 20:18:31 -0300 Subject: [media] af9035: make remote controller optional Do not compile remote controller when RC-core is disabled by Kconfig. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9035.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index ea37b5c3c33f..19b139494448 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -1146,6 +1146,7 @@ err: return ret; } +#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) static int af9035_rc_query(struct dvb_usb_device *d) { unsigned int key; @@ -1220,6 +1221,9 @@ err: return ret; } +#else + #define af9035_get_rc_config NULL +#endif /* interface 0 is used by DVB-T receiver and interface 1 is for remote controller (HID) */ -- cgit v1.2.3 From 7b75322af1bd3b365ce3a5f2ac6df329bf725656 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 20:23:22 -0300 Subject: [media] az6007: make remote controller optional Do not compile remote controller when RC-core is disabled by Kconfig. Signed-off-by: Antti Palosaari Acked-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/az6007.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c index d75dbf27e99e..3b33f1ec2295 100644 --- a/drivers/media/usb/dvb-usb-v2/az6007.c +++ b/drivers/media/usb/dvb-usb-v2/az6007.c @@ -189,6 +189,7 @@ static int az6007_streaming_ctrl(struct dvb_frontend *fe, int onoff) return az6007_write(d, 0xbc, onoff, 0, NULL, 0); } +#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) /* remote control stuff (does not work with my box) */ static int az6007_rc_query(struct dvb_usb_device *d) { @@ -215,6 +216,20 @@ static int az6007_rc_query(struct dvb_usb_device *d) return 0; } +static int az6007_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) +{ + pr_debug("Getting az6007 Remote Control properties\n"); + + rc->allowed_protos = RC_BIT_NEC; + rc->query = az6007_rc_query; + rc->interval = 400; + + return 0; +} +#else + #define az6007_get_rc_config NULL +#endif + static int az6007_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) @@ -822,17 +837,6 @@ static void az6007_usb_disconnect(struct usb_interface *intf) dvb_usbv2_disconnect(intf); } -static int az6007_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) -{ - pr_debug("Getting az6007 Remote Control properties\n"); - - rc->allowed_protos = RC_BIT_NEC; - rc->query = az6007_rc_query; - rc->interval = 400; - - return 0; -} - static int az6007_download_firmware(struct dvb_usb_device *d, const struct firmware *fw) { -- cgit v1.2.3 From b963c2083ae3ec67052dfc6e2ec8216c479843d8 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 20:27:59 -0300 Subject: [media] it913x: make remote controller optional Do not compile remote controller when RC-core is disabled by Kconfig. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/it913x.c | 36 +++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/it913x.c b/drivers/media/usb/dvb-usb-v2/it913x.c index 744c9f9f7680..b8593bda725c 100644 --- a/drivers/media/usb/dvb-usb-v2/it913x.c +++ b/drivers/media/usb/dvb-usb-v2/it913x.c @@ -308,6 +308,7 @@ static struct i2c_algorithm it913x_i2c_algo = { }; /* Callbacks for DVB USB */ +#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) #define IT913X_POLL 250 static int it913x_rc_query(struct dvb_usb_device *d) { @@ -334,6 +335,25 @@ static int it913x_rc_query(struct dvb_usb_device *d) return ret; } +static int it913x_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) +{ + struct it913x_state *st = d->priv; + + if (st->proprietary_ir == false) { + rc->map_name = NULL; + return 0; + } + + rc->allowed_protos = RC_BIT_NEC; + rc->query = it913x_rc_query; + rc->interval = 250; + + return 0; +} +#else + #define it913x_get_rc_config NULL +#endif + /* Firmware sets raw */ static const char fw_it9135_v1[] = FW_IT9135_V1; static const char fw_it9135_v2[] = FW_IT9135_V2; @@ -696,22 +716,6 @@ static int it913x_frontend_attach(struct dvb_usb_adapter *adap) } /* DVB USB Driver */ -static int it913x_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) -{ - struct it913x_state *st = d->priv; - - if (st->proprietary_ir == false) { - rc->map_name = NULL; - return 0; - } - - rc->allowed_protos = RC_BIT_NEC; - rc->query = it913x_rc_query; - rc->interval = 250; - - return 0; -} - static int it913x_get_adapter_count(struct dvb_usb_device *d) { struct it913x_state *st = d->priv; -- cgit v1.2.3 From 21ca20303671f77c34380105fdce849793ba92aa Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 20:32:09 -0300 Subject: [media] it913x: remove unused define and increase module version Signed-off-by: Antti Palosaari Acked-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/it913x.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/it913x.c b/drivers/media/usb/dvb-usb-v2/it913x.c index b8593bda725c..511c2aa00b97 100644 --- a/drivers/media/usb/dvb-usb-v2/it913x.c +++ b/drivers/media/usb/dvb-usb-v2/it913x.c @@ -309,7 +309,6 @@ static struct i2c_algorithm it913x_i2c_algo = { /* Callbacks for DVB USB */ #if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) -#define IT913X_POLL 250 static int it913x_rc_query(struct dvb_usb_device *d) { u8 ibuf[4]; @@ -814,7 +813,7 @@ module_usb_driver(it913x_driver); MODULE_AUTHOR("Malcolm Priestley "); MODULE_DESCRIPTION("it913x USB 2 Driver"); -MODULE_VERSION("1.32"); +MODULE_VERSION("1.33"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(FW_IT9135_V1); MODULE_FIRMWARE(FW_IT9135_V2); -- cgit v1.2.3 From 21354c6de395e2e3cf896cf72ac243d5773f7773 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 20:41:49 -0300 Subject: [media] dvb_usb_v2: remove rc-core stub implementations Those are not needed anymore as all dvb-usb-v2 drivers has proper dependency checks for RC-core. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/dvb_usb.h | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h index e2678a78db4c..059291b892b8 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h @@ -400,13 +400,4 @@ extern int dvb_usbv2_reset_resume(struct usb_interface *); extern int dvb_usbv2_generic_rw(struct dvb_usb_device *, u8 *, u16, u8 *, u16); extern int dvb_usbv2_generic_write(struct dvb_usb_device *, u8 *, u16); -/* stub implementations that will be never called when RC-core is disabled */ -#if !defined(CONFIG_RC_CORE) && !defined(CONFIG_RC_CORE_MODULE) -#define rc_repeat(args...) -#define rc_keydown(args...) -#define rc_keydown_notimeout(args...) -#define rc_keyup(args...) -#define rc_g_keycode_from_table(args...) 0 -#endif - #endif -- cgit v1.2.3 From ef3824029d0f5887c065ea461157bdf8b2287bf8 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 20:46:46 -0300 Subject: [media] dvb_usb_v2: use dummy function defines instead stub functions I think it is better (cheaper) to use dummy defines for functions that has no meaning when remote controller is disabled by Kconfig. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index 94f134c4e942..1330c644dd8d 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -204,15 +204,8 @@ static int dvb_usbv2_remote_exit(struct dvb_usb_device *d) return 0; } #else -static int dvb_usbv2_remote_init(struct dvb_usb_device *d) -{ - return 0; -} - -static int dvb_usbv2_remote_exit(struct dvb_usb_device *d) -{ - return 0; -} + #define dvb_usbv2_remote_init(args...) 0 + #define dvb_usbv2_remote_exit(args...) #endif static void dvb_usb_data_complete(struct usb_data_stream *stream, u8 *buf, -- cgit v1.2.3 From ac1c86c857368eb727b7ca2c7a48bd6e373fa628 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 21:23:30 -0300 Subject: [media] dvb_usb_v2: change rc polling active/deactive logic Use own flag to mark when rc polling is active/deactive and make decisions, like start/stop polling on suspend/resume, against that. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/dvb_usb.h | 3 ++- drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h index 059291b892b8..3cac8bd0b116 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h @@ -347,6 +347,7 @@ struct dvb_usb_adapter { * @props: device properties * @name: device name * @rc_map: name of rc codes table + * @rc_polling_active: set when RC polling is active * @udev: pointer to the device's struct usb_device * @intf: pointer to the device's usb interface * @rc: remote controller configuration @@ -364,7 +365,7 @@ struct dvb_usb_device { const struct dvb_usb_device_properties *props; const char *name; const char *rc_map; - + bool rc_polling_active; struct usb_device *udev; struct usb_interface *intf; struct dvb_usb_rc rc; diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index 1330c644dd8d..95968d39c1ca 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -113,13 +113,16 @@ static void dvb_usb_read_remote_control(struct work_struct *work) * When the parameter has been set to 1 via sysfs while the * driver was running, or when bulk mode is enabled after IR init. */ - if (dvb_usbv2_disable_rc_polling || d->rc.bulk_mode) + if (dvb_usbv2_disable_rc_polling || d->rc.bulk_mode) { + d->rc_polling_active = false; return; + } ret = d->rc.query(d); if (ret < 0) { dev_err(&d->udev->dev, "%s: rc.query() failed=%d\n", KBUILD_MODNAME, ret); + d->rc_polling_active = false; return; /* stop polling */ } @@ -183,6 +186,7 @@ static int dvb_usbv2_remote_init(struct dvb_usb_device *d) d->rc.interval); schedule_delayed_work(&d->rc_query_work, msecs_to_jiffies(d->rc.interval)); + d->rc_polling_active = true; } return 0; @@ -964,7 +968,7 @@ int dvb_usbv2_suspend(struct usb_interface *intf, pm_message_t msg) dev_dbg(&d->udev->dev, "%s:\n", __func__); /* stop remote controller poll */ - if (d->rc.query && !d->rc.bulk_mode) + if (d->rc_polling_active) cancel_delayed_work_sync(&d->rc_query_work); for (i = MAX_NO_OF_ADAPTER_PER_DEVICE - 1; i >= 0; i--) { @@ -1011,7 +1015,7 @@ static int dvb_usbv2_resume_common(struct dvb_usb_device *d) } /* start remote controller poll */ - if (d->rc.query && !d->rc.bulk_mode) + if (d->rc_polling_active) schedule_delayed_work(&d->rc_query_work, msecs_to_jiffies(d->rc.interval)); -- cgit v1.2.3 From 37b44a0f04184998073633887d2f1e724aee130a Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 4 Jan 2013 15:21:26 -0300 Subject: [media] dvb_usb_v2: use IS_ENABLED() macro replace: #if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) with: #if IS_ENABLED(CONFIG_RC_CORE) Reported-by: Fabio Estevam Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9015.c | 2 +- drivers/media/usb/dvb-usb-v2/af9035.c | 2 +- drivers/media/usb/dvb-usb-v2/anysee.c | 2 +- drivers/media/usb/dvb-usb-v2/az6007.c | 2 +- drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 2 +- drivers/media/usb/dvb-usb-v2/it913x.c | 2 +- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 4 ++-- 7 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index 51505d198f44..b86d0f27a398 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -1156,7 +1156,7 @@ error: return ret; } -#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) +#if IS_ENABLED(CONFIG_RC_CORE) struct af9015_rc_setup { unsigned int id; char *rc_codes; diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 19b139494448..f11cc42454f0 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -1146,7 +1146,7 @@ err: return ret; } -#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) +#if IS_ENABLED(CONFIG_RC_CORE) static int af9035_rc_query(struct dvb_usb_device *d) { unsigned int key; diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c index 5f450374cd03..a20d691d0b63 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.c +++ b/drivers/media/usb/dvb-usb-v2/anysee.c @@ -1019,7 +1019,7 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap) return ret; } -#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) +#if IS_ENABLED(CONFIG_RC_CORE) static int anysee_rc_query(struct dvb_usb_device *d) { u8 buf[] = {CMD_GET_IR_CODE}; diff --git a/drivers/media/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c index 3b33f1ec2295..70ec80d8be71 100644 --- a/drivers/media/usb/dvb-usb-v2/az6007.c +++ b/drivers/media/usb/dvb-usb-v2/az6007.c @@ -189,7 +189,7 @@ static int az6007_streaming_ctrl(struct dvb_frontend *fe, int onoff) return az6007_write(d, 0xbc, onoff, 0, NULL, 0); } -#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) +#if IS_ENABLED(CONFIG_RC_CORE) /* remote control stuff (does not work with my box) */ static int az6007_rc_query(struct dvb_usb_device *d) { diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index 95968d39c1ca..086792055912 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -102,7 +102,7 @@ static int dvb_usbv2_i2c_exit(struct dvb_usb_device *d) return 0; } -#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) +#if IS_ENABLED(CONFIG_RC_CORE) static void dvb_usb_read_remote_control(struct work_struct *work) { struct dvb_usb_device *d = container_of(work, diff --git a/drivers/media/usb/dvb-usb-v2/it913x.c b/drivers/media/usb/dvb-usb-v2/it913x.c index 511c2aa00b97..833847995c65 100644 --- a/drivers/media/usb/dvb-usb-v2/it913x.c +++ b/drivers/media/usb/dvb-usb-v2/it913x.c @@ -308,7 +308,7 @@ static struct i2c_algorithm it913x_i2c_algo = { }; /* Callbacks for DVB USB */ -#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) +#if IS_ENABLED(CONFIG_RC_CORE) static int it913x_rc_query(struct dvb_usb_device *d) { u8 ibuf[4]; diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 9a6890392e90..2d024716d630 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1125,7 +1125,7 @@ err: return ret; } -#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) +#if IS_ENABLED(CONFIG_RC_CORE) static int rtl2831u_rc_query(struct dvb_usb_device *d) { int ret, i; @@ -1212,7 +1212,7 @@ static int rtl2831u_get_rc_config(struct dvb_usb_device *d, #define rtl2831u_get_rc_config NULL #endif -#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) +#if IS_ENABLED(CONFIG_RC_CORE) static int rtl2832u_rc_query(struct dvb_usb_device *d) { int ret, i; -- cgit v1.2.3 From 3971e79a8707cc529f474c1d25e5502ddfa33ebc Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 16 Dec 2012 11:47:07 -0300 Subject: [media] rtl28xxu: [1b80:d3a8] ASUS My Cinema-U3100Mini Plus V2 RF-tuner is Fitipower FC0013 Reported-by: Renato Gallo Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 2d024716d630..ca54674e0f43 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1364,6 +1364,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = { &rtl2832u_props, "Dexatek DK mini DVB-T Dongle", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, 0x00d7, &rtl2832u_props, "TerraTec Cinergy T Stick+", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd3a8, + &rtl2832u_props, "ASUS My Cinema-U3100Mini Plus V2", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); -- cgit v1.2.3 From 78a5e70919e9416344409e523e53221613776d80 Mon Sep 17 00:00:00 2001 From: Alexander Inyukhin Date: Fri, 4 Jan 2013 18:19:02 -0300 Subject: [media] rtl28xxu: add Gigabyte U7300 DVB-T Dongle Device with ID 1b80:d393 is the Gigabyte U7300 DVB-T dongle. It contains decoder Realtek RTL2832U and tuner Fitipower FC0012. [crope@iki.fi: fix trivial merge conflict] Signed-off-by: Alexander Inyukhin Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index ca54674e0f43..39e2d7c3ed0d 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1366,6 +1366,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = { &rtl2832u_props, "TerraTec Cinergy T Stick+", NULL) }, { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd3a8, &rtl2832u_props, "ASUS My Cinema-U3100Mini Plus V2", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd393, + &rtl2832u_props, "Gigabyte U7300 DVB-T Dongle", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); -- cgit v1.2.3 From 9db15638e1bd4a1cfdb0a42907308ad441030a80 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 4 Jan 2013 18:35:00 -0300 Subject: [media] rtl28xxu: correct some device names Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 39e2d7c3ed0d..4ab0dd8dd9a8 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1345,13 +1345,13 @@ static const struct usb_device_id rtl28xxu_id_table[] = { { DVB_USB_DEVICE(USB_VID_REALTEK, 0x2838, &rtl2832u_props, "Realtek RTL2832U reference design", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_BLACK_REV1, - &rtl2832u_props, "Terratec Cinergy T Stick Black", NULL) }, + &rtl2832u_props, "TerraTec Cinergy T Stick Black", NULL) }, { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_DELOCK_USB2_DVBT, &rtl2832u_props, "G-Tek Electronics Group Lifeview LV5TDLX DVB-T", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK, - &rtl2832u_props, "NOXON DAB/DAB+ USB dongle", NULL) }, + &rtl2832u_props, "TerraTec NOXON DAB Stick", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK_REV2, - &rtl2832u_props, "NOXON DAB/DAB+ USB dongle (rev 2)", NULL) }, + &rtl2832u_props, "TerraTec NOXON DAB Stick (rev 2)", NULL) }, { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_TREKSTOR_TERRES_2_0, &rtl2832u_props, "Trekstor DVB-T Stick Terres 2.0", NULL) }, { DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1101, @@ -1367,7 +1367,7 @@ static const struct usb_device_id rtl28xxu_id_table[] = { { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd3a8, &rtl2832u_props, "ASUS My Cinema-U3100Mini Plus V2", NULL) }, { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd393, - &rtl2832u_props, "Gigabyte U7300 DVB-T Dongle", NULL) }, + &rtl2832u_props, "GIGABYTE U7300", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); -- cgit v1.2.3 From a96fbe0429ace98bf3d92340ab9caa03c80db88c Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 3 Jan 2013 12:53:53 -0300 Subject: [media] dib0700: do not lock interruptible on tear-down paths When mutex_lock_interruptible is used on paths where a signal can be pending, the device is not closed properly and cannot be reused. This usually happens when you start tzap for example and send it a TERM signal. The signal is pending while tear-down routines are called. Hence streaming is not properly stopped in that case. And the device stops working from that moment on. Signed-off-by: Jiri Slaby Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/dib0700_core.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c index 19b5ed2825d7..bf2a908d74cf 100644 --- a/drivers/media/usb/dvb-usb/dib0700_core.c +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -561,10 +561,7 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) } } - if (mutex_lock_interruptible(&adap->dev->usb_mutex) < 0) { - err("could not acquire lock"); - return -EINTR; - } + mutex_lock(&adap->dev->usb_mutex); st->buf[0] = REQUEST_ENABLE_VIDEO; /* this bit gives a kind of command, -- cgit v1.2.3 From 9898df6482f71fa0d27b029789ef2f37988c06b3 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Thu, 3 Jan 2013 16:37:25 -0300 Subject: [media] ts2020.c: ts2020_set_params [BUG] point to fe->tuner_priv Fixes corruption of fe->demodulator_priv Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/ts2020.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index 94e3fe21eefb..f50e237e1464 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c @@ -182,7 +182,7 @@ static int ts2020_set_tuner_rf(struct dvb_frontend *fe) static int ts2020_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct ts2020_priv *priv = fe->demodulator_priv; + struct ts2020_priv *priv = fe->tuner_priv; int ret; u32 frequency = c->frequency; s32 offset_khz; -- cgit v1.2.3 From c4fe29a32ffa16c1166edacad1edc2dcf0aaa08c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 4 Jan 2013 14:56:02 -0300 Subject: [media] dvb: unlock on error in dvb_ca_en50221_io_do_ioctl() We recently pushed the locking down into this function, but there was an error path where the unlock was missed. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_ca_en50221.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index 190e5e0f48c7..0aac3096728e 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -1227,8 +1227,10 @@ static int dvb_ca_en50221_io_do_ioctl(struct file *file, case CA_GET_SLOT_INFO: { struct ca_slot_info *info = parg; - if ((info->num > ca->slot_count) || (info->num < 0)) - return -EINVAL; + if ((info->num > ca->slot_count) || (info->num < 0)) { + err = -EINVAL; + goto out_unlock; + } info->type = CA_CI_LINK; info->flags = 0; @@ -1247,6 +1249,7 @@ static int dvb_ca_en50221_io_do_ioctl(struct file *file, break; } +out_unlock: mutex_unlock(&ca->ioctl_mutex); return err; } -- cgit v1.2.3 From 5e8d02bb346d6240b029f1990ddc295d7d59685b Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 5 Jan 2013 08:44:09 -0300 Subject: [media] em28xx: fix audio input for TV mode of device Terratec Cinergy 250 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remy Blank reported that audio over USB can be made working for the television input if .amux is changed from EM28XX_AMUX_LINE_IN to EM28XX_AMUX_VIDEO. An examination of his devices shows, that it is indeed supplied with an EM202 AC97 audio IC. We also use this setting for the Cinergy 200. Remy Blank also provided the original version of this patch (many thanks !). Fixes bug 14126 (see bug report for further device details). Signed-off-by: Frank Schäfer Signed-off-by: Remy Blank Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 4d849bfa2db3..0a5aa6223adf 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -493,7 +493,7 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_LINE_IN, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, -- cgit v1.2.3 From 668a8b3b57e26a14a5172c84da0d861fb9f697d9 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 5 Jan 2013 18:56:10 -0300 Subject: [media] v4l: Don't compile v4l2-int-device unless really needed Add a configuration option for v4l2-int-device so it is only compiled when necessary, which is only by omap24xxcam and tcm825x drivers. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 2 +- drivers/media/platform/Kconfig | 2 +- drivers/media/v4l2-core/Kconfig | 11 +++++++++++ drivers/media/v4l2-core/Makefile | 3 ++- 4 files changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 24d78e28e493..1e4b2d0f23ce 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -477,7 +477,7 @@ config VIDEO_MT9V032 config VIDEO_TCM825X tristate "TCM825x camera sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_INT_DEVICE depends on MEDIA_CAMERA_SUPPORT ---help--- This is a driver for the Toshiba TCM825x VGA camera sensor. diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index c071b079de23..2433e2bbeee4 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -92,7 +92,7 @@ config VIDEO_M32R_AR_M64278 config VIDEO_OMAP2 tristate "OMAP2 Camera Capture Interface driver" - depends on VIDEO_DEV && ARCH_OMAP2 + depends on VIDEO_DEV && ARCH_OMAP2 && VIDEO_V4L2_INT_DEVICE select VIDEOBUF_DMA_SG ---help--- This is a v4l2 driver for the TI OMAP2 camera capture interface diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index 65875c3aba1b..976d029e9925 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -82,3 +82,14 @@ config VIDEOBUF2_DMA_SG #depends on HAS_DMA select VIDEOBUF2_CORE select VIDEOBUF2_MEMOPS + +config VIDEO_V4L2_INT_DEVICE + tristate "V4L2 int device (DEPRECATED)" + depends on VIDEO_V4L2 + ---help--- + An early framework for a hardware-independent interface for + image sensors and bridges etc. Currently used by omap24xxcam and + tcm825x drivers that should be converted to V4L2 subdev. + + Do not use for new developments. + diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index c2d61d4f03d1..a9d355230e8e 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -10,7 +10,8 @@ ifeq ($(CONFIG_COMPAT),y) videodev-objs += v4l2-compat-ioctl32.o endif -obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-int-device.o +obj-$(CONFIG_VIDEO_DEV) += videodev.o +obj-$(CONFIG_VIDEO_V4L2_INT_DEVICE) += v4l2-int-device.o obj-$(CONFIG_VIDEO_V4L2) += v4l2-common.o obj-$(CONFIG_VIDEO_TUNER) += tuner.o -- cgit v1.2.3 From a0a030bdbe612b7d8a941fba672300f7fc21b275 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sun, 6 Jan 2013 08:40:42 -0300 Subject: [media] ts2020: call get_rf_strength from frontend Restore ds3000.c read_signal_strength. Call tuner get_rf_strength from frontend read_signal_strength. We are able to do a NULL check and doesn't limit the tuner attach to the frontend attach area. At the moment the lmedm04 tuner attach is stuck in frontend attach area. Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/ds3000.c | 10 ++++++++++ drivers/media/dvb-frontends/m88rs2000.c | 4 +++- drivers/media/dvb-frontends/ts2020.c | 1 - 3 files changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c index d128f85844e7..1e344b033277 100644 --- a/drivers/media/dvb-frontends/ds3000.c +++ b/drivers/media/dvb-frontends/ds3000.c @@ -533,6 +533,15 @@ static int ds3000_read_ber(struct dvb_frontend *fe, u32* ber) return 0; } +static int ds3000_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + if (fe->ops.tuner_ops.get_rf_strength) + fe->ops.tuner_ops.get_rf_strength(fe, signal_strength); + + return 0; +} + /* calculate DS3000 snr value in dB */ static int ds3000_read_snr(struct dvb_frontend *fe, u16 *snr) { @@ -1102,6 +1111,7 @@ static struct dvb_frontend_ops ds3000_ops = { .i2c_gate_ctrl = ds3000_i2c_gate_ctrl, .read_status = ds3000_read_status, .read_ber = ds3000_read_ber, + .read_signal_strength = ds3000_read_signal_strength, .read_snr = ds3000_read_snr, .read_ucblocks = ds3000_read_ucblocks, .set_voltage = ds3000_set_voltage, diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index 283c90fee374..4da5272075cb 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -446,7 +446,9 @@ static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber) static int m88rs2000_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { - *strength = 0; + if (fe->ops.tuner_ops.get_rf_strength) + fe->ops.tuner_ops.get_rf_strength(fe, strength); + return 0; } diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index f50e237e1464..ad7ad857ab2a 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c @@ -363,7 +363,6 @@ struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe, memcpy(&fe->ops.tuner_ops, &ts2020_tuner_ops, sizeof(struct dvb_tuner_ops)); - fe->ops.read_signal_strength = fe->ops.tuner_ops.get_rf_strength; return fe; } -- cgit v1.2.3 From 55ee64b30a38d688232e5eb2860467dddc493573 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 16 Dec 2012 16:04:46 -0300 Subject: [media] omap_vout: find_vma() needs ->mmap_sem held Walking rbtree while it's modified is a Bad Idea(tm); besides, the result of find_vma() can be freed just as it's getting returned to caller. Fortunately, it's easy to fix - just take ->mmap_sem a bit earlier (and don't bother with find_vma() at all if virtp >= PAGE_OFFSET - in that case we don't even look at its result). While we are at it, what prevents VIDIOC_PREPARE_BUF calling v4l_prepare_buf() -> (e.g) vb2_ioctl_prepare_buf() -> vb2_prepare_buf() -> __buf_prepare() -> __qbuf_userptr() -> vb2_vmalloc_get_userptr() -> find_vma(), AFAICS without having taken ->mmap_sem anywhere in process? The code flow is bloody convoluted and depends on a bunch of things done by initialization, so I certainly might've missed something... Cc: stable@vger.kernel.org [2.6.35] Signed-off-by: Al Viro Cc: Sakari Ailus Cc: Laurent Pinchart Cc: Archit Taneja Cc: Prabhakar Lad Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap/omap_vout.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index dade3ceab092..96c4a17e4280 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -205,19 +205,21 @@ static u32 omap_vout_uservirt_to_phys(u32 virtp) struct vm_area_struct *vma; struct mm_struct *mm = current->mm; - vma = find_vma(mm, virtp); /* For kernel direct-mapped memory, take the easy way */ - if (virtp >= PAGE_OFFSET) { - physp = virt_to_phys((void *) virtp); - } else if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) { + if (virtp >= PAGE_OFFSET) + return virt_to_phys((void *) virtp); + + down_read(¤t->mm->mmap_sem); + vma = find_vma(mm, virtp); + if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) { /* this will catch, kernel-allocated, mmaped-to-usermode addresses */ physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start); + up_read(¤t->mm->mmap_sem); } else { /* otherwise, use get_user_pages() for general userland pages */ int res, nr_pages = 1; struct page *pages; - down_read(¤t->mm->mmap_sem); res = get_user_pages(current, current->mm, virtp, nr_pages, 1, 0, &pages, NULL); -- cgit v1.2.3 From 73ec66c000e9816806c7380ca3420f4e0638c40e Mon Sep 17 00:00:00 2001 From: Evgeny Plehov Date: Thu, 29 Nov 2012 15:27:38 -0300 Subject: [media] stv0900: Multistream support Multistream support for stv0900. For Netup Dual S2 CI with STV0900BAC/AAC. Signed-off-by: Evgeny Plehov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/stv0900_core.c | 26 ++++++++++++++++++++++++++ drivers/media/dvb-frontends/stv0900_reg.h | 3 +++ 2 files changed, 29 insertions(+) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c index b551ca350e00..0fb34e1909d1 100644 --- a/drivers/media/dvb-frontends/stv0900_core.c +++ b/drivers/media/dvb-frontends/stv0900_core.c @@ -1558,6 +1558,27 @@ static int stv0900_status(struct stv0900_internal *intp, return locked; } +static int stv0900_set_mis(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod, int mis) +{ + enum fe_stv0900_error error = STV0900_NO_ERROR; + + dprintk("%s\n", __func__); + + if (mis < 0 || mis > 255) { + dprintk("Disable MIS filtering\n"); + stv0900_write_bits(intp, FILTER_EN, 0); + } else { + dprintk("Enable MIS filtering - %d\n", mis); + stv0900_write_bits(intp, FILTER_EN, 1); + stv0900_write_reg(intp, ISIENTRY, mis); + stv0900_write_reg(intp, ISIBITENA, 0xff); + } + + return error; +} + + static enum dvbfe_search stv0900_search(struct dvb_frontend *fe) { struct stv0900_state *state = fe->demodulator_priv; @@ -1578,6 +1599,8 @@ static enum dvbfe_search stv0900_search(struct dvb_frontend *fe) if (state->config->set_ts_params) state->config->set_ts_params(fe, 0); + stv0900_set_mis(intp, demod, c->stream_id); + p_result.locked = FALSE; p_search.path = demod; p_search.frequency = c->frequency; @@ -1935,6 +1958,9 @@ struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, if (err_stv0900) goto error; + if (state->internal->chip_id >= 0x30) + state->frontend.ops.info.caps |= FE_CAN_MULTISTREAM; + break; default: goto error; diff --git a/drivers/media/dvb-frontends/stv0900_reg.h b/drivers/media/dvb-frontends/stv0900_reg.h index 731afe93a823..511ed2a2d987 100644 --- a/drivers/media/dvb-frontends/stv0900_reg.h +++ b/drivers/media/dvb-frontends/stv0900_reg.h @@ -3446,8 +3446,11 @@ extern s32 shiftx(s32 x, int demod, s32 shift); #define R0900_P1_PDELCTRL1 0xf550 #define PDELCTRL1 REGx(R0900_P1_PDELCTRL1) #define F0900_P1_INV_MISMASK 0xf5500080 +#define INV_MISMASK FLDx(F0900_P1_INV_MISMASK) #define F0900_P1_FILTER_EN 0xf5500020 +#define FILTER_EN FLDx(F0900_P1_FILTER_EN) #define F0900_P1_EN_MIS00 0xf5500002 +#define EN_MIS00 FLDx(F0900_P1_EN_MIS00) #define F0900_P1_ALGOSWRST 0xf5500001 #define ALGOSWRST FLDx(F0900_P1_ALGOSWRST) -- cgit v1.2.3 From 9a6cecc846169159bfce511f4c0034bb96eea1ca Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Fri, 14 Sep 2012 17:41:57 -0500 Subject: dmaengine: add helper function to request a slave DMA channel Currently slave DMA channels are requested by calling dma_request_channel() and requires DMA clients to pass various filter parameters to obtain the appropriate channel. With device-tree being used by architectures such as arm and the addition of device-tree helper functions to extract the relevant DMA client information from device-tree, add a new function to request a slave DMA channel using device-tree. This function is currently a simple wrapper that calls the device-tree of_dma_request_slave_channel() function. Cc: Nicolas Ferre Cc: Benoit Cousson Cc: Stephen Warren Cc: Grant Likely Cc: Russell King Cc: Rob Herring Cc: Arnd Bergmann Cc: Vinod Koul Cc: Dan Williams Acked-by: Arnd Bergmann Signed-off-by: Jon Hunter Reviewed-by: Stephen Warren Acked-by: Rob Herring Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 16 ++++++++++++++++ include/linux/dmaengine.h | 6 ++++++ 2 files changed, 22 insertions(+) (limited to 'drivers') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index a815d44c70a4..d37cf95987b6 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -62,6 +62,7 @@ #include #include #include +#include static DEFINE_MUTEX(dma_list_mutex); static DEFINE_IDR(dma_idr); @@ -546,6 +547,21 @@ struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, v } EXPORT_SYMBOL_GPL(__dma_request_channel); +/** + * dma_request_slave_channel - try to allocate an exclusive slave channel + * @dev: pointer to client device structure + * @name: slave channel name + */ +struct dma_chan *dma_request_slave_channel(struct device *dev, char *name) +{ + /* If device-tree is present get slave info from here */ + if (dev->of_node) + return of_dma_request_slave_channel(dev->of_node, name); + + return NULL; +} +EXPORT_SYMBOL_GPL(dma_request_slave_channel); + void dma_release_channel(struct dma_chan *chan) { mutex_lock(&dma_list_mutex); diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index d3201e438d16..8cd0e2556d04 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -974,6 +974,7 @@ enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie); enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx); void dma_issue_pending_all(void); struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, void *fn_param); +struct dma_chan *dma_request_slave_channel(struct device *dev, char *name); void dma_release_channel(struct dma_chan *chan); #else static inline enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx) @@ -988,6 +989,11 @@ static inline struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, { return NULL; } +static inline struct dma_chan *dma_request_slave_channel(struct device *dev, + char *name) +{ + return NULL +} static inline void dma_release_channel(struct dma_chan *chan) { } -- cgit v1.2.3 From aa3da644c76d1c2083d085e453c18f7c51f19bc4 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Fri, 14 Sep 2012 17:41:56 -0500 Subject: of: Add generic device tree DMA helpers This is based upon the work by Benoit Cousson [1] and Nicolas Ferre [2] to add some basic helpers to retrieve a DMA controller device_node and the DMA request/channel information. Aim of DMA helpers - The purpose of device-tree is to describe the capabilites of the hardware. Thinking about DMA controllers purely from the context of the hardware to begin with, we can describe a device in terms of a DMA controller as follows ... 1. Number of DMA controllers 2. Number of channels (maybe physical or logical) 3. Mapping of DMA requests signals to DMA controller 4. Number of DMA interrupts 5. Mapping of DMA interrupts to channels - With the above in mind the aim of the DT DMA helper functions is to extract the above information from the DT and provide to the appropriate driver. However, due to the vast number of DMA controllers and not all are using a common driver (such as DMA Engine) it has been seen that this is not a trivial task. In previous discussions on this topic the following concerns have been raised ... 1. How does the binding support devices with multiple DMA controllers? 2. How to support both legacy DMA controllers not using DMA Engine as well as those that support DMA Engine. 3. When using with DMA Engine how do we support the various implementations where the opaque filter function parameter differs between implementations? 4. How do we handle DMA channels that are identified with a string versus a integer? - Hence the design of the DMA helpers has to accomodate the above or align on an agreement what can be or should be supported. Design of DMA helpers 1. Registering DMA controllers In the case of DMA controllers that are using DMA Engine, requesting a channel is performed by calling the following function. struct dma_chan *dma_request_channel(dma_cap_mask_t mask, dma_filter_fn filter_fn, void *filter_param); The mask variable is used to match a type of the device controller in a list of controllers. The filter_fn and filter_param are used to identify the required dma channel and return a handle to the dma channel of type dma_chan. From the examples I have seen, the mask and filter_fn are constant for a given DMA controller and therefore, we can specify these as controller specific data when registering the DMA controller with the device-tree DMA helpers. The filter_param variable is of an unknown type and is typically specific to the DMA engine implementation for a given DMA controller. To allow some flexibility in the type and formating of this filter_param we employ an xlate to translate the device-tree binding information into the appropriate format. The xlate function used for a DMA controller can also be specified when registering the DMA controller with the device-tree DMA helpers. Based upon the above, a function for registering the DMA controller with the DMA helpers now looks like the below. The data variable is used to pass a pointer to DMA controller specific data used by the xlate function. int of_dma_controller_register(struct device_node *np, struct dma_chan *(*of_dma_xlate) (struct of_phandle_args *, struct of_dma *), void *data) For example, in the case where DMA engine is used, we define the following structure (that stores the DMA engine capability mask and filter function) and pass this to the data variable in the above function. struct of_dma_filter_info { dma_cap_mask_t dma_cap; dma_filter_fn filter_fn; }; 2. Representing and requesting channel information Please see the dma binding documentation included in this patch for a description of how DMA controllers and client information should be represented with device-tree. For more information on how this binding came about please see [3]. In addition to this, feedback received from the Linux kernel summit showed a consensus (among those who attended) to use a name to identify DMA client information [4]. A DMA channel can be requested by calling the following function, where name is a required parameter used for identifying a DMA channel. This function has been designed to return a structure of type dma_chan to work with the DMA engine driver. Note that if DMA engine is used then drivers should be using the DMA engine API dma_request_slave_channel() (implemented in part 2 of this series, "dmaengine: add helper function to request a slave DMA channel") which will in turn call the below function if device-tree is present. The aim being to have a common DMA engine interface regardless of whether device tree is being used. struct dma_chan *of_dma_request_slave_channel(struct device_node *np, char *name) 3. Supporting legacy devices not using DMA Engine These devices present a problem, as there may not be a uniform way to easily support them with regard to device tree. Ideally, these should be migrated to DMA engine. However, if this is not possible, then they should still be able to use this binding, the only constaint imposed by this implementation is that when requesting a DMA channel via of_dma_request_slave_channel(), it will return a type of dma_chan. This implementation has been tested on OMAP4430 using the kernel v3.6-rc5. I have validated that MMC is working on the PANDA board with this implementation. My development branch for testing on OMAP can be found here [5]. v6: - minor corrections in DMA binding documentation v5: - minor update to binding documentation - added loop to exhaustively search for a slave channel in the case where there could be alternative channels available v4: - revert the removal of xlate function from v3 - update the proposed binding format and APIs based upon discussions [3] v3: - avoid passing an xlate function and instead pass DMA engine parameters - define number of dma channels and requests in dma-controller node v2: - remove of_dma_to_resource API - make property #dma-cells required (no fallback anymore) - another check in of_dma_xlate_onenumbercell() function [1] http://article.gmane.org/gmane.linux.drivers.devicetree/12022 [2] http://article.gmane.org/gmane.linux.ports.arm.omap/73622 [3] http://marc.info/?l=linux-omap&m=133582085008539&w=2 [4] http://pad.linaro.org/arm-mini-summit-2012 [5] https://github.com/jonhunter/linux/tree/dev-dt-dma Cc: Nicolas Ferre Cc: Benoit Cousson Cc: Stephen Warren Cc: Grant Likely Cc: Russell King Cc: Rob Herring Cc: Arnd Bergmann Cc: Vinod Koul Cc: Dan Williams Reviewed-by: Arnd Bergmann Reviewed-by: Nicolas Ferre Signed-off-by: Jon Hunter Reviewed-by: Stephen Warren Acked-by: Rob Herring Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/dma.txt | 81 ++++++++++ drivers/of/Makefile | 2 +- drivers/of/dma.c | 219 ++++++++++++++++++++++++++ include/linux/of_dma.h | 45 ++++++ 4 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/dma/dma.txt create mode 100644 drivers/of/dma.c create mode 100644 include/linux/of_dma.h (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/dma/dma.txt b/Documentation/devicetree/bindings/dma/dma.txt new file mode 100644 index 000000000000..a4f59a5967d4 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/dma.txt @@ -0,0 +1,81 @@ +* Generic DMA Controller and DMA request bindings + +Generic binding to provide a way for a driver using DMA Engine to retrieve the +DMA request or channel information that goes from a hardware device to a DMA +controller. + + +* DMA controller + +Required property: +- #dma-cells: Must be at least 1. Used to provide DMA controller + specific information. See DMA client binding below for + more details. + +Optional properties: +- #dma-channels: Number of DMA channels supported by the controller. +- #dma-requests: Number of DMA requests signals supported by the + controller. + +Example: + + dma: dma@48000000 { + compatible = "ti,omap-sdma" + reg = <0x48000000 0x1000>; + interrupts = <0 12 0x4 + 0 13 0x4 + 0 14 0x4 + 0 15 0x4>; + #dma-cells = <1>; + #dma-channels = <32>; + #dma-requests = <127>; + }; + + +* DMA client + +Client drivers should specify the DMA property using a phandle to the controller +followed by DMA controller specific data. + +Required property: +- dmas: List of one or more DMA specifiers, each consisting of + - A phandle pointing to DMA controller node + - A number of integer cells, as determined by the + #dma-cells property in the node referenced by phandle + containing DMA controller specific information. This + typically contains a DMA request line number or a + channel number, but can contain any data that is used + required for configuring a channel. +- dma-names: Contains one identifier string for each DMA specifier in + the dmas property. The specific strings that can be used + are defined in the binding of the DMA client device. + Multiple DMA specifiers can be used to represent + alternatives and in this case the dma-names for those + DMA specifiers must be identical (see examples). + +Examples: + +1. A device with one DMA read channel, one DMA write channel: + + i2c1: i2c@1 { + ... + dmas = <&dma 2 /* read channel */ + &dma 3>; /* write channel */ + dma-names = "rx", "tx" + ... + }; + +2. A single read-write channel with three alternative DMA controllers: + + dmas = <&dma1 5 + &dma2 7 + &dma3 2>; + dma-names = "rx-tx", "rx-tx", "rx-tx" + +3. A device with three channels, one of which has two alternatives: + + dmas = <&dma1 2 /* read channel */ + &dma1 3 /* write channel */ + &dma2 0 /* error read */ + &dma3 0>; /* alternative error read */ + dma-names = "rx", "tx", "error", "error"; diff --git a/drivers/of/Makefile b/drivers/of/Makefile index e027f444d10c..eafa107aed40 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -1,4 +1,4 @@ -obj-y = base.o +obj-y = base.o dma.o obj-$(CONFIG_OF_FLATTREE) += fdt.o obj-$(CONFIG_OF_PROMTREE) += pdt.o obj-$(CONFIG_OF_ADDRESS) += address.o diff --git a/drivers/of/dma.c b/drivers/of/dma.c new file mode 100644 index 000000000000..19ad37c066f5 --- /dev/null +++ b/drivers/of/dma.c @@ -0,0 +1,219 @@ +/* + * Device tree helpers for DMA request / controller + * + * Based on of_gpio.c + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(of_dma_list); + +/** + * of_dma_find_controller - Find a DMA controller in DT DMA helpers list + * @np: device node of DMA controller + */ +static struct of_dma *of_dma_find_controller(struct device_node *np) +{ + struct of_dma *ofdma; + + if (list_empty(&of_dma_list)) { + pr_err("empty DMA controller list\n"); + return NULL; + } + + list_for_each_entry_rcu(ofdma, &of_dma_list, of_dma_controllers) + if (ofdma->of_node == np) + return ofdma; + + return NULL; +} + +/** + * of_dma_controller_register - Register a DMA controller to DT DMA helpers + * @np: device node of DMA controller + * @of_dma_xlate: translation function which converts a phandle + * arguments list into a dma_chan structure + * @data pointer to controller specific data to be used by + * translation function + * + * Returns 0 on success or appropriate errno value on error. + * + * Allocated memory should be freed with appropriate of_dma_controller_free() + * call. + */ +int of_dma_controller_register(struct device_node *np, + struct dma_chan *(*of_dma_xlate) + (struct of_phandle_args *, struct of_dma *), + void *data) +{ + struct of_dma *ofdma; + int nbcells; + + if (!np || !of_dma_xlate) { + pr_err("%s: not enough information provided\n", __func__); + return -EINVAL; + } + + ofdma = kzalloc(sizeof(*ofdma), GFP_KERNEL); + if (!ofdma) + return -ENOMEM; + + nbcells = be32_to_cpup(of_get_property(np, "#dma-cells", NULL)); + if (!nbcells) { + pr_err("%s: #dma-cells property is missing or invalid\n", + __func__); + return -EINVAL; + } + + ofdma->of_node = np; + ofdma->of_dma_nbcells = nbcells; + ofdma->of_dma_xlate = of_dma_xlate; + ofdma->of_dma_data = data; + + /* Now queue of_dma controller structure in list */ + list_add_tail_rcu(&ofdma->of_dma_controllers, &of_dma_list); + + return 0; +} +EXPORT_SYMBOL_GPL(of_dma_controller_register); + +/** + * of_dma_controller_free - Remove a DMA controller from DT DMA helpers list + * @np: device node of DMA controller + * + * Memory allocated by of_dma_controller_register() is freed here. + */ +void of_dma_controller_free(struct device_node *np) +{ + struct of_dma *ofdma; + + ofdma = of_dma_find_controller(np); + if (ofdma) { + list_del_rcu(&ofdma->of_dma_controllers); + kfree(ofdma); + } +} +EXPORT_SYMBOL_GPL(of_dma_controller_free); + +/** + * of_dma_find_channel - Find a DMA channel by name + * @np: device node to look for DMA channels + * @name: name of desired channel + * @dma_spec: pointer to DMA specifier as found in the device tree + * + * Find a DMA channel by the name. Returns 0 on success or appropriate + * errno value on error. + */ +static int of_dma_find_channel(struct device_node *np, char *name, + struct of_phandle_args *dma_spec) +{ + int count, i; + const char *s; + + count = of_property_count_strings(np, "dma-names"); + if (count < 0) + return count; + + for (i = 0; i < count; i++) { + if (of_property_read_string_index(np, "dma-names", i, &s)) + continue; + + if (strcmp(name, s)) + continue; + + if (!of_parse_phandle_with_args(np, "dmas", "#dma-cells", i, + dma_spec)) + return 0; + } + + return -ENODEV; +} + +/** + * of_dma_request_slave_channel - Get the DMA slave channel + * @np: device node to get DMA request from + * @name: name of desired channel + * + * Returns pointer to appropriate dma channel on success or NULL on error. + */ +struct dma_chan *of_dma_request_slave_channel(struct device_node *np, + char *name) +{ + struct of_phandle_args dma_spec; + struct of_dma *ofdma; + struct dma_chan *chan; + int r; + + if (!np || !name) { + pr_err("%s: not enough information provided\n", __func__); + return NULL; + } + + do { + r = of_dma_find_channel(np, name, &dma_spec); + if (r) { + pr_err("%s: can't find DMA channel\n", np->full_name); + return NULL; + } + + ofdma = of_dma_find_controller(dma_spec.np); + if (!ofdma) { + pr_debug("%s: can't find DMA controller %s\n", + np->full_name, dma_spec.np->full_name); + continue; + } + + if (dma_spec.args_count != ofdma->of_dma_nbcells) { + pr_debug("%s: wrong #dma-cells for %s\n", np->full_name, + dma_spec.np->full_name); + continue; + } + + chan = ofdma->of_dma_xlate(&dma_spec, ofdma); + + of_node_put(dma_spec.np); + + } while (!chan); + + return chan; +} + +/** + * of_dma_simple_xlate - Simple DMA engine translation function + * @dma_spec: pointer to DMA specifier as found in the device tree + * @of_dma: pointer to DMA controller data + * + * A simple translation function for devices that use a 32-bit value for the + * filter_param when calling the DMA engine dma_request_channel() function. + * Note that this translation function requires that #dma-cells is equal to 1 + * and the argument of the dma specifier is the 32-bit filter_param. Returns + * pointer to appropriate dma channel on success or NULL on error. + */ +struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + int count = dma_spec->args_count; + struct of_dma_filter_info *info = ofdma->of_dma_data; + + if (!info || !info->filter_fn) + return NULL; + + if (count != 1) + return NULL; + + return dma_request_channel(info->dma_cap, info->filter_fn, + &dma_spec->args[0]); +} +EXPORT_SYMBOL_GPL(of_dma_simple_xlate); diff --git a/include/linux/of_dma.h b/include/linux/of_dma.h new file mode 100644 index 000000000000..337823dc6b90 --- /dev/null +++ b/include/linux/of_dma.h @@ -0,0 +1,45 @@ +/* + * OF helpers for DMA request / controller + * + * Based on of_gpio.h + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 __LINUX_OF_DMA_H +#define __LINUX_OF_DMA_H + +#include +#include + +struct device_node; + +struct of_dma { + struct list_head of_dma_controllers; + struct device_node *of_node; + int of_dma_nbcells; + struct dma_chan *(*of_dma_xlate) + (struct of_phandle_args *, struct of_dma *); + void *of_dma_data; +}; + +struct of_dma_filter_info { + dma_cap_mask_t dma_cap; + dma_filter_fn filter_fn; +}; + +extern int of_dma_controller_register(struct device_node *np, + struct dma_chan *(*of_dma_xlate) + (struct of_phandle_args *, struct of_dma *), + void *data); +extern void of_dma_controller_free(struct device_node *np); +extern struct dma_chan *of_dma_request_slave_channel(struct device_node *np, + char *name); +extern struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma); + +#endif /* __LINUX_OF_DMA_H */ -- cgit v1.2.3 From c6a0aec9216064f756c4eda80be0720707c898d5 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 23 Oct 2012 13:01:54 -0700 Subject: drivers/dma: remove CONFIG_EXPERIMENTAL This config item has not carried much meaning for a while now and is almost always enabled by default. As agreed during the Linux kernel summit, remove it. CC: Vinod Koul CC: Dan Williams Signed-off-by: Kees Cook Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index d4c12180c654..93ea87e59fdf 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -51,7 +51,7 @@ config ASYNC_TX_ENABLE_CHANNEL_SWITCH config AMBA_PL08X bool "ARM PrimeCell PL080 or PL081 support" - depends on ARM_AMBA && EXPERIMENTAL + depends on ARM_AMBA select DMA_ENGINE select DMA_VIRTUAL_CHANNELS help -- cgit v1.2.3 From 53b9989bc75e185d0bf946e939ebe8534c8a9740 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 10 Oct 2012 21:04:58 +0800 Subject: pch_dma: use module_pci_driver to simplify the code Use the module_pci_driver() macro to make the code simpler by eliminating module_init and module_exit calls. dpatch engine is used to auto generate this patch. (https://github.com/weiyj/dpatch) Signed-off-by: Wei Yongjun Signed-off-by: Vinod Koul --- drivers/dma/pch_dma.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index eca1c4ddf039..aed6e0c117ed 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -1029,18 +1029,7 @@ static struct pci_driver pch_dma_driver = { #endif }; -static int __init pch_dma_init(void) -{ - return pci_register_driver(&pch_dma_driver); -} - -static void __exit pch_dma_exit(void) -{ - pci_unregister_driver(&pch_dma_driver); -} - -module_init(pch_dma_init); -module_exit(pch_dma_exit); +module_pci_driver(pch_dma_driver); MODULE_DESCRIPTION("Intel EG20T PCH / LAPIS Semicon ML7213/ML7223/ML7831 IOH " "DMA controller driver"); -- cgit v1.2.3 From 3ecd9d01f7dffd5a2291267ba0410e1826c87530 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Thu, 20 Dec 2012 14:11:23 -0500 Subject: PCI: cpqphp: Cleanup and remove unreachable paths Remove redundant checks and unreachable paths. Signed-off-by: Sasha Levin Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/cpqphp_ctrl.c | 57 ++++++++++++++------------------------- 1 file changed, 20 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c index 36112fe212d3..d282019cda5f 100644 --- a/drivers/pci/hotplug/cpqphp_ctrl.c +++ b/drivers/pci/hotplug/cpqphp_ctrl.c @@ -1900,8 +1900,7 @@ static void interrupt_event_handler(struct controller *ctrl) dbg("power fault\n"); } else { /* refresh notification */ - if (p_slot) - update_slot_info(ctrl, p_slot); + update_slot_info(ctrl, p_slot); } ctrl->event_queue[loop].event_type = 0; @@ -2520,44 +2519,28 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func /* If we have IO resources copy them and fill in the bridge's * IO range registers */ - if (io_node) { - memcpy(hold_IO_node, io_node, sizeof(struct pci_resource)); - io_node->next = NULL; + memcpy(hold_IO_node, io_node, sizeof(struct pci_resource)); + io_node->next = NULL; - /* set IO base and Limit registers */ - temp_byte = io_node->base >> 8; - rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_BASE, temp_byte); + /* set IO base and Limit registers */ + temp_byte = io_node->base >> 8; + rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_BASE, temp_byte); - temp_byte = (io_node->base + io_node->length - 1) >> 8; - rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte); - } else { - kfree(hold_IO_node); - hold_IO_node = NULL; - } - - /* If we have memory resources copy them and fill in the - * bridge's memory range registers. Otherwise, fill in the - * range registers with values that disable them. */ - if (mem_node) { - memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource)); - mem_node->next = NULL; - - /* set Mem base and Limit registers */ - temp_word = mem_node->base >> 16; - rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word); + temp_byte = (io_node->base + io_node->length - 1) >> 8; + rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte); - temp_word = (mem_node->base + mem_node->length - 1) >> 16; - rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); - } else { - temp_word = 0xFFFF; - rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word); + /* Copy the memory resources and fill in the bridge's memory + * range registers. + */ + memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource)); + mem_node->next = NULL; - temp_word = 0x0000; - rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); + /* set Mem base and Limit registers */ + temp_word = mem_node->base >> 16; + rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word); - kfree(hold_mem_node); - hold_mem_node = NULL; - } + temp_word = (mem_node->base + mem_node->length - 1) >> 16; + rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); memcpy(hold_p_mem_node, p_mem_node, sizeof(struct pci_resource)); p_mem_node->next = NULL; @@ -2627,7 +2610,7 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func /* Return unused bus resources * First use the temporary node to store information for * the board */ - if (hold_bus_node && bus_node && temp_resources.bus_head) { + if (bus_node && temp_resources.bus_head) { hold_bus_node->length = bus_node->base - hold_bus_node->base; hold_bus_node->next = func->bus_head; @@ -2751,7 +2734,7 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func } /* If we have prefetchable memory space available and there * is some left at the end, return the unused portion */ - if (hold_p_mem_node && temp_resources.p_mem_head) { + if (temp_resources.p_mem_head) { p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head), &hold_p_mem_node, 0x100000); -- cgit v1.2.3 From 3c449ed0075994b3f3371f8254560428ba787efc Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 3 Nov 2012 21:39:31 -0700 Subject: PCI/ACPI: Reserve firmware-allocated resources for hot-added root buses Firmware may have assigned PCI BARs for hot-added devices, so reserve those resources before trying to allocate more. [bhelgaas: move empty weak definition here] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/acpi/pci_root.c | 4 +++- drivers/pci/bus.c | 2 ++ include/linux/pci.h | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 7928d4dc7056..dcbe9660e756 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -650,8 +650,10 @@ static int acpi_pci_root_start(struct acpi_device *device) struct acpi_pci_root *root = acpi_driver_data(device); struct acpi_pci_driver *driver; - if (system_state != SYSTEM_BOOTING) + if (system_state != SYSTEM_BOOTING) { + pcibios_resource_survey_bus(root->bus); pci_assign_unassigned_bus_resources(root->bus); + } mutex_lock(&acpi_pci_root_lock); list_for_each_entry(driver, &acpi_pci_drivers, node) diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index ad6a8b635692..847f3ca47bb8 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -158,6 +158,8 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, return ret; } +void __weak pcibios_resource_survey_bus(struct pci_bus *bus) { } + /** * pci_bus_add_device - add a single device * @dev: device to add diff --git a/include/linux/pci.h b/include/linux/pci.h index 15472d691ee6..907b455ab603 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -674,6 +674,7 @@ extern struct list_head pci_root_buses; /* list of all known PCI buses */ /* Some device drivers need know if pci is initiated */ extern int no_pci_devices(void); +void pcibios_resource_survey_bus(struct pci_bus *bus); void pcibios_fixup_bus(struct pci_bus *); int __must_check pcibios_enable_device(struct pci_dev *, int mask); /* Architecture specific versions may override this (weak) */ -- cgit v1.2.3 From a9ddb575d6d6c58c39e8c44a22b84445fedb0521 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 16 Oct 2012 09:49:17 +0530 Subject: dmaengine: dw_dmac: Enhance device tree support dw_dmac driver already supports device tree but it used to have its platform data passed the non-DT way. This patch does following changes: - pass platform data via DT, non-DT way still takes precedence if both are used. - create generic filter routine - Earlier slave information was made available by slave specific filter routines in chan->private field. Now, this information would be passed from within dmac DT node. Slave drivers would now be required to pass bus_id (a string) as parameter to this generic filter(), which would be compared against the slave data passed from DT, by the generic filter routine. - Update binding document Signed-off-by: Viresh Kumar Reviewed-by: Andy Shevchenko [Fixed __devinit usage] Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/snps-dma.txt | 44 +++++++ drivers/dma/dw_dmac.c | 134 +++++++++++++++++++++ drivers/dma/dw_dmac_regs.h | 4 + include/linux/dw_dmac.h | 43 ++++--- 4 files changed, 208 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/dma/snps-dma.txt b/Documentation/devicetree/bindings/dma/snps-dma.txt index c0d85dbcada5..5bb3dfb6f1d8 100644 --- a/Documentation/devicetree/bindings/dma/snps-dma.txt +++ b/Documentation/devicetree/bindings/dma/snps-dma.txt @@ -6,6 +6,26 @@ Required properties: - interrupt-parent: Should be the phandle for the interrupt controller that services interrupts for this device - interrupt: Should contain the DMAC interrupt number +- nr_channels: Number of channels supported by hardware +- is_private: The device channels should be marked as private and not for by the + general purpose DMA channel allocator. False if not passed. +- chan_allocation_order: order of allocation of channel, 0 (default): ascending, + 1: descending +- chan_priority: priority of channels. 0 (default): increase from chan 0->n, 1: + increase from chan n->0 +- block_size: Maximum block size supported by the controller +- nr_masters: Number of AHB masters supported by the controller +- data_width: Maximum data width supported by hardware per AHB master + (0 - 8bits, 1 - 16bits, ..., 5 - 256bits) +- slave_info: + - bus_id: name of this device channel, not just a device name since + devices may have more than one channel e.g. "foo_tx". For using the + dw_generic_filter(), slave drivers must pass exactly this string as + param to filter function. + - cfg_hi: Platform-specific initializer for the CFG_HI register + - cfg_lo: Platform-specific initializer for the CFG_LO register + - src_master: src master for transfers on allocated channel. + - dst_master: dest master for transfers on allocated channel. Example: @@ -14,4 +34,28 @@ Example: reg = <0xfc000000 0x1000>; interrupt-parent = <&vic1>; interrupts = <12>; + + nr_channels = <8>; + chan_allocation_order = <1>; + chan_priority = <1>; + block_size = <0xfff>; + nr_masters = <2>; + data_width = <3 3 0 0>; + + slave_info { + uart0-tx { + bus_id = "uart0-tx"; + cfg_hi = <0x4000>; /* 0x8 << 11 */ + cfg_lo = <0>; + src_master = <0>; + dst_master = <1>; + }; + spi0-tx { + bus_id = "spi0-tx"; + cfg_hi = <0x2000>; /* 0x4 << 11 */ + cfg_lo = <0>; + src_master = <0>; + dst_master = <0>; + }; + }; }; diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 8f0b111af4de..27b8e1d1845e 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1179,6 +1179,50 @@ static void dwc_free_chan_resources(struct dma_chan *chan) dev_vdbg(chan2dev(chan), "%s: done\n", __func__); } +bool dw_dma_generic_filter(struct dma_chan *chan, void *param) +{ + struct dw_dma *dw = to_dw_dma(chan->device); + static struct dw_dma *last_dw; + static char *last_bus_id; + int i = -1; + + /* + * dmaengine framework calls this routine for all channels of all dma + * controller, until true is returned. If 'param' bus_id is not + * registered with a dma controller (dw), then there is no need of + * running below function for all channels of dw. + * + * This block of code does this by saving the parameters of last + * failure. If dw and param are same, i.e. trying on same dw with + * different channel, return false. + */ + if ((last_dw == dw) && (last_bus_id == param)) + return false; + /* + * Return true: + * - If dw_dma's platform data is not filled with slave info, then all + * dma controllers are fine for transfer. + * - Or if param is NULL + */ + if (!dw->sd || !param) + return true; + + while (++i < dw->sd_count) { + if (!strcmp(dw->sd[i].bus_id, param)) { + chan->private = &dw->sd[i]; + last_dw = NULL; + last_bus_id = NULL; + + return true; + } + } + + last_dw = dw; + last_bus_id = param; + return false; +} +EXPORT_SYMBOL(dw_dma_generic_filter); + /* --------------------- Cyclic DMA API extensions -------------------- */ /** @@ -1462,6 +1506,91 @@ static void dw_dma_off(struct dw_dma *dw) dw->chan[i].initialized = false; } +#ifdef CONFIG_OF +static struct dw_dma_platform_data * +dw_dma_parse_dt(struct platform_device *pdev) +{ + struct device_node *sn, *cn, *np = pdev->dev.of_node; + struct dw_dma_platform_data *pdata; + struct dw_dma_slave *sd; + u32 tmp, arr[4]; + + if (!np) { + dev_err(&pdev->dev, "Missing DT data\n"); + return NULL; + } + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + if (of_property_read_u32(np, "nr_channels", &pdata->nr_channels)) + return NULL; + + if (of_property_read_bool(np, "is_private")) + pdata->is_private = true; + + if (!of_property_read_u32(np, "chan_allocation_order", &tmp)) + pdata->chan_allocation_order = (unsigned char)tmp; + + if (!of_property_read_u32(np, "chan_priority", &tmp)) + pdata->chan_priority = tmp; + + if (!of_property_read_u32(np, "block_size", &tmp)) + pdata->block_size = tmp; + + if (!of_property_read_u32(np, "nr_masters", &tmp)) { + if (tmp > 4) + return NULL; + + pdata->nr_masters = tmp; + } + + if (!of_property_read_u32_array(np, "data_width", arr, + pdata->nr_masters)) + for (tmp = 0; tmp < pdata->nr_masters; tmp++) + pdata->data_width[tmp] = arr[tmp]; + + /* parse slave data */ + sn = of_find_node_by_name(np, "slave_info"); + if (!sn) + return pdata; + + /* calculate number of slaves */ + tmp = of_get_child_count(sn); + if (!tmp) + return NULL; + + sd = devm_kzalloc(&pdev->dev, sizeof(*sd) * tmp, GFP_KERNEL); + if (!sd) + return NULL; + + pdata->sd = sd; + pdata->sd_count = tmp; + + for_each_child_of_node(sn, cn) { + sd->dma_dev = &pdev->dev; + of_property_read_string(cn, "bus_id", &sd->bus_id); + of_property_read_u32(cn, "cfg_hi", &sd->cfg_hi); + of_property_read_u32(cn, "cfg_lo", &sd->cfg_lo); + if (!of_property_read_u32(cn, "src_master", &tmp)) + sd->src_master = tmp; + + if (!of_property_read_u32(cn, "dst_master", &tmp)) + sd->dst_master = tmp; + sd++; + } + + return pdata; +} +#else +static inline struct dw_dma_platform_data * +dw_dma_parse_dt(struct platform_device *pdev) +{ + return NULL; +} +#endif + static int dw_probe(struct platform_device *pdev) { struct dw_dma_platform_data *pdata; @@ -1478,6 +1607,9 @@ static int dw_probe(struct platform_device *pdev) int i; pdata = dev_get_platdata(&pdev->dev); + if (!pdata) + pdata = dw_dma_parse_dt(pdev); + if (!pdata || pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) return -EINVAL; @@ -1512,6 +1644,8 @@ static int dw_probe(struct platform_device *pdev) clk_prepare_enable(dw->clk); dw->regs = regs; + dw->sd = pdata->sd; + dw->sd_count = pdata->sd_count; /* get hardware configuration parameters */ if (autocfg) { diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 88965597b7d0..88a069f66b89 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -239,6 +239,10 @@ struct dw_dma { struct tasklet_struct tasklet; struct clk *clk; + /* slave information */ + struct dw_dma_slave *sd; + unsigned int sd_count; + u8 all_chan_mask; /* hardware configuration */ diff --git a/include/linux/dw_dmac.h b/include/linux/dw_dmac.h index 62a6190dee22..41766de66e33 100644 --- a/include/linux/dw_dmac.h +++ b/include/linux/dw_dmac.h @@ -14,6 +14,26 @@ #include +/** + * struct dw_dma_slave - Controller-specific information about a slave + * + * @dma_dev: required DMA master device. Depricated. + * @bus_id: name of this device channel, not just a device name since + * devices may have more than one channel e.g. "foo_tx" + * @cfg_hi: Platform-specific initializer for the CFG_HI register + * @cfg_lo: Platform-specific initializer for the CFG_LO register + * @src_master: src master for transfers on allocated channel. + * @dst_master: dest master for transfers on allocated channel. + */ +struct dw_dma_slave { + struct device *dma_dev; + const char *bus_id; + u32 cfg_hi; + u32 cfg_lo; + u8 src_master; + u8 dst_master; +}; + /** * struct dw_dma_platform_data - Controller configuration parameters * @nr_channels: Number of channels supported by hardware (max 8) @@ -25,6 +45,8 @@ * @nr_masters: Number of AHB masters supported by the controller * @data_width: Maximum data width supported by hardware per AHB master * (0 - 8bits, 1 - 16bits, ..., 5 - 256bits) + * @sd: slave specific data. Used for configuring channels + * @sd_count: count of slave data structures passed. */ struct dw_dma_platform_data { unsigned int nr_channels; @@ -38,6 +60,9 @@ struct dw_dma_platform_data { unsigned short block_size; unsigned char nr_masters; unsigned char data_width[4]; + + struct dw_dma_slave *sd; + unsigned int sd_count; }; /* bursts size */ @@ -52,23 +77,6 @@ enum dw_dma_msize { DW_DMA_MSIZE_256, }; -/** - * struct dw_dma_slave - Controller-specific information about a slave - * - * @dma_dev: required DMA master device - * @cfg_hi: Platform-specific initializer for the CFG_HI register - * @cfg_lo: Platform-specific initializer for the CFG_LO register - * @src_master: src master for transfers on allocated channel. - * @dst_master: dest master for transfers on allocated channel. - */ -struct dw_dma_slave { - struct device *dma_dev; - u32 cfg_hi; - u32 cfg_lo; - u8 src_master; - u8 dst_master; -}; - /* Platform-configurable bits in CFG_HI */ #define DWC_CFGH_FCMODE (1 << 0) #define DWC_CFGH_FIFO_MODE (1 << 1) @@ -106,5 +114,6 @@ void dw_dma_cyclic_stop(struct dma_chan *chan); dma_addr_t dw_dma_get_src_addr(struct dma_chan *chan); dma_addr_t dw_dma_get_dst_addr(struct dma_chan *chan); +bool dw_dma_generic_filter(struct dma_chan *chan, void *param); #endif /* DW_DMAC_H */ -- cgit v1.2.3 From b73d6c0ae4d74588a2653835a2452944722cc64b Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 18 Oct 2012 17:34:07 +0300 Subject: dmaengine: dw_dmac: remove CLK dependency This driver could be used on different platforms. Thus, the HAVE_CLK dependency is dropped away. Signed-off-by: Heikki Krogerus Signed-off-by: Andy Shevchenko Reviewed-by: Felipe Balbi Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 93ea87e59fdf..6761cc8a59d5 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -83,7 +83,6 @@ config INTEL_IOP_ADMA config DW_DMAC tristate "Synopsys DesignWare AHB DMA support" - depends on HAVE_CLK select DMA_ENGINE default y if CPU_AT32AP7000 help -- cgit v1.2.3 From b801479bb6f5b66f5a35feaa72451ac5ac152ef5 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 18 Oct 2012 17:34:08 +0300 Subject: dmaengine: dw_dmac: amend description and indentation The driver will be used as a core part for various implementations of the DesignWare DMA device. The patch adjusts description on the top and corrects paragraph indentation in few places across the code. Signed-off-by: Heikki Krogerus Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Reviewed-by: Felipe Balbi Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 27b8e1d1845e..62bd85c8a517 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1,6 +1,5 @@ /* - * Driver for the Synopsys DesignWare DMA Controller (aka DMACA on - * AVR32 systems.) + * Core driver for the Synopsys DesignWare DMA Controller * * Copyright (C) 2007-2008 Atmel Corporation * Copyright (C) 2010-2011 ST Microelectronics @@ -9,6 +8,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + #include #include #include @@ -222,7 +222,6 @@ static inline void dwc_dump_chan_regs(struct dw_dma_chan *dwc) channel_readl(dwc, CTL_LO)); } - static inline void dwc_chan_disable(struct dw_dma *dw, struct dw_dma_chan *dwc) { channel_clear_bit(dw, CH_EN, dwc->mask); @@ -1813,6 +1812,7 @@ static int dw_resume_noirq(struct device *dev) clk_prepare_enable(dw->clk); dma_writel(dw, CFG, DW_CFG_DMA_EN); + return 0; } -- cgit v1.2.3 From 21d43f49cb5e132e74bde9e34ac0760f79ab85a9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 18 Oct 2012 17:34:09 +0300 Subject: dw_dmac: change dev_printk() to corresponding macros Change printk(KERN_INFO ..., dev_name(...), ...) to dev_info() as well. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Reviewed-by: Felipe Balbi Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 62bd85c8a517..155d3f108ab4 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -456,9 +456,8 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) static inline void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_lli *lli) { - dev_printk(KERN_CRIT, chan2dev(&dwc->chan), - " desc: s0x%x d0x%x l0x%x c0x%x:%x\n", - lli->sar, lli->dar, lli->llp, lli->ctlhi, lli->ctllo); + dev_crit(chan2dev(&dwc->chan), " desc: s0x%x d0x%x l0x%x c0x%x:%x\n", + lli->sar, lli->dar, lli->llp, lli->ctlhi, lli->ctllo); } static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) @@ -492,10 +491,8 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) * controller flagged an error instead of scribbling over * random memory locations. */ - dev_printk(KERN_CRIT, chan2dev(&dwc->chan), - "Bad descriptor submitted for DMA!\n"); - dev_printk(KERN_CRIT, chan2dev(&dwc->chan), - " cookie: %d\n", bad_desc->txd.cookie); + dev_crit(chan2dev(&dwc->chan), "Bad descriptor submitted for DMA!\n"); + dev_crit(chan2dev(&dwc->chan), " cookie: %d\n", bad_desc->txd.cookie); dwc_dump_lli(dwc, &bad_desc->lli); list_for_each_entry(child, &bad_desc->tx_list, desc_node) dwc_dump_lli(dwc, &child->lli); @@ -1759,8 +1756,8 @@ static int dw_probe(struct platform_device *pdev) dma_writel(dw, CFG, DW_CFG_DMA_EN); - printk(KERN_INFO "%s: DesignWare DMA Controller, %d channels\n", - dev_name(&pdev->dev), nr_channels); + dev_info(&pdev->dev, "DesignWare DMA Controller, %d channels\n", + nr_channels); dma_async_device_register(&dw->dma); -- cgit v1.2.3 From 6168d5670bd764557b5e06b1842964a44cf34a45 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 18 Oct 2012 17:34:10 +0300 Subject: dw_dmac: don't call platform_get_drvdata twice There is no need to call platform_get_drvdata twice as we have it already in dw variable. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Reviewed-by: Felipe Balbi Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 155d3f108ab4..d60ef9c7dd68 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1787,7 +1787,7 @@ static void dw_shutdown(struct platform_device *pdev) { struct dw_dma *dw = platform_get_drvdata(pdev); - dw_dma_off(platform_get_drvdata(pdev)); + dw_dma_off(dw); clk_disable_unprepare(dw->clk); } @@ -1796,7 +1796,7 @@ static int dw_suspend_noirq(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct dw_dma *dw = platform_get_drvdata(pdev); - dw_dma_off(platform_get_drvdata(pdev)); + dw_dma_off(dw); clk_disable_unprepare(dw->clk); return 0; -- cgit v1.2.3 From ba84bd7146b9244de0ce04cdc668521a73f5336f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 18 Oct 2012 17:34:11 +0300 Subject: dw_dmac: change dev_crit to dev_WARN in dwc_handle_error In case of handling a bad descriptor the dwc_handle_error() will dump a stack as well. It's a lot more verbose and more likely to get user's attention. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Reviewed-by: Felipe Balbi Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index d60ef9c7dd68..9a27fb70c950 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -485,14 +485,14 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) dwc_dostart(dwc, dwc_first_active(dwc)); /* - * KERN_CRITICAL may seem harsh, but since this only happens + * WARN may seem harsh, but since this only happens * when someone submits a bad physical address in a * descriptor, we should consider ourselves lucky that the * controller flagged an error instead of scribbling over * random memory locations. */ - dev_crit(chan2dev(&dwc->chan), "Bad descriptor submitted for DMA!\n"); - dev_crit(chan2dev(&dwc->chan), " cookie: %d\n", bad_desc->txd.cookie); + dev_WARN(chan2dev(&dwc->chan), "Bad descriptor submitted for DMA!\n" + " cookie: %d\n", bad_desc->txd.cookie); dwc_dump_lli(dwc, &bad_desc->lli); list_for_each_entry(child, &bad_desc->tx_list, desc_node) dwc_dump_lli(dwc, &child->lli); -- cgit v1.2.3 From e63a47a361e03eaf79e0f2f6cdaca8e7679d1867 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 18 Oct 2012 17:34:12 +0300 Subject: dw_dmac: introduce to_dw_desc() macro The to_dw_desc() macro helps to retrieve the dw_desc node from the corresponding list_head structure. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Reviewed-by: Felipe Balbi Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 6 ++---- drivers/dma/dw_dmac_regs.h | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 9a27fb70c950..476e9c8fb6ca 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -93,7 +93,7 @@ static struct device *chan2parent(struct dma_chan *chan) static struct dw_desc *dwc_first_active(struct dw_dma_chan *dwc) { - return list_entry(dwc->active_list.next, struct dw_desc, desc_node); + return to_dw_desc(dwc->active_list.next); } static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) @@ -600,9 +600,7 @@ static void dw_dma_tasklet(unsigned long data) if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { if (dwc->tx_node_active != dwc->tx_list) { struct dw_desc *desc = - list_entry(dwc->tx_node_active, - struct dw_desc, - desc_node); + to_dw_desc(dwc->tx_node_active); dma_writel(dw, CLEAR.XFER, dwc->mask); diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 88a069f66b89..8881e9b277a3 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -299,6 +299,8 @@ struct dw_desc { size_t len; }; +#define to_dw_desc(h) list_entry(h, struct dw_desc, desc_node) + static inline struct dw_desc * txd_to_dw_desc(struct dma_async_tx_descriptor *txd) { -- cgit v1.2.3 From 91998261dd0d5aefb56d87cef84f7810c32f6194 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 28 Oct 2012 00:49:31 +0900 Subject: dma: amba-pl08x: use vchan_dma_desc_free_list vchan_dma_desc_free_list() iterates through each virt_dma_desc in the specified list_head and calls vchan->desc_free(). We can use it instead of repeated execution of pl08x_desc_free() for each virt_dma_desc in the list_head. Because vchan->desc_free callback is set as pl08x_desc_free() for amba-pl08x driver. Signed-off-by: Akinobu Mita Cc: Vinod Koul Cc: Dan Williams Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index d1cc5791476b..6eb6a5c210bb 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1096,15 +1096,9 @@ static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x, struct pl08x_dma_chan *plchan) { LIST_HEAD(head); - struct pl08x_txd *txd; vchan_get_all_descriptors(&plchan->vc, &head); - - while (!list_empty(&head)) { - txd = list_first_entry(&head, struct pl08x_txd, vd.node); - list_del(&txd->vd.node); - pl08x_desc_free(&txd->vd); - } + vchan_dma_desc_free_list(&plchan->vc, &head); } /* -- cgit v1.2.3 From 8be9e32b310cd8c4302991c8ff6692689c7d9d76 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 28 Oct 2012 00:49:32 +0900 Subject: dmatest: adjust invalid module parameters for number of source buffers DMA Engine test module has module parameters to set the number of source buffers for xor and pq operations. We can set these values larger than the maximum number of sources that the device can support. These values are not adjusted and the unsupported number of source buffers are passed to the device. But most drivers don't check it, so unexpected results will happen. This makes an appropriate adjustment for these module parameters before use. Signed-off-by: Akinobu Mita Cc: Vinod Koul Cc: Dan Williams Signed-off-by: Vinod Koul --- drivers/dma/dmatest.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index 64b048d7fba7..3a8047b1f108 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -242,6 +242,13 @@ static inline void unmap_dst(struct device *dev, dma_addr_t *addr, size_t len, dma_unmap_single(dev, addr[count], len, DMA_BIDIRECTIONAL); } +static unsigned int min_odd(unsigned int x, unsigned int y) +{ + unsigned int val = min(x, y); + + return val % 2 ? val : val - 1; +} + /* * This function repeatedly tests DMA transfers of various lengths and * offsets for a given operation type until it is told to exit by @@ -262,6 +269,7 @@ static int dmatest_func(void *data) struct dmatest_thread *thread = data; struct dmatest_done done = { .wait = &done_wait }; struct dma_chan *chan; + struct dma_device *dev; const char *thread_name; unsigned int src_off, dst_off, len; unsigned int error_count; @@ -283,13 +291,16 @@ static int dmatest_func(void *data) smp_rmb(); chan = thread->chan; + dev = chan->device; if (thread->type == DMA_MEMCPY) src_cnt = dst_cnt = 1; else if (thread->type == DMA_XOR) { - src_cnt = xor_sources | 1; /* force odd to ensure dst = src */ + /* force odd to ensure dst = src */ + src_cnt = min_odd(xor_sources | 1, dev->max_xor); dst_cnt = 1; } else if (thread->type == DMA_PQ) { - src_cnt = pq_sources | 1; /* force odd to ensure dst = src */ + /* force odd to ensure dst = src */ + src_cnt = min_odd(pq_sources | 1, dma_maxpq(dev, 0)); dst_cnt = 2; for (i = 0; i < src_cnt; i++) pq_coefs[i] = 1; @@ -327,7 +338,6 @@ static int dmatest_func(void *data) while (!kthread_should_stop() && !(iterations && total_tests >= iterations)) { - struct dma_device *dev = chan->device; struct dma_async_tx_descriptor *tx = NULL; dma_addr_t dma_srcs[src_cnt]; dma_addr_t dma_dsts[dst_cnt]; -- cgit v1.2.3 From 1ba151cdf5ac120fc829ee6524fefedc6828947f Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 28 Oct 2012 01:05:44 -0700 Subject: dma: Convert dev_printk(KERN_ to dev_( dev_ calls take less code than dev_printk(KERN_ and reducing object size is good. Coalesce formats for easier grep. Signed-off-by: Joe Perches Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac_regs.h | 8 ++++---- drivers/dma/iop-adma.c | 45 ++++++++++++++++++++++----------------------- drivers/dma/mv_xor.c | 40 ++++++++++++++++------------------------ 3 files changed, 42 insertions(+), 51 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index 116e4adffb08..0eb3c1388667 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h @@ -369,10 +369,10 @@ static void vdbg_dump_regs(struct at_dma_chan *atchan) {} static void atc_dump_lli(struct at_dma_chan *atchan, struct at_lli *lli) { - dev_printk(KERN_CRIT, chan2dev(&atchan->chan_common), - " desc: s0x%x d0x%x ctrl0x%x:0x%x l0x%x\n", - lli->saddr, lli->daddr, - lli->ctrla, lli->ctrlb, lli->dscr); + dev_crit(chan2dev(&atchan->chan_common), + " desc: s0x%x d0x%x ctrl0x%x:0x%x l0x%x\n", + lli->saddr, lli->daddr, + lli->ctrla, lli->ctrlb, lli->dscr); } diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index 9072e173b860..62a59fd65723 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -936,7 +936,7 @@ static irqreturn_t iop_adma_err_handler(int irq, void *data) struct iop_adma_chan *chan = data; unsigned long status = iop_chan_get_status(chan); - dev_printk(KERN_ERR, chan->device->common.dev, + dev_err(chan->device->common.dev, "error ( %s%s%s%s%s%s%s)\n", iop_is_err_int_parity(status, chan) ? "int_parity " : "", iop_is_err_mcu_abort(status, chan) ? "mcu_abort " : "", @@ -1017,7 +1017,7 @@ static int iop_adma_memcpy_self_test(struct iop_adma_device *device) if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test copy timed out, disabling\n"); err = -ENODEV; goto free_resources; @@ -1027,7 +1027,7 @@ static int iop_adma_memcpy_self_test(struct iop_adma_device *device) dma_sync_single_for_cpu(&iop_chan->device->pdev->dev, dest_dma, IOP_ADMA_TEST_SIZE, DMA_FROM_DEVICE); if (memcmp(src, dest, IOP_ADMA_TEST_SIZE)) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test copy failed compare, disabling\n"); err = -ENODEV; goto free_resources; @@ -1117,7 +1117,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test xor timed out, disabling\n"); err = -ENODEV; goto free_resources; @@ -1129,7 +1129,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) for (i = 0; i < (PAGE_SIZE / sizeof(u32)); i++) { u32 *ptr = page_address(dest); if (ptr[i] != cmp_word) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test xor failed compare, disabling\n"); err = -ENODEV; goto free_resources; @@ -1163,14 +1163,14 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) msleep(8); if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test zero sum timed out, disabling\n"); err = -ENODEV; goto free_resources; } if (zero_sum_result != 0) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test zero sum failed compare, disabling\n"); err = -ENODEV; goto free_resources; @@ -1187,7 +1187,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) msleep(8); if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test memset timed out, disabling\n"); err = -ENODEV; goto free_resources; @@ -1196,7 +1196,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) for (i = 0; i < PAGE_SIZE/sizeof(u32); i++) { u32 *ptr = page_address(dest); if (ptr[i]) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test memset failed compare, disabling\n"); err = -ENODEV; goto free_resources; @@ -1219,14 +1219,14 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) msleep(8); if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test non-zero sum timed out, disabling\n"); err = -ENODEV; goto free_resources; } if (zero_sum_result != 1) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test non-zero sum failed compare, disabling\n"); err = -ENODEV; goto free_resources; @@ -1579,15 +1579,14 @@ static int iop_adma_probe(struct platform_device *pdev) goto err_free_iop_chan; } - dev_printk(KERN_INFO, &pdev->dev, "Intel(R) IOP: " - "( %s%s%s%s%s%s%s)\n", - dma_has_cap(DMA_PQ, dma_dev->cap_mask) ? "pq " : "", - dma_has_cap(DMA_PQ_VAL, dma_dev->cap_mask) ? "pq_val " : "", - dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "", - dma_has_cap(DMA_XOR_VAL, dma_dev->cap_mask) ? "xor_val " : "", - dma_has_cap(DMA_MEMSET, dma_dev->cap_mask) ? "fill " : "", - dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "", - dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : ""); + dev_info(&pdev->dev, "Intel(R) IOP: ( %s%s%s%s%s%s%s)\n", + dma_has_cap(DMA_PQ, dma_dev->cap_mask) ? "pq " : "", + dma_has_cap(DMA_PQ_VAL, dma_dev->cap_mask) ? "pq_val " : "", + dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "", + dma_has_cap(DMA_XOR_VAL, dma_dev->cap_mask) ? "xor_val " : "", + dma_has_cap(DMA_MEMSET, dma_dev->cap_mask) ? "fill " : "", + dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "", + dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : ""); dma_async_device_register(dma_dev); goto out; @@ -1651,8 +1650,8 @@ static void iop_chan_start_null_memcpy(struct iop_adma_chan *iop_chan) /* run the descriptor */ iop_chan_enable(iop_chan); } else - dev_printk(KERN_ERR, iop_chan->device->common.dev, - "failed to allocate null descriptor\n"); + dev_err(iop_chan->device->common.dev, + "failed to allocate null descriptor\n"); spin_unlock_bh(&iop_chan->lock); } @@ -1704,7 +1703,7 @@ static void iop_chan_start_null_xor(struct iop_adma_chan *iop_chan) /* run the descriptor */ iop_chan_enable(iop_chan); } else - dev_printk(KERN_ERR, iop_chan->device->common.dev, + dev_err(iop_chan->device->common.dev, "failed to allocate null descriptor\n"); spin_unlock_bh(&iop_chan->lock); } diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index ac71f555dd72..e26de4f680e1 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -210,7 +210,7 @@ static void mv_set_mode(struct mv_xor_chan *chan, break; default: dev_err(mv_chan_to_devp(chan), - "error: unsupported operation %d.\n", + "error: unsupported operation %d\n", type); BUG(); return; @@ -828,28 +828,22 @@ static void mv_dump_xor_regs(struct mv_xor_chan *chan) u32 val; val = __raw_readl(XOR_CONFIG(chan)); - dev_err(mv_chan_to_devp(chan), - "config 0x%08x.\n", val); + dev_err(mv_chan_to_devp(chan), "config 0x%08x\n", val); val = __raw_readl(XOR_ACTIVATION(chan)); - dev_err(mv_chan_to_devp(chan), - "activation 0x%08x.\n", val); + dev_err(mv_chan_to_devp(chan), "activation 0x%08x\n", val); val = __raw_readl(XOR_INTR_CAUSE(chan)); - dev_err(mv_chan_to_devp(chan), - "intr cause 0x%08x.\n", val); + dev_err(mv_chan_to_devp(chan), "intr cause 0x%08x\n", val); val = __raw_readl(XOR_INTR_MASK(chan)); - dev_err(mv_chan_to_devp(chan), - "intr mask 0x%08x.\n", val); + dev_err(mv_chan_to_devp(chan), "intr mask 0x%08x\n", val); val = __raw_readl(XOR_ERROR_CAUSE(chan)); - dev_err(mv_chan_to_devp(chan), - "error cause 0x%08x.\n", val); + dev_err(mv_chan_to_devp(chan), "error cause 0x%08x\n", val); val = __raw_readl(XOR_ERROR_ADDR(chan)); - dev_err(mv_chan_to_devp(chan), - "error addr 0x%08x.\n", val); + dev_err(mv_chan_to_devp(chan), "error addr 0x%08x\n", val); } static void mv_xor_err_interrupt_handler(struct mv_xor_chan *chan, @@ -862,7 +856,7 @@ static void mv_xor_err_interrupt_handler(struct mv_xor_chan *chan, } dev_err(mv_chan_to_devp(chan), - "error on chan %d. intr cause 0x%08x.\n", + "error on chan %d. intr cause 0x%08x\n", chan->idx, intr_cause); mv_dump_xor_regs(chan); @@ -1052,9 +1046,8 @@ mv_xor_xor_self_test(struct mv_xor_chan *mv_chan) u32 *ptr = page_address(dest); if (ptr[i] != cmp_word) { dev_err(dma_chan->device->dev, - "Self-test xor failed compare, disabling." - " index %d, data %x, expected %x\n", i, - ptr[i], cmp_word); + "Self-test xor failed compare, disabling. index %d, data %x, expected %x\n", + i, ptr[i], cmp_word); err = -ENODEV; goto free_resources; } @@ -1194,12 +1187,11 @@ mv_xor_channel_add(struct mv_xor_device *xordev, goto err_free_irq; } - dev_info(&pdev->dev, "Marvell XOR: " - "( %s%s%s%s)\n", - dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "", - dma_has_cap(DMA_MEMSET, dma_dev->cap_mask) ? "fill " : "", - dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "", - dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : ""); + dev_info(&pdev->dev, "Marvell XOR: ( %s%s%s%s)\n", + dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "", + dma_has_cap(DMA_MEMSET, dma_dev->cap_mask) ? "fill " : "", + dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "", + dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : ""); dma_async_device_register(dma_dev); return mv_chan; @@ -1253,7 +1245,7 @@ static int mv_xor_probe(struct platform_device *pdev) struct resource *res; int i, ret; - dev_notice(&pdev->dev, "Marvell XOR driver\n"); + dev_notice(&pdev->dev, "Marvell shared XOR driver\n"); xordev = devm_kzalloc(&pdev->dev, sizeof(*xordev), GFP_KERNEL); if (!xordev) -- cgit v1.2.3 From 522d974451743abcf674cbebd7c29d44fbd63586 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 5 Nov 2012 10:00:13 +0000 Subject: ioat: add missing DMA unmap to ioat_dma_self_test() Make ioat_dma_self_test() do DMA unmapping itself and fix handling of failure cases. Cc: Dan Williams Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams --- drivers/dma/ioat/dma.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index 73b2b65cb1de..464138a8a782 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c @@ -833,14 +833,14 @@ int __devinit ioat_dma_self_test(struct ioatdma_device *device) dma_src = dma_map_single(dev, src, IOAT_TEST_SIZE, DMA_TO_DEVICE); dma_dest = dma_map_single(dev, dest, IOAT_TEST_SIZE, DMA_FROM_DEVICE); - flags = DMA_COMPL_SRC_UNMAP_SINGLE | DMA_COMPL_DEST_UNMAP_SINGLE | + flags = DMA_COMPL_SKIP_SRC_UNMAP | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT; tx = device->common.device_prep_dma_memcpy(dma_chan, dma_dest, dma_src, IOAT_TEST_SIZE, flags); if (!tx) { dev_err(dev, "Self-test prep failed, disabling\n"); err = -ENODEV; - goto free_resources; + goto unmap_dma; } async_tx_ack(tx); @@ -851,7 +851,7 @@ int __devinit ioat_dma_self_test(struct ioatdma_device *device) if (cookie < 0) { dev_err(dev, "Self-test setup failed, disabling\n"); err = -ENODEV; - goto free_resources; + goto unmap_dma; } dma->device_issue_pending(dma_chan); @@ -862,7 +862,7 @@ int __devinit ioat_dma_self_test(struct ioatdma_device *device) != DMA_SUCCESS) { dev_err(dev, "Self-test copy timed out, disabling\n"); err = -ENODEV; - goto free_resources; + goto unmap_dma; } if (memcmp(src, dest, IOAT_TEST_SIZE)) { dev_err(dev, "Self-test copy failed compare, disabling\n"); @@ -870,6 +870,9 @@ int __devinit ioat_dma_self_test(struct ioatdma_device *device) goto free_resources; } +unmap_dma: + dma_unmap_single(dev, dma_src, IOAT_TEST_SIZE, DMA_TO_DEVICE); + dma_unmap_single(dev, dma_dest, IOAT_TEST_SIZE, DMA_FROM_DEVICE); free_resources: dma->device_free_chan_resources(dma_chan); out: -- cgit v1.2.3 From d1806a5c4d2248d2799f4367dbdb1800be94a26f Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 5 Nov 2012 10:00:14 +0000 Subject: mtd: fsmc_nand: add missing DMA unmap to dma_xfer() Make dma_xfer() do DMA unmapping itself and fix handling of failure cases. Cc: David Woodhouse Cc: Vipin Kumar Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams --- drivers/mtd/nand/fsmc_nand.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 1d7446434b0e..a66576863e53 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -573,23 +573,22 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, dma_dev = chan->device; dma_addr = dma_map_single(dma_dev->dev, buffer, len, direction); + flags |= DMA_COMPL_SKIP_SRC_UNMAP | DMA_COMPL_SKIP_DEST_UNMAP; + if (direction == DMA_TO_DEVICE) { dma_src = dma_addr; dma_dst = host->data_pa; - flags |= DMA_COMPL_SRC_UNMAP_SINGLE | DMA_COMPL_SKIP_DEST_UNMAP; } else { dma_src = host->data_pa; dma_dst = dma_addr; - flags |= DMA_COMPL_DEST_UNMAP_SINGLE | DMA_COMPL_SKIP_SRC_UNMAP; } tx = dma_dev->device_prep_dma_memcpy(chan, dma_dst, dma_src, len, flags); - if (!tx) { dev_err(host->dev, "device_prep_dma_memcpy error\n"); - dma_unmap_single(dma_dev->dev, dma_addr, len, direction); - return -EIO; + ret = -EIO; + goto unmap_dma; } tx->callback = dma_complete; @@ -599,7 +598,7 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, ret = dma_submit_error(cookie); if (ret) { dev_err(host->dev, "dma_submit_error %d\n", cookie); - return ret; + goto unmap_dma; } dma_async_issue_pending(chan); @@ -610,10 +609,17 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, if (ret <= 0) { chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); dev_err(host->dev, "wait_for_completion_timeout\n"); - return ret ? ret : -ETIMEDOUT; + if (!ret) + ret = -ETIMEDOUT; + goto unmap_dma; } - return 0; + ret = 0; + +unmap_dma: + dma_unmap_single(dma_dev->dev, dma_addr, len, direction); + + return ret; } /* -- cgit v1.2.3 From bfc191ea568a9c00ab652750686f83ad2daf92a8 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 5 Nov 2012 10:00:15 +0000 Subject: carma-fpga: pass correct flags to ->device_prep_dma_memcpy() DMA unmapping is handled by a driver so tell fsldma.c driver (which is the DMA engine driver used by carma-fpga) to skip unmapping destination and source buffers. Cc: Ira W. Snyder Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams --- drivers/misc/carma/carma-fpga.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c index 8835eabb3b87..6b43f8c7b3be 100644 --- a/drivers/misc/carma/carma-fpga.c +++ b/drivers/misc/carma/carma-fpga.c @@ -631,6 +631,8 @@ static int data_submit_dma(struct fpga_device *priv, struct data_buf *buf) struct dma_async_tx_descriptor *tx; dma_cookie_t cookie; dma_addr_t dst, src; + unsigned long dma_flags = DMA_COMPL_SKIP_DEST_UNMAP | + DMA_COMPL_SKIP_SRC_UNMAP; dst_sg = buf->vb.sglist; dst_nents = buf->vb.sglen; @@ -666,7 +668,7 @@ static int data_submit_dma(struct fpga_device *priv, struct data_buf *buf) src = SYS_FPGA_BLOCK; tx = chan->device->device_prep_dma_memcpy(chan, dst, src, REG_BLOCK_SIZE, - 0); + dma_flags); if (!tx) { dev_err(priv->dev, "unable to prep SYS-FPGA DMA\n"); return -ENOMEM; -- cgit v1.2.3 From 5ca7c109e8eee8d97267d3308548509bb80d8bf0 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 25 Sep 2012 13:59:31 -0500 Subject: of: dma: fix potential deadlock when requesting a slave channel In the latest version of the OF dma handlers I added support (rather hastily) to exhaustively search for an available dma slave channel, for the use-case where we have alternative slave channels that can be used. In the current implementation a deadlock scenario can occur causing the CPU to loop forever. The scenario is as follows ... 1. There are alternative channels avaialble 2. The first channel that is found by calling of_dma_find_channel() is not available and so the call to the xlate function returns NULL. In this case we will call of_dma_find_channel() again but we will return the same channel that we found the first time and hence, again the xlate will return NULL and we will loop here forever. Fix this potential deadlock by just using a single for-loop and not a for-loop nested in a do-while loop. This change also replaces the function of_dma_find_channel() with of_dma_match_channel() which performs a simple check to see if a DMA channel matches the name specified. I have tested this implementation on an OMAP4 panda board by adding a dummy DMA specifier, that will cause the xlate function to return NULL, to the beginning of a list of DMA specifiers for a DMA client. Cc: Nicolas Ferre Cc: Benoit Cousson Cc: Stephen Warren Cc: Grant Likely Cc: Russell King Cc: Rob Herring Cc: Arnd Bergmann Cc: Vinod Koul Cc: Dan Williams Signed-off-by: Jon Hunter Signed-off-by: Vinod Koul --- drivers/of/dma.c | 60 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/of/dma.c b/drivers/of/dma.c index 19ad37c066f5..4bed490a69e4 100644 --- a/drivers/of/dma.c +++ b/drivers/of/dma.c @@ -108,37 +108,32 @@ void of_dma_controller_free(struct device_node *np) EXPORT_SYMBOL_GPL(of_dma_controller_free); /** - * of_dma_find_channel - Find a DMA channel by name + * of_dma_match_channel - Check if a DMA specifier matches name * @np: device node to look for DMA channels - * @name: name of desired channel + * @name: channel name to be matched + * @index: index of DMA specifier in list of DMA specifiers * @dma_spec: pointer to DMA specifier as found in the device tree * - * Find a DMA channel by the name. Returns 0 on success or appropriate - * errno value on error. + * Check if the DMA specifier pointed to by the index in a list of DMA + * specifiers, matches the name provided. Returns 0 if the name matches and + * a valid pointer to the DMA specifier is found. Otherwise returns -ENODEV. */ -static int of_dma_find_channel(struct device_node *np, char *name, - struct of_phandle_args *dma_spec) +static int of_dma_match_channel(struct device_node *np, char *name, int index, + struct of_phandle_args *dma_spec) { - int count, i; const char *s; - count = of_property_count_strings(np, "dma-names"); - if (count < 0) - return count; + if (of_property_read_string_index(np, "dma-names", index, &s)) + return -ENODEV; - for (i = 0; i < count; i++) { - if (of_property_read_string_index(np, "dma-names", i, &s)) - continue; + if (strcmp(name, s)) + return -ENODEV; - if (strcmp(name, s)) - continue; + if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", index, + dma_spec)) + return -ENODEV; - if (!of_parse_phandle_with_args(np, "dmas", "#dma-cells", i, - dma_spec)) - return 0; - } - - return -ENODEV; + return 0; } /** @@ -154,19 +149,22 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np, struct of_phandle_args dma_spec; struct of_dma *ofdma; struct dma_chan *chan; - int r; + int count, i; if (!np || !name) { pr_err("%s: not enough information provided\n", __func__); return NULL; } - do { - r = of_dma_find_channel(np, name, &dma_spec); - if (r) { - pr_err("%s: can't find DMA channel\n", np->full_name); - return NULL; - } + count = of_property_count_strings(np, "dma-names"); + if (count < 0) { + pr_err("%s: dma-names property missing or empty\n", __func__); + return NULL; + } + + for (i = 0; i < count; i++) { + if (of_dma_match_channel(np, name, i, &dma_spec)) + continue; ofdma = of_dma_find_controller(dma_spec.np); if (!ofdma) { @@ -185,9 +183,11 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np, of_node_put(dma_spec.np); - } while (!chan); + if (chan) + return chan; + } - return chan; + return NULL; } /** -- cgit v1.2.3 From 9743a3b62dee8c9d8af1319f8d1c1ff39130267d Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Thu, 11 Oct 2012 14:43:01 -0500 Subject: of: dma: fix protection of DMA controller data stored by DMA helpers In the current implementation of the OF DMA helpers, read-copy-update (RCU) linked lists are being used for storing and accessing the DMA controller data. This part of implementation is based upon V2 of the DMA helpers by Nicolas [1]. During a recent review of RCU, it became apparent that the code is missing the required rcu_read_lock()/unlock() calls as well as synchronisation calls before freeing any memory protected by RCU. Having looked into adding the appropriate RCU calls to protect the DMA data it became apparent that with the current DMA helper implementation, using RCU is not as attractive as it may have been before. The main reasons being that ... 1. We need to protect the DMA data around calls to the xlate function. 2. The of_dma_simple_xlate() function calls the DMA engine function dma_request_channel() which employs a mutex and so could sleep. 3. The RCU read-side critical sections must not sleep and so we cannot hold an RCU read lock around the xlate function. Therefore, instead of using RCU, an alternative for this use-case is to employ a simple spinlock inconjunction with a usage count variable to keep track of how many current users of the DMA data structure there are. With this implementation, the DMA data cannot be freed until all current users of the DMA data are finished. This patch is based upon the DMA helpers fix for potential deadlock [2]. [1] http://article.gmane.org/gmane.linux.ports.arm.omap/73622 [2] http://marc.info/?l=linux-arm-kernel&m=134859982520984&w=2 Signed-off-by: Jon Hunter Signed-off-by: Vinod Koul --- drivers/of/dma.c | 89 +++++++++++++++++++++++++++++++++++++------------- include/linux/of_dma.h | 5 +-- 2 files changed, 70 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/of/dma.c b/drivers/of/dma.c index 4bed490a69e4..59631b2c4666 100644 --- a/drivers/of/dma.c +++ b/drivers/of/dma.c @@ -19,27 +19,60 @@ #include static LIST_HEAD(of_dma_list); +static DEFINE_SPINLOCK(of_dma_lock); /** - * of_dma_find_controller - Find a DMA controller in DT DMA helpers list - * @np: device node of DMA controller + * of_dma_get_controller - Get a DMA controller in DT DMA helpers list + * @dma_spec: pointer to DMA specifier as found in the device tree + * + * Finds a DMA controller with matching device node and number for dma cells + * in a list of registered DMA controllers. If a match is found the use_count + * variable is increased and a valid pointer to the DMA data stored is retuned. + * A NULL pointer is returned if no match is found. */ -static struct of_dma *of_dma_find_controller(struct device_node *np) +static struct of_dma *of_dma_get_controller(struct of_phandle_args *dma_spec) { struct of_dma *ofdma; + spin_lock(&of_dma_lock); + if (list_empty(&of_dma_list)) { - pr_err("empty DMA controller list\n"); + spin_unlock(&of_dma_lock); return NULL; } - list_for_each_entry_rcu(ofdma, &of_dma_list, of_dma_controllers) - if (ofdma->of_node == np) + list_for_each_entry(ofdma, &of_dma_list, of_dma_controllers) + if ((ofdma->of_node == dma_spec->np) && + (ofdma->of_dma_nbcells == dma_spec->args_count)) { + ofdma->use_count++; + spin_unlock(&of_dma_lock); return ofdma; + } + + spin_unlock(&of_dma_lock); + + pr_debug("%s: can't find DMA controller %s\n", __func__, + dma_spec->np->full_name); return NULL; } +/** + * of_dma_put_controller - Decrement use count for a registered DMA controller + * @of_dma: pointer to DMA controller data + * + * Decrements the use_count variable in the DMA data structure. This function + * should be called only when a valid pointer is returned from + * of_dma_get_controller() and no further accesses to data referenced by that + * pointer are needed. + */ +static void of_dma_put_controller(struct of_dma *ofdma) +{ + spin_lock(&of_dma_lock); + ofdma->use_count--; + spin_unlock(&of_dma_lock); +} + /** * of_dma_controller_register - Register a DMA controller to DT DMA helpers * @np: device node of DMA controller @@ -81,9 +114,10 @@ int of_dma_controller_register(struct device_node *np, ofdma->of_dma_nbcells = nbcells; ofdma->of_dma_xlate = of_dma_xlate; ofdma->of_dma_data = data; + ofdma->use_count = 0; /* Now queue of_dma controller structure in list */ - list_add_tail_rcu(&ofdma->of_dma_controllers, &of_dma_list); + list_add_tail(&ofdma->of_dma_controllers, &of_dma_list); return 0; } @@ -95,15 +129,32 @@ EXPORT_SYMBOL_GPL(of_dma_controller_register); * * Memory allocated by of_dma_controller_register() is freed here. */ -void of_dma_controller_free(struct device_node *np) +int of_dma_controller_free(struct device_node *np) { struct of_dma *ofdma; - ofdma = of_dma_find_controller(np); - if (ofdma) { - list_del_rcu(&ofdma->of_dma_controllers); - kfree(ofdma); + spin_lock(&of_dma_lock); + + if (list_empty(&of_dma_list)) { + spin_unlock(&of_dma_lock); + return -ENODEV; } + + list_for_each_entry(ofdma, &of_dma_list, of_dma_controllers) + if (ofdma->of_node == np) { + if (ofdma->use_count) { + spin_unlock(&of_dma_lock); + return -EBUSY; + } + + list_del(&ofdma->of_dma_controllers); + spin_unlock(&of_dma_lock); + kfree(ofdma); + return 0; + } + + spin_unlock(&of_dma_lock); + return -ENODEV; } EXPORT_SYMBOL_GPL(of_dma_controller_free); @@ -166,21 +217,15 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np, if (of_dma_match_channel(np, name, i, &dma_spec)) continue; - ofdma = of_dma_find_controller(dma_spec.np); - if (!ofdma) { - pr_debug("%s: can't find DMA controller %s\n", - np->full_name, dma_spec.np->full_name); - continue; - } + ofdma = of_dma_get_controller(&dma_spec); - if (dma_spec.args_count != ofdma->of_dma_nbcells) { - pr_debug("%s: wrong #dma-cells for %s\n", np->full_name, - dma_spec.np->full_name); + if (!ofdma) continue; - } chan = ofdma->of_dma_xlate(&dma_spec, ofdma); + of_dma_put_controller(ofdma); + of_node_put(dma_spec.np); if (chan) diff --git a/include/linux/of_dma.h b/include/linux/of_dma.h index 67158ddd1f3e..84b64f857e23 100644 --- a/include/linux/of_dma.h +++ b/include/linux/of_dma.h @@ -25,6 +25,7 @@ struct of_dma { struct dma_chan *(*of_dma_xlate) (struct of_phandle_args *, struct of_dma *); void *of_dma_data; + int use_count; }; struct of_dma_filter_info { @@ -37,7 +38,7 @@ extern int of_dma_controller_register(struct device_node *np, struct dma_chan *(*of_dma_xlate) (struct of_phandle_args *, struct of_dma *), void *data); -extern void of_dma_controller_free(struct device_node *np); +extern int of_dma_controller_free(struct device_node *np); extern struct dma_chan *of_dma_request_slave_channel(struct device_node *np, char *name); extern struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, @@ -51,7 +52,7 @@ static int of_dma_controller_register(struct device_node *np, return -ENODEV; } -static void of_dma_controller_free(struct device_node *np) +static int of_dma_controller_free(struct device_node *np) { } -- cgit v1.2.3 From f7d935dcc34fae9ad4a39f2cf8e2a96199c48948 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Thu, 1 Nov 2012 22:54:43 +0800 Subject: dmaengine: sirf: enable the driver support new SiRFmarco SoC The driver supports old up SiRFprimaII SoCs, this patch makes it support the new SiRFmarco as well. SiRFmarco, as a SMP SoC, adds new DMA_INT_EN_CLR and DMA_CH_LOOP_CTRL_CLR registers, to disable IRQ/Channel, we should write 1 to the corresponding bit in the two CLEAR register. Tested on SiRFmarco using SPI driver: $ /mnt/spidev-sirftest -D /dev/spidev32766.0 spi mode: 0 bits per word: 8 max speed: 500000 Hz (500 KHz) 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 $ cat /proc/interrupts CPU0 CPU1 32: 1593 0 GIC sirfsoc_timer0 33: 0 3533 GIC sirfsoc_timer1 44: 0 0 GIC sirfsoc_dma 45: 16 0 GIC sirfsoc_dma 47: 6 0 GIC sirfsoc_spi 50: 5654 0 GIC sirfsoc-uart ... Signed-off-by: Barry Song Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 4 ++-- drivers/dma/sirf-dma.c | 25 +++++++++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 6761cc8a59d5..0b408bbb6a17 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -212,8 +212,8 @@ config TIMB_DMA Enable support for the Timberdale FPGA DMA engine. config SIRF_DMA - tristate "CSR SiRFprimaII DMA support" - depends on ARCH_PRIMA2 + tristate "CSR SiRFprimaII/SiRFmarco DMA support" + depends on ARCH_SIRF select DMA_ENGINE help Enable support for the CSR SiRFprimaII DMA engine. diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index c3de6edb9651..3c210ba9f938 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c @@ -32,7 +32,9 @@ #define SIRFSOC_DMA_CH_VALID 0x140 #define SIRFSOC_DMA_CH_INT 0x144 #define SIRFSOC_DMA_INT_EN 0x148 +#define SIRFSOC_DMA_INT_EN_CLR 0x14C #define SIRFSOC_DMA_CH_LOOP_CTRL 0x150 +#define SIRFSOC_DMA_CH_LOOP_CTRL_CLR 0x15C #define SIRFSOC_DMA_MODE_CTRL_BIT 4 #define SIRFSOC_DMA_DIR_CTRL_BIT 5 @@ -76,6 +78,7 @@ struct sirfsoc_dma { struct sirfsoc_dma_chan channels[SIRFSOC_DMA_CHANNELS]; void __iomem *base; int irq; + bool is_marco; }; #define DRV_NAME "sirfsoc_dma" @@ -288,13 +291,19 @@ static int sirfsoc_dma_terminate_all(struct sirfsoc_dma_chan *schan) int cid = schan->chan.chan_id; unsigned long flags; - writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_INT_EN) & - ~(1 << cid), sdma->base + SIRFSOC_DMA_INT_EN); - writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_CH_VALID); - - writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL) - & ~((1 << cid) | 1 << (cid + 16)), + if (!sdma->is_marco) { + writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_INT_EN) & + ~(1 << cid), sdma->base + SIRFSOC_DMA_INT_EN); + writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL) + & ~((1 << cid) | 1 << (cid + 16)), sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL); + } else { + writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_INT_EN_CLR); + writel_relaxed((1 << cid) | 1 << (cid + 16), + sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL_CLR); + } + + writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_CH_VALID); spin_lock_irqsave(&schan->lock, flags); list_splice_tail_init(&schan->active, &schan->free); @@ -568,6 +577,9 @@ static int sirfsoc_dma_probe(struct platform_device *op) return -ENOMEM; } + if (of_device_is_compatible(dn, "sirf,marco-dmac")) + sdma->is_marco = true; + if (of_property_read_u32(dn, "cell-index", &id)) { dev_err(dev, "Fail to get DMAC index\n"); return -ENODEV; @@ -668,6 +680,7 @@ static int __devexit sirfsoc_dma_remove(struct platform_device *op) static struct of_device_id sirfsoc_dma_match[] = { { .compatible = "sirf,prima2-dmac", }, + { .compatible = "sirf,marco-dmac", }, {}, }; -- cgit v1.2.3 From e4d43c1764bc3ee1150f24e530db2b5b23e91425 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 15 Nov 2012 06:27:50 +0000 Subject: DMA: PL330: Use devm_* functions devm_* functions are device managed and make the code and error handling a bit simpler. Cc: Jassi Brar Signed-off-by: Sachin Kamat Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 37 ++++++++++--------------------------- 1 file changed, 10 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 95555f37ea6d..f7edb6f0ee87 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2866,7 +2866,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pdat = adev->dev.platform_data; /* Allocate a new DMAC and its Channels */ - pdmac = kzalloc(sizeof(*pdmac), GFP_KERNEL); + pdmac = devm_kzalloc(&adev->dev, sizeof(*pdmac), GFP_KERNEL); if (!pdmac) { dev_err(&adev->dev, "unable to allocate mem\n"); return -ENOMEM; @@ -2878,13 +2878,9 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pi->mcbufsz = pdat ? pdat->mcbuf_sz : 0; res = &adev->res; - request_mem_region(res->start, resource_size(res), "dma-pl330"); - - pi->base = ioremap(res->start, resource_size(res)); - if (!pi->base) { - ret = -ENXIO; - goto probe_err1; - } + pi->base = devm_request_and_ioremap(&adev->dev, res); + if (!pi->base) + return -ENXIO; amba_set_drvdata(adev, pdmac); @@ -2892,11 +2888,11 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) ret = request_irq(irq, pl330_irq_handler, 0, dev_name(&adev->dev), pi); if (ret) - goto probe_err2; + return ret; ret = pl330_add(pi); if (ret) - goto probe_err3; + goto probe_err1; INIT_LIST_HEAD(&pdmac->desc_pool); spin_lock_init(&pdmac->pool_lock); @@ -2918,7 +2914,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) if (!pdmac->peripherals) { ret = -ENOMEM; dev_err(&adev->dev, "unable to allocate pdmac->peripherals\n"); - goto probe_err4; + goto probe_err2; } for (i = 0; i < num_chan; i++) { @@ -2962,7 +2958,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) ret = dma_async_device_register(pd); if (ret) { dev_err(&adev->dev, "unable to register DMAC\n"); - goto probe_err4; + goto probe_err2; } dev_info(&adev->dev, @@ -2975,15 +2971,10 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) return 0; -probe_err4: - pl330_del(pi); -probe_err3: - free_irq(irq, pi); probe_err2: - iounmap(pi->base); + pl330_del(pi); probe_err1: - release_mem_region(res->start, resource_size(res)); - kfree(pdmac); + free_irq(irq, pi); return ret; } @@ -2993,7 +2984,6 @@ static int __devexit pl330_remove(struct amba_device *adev) struct dma_pl330_dmac *pdmac = amba_get_drvdata(adev); struct dma_pl330_chan *pch, *_p; struct pl330_info *pi; - struct resource *res; int irq; if (!pdmac) @@ -3020,13 +3010,6 @@ static int __devexit pl330_remove(struct amba_device *adev) irq = adev->irq[0]; free_irq(irq, pi); - iounmap(pi->base); - - res = &adev->res; - release_mem_region(res->start, resource_size(res)); - - kfree(pdmac); - return 0; } -- cgit v1.2.3 From 944ea4dd38b8575e30a5699633c81945bff1864d Mon Sep 17 00:00:00 2001 From: Jon Mason Date: Sun, 11 Nov 2012 23:03:20 +0000 Subject: dmatest: Fix NULL pointer dereference on ioat device_control is an optional and not implemented in all DMA drivers. Any calls to these will result in a NULL pointer dereference. dmatest makes two of these calls when completing the kernel thread and removing the module. These are corrected by calling the dmaengine_device_control wrapper and checking for a non-existant device_control function pointer there. Signed-off-by: Jon Mason CC: Vinod Koul CC: Dan Williams Reviewed-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dmatest.c | 4 ++-- include/linux/dmaengine.h | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index 3a8047b1f108..99a75e5d66be 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -536,7 +536,7 @@ err_srcs: thread_name, total_tests, failed_tests, ret); /* terminate all transfers on specified channels */ - chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); + dmaengine_terminate_all(chan); if (iterations > 0) while (!kthread_should_stop()) { DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait_dmatest_exit); @@ -561,7 +561,7 @@ static void dmatest_cleanup_channel(struct dmatest_chan *dtc) } /* terminate all transfers on specified channels */ - dtc->chan->device->device_control(dtc->chan, DMA_TERMINATE_ALL, 0); + dmaengine_terminate_all(dtc->chan); kfree(dtc); } diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 3aa76dbed166..be6e95395b11 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -608,7 +608,10 @@ static inline int dmaengine_device_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) { - return chan->device->device_control(chan, cmd, arg); + if (chan->device->device_control) + return chan->device->device_control(chan, cmd, arg); + else + return -ENOSYS; } static inline int dmaengine_slave_config(struct dma_chan *chan, -- cgit v1.2.3 From 7c1119bdd650fa58dad8157bc75c5fcf6ed97843 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 28 Nov 2012 06:49:47 +0000 Subject: dma: sh: Don't use ENODEV for failing slave lookup If dmaengine driver's .device_alloc_chan_resources() method returns -ENODEV, dma_request_channel() will decide, that the driver has been removed and will remove the device from its list. To prevent this use ENXIO if a slave lookup fails. Reported-by: Kuninori Morimoto Signed-off-by: Guennadi Liakhovetski Cc: stable@vger.kernel.org Signed-off-by: Vinod Koul --- drivers/dma/sh/shdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/sh/shdma.c b/drivers/dma/sh/shdma.c index 8201bb4e0cd7..62eff4800521 100644 --- a/drivers/dma/sh/shdma.c +++ b/drivers/dma/sh/shdma.c @@ -326,7 +326,7 @@ static int sh_dmae_set_slave(struct shdma_chan *schan, shdma_chan); const struct sh_dmae_slave_config *cfg = dmae_find_slave(sh_chan, slave_id); if (!cfg) - return -ENODEV; + return -ENXIO; if (!try) sh_chan->config = cfg; -- cgit v1.2.3 From 5e034f7b659be9d94e64aaaa985ab530dd847fdb Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Fri, 9 Nov 2012 15:26:29 +0000 Subject: dmaengine/dmatest: terminate transfers only in case of errors dmatest erroneously terminated transfers in normal cases also leading to test failures for multiple threads over a channel. Fix this and terminate transfers only in case of errors. Signed-off-by: Shiraz Hashim Signed-off-by: Deepak Sikri Signed-off-by: Vinod Koul --- drivers/dma/dmatest.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index 99a75e5d66be..a2c8904b63ea 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -536,7 +536,9 @@ err_srcs: thread_name, total_tests, failed_tests, ret); /* terminate all transfers on specified channels */ - dmaengine_terminate_all(chan); + if (ret) + dmaengine_terminate_all(chan); + if (iterations > 0) while (!kthread_should_stop()) { DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait_dmatest_exit); -- cgit v1.2.3 From 7369f56e3e7193583576ec705d95647b04838b05 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 5 Nov 2012 10:00:19 +0000 Subject: ioat3: add missing DMA unmap to ioat_xor_val_self_test() Make ioat_xor_val_self_test() do DMA unmapping itself and fix handling of failure cases. Cc: Dan Williams Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams --- drivers/dma/ioat/dma_v3.c | 76 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 59 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index f7f1dc62c15c..6456f7d38e13 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -863,6 +863,7 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) unsigned long tmo; struct device *dev = &device->pdev->dev; struct dma_device *dma = &device->common; + u8 op = 0; dev_dbg(dev, "%s\n", __func__); @@ -908,18 +909,22 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) } /* test xor */ + op = IOAT_OP_XOR; + dest_dma = dma_map_page(dev, dest, 0, PAGE_SIZE, DMA_FROM_DEVICE); for (i = 0; i < IOAT_NUM_SRC_TEST; i++) dma_srcs[i] = dma_map_page(dev, xor_srcs[i], 0, PAGE_SIZE, DMA_TO_DEVICE); tx = dma->device_prep_dma_xor(dma_chan, dest_dma, dma_srcs, IOAT_NUM_SRC_TEST, PAGE_SIZE, - DMA_PREP_INTERRUPT); + DMA_PREP_INTERRUPT | + DMA_COMPL_SKIP_SRC_UNMAP | + DMA_COMPL_SKIP_DEST_UNMAP); if (!tx) { dev_err(dev, "Self-test xor prep failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } async_tx_ack(tx); @@ -930,7 +935,7 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (cookie < 0) { dev_err(dev, "Self-test xor setup failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } dma->device_issue_pending(dma_chan); @@ -939,9 +944,13 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_err(dev, "Self-test xor timed out\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } + dma_unmap_page(dev, dest_dma, PAGE_SIZE, DMA_FROM_DEVICE); + for (i = 0; i < IOAT_NUM_SRC_TEST; i++) + dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, DMA_TO_DEVICE); + dma_sync_single_for_cpu(dev, dest_dma, PAGE_SIZE, DMA_FROM_DEVICE); for (i = 0; i < (PAGE_SIZE / sizeof(u32)); i++) { u32 *ptr = page_address(dest); @@ -957,6 +966,8 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (!dma_has_cap(DMA_XOR_VAL, dma_chan->device->cap_mask)) goto free_resources; + op = IOAT_OP_XOR_VAL; + /* validate the sources with the destintation page */ for (i = 0; i < IOAT_NUM_SRC_TEST; i++) xor_val_srcs[i] = xor_srcs[i]; @@ -969,11 +980,13 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) DMA_TO_DEVICE); tx = dma->device_prep_dma_xor_val(dma_chan, dma_srcs, IOAT_NUM_SRC_TEST + 1, PAGE_SIZE, - &xor_val_result, DMA_PREP_INTERRUPT); + &xor_val_result, DMA_PREP_INTERRUPT | + DMA_COMPL_SKIP_SRC_UNMAP | + DMA_COMPL_SKIP_DEST_UNMAP); if (!tx) { dev_err(dev, "Self-test zero prep failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } async_tx_ack(tx); @@ -984,7 +997,7 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (cookie < 0) { dev_err(dev, "Self-test zero setup failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } dma->device_issue_pending(dma_chan); @@ -993,9 +1006,12 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_err(dev, "Self-test validate timed out\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } + for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++) + dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, DMA_TO_DEVICE); + if (xor_val_result != 0) { dev_err(dev, "Self-test validate failed compare\n"); err = -ENODEV; @@ -1007,14 +1023,18 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) goto free_resources; /* test memset */ + op = IOAT_OP_FILL; + dma_addr = dma_map_page(dev, dest, 0, PAGE_SIZE, DMA_FROM_DEVICE); tx = dma->device_prep_dma_memset(dma_chan, dma_addr, 0, PAGE_SIZE, - DMA_PREP_INTERRUPT); + DMA_PREP_INTERRUPT | + DMA_COMPL_SKIP_SRC_UNMAP | + DMA_COMPL_SKIP_DEST_UNMAP); if (!tx) { dev_err(dev, "Self-test memset prep failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } async_tx_ack(tx); @@ -1025,7 +1045,7 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (cookie < 0) { dev_err(dev, "Self-test memset setup failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } dma->device_issue_pending(dma_chan); @@ -1034,9 +1054,11 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_err(dev, "Self-test memset timed out\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } + dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_FROM_DEVICE); + for (i = 0; i < PAGE_SIZE/sizeof(u32); i++) { u32 *ptr = page_address(dest); if (ptr[i]) { @@ -1047,17 +1069,21 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) } /* test for non-zero parity sum */ + op = IOAT_OP_XOR_VAL; + xor_val_result = 0; for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++) dma_srcs[i] = dma_map_page(dev, xor_val_srcs[i], 0, PAGE_SIZE, DMA_TO_DEVICE); tx = dma->device_prep_dma_xor_val(dma_chan, dma_srcs, IOAT_NUM_SRC_TEST + 1, PAGE_SIZE, - &xor_val_result, DMA_PREP_INTERRUPT); + &xor_val_result, DMA_PREP_INTERRUPT | + DMA_COMPL_SKIP_SRC_UNMAP | + DMA_COMPL_SKIP_DEST_UNMAP); if (!tx) { dev_err(dev, "Self-test 2nd zero prep failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } async_tx_ack(tx); @@ -1068,7 +1094,7 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (cookie < 0) { dev_err(dev, "Self-test 2nd zero setup failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } dma->device_issue_pending(dma_chan); @@ -1077,15 +1103,31 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_err(dev, "Self-test 2nd validate timed out\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } if (xor_val_result != SUM_CHECK_P_RESULT) { dev_err(dev, "Self-test validate failed compare\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } + for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++) + dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, DMA_TO_DEVICE); + + goto free_resources; +dma_unmap: + if (op == IOAT_OP_XOR) { + dma_unmap_page(dev, dest_dma, PAGE_SIZE, DMA_FROM_DEVICE); + for (i = 0; i < IOAT_NUM_SRC_TEST; i++) + dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, + DMA_TO_DEVICE); + } else if (op == IOAT_OP_XOR_VAL) { + for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++) + dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, + DMA_TO_DEVICE); + } else if (op == IOAT_OP_FILL) + dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_FROM_DEVICE); free_resources: dma->device_free_chan_resources(dma_chan); out: -- cgit v1.2.3 From b9ee86830f34737a08deead93872a79a37419a13 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 8 Nov 2012 09:59:54 +0000 Subject: dmaengine: remove dma_async_memcpy_pending() macro Just use dma_async_issue_pending() directly. Cc: Vinod Koul Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams --- drivers/misc/carma/carma-fpga-program.c | 2 +- drivers/misc/carma/carma-fpga.c | 2 +- include/linux/dmaengine.h | 2 -- net/ipv4/tcp.c | 6 +++--- 4 files changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/carma/carma-fpga-program.c b/drivers/misc/carma/carma-fpga-program.c index eaddfe9db149..736c7714f565 100644 --- a/drivers/misc/carma/carma-fpga-program.c +++ b/drivers/misc/carma/carma-fpga-program.c @@ -546,7 +546,7 @@ static noinline int fpga_program_dma(struct fpga_dev *priv) goto out_dma_unmap; } - dma_async_memcpy_issue_pending(chan); + dma_async_issue_pending(chan); /* Set the total byte count */ fpga_set_byte_count(priv->regs, priv->bytes); diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c index 6b43f8c7b3be..7508cafff103 100644 --- a/drivers/misc/carma/carma-fpga.c +++ b/drivers/misc/carma/carma-fpga.c @@ -751,7 +751,7 @@ static irqreturn_t data_irq(int irq, void *dev_id) submitted = true; /* Start the DMA Engine */ - dma_async_memcpy_issue_pending(priv->chan); + dma_async_issue_pending(priv->chan); out: /* If no DMA was submitted, re-enable interrupts */ diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index be6e95395b11..cd15958d4d1d 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -901,8 +901,6 @@ static inline void dma_async_issue_pending(struct dma_chan *chan) chan->device->device_issue_pending(chan); } -#define dma_async_memcpy_issue_pending(chan) dma_async_issue_pending(chan) - /** * dma_async_is_tx_complete - poll for transaction completion * @chan: DMA channel diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 1ca253635f7a..cf949a119a54 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1406,7 +1406,7 @@ static void tcp_service_net_dma(struct sock *sk, bool wait) return; last_issued = tp->ucopy.dma_cookie; - dma_async_memcpy_issue_pending(tp->ucopy.dma_chan); + dma_async_issue_pending(tp->ucopy.dma_chan); do { if (dma_async_memcpy_complete(tp->ucopy.dma_chan, @@ -1744,7 +1744,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, tcp_service_net_dma(sk, true); tcp_cleanup_rbuf(sk, copied); } else - dma_async_memcpy_issue_pending(tp->ucopy.dma_chan); + dma_async_issue_pending(tp->ucopy.dma_chan); } #endif if (copied >= target) { @@ -1837,7 +1837,7 @@ do_prequeue: break; } - dma_async_memcpy_issue_pending(tp->ucopy.dma_chan); + dma_async_issue_pending(tp->ucopy.dma_chan); if ((offset + used) == skb->len) copied_early = true; -- cgit v1.2.3 From 2cbe7feba1ac521b5668609c35b94536bbbcd52f Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 8 Nov 2012 10:02:07 +0000 Subject: dmaengine: add cpu_relax() to busy-loop in dma_sync_wait() Removal of the busy-loop from dma_sync_wait() is not a trivial task so just add cpu_relax() to the loop for now. Cc: Vinod Koul Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams --- drivers/dma/dmaengine.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index d37cf95987b6..242b8c0a3de8 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -267,7 +267,10 @@ enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie) pr_err("%s: timeout!\n", __func__); return DMA_ERROR; } - } while (status == DMA_IN_PROGRESS); + if (status != DMA_IN_PROGRESS) + break; + cpu_relax(); + } while (1); return status; } -- cgit v1.2.3 From 1a363068dcc2269931daef360ed14d2a262f19f7 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Mon, 3 Dec 2012 16:08:37 -0700 Subject: ioat: Add alignment workaround for IVB platforms The PCI IDs for IvyBridge IOAT DMA needs to go into a header file since dma_v3.c looks them up for certain hardware workarounds. Need to add to the alignment workaround for IOAT 3.2 since it wasn't fixed in IVB. Signed-off-by: Dave Jiang Signed-off-by: Dan Williams --- drivers/dma/ioat/dma_v3.c | 22 +++++++++++++++++++++- drivers/dma/ioat/hw.h | 11 +++++++++++ drivers/dma/ioat/pci.c | 11 ----------- 3 files changed, 32 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index 6456f7d38e13..9d6f3bbf0e53 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -1229,6 +1229,26 @@ static bool is_snb_ioat(struct pci_dev *pdev) } } +static bool is_ivb_ioat(struct pci_dev *pdev) +{ + switch (pdev->device) { + case PCI_DEVICE_ID_INTEL_IOAT_IVB0: + case PCI_DEVICE_ID_INTEL_IOAT_IVB1: + case PCI_DEVICE_ID_INTEL_IOAT_IVB2: + case PCI_DEVICE_ID_INTEL_IOAT_IVB3: + case PCI_DEVICE_ID_INTEL_IOAT_IVB4: + case PCI_DEVICE_ID_INTEL_IOAT_IVB5: + case PCI_DEVICE_ID_INTEL_IOAT_IVB6: + case PCI_DEVICE_ID_INTEL_IOAT_IVB7: + case PCI_DEVICE_ID_INTEL_IOAT_IVB8: + case PCI_DEVICE_ID_INTEL_IOAT_IVB9: + return true; + default: + return false; + } + +} + int __devinit ioat3_dma_probe(struct ioatdma_device *device, int dca) { struct pci_dev *pdev = device->pdev; @@ -1249,7 +1269,7 @@ int __devinit ioat3_dma_probe(struct ioatdma_device *device, int dca) dma->device_alloc_chan_resources = ioat2_alloc_chan_resources; dma->device_free_chan_resources = ioat2_free_chan_resources; - if (is_jf_ioat(pdev) || is_snb_ioat(pdev)) + if (is_jf_ioat(pdev) || is_snb_ioat(pdev) || is_ivb_ioat(pdev)) dma->copy_align = 6; dma_cap_set(DMA_INTERRUPT, dma->cap_mask); diff --git a/drivers/dma/ioat/hw.h b/drivers/dma/ioat/hw.h index d2ff3fda0b18..7cb74c62c719 100644 --- a/drivers/dma/ioat/hw.h +++ b/drivers/dma/ioat/hw.h @@ -35,6 +35,17 @@ #define IOAT_VER_3_0 0x30 /* Version 3.0 */ #define IOAT_VER_3_2 0x32 /* Version 3.2 */ +#define PCI_DEVICE_ID_INTEL_IOAT_IVB0 0x0e20 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB1 0x0e21 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB2 0x0e22 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB3 0x0e23 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB4 0x0e24 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB5 0x0e25 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB6 0x0e26 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB7 0x0e27 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB8 0x0e2e +#define PCI_DEVICE_ID_INTEL_IOAT_IVB9 0x0e2f + int system_has_dca_enabled(struct pci_dev *pdev); struct ioat_dma_descriptor { diff --git a/drivers/dma/ioat/pci.c b/drivers/dma/ioat/pci.c index bfa9a3536e09..f4e6163a1e73 100644 --- a/drivers/dma/ioat/pci.c +++ b/drivers/dma/ioat/pci.c @@ -40,17 +40,6 @@ MODULE_VERSION(IOAT_DMA_VERSION); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Intel Corporation"); -#define PCI_DEVICE_ID_INTEL_IOAT_IVB0 0x0e20 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB1 0x0e21 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB2 0x0e22 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB3 0x0e23 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB4 0x0e24 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB5 0x0e25 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB6 0x0e26 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB7 0x0e27 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB8 0x0e2e -#define PCI_DEVICE_ID_INTEL_IOAT_IVB9 0x0e2f - static struct pci_device_id ioat_pci_tbl[] = { /* I/OAT v1 platforms */ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT) }, -- cgit v1.2.3 From 6decffd5f6afaf55722d9c85b8739621dca63d0f Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Tue, 27 Nov 2012 15:16:08 -0700 Subject: ioat: remove chanerr mask setting for IOAT v3.x The existing code set a value in the PCI_CHANERRMSK_INT register for a workaround to address a pre-silicon bug on the Intel 5520 IO hub that has been fixed when the hardware was released. There is no need for this code. Signed-off-by: Dave Jiang Signed-off-by: Dan Williams --- drivers/dma/ioat/dma_v3.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index 9d6f3bbf0e53..e52cf1eb6839 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -1168,12 +1168,7 @@ static int ioat3_reset_hw(struct ioat_chan_common *chan) chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); writel(chanerr, chan->reg_base + IOAT_CHANERR_OFFSET); - /* -= IOAT ver.3 workarounds =- */ - /* Write CHANERRMSK_INT with 3E07h to mask out the errors - * that can cause stability issues for IOAT ver.3, and clear any - * pending errors - */ - pci_write_config_dword(pdev, IOAT_PCI_CHANERRMASK_INT_OFFSET, 0x3e07); + /* clear any pending errors */ err = pci_read_config_dword(pdev, IOAT_PCI_CHANERR_INT_OFFSET, &chanerr); if (err) { dev_err(&pdev->dev, "channel error register unreachable\n"); -- cgit v1.2.3 From c419fcfd071cf34ba00f9f65282583772d2655e7 Mon Sep 17 00:00:00 2001 From: Maciej Sosnowski Date: Wed, 23 May 2012 17:27:07 +0200 Subject: dca: check against empty dca_domains list before unregister provider When providers get blocked unregister_dca_providers() is called ending up with dca_providers and dca_domain lists emptied. Dca should be prevented from trying to unregister any provider if dca_domain list is found empty. Cc: Reported-by: Jiang Liu Tested-by: Gaohuai Han Signed-off-by: Maciej Sosnowski Signed-off-by: Dan Williams --- drivers/dca/dca-core.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/dca/dca-core.c b/drivers/dca/dca-core.c index bc6f5faa1e9e..819dfda88236 100644 --- a/drivers/dca/dca-core.c +++ b/drivers/dca/dca-core.c @@ -420,6 +420,11 @@ void unregister_dca_provider(struct dca_provider *dca, struct device *dev) raw_spin_lock_irqsave(&dca_lock, flags); + if (list_empty(&dca_domains)) { + raw_spin_unlock_irqrestore(&dca_lock, flags); + return; + } + list_del(&dca->node); pci_rc = dca_pci_rc_from_dev(dev); -- cgit v1.2.3 From e65f32ca21faed30ce37cd6480271697fe671f74 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 6 Sep 2012 09:19:35 +0200 Subject: dma: ipu: Drop unused spinlock I was checking why this spinlock was never initialized, but it turns out it's not used anywhere, so we can drop it. Signed-off-by: Jean Delvare Cc: Vinod Koul Signed-off-by: Dan Williams --- drivers/dma/ipu/ipu_irq.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/ipu/ipu_irq.c b/drivers/dma/ipu/ipu_irq.c index a5ee37d5320f..2e284a4438bc 100644 --- a/drivers/dma/ipu/ipu_irq.c +++ b/drivers/dma/ipu/ipu_irq.c @@ -44,7 +44,6 @@ static void ipu_write_reg(struct ipu *ipu, u32 value, unsigned long reg) struct ipu_irq_bank { unsigned int control; unsigned int status; - spinlock_t lock; struct ipu *ipu; }; -- cgit v1.2.3 From 1b140908c4cda43c653bb080c244d112e619008f Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Sun, 6 Jan 2013 21:52:02 +0530 Subject: dma: tegra: add support for channel wise pause NVIDIA's some SoCs like Tegra114 support the channel wise pause control inplace of global pause which pauses all DMA channels. When SoCs support the channel wise pause control then it uses the global pause for clock gating for register access as well as all DMA channel pause. Hence DMA registers are not accessible if DMAs are globally paused on these new SoCs. Add support for channel wise pause feature if SoCs support it. Signed-off-by: Laxman Dewangan Reviewed-by: Stephen Warren Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index efdfffa13349..2c46ac46e7ad 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -62,6 +62,9 @@ #define TEGRA_APBDMA_STATUS_COUNT_SHIFT 2 #define TEGRA_APBDMA_STATUS_COUNT_MASK 0xFFFC +#define TEGRA_APBDMA_CHAN_CSRE 0x00C +#define TEGRA_APBDMA_CHAN_CSRE_PAUSE (1 << 31) + /* AHB memory address */ #define TEGRA_APBDMA_CHAN_AHBPTR 0x010 @@ -112,10 +115,12 @@ struct tegra_dma; * tegra_dma_chip_data Tegra chip specific DMA data * @nr_channels: Number of channels available in the controller. * @max_dma_count: Maximum DMA transfer count supported by DMA controller. + * @support_channel_pause: Support channel wise pause of dma. */ struct tegra_dma_chip_data { int nr_channels; int max_dma_count; + bool support_channel_pause; }; /* DMA channel registers */ @@ -353,6 +358,32 @@ static void tegra_dma_global_resume(struct tegra_dma_channel *tdc) spin_unlock(&tdma->global_lock); } +static void tegra_dma_pause(struct tegra_dma_channel *tdc, + bool wait_for_burst_complete) +{ + struct tegra_dma *tdma = tdc->tdma; + + if (tdma->chip_data->support_channel_pause) { + tdc_write(tdc, TEGRA_APBDMA_CHAN_CSRE, + TEGRA_APBDMA_CHAN_CSRE_PAUSE); + if (wait_for_burst_complete) + udelay(TEGRA_APBDMA_BURST_COMPLETE_TIME); + } else { + tegra_dma_global_pause(tdc, wait_for_burst_complete); + } +} + +static void tegra_dma_resume(struct tegra_dma_channel *tdc) +{ + struct tegra_dma *tdma = tdc->tdma; + + if (tdma->chip_data->support_channel_pause) { + tdc_write(tdc, TEGRA_APBDMA_CHAN_CSRE, 0); + } else { + tegra_dma_global_resume(tdc); + } +} + static void tegra_dma_stop(struct tegra_dma_channel *tdc) { u32 csr; @@ -408,7 +439,7 @@ static void tegra_dma_configure_for_next(struct tegra_dma_channel *tdc, * If there is already IEC status then interrupt handler need to * load new configuration. */ - tegra_dma_global_pause(tdc, false); + tegra_dma_pause(tdc, false); status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS); /* @@ -418,7 +449,7 @@ static void tegra_dma_configure_for_next(struct tegra_dma_channel *tdc, if (status & TEGRA_APBDMA_STATUS_ISE_EOC) { dev_err(tdc2dev(tdc), "Skipping new configuration as interrupt is pending\n"); - tegra_dma_global_resume(tdc); + tegra_dma_resume(tdc); return; } @@ -429,7 +460,7 @@ static void tegra_dma_configure_for_next(struct tegra_dma_channel *tdc, nsg_req->ch_regs.csr | TEGRA_APBDMA_CSR_ENB); nsg_req->configured = true; - tegra_dma_global_resume(tdc); + tegra_dma_resume(tdc); } static void tdc_start_head_req(struct tegra_dma_channel *tdc) @@ -690,7 +721,7 @@ static void tegra_dma_terminate_all(struct dma_chan *dc) goto skip_dma_stop; /* Pause DMA before checking the queue status */ - tegra_dma_global_pause(tdc, true); + tegra_dma_pause(tdc, true); status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS); if (status & TEGRA_APBDMA_STATUS_ISE_EOC) { @@ -708,7 +739,7 @@ static void tegra_dma_terminate_all(struct dma_chan *dc) sgreq->dma_desc->bytes_transferred += get_current_xferred_count(tdc, sgreq, status); } - tegra_dma_global_resume(tdc); + tegra_dma_resume(tdc); skip_dma_stop: tegra_dma_abort_all(tdc); @@ -1175,6 +1206,7 @@ static void tegra_dma_free_chan_resources(struct dma_chan *dc) static const struct tegra_dma_chip_data tegra20_dma_chip_data = { .nr_channels = 16, .max_dma_count = 1024UL * 64, + .support_channel_pause = false, }; #if defined(CONFIG_OF) @@ -1182,6 +1214,7 @@ static const struct tegra_dma_chip_data tegra20_dma_chip_data = { static const struct tegra_dma_chip_data tegra30_dma_chip_data = { .nr_channels = 32, .max_dma_count = 1024UL * 64, + .support_channel_pause = false, }; static const struct of_device_id tegra_dma_of_match[] __devinitconst = { -- cgit v1.2.3 From 5ea7caf30debefc1c4319f77146288fd5e92a803 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Sun, 6 Jan 2013 21:52:03 +0530 Subject: dma: tegra: add support for Tegra114 SoC NVIDIA's Tegra114 has APB DMA controller which has 32 dma channels and support support channel wise pause control. Add support for Tegra114 which uses the channel wise pause control hardware feature. Signed-off-by: Laxman Dewangan Reviewed-by: Stephen Warren Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 2c46ac46e7ad..6c144814a896 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -1217,8 +1217,19 @@ static const struct tegra_dma_chip_data tegra30_dma_chip_data = { .support_channel_pause = false, }; -static const struct of_device_id tegra_dma_of_match[] __devinitconst = { +/* Tegra114 specific DMA controller information */ +static const struct tegra_dma_chip_data tegra114_dma_chip_data = { + .nr_channels = 32, + .max_dma_count = 1024UL * 64, + .support_channel_pause = true, +}; + + +static const struct of_device_id tegra_dma_of_match[] = { { + .compatible = "nvidia,tegra114-apbdma", + .data = &tegra114_dma_chip_data, + }, { .compatible = "nvidia,tegra30-apbdma", .data = &tegra30_dma_chip_data, }, { -- cgit v1.2.3 From f2ad6992546674e5915a34a1bc36dcdd8fb29bd2 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 7 Jan 2013 23:48:39 -0200 Subject: dma: mxs-dma: Fix build warnings with W=1 Fix the following warnings when building with W=1 option: drivers/dma/mxs-dma.c: In function 'mxs_dma_alloc_chan_resources': drivers/dma/mxs-dma.c:368:25: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] drivers/dma/mxs-dma.c: In function 'mxs_dma_prep_slave_sg': drivers/dma/mxs-dma.c:481:17: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] drivers/dma/mxs-dma.c:494:3: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] drivers/dma/mxs-dma.c:515:14: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] drivers/dma/mxs-dma.c: In function 'mxs_dma_prep_dma_cyclic': drivers/dma/mxs-dma.c:563:13: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] Signed-off-by: Fabio Estevam Signed-off-by: Vinod Koul --- drivers/dma/mxs-dma.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 9f02e794b12b..8f6d30d37c45 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -109,7 +109,7 @@ struct mxs_dma_chan { struct dma_chan chan; struct dma_async_tx_descriptor desc; struct tasklet_struct tasklet; - int chan_irq; + unsigned int chan_irq; struct mxs_dma_ccw *ccw; dma_addr_t ccw_phys; int desc_count; @@ -441,7 +441,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; struct mxs_dma_ccw *ccw; struct scatterlist *sg; - int i, j; + u32 i, j; u32 *pio; bool append = flags & DMA_PREP_INTERRUPT; int idx = append ? mxs_chan->desc_count : 0; @@ -537,8 +537,8 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic( { struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; - int num_periods = buf_len / period_len; - int i = 0, buf = 0; + u32 num_periods = buf_len / period_len; + u32 i = 0, buf = 0; if (mxs_chan->status == DMA_IN_PROGRESS) return NULL; -- cgit v1.2.3 From 9421bade0765d8ffb86b8a99213b611278a3542a Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Tue, 8 Jan 2013 16:36:42 +0100 Subject: pwm: atmel: add Timer Counter Block PWM driver This patch adds a PWM driver based on Atmel Timer Counter Block. The Timer Counter Block is used in Waveform generator mode. A Timer Counter Block provides up to 6 PWM devices grouped by 2: * group 0 = PWM 0 and 1 * group 1 = PWM 2 and 3 * group 2 = PMW 4 and 5 PWM devices in a given group must be configured with the same period value. If a PWM device in a group tries to change the period value and the other device is already configured with a different value an error will be returned. This driver requires device tree support. The Timer Counter Block number used to create a PWM chip is given by the tc-block field in an "atmel,tcb-pwm" compatible node. This patch was tested on kizbox board (at91sam9g20 SoC) with pwm-leds. Signed-off-by: Boris BREZILLON Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/atmel-tcb-pwm.txt | 18 + drivers/pwm/Kconfig | 12 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-atmel-tcb.c | 445 +++++++++++++++++++++ 4 files changed, 476 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/atmel-tcb-pwm.txt create mode 100644 drivers/pwm/pwm-atmel-tcb.c (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/pwm/atmel-tcb-pwm.txt b/Documentation/devicetree/bindings/pwm/atmel-tcb-pwm.txt new file mode 100644 index 000000000000..de0eaed86651 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/atmel-tcb-pwm.txt @@ -0,0 +1,18 @@ +Atmel TCB PWM controller + +Required properties: +- compatible: should be "atmel,tcb-pwm" +- #pwm-cells: Should be 3. The first cell specifies the per-chip index + of the PWM to use, the second cell is the period in nanoseconds and + bit 0 in the third cell is used to encode the polarity of PWM output. + Set bit 0 of the third cell in PWM specifier to 1 for inverse polarity & + set to 0 for normal polarity. +- tc-block: The Timer Counter block to use as a PWM chip. + +Example: + +pwm { + compatible = "atmel,tcb-pwm"; + #pwm-cells = <3>; + tc-block = <1>; +}; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index e513cd998170..10b6afc94bc3 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -37,6 +37,18 @@ config PWM_AB8500 To compile this driver as a module, choose M here: the module will be called pwm-ab8500. +config PWM_ATMEL_TCB + tristate "TC Block PWM" + depends on ATMEL_TCLIB && OF + help + Generic PWM framework driver for Atmel Timer Counter Block. + + A Timer Counter Block provides 6 PWM devices grouped by 2. + Devices in a given group must have the same period. + + To compile this driver as a module, choose M here: the module + will be called pwm-atmel-tcb. + config PWM_BFIN tristate "Blackfin PWM support" depends on BFIN_GPTIMERS diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 62a2963cfe58..94ba21e24bd6 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_PWM) += core.o obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o +obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o obj-$(CONFIG_PWM_IMX) += pwm-imx.o obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c new file mode 100644 index 000000000000..16cb53092857 --- /dev/null +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -0,0 +1,445 @@ +/* + * Copyright (C) Overkiz SAS 2012 + * + * Author: Boris BREZILLON + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NPWM 6 + +#define ATMEL_TC_ACMR_MASK (ATMEL_TC_ACPA | ATMEL_TC_ACPC | \ + ATMEL_TC_AEEVT | ATMEL_TC_ASWTRG) + +#define ATMEL_TC_BCMR_MASK (ATMEL_TC_BCPB | ATMEL_TC_BCPC | \ + ATMEL_TC_BEEVT | ATMEL_TC_BSWTRG) + +struct atmel_tcb_pwm_device { + enum pwm_polarity polarity; /* PWM polarity */ + unsigned div; /* PWM clock divider */ + unsigned duty; /* PWM duty expressed in clk cycles */ + unsigned period; /* PWM period expressed in clk cycles */ +}; + +struct atmel_tcb_pwm_chip { + struct pwm_chip chip; + spinlock_t lock; + struct atmel_tc *tc; + struct atmel_tcb_pwm_device *pwms[NPWM]; +}; + +static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct atmel_tcb_pwm_chip, chip); +} + +static int atmel_tcb_pwm_set_polarity(struct pwm_chip *chip, + struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm); + + tcbpwm->polarity = polarity; + + return 0; +} + +static int atmel_tcb_pwm_request(struct pwm_chip *chip, + struct pwm_device *pwm) +{ + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); + struct atmel_tcb_pwm_device *tcbpwm; + struct atmel_tc *tc = tcbpwmc->tc; + void __iomem *regs = tc->regs; + unsigned group = pwm->hwpwm / 2; + unsigned index = pwm->hwpwm % 2; + unsigned cmr; + int ret; + + tcbpwm = devm_kzalloc(chip->dev, sizeof(*tcbpwm), GFP_KERNEL); + if (!tcbpwm) + return -ENOMEM; + + ret = clk_enable(tc->clk[group]); + if (ret) { + devm_kfree(chip->dev, tcbpwm); + return ret; + } + + pwm_set_chip_data(pwm, tcbpwm); + tcbpwm->polarity = PWM_POLARITY_NORMAL; + tcbpwm->duty = 0; + tcbpwm->period = 0; + tcbpwm->div = 0; + + spin_lock(&tcbpwmc->lock); + cmr = __raw_readl(regs + ATMEL_TC_REG(group, CMR)); + /* + * Get init config from Timer Counter registers if + * Timer Counter is already configured as a PWM generator. + */ + if (cmr & ATMEL_TC_WAVE) { + if (index == 0) + tcbpwm->duty = + __raw_readl(regs + ATMEL_TC_REG(group, RA)); + else + tcbpwm->duty = + __raw_readl(regs + ATMEL_TC_REG(group, RB)); + + tcbpwm->div = cmr & ATMEL_TC_TCCLKS; + tcbpwm->period = __raw_readl(regs + ATMEL_TC_REG(group, RC)); + cmr &= (ATMEL_TC_TCCLKS | ATMEL_TC_ACMR_MASK | + ATMEL_TC_BCMR_MASK); + } else + cmr = 0; + + cmr |= ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO | ATMEL_TC_EEVT_XC0; + __raw_writel(cmr, regs + ATMEL_TC_REG(group, CMR)); + spin_unlock(&tcbpwmc->lock); + + tcbpwmc->pwms[pwm->hwpwm] = tcbpwm; + + return 0; +} + +static void atmel_tcb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); + struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm); + struct atmel_tc *tc = tcbpwmc->tc; + + clk_disable(tc->clk[pwm->hwpwm / 2]); + tcbpwmc->pwms[pwm->hwpwm] = NULL; + devm_kfree(chip->dev, tcbpwm); +} + +static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); + struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm); + struct atmel_tc *tc = tcbpwmc->tc; + void __iomem *regs = tc->regs; + unsigned group = pwm->hwpwm / 2; + unsigned index = pwm->hwpwm % 2; + unsigned cmr; + enum pwm_polarity polarity = tcbpwm->polarity; + + /* + * If duty is 0 the timer will be stopped and we have to + * configure the output correctly on software trigger: + * - set output to high if PWM_POLARITY_INVERSED + * - set output to low if PWM_POLARITY_NORMAL + * + * This is why we're reverting polarity in this case. + */ + if (tcbpwm->duty == 0) + polarity = !polarity; + + spin_lock(&tcbpwmc->lock); + cmr = __raw_readl(regs + ATMEL_TC_REG(group, CMR)); + + /* flush old setting and set the new one */ + if (index == 0) { + cmr &= ~ATMEL_TC_ACMR_MASK; + if (polarity == PWM_POLARITY_INVERSED) + cmr |= ATMEL_TC_ASWTRG_CLEAR; + else + cmr |= ATMEL_TC_ASWTRG_SET; + } else { + cmr &= ~ATMEL_TC_BCMR_MASK; + if (polarity == PWM_POLARITY_INVERSED) + cmr |= ATMEL_TC_BSWTRG_CLEAR; + else + cmr |= ATMEL_TC_BSWTRG_SET; + } + + __raw_writel(cmr, regs + ATMEL_TC_REG(group, CMR)); + + /* + * Use software trigger to apply the new setting. + * If both PWM devices in this group are disabled we stop the clock. + */ + if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC))) + __raw_writel(ATMEL_TC_SWTRG | ATMEL_TC_CLKDIS, + regs + ATMEL_TC_REG(group, CCR)); + else + __raw_writel(ATMEL_TC_SWTRG, regs + + ATMEL_TC_REG(group, CCR)); + + spin_unlock(&tcbpwmc->lock); +} + +static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); + struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm); + struct atmel_tc *tc = tcbpwmc->tc; + void __iomem *regs = tc->regs; + unsigned group = pwm->hwpwm / 2; + unsigned index = pwm->hwpwm % 2; + u32 cmr; + enum pwm_polarity polarity = tcbpwm->polarity; + + /* + * If duty is 0 the timer will be stopped and we have to + * configure the output correctly on software trigger: + * - set output to high if PWM_POLARITY_INVERSED + * - set output to low if PWM_POLARITY_NORMAL + * + * This is why we're reverting polarity in this case. + */ + if (tcbpwm->duty == 0) + polarity = !polarity; + + spin_lock(&tcbpwmc->lock); + cmr = __raw_readl(regs + ATMEL_TC_REG(group, CMR)); + + /* flush old setting and set the new one */ + cmr &= ~ATMEL_TC_TCCLKS; + + if (index == 0) { + cmr &= ~ATMEL_TC_ACMR_MASK; + + /* Set CMR flags according to given polarity */ + if (polarity == PWM_POLARITY_INVERSED) + cmr |= ATMEL_TC_ASWTRG_CLEAR; + else + cmr |= ATMEL_TC_ASWTRG_SET; + } else { + cmr &= ~ATMEL_TC_BCMR_MASK; + if (polarity == PWM_POLARITY_INVERSED) + cmr |= ATMEL_TC_BSWTRG_CLEAR; + else + cmr |= ATMEL_TC_BSWTRG_SET; + } + + /* + * If duty is 0 or equal to period there's no need to register + * a specific action on RA/RB and RC compare. + * The output will be configured on software trigger and keep + * this config till next config call. + */ + if (tcbpwm->duty != tcbpwm->period && tcbpwm->duty > 0) { + if (index == 0) { + if (polarity == PWM_POLARITY_INVERSED) + cmr |= ATMEL_TC_ACPA_SET | ATMEL_TC_ACPC_CLEAR; + else + cmr |= ATMEL_TC_ACPA_CLEAR | ATMEL_TC_ACPC_SET; + } else { + if (polarity == PWM_POLARITY_INVERSED) + cmr |= ATMEL_TC_BCPB_SET | ATMEL_TC_BCPC_CLEAR; + else + cmr |= ATMEL_TC_BCPB_CLEAR | ATMEL_TC_BCPC_SET; + } + } + + __raw_writel(cmr, regs + ATMEL_TC_REG(group, CMR)); + + if (index == 0) + __raw_writel(tcbpwm->duty, regs + ATMEL_TC_REG(group, RA)); + else + __raw_writel(tcbpwm->duty, regs + ATMEL_TC_REG(group, RB)); + + __raw_writel(tcbpwm->period, regs + ATMEL_TC_REG(group, RC)); + + /* Use software trigger to apply the new setting */ + __raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, + regs + ATMEL_TC_REG(group, CCR)); + spin_unlock(&tcbpwmc->lock); + return 0; +} + +static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); + struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm); + unsigned group = pwm->hwpwm / 2; + unsigned index = pwm->hwpwm % 2; + struct atmel_tcb_pwm_device *atcbpwm = NULL; + struct atmel_tc *tc = tcbpwmc->tc; + int i; + int slowclk = 0; + unsigned period; + unsigned duty; + unsigned rate = clk_get_rate(tc->clk[group]); + unsigned long long min; + unsigned long long max; + + /* + * Find best clk divisor: + * the smallest divisor which can fulfill the period_ns requirements. + */ + for (i = 0; i < 5; ++i) { + if (atmel_tc_divisors[i] == 0) { + slowclk = i; + continue; + } + min = div_u64((u64)NSEC_PER_SEC * atmel_tc_divisors[i], rate); + max = min << tc->tcb_config->counter_width; + if (max >= period_ns) + break; + } + + /* + * If none of the divisor are small enough to represent period_ns + * take slow clock (32KHz). + */ + if (i == 5) { + i = slowclk; + rate = 32768; + min = div_u64(NSEC_PER_SEC, rate); + max = min << 16; + + /* If period is too big return ERANGE error */ + if (max < period_ns) + return -ERANGE; + } + + duty = div_u64(duty_ns, min); + period = div_u64(period_ns, min); + + if (index == 0) + atcbpwm = tcbpwmc->pwms[pwm->hwpwm + 1]; + else + atcbpwm = tcbpwmc->pwms[pwm->hwpwm - 1]; + + /* + * PWM devices provided by TCB driver are grouped by 2: + * - group 0: PWM 0 & 1 + * - group 1: PWM 2 & 3 + * - group 2: PWM 4 & 5 + * + * PWM devices in a given group must be configured with the + * same period_ns. + * + * We're checking the period value of the second PWM device + * in this group before applying the new config. + */ + if ((atcbpwm && atcbpwm->duty > 0 && + atcbpwm->duty != atcbpwm->period) && + (atcbpwm->div != i || atcbpwm->period != period)) { + dev_err(chip->dev, + "failed to configure period_ns: PWM group already configured with a different value\n"); + return -EINVAL; + } + + tcbpwm->period = period; + tcbpwm->div = i; + tcbpwm->duty = duty; + + /* If the PWM is enabled, call enable to apply the new conf */ + if (test_bit(PWMF_ENABLED, &pwm->flags)) + atmel_tcb_pwm_enable(chip, pwm); + + return 0; +} + +static const struct pwm_ops atmel_tcb_pwm_ops = { + .request = atmel_tcb_pwm_request, + .free = atmel_tcb_pwm_free, + .config = atmel_tcb_pwm_config, + .set_polarity = atmel_tcb_pwm_set_polarity, + .enable = atmel_tcb_pwm_enable, + .disable = atmel_tcb_pwm_disable, +}; + +static int atmel_tcb_pwm_probe(struct platform_device *pdev) +{ + struct atmel_tcb_pwm_chip *tcbpwm; + struct device_node *np = pdev->dev.of_node; + struct atmel_tc *tc; + int err; + int tcblock; + + err = of_property_read_u32(np, "tc-block", &tcblock); + if (err < 0) { + dev_err(&pdev->dev, + "failed to get Timer Counter Block number from device tree (error: %d)\n", + err); + return err; + } + + tc = atmel_tc_alloc(tcblock, "tcb-pwm"); + if (tc == NULL) { + dev_err(&pdev->dev, "failed to allocate Timer Counter Block\n"); + return -ENOMEM; + } + + tcbpwm = devm_kzalloc(&pdev->dev, sizeof(*tcbpwm), GFP_KERNEL); + if (tcbpwm == NULL) { + atmel_tc_free(tc); + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + tcbpwm->chip.dev = &pdev->dev; + tcbpwm->chip.ops = &atmel_tcb_pwm_ops; + tcbpwm->chip.of_xlate = of_pwm_xlate_with_flags; + tcbpwm->chip.of_pwm_n_cells = 3; + tcbpwm->chip.base = -1; + tcbpwm->chip.npwm = NPWM; + tcbpwm->tc = tc; + + spin_lock_init(&tcbpwm->lock); + + err = pwmchip_add(&tcbpwm->chip); + if (err < 0) { + atmel_tc_free(tc); + return err; + } + + platform_set_drvdata(pdev, tcbpwm); + + return 0; +} + +static int atmel_tcb_pwm_remove(struct platform_device *pdev) +{ + struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev); + int err; + + err = pwmchip_remove(&tcbpwm->chip); + if (err < 0) + return err; + + atmel_tc_free(tcbpwm->tc); + + return 0; +} + +static const struct of_device_id atmel_tcb_pwm_dt_ids[] = { + { .compatible = "atmel,tcb-pwm", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids); + +static struct platform_driver atmel_tcb_pwm_driver = { + .driver = { + .name = "atmel-tcb-pwm", + .of_match_table = atmel_tcb_pwm_dt_ids, + }, + .probe = atmel_tcb_pwm_probe, + .remove = atmel_tcb_pwm_remove, +}; +module_platform_driver(atmel_tcb_pwm_driver); + +MODULE_AUTHOR("Boris BREZILLON "); +MODULE_DESCRIPTION("Atmel Timer Counter Pulse Width Modulation Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 8ab432caa46413c9f3ca81d82ea9fa5bae07c3c1 Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Thu, 3 Jan 2013 08:44:15 +1300 Subject: pwm: vt8500: Register write busy test performed incorrectly Correct operation for register writes is to perform a busy-wait after writing the register. Currently the busy wait it performed before, meaning subsequent register writes to bitfields may occur before the previous field has been updated. Also, all registers are defined as 32-bit read/write. Change pwm_busy_wait() to use readl rather than readb. Improve readability of code with defines for registers and bitfields. Signed-off-by: Tony Prisk Signed-off-by: Thierry Reding --- drivers/pwm/pwm-vt8500.c | 64 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index b0ba2d403439..bbc37504103a 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -36,6 +36,25 @@ */ #define VT8500_NR_PWMS 2 +#define REG_CTRL(pwm) (((pwm) << 4) + 0x00) +#define REG_SCALAR(pwm) (((pwm) << 4) + 0x04) +#define REG_PERIOD(pwm) (((pwm) << 4) + 0x08) +#define REG_DUTY(pwm) (((pwm) << 4) + 0x0C) +#define REG_STATUS 0x40 + +#define CTRL_ENABLE BIT(0) +#define CTRL_INVERT BIT(1) +#define CTRL_AUTOLOAD BIT(2) +#define CTRL_STOP_IMM BIT(3) +#define CTRL_LOAD_PRESCALE BIT(4) +#define CTRL_LOAD_PERIOD BIT(5) + +#define STATUS_CTRL_UPDATE BIT(0) +#define STATUS_SCALAR_UPDATE BIT(1) +#define STATUS_PERIOD_UPDATE BIT(2) +#define STATUS_DUTY_UPDATE BIT(3) +#define STATUS_ALL_UPDATE 0x0F + struct vt8500_chip { struct pwm_chip chip; void __iomem *base; @@ -45,15 +64,17 @@ struct vt8500_chip { #define to_vt8500_chip(chip) container_of(chip, struct vt8500_chip, chip) #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) -static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask) +static inline void pwm_busy_wait(struct vt8500_chip *vt8500, int nr, u8 bitmask) { int loops = msecs_to_loops(10); - while ((readb(reg) & bitmask) && --loops) + u32 mask = bitmask << (nr << 8); + + while ((readl(vt8500->base + REG_STATUS) & mask) && --loops) cpu_relax(); if (unlikely(!loops)) - pr_warn("Waiting for status bits 0x%x to clear timed out\n", - bitmask); + dev_warn(vt8500->chip.dev, "Waiting for status bits 0x%x to clear timed out\n", + mask); } static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, @@ -63,6 +84,7 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, unsigned long long c; unsigned long period_cycles, prescale, pv, dc; int err; + u32 val; err = clk_enable(vt8500->clk); if (err < 0) { @@ -91,14 +113,19 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, do_div(c, period_ns); dc = c; - pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 1)); - writel(prescale, vt8500->base + 0x4 + (pwm->hwpwm << 4)); + writel(prescale, vt8500->base + REG_SCALAR(pwm->hwpwm)); + pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_SCALAR_UPDATE); + + writel(pv, vt8500->base + REG_PERIOD(pwm->hwpwm)); + pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_PERIOD_UPDATE); - pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 2)); - writel(pv, vt8500->base + 0x8 + (pwm->hwpwm << 4)); + writel(dc, vt8500->base + REG_DUTY(pwm->hwpwm)); + pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_DUTY_UPDATE); - pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 3)); - writel(dc, vt8500->base + 0xc + (pwm->hwpwm << 4)); + val = readl(vt8500->base + REG_CTRL(pwm->hwpwm)); + val |= CTRL_AUTOLOAD; + writel(val, vt8500->base + REG_CTRL(pwm->hwpwm)); + pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); clk_disable(vt8500->clk); return 0; @@ -106,8 +133,9 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, static int vt8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { - int err; struct vt8500_chip *vt8500 = to_vt8500_chip(chip); + int err; + u32 val; err = clk_enable(vt8500->clk); if (err < 0) { @@ -115,17 +143,23 @@ static int vt8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) return err; } - pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0)); - writel(5, vt8500->base + (pwm->hwpwm << 4)); + val = readl(vt8500->base + REG_CTRL(pwm->hwpwm)); + val |= CTRL_ENABLE; + writel(val, vt8500->base + REG_CTRL(pwm->hwpwm)); + pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); + return 0; } static void vt8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { struct vt8500_chip *vt8500 = to_vt8500_chip(chip); + u32 val; - pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0)); - writel(0, vt8500->base + (pwm->hwpwm << 4)); + val = readl(vt8500->base + REG_CTRL(pwm->hwpwm)); + val &= ~CTRL_ENABLE; + writel(val, vt8500->base + REG_CTRL(pwm->hwpwm)); + pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); clk_disable(vt8500->clk); } -- cgit v1.2.3 From 3ccb1c1702ed4bb07006d20c8173899a69dae242 Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Thu, 3 Jan 2013 08:44:16 +1300 Subject: pwm: vt8500: Add polarity support Add support to set polarity on PWM devices, allowing for inverted duty cycles. Also update the binding document to #pwm-cells = <3> to allow passing the flags from devicetree. Signed-off-by: Tony Prisk Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/vt8500-pwm.txt | 9 ++++++--- drivers/pwm/pwm-vt8500.c | 23 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt b/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt index bcc63678a9a5..d21d82d29855 100644 --- a/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt +++ b/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt @@ -3,14 +3,17 @@ VIA/Wondermedia VT8500/WM8xxx series SoC PWM controller Required properties: - compatible: should be "via,vt8500-pwm" - reg: physical base address and length of the controller's registers -- #pwm-cells: should be 2. The first cell specifies the per-chip index - of the PWM to use and the second cell is the period in nanoseconds. +- #pwm-cells: Should be 3. Number of cells being used to specify PWM property. + First cell specifies the per-chip index of the PWM to use, the second + cell is the period in nanoseconds and bit 0 in the third cell is used to + encode the polarity of PWM output. Set bit 0 of the third in PWM specifier + to 1 for inverse polarity & set to 0 for normal polarity. - clocks: phandle to the PWM source clock Example: pwm1: pwm@d8220000 { - #pwm-cells = <2>; + #pwm-cells = <3>; compatible = "via,vt8500-pwm"; reg = <0xd8220000 0x1000>; clocks = <&clkpwm>; diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index bbc37504103a..98d79e9f0144 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -164,10 +164,31 @@ static void vt8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) clk_disable(vt8500->clk); } +static int vt8500_pwm_set_polarity(struct pwm_chip *chip, + struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + struct vt8500_chip *vt8500 = to_vt8500_chip(chip); + u32 val; + + val = readl(vt8500->base + REG_CTRL(pwm->hwpwm)); + + if (polarity == PWM_POLARITY_INVERSED) + val |= CTRL_INVERT; + else + val &= ~CTRL_INVERT; + + writel(val, vt8500->base + REG_CTRL(pwm->hwpwm)); + pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); + + return 0; +} + static struct pwm_ops vt8500_pwm_ops = { .enable = vt8500_pwm_enable, .disable = vt8500_pwm_disable, .config = vt8500_pwm_config, + .set_polarity = vt8500_pwm_set_polarity, .owner = THIS_MODULE, }; @@ -197,6 +218,8 @@ static int vt8500_pwm_probe(struct platform_device *pdev) chip->chip.dev = &pdev->dev; chip->chip.ops = &vt8500_pwm_ops; + chip->chip.of_xlate = of_pwm_xlate_with_flags; + chip->chip.of_pwm_n_cells = 3; chip->chip.base = -1; chip->chip.npwm = VT8500_NR_PWMS; -- cgit v1.2.3 From 123de543414bce42da9729071962d4a9512612c8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 9 Jan 2013 10:17:01 +0200 Subject: dw_dmac: absence of pdata isn't critical when autocfg is set The patch allows to probe the device when platform data is absent and hardware auto configuration is enabled. In that case the default platform data is used where the channel allocation order is set to ascending, channel priority is set to ascending, and private property is set to true. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 476e9c8fb6ca..c0c9370ddd3f 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1600,13 +1600,6 @@ static int dw_probe(struct platform_device *pdev) int err; int i; - pdata = dev_get_platdata(&pdev->dev); - if (!pdata) - pdata = dw_dma_parse_dt(pdev); - - if (!pdata || pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) - return -EINVAL; - io = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!io) return -EINVAL; @@ -1622,6 +1615,22 @@ static int dw_probe(struct platform_device *pdev) dw_params = dma_read_byaddr(regs, DW_PARAMS); autocfg = dw_params >> DW_PARAMS_EN & 0x1; + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) + pdata = dw_dma_parse_dt(pdev); + + if (!pdata && autocfg) { + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + /* Fill platform data with the default values */ + pdata->is_private = true; + pdata->chan_allocation_order = CHAN_ALLOCATION_ASCENDING; + pdata->chan_priority = CHAN_PRIORITY_ASCENDING; + } else if (!pdata || pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) + return -EINVAL; + if (autocfg) nr_channels = (dw_params >> DW_PARAMS_NR_CHAN & 0x7) + 1; else -- cgit v1.2.3 From cbd65312ba6b508e994d40729e84a51301870bcc Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 9 Jan 2013 10:17:11 +0200 Subject: dw_dmac: check for mapping errors Otherwise we get a warning in case of CONFIG_DMA_API_DEBUG=y [ 45.775943] WARNING: at lib/dma-debug.c:933 check_unmap+0x5d6/0x6ac() [ 45.782369] dw_dmac dw_dmac.0: DMA-API: device driver failed to check map error[device address=0x00000000356efcc0] [size=28 bytes] [mapped as single] Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index c0c9370ddd3f..dd7a8192d5a0 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1087,6 +1087,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) struct dw_desc *desc; int i; unsigned long flags; + int ret; dev_vdbg(chan2dev(chan), "%s\n", __func__); @@ -1110,12 +1111,8 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) spin_unlock_irqrestore(&dwc->lock, flags); desc = kzalloc(sizeof(struct dw_desc), GFP_KERNEL); - if (!desc) { - dev_info(chan2dev(chan), - "only allocated %d descriptors\n", i); - spin_lock_irqsave(&dwc->lock, flags); - break; - } + if (!desc) + goto err_desc_alloc; INIT_LIST_HEAD(&desc->tx_list); dma_async_tx_descriptor_init(&desc->txd, chan); @@ -1123,6 +1120,10 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) desc->txd.flags = DMA_CTRL_ACK; desc->txd.phys = dma_map_single(chan2parent(chan), &desc->lli, sizeof(desc->lli), DMA_TO_DEVICE); + ret = dma_mapping_error(chan2parent(chan), desc->txd.phys); + if (ret) + goto err_desc_alloc; + dwc_desc_put(dwc, desc); spin_lock_irqsave(&dwc->lock, flags); @@ -1133,6 +1134,13 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) dev_dbg(chan2dev(chan), "%s: allocated %d descriptors\n", __func__, i); + return i; + +err_desc_alloc: + kfree(desc); + + dev_info(chan2dev(chan), "only allocated %d descriptors\n", i); + return i; } -- cgit v1.2.3 From 21e93c1e7dae0e8b1914a522c331f0f7763fa89d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 9 Jan 2013 10:17:12 +0200 Subject: dw_dmac: remove redundant check There is no need to check the callback_required parameter, due to we check the callback pointer to be a non-NULL. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index dd7a8192d5a0..c74a7ec9bb9f 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -347,7 +347,7 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, spin_unlock_irqrestore(&dwc->lock, flags); - if (callback_required && callback) + if (callback) callback(param); } -- cgit v1.2.3 From f5c6a7df35b04db906577e90fa5e133e56433bcf Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 9 Jan 2013 10:17:13 +0200 Subject: dw_dmac: update tx_node_active in dwc_do_single_block The "else" keyword in the dw_dma_tasklet is removed as well. All together simplifies the logic of the code and understanding of what is happening there. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index c74a7ec9bb9f..d6b322a1f565 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -247,6 +247,9 @@ static inline void dwc_do_single_block(struct dw_dma_chan *dwc, channel_writel(dwc, CTL_LO, ctllo); channel_writel(dwc, CTL_HI, desc->lli.ctlhi); channel_set_bit(dw, CH_EN, dwc->mask); + + /* Move pointer to next descriptor */ + dwc->tx_node_active = dwc->tx_node_active->next; } /* Called with dwc->lock held and bh disabled */ @@ -278,7 +281,7 @@ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) dwc_initialize(dwc); dwc->tx_list = &first->tx_list; - dwc->tx_node_active = first->tx_list.next; + dwc->tx_node_active = &first->tx_list; dwc_do_single_block(dwc, first); @@ -604,18 +607,13 @@ static void dw_dma_tasklet(unsigned long data) dma_writel(dw, CLEAR.XFER, dwc->mask); - /* move pointer to next descriptor */ - dwc->tx_node_active = - dwc->tx_node_active->next; - dwc_do_single_block(dwc, desc); spin_unlock_irqrestore(&dwc->lock, flags); continue; - } else { - /* we are done here */ - clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); } + /* we are done here */ + clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); } spin_unlock_irqrestore(&dwc->lock, flags); -- cgit v1.2.3 From 21fe3c5245647d200a7ba25d42b80d21c578a8dc Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 9 Jan 2013 10:17:14 +0200 Subject: dma: dw_dmac: add dwc_chan_pause and dwc_chan_resume We will use at least the dwc_chan_resume() later. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index d6b322a1f565..6c9e20a7ff51 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -988,6 +988,26 @@ set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig) return 0; } +static inline void dwc_chan_pause(struct dw_dma_chan *dwc) +{ + u32 cfglo = channel_readl(dwc, CFG_LO); + + channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); + while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY)) + cpu_relax(); + + dwc->paused = true; +} + +static inline void dwc_chan_resume(struct dw_dma_chan *dwc) +{ + u32 cfglo = channel_readl(dwc, CFG_LO); + + channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP); + + dwc->paused = false; +} + static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) { @@ -995,18 +1015,13 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, struct dw_dma *dw = to_dw_dma(chan->device); struct dw_desc *desc, *_desc; unsigned long flags; - u32 cfglo; LIST_HEAD(list); if (cmd == DMA_PAUSE) { spin_lock_irqsave(&dwc->lock, flags); - cfglo = channel_readl(dwc, CFG_LO); - channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); - while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY)) - cpu_relax(); + dwc_chan_pause(dwc); - dwc->paused = true; spin_unlock_irqrestore(&dwc->lock, flags); } else if (cmd == DMA_RESUME) { if (!dwc->paused) @@ -1014,9 +1029,7 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, spin_lock_irqsave(&dwc->lock, flags); - cfglo = channel_readl(dwc, CFG_LO); - channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP); - dwc->paused = false; + dwc_chan_resume(dwc); spin_unlock_irqrestore(&dwc->lock, flags); } else if (cmd == DMA_TERMINATE_ALL) { -- cgit v1.2.3 From 73fa21ea4fc662a2e8e85f84c4ca3fcb55fa4da2 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 7 Jan 2013 15:51:51 +0100 Subject: KVM: s390: Dynamic allocation of virtio-ccw I/O data. Dynamically allocate any data structures like ccw used when doing channel I/O. Otherwise, we'd need to add extra serialization for the different callbacks using the same data structures. Reported-by: Christian Borntraeger Signed-off-by: Cornelia Huck Signed-off-by: Marcelo Tosatti --- drivers/s390/kvm/virtio_ccw.c | 280 ++++++++++++++++++++++++++---------------- 1 file changed, 174 insertions(+), 106 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c index 1a5aff31d752..70419a75d0e0 100644 --- a/drivers/s390/kvm/virtio_ccw.c +++ b/drivers/s390/kvm/virtio_ccw.c @@ -46,11 +46,9 @@ struct vq_config_block { struct virtio_ccw_device { struct virtio_device vdev; - __u8 status; + __u8 *status; __u8 config[VIRTIO_CCW_CONFIG_SIZE]; struct ccw_device *cdev; - struct ccw1 *ccw; - __u32 area; __u32 curr_io; int err; wait_queue_head_t wait_q; @@ -127,14 +125,15 @@ static int doing_io(struct virtio_ccw_device *vcdev, __u32 flag) return ret; } -static int ccw_io_helper(struct virtio_ccw_device *vcdev, __u32 intparm) +static int ccw_io_helper(struct virtio_ccw_device *vcdev, + struct ccw1 *ccw, __u32 intparm) { int ret; unsigned long flags; int flag = intparm & VIRTIO_CCW_INTPARM_MASK; spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags); - ret = ccw_device_start(vcdev->cdev, vcdev->ccw, intparm, 0, 0); + ret = ccw_device_start(vcdev->cdev, ccw, intparm, 0, 0); if (!ret) vcdev->curr_io |= flag; spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags); @@ -167,18 +166,19 @@ static void virtio_ccw_kvm_notify(struct virtqueue *vq) do_kvm_notify(schid, virtqueue_get_queue_index(vq)); } -static int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev, int index) +static int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev, + struct ccw1 *ccw, int index) { vcdev->config_block->index = index; - vcdev->ccw->cmd_code = CCW_CMD_READ_VQ_CONF; - vcdev->ccw->flags = 0; - vcdev->ccw->count = sizeof(struct vq_config_block); - vcdev->ccw->cda = (__u32)(unsigned long)(vcdev->config_block); - ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_VQ_CONF); + ccw->cmd_code = CCW_CMD_READ_VQ_CONF; + ccw->flags = 0; + ccw->count = sizeof(struct vq_config_block); + ccw->cda = (__u32)(unsigned long)(vcdev->config_block); + ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_VQ_CONF); return vcdev->config_block->num; } -static void virtio_ccw_del_vq(struct virtqueue *vq) +static void virtio_ccw_del_vq(struct virtqueue *vq, struct ccw1 *ccw) { struct virtio_ccw_device *vcdev = to_vc_device(vq->vdev); struct virtio_ccw_vq_info *info = vq->priv; @@ -197,11 +197,12 @@ static void virtio_ccw_del_vq(struct virtqueue *vq) info->info_block->align = 0; info->info_block->index = index; info->info_block->num = 0; - vcdev->ccw->cmd_code = CCW_CMD_SET_VQ; - vcdev->ccw->flags = 0; - vcdev->ccw->count = sizeof(*info->info_block); - vcdev->ccw->cda = (__u32)(unsigned long)(info->info_block); - ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_VQ | index); + ccw->cmd_code = CCW_CMD_SET_VQ; + ccw->flags = 0; + ccw->count = sizeof(*info->info_block); + ccw->cda = (__u32)(unsigned long)(info->info_block); + ret = ccw_io_helper(vcdev, ccw, + VIRTIO_CCW_DOING_SET_VQ | index); /* * -ENODEV isn't considered an error: The device is gone anyway. * This may happen on device detach. @@ -220,14 +221,23 @@ static void virtio_ccw_del_vq(struct virtqueue *vq) static void virtio_ccw_del_vqs(struct virtio_device *vdev) { struct virtqueue *vq, *n; + struct ccw1 *ccw; + + ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); + if (!ccw) + return; + list_for_each_entry_safe(vq, n, &vdev->vqs, list) - virtio_ccw_del_vq(vq); + virtio_ccw_del_vq(vq, ccw); + + kfree(ccw); } static struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev, int i, vq_callback_t *callback, - const char *name) + const char *name, + struct ccw1 *ccw) { struct virtio_ccw_device *vcdev = to_vc_device(vdev); int err; @@ -250,7 +260,7 @@ static struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev, err = -ENOMEM; goto out_err; } - info->num = virtio_ccw_read_vq_conf(vcdev, i); + info->num = virtio_ccw_read_vq_conf(vcdev, ccw, i); size = PAGE_ALIGN(vring_size(info->num, KVM_VIRTIO_CCW_RING_ALIGN)); info->queue = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO); if (info->queue == NULL) { @@ -277,11 +287,11 @@ static struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev, info->info_block->align = KVM_VIRTIO_CCW_RING_ALIGN; info->info_block->index = i; info->info_block->num = info->num; - vcdev->ccw->cmd_code = CCW_CMD_SET_VQ; - vcdev->ccw->flags = 0; - vcdev->ccw->count = sizeof(*info->info_block); - vcdev->ccw->cda = (__u32)(unsigned long)(info->info_block); - err = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_VQ | i); + ccw->cmd_code = CCW_CMD_SET_VQ; + ccw->flags = 0; + ccw->count = sizeof(*info->info_block); + ccw->cda = (__u32)(unsigned long)(info->info_block); + err = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_VQ | i); if (err) { dev_warn(&vcdev->cdev->dev, "SET_VQ failed\n"); free_pages_exact(info->queue, size); @@ -312,9 +322,15 @@ static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, struct virtio_ccw_device *vcdev = to_vc_device(vdev); unsigned long *indicatorp = NULL; int ret, i; + struct ccw1 *ccw; + + ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); + if (!ccw) + return -ENOMEM; for (i = 0; i < nvqs; ++i) { - vqs[i] = virtio_ccw_setup_vq(vdev, i, callbacks[i], names[i]); + vqs[i] = virtio_ccw_setup_vq(vdev, i, callbacks[i], names[i], + ccw); if (IS_ERR(vqs[i])) { ret = PTR_ERR(vqs[i]); vqs[i] = NULL; @@ -329,28 +345,30 @@ static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, *indicatorp = (unsigned long) &vcdev->indicators; /* Register queue indicators with host. */ vcdev->indicators = 0; - vcdev->ccw->cmd_code = CCW_CMD_SET_IND; - vcdev->ccw->flags = 0; - vcdev->ccw->count = sizeof(vcdev->indicators); - vcdev->ccw->cda = (__u32)(unsigned long) indicatorp; - ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_IND); + ccw->cmd_code = CCW_CMD_SET_IND; + ccw->flags = 0; + ccw->count = sizeof(vcdev->indicators); + ccw->cda = (__u32)(unsigned long) indicatorp; + ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND); if (ret) goto out; /* Register indicators2 with host for config changes */ *indicatorp = (unsigned long) &vcdev->indicators2; vcdev->indicators2 = 0; - vcdev->ccw->cmd_code = CCW_CMD_SET_CONF_IND; - vcdev->ccw->flags = 0; - vcdev->ccw->count = sizeof(vcdev->indicators2); - vcdev->ccw->cda = (__u32)(unsigned long) indicatorp; - ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_CONF_IND); + ccw->cmd_code = CCW_CMD_SET_CONF_IND; + ccw->flags = 0; + ccw->count = sizeof(vcdev->indicators2); + ccw->cda = (__u32)(unsigned long) indicatorp; + ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_CONF_IND); if (ret) goto out; kfree(indicatorp); + kfree(ccw); return 0; out: kfree(indicatorp); + kfree(ccw); virtio_ccw_del_vqs(vdev); return ret; } @@ -358,64 +376,95 @@ out: static void virtio_ccw_reset(struct virtio_device *vdev) { struct virtio_ccw_device *vcdev = to_vc_device(vdev); + struct ccw1 *ccw; + + ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); + if (!ccw) + return; /* Zero status bits. */ - vcdev->status = 0; + *vcdev->status = 0; /* Send a reset ccw on device. */ - vcdev->ccw->cmd_code = CCW_CMD_VDEV_RESET; - vcdev->ccw->flags = 0; - vcdev->ccw->count = 0; - vcdev->ccw->cda = 0; - ccw_io_helper(vcdev, VIRTIO_CCW_DOING_RESET); + ccw->cmd_code = CCW_CMD_VDEV_RESET; + ccw->flags = 0; + ccw->count = 0; + ccw->cda = 0; + ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_RESET); + kfree(ccw); } static u32 virtio_ccw_get_features(struct virtio_device *vdev) { struct virtio_ccw_device *vcdev = to_vc_device(vdev); - struct virtio_feature_desc features; - int ret; + struct virtio_feature_desc *features; + int ret, rc; + struct ccw1 *ccw; + ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); + if (!ccw) + return 0; + + features = kzalloc(sizeof(*features), GFP_DMA | GFP_KERNEL); + if (!features) { + rc = 0; + goto out_free; + } /* Read the feature bits from the host. */ /* TODO: Features > 32 bits */ - features.index = 0; - vcdev->ccw->cmd_code = CCW_CMD_READ_FEAT; - vcdev->ccw->flags = 0; - vcdev->ccw->count = sizeof(features); - vcdev->ccw->cda = vcdev->area; - ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_FEAT); - if (ret) - return 0; + features->index = 0; + ccw->cmd_code = CCW_CMD_READ_FEAT; + ccw->flags = 0; + ccw->count = sizeof(*features); + ccw->cda = (__u32)(unsigned long)features; + ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_FEAT); + if (ret) { + rc = 0; + goto out_free; + } + + rc = le32_to_cpu(features->features); - memcpy(&features, (void *)(unsigned long)vcdev->area, - sizeof(features)); - return le32_to_cpu(features.features); +out_free: + kfree(features); + kfree(ccw); + return rc; } static void virtio_ccw_finalize_features(struct virtio_device *vdev) { struct virtio_ccw_device *vcdev = to_vc_device(vdev); - struct virtio_feature_desc features; + struct virtio_feature_desc *features; int i; + struct ccw1 *ccw; + + ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); + if (!ccw) + return; + + features = kzalloc(sizeof(*features), GFP_DMA | GFP_KERNEL); + if (!features) + goto out_free; /* Give virtio_ring a chance to accept features. */ vring_transport_features(vdev); - for (i = 0; i < sizeof(*vdev->features) / sizeof(features.features); + for (i = 0; i < sizeof(*vdev->features) / sizeof(features->features); i++) { int highbits = i % 2 ? 32 : 0; - features.index = i; - features.features = cpu_to_le32(vdev->features[i / 2] - >> highbits); - memcpy((void *)(unsigned long)vcdev->area, &features, - sizeof(features)); + features->index = i; + features->features = cpu_to_le32(vdev->features[i / 2] + >> highbits); /* Write the feature bits to the host. */ - vcdev->ccw->cmd_code = CCW_CMD_WRITE_FEAT; - vcdev->ccw->flags = 0; - vcdev->ccw->count = sizeof(features); - vcdev->ccw->cda = vcdev->area; - ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_FEAT); + ccw->cmd_code = CCW_CMD_WRITE_FEAT; + ccw->flags = 0; + ccw->count = sizeof(*features); + ccw->cda = (__u32)(unsigned long)features; + ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_FEAT); } +out_free: + kfree(features); + kfree(ccw); } static void virtio_ccw_get_config(struct virtio_device *vdev, @@ -423,19 +472,32 @@ static void virtio_ccw_get_config(struct virtio_device *vdev, { struct virtio_ccw_device *vcdev = to_vc_device(vdev); int ret; + struct ccw1 *ccw; + void *config_area; + + ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); + if (!ccw) + return; + + config_area = kzalloc(VIRTIO_CCW_CONFIG_SIZE, GFP_DMA | GFP_KERNEL); + if (!config_area) + goto out_free; /* Read the config area from the host. */ - vcdev->ccw->cmd_code = CCW_CMD_READ_CONF; - vcdev->ccw->flags = 0; - vcdev->ccw->count = offset + len; - vcdev->ccw->cda = vcdev->area; - ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_CONFIG); + ccw->cmd_code = CCW_CMD_READ_CONF; + ccw->flags = 0; + ccw->count = offset + len; + ccw->cda = (__u32)(unsigned long)config_area; + ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_CONFIG); if (ret) - return; + goto out_free; - memcpy(vcdev->config, (void *)(unsigned long)vcdev->area, - sizeof(vcdev->config)); + memcpy(vcdev->config, config_area, sizeof(vcdev->config)); memcpy(buf, &vcdev->config[offset], len); + +out_free: + kfree(config_area); + kfree(ccw); } static void virtio_ccw_set_config(struct virtio_device *vdev, @@ -443,37 +505,55 @@ static void virtio_ccw_set_config(struct virtio_device *vdev, unsigned len) { struct virtio_ccw_device *vcdev = to_vc_device(vdev); + struct ccw1 *ccw; + void *config_area; + + ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); + if (!ccw) + return; + + config_area = kzalloc(VIRTIO_CCW_CONFIG_SIZE, GFP_DMA | GFP_KERNEL); + if (!config_area) + goto out_free; memcpy(&vcdev->config[offset], buf, len); /* Write the config area to the host. */ - memcpy((void *)(unsigned long)vcdev->area, vcdev->config, - sizeof(vcdev->config)); - vcdev->ccw->cmd_code = CCW_CMD_WRITE_CONF; - vcdev->ccw->flags = 0; - vcdev->ccw->count = offset + len; - vcdev->ccw->cda = vcdev->area; - ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_CONFIG); + memcpy(config_area, vcdev->config, sizeof(vcdev->config)); + ccw->cmd_code = CCW_CMD_WRITE_CONF; + ccw->flags = 0; + ccw->count = offset + len; + ccw->cda = (__u32)(unsigned long)config_area; + ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_CONFIG); + +out_free: + kfree(config_area); + kfree(ccw); } static u8 virtio_ccw_get_status(struct virtio_device *vdev) { struct virtio_ccw_device *vcdev = to_vc_device(vdev); - return vcdev->status; + return *vcdev->status; } static void virtio_ccw_set_status(struct virtio_device *vdev, u8 status) { struct virtio_ccw_device *vcdev = to_vc_device(vdev); + struct ccw1 *ccw; + + ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); + if (!ccw) + return; /* Write the status to the host. */ - vcdev->status = status; - memcpy((void *)(unsigned long)vcdev->area, &status, sizeof(status)); - vcdev->ccw->cmd_code = CCW_CMD_WRITE_STATUS; - vcdev->ccw->flags = 0; - vcdev->ccw->count = sizeof(status); - vcdev->ccw->cda = vcdev->area; - ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_STATUS); + *vcdev->status = status; + ccw->cmd_code = CCW_CMD_WRITE_STATUS; + ccw->flags = 0; + ccw->count = sizeof(status); + ccw->cda = (__u32)(unsigned long)vcdev->status; + ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_STATUS); + kfree(ccw); } static struct virtio_config_ops virtio_ccw_config_ops = { @@ -499,9 +579,8 @@ static void virtio_ccw_release_dev(struct device *_d) dev); struct virtio_ccw_device *vcdev = to_vc_device(dev); - kfree((void *)(unsigned long)vcdev->area); + kfree(vcdev->status); kfree(vcdev->config_block); - kfree(vcdev->ccw); kfree(vcdev); } @@ -657,9 +736,6 @@ static int virtio_ccw_offline(struct ccw_device *cdev) } -/* Area needs to be big enough to fit status, features or configuration. */ -#define VIRTIO_AREA_SIZE VIRTIO_CCW_CONFIG_SIZE /* biggest possible use */ - static int virtio_ccw_online(struct ccw_device *cdev) { int ret; @@ -671,21 +747,14 @@ static int virtio_ccw_online(struct ccw_device *cdev) ret = -ENOMEM; goto out_free; } - vcdev->area = (__u32)(unsigned long)kzalloc(VIRTIO_AREA_SIZE, - GFP_DMA | GFP_KERNEL); - if (!vcdev->area) { - dev_warn(&cdev->dev, "Cound not get memory for virtio\n"); - ret = -ENOMEM; - goto out_free; - } vcdev->config_block = kzalloc(sizeof(*vcdev->config_block), GFP_DMA | GFP_KERNEL); if (!vcdev->config_block) { ret = -ENOMEM; goto out_free; } - vcdev->ccw = kzalloc(sizeof(*vcdev->ccw), GFP_DMA | GFP_KERNEL); - if (!vcdev->ccw) { + vcdev->status = kzalloc(sizeof(*vcdev->status), GFP_DMA | GFP_KERNEL); + if (!vcdev->status) { ret = -ENOMEM; goto out_free; } @@ -714,9 +783,8 @@ out_put: return ret; out_free: if (vcdev) { - kfree((void *)(unsigned long)vcdev->area); + kfree(vcdev->status); kfree(vcdev->config_block); - kfree(vcdev->ccw); } kfree(vcdev); return ret; -- cgit v1.2.3 From b26ba22bb4f12289f9d5eb878c490e674934a197 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Mon, 7 Jan 2013 15:51:52 +0100 Subject: KVM: s390: Gracefully handle busy conditions on ccw_device_start In rare cases a virtio command might try to issue a ccw before a former ccw was answered with a tsch. This will cause CC=2 (busy). Lets just retry in that case. Signed-off-by: Christian Borntraeger Signed-off-by: Cornelia Huck Signed-off-by: Marcelo Tosatti --- drivers/s390/kvm/virtio_ccw.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c index 70419a75d0e0..2edd94af131c 100644 --- a/drivers/s390/kvm/virtio_ccw.c +++ b/drivers/s390/kvm/virtio_ccw.c @@ -132,11 +132,14 @@ static int ccw_io_helper(struct virtio_ccw_device *vcdev, unsigned long flags; int flag = intparm & VIRTIO_CCW_INTPARM_MASK; - spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags); - ret = ccw_device_start(vcdev->cdev, ccw, intparm, 0, 0); - if (!ret) - vcdev->curr_io |= flag; - spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags); + do { + spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags); + ret = ccw_device_start(vcdev->cdev, ccw, intparm, 0, 0); + if (!ret) + vcdev->curr_io |= flag; + spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags); + cpu_relax(); + } while (ret == -EBUSY); wait_event(vcdev->wait_q, doing_io(vcdev, flag) == 0); return ret ? ret : vcdev->err; } -- cgit v1.2.3 From c7c360eedb8c1eea302a224a8c83d19a4e4cf608 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 2 Dec 2012 03:00:09 +0000 Subject: powerpc/windfarm: Use for_each_node_by_type() macro Use for_each_node_by_type() macro instead of open coding it. Signed-off-by: Wei Yongjun Signed-off-by: Benjamin Herrenschmidt --- drivers/macintosh/windfarm_pm112.c | 2 +- drivers/macintosh/windfarm_pm72.c | 2 +- drivers/macintosh/windfarm_rm31.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/macintosh/windfarm_pm112.c b/drivers/macintosh/windfarm_pm112.c index 35ef6e2582b8..3024685e4cca 100644 --- a/drivers/macintosh/windfarm_pm112.c +++ b/drivers/macintosh/windfarm_pm112.c @@ -681,7 +681,7 @@ static int __init wf_pm112_init(void) /* Count the number of CPU cores */ nr_cores = 0; - for (cpu = NULL; (cpu = of_find_node_by_type(cpu, "cpu")) != NULL; ) + for_each_node_by_type(cpu, "cpu") ++nr_cores; printk(KERN_INFO "windfarm: initializing for dual-core desktop G5\n"); diff --git a/drivers/macintosh/windfarm_pm72.c b/drivers/macintosh/windfarm_pm72.c index 6e5585357cd3..2f506b9d5a52 100644 --- a/drivers/macintosh/windfarm_pm72.c +++ b/drivers/macintosh/windfarm_pm72.c @@ -804,7 +804,7 @@ static int __init wf_pm72_init(void) /* Count the number of CPU cores */ nr_chips = 0; - for (cpu = NULL; (cpu = of_find_node_by_type(cpu, "cpu")) != NULL; ) + for_each_node_by_type(cpu, "cpu") ++nr_chips; if (nr_chips > NR_CHIPS) nr_chips = NR_CHIPS; diff --git a/drivers/macintosh/windfarm_rm31.c b/drivers/macintosh/windfarm_rm31.c index 844003fb4ef0..0b9a79b2f48a 100644 --- a/drivers/macintosh/windfarm_rm31.c +++ b/drivers/macintosh/windfarm_rm31.c @@ -696,7 +696,7 @@ static int __init wf_rm31_init(void) /* Count the number of CPU cores */ nr_chips = 0; - for (cpu = NULL; (cpu = of_find_node_by_type(cpu, "cpu")) != NULL; ) + for_each_node_by_type(cpu, "cpu") ++nr_chips; if (nr_chips > NR_CHIPS) nr_chips = NR_CHIPS; -- cgit v1.2.3 From 7943b310001fd7858154bc3af2a78e0dd6bfea03 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 3 Dec 2012 19:16:18 +0000 Subject: tty/hvsi: Use for_each_compatible_node() macro Use for_each_compatible_node() macro instead of open coding it. Signed-off-by: Wei Yongjun Acked-by: Grant Likely Signed-off-by: Benjamin Herrenschmidt --- drivers/tty/hvc/hvsi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index 68357a6e4de9..60ecdd5ca375 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c @@ -1187,9 +1187,7 @@ static int __init hvsi_console_init(void) hvsi_wait = poll_for_state; /* no irqs yet; must poll */ /* search device tree for vty nodes */ - for (vty = of_find_compatible_node(NULL, "serial", "hvterm-protocol"); - vty != NULL; - vty = of_find_compatible_node(vty, "serial", "hvterm-protocol")) { + for_each_compatible_node(vty, "serial", "hvterm-protocol") { struct hvsi_struct *hp; const uint32_t *vtermno, *irq; -- cgit v1.2.3 From ebe5a0596f2ee4182dc42b39583be71e81aa0443 Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Mon, 31 Dec 2012 09:29:34 +1300 Subject: gpio: vt8500: Export dedicated GPIO before multifunction pins. The vendor does not provide numbering for gpio pins. Vendor source exports dedicated gpio pins first, followed by multifunction pins. As this is what end users expect, this patch changes vt8500 and wm8505 to do the same. Signed-off-by: Tony Prisk Signed-off-by: Linus Walleij --- drivers/gpio/gpio-vt8500.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-vt8500.c b/drivers/gpio/gpio-vt8500.c index b53320a16fc8..9a7c434d2947 100644 --- a/drivers/gpio/gpio-vt8500.c +++ b/drivers/gpio/gpio-vt8500.c @@ -73,19 +73,20 @@ struct vt8500_gpio_data { static struct vt8500_gpio_data vt8500_data = { .num_banks = 7, .banks = { + VT8500_BANK(NO_REG, 0x3C, 0x5C, 0x7C, 9), VT8500_BANK(0x00, 0x20, 0x40, 0x60, 26), VT8500_BANK(0x04, 0x24, 0x44, 0x64, 28), VT8500_BANK(0x08, 0x28, 0x48, 0x68, 31), VT8500_BANK(0x0C, 0x2C, 0x4C, 0x6C, 19), VT8500_BANK(0x10, 0x30, 0x50, 0x70, 19), VT8500_BANK(0x14, 0x34, 0x54, 0x74, 23), - VT8500_BANK(NO_REG, 0x3C, 0x5C, 0x7C, 9), }, }; static struct vt8500_gpio_data wm8505_data = { .num_banks = 10, .banks = { + VT8500_BANK(0x64, 0x8C, 0xB4, 0xDC, 22), VT8500_BANK(0x40, 0x68, 0x90, 0xB8, 8), VT8500_BANK(0x44, 0x6C, 0x94, 0xBC, 32), VT8500_BANK(0x48, 0x70, 0x98, 0xC0, 6), @@ -95,7 +96,6 @@ static struct vt8500_gpio_data wm8505_data = { VT8500_BANK(0x58, 0x80, 0xA8, 0xD0, 5), VT8500_BANK(0x5C, 0x84, 0xAC, 0xD4, 12), VT8500_BANK(0x60, 0x88, 0xB0, 0xD8, 16), - VT8500_BANK(0x64, 0x8C, 0xB4, 0xDC, 22), VT8500_BANK(0x500, 0x504, 0x508, 0x50C, 6), }, }; -- cgit v1.2.3 From 097e3635dc35d1cfc14057f8006f1b1f0eaf1987 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Mon, 7 Jan 2013 18:51:52 +1100 Subject: iommu: moving initialization earlier The iommu_init() initializes IOMMU internal structures and data required for the IOMMU API as iommu_group_alloc(). It is registered as a subsys_initcall now. One of the IOMMU users is going to be a PCI subsystem on POWER. It discovers new IOMMU tables during the PCI scan so the logical place to call iommu_group_alloc() is the moment when a new group is discovered. However PCI scan is done from subsys_initcall hook as IOMMU does so PCI hook can be (and is) called before the IOMMU one. The patch moves IOMMU subsystem initialization one step earlier to make sure that IOMMU is initialized before PCI scan begins. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index ddbdacad7768..1065a1a19478 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -861,7 +861,7 @@ static int __init iommu_init(void) return 0; } -subsys_initcall(iommu_init); +arch_initcall(iommu_init); int iommu_domain_get_attr(struct iommu_domain *domain, enum iommu_attr attr, void *data) -- cgit v1.2.3 From 16c906e51c6f08a15763a85b5686e1ded35e77ab Mon Sep 17 00:00:00 2001 From: Asai Thambi S P Date: Thu, 20 Dec 2012 07:46:25 -0800 Subject: mtip32xx: Add workqueue and NUMA support This patch contains * parallel command completion using workers * bind the workers to the chosen numa node * bind isr to the chosen numa node * allocating memory in the chosen numa node Signed-off-by: Asai Thambi S P Signed-off-by: Sam Bradshaw Signed-off-by: Jens Axboe --- drivers/block/mtip32xx/mtip32xx.c | 335 +++++++++++++++++++++++++++++--------- drivers/block/mtip32xx/mtip32xx.h | 31 +++- 2 files changed, 283 insertions(+), 83 deletions(-) (limited to 'drivers') diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index 9694dd99bbbc..99c2cf461be3 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -88,6 +88,8 @@ static int instance; static int mtip_major; static struct dentry *dfs_parent; +static u32 cpu_use[NR_CPUS]; + static DEFINE_SPINLOCK(rssd_index_lock); static DEFINE_IDA(rssd_index_ida); @@ -296,16 +298,17 @@ static int hba_reset_nosleep(struct driver_data *dd) */ static inline void mtip_issue_ncq_command(struct mtip_port *port, int tag) { - atomic_set(&port->commands[tag].active, 1); + int group = tag >> 5; - spin_lock(&port->cmd_issue_lock); + atomic_set(&port->commands[tag].active, 1); + /* guard SACT and CI registers */ + spin_lock(&port->cmd_issue_lock[group]); writel((1 << MTIP_TAG_BIT(tag)), port->s_active[MTIP_TAG_INDEX(tag)]); writel((1 << MTIP_TAG_BIT(tag)), port->cmd_issue[MTIP_TAG_INDEX(tag)]); - - spin_unlock(&port->cmd_issue_lock); + spin_unlock(&port->cmd_issue_lock[group]); /* Set the command's timeout value.*/ port->commands[tag].comp_time = jiffies + msecs_to_jiffies( @@ -963,56 +966,56 @@ handle_tfe_exit: /* * Handle a set device bits interrupt */ -static inline void mtip_process_sdbf(struct driver_data *dd) +static inline void mtip_workq_sdbfx(struct mtip_port *port, int group, + u32 completed) { - struct mtip_port *port = dd->port; - int group, tag, bit; - u32 completed; + struct driver_data *dd = port->dd; + int tag, bit; struct mtip_cmd *command; - /* walk all bits in all slot groups */ - for (group = 0; group < dd->slot_groups; group++) { - completed = readl(port->completed[group]); - if (!completed) - continue; + if (!completed) { + WARN_ON_ONCE(!completed); + return; + } + /* clear completed status register in the hardware.*/ + writel(completed, port->completed[group]); - /* clear completed status register in the hardware.*/ - writel(completed, port->completed[group]); + /* Process completed commands. */ + for (bit = 0; (bit < 32) && completed; bit++) { + if (completed & 0x01) { + tag = (group << 5) | bit; - /* Process completed commands. */ - for (bit = 0; - (bit < 32) && completed; - bit++, completed >>= 1) { - if (completed & 0x01) { - tag = (group << 5) | bit; + /* skip internal command slot. */ + if (unlikely(tag == MTIP_TAG_INTERNAL)) + continue; - /* skip internal command slot. */ - if (unlikely(tag == MTIP_TAG_INTERNAL)) - continue; + command = &port->commands[tag]; + /* make internal callback */ + if (likely(command->comp_func)) { + command->comp_func( + port, + tag, + command->comp_data, + 0); + } else { + dev_warn(&dd->pdev->dev, + "Null completion " + "for tag %d", + tag); - command = &port->commands[tag]; - /* make internal callback */ - if (likely(command->comp_func)) { - command->comp_func( - port, - tag, - command->comp_data, - 0); - } else { - dev_warn(&dd->pdev->dev, - "Null completion " - "for tag %d", - tag); - - if (mtip_check_surprise_removal( - dd->pdev)) { - mtip_command_cleanup(dd); - return; - } + if (mtip_check_surprise_removal( + dd->pdev)) { + mtip_command_cleanup(dd); + return; } } } + completed >>= 1; } + + /* If last, re-enable interrupts */ + if (atomic_dec_return(&dd->irq_workers_active) == 0) + writel(0xffffffff, dd->mmio + HOST_IRQ_STAT); } /* @@ -1071,6 +1074,8 @@ static inline irqreturn_t mtip_handle_irq(struct driver_data *data) struct mtip_port *port = dd->port; u32 hba_stat, port_stat; int rv = IRQ_NONE; + int do_irq_enable = 1, i, workers; + struct mtip_work *twork; hba_stat = readl(dd->mmio + HOST_IRQ_STAT); if (hba_stat) { @@ -1081,8 +1086,42 @@ static inline irqreturn_t mtip_handle_irq(struct driver_data *data) writel(port_stat, port->mmio + PORT_IRQ_STAT); /* Demux port status */ - if (likely(port_stat & PORT_IRQ_SDB_FIS)) - mtip_process_sdbf(dd); + if (likely(port_stat & PORT_IRQ_SDB_FIS)) { + do_irq_enable = 0; + WARN_ON_ONCE(atomic_read(&dd->irq_workers_active) != 0); + + /* Start at 1: group zero is always local? */ + for (i = 0, workers = 0; i < MTIP_MAX_SLOT_GROUPS; + i++) { + twork = &dd->work[i]; + twork->completed = readl(port->completed[i]); + if (twork->completed) + workers++; + } + + atomic_set(&dd->irq_workers_active, workers); + if (workers) { + for (i = 1; i < MTIP_MAX_SLOT_GROUPS; i++) { + twork = &dd->work[i]; + if (twork->completed) + queue_work_on( + twork->cpu_binding, + dd->isr_workq, + &twork->work); + } + + if (likely(dd->work[0].completed)) + mtip_workq_sdbfx(port, 0, + dd->work[0].completed); + + } else { + /* + * Chip quirk: SDB interrupt but nothing + * to complete + */ + do_irq_enable = 1; + } + } if (unlikely(port_stat & PORT_IRQ_ERR)) { if (unlikely(mtip_check_surprise_removal(dd->pdev))) { @@ -1102,20 +1141,12 @@ static inline irqreturn_t mtip_handle_irq(struct driver_data *data) } /* acknowledge interrupt */ - writel(hba_stat, dd->mmio + HOST_IRQ_STAT); + if (unlikely(do_irq_enable)) + writel(hba_stat, dd->mmio + HOST_IRQ_STAT); return rv; } -/* - * Wrapper for mtip_handle_irq - * (ignores return code) - */ -static void mtip_tasklet(unsigned long data) -{ - mtip_handle_irq((struct driver_data *) data); -} - /* * HBA interrupt subroutine. * @@ -1129,8 +1160,8 @@ static void mtip_tasklet(unsigned long data) static irqreturn_t mtip_irq_handler(int irq, void *instance) { struct driver_data *dd = instance; - tasklet_schedule(&dd->tasklet); - return IRQ_HANDLED; + + return mtip_handle_irq(dd); } static void mtip_issue_non_ncq_command(struct mtip_port *port, int tag) @@ -3004,20 +3035,24 @@ static int mtip_hw_init(struct driver_data *dd) hba_setup(dd); - tasklet_init(&dd->tasklet, mtip_tasklet, (unsigned long)dd); - - dd->port = kzalloc(sizeof(struct mtip_port), GFP_KERNEL); + dd->port = kzalloc_node(sizeof(struct mtip_port), GFP_KERNEL, + dd->numa_node); if (!dd->port) { dev_err(&dd->pdev->dev, "Memory allocation: port structure\n"); return -ENOMEM; } + /* Continue workqueue setup */ + for (i = 0; i < MTIP_MAX_SLOT_GROUPS; i++) + dd->work[i].port = dd->port; + /* Counting semaphore to track command slot usage */ sema_init(&dd->port->cmd_slot, num_command_slots - 1); /* Spinlock to prevent concurrent issue */ - spin_lock_init(&dd->port->cmd_issue_lock); + for (i = 0; i < MTIP_MAX_SLOT_GROUPS; i++) + spin_lock_init(&dd->port->cmd_issue_lock[i]); /* Set the port mmio base address. */ dd->port->mmio = dd->mmio + PORT_OFFSET; @@ -3164,6 +3199,7 @@ static int mtip_hw_init(struct driver_data *dd) "Unable to allocate IRQ %d\n", dd->pdev->irq); goto out2; } + irq_set_affinity_hint(dd->pdev->irq, get_cpu_mask(dd->isr_binding)); /* Enable interrupts on the HBA. */ writel(readl(dd->mmio + HOST_CTL) | HOST_IRQ_EN, @@ -3240,7 +3276,8 @@ out3: writel(readl(dd->mmio + HOST_CTL) & ~HOST_IRQ_EN, dd->mmio + HOST_CTL); - /*Release the IRQ. */ + /* Release the IRQ. */ + irq_set_affinity_hint(dd->pdev->irq, NULL); devm_free_irq(&dd->pdev->dev, dd->pdev->irq, dd); out2: @@ -3290,11 +3327,9 @@ static int mtip_hw_exit(struct driver_data *dd) del_timer_sync(&dd->port->cmd_timer); /* Release the IRQ. */ + irq_set_affinity_hint(dd->pdev->irq, NULL); devm_free_irq(&dd->pdev->dev, dd->pdev->irq, dd); - /* Stop the bottom half tasklet. */ - tasklet_kill(&dd->tasklet); - /* Free the command/command header memory. */ dmam_free_coherent(&dd->pdev->dev, HW_PORT_PRIV_DMA_SZ + (ATA_SECT_SIZE * 4), @@ -3710,7 +3745,7 @@ static int mtip_block_initialize(struct driver_data *dd) goto protocol_init_error; } - dd->disk = alloc_disk(MTIP_MAX_MINORS); + dd->disk = alloc_disk_node(MTIP_MAX_MINORS, dd->numa_node); if (dd->disk == NULL) { dev_err(&dd->pdev->dev, "Unable to allocate gendisk structure\n"); @@ -3754,7 +3789,7 @@ static int mtip_block_initialize(struct driver_data *dd) skip_create_disk: /* Allocate the request queue. */ - dd->queue = blk_alloc_queue(GFP_KERNEL); + dd->queue = blk_alloc_queue_node(GFP_KERNEL, dd->numa_node); if (dd->queue == NULL) { dev_err(&dd->pdev->dev, "Unable to allocate request queue\n"); @@ -3812,9 +3847,8 @@ skip_create_disk: start_service_thread: sprintf(thd_name, "mtip_svc_thd_%02d", index); - - dd->mtip_svc_handler = kthread_run(mtip_service_thread, - dd, thd_name); + dd->mtip_svc_handler = kthread_create_on_node(mtip_service_thread, + dd, dd->numa_node, thd_name); if (IS_ERR(dd->mtip_svc_handler)) { dev_err(&dd->pdev->dev, "service thread failed to start\n"); @@ -3822,7 +3856,7 @@ start_service_thread: rv = -EFAULT; goto kthread_run_error; } - + wake_up_process(dd->mtip_svc_handler); if (wait_for_rebuild == MTIP_FTL_REBUILD_MAGIC) rv = wait_for_rebuild; @@ -3951,6 +3985,56 @@ static int mtip_block_resume(struct driver_data *dd) return 0; } +static void drop_cpu(int cpu) +{ + cpu_use[cpu]--; +} + +static int get_least_used_cpu_on_node(int node) +{ + int cpu, least_used_cpu, least_cnt; + const struct cpumask *node_mask; + + node_mask = cpumask_of_node(node); + least_used_cpu = cpumask_first(node_mask); + least_cnt = cpu_use[least_used_cpu]; + cpu = least_used_cpu; + + for_each_cpu(cpu, node_mask) { + if (cpu_use[cpu] < least_cnt) { + least_used_cpu = cpu; + least_cnt = cpu_use[cpu]; + } + } + cpu_use[least_used_cpu]++; + return least_used_cpu; +} + +/* Helper for selecting a node in round robin mode */ +static inline int mtip_get_next_rr_node(void) +{ + static int next_node = -1; + + if (next_node == -1) { + next_node = first_online_node; + return next_node; + } + + next_node = next_online_node(next_node); + if (next_node == MAX_NUMNODES) + next_node = first_online_node; + return next_node; +} + +DEFINE_HANDLER(0); +DEFINE_HANDLER(1); +DEFINE_HANDLER(2); +DEFINE_HANDLER(3); +DEFINE_HANDLER(4); +DEFINE_HANDLER(5); +DEFINE_HANDLER(6); +DEFINE_HANDLER(7); + /* * Called for each supported PCI device detected. * @@ -3965,9 +4049,25 @@ static int mtip_pci_probe(struct pci_dev *pdev, { int rv = 0; struct driver_data *dd = NULL; + char cpu_list[256]; + const struct cpumask *node_mask; + int cpu, i = 0, j = 0; + int my_node = NUMA_NO_NODE; /* Allocate memory for this devices private data. */ - dd = kzalloc(sizeof(struct driver_data), GFP_KERNEL); + my_node = pcibus_to_node(pdev->bus); + if (my_node != NUMA_NO_NODE) { + if (!node_online(my_node)) + my_node = mtip_get_next_rr_node(); + } else { + dev_info(&pdev->dev, "Kernel not reporting proximity, choosing a node\n"); + my_node = mtip_get_next_rr_node(); + } + dev_info(&pdev->dev, "NUMA node %d (closest: %d,%d, probe on %d:%d)\n", + my_node, pcibus_to_node(pdev->bus), dev_to_node(&pdev->dev), + cpu_to_node(smp_processor_id()), smp_processor_id()); + + dd = kzalloc_node(sizeof(struct driver_data), GFP_KERNEL, my_node); if (dd == NULL) { dev_err(&pdev->dev, "Unable to allocate memory for driver data\n"); @@ -4004,19 +4104,82 @@ static int mtip_pci_probe(struct pci_dev *pdev, } } - pci_set_master(pdev); + /* Copy the info we may need later into the private data structure. */ + dd->major = mtip_major; + dd->instance = instance; + dd->pdev = pdev; + dd->numa_node = my_node; + + memset(dd->workq_name, 0, 32); + snprintf(dd->workq_name, 31, "mtipq%d", dd->instance); + dd->isr_workq = create_workqueue(dd->workq_name); + if (!dd->isr_workq) { + dev_warn(&pdev->dev, "Can't create wq %d\n", dd->instance); + goto block_initialize_err; + } + + memset(cpu_list, 0, sizeof(cpu_list)); + + node_mask = cpumask_of_node(dd->numa_node); + if (!cpumask_empty(node_mask)) { + for_each_cpu(cpu, node_mask) + { + snprintf(&cpu_list[j], 256 - j, "%d ", cpu); + j = strlen(cpu_list); + } + + dev_info(&pdev->dev, "Node %d on package %d has %d cpu(s): %s\n", + dd->numa_node, + topology_physical_package_id(cpumask_first(node_mask)), + nr_cpus_node(dd->numa_node), + cpu_list); + } else + dev_dbg(&pdev->dev, "mtip32xx: node_mask empty\n"); + + dd->isr_binding = get_least_used_cpu_on_node(dd->numa_node); + dev_info(&pdev->dev, "Initial IRQ binding node:cpu %d:%d\n", + cpu_to_node(dd->isr_binding), dd->isr_binding); + + /* first worker context always runs in ISR */ + dd->work[0].cpu_binding = dd->isr_binding; + dd->work[1].cpu_binding = get_least_used_cpu_on_node(dd->numa_node); + dd->work[2].cpu_binding = get_least_used_cpu_on_node(dd->numa_node); + dd->work[3].cpu_binding = dd->work[0].cpu_binding; + dd->work[4].cpu_binding = dd->work[1].cpu_binding; + dd->work[5].cpu_binding = dd->work[2].cpu_binding; + dd->work[6].cpu_binding = dd->work[2].cpu_binding; + dd->work[7].cpu_binding = dd->work[1].cpu_binding; + + /* Log the bindings */ + for_each_present_cpu(cpu) { + memset(cpu_list, 0, sizeof(cpu_list)); + for (i = 0, j = 0; i < MTIP_MAX_SLOT_GROUPS; i++) { + if (dd->work[i].cpu_binding == cpu) { + snprintf(&cpu_list[j], 256 - j, "%d ", i); + j = strlen(cpu_list); + } + } + if (j) + dev_info(&pdev->dev, "CPU %d: WQs %s\n", cpu, cpu_list); + } + + INIT_WORK(&dd->work[0].work, mtip_workq_sdbf0); + INIT_WORK(&dd->work[1].work, mtip_workq_sdbf1); + INIT_WORK(&dd->work[2].work, mtip_workq_sdbf2); + INIT_WORK(&dd->work[3].work, mtip_workq_sdbf3); + INIT_WORK(&dd->work[4].work, mtip_workq_sdbf4); + INIT_WORK(&dd->work[5].work, mtip_workq_sdbf5); + INIT_WORK(&dd->work[6].work, mtip_workq_sdbf6); + INIT_WORK(&dd->work[7].work, mtip_workq_sdbf7); + + pci_set_master(pdev); if (pci_enable_msi(pdev)) { dev_warn(&pdev->dev, "Unable to enable MSI interrupt.\n"); goto block_initialize_err; } - /* Copy the info we may need later into the private data structure. */ - dd->major = mtip_major; - dd->instance = instance; - dd->pdev = pdev; - /* Initialize the block layer. */ rv = mtip_block_initialize(dd); if (rv < 0) { @@ -4036,7 +4199,13 @@ static int mtip_pci_probe(struct pci_dev *pdev, block_initialize_err: pci_disable_msi(pdev); - + if (dd->isr_workq) { + flush_workqueue(dd->isr_workq); + destroy_workqueue(dd->isr_workq); + drop_cpu(dd->work[0].cpu_binding); + drop_cpu(dd->work[1].cpu_binding); + drop_cpu(dd->work[2].cpu_binding); + } setmask_err: pcim_iounmap_regions(pdev, 1 << MTIP_ABAR); @@ -4077,6 +4246,14 @@ static void mtip_pci_remove(struct pci_dev *pdev) /* Clean up the block layer. */ mtip_block_remove(dd); + if (dd->isr_workq) { + flush_workqueue(dd->isr_workq); + destroy_workqueue(dd->isr_workq); + drop_cpu(dd->work[0].cpu_binding); + drop_cpu(dd->work[1].cpu_binding); + drop_cpu(dd->work[2].cpu_binding); + } + pci_disable_msi(pdev); kfree(dd); diff --git a/drivers/block/mtip32xx/mtip32xx.h b/drivers/block/mtip32xx/mtip32xx.h index b1742640556a..d782b1aa913e 100644 --- a/drivers/block/mtip32xx/mtip32xx.h +++ b/drivers/block/mtip32xx/mtip32xx.h @@ -164,6 +164,20 @@ struct smart_attr { u8 res[3]; } __packed; +struct mtip_work { + struct work_struct work; + void *port; + int cpu_binding; + u32 completed; +} ____cacheline_aligned_in_smp; + +#define DEFINE_HANDLER(group) \ + void mtip_workq_sdbf##group(struct work_struct *work) \ + { \ + struct mtip_work *w = (struct mtip_work *) work; \ + mtip_workq_sdbfx(w->port, group, w->completed); \ + } + /* Register Frame Information Structure (FIS), host to device. */ struct host_to_dev_fis { /* @@ -424,7 +438,7 @@ struct mtip_port { */ struct semaphore cmd_slot; /* Spinlock for working around command-issue bug. */ - spinlock_t cmd_issue_lock; + spinlock_t cmd_issue_lock[MTIP_MAX_SLOT_GROUPS]; }; /* @@ -447,9 +461,6 @@ struct driver_data { struct mtip_port *port; /* Pointer to the port data structure. */ - /* Tasklet used to process the bottom half of the ISR. */ - struct tasklet_struct tasklet; - unsigned product_type; /* magic value declaring the product type */ unsigned slot_groups; /* number of slot groups the product supports */ @@ -461,6 +472,18 @@ struct driver_data { struct task_struct *mtip_svc_handler; /* task_struct of svc thd */ struct dentry *dfs_node; + + int numa_node; /* NUMA support */ + + char workq_name[32]; + + struct workqueue_struct *isr_workq; + + struct mtip_work work[MTIP_MAX_SLOT_GROUPS]; + + atomic_t irq_workers_active; + + int isr_binding; }; #endif -- cgit v1.2.3 From 152834694dcb90d3088bc77fa436755c66553929 Mon Sep 17 00:00:00 2001 From: Asai Thambi S P Date: Fri, 11 Jan 2013 14:41:34 +0100 Subject: mtip32xx: add trim support TRIM support added through vendor unique command. Signed-off-by: Sam Bradshaw < sbradshaw@micron.com> Signed-off-by: Asai Thambi S P Signed-off-by: Jens Axboe --- drivers/block/mtip32xx/mtip32xx.c | 96 +++++++++++++++++++++++++++++++++++++++ drivers/block/mtip32xx/mtip32xx.h | 17 +++++++ 2 files changed, 113 insertions(+) (limited to 'drivers') diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index 99c2cf461be3..b7579b76b4a1 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -1519,6 +1519,12 @@ static int mtip_get_identify(struct mtip_port *port, void __user *user_buffer) } #endif + /* Demux ID.DRAT & ID.RZAT to determine trim support */ + if (port->identify[69] & (1 << 14) && port->identify[69] & (1 << 5)) + port->dd->trim_supp = true; + else + port->dd->trim_supp = false; + /* Set the identify buffer as valid. */ port->identify_valid = 1; @@ -1705,6 +1711,81 @@ static int mtip_get_smart_attr(struct mtip_port *port, unsigned int id, return rv; } +/* + * Trim unused sectors + * + * @dd pointer to driver_data structure + * @lba starting lba + * @len # of 512b sectors to trim + * + * return value + * -ENOMEM Out of dma memory + * -EINVAL Invalid parameters passed in, trim not supported + * -EIO Error submitting trim request to hw + */ +int mtip_send_trim(struct driver_data *dd, unsigned int lba, unsigned int len) +{ + int i, rv = 0; + u64 tlba, tlen, sect_left; + struct mtip_trim_entry *buf; + dma_addr_t dma_addr; + struct host_to_dev_fis fis; + + if (!len || dd->trim_supp == false) + return -EINVAL; + + /* Trim request too big */ + WARN_ON(len > (MTIP_MAX_TRIM_ENTRY_LEN * MTIP_MAX_TRIM_ENTRIES)); + + /* Trim request not aligned on 4k boundary */ + WARN_ON(len % 8 != 0); + + /* Warn if vu_trim structure is too big */ + WARN_ON(sizeof(struct mtip_trim) > ATA_SECT_SIZE); + + /* Allocate a DMA buffer for the trim structure */ + buf = dmam_alloc_coherent(&dd->pdev->dev, ATA_SECT_SIZE, &dma_addr, + GFP_KERNEL); + if (!buf) + return -ENOMEM; + memset(buf, 0, ATA_SECT_SIZE); + + for (i = 0, sect_left = len, tlba = lba; + i < MTIP_MAX_TRIM_ENTRIES && sect_left; + i++) { + tlen = (sect_left >= MTIP_MAX_TRIM_ENTRY_LEN ? + MTIP_MAX_TRIM_ENTRY_LEN : + sect_left); + buf[i].lba = __force_bit2int cpu_to_le32(tlba); + buf[i].range = __force_bit2int cpu_to_le16(tlen); + tlba += tlen; + sect_left -= tlen; + } + WARN_ON(sect_left != 0); + + /* Build the fis */ + memset(&fis, 0, sizeof(struct host_to_dev_fis)); + fis.type = 0x27; + fis.opts = 1 << 7; + fis.command = 0xfb; + fis.features = 0x60; + fis.sect_count = 1; + fis.device = ATA_DEVICE_OBS; + + if (mtip_exec_internal_command(dd->port, + &fis, + 5, + dma_addr, + ATA_SECT_SIZE, + 0, + GFP_KERNEL, + MTIP_TRIM_TIMEOUT_MS) < 0) + rv = -EIO; + + dmam_free_coherent(&dd->pdev->dev, ATA_SECT_SIZE, buf, dma_addr); + return rv; +} + /* * Get the drive capacity. * @@ -3675,6 +3756,12 @@ static void mtip_make_request(struct request_queue *queue, struct bio *bio) } } + if (unlikely(bio->bi_rw & REQ_DISCARD)) { + bio_endio(bio, mtip_send_trim(dd, bio->bi_sector, + bio_sectors(bio))); + return; + } + if (unlikely(!bio_has_data(bio))) { blk_queue_flush(queue, 0); bio_endio(bio, 0); @@ -3817,6 +3904,15 @@ skip_create_disk: */ blk_queue_flush(dd->queue, 0); + /* Signal trim support */ + if (dd->trim_supp == true) { + set_bit(QUEUE_FLAG_DISCARD, &dd->queue->queue_flags); + dd->queue->limits.discard_granularity = 4096; + blk_queue_max_discard_sectors(dd->queue, + MTIP_MAX_TRIM_ENTRY_LEN * MTIP_MAX_TRIM_ENTRIES); + dd->queue->limits.discard_zeroes_data = 0; + } + /* Set the capacity of the device in 512 byte sectors. */ if (!(mtip_hw_get_capacity(dd, &capacity))) { dev_warn(&dd->pdev->dev, diff --git a/drivers/block/mtip32xx/mtip32xx.h b/drivers/block/mtip32xx/mtip32xx.h index d782b1aa913e..3bffff5f670c 100644 --- a/drivers/block/mtip32xx/mtip32xx.h +++ b/drivers/block/mtip32xx/mtip32xx.h @@ -178,6 +178,21 @@ struct mtip_work { mtip_workq_sdbfx(w->port, group, w->completed); \ } +#define MTIP_TRIM_TIMEOUT_MS 240000 +#define MTIP_MAX_TRIM_ENTRIES 8 +#define MTIP_MAX_TRIM_ENTRY_LEN 0xfff8 + +struct mtip_trim_entry { + u32 lba; /* starting lba of region */ + u16 rsvd; /* unused */ + u16 range; /* # of 512b blocks to trim */ +} __packed; + +struct mtip_trim { + /* Array of regions to trim */ + struct mtip_trim_entry entry[MTIP_MAX_TRIM_ENTRIES]; +} __packed; + /* Register Frame Information Structure (FIS), host to device. */ struct host_to_dev_fis { /* @@ -473,6 +488,8 @@ struct driver_data { struct dentry *dfs_node; + bool trim_supp; /* flag indicating trim support */ + int numa_node; /* NUMA support */ char workq_name[32]; -- cgit v1.2.3 From 3d6a87430e764cf4132710538139c10970929189 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 11 Jan 2013 09:52:44 +0300 Subject: dac960: return success instead of -ENOTTY There is a missing break statement here. This used to return directly but we re-worked it in 2008 to add locking as part of the BKL push down. Signed-off-by: Dan Carpenter Signed-off-by: Jens Axboe --- drivers/block/DAC960.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c index 9a13e889837e..0d3ffc51a7bd 100644 --- a/drivers/block/DAC960.c +++ b/drivers/block/DAC960.c @@ -7054,6 +7054,7 @@ static long DAC960_gam_ioctl(struct file *file, unsigned int Request, else ErrorCode = 0; } + break; default: ErrorCode = -ENOTTY; } -- cgit v1.2.3 From 3151d14aa6e983aa36d51a80d0477859f9ba12af Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Thu, 10 Jan 2013 21:35:34 -0300 Subject: [media] media: remove __dev* annotations Hi Mauro, After merging the v4l-dvb tree, today's linux-next build (x86_64 allmodconfig) failed like this: drivers/media/platform/sh_veu.c:1146:22: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'sh_veu_probe' drivers/media/platform/sh_veu.c:1228:22: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'sh_veu_remove' drivers/media/platform/sh_veu.c:1244:2: error: implicit declaration of function '__devexit_p' [-Werror=implicit-function-declaration] drivers/media/platform/sh_veu.c:1244:25: error: 'sh_veu_remove' undeclared here (not in a function) drivers/media/platform/sh_veu.c: In function 'sh_veu_init': drivers/media/platform/sh_veu.c:1253:45: error: 'sh_veu_probe' undeclared (first use in this function) drivers/media/platform/sh_veu.c:1253:45: note: each undeclared identifier is reported only once for each function it appears in drivers/media/platform/sh_veu.c: At top level: drivers/media/platform/sh_veu.c:1095:20: warning: 'sh_veu_bh' defined but not used [-Wunused-function] drivers/media/platform/sh_veu.c:1109:20: warning: 'sh_veu_isr' defined but not used [-Wunused-function] drivers/media/platform/sh_veu.c: In function 'sh_veu_init': drivers/media/platform/sh_veu.c:1254:1: warning: control reaches end of non-void function [-Wreturn-type] Caused by commit 05efa71bdc0e ("[media] media: add a VEU MEM2MEM format conversion and scaling driver") interacting with commit 54b956b90360 ("Remove __dev* markings from init.h") from the driver-core.current tree. I have applied the following merge fix patch which could be applied directly to the v4l-dvb tree (please): CONFIG_HOTPLUG is always true now and the __dev* macros have meen removed. Cc: Guennadi Liakhovetski Signed-off-by: Stephen Rothwell Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_veu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index a0186768479d..cb54c69d5748 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -1143,7 +1143,7 @@ static irqreturn_t sh_veu_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static int __devinit sh_veu_probe(struct platform_device *pdev) +static int sh_veu_probe(struct platform_device *pdev) { struct sh_veu_dev *veu; struct resource *reg_res; @@ -1225,7 +1225,7 @@ einitctx: return ret; } -static int __devexit sh_veu_remove(struct platform_device *pdev) +static int sh_veu_remove(struct platform_device *pdev) { struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); struct sh_veu_dev *veu = container_of(v4l2_dev, @@ -1241,7 +1241,7 @@ static int __devexit sh_veu_remove(struct platform_device *pdev) } static struct platform_driver __refdata sh_veu_pdrv = { - .remove = __devexit_p(sh_veu_remove), + .remove = sh_veu_remove, .driver = { .name = "sh_veu", .owner = THIS_MODULE, -- cgit v1.2.3 From 25bac122b8d874d625982aead1974be819eca841 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Sat, 12 Jan 2013 15:31:40 +0800 Subject: drivers/block/mtip32xx/mtip32xx.c:4029:1: sparse: symbol 'mtip_workq_sdbf0' was not declared. Should it be static? Hi Asai, FYI, there are new sparse warnings show up in tree: git://git.kernel.dk/linux-block.git for-3.9/drivers head: 3d6a87430e764cf4132710538139c10970929189 commit: 16c906e51c6f08a15763a85b5686e1ded35e77ab [1/3] mtip32xx: Add workqueue and NUMA support drivers/block/mtip32xx/mtip32xx.c:3267:17: sparse: cast to restricted __le32 >> drivers/block/mtip32xx/mtip32xx.c:4029:1: sparse: symbol 'mtip_workq_sdbf0' was not declared. Should it be static? >> drivers/block/mtip32xx/mtip32xx.c:4030:1: sparse: symbol 'mtip_workq_sdbf1' was not declared. Should it be static? >> drivers/block/mtip32xx/mtip32xx.c:4031:1: sparse: symbol 'mtip_workq_sdbf2' was not declared. Should it be static? >> drivers/block/mtip32xx/mtip32xx.c:4032:1: sparse: symbol 'mtip_workq_sdbf3' was not declared. Should it be static? >> drivers/block/mtip32xx/mtip32xx.c:4033:1: sparse: symbol 'mtip_workq_sdbf4' was not declared. Should it be static? >> drivers/block/mtip32xx/mtip32xx.c:4034:1: sparse: symbol 'mtip_workq_sdbf5' was not declared. Should it be static? >> drivers/block/mtip32xx/mtip32xx.c:4035:1: sparse: symbol 'mtip_workq_sdbf6' was not declared. Should it be static? >> drivers/block/mtip32xx/mtip32xx.c:4036:1: sparse: symbol 'mtip_workq_sdbf7' was not declared. Should it be static? drivers/block/mtip32xx/mtip32xx.c: In function 'mtip_hw_read_flags': drivers/block/mtip32xx/mtip32xx.c:2723:1: warning: the frame size of 1036 bytes is larger than 1024 bytes [-Wframe-larger-than=] drivers/block/mtip32xx/mtip32xx.c: In function 'mtip_hw_read_registers': drivers/block/mtip32xx/mtip32xx.c:2700:1: warning: the frame size of 1044 bytes is larger than 1024 bytes [-Wframe-larger-than=] Please consider folding the below diff :-) Signed-off-by: Jens Axboe --- drivers/block/mtip32xx/mtip32xx.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index b7579b76b4a1..f07dbcba7ba6 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -4122,14 +4122,14 @@ static inline int mtip_get_next_rr_node(void) return next_node; } -DEFINE_HANDLER(0); -DEFINE_HANDLER(1); -DEFINE_HANDLER(2); -DEFINE_HANDLER(3); -DEFINE_HANDLER(4); -DEFINE_HANDLER(5); -DEFINE_HANDLER(6); -DEFINE_HANDLER(7); +static DEFINE_HANDLER(0); +static DEFINE_HANDLER(1); +static DEFINE_HANDLER(2); +static DEFINE_HANDLER(3); +static DEFINE_HANDLER(4); +static DEFINE_HANDLER(5); +static DEFINE_HANDLER(6); +static DEFINE_HANDLER(7); /* * Called for each supported PCI device detected. -- cgit v1.2.3 From 478c030eecbec927d62561c5f48a4515ea0fa21a Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Sat, 12 Jan 2013 16:13:01 +0800 Subject: drivers/block/mtip32xx/mtip32xx.c:1726:5: sparse: symbol 'mtip_send_trim' was not declared. Should it be static? Hi Asai, FYI, there are new sparse warnings show up in tree: git://git.kernel.dk/linux-block.git for-3.9/drivers head: 3d6a87430e764cf4132710538139c10970929189 commit: 152834694dcb90d3088bc77fa436755c66553929 [2/3] mtip32xx: add trim support >> drivers/block/mtip32xx/mtip32xx.c:1726:5: sparse: symbol 'mtip_send_trim' was not declared. Should it be static? drivers/block/mtip32xx/mtip32xx.c:3348:17: sparse: cast to restricted __le32 drivers/block/mtip32xx/mtip32xx.c:4125:1: sparse: symbol 'mtip_workq_sdbf0' was not declared. Should it be static? drivers/block/mtip32xx/mtip32xx.c:4126:1: sparse: symbol 'mtip_workq_sdbf1' was not declared. Should it be static? drivers/block/mtip32xx/mtip32xx.c:4127:1: sparse: symbol 'mtip_workq_sdbf2' was not declared. Should it be static? drivers/block/mtip32xx/mtip32xx.c:4128:1: sparse: symbol 'mtip_workq_sdbf3' was not declared. Should it be static? drivers/block/mtip32xx/mtip32xx.c:4129:1: sparse: symbol 'mtip_workq_sdbf4' was not declared. Should it be static? drivers/block/mtip32xx/mtip32xx.c:4130:1: sparse: symbol 'mtip_workq_sdbf5' was not declared. Should it be static? drivers/block/mtip32xx/mtip32xx.c:4131:1: sparse: symbol 'mtip_workq_sdbf6' was not declared. Should it be static? drivers/block/mtip32xx/mtip32xx.c:4132:1: sparse: symbol 'mtip_workq_sdbf7' was not declared. Should it be static? drivers/block/mtip32xx/mtip32xx.c: In function 'mtip_hw_read_flags': drivers/block/mtip32xx/mtip32xx.c:2804:1: warning: the frame size of 1036 bytes is larger than 1024 bytes [-Wframe-larger-than=] drivers/block/mtip32xx/mtip32xx.c: In function 'mtip_hw_read_registers': drivers/block/mtip32xx/mtip32xx.c:2781:1: warning: the frame size of 1044 bytes is larger than 1024 bytes [-Wframe-larger-than=] Signed-off-by: Jens Axboe --- drivers/block/mtip32xx/mtip32xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index f07dbcba7ba6..9790666f90f2 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -1723,7 +1723,7 @@ static int mtip_get_smart_attr(struct mtip_port *port, unsigned int id, * -EINVAL Invalid parameters passed in, trim not supported * -EIO Error submitting trim request to hw */ -int mtip_send_trim(struct driver_data *dd, unsigned int lba, unsigned int len) +static int mtip_send_trim(struct driver_data *dd, unsigned int lba, unsigned int len) { int i, rv = 0; u64 tlba, tlen, sect_left; -- cgit v1.2.3 From 0e7264cc79a2d5c0ffa32c08d8f1cf84b2ec4fef Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:52:57 +0200 Subject: dma: at_hdmac: check direction properly for cyclic transfers dma_transfer_direction is a normal enum. It means we can't usually use the values as bit fields. Let's adjust this check and move it above the usage of the direction parameter, due to the nature of the following usage of it. Signed-off-by: Andy Shevchenko Reviewed-by: Viresh Kumar Acked-by: Nicolas Ferre Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 13a02f4425b0..6e13f262139a 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -778,7 +778,7 @@ err: */ static int atc_dma_cyclic_check_values(unsigned int reg_width, dma_addr_t buf_addr, - size_t period_len, enum dma_transfer_direction direction) + size_t period_len) { if (period_len > (ATC_BTSIZE_MAX << reg_width)) goto err_out; @@ -786,8 +786,6 @@ atc_dma_cyclic_check_values(unsigned int reg_width, dma_addr_t buf_addr, goto err_out; if (unlikely(buf_addr & ((1 << reg_width) - 1))) goto err_out; - if (unlikely(!(direction & (DMA_DEV_TO_MEM | DMA_MEM_TO_DEV)))) - goto err_out; return 0; @@ -886,14 +884,16 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, return NULL; } + if (unlikely(!is_slave_direction(direction))) + goto err_out; + if (sconfig->direction == DMA_MEM_TO_DEV) reg_width = convert_buswidth(sconfig->dst_addr_width); else reg_width = convert_buswidth(sconfig->src_addr_width); /* Check for too big/unaligned periods and unaligned DMA buffer */ - if (atc_dma_cyclic_check_values(reg_width, buf_addr, - period_len, direction)) + if (atc_dma_cyclic_check_values(reg_width, buf_addr, period_len)) goto err_out; /* build cyclic linked list */ -- cgit v1.2.3 From f44b92f4dd2f6caf326b149e0b9636a1d4e50184 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:52:58 +0200 Subject: dma: dw_dmac: check direction properly in dw_dma_cyclic_prep dma_transfer_direction is a normal enum. It means we can't usually use the values as bit fields. Let's adjust this check and move it above the usage of the direction parameter, due to the nature of the following usage of it. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 6c9e20a7ff51..ca996bc5ccaf 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1355,6 +1355,9 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, retval = ERR_PTR(-EINVAL); + if (unlikely(!is_slave_direction(direction))) + goto out_err; + if (direction == DMA_MEM_TO_DEV) reg_width = __ffs(sconfig->dst_addr_width); else @@ -1369,8 +1372,6 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, goto out_err; if (unlikely(buf_addr & ((1 << reg_width) - 1))) goto out_err; - if (unlikely(!(direction & (DMA_MEM_TO_DEV | DMA_DEV_TO_MEM)))) - goto out_err; retval = ERR_PTR(-ENOMEM); -- cgit v1.2.3 From 0efcdb20f4a83967c99da3d3bef9018f86532fae Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:52:59 +0200 Subject: dma: ep93xx_dma: reuse is_slave_direction helper The is_slave_direction helps to check if the transfer type is slave. Signed-off-by: Andy Shevchenko Reviewed-by: Viresh Kumar Acked-by: Mika Westerberg Signed-off-by: Vinod Koul --- drivers/dma/ep93xx_dma.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index bcfde400904f..f2bf8c0c4675 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -903,8 +903,7 @@ static int ep93xx_dma_alloc_chan_resources(struct dma_chan *chan) switch (data->port) { case EP93XX_DMA_SSP: case EP93XX_DMA_IDE: - if (data->direction != DMA_MEM_TO_DEV && - data->direction != DMA_DEV_TO_MEM) + if (!is_slave_direction(data->direction)) return -EINVAL; break; default: -- cgit v1.2.3 From 5127c4f8a314b798459985d93f7829cf9cf9bbc3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:53:00 +0200 Subject: dma: ipu_idmac: reuse is_slave_direction helper The is_slave_direction helps to check if the transfer type is slave. Signed-off-by: Andy Shevchenko Reviewed-by: Viresh Kumar Cc: Guennadi Liakhovetski Signed-off-by: Vinod Koul --- drivers/dma/ipu/ipu_idmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index 65855373cee6..8c61d17a86bf 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c @@ -1347,7 +1347,7 @@ static struct dma_async_tx_descriptor *idmac_prep_slave_sg(struct dma_chan *chan chan->chan_id != IDMAC_IC_7) return NULL; - if (direction != DMA_DEV_TO_MEM && direction != DMA_MEM_TO_DEV) { + if (!is_slave_direction(direction)) { dev_err(chan->device->dev, "Invalid DMA direction %d!\n", direction); return NULL; } -- cgit v1.2.3 From a725dcc0342b4d9ffc6ae4aedc2973d902aabeb1 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:53:01 +0200 Subject: dma: ste_dma40: reuse is_slave_direction helper The is_slave_direction helps to check if the transfer type is slave. Signed-off-by: Andy Shevchenko Reviewed-by: Viresh Kumar Reviewed-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/ste_dma40.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 23c5573e62dd..e5e60bb68d9d 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2337,7 +2337,7 @@ static struct dma_async_tx_descriptor *d40_prep_slave_sg(struct dma_chan *chan, unsigned long dma_flags, void *context) { - if (direction != DMA_DEV_TO_MEM && direction != DMA_MEM_TO_DEV) + if (!is_slave_direction(direction)) return NULL; return d40_prep_sg(chan, sgl, sgl, sg_len, direction, dma_flags); -- cgit v1.2.3 From 01126856ff4f7d4cc5899c208fd4d3c7d0a2b83a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:53:02 +0200 Subject: dw_dmac: call .probe after we have a device in place If we don't yet have the platform device for the driver when it is being loaded we fail to probe the driver. So instead of calling probe() directly we call platform_driver_register(). It will call the probe() immediately if we have the device but also makes the driver to work on platforms where the platform device is created later. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index ca996bc5ccaf..e554027849b7 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1858,6 +1858,7 @@ MODULE_DEVICE_TABLE(of, dw_dma_id_table); #endif static struct platform_driver dw_driver = { + .probe = dw_probe, .remove = dw_remove, .shutdown = dw_shutdown, .driver = { @@ -1869,7 +1870,7 @@ static struct platform_driver dw_driver = { static int __init dw_init(void) { - return platform_driver_probe(&dw_driver, dw_probe); + return platform_driver_register(&dw_driver); } subsys_initcall(dw_init); -- cgit v1.2.3 From 0fdb567fc72da906e230ce7e2aae2feba260a6be Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:53:03 +0200 Subject: dw_dmac: store direction in the custom channel structure Currently the direction value comes from the generic slave configuration structure and explicitly as a preparation function parameter. The first one is kinda obsoleted. Thus, we have to store the value passed to the preparation function somewhere in our structures to be able to use it later. The best candidate to provide the storage is a custom channel structure. Until now we still keep and check the direction field of the slave config structure as well. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 12 ++++++++++-- drivers/dma/dw_dmac_regs.h | 14 ++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index e554027849b7..a2c5a60bc2e2 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -178,9 +178,9 @@ static void dwc_initialize(struct dw_dma_chan *dwc) cfghi = dws->cfg_hi; cfglo |= dws->cfg_lo & ~DWC_CFGL_CH_PRIOR_MASK; } else { - if (dwc->dma_sconfig.direction == DMA_MEM_TO_DEV) + if (dwc->direction == DMA_MEM_TO_DEV) cfghi = DWC_CFGH_DST_PER(dwc->dma_sconfig.slave_id); - else if (dwc->dma_sconfig.direction == DMA_DEV_TO_MEM) + else if (dwc->direction == DMA_DEV_TO_MEM) cfghi = DWC_CFGH_SRC_PER(dwc->dma_sconfig.slave_id); } @@ -721,6 +721,8 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, return NULL; } + dwc->direction = DMA_MEM_TO_MEM; + data_width = min_t(unsigned int, dwc->dw->data_width[dwc_get_sms(dws)], dwc->dw->data_width[dwc_get_dms(dws)]); @@ -805,6 +807,8 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, if (unlikely(!dws || !sg_len)) return NULL; + dwc->direction = direction; + prev = first = NULL; switch (direction) { @@ -981,6 +985,7 @@ set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig) return -EINVAL; memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig)); + dwc->direction = sconfig->direction; convert_burst(&dwc->dma_sconfig.src_maxburst); convert_burst(&dwc->dma_sconfig.dst_maxburst); @@ -1358,6 +1363,8 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, if (unlikely(!is_slave_direction(direction))) goto out_err; + dwc->direction = direction; + if (direction == DMA_MEM_TO_DEV) reg_width = __ffs(sconfig->dst_addr_width); else @@ -1732,6 +1739,7 @@ static int dw_probe(struct platform_device *pdev) channel_clear_bit(dw, CH_EN, dwc->mask); dwc->dw = dw; + dwc->direction = DMA_TRANS_NONE; /* hardware configuration */ if (autocfg) { diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 8881e9b277a3..f9532c29b808 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -9,6 +9,7 @@ * published by the Free Software Foundation. */ +#include #include #define DW_DMA_MAX_NR_CHANNELS 8 @@ -184,12 +185,13 @@ enum dw_dmac_flags { }; struct dw_dma_chan { - struct dma_chan chan; - void __iomem *ch_regs; - u8 mask; - u8 priority; - bool paused; - bool initialized; + struct dma_chan chan; + void __iomem *ch_regs; + u8 mask; + u8 priority; + enum dma_transfer_direction direction; + bool paused; + bool initialized; /* software emulation of the LLP transfers */ struct list_head *tx_list; -- cgit v1.2.3 From 495aea4b571d1b7f77883f87754247b115627f68 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 11:11:41 +0200 Subject: dw_dmac: make usage of dw_dma_slave optional The driver requires a custom slave configuration to be present to be able to make the slave transfers. Nevertheless, in some cases we need only the request line as an additional information to the generic slave configuration. The request line is provided by slave_id parameter of the dma_slave_config structure. That's why the custom slave configuration could be optional for such cases. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index a2c5a60bc2e2..e5378b5741be 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -50,11 +50,12 @@ static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave) struct dw_dma_slave *__slave = (_chan->private); \ struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \ struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \ + bool _is_slave = is_slave_direction(_dwc->direction); \ int _dms = dwc_get_dms(__slave); \ int _sms = dwc_get_sms(__slave); \ - u8 _smsize = __slave ? _sconfig->src_maxburst : \ + u8 _smsize = _is_slave ? _sconfig->src_maxburst : \ DW_DMA_MSIZE_16; \ - u8 _dmsize = __slave ? _sconfig->dst_maxburst : \ + u8 _dmsize = _is_slave ? _sconfig->dst_maxburst : \ DW_DMA_MSIZE_16; \ \ (DWC_CTLL_DST_MSIZE(_dmsize) \ @@ -328,7 +329,7 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, list_splice_init(&desc->tx_list, &dwc->free_list); list_move(&desc->desc_node, &dwc->free_list); - if (!dwc->chan.private) { + if (!is_slave_direction(dwc->direction)) { struct device *parent = chan2parent(&dwc->chan); if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) { if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE) @@ -804,7 +805,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, dev_vdbg(chan2dev(chan), "%s\n", __func__); - if (unlikely(!dws || !sg_len)) + if (unlikely(!is_slave_direction(direction) || !sg_len)) return NULL; dwc->direction = direction; @@ -980,8 +981,8 @@ set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - /* Check if it is chan is configured for slave transfers */ - if (!chan->private) + /* Check if chan will be configured for slave transfers */ + if (!is_slave_direction(sconfig->direction)) return -EINVAL; memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig)); -- cgit v1.2.3 From 23d5f4ec9de43dbc73a42f1483d9339b907c3dff Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:53:05 +0200 Subject: dw_dmac: backlink to dw_dma in dw_dma_chan is superfluous The same information could be extracted from the struct dma_chan. The patch introduces helper function dwc_get_data_width() as well. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 27 ++++++++++++++++++++------- drivers/dma/dw_dmac_regs.h | 3 --- 2 files changed, 20 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index e5378b5741be..154952abc2e9 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -73,6 +73,22 @@ static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave) */ #define NR_DESCS_PER_CHANNEL 64 +#define SRC_MASTER 0 +#define DST_MASTER 1 + +static inline unsigned int dwc_get_data_width(struct dma_chan *chan, int master) +{ + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_dma_slave *dws = chan->private; + + if (master == SRC_MASTER) + return dw->data_width[dwc_get_sms(dws)]; + else if (master == DST_MASTER) + return dw->data_width[dwc_get_dms(dws)]; + + return 0; +} + /*----------------------------------------------------------------------*/ /* @@ -701,7 +717,6 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, size_t len, unsigned long flags) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma_slave *dws = chan->private; struct dw_desc *desc; struct dw_desc *first; struct dw_desc *prev; @@ -724,8 +739,8 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, dwc->direction = DMA_MEM_TO_MEM; - data_width = min_t(unsigned int, dwc->dw->data_width[dwc_get_sms(dws)], - dwc->dw->data_width[dwc_get_dms(dws)]); + data_width = min_t(unsigned int, dwc_get_data_width(chan, SRC_MASTER), + dwc_get_data_width(chan, DST_MASTER)); src_width = dst_width = min_t(unsigned int, data_width, dwc_fast_fls(src | dest | len)); @@ -790,7 +805,6 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned long flags, void *context) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma_slave *dws = chan->private; struct dma_slave_config *sconfig = &dwc->dma_sconfig; struct dw_desc *prev; struct dw_desc *first; @@ -824,7 +838,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) : DWC_CTLL_FC(DW_DMA_FC_D_M2P); - data_width = dwc->dw->data_width[dwc_get_sms(dws)]; + data_width = dwc_get_data_width(chan, SRC_MASTER); for_each_sg(sgl, sg, sg_len, i) { struct dw_desc *desc; @@ -887,7 +901,7 @@ slave_sg_todev_fill_desc: ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_P2M) : DWC_CTLL_FC(DW_DMA_FC_D_P2M); - data_width = dwc->dw->data_width[dwc_get_dms(dws)]; + data_width = dwc_get_data_width(chan, DST_MASTER); for_each_sg(sgl, sg, sg_len, i) { struct dw_desc *desc; @@ -1739,7 +1753,6 @@ static int dw_probe(struct platform_device *pdev) channel_clear_bit(dw, CH_EN, dwc->mask); - dwc->dw = dw; dwc->direction = DMA_TRANS_NONE; /* hardware configuration */ diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index f9532c29b808..577f2dd35882 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -214,9 +214,6 @@ struct dw_dma_chan { /* configuration passed via DMA_SLAVE_CONFIG */ struct dma_slave_config dma_sconfig; - - /* backlink to dw_dma */ - struct dw_dma *dw; }; static inline struct dw_dma_chan_regs __iomem * -- cgit v1.2.3 From a5dbff111cacecd2e79843a51cc86d21d3648af5 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 10 Jan 2013 10:53:06 +0200 Subject: dma: dw_dmac: clear suspend bit during termination The DMA transfer could not be established if previously it was paused and terminated. In that case the channel's suspend bit remains set that prevents to transfer anything until channel is resumed. The patch adds the dwc_chan_resume() call instead of a plain flag assignment. That clears the DWC_CFGL_CH_SUSP bit as well during termination. Signed-off-by: Heikki Krogerus Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 154952abc2e9..28d5f01c350c 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1059,7 +1059,7 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, dwc_chan_disable(dw, dwc); - dwc->paused = false; + dwc_chan_resume(dwc); /* active_list entries will end up before queued entries */ list_splice_init(&dwc->queue, &list); -- cgit v1.2.3 From 3a95b9fbba893ebfa9b83de105707539e0228e0c Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Sat, 24 Nov 2012 00:22:56 +0000 Subject: pl080.h: moved from arm/include/asm/hardware to include/linux/amba/ The header is used by drivers/dma/amba-pl08x.c, which can be compiled under x86, where PL080 exists under a PCI-to-AMBA bridge. This patche moves it where it can be accessed by other architectures, and fixes all users. Signed-off-by: Alessandro Rubini Acked-by: Giancarlo Asnaghi Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- arch/arm/include/asm/hardware/pl080.h | 146 ---------------------------------- arch/arm/mach-s3c64xx/dma.c | 2 +- arch/arm/mach-spear3xx/spear3xx.c | 1 - arch/arm/mach-spear6xx/spear6xx.c | 2 +- drivers/dma/amba-pl08x.c | 2 +- include/linux/amba/pl080.h | 146 ++++++++++++++++++++++++++++++++++ 6 files changed, 149 insertions(+), 150 deletions(-) delete mode 100644 arch/arm/include/asm/hardware/pl080.h create mode 100644 include/linux/amba/pl080.h (limited to 'drivers') diff --git a/arch/arm/include/asm/hardware/pl080.h b/arch/arm/include/asm/hardware/pl080.h deleted file mode 100644 index 4eea2107214b..000000000000 --- a/arch/arm/include/asm/hardware/pl080.h +++ /dev/null @@ -1,146 +0,0 @@ -/* arch/arm/include/asm/hardware/pl080.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * ARM PrimeCell PL080 DMA controller - * - * 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. -*/ - -/* Note, there are some Samsung updates to this controller block which - * make it not entierly compatible with the PL080 specification from - * ARM. When in doubt, check the Samsung documentation first. - * - * The Samsung defines are PL080S, and add an extra control register, - * the ability to move more than 2^11 counts of data and some extra - * OneNAND features. -*/ - -#ifndef ASM_PL080_H -#define ASM_PL080_H - -#define PL080_INT_STATUS (0x00) -#define PL080_TC_STATUS (0x04) -#define PL080_TC_CLEAR (0x08) -#define PL080_ERR_STATUS (0x0C) -#define PL080_ERR_CLEAR (0x10) -#define PL080_RAW_TC_STATUS (0x14) -#define PL080_RAW_ERR_STATUS (0x18) -#define PL080_EN_CHAN (0x1c) -#define PL080_SOFT_BREQ (0x20) -#define PL080_SOFT_SREQ (0x24) -#define PL080_SOFT_LBREQ (0x28) -#define PL080_SOFT_LSREQ (0x2C) - -#define PL080_CONFIG (0x30) -#define PL080_CONFIG_M2_BE (1 << 2) -#define PL080_CONFIG_M1_BE (1 << 1) -#define PL080_CONFIG_ENABLE (1 << 0) - -#define PL080_SYNC (0x34) - -/* Per channel configuration registers */ - -#define PL080_Cx_STRIDE (0x20) -#define PL080_Cx_BASE(x) ((0x100 + (x * 0x20))) -#define PL080_Cx_SRC_ADDR(x) ((0x100 + (x * 0x20))) -#define PL080_Cx_DST_ADDR(x) ((0x104 + (x * 0x20))) -#define PL080_Cx_LLI(x) ((0x108 + (x * 0x20))) -#define PL080_Cx_CONTROL(x) ((0x10C + (x * 0x20))) -#define PL080_Cx_CONFIG(x) ((0x110 + (x * 0x20))) -#define PL080S_Cx_CONTROL2(x) ((0x110 + (x * 0x20))) -#define PL080S_Cx_CONFIG(x) ((0x114 + (x * 0x20))) - -#define PL080_CH_SRC_ADDR (0x00) -#define PL080_CH_DST_ADDR (0x04) -#define PL080_CH_LLI (0x08) -#define PL080_CH_CONTROL (0x0C) -#define PL080_CH_CONFIG (0x10) -#define PL080S_CH_CONTROL2 (0x10) -#define PL080S_CH_CONFIG (0x14) - -#define PL080_LLI_ADDR_MASK (0x3fffffff << 2) -#define PL080_LLI_ADDR_SHIFT (2) -#define PL080_LLI_LM_AHB2 (1 << 0) - -#define PL080_CONTROL_TC_IRQ_EN (1 << 31) -#define PL080_CONTROL_PROT_MASK (0x7 << 28) -#define PL080_CONTROL_PROT_SHIFT (28) -#define PL080_CONTROL_PROT_CACHE (1 << 30) -#define PL080_CONTROL_PROT_BUFF (1 << 29) -#define PL080_CONTROL_PROT_SYS (1 << 28) -#define PL080_CONTROL_DST_INCR (1 << 27) -#define PL080_CONTROL_SRC_INCR (1 << 26) -#define PL080_CONTROL_DST_AHB2 (1 << 25) -#define PL080_CONTROL_SRC_AHB2 (1 << 24) -#define PL080_CONTROL_DWIDTH_MASK (0x7 << 21) -#define PL080_CONTROL_DWIDTH_SHIFT (21) -#define PL080_CONTROL_SWIDTH_MASK (0x7 << 18) -#define PL080_CONTROL_SWIDTH_SHIFT (18) -#define PL080_CONTROL_DB_SIZE_MASK (0x7 << 15) -#define PL080_CONTROL_DB_SIZE_SHIFT (15) -#define PL080_CONTROL_SB_SIZE_MASK (0x7 << 12) -#define PL080_CONTROL_SB_SIZE_SHIFT (12) -#define PL080_CONTROL_TRANSFER_SIZE_MASK (0xfff << 0) -#define PL080_CONTROL_TRANSFER_SIZE_SHIFT (0) - -#define PL080_BSIZE_1 (0x0) -#define PL080_BSIZE_4 (0x1) -#define PL080_BSIZE_8 (0x2) -#define PL080_BSIZE_16 (0x3) -#define PL080_BSIZE_32 (0x4) -#define PL080_BSIZE_64 (0x5) -#define PL080_BSIZE_128 (0x6) -#define PL080_BSIZE_256 (0x7) - -#define PL080_WIDTH_8BIT (0x0) -#define PL080_WIDTH_16BIT (0x1) -#define PL080_WIDTH_32BIT (0x2) - -#define PL080N_CONFIG_ITPROT (1 << 20) -#define PL080N_CONFIG_SECPROT (1 << 19) -#define PL080_CONFIG_HALT (1 << 18) -#define PL080_CONFIG_ACTIVE (1 << 17) /* RO */ -#define PL080_CONFIG_LOCK (1 << 16) -#define PL080_CONFIG_TC_IRQ_MASK (1 << 15) -#define PL080_CONFIG_ERR_IRQ_MASK (1 << 14) -#define PL080_CONFIG_FLOW_CONTROL_MASK (0x7 << 11) -#define PL080_CONFIG_FLOW_CONTROL_SHIFT (11) -#define PL080_CONFIG_DST_SEL_MASK (0xf << 6) -#define PL080_CONFIG_DST_SEL_SHIFT (6) -#define PL080_CONFIG_SRC_SEL_MASK (0xf << 1) -#define PL080_CONFIG_SRC_SEL_SHIFT (1) -#define PL080_CONFIG_ENABLE (1 << 0) - -#define PL080_FLOW_MEM2MEM (0x0) -#define PL080_FLOW_MEM2PER (0x1) -#define PL080_FLOW_PER2MEM (0x2) -#define PL080_FLOW_SRC2DST (0x3) -#define PL080_FLOW_SRC2DST_DST (0x4) -#define PL080_FLOW_MEM2PER_PER (0x5) -#define PL080_FLOW_PER2MEM_PER (0x6) -#define PL080_FLOW_SRC2DST_SRC (0x7) - -/* DMA linked list chain structure */ - -struct pl080_lli { - u32 src_addr; - u32 dst_addr; - u32 next_lli; - u32 control0; -}; - -struct pl080s_lli { - u32 src_addr; - u32 dst_addr; - u32 next_lli; - u32 control0; - u32 control1; -}; - -#endif /* ASM_PL080_H */ diff --git a/arch/arm/mach-s3c64xx/dma.c b/arch/arm/mach-s3c64xx/dma.c index f2a7a1725596..a77f5214bbe8 100644 --- a/arch/arm/mach-s3c64xx/dma.c +++ b/arch/arm/mach-s3c64xx/dma.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -30,7 +31,6 @@ #include -#include /* dma channel state information */ diff --git a/arch/arm/mach-spear3xx/spear3xx.c b/arch/arm/mach-spear3xx/spear3xx.c index 38fe95db31a7..3d9b1b5e8ed9 100644 --- a/arch/arm/mach-spear3xx/spear3xx.c +++ b/arch/arm/mach-spear3xx/spear3xx.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/arm/mach-spear6xx/spear6xx.c b/arch/arm/mach-spear6xx/spear6xx.c index 5a5a52db252b..8ce65a23b06e 100644 --- a/arch/arm/mach-spear6xx/spear6xx.c +++ b/arch/arm/mach-spear6xx/spear6xx.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 6eb6a5c210bb..8bad254a498d 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -83,7 +83,7 @@ #include #include #include -#include +#include #include "dmaengine.h" #include "virt-dma.h" diff --git a/include/linux/amba/pl080.h b/include/linux/amba/pl080.h new file mode 100644 index 000000000000..3e7b62fbefbd --- /dev/null +++ b/include/linux/amba/pl080.h @@ -0,0 +1,146 @@ +/* include/linux/amba/pl080.h + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks + * + * ARM PrimeCell PL080 DMA controller + * + * 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. +*/ + +/* Note, there are some Samsung updates to this controller block which + * make it not entierly compatible with the PL080 specification from + * ARM. When in doubt, check the Samsung documentation first. + * + * The Samsung defines are PL080S, and add an extra control register, + * the ability to move more than 2^11 counts of data and some extra + * OneNAND features. +*/ + +#ifndef ASM_PL080_H +#define ASM_PL080_H + +#define PL080_INT_STATUS (0x00) +#define PL080_TC_STATUS (0x04) +#define PL080_TC_CLEAR (0x08) +#define PL080_ERR_STATUS (0x0C) +#define PL080_ERR_CLEAR (0x10) +#define PL080_RAW_TC_STATUS (0x14) +#define PL080_RAW_ERR_STATUS (0x18) +#define PL080_EN_CHAN (0x1c) +#define PL080_SOFT_BREQ (0x20) +#define PL080_SOFT_SREQ (0x24) +#define PL080_SOFT_LBREQ (0x28) +#define PL080_SOFT_LSREQ (0x2C) + +#define PL080_CONFIG (0x30) +#define PL080_CONFIG_M2_BE (1 << 2) +#define PL080_CONFIG_M1_BE (1 << 1) +#define PL080_CONFIG_ENABLE (1 << 0) + +#define PL080_SYNC (0x34) + +/* Per channel configuration registers */ + +#define PL080_Cx_STRIDE (0x20) +#define PL080_Cx_BASE(x) ((0x100 + (x * 0x20))) +#define PL080_Cx_SRC_ADDR(x) ((0x100 + (x * 0x20))) +#define PL080_Cx_DST_ADDR(x) ((0x104 + (x * 0x20))) +#define PL080_Cx_LLI(x) ((0x108 + (x * 0x20))) +#define PL080_Cx_CONTROL(x) ((0x10C + (x * 0x20))) +#define PL080_Cx_CONFIG(x) ((0x110 + (x * 0x20))) +#define PL080S_Cx_CONTROL2(x) ((0x110 + (x * 0x20))) +#define PL080S_Cx_CONFIG(x) ((0x114 + (x * 0x20))) + +#define PL080_CH_SRC_ADDR (0x00) +#define PL080_CH_DST_ADDR (0x04) +#define PL080_CH_LLI (0x08) +#define PL080_CH_CONTROL (0x0C) +#define PL080_CH_CONFIG (0x10) +#define PL080S_CH_CONTROL2 (0x10) +#define PL080S_CH_CONFIG (0x14) + +#define PL080_LLI_ADDR_MASK (0x3fffffff << 2) +#define PL080_LLI_ADDR_SHIFT (2) +#define PL080_LLI_LM_AHB2 (1 << 0) + +#define PL080_CONTROL_TC_IRQ_EN (1 << 31) +#define PL080_CONTROL_PROT_MASK (0x7 << 28) +#define PL080_CONTROL_PROT_SHIFT (28) +#define PL080_CONTROL_PROT_CACHE (1 << 30) +#define PL080_CONTROL_PROT_BUFF (1 << 29) +#define PL080_CONTROL_PROT_SYS (1 << 28) +#define PL080_CONTROL_DST_INCR (1 << 27) +#define PL080_CONTROL_SRC_INCR (1 << 26) +#define PL080_CONTROL_DST_AHB2 (1 << 25) +#define PL080_CONTROL_SRC_AHB2 (1 << 24) +#define PL080_CONTROL_DWIDTH_MASK (0x7 << 21) +#define PL080_CONTROL_DWIDTH_SHIFT (21) +#define PL080_CONTROL_SWIDTH_MASK (0x7 << 18) +#define PL080_CONTROL_SWIDTH_SHIFT (18) +#define PL080_CONTROL_DB_SIZE_MASK (0x7 << 15) +#define PL080_CONTROL_DB_SIZE_SHIFT (15) +#define PL080_CONTROL_SB_SIZE_MASK (0x7 << 12) +#define PL080_CONTROL_SB_SIZE_SHIFT (12) +#define PL080_CONTROL_TRANSFER_SIZE_MASK (0xfff << 0) +#define PL080_CONTROL_TRANSFER_SIZE_SHIFT (0) + +#define PL080_BSIZE_1 (0x0) +#define PL080_BSIZE_4 (0x1) +#define PL080_BSIZE_8 (0x2) +#define PL080_BSIZE_16 (0x3) +#define PL080_BSIZE_32 (0x4) +#define PL080_BSIZE_64 (0x5) +#define PL080_BSIZE_128 (0x6) +#define PL080_BSIZE_256 (0x7) + +#define PL080_WIDTH_8BIT (0x0) +#define PL080_WIDTH_16BIT (0x1) +#define PL080_WIDTH_32BIT (0x2) + +#define PL080N_CONFIG_ITPROT (1 << 20) +#define PL080N_CONFIG_SECPROT (1 << 19) +#define PL080_CONFIG_HALT (1 << 18) +#define PL080_CONFIG_ACTIVE (1 << 17) /* RO */ +#define PL080_CONFIG_LOCK (1 << 16) +#define PL080_CONFIG_TC_IRQ_MASK (1 << 15) +#define PL080_CONFIG_ERR_IRQ_MASK (1 << 14) +#define PL080_CONFIG_FLOW_CONTROL_MASK (0x7 << 11) +#define PL080_CONFIG_FLOW_CONTROL_SHIFT (11) +#define PL080_CONFIG_DST_SEL_MASK (0xf << 6) +#define PL080_CONFIG_DST_SEL_SHIFT (6) +#define PL080_CONFIG_SRC_SEL_MASK (0xf << 1) +#define PL080_CONFIG_SRC_SEL_SHIFT (1) +#define PL080_CONFIG_ENABLE (1 << 0) + +#define PL080_FLOW_MEM2MEM (0x0) +#define PL080_FLOW_MEM2PER (0x1) +#define PL080_FLOW_PER2MEM (0x2) +#define PL080_FLOW_SRC2DST (0x3) +#define PL080_FLOW_SRC2DST_DST (0x4) +#define PL080_FLOW_MEM2PER_PER (0x5) +#define PL080_FLOW_PER2MEM_PER (0x6) +#define PL080_FLOW_SRC2DST_SRC (0x7) + +/* DMA linked list chain structure */ + +struct pl080_lli { + u32 src_addr; + u32 dst_addr; + u32 next_lli; + u32 control0; +}; + +struct pl080s_lli { + u32 src_addr; + u32 dst_addr; + u32 next_lli; + u32 control0; + u32 control1; +}; + +#endif /* ASM_PL080_H */ -- cgit v1.2.3 From 6c0cc950ae670403a362bdcbf3cde0df33744928 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 9 Jan 2013 22:33:37 +0100 Subject: ACPI / PCI: Set root bridge ACPI handle in advance The ACPI handles of PCI root bridges need to be known to acpi_bind_one(), so that it can create the appropriate "firmware_node" and "physical_node" files for them, but currently the way it gets to know those handles is not exactly straightforward (to put it lightly). This is how it works, roughly: 1. acpi_bus_scan() finds the handle of a PCI root bridge, creates a struct acpi_device object for it and passes that object to acpi_pci_root_add(). 2. acpi_pci_root_add() creates a struct acpi_pci_root object, populates its "device" field with its argument's address (device->handle is the ACPI handle found in step 1). 3. The struct acpi_pci_root object created in step 2 is passed to pci_acpi_scan_root() and used to get resources that are passed to pci_create_root_bus(). 4. pci_create_root_bus() creates a struct pci_host_bridge object and passes its "dev" member to device_register(). 5. platform_notify(), which for systems with ACPI is set to acpi_platform_notify(), is called. So far, so good. Now it starts to be "interesting". 6. acpi_find_bridge_device() is used to find the ACPI handle of the given device (which is the PCI root bridge) and executes acpi_pci_find_root_bridge(), among other things, for the given device object. 7. acpi_pci_find_root_bridge() uses the name (sic!) of the given device object to extract the segment and bus numbers of the PCI root bridge and passes them to acpi_get_pci_rootbridge_handle(). 8. acpi_get_pci_rootbridge_handle() browses the list of ACPI PCI root bridges and finds the one that matches the given segment and bus numbers. Its handle is then used to initialize the ACPI handle of the PCI root bridge's device object by acpi_bind_one(). However, this is *exactly* the ACPI handle we started with in step 1. Needless to say, this is quite embarassing, but it may be avoided thanks to commit f3fd0c8 (ACPI: Allow ACPI handles of devices to be initialized in advance), which makes it possible to initialize the ACPI handle of a device before passing it to device_register(). Accordingly, add a new __weak routine, pcibios_root_bridge_prepare(), defaulting to an empty implementation that can be replaced by the interested architecutres (x86 and ia64 at the moment) with functions that will set the root bridge's ACPI handle before its dev member is passed to device_register(). Make both x86 and ia64 provide such implementations of pcibios_root_bridge_prepare() and remove acpi_pci_find_root_bridge() and acpi_get_pci_rootbridge_handle() that aren't necessary any more. Included is a fix for breakage on systems with non-ACPI PCI host bridges from Bjorn Helgaas. Signed-off-by: Rafael J. Wysocki Signed-off-by: Bjorn Helgaas --- arch/ia64/pci/pci.c | 8 ++++++++ arch/x86/include/asm/pci.h | 3 +++ arch/x86/pci/acpi.c | 9 +++++++++ drivers/acpi/pci_root.c | 18 ------------------ drivers/pci/pci-acpi.c | 19 ------------------- drivers/pci/probe.c | 16 ++++++++++++++++ include/acpi/acpi_bus.h | 1 - include/linux/pci.h | 2 ++ 8 files changed, 38 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 5faa66c5c2a8..00e59c7ad3c0 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -396,6 +396,14 @@ out1: return NULL; } +int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) +{ + struct pci_controller *controller = bridge->bus->sysdata; + + ACPI_HANDLE_SET(&bridge->dev, controller->acpi_handle); + return 0; +} + static int __devinit is_valid_resource(struct pci_dev *dev, int idx) { unsigned int i, type_mask = IORESOURCE_IO | IORESOURCE_MEM; diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index dba7805176bf..9f437e97e9e8 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -14,6 +14,9 @@ struct pci_sysdata { int domain; /* PCI domain */ int node; /* NUMA node */ +#ifdef CONFIG_ACPI + void *acpi; /* ACPI-specific data */ +#endif #ifdef CONFIG_X86_64 void *iommu; /* IOMMU private data */ #endif diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 0c01261fe5a8..3d49094ed3e8 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -522,6 +522,7 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) sd = &info->sd; sd->domain = domain; sd->node = node; + sd->acpi = device->handle; /* * Maybe the desired pci bus has been already scanned. In such case * it is unnecessary to scan the pci bus with the given domain,busnum. @@ -593,6 +594,14 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) return bus; } +int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) +{ + struct pci_sysdata *sd = bridge->bus->sysdata; + + ACPI_HANDLE_SET(&bridge->dev, sd->acpi); + return 0; +} + int __init pci_acpi_init(void) { struct pci_dev *dev = NULL; diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 471b2dcb1c67..bf5108ad4d63 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -107,24 +107,6 @@ void acpi_pci_unregister_driver(struct acpi_pci_driver *driver) } EXPORT_SYMBOL(acpi_pci_unregister_driver); -acpi_handle acpi_get_pci_rootbridge_handle(unsigned int seg, unsigned int bus) -{ - struct acpi_pci_root *root; - acpi_handle handle = NULL; - - mutex_lock(&acpi_pci_root_lock); - list_for_each_entry(root, &acpi_pci_roots, node) - if ((root->segment == (u16) seg) && - (root->secondary.start == (u16) bus)) { - handle = root->device->handle; - break; - } - mutex_unlock(&acpi_pci_root_lock); - return handle; -} - -EXPORT_SYMBOL_GPL(acpi_get_pci_rootbridge_handle); - /** * acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge * @handle - the ACPI CA node in question. diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 42736e213f25..1c2587c40299 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -302,24 +302,6 @@ static int acpi_pci_find_device(struct device *dev, acpi_handle *handle) return 0; } -static int acpi_pci_find_root_bridge(struct device *dev, acpi_handle *handle) -{ - int num; - unsigned int seg, bus; - - /* - * The string should be the same as root bridge's name - * Please look at 'pci_scan_bus_parented' - */ - num = sscanf(dev_name(dev), "pci%04x:%02x", &seg, &bus); - if (num != 2) - return -ENODEV; - *handle = acpi_get_pci_rootbridge_handle(seg, bus); - if (!*handle) - return -ENODEV; - return 0; -} - static void pci_acpi_setup(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -378,7 +360,6 @@ static void pci_acpi_cleanup(struct device *dev) static struct acpi_bus_type acpi_pci_bus = { .bus = &pci_bus_type, .find_device = acpi_pci_find_device, - .find_bridge = acpi_pci_find_root_bridge, .setup = pci_acpi_setup, .cleanup = pci_acpi_cleanup, }; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 2dcd22d9c816..bbe4be7fc685 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1632,6 +1632,18 @@ unsigned int pci_scan_child_bus(struct pci_bus *bus) return max; } +/** + * pcibios_root_bridge_prepare - Platform-specific host bridge setup. + * @bridge: Host bridge to set up. + * + * Default empty implementation. Replace with an architecture-specific setup + * routine, if necessary. + */ +int __weak pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) +{ + return 0; +} + struct pci_bus *pci_create_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources) { @@ -1665,6 +1677,10 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, bridge->dev.parent = parent; bridge->dev.release = pci_release_bus_bridge_dev; dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus); + error = pcibios_root_bridge_prepare(bridge); + if (error) + goto bridge_dev_reg_err; + error = device_register(&bridge->dev); if (error) goto bridge_dev_reg_err; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index a9e1421cd007..796ccc3247df 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -402,7 +402,6 @@ struct acpi_pci_root { /* helper */ acpi_handle acpi_get_child(acpi_handle, u64); int acpi_is_root_bridge(acpi_handle); -acpi_handle acpi_get_pci_rootbridge_handle(unsigned int, unsigned int); struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle); #define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)ACPI_HANDLE(dev)) diff --git a/include/linux/pci.h b/include/linux/pci.h index 907b455ab603..6860f4dec997 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -378,6 +378,8 @@ void pci_set_host_bridge_release(struct pci_host_bridge *bridge, void (*release_fn)(struct pci_host_bridge *), void *release_data); +int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge); + /* * The first PCI_BRIDGE_RESOURCE_NUM PCI bus resources (those that correspond * to P2P or CardBus bridge windows) go in a table. Additional ones (for -- cgit v1.2.3 From 0074b49b3fa0886047413dbca0508594b1d80c61 Mon Sep 17 00:00:00 2001 From: "Philip, Avinash" Date: Thu, 10 Jan 2013 18:35:26 +0530 Subject: pwm: pwm-tiehrpwm: Update the clock handling of pwm-tiehrpwm driver The clock framework has changed and it's now better to invoke clock_prepare_enable() and clk_disable_unprepare() rather than the legacy clk_enable() and clk_disable() calls. This patch converts the pwm-tiehrpwm driver to the new framework. Signed-off-by: Philip Avinash Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tiehrpwm.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 72a6dd40c9ec..4fcafbfba60e 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -318,6 +318,7 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); unsigned short aqcsfrc_val, aqcsfrc_mask; + int ret; /* Leave clock enabled on enabling PWM */ pm_runtime_get_sync(chip->dev); @@ -341,7 +342,12 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) configure_polarity(pc, pwm->hwpwm); /* Enable TBCLK before enabling PWM device */ - clk_enable(pc->tbclk); + ret = clk_prepare_enable(pc->tbclk); + if (ret) { + pr_err("Failed to enable TBCLK for %s\n", + dev_name(pc->chip.dev)); + return ret; + } /* Enable time counter for free_run */ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN); @@ -372,7 +378,7 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); /* Disabling TBCLK on PWM disable */ - clk_disable(pc->tbclk); + clk_disable_unprepare(pc->tbclk); /* Stop Time base counter */ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_STOP_NEXT); -- cgit v1.2.3 From 0fd602235dd702d16722857da748d15c26b81ed1 Mon Sep 17 00:00:00 2001 From: Narayanan Date: Tue, 13 Sep 2011 17:00:22 +0530 Subject: dmaengine: ste_dma40: reset priority bit for logical channels This patch sets the SSCFG/SDCFG bit[7] PRI only for physical channel requests with high priority. For logical channels, this bit will be zero. Signed-off-by: Narayanan G Reviewed-by: Rabin Vincent Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40_ll.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/ste_dma40_ll.c b/drivers/dma/ste_dma40_ll.c index 851ad56e8409..d64b72ae2dc8 100644 --- a/drivers/dma/ste_dma40_ll.c +++ b/drivers/dma/ste_dma40_ll.c @@ -102,17 +102,18 @@ void d40_phy_cfg(struct stedma40_chan_cfg *cfg, src |= cfg->src_info.data_width << D40_SREG_CFG_ESIZE_POS; dst |= cfg->dst_info.data_width << D40_SREG_CFG_ESIZE_POS; + /* Set the priority bit to high for the physical channel */ + if (cfg->high_priority) { + src |= 1 << D40_SREG_CFG_PRI_POS; + dst |= 1 << D40_SREG_CFG_PRI_POS; + } + } else { /* Logical channel */ dst |= 1 << D40_SREG_CFG_LOG_GIM_POS; src |= 1 << D40_SREG_CFG_LOG_GIM_POS; } - if (cfg->high_priority) { - src |= 1 << D40_SREG_CFG_PRI_POS; - dst |= 1 << D40_SREG_CFG_PRI_POS; - } - if (cfg->src_info.big_endian) src |= 1 << D40_SREG_CFG_LBE_POS; if (cfg->dst_info.big_endian) -- cgit v1.2.3 From 8a5d2039ab9050a8a2e649eaf3ca4e372a7709f1 Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Wed, 28 Sep 2011 09:32:20 +0200 Subject: dmaengine: ste_dma40: use writel_relaxed for lcxa lcpa and lcla are written often and the cache_sync() overhead in writel is costly, especially for wlan where every single network packet (in RX mode) corresponds to a separate DMA transfer. Signed-off-by: Per Forlin Reviewed-by: Narayanan Gopalakrishnan Reviewed-by: Rabin Vincent Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40_ll.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/ste_dma40_ll.c b/drivers/dma/ste_dma40_ll.c index d64b72ae2dc8..1cfe7ab50c6b 100644 --- a/drivers/dma/ste_dma40_ll.c +++ b/drivers/dma/ste_dma40_ll.c @@ -332,10 +332,10 @@ void d40_log_lli_lcpa_write(struct d40_log_lli_full *lcpa, { d40_log_lli_link(lli_dst, lli_src, next, flags); - writel(lli_src->lcsp02, &lcpa[0].lcsp0); - writel(lli_src->lcsp13, &lcpa[0].lcsp1); - writel(lli_dst->lcsp02, &lcpa[0].lcsp2); - writel(lli_dst->lcsp13, &lcpa[0].lcsp3); + writel_relaxed(lli_src->lcsp02, &lcpa[0].lcsp0); + writel_relaxed(lli_src->lcsp13, &lcpa[0].lcsp1); + writel_relaxed(lli_dst->lcsp02, &lcpa[0].lcsp2); + writel_relaxed(lli_dst->lcsp13, &lcpa[0].lcsp3); } void d40_log_lli_lcla_write(struct d40_log_lli *lcla, @@ -345,10 +345,10 @@ void d40_log_lli_lcla_write(struct d40_log_lli *lcla, { d40_log_lli_link(lli_dst, lli_src, next, flags); - writel(lli_src->lcsp02, &lcla[0].lcsp02); - writel(lli_src->lcsp13, &lcla[0].lcsp13); - writel(lli_dst->lcsp02, &lcla[1].lcsp02); - writel(lli_dst->lcsp13, &lcla[1].lcsp13); + writel_relaxed(lli_src->lcsp02, &lcla[0].lcsp02); + writel_relaxed(lli_src->lcsp13, &lcla[0].lcsp13); + writel_relaxed(lli_dst->lcsp02, &lcla[1].lcsp02); + writel_relaxed(lli_dst->lcsp13, &lcla[1].lcsp13); } static void d40_log_fill_lli(struct d40_log_lli *lli, -- cgit v1.2.3 From b96710e5b22609aa6e4ba5c3936ea7f026a7c427 Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Tue, 18 Oct 2011 18:39:47 +0200 Subject: dmaengine: ste_dma40: set dma max seg size Maximum DMA seg size is (0xffff x data_width). If max seg size is not set it deafults to 64k. This results in failure if transferring 64k in byte mode. Large seg sizes may be supported by splitting large transfer. Signed-off-by: Per Forlin Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 23c5573e62dd..f5724d95ed48 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -344,6 +344,7 @@ struct d40_base { int irq; int num_phy_chans; int num_log_chans; + struct device_dma_parameters dma_parms; struct dma_device dma_both; struct dma_device dma_slave; struct dma_device dma_memcpy; @@ -3362,6 +3363,13 @@ static int __init d40_probe(struct platform_device *pdev) if (err) goto failure; + base->dev->dma_parms = &base->dma_parms; + err = dma_set_max_seg_size(base->dev, STEDMA40_MAX_SEG_SIZE); + if (err) { + d40_err(&pdev->dev, "Failed to set dma max seg size\n"); + goto failure; + } + d40_hw_init(base); dev_info(base->dev, "initialized\n"); -- cgit v1.2.3 From 92bb6cdb5302a4b0b3c6b6cfc0854aaed882c4bc Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Thu, 13 Oct 2011 12:11:36 +0200 Subject: dmaengine: ste_dma40: limit burst size to 16 The client is not aware of the maximum burst size in the dma driver. If the size exceeds 16 set max to 16. Signed-off-by: Per Forlin Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index f5724d95ed48..2d0c63dcd84c 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2578,6 +2578,14 @@ static int d40_set_runtime_config(struct dma_chan *chan, return -EINVAL; } + if (src_maxburst > 16) { + src_maxburst = 16; + dst_maxburst = src_maxburst * src_addr_width / dst_addr_width; + } else if (dst_maxburst > 16) { + dst_maxburst = 16; + src_maxburst = dst_maxburst * dst_addr_width / src_addr_width; + } + ret = dma40_config_to_halfchannel(d40c, &cfg->src_info, src_addr_width, src_maxburst); -- cgit v1.2.3 From 42365cf0fa19473dde5fe226b0e7e9ab8ea18af8 Mon Sep 17 00:00:00 2001 From: Narayanan G Date: Fri, 20 Jan 2012 13:56:14 +0530 Subject: dmaengine: ste_dma40: don't check for pm_runtime_suspended() The check for runtime suspend is not needed during a regular suspend, as the framework takes care of this. This fixes the issue of DMA driver not letting the system to go to deepsleep in the first attempt. Signed-off-by: Narayanan G Reviewed-by: Rabin Vincent Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 2d0c63dcd84c..760576b85641 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2782,8 +2782,6 @@ static int dma40_pm_suspend(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct d40_base *base = platform_get_drvdata(pdev); int ret = 0; - if (!pm_runtime_suspended(dev)) - return -EBUSY; if (base->lcpa_regulator) ret = regulator_disable(base->lcpa_regulator); -- cgit v1.2.3 From ccc3d6976433aa67131117fccd2b5143d82a6f48 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Thu, 17 May 2012 13:47:38 +0530 Subject: dmaengine: ste_dma40: don't allow high priority dest event lines Hardware bug: when a logical channel is triggerred by a high priority destination event line, an extra packet transaction is generated in case of important data write response latency on previous logical channel A and if the source transfer of current logical channel B is already completed and if no other channel with a higher priority than B is waiting for execution. Software workaround: do not set the high priority level for the destination event lines that trigger logical channels. Signed-off-by: Rabin Vincent Reviewed-by: Shreshtha Kumar Sahu Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 760576b85641..9f8964a0a287 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2180,11 +2180,24 @@ static void __d40_set_prio_rt(struct d40_chan *d40c, int dev_type, bool src) { bool realtime = d40c->dma_cfg.realtime; bool highprio = d40c->dma_cfg.high_priority; - u32 prioreg = highprio ? D40_DREG_PSEG1 : D40_DREG_PCEG1; u32 rtreg = realtime ? D40_DREG_RSEG1 : D40_DREG_RCEG1; u32 event = D40_TYPE_TO_EVENT(dev_type); u32 group = D40_TYPE_TO_GROUP(dev_type); u32 bit = 1 << event; + u32 prioreg; + + /* + * Due to a hardware bug, in some cases a logical channel triggered by + * a high priority destination event line can generate extra packet + * transactions. + * + * The workaround is to not set the high priority level for the + * destination event lines that trigger logical channels. + */ + if (!src && chan_is_logical(d40c)) + highprio = false; + + prioreg = highprio ? D40_DREG_PSEG1 : D40_DREG_PCEG1; /* Destination event lines are stored in the upper halfword */ if (!src) -- cgit v1.2.3 From f000df8c5a0e2002acc5989aad99a97d32a24718 Mon Sep 17 00:00:00 2001 From: Gerald Baeza Date: Thu, 8 Nov 2012 14:39:07 +0100 Subject: dmaengine: ste_dma40: support fixed physical channel allocation This patch makes existing use_fixed_channel field (of stedma40_chan_cfg structure) applicable to physical channels. Signed-off-by: Gerald Baeza Tested-by: Yannick Fertre Reviewed-by: Per Forlin Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 9f8964a0a287..5feab7db9449 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -1711,10 +1711,12 @@ static int d40_allocate_channel(struct d40_chan *d40c, bool *first_phy_user) int i; int j; int log_num; + int num_phy_chans; bool is_src; bool is_log = d40c->dma_cfg.mode == STEDMA40_MODE_LOGICAL; phys = d40c->base->phy_res; + num_phy_chans = d40c->base->num_phy_chans; if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) { dev_type = d40c->dma_cfg.src_dev_type; @@ -1735,12 +1737,19 @@ static int d40_allocate_channel(struct d40_chan *d40c, bool *first_phy_user) if (!is_log) { if (d40c->dma_cfg.dir == STEDMA40_MEM_TO_MEM) { /* Find physical half channel */ - for (i = 0; i < d40c->base->num_phy_chans; i++) { - + if (d40c->dma_cfg.use_fixed_channel) { + i = d40c->dma_cfg.phy_channel; if (d40_alloc_mask_set(&phys[i], is_src, 0, is_log, first_phy_user)) goto found_phy; + } else { + for (i = 0; i < num_phy_chans; i++) { + if (d40_alloc_mask_set(&phys[i], is_src, + 0, is_log, + first_phy_user)) + goto found_phy; + } } } else for (j = 0; j < d40c->base->num_phy_chans; j += 8) { -- cgit v1.2.3 From 47db92f4a63499b1605b4c66f9347ba5479e7b19 Mon Sep 17 00:00:00 2001 From: Gerald Baeza Date: Fri, 21 Sep 2012 21:21:37 +0200 Subject: dmaengine: ste_dma40: physical channels number correction DMAC_ICFG[0:2]=SCHNB only allows to count 'multiple of 4' physical channels so it was ok with platforms having 8 channels but cannot be used for next versions (with 10 or 14 channels). This patch allows to provide the number of physical channels for a DMA device via platform_data, or still rely on SCHNB if platform_data announces 0 channel. Signed-off-by: Gerald Baeza Reviewed-by: Per Forlin Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 15 ++++++++++----- include/linux/platform_data/dma-ste-dma40.h | 4 ++++ 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 5feab7db9449..ca18117def0a 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -3004,14 +3004,21 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) * ? has revision 1 * DB8500v1 has revision 2 * DB8500v2 has revision 3 + * AP9540v1 has revision 4 + * DB8540v1 has revision 4 */ rev = AMBA_REV_BITS(pid); + plat_data = pdev->dev.platform_data; + /* The number of physical channels on this HW */ - num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4; + if (plat_data->num_of_phy_chans) + num_phy_chans = plat_data->num_of_phy_chans; + else + num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4; - dev_info(&pdev->dev, "hardware revision: %d @ 0x%x\n", - rev, res->start); + dev_info(&pdev->dev, "hardware revision: %d @ 0x%x with %d physical channels\n", + rev, res->start, num_phy_chans); if (rev < 2) { d40_err(&pdev->dev, "hardware revision: %d is not supported", @@ -3019,8 +3026,6 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) goto failure; } - plat_data = pdev->dev.platform_data; - /* Count the number of logical channels in use */ for (i = 0; i < plat_data->dev_len; i++) if (plat_data->dev_rx[i] != 0) diff --git a/include/linux/platform_data/dma-ste-dma40.h b/include/linux/platform_data/dma-ste-dma40.h index 9ff93b065686..833cb959f3df 100644 --- a/include/linux/platform_data/dma-ste-dma40.h +++ b/include/linux/platform_data/dma-ste-dma40.h @@ -147,6 +147,9 @@ struct stedma40_chan_cfg { * @memcpy_conf_log: default configuration of logical channel memcpy * @disabled_channels: A vector, ending with -1, that marks physical channels * that are for different reasons not available for the driver. + * @num_of_phy_chans: The number of physical channels implemented in HW. + * 0 means reading the number of channels from DMA HW but this is only valid + * for 'multiple of 4' channels, like 8. */ struct stedma40_platform_data { u32 dev_len; @@ -158,6 +161,7 @@ struct stedma40_platform_data { struct stedma40_chan_cfg *memcpy_conf_log; int disabled_channels[STEDMA40_MAX_PHYS]; bool use_esram_lcla; + int num_of_phy_chans; }; #ifdef CONFIG_STE_DMA40 -- cgit v1.2.3 From 3cb645dc85a050c8a6b5c2cbdcbe4b8f39dba1b8 Mon Sep 17 00:00:00 2001 From: Tong Liu Date: Wed, 26 Sep 2012 10:07:30 +0000 Subject: dmaengine: ste_dma40: support more than 128 event lines U8540 DMA controller is different from u9540 we need define new registers and use them to support handling more than 128 event lines. Signed-off-by: Tong Liu Reviewed-by: Per Forlin Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 306 +++++++++++++++++++++++++++++++++------------ drivers/dma/ste_dma40_ll.h | 130 ++++++++++++++++++- 2 files changed, 355 insertions(+), 81 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index ca18117def0a..67e565bffe16 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -53,6 +53,8 @@ #define D40_ALLOC_PHY (1 << 30) #define D40_ALLOC_LOG_FREE 0 +#define MAX(a, b) (((a) < (b)) ? (b) : (a)) + /** * enum 40_command - The different commands and/or statuses. * @@ -100,8 +102,19 @@ static u32 d40_backup_regs[] = { #define BACKUP_REGS_SZ ARRAY_SIZE(d40_backup_regs) -/* TODO: Check if all these registers have to be saved/restored on dma40 v3 */ -static u32 d40_backup_regs_v3[] = { +/* + * since 9540 and 8540 has the same HW revision + * use v4a for 9540 or ealier + * use v4b for 8540 or later + * HW revision: + * DB8500ed has revision 0 + * DB8500v1 has revision 2 + * DB8500v2 has revision 3 + * AP9540v1 has revision 4 + * DB8540v1 has revision 4 + * TODO: Check if all these registers have to be saved/restored on dma40 v4a + */ +static u32 d40_backup_regs_v4a[] = { D40_DREG_PSEG1, D40_DREG_PSEG2, D40_DREG_PSEG3, @@ -120,7 +133,32 @@ static u32 d40_backup_regs_v3[] = { D40_DREG_RCEG4, }; -#define BACKUP_REGS_SZ_V3 ARRAY_SIZE(d40_backup_regs_v3) +#define BACKUP_REGS_SZ_V4A ARRAY_SIZE(d40_backup_regs_v4a) + +static u32 d40_backup_regs_v4b[] = { + D40_DREG_CPSEG1, + D40_DREG_CPSEG2, + D40_DREG_CPSEG3, + D40_DREG_CPSEG4, + D40_DREG_CPSEG5, + D40_DREG_CPCEG1, + D40_DREG_CPCEG2, + D40_DREG_CPCEG3, + D40_DREG_CPCEG4, + D40_DREG_CPCEG5, + D40_DREG_CRSEG1, + D40_DREG_CRSEG2, + D40_DREG_CRSEG3, + D40_DREG_CRSEG4, + D40_DREG_CRSEG5, + D40_DREG_CRCEG1, + D40_DREG_CRCEG2, + D40_DREG_CRCEG3, + D40_DREG_CRCEG4, + D40_DREG_CRCEG5, +}; + +#define BACKUP_REGS_SZ_V4B ARRAY_SIZE(d40_backup_regs_v4b) static u32 d40_backup_regs_chan[] = { D40_CHAN_REG_SSCFG, @@ -133,6 +171,102 @@ static u32 d40_backup_regs_chan[] = { D40_CHAN_REG_SDLNK, }; +/** + * struct d40_interrupt_lookup - lookup table for interrupt handler + * + * @src: Interrupt mask register. + * @clr: Interrupt clear register. + * @is_error: true if this is an error interrupt. + * @offset: start delta in the lookup_log_chans in d40_base. If equals to + * D40_PHY_CHAN, the lookup_phy_chans shall be used instead. + */ +struct d40_interrupt_lookup { + u32 src; + u32 clr; + bool is_error; + int offset; +}; + + +static struct d40_interrupt_lookup il_v4a[] = { + {D40_DREG_LCTIS0, D40_DREG_LCICR0, false, 0}, + {D40_DREG_LCTIS1, D40_DREG_LCICR1, false, 32}, + {D40_DREG_LCTIS2, D40_DREG_LCICR2, false, 64}, + {D40_DREG_LCTIS3, D40_DREG_LCICR3, false, 96}, + {D40_DREG_LCEIS0, D40_DREG_LCICR0, true, 0}, + {D40_DREG_LCEIS1, D40_DREG_LCICR1, true, 32}, + {D40_DREG_LCEIS2, D40_DREG_LCICR2, true, 64}, + {D40_DREG_LCEIS3, D40_DREG_LCICR3, true, 96}, + {D40_DREG_PCTIS, D40_DREG_PCICR, false, D40_PHY_CHAN}, + {D40_DREG_PCEIS, D40_DREG_PCICR, true, D40_PHY_CHAN}, +}; + +static struct d40_interrupt_lookup il_v4b[] = { + {D40_DREG_CLCTIS1, D40_DREG_CLCICR1, false, 0}, + {D40_DREG_CLCTIS2, D40_DREG_CLCICR2, false, 32}, + {D40_DREG_CLCTIS3, D40_DREG_CLCICR3, false, 64}, + {D40_DREG_CLCTIS4, D40_DREG_CLCICR4, false, 96}, + {D40_DREG_CLCTIS5, D40_DREG_CLCICR5, false, 128}, + {D40_DREG_CLCEIS1, D40_DREG_CLCICR1, true, 0}, + {D40_DREG_CLCEIS2, D40_DREG_CLCICR2, true, 32}, + {D40_DREG_CLCEIS3, D40_DREG_CLCICR3, true, 64}, + {D40_DREG_CLCEIS4, D40_DREG_CLCICR4, true, 96}, + {D40_DREG_CLCEIS5, D40_DREG_CLCICR5, true, 128}, + {D40_DREG_CPCTIS, D40_DREG_CPCICR, false, D40_PHY_CHAN}, + {D40_DREG_CPCEIS, D40_DREG_CPCICR, true, D40_PHY_CHAN}, +}; + +/** + * struct d40_reg_val - simple lookup struct + * + * @reg: The register. + * @val: The value that belongs to the register in reg. + */ +struct d40_reg_val { + unsigned int reg; + unsigned int val; +}; + +static __initdata struct d40_reg_val dma_init_reg_v4a[] = { + /* Clock every part of the DMA block from start */ + { .reg = D40_DREG_GCC, .val = D40_DREG_GCC_ENABLE_ALL}, + + /* Interrupts on all logical channels */ + { .reg = D40_DREG_LCMIS0, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCMIS1, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCMIS2, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCMIS3, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCICR0, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCICR1, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCICR2, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCICR3, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCTIS0, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCTIS1, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCTIS2, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCTIS3, .val = 0xFFFFFFFF} +}; +static __initdata struct d40_reg_val dma_init_reg_v4b[] = { + /* Clock every part of the DMA block from start */ + { .reg = D40_DREG_GCC, .val = D40_DREG_GCC_ENABLE_ALL}, + + /* Interrupts on all logical channels */ + { .reg = D40_DREG_CLCMIS1, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCMIS2, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCMIS3, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCMIS4, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCMIS5, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCICR1, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCICR2, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCICR3, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCICR4, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCICR5, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCTIS1, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCTIS2, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCTIS3, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCTIS4, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCTIS5, .val = 0xFFFFFFFF} +}; + /** * struct d40_lli_pool - Structure for keeping LLIs in memory * @@ -288,6 +422,38 @@ struct d40_chan { enum dma_transfer_direction runtime_direction; }; +/** + * struct d40_gen_dmac - generic values to represent u8500/u8540 DMA + * controller + * + * @backup: the pointer to the registers address array for backup + * @backup_size: the size of the registers address array for backup + * @realtime_en: the realtime enable register + * @realtime_clear: the realtime clear register + * @high_prio_en: the high priority enable register + * @high_prio_clear: the high priority clear register + * @interrupt_en: the interrupt enable register + * @interrupt_clear: the interrupt clear register + * @il: the pointer to struct d40_interrupt_lookup + * @il_size: the size of d40_interrupt_lookup array + * @init_reg: the pointer to the struct d40_reg_val + * @init_reg_size: the size of d40_reg_val array + */ +struct d40_gen_dmac { + u32 *backup; + u32 backup_size; + u32 realtime_en; + u32 realtime_clear; + u32 high_prio_en; + u32 high_prio_clear; + u32 interrupt_en; + u32 interrupt_clear; + struct d40_interrupt_lookup *il; + u32 il_size; + struct d40_reg_val *init_reg; + u32 init_reg_size; +}; + /** * struct d40_base - The big global struct, one for each probe'd instance. * @@ -326,11 +492,13 @@ struct d40_chan { * @desc_slab: cache for descriptors. * @reg_val_backup: Here the values of some hardware registers are stored * before the DMA is powered off. They are restored when the power is back on. - * @reg_val_backup_v3: Backup of registers that only exits on dma40 v3 and - * later. + * @reg_val_backup_v4: Backup of registers that only exits on dma40 v3 and + * later * @reg_val_backup_chan: Backup data for standard channel parameter registers. * @gcc_pwr_off_mask: Mask to maintain the channels that can be turned off. * @initialized: true if the dma has been initialized + * @gen_dmac: the struct for generic registers values to represent u8500/8540 + * DMA controller */ struct d40_base { spinlock_t interrupt_lock; @@ -362,37 +530,11 @@ struct d40_base { resource_size_t lcpa_size; struct kmem_cache *desc_slab; u32 reg_val_backup[BACKUP_REGS_SZ]; - u32 reg_val_backup_v3[BACKUP_REGS_SZ_V3]; + u32 reg_val_backup_v4[MAX(BACKUP_REGS_SZ_V4A, BACKUP_REGS_SZ_V4B)]; u32 *reg_val_backup_chan; u16 gcc_pwr_off_mask; bool initialized; -}; - -/** - * struct d40_interrupt_lookup - lookup table for interrupt handler - * - * @src: Interrupt mask register. - * @clr: Interrupt clear register. - * @is_error: true if this is an error interrupt. - * @offset: start delta in the lookup_log_chans in d40_base. If equals to - * D40_PHY_CHAN, the lookup_phy_chans shall be used instead. - */ -struct d40_interrupt_lookup { - u32 src; - u32 clr; - bool is_error; - int offset; -}; - -/** - * struct d40_reg_val - simple lookup struct - * - * @reg: The register. - * @val: The value that belongs to the register in reg. - */ -struct d40_reg_val { - unsigned int reg; - unsigned int val; + struct d40_gen_dmac gen_dmac; }; static struct device *chan2dev(struct d40_chan *d40c) @@ -875,11 +1017,11 @@ static void d40_save_restore_registers(struct d40_base *base, bool save) save); /* Save/Restore registers only existing on dma40 v3 and later */ - if (base->rev >= 3) - dma40_backup(base->virtbase, base->reg_val_backup_v3, - d40_backup_regs_v3, - ARRAY_SIZE(d40_backup_regs_v3), - save); + if (base->gen_dmac.backup) + dma40_backup(base->virtbase, base->reg_val_backup_v4, + base->gen_dmac.backup, + base->gen_dmac.backup_size, + save); } #else static void d40_save_restore_registers(struct d40_base *base, bool save) @@ -1470,41 +1612,30 @@ err: static irqreturn_t d40_handle_interrupt(int irq, void *data) { - static const struct d40_interrupt_lookup il[] = { - {D40_DREG_LCTIS0, D40_DREG_LCICR0, false, 0}, - {D40_DREG_LCTIS1, D40_DREG_LCICR1, false, 32}, - {D40_DREG_LCTIS2, D40_DREG_LCICR2, false, 64}, - {D40_DREG_LCTIS3, D40_DREG_LCICR3, false, 96}, - {D40_DREG_LCEIS0, D40_DREG_LCICR0, true, 0}, - {D40_DREG_LCEIS1, D40_DREG_LCICR1, true, 32}, - {D40_DREG_LCEIS2, D40_DREG_LCICR2, true, 64}, - {D40_DREG_LCEIS3, D40_DREG_LCICR3, true, 96}, - {D40_DREG_PCTIS, D40_DREG_PCICR, false, D40_PHY_CHAN}, - {D40_DREG_PCEIS, D40_DREG_PCICR, true, D40_PHY_CHAN}, - }; - int i; - u32 regs[ARRAY_SIZE(il)]; u32 idx; u32 row; long chan = -1; struct d40_chan *d40c; unsigned long flags; struct d40_base *base = data; + u32 regs[base->gen_dmac.il_size]; + struct d40_interrupt_lookup *il = base->gen_dmac.il; + u32 il_size = base->gen_dmac.il_size; spin_lock_irqsave(&base->interrupt_lock, flags); /* Read interrupt status of both logical and physical channels */ - for (i = 0; i < ARRAY_SIZE(il); i++) + for (i = 0; i < il_size; i++) regs[i] = readl(base->virtbase + il[i].src); for (;;) { chan = find_next_bit((unsigned long *)regs, - BITS_PER_LONG * ARRAY_SIZE(il), chan + 1); + BITS_PER_LONG * il_size, chan + 1); /* No more set bits found? */ - if (chan == BITS_PER_LONG * ARRAY_SIZE(il)) + if (chan == BITS_PER_LONG * il_size) break; row = chan / BITS_PER_LONG; @@ -2189,12 +2320,14 @@ static void __d40_set_prio_rt(struct d40_chan *d40c, int dev_type, bool src) { bool realtime = d40c->dma_cfg.realtime; bool highprio = d40c->dma_cfg.high_priority; - u32 rtreg = realtime ? D40_DREG_RSEG1 : D40_DREG_RCEG1; + u32 rtreg; u32 event = D40_TYPE_TO_EVENT(dev_type); u32 group = D40_TYPE_TO_GROUP(dev_type); u32 bit = 1 << event; u32 prioreg; + struct d40_gen_dmac *dmac = &d40c->base->gen_dmac; + rtreg = realtime ? dmac->realtime_en : dmac->realtime_clear; /* * Due to a hardware bug, in some cases a logical channel triggered by * a high priority destination event line can generate extra packet @@ -2206,7 +2339,7 @@ static void __d40_set_prio_rt(struct d40_chan *d40c, int dev_type, bool src) if (!src && chan_is_logical(d40c)) highprio = false; - prioreg = highprio ? D40_DREG_PSEG1 : D40_DREG_PCEG1; + prioreg = highprio ? dmac->high_prio_en : dmac->high_prio_clear; /* Destination event lines are stored in the upper halfword */ if (!src) @@ -3056,6 +3189,36 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) base->phy_chans = ((void *)base) + ALIGN(sizeof(struct d40_base), 4); base->log_chans = &base->phy_chans[num_phy_chans]; + if (base->plat_data->num_of_phy_chans == 14) { + base->gen_dmac.backup = d40_backup_regs_v4b; + base->gen_dmac.backup_size = BACKUP_REGS_SZ_V4B; + base->gen_dmac.interrupt_en = D40_DREG_CPCMIS; + base->gen_dmac.interrupt_clear = D40_DREG_CPCICR; + base->gen_dmac.realtime_en = D40_DREG_CRSEG1; + base->gen_dmac.realtime_clear = D40_DREG_CRCEG1; + base->gen_dmac.high_prio_en = D40_DREG_CPSEG1; + base->gen_dmac.high_prio_clear = D40_DREG_CPCEG1; + base->gen_dmac.il = il_v4b; + base->gen_dmac.il_size = ARRAY_SIZE(il_v4b); + base->gen_dmac.init_reg = dma_init_reg_v4b; + base->gen_dmac.init_reg_size = ARRAY_SIZE(dma_init_reg_v4b); + } else { + if (base->rev >= 3) { + base->gen_dmac.backup = d40_backup_regs_v4a; + base->gen_dmac.backup_size = BACKUP_REGS_SZ_V4A; + } + base->gen_dmac.interrupt_en = D40_DREG_PCMIS; + base->gen_dmac.interrupt_clear = D40_DREG_PCICR; + base->gen_dmac.realtime_en = D40_DREG_RSEG1; + base->gen_dmac.realtime_clear = D40_DREG_RCEG1; + base->gen_dmac.high_prio_en = D40_DREG_PSEG1; + base->gen_dmac.high_prio_clear = D40_DREG_PCEG1; + base->gen_dmac.il = il_v4a; + base->gen_dmac.il_size = ARRAY_SIZE(il_v4a); + base->gen_dmac.init_reg = dma_init_reg_v4a; + base->gen_dmac.init_reg_size = ARRAY_SIZE(dma_init_reg_v4a); + } + base->phy_res = kzalloc(num_phy_chans * sizeof(struct d40_phy_res), GFP_KERNEL); if (!base->phy_res) @@ -3127,31 +3290,15 @@ failure: static void __init d40_hw_init(struct d40_base *base) { - static struct d40_reg_val dma_init_reg[] = { - /* Clock every part of the DMA block from start */ - { .reg = D40_DREG_GCC, .val = D40_DREG_GCC_ENABLE_ALL}, - - /* Interrupts on all logical channels */ - { .reg = D40_DREG_LCMIS0, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCMIS1, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCMIS2, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCMIS3, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCICR0, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCICR1, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCICR2, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCICR3, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCTIS0, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCTIS1, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCTIS2, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCTIS3, .val = 0xFFFFFFFF} - }; int i; u32 prmseo[2] = {0, 0}; u32 activeo[2] = {0xFFFFFFFF, 0xFFFFFFFF}; u32 pcmis = 0; u32 pcicr = 0; + struct d40_reg_val *dma_init_reg = base->gen_dmac.init_reg; + u32 reg_size = base->gen_dmac.init_reg_size; - for (i = 0; i < ARRAY_SIZE(dma_init_reg); i++) + for (i = 0; i < reg_size; i++) writel(dma_init_reg[i].val, base->virtbase + dma_init_reg[i].reg); @@ -3184,11 +3331,14 @@ static void __init d40_hw_init(struct d40_base *base) writel(activeo[0], base->virtbase + D40_DREG_ACTIVO); /* Write which interrupt to enable */ - writel(pcmis, base->virtbase + D40_DREG_PCMIS); + writel(pcmis, base->virtbase + base->gen_dmac.interrupt_en); /* Write which interrupt to clear */ - writel(pcicr, base->virtbase + D40_DREG_PCICR); + writel(pcicr, base->virtbase + base->gen_dmac.interrupt_clear); + /* These are __initdata and cannot be accessed after init */ + base->gen_dmac.init_reg = NULL; + base->gen_dmac.init_reg_size = 0; } static int __init d40_lcla_allocate(struct d40_base *base) diff --git a/drivers/dma/ste_dma40_ll.h b/drivers/dma/ste_dma40_ll.h index 6d47373f3f58..fdde8ef77542 100644 --- a/drivers/dma/ste_dma40_ll.h +++ b/drivers/dma/ste_dma40_ll.h @@ -125,7 +125,7 @@ #define D40_DREG_GCC 0x000 #define D40_DREG_GCC_ENA 0x1 /* This assumes that there are only 4 event groups */ -#define D40_DREG_GCC_ENABLE_ALL 0xff01 +#define D40_DREG_GCC_ENABLE_ALL 0x3ff01 #define D40_DREG_GCC_EVTGRP_POS 8 #define D40_DREG_GCC_SRC 0 #define D40_DREG_GCC_DST 1 @@ -148,14 +148,31 @@ #define D40_DREG_LCPA 0x020 #define D40_DREG_LCLA 0x024 + +#define D40_DREG_SSEG1 0x030 +#define D40_DREG_SSEG2 0x034 +#define D40_DREG_SSEG3 0x038 +#define D40_DREG_SSEG4 0x03C + +#define D40_DREG_SCEG1 0x040 +#define D40_DREG_SCEG2 0x044 +#define D40_DREG_SCEG3 0x048 +#define D40_DREG_SCEG4 0x04C + #define D40_DREG_ACTIVE 0x050 #define D40_DREG_ACTIVO 0x054 -#define D40_DREG_FSEB1 0x058 -#define D40_DREG_FSEB2 0x05C +#define D40_DREG_CIDMOD 0x058 +#define D40_DREG_TCIDV 0x05C #define D40_DREG_PCMIS 0x060 #define D40_DREG_PCICR 0x064 #define D40_DREG_PCTIS 0x068 #define D40_DREG_PCEIS 0x06C + +#define D40_DREG_SPCMIS 0x070 +#define D40_DREG_SPCICR 0x074 +#define D40_DREG_SPCTIS 0x078 +#define D40_DREG_SPCEIS 0x07C + #define D40_DREG_LCMIS0 0x080 #define D40_DREG_LCMIS1 0x084 #define D40_DREG_LCMIS2 0x088 @@ -172,6 +189,33 @@ #define D40_DREG_LCEIS1 0x0B4 #define D40_DREG_LCEIS2 0x0B8 #define D40_DREG_LCEIS3 0x0BC + +#define D40_DREG_SLCMIS1 0x0C0 +#define D40_DREG_SLCMIS2 0x0C4 +#define D40_DREG_SLCMIS3 0x0C8 +#define D40_DREG_SLCMIS4 0x0CC + +#define D40_DREG_SLCICR1 0x0D0 +#define D40_DREG_SLCICR2 0x0D4 +#define D40_DREG_SLCICR3 0x0D8 +#define D40_DREG_SLCICR4 0x0DC + +#define D40_DREG_SLCTIS1 0x0E0 +#define D40_DREG_SLCTIS2 0x0E4 +#define D40_DREG_SLCTIS3 0x0E8 +#define D40_DREG_SLCTIS4 0x0EC + +#define D40_DREG_SLCEIS1 0x0F0 +#define D40_DREG_SLCEIS2 0x0F4 +#define D40_DREG_SLCEIS3 0x0F8 +#define D40_DREG_SLCEIS4 0x0FC + +#define D40_DREG_FSESS1 0x100 +#define D40_DREG_FSESS2 0x104 + +#define D40_DREG_FSEBS1 0x108 +#define D40_DREG_FSEBS2 0x10C + #define D40_DREG_PSEG1 0x110 #define D40_DREG_PSEG2 0x114 #define D40_DREG_PSEG3 0x118 @@ -188,6 +232,86 @@ #define D40_DREG_RCEG2 0x144 #define D40_DREG_RCEG3 0x148 #define D40_DREG_RCEG4 0x14C + +#define D40_DREG_PREFOT 0x15C +#define D40_DREG_EXTCFG 0x160 + +#define D40_DREG_CPSEG1 0x200 +#define D40_DREG_CPSEG2 0x204 +#define D40_DREG_CPSEG3 0x208 +#define D40_DREG_CPSEG4 0x20C +#define D40_DREG_CPSEG5 0x210 + +#define D40_DREG_CPCEG1 0x220 +#define D40_DREG_CPCEG2 0x224 +#define D40_DREG_CPCEG3 0x228 +#define D40_DREG_CPCEG4 0x22C +#define D40_DREG_CPCEG5 0x230 + +#define D40_DREG_CRSEG1 0x240 +#define D40_DREG_CRSEG2 0x244 +#define D40_DREG_CRSEG3 0x248 +#define D40_DREG_CRSEG4 0x24C +#define D40_DREG_CRSEG5 0x250 + +#define D40_DREG_CRCEG1 0x260 +#define D40_DREG_CRCEG2 0x264 +#define D40_DREG_CRCEG3 0x268 +#define D40_DREG_CRCEG4 0x26C +#define D40_DREG_CRCEG5 0x270 + +#define D40_DREG_CFSESS1 0x280 +#define D40_DREG_CFSESS2 0x284 +#define D40_DREG_CFSESS3 0x288 + +#define D40_DREG_CFSEBS1 0x290 +#define D40_DREG_CFSEBS2 0x294 +#define D40_DREG_CFSEBS3 0x298 + +#define D40_DREG_CLCMIS1 0x300 +#define D40_DREG_CLCMIS2 0x304 +#define D40_DREG_CLCMIS3 0x308 +#define D40_DREG_CLCMIS4 0x30C +#define D40_DREG_CLCMIS5 0x310 + +#define D40_DREG_CLCICR1 0x320 +#define D40_DREG_CLCICR2 0x324 +#define D40_DREG_CLCICR3 0x328 +#define D40_DREG_CLCICR4 0x32C +#define D40_DREG_CLCICR5 0x330 + +#define D40_DREG_CLCTIS1 0x340 +#define D40_DREG_CLCTIS2 0x344 +#define D40_DREG_CLCTIS3 0x348 +#define D40_DREG_CLCTIS4 0x34C +#define D40_DREG_CLCTIS5 0x350 + +#define D40_DREG_CLCEIS1 0x360 +#define D40_DREG_CLCEIS2 0x364 +#define D40_DREG_CLCEIS3 0x368 +#define D40_DREG_CLCEIS4 0x36C +#define D40_DREG_CLCEIS5 0x370 + +#define D40_DREG_CPCMIS 0x380 +#define D40_DREG_CPCICR 0x384 +#define D40_DREG_CPCTIS 0x388 +#define D40_DREG_CPCEIS 0x38C + +#define D40_DREG_SCCIDA1 0xE80 +#define D40_DREG_SCCIDA2 0xE90 +#define D40_DREG_SCCIDA3 0xEA0 +#define D40_DREG_SCCIDA4 0xEB0 +#define D40_DREG_SCCIDA5 0xEC0 + +#define D40_DREG_SCCIDB1 0xE84 +#define D40_DREG_SCCIDB2 0xE94 +#define D40_DREG_SCCIDB3 0xEA4 +#define D40_DREG_SCCIDB4 0xEB4 +#define D40_DREG_SCCIDB5 0xEC4 + +#define D40_DREG_PRSCCIDA 0xF80 +#define D40_DREG_PRSCCIDB 0xF84 + #define D40_DREG_STFU 0xFC8 #define D40_DREG_ICFG 0xFCC #define D40_DREG_PERIPHID0 0xFE0 -- cgit v1.2.3 From 4226dd86b10ac44f8e98599f6a73e3a1b929f8eb Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Thu, 13 Dec 2012 13:46:16 +0100 Subject: dmaengine: ste_dma40: add a done queue for completed descriptors This is to keep the active queue for only those transfers which are actually active in the hardware. Descriptors will be moved to the done queue after they are completed in the hardware (interrupt handler) but before all the cleanup work has been completed (tasklet). Mostly based on a previous patch by Rabin Vincent. Cc: Rabin Vincent Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 67e565bffe16..ff21dcbd8208 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -382,6 +382,7 @@ struct d40_base; * @client: Cliented owned descriptor list. * @pending_queue: Submitted jobs, to be issued by issue_pending() * @active: Active descriptor. + * @done: Completed jobs * @queue: Queued jobs. * @prepare_queue: Prepared jobs. * @dma_cfg: The client configuration of this dma channel. @@ -407,6 +408,7 @@ struct d40_chan { struct list_head client; struct list_head pending_queue; struct list_head active; + struct list_head done; struct list_head queue; struct list_head prepare_queue; struct stedma40_chan_cfg dma_cfg; @@ -754,6 +756,11 @@ static void d40_phy_lli_load(struct d40_chan *chan, struct d40_desc *desc) writel(lli_dst->reg_lnk, base + D40_CHAN_REG_SDLNK); } +static void d40_desc_done(struct d40_chan *d40c, struct d40_desc *desc) +{ + list_add_tail(&desc->node, &d40c->done); +} + static void d40_log_lli_to_lcxa(struct d40_chan *chan, struct d40_desc *desc) { struct d40_lcla_pool *pool = &chan->base->lcla_pool; @@ -914,6 +921,14 @@ static struct d40_desc *d40_first_queued(struct d40_chan *d40c) return d; } +static struct d40_desc *d40_first_done(struct d40_chan *d40c) +{ + if (list_empty(&d40c->done)) + return NULL; + + return list_first_entry(&d40c->done, struct d40_desc, node); +} + static int d40_psize_2_burst_size(bool is_log, int psize) { if (is_log) { @@ -1104,6 +1119,12 @@ static void d40_term_all(struct d40_chan *d40c) struct d40_desc *d40d; struct d40_desc *_d; + /* Release completed descriptors */ + while ((d40d = d40_first_done(d40c))) { + d40_desc_remove(d40d); + d40_desc_free(d40c, d40d); + } + /* Release active descriptors */ while ((d40d = d40_first_active_get(d40c))) { d40_desc_remove(d40d); @@ -1541,6 +1562,9 @@ static void dma_tc_handle(struct d40_chan *d40c) pm_runtime_put_autosuspend(d40c->base->dev); } + d40_desc_remove(d40d); + d40_desc_done(d40c, d40d); + d40c->pending_tx++; tasklet_schedule(&d40c->tasklet); @@ -1556,10 +1580,14 @@ static void dma_tasklet(unsigned long data) spin_lock_irqsave(&d40c->lock, flags); - /* Get first active entry from list */ - d40d = d40_first_active_get(d40c); - if (d40d == NULL) - goto err; + /* Get first entry from the done list */ + d40d = d40_first_done(d40c); + if (d40d == NULL) { + /* Check if we have reached here for cyclic job */ + d40d = d40_first_active_get(d40c); + if (d40d == NULL || !d40d->cyclic) + goto err; + } if (!d40d->cyclic) dma_cookie_complete(&d40d->txd); @@ -2823,6 +2851,7 @@ static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma, d40c->log_num = D40_PHY_CHAN; + INIT_LIST_HEAD(&d40c->done); INIT_LIST_HEAD(&d40c->active); INIT_LIST_HEAD(&d40c->queue); INIT_LIST_HEAD(&d40c->pending_queue); -- cgit v1.2.3 From f26e03ad2b50be50c98f8ecb1fd9dbdf94db91ab Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Thu, 13 Dec 2012 17:12:37 +0100 Subject: dmaengine: ste_dma40: minor cosmetic fixes This patch contains various non functional cosmetic fixes. Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 33 ++++++++++++--------------------- drivers/dma/ste_dma40_ll.c | 2 +- 2 files changed, 13 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index ff21dcbd8208..623779e580c3 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -1609,13 +1609,11 @@ static void dma_tasklet(unsigned long data) if (async_tx_test_ack(&d40d->txd)) { d40_desc_remove(d40d); d40_desc_free(d40c, d40d); - } else { - if (!d40d->is_in_client_list) { - d40_desc_remove(d40d); - d40_lcla_free_all(d40c, d40d); - list_add_tail(&d40d->node, &d40c->client); - d40d->is_in_client_list = true; - } + } else if (!d40d->is_in_client_list) { + d40_desc_remove(d40d); + d40_lcla_free_all(d40c, d40d); + list_add_tail(&d40d->node, &d40c->client); + d40d->is_in_client_list = true; } } @@ -2123,7 +2121,6 @@ _exit: } - static u32 stedma40_residue(struct dma_chan *chan) { struct d40_chan *d40c = @@ -2199,7 +2196,6 @@ d40_prep_sg_phy(struct d40_chan *chan, struct d40_desc *desc, return ret < 0 ? ret : 0; } - static struct d40_desc * d40_prep_desc(struct d40_chan *chan, struct scatterlist *sg, unsigned int sg_len, unsigned long dma_flags) @@ -2225,7 +2221,6 @@ d40_prep_desc(struct d40_chan *chan, struct scatterlist *sg, goto err; } - desc->lli_current = 0; desc->txd.flags = dma_flags; desc->txd.tx_submit = d40_tx_submit; @@ -2274,7 +2269,6 @@ d40_prep_sg(struct dma_chan *dchan, struct scatterlist *sg_src, return NULL; } - spin_lock_irqsave(&chan->lock, flags); desc = d40_prep_desc(chan, sg_src, sg_len, dma_flags); @@ -2432,11 +2426,11 @@ static int d40_alloc_chan_resources(struct dma_chan *chan) if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) d40c->lcpa = d40c->base->lcpa_base + - d40c->dma_cfg.src_dev_type * D40_LCPA_CHAN_SIZE; + d40c->dma_cfg.src_dev_type * D40_LCPA_CHAN_SIZE; else d40c->lcpa = d40c->base->lcpa_base + - d40c->dma_cfg.dst_dev_type * - D40_LCPA_CHAN_SIZE + D40_LCPA_CHAN_DST_DELTA; + d40c->dma_cfg.dst_dev_type * + D40_LCPA_CHAN_SIZE + D40_LCPA_CHAN_DST_DELTA; } dev_dbg(chan2dev(d40c), "allocated %s channel (phy %d%s)\n", @@ -2471,7 +2465,6 @@ static void d40_free_chan_resources(struct dma_chan *chan) return; } - spin_lock_irqsave(&d40c->lock, flags); err = d40_free_dma(d40c); @@ -2514,12 +2507,10 @@ d40_prep_memcpy_sg(struct dma_chan *chan, return d40_prep_sg(chan, src_sg, dst_sg, src_nents, DMA_NONE, dma_flags); } -static struct dma_async_tx_descriptor *d40_prep_slave_sg(struct dma_chan *chan, - struct scatterlist *sgl, - unsigned int sg_len, - enum dma_transfer_direction direction, - unsigned long dma_flags, - void *context) +static struct dma_async_tx_descriptor * +d40_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction direction, + unsigned long dma_flags, void *context) { if (direction != DMA_DEV_TO_MEM && direction != DMA_MEM_TO_DEV) return NULL; diff --git a/drivers/dma/ste_dma40_ll.c b/drivers/dma/ste_dma40_ll.c index 1cfe7ab50c6b..7180e0d41722 100644 --- a/drivers/dma/ste_dma40_ll.c +++ b/drivers/dma/ste_dma40_ll.c @@ -251,7 +251,7 @@ d40_phy_buf_to_lli(struct d40_phy_lli *lli, dma_addr_t addr, u32 size, return lli; - err: +err: return NULL; } -- cgit v1.2.3 From 7ce529efbcf6fdcb1854e4634adf7f6a18216e81 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Tue, 18 Dec 2012 16:59:09 +0100 Subject: dmaengine: ste_dma40: minor code readability fixes Use internal variables to the cycles to improve code readability, no functional changes. Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 623779e580c3..e317debdbe5a 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -639,19 +639,18 @@ static int d40_lcla_alloc_one(struct d40_chan *d40c, unsigned long flags; int i; int ret = -EINVAL; - int p; spin_lock_irqsave(&d40c->base->lcla_pool.lock, flags); - p = d40c->phy_chan->num * D40_LCLA_LINK_PER_EVENT_GRP; - /* * Allocate both src and dst at the same time, therefore the half * start on 1 since 0 can't be used since zero is used as end marker. */ for (i = 1 ; i < D40_LCLA_LINK_PER_EVENT_GRP / 2; i++) { - if (!d40c->base->lcla_pool.alloc_map[p + i]) { - d40c->base->lcla_pool.alloc_map[p + i] = d40d; + int idx = d40c->phy_chan->num * D40_LCLA_LINK_PER_EVENT_GRP + i; + + if (!d40c->base->lcla_pool.alloc_map[idx]) { + d40c->base->lcla_pool.alloc_map[idx] = d40d; d40d->lcla_alloc++; ret = i; break; @@ -676,10 +675,10 @@ static int d40_lcla_free_all(struct d40_chan *d40c, spin_lock_irqsave(&d40c->base->lcla_pool.lock, flags); for (i = 1 ; i < D40_LCLA_LINK_PER_EVENT_GRP / 2; i++) { - if (d40c->base->lcla_pool.alloc_map[d40c->phy_chan->num * - D40_LCLA_LINK_PER_EVENT_GRP + i] == d40d) { - d40c->base->lcla_pool.alloc_map[d40c->phy_chan->num * - D40_LCLA_LINK_PER_EVENT_GRP + i] = NULL; + int idx = d40c->phy_chan->num * D40_LCLA_LINK_PER_EVENT_GRP + i; + + if (d40c->base->lcla_pool.alloc_map[idx] == d40d) { + d40c->base->lcla_pool.alloc_map[idx] = NULL; d40d->lcla_alloc--; if (d40d->lcla_alloc == 0) { ret = 0; -- cgit v1.2.3 From 7407048bec896268b50e3c43c1d012a4764dc210 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Tue, 18 Dec 2012 12:25:14 +0100 Subject: dmaengine: ste_dma40: add software lli support This patch add support to manage LLI by SW for select phy channels. There is a HW issue in certain controllers due to which on certain occassions HW LLI cannot be used on some physical channels. To avoid the HW issue on a specific phy channel, the phy channel number can be added to the list of soft_lli_channels and there after all the transfers on that channel will use software LLI, for peripheral to memory transfers. SoftLLI introduces relink overhead, that could impact performace for certain use cases. This is based on a previous patch of Narayanan Gopalakrishnan. Cc: Shreshtha Kumar Sahu Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 20 +++++++++++++++++++- include/linux/platform_data/dma-ste-dma40.h | 8 ++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index e317debdbe5a..2ecefb7113b9 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -355,6 +355,7 @@ struct d40_lcla_pool { * @allocated_dst: Same as for src but is dst. * allocated_dst and allocated_src uses the D40_ALLOC* defines as well as * event line number. + * @use_soft_lli: To mark if the linked lists of channel are managed by SW. */ struct d40_phy_res { spinlock_t lock; @@ -362,6 +363,7 @@ struct d40_phy_res { int num; u32 allocated_src; u32 allocated_dst; + bool use_soft_lli; }; struct d40_base; @@ -783,7 +785,16 @@ static void d40_log_lli_to_lcxa(struct d40_chan *chan, struct d40_desc *desc) * can't link back to the one in LCPA space */ if (linkback || (lli_len - lli_current > 1)) { - curr_lcla = d40_lcla_alloc_one(chan, desc); + /* + * If the channel is expected to use only soft_lli don't + * allocate a lcla. This is to avoid a HW issue that exists + * in some controller during a peripheral to memory transfer + * that uses linked lists. + */ + if (!(chan->phy_chan->use_soft_lli && + chan->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM)) + curr_lcla = d40_lcla_alloc_one(chan, desc); + first_lcla = curr_lcla; } @@ -3063,6 +3074,13 @@ static int __init d40_phy_res_init(struct d40_base *base) num_phy_chans_avail--; } + /* Mark soft_lli channels */ + for (i = 0; i < base->plat_data->num_of_soft_lli_chans; i++) { + int chan = base->plat_data->soft_lli_chans[i]; + + base->phy_res[chan].use_soft_lli = true; + } + dev_info(base->dev, "%d of %d physical DMA channels available\n", num_phy_chans_avail, base->num_phy_chans); diff --git a/include/linux/platform_data/dma-ste-dma40.h b/include/linux/platform_data/dma-ste-dma40.h index b99024ba94ec..4b781014b0a0 100644 --- a/include/linux/platform_data/dma-ste-dma40.h +++ b/include/linux/platform_data/dma-ste-dma40.h @@ -147,6 +147,12 @@ struct stedma40_chan_cfg { * @memcpy_conf_log: default configuration of logical channel memcpy * @disabled_channels: A vector, ending with -1, that marks physical channels * that are for different reasons not available for the driver. + * @soft_lli_chans: A vector, that marks physical channels will use LLI by SW + * which avoids HW bug that exists in some versions of the controller. + * SoftLLI introduces relink overhead that could impact performace for + * certain use cases. + * @num_of_soft_lli_chans: The number of channels that needs to be configured + * to use SoftLLI. * @use_esram_lcla: flag for mapping the lcla into esram region * @num_of_phy_chans: The number of physical channels implemented in HW. * 0 means reading the number of channels from DMA HW but this is only valid @@ -161,6 +167,8 @@ struct stedma40_platform_data { struct stedma40_chan_cfg *memcpy_conf_phy; struct stedma40_chan_cfg *memcpy_conf_log; int disabled_channels[STEDMA40_MAX_PHYS]; + int *soft_lli_chans; + int num_of_soft_lli_chans; bool use_esram_lcla; int num_of_phy_chans; }; -- cgit v1.2.3 From 53d6d68f3c1792bce0144d8499435468e425a995 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 19 Dec 2012 14:41:56 +0100 Subject: dmaengine: set_dma40: ignore spurious interrupts Some DMA channels may be used by other cores in the SoC. This patch modifies the dma interrupt handler to ignore interrupts from unknown channels. Cc: Rabin Vincent Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 2ecefb7113b9..a97bbd3a4a47 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -1677,13 +1677,22 @@ static irqreturn_t d40_handle_interrupt(int irq, void *data) row = chan / BITS_PER_LONG; idx = chan & (BITS_PER_LONG - 1); - /* ACK interrupt */ - writel(1 << idx, base->virtbase + il[row].clr); - if (il[row].offset == D40_PHY_CHAN) d40c = base->lookup_phy_chans[idx]; else d40c = base->lookup_log_chans[il[row].offset + idx]; + + if (!d40c) { + /* + * No error because this can happen if something else + * in the system is using the channel. + */ + continue; + } + + /* ACK interrupt */ + writel(1 << idx, base->virtbase + il[row].clr); + spin_lock(&d40c->lock); if (!il[row].is_error) -- cgit v1.2.3 From da2ac56a1bc9c6c56244aa9ca990d5c5c7574b5f Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Mon, 7 Jan 2013 10:58:35 +0100 Subject: dmaengine: set_dma40: balance clock in probe fail code Clock code was changed to use clk_prepare_enable in: b707c65 dma/ste_dma40: Fixup clock usage during probe but clk_disable on probe fail path was not updated. This patch fix this by using clk_disable_unprepare in place of clk_disable. Acked-by: Ulf Hansson Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index a97bbd3a4a47..d77d41d7c463 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -3634,7 +3634,7 @@ failure: release_mem_region(base->phy_start, base->phy_size); if (base->clk) { - clk_disable(base->clk); + clk_disable_unprepare(base->clk); clk_put(base->clk); } -- cgit v1.2.3 From 3a366e614d0837d9fc23f78cdb1a1186ebc3387f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 11 Jan 2013 13:06:33 -0800 Subject: block: add missing block_bio_complete() tracepoint bio completion didn't kick block_bio_complete TP. Only dm was explicitly triggering the TP on IO completion. This makes block_bio_complete TP useless for tracers which want to know about bios, and all other bio based drivers skip generating blktrace completion events. This patch makes all bio completions via bio_endio() generate block_bio_complete TP. * Explicit trace_block_bio_complete() invocation removed from dm and the trace point is unexported. * @rq dropped from trace_block_bio_complete(). bios may fly around w/o queue associated. Verifying and accessing the assocaited queue belongs to TP probes. * blktrace now gets both request and bio completions. Make it ignore bio completions if request completion path is happening. This makes all bio based drivers generate blktrace completion events properly and makes the block_bio_complete TP actually useful. v2: With this change, block_bio_complete TP could be invoked on sg commands which have bio's with %NULL bi_bdev. Update TP assignment code to check whether bio->bi_bdev is %NULL before dereferencing. Signed-off-by: Tejun Heo Original-patch-by: Namhyung Kim Cc: Tejun Heo Cc: Steven Rostedt Cc: Alasdair Kergon Cc: dm-devel@redhat.com Cc: Neil Brown Signed-off-by: Jens Axboe --- block/blk-core.c | 1 - drivers/md/dm.c | 1 - drivers/md/raid5.c | 11 +---------- fs/bio.c | 2 ++ include/linux/blktrace_api.h | 1 + include/trace/events/block.h | 8 ++++---- kernel/trace/blktrace.c | 26 +++++++++++++++++++++++--- 7 files changed, 31 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/block/blk-core.c b/block/blk-core.c index aca5d82ff13c..4f5aec708be6 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -39,7 +39,6 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(block_bio_remap); EXPORT_TRACEPOINT_SYMBOL_GPL(block_rq_remap); -EXPORT_TRACEPOINT_SYMBOL_GPL(block_bio_complete); EXPORT_TRACEPOINT_SYMBOL_GPL(block_unplug); DEFINE_IDA(blk_queue_ida); diff --git a/drivers/md/dm.c b/drivers/md/dm.c index c72e4d5a9617..650ec2866e34 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -627,7 +627,6 @@ static void dec_pending(struct dm_io *io, int error) queue_io(md, bio); } else { /* done with normal IO or empty flush */ - trace_block_bio_complete(md->queue, bio, io_error); bio_endio(bio, io_error); } } diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 19d77a026639..9ab506df42da 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -184,8 +184,6 @@ static void return_io(struct bio *return_bi) return_bi = bi->bi_next; bi->bi_next = NULL; bi->bi_size = 0; - trace_block_bio_complete(bdev_get_queue(bi->bi_bdev), - bi, 0); bio_endio(bi, 0); bi = return_bi; } @@ -3917,8 +3915,6 @@ static void raid5_align_endio(struct bio *bi, int error) rdev_dec_pending(rdev, conf->mddev); if (!error && uptodate) { - trace_block_bio_complete(bdev_get_queue(raid_bi->bi_bdev), - raid_bi, 0); bio_endio(raid_bi, 0); if (atomic_dec_and_test(&conf->active_aligned_reads)) wake_up(&conf->wait_for_stripe); @@ -4377,8 +4373,6 @@ static void make_request(struct mddev *mddev, struct bio * bi) if ( rw == WRITE ) md_write_end(mddev); - trace_block_bio_complete(bdev_get_queue(bi->bi_bdev), - bi, 0); bio_endio(bi, 0); } } @@ -4755,11 +4749,8 @@ static int retry_aligned_read(struct r5conf *conf, struct bio *raid_bio) handled++; } remaining = raid5_dec_bi_active_stripes(raid_bio); - if (remaining == 0) { - trace_block_bio_complete(bdev_get_queue(raid_bio->bi_bdev), - raid_bio, 0); + if (remaining == 0) bio_endio(raid_bio, 0); - } if (atomic_dec_and_test(&conf->active_aligned_reads)) wake_up(&conf->wait_for_stripe); return handled; diff --git a/fs/bio.c b/fs/bio.c index b96fc6ce4855..bb5768f59b32 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -1428,6 +1428,8 @@ void bio_endio(struct bio *bio, int error) else if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) error = -EIO; + trace_block_bio_complete(bio, error); + if (bio->bi_end_io) bio->bi_end_io(bio, error); } diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index 7c2e030e72f1..0ea61e07a91c 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -12,6 +12,7 @@ struct blk_trace { int trace_state; + bool rq_based; struct rchan *rchan; unsigned long __percpu *sequence; unsigned char __percpu *msg_data; diff --git a/include/trace/events/block.h b/include/trace/events/block.h index 05c5e61f0a7c..8a168db9a645 100644 --- a/include/trace/events/block.h +++ b/include/trace/events/block.h @@ -206,7 +206,6 @@ TRACE_EVENT(block_bio_bounce, /** * block_bio_complete - completed all work on the block operation - * @q: queue holding the block operation * @bio: block operation completed * @error: io error value * @@ -215,9 +214,9 @@ TRACE_EVENT(block_bio_bounce, */ TRACE_EVENT(block_bio_complete, - TP_PROTO(struct request_queue *q, struct bio *bio, int error), + TP_PROTO(struct bio *bio, int error), - TP_ARGS(q, bio, error), + TP_ARGS(bio, error), TP_STRUCT__entry( __field( dev_t, dev ) @@ -228,7 +227,8 @@ TRACE_EVENT(block_bio_complete, ), TP_fast_assign( - __entry->dev = bio->bi_bdev->bd_dev; + __entry->dev = bio->bi_bdev ? + bio->bi_bdev->bd_dev : 0; __entry->sector = bio->bi_sector; __entry->nr_sector = bio->bi_size >> 9; __entry->error = error; diff --git a/kernel/trace/blktrace.c b/kernel/trace/blktrace.c index c0bd0308741c..190d98fbed27 100644 --- a/kernel/trace/blktrace.c +++ b/kernel/trace/blktrace.c @@ -739,6 +739,12 @@ static void blk_add_trace_rq_complete(void *ignore, struct request_queue *q, struct request *rq) { + struct blk_trace *bt = q->blk_trace; + + /* if control ever passes through here, it's a request based driver */ + if (unlikely(bt && !bt->rq_based)) + bt->rq_based = true; + blk_add_trace_rq(q, rq, BLK_TA_COMPLETE); } @@ -774,10 +780,24 @@ static void blk_add_trace_bio_bounce(void *ignore, blk_add_trace_bio(q, bio, BLK_TA_BOUNCE, 0); } -static void blk_add_trace_bio_complete(void *ignore, - struct request_queue *q, struct bio *bio, - int error) +static void blk_add_trace_bio_complete(void *ignore, struct bio *bio, int error) { + struct request_queue *q; + struct blk_trace *bt; + + if (!bio->bi_bdev) + return; + + q = bdev_get_queue(bio->bi_bdev); + bt = q->blk_trace; + + /* + * Request based drivers will generate both rq and bio completions. + * Ignore bio ones. + */ + if (likely(!bt) || bt->rq_based) + return; + blk_add_trace_bio(q, bio, BLK_TA_COMPLETE, error); } -- cgit v1.2.3 From 25effc3647635b8775aefcfd884a359b9fa31e9d Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 10 Jan 2013 11:05:06 +0900 Subject: pata_samsung_cf: Use devm_clk_get() Use devm_clk_get() rather than clk_get() to make cleanup paths more simple. Signed-off-by: Jingoo Han Signed-off-by: Jeff Garzik --- drivers/ata/pata_samsung_cf.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/pata_samsung_cf.c b/drivers/ata/pata_samsung_cf.c index 63ffb002ec67..70b0e01372b3 100644 --- a/drivers/ata/pata_samsung_cf.c +++ b/drivers/ata/pata_samsung_cf.c @@ -512,7 +512,7 @@ static int __init pata_s3c_probe(struct platform_device *pdev) return -ENOMEM; } - info->clk = clk_get(&pdev->dev, "cfcon"); + info->clk = devm_clk_get(&pdev->dev, "cfcon"); if (IS_ERR(info->clk)) { dev_err(dev, "failed to get access to cf controller clock\n"); ret = PTR_ERR(info->clk); @@ -589,7 +589,6 @@ static int __init pata_s3c_probe(struct platform_device *pdev) stop_clk: clk_disable(info->clk); - clk_put(info->clk); return ret; } @@ -601,7 +600,6 @@ static int __exit pata_s3c_remove(struct platform_device *pdev) ata_host_detach(host); clk_disable(info->clk); - clk_put(info->clk); return 0; } -- cgit v1.2.3 From 1757d902b029a29dfcef63609964385cf8865b5a Mon Sep 17 00:00:00 2001 From: David Milburn Date: Mon, 14 Jan 2013 09:59:30 -0600 Subject: libata: export host controller number thru /sys As low-level drivers register their host controller(s), keep track of the number of controllers and export thru /sys in a format so that udev can better match up port numbers with a specific controller. # pwd /sys/devices/pci0000:00 # find . -name 'ata*' -print (2nd controller with port multiplier attached) ./0000:00:1e.0/0000:05:01.0/ata2.7 ./0000:00:1e.0/0000:05:01.0/ata2.7/link7/dev7.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.0/dev7.0.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.0/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.1/dev7.1.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.1/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.2/dev7.2.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.2/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.3/dev7.3.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.3/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.4/dev7.4.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.4/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.5/dev7.5.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.5/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.6/dev7.6.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.6/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.7/dev7.7.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.7/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.8/dev7.8.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.8/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.9/dev7.9.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.9/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/ata_port ./0000:00:1e.0/0000:05:01.0/ata2.7/ata_port/ata2.7 ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.10/dev7.10.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.10/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.11/dev7.11.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.11/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.12/dev7.12.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.12/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.13/dev7.13.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.13/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.14/dev7.14.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.14/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.8 ./0000:00:1e.0/0000:05:01.0/ata2.8/link8/dev8.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.8/link8/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.8/ata_port ./0000:00:1e.0/0000:05:01.0/ata2.8/ata_port/ata2.8 (1st controller) ./0000:00:1f.2/ata1.1 ./0000:00:1f.2/ata1.1/link1/dev1.0/ata_device ./0000:00:1f.2/ata1.1/link1/ata_link ./0000:00:1f.2/ata1.1/ata_port ./0000:00:1f.2/ata1.1/ata_port/ata1.1 ./0000:00:1f.2/ata1.2 ./0000:00:1f.2/ata1.2/link2/dev2.0/ata_device ./0000:00:1f.2/ata1.2/link2/ata_link ./0000:00:1f.2/ata1.2/ata_port ./0000:00:1f.2/ata1.2/ata_port/ata1.2 ./0000:00:1f.2/ata1.3 ./0000:00:1f.2/ata1.3/link3/dev3.0/ata_device ./0000:00:1f.2/ata1.3/link3/ata_link ./0000:00:1f.2/ata1.3/ata_port ./0000:00:1f.2/ata1.3/ata_port/ata1.3 ./0000:00:1f.2/ata1.4 ./0000:00:1f.2/ata1.4/link4/dev4.0/ata_device ./0000:00:1f.2/ata1.4/link4/ata_link ./0000:00:1f.2/ata1.4/ata_port ./0000:00:1f.2/ata1.4/ata_port/ata1.4 ./0000:00:1f.2/ata1.5 ./0000:00:1f.2/ata1.5/link5/dev5.0/ata_device ./0000:00:1f.2/ata1.5/link5/ata_link ./0000:00:1f.2/ata1.5/ata_port ./0000:00:1f.2/ata1.5/ata_port/ata1.5 ./0000:00:1f.2/ata1.6 ./0000:00:1f.2/ata1.6/link6/dev6.0/ata_device ./0000:00:1f.2/ata1.6/link6/ata_link ./0000:00:1f.2/ata1.6/ata_port ./0000:00:1f.2/ata1.6/ata_port/ata1.6 Signed-off-by: David Milburn Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 4 ++++ drivers/ata/libata-transport.c | 2 +- include/linux/libata.h | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 46cd3f4c6aaa..275941b576a8 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -99,6 +99,7 @@ static void ata_dev_xfermask(struct ata_device *dev); static unsigned long ata_dev_blacklisted(const struct ata_device *dev); atomic_t ata_print_id = ATOMIC_INIT(0); +atomic_t host_print_id = ATOMIC_INIT(0); struct ata_force_param { const char *name; @@ -6097,6 +6098,9 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) for (i = host->n_ports; host->ports[i]; i++) kfree(host->ports[i]); + /* track host controller */ + host->host_id = atomic_inc_return(&host_print_id); + /* give ports names and add SCSI hosts */ for (i = 0; i < host->n_ports; i++) host->ports[i]->print_id = atomic_inc_return(&ata_print_id); diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index c04d393d20c1..61dca7a20a31 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -284,7 +284,7 @@ int ata_tport_add(struct device *parent, dev->parent = get_device(parent); dev->release = ata_tport_release; - dev_set_name(dev, "ata%d", ap->print_id); + dev_set_name(dev, "ata%d.%d", ap->host->host_id, ap->print_id); transport_setup_device(dev); error = device_add(dev); if (error) { diff --git a/include/linux/libata.h b/include/linux/libata.h index 649e5f86b5f0..7ae207eb29a0 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -546,6 +546,7 @@ struct ata_host { void *private_data; struct ata_port_operations *ops; unsigned long flags; + unsigned int host_id; /* user visible host ID */ struct mutex eh_mutex; struct task_struct *eh_owner; -- cgit v1.2.3 From 42f6e3da974dc8ad81775110c8d06835acdf375e Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 15 Jan 2013 14:53:18 +0100 Subject: drm/vmwgfx: always use ttm_bo_is_reserved Signed-off-by: Maarten Lankhorst Reviewed-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index e01a17b407b2..16556170fb32 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -959,13 +959,13 @@ void vmw_resource_unreserve(struct vmw_resource *res, if (new_backup && new_backup != res->backup) { if (res->backup) { - BUG_ON(atomic_read(&res->backup->base.reserved) == 0); + BUG_ON(!ttm_bo_is_reserved(&res->backup->base)); list_del_init(&res->mob_head); vmw_dmabuf_unreference(&res->backup); } res->backup = vmw_dmabuf_reference(new_backup); - BUG_ON(atomic_read(&new_backup->base.reserved) == 0); + BUG_ON(!ttm_bo_is_reserved(&new_backup->base)); list_add_tail(&res->mob_head, &new_backup->res_list); } if (new_backup) -- cgit v1.2.3 From 979ee290ff0a543352243145dc3654af5a856ab8 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 15 Jan 2013 14:54:22 +0100 Subject: drm/nouveau: increase reservation sequence every retry This is temporary until the fence framework can be used. With the lru/reservation atomicity removal it is possible to see your old sequence number and the buffer being reserved, leading to erroneously reporting -EDEADLK. Workaround it by bumping the sequence number every retry. Signed-off-by: Maarten Lankhorst --- drivers/gpu/drm/nouveau/nouveau_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 8bf695c52f95..9fcfcb2a020c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -321,8 +321,8 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv, int trycnt = 0; int ret, i; - sequence = atomic_add_return(1, &drm->ttm.validate_sequence); retry: + sequence = atomic_add_return(1, &drm->ttm.validate_sequence); if (++trycnt > 100000) { NV_ERROR(drm, "%s failed and gave up.\n", __func__); return -EINVAL; -- cgit v1.2.3 From 63d0a4195560362e2e00a3ad38fc331d34e1da9b Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 15 Jan 2013 14:56:37 +0100 Subject: drm/ttm: remove lru_lock around ttm_bo_reserve There should no longer be assumptions that reserve will always succeed with the lru lock held, so we can safely break the whole atomic reserve/lru thing. As a bonus this fixes most lockdep annotations for reservations. Signed-off-by: Maarten Lankhorst Reviewed-by: Jerome Glisse --- drivers/gpu/drm/ttm/ttm_bo.c | 54 ++++++++++++++++++++++------------ drivers/gpu/drm/ttm/ttm_execbuf_util.c | 2 +- include/drm/ttm/ttm_bo_driver.h | 19 +++--------- 3 files changed, 40 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 33d20be87db5..e8e4814b1295 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -213,14 +213,13 @@ int ttm_bo_del_from_lru(struct ttm_buffer_object *bo) return put_count; } -int ttm_bo_reserve_locked(struct ttm_buffer_object *bo, +int ttm_bo_reserve_nolru(struct ttm_buffer_object *bo, bool interruptible, bool no_wait, bool use_sequence, uint32_t sequence) { - struct ttm_bo_global *glob = bo->glob; int ret; - while (unlikely(atomic_read(&bo->reserved) != 0)) { + while (unlikely(atomic_xchg(&bo->reserved, 1) != 0)) { /** * Deadlock avoidance for multi-bo reserving. */ @@ -241,26 +240,36 @@ int ttm_bo_reserve_locked(struct ttm_buffer_object *bo, if (no_wait) return -EBUSY; - spin_unlock(&glob->lru_lock); ret = ttm_bo_wait_unreserved(bo, interruptible); - spin_lock(&glob->lru_lock); if (unlikely(ret)) return ret; } - atomic_set(&bo->reserved, 1); if (use_sequence) { + bool wake_up = false; /** * Wake up waiters that may need to recheck for deadlock, * if we decreased the sequence number. */ if (unlikely((bo->val_seq - sequence < (1 << 31)) || !bo->seq_valid)) - wake_up_all(&bo->event_queue); + wake_up = true; + /* + * In the worst case with memory ordering these values can be + * seen in the wrong order. However since we call wake_up_all + * in that case, this will hopefully not pose a problem, + * and the worst case would only cause someone to accidentally + * hit -EAGAIN in ttm_bo_reserve when they see old value of + * val_seq. However this would only happen if seq_valid was + * written before val_seq was, and just means some slightly + * increased cpu usage + */ bo->val_seq = sequence; bo->seq_valid = true; + if (wake_up) + wake_up_all(&bo->event_queue); } else { bo->seq_valid = false; } @@ -289,14 +298,14 @@ int ttm_bo_reserve(struct ttm_buffer_object *bo, int put_count = 0; int ret; - spin_lock(&glob->lru_lock); - ret = ttm_bo_reserve_locked(bo, interruptible, no_wait, use_sequence, - sequence); - if (likely(ret == 0)) + ret = ttm_bo_reserve_nolru(bo, interruptible, no_wait, use_sequence, + sequence); + if (likely(ret == 0)) { + spin_lock(&glob->lru_lock); put_count = ttm_bo_del_from_lru(bo); - spin_unlock(&glob->lru_lock); - - ttm_bo_list_ref_sub(bo, put_count, true); + spin_unlock(&glob->lru_lock); + ttm_bo_list_ref_sub(bo, put_count, true); + } return ret; } @@ -510,7 +519,7 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo) int ret; spin_lock(&glob->lru_lock); - ret = ttm_bo_reserve_locked(bo, false, true, false, 0); + ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); spin_lock(&bdev->fence_lock); (void) ttm_bo_wait(bo, false, false, true); @@ -603,7 +612,7 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo, return ret; spin_lock(&glob->lru_lock); - ret = ttm_bo_reserve_locked(bo, false, true, false, 0); + ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); /* * We raced, and lost, someone else holds the reservation now, @@ -667,7 +676,14 @@ static int ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool remove_all) kref_get(&nentry->list_kref); } - ret = ttm_bo_reserve_locked(entry, false, !remove_all, false, 0); + ret = ttm_bo_reserve_nolru(entry, false, true, false, 0); + if (remove_all && ret) { + spin_unlock(&glob->lru_lock); + ret = ttm_bo_reserve_nolru(entry, false, false, + false, 0); + spin_lock(&glob->lru_lock); + } + if (!ret) ret = ttm_bo_cleanup_refs_and_unlock(entry, false, !remove_all); @@ -815,7 +831,7 @@ static int ttm_mem_evict_first(struct ttm_bo_device *bdev, spin_lock(&glob->lru_lock); list_for_each_entry(bo, &man->lru, lru) { - ret = ttm_bo_reserve_locked(bo, false, true, false, 0); + ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); if (!ret) break; } @@ -1796,7 +1812,7 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink) spin_lock(&glob->lru_lock); list_for_each_entry(bo, &glob->swap_lru, swap) { - ret = ttm_bo_reserve_locked(bo, false, true, false, 0); + ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); if (!ret) break; } diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c index cd9e4523dc56..bd37b5cb8553 100644 --- a/drivers/gpu/drm/ttm/ttm_execbuf_util.c +++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c @@ -153,7 +153,7 @@ retry: struct ttm_buffer_object *bo = entry->bo; retry_this_bo: - ret = ttm_bo_reserve_locked(bo, true, true, true, val_seq); + ret = ttm_bo_reserve_nolru(bo, true, true, true, val_seq); switch (ret) { case 0: break; diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index e3a43a47d78c..6fff43222e20 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -790,16 +790,7 @@ extern void ttm_mem_io_unlock(struct ttm_mem_type_manager *man); * to make room for a buffer already reserved. (Buffers are reserved before * they are evicted). The following algorithm prevents such deadlocks from * occurring: - * 1) Buffers are reserved with the lru spinlock held. Upon successful - * reservation they are removed from the lru list. This stops a reserved buffer - * from being evicted. However the lru spinlock is released between the time - * a buffer is selected for eviction and the time it is reserved. - * Therefore a check is made when a buffer is reserved for eviction, that it - * is still the first buffer in the lru list, before it is removed from the - * list. @check_lru == 1 forces this check. If it fails, the function returns - * -EINVAL, and the caller should then choose a new buffer to evict and repeat - * the procedure. - * 2) Processes attempting to reserve multiple buffers other than for eviction, + * Processes attempting to reserve multiple buffers other than for eviction, * (typically execbuf), should first obtain a unique 32-bit * validation sequence number, * and call this function with @use_sequence == 1 and @sequence == the unique @@ -832,7 +823,7 @@ extern int ttm_bo_reserve(struct ttm_buffer_object *bo, /** - * ttm_bo_reserve_locked: + * ttm_bo_reserve_nolru: * * @bo: A pointer to a struct ttm_buffer_object. * @interruptible: Sleep interruptible if waiting. @@ -840,9 +831,7 @@ extern int ttm_bo_reserve(struct ttm_buffer_object *bo, * @use_sequence: If @bo is already reserved, Only sleep waiting for * it to become unreserved if @sequence < (@bo)->sequence. * - * Must be called with struct ttm_bo_global::lru_lock held, - * and will not remove reserved buffers from the lru lists. - * The function may release the LRU spinlock if it needs to sleep. + * Will not remove reserved buffers from the lru lists. * Otherwise identical to ttm_bo_reserve. * * Returns: @@ -855,7 +844,7 @@ extern int ttm_bo_reserve(struct ttm_buffer_object *bo, * -EDEADLK: Bo already reserved using @sequence. This error code will only * be returned if @use_sequence is set to true. */ -extern int ttm_bo_reserve_locked(struct ttm_buffer_object *bo, +extern int ttm_bo_reserve_nolru(struct ttm_buffer_object *bo, bool interruptible, bool no_wait, bool use_sequence, uint32_t sequence); -- cgit v1.2.3 From 7a1863084c9d90ce4b67d645bf9b0f1612e68f62 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 15 Jan 2013 14:56:48 +0100 Subject: drm/ttm: cleanup ttm_eu_reserve_buffers handling With the lru lock no longer required for protecting reservations we can just do a ttm_bo_reserve_nolru on -EBUSY, and handle all errors in a single path. Signed-off-by: Maarten Lankhorst Reviewed-by: Jerome Glisse --- drivers/gpu/drm/ttm/ttm_execbuf_util.c | 53 ++++++++++++++-------------------- 1 file changed, 21 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c index bd37b5cb8553..c7d323657798 100644 --- a/drivers/gpu/drm/ttm/ttm_execbuf_util.c +++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c @@ -82,22 +82,6 @@ static void ttm_eu_list_ref_sub(struct list_head *list) } } -static int ttm_eu_wait_unreserved_locked(struct list_head *list, - struct ttm_buffer_object *bo) -{ - struct ttm_bo_global *glob = bo->glob; - int ret; - - ttm_eu_del_from_lru_locked(list); - spin_unlock(&glob->lru_lock); - ret = ttm_bo_wait_unreserved(bo, true); - spin_lock(&glob->lru_lock); - if (unlikely(ret != 0)) - ttm_eu_backoff_reservation_locked(list); - return ret; -} - - void ttm_eu_backoff_reservation(struct list_head *list) { struct ttm_validate_buffer *entry; @@ -152,19 +136,23 @@ retry: list_for_each_entry(entry, list, head) { struct ttm_buffer_object *bo = entry->bo; -retry_this_bo: ret = ttm_bo_reserve_nolru(bo, true, true, true, val_seq); switch (ret) { case 0: break; case -EBUSY: - ret = ttm_eu_wait_unreserved_locked(list, bo); - if (unlikely(ret != 0)) { - spin_unlock(&glob->lru_lock); - ttm_eu_list_ref_sub(list); - return ret; - } - goto retry_this_bo; + ttm_eu_del_from_lru_locked(list); + spin_unlock(&glob->lru_lock); + ret = ttm_bo_reserve_nolru(bo, true, false, + true, val_seq); + spin_lock(&glob->lru_lock); + if (!ret) + break; + + if (unlikely(ret != -EAGAIN)) + goto err; + + /* fallthrough */ case -EAGAIN: ttm_eu_backoff_reservation_locked(list); spin_unlock(&glob->lru_lock); @@ -174,18 +162,13 @@ retry_this_bo: return ret; goto retry; default: - ttm_eu_backoff_reservation_locked(list); - spin_unlock(&glob->lru_lock); - ttm_eu_list_ref_sub(list); - return ret; + goto err; } entry->reserved = true; if (unlikely(atomic_read(&bo->cpu_writers) > 0)) { - ttm_eu_backoff_reservation_locked(list); - spin_unlock(&glob->lru_lock); - ttm_eu_list_ref_sub(list); - return -EBUSY; + ret = -EBUSY; + goto err; } } @@ -194,6 +177,12 @@ retry_this_bo: ttm_eu_list_ref_sub(list); return 0; + +err: + ttm_eu_backoff_reservation_locked(list); + spin_unlock(&glob->lru_lock); + ttm_eu_list_ref_sub(list); + return ret; } EXPORT_SYMBOL(ttm_eu_reserve_buffers); -- cgit v1.2.3 From 5e45d7dfd74100d622f9cdc70bfd1f9fae1671de Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 15 Jan 2013 14:57:05 +0100 Subject: drm/ttm: add ttm_bo_reserve_slowpath Instead of dropping everything, waiting for the bo to be unreserved and trying over, a better strategy would be to do a blocking wait. This can be mapped a lot better to a mutex_lock-like call. Signed-off-by: Maarten Lankhorst Reviewed-by: Jerome Glisse --- drivers/gpu/drm/ttm/ttm_bo.c | 47 +++++++++++++++++++++++++++++++++++++++++ include/drm/ttm/ttm_bo_driver.h | 30 ++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index e8e4814b1295..4dd6f9e77a7d 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -310,6 +310,53 @@ int ttm_bo_reserve(struct ttm_buffer_object *bo, return ret; } +int ttm_bo_reserve_slowpath_nolru(struct ttm_buffer_object *bo, + bool interruptible, uint32_t sequence) +{ + bool wake_up = false; + int ret; + + while (unlikely(atomic_xchg(&bo->reserved, 1) != 0)) { + WARN_ON(bo->seq_valid && sequence == bo->val_seq); + + ret = ttm_bo_wait_unreserved(bo, interruptible); + + if (unlikely(ret)) + return ret; + } + + if ((bo->val_seq - sequence < (1 << 31)) || !bo->seq_valid) + wake_up = true; + + /** + * Wake up waiters that may need to recheck for deadlock, + * if we decreased the sequence number. + */ + bo->val_seq = sequence; + bo->seq_valid = true; + if (wake_up) + wake_up_all(&bo->event_queue); + + return 0; +} + +int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo, + bool interruptible, uint32_t sequence) +{ + struct ttm_bo_global *glob = bo->glob; + int put_count, ret; + + ret = ttm_bo_reserve_slowpath_nolru(bo, interruptible, sequence); + if (likely(!ret)) { + spin_lock(&glob->lru_lock); + put_count = ttm_bo_del_from_lru(bo); + spin_unlock(&glob->lru_lock); + ttm_bo_list_ref_sub(bo, put_count, true); + } + return ret; +} +EXPORT_SYMBOL(ttm_bo_reserve_slowpath); + void ttm_bo_unreserve_locked(struct ttm_buffer_object *bo) { ttm_bo_add_to_lru(bo); diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index 6fff43222e20..5af71af6bf88 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -821,6 +821,36 @@ extern int ttm_bo_reserve(struct ttm_buffer_object *bo, bool interruptible, bool no_wait, bool use_sequence, uint32_t sequence); +/** + * ttm_bo_reserve_slowpath_nolru: + * @bo: A pointer to a struct ttm_buffer_object. + * @interruptible: Sleep interruptible if waiting. + * @sequence: Set (@bo)->sequence to this value after lock + * + * This is called after ttm_bo_reserve returns -EAGAIN and we backed off + * from all our other reservations. Because there are no other reservations + * held by us, this function cannot deadlock any more. + * + * Will not remove reserved buffers from the lru lists. + * Otherwise identical to ttm_bo_reserve_slowpath. + */ +extern int ttm_bo_reserve_slowpath_nolru(struct ttm_buffer_object *bo, + bool interruptible, + uint32_t sequence); + + +/** + * ttm_bo_reserve_slowpath: + * @bo: A pointer to a struct ttm_buffer_object. + * @interruptible: Sleep interruptible if waiting. + * @sequence: Set (@bo)->sequence to this value after lock + * + * This is called after ttm_bo_reserve returns -EAGAIN and we backed off + * from all our other reservations. Because there are no other reservations + * held by us, this function cannot deadlock any more. + */ +extern int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo, + bool interruptible, uint32_t sequence); /** * ttm_bo_reserve_nolru: -- cgit v1.2.3 From f2d476a110bc24fde008698ae9018c99e803e25c Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 15 Jan 2013 14:57:10 +0100 Subject: drm/ttm: use ttm_bo_reserve_slowpath_nolru in ttm_eu_reserve_buffers, v2 This requires re-use of the seqno, which increases fairness slightly. Instead of spinning with a new seqno every time we keep the current one, but still drop all other reservations we hold. Only when we succeed, we try to get back our other reservations again. This should increase fairness slightly as well. Changes since v1: - Increase val_seq before calling ttm_bo_reserve_slowpath_nolru and retrying to take all entries to prevent a race. Signed-off-by: Maarten Lankhorst Reviewed-by: Jerome Glisse --- drivers/gpu/drm/ttm/ttm_execbuf_util.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c index c7d323657798..7b90def15674 100644 --- a/drivers/gpu/drm/ttm/ttm_execbuf_util.c +++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c @@ -129,13 +129,17 @@ int ttm_eu_reserve_buffers(struct list_head *list) entry = list_first_entry(list, struct ttm_validate_buffer, head); glob = entry->bo->glob; -retry: spin_lock(&glob->lru_lock); val_seq = entry->bo->bdev->val_seq++; +retry: list_for_each_entry(entry, list, head) { struct ttm_buffer_object *bo = entry->bo; + /* already slowpath reserved? */ + if (entry->reserved) + continue; + ret = ttm_bo_reserve_nolru(bo, true, true, true, val_seq); switch (ret) { case 0: @@ -155,11 +159,26 @@ retry: /* fallthrough */ case -EAGAIN: ttm_eu_backoff_reservation_locked(list); + + /* + * temporarily increase sequence number every retry, + * to prevent us from seeing our old reservation + * sequence when someone else reserved the buffer, + * but hasn't updated the seq_valid/seqno members yet. + */ + val_seq = entry->bo->bdev->val_seq++; + spin_unlock(&glob->lru_lock); ttm_eu_list_ref_sub(list); - ret = ttm_bo_wait_unreserved(bo, true); + ret = ttm_bo_reserve_slowpath_nolru(bo, true, val_seq); if (unlikely(ret != 0)) return ret; + spin_lock(&glob->lru_lock); + entry->reserved = true; + if (unlikely(atomic_read(&bo->cpu_writers) > 0)) { + ret = -EBUSY; + goto err; + } goto retry; default: goto err; -- cgit v1.2.3 From c354c893dd57aac11f5d96ada7c47a20fe090a6e Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 15 Jan 2013 14:57:20 +0100 Subject: drm/nouveau: use ttm_bo_reserve_slowpath in validate_init, v2 Similar rationale to the identical commit in drm/ttm. Instead of only waiting for unreservation, we make sure we actually own the reservation, then retry to get the rest. Changes since v1: - Increase the seqno before calling ttm_bo_reserve_slowpath Signed-off-by: Maarten Lankhorst Reviewed-by: Jerome Glisse --- drivers/gpu/drm/nouveau/nouveau_gem.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 9fcfcb2a020c..7fa6882c2942 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -320,9 +320,10 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv, uint32_t sequence; int trycnt = 0; int ret, i; + struct nouveau_bo *res_bo = NULL; -retry: sequence = atomic_add_return(1, &drm->ttm.validate_sequence); +retry: if (++trycnt > 100000) { NV_ERROR(drm, "%s failed and gave up.\n", __func__); return -EINVAL; @@ -340,6 +341,11 @@ retry: return -ENOENT; } nvbo = gem->driver_private; + if (nvbo == res_bo) { + res_bo = NULL; + drm_gem_object_unreference_unlocked(gem); + continue; + } if (nvbo->reserved_by && nvbo->reserved_by == file_priv) { NV_ERROR(drm, "multiple instances of buffer %d on " @@ -352,15 +358,19 @@ retry: ret = ttm_bo_reserve(&nvbo->bo, true, false, true, sequence); if (ret) { validate_fini(op, NULL); - if (unlikely(ret == -EAGAIN)) - ret = ttm_bo_wait_unreserved(&nvbo->bo, true); - drm_gem_object_unreference_unlocked(gem); + if (unlikely(ret == -EAGAIN)) { + sequence = atomic_add_return(1, &drm->ttm.validate_sequence); + ret = ttm_bo_reserve_slowpath(&nvbo->bo, true, + sequence); + if (!ret) + res_bo = nvbo; + } if (unlikely(ret)) { + drm_gem_object_unreference_unlocked(gem); if (ret != -ERESTARTSYS) NV_ERROR(drm, "fail reserve\n"); return ret; } - goto retry; } b->user_priv = (uint64_t)(unsigned long)nvbo; @@ -382,6 +392,8 @@ retry: validate_fini(op, NULL); return -EINVAL; } + if (nvbo == res_bo) + goto retry; } return 0; -- cgit v1.2.3 From cc4c0c4de3c775be22072ec3251f2e581b63d9a0 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 15 Jan 2013 14:57:28 +0100 Subject: drm/ttm: unexport ttm_bo_wait_unreserved All legitimate users of this function outside ttm_bo.c are gone, now it's only an implementation detail. Signed-off-by: Maarten Lankhorst Reviewed-by: Jerome Glisse --- drivers/gpu/drm/ttm/ttm_bo.c | 4 ++-- include/drm/ttm/ttm_bo_driver.h | 12 ------------ 2 files changed, 2 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 4dd6f9e77a7d..4df47f72214a 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -158,7 +158,8 @@ static void ttm_bo_release_list(struct kref *list_kref) ttm_mem_global_free(bdev->glob->mem_glob, acc_size); } -int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, bool interruptible) +static int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, + bool interruptible) { if (interruptible) { return wait_event_interruptible(bo->event_queue, @@ -168,7 +169,6 @@ int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, bool interruptible) return 0; } } -EXPORT_SYMBOL(ttm_bo_wait_unreserved); void ttm_bo_add_to_lru(struct ttm_buffer_object *bo) { diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index 5af71af6bf88..0fbd046e7c93 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -898,18 +898,6 @@ extern void ttm_bo_unreserve(struct ttm_buffer_object *bo); */ extern void ttm_bo_unreserve_locked(struct ttm_buffer_object *bo); -/** - * ttm_bo_wait_unreserved - * - * @bo: A pointer to a struct ttm_buffer_object. - * - * Wait for a struct ttm_buffer_object to become unreserved. - * This is typically used in the execbuf code to relax cpu-usage when - * a potential deadlock condition backoff. - */ -extern int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, - bool interruptible); - /* * ttm_bo_util.c */ -- cgit v1.2.3 From 8ab3e6a08a98f7ff18c6814065eb30ba2e000233 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Wed, 2 Jan 2013 15:29:39 +0000 Subject: thermal: Use thermal zone device id in netlink messages This patch changes the function thermal_generate_netlink_event to receive a thermal zone device instead of a originator id. This way, the messages will always be bound to a thermal zone. Signed-off-by: Eduardo Valentin Reviewed-by: Durgadoss R Signed-off-by: Zhang Rui --- Documentation/thermal/sysfs-api.txt | 5 +++-- drivers/thermal/thermal_sys.c | 8 ++++++-- include/linux/thermal.h | 6 ++++-- 3 files changed, 13 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt index 88c02334e356..526d4b90d6c1 100644 --- a/Documentation/thermal/sysfs-api.txt +++ b/Documentation/thermal/sysfs-api.txt @@ -329,8 +329,9 @@ The framework includes a simple notification mechanism, in the form of a netlink event. Netlink socket initialization is done during the _init_ of the framework. Drivers which intend to use the notification mechanism just need to call thermal_generate_netlink_event() with two arguments viz -(originator, event). Typically the originator will be an integer assigned -to a thermal_zone_device when it registers itself with the framework. The +(originator, event). The originator is a pointer to struct thermal_zone_device +from where the event has been originated. An integer which represents the +thermal zone device will be used in the message to identify the zone. The event will be one of:{THERMAL_AUX0, THERMAL_AUX1, THERMAL_CRITICAL, THERMAL_DEV_FAULT}. Notification can be sent when the current temperature crosses any of the configured thresholds. diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 8c8ce806180f..d85f51f433be 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -1711,7 +1711,8 @@ static struct genl_multicast_group thermal_event_mcgrp = { .name = THERMAL_GENL_MCAST_GROUP_NAME, }; -int thermal_generate_netlink_event(u32 orig, enum events event) +int thermal_generate_netlink_event(struct thermal_zone_device *tz, + enum events event) { struct sk_buff *skb; struct nlattr *attr; @@ -1721,6 +1722,9 @@ int thermal_generate_netlink_event(u32 orig, enum events event) int result; static unsigned int thermal_event_seqnum; + if (!tz) + return -EINVAL; + /* allocate memory */ size = nla_total_size(sizeof(struct thermal_genl_event)) + nla_total_size(0); @@ -1755,7 +1759,7 @@ int thermal_generate_netlink_event(u32 orig, enum events event) memset(thermal_event, 0, sizeof(struct thermal_genl_event)); - thermal_event->orig = orig; + thermal_event->orig = tz->id; thermal_event->event = event; /* send multicast genetlink message */ diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 883bcda7e1e4..9b78f8c6f773 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -246,9 +246,11 @@ int thermal_register_governor(struct thermal_governor *); void thermal_unregister_governor(struct thermal_governor *); #ifdef CONFIG_NET -extern int thermal_generate_netlink_event(u32 orig, enum events event); +extern int thermal_generate_netlink_event(struct thermal_zone_device *tz, + enum events event); #else -static inline int thermal_generate_netlink_event(u32 orig, enum events event) +static int thermal_generate_netlink_event(struct thermal_zone_device *tz, + enum events event) { return 0; } -- cgit v1.2.3 From ba38bb8c72fc259a8e3f2f1feb5761ae6f5858f0 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Wed, 2 Jan 2013 15:29:40 +0000 Subject: thermal: remove unnecessary include No need for spinlocks in this file, then removing its header. Signed-off-by: Eduardo Valentin Reviewed-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/thermal_sys.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index d85f51f433be..70ce100d34af 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 923e0b1e8dbe0939d9fc41c226dfc5d53884d8c6 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Wed, 2 Jan 2013 15:29:41 +0000 Subject: thermal: cleanup: use dev_* helper functions Change the logging messages to used dev_* helper functions. Signed-off-by: Eduardo Valentin Reviewed-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/thermal_sys.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 70ce100d34af..fba27c36d707 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -354,8 +354,9 @@ static void handle_critical_trips(struct thermal_zone_device *tz, tz->ops->notify(tz, trip, trip_type); if (trip_type == THERMAL_TRIP_CRITICAL) { - pr_emerg("Critical temperature reached(%d C),shutting down\n", - tz->temperature / 1000); + dev_emerg(&tz->device, + "critical temperature reached(%d C),shutting down\n", + tz->temperature / 1000); orderly_poweroff(true); } } @@ -386,7 +387,8 @@ static void update_temperature(struct thermal_zone_device *tz) ret = tz->ops->get_temp(tz, &temp); if (ret) { - pr_warn("failed to read out thermal zone %d\n", tz->id); + dev_warn(&tz->device, "failed to read out thermal zone %d\n", + tz->id); goto exit; } @@ -1770,7 +1772,7 @@ int thermal_generate_netlink_event(struct thermal_zone_device *tz, result = genlmsg_multicast(skb, 0, thermal_event_mcgrp.id, GFP_ATOMIC); if (result) - pr_info("failed to send netlink event:%d\n", result); + dev_err(&tz->device, "Failed to send netlink event:%d", result); return result; } -- cgit v1.2.3 From 6b2aa51d698492e8dc0a0ce6ce5b3193ccaec269 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Wed, 2 Jan 2013 15:29:42 +0000 Subject: thermal: check for invalid trip setup when registering thermal device This patch adds an extra check in the data structure while registering a thermal device. The check is to avoid registering zones with a number of trips greater than zero, but with no .get_trip_temp nor .get_trip_type callbacks. Receiving such data structure may end in wrong data access. Signed-off-by: Eduardo Valentin Reviewed-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/thermal_sys.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index fba27c36d707..0a1bf6b032ea 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -1530,6 +1530,9 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, if (!ops || !ops->get_temp) return ERR_PTR(-EINVAL); + if (trips > 0 && !ops->get_trip_type) + return ERR_PTR(-EINVAL); + tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL); if (!tz) return ERR_PTR(-ENOMEM); -- cgit v1.2.3 From 72c7901ef00925c6d0cc7ab69183a684908303bc Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 6 Dec 2012 10:52:05 +0000 Subject: gpio: twl4030: Introduce private structure to store variables needed runtime Move most of the global variables inside a private structure and allocate it dynamically. Signed-off-by: Peter Ujfalusi Signed-off-by: Linus Walleij --- drivers/gpio/gpio-twl4030.c | 82 +++++++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index 9572aa137e6f..4643f9cd0cae 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -49,11 +49,6 @@ * There are also two LED pins used sometimes as output-only GPIOs. */ - -static struct gpio_chip twl_gpiochip; -static int twl4030_gpio_base; -static int twl4030_gpio_irq_base; - /* genirq interfaces are not available to modules */ #ifdef MODULE #define is_module() true @@ -72,11 +67,20 @@ static int twl4030_gpio_irq_base; /* Data structures */ static DEFINE_MUTEX(gpio_lock); -/* store usage of each GPIO. - each bit represents one GPIO */ -static unsigned int gpio_usage_count; +struct gpio_twl4030_priv { + struct gpio_chip gpio_chip; + int irq_base; + + unsigned int usage_count; +}; /*----------------------------------------------------------------------*/ +static inline struct gpio_twl4030_priv *to_gpio_twl4030(struct gpio_chip *chip) +{ + return container_of(chip, struct gpio_twl4030_priv, gpio_chip); +} + /* * To configure TWL4030 GPIO module registers */ @@ -193,10 +197,6 @@ static int twl4030_get_gpio_datain(int gpio) u8 base = 0; int ret = 0; - if (unlikely((gpio >= TWL4030_GPIO_MAX) - || !(gpio_usage_count & BIT(gpio)))) - return -EPERM; - base = REG_GPIODATAIN1 + d_bnk; ret = gpio_twl4030_read(base); if (ret > 0) @@ -209,6 +209,7 @@ static int twl4030_get_gpio_datain(int gpio) static int twl_request(struct gpio_chip *chip, unsigned offset) { + struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); int status = 0; mutex_lock(&gpio_lock); @@ -252,7 +253,7 @@ static int twl_request(struct gpio_chip *chip, unsigned offset) } /* on first use, turn GPIO module "on" */ - if (!gpio_usage_count) { + if (!priv->usage_count) { struct twl4030_gpio_platform_data *pdata; u8 value = MASK_GPIO_CTRL_GPIO_ON; @@ -266,16 +267,18 @@ static int twl_request(struct gpio_chip *chip, unsigned offset) status = gpio_twl4030_write(REG_GPIO_CTRL, value); } +done: if (!status) - gpio_usage_count |= (0x1 << offset); + priv->usage_count |= BIT(offset); -done: mutex_unlock(&gpio_lock); return status; } static void twl_free(struct gpio_chip *chip, unsigned offset) { + struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); + if (offset >= TWL4030_GPIO_MAX) { twl4030_led_set_value(offset - TWL4030_GPIO_MAX, 1); return; @@ -283,10 +286,10 @@ static void twl_free(struct gpio_chip *chip, unsigned offset) mutex_lock(&gpio_lock); - gpio_usage_count &= ~BIT(offset); + priv->usage_count &= ~BIT(offset); /* on last use, switch off GPIO module */ - if (!gpio_usage_count) + if (!priv->usage_count) gpio_twl4030_write(REG_GPIO_CTRL, 0x0); mutex_unlock(&gpio_lock); @@ -301,14 +304,19 @@ static int twl_direction_in(struct gpio_chip *chip, unsigned offset) static int twl_get(struct gpio_chip *chip, unsigned offset) { + struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); int status = 0; + if (!(priv->usage_count & BIT(offset))) + return -EPERM; + if (offset < TWL4030_GPIO_MAX) status = twl4030_get_gpio_datain(offset); else if (offset == TWL4030_GPIO_MAX) status = cached_leden & LEDEN_LEDAON; else status = cached_leden & LEDEN_LEDBON; + return (status < 0) ? 0 : status; } @@ -333,12 +341,14 @@ static void twl_set(struct gpio_chip *chip, unsigned offset, int value) static int twl_to_irq(struct gpio_chip *chip, unsigned offset) { - return (twl4030_gpio_irq_base && (offset < TWL4030_GPIO_MAX)) - ? (twl4030_gpio_irq_base + offset) + struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); + + return (priv->irq_base && (offset < TWL4030_GPIO_MAX)) + ? (priv->irq_base + offset) : -EINVAL; } -static struct gpio_chip twl_gpiochip = { +static struct gpio_chip template_chip = { .label = "twl4030", .owner = THIS_MODULE, .request = twl_request, @@ -424,8 +434,14 @@ static int gpio_twl4030_probe(struct platform_device *pdev) { struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data; struct device_node *node = pdev->dev.of_node; + struct gpio_twl4030_priv *priv; int ret, irq_base; + priv = devm_kzalloc(&pdev->dev, sizeof(struct gpio_twl4030_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + /* maybe setup IRQs */ if (is_module()) { dev_err(&pdev->dev, "can't dispatch IRQs from modules\n"); @@ -445,12 +461,13 @@ static int gpio_twl4030_probe(struct platform_device *pdev) if (ret < 0) return ret; - twl4030_gpio_irq_base = irq_base; + priv->irq_base = irq_base; no_irqs: - twl_gpiochip.base = -1; - twl_gpiochip.ngpio = TWL4030_GPIO_MAX; - twl_gpiochip.dev = &pdev->dev; + priv->gpio_chip = template_chip; + priv->gpio_chip.base = -1; + priv->gpio_chip.ngpio = TWL4030_GPIO_MAX; + priv->gpio_chip.dev = &pdev->dev; if (node) pdata = of_gpio_twl4030(&pdev->dev); @@ -481,23 +498,23 @@ no_irqs: * is (still) clear if use_leds is set. */ if (pdata->use_leds) - twl_gpiochip.ngpio += 2; + priv->gpio_chip.ngpio += 2; - ret = gpiochip_add(&twl_gpiochip); + ret = gpiochip_add(&priv->gpio_chip); if (ret < 0) { dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret); - twl_gpiochip.ngpio = 0; + priv->gpio_chip.ngpio = 0; gpio_twl4030_remove(pdev); goto out; } - twl4030_gpio_base = twl_gpiochip.base; + platform_set_drvdata(pdev, priv); if (pdata && pdata->setup) { int status; - status = pdata->setup(&pdev->dev, - twl4030_gpio_base, TWL4030_GPIO_MAX); + status = pdata->setup(&pdev->dev, priv->gpio_chip.base, + TWL4030_GPIO_MAX); if (status) dev_dbg(&pdev->dev, "setup --> %d\n", status); } @@ -510,18 +527,19 @@ out: static int gpio_twl4030_remove(struct platform_device *pdev) { struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data; + struct gpio_twl4030_priv *priv = platform_get_drvdata(pdev); int status; if (pdata && pdata->teardown) { - status = pdata->teardown(&pdev->dev, - twl4030_gpio_base, TWL4030_GPIO_MAX); + status = pdata->teardown(&pdev->dev, priv->gpio_chip.base, + TWL4030_GPIO_MAX); if (status) { dev_dbg(&pdev->dev, "teardown --> %d\n", status); return status; } } - status = gpiochip_remove(&twl_gpiochip); + status = gpiochip_remove(&priv->gpio_chip); if (status < 0) return status; -- cgit v1.2.3 From c111feabe2e200b15300d97107ffc1280bf8de2a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 20 Dec 2012 10:44:11 +0100 Subject: gpio: twl4030: Cache the direction and output states in private data Use more coherent locking in the driver. Use bitfield to store the GPIO direction and if the pin is configured as output store the status also in a bitfiled. In this way we can just look at these bitfields when we need information about the pin status and only reach out to the chip when it is needed. Signed-off-by: Peter Ujfalusi Acked-by: Linus Walleij Signed-off-by: Linus Walleij --- drivers/gpio/gpio-twl4030.c | 100 ++++++++++++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index 4643f9cd0cae..4d330e36da1d 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -37,7 +37,6 @@ #include - /* * The GPIO "subchip" supports 18 GPIOs which can be configured as * inputs or outputs, with pullups or pulldowns on each pin. Each @@ -64,14 +63,15 @@ /* Mask for GPIO registers when aggregated into a 32-bit integer */ #define GPIO_32_MASK 0x0003ffff -/* Data structures */ -static DEFINE_MUTEX(gpio_lock); - struct gpio_twl4030_priv { struct gpio_chip gpio_chip; + struct mutex mutex; int irq_base; + /* Bitfields for state caching */ unsigned int usage_count; + unsigned int direction; + unsigned int out_state; }; /*----------------------------------------------------------------------*/ @@ -130,7 +130,7 @@ static inline int gpio_twl4030_read(u8 address) /*----------------------------------------------------------------------*/ -static u8 cached_leden; /* protected by gpio_lock */ +static u8 cached_leden; /* The LED lines are open drain outputs ... a FET pulls to GND, so an * external pullup is needed. We could also expose the integrated PWM @@ -144,14 +144,12 @@ static void twl4030_led_set_value(int led, int value) if (led) mask <<= 1; - mutex_lock(&gpio_lock); if (value) cached_leden &= ~mask; else cached_leden |= mask; status = twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden, TWL4030_LED_LEDEN_REG); - mutex_unlock(&gpio_lock); } static int twl4030_set_gpio_direction(int gpio, int is_input) @@ -162,7 +160,6 @@ static int twl4030_set_gpio_direction(int gpio, int is_input) u8 base = REG_GPIODATADIR1 + d_bnk; int ret = 0; - mutex_lock(&gpio_lock); ret = gpio_twl4030_read(base); if (ret >= 0) { if (is_input) @@ -172,7 +169,6 @@ static int twl4030_set_gpio_direction(int gpio, int is_input) ret = gpio_twl4030_write(base, reg); } - mutex_unlock(&gpio_lock); return ret; } @@ -212,7 +208,7 @@ static int twl_request(struct gpio_chip *chip, unsigned offset) struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); int status = 0; - mutex_lock(&gpio_lock); + mutex_lock(&priv->mutex); /* Support the two LED outputs as output-only GPIOs. */ if (offset >= TWL4030_GPIO_MAX) { @@ -271,7 +267,7 @@ done: if (!status) priv->usage_count |= BIT(offset); - mutex_unlock(&gpio_lock); + mutex_unlock(&priv->mutex); return status; } @@ -279,64 +275,96 @@ static void twl_free(struct gpio_chip *chip, unsigned offset) { struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); + mutex_lock(&priv->mutex); if (offset >= TWL4030_GPIO_MAX) { twl4030_led_set_value(offset - TWL4030_GPIO_MAX, 1); - return; + goto out; } - mutex_lock(&gpio_lock); - priv->usage_count &= ~BIT(offset); /* on last use, switch off GPIO module */ if (!priv->usage_count) gpio_twl4030_write(REG_GPIO_CTRL, 0x0); - mutex_unlock(&gpio_lock); +out: + mutex_unlock(&priv->mutex); } static int twl_direction_in(struct gpio_chip *chip, unsigned offset) { - return (offset < TWL4030_GPIO_MAX) - ? twl4030_set_gpio_direction(offset, 1) - : -EINVAL; + struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); + int ret; + + mutex_lock(&priv->mutex); + if (offset < TWL4030_GPIO_MAX) + ret = twl4030_set_gpio_direction(offset, 1); + else + ret = -EINVAL; + + if (!ret) + priv->direction &= ~BIT(offset); + + mutex_unlock(&priv->mutex); + + return ret; } static int twl_get(struct gpio_chip *chip, unsigned offset) { struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); + int ret; int status = 0; - if (!(priv->usage_count & BIT(offset))) - return -EPERM; + mutex_lock(&priv->mutex); + if (!(priv->usage_count & BIT(offset))) { + ret = -EPERM; + goto out; + } - if (offset < TWL4030_GPIO_MAX) - status = twl4030_get_gpio_datain(offset); - else if (offset == TWL4030_GPIO_MAX) - status = cached_leden & LEDEN_LEDAON; + if (priv->direction & BIT(offset)) + status = priv->out_state & BIT(offset); else - status = cached_leden & LEDEN_LEDBON; + status = twl4030_get_gpio_datain(offset); - return (status < 0) ? 0 : status; + ret = (status <= 0) ? 0 : 1; +out: + mutex_unlock(&priv->mutex); + return ret; } -static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value) +static void twl_set(struct gpio_chip *chip, unsigned offset, int value) { - if (offset < TWL4030_GPIO_MAX) { + struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); + + mutex_lock(&priv->mutex); + if (offset < TWL4030_GPIO_MAX) twl4030_set_gpio_dataout(offset, value); - return twl4030_set_gpio_direction(offset, 0); - } else { + else twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value); - return 0; - } + + if (value) + priv->out_state |= BIT(offset); + else + priv->out_state &= ~BIT(offset); + + mutex_unlock(&priv->mutex); } -static void twl_set(struct gpio_chip *chip, unsigned offset, int value) +static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value) { + struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); + + mutex_lock(&priv->mutex); if (offset < TWL4030_GPIO_MAX) twl4030_set_gpio_dataout(offset, value); - else - twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value); + + priv->direction |= BIT(offset); + mutex_unlock(&priv->mutex); + + twl_set(chip, offset, value); + + return 0; } static int twl_to_irq(struct gpio_chip *chip, unsigned offset) @@ -469,6 +497,8 @@ no_irqs: priv->gpio_chip.ngpio = TWL4030_GPIO_MAX; priv->gpio_chip.dev = &pdev->dev; + mutex_init(&priv->mutex); + if (node) pdata = of_gpio_twl4030(&pdev->dev); -- cgit v1.2.3 From 0e2feb17dec9d97f6b44d5af872633f7ffba9ffb Mon Sep 17 00:00:00 2001 From: Philip Avinash Date: Thu, 17 Jan 2013 14:50:02 +0530 Subject: pwm: pwm-tiehrpwm: Low power sleep support In low power modes of AM33XX platforms, peripherals power is cut off. This patch supports low power sleep transition support for EHRPWM driver. Signed-off-by: Philip Avinash Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tiehrpwm.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) (limited to 'drivers') diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 4fcafbfba60e..33e15c4fba02 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -113,6 +113,17 @@ #define NUM_PWM_CHANNEL 2 /* EHRPWM channels */ +struct ehrpwm_context { + u16 tbctl; + u16 tbprd; + u16 cmpa; + u16 cmpb; + u16 aqctla; + u16 aqctlb; + u16 aqsfrc; + u16 aqcsfrc; +}; + struct ehrpwm_pwm_chip { struct pwm_chip chip; unsigned int clk_rate; @@ -120,6 +131,7 @@ struct ehrpwm_pwm_chip { unsigned long period_cycles[NUM_PWM_CHANNEL]; enum pwm_polarity polarity[NUM_PWM_CHANNEL]; struct clk *tbclk; + struct ehrpwm_context ctx; }; static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip) @@ -127,6 +139,11 @@ static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip) return container_of(chip, struct ehrpwm_pwm_chip, chip); } +static u16 ehrpwm_read(void *base, int offset) +{ + return readw(base + offset); +} + static void ehrpwm_write(void *base, int offset, unsigned int val) { writew(val & 0xFFFF, base + offset); @@ -516,11 +533,77 @@ static int ehrpwm_pwm_remove(struct platform_device *pdev) return pwmchip_remove(&pc->chip); } +void ehrpwm_pwm_save_context(struct ehrpwm_pwm_chip *pc) +{ + pm_runtime_get_sync(pc->chip.dev); + pc->ctx.tbctl = ehrpwm_read(pc->mmio_base, TBCTL); + pc->ctx.tbprd = ehrpwm_read(pc->mmio_base, TBPRD); + pc->ctx.cmpa = ehrpwm_read(pc->mmio_base, CMPA); + pc->ctx.cmpb = ehrpwm_read(pc->mmio_base, CMPB); + pc->ctx.aqctla = ehrpwm_read(pc->mmio_base, AQCTLA); + pc->ctx.aqctlb = ehrpwm_read(pc->mmio_base, AQCTLB); + pc->ctx.aqsfrc = ehrpwm_read(pc->mmio_base, AQSFRC); + pc->ctx.aqcsfrc = ehrpwm_read(pc->mmio_base, AQCSFRC); + pm_runtime_put_sync(pc->chip.dev); +} + +void ehrpwm_pwm_restore_context(struct ehrpwm_pwm_chip *pc) +{ + ehrpwm_write(pc->mmio_base, TBPRD, pc->ctx.tbprd); + ehrpwm_write(pc->mmio_base, CMPA, pc->ctx.cmpa); + ehrpwm_write(pc->mmio_base, CMPB, pc->ctx.cmpb); + ehrpwm_write(pc->mmio_base, AQCTLA, pc->ctx.aqctla); + ehrpwm_write(pc->mmio_base, AQCTLB, pc->ctx.aqctlb); + ehrpwm_write(pc->mmio_base, AQSFRC, pc->ctx.aqsfrc); + ehrpwm_write(pc->mmio_base, AQCSFRC, pc->ctx.aqcsfrc); + ehrpwm_write(pc->mmio_base, TBCTL, pc->ctx.tbctl); +} + +static int ehrpwm_pwm_suspend(struct device *dev) +{ + struct ehrpwm_pwm_chip *pc = dev_get_drvdata(dev); + int i; + + ehrpwm_pwm_save_context(pc); + for (i = 0; i < pc->chip.npwm; i++) { + struct pwm_device *pwm = &pc->chip.pwms[i]; + + if (!test_bit(PWMF_ENABLED, &pwm->flags)) + continue; + + /* Disable explicitly if PWM is running */ + pm_runtime_put_sync(dev); + } + return 0; +} + +static int ehrpwm_pwm_resume(struct device *dev) +{ + struct ehrpwm_pwm_chip *pc = dev_get_drvdata(dev); + int i; + + for (i = 0; i < pc->chip.npwm; i++) { + struct pwm_device *pwm = &pc->chip.pwms[i]; + + if (!test_bit(PWMF_ENABLED, &pwm->flags)) + continue; + + /* Enable explicitly if PWM was running */ + pm_runtime_get_sync(dev); + } + ehrpwm_pwm_restore_context(pc); + return 0; +} + +static SIMPLE_DEV_PM_OPS(ehrpwm_pwm_pm_ops, ehrpwm_pwm_suspend, + ehrpwm_pwm_resume); + static struct platform_driver ehrpwm_pwm_driver = { .driver = { .name = "ehrpwm", .owner = THIS_MODULE, .of_match_table = ehrpwm_of_match, + .pm = &ehrpwm_pwm_pm_ops, }, .probe = ehrpwm_pwm_probe, .remove = ehrpwm_pwm_remove, -- cgit v1.2.3 From 0d75c203effefa4f62ca4d30bf52bd9ec246717f Mon Sep 17 00:00:00 2001 From: Philip Avinash Date: Thu, 17 Jan 2013 14:50:03 +0530 Subject: pwm: pwm-tiecap: Low power sleep support In low power modes of AM33XX platforms, peripherals power is cut off. This patch supports low power sleep transition support for ECAP driver. Signed-off-by: Philip Avinash Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tiecap.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'drivers') diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index 5cf016dd9822..05b676db88e6 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -41,10 +41,17 @@ #define ECCTL2_SYNC_SEL_DISA (BIT(7) | BIT(6)) #define ECCTL2_TSCTR_FREERUN BIT(4) +struct ecap_context { + u32 cap3; + u32 cap4; + u16 ecctl2; +}; + struct ecap_pwm_chip { struct pwm_chip chip; unsigned int clk_rate; void __iomem *mmio_base; + struct ecap_context ctx; }; static inline struct ecap_pwm_chip *to_ecap_pwm_chip(struct pwm_chip *chip) @@ -288,11 +295,57 @@ static int ecap_pwm_remove(struct platform_device *pdev) return pwmchip_remove(&pc->chip); } +void ecap_pwm_save_context(struct ecap_pwm_chip *pc) +{ + pm_runtime_get_sync(pc->chip.dev); + pc->ctx.ecctl2 = readw(pc->mmio_base + ECCTL2); + pc->ctx.cap4 = readl(pc->mmio_base + CAP4); + pc->ctx.cap3 = readl(pc->mmio_base + CAP3); + pm_runtime_put_sync(pc->chip.dev); +} + +void ecap_pwm_restore_context(struct ecap_pwm_chip *pc) +{ + writel(pc->ctx.cap3, pc->mmio_base + CAP3); + writel(pc->ctx.cap4, pc->mmio_base + CAP4); + writew(pc->ctx.ecctl2, pc->mmio_base + ECCTL2); +} + +static int ecap_pwm_suspend(struct device *dev) +{ + struct ecap_pwm_chip *pc = dev_get_drvdata(dev); + struct pwm_device *pwm = pc->chip.pwms; + + ecap_pwm_save_context(pc); + + /* Disable explicitly if PWM is running */ + if (test_bit(PWMF_ENABLED, &pwm->flags)) + pm_runtime_put_sync(dev); + + return 0; +} + +static int ecap_pwm_resume(struct device *dev) +{ + struct ecap_pwm_chip *pc = dev_get_drvdata(dev); + struct pwm_device *pwm = pc->chip.pwms; + + /* Enable explicitly if PWM was running */ + if (test_bit(PWMF_ENABLED, &pwm->flags)) + pm_runtime_get_sync(dev); + + ecap_pwm_restore_context(pc); + return 0; +} + +static SIMPLE_DEV_PM_OPS(ecap_pwm_pm_ops, ecap_pwm_suspend, ecap_pwm_resume); + static struct platform_driver ecap_pwm_driver = { .driver = { .name = "ecap", .owner = THIS_MODULE, .of_match_table = ecap_of_match, + .pm = &ecap_pwm_pm_ops, }, .probe = ecap_pwm_probe, .remove = ecap_pwm_remove, -- cgit v1.2.3 From c66c6e0c0b04ce4a152fe02c562dd0752665d580 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 1 Nov 2012 08:39:26 -0500 Subject: rbd: document rbd_spec structure I promised Josh I would document whether there were any restrictions needed for accessing fields of an rbd_spec structure. This adds a big block of comments that documents the structure and how it is used--including the fact that we don't attempt to synchronize access to it. Signed-off-by: Alex Elder Reviewed-by: David Zafman Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 89576a0b3f2e..128978c6a4e0 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -119,7 +119,26 @@ struct rbd_image_header { * An rbd image specification. * * The tuple (pool_id, image_id, snap_id) is sufficient to uniquely - * identify an image. + * identify an image. Each rbd_dev structure includes a pointer to + * an rbd_spec structure that encapsulates this identity. + * + * Each of the id's in an rbd_spec has an associated name. For a + * user-mapped image, the names are supplied and the id's associated + * with them are looked up. For a layered image, a parent image is + * defined by the tuple, and the names are looked up. + * + * An rbd_dev structure contains a parent_spec pointer which is + * non-null if the image it represents is a child in a layered + * image. This pointer will refer to the rbd_spec structure used + * by the parent rbd_dev for its own identity (i.e., the structure + * is shared between the parent and child). + * + * Since these structures are populated once, during the discovery + * phase of image construction, they are effectively immutable so + * we make no effort to synchronize access to them. + * + * Note that code herein does not assume the image name is known (it + * could be a null pointer). */ struct rbd_spec { u64 pool_id; -- cgit v1.2.3 From 69e7a02f633ba7de0aefa87f3f0e3b31e953b09a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 1 Nov 2012 08:39:26 -0500 Subject: rbd: kill rbd_spec->image_name_len There may have been a benefit to hanging on to the length of an image name before, but there is really none now. The only time it's used is when probing for rbd images, so we can just compute the length then. Signed-off-by: Alex Elder Reviewed-by: David Zafman Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 128978c6a4e0..a002984891d7 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -147,7 +147,6 @@ struct rbd_spec { char *image_id; size_t image_id_len; char *image_name; - size_t image_name_len; u64 snap_id; char *snap_name; @@ -2563,15 +2562,15 @@ static char *rbd_dev_image_name(struct rbd_device *rbd_dev) rbd_assert(!rbd_dev->spec->image_name); - image_id_size = sizeof (__le32) + rbd_dev->spec->image_id_len; + len = strlen(rbd_dev->spec->image_id); + image_id_size = sizeof (__le32) + len; image_id = kmalloc(image_id_size, GFP_KERNEL); if (!image_id) return NULL; p = image_id; end = (char *) image_id + image_id_size; - ceph_encode_string(&p, end, rbd_dev->spec->image_id, - (u32) rbd_dev->spec->image_id_len); + ceph_encode_string(&p, end, rbd_dev->spec->image_id, (u32) len); size = sizeof (__le32) + RBD_IMAGE_NAME_LEN_MAX; reply_buf = kmalloc(size, GFP_KERNEL); @@ -2631,14 +2630,12 @@ static int rbd_dev_probe_update_spec(struct rbd_device *rbd_dev) /* Fetch the image name; tolerate failure here */ name = rbd_dev_image_name(rbd_dev); - if (name) { - rbd_dev->spec->image_name_len = strlen(name); + if (name) rbd_dev->spec->image_name = (char *) name; - } else { + else pr_warning(RBD_DRV_NAME "%d " "unable to get image name for image id %s\n", rbd_dev->major, rbd_dev->spec->image_id); - } /* Look up the snapshot name. */ @@ -3252,7 +3249,7 @@ static int rbd_add_parse_args(const char *buf, if (!*spec->pool_name) goto out_err; /* Missing pool name */ - spec->image_name = dup_token(&buf, &spec->image_name_len); + spec->image_name = dup_token(&buf, NULL); if (!spec->image_name) goto out_mem; if (!*spec->image_name) @@ -3342,7 +3339,7 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev) * First, see if the format 2 image id file exists, and if * so, get the image's persistent id from it. */ - size = sizeof (RBD_ID_PREFIX) + rbd_dev->spec->image_name_len; + size = sizeof (RBD_ID_PREFIX) + strlen(rbd_dev->spec->image_name); object_name = kmalloc(size, GFP_NOIO); if (!object_name) return -ENOMEM; @@ -3400,7 +3397,7 @@ static int rbd_dev_v1_probe(struct rbd_device *rbd_dev) /* Record the header object name for this rbd image. */ - size = rbd_dev->spec->image_name_len + sizeof (RBD_SUFFIX); + size = strlen(rbd_dev->spec->image_name) + sizeof (RBD_SUFFIX); rbd_dev->header_name = kmalloc(size, GFP_KERNEL); if (!rbd_dev->header_name) { ret = -ENOMEM; -- cgit v1.2.3 From 979ed480a2722ad8d9f87054635158f652a1241e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 1 Nov 2012 08:39:26 -0500 Subject: rbd: kill rbd_spec->image_id_len There is no real benefit to keeping the length of an image id, so get rid of it. Signed-off-by: Alex Elder Reviewed-by: David Zafman Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index a002984891d7..e01dbb12ad03 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -145,7 +145,6 @@ struct rbd_spec { char *pool_name; char *image_id; - size_t image_id_len; char *image_name; u64 snap_id; @@ -2492,7 +2491,6 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) void *end; char *image_id; u64 overlap; - size_t len = 0; int ret; parent_spec = rbd_spec_alloc(); @@ -2526,13 +2524,12 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) if (parent_spec->pool_id == CEPH_NOPOOL) goto out; /* No parent? No problem. */ - image_id = ceph_extract_encoded_string(&p, end, &len, GFP_KERNEL); + image_id = ceph_extract_encoded_string(&p, end, NULL, GFP_KERNEL); if (IS_ERR(image_id)) { ret = PTR_ERR(image_id); goto out_err; } parent_spec->image_id = image_id; - parent_spec->image_id_len = len; ceph_decode_64_safe(&p, end, parent_spec->snap_id, out_err); ceph_decode_64_safe(&p, end, overlap, out_err); @@ -3368,8 +3365,7 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev) p = response; rbd_dev->spec->image_id = ceph_extract_encoded_string(&p, p + RBD_IMAGE_ID_LEN_MAX, - &rbd_dev->spec->image_id_len, - GFP_NOIO); + NULL, GFP_NOIO); if (IS_ERR(rbd_dev->spec->image_id)) { ret = PTR_ERR(rbd_dev->spec->image_id); rbd_dev->spec->image_id = NULL; @@ -3393,7 +3389,6 @@ static int rbd_dev_v1_probe(struct rbd_device *rbd_dev) rbd_dev->spec->image_id = kstrdup("", GFP_KERNEL); if (!rbd_dev->spec->image_id) return -ENOMEM; - rbd_dev->spec->image_id_len = 0; /* Record the header object name for this rbd image. */ @@ -3443,7 +3438,7 @@ static int rbd_dev_v2_probe(struct rbd_device *rbd_dev) * Image id was filled in by the caller. Record the header * object name for this rbd image. */ - size = sizeof (RBD_HEADER_PREFIX) + rbd_dev->spec->image_id_len; + size = sizeof (RBD_HEADER_PREFIX) + strlen(rbd_dev->spec->image_id); rbd_dev->header_name = kmalloc(size, GFP_KERNEL); if (!rbd_dev->header_name) return -ENOMEM; -- cgit v1.2.3 From 4caf35f9ecdca1feef1d2e5e223b78e52ffbea87 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 1 Nov 2012 08:39:27 -0500 Subject: rbd: use kmemdup() This replaces two kmalloc()/memcpy() combinations with a single call to kmemdup(). Signed-off-by: Alex Elder Reviewed-by: David Zafman Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index e01dbb12ad03..d97611e2b4ee 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -3151,11 +3151,9 @@ static inline char *dup_token(const char **buf, size_t *lenp) size_t len; len = next_token(buf); - dup = kmalloc(len + 1, GFP_KERNEL); + dup = kmemdup(*buf, len + 1, GFP_KERNEL); if (!dup) return NULL; - - memcpy(dup, *buf, len); *(dup + len) = '\0'; *buf += len; @@ -3264,10 +3262,9 @@ static int rbd_add_parse_args(const char *buf, ret = -ENAMETOOLONG; goto out_err; } - spec->snap_name = kmalloc(len + 1, GFP_KERNEL); + spec->snap_name = kmemdup(buf, len + 1, GFP_KERNEL); if (!spec->snap_name) goto out_mem; - memcpy(spec->snap_name, buf, len); *(spec->snap_name + len) = '\0'; /* Initialize all rbd options to the defaults */ -- cgit v1.2.3 From 06ecc6cbf7a60cd5abd9fd2bda8ae69b395c2be3 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 1 Nov 2012 10:17:15 -0500 Subject: rbd: define and use rbd_warn() Define a new function rbd_warn() that produces a boilerplate warning message, identifying in the resulting message the affected rbd device in the best way available. Use it in a few places that now use pr_warning(). Signed-off-by: Alex Elder Reviewed-by: Dan Mick Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index d97611e2b4ee..635b81d0ebdb 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -294,6 +294,33 @@ static struct device rbd_root_dev = { .release = rbd_root_dev_release, }; +static __printf(2, 3) +void rbd_warn(struct rbd_device *rbd_dev, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + + if (!rbd_dev) + printk(KERN_WARNING "%s: %pV\n", RBD_DRV_NAME, &vaf); + else if (rbd_dev->disk) + printk(KERN_WARNING "%s: %s: %pV\n", + RBD_DRV_NAME, rbd_dev->disk->disk_name, &vaf); + else if (rbd_dev->spec && rbd_dev->spec->image_name) + printk(KERN_WARNING "%s: image %s: %pV\n", + RBD_DRV_NAME, rbd_dev->spec->image_name, &vaf); + else if (rbd_dev->spec && rbd_dev->spec->image_id) + printk(KERN_WARNING "%s: id %s: %pV\n", + RBD_DRV_NAME, rbd_dev->spec->image_id, &vaf); + else /* punt */ + printk(KERN_WARNING "%s: rbd_dev %p: %pV\n", + RBD_DRV_NAME, rbd_dev, &vaf); + va_end(args); +} + #ifdef RBD_DEBUG #define rbd_assert(expr) \ if (unlikely(!(expr))) { \ @@ -1403,8 +1430,8 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) (unsigned int) opcode); rc = rbd_dev_refresh(rbd_dev, &hver); if (rc) - pr_warning(RBD_DRV_NAME "%d got notification but failed to " - " update snaps: %d\n", rbd_dev->major, rc); + rbd_warn(rbd_dev, "got notification but failed to " + " update snaps: %d\n", rc); rbd_req_sync_notify_ack(rbd_dev, hver, notify_id); } @@ -1767,15 +1794,13 @@ rbd_dev_v1_header_read(struct rbd_device *rbd_dev, u64 *version) goto out_err; if (WARN_ON((size_t) ret < size)) { ret = -ENXIO; - pr_warning("short header read for image %s" - " (want %zd got %d)\n", - rbd_dev->spec->image_name, size, ret); + rbd_warn(rbd_dev, "short header read (want %zd got %d)", + size, ret); goto out_err; } if (!rbd_dev_ondisk_valid(ondisk)) { ret = -ENXIO; - pr_warning("invalid header for image %s\n", - rbd_dev->spec->image_name); + rbd_warn(rbd_dev, "invalid header"); goto out_err; } @@ -2630,9 +2655,7 @@ static int rbd_dev_probe_update_spec(struct rbd_device *rbd_dev) if (name) rbd_dev->spec->image_name = (char *) name; else - pr_warning(RBD_DRV_NAME "%d " - "unable to get image name for image id %s\n", - rbd_dev->major, rbd_dev->spec->image_id); + rbd_warn(rbd_dev, "unable to get image name"); /* Look up the snapshot name. */ -- cgit v1.2.3 From 4fb5d671399e83d3875593db2f56d5b57fcb104f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 1 Nov 2012 10:17:15 -0500 Subject: rbd: add warning messages for missing arguments Tell the user (via dmesg) what was wrong with the arguments provided via /sys/bus/rbd/add. Signed-off-by: Alex Elder Reviewed-by: Dan Mick --- drivers/block/rbd.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 635b81d0ebdb..31da8c538480 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -3244,8 +3244,10 @@ static int rbd_add_parse_args(const char *buf, /* The first four tokens are required */ len = next_token(&buf); - if (!len) - return -EINVAL; /* Missing monitor address(es) */ + if (!len) { + rbd_warn(NULL, "no monitor address(es) provided"); + return -EINVAL; + } mon_addrs = buf; mon_addrs_size = len + 1; buf += len; @@ -3254,8 +3256,10 @@ static int rbd_add_parse_args(const char *buf, options = dup_token(&buf, NULL); if (!options) return -ENOMEM; - if (!*options) - goto out_err; /* Missing options */ + if (!*options) { + rbd_warn(NULL, "no options provided"); + goto out_err; + } spec = rbd_spec_alloc(); if (!spec) @@ -3264,14 +3268,18 @@ static int rbd_add_parse_args(const char *buf, spec->pool_name = dup_token(&buf, NULL); if (!spec->pool_name) goto out_mem; - if (!*spec->pool_name) - goto out_err; /* Missing pool name */ + if (!*spec->pool_name) { + rbd_warn(NULL, "no pool name provided"); + goto out_err; + } spec->image_name = dup_token(&buf, NULL); if (!spec->image_name) goto out_mem; - if (!*spec->image_name) - goto out_err; /* Missing image name */ + if (!*spec->image_name) { + rbd_warn(NULL, "no image name provided"); + goto out_err; + } /* * Snapshot name is optional; default is to use "-" -- cgit v1.2.3 From f5400b7a0e78a53edce8960a079aa022640849a1 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 1 Nov 2012 10:17:15 -0500 Subject: rbd: add a warning in bio_chain_clone_range() Add a warning in bio_chain_clone_range() to help a user determine what exactly might have led to a failure. There is only one; please say something if you disagree with the following reasoning. There are three places this can return abnormally: - Initially, if there is nothing to clone. It turns out that right now this cannot happen anyway. The test is in place because the code below it doesn't work if those conditions don't hold. As such they could be assertions but since I can return a null to indicate an error I just do that instead. I have not added a warning here because it won't happen. - While processing bio's, if none remain but there are supposed to be more bytes to clone. Here I have added a warning. - If bio_clone_range() returns a null pointer. That function will have already produced a warning (at least the first time, via WARN_ON_ONCE()) to distinguish the cause of the error. The only exception is memory exhaustion, and I'd rather not pepper the code with warnings in all those spots. So no warning is added in that place. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 31da8c538480..ce6c0cbb3d7a 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -993,8 +993,10 @@ static struct bio *bio_chain_clone_range(struct bio **bio_src, unsigned int bi_size; struct bio *bio; - if (!bi) + if (!bi) { + rbd_warn(NULL, "bio_chain exhausted with %u left", len); goto out_err; /* EINVAL; ran out of bio's */ + } bi_size = min_t(unsigned int, bi->bi_size - off, len); bio = bio_clone_range(bi, off, bi_size, gfpmask); if (!bio) -- cgit v1.2.3 From 935dc89f3e29e2ef1d7c89778cdb9f37ff36e94b Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 1 Nov 2012 10:17:15 -0500 Subject: rbd: add warnings to rbd_dev_probe_update_spec() Josh suggested adding warnings to this function to help users diagnose problems. Other than memory allocatino errors, there are two places where errors can be returned. Both represent problems that should have been caught earlier, and as such might well have been handled with BUG_ON() calls. But if either ever did manage to happen, it will be reported. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index ce6c0cbb3d7a..530a1217a0fa 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -2644,8 +2644,11 @@ static int rbd_dev_probe_update_spec(struct rbd_device *rbd_dev) osdc = &rbd_dev->rbd_client->client->osdc; name = ceph_pg_pool_name_by_id(osdc->osdmap, rbd_dev->spec->pool_id); - if (!name) - return -EIO; /* pool id too large (>= 2^31) */ + if (!name) { + rbd_warn(rbd_dev, "there is no pool with id %llu", + rbd_dev->spec->pool_id); /* Really a BUG() */ + return -EIO; + } rbd_dev->spec->pool_name = kstrdup(name, GFP_KERNEL); if (!rbd_dev->spec->pool_name) @@ -2663,6 +2666,8 @@ static int rbd_dev_probe_update_spec(struct rbd_device *rbd_dev) name = rbd_snap_name(rbd_dev, rbd_dev->spec->snap_id); if (!name) { + rbd_warn(rbd_dev, "no snapshot with id %llu", + rbd_dev->spec->snap_id); /* Really a BUG() */ ret = -EIO; goto out_err; } -- cgit v1.2.3 From 725afc97c91cd5f71a015143da5095d20cd668b9 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 8 Nov 2012 08:01:39 -0600 Subject: rbd: standardize rbd_request variable names There are two names used for items of rbd_request structure type: "req" and "req_data". The former name is also used to represent items of pointers to struct ceph_osd_request. Change all variables that have these names so they are instead called "rbd_req" consistently. Signed-off-by: Alex Elder Reviewed-by: Dan Mick Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 50 ++++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 530a1217a0fa..0091fa466f83 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1088,10 +1088,12 @@ static void rbd_coll_end_req_index(struct request *rq, spin_unlock_irq(q->queue_lock); } -static void rbd_coll_end_req(struct rbd_request *req, +static void rbd_coll_end_req(struct rbd_request *rbd_req, int ret, u64 len) { - rbd_coll_end_req_index(req->rq, req->coll, req->coll_index, ret, len); + rbd_coll_end_req_index(rbd_req->rq, + rbd_req->coll, rbd_req->coll_index, + ret, len); } /* @@ -1119,12 +1121,12 @@ static int rbd_do_request(struct request *rq, int ret; u64 bno; struct timespec mtime = CURRENT_TIME; - struct rbd_request *req_data; + struct rbd_request *rbd_req; struct ceph_osd_request_head *reqhead; struct ceph_osd_client *osdc; - req_data = kzalloc(sizeof(*req_data), GFP_NOIO); - if (!req_data) { + rbd_req = kzalloc(sizeof(*rbd_req), GFP_NOIO); + if (!rbd_req) { if (coll) rbd_coll_end_req_index(rq, coll, coll_index, -ENOMEM, len); @@ -1132,8 +1134,8 @@ static int rbd_do_request(struct request *rq, } if (coll) { - req_data->coll = coll; - req_data->coll_index = coll_index; + rbd_req->coll = coll; + rbd_req->coll_index = coll_index; } dout("rbd_do_request object_name=%s ofs=%llu len=%llu coll=%p[%d]\n", @@ -1150,12 +1152,12 @@ static int rbd_do_request(struct request *rq, req->r_callback = rbd_cb; - req_data->rq = rq; - req_data->bio = bio; - req_data->pages = pages; - req_data->len = len; + rbd_req->rq = rq; + rbd_req->bio = bio; + rbd_req->pages = pages; + rbd_req->len = len; - req->r_priv = req_data; + req->r_priv = rbd_req; reqhead = req->r_request->front.iov_base; reqhead->snapid = cpu_to_le64(CEPH_NOSNAP); @@ -1200,11 +1202,11 @@ static int rbd_do_request(struct request *rq, return ret; done_err: - bio_chain_put(req_data->bio); + bio_chain_put(rbd_req->bio); ceph_osdc_put_request(req); done_pages: - rbd_coll_end_req(req_data, ret, len); - kfree(req_data); + rbd_coll_end_req(rbd_req, ret, len); + kfree(rbd_req); return ret; } @@ -1213,7 +1215,7 @@ done_pages: */ static void rbd_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg) { - struct rbd_request *req_data = req->r_priv; + struct rbd_request *rbd_req = req->r_priv; struct ceph_osd_reply_head *replyhead; struct ceph_osd_op *op; __s32 rc; @@ -1232,20 +1234,20 @@ static void rbd_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg) (unsigned long long) bytes, read_op, (int) rc); if (rc == -ENOENT && read_op) { - zero_bio_chain(req_data->bio, 0); + zero_bio_chain(rbd_req->bio, 0); rc = 0; - } else if (rc == 0 && read_op && bytes < req_data->len) { - zero_bio_chain(req_data->bio, bytes); - bytes = req_data->len; + } else if (rc == 0 && read_op && bytes < rbd_req->len) { + zero_bio_chain(rbd_req->bio, bytes); + bytes = rbd_req->len; } - rbd_coll_end_req(req_data, rc, bytes); + rbd_coll_end_req(rbd_req, rc, bytes); - if (req_data->bio) - bio_chain_put(req_data->bio); + if (rbd_req->bio) + bio_chain_put(rbd_req->bio); ceph_osdc_put_request(req); - kfree(req_data); + kfree(rbd_req); } static void rbd_simple_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg) -- cgit v1.2.3 From 5f29ddd4f0954ad6c84e28b934773f128840f064 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 8 Nov 2012 08:01:39 -0600 Subject: rbd: standardize ceph_osd_request variable names There are spots where a ceph_osds_request pointer variable is given the name "req". Since we're dealing with (at least) three types of requests (block layer, rbd, and osd), I find this slightly distracting. Change such instances to use "osd_req" consistently to make the abstraction represented a little more obvious. Signed-off-by: Alex Elder Reviewed-by: Dan Mick Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 60 +++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 0091fa466f83..85131de90012 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1111,12 +1111,12 @@ static int rbd_do_request(struct request *rq, struct ceph_osd_req_op *ops, struct rbd_req_coll *coll, int coll_index, - void (*rbd_cb)(struct ceph_osd_request *req, - struct ceph_msg *msg), + void (*rbd_cb)(struct ceph_osd_request *, + struct ceph_msg *), struct ceph_osd_request **linger_req, u64 *ver) { - struct ceph_osd_request *req; + struct ceph_osd_request *osd_req; struct ceph_file_layout *layout; int ret; u64 bno; @@ -1143,67 +1143,68 @@ static int rbd_do_request(struct request *rq, (unsigned long long) len, coll, coll_index); osdc = &rbd_dev->rbd_client->client->osdc; - req = ceph_osdc_alloc_request(osdc, flags, snapc, ops, + osd_req = ceph_osdc_alloc_request(osdc, flags, snapc, ops, false, GFP_NOIO, pages, bio); - if (!req) { + if (!osd_req) { ret = -ENOMEM; goto done_pages; } - req->r_callback = rbd_cb; + osd_req->r_callback = rbd_cb; rbd_req->rq = rq; rbd_req->bio = bio; rbd_req->pages = pages; rbd_req->len = len; - req->r_priv = rbd_req; + osd_req->r_priv = rbd_req; - reqhead = req->r_request->front.iov_base; + reqhead = osd_req->r_request->front.iov_base; reqhead->snapid = cpu_to_le64(CEPH_NOSNAP); - strncpy(req->r_oid, object_name, sizeof(req->r_oid)); - req->r_oid_len = strlen(req->r_oid); + strncpy(osd_req->r_oid, object_name, sizeof(osd_req->r_oid)); + osd_req->r_oid_len = strlen(osd_req->r_oid); - layout = &req->r_file_layout; + layout = &osd_req->r_file_layout; memset(layout, 0, sizeof(*layout)); layout->fl_stripe_unit = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); layout->fl_stripe_count = cpu_to_le32(1); layout->fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); layout->fl_pg_pool = cpu_to_le32((int) rbd_dev->spec->pool_id); ret = ceph_calc_raw_layout(osdc, layout, snapid, ofs, &len, &bno, - req, ops); + osd_req, ops); rbd_assert(ret == 0); - ceph_osdc_build_request(req, ofs, &len, + ceph_osdc_build_request(osd_req, ofs, &len, ops, snapc, &mtime, - req->r_oid, req->r_oid_len); + osd_req->r_oid, osd_req->r_oid_len); if (linger_req) { - ceph_osdc_set_request_linger(osdc, req); - *linger_req = req; + ceph_osdc_set_request_linger(osdc, osd_req); + *linger_req = osd_req; } - ret = ceph_osdc_start_request(osdc, req, false); + ret = ceph_osdc_start_request(osdc, osd_req, false); if (ret < 0) goto done_err; if (!rbd_cb) { - ret = ceph_osdc_wait_request(osdc, req); + u64 version; + + ret = ceph_osdc_wait_request(osdc, osd_req); + version = le64_to_cpu(osd_req->r_reassert_version.version); if (ver) - *ver = le64_to_cpu(req->r_reassert_version.version); - dout("reassert_ver=%llu\n", - (unsigned long long) - le64_to_cpu(req->r_reassert_version.version)); - ceph_osdc_put_request(req); + *ver = version; + dout("reassert_ver=%llu\n", (unsigned long long) version); + ceph_osdc_put_request(osd_req); } return ret; done_err: bio_chain_put(rbd_req->bio); - ceph_osdc_put_request(req); + ceph_osdc_put_request(osd_req); done_pages: rbd_coll_end_req(rbd_req, ret, len); kfree(rbd_req); @@ -1213,9 +1214,9 @@ done_pages: /* * Ceph osd op callback */ -static void rbd_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg) +static void rbd_req_cb(struct ceph_osd_request *osd_req, struct ceph_msg *msg) { - struct rbd_request *rbd_req = req->r_priv; + struct rbd_request *rbd_req = osd_req->r_priv; struct ceph_osd_reply_head *replyhead; struct ceph_osd_op *op; __s32 rc; @@ -1246,13 +1247,14 @@ static void rbd_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg) if (rbd_req->bio) bio_chain_put(rbd_req->bio); - ceph_osdc_put_request(req); + ceph_osdc_put_request(osd_req); kfree(rbd_req); } -static void rbd_simple_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg) +static void rbd_simple_req_cb(struct ceph_osd_request *osd_req, + struct ceph_msg *msg) { - ceph_osdc_put_request(req); + ceph_osdc_put_request(osd_req); } /* -- cgit v1.2.3 From 8986cb37b1cf1f54b35f062f0a12dc68dd89f311 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 8 Nov 2012 08:01:39 -0600 Subject: rbd: be picky about osd request status type The result field in a ceph osd reply header is a signed 32-bit type, but rbd code often casually uses int to represent it. The following changes the types of variables that handle this result value to be "s32" instead of "int" to be completely explicit about it. Only at the point we pass that result to __blk_end_request() does the type get converted to the plain old int defined for that interface. There is almost certainly no binary impact of this change, but I prefer to show the exact size and signedness of the value since we know it. Signed-off-by: Alex Elder Reviewed-by: Dan Mick --- drivers/block/rbd.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 85131de90012..8d93b6a649d8 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -171,7 +171,7 @@ struct rbd_client { */ struct rbd_req_status { int done; - int rc; + s32 rc; u64 bytes; }; @@ -1053,13 +1053,13 @@ static void rbd_destroy_ops(struct ceph_osd_req_op *ops) static void rbd_coll_end_req_index(struct request *rq, struct rbd_req_coll *coll, int index, - int ret, u64 len) + s32 ret, u64 len) { struct request_queue *q; int min, max, i; dout("rbd_coll_end_req_index %p index %d ret %d len %llu\n", - coll, index, ret, (unsigned long long) len); + coll, index, (int)ret, (unsigned long long)len); if (!rq) return; @@ -1080,7 +1080,7 @@ static void rbd_coll_end_req_index(struct request *rq, max++; for (i = min; istatus[i].rc, + __blk_end_request(rq, (int)coll->status[i].rc, coll->status[i].bytes); coll->num_done++; kref_put(&coll->kref, rbd_coll_release); @@ -1089,7 +1089,7 @@ static void rbd_coll_end_req_index(struct request *rq, } static void rbd_coll_end_req(struct rbd_request *rbd_req, - int ret, u64 len) + s32 ret, u64 len) { rbd_coll_end_req_index(rbd_req->rq, rbd_req->coll, rbd_req->coll_index, @@ -1129,7 +1129,7 @@ static int rbd_do_request(struct request *rq, if (!rbd_req) { if (coll) rbd_coll_end_req_index(rq, coll, coll_index, - -ENOMEM, len); + (s32)-ENOMEM, len); return -ENOMEM; } @@ -1206,7 +1206,7 @@ done_err: bio_chain_put(rbd_req->bio); ceph_osdc_put_request(osd_req); done_pages: - rbd_coll_end_req(rbd_req, ret, len); + rbd_coll_end_req(rbd_req, (s32)ret, len); kfree(rbd_req); return ret; } @@ -1219,7 +1219,7 @@ static void rbd_req_cb(struct ceph_osd_request *osd_req, struct ceph_msg *msg) struct rbd_request *rbd_req = osd_req->r_priv; struct ceph_osd_reply_head *replyhead; struct ceph_osd_op *op; - __s32 rc; + s32 rc; u64 bytes; int read_op; @@ -1227,14 +1227,14 @@ static void rbd_req_cb(struct ceph_osd_request *osd_req, struct ceph_msg *msg) replyhead = msg->front.iov_base; WARN_ON(le32_to_cpu(replyhead->num_ops) == 0); op = (void *)(replyhead + 1); - rc = le32_to_cpu(replyhead->result); + rc = (s32)le32_to_cpu(replyhead->result); bytes = le64_to_cpu(op->extent.length); read_op = (le16_to_cpu(op->op) == CEPH_OSD_OP_READ); dout("rbd_req_cb bytes=%llu readop=%d rc=%d\n", (unsigned long long) bytes, read_op, (int) rc); - if (rc == -ENOENT && read_op) { + if (rc == (s32)-ENOENT && read_op) { zero_bio_chain(rbd_req->bio, 0); rc = 0; } else if (rc == 0 && read_op && bytes < rbd_req->len) { @@ -1679,7 +1679,8 @@ static void rbd_rq_fn(struct request_queue *q) bio_chain, coll, cur_seg); else rbd_coll_end_req_index(rq, coll, cur_seg, - -ENOMEM, chain_size); + (s32)-ENOMEM, + chain_size); size -= chain_size; ofs += chain_size; -- cgit v1.2.3 From 8295cda7ceceb7b25f9a12cd21bbfb6206e28a6d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 8 Nov 2012 08:01:39 -0600 Subject: rbd: encapsulate handling for a single request In rbd_rq_fn(), requests are fetched from the block layer and each request is processed, looping through the request's list of bio's until they've all been consumed. Separate the handling for a single request into its own function to make it a bit easier to see what's going on. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 119 +++++++++++++++++++++++++++------------------------- 1 file changed, 63 insertions(+), 56 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 8d93b6a649d8..738d1e4c0ab5 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1583,6 +1583,64 @@ static struct rbd_req_coll *rbd_alloc_coll(int num_reqs) return coll; } +static int rbd_dev_do_request(struct request *rq, + struct rbd_device *rbd_dev, + struct ceph_snap_context *snapc, + u64 ofs, unsigned int size, + struct bio *bio_chain) +{ + int num_segs; + struct rbd_req_coll *coll; + unsigned int bio_offset; + int cur_seg = 0; + + dout("%s 0x%x bytes at 0x%llx\n", + rq_data_dir(rq) == WRITE ? "write" : "read", + size, (unsigned long long) blk_rq_pos(rq) * SECTOR_SIZE); + + num_segs = rbd_get_num_segments(&rbd_dev->header, ofs, size); + if (num_segs <= 0) + return num_segs; + + coll = rbd_alloc_coll(num_segs); + if (!coll) + return -ENOMEM; + + bio_offset = 0; + do { + u64 limit = rbd_segment_length(rbd_dev, ofs, size); + unsigned int clone_size; + struct bio *bio_clone; + + BUG_ON(limit > (u64)UINT_MAX); + clone_size = (unsigned int)limit; + dout("bio_chain->bi_vcnt=%hu\n", bio_chain->bi_vcnt); + + kref_get(&coll->kref); + + /* Pass a cloned bio chain via an osd request */ + + bio_clone = bio_chain_clone_range(&bio_chain, + &bio_offset, clone_size, + GFP_ATOMIC); + if (bio_clone) + (void)rbd_do_op(rq, rbd_dev, snapc, + ofs, clone_size, + bio_clone, coll, cur_seg); + else + rbd_coll_end_req_index(rq, coll, cur_seg, + (s32)-ENOMEM, + clone_size); + size -= clone_size; + ofs += clone_size; + + cur_seg++; + } while (size > 0); + kref_put(&coll->kref, rbd_coll_release); + + return 0; +} + /* * block device queue callback */ @@ -1596,10 +1654,8 @@ static void rbd_rq_fn(struct request_queue *q) bool do_write; unsigned int size; u64 ofs; - int num_segs, cur_seg = 0; - struct rbd_req_coll *coll; struct ceph_snap_context *snapc; - unsigned int bio_offset; + int result; dout("fetched request\n"); @@ -1637,60 +1693,11 @@ static void rbd_rq_fn(struct request_queue *q) ofs = blk_rq_pos(rq) * SECTOR_SIZE; bio = rq->bio; - dout("%s 0x%x bytes at 0x%llx\n", - do_write ? "write" : "read", - size, (unsigned long long) blk_rq_pos(rq) * SECTOR_SIZE); - - num_segs = rbd_get_num_segments(&rbd_dev->header, ofs, size); - if (num_segs <= 0) { - spin_lock_irq(q->queue_lock); - __blk_end_request_all(rq, num_segs); - ceph_put_snap_context(snapc); - continue; - } - coll = rbd_alloc_coll(num_segs); - if (!coll) { - spin_lock_irq(q->queue_lock); - __blk_end_request_all(rq, -ENOMEM); - ceph_put_snap_context(snapc); - continue; - } - - bio_offset = 0; - do { - u64 limit = rbd_segment_length(rbd_dev, ofs, size); - unsigned int chain_size; - struct bio *bio_chain; - - BUG_ON(limit > (u64) UINT_MAX); - chain_size = (unsigned int) limit; - dout("rq->bio->bi_vcnt=%hu\n", rq->bio->bi_vcnt); - - kref_get(&coll->kref); - - /* Pass a cloned bio chain via an osd request */ - - bio_chain = bio_chain_clone_range(&bio, - &bio_offset, chain_size, - GFP_ATOMIC); - if (bio_chain) - (void) rbd_do_op(rq, rbd_dev, snapc, - ofs, chain_size, - bio_chain, coll, cur_seg); - else - rbd_coll_end_req_index(rq, coll, cur_seg, - (s32)-ENOMEM, - chain_size); - size -= chain_size; - ofs += chain_size; - - cur_seg++; - } while (size > 0); - kref_put(&coll->kref, rbd_coll_release); - - spin_lock_irq(q->queue_lock); - + result = rbd_dev_do_request(rq, rbd_dev, snapc, ofs, size, bio); ceph_put_snap_context(snapc); + spin_lock_irq(q->queue_lock); + if (!size || result < 0) + __blk_end_request_all(rq, result); } } -- cgit v1.2.3 From 2c10d571169abab9181e0c91b259e0c4e392bc5b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 20 Dec 2012 21:24:07 +0100 Subject: drm/i915: wake up all pageflip waiters Otherwise it seems like we can get stuck with concurrent waiters. Right now this /shouldn't/ be a problem, since all pending pageflip waiters are serialized by the one mode_config.mutex, so there's at most on waiter. But better paranoid than sorry, since this is tricky code. v2: WARN_ON(waitqueue_active) before waiting, as suggested by Chris Wilson. Acked-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8c36a11a9a57..40b6b5e9d6ef 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2220,6 +2220,8 @@ intel_finish_fb(struct drm_framebuffer *old_fb) bool was_interruptible = dev_priv->mm.interruptible; int ret; + WARN_ON(waitqueue_active(&dev_priv->pending_flip_queue)); + wait_event(dev_priv->pending_flip_queue, atomic_read(&dev_priv->mm.wedged) || atomic_read(&obj->pending_flip) == 0); @@ -2887,6 +2889,8 @@ static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc) if (crtc->fb == NULL) return; + WARN_ON(waitqueue_active(&dev_priv->pending_flip_queue)); + wait_event(dev_priv->pending_flip_queue, !intel_crtc_has_pending_flip(crtc)); @@ -6824,7 +6828,7 @@ static void do_intel_finish_page_flip(struct drm_device *dev, obj = work->old_fb_obj; - wake_up(&dev_priv->pending_flip_queue); + wake_up_all(&dev_priv->pending_flip_queue); queue_work(dev_priv->wq, &work->work); -- cgit v1.2.3 From af5163acd8319dbc901bb59a8d503d8bb774d88b Mon Sep 17 00:00:00 2001 From: Egbert Eich Date: Thu, 10 Jan 2013 10:02:39 -0500 Subject: drm/i915: Remove pch_rq_mask from struct drm_i915_private. This variable is only used locally in the irq postinstall functions for ivybridge and ironlake. Signed-off-by: Egbert Eich Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 1 - drivers/gpu/drm/i915/i915_irq.c | 10 ++++++---- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index b1b1b7350ca4..910cc0b545a6 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -697,7 +697,6 @@ typedef struct drm_i915_private { u32 pipestat[2]; u32 irq_mask; u32 gt_irq_mask; - u32 pch_irq_mask; u32 hotplug_supported_mask; struct work_struct hotplug_work; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 6689a61b02a3..202813128441 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1892,6 +1892,7 @@ static int ironlake_irq_postinstall(struct drm_device *dev) DE_AUX_CHANNEL_A; u32 render_irqs; u32 hotplug_mask; + u32 pch_irq_mask; dev_priv->irq_mask = ~display_mask; @@ -1935,10 +1936,10 @@ static int ironlake_irq_postinstall(struct drm_device *dev) SDE_AUX_MASK); } - dev_priv->pch_irq_mask = ~hotplug_mask; + pch_irq_mask = ~hotplug_mask; I915_WRITE(SDEIIR, I915_READ(SDEIIR)); - I915_WRITE(SDEIMR, dev_priv->pch_irq_mask); + I915_WRITE(SDEIMR, pch_irq_mask); I915_WRITE(SDEIER, hotplug_mask); POSTING_READ(SDEIER); @@ -1966,6 +1967,7 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) DE_AUX_CHANNEL_A_IVB; u32 render_irqs; u32 hotplug_mask; + u32 pch_irq_mask; dev_priv->irq_mask = ~display_mask; @@ -1995,10 +1997,10 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) SDE_PORTD_HOTPLUG_CPT | SDE_GMBUS_CPT | SDE_AUX_MASK_CPT); - dev_priv->pch_irq_mask = ~hotplug_mask; + pch_irq_mask = ~hotplug_mask; I915_WRITE(SDEIIR, I915_READ(SDEIIR)); - I915_WRITE(SDEIMR, dev_priv->pch_irq_mask); + I915_WRITE(SDEIMR, pch_irq_mask); I915_WRITE(SDEIER, hotplug_mask); POSTING_READ(SDEIER); -- cgit v1.2.3 From d865110cc2345d67752cd7e1350b391c34feb2aa Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 7 Jan 2013 21:47:33 +0200 Subject: drm/i915: merge get_gtt_alignment/get_unfenced_gtt_alignment() The two functions are rather similar, so merge them. Signed-off-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 5 ++-- drivers/gpu/drm/i915/i915_gem.c | 44 ++++++---------------------------- drivers/gpu/drm/i915/i915_gem_tiling.c | 6 ++--- 3 files changed, 12 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 910cc0b545a6..0b09361d37ae 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1565,9 +1565,8 @@ void i915_gem_free_all_phys_object(struct drm_device *dev); void i915_gem_release(struct drm_device *dev, struct drm_file *file); uint32_t -i915_gem_get_unfenced_gtt_alignment(struct drm_device *dev, - uint32_t size, - int tiling_mode); +i915_gem_get_gtt_alignment(struct drm_device *dev, uint32_t size, + int tiling_mode, bool fenced); int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, enum i915_cache_level cache_level); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index e6cc020ea32c..2166b61053c6 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1463,16 +1463,15 @@ i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode) * Return the required GTT alignment for an object, taking into account * potential fence register mapping. */ -static uint32_t -i915_gem_get_gtt_alignment(struct drm_device *dev, - uint32_t size, - int tiling_mode) +uint32_t +i915_gem_get_gtt_alignment(struct drm_device *dev, uint32_t size, + int tiling_mode, bool fenced) { /* * Minimum alignment is 4k (GTT page size), but might be greater * if a fence register is needed for the object. */ - if (INTEL_INFO(dev)->gen >= 4 || + if (INTEL_INFO(dev)->gen >= 4 || (!fenced && IS_G33(dev)) || tiling_mode == I915_TILING_NONE) return 4096; @@ -1483,35 +1482,6 @@ i915_gem_get_gtt_alignment(struct drm_device *dev, return i915_gem_get_gtt_size(dev, size, tiling_mode); } -/** - * i915_gem_get_unfenced_gtt_alignment - return required GTT alignment for an - * unfenced object - * @dev: the device - * @size: size of the object - * @tiling_mode: tiling mode of the object - * - * Return the required GTT alignment for an object, only taking into account - * unfenced tiled surface requirements. - */ -uint32_t -i915_gem_get_unfenced_gtt_alignment(struct drm_device *dev, - uint32_t size, - int tiling_mode) -{ - /* - * Minimum alignment is 4k (GTT page size) for sane hw. - */ - if (INTEL_INFO(dev)->gen >= 4 || IS_G33(dev) || - tiling_mode == I915_TILING_NONE) - return 4096; - - /* Previous hardware however needs to be aligned to a power-of-two - * tile height. The simplest method for determining this is to reuse - * the power-of-tile object size. - */ - return i915_gem_get_gtt_size(dev, size, tiling_mode); -} - static int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj) { struct drm_i915_private *dev_priv = obj->base.dev->dev_private; @@ -2934,11 +2904,11 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, obj->tiling_mode); fence_alignment = i915_gem_get_gtt_alignment(dev, obj->base.size, - obj->tiling_mode); + obj->tiling_mode, true); unfenced_alignment = - i915_gem_get_unfenced_gtt_alignment(dev, + i915_gem_get_gtt_alignment(dev, obj->base.size, - obj->tiling_mode); + obj->tiling_mode, false); if (alignment == 0) alignment = map_and_fenceable ? fence_alignment : diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index 65f1d4f3f775..cb71ded7122e 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -374,9 +374,9 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, /* Rebind if we need a change of alignment */ if (!obj->map_and_fenceable) { u32 unfenced_alignment = - i915_gem_get_unfenced_gtt_alignment(dev, - obj->base.size, - args->tiling_mode); + i915_gem_get_gtt_alignment(dev, obj->base.size, + args->tiling_mode, + false); if (obj->gtt_offset & (unfenced_alignment - 1)) ret = i915_gem_object_unbind(obj); } -- cgit v1.2.3 From 56c844e539f1f6f5768c5f73f119e6f4aed9d320 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 7 Jan 2013 21:47:34 +0200 Subject: drm/i915: merge {i965, sandybridge}_write_fence_reg() The two functions are rather similar, so merge them. Signed-off-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 44 ++++++++++++++--------------------------- 1 file changed, 15 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 2166b61053c6..718574eb66de 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2507,52 +2507,38 @@ int i915_gpu_idle(struct drm_device *dev) return 0; } -static void sandybridge_write_fence_reg(struct drm_device *dev, int reg, - struct drm_i915_gem_object *obj) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - uint64_t val; - - if (obj) { - u32 size = obj->gtt_space->size; - - val = (uint64_t)((obj->gtt_offset + size - 4096) & - 0xfffff000) << 32; - val |= obj->gtt_offset & 0xfffff000; - val |= (uint64_t)((obj->stride / 128) - 1) << - SANDYBRIDGE_FENCE_PITCH_SHIFT; - - if (obj->tiling_mode == I915_TILING_Y) - val |= 1 << I965_FENCE_TILING_Y_SHIFT; - val |= I965_FENCE_REG_VALID; - } else - val = 0; - - I915_WRITE64(FENCE_REG_SANDYBRIDGE_0 + reg * 8, val); - POSTING_READ(FENCE_REG_SANDYBRIDGE_0 + reg * 8); -} - static void i965_write_fence_reg(struct drm_device *dev, int reg, struct drm_i915_gem_object *obj) { drm_i915_private_t *dev_priv = dev->dev_private; + int fence_reg; + int fence_pitch_shift; uint64_t val; + if (INTEL_INFO(dev)->gen >= 6) { + fence_reg = FENCE_REG_SANDYBRIDGE_0; + fence_pitch_shift = SANDYBRIDGE_FENCE_PITCH_SHIFT; + } else { + fence_reg = FENCE_REG_965_0; + fence_pitch_shift = I965_FENCE_PITCH_SHIFT; + } + if (obj) { u32 size = obj->gtt_space->size; val = (uint64_t)((obj->gtt_offset + size - 4096) & 0xfffff000) << 32; val |= obj->gtt_offset & 0xfffff000; - val |= ((obj->stride / 128) - 1) << I965_FENCE_PITCH_SHIFT; + val |= (uint64_t)((obj->stride / 128) - 1) << fence_pitch_shift; if (obj->tiling_mode == I915_TILING_Y) val |= 1 << I965_FENCE_TILING_Y_SHIFT; val |= I965_FENCE_REG_VALID; } else val = 0; - I915_WRITE64(FENCE_REG_965_0 + reg * 8, val); - POSTING_READ(FENCE_REG_965_0 + reg * 8); + fence_reg += reg * 8; + I915_WRITE64(fence_reg, val); + POSTING_READ(fence_reg); } static void i915_write_fence_reg(struct drm_device *dev, int reg, @@ -2636,7 +2622,7 @@ static void i915_gem_write_fence(struct drm_device *dev, int reg, { switch (INTEL_INFO(dev)->gen) { case 7: - case 6: sandybridge_write_fence_reg(dev, reg, obj); break; + case 6: case 5: case 4: i965_write_fence_reg(dev, reg, obj); break; case 3: i915_write_fence_reg(dev, reg, obj); break; -- cgit v1.2.3 From 0fa877965194fa79fe87944844185d90cfc35352 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 7 Jan 2013 21:47:35 +0200 Subject: drm/i915: use gtt_get_size() instead of open coding it Signed-off-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/i915_gem.c | 2 +- drivers/gpu/drm/i915/i915_gem_tiling.c | 13 +------------ 3 files changed, 4 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 0b09361d37ae..35ecabc711ed 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1564,6 +1564,8 @@ void i915_gem_detach_phys_object(struct drm_device *dev, void i915_gem_free_all_phys_object(struct drm_device *dev); void i915_gem_release(struct drm_device *dev, struct drm_file *file); +uint32_t +i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode); uint32_t i915_gem_get_gtt_alignment(struct drm_device *dev, uint32_t size, int tiling_mode, bool fenced); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 718574eb66de..313bdbaba3cd 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1435,7 +1435,7 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj) obj->fault_mappable = false; } -static uint32_t +uint32_t i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode) { uint32_t gtt_size; diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index cb71ded7122e..e76f0d8470a1 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -272,18 +272,7 @@ i915_gem_object_fence_ok(struct drm_i915_gem_object *obj, int tiling_mode) return false; } - /* - * Previous chips need to be aligned to the size of the smallest - * fence register that can contain the object. - */ - if (INTEL_INFO(obj->base.dev)->gen == 3) - size = 1024*1024; - else - size = 512*1024; - - while (size < obj->base.size) - size <<= 1; - + size = i915_gem_get_gtt_size(obj->base.dev, obj->base.size, tiling_mode); if (obj->gtt_space->size != size) return false; -- cgit v1.2.3 From dd624afd533bdc87b8c10835515a0c8b2b9868b1 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 15 Jan 2013 12:39:35 +0000 Subject: drm/i915: Add a debug interface to forcibly evict and shrink our object caches As a means to investigate some bad system behaviour related to the purging of the active, inactive and unbound lists, it is useful to be able to manually control when those lists should be cleared. v2: use _safe list iterators as we kick objects from the list as we walk. Signed-off-by: Chris Wilson [danvet: Add a small comment explaining why we don't need to check and wait for gpu resets, acked by Chris on irc.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 104 ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_gem.c | 2 +- 3 files changed, 106 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index f7d88e99ebf0..f94418bd1ed2 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1776,6 +1776,102 @@ static const struct file_operations i915_ring_stop_fops = { .llseek = default_llseek, }; +#define DROP_UNBOUND 0x1 +#define DROP_BOUND 0x2 +#define DROP_RETIRE 0x4 +#define DROP_ACTIVE 0x8 +#define DROP_ALL (DROP_UNBOUND | \ + DROP_BOUND | \ + DROP_RETIRE | \ + DROP_ACTIVE) +static ssize_t +i915_drop_caches_read(struct file *filp, + char __user *ubuf, + size_t max, + loff_t *ppos) +{ + char buf[20]; + int len; + + len = snprintf(buf, sizeof(buf), "0x%08x\n", DROP_ALL); + if (len > sizeof(buf)) + len = sizeof(buf); + + return simple_read_from_buffer(ubuf, max, ppos, buf, len); +} + +static ssize_t +i915_drop_caches_write(struct file *filp, + const char __user *ubuf, + size_t cnt, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj, *next; + char buf[20]; + int val = 0, ret; + + if (cnt > 0) { + if (cnt > sizeof(buf) - 1) + return -EINVAL; + + if (copy_from_user(buf, ubuf, cnt)) + return -EFAULT; + buf[cnt] = 0; + + val = simple_strtoul(buf, NULL, 0); + } + + DRM_DEBUG_DRIVER("Dropping caches: 0x%08x\n", val); + + /* No need to check and wait for gpu resets, only libdrm auto-restarts + * on ioctls on -EAGAIN. */ + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + if (val & DROP_ACTIVE) { + ret = i915_gpu_idle(dev); + if (ret) + goto unlock; + } + + if (val & (DROP_RETIRE | DROP_ACTIVE)) + i915_gem_retire_requests(dev); + + if (val & DROP_BOUND) { + list_for_each_entry_safe(obj, next, &dev_priv->mm.inactive_list, mm_list) + if (obj->pin_count == 0) { + ret = i915_gem_object_unbind(obj); + if (ret) + goto unlock; + } + } + + if (val & DROP_UNBOUND) { + list_for_each_entry_safe(obj, next, &dev_priv->mm.unbound_list, gtt_list) + if (obj->pages_pin_count == 0) { + ret = i915_gem_object_put_pages(obj); + if (ret) + goto unlock; + } + } + +unlock: + mutex_unlock(&dev->struct_mutex); + + return ret ?: cnt; +} + +static const struct file_operations i915_drop_caches_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = i915_drop_caches_read, + .write = i915_drop_caches_write, + .llseek = default_llseek, +}; + static ssize_t i915_max_freq_read(struct file *filp, char __user *ubuf, @@ -2172,6 +2268,12 @@ int i915_debugfs_init(struct drm_minor *minor) if (ret) return ret; + ret = i915_debugfs_create(minor->debugfs_root, minor, + "i915_gem_drop_caches", + &i915_drop_caches_fops); + if (ret) + return ret; + ret = i915_debugfs_create(minor->debugfs_root, minor, "i915_error_state", &i915_error_state_fops); @@ -2203,6 +2305,8 @@ void i915_debugfs_cleanup(struct drm_minor *minor) 1, minor); drm_debugfs_remove_files((struct drm_info_list *) &i915_cache_sharing_fops, 1, minor); + drm_debugfs_remove_files((struct drm_info_list *) &i915_drop_caches_fops, + 1, minor); drm_debugfs_remove_files((struct drm_info_list *) &i915_ring_stop_fops, 1, minor); drm_debugfs_remove_files((struct drm_info_list *) &i915_error_state_fops, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 35ecabc711ed..8f5b2816fc03 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1445,6 +1445,7 @@ int __must_check i915_gem_object_pin(struct drm_i915_gem_object *obj, bool nonblocking); void i915_gem_object_unpin(struct drm_i915_gem_object *obj); int __must_check i915_gem_object_unbind(struct drm_i915_gem_object *obj); +int i915_gem_object_put_pages(struct drm_i915_gem_object *obj); void i915_gem_release_mmap(struct drm_i915_gem_object *obj); void i915_gem_lastclose(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 313bdbaba3cd..9d33fb3f8d42 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1662,7 +1662,7 @@ i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj) kfree(obj->pages); } -static int +int i915_gem_object_put_pages(struct drm_i915_gem_object *obj) { const struct drm_i915_gem_object_ops *ops = obj->ops; -- cgit v1.2.3 From 43e28f092b2fa4ebc46bdc210134a80610815785 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 8 Jan 2013 10:53:09 +0000 Subject: drm/i915: Bail if we attempt to allocate pages for a purged object Move the existing checking inside bind_to_gtt() to the more appropriate layer in order to prevent recreation of the pages after they have been explicitly truncated. Signed-off-by: Chris Wilson Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 9d33fb3f8d42..a2bb18914329 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1828,6 +1828,11 @@ i915_gem_object_get_pages(struct drm_i915_gem_object *obj) if (obj->pages) return 0; + if (obj->madv != I915_MADV_WILLNEED) { + DRM_ERROR("Attempting to obtain a purgeable object\n"); + return -EINVAL; + } + BUG_ON(obj->pages_pin_count); ret = ops->get_pages(obj); @@ -2440,7 +2445,7 @@ int i915_gem_object_unbind(struct drm_i915_gem_object *obj) { drm_i915_private_t *dev_priv = obj->base.dev->dev_private; - int ret = 0; + int ret; if (obj->gtt_space == NULL) return 0; @@ -2880,11 +2885,6 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, bool mappable, fenceable; int ret; - if (obj->madv != I915_MADV_WILLNEED) { - DRM_ERROR("Attempting to bind a purgeable object\n"); - return -EINVAL; - } - fence_size = i915_gem_get_gtt_size(dev, obj->base.size, obj->tiling_mode); -- cgit v1.2.3 From 419fa72a1973c9ba2fd2c2505dc889f54b857459 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 8 Jan 2013 10:53:13 +0000 Subject: drm/i915: Mark a temporary allocation for copy-from-user as such The difference is that the kernel will then know that this memory will be reclaimable in the near future. Signed-off-by: Chris Wilson Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 163bb52bd3b3..986eb98f0d25 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -1113,7 +1113,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data, } exec2_list = kmalloc(sizeof(*exec2_list)*args->buffer_count, - GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY); + GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY); if (exec2_list == NULL) exec2_list = drm_malloc_ab(sizeof(*exec2_list), args->buffer_count); -- cgit v1.2.3 From 3b96eff447b4ca34ca8ccd42e2651be2955f34b4 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 8 Jan 2013 10:53:14 +0000 Subject: drm/i915: Take the handle idr spinlock once for looking up the exec objects Signed-off-by: Chris Wilson Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 86 ++++++++++++++++-------------- 1 file changed, 46 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 986eb98f0d25..da103c179e3f 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -69,6 +69,46 @@ eb_add_object(struct eb_objects *eb, struct drm_i915_gem_object *obj) &eb->buckets[obj->exec_handle & eb->and]); } +static int +eb_lookup_objects(struct eb_objects *eb, + struct drm_i915_gem_exec_object2 *exec, + int count, + struct drm_file *file, + struct list_head *objects) +{ + int i; + + spin_lock(&file->table_lock); + for (i = 0; i < count; i++) { + struct drm_i915_gem_object *obj; + + obj = to_intel_bo(idr_find(&file->object_idr, exec[i].handle)); + if (obj == NULL) { + spin_unlock(&file->table_lock); + DRM_DEBUG("Invalid object handle %d at index %d\n", + exec[i].handle, i); + return -ENOENT; + } + + if (!list_empty(&obj->exec_list)) { + spin_unlock(&file->table_lock); + DRM_DEBUG("Object %p [handle %d, index %d] appears more than once in object list\n", + obj, exec[i].handle, i); + return -EINVAL; + } + + drm_gem_object_reference(&obj->base); + list_add_tail(&obj->exec_list, objects); + + obj->exec_handle = exec[i].handle; + obj->exec_entry = &exec[i]; + eb_add_object(eb, obj); + } + spin_unlock(&file->table_lock); + + return 0; +} + static struct drm_i915_gem_object * eb_get_object(struct eb_objects *eb, unsigned long handle) { @@ -550,21 +590,9 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, /* reacquire the objects */ eb_reset(eb); - for (i = 0; i < count; i++) { - obj = to_intel_bo(drm_gem_object_lookup(dev, file, - exec[i].handle)); - if (&obj->base == NULL) { - DRM_DEBUG("Invalid object handle %d at index %d\n", - exec[i].handle, i); - ret = -ENOENT; - goto err; - } - - list_add_tail(&obj->exec_list, objects); - obj->exec_handle = exec[i].handle; - obj->exec_entry = &exec[i]; - eb_add_object(eb, obj); - } + ret = eb_lookup_objects(eb, exec, count, file, objects); + if (ret) + goto err; ret = i915_gem_execbuffer_reserve(ring, file, objects); if (ret) @@ -872,31 +900,9 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, /* Look up object handles */ INIT_LIST_HEAD(&objects); - for (i = 0; i < args->buffer_count; i++) { - struct drm_i915_gem_object *obj; - - obj = to_intel_bo(drm_gem_object_lookup(dev, file, - exec[i].handle)); - if (&obj->base == NULL) { - DRM_DEBUG("Invalid object handle %d at index %d\n", - exec[i].handle, i); - /* prevent error path from reading uninitialized data */ - ret = -ENOENT; - goto err; - } - - if (!list_empty(&obj->exec_list)) { - DRM_DEBUG("Object %p [handle %d, index %d] appears more than once in object list\n", - obj, exec[i].handle, i); - ret = -EINVAL; - goto err; - } - - list_add_tail(&obj->exec_list, &objects); - obj->exec_handle = exec[i].handle; - obj->exec_entry = &exec[i]; - eb_add_object(eb, obj); - } + ret = eb_lookup_objects(eb, exec, args->buffer_count, file, &objects); + if (ret) + goto err; /* take note of the batch buffer before we might reorder the lists */ batch_obj = list_entry(objects.prev, -- cgit v1.2.3 From bcffc3faa692d6b2ef734e4f0c8f097175284db6 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 8 Jan 2013 10:53:15 +0000 Subject: drm/i915: Move the execbuffer objects list from the stack into the tracker Instead of passing around the eb-objects hashtable and a separate object list, we can include the object list into the eb-objects structure for convenience. Signed-off-by: Chris Wilson Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 58 ++++++++++++++---------------- 1 file changed, 27 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index da103c179e3f..386677f8fd38 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -34,6 +34,7 @@ #include struct eb_objects { + struct list_head objects; int and; struct hlist_head buckets[0]; }; @@ -53,6 +54,7 @@ eb_create(int size) return eb; eb->and = count - 1; + INIT_LIST_HEAD(&eb->objects); return eb; } @@ -73,8 +75,7 @@ static int eb_lookup_objects(struct eb_objects *eb, struct drm_i915_gem_exec_object2 *exec, int count, - struct drm_file *file, - struct list_head *objects) + struct drm_file *file) { int i; @@ -98,7 +99,7 @@ eb_lookup_objects(struct eb_objects *eb, } drm_gem_object_reference(&obj->base); - list_add_tail(&obj->exec_list, objects); + list_add_tail(&obj->exec_list, &eb->objects); obj->exec_handle = exec[i].handle; obj->exec_entry = &exec[i]; @@ -129,6 +130,15 @@ eb_get_object(struct eb_objects *eb, unsigned long handle) static void eb_destroy(struct eb_objects *eb) { + while (!list_empty(&eb->objects)) { + struct drm_i915_gem_object *obj; + + obj = list_first_entry(&eb->objects, + struct drm_i915_gem_object, + exec_list); + list_del_init(&obj->exec_list); + drm_gem_object_unreference(&obj->base); + } kfree(eb); } @@ -328,8 +338,7 @@ i915_gem_execbuffer_relocate_object_slow(struct drm_i915_gem_object *obj, static int i915_gem_execbuffer_relocate(struct drm_device *dev, - struct eb_objects *eb, - struct list_head *objects) + struct eb_objects *eb) { struct drm_i915_gem_object *obj; int ret = 0; @@ -342,7 +351,7 @@ i915_gem_execbuffer_relocate(struct drm_device *dev, * lockdep complains vehemently. */ pagefault_disable(); - list_for_each_entry(obj, objects, exec_list) { + list_for_each_entry(obj, &eb->objects, exec_list) { ret = i915_gem_execbuffer_relocate_object(obj, eb); if (ret) break; @@ -531,7 +540,6 @@ static int i915_gem_execbuffer_relocate_slow(struct drm_device *dev, struct drm_file *file, struct intel_ring_buffer *ring, - struct list_head *objects, struct eb_objects *eb, struct drm_i915_gem_exec_object2 *exec, int count) @@ -542,8 +550,8 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, int i, total, ret; /* We may process another execbuffer during the unlock... */ - while (!list_empty(objects)) { - obj = list_first_entry(objects, + while (!list_empty(&eb->objects)) { + obj = list_first_entry(&eb->objects, struct drm_i915_gem_object, exec_list); list_del_init(&obj->exec_list); @@ -590,15 +598,15 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, /* reacquire the objects */ eb_reset(eb); - ret = eb_lookup_objects(eb, exec, count, file, objects); + ret = eb_lookup_objects(eb, exec, count, file); if (ret) goto err; - ret = i915_gem_execbuffer_reserve(ring, file, objects); + ret = i915_gem_execbuffer_reserve(ring, file, &eb->objects); if (ret) goto err; - list_for_each_entry(obj, objects, exec_list) { + list_for_each_entry(obj, &eb->objects, exec_list) { int offset = obj->exec_entry - exec; ret = i915_gem_execbuffer_relocate_object_slow(obj, eb, reloc + reloc_offset[offset]); @@ -756,7 +764,6 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, struct drm_i915_gem_exec_object2 *exec) { drm_i915_private_t *dev_priv = dev->dev_private; - struct list_head objects; struct eb_objects *eb; struct drm_i915_gem_object *batch_obj; struct drm_clip_rect *cliprects = NULL; @@ -899,28 +906,26 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, } /* Look up object handles */ - INIT_LIST_HEAD(&objects); - ret = eb_lookup_objects(eb, exec, args->buffer_count, file, &objects); + ret = eb_lookup_objects(eb, exec, args->buffer_count, file); if (ret) goto err; /* take note of the batch buffer before we might reorder the lists */ - batch_obj = list_entry(objects.prev, + batch_obj = list_entry(eb->objects.prev, struct drm_i915_gem_object, exec_list); /* Move the objects en-masse into the GTT, evicting if necessary. */ - ret = i915_gem_execbuffer_reserve(ring, file, &objects); + ret = i915_gem_execbuffer_reserve(ring, file, &eb->objects); if (ret) goto err; /* The objects are in their final locations, apply the relocations. */ - ret = i915_gem_execbuffer_relocate(dev, eb, &objects); + ret = i915_gem_execbuffer_relocate(dev, eb); if (ret) { if (ret == -EFAULT) { ret = i915_gem_execbuffer_relocate_slow(dev, file, ring, - &objects, eb, - exec, + eb, exec, args->buffer_count); BUG_ON(!mutex_is_locked(&dev->struct_mutex)); } @@ -943,7 +948,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, 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); + ret = i915_gem_execbuffer_move_to_gpu(ring, &eb->objects); if (ret) goto err; @@ -997,20 +1002,11 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, trace_i915_gem_ring_dispatch(ring, intel_ring_get_seqno(ring), flags); - i915_gem_execbuffer_move_to_active(&objects, ring); + i915_gem_execbuffer_move_to_active(&eb->objects, ring); i915_gem_execbuffer_retire_commands(dev, file, ring); err: eb_destroy(eb); - while (!list_empty(&objects)) { - struct drm_i915_gem_object *obj; - - obj = list_first_entry(&objects, - struct drm_i915_gem_object, - exec_list); - list_del_init(&obj->exec_list); - drm_gem_object_unreference(&obj->base); - } mutex_unlock(&dev->struct_mutex); -- cgit v1.2.3 From ed5982e6ce5f106abcbf071f80730db344a6da42 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 17 Jan 2013 22:23:36 +0100 Subject: drm/i915: Allow userspace to hint that the relocations were known Userspace is able to hint to the kernel that its command stream and auxiliary state buffers already hold the correct presumed addresses and so the relocation process may be skipped if the kernel does not need to move any buffers in preparation for the execbuffer. Thus for the common case where the allotment of buffers is static between batches, we can avoid the overhead of individually checking the relocation entries. Note that this requires userspace to supply the domain tracking and requests for workarounds itself that would otherwise be computed based upon the relocation entries. Using copywinwin10 as an example that is dependent upon emitting a lot of relocations (2 per operation), we see improvements of: c2d/gm45: 618000.0/sec to 632000.0/sec. i3-330m: 748000.0/sec to 830000.0/sec. (measured relative to a baseline with neither optimisations applied). Signed-off-by: Chris Wilson Reviewed-by: Imre Deak [danvet: Fixup merge conflict in userspace header due to different baseline trees.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 3 ++ drivers/gpu/drm/i915/i915_gem_execbuffer.c | 68 ++++++++++++++++++++---------- include/uapi/drm/i915_drm.h | 14 ++++++ 3 files changed, 62 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 6d8a1dc74934..a6e047d533ec 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -992,6 +992,9 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_HAS_PINNED_BATCHES: value = 1; break; + case I915_PARAM_HAS_EXEC_NO_RELOC: + value = 1; + 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 386677f8fd38..34f6cdffa9f8 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -373,7 +373,8 @@ need_reloc_mappable(struct drm_i915_gem_object *obj) static int i915_gem_execbuffer_reserve_object(struct drm_i915_gem_object *obj, - struct intel_ring_buffer *ring) + struct intel_ring_buffer *ring, + bool *need_reloc) { struct drm_i915_private *dev_priv = obj->base.dev->dev_private; struct drm_i915_gem_exec_object2 *entry = obj->exec_entry; @@ -414,7 +415,20 @@ i915_gem_execbuffer_reserve_object(struct drm_i915_gem_object *obj, obj->has_aliasing_ppgtt_mapping = 1; } - entry->offset = obj->gtt_offset; + if (entry->offset != obj->gtt_offset) { + entry->offset = obj->gtt_offset; + *need_reloc = true; + } + + if (entry->flags & EXEC_OBJECT_WRITE) { + obj->base.pending_read_domains = I915_GEM_DOMAIN_RENDER; + obj->base.pending_write_domain = I915_GEM_DOMAIN_RENDER; + } + + if (entry->flags & EXEC_OBJECT_NEEDS_GTT && + !obj->has_global_gtt_mapping) + i915_gem_gtt_bind_object(obj, obj->cache_level); + return 0; } @@ -440,7 +454,8 @@ i915_gem_execbuffer_unreserve_object(struct drm_i915_gem_object *obj) static int i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, struct drm_file *file, - struct list_head *objects) + struct list_head *objects, + bool *need_relocs) { struct drm_i915_gem_object *obj; struct list_head ordered_objects; @@ -468,7 +483,7 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, else list_move_tail(&obj->exec_list, &ordered_objects); - obj->base.pending_read_domains = 0; + obj->base.pending_read_domains = I915_GEM_GPU_DOMAINS & ~I915_GEM_DOMAIN_COMMAND; obj->base.pending_write_domain = 0; obj->pending_fenced_gpu_access = false; } @@ -508,7 +523,7 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, (need_mappable && !obj->map_and_fenceable)) ret = i915_gem_object_unbind(obj); else - ret = i915_gem_execbuffer_reserve_object(obj, ring); + ret = i915_gem_execbuffer_reserve_object(obj, ring, need_relocs); if (ret) goto err; } @@ -518,7 +533,7 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, if (obj->gtt_space) continue; - ret = i915_gem_execbuffer_reserve_object(obj, ring); + ret = i915_gem_execbuffer_reserve_object(obj, ring, need_relocs); if (ret) goto err; } @@ -538,16 +553,18 @@ err: /* Decrement pin count for bound objects */ static int i915_gem_execbuffer_relocate_slow(struct drm_device *dev, + struct drm_i915_gem_execbuffer2 *args, struct drm_file *file, struct intel_ring_buffer *ring, struct eb_objects *eb, - struct drm_i915_gem_exec_object2 *exec, - int count) + struct drm_i915_gem_exec_object2 *exec) { struct drm_i915_gem_relocation_entry *reloc; struct drm_i915_gem_object *obj; + bool need_relocs; int *reloc_offset; int i, total, ret; + int count = args->buffer_count; /* We may process another execbuffer during the unlock... */ while (!list_empty(&eb->objects)) { @@ -602,7 +619,8 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, if (ret) goto err; - ret = i915_gem_execbuffer_reserve(ring, file, &eb->objects); + need_relocs = (args->flags & I915_EXEC_NO_RELOC) == 0; + ret = i915_gem_execbuffer_reserve(ring, file, &eb->objects, &need_relocs); if (ret) goto err; @@ -660,6 +678,9 @@ i915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring, static bool i915_gem_check_execbuffer(struct drm_i915_gem_execbuffer2 *exec) { + if (exec->flags & __I915_EXEC_UNKNOWN_FLAGS) + return false; + return ((exec->batch_start_offset | exec->batch_len) & 0x7) == 0; } @@ -673,6 +694,9 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec, char __user *ptr = (char __user *)(uintptr_t)exec[i].relocs_ptr; int length; /* limited by fault_in_pages_readable() */ + if (exec[i].flags & __EXEC_OBJECT_UNKNOWN_FLAGS) + return -EINVAL; + /* First check for malicious input causing overflow */ if (exec[i].relocation_count > INT_MAX / sizeof(struct drm_i915_gem_relocation_entry)) @@ -680,9 +704,6 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec, length = exec[i].relocation_count * sizeof(struct drm_i915_gem_relocation_entry); - if (!access_ok(VERIFY_READ, ptr, length)) - return -EFAULT; - /* we may also need to update the presumed offsets */ if (!access_ok(VERIFY_WRITE, ptr, length)) return -EFAULT; @@ -704,8 +725,10 @@ i915_gem_execbuffer_move_to_active(struct list_head *objects, u32 old_read = obj->base.read_domains; u32 old_write = obj->base.write_domain; - obj->base.read_domains = obj->base.pending_read_domains; obj->base.write_domain = obj->base.pending_write_domain; + if (obj->base.write_domain == 0) + obj->base.pending_read_domains |= obj->base.read_domains; + obj->base.read_domains = obj->base.pending_read_domains; obj->fenced_gpu_access = obj->pending_fenced_gpu_access; i915_gem_object_move_to_active(obj, ring); @@ -770,14 +793,12 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, struct intel_ring_buffer *ring; u32 ctx_id = i915_execbuffer2_get_context_id(*args); u32 exec_start, exec_len; - u32 mask; - u32 flags; + u32 mask, flags; int ret, mode, i; + bool need_relocs; - if (!i915_gem_check_execbuffer(args)) { - DRM_DEBUG("execbuf with invalid offset/length\n"); + if (!i915_gem_check_execbuffer(args)) return -EINVAL; - } ret = validate_exec_list(exec, args->buffer_count); if (ret) @@ -916,17 +937,18 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, exec_list); /* Move the objects en-masse into the GTT, evicting if necessary. */ - ret = i915_gem_execbuffer_reserve(ring, file, &eb->objects); + need_relocs = (args->flags & I915_EXEC_NO_RELOC) == 0; + ret = i915_gem_execbuffer_reserve(ring, file, &eb->objects, &need_relocs); if (ret) goto err; /* The objects are in their final locations, apply the relocations. */ - ret = i915_gem_execbuffer_relocate(dev, eb); + if (need_relocs) + ret = i915_gem_execbuffer_relocate(dev, eb); if (ret) { if (ret == -EFAULT) { - ret = i915_gem_execbuffer_relocate_slow(dev, file, ring, - eb, exec, - args->buffer_count); + ret = i915_gem_execbuffer_relocate_slow(dev, args, file, ring, + eb, exec); BUG_ON(!mutex_is_locked(&dev->struct_mutex)); } if (ret) diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index c4d2e9c74002..2430b6ad6a85 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -308,6 +308,7 @@ typedef struct drm_i915_irq_wait { #define I915_PARAM_RSVD_FOR_FUTURE_USE 22 #define I915_PARAM_HAS_SECURE_BATCHES 23 #define I915_PARAM_HAS_PINNED_BATCHES 24 +#define I915_PARAM_HAS_EXEC_NO_RELOC 25 typedef struct drm_i915_getparam { int param; @@ -628,7 +629,11 @@ struct drm_i915_gem_exec_object2 { __u64 offset; #define EXEC_OBJECT_NEEDS_FENCE (1<<0) +#define EXEC_OBJECT_NEEDS_GTT (1<<1) +#define EXEC_OBJECT_WRITE (1<<2) +#define __EXEC_OBJECT_UNKNOWN_FLAGS -(EXEC_OBJECT_WRITE<<1) __u64 flags; + __u64 rsvd1; __u64 rsvd2; }; @@ -687,6 +692,15 @@ struct drm_i915_gem_execbuffer2 { */ #define I915_EXEC_IS_PINNED (1<<10) +/** Provide a hint to the kernel that the command stream and auxilliary + * state buffers already holds the correct presumed addresses and so the + * relocation process may be skipped if no buffers need to be moved in + * preparation for the execbuffer. + */ +#define I915_EXEC_NO_RELOC (1<<11) + +#define __I915_EXEC_UNKNOWN_FLAGS -(I915_EXEC_NO_RELOC<<1) + #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 From eef90ccb8a4d50b219a95cc53878ebb007315b32 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 8 Jan 2013 10:53:17 +0000 Subject: drm/i915: Use the reloc.handle as an index into the execbuffer array Using copywinwin10 as an example that is dependent upon emitting a lot of relocations (2 per operation), we see improvements of: c2d/gm45: 618000.0/sec to 623000.0/sec. i3-330m: 748000.0/sec to 789000.0/sec. (measured relative to a baseline with neither optimisations applied). Signed-off-by: Chris Wilson Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 3 + drivers/gpu/drm/i915/i915_gem_execbuffer.c | 100 ++++++++++++++++++----------- include/uapi/drm/i915_drm.h | 8 ++- 3 files changed, 71 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index a6e047d533ec..442118293883 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -995,6 +995,9 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_HAS_EXEC_NO_RELOC: value = 1; break; + case I915_PARAM_HAS_EXEC_HANDLE_LUT: + value = 1; + 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 34f6cdffa9f8..f5a11ecf5494 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -36,24 +36,40 @@ struct eb_objects { struct list_head objects; int and; - struct hlist_head buckets[0]; + union { + struct drm_i915_gem_object *lut[0]; + struct hlist_head buckets[0]; + }; }; static struct eb_objects * -eb_create(int size) +eb_create(struct drm_i915_gem_execbuffer2 *args) { - struct eb_objects *eb; - int count = PAGE_SIZE / sizeof(struct hlist_head) / 2; - BUILD_BUG_ON(!is_power_of_2(PAGE_SIZE / sizeof(struct hlist_head))); - while (count > size) - count >>= 1; - eb = kzalloc(count*sizeof(struct hlist_head) + - sizeof(struct eb_objects), - GFP_KERNEL); - if (eb == NULL) - return eb; - - eb->and = count - 1; + struct eb_objects *eb = NULL; + + if (args->flags & I915_EXEC_HANDLE_LUT) { + int size = args->buffer_count; + size *= sizeof(struct drm_i915_gem_object *); + size += sizeof(struct eb_objects); + eb = kmalloc(size, GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY); + } + + if (eb == NULL) { + int size = args->buffer_count; + int count = PAGE_SIZE / sizeof(struct hlist_head) / 2; + BUILD_BUG_ON(!is_power_of_2(PAGE_SIZE / sizeof(struct hlist_head))); + while (count > 2*size) + count >>= 1; + eb = kzalloc(count*sizeof(struct hlist_head) + + sizeof(struct eb_objects), + GFP_TEMPORARY); + if (eb == NULL) + return eb; + + eb->and = count - 1; + } else + eb->and = -args->buffer_count; + INIT_LIST_HEAD(&eb->objects); return eb; } @@ -61,26 +77,20 @@ eb_create(int size) static void eb_reset(struct eb_objects *eb) { - memset(eb->buckets, 0, (eb->and+1)*sizeof(struct hlist_head)); -} - -static void -eb_add_object(struct eb_objects *eb, struct drm_i915_gem_object *obj) -{ - hlist_add_head(&obj->exec_node, - &eb->buckets[obj->exec_handle & eb->and]); + if (eb->and >= 0) + memset(eb->buckets, 0, (eb->and+1)*sizeof(struct hlist_head)); } static int eb_lookup_objects(struct eb_objects *eb, struct drm_i915_gem_exec_object2 *exec, - int count, + const struct drm_i915_gem_execbuffer2 *args, struct drm_file *file) { int i; spin_lock(&file->table_lock); - for (i = 0; i < count; i++) { + for (i = 0; i < args->buffer_count; i++) { struct drm_i915_gem_object *obj; obj = to_intel_bo(idr_find(&file->object_idr, exec[i].handle)); @@ -101,9 +111,15 @@ eb_lookup_objects(struct eb_objects *eb, drm_gem_object_reference(&obj->base); list_add_tail(&obj->exec_list, &eb->objects); - obj->exec_handle = exec[i].handle; obj->exec_entry = &exec[i]; - eb_add_object(eb, obj); + if (eb->and < 0) { + eb->lut[i] = obj; + } else { + uint32_t handle = args->flags & I915_EXEC_HANDLE_LUT ? i : exec[i].handle; + obj->exec_handle = handle; + hlist_add_head(&obj->exec_node, + &eb->buckets[handle & eb->and]); + } } spin_unlock(&file->table_lock); @@ -113,18 +129,24 @@ eb_lookup_objects(struct eb_objects *eb, static struct drm_i915_gem_object * eb_get_object(struct eb_objects *eb, unsigned long handle) { - struct hlist_head *head; - struct hlist_node *node; - struct drm_i915_gem_object *obj; + if (eb->and < 0) { + if (handle >= -eb->and) + return NULL; + return eb->lut[handle]; + } else { + struct hlist_head *head; + struct hlist_node *node; - head = &eb->buckets[handle & eb->and]; - hlist_for_each(node, head) { - obj = hlist_entry(node, struct drm_i915_gem_object, exec_node); - if (obj->exec_handle == handle) - return obj; - } + head = &eb->buckets[handle & eb->and]; + hlist_for_each(node, head) { + struct drm_i915_gem_object *obj; - return NULL; + obj = hlist_entry(node, struct drm_i915_gem_object, exec_node); + if (obj->exec_handle == handle) + return obj; + } + return NULL; + } } static void @@ -615,7 +637,7 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, /* reacquire the objects */ eb_reset(eb); - ret = eb_lookup_objects(eb, exec, count, file); + ret = eb_lookup_objects(eb, exec, args, file); if (ret) goto err; @@ -919,7 +941,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, goto pre_mutex_err; } - eb = eb_create(args->buffer_count); + eb = eb_create(args); if (eb == NULL) { mutex_unlock(&dev->struct_mutex); ret = -ENOMEM; @@ -927,7 +949,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, } /* Look up object handles */ - ret = eb_lookup_objects(eb, exec, args->buffer_count, file); + ret = eb_lookup_objects(eb, exec, args, file); if (ret) goto err; diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 2430b6ad6a85..07d59419fe6b 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -309,6 +309,7 @@ typedef struct drm_i915_irq_wait { #define I915_PARAM_HAS_SECURE_BATCHES 23 #define I915_PARAM_HAS_PINNED_BATCHES 24 #define I915_PARAM_HAS_EXEC_NO_RELOC 25 +#define I915_PARAM_HAS_EXEC_HANDLE_LUT 26 typedef struct drm_i915_getparam { int param; @@ -699,7 +700,12 @@ struct drm_i915_gem_execbuffer2 { */ #define I915_EXEC_NO_RELOC (1<<11) -#define __I915_EXEC_UNKNOWN_FLAGS -(I915_EXEC_NO_RELOC<<1) +/** Use the reloc.handle as an index into the exec object array rather + * than as the per-file handle. + */ +#define I915_EXEC_HANDLE_LUT (1<<12) + +#define __I915_EXEC_UNKNOWN_FLAGS -(I915_EXEC_HANDLE_LUT<<1) #define I915_EXEC_CONTEXT_ID_MASK (0xffffffff) #define i915_execbuffer2_set_context_id(eb2, context) \ -- cgit v1.2.3 From c70af1e4b60a1853feeaae2bf6e2aa98de68acd9 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 16 Jan 2013 19:59:03 +0200 Subject: drm/i915: Fix SPRITE0_FLIP_DONE_INT_EN_VLV and SPRITE0_FLIPDONE_INT_STATUS_VLV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix up some copypaste errors in the PIPESTAT register for VLV. SPRITE0_FLIP_DONE_INT_EN_VLV is bit 22, not bit 26. SPRITE0_FLIPDONE_INT_STATUS_VLV is bit 14, not bit 15. Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 3b039f4268e3..4c33bd2bd4e6 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2672,7 +2672,7 @@ #define PIPE_VSYNC_INTERRUPT_ENABLE (1UL<<25) #define PIPE_DISPLAY_LINE_COMPARE_ENABLE (1UL<<24) #define PIPE_DPST_EVENT_ENABLE (1UL<<23) -#define SPRITE0_FLIP_DONE_INT_EN_VLV (1UL<<26) +#define SPRITE0_FLIP_DONE_INT_EN_VLV (1UL<<22) #define PIPE_LEGACY_BLC_EVENT_ENABLE (1UL<<22) #define PIPE_ODD_FIELD_INTERRUPT_ENABLE (1UL<<21) #define PIPE_EVEN_FIELD_INTERRUPT_ENABLE (1UL<<20) @@ -2682,7 +2682,7 @@ #define PIPEA_HBLANK_INT_EN_VLV (1UL<<16) #define PIPE_OVERLAY_UPDATED_ENABLE (1UL<<16) #define SPRITE1_FLIPDONE_INT_STATUS_VLV (1UL<<15) -#define SPRITE0_FLIPDONE_INT_STATUS_VLV (1UL<<15) +#define SPRITE0_FLIPDONE_INT_STATUS_VLV (1UL<<14) #define PIPE_CRC_ERROR_INTERRUPT_STATUS (1UL<<13) #define PIPE_CRC_DONE_INTERRUPT_STATUS (1UL<<12) #define PIPE_GMBUS_INTERRUPT_STATUS (1UL<<11) -- cgit v1.2.3 From c1fc6521efbed7d3aca4e37ac61b23300f161cd1 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 17 Jan 2013 12:45:12 -0800 Subject: drm/i915: Kill gtt_end It's duplicated in the more useful gtt_total. Reviewed-by: Rodrigo Vivi Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 1 - drivers/gpu/drm/i915/i915_gem_gtt.c | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 8f5b2816fc03..d58189e60844 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -801,7 +801,6 @@ typedef struct drm_i915_private { /** Usable portion of the GTT for GEM */ unsigned long gtt_start; unsigned long gtt_mappable_end; - unsigned long gtt_end; unsigned long stolen_base; /* limited to low memory (32-bit) */ /** "Graphics Stolen Memory" holds the global PTEs */ diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index a4af0f79e972..3ffcf528f166 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -394,7 +394,7 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev) /* First fill our portion of the GTT with scratch pages */ i915_ggtt_clear_range(dev, dev_priv->mm.gtt_start / PAGE_SIZE, - (dev_priv->mm.gtt_end - dev_priv->mm.gtt_start) / PAGE_SIZE); + dev_priv->mm.gtt_total / PAGE_SIZE); list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) { i915_gem_clflush_object(obj); @@ -556,7 +556,6 @@ void i915_gem_setup_global_gtt(struct drm_device *dev, dev_priv->mm.gtt_start = start; dev_priv->mm.gtt_mappable_end = mappable_end; - dev_priv->mm.gtt_end = end; dev_priv->mm.gtt_total = end - start; dev_priv->mm.mappable_gtt_total = min(end, mappable_end) - start; -- cgit v1.2.3 From 35451cb6fbd3655ede4694b11761a587d400d647 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 17 Jan 2013 12:45:13 -0800 Subject: drm/i915: Mappable_end can't ever be > end Both DRI1 and DRI2 can never specify a mappable size which goes past the GTT size. Don't pretend otherwise. Reviewed-by: Rodrigo Vivi Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 3ffcf528f166..e66c70008767 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -536,6 +536,8 @@ void i915_gem_setup_global_gtt(struct drm_device *dev, struct drm_i915_gem_object *obj; unsigned long hole_start, hole_end; + BUG_ON(mappable_end > end); + /* Subtract the guard page ... */ drm_mm_init(&dev_priv->mm.gtt_space, start, end - start - PAGE_SIZE); if (!HAS_LLC(dev)) @@ -557,7 +559,7 @@ void i915_gem_setup_global_gtt(struct drm_device *dev, dev_priv->mm.gtt_start = start; dev_priv->mm.gtt_mappable_end = mappable_end; dev_priv->mm.gtt_total = end - start; - dev_priv->mm.mappable_gtt_total = min(end, mappable_end) - start; + dev_priv->mm.mappable_gtt_total = mappable_end - start; /* Clear any non-preallocated blocks */ drm_mm_for_each_hole(entry, &dev_priv->mm.gtt_space, -- cgit v1.2.3 From 00fc2c3c53d7bfc9a29e5f4bdf2677f0c399f3bc Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 17 Jan 2013 12:45:14 -0800 Subject: drm/i915: Remove gtt_mappable_total With the assertion from the previous patch in place, it should be safe to get rid gtt_mappable_total. Keeps things saner to not have to track the same info in two places. In order to keep the diff as simple as possible and keep with the existing gtt_setup semantics we opt to keep gtt_mappable_end. It's not as consistent with the 'total' used in the previous patch, but that can be fixed later. Reviewed-by: Rodrigo Vivi [Ben modified commit message] Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 3 ++- drivers/gpu/drm/i915/i915_drv.h | 1 - drivers/gpu/drm/i915/i915_gem_gtt.c | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index f94418bd1ed2..35d326d70fab 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -259,7 +259,8 @@ static int i915_gem_object_info(struct seq_file *m, void* data) count, size); seq_printf(m, "%zu [%zu] gtt total\n", - dev_priv->mm.gtt_total, dev_priv->mm.mappable_gtt_total); + dev_priv->mm.gtt_total, + dev_priv->mm.gtt_mappable_end - dev_priv->mm.gtt_start); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index d58189e60844..6e2c10b65c8d 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -886,7 +886,6 @@ typedef struct drm_i915_private { /* accounting, useful for userland debugging */ size_t gtt_total; - size_t mappable_gtt_total; size_t object_memory; u32 object_count; } mm; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index e66c70008767..8c42ddd467b6 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -559,7 +559,6 @@ void i915_gem_setup_global_gtt(struct drm_device *dev, dev_priv->mm.gtt_start = start; dev_priv->mm.gtt_mappable_end = mappable_end; dev_priv->mm.gtt_total = end - start; - dev_priv->mm.mappable_gtt_total = mappable_end - start; /* Clear any non-preallocated blocks */ drm_mm_for_each_hole(entry, &dev_priv->mm.gtt_space, -- cgit v1.2.3 From cd323ac0eb433b14cbb270bfc5a82f308f2662de Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 8 Nov 2012 08:01:39 -0600 Subject: rbd: end request on error in rbd_do_request() caller Only one of the three callers of rbd_do_request() provide a collection structure to aggregate status. If an error occurs in rbd_do_request(), have the caller take care of calling rbd_coll_end_req() if necessary in that one spot. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 738d1e4c0ab5..21468d0b2792 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1126,12 +1126,8 @@ static int rbd_do_request(struct request *rq, struct ceph_osd_client *osdc; rbd_req = kzalloc(sizeof(*rbd_req), GFP_NOIO); - if (!rbd_req) { - if (coll) - rbd_coll_end_req_index(rq, coll, coll_index, - (s32)-ENOMEM, len); + if (!rbd_req) return -ENOMEM; - } if (coll) { rbd_req->coll = coll; @@ -1206,7 +1202,6 @@ done_err: bio_chain_put(rbd_req->bio); ceph_osdc_put_request(osd_req); done_pages: - rbd_coll_end_req(rbd_req, (s32)ret, len); kfree(rbd_req); return ret; } @@ -1359,7 +1354,9 @@ static int rbd_do_op(struct request *rq, ops, coll, coll_index, rbd_req_cb, 0, NULL); - + if (ret < 0) + rbd_coll_end_req_index(rq, coll, coll_index, + (s32)ret, seg_len); rbd_destroy_ops(ops); done: kfree(seg_name); -- cgit v1.2.3 From 5d4545aef561ad47f91bcf75814af20c104b5a9e Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 17 Jan 2013 12:45:15 -0800 Subject: drm/i915: Create a gtt structure The purpose of the gtt structure is to help isolate our gtt specific properties from the rest of the code (in doing so it help us finish the isolation from the AGP connection). The following members are pulled out (and renamed): gtt_start gtt_total gtt_mappable_end gtt_mappable gtt_base_addr gsm The gtt structure will serve as a nice place to put gen specific gtt routines in upcoming patches. As far as what else I feel belongs in this structure: it is meant to encapsulate the GTT's physical properties. This is why I've not added fields which track various drm_mm properties, or things like gtt_mtrr (which is itself a pretty transient field). Reviewed-by: Rodrigo Vivi [Ben modified commit messages] Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 4 ++-- drivers/gpu/drm/i915/i915_dma.c | 20 ++++++++++---------- drivers/gpu/drm/i915/i915_drv.h | 29 +++++++++++++++++++++-------- drivers/gpu/drm/i915/i915_gem.c | 14 +++++++------- drivers/gpu/drm/i915/i915_gem_evict.c | 2 +- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 2 +- drivers/gpu/drm/i915/i915_gem_gtt.c | 24 ++++++++++++------------ drivers/gpu/drm/i915/i915_gem_tiling.c | 2 +- drivers/gpu/drm/i915/i915_irq.c | 4 ++-- drivers/gpu/drm/i915/intel_display.c | 2 +- drivers/gpu/drm/i915/intel_fb.c | 2 +- drivers/gpu/drm/i915/intel_overlay.c | 4 ++-- 12 files changed, 61 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 35d326d70fab..773b23ecc83b 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -259,8 +259,8 @@ static int i915_gem_object_info(struct seq_file *m, void* data) count, size); seq_printf(m, "%zu [%zu] gtt total\n", - dev_priv->mm.gtt_total, - dev_priv->mm.gtt_mappable_end - dev_priv->mm.gtt_start); + dev_priv->gtt.total, + dev_priv->gtt.mappable_end - dev_priv->gtt.start); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 442118293883..bb622135798a 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1076,7 +1076,7 @@ static int i915_set_status_page(struct drm_device *dev, void *data, ring->status_page.gfx_addr = hws->addr & (0x1ffff<<12); dev_priv->dri1.gfx_hws_cpu_addr = - ioremap_wc(dev_priv->mm.gtt_base_addr + hws->addr, 4096); + ioremap_wc(dev_priv->gtt.mappable_base + hws->addr, 4096); if (dev_priv->dri1.gfx_hws_cpu_addr == NULL) { i915_dma_cleanup(dev); ring->status_page.gfx_addr = 0; @@ -1543,17 +1543,17 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) } aperture_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; - dev_priv->mm.gtt_base_addr = dev_priv->mm.gtt->gma_bus_addr; + dev_priv->gtt.mappable_base = dev_priv->mm.gtt->gma_bus_addr; - dev_priv->mm.gtt_mapping = - io_mapping_create_wc(dev_priv->mm.gtt_base_addr, + dev_priv->gtt.mappable = + io_mapping_create_wc(dev_priv->gtt.mappable_base, aperture_size); - if (dev_priv->mm.gtt_mapping == NULL) { + if (dev_priv->gtt.mappable == NULL) { ret = -EIO; goto out_rmmap; } - i915_mtrr_setup(dev_priv, dev_priv->mm.gtt_base_addr, + i915_mtrr_setup(dev_priv, dev_priv->gtt.mappable_base, aperture_size); /* The i915 workqueue is primarily used for batched retirement of @@ -1658,11 +1658,11 @@ out_gem_unload: out_mtrrfree: if (dev_priv->mm.gtt_mtrr >= 0) { mtrr_del(dev_priv->mm.gtt_mtrr, - dev_priv->mm.gtt_base_addr, + dev_priv->gtt.mappable_base, aperture_size); dev_priv->mm.gtt_mtrr = -1; } - io_mapping_free(dev_priv->mm.gtt_mapping); + io_mapping_free(dev_priv->gtt.mappable); out_rmmap: pci_iounmap(dev->pdev, dev_priv->regs); put_gmch: @@ -1696,10 +1696,10 @@ int i915_driver_unload(struct drm_device *dev) /* Cancel the retire work handler, which should be idle now. */ cancel_delayed_work_sync(&dev_priv->mm.retire_work); - io_mapping_free(dev_priv->mm.gtt_mapping); + io_mapping_free(dev_priv->gtt.mappable); if (dev_priv->mm.gtt_mtrr >= 0) { mtrr_del(dev_priv->mm.gtt_mtrr, - dev_priv->mm.gtt_base_addr, + dev_priv->gtt.mappable_base, dev_priv->mm.gtt->gtt_mappable_entries * PAGE_SIZE); dev_priv->mm.gtt_mtrr = -1; } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 6e2c10b65c8d..f3f2e5e1393f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -364,6 +364,25 @@ struct intel_device_info { u8 has_llc:1; }; +/* The Graphics Translation Table is the way in which GEN hardware translates a + * Graphics Virtual Address into a Physical Address. In addition to the normal + * collateral associated with any va->pa translations GEN hardware also has a + * portion of the GTT which can be mapped by the CPU and remain both coherent + * and correct (in cases like swizzling). That region is referred to as GMADR in + * the spec. + */ +struct i915_gtt { + unsigned long start; /* Start offset of used GTT */ + size_t total; /* Total size GTT can map */ + + unsigned long mappable_end; /* End offset that we can CPU map */ + struct io_mapping *mappable; /* Mapping to our CPU mappable region */ + phys_addr_t mappable_base; /* PA of our GMADR */ + + /** "Graphics Stolen Memory" holds the global PTEs */ + void __iomem *gsm; +}; + #define I915_PPGTT_PD_ENTRIES 512 #define I915_PPGTT_PT_ENTRIES 1024 struct i915_hw_ppgtt { @@ -781,6 +800,8 @@ typedef struct drm_i915_private { /* Register state */ bool modeset_on_lid; + struct i915_gtt gtt; + struct { /** Bridge to intel-gtt-ko */ struct intel_gtt *gtt; @@ -799,15 +820,8 @@ typedef struct drm_i915_private { struct list_head unbound_list; /** Usable portion of the GTT for GEM */ - unsigned long gtt_start; - unsigned long gtt_mappable_end; unsigned long stolen_base; /* limited to low memory (32-bit) */ - /** "Graphics Stolen Memory" holds the global PTEs */ - void __iomem *gsm; - - struct io_mapping *gtt_mapping; - phys_addr_t gtt_base_addr; int gtt_mtrr; /** PPGTT used for aliasing the PPGTT with the GTT */ @@ -885,7 +899,6 @@ typedef struct drm_i915_private { struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT]; /* accounting, useful for userland debugging */ - size_t gtt_total; size_t object_memory; u32 object_count; } mm; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index a2bb18914329..51fdf16181a7 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -186,7 +186,7 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, pinned += obj->gtt_space->size; mutex_unlock(&dev->struct_mutex); - args->aper_size = dev_priv->mm.gtt_total; + args->aper_size = dev_priv->gtt.total; args->aper_available_size = args->aper_size - pinned; return 0; @@ -637,7 +637,7 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev, * source page isn't available. Return the error and we'll * retry in the slow path. */ - if (fast_user_write(dev_priv->mm.gtt_mapping, page_base, + if (fast_user_write(dev_priv->gtt.mappable, page_base, page_offset, user_data, page_length)) { ret = -EFAULT; goto out_unpin; @@ -1362,7 +1362,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) obj->fault_mappable = true; - pfn = ((dev_priv->mm.gtt_base_addr + obj->gtt_offset) >> PAGE_SHIFT) + + pfn = ((dev_priv->gtt.mappable_base + obj->gtt_offset) >> PAGE_SHIFT) + page_offset; /* Finally, remap it using the new GTT offset */ @@ -1544,7 +1544,7 @@ i915_gem_mmap_gtt(struct drm_file *file, goto unlock; } - if (obj->base.size > dev_priv->mm.gtt_mappable_end) { + if (obj->base.size > dev_priv->gtt.mappable_end) { ret = -E2BIG; goto out; } @@ -2910,7 +2910,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, * before evicting everything in a vain attempt to find space. */ if (obj->base.size > - (map_and_fenceable ? dev_priv->mm.gtt_mappable_end : dev_priv->mm.gtt_total)) { + (map_and_fenceable ? dev_priv->gtt.mappable_end : dev_priv->gtt.total)) { DRM_ERROR("Attempting to bind an object larger than the aperture\n"); return -E2BIG; } @@ -2931,7 +2931,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, if (map_and_fenceable) ret = drm_mm_insert_node_in_range_generic(&dev_priv->mm.gtt_space, node, size, alignment, obj->cache_level, - 0, dev_priv->mm.gtt_mappable_end); + 0, dev_priv->gtt.mappable_end); else ret = drm_mm_insert_node_generic(&dev_priv->mm.gtt_space, node, size, alignment, obj->cache_level); @@ -2971,7 +2971,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, (node->start & (fence_alignment - 1)) == 0; mappable = - obj->gtt_offset + obj->base.size <= dev_priv->mm.gtt_mappable_end; + obj->gtt_offset + obj->base.size <= dev_priv->gtt.mappable_end; obj->map_and_fenceable = mappable && fenceable; diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index 776a3225184c..c86d5d9356fd 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -80,7 +80,7 @@ i915_gem_evict_something(struct drm_device *dev, int min_size, if (mappable) drm_mm_init_scan_with_range(&dev_priv->mm.gtt_space, min_size, alignment, cache_level, - 0, dev_priv->mm.gtt_mappable_end); + 0, dev_priv->gtt.mappable_end); else drm_mm_init_scan(&dev_priv->mm.gtt_space, min_size, alignment, cache_level); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index f5a11ecf5494..27269103b621 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -281,7 +281,7 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, /* Map the page containing the relocation we're going to perform. */ reloc->offset += obj->gtt_offset; - reloc_page = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, + reloc_page = io_mapping_map_atomic_wc(dev_priv->gtt.mappable, reloc->offset & PAGE_MASK); reloc_entry = (uint32_t __iomem *) (reloc_page + (reloc->offset & ~PAGE_MASK)); diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 8c42ddd467b6..61bfb12e1016 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -290,7 +290,7 @@ void i915_gem_init_ppgtt(struct drm_device *dev) return; - pd_addr = (gtt_pte_t __iomem*)dev_priv->mm.gsm + ppgtt->pd_offset/sizeof(gtt_pte_t); + pd_addr = (gtt_pte_t __iomem*)dev_priv->gtt.gsm + ppgtt->pd_offset/sizeof(gtt_pte_t); for (i = 0; i < ppgtt->num_pd_entries; i++) { dma_addr_t pt_addr; @@ -367,7 +367,7 @@ static void i915_ggtt_clear_range(struct drm_device *dev, { struct drm_i915_private *dev_priv = dev->dev_private; gtt_pte_t scratch_pte; - gtt_pte_t __iomem *gtt_base = (gtt_pte_t __iomem *) dev_priv->mm.gsm + first_entry; + gtt_pte_t __iomem *gtt_base = (gtt_pte_t __iomem *) dev_priv->gtt.gsm + first_entry; const int max_entries = dev_priv->mm.gtt->gtt_total_entries - first_entry; int i; @@ -393,8 +393,8 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev) struct drm_i915_gem_object *obj; /* First fill our portion of the GTT with scratch pages */ - i915_ggtt_clear_range(dev, dev_priv->mm.gtt_start / PAGE_SIZE, - dev_priv->mm.gtt_total / PAGE_SIZE); + i915_ggtt_clear_range(dev, dev_priv->gtt.start / PAGE_SIZE, + dev_priv->gtt.total / PAGE_SIZE); list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) { i915_gem_clflush_object(obj); @@ -433,7 +433,7 @@ static void gen6_ggtt_bind_object(struct drm_i915_gem_object *obj, 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 = - (gtt_pte_t __iomem *)dev_priv->mm.gsm + first_entry; + (gtt_pte_t __iomem *)dev_priv->gtt.gsm + first_entry; int unused, i = 0; unsigned int len, m = 0; dma_addr_t addr; @@ -556,9 +556,9 @@ void i915_gem_setup_global_gtt(struct drm_device *dev, obj->has_global_gtt_mapping = 1; } - dev_priv->mm.gtt_start = start; - dev_priv->mm.gtt_mappable_end = mappable_end; - dev_priv->mm.gtt_total = end - start; + dev_priv->gtt.start = start; + dev_priv->gtt.mappable_end = mappable_end; + dev_priv->gtt.total = end - start; /* Clear any non-preallocated blocks */ drm_mm_for_each_hole(entry, &dev_priv->mm.gtt_space, @@ -752,9 +752,9 @@ int i915_gem_gtt_init(struct drm_device *dev) goto err_out; } - dev_priv->mm.gsm = ioremap_wc(gtt_bus_addr, - dev_priv->mm.gtt->gtt_total_entries * sizeof(gtt_pte_t)); - if (!dev_priv->mm.gsm) { + dev_priv->gtt.gsm = ioremap_wc(gtt_bus_addr, + dev_priv->mm.gtt->gtt_total_entries * sizeof(gtt_pte_t)); + if (!dev_priv->gtt.gsm) { DRM_ERROR("Failed to map the gtt page table\n"); teardown_scratch_page(dev); ret = -ENOMEM; @@ -778,7 +778,7 @@ err_out: void i915_gem_gtt_fini(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - iounmap(dev_priv->mm.gsm); + iounmap(dev_priv->gtt.gsm); teardown_scratch_page(dev); if (INTEL_INFO(dev)->gen < 6) intel_gmch_remove(); diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index e76f0d8470a1..abcba2f5a788 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -357,7 +357,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, obj->map_and_fenceable = obj->gtt_space == NULL || - (obj->gtt_offset + obj->base.size <= dev_priv->mm.gtt_mappable_end && + (obj->gtt_offset + obj->base.size <= dev_priv->gtt.mappable_end && i915_gem_object_fence_ok(obj, args->tiling_mode)); /* Rebind if we need a change of alignment */ diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 202813128441..cc49a6ddc052 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -939,7 +939,7 @@ i915_error_object_create(struct drm_i915_private *dev_priv, goto unwind; local_irq_save(flags); - if (reloc_offset < dev_priv->mm.gtt_mappable_end && + if (reloc_offset < dev_priv->gtt.mappable_end && src->has_global_gtt_mapping) { void __iomem *s; @@ -948,7 +948,7 @@ i915_error_object_create(struct drm_i915_private *dev_priv, * captures what the GPU read. */ - s = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, + s = io_mapping_map_atomic_wc(dev_priv->gtt.mappable, reloc_offset); memcpy_fromio(d, s, PAGE_SIZE); io_mapping_unmap_atomic(s); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 40b6b5e9d6ef..e4c5067a54d3 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8687,7 +8687,7 @@ void intel_modeset_init(struct drm_device *dev) dev->mode_config.max_width = 8192; dev->mode_config.max_height = 8192; } - dev->mode_config.fb_base = dev_priv->mm.gtt_base_addr; + dev->mode_config.fb_base = dev_priv->gtt.mappable_base; DRM_DEBUG_KMS("%d display pipe%s available.\n", dev_priv->num_pipe, dev_priv->num_pipe > 1 ? "s" : ""); diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 71d55801c0d9..ce02af8ca96e 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -142,7 +142,7 @@ static int intelfb_create(struct intel_fbdev *ifbdev, info->fix.smem_len = size; info->screen_base = - ioremap_wc(dev_priv->mm.gtt_base_addr + obj->gtt_offset, + ioremap_wc(dev_priv->gtt.mappable_base + obj->gtt_offset, size); if (!info->screen_base) { ret = -ENOSPC; diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index fabe0acf808d..ba978bf93a2e 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -195,7 +195,7 @@ intel_overlay_map_regs(struct intel_overlay *overlay) if (OVERLAY_NEEDS_PHYSICAL(overlay->dev)) regs = (struct overlay_registers __iomem *)overlay->reg_bo->phys_obj->handle->vaddr; else - regs = io_mapping_map_wc(dev_priv->mm.gtt_mapping, + regs = io_mapping_map_wc(dev_priv->gtt.mappable, overlay->reg_bo->gtt_offset); return regs; @@ -1434,7 +1434,7 @@ intel_overlay_map_regs_atomic(struct intel_overlay *overlay) regs = (struct overlay_registers __iomem *) overlay->reg_bo->phys_obj->handle->vaddr; else - regs = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, + regs = io_mapping_map_atomic_wc(dev_priv->gtt.mappable, overlay->reg_bo->gtt_offset); return regs; -- cgit v1.2.3 From dabb7a91ae6442d642a52e0de22d781a8ad2af12 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 17 Jan 2013 12:45:16 -0800 Subject: drm/i915: Remove use on gma_bus_addr on gen6+ We have enough info to not use the intel_gtt bridge stuff. v2: Move setup of mappable_base above the legacy init stuff because we still need that on older platforms. (Daniel) v3: Remove the dev_priv hunk which was rebased in by accident Reviewed-by: Rodrigo Vivi (v2) Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 3 +-- drivers/gpu/drm/i915/i915_gem_gtt.c | 3 ++- drivers/gpu/drm/i915/intel_ringbuffer.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index bb622135798a..468d2a0fc378 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1426,7 +1426,7 @@ static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) if (!ap) return; - ap->ranges[0].base = dev_priv->mm.gtt->gma_bus_addr; + ap->ranges[0].base = dev_priv->gtt.mappable_base; ap->ranges[0].size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; primary = @@ -1543,7 +1543,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) } aperture_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; - dev_priv->gtt.mappable_base = dev_priv->mm.gtt->gma_bus_addr; dev_priv->gtt.mappable = io_mapping_create_wc(dev_priv->gtt.mappable_base, diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 61bfb12e1016..0b8930577953 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -691,6 +691,8 @@ int i915_gem_gtt_init(struct drm_device *dev) u16 snb_gmch_ctl; int ret; + dev_priv->gtt.mappable_base = pci_resource_start(dev->pdev, 2); + /* On modern platforms we need not worry ourself with the legacy * hostbridge query stuff. Skip it entirely */ @@ -723,7 +725,6 @@ int i915_gem_gtt_init(struct drm_device *dev) /* For GEN6+ the PTEs for the ggtt live at 2MB + BAR0 */ gtt_bus_addr = pci_resource_start(dev->pdev, 0) + (2<<20); - dev_priv->mm.gtt->gma_bus_addr = pci_resource_start(dev->pdev, 2); /* i9xx_setup */ pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &snb_gmch_ctl); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 59e02691baf3..ce1d07487402 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1203,7 +1203,7 @@ static int intel_init_ring_buffer(struct drm_device *dev, goto err_unpin; ring->virtual_start = - ioremap_wc(dev_priv->mm.gtt->gma_bus_addr + obj->gtt_offset, + ioremap_wc(dev_priv->gtt.mappable_base + obj->gtt_offset, ring->size); if (ring->virtual_start == NULL) { DRM_ERROR("Failed to map ringbuffer.\n"); -- cgit v1.2.3 From b395e8b5b8f06399e3fe3ee016c9cf41ff665efc Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 8 Nov 2012 08:01:39 -0600 Subject: rbd: a little more cleanup of rbd_rq_fn() Now that a big hunk in the middle of rbd_rq_fn() has been moved into its own routine we can simplify it a little more. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 50 ++++++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 21468d0b2792..9d49e5b888d8 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1644,53 +1644,51 @@ static int rbd_dev_do_request(struct request *rq, static void rbd_rq_fn(struct request_queue *q) { struct rbd_device *rbd_dev = q->queuedata; + bool read_only = rbd_dev->mapping.read_only; struct request *rq; while ((rq = blk_fetch_request(q))) { - struct bio *bio; - bool do_write; - unsigned int size; - u64 ofs; - struct ceph_snap_context *snapc; + struct ceph_snap_context *snapc = NULL; + unsigned int size = 0; int result; dout("fetched request\n"); - /* filter out block requests we don't understand */ + /* Filter out block requests we don't understand */ + if ((rq->cmd_type != REQ_TYPE_FS)) { __blk_end_request_all(rq, 0); continue; } + spin_unlock_irq(q->queue_lock); - /* deduce our operation (read, write) */ - do_write = (rq_data_dir(rq) == WRITE); - if (do_write && rbd_dev->mapping.read_only) { - __blk_end_request_all(rq, -EROFS); - continue; - } + /* Stop writes to a read-only device */ - spin_unlock_irq(q->queue_lock); + result = -EROFS; + if (read_only && rq_data_dir(rq) == WRITE) + goto out_end_request; + + /* Grab a reference to the snapshot context */ down_read(&rbd_dev->header_rwsem); + if (rbd_dev->exists) { + snapc = ceph_get_snap_context(rbd_dev->header.snapc); + rbd_assert(snapc != NULL); + } + up_read(&rbd_dev->header_rwsem); - if (!rbd_dev->exists) { + if (!snapc) { rbd_assert(rbd_dev->spec->snap_id != CEPH_NOSNAP); - up_read(&rbd_dev->header_rwsem); dout("request for non-existent snapshot"); - spin_lock_irq(q->queue_lock); - __blk_end_request_all(rq, -ENXIO); - continue; + result = -ENXIO; + goto out_end_request; } - snapc = ceph_get_snap_context(rbd_dev->header.snapc); - - up_read(&rbd_dev->header_rwsem); - size = blk_rq_bytes(rq); - ofs = blk_rq_pos(rq) * SECTOR_SIZE; - bio = rq->bio; - - result = rbd_dev_do_request(rq, rbd_dev, snapc, ofs, size, bio); + result = rbd_dev_do_request(rq, rbd_dev, snapc, + blk_rq_pos(rq) * SECTOR_SIZE, + size, rq->bio); +out_end_request: ceph_put_snap_context(snapc); spin_lock_irq(q->queue_lock); if (!size || result < 0) -- cgit v1.2.3 From d78b650a595e23e5a115d332e3c37e019baf7703 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 9 Nov 2012 08:43:15 -0600 Subject: rbd: make exists flag atomic The rbd_device->exists field can be updated asynchronously, changing from set to clear if a mapped snapshot disappears from the base image's snapshot context. Currently, value of the "exists" flag is only read and modified under protection of the header semaphore, but that will change with the next patch. Making it atomic ensures this won't be a problem because the a the non-existence of device will be immediately known. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 9d49e5b888d8..2846536d446e 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -229,7 +229,7 @@ struct rbd_device { spinlock_t lock; /* queue lock */ struct rbd_image_header header; - bool exists; + atomic_t exists; struct rbd_spec *spec; char *header_name; @@ -751,7 +751,7 @@ static int rbd_dev_set_mapping(struct rbd_device *rbd_dev) goto done; rbd_dev->mapping.read_only = true; } - rbd_dev->exists = true; + atomic_set(&rbd_dev->exists, 1); done: return ret; } @@ -1671,7 +1671,7 @@ static void rbd_rq_fn(struct request_queue *q) /* Grab a reference to the snapshot context */ down_read(&rbd_dev->header_rwsem); - if (rbd_dev->exists) { + if (atomic_read(&rbd_dev->exists)) { snapc = ceph_get_snap_context(rbd_dev->header.snapc); rbd_assert(snapc != NULL); } @@ -2294,6 +2294,7 @@ struct rbd_device *rbd_dev_create(struct rbd_client *rbdc, return NULL; spin_lock_init(&rbd_dev->lock); + atomic_set(&rbd_dev->exists, 0); INIT_LIST_HEAD(&rbd_dev->node); INIT_LIST_HEAD(&rbd_dev->snaps); init_rwsem(&rbd_dev->header_rwsem); @@ -2918,7 +2919,7 @@ static int rbd_dev_snaps_update(struct rbd_device *rbd_dev) /* Existing snapshot not in the new snap context */ if (rbd_dev->spec->snap_id == snap->id) - rbd_dev->exists = false; + atomic_set(&rbd_dev->exists, 0); rbd_remove_snap_dev(snap); dout("%ssnap id %llu has been removed\n", rbd_dev->spec->snap_id == snap->id ? -- cgit v1.2.3 From a7b4c65f4f15aa657b09d13da8f45ba0b72ec0df Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 9 Nov 2012 08:43:15 -0600 Subject: rbd: only get snap context for write requests Right now we get the snapshot context for an rbd image (under protection of the header semaphore) for every request processed. There's no need to get the snap context if we're doing a read, so avoid doing so in that case. Note that we no longer need to hold the header semaphore to check the rbd_dev's existence flag. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 2846536d446e..0fbe9add0446 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1331,7 +1331,7 @@ static int rbd_do_op(struct request *rq, } else { opcode = CEPH_OSD_OP_READ; flags = CEPH_OSD_FLAG_READ; - snapc = NULL; + rbd_assert(!snapc); snapid = rbd_dev->spec->snap_id; payload_len = 0; } @@ -1662,22 +1662,25 @@ static void rbd_rq_fn(struct request_queue *q) } spin_unlock_irq(q->queue_lock); - /* Stop writes to a read-only device */ - - result = -EROFS; - if (read_only && rq_data_dir(rq) == WRITE) - goto out_end_request; - - /* Grab a reference to the snapshot context */ - - down_read(&rbd_dev->header_rwsem); - if (atomic_read(&rbd_dev->exists)) { + /* Write requests need a reference to the snapshot context */ + + if (rq_data_dir(rq) == WRITE) { + result = -EROFS; + if (read_only) /* Can't write to a read-only device */ + goto out_end_request; + + /* + * Note that each osd request will take its + * own reference to the snapshot context + * supplied. The reference we take here + * just guarantees the one we provide stays + * valid. + */ + down_read(&rbd_dev->header_rwsem); snapc = ceph_get_snap_context(rbd_dev->header.snapc); + up_read(&rbd_dev->header_rwsem); rbd_assert(snapc != NULL); - } - up_read(&rbd_dev->header_rwsem); - - if (!snapc) { + } else if (!atomic_read(&rbd_dev->exists)) { rbd_assert(rbd_dev->spec->snap_id != CEPH_NOSNAP); dout("request for non-existent snapshot"); result = -ENXIO; @@ -1689,7 +1692,8 @@ static void rbd_rq_fn(struct request_queue *q) blk_rq_pos(rq) * SECTOR_SIZE, size, rq->bio); out_end_request: - ceph_put_snap_context(snapc); + if (snapc) + ceph_put_snap_context(snapc); spin_lock_irq(q->queue_lock); if (!size || result < 0) __blk_end_request_all(rq, result); -- cgit v1.2.3 From 0ec8ce87f3bb5e4a561190f5320934e003405b6f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 9 Nov 2012 08:43:15 -0600 Subject: rbd: separate layout init Pull a block of code that initializes the layout structure in an osd request into its own function so it can be reused. Signed-off-by: Alex Elder Reviewed-by: Dan Mick Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 0fbe9add0446..d4e93a28fb6a 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -54,6 +54,7 @@ /* It might be useful to have this defined elsewhere too */ +#define U32_MAX ((u32) (~0U)) #define U64_MAX ((u64) (~0ULL)) #define RBD_DRV_NAME "rbd" @@ -1096,6 +1097,16 @@ static void rbd_coll_end_req(struct rbd_request *rbd_req, ret, len); } +static void rbd_layout_init(struct ceph_file_layout *layout, u64 pool_id) +{ + memset(layout, 0, sizeof (*layout)); + layout->fl_stripe_unit = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); + layout->fl_stripe_count = cpu_to_le32(1); + layout->fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); + rbd_assert(pool_id <= (u64) U32_MAX); + layout->fl_pg_pool = cpu_to_le32((u32) pool_id); +} + /* * Send ceph osd request */ @@ -1117,7 +1128,6 @@ static int rbd_do_request(struct request *rq, u64 *ver) { struct ceph_osd_request *osd_req; - struct ceph_file_layout *layout; int ret; u64 bno; struct timespec mtime = CURRENT_TIME; @@ -1161,14 +1171,9 @@ static int rbd_do_request(struct request *rq, strncpy(osd_req->r_oid, object_name, sizeof(osd_req->r_oid)); osd_req->r_oid_len = strlen(osd_req->r_oid); - layout = &osd_req->r_file_layout; - memset(layout, 0, sizeof(*layout)); - layout->fl_stripe_unit = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); - layout->fl_stripe_count = cpu_to_le32(1); - layout->fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); - layout->fl_pg_pool = cpu_to_le32((int) rbd_dev->spec->pool_id); - ret = ceph_calc_raw_layout(osdc, layout, snapid, ofs, &len, &bno, - osd_req, ops); + rbd_layout_init(&osd_req->r_file_layout, rbd_dev->spec->pool_id); + ret = ceph_calc_raw_layout(osdc, &osd_req->r_file_layout, + snapid, ofs, &len, &bno, osd_req, ops); rbd_assert(ret == 0); ceph_osdc_build_request(osd_req, ofs, &len, -- cgit v1.2.3 From af77f26caa35a95af09d1dac5c513b3901de7e37 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 9 Nov 2012 08:43:15 -0600 Subject: rbd: drop oid parameters from ceph_osdc_build_request() The last two parameters to ceph_osd_build_request() describe the object id, but the values passed always come from the osd request structure whose address is also provided. Get rid of those last two parameters. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 6 +----- include/linux/ceph/osd_client.h | 4 +--- net/ceph/osd_client.c | 13 +++++-------- 3 files changed, 7 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index d4e93a28fb6a..9a701effa0ef 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1176,11 +1176,7 @@ static int rbd_do_request(struct request *rq, snapid, ofs, &len, &bno, osd_req, ops); rbd_assert(ret == 0); - ceph_osdc_build_request(osd_req, ofs, &len, - ops, - snapc, - &mtime, - osd_req->r_oid, osd_req->r_oid_len); + ceph_osdc_build_request(osd_req, ofs, &len, ops, snapc, &mtime); if (linger_req) { ceph_osdc_set_request_linger(osdc, osd_req); diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index d9b880e977e6..f2e5d2cdca06 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -227,9 +227,7 @@ extern void ceph_osdc_build_request(struct ceph_osd_request *req, u64 off, u64 *plen, struct ceph_osd_req_op *src_ops, struct ceph_snap_context *snapc, - struct timespec *mtime, - const char *oid, - int oid_len); + struct timespec *mtime); extern struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *, struct ceph_file_layout *layout, diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index eade41bb7102..7d38327a8e89 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -376,9 +376,7 @@ void ceph_osdc_build_request(struct ceph_osd_request *req, u64 off, u64 *plen, struct ceph_osd_req_op *src_ops, struct ceph_snap_context *snapc, - struct timespec *mtime, - const char *oid, - int oid_len) + struct timespec *mtime) { struct ceph_msg *msg = req->r_request; struct ceph_osd_request_head *head; @@ -405,9 +403,9 @@ void ceph_osdc_build_request(struct ceph_osd_request *req, /* fill in oid */ - head->object_len = cpu_to_le32(oid_len); - memcpy(p, oid, oid_len); - p += oid_len; + head->object_len = cpu_to_le32(req->r_oid_len); + memcpy(p, req->r_oid, req->r_oid_len); + p += req->r_oid_len; src_op = src_ops; while (src_op->op) { @@ -506,8 +504,7 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, ceph_osdc_build_request(req, off, plen, ops, snapc, - mtime, - req->r_oid, req->r_oid_len); + mtime); return req; } -- cgit v1.2.3 From 4775618d9255c0c135580bbee8bee6815f8194cf Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 9 Nov 2012 08:43:15 -0600 Subject: rbd: drop snapid parameter from rbd_req_sync_read() There is only one caller of rbd_req_sync_read(), and it passes CEPH_NOSNAP as the snapshot id argument. Delete that parameter and just use CEPH_NOSNAP within the function. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 9a701effa0ef..07e064482cd8 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1368,7 +1368,6 @@ done: * Request sync osd read */ static int rbd_req_sync_read(struct rbd_device *rbd_dev, - u64 snapid, const char *object_name, u64 ofs, u64 len, char *buf, @@ -1382,7 +1381,7 @@ static int rbd_req_sync_read(struct rbd_device *rbd_dev, return -ENOMEM; ret = rbd_req_sync_op(rbd_dev, NULL, - snapid, + CEPH_NOSNAP, CEPH_OSD_FLAG_READ, ops, object_name, ofs, len, buf, NULL, ver); rbd_destroy_ops(ops); @@ -1799,8 +1798,7 @@ rbd_dev_v1_header_read(struct rbd_device *rbd_dev, u64 *version) if (!ondisk) return ERR_PTR(-ENOMEM); - ret = rbd_req_sync_read(rbd_dev, CEPH_NOSNAP, - rbd_dev->header_name, + ret = rbd_req_sync_read(rbd_dev, rbd_dev->header_name, 0, size, (char *) ondisk, version); -- cgit v1.2.3 From 07b2391fbbcefdecbc2f16321f8e454802e0b926 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 9 Nov 2012 08:43:16 -0600 Subject: rbd: drop flags parameter from rbd_req_sync_exec() All callers of rbd_req_sync_exec() pass CEPH_OSD_FLAG_READ as their flags argument. Delete that parameter and use CEPH_OSD_FLAG_READ within the function. If we find a need to support write operations we can add it back again. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 07e064482cd8..5d48bcd1709b 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1524,7 +1524,6 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, size_t outbound_size, char *inbound, size_t inbound_size, - int flags, u64 *ver) { struct ceph_osd_req_op *ops; @@ -1555,8 +1554,7 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, ops[0].cls.indata_len = outbound_size; ret = rbd_req_sync_op(rbd_dev, NULL, - CEPH_NOSNAP, - flags, ops, + CEPH_NOSNAP, CEPH_OSD_FLAG_READ, ops, object_name, 0, inbound_size, inbound, NULL, ver); @@ -2418,8 +2416,7 @@ static int _rbd_dev_v2_snap_size(struct rbd_device *rbd_dev, u64 snap_id, ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, "rbd", "get_size", (char *) &snapid, sizeof (snapid), - (char *) &size_buf, sizeof (size_buf), - CEPH_OSD_FLAG_READ, NULL); + (char *) &size_buf, sizeof (size_buf), NULL); dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); if (ret < 0) return ret; @@ -2454,8 +2451,7 @@ static int rbd_dev_v2_object_prefix(struct rbd_device *rbd_dev) ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, "rbd", "get_object_prefix", NULL, 0, - reply_buf, RBD_OBJ_PREFIX_LEN_MAX, - CEPH_OSD_FLAG_READ, NULL); + reply_buf, RBD_OBJ_PREFIX_LEN_MAX, NULL); dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); if (ret < 0) goto out; @@ -2494,7 +2490,7 @@ static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id, "rbd", "get_features", (char *) &snapid, sizeof (snapid), (char *) &features_buf, sizeof (features_buf), - CEPH_OSD_FLAG_READ, NULL); + NULL); dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); if (ret < 0) return ret; @@ -2549,8 +2545,7 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, "rbd", "get_parent", (char *) &snapid, sizeof (snapid), - (char *) reply_buf, size, - CEPH_OSD_FLAG_READ, NULL); + (char *) reply_buf, size, NULL); dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); if (ret < 0) goto out_err; @@ -2615,8 +2610,7 @@ static char *rbd_dev_image_name(struct rbd_device *rbd_dev) ret = rbd_req_sync_exec(rbd_dev, RBD_DIRECTORY, "rbd", "dir_get_name", image_id, image_id_size, - (char *) reply_buf, size, - CEPH_OSD_FLAG_READ, NULL); + (char *) reply_buf, size, NULL); if (ret < 0) goto out; p = reply_buf; @@ -2722,8 +2716,7 @@ static int rbd_dev_v2_snap_context(struct rbd_device *rbd_dev, u64 *ver) ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, "rbd", "get_snapcontext", NULL, 0, - reply_buf, size, - CEPH_OSD_FLAG_READ, ver); + reply_buf, size, ver); dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); if (ret < 0) goto out; @@ -2792,8 +2785,7 @@ static char *rbd_dev_v2_snap_name(struct rbd_device *rbd_dev, u32 which) ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, "rbd", "get_snapshot_name", (char *) &snap_id, sizeof (snap_id), - reply_buf, size, - CEPH_OSD_FLAG_READ, NULL); + reply_buf, size, NULL); dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); if (ret < 0) goto out; @@ -3401,8 +3393,7 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev) ret = rbd_req_sync_exec(rbd_dev, object_name, "rbd", "get_id", NULL, 0, - response, RBD_IMAGE_ID_LEN_MAX, - CEPH_OSD_FLAG_READ, NULL); + response, RBD_IMAGE_ID_LEN_MAX, NULL); dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); if (ret < 0) goto out; -- cgit v1.2.3 From 25704ac9de30ac3e73c123e7b2734f7ca744c8d8 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 9 Nov 2012 08:43:16 -0600 Subject: rbd: kill rbd_req_sync_op() snapc and snapid parameters The snapc and snapid parameters to rbd_req_sync_op() always take the values NULL and CEPH_NOSNAP, respectively. So just get rid of them and use those values where needed. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 5d48bcd1709b..85c5852378d2 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1257,8 +1257,6 @@ static void rbd_simple_req_cb(struct ceph_osd_request *osd_req, * Do a synchronous ceph osd operation */ static int rbd_req_sync_op(struct rbd_device *rbd_dev, - struct ceph_snap_context *snapc, - u64 snapid, int flags, struct ceph_osd_req_op *ops, const char *object_name, @@ -1278,7 +1276,7 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, if (IS_ERR(pages)) return PTR_ERR(pages); - ret = rbd_do_request(NULL, rbd_dev, snapc, snapid, + ret = rbd_do_request(NULL, rbd_dev, NULL, CEPH_NOSNAP, object_name, ofs, inbound_size, NULL, pages, num_pages, flags, @@ -1380,9 +1378,7 @@ static int rbd_req_sync_read(struct rbd_device *rbd_dev, if (!ops) return -ENOMEM; - ret = rbd_req_sync_op(rbd_dev, NULL, - CEPH_NOSNAP, - CEPH_OSD_FLAG_READ, + ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, ops, object_name, ofs, len, buf, NULL, ver); rbd_destroy_ops(ops); @@ -1461,8 +1457,7 @@ static int rbd_req_sync_watch(struct rbd_device *rbd_dev) ops[0].watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); ops[0].watch.flag = 1; - ret = rbd_req_sync_op(rbd_dev, NULL, - CEPH_NOSNAP, + ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, ops, rbd_dev->header_name, @@ -1499,8 +1494,7 @@ static int rbd_req_sync_unwatch(struct rbd_device *rbd_dev) ops[0].watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); ops[0].watch.flag = 0; - ret = rbd_req_sync_op(rbd_dev, NULL, - CEPH_NOSNAP, + ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, ops, rbd_dev->header_name, @@ -1553,8 +1547,7 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, ops[0].cls.indata = outbound; ops[0].cls.indata_len = outbound_size; - ret = rbd_req_sync_op(rbd_dev, NULL, - CEPH_NOSNAP, CEPH_OSD_FLAG_READ, ops, + ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, ops, object_name, 0, inbound_size, inbound, NULL, ver); -- cgit v1.2.3 From 7c3d22cf16f1bbcb37a73e88338c042bb49ff112 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 9 Nov 2012 12:50:10 -0600 Subject: rbd: don't bother setting snapid in rbd_do_request() For some reason, the snapid field of the osd request header is explicitly set to CEPH_NOSNAP in rbd_do_request(). Just a few lines later--with no code that would access this field in between--a call is made to ceph_calc_raw_layout() passing the snapid provided to rbd_do_request(), which encodes the snapid value it is provided into that field instead. In other words, there is no need to fill in CEPH_NOSNAP, and doing so suggests it might be necessary. Don't do that any more. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 85c5852378d2..54bd9fc3ef7c 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1132,7 +1132,6 @@ static int rbd_do_request(struct request *rq, u64 bno; struct timespec mtime = CURRENT_TIME; struct rbd_request *rbd_req; - struct ceph_osd_request_head *reqhead; struct ceph_osd_client *osdc; rbd_req = kzalloc(sizeof(*rbd_req), GFP_NOIO); @@ -1165,9 +1164,6 @@ static int rbd_do_request(struct request *rq, osd_req->r_priv = rbd_req; - reqhead = osd_req->r_request->front.iov_base; - reqhead->snapid = cpu_to_le64(CEPH_NOSNAP); - strncpy(osd_req->r_oid, object_name, sizeof(osd_req->r_oid)); osd_req->r_oid_len = strlen(osd_req->r_oid); -- cgit v1.2.3 From 0120be3c60d46d6d55f4bf7a3d654cc705eb0c54 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 14 Nov 2012 09:38:19 -0600 Subject: libceph: pass length to ceph_osdc_build_request() The len argument to ceph_osdc_build_request() is set up to be passed by address, but that function never updates its value so there's no need to do this. Tighten up the interface by passing the length directly. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 2 +- include/linux/ceph/osd_client.h | 2 +- net/ceph/osd_client.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 54bd9fc3ef7c..c1b135b6cb97 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1172,7 +1172,7 @@ static int rbd_do_request(struct request *rq, snapid, ofs, &len, &bno, osd_req, ops); rbd_assert(ret == 0); - ceph_osdc_build_request(osd_req, ofs, &len, ops, snapc, &mtime); + ceph_osdc_build_request(osd_req, ofs, len, ops, snapc, &mtime); if (linger_req) { ceph_osdc_set_request_linger(osdc, osd_req); diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 61562c792855..4bfb4582439a 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -224,7 +224,7 @@ extern struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client * struct bio *bio); extern void ceph_osdc_build_request(struct ceph_osd_request *req, - u64 off, u64 *plen, + u64 off, u64 len, struct ceph_osd_req_op *src_ops, struct ceph_snap_context *snapc, struct timespec *mtime); diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 37d43d5b828c..e29a3ed92958 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -336,7 +336,7 @@ static void osd_req_encode_op(struct ceph_osd_request *req, * */ void ceph_osdc_build_request(struct ceph_osd_request *req, - u64 off, u64 *plen, + u64 off, u64 len, struct ceph_osd_req_op *src_ops, struct ceph_snap_context *snapc, struct timespec *mtime) @@ -390,7 +390,7 @@ void ceph_osdc_build_request(struct ceph_osd_request *req, if (flags & CEPH_OSD_FLAG_WRITE) { req->r_request->hdr.data_off = cpu_to_le16(off); - req->r_request->hdr.data_len = cpu_to_le32(*plen + data_len); + req->r_request->hdr.data_len = cpu_to_le32(len + data_len); } else if (data_len) { req->r_request->hdr.data_off = 0; req->r_request->hdr.data_len = cpu_to_le32(data_len); @@ -464,7 +464,7 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, req->r_num_pages = calc_pages_for(page_align, *plen); req->r_page_alignment = page_align; - ceph_osdc_build_request(req, off, plen, ops, + ceph_osdc_build_request(req, off, *plen, ops, snapc, mtime); -- cgit v1.2.3 From 4d6b250bf18d44571d69a0f4afec4b6a1969729f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 13 Nov 2012 21:11:15 -0600 Subject: libceph: drop snapid in ceph_calc_raw_layout() A snapshot id must be provided to ceph_calc_raw_layout() even though it is not needed at all for calculating the layout. Where the snapshot id *is* needed is when building the request message for an osd operation. Drop the snapid parameter from ceph_calc_raw_layout() and pass that value instead in ceph_osdc_build_request(). Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 4 ++-- include/linux/ceph/osd_client.h | 2 +- net/ceph/osd_client.c | 14 ++++---------- 3 files changed, 7 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index c1b135b6cb97..fa371868e9b0 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1169,10 +1169,10 @@ static int rbd_do_request(struct request *rq, rbd_layout_init(&osd_req->r_file_layout, rbd_dev->spec->pool_id); ret = ceph_calc_raw_layout(osdc, &osd_req->r_file_layout, - snapid, ofs, &len, &bno, osd_req, ops); + ofs, &len, &bno, osd_req, ops); rbd_assert(ret == 0); - ceph_osdc_build_request(osd_req, ofs, len, ops, snapc, &mtime); + ceph_osdc_build_request(osd_req, ofs, len, ops, snapc, snapid, &mtime); if (linger_req) { ceph_osdc_set_request_linger(osdc, osd_req); diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 4bfb4582439a..0e82a0a967ef 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -209,7 +209,6 @@ extern void ceph_osdc_handle_map(struct ceph_osd_client *osdc, extern int ceph_calc_raw_layout(struct ceph_osd_client *osdc, struct ceph_file_layout *layout, - u64 snapid, u64 off, u64 *plen, u64 *bno, struct ceph_osd_request *req, struct ceph_osd_req_op *op); @@ -227,6 +226,7 @@ extern void ceph_osdc_build_request(struct ceph_osd_request *req, u64 off, u64 len, struct ceph_osd_req_op *src_ops, struct ceph_snap_context *snapc, + u64 snap_id, struct timespec *mtime); extern struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *, diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 47e5f5b1f94c..b5a4b2875e8a 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -40,18 +40,14 @@ static int op_has_extent(int op) int ceph_calc_raw_layout(struct ceph_osd_client *osdc, struct ceph_file_layout *layout, - u64 snapid, u64 off, u64 *plen, u64 *bno, struct ceph_osd_request *req, struct ceph_osd_req_op *op) { - struct ceph_osd_request_head *reqhead = req->r_request->front.iov_base; u64 orig_len = *plen; u64 objoff, objlen; /* extent in object */ int r; - reqhead->snapid = cpu_to_le64(snapid); - /* object extent? */ r = ceph_calc_file_object_mapping(layout, off, orig_len, bno, &objoff, &objlen); @@ -121,8 +117,7 @@ static int calc_layout(struct ceph_osd_client *osdc, u64 bno; int r; - r = ceph_calc_raw_layout(osdc, layout, vino.snap, off, - plen, &bno, req, op); + r = ceph_calc_raw_layout(osdc, layout, off, plen, &bno, req, op); if (r < 0) return r; @@ -340,7 +335,7 @@ static void osd_req_encode_op(struct ceph_osd_request *req, void ceph_osdc_build_request(struct ceph_osd_request *req, u64 off, u64 len, struct ceph_osd_req_op *src_ops, - struct ceph_snap_context *snapc, + struct ceph_snap_context *snapc, u64 snap_id, struct timespec *mtime) { struct ceph_msg *msg = req->r_request; @@ -355,6 +350,7 @@ void ceph_osdc_build_request(struct ceph_osd_request *req, int i; head = msg->front.iov_base; + head->snapid = cpu_to_le64(snap_id); op = (void *)(head + 1); p = (void *)(op + num_op); @@ -466,9 +462,7 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, req->r_num_pages = calc_pages_for(page_align, *plen); req->r_page_alignment = page_align; - ceph_osdc_build_request(req, off, *plen, ops, - snapc, - mtime); + ceph_osdc_build_request(req, off, *plen, ops, snapc, vino.snap, mtime); return req; } -- cgit v1.2.3 From e75b45cf36565fd8ba206a9d80f670a86e61ba2f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 13 Nov 2012 21:11:14 -0600 Subject: libceph: drop osdc from ceph_calc_raw_layout() The osdc parameter to ceph_calc_raw_layout() is not used, so get rid of it. Consequently, the corresponding parameter in calc_layout() becomes unused, so get rid of that as well. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 2 +- include/linux/ceph/osd_client.h | 3 +-- net/ceph/osd_client.c | 10 ++++------ 3 files changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index fa371868e9b0..ac8fd3856509 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1168,7 +1168,7 @@ static int rbd_do_request(struct request *rq, osd_req->r_oid_len = strlen(osd_req->r_oid); rbd_layout_init(&osd_req->r_file_layout, rbd_dev->spec->pool_id); - ret = ceph_calc_raw_layout(osdc, &osd_req->r_file_layout, + ret = ceph_calc_raw_layout(&osd_req->r_file_layout, ofs, &len, &bno, osd_req, ops); rbd_assert(ret == 0); diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 0e82a0a967ef..fe3a6e8db1f9 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -207,8 +207,7 @@ extern void ceph_osdc_handle_reply(struct ceph_osd_client *osdc, extern void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg); -extern int ceph_calc_raw_layout(struct ceph_osd_client *osdc, - struct ceph_file_layout *layout, +extern int ceph_calc_raw_layout(struct ceph_file_layout *layout, u64 off, u64 *plen, u64 *bno, struct ceph_osd_request *req, struct ceph_osd_req_op *op); diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index b5a4b2875e8a..cd9c28387de3 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -38,8 +38,7 @@ static int op_has_extent(int op) op == CEPH_OSD_OP_WRITE); } -int ceph_calc_raw_layout(struct ceph_osd_client *osdc, - struct ceph_file_layout *layout, +int ceph_calc_raw_layout(struct ceph_file_layout *layout, u64 off, u64 *plen, u64 *bno, struct ceph_osd_request *req, struct ceph_osd_req_op *op) @@ -107,8 +106,7 @@ EXPORT_SYMBOL(ceph_calc_raw_layout); * * fill osd op in request message. */ -static int calc_layout(struct ceph_osd_client *osdc, - struct ceph_vino vino, +static int calc_layout(struct ceph_vino vino, struct ceph_file_layout *layout, u64 off, u64 *plen, struct ceph_osd_request *req, @@ -117,7 +115,7 @@ static int calc_layout(struct ceph_osd_client *osdc, u64 bno; int r; - r = ceph_calc_raw_layout(osdc, layout, off, plen, &bno, req, op); + r = ceph_calc_raw_layout(layout, off, plen, &bno, req, op); if (r < 0) return r; @@ -452,7 +450,7 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, return ERR_PTR(-ENOMEM); /* calculate max write size */ - r = calc_layout(osdc, vino, layout, off, plen, req, ops); + r = calc_layout(vino, layout, off, plen, req, ops); if (r < 0) return ERR_PTR(r); req->r_file_layout = *layout; /* keep a copy */ -- cgit v1.2.3 From d178a9e74006e80f568d87e29f2a68f14fc7cbb1 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 13 Nov 2012 21:11:15 -0600 Subject: libceph: don't set flags in ceph_osdc_alloc_request() The only thing ceph_osdc_alloc_request() really does with the flags value it is passed is assign it to the newly-created osd request structure. Do that in the caller instead. Both callers subsequently call ceph_osdc_build_request(), so have that function (instead of ceph_osdc_alloc_request()) issue a warning if a request comes through with neither the read nor write flags set. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 3 ++- include/linux/ceph/osd_client.h | 1 - net/ceph/osd_client.c | 11 ++++------- 3 files changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index ac8fd3856509..bdbaa4cfd9d3 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1148,13 +1148,14 @@ static int rbd_do_request(struct request *rq, (unsigned long long) len, coll, coll_index); osdc = &rbd_dev->rbd_client->client->osdc; - osd_req = ceph_osdc_alloc_request(osdc, flags, snapc, ops, + osd_req = ceph_osdc_alloc_request(osdc, snapc, ops, false, GFP_NOIO, pages, bio); if (!osd_req) { ret = -ENOMEM; goto done_pages; } + osd_req->r_flags = flags; osd_req->r_callback = rbd_cb; rbd_req->rq = rq; diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index fe3a6e8db1f9..6ddda5bbd1a6 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -213,7 +213,6 @@ extern int ceph_calc_raw_layout(struct ceph_file_layout *layout, struct ceph_osd_req_op *op); extern struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, - int flags, struct ceph_snap_context *snapc, struct ceph_osd_req_op *ops, bool use_mempool, diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index cd9c28387de3..77ce1edaa07d 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -171,7 +171,6 @@ static int get_num_ops(struct ceph_osd_req_op *ops) } struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, - int flags, struct ceph_snap_context *snapc, struct ceph_osd_req_op *ops, bool use_mempool, @@ -208,10 +207,6 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, INIT_LIST_HEAD(&req->r_req_lru_item); INIT_LIST_HEAD(&req->r_osd_item); - req->r_flags = flags; - - WARN_ON((flags & (CEPH_OSD_FLAG_READ|CEPH_OSD_FLAG_WRITE)) == 0); - /* create reply message */ if (use_mempool) msg = ceph_msgpool_get(&osdc->msgpool_op_reply, 0); @@ -347,6 +342,8 @@ void ceph_osdc_build_request(struct ceph_osd_request *req, u64 data_len = 0; int i; + WARN_ON((flags & (CEPH_OSD_FLAG_READ|CEPH_OSD_FLAG_WRITE)) == 0); + head = msg->front.iov_base; head->snapid = cpu_to_le64(snap_id); op = (void *)(head + 1); @@ -442,12 +439,12 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, } else ops[1].op = 0; - req = ceph_osdc_alloc_request(osdc, flags, - snapc, ops, + req = ceph_osdc_alloc_request(osdc, snapc, ops, use_mempool, GFP_NOFS, NULL, NULL); if (!req) return ERR_PTR(-ENOMEM); + req->r_flags = flags; /* calculate max write size */ r = calc_layout(vino, layout, off, plen, req, ops); -- cgit v1.2.3 From 54a5400721da7fa5a16cea151aade5bdfee74111 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 13 Nov 2012 21:11:15 -0600 Subject: libceph: don't set pages or bio in ceph_osdc_alloc_request() Only one of the two callers of ceph_osdc_alloc_request() provides page or bio data for its payload. And essentially all that function was doing with those arguments was assigning them to fields in the osd request structure. Simplify ceph_osdc_alloc_request() by having the caller take care of making those assignments Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 8 ++++++-- include/linux/ceph/osd_client.h | 4 +--- net/ceph/osd_client.c | 15 ++------------- 3 files changed, 9 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index bdbaa4cfd9d3..d1445df6398a 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1148,14 +1148,18 @@ static int rbd_do_request(struct request *rq, (unsigned long long) len, coll, coll_index); osdc = &rbd_dev->rbd_client->client->osdc; - osd_req = ceph_osdc_alloc_request(osdc, snapc, ops, - false, GFP_NOIO, pages, bio); + osd_req = ceph_osdc_alloc_request(osdc, snapc, ops, false, GFP_NOIO); if (!osd_req) { ret = -ENOMEM; goto done_pages; } osd_req->r_flags = flags; + osd_req->r_pages = pages; + if (bio) { + osd_req->r_bio = bio; + bio_get(osd_req->r_bio); + } osd_req->r_callback = rbd_cb; rbd_req->rq = rq; diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 6ddda5bbd1a6..75f56d372d44 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -216,9 +216,7 @@ extern struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client * struct ceph_snap_context *snapc, struct ceph_osd_req_op *ops, bool use_mempool, - gfp_t gfp_flags, - struct page **pages, - struct bio *bio); + gfp_t gfp_flags); extern void ceph_osdc_build_request(struct ceph_osd_request *req, u64 off, u64 len, diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 77ce1edaa07d..bdc3bb12bfd5 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -174,9 +174,7 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, struct ceph_snap_context *snapc, struct ceph_osd_req_op *ops, bool use_mempool, - gfp_t gfp_flags, - struct page **pages, - struct bio *bio) + gfp_t gfp_flags) { struct ceph_osd_request *req; struct ceph_msg *msg; @@ -237,13 +235,6 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, memset(msg->front.iov_base, 0, msg->front.iov_len); req->r_request = msg; - req->r_pages = pages; -#ifdef CONFIG_BLOCK - if (bio) { - req->r_bio = bio; - bio_get(req->r_bio); - } -#endif return req; } @@ -439,9 +430,7 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, } else ops[1].op = 0; - req = ceph_osdc_alloc_request(osdc, snapc, ops, - use_mempool, - GFP_NOFS, NULL, NULL); + req = ceph_osdc_alloc_request(osdc, snapc, ops, use_mempool, GFP_NOFS); if (!req) return ERR_PTR(-ENOMEM); req->r_flags = flags; -- cgit v1.2.3 From d07c09589f533db9ab500ac38151bc9f3a4d0648 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 13 Nov 2012 21:11:15 -0600 Subject: rbd: pass num_op with ops array Add a num_op parameter to rbd_do_request() and rbd_req_sync_op() to indicate the number of entries in the array. The callers of these functions always know how many entries are in the array, so just pass that information down. This is in anticipation of eliminating the extra zero-filled entry in these ops arrays. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index d1445df6398a..b6872d3cb04c 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1119,6 +1119,7 @@ static int rbd_do_request(struct request *rq, struct page **pages, int num_pages, int flags, + unsigned int num_op, struct ceph_osd_req_op *ops, struct rbd_req_coll *coll, int coll_index, @@ -1259,6 +1260,7 @@ static void rbd_simple_req_cb(struct ceph_osd_request *osd_req, */ static int rbd_req_sync_op(struct rbd_device *rbd_dev, int flags, + unsigned int num_op, struct ceph_osd_req_op *ops, const char *object_name, u64 ofs, u64 inbound_size, @@ -1281,7 +1283,7 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, object_name, ofs, inbound_size, NULL, pages, num_pages, flags, - ops, + num_op, ops, NULL, 0, NULL, linger_req, ver); @@ -1351,7 +1353,7 @@ static int rbd_do_op(struct request *rq, bio, NULL, 0, flags, - ops, + 1, ops, coll, coll_index, rbd_req_cb, 0, NULL); if (ret < 0) @@ -1380,7 +1382,7 @@ static int rbd_req_sync_read(struct rbd_device *rbd_dev, return -ENOMEM; ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, - ops, object_name, ofs, len, buf, NULL, ver); + 1, ops, object_name, ofs, len, buf, NULL, ver); rbd_destroy_ops(ops); return ret; @@ -1408,7 +1410,7 @@ static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, rbd_dev->header_name, 0, 0, NULL, NULL, 0, CEPH_OSD_FLAG_READ, - ops, + 1, ops, NULL, 0, rbd_simple_req_cb, 0, NULL); @@ -1460,7 +1462,7 @@ static int rbd_req_sync_watch(struct rbd_device *rbd_dev) ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, - ops, + 1, ops, rbd_dev->header_name, 0, 0, NULL, &rbd_dev->watch_request, NULL); @@ -1497,7 +1499,7 @@ static int rbd_req_sync_unwatch(struct rbd_device *rbd_dev) ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, - ops, + 1, ops, rbd_dev->header_name, 0, 0, NULL, NULL, NULL); @@ -1548,7 +1550,7 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, ops[0].cls.indata = outbound; ops[0].cls.indata_len = outbound_size; - ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, ops, + ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, 1, ops, object_name, 0, inbound_size, inbound, NULL, ver); -- cgit v1.2.3 From ae7ca4a35b1f5df86e2c32b2cfc01a8d528c7b8c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 13 Nov 2012 21:11:15 -0600 Subject: libceph: pass num_op with ops Both ceph_osdc_alloc_request() and ceph_osdc_build_request() are provided an array of ceph osd request operations. Rather than just passing the number of operations in the array, the caller is required append an additional zeroed operation structure to signal the end of the array. All callers know the number of operations at the time these functions are called, so drop the silly zero entry and supply that number directly. As a result, get_num_ops() is no longer needed. This also means that ceph_osdc_alloc_request() never uses its ops argument, so that can be dropped. Also rbd_create_rw_ops() no longer needs to add one to reserve room for the additional op. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 9 +++++---- include/linux/ceph/osd_client.h | 3 ++- net/ceph/osd_client.c | 43 ++++++++++++++--------------------------- 3 files changed, 22 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index b6872d3cb04c..88de8ccb29bd 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1026,12 +1026,12 @@ out_err: /* * helpers for osd request op vectors. */ -static struct ceph_osd_req_op *rbd_create_rw_ops(int num_ops, +static struct ceph_osd_req_op *rbd_create_rw_ops(int num_op, int opcode, u32 payload_len) { struct ceph_osd_req_op *ops; - ops = kzalloc(sizeof (*ops) * (num_ops + 1), GFP_NOIO); + ops = kzalloc(num_op * sizeof (*ops), GFP_NOIO); if (!ops) return NULL; @@ -1149,7 +1149,7 @@ static int rbd_do_request(struct request *rq, (unsigned long long) len, coll, coll_index); osdc = &rbd_dev->rbd_client->client->osdc; - osd_req = ceph_osdc_alloc_request(osdc, snapc, ops, false, GFP_NOIO); + osd_req = ceph_osdc_alloc_request(osdc, snapc, num_op, false, GFP_NOIO); if (!osd_req) { ret = -ENOMEM; goto done_pages; @@ -1178,7 +1178,8 @@ static int rbd_do_request(struct request *rq, ofs, &len, &bno, osd_req, ops); rbd_assert(ret == 0); - ceph_osdc_build_request(osd_req, ofs, len, ops, snapc, snapid, &mtime); + ceph_osdc_build_request(osd_req, ofs, len, num_op, ops, + snapc, snapid, &mtime); if (linger_req) { ceph_osdc_set_request_linger(osdc, osd_req); diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 75f56d372d44..2b04d054e09d 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -214,12 +214,13 @@ extern int ceph_calc_raw_layout(struct ceph_file_layout *layout, extern struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, struct ceph_snap_context *snapc, - struct ceph_osd_req_op *ops, + unsigned int num_op, bool use_mempool, gfp_t gfp_flags); extern void ceph_osdc_build_request(struct ceph_osd_request *req, u64 off, u64 len, + unsigned int num_op, struct ceph_osd_req_op *src_ops, struct ceph_snap_context *snapc, u64 snap_id, diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index bdc3bb12bfd5..500ae8b49321 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -160,25 +160,14 @@ void ceph_osdc_release_request(struct kref *kref) } EXPORT_SYMBOL(ceph_osdc_release_request); -static int get_num_ops(struct ceph_osd_req_op *ops) -{ - int i = 0; - - while (ops[i].op) - i++; - - return i; -} - struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, struct ceph_snap_context *snapc, - struct ceph_osd_req_op *ops, + unsigned int num_op, bool use_mempool, gfp_t gfp_flags) { struct ceph_osd_request *req; struct ceph_msg *msg; - int num_op = get_num_ops(ops); size_t msg_size = sizeof(struct ceph_osd_request_head); msg_size += num_op*sizeof(struct ceph_osd_op); @@ -317,7 +306,7 @@ static void osd_req_encode_op(struct ceph_osd_request *req, * */ void ceph_osdc_build_request(struct ceph_osd_request *req, - u64 off, u64 len, + u64 off, u64 len, unsigned int num_op, struct ceph_osd_req_op *src_ops, struct ceph_snap_context *snapc, u64 snap_id, struct timespec *mtime) @@ -327,7 +316,6 @@ void ceph_osdc_build_request(struct ceph_osd_request *req, struct ceph_osd_req_op *src_op; struct ceph_osd_op *op; void *p; - int num_op = get_num_ops(src_ops); size_t msg_size = sizeof(*head) + num_op*sizeof(*op); int flags = req->r_flags; u64 data_len = 0; @@ -346,20 +334,17 @@ void ceph_osdc_build_request(struct ceph_osd_request *req, head->flags = cpu_to_le32(flags); if (flags & CEPH_OSD_FLAG_WRITE) ceph_encode_timespec(&head->mtime, mtime); + BUG_ON(num_op > (unsigned int) ((u16) -1)); head->num_ops = cpu_to_le16(num_op); - /* fill in oid */ head->object_len = cpu_to_le32(req->r_oid_len); memcpy(p, req->r_oid, req->r_oid_len); p += req->r_oid_len; src_op = src_ops; - while (src_op->op) { - osd_req_encode_op(req, op, src_op); - src_op++; - op++; - } + while (num_op--) + osd_req_encode_op(req, op++, src_op++); data_len += req->r_trail.length; @@ -414,23 +399,24 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, bool use_mempool, int num_reply, int page_align) { - struct ceph_osd_req_op ops[3]; + struct ceph_osd_req_op ops[2]; struct ceph_osd_request *req; + unsigned int num_op = 1; int r; + memset(&ops, 0, sizeof ops); + ops[0].op = opcode; ops[0].extent.truncate_seq = truncate_seq; ops[0].extent.truncate_size = truncate_size; - ops[0].payload_len = 0; if (do_sync) { ops[1].op = CEPH_OSD_OP_STARTSYNC; - ops[1].payload_len = 0; - ops[2].op = 0; - } else - ops[1].op = 0; + num_op++; + } - req = ceph_osdc_alloc_request(osdc, snapc, ops, use_mempool, GFP_NOFS); + req = ceph_osdc_alloc_request(osdc, snapc, num_op, use_mempool, + GFP_NOFS); if (!req) return ERR_PTR(-ENOMEM); req->r_flags = flags; @@ -446,7 +432,8 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, req->r_num_pages = calc_pages_for(page_align, *plen); req->r_page_alignment = page_align; - ceph_osdc_build_request(req, off, *plen, ops, snapc, vino.snap, mtime); + ceph_osdc_build_request(req, off, *plen, num_op, ops, + snapc, vino.snap, mtime); return req; } -- cgit v1.2.3 From 139b4318ad93ae4370d88882ff89b42dcbfaaab1 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 13 Nov 2012 21:11:15 -0600 Subject: rbd: there is really only one op Throughout the rbd code there are spots where it appears we can handle an osd request containing more than one osd request op. But that is only the way it appears. In fact, currently only one operation at a time can be supported, and supporting more than one will require much more than fleshing out the support that's there now. This patch changes names to make it perfectly clear that anywhere we're dealing with a block of ops, we're in fact dealing with exactly one of them. We'll be able to simplify some things as a result. When multiple op support is implemented, we can update things again accordingly. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 118 +++++++++++++++++++++++++--------------------------- 1 file changed, 56 insertions(+), 62 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 88de8ccb29bd..cc8924d8f263 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1023,32 +1023,26 @@ out_err: return NULL; } -/* - * helpers for osd request op vectors. - */ -static struct ceph_osd_req_op *rbd_create_rw_ops(int num_op, - int opcode, u32 payload_len) +static struct ceph_osd_req_op *rbd_create_rw_op(int opcode, u32 payload_len) { - struct ceph_osd_req_op *ops; + struct ceph_osd_req_op *op; - ops = kzalloc(num_op * sizeof (*ops), GFP_NOIO); - if (!ops) + op = kzalloc(sizeof (*op), GFP_NOIO); + if (!op) return NULL; - - ops[0].op = opcode; - /* * op extent offset and length will be set later on * in calc_raw_layout() */ - ops[0].payload_len = payload_len; + op->op = opcode; + op->payload_len = payload_len; - return ops; + return op; } -static void rbd_destroy_ops(struct ceph_osd_req_op *ops) +static void rbd_destroy_op(struct ceph_osd_req_op *op) { - kfree(ops); + kfree(op); } static void rbd_coll_end_req_index(struct request *rq, @@ -1314,7 +1308,7 @@ static int rbd_do_op(struct request *rq, u64 seg_ofs; u64 seg_len; int ret; - struct ceph_osd_req_op *ops; + struct ceph_osd_req_op *op; u32 payload_len; int opcode; int flags; @@ -1340,8 +1334,8 @@ static int rbd_do_op(struct request *rq, } ret = -ENOMEM; - ops = rbd_create_rw_ops(1, opcode, payload_len); - if (!ops) + op = rbd_create_rw_op(opcode, payload_len); + if (!op) goto done; /* we've taken care of segment sizes earlier when we @@ -1354,13 +1348,13 @@ static int rbd_do_op(struct request *rq, bio, NULL, 0, flags, - 1, ops, + 1, op, coll, coll_index, rbd_req_cb, 0, NULL); if (ret < 0) rbd_coll_end_req_index(rq, coll, coll_index, (s32)ret, seg_len); - rbd_destroy_ops(ops); + rbd_destroy_op(op); done: kfree(seg_name); return ret; @@ -1375,16 +1369,16 @@ static int rbd_req_sync_read(struct rbd_device *rbd_dev, char *buf, u64 *ver) { - struct ceph_osd_req_op *ops; + struct ceph_osd_req_op *op; int ret; - ops = rbd_create_rw_ops(1, CEPH_OSD_OP_READ, 0); - if (!ops) + op = rbd_create_rw_op(CEPH_OSD_OP_READ, 0); + if (!op) return -ENOMEM; ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, - 1, ops, object_name, ofs, len, buf, NULL, ver); - rbd_destroy_ops(ops); + 1, op, object_name, ofs, len, buf, NULL, ver); + rbd_destroy_op(op); return ret; } @@ -1396,26 +1390,26 @@ static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, u64 ver, u64 notify_id) { - struct ceph_osd_req_op *ops; + struct ceph_osd_req_op *op; int ret; - ops = rbd_create_rw_ops(1, CEPH_OSD_OP_NOTIFY_ACK, 0); - if (!ops) + op = rbd_create_rw_op(CEPH_OSD_OP_NOTIFY_ACK, 0); + if (!op) return -ENOMEM; - ops[0].watch.ver = cpu_to_le64(ver); - ops[0].watch.cookie = notify_id; - ops[0].watch.flag = 0; + op->watch.ver = cpu_to_le64(ver); + op->watch.cookie = notify_id; + op->watch.flag = 0; ret = rbd_do_request(NULL, rbd_dev, NULL, CEPH_NOSNAP, rbd_dev->header_name, 0, 0, NULL, NULL, 0, CEPH_OSD_FLAG_READ, - 1, ops, + 1, op, NULL, 0, rbd_simple_req_cb, 0, NULL); - rbd_destroy_ops(ops); + rbd_destroy_op(op); return ret; } @@ -1444,12 +1438,12 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) */ static int rbd_req_sync_watch(struct rbd_device *rbd_dev) { - struct ceph_osd_req_op *ops; + struct ceph_osd_req_op *op; struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc; int ret; - ops = rbd_create_rw_ops(1, CEPH_OSD_OP_WATCH, 0); - if (!ops) + op = rbd_create_rw_op(CEPH_OSD_OP_WATCH, 0); + if (!op) return -ENOMEM; ret = ceph_osdc_create_event(osdc, rbd_watch_cb, 0, @@ -1457,13 +1451,13 @@ static int rbd_req_sync_watch(struct rbd_device *rbd_dev) if (ret < 0) goto fail; - ops[0].watch.ver = cpu_to_le64(rbd_dev->header.obj_version); - ops[0].watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); - ops[0].watch.flag = 1; + op->watch.ver = cpu_to_le64(rbd_dev->header.obj_version); + op->watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); + op->watch.flag = 1; ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, - 1, ops, + 1, op, rbd_dev->header_name, 0, 0, NULL, &rbd_dev->watch_request, NULL); @@ -1471,14 +1465,14 @@ static int rbd_req_sync_watch(struct rbd_device *rbd_dev) if (ret < 0) goto fail_event; - rbd_destroy_ops(ops); + rbd_destroy_op(op); return 0; fail_event: ceph_osdc_cancel_event(rbd_dev->watch_event); rbd_dev->watch_event = NULL; fail: - rbd_destroy_ops(ops); + rbd_destroy_op(op); return ret; } @@ -1487,25 +1481,25 @@ fail: */ static int rbd_req_sync_unwatch(struct rbd_device *rbd_dev) { - struct ceph_osd_req_op *ops; + struct ceph_osd_req_op *op; int ret; - ops = rbd_create_rw_ops(1, CEPH_OSD_OP_WATCH, 0); - if (!ops) + op = rbd_create_rw_op(CEPH_OSD_OP_WATCH, 0); + if (!op) return -ENOMEM; - ops[0].watch.ver = 0; - ops[0].watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); - ops[0].watch.flag = 0; + op->watch.ver = 0; + op->watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); + op->watch.flag = 0; ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, - 1, ops, + 1, op, rbd_dev->header_name, 0, 0, NULL, NULL, NULL); - rbd_destroy_ops(ops); + rbd_destroy_op(op); ceph_osdc_cancel_event(rbd_dev->watch_event); rbd_dev->watch_event = NULL; return ret; @@ -1524,7 +1518,7 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, size_t inbound_size, u64 *ver) { - struct ceph_osd_req_op *ops; + struct ceph_osd_req_op *op; int class_name_len = strlen(class_name); int method_name_len = strlen(method_name); int payload_size; @@ -1539,23 +1533,23 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, * operation. */ payload_size = class_name_len + method_name_len + outbound_size; - ops = rbd_create_rw_ops(1, CEPH_OSD_OP_CALL, payload_size); - if (!ops) + op = rbd_create_rw_op(CEPH_OSD_OP_CALL, payload_size); + if (!op) return -ENOMEM; - ops[0].cls.class_name = class_name; - ops[0].cls.class_len = (__u8) class_name_len; - ops[0].cls.method_name = method_name; - ops[0].cls.method_len = (__u8) method_name_len; - ops[0].cls.argc = 0; - ops[0].cls.indata = outbound; - ops[0].cls.indata_len = outbound_size; + op->cls.class_name = class_name; + op->cls.class_len = (__u8) class_name_len; + op->cls.method_name = method_name; + op->cls.method_len = (__u8) method_name_len; + op->cls.argc = 0; + op->cls.indata = outbound; + op->cls.indata_len = outbound_size; - ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, 1, ops, + ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, 1, op, object_name, 0, inbound_size, inbound, NULL, ver); - rbd_destroy_ops(ops); + rbd_destroy_op(op); dout("cls_exec returned %d\n", ret); return ret; -- cgit v1.2.3 From 30573d680355ca0de4db2113b9080cd078ac726f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 13 Nov 2012 21:11:15 -0600 Subject: rbd: assume single op in a request We now know that every of rbd_req_sync_op() passes an array of exactly one operation, as evidenced by all callers passing 1 as its num_op argument. So get rid of that argument, assuming a single op. Similarly, we now know that all callers of rbd_do_request() pass 1 as the num_op value, so that parameter can be eliminated as well. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index cc8924d8f263..7ce178fd6fe5 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1113,8 +1113,7 @@ static int rbd_do_request(struct request *rq, struct page **pages, int num_pages, int flags, - unsigned int num_op, - struct ceph_osd_req_op *ops, + struct ceph_osd_req_op *op, struct rbd_req_coll *coll, int coll_index, void (*rbd_cb)(struct ceph_osd_request *, @@ -1143,7 +1142,7 @@ static int rbd_do_request(struct request *rq, (unsigned long long) len, coll, coll_index); osdc = &rbd_dev->rbd_client->client->osdc; - osd_req = ceph_osdc_alloc_request(osdc, snapc, num_op, false, GFP_NOIO); + osd_req = ceph_osdc_alloc_request(osdc, snapc, 1, false, GFP_NOIO); if (!osd_req) { ret = -ENOMEM; goto done_pages; @@ -1169,10 +1168,10 @@ static int rbd_do_request(struct request *rq, rbd_layout_init(&osd_req->r_file_layout, rbd_dev->spec->pool_id); ret = ceph_calc_raw_layout(&osd_req->r_file_layout, - ofs, &len, &bno, osd_req, ops); + ofs, &len, &bno, osd_req, op); rbd_assert(ret == 0); - ceph_osdc_build_request(osd_req, ofs, len, num_op, ops, + ceph_osdc_build_request(osd_req, ofs, len, 1, op, snapc, snapid, &mtime); if (linger_req) { @@ -1255,8 +1254,7 @@ static void rbd_simple_req_cb(struct ceph_osd_request *osd_req, */ static int rbd_req_sync_op(struct rbd_device *rbd_dev, int flags, - unsigned int num_op, - struct ceph_osd_req_op *ops, + struct ceph_osd_req_op *op, const char *object_name, u64 ofs, u64 inbound_size, char *inbound, @@ -1267,7 +1265,7 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, struct page **pages; int num_pages; - rbd_assert(ops != NULL); + rbd_assert(op != NULL); num_pages = calc_pages_for(ofs, inbound_size); pages = ceph_alloc_page_vector(num_pages, GFP_KERNEL); @@ -1278,7 +1276,7 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, object_name, ofs, inbound_size, NULL, pages, num_pages, flags, - num_op, ops, + op, NULL, 0, NULL, linger_req, ver); @@ -1348,7 +1346,7 @@ static int rbd_do_op(struct request *rq, bio, NULL, 0, flags, - 1, op, + op, coll, coll_index, rbd_req_cb, 0, NULL); if (ret < 0) @@ -1377,7 +1375,7 @@ static int rbd_req_sync_read(struct rbd_device *rbd_dev, return -ENOMEM; ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, - 1, op, object_name, ofs, len, buf, NULL, ver); + op, object_name, ofs, len, buf, NULL, ver); rbd_destroy_op(op); return ret; @@ -1405,7 +1403,7 @@ static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, rbd_dev->header_name, 0, 0, NULL, NULL, 0, CEPH_OSD_FLAG_READ, - 1, op, + op, NULL, 0, rbd_simple_req_cb, 0, NULL); @@ -1457,7 +1455,7 @@ static int rbd_req_sync_watch(struct rbd_device *rbd_dev) ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, - 1, op, + op, rbd_dev->header_name, 0, 0, NULL, &rbd_dev->watch_request, NULL); @@ -1494,7 +1492,7 @@ static int rbd_req_sync_unwatch(struct rbd_device *rbd_dev) ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, - 1, op, + op, rbd_dev->header_name, 0, 0, NULL, NULL, NULL); @@ -1545,7 +1543,7 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, op->cls.indata = outbound; op->cls.indata_len = outbound_size; - ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, 1, op, + ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, op, object_name, 0, inbound_size, inbound, NULL, ver); -- cgit v1.2.3 From 0829661863fb5c8031c1c5c119693ea157517783 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 14 Nov 2012 12:25:18 -0600 Subject: rbd: pull in ceph_calc_raw_layout() This is the first in a series of patches aimed at eliminating the use of ceph_calc_raw_layout() by rbd. It simply pulls in a copy of that function and renames it rbd_calc_raw_layout(). Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 7ce178fd6fe5..eee0d3649dcf 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1101,6 +1101,40 @@ static void rbd_layout_init(struct ceph_file_layout *layout, u64 pool_id) layout->fl_pg_pool = cpu_to_le32((u32) pool_id); } +int rbd_calc_raw_layout(struct ceph_file_layout *layout, + u64 off, u64 *plen, u64 *bno, + struct ceph_osd_request *req, + struct ceph_osd_req_op *op) +{ + u64 orig_len = *plen; + u64 objoff, objlen; /* extent in object */ + int r; + + /* object extent? */ + r = ceph_calc_file_object_mapping(layout, off, orig_len, bno, + &objoff, &objlen); + if (r < 0) + return r; + if (objlen < orig_len) { + *plen = objlen; + dout(" skipping last %llu, final file extent %llu~%llu\n", + orig_len - *plen, off, *plen); + } + + if (op->op == CEPH_OSD_OP_READ || op->op == CEPH_OSD_OP_WRITE) { + op->extent.offset = objoff; + op->extent.length = objlen; + } + req->r_num_pages = calc_pages_for(off, *plen); + req->r_page_alignment = off & ~PAGE_MASK; + if (op->op == CEPH_OSD_OP_WRITE) + op->payload_len = *plen; + + dout("calc_layout bno=%llx %llu~%llu (%d pages)\n", + *bno, objoff, objlen, req->r_num_pages); + return 0; +} + /* * Send ceph osd request */ @@ -1167,7 +1201,7 @@ static int rbd_do_request(struct request *rq, osd_req->r_oid_len = strlen(osd_req->r_oid); rbd_layout_init(&osd_req->r_file_layout, rbd_dev->spec->pool_id); - ret = ceph_calc_raw_layout(&osd_req->r_file_layout, + ret = rbd_calc_raw_layout(&osd_req->r_file_layout, ofs, &len, &bno, osd_req, op); rbd_assert(ret == 0); -- cgit v1.2.3 From e01e79273b251dbb35ff2522a688229b09481923 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 14 Nov 2012 12:25:18 -0600 Subject: rbd: open code rbd_calc_raw_layout() This patch gets rid of rbd_calc_raw_layout() by simply open coding it in its one caller. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 55 ++++++++++++++++++----------------------------------- 1 file changed, 18 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index eee0d3649dcf..f5f4e4a8fc87 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1032,7 +1032,7 @@ static struct ceph_osd_req_op *rbd_create_rw_op(int opcode, u32 payload_len) return NULL; /* * op extent offset and length will be set later on - * in calc_raw_layout() + * after ceph_calc_file_object_mapping(). */ op->op = opcode; op->payload_len = payload_len; @@ -1101,40 +1101,6 @@ static void rbd_layout_init(struct ceph_file_layout *layout, u64 pool_id) layout->fl_pg_pool = cpu_to_le32((u32) pool_id); } -int rbd_calc_raw_layout(struct ceph_file_layout *layout, - u64 off, u64 *plen, u64 *bno, - struct ceph_osd_request *req, - struct ceph_osd_req_op *op) -{ - u64 orig_len = *plen; - u64 objoff, objlen; /* extent in object */ - int r; - - /* object extent? */ - r = ceph_calc_file_object_mapping(layout, off, orig_len, bno, - &objoff, &objlen); - if (r < 0) - return r; - if (objlen < orig_len) { - *plen = objlen; - dout(" skipping last %llu, final file extent %llu~%llu\n", - orig_len - *plen, off, *plen); - } - - if (op->op == CEPH_OSD_OP_READ || op->op == CEPH_OSD_OP_WRITE) { - op->extent.offset = objoff; - op->extent.length = objlen; - } - req->r_num_pages = calc_pages_for(off, *plen); - req->r_page_alignment = off & ~PAGE_MASK; - if (op->op == CEPH_OSD_OP_WRITE) - op->payload_len = *plen; - - dout("calc_layout bno=%llx %llu~%llu (%d pages)\n", - *bno, objoff, objlen, req->r_num_pages); - return 0; -} - /* * Send ceph osd request */ @@ -1158,6 +1124,8 @@ static int rbd_do_request(struct request *rq, struct ceph_osd_request *osd_req; int ret; u64 bno; + u64 obj_off = 0; + u64 obj_len = 0; struct timespec mtime = CURRENT_TIME; struct rbd_request *rbd_req; struct ceph_osd_client *osdc; @@ -1201,9 +1169,22 @@ static int rbd_do_request(struct request *rq, osd_req->r_oid_len = strlen(osd_req->r_oid); rbd_layout_init(&osd_req->r_file_layout, rbd_dev->spec->pool_id); - ret = rbd_calc_raw_layout(&osd_req->r_file_layout, - ofs, &len, &bno, osd_req, op); + ret = ceph_calc_file_object_mapping(&osd_req->r_file_layout, ofs, len, + &bno, &obj_off, &obj_len); rbd_assert(ret == 0); + if (obj_len < len) { + dout(" skipping last %llu, final file extent %llu~%llu\n", + len - obj_len, ofs, obj_len); + len = obj_len; + } + if (op->op == CEPH_OSD_OP_READ || op->op == CEPH_OSD_OP_WRITE) { + op->extent.offset = obj_off; + op->extent.length = obj_len; + if (op->op == CEPH_OSD_OP_WRITE) + op->payload_len = obj_len; + } + osd_req->r_num_pages = calc_pages_for(ofs, len); + osd_req->r_page_alignment = ofs & ~PAGE_MASK; ceph_osdc_build_request(osd_req, ofs, len, 1, op, snapc, snapid, &mtime); -- cgit v1.2.3 From 47dba7ba2623b088cbbe1ac0aaa1a034f3249b6d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 14 Nov 2012 12:25:19 -0600 Subject: rbd: don't bother calculating file mapping When rbd_do_request() has a request to process it initializes a ceph file layout structure and uses it to compute offsets and limits for the range of the request using ceph_calc_file_object_mapping(). The layout used is fixed, and is based on RBD_MAX_OBJ_ORDER (30). It sets the layout's object size and stripe unit to be 1 GB (2^30), and sets the stripe count to be 1. The job of ceph_calc_file_object_mapping() is to determine which of a sequence of objects will contain data covered by range, and within that object, at what offset the range starts. It also truncates the length of the range at the end of the selected object if necessary. This is needed for ceph fs, but for rbd it really serves no purpose. It does its own blocking of images into objects, echo of which is (1 << obj_order) in size, and as a result it ignores the "bno" value returned by ceph_calc_file_object_mapping(). In addition, by the point a request has reached this function, it is already destined for a single rbd object, and its length will not exceed that object's extent. Because of this, and because the mapping will result in blocking up the range using an integer multiple of the image's object order, ceph_calc_file_object_mapping() will never change the offset or length values defined by the request. In other words, this call is a big no-op for rbd data requests. There is one exception. We read the header object using this function, and in that case we will not have already limited the request size. However, the header is a single object (not a file or rbd image), and should not be broken into pieces anyway. So in fact we should *not* be calling ceph_calc_file_object_mapping() when operating on the header object. So... Don't call ceph_calc_file_object_mapping() in rbd_do_request(), because useless for image data and incorrect to do sofor the image header. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index f5f4e4a8fc87..1c0192c3cf47 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1123,9 +1123,6 @@ static int rbd_do_request(struct request *rq, { struct ceph_osd_request *osd_req; int ret; - u64 bno; - u64 obj_off = 0; - u64 obj_len = 0; struct timespec mtime = CURRENT_TIME; struct rbd_request *rbd_req; struct ceph_osd_client *osdc; @@ -1169,19 +1166,12 @@ static int rbd_do_request(struct request *rq, osd_req->r_oid_len = strlen(osd_req->r_oid); rbd_layout_init(&osd_req->r_file_layout, rbd_dev->spec->pool_id); - ret = ceph_calc_file_object_mapping(&osd_req->r_file_layout, ofs, len, - &bno, &obj_off, &obj_len); - rbd_assert(ret == 0); - if (obj_len < len) { - dout(" skipping last %llu, final file extent %llu~%llu\n", - len - obj_len, ofs, obj_len); - len = obj_len; - } + if (op->op == CEPH_OSD_OP_READ || op->op == CEPH_OSD_OP_WRITE) { - op->extent.offset = obj_off; - op->extent.length = obj_len; + op->extent.offset = ofs; + op->extent.length = len; if (op->op == CEPH_OSD_OP_WRITE) - op->payload_len = obj_len; + op->payload_len = len; } osd_req->r_num_pages = calc_pages_for(ofs, len); osd_req->r_page_alignment = ofs & ~PAGE_MASK; -- cgit v1.2.3 From 0903e875caa93e1fb231dd66c69b118dbdad25cb Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 14 Nov 2012 12:25:19 -0600 Subject: rbd: use a common layout for each device Each osd message includes a layout structure, and for rbd it is always the same (at least for osd's in a given pool). Initialize a layout structure when an rbd_dev gets created and just copy that into osd requests for the rbd image. Replace an assertion that was done when initializing the layout structures with code that catches and handles anything that would trigger the assertion as soon as it is identified. This precludes that (bad) condition from ever occurring. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 1c0192c3cf47..d2a6e9589fab 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -235,6 +235,8 @@ struct rbd_device { char *header_name; + struct ceph_file_layout layout; + struct ceph_osd_event *watch_event; struct ceph_osd_request *watch_request; @@ -1091,16 +1093,6 @@ static void rbd_coll_end_req(struct rbd_request *rbd_req, ret, len); } -static void rbd_layout_init(struct ceph_file_layout *layout, u64 pool_id) -{ - memset(layout, 0, sizeof (*layout)); - layout->fl_stripe_unit = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); - layout->fl_stripe_count = cpu_to_le32(1); - layout->fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); - rbd_assert(pool_id <= (u64) U32_MAX); - layout->fl_pg_pool = cpu_to_le32((u32) pool_id); -} - /* * Send ceph osd request */ @@ -1165,7 +1157,7 @@ static int rbd_do_request(struct request *rq, strncpy(osd_req->r_oid, object_name, sizeof(osd_req->r_oid)); osd_req->r_oid_len = strlen(osd_req->r_oid); - rbd_layout_init(&osd_req->r_file_layout, rbd_dev->spec->pool_id); + osd_req->r_file_layout = rbd_dev->layout; /* struct */ if (op->op == CEPH_OSD_OP_READ || op->op == CEPH_OSD_OP_WRITE) { op->extent.offset = ofs; @@ -2297,6 +2289,13 @@ struct rbd_device *rbd_dev_create(struct rbd_client *rbdc, rbd_dev->spec = spec; rbd_dev->rbd_client = rbdc; + /* Initialize the layout used for all rbd requests */ + + rbd_dev->layout.fl_stripe_unit = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); + rbd_dev->layout.fl_stripe_count = cpu_to_le32(1); + rbd_dev->layout.fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); + rbd_dev->layout.fl_pg_pool = cpu_to_le32((u32) spec->pool_id); + return rbd_dev; } @@ -2551,6 +2550,12 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) if (parent_spec->pool_id == CEPH_NOPOOL) goto out; /* No parent? No problem. */ + /* The ceph file layout needs to fit pool id in 32 bits */ + + ret = -EIO; + if (WARN_ON(parent_spec->pool_id > (u64) U32_MAX)) + goto out; + image_id = ceph_extract_encoded_string(&p, end, NULL, GFP_KERNEL); if (IS_ERR(image_id)) { ret = PTR_ERR(image_id); @@ -3680,6 +3685,13 @@ static ssize_t rbd_add(struct bus_type *bus, goto err_out_client; spec->pool_id = (u64) rc; + /* The ceph file layout needs to fit pool id in 32 bits */ + + if (WARN_ON(spec->pool_id > (u64) U32_MAX)) { + rc = -EIO; + goto err_out_client; + } + rbd_dev = rbd_dev_create(rbdc, spec); if (!rbd_dev) goto err_out_client; -- cgit v1.2.3 From 907703d050df92979b3848ee42f88d5c9c6c13fe Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 13 Nov 2012 21:11:15 -0600 Subject: rbd: combine rbd sync watch/unwatch functions The rbd_req_sync_watch() and rbd_req_sync_unwatch() functions are nearly identical. Combine them into a single function with a flag indicating whether a watch is to be initiated or torn down. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 81 ++++++++++++++++++----------------------------------- 1 file changed, 27 insertions(+), 54 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index d2a6e9589fab..9d21fcd7e188 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1429,74 +1429,48 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) } /* - * Request sync osd watch + * Request sync osd watch/unwatch. The value of "start" determines + * whether a watch request is being initiated or torn down. */ -static int rbd_req_sync_watch(struct rbd_device *rbd_dev) +static int rbd_req_sync_watch(struct rbd_device *rbd_dev, int start) { struct ceph_osd_req_op *op; - struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc; + struct ceph_osd_request **linger_req = NULL; + __le64 version = 0; int ret; op = rbd_create_rw_op(CEPH_OSD_OP_WATCH, 0); if (!op) return -ENOMEM; - ret = ceph_osdc_create_event(osdc, rbd_watch_cb, 0, - (void *)rbd_dev, &rbd_dev->watch_event); - if (ret < 0) - goto fail; - - op->watch.ver = cpu_to_le64(rbd_dev->header.obj_version); - op->watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); - op->watch.flag = 1; - - ret = rbd_req_sync_op(rbd_dev, - CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, - op, - rbd_dev->header_name, - 0, 0, NULL, - &rbd_dev->watch_request, NULL); - - if (ret < 0) - goto fail_event; - - rbd_destroy_op(op); - return 0; - -fail_event: - ceph_osdc_cancel_event(rbd_dev->watch_event); - rbd_dev->watch_event = NULL; -fail: - rbd_destroy_op(op); - return ret; -} - -/* - * Request sync osd unwatch - */ -static int rbd_req_sync_unwatch(struct rbd_device *rbd_dev) -{ - struct ceph_osd_req_op *op; - int ret; + if (start) { + struct ceph_osd_client *osdc; - op = rbd_create_rw_op(CEPH_OSD_OP_WATCH, 0); - if (!op) - return -ENOMEM; + osdc = &rbd_dev->rbd_client->client->osdc; + ret = ceph_osdc_create_event(osdc, rbd_watch_cb, 0, rbd_dev, + &rbd_dev->watch_event); + if (ret < 0) + goto done; + version = cpu_to_le64(rbd_dev->header.obj_version); + linger_req = &rbd_dev->watch_request; + } - op->watch.ver = 0; + op->watch.ver = version; op->watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); - op->watch.flag = 0; + op->watch.flag = (u8) start ? 1 : 0; ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, - op, - rbd_dev->header_name, - 0, 0, NULL, NULL, NULL); - + op, rbd_dev->header_name, + 0, 0, NULL, linger_req, NULL); + if (!start || ret < 0) { + ceph_osdc_cancel_event(rbd_dev->watch_event); + rbd_dev->watch_event = NULL; + } +done: rbd_destroy_op(op); - ceph_osdc_cancel_event(rbd_dev->watch_event); - rbd_dev->watch_event = NULL; + return ret; } @@ -3033,7 +3007,7 @@ static int rbd_init_watch_dev(struct rbd_device *rbd_dev) int ret, rc; do { - ret = rbd_req_sync_watch(rbd_dev); + ret = rbd_req_sync_watch(rbd_dev, 1); if (ret == -ERANGE) { rc = rbd_dev_refresh(rbd_dev, NULL); if (rc < 0) @@ -3752,8 +3726,7 @@ static void rbd_dev_release(struct device *dev) rbd_dev->watch_request); } if (rbd_dev->watch_event) - rbd_req_sync_unwatch(rbd_dev); - + rbd_req_sync_watch(rbd_dev, 0); /* clean up and free blkdev */ rbd_free_disk(rbd_dev); -- cgit v1.2.3 From 2e53c6c379b65372df21f4d6019f6eb63af81384 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 30 Nov 2012 09:59:47 -0600 Subject: rbd: don't leak rbd_req on synchronous requests When rbd_do_request() is called it allocates and populates an rbd_req structure to hold information about the osd request to be sent. This is done for the benefit of the callback function (in particular, rbd_req_cb()), which uses this in processing when the request completes. Synchronous requests provide no callback function, in which case rbd_do_request() waits for the request to complete before returning. This case is not handling the needed free of the rbd_req structure like it should, so it is getting leaked. Note however that the synchronous case has no need for the rbd_req structure at all. So rather than simply freeing this structure for synchronous requests, just don't allocate it to begin with. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 9d21fcd7e188..28b62367ff93 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1113,20 +1113,11 @@ static int rbd_do_request(struct request *rq, struct ceph_osd_request **linger_req, u64 *ver) { + struct ceph_osd_client *osdc; struct ceph_osd_request *osd_req; - int ret; + struct rbd_request *rbd_req = NULL; struct timespec mtime = CURRENT_TIME; - struct rbd_request *rbd_req; - struct ceph_osd_client *osdc; - - rbd_req = kzalloc(sizeof(*rbd_req), GFP_NOIO); - if (!rbd_req) - return -ENOMEM; - - if (coll) { - rbd_req->coll = coll; - rbd_req->coll_index = coll_index; - } + int ret; dout("rbd_do_request object_name=%s ofs=%llu len=%llu coll=%p[%d]\n", object_name, (unsigned long long) ofs, @@ -1134,10 +1125,8 @@ static int rbd_do_request(struct request *rq, osdc = &rbd_dev->rbd_client->client->osdc; osd_req = ceph_osdc_alloc_request(osdc, snapc, 1, false, GFP_NOIO); - if (!osd_req) { - ret = -ENOMEM; - goto done_pages; - } + if (!osd_req) + return -ENOMEM; osd_req->r_flags = flags; osd_req->r_pages = pages; @@ -1145,13 +1134,22 @@ static int rbd_do_request(struct request *rq, osd_req->r_bio = bio; bio_get(osd_req->r_bio); } - osd_req->r_callback = rbd_cb; - rbd_req->rq = rq; - rbd_req->bio = bio; - rbd_req->pages = pages; - rbd_req->len = len; + if (rbd_cb) { + ret = -ENOMEM; + rbd_req = kmalloc(sizeof(*rbd_req), GFP_NOIO); + if (!rbd_req) + goto done_osd_req; + + rbd_req->rq = rq; + rbd_req->bio = bio; + rbd_req->pages = pages; + rbd_req->len = len; + rbd_req->coll = coll; + rbd_req->coll_index = coll ? coll_index : 0; + } + osd_req->r_callback = rbd_cb; osd_req->r_priv = rbd_req; strncpy(osd_req->r_oid, object_name, sizeof(osd_req->r_oid)); @@ -1193,10 +1191,12 @@ static int rbd_do_request(struct request *rq, return ret; done_err: - bio_chain_put(rbd_req->bio); - ceph_osdc_put_request(osd_req); -done_pages: + if (bio) + bio_chain_put(osd_req->r_bio); kfree(rbd_req); +done_osd_req: + ceph_osdc_put_request(osd_req); + return ret; } -- cgit v1.2.3 From 1821665749a3d7e26ad470c63fc2c46990dc6041 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 30 Nov 2012 09:59:47 -0600 Subject: rbd: don't leak rbd_req for rbd_req_sync_notify_ack() When rbd_req_sync_notify_ack() calls rbd_do_request() it supplies rbd_simple_req_cb() as its callback function. Because the callback is supplied, an rbd_req structure gets allocated and populated so it can be used by the callback. However rbd_simple_req_cb() is not freeing (or even using) the rbd_req structure, so it's getting leaked. Since rbd_simple_req_cb() has no need for the rbd_req structure, just avoid allocating one for this case. Of the three calls to rbd_do_request(), only the one from rbd_do_op() needs the rbd_req structure, and that call can be distinguished from the other two because it supplies a non-null rbd_collection pointer. So fix this leak by only allocating the rbd_req structure if a non-null "coll" value is provided to rbd_do_request(). Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 28b62367ff93..619d680960b1 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1135,7 +1135,7 @@ static int rbd_do_request(struct request *rq, bio_get(osd_req->r_bio); } - if (rbd_cb) { + if (coll) { ret = -ENOMEM; rbd_req = kmalloc(sizeof(*rbd_req), GFP_NOIO); if (!rbd_req) @@ -1146,7 +1146,7 @@ static int rbd_do_request(struct request *rq, rbd_req->pages = pages; rbd_req->len = len; rbd_req->coll = coll; - rbd_req->coll_index = coll ? coll_index : 0; + rbd_req->coll_index = coll_index; } osd_req->r_callback = rbd_cb; -- cgit v1.2.3 From c561191813e232aa52022532751855ff5c9fa319 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 19 Nov 2012 22:55:21 -0600 Subject: rbd: don't assign extent info in rbd_do_request() In rbd_do_request() there's a sort of last-minute assignment of the extent offset and length and payload length for read and write operations. Move those assignments into the caller (in those spots that might initiate read or write operations) Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 619d680960b1..c6917b11800b 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1156,13 +1156,6 @@ static int rbd_do_request(struct request *rq, osd_req->r_oid_len = strlen(osd_req->r_oid); osd_req->r_file_layout = rbd_dev->layout; /* struct */ - - if (op->op == CEPH_OSD_OP_READ || op->op == CEPH_OSD_OP_WRITE) { - op->extent.offset = ofs; - op->extent.length = len; - if (op->op == CEPH_OSD_OP_WRITE) - op->payload_len = len; - } osd_req->r_num_pages = calc_pages_for(ofs, len); osd_req->r_page_alignment = ofs & ~PAGE_MASK; @@ -1269,6 +1262,13 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, if (IS_ERR(pages)) return PTR_ERR(pages); + if (op->op == CEPH_OSD_OP_READ || op->op == CEPH_OSD_OP_WRITE) { + op->extent.offset = ofs; + op->extent.length = inbound_size; + if (op->op == CEPH_OSD_OP_WRITE) + op->payload_len = inbound_size; + } + ret = rbd_do_request(NULL, rbd_dev, NULL, CEPH_NOSNAP, object_name, ofs, inbound_size, NULL, pages, num_pages, @@ -1332,6 +1332,9 @@ static int rbd_do_op(struct request *rq, op = rbd_create_rw_op(opcode, payload_len); if (!op) goto done; + op->extent.offset = seg_ofs; + op->extent.length = seg_len; + op->payload_len = payload_len; /* we've taken care of segment sizes earlier when we cloned the bios. We should never have a segment -- cgit v1.2.3 From 8d23bf29095e5fab84535035e7a27c4920812c44 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 19 Nov 2012 22:55:21 -0600 Subject: rbd: don't assign extent info in rbd_req_sync_op() Move the assignment of the extent offset and length and payload length out of rbd_req_sync_op() and into its caller in the one spot where a read (and note--no write) operation might be initiated. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 72 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index c6917b11800b..235cda083137 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1025,19 +1025,15 @@ out_err: return NULL; } -static struct ceph_osd_req_op *rbd_create_rw_op(int opcode, u32 payload_len) +static struct ceph_osd_req_op *rbd_create_rw_op(int opcode, u64 ofs, u64 len) { struct ceph_osd_req_op *op; op = kzalloc(sizeof (*op), GFP_NOIO); if (!op) return NULL; - /* - * op extent offset and length will be set later on - * after ceph_calc_file_object_mapping(). - */ + op->op = opcode; - op->payload_len = payload_len; return op; } @@ -1047,6 +1043,42 @@ static void rbd_destroy_op(struct ceph_osd_req_op *op) kfree(op); } +struct ceph_osd_req_op *rbd_osd_req_op_create(u16 opcode, ...) +{ + struct ceph_osd_req_op *op; + va_list args; + + op = kzalloc(sizeof (*op), GFP_NOIO); + if (!op) + return NULL; + op->op = opcode; + va_start(args, opcode); + switch (opcode) { + case CEPH_OSD_OP_READ: + case CEPH_OSD_OP_WRITE: + /* rbd_osd_req_op_create(READ, offset, length) */ + /* rbd_osd_req_op_create(WRITE, offset, length) */ + op->extent.offset = va_arg(args, u64); + op->extent.length = va_arg(args, u64); + if (opcode == CEPH_OSD_OP_WRITE) + op->payload_len = op->extent.length; + break; + default: + rbd_warn(NULL, "unsupported opcode %hu\n", opcode); + kfree(op); + op = NULL; + break; + } + va_end(args); + + return op; +} + +static void rbd_osd_req_op_destroy(struct ceph_osd_req_op *op) +{ + kfree(op); +} + static void rbd_coll_end_req_index(struct request *rq, struct rbd_req_coll *coll, int index, @@ -1262,13 +1294,6 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, if (IS_ERR(pages)) return PTR_ERR(pages); - if (op->op == CEPH_OSD_OP_READ || op->op == CEPH_OSD_OP_WRITE) { - op->extent.offset = ofs; - op->extent.length = inbound_size; - if (op->op == CEPH_OSD_OP_WRITE) - op->payload_len = inbound_size; - } - ret = rbd_do_request(NULL, rbd_dev, NULL, CEPH_NOSNAP, object_name, ofs, inbound_size, NULL, pages, num_pages, @@ -1304,7 +1329,6 @@ static int rbd_do_op(struct request *rq, u64 seg_len; int ret; struct ceph_osd_req_op *op; - u32 payload_len; int opcode; int flags; u64 snapid; @@ -1319,22 +1343,17 @@ static int rbd_do_op(struct request *rq, opcode = CEPH_OSD_OP_WRITE; flags = CEPH_OSD_FLAG_WRITE|CEPH_OSD_FLAG_ONDISK; snapid = CEPH_NOSNAP; - payload_len = seg_len; } else { opcode = CEPH_OSD_OP_READ; flags = CEPH_OSD_FLAG_READ; rbd_assert(!snapc); snapid = rbd_dev->spec->snap_id; - payload_len = 0; } ret = -ENOMEM; - op = rbd_create_rw_op(opcode, payload_len); + op = rbd_osd_req_op_create(opcode, seg_ofs, seg_len); if (!op) goto done; - op->extent.offset = seg_ofs; - op->extent.length = seg_len; - op->payload_len = payload_len; /* we've taken care of segment sizes earlier when we cloned the bios. We should never have a segment @@ -1352,7 +1371,7 @@ static int rbd_do_op(struct request *rq, if (ret < 0) rbd_coll_end_req_index(rq, coll, coll_index, (s32)ret, seg_len); - rbd_destroy_op(op); + rbd_osd_req_op_destroy(op); done: kfree(seg_name); return ret; @@ -1370,13 +1389,13 @@ static int rbd_req_sync_read(struct rbd_device *rbd_dev, struct ceph_osd_req_op *op; int ret; - op = rbd_create_rw_op(CEPH_OSD_OP_READ, 0); + op = rbd_osd_req_op_create(CEPH_OSD_OP_READ, ofs, len); if (!op) return -ENOMEM; ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, op, object_name, ofs, len, buf, NULL, ver); - rbd_destroy_op(op); + rbd_osd_req_op_destroy(op); return ret; } @@ -1391,7 +1410,7 @@ static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, struct ceph_osd_req_op *op; int ret; - op = rbd_create_rw_op(CEPH_OSD_OP_NOTIFY_ACK, 0); + op = rbd_create_rw_op(CEPH_OSD_OP_NOTIFY_ACK, 0, 0); if (!op) return -ENOMEM; @@ -1442,7 +1461,7 @@ static int rbd_req_sync_watch(struct rbd_device *rbd_dev, int start) __le64 version = 0; int ret; - op = rbd_create_rw_op(CEPH_OSD_OP_WATCH, 0); + op = rbd_create_rw_op(CEPH_OSD_OP_WATCH, 0, 0); if (!op) return -ENOMEM; @@ -1505,9 +1524,10 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, * operation. */ payload_size = class_name_len + method_name_len + outbound_size; - op = rbd_create_rw_op(CEPH_OSD_OP_CALL, payload_size); + op = rbd_create_rw_op(CEPH_OSD_OP_CALL, 0, 0); if (!op) return -ENOMEM; + op->payload_len = payload_size; op->cls.class_name = class_name; op->cls.class_len = (__u8) class_name_len; -- cgit v1.2.3 From 2647ba38100765298fc67ce1ec5d32e80d9fe046 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 19 Nov 2012 22:55:21 -0600 Subject: rbd: move call osd op setup into rbd_osd_req_op_create() Move the initialization of the CEPH_OSD_OP_CALL operation into rbd_osd_req_op_create(). Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 48 ++++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 235cda083137..760be82548d9 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -52,10 +52,12 @@ #define SECTOR_SHIFT 9 #define SECTOR_SIZE (1ULL << SECTOR_SHIFT) -/* It might be useful to have this defined elsewhere too */ +/* It might be useful to have these defined elsewhere */ -#define U32_MAX ((u32) (~0U)) -#define U64_MAX ((u64) (~0ULL)) +#define U8_MAX ((u8) (~0U)) +#define U16_MAX ((u16) (~0U)) +#define U32_MAX ((u32) (~0U)) +#define U64_MAX ((u64) (~0ULL)) #define RBD_DRV_NAME "rbd" #define RBD_DRV_NAME_LONG "rbd (rados block device)" @@ -1047,6 +1049,7 @@ struct ceph_osd_req_op *rbd_osd_req_op_create(u16 opcode, ...) { struct ceph_osd_req_op *op; va_list args; + size_t size; op = kzalloc(sizeof (*op), GFP_NOIO); if (!op) @@ -1063,6 +1066,27 @@ struct ceph_osd_req_op *rbd_osd_req_op_create(u16 opcode, ...) if (opcode == CEPH_OSD_OP_WRITE) op->payload_len = op->extent.length; break; + case CEPH_OSD_OP_CALL: + /* rbd_osd_req_op_create(CALL, class, method, data, datalen) */ + op->cls.class_name = va_arg(args, char *); + size = strlen(op->cls.class_name); + rbd_assert(size <= (size_t) U8_MAX); + op->cls.class_len = size; + op->payload_len = size; + + op->cls.method_name = va_arg(args, char *); + size = strlen(op->cls.method_name); + rbd_assert(size <= (size_t) U8_MAX); + op->cls.method_len = size; + op->payload_len += size; + + op->cls.argc = 0; + op->cls.indata = va_arg(args, void *); + size = va_arg(args, size_t); + rbd_assert(size <= (size_t) U32_MAX); + op->cls.indata_len = (u32) size; + op->payload_len += size; + break; default: rbd_warn(NULL, "unsupported opcode %hu\n", opcode); kfree(op); @@ -1510,9 +1534,6 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, u64 *ver) { struct ceph_osd_req_op *op; - int class_name_len = strlen(class_name); - int method_name_len = strlen(method_name); - int payload_size; int ret; /* @@ -1523,25 +1544,16 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, * the perspective of the server side) in the OSD request * operation. */ - payload_size = class_name_len + method_name_len + outbound_size; - op = rbd_create_rw_op(CEPH_OSD_OP_CALL, 0, 0); + op = rbd_osd_req_op_create(CEPH_OSD_OP_CALL, class_name, + method_name, outbound, outbound_size); if (!op) return -ENOMEM; - op->payload_len = payload_size; - - op->cls.class_name = class_name; - op->cls.class_len = (__u8) class_name_len; - op->cls.method_name = method_name; - op->cls.method_len = (__u8) method_name_len; - op->cls.argc = 0; - op->cls.indata = outbound; - op->cls.indata_len = outbound_size; ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, op, object_name, 0, inbound_size, inbound, NULL, ver); - rbd_destroy_op(op); + rbd_osd_req_op_destroy(op); dout("cls_exec returned %d\n", ret); return ret; -- cgit v1.2.3 From 5efea49a98d1a3b3a7301d3a17f826ad4c31b290 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 19 Nov 2012 22:55:21 -0600 Subject: rbd: move remaining osd op setup into rbd_osd_req_op_create() The two remaining osd ops used by rbd are CEPH_OSD_OP_WATCH and CEPH_OSD_OP_NOTIFY_ACK. Move the setup of those operations into rbd_osd_req_op_create(), and get rid of rbd_create_rw_op() and rbd_destroy_op(). Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 68 +++++++++++++++++++++-------------------------------- 1 file changed, 27 insertions(+), 41 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 760be82548d9..3802a7857280 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1027,24 +1027,6 @@ out_err: return NULL; } -static struct ceph_osd_req_op *rbd_create_rw_op(int opcode, u64 ofs, u64 len) -{ - struct ceph_osd_req_op *op; - - op = kzalloc(sizeof (*op), GFP_NOIO); - if (!op) - return NULL; - - op->op = opcode; - - return op; -} - -static void rbd_destroy_op(struct ceph_osd_req_op *op) -{ - kfree(op); -} - struct ceph_osd_req_op *rbd_osd_req_op_create(u16 opcode, ...) { struct ceph_osd_req_op *op; @@ -1087,6 +1069,16 @@ struct ceph_osd_req_op *rbd_osd_req_op_create(u16 opcode, ...) op->cls.indata_len = (u32) size; op->payload_len += size; break; + case CEPH_OSD_OP_NOTIFY_ACK: + case CEPH_OSD_OP_WATCH: + /* rbd_osd_req_op_create(NOTIFY_ACK, cookie, version) */ + /* rbd_osd_req_op_create(WATCH, cookie, version, flag) */ + op->watch.cookie = va_arg(args, u64); + op->watch.ver = va_arg(args, u64); + op->watch.ver = cpu_to_le64(op->watch.ver); + if (opcode == CEPH_OSD_OP_WATCH && va_arg(args, int)) + op->watch.flag = (u8) 1; + break; default: rbd_warn(NULL, "unsupported opcode %hu\n", opcode); kfree(op); @@ -1434,14 +1426,10 @@ static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, struct ceph_osd_req_op *op; int ret; - op = rbd_create_rw_op(CEPH_OSD_OP_NOTIFY_ACK, 0, 0); + op = rbd_osd_req_op_create(CEPH_OSD_OP_NOTIFY_ACK, notify_id, ver); if (!op) return -ENOMEM; - op->watch.ver = cpu_to_le64(ver); - op->watch.cookie = notify_id; - op->watch.flag = 0; - ret = rbd_do_request(NULL, rbd_dev, NULL, CEPH_NOSNAP, rbd_dev->header_name, 0, 0, NULL, NULL, 0, @@ -1450,7 +1438,8 @@ static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, NULL, 0, rbd_simple_req_cb, 0, NULL); - rbd_destroy_op(op); + rbd_osd_req_op_destroy(op); + return ret; } @@ -1480,14 +1469,9 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) */ static int rbd_req_sync_watch(struct rbd_device *rbd_dev, int start) { - struct ceph_osd_req_op *op; struct ceph_osd_request **linger_req = NULL; - __le64 version = 0; - int ret; - - op = rbd_create_rw_op(CEPH_OSD_OP_WATCH, 0, 0); - if (!op) - return -ENOMEM; + struct ceph_osd_req_op *op; + int ret = 0; if (start) { struct ceph_osd_client *osdc; @@ -1496,26 +1480,28 @@ static int rbd_req_sync_watch(struct rbd_device *rbd_dev, int start) ret = ceph_osdc_create_event(osdc, rbd_watch_cb, 0, rbd_dev, &rbd_dev->watch_event); if (ret < 0) - goto done; - version = cpu_to_le64(rbd_dev->header.obj_version); + return ret; linger_req = &rbd_dev->watch_request; + } else { + rbd_assert(rbd_dev->watch_request != NULL); } - op->watch.ver = version; - op->watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); - op->watch.flag = (u8) start ? 1 : 0; - - ret = rbd_req_sync_op(rbd_dev, + op = rbd_osd_req_op_create(CEPH_OSD_OP_WATCH, + rbd_dev->watch_event->cookie, + rbd_dev->header.obj_version, start); + if (op) + ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, op, rbd_dev->header_name, 0, 0, NULL, linger_req, NULL); - if (!start || ret < 0) { + /* Cancel the event if we're tearing down, or on error */ + + if (!start || !op || ret < 0) { ceph_osdc_cancel_event(rbd_dev->watch_event); rbd_dev->watch_event = NULL; } -done: - rbd_destroy_op(op); + rbd_osd_req_op_destroy(op); return ret; } -- cgit v1.2.3 From 8b84de7940b69fd7326946ba244621aa5fc412e0 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 20 Nov 2012 14:17:17 -0600 Subject: rbd: assign watch request more directly Both rbd_req_sync_op() and rbd_do_request() have a "linger" parameter, which is the address of a pointer that should refer to the osd request structure used to issue a request to an osd. Only one case ever supplies a non-null "linger" argument: an CEPH_OSD_OP_WATCH start. And in that one case it is assigned &rbd_dev->watch_request. Within rbd_do_request() (where the assignment ultimately gets made) we know the rbd_dev and therefore its watch_request field. We also know whether the op being sent is CEPH_OSD_OP_WATCH start. Stop opaquely passing down the "linger" pointer, and instead just assign the value directly inside rbd_do_request() when it's needed. This makes it unnecessary for rbd_req_sync_watch() to make arrangements to hold a value that's not available until a bit later. This more clearly separates setting up a watch request from submitting it. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 3802a7857280..a3a11c3c1cac 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1158,7 +1158,6 @@ static int rbd_do_request(struct request *rq, int coll_index, void (*rbd_cb)(struct ceph_osd_request *, struct ceph_msg *), - struct ceph_osd_request **linger_req, u64 *ver) { struct ceph_osd_client *osdc; @@ -1210,9 +1209,9 @@ static int rbd_do_request(struct request *rq, ceph_osdc_build_request(osd_req, ofs, len, 1, op, snapc, snapid, &mtime); - if (linger_req) { + if (op->op == CEPH_OSD_OP_WATCH && op->watch.flag) { ceph_osdc_set_request_linger(osdc, osd_req); - *linger_req = osd_req; + rbd_dev->watch_request = osd_req; } ret = ceph_osdc_start_request(osdc, osd_req, false); @@ -1296,7 +1295,6 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, const char *object_name, u64 ofs, u64 inbound_size, char *inbound, - struct ceph_osd_request **linger_req, u64 *ver) { int ret; @@ -1317,7 +1315,7 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, op, NULL, 0, NULL, - linger_req, ver); + ver); if (ret < 0) goto done; @@ -1383,7 +1381,7 @@ static int rbd_do_op(struct request *rq, flags, op, coll, coll_index, - rbd_req_cb, 0, NULL); + rbd_req_cb, NULL); if (ret < 0) rbd_coll_end_req_index(rq, coll, coll_index, (s32)ret, seg_len); @@ -1410,7 +1408,7 @@ static int rbd_req_sync_read(struct rbd_device *rbd_dev, return -ENOMEM; ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, - op, object_name, ofs, len, buf, NULL, ver); + op, object_name, ofs, len, buf, ver); rbd_osd_req_op_destroy(op); return ret; @@ -1436,7 +1434,7 @@ static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, CEPH_OSD_FLAG_READ, op, NULL, 0, - rbd_simple_req_cb, 0, NULL); + rbd_simple_req_cb, NULL); rbd_osd_req_op_destroy(op); @@ -1469,7 +1467,6 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) */ static int rbd_req_sync_watch(struct rbd_device *rbd_dev, int start) { - struct ceph_osd_request **linger_req = NULL; struct ceph_osd_req_op *op; int ret = 0; @@ -1481,7 +1478,6 @@ static int rbd_req_sync_watch(struct rbd_device *rbd_dev, int start) &rbd_dev->watch_event); if (ret < 0) return ret; - linger_req = &rbd_dev->watch_request; } else { rbd_assert(rbd_dev->watch_request != NULL); } @@ -1493,7 +1489,7 @@ static int rbd_req_sync_watch(struct rbd_device *rbd_dev, int start) ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, op, rbd_dev->header_name, - 0, 0, NULL, linger_req, NULL); + 0, 0, NULL, NULL); /* Cancel the event if we're tearing down, or on error */ @@ -1537,7 +1533,7 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, op, object_name, 0, inbound_size, inbound, - NULL, ver); + ver); rbd_osd_req_op_destroy(op); -- cgit v1.2.3 From e0b49868d3629708eda593b6739cb78f33ab238a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 9 Jan 2013 14:44:18 -0600 Subject: rbd: fix type of snap_id in rbd_dev_v2_snap_info() The type of the snap_id local variable is defined with the wrong byte order. Fix that. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index a3a11c3c1cac..007b726ea0eb 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -2802,7 +2802,7 @@ out: static char *rbd_dev_v2_snap_info(struct rbd_device *rbd_dev, u32 which, u64 *snap_size, u64 *snap_features) { - __le64 snap_id; + u64 snap_id; u8 order; int ret; -- cgit v1.2.3 From 17bebdcd5c7c56cde82b8ccb02c5cea69e05f6d3 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 7 Jan 2013 11:00:16 +0100 Subject: crypto: bfin_crc - reposition free_irq to avoid access to invalid data The data referenced by an interrupt handler should not be freed before the interrupt is ended. The handler is bfin_crypto_crc_handler. It may refer to crc->regs, which is released by the iounmap. Furthermore, the second argument to all calls to free_irq is incorrect. It should be the same as the last argument of request_irq, which is crc, rather than crc->dev. The semantic match that finds the first problem is as follows: (http://coccinelle.lip6.fr/) // @fn exists@ expression list es; expression a,b; identifier f; @@ if (...) { ... when any free_irq(a,b); ... when any f(es); ... when any return ...; } @@ expression list fn.es; expression fn.a,fn.b; identifier fn.f; @@ *f(es); ... when any *free_irq(a,b); // Signed-off-by: Julia Lawall Signed-off-by: Herbert Xu --- drivers/crypto/bfin_crc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/crypto/bfin_crc.c b/drivers/crypto/bfin_crc.c index 5398580b4313..06fa1e4f49d3 100644 --- a/drivers/crypto/bfin_crc.c +++ b/drivers/crypto/bfin_crc.c @@ -694,7 +694,7 @@ out_error_dma: dma_free_coherent(&pdev->dev, PAGE_SIZE, crc->sg_cpu, crc->sg_dma); free_dma(crc->dma_ch); out_error_irq: - free_irq(crc->irq, crc->dev); + free_irq(crc->irq, crc); out_error_unmap: iounmap((void *)crc->regs); out_error_free_mem: @@ -720,10 +720,10 @@ static int __devexit bfin_crypto_crc_remove(struct platform_device *pdev) crypto_unregister_ahash(&algs); tasklet_kill(&crc->done_task); - iounmap((void *)crc->regs); free_dma(crc->dma_ch); if (crc->irq > 0) - free_irq(crc->irq, crc->dev); + free_irq(crc->irq, crc); + iounmap((void *)crc->regs); kfree(crc); return 0; -- cgit v1.2.3 From 05f369a89a8ee44e79553462a1322c083dfbb760 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 8 Jan 2013 11:57:38 -0700 Subject: crypto: omap-aes - Remmove unnecessary pr_info noise Remove the unnecessary pr_info() calls from omap_aes_probe() and omap_aes_mod_init(). CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index e66e8ee5a9af..481da719b16e 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -880,8 +880,6 @@ static int omap_aes_probe(struct platform_device *pdev) goto err_algs; } - pr_info("probe() done\n"); - return 0; err_algs: for (j = 0; j < i; j++) @@ -938,8 +936,6 @@ static struct platform_driver omap_aes_driver = { static int __init omap_aes_mod_init(void) { - pr_info("loading %s driver\n", "omap-aes"); - return platform_driver_register(&omap_aes_driver); } -- cgit v1.2.3 From 7219368b05bd05bd3366bfb22fc38d2dc41085e5 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 8 Jan 2013 11:57:39 -0700 Subject: crypto: omap-aes - Don't reset controller for every operation The AES controller only needs to be reset once and that will be done by the hwmod infrastructure, if possible. Therefore, remove the reset code from the omap-aes driver. CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 27 --------------------------- 1 file changed, 27 deletions(-) (limited to 'drivers') diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index 481da719b16e..33cd78305461 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -160,19 +160,6 @@ static void omap_aes_write_n(struct omap_aes_dev *dd, u32 offset, omap_aes_write(dd, offset, *value); } -static int omap_aes_wait(struct omap_aes_dev *dd, u32 offset, u32 bit) -{ - unsigned long timeout = jiffies + DEFAULT_TIMEOUT; - - while (!(omap_aes_read(dd, offset) & bit)) { - if (time_is_before_jiffies(timeout)) { - dev_err(dd->dev, "omap-aes timeout\n"); - return -ETIMEDOUT; - } - } - return 0; -} - static int omap_aes_hw_init(struct omap_aes_dev *dd) { /* @@ -183,20 +170,6 @@ static int omap_aes_hw_init(struct omap_aes_dev *dd) clk_enable(dd->iclk); if (!(dd->flags & FLAGS_INIT)) { - /* is it necessary to reset before every operation? */ - omap_aes_write_mask(dd, AES_REG_MASK, AES_REG_MASK_SOFTRESET, - AES_REG_MASK_SOFTRESET); - /* - * prevent OCP bus error (SRESP) in case an access to the module - * is performed while the module is coming out of soft reset - */ - __asm__ __volatile__("nop"); - __asm__ __volatile__("nop"); - - if (omap_aes_wait(dd, AES_REG_SYSSTATUS, - AES_REG_SYSSTATUS_RESETDONE)) - return -ETIMEDOUT; - dd->flags |= FLAGS_INIT; dd->err = 0; } -- cgit v1.2.3 From 5946c4a5e7707d255faf430969d344ad98430b69 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 8 Jan 2013 11:57:40 -0700 Subject: crypto: omap-aes - Convert to use pm_runtime API Convert the omap-aes crypto driver to use the pm_runtime API instead of the clk API. CC: Kevin Hilman CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index 33cd78305461..c2298521388c 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -19,10 +19,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -96,7 +96,6 @@ struct omap_aes_dev { struct list_head list; unsigned long phys_base; void __iomem *io_base; - struct clk *iclk; struct omap_aes_ctx *ctx; struct device *dev; unsigned long flags; @@ -167,7 +166,7 @@ static int omap_aes_hw_init(struct omap_aes_dev *dd) * It may be long delays between requests. * Device might go to off mode to save power. */ - clk_enable(dd->iclk); + pm_runtime_get_sync(dd->dev); if (!(dd->flags & FLAGS_INIT)) { dd->flags |= FLAGS_INIT; @@ -518,7 +517,7 @@ static void omap_aes_finish_req(struct omap_aes_dev *dd, int err) pr_debug("err: %d\n", err); - clk_disable(dd->iclk); + pm_runtime_put_sync(dd->dev); dd->flags &= ~FLAGS_BUSY; req->base.complete(&req->base, err); @@ -813,26 +812,21 @@ static int omap_aes_probe(struct platform_device *pdev) else dd->dma_in = res->start; - /* Initializing the clock */ - dd->iclk = clk_get(dev, "ick"); - if (IS_ERR(dd->iclk)) { - dev_err(dev, "clock intialization failed.\n"); - err = PTR_ERR(dd->iclk); - goto err_res; - } - dd->io_base = ioremap(dd->phys_base, SZ_4K); if (!dd->io_base) { dev_err(dev, "can't ioremap\n"); err = -ENOMEM; - goto err_io; + goto err_res; } - clk_enable(dd->iclk); + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + reg = omap_aes_read(dd, AES_REG_REV); dev_info(dev, "OMAP AES hw accel rev: %u.%u\n", (reg & AES_REG_REV_MAJOR) >> 4, reg & AES_REG_REV_MINOR); - clk_disable(dd->iclk); + + pm_runtime_put_sync(dev); tasklet_init(&dd->done_task, omap_aes_done_task, (unsigned long)dd); tasklet_init(&dd->queue_task, omap_aes_queue_task, (unsigned long)dd); @@ -862,8 +856,7 @@ err_dma: tasklet_kill(&dd->done_task); tasklet_kill(&dd->queue_task); iounmap(dd->io_base); -err_io: - clk_put(dd->iclk); + pm_runtime_disable(dev); err_res: kfree(dd); dd = NULL; @@ -891,7 +884,7 @@ static int omap_aes_remove(struct platform_device *pdev) tasklet_kill(&dd->queue_task); omap_aes_dma_cleanup(dd); iounmap(dd->io_base); - clk_put(dd->iclk); + pm_runtime_disable(dd->dev); kfree(dd); dd = NULL; -- cgit v1.2.3 From 0635fb3a3c6a6d1a70996428016dca6d3d8f0961 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 8 Jan 2013 11:57:41 -0700 Subject: crypto: omap-aes - Add suspend/resume support Add suspend/resume support to the OMAP AES driver. CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers') diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index c2298521388c..3262139eb9bd 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -891,12 +891,31 @@ static int omap_aes_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int omap_aes_suspend(struct device *dev) +{ + pm_runtime_put_sync(dev); + return 0; +} + +static int omap_aes_resume(struct device *dev) +{ + pm_runtime_get_sync(dev); + return 0; +} +#endif + +static const struct dev_pm_ops omap_aes_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(omap_aes_suspend, omap_aes_resume) +}; + static struct platform_driver omap_aes_driver = { .probe = omap_aes_probe, .remove = omap_aes_remove, .driver = { .name = "omap-aes", .owner = THIS_MODULE, + .pm = &omap_aes_pm_ops, }, }; -- cgit v1.2.3 From ebedbf79026bebe6667322c2407bf05023600929 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 8 Jan 2013 11:57:42 -0700 Subject: crypto: omap-aes - Add code to use dmaengine API Add code to use the new dmaengine API alongside the existing DMA code that uses the private OMAP DMA API. The API to use is chosen by defining or undefining 'OMAP_AES_DMA_PRIVATE'. CC: Russell King CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 184 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 183 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index 3262139eb9bd..14ec9e209ef8 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -12,6 +12,8 @@ * */ +#define OMAP_AES_DMA_PRIVATE + #define pr_fmt(fmt) "%s: " fmt, __func__ #include @@ -22,6 +24,8 @@ #include #include #include +#include +#include #include #include #include @@ -29,7 +33,8 @@ #include #include -#include +#define DST_MAXBURST 4 +#define DMA_MIN (DST_MAXBURST * sizeof(u32)) /* OMAP TRM gives bitfields as start:end, where start is the higher bit number. For example 7:0 */ @@ -110,19 +115,33 @@ struct omap_aes_dev { struct ablkcipher_request *req; size_t total; struct scatterlist *in_sg; +#ifndef OMAP_AES_DMA_PRIVATE + struct scatterlist in_sgl; +#endif size_t in_offset; struct scatterlist *out_sg; +#ifndef OMAP_AES_DMA_PRIVATE + struct scatterlist out_sgl; +#endif size_t out_offset; size_t buflen; void *buf_in; size_t dma_size; int dma_in; +#ifdef OMAP_AES_DMA_PRIVATE int dma_lch_in; +#else + struct dma_chan *dma_lch_in; +#endif dma_addr_t dma_addr_in; void *buf_out; int dma_out; +#ifdef OMAP_AES_DMA_PRIVATE int dma_lch_out; +#else + struct dma_chan *dma_lch_out; +#endif dma_addr_t dma_addr_out; }; @@ -187,10 +206,17 @@ static int omap_aes_write_ctrl(struct omap_aes_dev *dd) return err; val = 0; +#ifdef OMAP_AES_DMA_PRIVATE if (dd->dma_lch_out >= 0) val |= AES_REG_MASK_DMA_OUT_EN; if (dd->dma_lch_in >= 0) val |= AES_REG_MASK_DMA_IN_EN; +#else + if (dd->dma_lch_out != NULL) + val |= AES_REG_MASK_DMA_OUT_EN; + if (dd->dma_lch_in != NULL) + val |= AES_REG_MASK_DMA_IN_EN; +#endif mask = AES_REG_MASK_DMA_IN_EN | AES_REG_MASK_DMA_OUT_EN; @@ -218,6 +244,7 @@ static int omap_aes_write_ctrl(struct omap_aes_dev *dd) omap_aes_write_mask(dd, AES_REG_CTRL, val, mask); +#ifdef OMAP_AES_DMA_PRIVATE /* IN */ omap_set_dma_dest_params(dd->dma_lch_in, 0, OMAP_DMA_AMODE_CONSTANT, dd->phys_base + AES_REG_DATA, 0, 4); @@ -231,6 +258,7 @@ static int omap_aes_write_ctrl(struct omap_aes_dev *dd) omap_set_dma_src_burst_mode(dd->dma_lch_out, OMAP_DMA_DATA_BURST_4); omap_set_dma_dest_burst_mode(dd->dma_lch_out, OMAP_DMA_DATA_BURST_4); +#endif return 0; } @@ -256,6 +284,7 @@ static struct omap_aes_dev *omap_aes_find_dev(struct omap_aes_ctx *ctx) return dd; } +#ifdef OMAP_AES_DMA_PRIVATE static void omap_aes_dma_callback(int lch, u16 ch_status, void *data) { struct omap_aes_dev *dd = data; @@ -271,13 +300,30 @@ static void omap_aes_dma_callback(int lch, u16 ch_status, void *data) /* dma_lch_out - completed */ tasklet_schedule(&dd->done_task); } +#else +static void omap_aes_dma_out_callback(void *data) +{ + struct omap_aes_dev *dd = data; + + /* dma_lch_out - completed */ + tasklet_schedule(&dd->done_task); +} +#endif static int omap_aes_dma_init(struct omap_aes_dev *dd) { int err = -ENOMEM; +#ifndef OMAP_AES_DMA_PRIVATE + dma_cap_mask_t mask; +#endif +#ifdef OMAP_AES_DMA_PRIVATE dd->dma_lch_out = -1; dd->dma_lch_in = -1; +#else + dd->dma_lch_out = NULL; + dd->dma_lch_in = NULL; +#endif dd->buf_in = (void *)__get_free_pages(GFP_KERNEL, OMAP_AES_CACHE_SIZE); dd->buf_out = (void *)__get_free_pages(GFP_KERNEL, OMAP_AES_CACHE_SIZE); @@ -306,6 +352,7 @@ static int omap_aes_dma_init(struct omap_aes_dev *dd) goto err_map_out; } +#ifdef OMAP_AES_DMA_PRIVATE err = omap_request_dma(dd->dma_in, "omap-aes-rx", omap_aes_dma_callback, dd, &dd->dma_lch_in); if (err) { @@ -318,11 +365,33 @@ static int omap_aes_dma_init(struct omap_aes_dev *dd) dev_err(dd->dev, "Unable to request DMA channel\n"); goto err_dma_out; } +#else + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + dd->dma_lch_in = dma_request_channel(mask, omap_dma_filter_fn, + &dd->dma_in); + if (!dd->dma_lch_in) { + dev_err(dd->dev, "Unable to request in DMA channel\n"); + goto err_dma_in; + } + + dd->dma_lch_out = dma_request_channel(mask, omap_dma_filter_fn, + &dd->dma_out); + if (!dd->dma_lch_out) { + dev_err(dd->dev, "Unable to request out DMA channel\n"); + goto err_dma_out; + } +#endif return 0; err_dma_out: +#ifdef OMAP_AES_DMA_PRIVATE omap_free_dma(dd->dma_lch_in); +#else + dma_release_channel(dd->dma_lch_in); +#endif err_dma_in: dma_unmap_single(dd->dev, dd->dma_addr_out, dd->buflen, DMA_FROM_DEVICE); @@ -339,8 +408,13 @@ err_alloc: static void omap_aes_dma_cleanup(struct omap_aes_dev *dd) { +#ifdef OMAP_AES_DMA_PRIVATE omap_free_dma(dd->dma_lch_out); omap_free_dma(dd->dma_lch_in); +#else + dma_release_channel(dd->dma_lch_out); + dma_release_channel(dd->dma_lch_in); +#endif dma_unmap_single(dd->dev, dd->dma_addr_out, dd->buflen, DMA_FROM_DEVICE); dma_unmap_single(dd->dev, dd->dma_addr_in, dd->buflen, DMA_TO_DEVICE); @@ -398,12 +472,24 @@ static int sg_copy(struct scatterlist **sg, size_t *offset, void *buf, return off; } +#ifdef OMAP_AES_DMA_PRIVATE static int omap_aes_crypt_dma(struct crypto_tfm *tfm, dma_addr_t dma_addr_in, dma_addr_t dma_addr_out, int length) +#else +static int omap_aes_crypt_dma(struct crypto_tfm *tfm, + struct scatterlist *in_sg, struct scatterlist *out_sg) +#endif { struct omap_aes_ctx *ctx = crypto_tfm_ctx(tfm); struct omap_aes_dev *dd = ctx->dd; +#ifdef OMAP_AES_DMA_PRIVATE int len32; +#else + struct dma_async_tx_descriptor *tx_in, *tx_out; + struct dma_slave_config cfg; + dma_addr_t dma_addr_in = sg_dma_address(in_sg); + int ret, length = sg_dma_len(in_sg); +#endif pr_debug("len: %d\n", length); @@ -413,6 +499,7 @@ static int omap_aes_crypt_dma(struct crypto_tfm *tfm, dma_addr_t dma_addr_in, dma_sync_single_for_device(dd->dev, dma_addr_in, length, DMA_TO_DEVICE); +#ifdef OMAP_AES_DMA_PRIVATE len32 = DIV_ROUND_UP(length, sizeof(u32)); /* IN */ @@ -433,6 +520,60 @@ static int omap_aes_crypt_dma(struct crypto_tfm *tfm, dma_addr_t dma_addr_in, omap_start_dma(dd->dma_lch_in); omap_start_dma(dd->dma_lch_out); +#else + memset(&cfg, 0, sizeof(cfg)); + + cfg.src_addr = dd->phys_base + AES_REG_DATA; + cfg.dst_addr = dd->phys_base + AES_REG_DATA; + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.src_maxburst = DST_MAXBURST; + cfg.dst_maxburst = DST_MAXBURST; + + /* IN */ + ret = dmaengine_slave_config(dd->dma_lch_in, &cfg); + if (ret) { + dev_err(dd->dev, "can't configure IN dmaengine slave: %d\n", + ret); + return ret; + } + + tx_in = dmaengine_prep_slave_sg(dd->dma_lch_in, in_sg, 1, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tx_in) { + dev_err(dd->dev, "IN prep_slave_sg() failed\n"); + return -EINVAL; + } + + /* No callback necessary */ + tx_in->callback_param = dd; + + /* OUT */ + ret = dmaengine_slave_config(dd->dma_lch_out, &cfg); + if (ret) { + dev_err(dd->dev, "can't configure OUT dmaengine slave: %d\n", + ret); + return ret; + } + + tx_out = dmaengine_prep_slave_sg(dd->dma_lch_out, out_sg, 1, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tx_out) { + dev_err(dd->dev, "OUT prep_slave_sg() failed\n"); + return -EINVAL; + } + + tx_out->callback = omap_aes_dma_out_callback; + tx_out->callback_param = dd; + + dmaengine_submit(tx_in); + dmaengine_submit(tx_out); + + dma_async_issue_pending(dd->dma_lch_in); + dma_async_issue_pending(dd->dma_lch_out); +#endif /* start DMA or disable idle mode */ omap_aes_write_mask(dd, AES_REG_MASK, AES_REG_MASK_START, @@ -448,6 +589,10 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd) int err, fast = 0, in, out; size_t count; dma_addr_t addr_in, addr_out; +#ifndef OMAP_AES_DMA_PRIVATE + struct scatterlist *in_sg, *out_sg; + int len32; +#endif pr_debug("total: %d\n", dd->total); @@ -486,6 +631,11 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd) addr_in = sg_dma_address(dd->in_sg); addr_out = sg_dma_address(dd->out_sg); +#ifndef OMAP_AES_DMA_PRIVATE + in_sg = dd->in_sg; + out_sg = dd->out_sg; +#endif + dd->flags |= FLAGS_FAST; } else { @@ -493,6 +643,29 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd) count = sg_copy(&dd->in_sg, &dd->in_offset, dd->buf_in, dd->buflen, dd->total, 0); +#ifndef OMAP_AES_DMA_PRIVATE + len32 = DIV_ROUND_UP(count, DMA_MIN) * DMA_MIN; + + /* + * The data going into the AES module has been copied + * to a local buffer and the data coming out will go + * into a local buffer so set up local SG entries for + * both. + */ + sg_init_table(&dd->in_sgl, 1); + dd->in_sgl.offset = dd->in_offset; + sg_dma_len(&dd->in_sgl) = len32; + sg_dma_address(&dd->in_sgl) = dd->dma_addr_in; + + sg_init_table(&dd->out_sgl, 1); + dd->out_sgl.offset = dd->out_offset; + sg_dma_len(&dd->out_sgl) = len32; + sg_dma_address(&dd->out_sgl) = dd->dma_addr_out; + + in_sg = &dd->in_sgl; + out_sg = &dd->out_sgl; +#endif + addr_in = dd->dma_addr_in; addr_out = dd->dma_addr_out; @@ -502,7 +675,11 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd) dd->total -= count; +#ifdef OMAP_AES_DMA_PRIVATE err = omap_aes_crypt_dma(tfm, addr_in, addr_out, count); +#else + err = omap_aes_crypt_dma(tfm, in_sg, out_sg); +#endif if (err) { dma_unmap_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE); dma_unmap_sg(dd->dev, dd->out_sg, 1, DMA_TO_DEVICE); @@ -532,8 +709,13 @@ static int omap_aes_crypt_dma_stop(struct omap_aes_dev *dd) omap_aes_write_mask(dd, AES_REG_MASK, 0, AES_REG_MASK_START); +#ifdef OMAP_AES_DMA_PRIVATE omap_stop_dma(dd->dma_lch_in); omap_stop_dma(dd->dma_lch_out); +#else + dmaengine_terminate_all(dd->dma_lch_in); + dmaengine_terminate_all(dd->dma_lch_out); +#endif if (dd->flags & FLAGS_FAST) { dma_unmap_sg(dd->dev, dd->out_sg, 1, DMA_FROM_DEVICE); -- cgit v1.2.3 From 44f04c1d6f34fee940daed71151ca35b3f1f1e64 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 8 Jan 2013 11:57:43 -0700 Subject: crypto: omap-aes - Remove usage of private DMA API Remove usage of the private OMAP DMA API. The dmaengine API will be used instead. CC: Russell King CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 133 ---------------------------------------------- 1 file changed, 133 deletions(-) (limited to 'drivers') diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index 14ec9e209ef8..faf522ff82d7 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -12,8 +12,6 @@ * */ -#define OMAP_AES_DMA_PRIVATE - #define pr_fmt(fmt) "%s: " fmt, __func__ #include @@ -115,33 +113,21 @@ struct omap_aes_dev { struct ablkcipher_request *req; size_t total; struct scatterlist *in_sg; -#ifndef OMAP_AES_DMA_PRIVATE struct scatterlist in_sgl; -#endif size_t in_offset; struct scatterlist *out_sg; -#ifndef OMAP_AES_DMA_PRIVATE struct scatterlist out_sgl; -#endif size_t out_offset; size_t buflen; void *buf_in; size_t dma_size; int dma_in; -#ifdef OMAP_AES_DMA_PRIVATE - int dma_lch_in; -#else struct dma_chan *dma_lch_in; -#endif dma_addr_t dma_addr_in; void *buf_out; int dma_out; -#ifdef OMAP_AES_DMA_PRIVATE - int dma_lch_out; -#else struct dma_chan *dma_lch_out; -#endif dma_addr_t dma_addr_out; }; @@ -206,17 +192,10 @@ static int omap_aes_write_ctrl(struct omap_aes_dev *dd) return err; val = 0; -#ifdef OMAP_AES_DMA_PRIVATE - if (dd->dma_lch_out >= 0) - val |= AES_REG_MASK_DMA_OUT_EN; - if (dd->dma_lch_in >= 0) - val |= AES_REG_MASK_DMA_IN_EN; -#else if (dd->dma_lch_out != NULL) val |= AES_REG_MASK_DMA_OUT_EN; if (dd->dma_lch_in != NULL) val |= AES_REG_MASK_DMA_IN_EN; -#endif mask = AES_REG_MASK_DMA_IN_EN | AES_REG_MASK_DMA_OUT_EN; @@ -244,22 +223,6 @@ static int omap_aes_write_ctrl(struct omap_aes_dev *dd) omap_aes_write_mask(dd, AES_REG_CTRL, val, mask); -#ifdef OMAP_AES_DMA_PRIVATE - /* IN */ - omap_set_dma_dest_params(dd->dma_lch_in, 0, OMAP_DMA_AMODE_CONSTANT, - dd->phys_base + AES_REG_DATA, 0, 4); - - omap_set_dma_dest_burst_mode(dd->dma_lch_in, OMAP_DMA_DATA_BURST_4); - omap_set_dma_src_burst_mode(dd->dma_lch_in, OMAP_DMA_DATA_BURST_4); - - /* OUT */ - omap_set_dma_src_params(dd->dma_lch_out, 0, OMAP_DMA_AMODE_CONSTANT, - dd->phys_base + AES_REG_DATA, 0, 4); - - omap_set_dma_src_burst_mode(dd->dma_lch_out, OMAP_DMA_DATA_BURST_4); - omap_set_dma_dest_burst_mode(dd->dma_lch_out, OMAP_DMA_DATA_BURST_4); -#endif - return 0; } @@ -284,23 +247,6 @@ static struct omap_aes_dev *omap_aes_find_dev(struct omap_aes_ctx *ctx) return dd; } -#ifdef OMAP_AES_DMA_PRIVATE -static void omap_aes_dma_callback(int lch, u16 ch_status, void *data) -{ - struct omap_aes_dev *dd = data; - - if (ch_status != OMAP_DMA_BLOCK_IRQ) { - pr_err("omap-aes DMA error status: 0x%hx\n", ch_status); - dd->err = -EIO; - dd->flags &= ~FLAGS_INIT; /* request to re-initialize */ - } else if (lch == dd->dma_lch_in) { - return; - } - - /* dma_lch_out - completed */ - tasklet_schedule(&dd->done_task); -} -#else static void omap_aes_dma_out_callback(void *data) { struct omap_aes_dev *dd = data; @@ -308,22 +254,14 @@ static void omap_aes_dma_out_callback(void *data) /* dma_lch_out - completed */ tasklet_schedule(&dd->done_task); } -#endif static int omap_aes_dma_init(struct omap_aes_dev *dd) { int err = -ENOMEM; -#ifndef OMAP_AES_DMA_PRIVATE dma_cap_mask_t mask; -#endif -#ifdef OMAP_AES_DMA_PRIVATE - dd->dma_lch_out = -1; - dd->dma_lch_in = -1; -#else dd->dma_lch_out = NULL; dd->dma_lch_in = NULL; -#endif dd->buf_in = (void *)__get_free_pages(GFP_KERNEL, OMAP_AES_CACHE_SIZE); dd->buf_out = (void *)__get_free_pages(GFP_KERNEL, OMAP_AES_CACHE_SIZE); @@ -352,20 +290,6 @@ static int omap_aes_dma_init(struct omap_aes_dev *dd) goto err_map_out; } -#ifdef OMAP_AES_DMA_PRIVATE - err = omap_request_dma(dd->dma_in, "omap-aes-rx", - omap_aes_dma_callback, dd, &dd->dma_lch_in); - if (err) { - dev_err(dd->dev, "Unable to request DMA channel\n"); - goto err_dma_in; - } - err = omap_request_dma(dd->dma_out, "omap-aes-tx", - omap_aes_dma_callback, dd, &dd->dma_lch_out); - if (err) { - dev_err(dd->dev, "Unable to request DMA channel\n"); - goto err_dma_out; - } -#else dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); @@ -382,16 +306,11 @@ static int omap_aes_dma_init(struct omap_aes_dev *dd) dev_err(dd->dev, "Unable to request out DMA channel\n"); goto err_dma_out; } -#endif return 0; err_dma_out: -#ifdef OMAP_AES_DMA_PRIVATE - omap_free_dma(dd->dma_lch_in); -#else dma_release_channel(dd->dma_lch_in); -#endif err_dma_in: dma_unmap_single(dd->dev, dd->dma_addr_out, dd->buflen, DMA_FROM_DEVICE); @@ -408,13 +327,8 @@ err_alloc: static void omap_aes_dma_cleanup(struct omap_aes_dev *dd) { -#ifdef OMAP_AES_DMA_PRIVATE - omap_free_dma(dd->dma_lch_out); - omap_free_dma(dd->dma_lch_in); -#else dma_release_channel(dd->dma_lch_out); dma_release_channel(dd->dma_lch_in); -#endif dma_unmap_single(dd->dev, dd->dma_addr_out, dd->buflen, DMA_FROM_DEVICE); dma_unmap_single(dd->dev, dd->dma_addr_in, dd->buflen, DMA_TO_DEVICE); @@ -472,24 +386,15 @@ static int sg_copy(struct scatterlist **sg, size_t *offset, void *buf, return off; } -#ifdef OMAP_AES_DMA_PRIVATE -static int omap_aes_crypt_dma(struct crypto_tfm *tfm, dma_addr_t dma_addr_in, - dma_addr_t dma_addr_out, int length) -#else static int omap_aes_crypt_dma(struct crypto_tfm *tfm, struct scatterlist *in_sg, struct scatterlist *out_sg) -#endif { struct omap_aes_ctx *ctx = crypto_tfm_ctx(tfm); struct omap_aes_dev *dd = ctx->dd; -#ifdef OMAP_AES_DMA_PRIVATE - int len32; -#else struct dma_async_tx_descriptor *tx_in, *tx_out; struct dma_slave_config cfg; dma_addr_t dma_addr_in = sg_dma_address(in_sg); int ret, length = sg_dma_len(in_sg); -#endif pr_debug("len: %d\n", length); @@ -499,28 +404,6 @@ static int omap_aes_crypt_dma(struct crypto_tfm *tfm, dma_sync_single_for_device(dd->dev, dma_addr_in, length, DMA_TO_DEVICE); -#ifdef OMAP_AES_DMA_PRIVATE - len32 = DIV_ROUND_UP(length, sizeof(u32)); - - /* IN */ - omap_set_dma_transfer_params(dd->dma_lch_in, OMAP_DMA_DATA_TYPE_S32, - len32, 1, OMAP_DMA_SYNC_PACKET, dd->dma_in, - OMAP_DMA_DST_SYNC); - - omap_set_dma_src_params(dd->dma_lch_in, 0, OMAP_DMA_AMODE_POST_INC, - dma_addr_in, 0, 0); - - /* OUT */ - omap_set_dma_transfer_params(dd->dma_lch_out, OMAP_DMA_DATA_TYPE_S32, - len32, 1, OMAP_DMA_SYNC_PACKET, - dd->dma_out, OMAP_DMA_SRC_SYNC); - - omap_set_dma_dest_params(dd->dma_lch_out, 0, OMAP_DMA_AMODE_POST_INC, - dma_addr_out, 0, 0); - - omap_start_dma(dd->dma_lch_in); - omap_start_dma(dd->dma_lch_out); -#else memset(&cfg, 0, sizeof(cfg)); cfg.src_addr = dd->phys_base + AES_REG_DATA; @@ -573,7 +456,6 @@ static int omap_aes_crypt_dma(struct crypto_tfm *tfm, dma_async_issue_pending(dd->dma_lch_in); dma_async_issue_pending(dd->dma_lch_out); -#endif /* start DMA or disable idle mode */ omap_aes_write_mask(dd, AES_REG_MASK, AES_REG_MASK_START, @@ -589,10 +471,8 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd) int err, fast = 0, in, out; size_t count; dma_addr_t addr_in, addr_out; -#ifndef OMAP_AES_DMA_PRIVATE struct scatterlist *in_sg, *out_sg; int len32; -#endif pr_debug("total: %d\n", dd->total); @@ -631,10 +511,8 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd) addr_in = sg_dma_address(dd->in_sg); addr_out = sg_dma_address(dd->out_sg); -#ifndef OMAP_AES_DMA_PRIVATE in_sg = dd->in_sg; out_sg = dd->out_sg; -#endif dd->flags |= FLAGS_FAST; @@ -643,7 +521,6 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd) count = sg_copy(&dd->in_sg, &dd->in_offset, dd->buf_in, dd->buflen, dd->total, 0); -#ifndef OMAP_AES_DMA_PRIVATE len32 = DIV_ROUND_UP(count, DMA_MIN) * DMA_MIN; /* @@ -664,7 +541,6 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd) in_sg = &dd->in_sgl; out_sg = &dd->out_sgl; -#endif addr_in = dd->dma_addr_in; addr_out = dd->dma_addr_out; @@ -675,11 +551,7 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd) dd->total -= count; -#ifdef OMAP_AES_DMA_PRIVATE - err = omap_aes_crypt_dma(tfm, addr_in, addr_out, count); -#else err = omap_aes_crypt_dma(tfm, in_sg, out_sg); -#endif if (err) { dma_unmap_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE); dma_unmap_sg(dd->dev, dd->out_sg, 1, DMA_TO_DEVICE); @@ -709,13 +581,8 @@ static int omap_aes_crypt_dma_stop(struct omap_aes_dev *dd) omap_aes_write_mask(dd, AES_REG_MASK, 0, AES_REG_MASK_START); -#ifdef OMAP_AES_DMA_PRIVATE - omap_stop_dma(dd->dma_lch_in); - omap_stop_dma(dd->dma_lch_out); -#else dmaengine_terminate_all(dd->dma_lch_in); dmaengine_terminate_all(dd->dma_lch_out); -#endif if (dd->flags & FLAGS_FAST) { dma_unmap_sg(dd->dev, dd->out_sg, 1, DMA_FROM_DEVICE); -- cgit v1.2.3 From bc69d124d8141dff942c8c7fbaae76f9c0f4c796 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 8 Jan 2013 11:57:44 -0700 Subject: crypto: omap-aes - Add Device Tree Support Add Device Tree suport to the omap-aes crypto driver. Currently, only support for OMAP2 and OMAP3 is being added but support for OMAP4 will be added in a subsequent patch. CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 123 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 97 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index faf522ff82d7..dfebd4025654 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -25,6 +25,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -819,11 +822,97 @@ static struct crypto_alg algs[] = { } }; +#ifdef CONFIG_OF +static const struct of_device_id omap_aes_of_match[] = { + { + .compatible = "ti,omap2-aes", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, omap_aes_of_match); + +static int omap_aes_get_res_of(struct omap_aes_dev *dd, + struct device *dev, struct resource *res) +{ + struct device_node *node = dev->of_node; + const struct of_device_id *match; + int err = 0; + + match = of_match_device(of_match_ptr(omap_aes_of_match), dev); + if (!match) { + dev_err(dev, "no compatible OF match\n"); + err = -EINVAL; + goto err; + } + + err = of_address_to_resource(node, 0, res); + if (err < 0) { + dev_err(dev, "can't translate OF node address\n"); + err = -EINVAL; + goto err; + } + + dd->dma_out = -1; /* Dummy value that's unused */ + dd->dma_in = -1; /* Dummy value that's unused */ + +err: + return err; +} +#else +static const struct of_device_id omap_aes_of_match[] = { + {}, +}; + +static int omap_aes_get_res_of(struct omap_aes_dev *dd, + struct device *dev, struct resource *res) +{ + return -EINVAL; +} +#endif + +static int omap_aes_get_res_pdev(struct omap_aes_dev *dd, + struct platform_device *pdev, struct resource *res) +{ + struct device *dev = &pdev->dev; + struct resource *r; + int err = 0; + + /* Get the base address */ + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(dev, "no MEM resource info\n"); + err = -ENODEV; + goto err; + } + memcpy(res, r, sizeof(*res)); + + /* Get the DMA out channel */ + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!r) { + dev_err(dev, "no DMA out resource info\n"); + err = -ENODEV; + goto err; + } + dd->dma_out = r->start; + + /* Get the DMA in channel */ + r = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!r) { + dev_err(dev, "no DMA in resource info\n"); + err = -ENODEV; + goto err; + } + dd->dma_in = r->start; + +err: + return err; +} + static int omap_aes_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct omap_aes_dev *dd; - struct resource *res; + struct resource res; int err = -ENOMEM, i, j; u32 reg; @@ -838,35 +927,18 @@ static int omap_aes_probe(struct platform_device *pdev) spin_lock_init(&dd->lock); crypto_init_queue(&dd->queue, OMAP_AES_QUEUE_LENGTH); - /* Get the base address */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "invalid resource type\n"); - err = -ENODEV; + err = (dev->of_node) ? omap_aes_get_res_of(dd, dev, &res) : + omap_aes_get_res_pdev(dd, pdev, &res); + if (err) goto err_res; - } - dd->phys_base = res->start; - - /* Get the DMA */ - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!res) - dev_info(dev, "no DMA info\n"); - else - dd->dma_out = res->start; - - /* Get the DMA */ - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!res) - dev_info(dev, "no DMA info\n"); - else - dd->dma_in = res->start; - - dd->io_base = ioremap(dd->phys_base, SZ_4K); + + dd->io_base = devm_request_and_ioremap(dev, &res); if (!dd->io_base) { dev_err(dev, "can't ioremap\n"); err = -ENOMEM; goto err_res; } + dd->phys_base = res.start; pm_runtime_enable(dev); pm_runtime_get_sync(dev); @@ -904,7 +976,6 @@ err_algs: err_dma: tasklet_kill(&dd->done_task); tasklet_kill(&dd->queue_task); - iounmap(dd->io_base); pm_runtime_disable(dev); err_res: kfree(dd); @@ -932,7 +1003,6 @@ static int omap_aes_remove(struct platform_device *pdev) tasklet_kill(&dd->done_task); tasklet_kill(&dd->queue_task); omap_aes_dma_cleanup(dd); - iounmap(dd->io_base); pm_runtime_disable(dd->dev); kfree(dd); dd = NULL; @@ -965,6 +1035,7 @@ static struct platform_driver omap_aes_driver = { .name = "omap-aes", .owner = THIS_MODULE, .pm = &omap_aes_pm_ops, + .of_match_table = omap_aes_of_match, }, }; -- cgit v1.2.3 From b4b87a934c30fb91cbdd18ae028acdc361e1cf0f Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 8 Jan 2013 11:57:45 -0700 Subject: crypto: omap-aes - Convert to dma_request_slave_channel_compat() Use the dma_request_slave_channel_compat() call instead of the dma_request_channel() call to request a DMA channel. This allows the omap-aes driver use different DMA engines. CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index dfebd4025654..d34aa5df3dc1 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -296,15 +296,19 @@ static int omap_aes_dma_init(struct omap_aes_dev *dd) dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - dd->dma_lch_in = dma_request_channel(mask, omap_dma_filter_fn, - &dd->dma_in); + dd->dma_lch_in = dma_request_slave_channel_compat(mask, + omap_dma_filter_fn, + &dd->dma_in, + dd->dev, "rx"); if (!dd->dma_lch_in) { dev_err(dd->dev, "Unable to request in DMA channel\n"); goto err_dma_in; } - dd->dma_lch_out = dma_request_channel(mask, omap_dma_filter_fn, - &dd->dma_out); + dd->dma_lch_out = dma_request_slave_channel_compat(mask, + omap_dma_filter_fn, + &dd->dma_out, + dd->dev, "tx"); if (!dd->dma_lch_out) { dev_err(dd->dev, "Unable to request out DMA channel\n"); goto err_dma_out; -- cgit v1.2.3 From 0d35583a13ad29af06375678daa2e11772ec9267 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 8 Jan 2013 11:57:46 -0700 Subject: crypto: omap-aes - Add OMAP4/AM33XX AES Support Add support for the OMAP4 version of the AES module that is present on OMAP4 and AM33xx SoCs. The modules have several differences including register offsets and how DMA is triggered. To handle these differences, a platform_data structure is defined and contains routine pointers, register offsets, and bit offsets within registers. OMAP2/OMAP3-specific routines are suffixed with '_omap2' and OMAP4/AM33xx routines are suffixed with '_omap4'. Note: The code being integrated is from the TI AM33xx SDK and was written by Greg Turner and Herman Schuurman (current email unknown) while at TI. CC: Greg Turner CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 158 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 125 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index d34aa5df3dc1..bd1ad97404ab 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -5,6 +5,7 @@ * * Copyright (c) 2010 Nokia Corporation * Author: Dmitry Kasatkin + * Copyright (c) 2011 Texas Instruments Incorporated * * 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 @@ -42,10 +43,11 @@ #define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end)) #define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end)) -#define AES_REG_KEY(x) (0x1C - ((x ^ 0x01) * 0x04)) -#define AES_REG_IV(x) (0x20 + ((x) * 0x04)) +#define AES_REG_KEY(dd, x) ((dd)->pdata->key_ofs - \ + ((x ^ 0x01) * 0x04)) +#define AES_REG_IV(dd, x) ((dd)->pdata->iv_ofs + ((x) * 0x04)) -#define AES_REG_CTRL 0x30 +#define AES_REG_CTRL(dd) ((dd)->pdata->ctrl_ofs) #define AES_REG_CTRL_CTR_WIDTH (1 << 7) #define AES_REG_CTRL_CTR (1 << 6) #define AES_REG_CTRL_CBC (1 << 5) @@ -54,14 +56,11 @@ #define AES_REG_CTRL_INPUT_READY (1 << 1) #define AES_REG_CTRL_OUTPUT_READY (1 << 0) -#define AES_REG_DATA 0x34 -#define AES_REG_DATA_N(x) (0x34 + ((x) * 0x04)) +#define AES_REG_DATA_N(dd, x) ((dd)->pdata->data_ofs + ((x) * 0x04)) -#define AES_REG_REV 0x44 -#define AES_REG_REV_MAJOR 0xF0 -#define AES_REG_REV_MINOR 0x0F +#define AES_REG_REV(dd) ((dd)->pdata->rev_ofs) -#define AES_REG_MASK 0x48 +#define AES_REG_MASK(dd) ((dd)->pdata->mask_ofs) #define AES_REG_MASK_SIDLE (1 << 6) #define AES_REG_MASK_START (1 << 5) #define AES_REG_MASK_DMA_OUT_EN (1 << 3) @@ -69,8 +68,7 @@ #define AES_REG_MASK_SOFTRESET (1 << 1) #define AES_REG_AUTOIDLE (1 << 0) -#define AES_REG_SYSSTATUS 0x4C -#define AES_REG_SYSSTATUS_RESETDONE (1 << 0) +#define AES_REG_LENGTH_N(x) (0x54 + ((x) * 0x04)) #define DEFAULT_TIMEOUT (5*HZ) @@ -98,6 +96,26 @@ struct omap_aes_reqctx { #define OMAP_AES_QUEUE_LENGTH 1 #define OMAP_AES_CACHE_SIZE 0 +struct omap_aes_pdata { + void (*trigger)(struct omap_aes_dev *dd, int length); + + u32 key_ofs; + u32 iv_ofs; + u32 ctrl_ofs; + u32 data_ofs; + u32 rev_ofs; + u32 mask_ofs; + + u32 dma_enable_in; + u32 dma_enable_out; + u32 dma_start; + + u32 major_mask; + u32 major_shift; + u32 minor_mask; + u32 minor_shift; +}; + struct omap_aes_dev { struct list_head list; unsigned long phys_base; @@ -132,6 +150,8 @@ struct omap_aes_dev { int dma_out; struct dma_chan *dma_lch_out; dma_addr_t dma_addr_out; + + const struct omap_aes_pdata *pdata; }; /* keep registered devices data here */ @@ -194,26 +214,16 @@ static int omap_aes_write_ctrl(struct omap_aes_dev *dd) if (err) return err; - val = 0; - if (dd->dma_lch_out != NULL) - val |= AES_REG_MASK_DMA_OUT_EN; - if (dd->dma_lch_in != NULL) - val |= AES_REG_MASK_DMA_IN_EN; - - mask = AES_REG_MASK_DMA_IN_EN | AES_REG_MASK_DMA_OUT_EN; - - omap_aes_write_mask(dd, AES_REG_MASK, val, mask); - key32 = dd->ctx->keylen / sizeof(u32); /* it seems a key should always be set even if it has not changed */ for (i = 0; i < key32; i++) { - omap_aes_write(dd, AES_REG_KEY(i), + omap_aes_write(dd, AES_REG_KEY(dd, i), __le32_to_cpu(dd->ctx->key[i])); } if ((dd->flags & FLAGS_CBC) && dd->req->info) - omap_aes_write_n(dd, AES_REG_IV(0), dd->req->info, 4); + omap_aes_write_n(dd, AES_REG_IV(dd, 0), dd->req->info, 4); val = FLD_VAL(((dd->ctx->keylen >> 3) - 1), 4, 3); if (dd->flags & FLAGS_CBC) @@ -224,11 +234,47 @@ static int omap_aes_write_ctrl(struct omap_aes_dev *dd) mask = AES_REG_CTRL_CBC | AES_REG_CTRL_DIRECTION | AES_REG_CTRL_KEY_SIZE; - omap_aes_write_mask(dd, AES_REG_CTRL, val, mask); + omap_aes_write_mask(dd, AES_REG_CTRL(dd), val, mask); return 0; } +static void omap_aes_dma_trigger_omap2(struct omap_aes_dev *dd, int length) +{ + u32 mask, val; + + val = dd->pdata->dma_start; + + if (dd->dma_lch_out != NULL) + val |= dd->pdata->dma_enable_out; + if (dd->dma_lch_in != NULL) + val |= dd->pdata->dma_enable_in; + + mask = dd->pdata->dma_enable_out | dd->pdata->dma_enable_in | + dd->pdata->dma_start; + + omap_aes_write_mask(dd, AES_REG_MASK(dd), val, mask); + +} + +static void omap_aes_dma_trigger_omap4(struct omap_aes_dev *dd, int length) +{ + omap_aes_write(dd, AES_REG_LENGTH_N(0), length); + omap_aes_write(dd, AES_REG_LENGTH_N(1), 0); + + omap_aes_dma_trigger_omap2(dd, length); +} + +static void omap_aes_dma_stop(struct omap_aes_dev *dd) +{ + u32 mask; + + mask = dd->pdata->dma_enable_out | dd->pdata->dma_enable_in | + dd->pdata->dma_start; + + omap_aes_write_mask(dd, AES_REG_MASK(dd), 0, mask); +} + static struct omap_aes_dev *omap_aes_find_dev(struct omap_aes_ctx *ctx) { struct omap_aes_dev *dd = NULL, *tmp; @@ -413,8 +459,8 @@ static int omap_aes_crypt_dma(struct crypto_tfm *tfm, memset(&cfg, 0, sizeof(cfg)); - cfg.src_addr = dd->phys_base + AES_REG_DATA; - cfg.dst_addr = dd->phys_base + AES_REG_DATA; + cfg.src_addr = dd->phys_base + AES_REG_DATA_N(dd, 0); + cfg.dst_addr = dd->phys_base + AES_REG_DATA_N(dd, 0); cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; cfg.src_maxburst = DST_MAXBURST; @@ -464,9 +510,8 @@ static int omap_aes_crypt_dma(struct crypto_tfm *tfm, dma_async_issue_pending(dd->dma_lch_in); dma_async_issue_pending(dd->dma_lch_out); - /* start DMA or disable idle mode */ - omap_aes_write_mask(dd, AES_REG_MASK, AES_REG_MASK_START, - AES_REG_MASK_START); + /* start DMA */ + dd->pdata->trigger(dd, length); return 0; } @@ -586,7 +631,7 @@ static int omap_aes_crypt_dma_stop(struct omap_aes_dev *dd) pr_debug("total: %d\n", dd->total); - omap_aes_write_mask(dd, AES_REG_MASK, 0, AES_REG_MASK_START); + omap_aes_dma_stop(dd); dmaengine_terminate_all(dd->dma_lch_in); dmaengine_terminate_all(dd->dma_lch_out); @@ -826,10 +871,48 @@ static struct crypto_alg algs[] = { } }; +static const struct omap_aes_pdata omap_aes_pdata_omap2 = { + .trigger = omap_aes_dma_trigger_omap2, + .key_ofs = 0x1c, + .iv_ofs = 0x20, + .ctrl_ofs = 0x30, + .data_ofs = 0x34, + .rev_ofs = 0x44, + .mask_ofs = 0x48, + .dma_enable_in = BIT(2), + .dma_enable_out = BIT(3), + .dma_start = BIT(5), + .major_mask = 0xf0, + .major_shift = 4, + .minor_mask = 0x0f, + .minor_shift = 0, +}; + #ifdef CONFIG_OF +static const struct omap_aes_pdata omap_aes_pdata_omap4 = { + .trigger = omap_aes_dma_trigger_omap4, + .key_ofs = 0x3c, + .iv_ofs = 0x40, + .ctrl_ofs = 0x50, + .data_ofs = 0x60, + .rev_ofs = 0x80, + .mask_ofs = 0x84, + .dma_enable_in = BIT(5), + .dma_enable_out = BIT(6), + .major_mask = 0x0700, + .major_shift = 8, + .minor_mask = 0x003f, + .minor_shift = 0, +}; + static const struct of_device_id omap_aes_of_match[] = { { .compatible = "ti,omap2-aes", + .data = &omap_aes_pdata_omap2, + }, + { + .compatible = "ti,omap4-aes", + .data = &omap_aes_pdata_omap4, }, {}, }; @@ -859,6 +942,8 @@ static int omap_aes_get_res_of(struct omap_aes_dev *dd, dd->dma_out = -1; /* Dummy value that's unused */ dd->dma_in = -1; /* Dummy value that's unused */ + dd->pdata = match->data; + err: return err; } @@ -908,6 +993,9 @@ static int omap_aes_get_res_pdev(struct omap_aes_dev *dd, } dd->dma_in = r->start; + /* Only OMAP2/3 can be non-DT */ + dd->pdata = &omap_aes_pdata_omap2; + err: return err; } @@ -947,12 +1035,16 @@ static int omap_aes_probe(struct platform_device *pdev) pm_runtime_enable(dev); pm_runtime_get_sync(dev); - reg = omap_aes_read(dd, AES_REG_REV); - dev_info(dev, "OMAP AES hw accel rev: %u.%u\n", - (reg & AES_REG_REV_MAJOR) >> 4, reg & AES_REG_REV_MINOR); + omap_aes_dma_stop(dd); + + reg = omap_aes_read(dd, AES_REG_REV(dd)); pm_runtime_put_sync(dev); + dev_info(dev, "OMAP AES hw accel rev: %u.%u\n", + (reg & dd->pdata->major_mask) >> dd->pdata->major_shift, + (reg & dd->pdata->minor_mask) >> dd->pdata->minor_shift); + tasklet_init(&dd->done_task, omap_aes_done_task, (unsigned long)dd); tasklet_init(&dd->queue_task, omap_aes_queue_task, (unsigned long)dd); -- cgit v1.2.3 From f9fb69e73c774a6490a13128381af9ba468d3a6e Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 8 Jan 2013 11:57:47 -0700 Subject: crypto: omap-aes - Add CTR algorithm Support The OMAP3 and OMAP4/AM33xx versions of the AES crypto module support the CTR algorithm in addition to ECB and CBC that the OMAP2 version of the module supports. So, OMAP2 and OMAP3 share a common register set but OMAP3 supports CTR while OMAP2 doesn't. OMAP4/AM33XX uses a different register set from OMAP2/OMAP3 and also supports CTR. To add this support, use the platform_data introduced in an ealier commit to hold the list of algorithms supported by the current module. The probe routine will use that list to register the correct algorithms. Note: The code being integrated is from the TI AM33xx SDK and was written by Greg Turner and Herman Schuurman (current email unknown) while at TI. CC: Greg Turner CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 143 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 128 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index bd1ad97404ab..6aa425fe0ed5 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -48,7 +48,11 @@ #define AES_REG_IV(dd, x) ((dd)->pdata->iv_ofs + ((x) * 0x04)) #define AES_REG_CTRL(dd) ((dd)->pdata->ctrl_ofs) -#define AES_REG_CTRL_CTR_WIDTH (1 << 7) +#define AES_REG_CTRL_CTR_WIDTH_MASK (3 << 7) +#define AES_REG_CTRL_CTR_WIDTH_32 (0 << 7) +#define AES_REG_CTRL_CTR_WIDTH_64 (1 << 7) +#define AES_REG_CTRL_CTR_WIDTH_96 (2 << 7) +#define AES_REG_CTRL_CTR_WIDTH_128 (3 << 7) #define AES_REG_CTRL_CTR (1 << 6) #define AES_REG_CTRL_CBC (1 << 5) #define AES_REG_CTRL_KEY_SIZE (3 << 3) @@ -76,6 +80,7 @@ #define FLAGS_ENCRYPT BIT(0) #define FLAGS_CBC BIT(1) #define FLAGS_GIV BIT(2) +#define FLAGS_CTR BIT(3) #define FLAGS_INIT BIT(4) #define FLAGS_FAST BIT(5) @@ -96,7 +101,16 @@ struct omap_aes_reqctx { #define OMAP_AES_QUEUE_LENGTH 1 #define OMAP_AES_CACHE_SIZE 0 +struct omap_aes_algs_info { + struct crypto_alg *algs_list; + unsigned int size; + unsigned int registered; +}; + struct omap_aes_pdata { + struct omap_aes_algs_info *algs_info; + unsigned int algs_info_size; + void (*trigger)(struct omap_aes_dev *dd, int length); u32 key_ofs; @@ -208,7 +222,7 @@ static int omap_aes_write_ctrl(struct omap_aes_dev *dd) { unsigned int key32; int i, err; - u32 val, mask; + u32 val, mask = 0; err = omap_aes_hw_init(dd); if (err) @@ -222,16 +236,20 @@ static int omap_aes_write_ctrl(struct omap_aes_dev *dd) __le32_to_cpu(dd->ctx->key[i])); } - if ((dd->flags & FLAGS_CBC) && dd->req->info) + if ((dd->flags & (FLAGS_CBC | FLAGS_CTR)) && dd->req->info) omap_aes_write_n(dd, AES_REG_IV(dd, 0), dd->req->info, 4); val = FLD_VAL(((dd->ctx->keylen >> 3) - 1), 4, 3); if (dd->flags & FLAGS_CBC) val |= AES_REG_CTRL_CBC; + if (dd->flags & FLAGS_CTR) { + val |= AES_REG_CTRL_CTR | AES_REG_CTRL_CTR_WIDTH_32; + mask = AES_REG_CTRL_CTR | AES_REG_CTRL_CTR_WIDTH_MASK; + } if (dd->flags & FLAGS_ENCRYPT) val |= AES_REG_CTRL_DIRECTION; - mask = AES_REG_CTRL_CBC | AES_REG_CTRL_DIRECTION | + mask |= AES_REG_CTRL_CBC | AES_REG_CTRL_DIRECTION | AES_REG_CTRL_KEY_SIZE; omap_aes_write_mask(dd, AES_REG_CTRL(dd), val, mask); @@ -807,6 +825,16 @@ static int omap_aes_cbc_decrypt(struct ablkcipher_request *req) return omap_aes_crypt(req, FLAGS_CBC); } +static int omap_aes_ctr_encrypt(struct ablkcipher_request *req) +{ + return omap_aes_crypt(req, FLAGS_ENCRYPT | FLAGS_CTR); +} + +static int omap_aes_ctr_decrypt(struct ablkcipher_request *req) +{ + return omap_aes_crypt(req, FLAGS_CTR); +} + static int omap_aes_cra_init(struct crypto_tfm *tfm) { pr_debug("enter\n"); @@ -823,7 +851,7 @@ static void omap_aes_cra_exit(struct crypto_tfm *tfm) /* ********************** ALGS ************************************ */ -static struct crypto_alg algs[] = { +static struct crypto_alg algs_ecb_cbc[] = { { .cra_name = "ecb(aes)", .cra_driver_name = "ecb-aes-omap", @@ -871,7 +899,43 @@ static struct crypto_alg algs[] = { } }; +static struct crypto_alg algs_ctr[] = { +{ + .cra_name = "ctr(aes)", + .cra_driver_name = "ctr-aes-omap", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | + CRYPTO_ALG_KERN_DRIVER_ONLY | + CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct omap_aes_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = omap_aes_cra_init, + .cra_exit = omap_aes_cra_exit, + .cra_u.ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .geniv = "eseqiv", + .ivsize = AES_BLOCK_SIZE, + .setkey = omap_aes_setkey, + .encrypt = omap_aes_ctr_encrypt, + .decrypt = omap_aes_ctr_decrypt, + } +} , +}; + +static struct omap_aes_algs_info omap_aes_algs_info_ecb_cbc[] = { + { + .algs_list = algs_ecb_cbc, + .size = ARRAY_SIZE(algs_ecb_cbc), + }, +}; + static const struct omap_aes_pdata omap_aes_pdata_omap2 = { + .algs_info = omap_aes_algs_info_ecb_cbc, + .algs_info_size = ARRAY_SIZE(omap_aes_algs_info_ecb_cbc), .trigger = omap_aes_dma_trigger_omap2, .key_ofs = 0x1c, .iv_ofs = 0x20, @@ -889,7 +953,39 @@ static const struct omap_aes_pdata omap_aes_pdata_omap2 = { }; #ifdef CONFIG_OF +static struct omap_aes_algs_info omap_aes_algs_info_ecb_cbc_ctr[] = { + { + .algs_list = algs_ecb_cbc, + .size = ARRAY_SIZE(algs_ecb_cbc), + }, + { + .algs_list = algs_ctr, + .size = ARRAY_SIZE(algs_ctr), + }, +}; + +static const struct omap_aes_pdata omap_aes_pdata_omap3 = { + .algs_info = omap_aes_algs_info_ecb_cbc_ctr, + .algs_info_size = ARRAY_SIZE(omap_aes_algs_info_ecb_cbc_ctr), + .trigger = omap_aes_dma_trigger_omap2, + .key_ofs = 0x1c, + .iv_ofs = 0x20, + .ctrl_ofs = 0x30, + .data_ofs = 0x34, + .rev_ofs = 0x44, + .mask_ofs = 0x48, + .dma_enable_in = BIT(2), + .dma_enable_out = BIT(3), + .dma_start = BIT(5), + .major_mask = 0xf0, + .major_shift = 4, + .minor_mask = 0x0f, + .minor_shift = 0, +}; + static const struct omap_aes_pdata omap_aes_pdata_omap4 = { + .algs_info = omap_aes_algs_info_ecb_cbc_ctr, + .algs_info_size = ARRAY_SIZE(omap_aes_algs_info_ecb_cbc_ctr), .trigger = omap_aes_dma_trigger_omap4, .key_ofs = 0x3c, .iv_ofs = 0x40, @@ -910,6 +1006,10 @@ static const struct of_device_id omap_aes_of_match[] = { .compatible = "ti,omap2-aes", .data = &omap_aes_pdata_omap2, }, + { + .compatible = "ti,omap3-aes", + .data = &omap_aes_pdata_omap3, + }, { .compatible = "ti,omap4-aes", .data = &omap_aes_pdata_omap4, @@ -1004,6 +1104,7 @@ static int omap_aes_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct omap_aes_dev *dd; + struct crypto_alg *algp; struct resource res; int err = -ENOMEM, i, j; u32 reg; @@ -1057,17 +1158,27 @@ static int omap_aes_probe(struct platform_device *pdev) list_add_tail(&dd->list, &dev_list); spin_unlock(&list_lock); - for (i = 0; i < ARRAY_SIZE(algs); i++) { - pr_debug("i: %d\n", i); - err = crypto_register_alg(&algs[i]); - if (err) - goto err_algs; + for (i = 0; i < dd->pdata->algs_info_size; i++) { + for (j = 0; j < dd->pdata->algs_info[i].size; j++) { + algp = &dd->pdata->algs_info[i].algs_list[j]; + + pr_debug("reg alg: %s\n", algp->cra_name); + INIT_LIST_HEAD(&algp->cra_list); + + err = crypto_register_alg(algp); + if (err) + goto err_algs; + + dd->pdata->algs_info[i].registered++; + } } return 0; err_algs: - for (j = 0; j < i; j++) - crypto_unregister_alg(&algs[j]); + for (i = dd->pdata->algs_info_size - 1; i >= 0; i--) + for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--) + crypto_unregister_alg( + &dd->pdata->algs_info[i].algs_list[j]); omap_aes_dma_cleanup(dd); err_dma: tasklet_kill(&dd->done_task); @@ -1084,7 +1195,7 @@ err_data: static int omap_aes_remove(struct platform_device *pdev) { struct omap_aes_dev *dd = platform_get_drvdata(pdev); - int i; + int i, j; if (!dd) return -ENODEV; @@ -1093,8 +1204,10 @@ static int omap_aes_remove(struct platform_device *pdev) list_del(&dd->list); spin_unlock(&list_lock); - for (i = 0; i < ARRAY_SIZE(algs); i++) - crypto_unregister_alg(&algs[i]); + for (i = dd->pdata->algs_info_size - 1; i >= 0; i--) + for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--) + crypto_unregister_alg( + &dd->pdata->algs_info[i].algs_list[j]); tasklet_kill(&dd->done_task); tasklet_kill(&dd->queue_task); -- cgit v1.2.3 From 5c22ba6619796da82ea0aa18c72caf4fe003a329 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 10 Jan 2013 11:05:30 +0900 Subject: crypto: s5p-sss - Use devm_clk_get() Use devm_clk_get() rather than clk_get() to make cleanup paths more simple. Signed-off-by: Jingoo Han Signed-off-by: Herbert Xu --- drivers/crypto/s5p-sss.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/crypto/s5p-sss.c b/drivers/crypto/s5p-sss.c index 49ad8cbade69..4b314326f48a 100644 --- a/drivers/crypto/s5p-sss.c +++ b/drivers/crypto/s5p-sss.c @@ -580,7 +580,7 @@ static int s5p_aes_probe(struct platform_device *pdev) resource_size(res), pdev->name)) return -EBUSY; - pdata->clk = clk_get(dev, "secss"); + pdata->clk = devm_clk_get(dev, "secss"); if (IS_ERR(pdata->clk)) { dev_err(dev, "failed to find secss clock source\n"); return -ENOENT; @@ -645,7 +645,6 @@ static int s5p_aes_probe(struct platform_device *pdev) err_irq: clk_disable(pdata->clk); - clk_put(pdata->clk); s5p_dev = NULL; platform_set_drvdata(pdev, NULL); @@ -667,7 +666,6 @@ static int s5p_aes_remove(struct platform_device *pdev) tasklet_kill(&pdata->tasklet); clk_disable(pdata->clk); - clk_put(pdata->clk); s5p_dev = NULL; platform_set_drvdata(pdev, NULL); -- cgit v1.2.3 From c3c3b3292d202e9924fb3af0f4139848fd7e1de0 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 15 Jan 2013 13:53:02 -0700 Subject: crypto: omap-sham - Fix compile errors when CONFIG_OF not defined Fix the compile errors created by commit 2545e8d (crypto: omap-sham - Add Device Tree Support) when CONFIG_OF is not defined. This includes changing omap_sham_get_res_dev() to omap_sham_get_res_of() and creating an empty version of omap_sham_of_match[]. Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-sham.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index edff981edfb1..dc2d354219c6 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -1607,7 +1607,11 @@ err: return err; } #else -static int omap_sham_get_res_dev(struct omap_sham_dev *dd, +static const struct of_device_id omap_sham_of_match[] = { + {}, +}; + +static int omap_sham_get_res_of(struct omap_sham_dev *dd, struct device *dev, struct resource *res) { return -EINVAL; -- cgit v1.2.3 From 661f7cb55c61fa7491e0caf21e55f59e5bc49abe Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 10 Jan 2013 13:41:04 -0500 Subject: dma: edma: fix slave config dependency on direction The edma_slave_config() implementation depends on the direction field such that it will not properly configure a slave channel when called without direction set. This fixes the implementation so that the slave config is copied as is and prep_slave_sg() handles the direction dependent handling. spi-omap2-mcspi and omap_hsmmc both expose this bug as they configure the slave channel config from a common path with an unconfigured direction field. Signed-off-by: Matt Porter Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 55 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 232b4583ae93..82c8672f26e8 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -69,9 +69,7 @@ struct edma_chan { int ch_num; bool alloced; int slot[EDMA_MAX_SLOTS]; - dma_addr_t addr; - int addr_width; - int maxburst; + struct dma_slave_config cfg; }; struct edma_cc { @@ -178,29 +176,14 @@ static int edma_terminate_all(struct edma_chan *echan) return 0; } - static int edma_slave_config(struct edma_chan *echan, - struct dma_slave_config *config) + struct dma_slave_config *cfg) { - if ((config->src_addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES) || - (config->dst_addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES)) + if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES || + cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) return -EINVAL; - if (config->direction == DMA_MEM_TO_DEV) { - if (config->dst_addr) - echan->addr = config->dst_addr; - if (config->dst_addr_width) - echan->addr_width = config->dst_addr_width; - if (config->dst_maxburst) - echan->maxburst = config->dst_maxburst; - } else if (config->direction == DMA_DEV_TO_MEM) { - if (config->src_addr) - echan->addr = config->src_addr; - if (config->src_addr_width) - echan->addr_width = config->src_addr_width; - if (config->src_maxburst) - echan->maxburst = config->src_maxburst; - } + memcpy(&echan->cfg, cfg, sizeof(echan->cfg)); return 0; } @@ -235,6 +218,9 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( struct edma_chan *echan = to_edma_chan(chan); struct device *dev = chan->device->dev; struct edma_desc *edesc; + dma_addr_t dev_addr; + enum dma_slave_buswidth dev_width; + u32 burst; struct scatterlist *sg; int i; int acnt, bcnt, ccnt, src, dst, cidx; @@ -243,7 +229,20 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( if (unlikely(!echan || !sgl || !sg_len)) return NULL; - if (echan->addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) { + if (direction == DMA_DEV_TO_MEM) { + dev_addr = echan->cfg.src_addr; + dev_width = echan->cfg.src_addr_width; + burst = echan->cfg.src_maxburst; + } else if (direction == DMA_MEM_TO_DEV) { + dev_addr = echan->cfg.dst_addr; + dev_width = echan->cfg.dst_addr_width; + burst = echan->cfg.dst_maxburst; + } else { + dev_err(dev, "%s: bad direction?\n", __func__); + return NULL; + } + + if (dev_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) { dev_err(dev, "Undefined slave buswidth\n"); return NULL; } @@ -275,14 +274,14 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( } } - acnt = echan->addr_width; + acnt = dev_width; /* * If the maxburst is equal to the fifo width, use * A-synced transfers. This allows for large contiguous * buffer transfers using only one PaRAM set. */ - if (echan->maxburst == 1) { + if (burst == 1) { edesc->absync = false; ccnt = sg_dma_len(sg) / acnt / (SZ_64K - 1); bcnt = sg_dma_len(sg) / acnt - ccnt * (SZ_64K - 1); @@ -302,7 +301,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( */ } else { edesc->absync = true; - bcnt = echan->maxburst; + bcnt = burst; ccnt = sg_dma_len(sg) / (acnt * bcnt); if (ccnt > (SZ_64K - 1)) { dev_err(dev, "Exceeded max SG segment size\n"); @@ -313,13 +312,13 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( if (direction == DMA_MEM_TO_DEV) { src = sg_dma_address(sg); - dst = echan->addr; + dst = dev_addr; src_bidx = acnt; src_cidx = cidx; dst_bidx = 0; dst_cidx = 0; } else { - src = echan->addr; + src = dev_addr; dst = sg_dma_address(sg); src_bidx = 0; src_cidx = 0; -- cgit v1.2.3 From 93d187993b783c68383a884091a600d9ad499ea6 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 17 Jan 2013 12:45:17 -0800 Subject: drm/i915: Remove use of gtt_mappable_entries Mappable_end, ie. size is almost always what you want as opposed to the number of entries. Since we already have that information, we can scrap the number of entries and only calculate it when needed. If gtt_start is !0, this will have slightly different behavior. This difference can only occur in DRI1, and exists when we try to kick out the firmware fb. The new code seems like a bugfix to me. The other case where we've changed the behavior is during init we check the mappable region against our current known upper and lower limits (64MB, and 512MB). This now matches the comment, and makes things more convenient after removing gtt_mappable_entries. Also worth noting is the setting of mappable_end is taken out of setup because we do it earlier now in the DRI2 case and therefore need to add that tiny hunk to support the DRI1 IOCTL. v2: Move up mappable end to before legacy AGP init v3: Add the dev_priv inclusion here from previous rebase error in patch 5 Reviewed-by: Rodrigo Vivi (v2) Signed-off-by: Ben Widawsky [danvet: squash in fix for a printk format flag mismatch warning.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 2 +- drivers/gpu/drm/i915/i915_dma.c | 8 ++++---- drivers/gpu/drm/i915/i915_gem.c | 2 ++ drivers/gpu/drm/i915/i915_gem_gtt.c | 15 +++++++-------- drivers/gpu/drm/i915/intel_fb.c | 3 +-- 5 files changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 773b23ecc83b..90a6fc506dd5 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -258,7 +258,7 @@ static int i915_gem_object_info(struct seq_file *m, void* data) seq_printf(m, "%u fault mappable objects, %zu bytes\n", count, size); - seq_printf(m, "%zu [%zu] gtt total\n", + seq_printf(m, "%zu [%lu] gtt total\n", dev_priv->gtt.total, dev_priv->gtt.mappable_end - dev_priv->gtt.start); diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 468d2a0fc378..3f70178c63ca 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1427,8 +1427,8 @@ static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) return; ap->ranges[0].base = dev_priv->gtt.mappable_base; - ap->ranges[0].size = - dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; + ap->ranges[0].size = dev_priv->gtt.mappable_end - dev_priv->gtt.start; + primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; @@ -1542,7 +1542,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto put_gmch; } - aperture_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; + aperture_size = dev_priv->gtt.mappable_end; dev_priv->gtt.mappable = io_mapping_create_wc(dev_priv->gtt.mappable_base, @@ -1699,7 +1699,7 @@ int i915_driver_unload(struct drm_device *dev) if (dev_priv->mm.gtt_mtrr >= 0) { mtrr_del(dev_priv->mm.gtt_mtrr, dev_priv->gtt.mappable_base, - dev_priv->mm.gtt->gtt_mappable_entries * PAGE_SIZE); + dev_priv->gtt.mappable_end); dev_priv->mm.gtt_mtrr = -1; } diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 51fdf16181a7..e4132ffb7609 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -149,6 +149,7 @@ int i915_gem_init_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_init *args = data; if (drm_core_check_feature(dev, DRIVER_MODESET)) @@ -165,6 +166,7 @@ i915_gem_init_ioctl(struct drm_device *dev, void *data, mutex_lock(&dev->struct_mutex); i915_gem_setup_global_gtt(dev, args->gtt_start, args->gtt_end, args->gtt_end); + dev_priv->gtt.mappable_end = args->gtt_end; mutex_unlock(&dev->struct_mutex); return 0; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 0b8930577953..0f0db02c1e56 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -557,7 +557,6 @@ void i915_gem_setup_global_gtt(struct drm_device *dev, } dev_priv->gtt.start = start; - dev_priv->gtt.mappable_end = mappable_end; dev_priv->gtt.total = end - start; /* Clear any non-preallocated blocks */ @@ -596,7 +595,7 @@ void i915_gem_init_global_gtt(struct drm_device *dev) int ret; gtt_size = dev_priv->mm.gtt->gtt_total_entries << PAGE_SHIFT; - mappable_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; + mappable_size = dev_priv->gtt.mappable_end; if (intel_enable_ppgtt(dev) && HAS_ALIASING_PPGTT(dev)) { /* PPGTT pdes are stolen from global gtt ptes, so shrink the @@ -692,6 +691,7 @@ int i915_gem_gtt_init(struct drm_device *dev) int ret; dev_priv->gtt.mappable_base = pci_resource_start(dev->pdev, 2); + dev_priv->gtt.mappable_end = pci_resource_len(dev->pdev, 2); /* On modern platforms we need not worry ourself with the legacy * hostbridge query stuff. Skip it entirely @@ -735,14 +735,13 @@ int i915_gem_gtt_init(struct drm_device *dev) else dev_priv->mm.gtt->stolen_size = gen7_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); + if ((dev_priv->gtt.mappable_end < (64<<20) || + (dev_priv->gtt.mappable_end > (512<<20)))) { + DRM_ERROR("Unknown GMADR size (%lx)\n", + dev_priv->gtt.mappable_end); ret = -ENXIO; goto err_out; } @@ -764,7 +763,7 @@ int i915_gem_gtt_init(struct drm_device *dev) /* GMADR is the PCI aperture used by SW to access tiled GFX surfaces in a linear fashion. */ DRM_INFO("Memory usable by graphics device = %dM\n", dev_priv->mm.gtt->gtt_total_entries >> 8); - DRM_DEBUG_DRIVER("GMADR size = %dM\n", dev_priv->mm.gtt->gtt_mappable_entries >> 8); + DRM_DEBUG_DRIVER("GMADR size = %ldM\n", dev_priv->gtt.mappable_end >> 20); DRM_DEBUG_DRIVER("GTT stolen size = %dM\n", dev_priv->mm.gtt->stolen_size >> 20); return 0; diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index ce02af8ca96e..ce5f54498426 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -135,8 +135,7 @@ static int intelfb_create(struct intel_fbdev *ifbdev, goto out_unpin; } info->apertures->ranges[0].base = dev->mode_config.fb_base; - info->apertures->ranges[0].size = - dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; + info->apertures->ranges[0].size = dev_priv->gtt.mappable_end; info->fix.smem_start = dev->mode_config.fb_base + obj->gtt_offset; info->fix.smem_len = size; -- cgit v1.2.3 From 3685a8f38f2c54bb6058c0e66ae0562f8ab84e66 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Thu, 17 Jan 2013 16:31:28 +0200 Subject: drm/i915: Fix RGB color range property for PCH platforms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RGB color range select bit on the DP/SDVO/HDMI registers disappeared when PCH was introduced, and instead a new PIPECONF bit was added that performs the same function. Add a new INTEL_MODE_LIMITED_COLOR_RANGE private mode flag, and set it in the encoder mode_fixup if limited color range is requested. Set the the PIPECONF bit 13 based on the flag. Experimentation showed that simply toggling the bit while the pipe is active doesn't work. We need to restart the pipe, which luckily already happens. The DP/SDVO/HDMI bit 8 is marked MBZ in the docs, so avoid setting it, although it doesn't seem to do any harm in practice. TODO: - the PIPECONF bit too seems to have disappeared from HSW. Need a volunteer to test if it's just a documentation issue or if it's really gone. If the bit is gone and no easy replacement is found, then I suppose we may need to use the pipe CSC unit to perform the range compression. v2: Use mode private_flags instead of intel_encoder virtual functions v3: Moved the intel_dp color_range handling after bpc check to help later patches Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=46800 Reviewed-by: Paulo Zanoni Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_display.c | 5 +++++ drivers/gpu/drm/i915/intel_dp.c | 7 ++++++- drivers/gpu/drm/i915/intel_drv.h | 5 +++++ drivers/gpu/drm/i915/intel_hdmi.c | 5 +++++ drivers/gpu/drm/i915/intel_sdvo.c | 5 ++++- 6 files changed, 26 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 4c33bd2bd4e6..2521617b55da 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2650,6 +2650,7 @@ #define PIPECONF_INTERLACED_DBL_ILK (4 << 21) /* ilk/snb only */ #define PIPECONF_PFIT_PF_INTERLACED_DBL_ILK (5 << 21) /* ilk/snb only */ #define PIPECONF_CXSR_DOWNCLOCK (1<<16) +#define PIPECONF_COLOR_RANGE_SELECT (1 << 13) #define PIPECONF_BPC_MASK (0x7 << 5) #define PIPECONF_8BPC (0<<5) #define PIPECONF_10BPC (1<<5) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e4c5067a54d3..b35902e5d925 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5096,6 +5096,11 @@ static void ironlake_set_pipeconf(struct drm_crtc *crtc, else val |= PIPECONF_PROGRESSIVE; + if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE) + val |= PIPECONF_COLOR_RANGE_SELECT; + else + val &= ~PIPECONF_COLOR_RANGE_SELECT; + I915_WRITE(PIPECONF(pipe), val); POSTING_READ(PIPECONF(pipe)); } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 5f12eb2d0fb5..d9956278a56e 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -763,6 +763,10 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, return false; bpp = adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC ? 18 : 24; + + if (intel_dp->color_range) + adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE; + mode_rate = intel_dp_link_required(adjusted_mode->clock, bpp); for (clock = 0; clock <= max_clock; clock++) { @@ -967,7 +971,8 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, else intel_dp->DP |= DP_PLL_FREQ_270MHZ; } else if (!HAS_PCH_CPT(dev) || is_cpu_edp(intel_dp)) { - intel_dp->DP |= intel_dp->color_range; + if (!HAS_PCH_SPLIT(dev)) + intel_dp->DP |= intel_dp->color_range; if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) intel_dp->DP |= DP_SYNC_HS_HIGH; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 54a034c82061..4df47be84abd 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -109,6 +109,11 @@ * timings in the mode to prevent the crtc fixup from overwriting them. * Currently only lvds needs that. */ #define INTEL_MODE_CRTC_TIMINGS_SET (0x20) +/* + * Set when limited 16-235 (as opposed to full 0-255) RGB color range is + * to be used. + */ +#define INTEL_MODE_LIMITED_COLOR_RANGE (0x40) static inline void intel_mode_set_pixel_multiplier(struct drm_display_mode *mode, diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 6387f9b0df99..f194d756a58c 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -766,6 +766,11 @@ bool intel_hdmi_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + + if (intel_hdmi->color_range) + adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE; + return true; } diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 153377bed66a..3b8491af1f23 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1064,6 +1064,9 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, multiplier = intel_sdvo_get_pixel_multiplier(adjusted_mode); intel_mode_set_pixel_multiplier(adjusted_mode, multiplier); + if (intel_sdvo->color_range) + adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE; + return true; } @@ -1153,7 +1156,7 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, /* The real mode polarity is set by the SDVO commands, using * struct intel_sdvo_dtd. */ sdvox = SDVO_VSYNC_ACTIVE_HIGH | SDVO_HSYNC_ACTIVE_HIGH; - if (intel_sdvo->is_hdmi) + if (!HAS_PCH_SPLIT(dev) && intel_sdvo->is_hdmi) sdvox |= intel_sdvo->color_range; if (INTEL_INFO(dev)->gen < 5) sdvox |= SDVO_BORDER_ENABLE; -- cgit v1.2.3 From 55bc60db5988c8366751d3d04dd690698a53412c Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Thu, 17 Jan 2013 16:31:29 +0200 Subject: drm/i915: Add "Automatic" mode for the "Broadcast RGB" property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new "Automatic" mode to the "Broadcast RGB" range property. When selected the driver automagically selects between full range and limited range output. Based on CEA-861 [1] guidelines, limited range output is selected if the mode is a CEA mode, except 640x480. Otherwise full range output is used. Additionally DVI monitors should most likely default to full range always. As per DP1.2a [2] DisplayPort should always use full range for 18bpp, and otherwise will follow CEA-861 rules. NOTE: The default value for the property will now be "Automatic" so some people may be affected in case they're relying on the current full range default. [1] CEA-861-E - 5.1 Default Encoding Parameters [2] VESA DisplayPort Ver.1.2a - 5.1.1.1 Video Colorimetry v2: Use has_hdmi_sink to check if a HDMI monitor is present v3: Add information about relevant spec chapters Reviewed-by: Paulo Zanoni Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 4 ++++ drivers/gpu/drm/i915/intel_dp.c | 32 ++++++++++++++++++++++++++++---- drivers/gpu/drm/i915/intel_drv.h | 2 ++ drivers/gpu/drm/i915/intel_hdmi.c | 29 +++++++++++++++++++++++++---- drivers/gpu/drm/i915/intel_modes.c | 5 +++-- drivers/gpu/drm/i915/intel_sdvo.c | 38 +++++++++++++++++++++++++++++++------- 6 files changed, 93 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index f3f2e5e1393f..0a2a18b8a075 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1811,5 +1811,9 @@ __i915_write(64, q) #define POSTING_READ(reg) (void)I915_READ_NOTRACE(reg) #define POSTING_READ16(reg) (void)I915_READ16_NOTRACE(reg) +/* "Broadcast RGB" property */ +#define INTEL_BROADCAST_RGB_AUTO 0 +#define INTEL_BROADCAST_RGB_FULL 1 +#define INTEL_BROADCAST_RGB_LIMITED 2 #endif diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index d9956278a56e..1492706ed087 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -764,6 +764,18 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, bpp = adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC ? 18 : 24; + if (intel_dp->color_range_auto) { + /* + * See: + * CEA-861-E - 5.1 Default Encoding Parameters + * VESA DisplayPort Ver.1.2a - 5.1.1.1 Video Colorimetry + */ + if (bpp != 18 && drm_mode_cea_vic(adjusted_mode) > 1) + intel_dp->color_range = DP_COLOR_RANGE_16_235; + else + intel_dp->color_range = 0; + } + if (intel_dp->color_range) adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE; @@ -2462,10 +2474,21 @@ intel_dp_set_property(struct drm_connector *connector, } if (property == dev_priv->broadcast_rgb_property) { - if (val == !!intel_dp->color_range) - return 0; - - intel_dp->color_range = val ? DP_COLOR_RANGE_16_235 : 0; + switch (val) { + case INTEL_BROADCAST_RGB_AUTO: + intel_dp->color_range_auto = true; + break; + case INTEL_BROADCAST_RGB_FULL: + intel_dp->color_range_auto = false; + intel_dp->color_range = 0; + break; + case INTEL_BROADCAST_RGB_LIMITED: + intel_dp->color_range_auto = false; + intel_dp->color_range = DP_COLOR_RANGE_16_235; + break; + default: + return -EINVAL; + } goto done; } @@ -2606,6 +2629,7 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect intel_attach_force_audio_property(connector); intel_attach_broadcast_rgb_property(connector); + intel_dp->color_range_auto = true; if (is_edp(intel_dp)) { drm_mode_create_scaling_mode_property(connector->dev); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 4df47be84abd..1a698c6f16e7 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -343,6 +343,7 @@ struct intel_hdmi { u32 sdvox_reg; int ddc_bus; uint32_t color_range; + bool color_range_auto; bool has_hdmi_sink; bool has_audio; enum hdmi_force_audio force_audio; @@ -362,6 +363,7 @@ struct intel_dp { bool has_audio; enum hdmi_force_audio force_audio; uint32_t color_range; + bool color_range_auto; uint8_t link_bw; uint8_t lane_count; uint8_t dpcd[DP_RECEIVER_CAP_SIZE]; diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index f194d756a58c..db67be66639b 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -768,6 +768,15 @@ bool intel_hdmi_mode_fixup(struct drm_encoder *encoder, { struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + if (intel_hdmi->color_range_auto) { + /* See CEA-861-E - 5.1 Default Encoding Parameters */ + if (intel_hdmi->has_hdmi_sink && + drm_mode_cea_vic(adjusted_mode) > 1) + intel_hdmi->color_range = SDVO_COLOR_RANGE_16_235; + else + intel_hdmi->color_range = 0; + } + if (intel_hdmi->color_range) adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE; @@ -912,10 +921,21 @@ intel_hdmi_set_property(struct drm_connector *connector, } if (property == dev_priv->broadcast_rgb_property) { - if (val == !!intel_hdmi->color_range) - return 0; - - intel_hdmi->color_range = val ? SDVO_COLOR_RANGE_16_235 : 0; + switch (val) { + case INTEL_BROADCAST_RGB_AUTO: + intel_hdmi->color_range_auto = true; + break; + case INTEL_BROADCAST_RGB_FULL: + intel_hdmi->color_range_auto = false; + intel_hdmi->color_range = 0; + break; + case INTEL_BROADCAST_RGB_LIMITED: + intel_hdmi->color_range_auto = false; + intel_hdmi->color_range = SDVO_COLOR_RANGE_16_235; + break; + default: + return -EINVAL; + } goto done; } @@ -964,6 +984,7 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *c { intel_attach_force_audio_property(connector); intel_attach_broadcast_rgb_property(connector); + intel_hdmi->color_range_auto = true; } void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c index 49249bb97485..0e860f39933d 100644 --- a/drivers/gpu/drm/i915/intel_modes.c +++ b/drivers/gpu/drm/i915/intel_modes.c @@ -100,8 +100,9 @@ intel_attach_force_audio_property(struct drm_connector *connector) } static const struct drm_prop_enum_list broadcast_rgb_names[] = { - { 0, "Full" }, - { 1, "Limited 16:235" }, + { INTEL_BROADCAST_RGB_AUTO, "Automatic" }, + { INTEL_BROADCAST_RGB_FULL, "Full" }, + { INTEL_BROADCAST_RGB_LIMITED, "Limited 16:235" }, }; void diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 3b8491af1f23..3e34a3592e32 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -103,6 +103,7 @@ struct intel_sdvo { * It is only valid when using TMDS encoding and 8 bit per color mode. */ uint32_t color_range; + bool color_range_auto; /** * This is set if we're going to treat the device as TV-out. @@ -1064,6 +1065,15 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, multiplier = intel_sdvo_get_pixel_multiplier(adjusted_mode); intel_mode_set_pixel_multiplier(adjusted_mode, multiplier); + if (intel_sdvo->color_range_auto) { + /* See CEA-861-E - 5.1 Default Encoding Parameters */ + if (intel_sdvo->has_hdmi_monitor && + drm_mode_cea_vic(adjusted_mode) > 1) + intel_sdvo->color_range = SDVO_COLOR_RANGE_16_235; + else + intel_sdvo->color_range = 0; + } + if (intel_sdvo->color_range) adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE; @@ -1900,10 +1910,21 @@ intel_sdvo_set_property(struct drm_connector *connector, } if (property == dev_priv->broadcast_rgb_property) { - if (val == !!intel_sdvo->color_range) - return 0; - - intel_sdvo->color_range = val ? SDVO_COLOR_RANGE_16_235 : 0; + switch (val) { + case INTEL_BROADCAST_RGB_AUTO: + intel_sdvo->color_range_auto = true; + break; + case INTEL_BROADCAST_RGB_FULL: + intel_sdvo->color_range_auto = false; + intel_sdvo->color_range = 0; + break; + case INTEL_BROADCAST_RGB_LIMITED: + intel_sdvo->color_range_auto = false; + intel_sdvo->color_range = SDVO_COLOR_RANGE_16_235; + break; + default: + return -EINVAL; + } goto done; } @@ -2200,13 +2221,16 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector, } static void -intel_sdvo_add_hdmi_properties(struct intel_sdvo_connector *connector) +intel_sdvo_add_hdmi_properties(struct intel_sdvo *intel_sdvo, + struct intel_sdvo_connector *connector) { struct drm_device *dev = connector->base.base.dev; intel_attach_force_audio_property(&connector->base.base); - if (INTEL_INFO(dev)->gen >= 4 && IS_MOBILE(dev)) + if (INTEL_INFO(dev)->gen >= 4 && IS_MOBILE(dev)) { intel_attach_broadcast_rgb_property(&connector->base.base); + intel_sdvo->color_range_auto = true; + } } static bool @@ -2254,7 +2278,7 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device) intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo); if (intel_sdvo->is_hdmi) - intel_sdvo_add_hdmi_properties(intel_sdvo_connector); + intel_sdvo_add_hdmi_properties(intel_sdvo, intel_sdvo_connector); return true; } -- cgit v1.2.3 From b1edd6a6ecd436af33f210a5c75e0249466fd200 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Thu, 17 Jan 2013 16:31:30 +0200 Subject: drm/edid: Add drm_rgb_quant_range_selectable() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drm_rgb_quant_range_selectable() will report whether the monitor claims to support for RGB quantization range selection. The information can be found in the CEA Video capability block. v2: s/quantzation/quantization/ in the comment Reviewed-by: Paulo Zanoni Signed-off-by: Ville Syrjälä Acked-by: David Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_edid.c | 33 +++++++++++++++++++++++++++++++++ include/drm/drm_crtc.h | 1 + 2 files changed, 34 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 5a3770fbd770..a3a3b61059ff 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1483,9 +1483,11 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid, #define VIDEO_BLOCK 0x02 #define VENDOR_BLOCK 0x03 #define SPEAKER_BLOCK 0x04 +#define VIDEO_CAPABILITY_BLOCK 0x07 #define EDID_BASIC_AUDIO (1 << 6) #define EDID_CEA_YCRCB444 (1 << 5) #define EDID_CEA_YCRCB422 (1 << 4) +#define EDID_CEA_VCDB_QS (1 << 6) /** * Search EDID for CEA extension block. @@ -1901,6 +1903,37 @@ end: } EXPORT_SYMBOL(drm_detect_monitor_audio); +/** + * drm_rgb_quant_range_selectable - is RGB quantization range selectable? + * + * Check whether the monitor reports the RGB quantization range selection + * as supported. The AVI infoframe can then be used to inform the monitor + * which quantization range (full or limited) is used. + */ +bool drm_rgb_quant_range_selectable(struct edid *edid) +{ + u8 *edid_ext; + int i, start, end; + + edid_ext = drm_find_cea_extension(edid); + if (!edid_ext) + return false; + + if (cea_db_offsets(edid_ext, &start, &end)) + return false; + + for_each_cea_db(edid_ext, i, start, end) { + if (cea_db_tag(&edid_ext[i]) == VIDEO_CAPABILITY_BLOCK && + cea_db_payload_len(&edid_ext[i]) == 2) { + DRM_DEBUG_KMS("CEA VCDB 0x%02x\n", edid_ext[i + 2]); + return edid_ext[i + 2] & EDID_CEA_VCDB_QS; + } + } + + return false; +} +EXPORT_SYMBOL(drm_rgb_quant_range_selectable); + /** * drm_add_display_info - pull display info out if present * @edid: EDID data diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 00d78b5161c0..30892dc9376e 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1033,6 +1033,7 @@ 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 bool drm_rgb_quant_range_selectable(struct edid *edid); extern int drm_mode_page_flip_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, -- cgit v1.2.3 From abedc077b45eff0b5a8630af8431ad5d59213582 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Thu, 17 Jan 2013 16:31:31 +0200 Subject: drm/i915: Provide the quantization range in the AVI infoframe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AVI infoframe is able to inform the display whether the source is sending full or limited range RGB data. As per CEA-861 [1] we must first check whether the display reports the quantization range as selectable, and if so we can set the approriate bits in the AVI inforframe. [1] CEA-861-E - 6.4 Format of Version 2 AVI InfoFrame v2: Give the Q bits better names, add spec chapter information Reviewed-by: Paulo Zanoni Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_drv.h | 4 ++++ drivers/gpu/drm/i915/intel_hdmi.c | 11 +++++++++++ drivers/gpu/drm/i915/intel_sdvo.c | 16 ++++++++++++++-- 3 files changed, 29 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 1a698c6f16e7..aeff0d1067ad 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -289,6 +289,9 @@ struct cxsr_latency { #define DIP_LEN_AVI 13 #define DIP_AVI_PR_1 0 #define DIP_AVI_PR_2 1 +#define DIP_AVI_RGB_QUANT_RANGE_DEFAULT (0 << 2) +#define DIP_AVI_RGB_QUANT_RANGE_LIMITED (1 << 2) +#define DIP_AVI_RGB_QUANT_RANGE_FULL (2 << 2) #define DIP_TYPE_SPD 0x83 #define DIP_VERSION_SPD 0x1 @@ -347,6 +350,7 @@ struct intel_hdmi { bool has_hdmi_sink; bool has_audio; enum hdmi_force_audio force_audio; + bool rgb_quant_range_selectable; void (*write_infoframe)(struct drm_encoder *encoder, struct dip_infoframe *frame); void (*set_infoframes)(struct drm_encoder *encoder, diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index db67be66639b..d53b73131b0e 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -331,6 +331,7 @@ static void intel_set_infoframe(struct drm_encoder *encoder, static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, struct drm_display_mode *adjusted_mode) { + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); struct dip_infoframe avi_if = { .type = DIP_TYPE_AVI, .ver = DIP_VERSION_AVI, @@ -340,6 +341,13 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK) avi_if.body.avi.YQ_CN_PR |= DIP_AVI_PR_2; + if (intel_hdmi->rgb_quant_range_selectable) { + if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE) + avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_LIMITED; + else + avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_FULL; + } + avi_if.body.avi.VIC = drm_mode_cea_vic(adjusted_mode); intel_set_infoframe(encoder, &avi_if); @@ -825,6 +833,7 @@ intel_hdmi_detect(struct drm_connector *connector, bool force) intel_hdmi->has_hdmi_sink = false; intel_hdmi->has_audio = false; + intel_hdmi->rgb_quant_range_selectable = false; edid = drm_get_edid(connector, intel_gmbus_get_adapter(dev_priv, intel_hdmi->ddc_bus)); @@ -836,6 +845,8 @@ intel_hdmi_detect(struct drm_connector *connector, bool force) intel_hdmi->has_hdmi_sink = drm_detect_hdmi_monitor(edid); intel_hdmi->has_audio = drm_detect_monitor_audio(edid); + intel_hdmi->rgb_quant_range_selectable = + drm_rgb_quant_range_selectable(edid); } kfree(edid); } diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 3e34a3592e32..f01063a2323a 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -126,6 +126,7 @@ struct intel_sdvo { bool is_hdmi; bool has_hdmi_monitor; bool has_hdmi_audio; + bool rgb_quant_range_selectable; /** * This is set if we detect output of sdvo device as LVDS and @@ -947,7 +948,8 @@ static bool intel_sdvo_write_infoframe(struct intel_sdvo *intel_sdvo, &tx_rate, 1); } -static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo) +static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo, + const struct drm_display_mode *adjusted_mode) { struct dip_infoframe avi_if = { .type = DIP_TYPE_AVI, @@ -956,6 +958,13 @@ static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo) }; uint8_t sdvo_data[4 + sizeof(avi_if.body.avi)]; + if (intel_sdvo->rgb_quant_range_selectable) { + if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE) + avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_LIMITED; + else + avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_FULL; + } + intel_dip_infoframe_csum(&avi_if); /* sdvo spec says that the ecc is handled by the hw, and it looks like @@ -1134,7 +1143,7 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_HDMI); intel_sdvo_set_colorimetry(intel_sdvo, SDVO_COLORIMETRY_RGB256); - intel_sdvo_set_avi_infoframe(intel_sdvo); + intel_sdvo_set_avi_infoframe(intel_sdvo, adjusted_mode); } else intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_DVI); @@ -1526,6 +1535,8 @@ intel_sdvo_tmds_sink_detect(struct drm_connector *connector) if (intel_sdvo->is_hdmi) { intel_sdvo->has_hdmi_monitor = drm_detect_hdmi_monitor(edid); intel_sdvo->has_hdmi_audio = drm_detect_monitor_audio(edid); + intel_sdvo->rgb_quant_range_selectable = + drm_rgb_quant_range_selectable(edid); } } else status = connector_status_disconnected; @@ -1577,6 +1588,7 @@ intel_sdvo_detect(struct drm_connector *connector, bool force) intel_sdvo->has_hdmi_monitor = false; intel_sdvo->has_hdmi_audio = false; + intel_sdvo->rgb_quant_range_selectable = false; if ((intel_sdvo_connector->output_flag & response) == 0) ret = connector_status_disconnected; -- cgit v1.2.3 From a81cc00c11ab6816fbcb7dd99a60b50e71765d25 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Fri, 18 Jan 2013 12:30:31 -0800 Subject: drm/i915: Cut out the infamous ILK w/a from AGP layer And, move it to where the rest of the logic is. There is some slight functionality changes. There was extra paranoid checks in AGP code making sure we never do idle maps on gen2 parts. That was not duplicated as the simple PCI id check should do the right thing. v2: use IS_GEN5 && IS_MOBILE check instead. For now, this is the same as IS_IRONLAKE_M but is more future proof. The workaround docs hint that more than one platform may be effected, but we've never seen such a platform in the wild. (Rodrigo, Daniel) Reviewed-by: Rodrigo Vivi (v1) Cc: Dave Airlie Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 27 --------------------------- drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/i915_gem_gtt.c | 24 +++++++++++++++++++++--- include/drm/intel-gtt.h | 2 -- 4 files changed, 23 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index c8d9dcb15db0..12c31026eb56 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -840,9 +840,6 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem, { int ret = -EINVAL; - if (intel_private.base.do_idle_maps) - return -ENODEV; - if (intel_private.clear_fake_agp) { int start = intel_private.base.stolen_size / PAGE_SIZE; int end = intel_private.base.gtt_mappable_entries; @@ -907,9 +904,6 @@ static int intel_fake_agp_remove_entries(struct agp_memory *mem, if (mem->page_count == 0) return 0; - if (intel_private.base.do_idle_maps) - return -ENODEV; - intel_gtt_clear_range(pg_start, mem->page_count); if (intel_private.base.needs_dmar) { @@ -1069,24 +1063,6 @@ static void i965_write_entry(dma_addr_t addr, writel(addr | pte_flags, intel_private.gtt + entry); } -/* Certain Gen5 chipsets require require idling the GPU before - * unmapping anything from the GTT when VT-d is enabled. - */ -static inline int needs_idle_maps(void) -{ -#ifdef CONFIG_INTEL_IOMMU - const unsigned short gpu_devid = intel_private.pcidev->device; - - /* Query intel_iommu to see if we need the workaround. Presumably that - * was loaded first. - */ - if ((gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB || - gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG) && - intel_iommu_gfx_mapped) - return 1; -#endif - return 0; -} static int i9xx_setup(void) { @@ -1115,9 +1091,6 @@ static int i9xx_setup(void) break; } - if (needs_idle_maps()) - intel_private.base.do_idle_maps = 1; - intel_i9xx_setup_flush(); return 0; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 0a2a18b8a075..1fe802f4df13 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -381,6 +381,8 @@ struct i915_gtt { /** "Graphics Stolen Memory" holds the global PTEs */ void __iomem *gsm; + + bool do_idle_maps; }; #define I915_PPGTT_PD_ENTRIES 512 diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 0f0db02c1e56..84c521fd11a2 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -338,11 +338,27 @@ void i915_gem_init_ppgtt(struct drm_device *dev) } } +extern int intel_iommu_gfx_mapped; +/* Certain Gen5 chipsets require require idling the GPU before + * unmapping anything from the GTT when VT-d is enabled. + */ +static inline bool needs_idle_maps(struct drm_device *dev) +{ +#ifdef CONFIG_INTEL_IOMMU + /* Query intel_iommu to see if we need the workaround. Presumably that + * was loaded first. + */ + if (IS_GEN5(dev) && IS_MOBILE(dev) && intel_iommu_gfx_mapped) + return true; +#endif + return false; +} + static bool do_idling(struct drm_i915_private *dev_priv) { bool ret = dev_priv->mm.interruptible; - if (unlikely(dev_priv->mm.gtt->do_idle_maps)) { + if (unlikely(dev_priv->gtt.do_idle_maps)) { dev_priv->mm.interruptible = false; if (i915_gpu_idle(dev_priv->dev)) { DRM_ERROR("Couldn't idle GPU\n"); @@ -356,11 +372,10 @@ static bool do_idling(struct drm_i915_private *dev_priv) static void undo_idling(struct drm_i915_private *dev_priv, bool interruptible) { - if (unlikely(dev_priv->mm.gtt->do_idle_maps)) + if (unlikely(dev_priv->gtt.do_idle_maps)) dev_priv->mm.interruptible = interruptible; } - static void i915_ggtt_clear_range(struct drm_device *dev, unsigned first_entry, unsigned num_entries) @@ -709,6 +724,9 @@ int i915_gem_gtt_init(struct drm_device *dev) intel_gmch_remove(); return -ENODEV; } + + dev_priv->gtt.do_idle_maps = needs_idle_maps(dev); + return 0; } diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index 3e3a166a2690..1747aa095f41 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -13,8 +13,6 @@ struct intel_gtt { unsigned int gtt_mappable_entries; /* Whether i915 needs to use the dmar apis or not. */ unsigned int needs_dmar : 1; - /* Whether we idle the gpu before mapping/unmapping */ - unsigned int do_idle_maps : 1; /* Share the scratch page dma with ppgtts. */ dma_addr_t scratch_page_dma; struct page *scratch_page; -- cgit v1.2.3 From 9c61a32d31a55c8c6e590d83ae5645e14fde09f2 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Fri, 18 Jan 2013 12:30:32 -0800 Subject: drm/i915: Remove scratch page from shared We already had a mapping in both (minus the phys_addr in AGP). Reviewed-by: Rodrigo Vivi Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 9 +++++---- drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/i915_gem_gtt.c | 16 ++++++++-------- include/drm/intel-gtt.h | 3 --- 4 files changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 12c31026eb56..7fcee5c2e986 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -75,6 +75,7 @@ static struct _intel_private { struct resource ifp_resource; int resource_valid; struct page *scratch_page; + phys_addr_t scratch_page_dma; int refcount; } intel_private; @@ -297,9 +298,9 @@ static int intel_gtt_setup_scratch_page(void) if (pci_dma_mapping_error(intel_private.pcidev, dma_addr)) return -EINVAL; - intel_private.base.scratch_page_dma = dma_addr; + intel_private.scratch_page_dma = dma_addr; } else - intel_private.base.scratch_page_dma = page_to_phys(page); + intel_private.scratch_page_dma = page_to_phys(page); intel_private.scratch_page = page; @@ -546,7 +547,7 @@ static unsigned int intel_gtt_mappable_entries(void) static void intel_gtt_teardown_scratch_page(void) { set_pages_wb(intel_private.scratch_page, 1); - pci_unmap_page(intel_private.pcidev, intel_private.base.scratch_page_dma, + pci_unmap_page(intel_private.pcidev, intel_private.scratch_page_dma, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); put_page(intel_private.scratch_page); __free_page(intel_private.scratch_page); @@ -891,7 +892,7 @@ void intel_gtt_clear_range(unsigned int first_entry, unsigned int num_entries) unsigned int i; for (i = first_entry; i < (first_entry + num_entries); i++) { - intel_private.driver->write_entry(intel_private.base.scratch_page_dma, + intel_private.driver->write_entry(intel_private.scratch_page_dma, i, 0); } readl(intel_private.gtt+i-1); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 1fe802f4df13..3189034deeb8 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -383,6 +383,8 @@ struct i915_gtt { void __iomem *gsm; bool do_idle_maps; + dma_addr_t scratch_page_dma; + struct page *scratch_page; }; #define I915_PPGTT_PD_ENTRIES 512 diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 84c521fd11a2..3c1f66ebc245 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -162,7 +162,7 @@ int i915_gem_init_aliasing_ppgtt(struct drm_device *dev) } } - ppgtt->scratch_page_dma_addr = dev_priv->mm.gtt->scratch_page_dma; + ppgtt->scratch_page_dma_addr = dev_priv->gtt.scratch_page_dma; i915_ppgtt_clear_range(ppgtt, 0, ppgtt->num_pd_entries*I915_PPGTT_PT_ENTRIES); @@ -396,7 +396,7 @@ static void i915_ggtt_clear_range(struct drm_device *dev, 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); + scratch_pte = pte_encode(dev, dev_priv->gtt.scratch_page_dma, I915_CACHE_LLC); for (i = 0; i < num_entries; i++) iowrite32(scratch_pte, >t_base[i]); readl(gtt_base); @@ -659,8 +659,8 @@ static int setup_scratch_page(struct drm_device *dev) #else dma_addr = page_to_phys(page); #endif - dev_priv->mm.gtt->scratch_page = page; - dev_priv->mm.gtt->scratch_page_dma = dma_addr; + dev_priv->gtt.scratch_page = page; + dev_priv->gtt.scratch_page_dma = dma_addr; return 0; } @@ -668,11 +668,11 @@ static int setup_scratch_page(struct drm_device *dev) 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, + set_pages_wb(dev_priv->gtt.scratch_page, 1); + pci_unmap_page(dev->pdev, dev_priv->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); + put_page(dev_priv->gtt.scratch_page); + __free_page(dev_priv->gtt.scratch_page); } static inline unsigned int gen6_get_total_gtt_size(u16 snb_gmch_ctl) diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index 1747aa095f41..c787ee4c4c07 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -13,9 +13,6 @@ struct intel_gtt { unsigned int gtt_mappable_entries; /* Whether i915 needs to use the dmar apis or not. */ unsigned int needs_dmar : 1; - /* Share the scratch page dma with ppgtts. */ - dma_addr_t scratch_page_dma; - struct page *scratch_page; /* needed for ioremap in drm/i915 */ phys_addr_t gma_bus_addr; } *intel_gtt_get(void); -- cgit v1.2.3 From 8d2e630899165d413ae8a2adc36846ac0b71bada Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Fri, 18 Jan 2013 12:30:33 -0800 Subject: drm/i915: Needs_dmar, not The reasoning behind our code taking two paths depending upon whether or not we may have been configured for IOMMU isn't clear to me. It should always be safe to use the pci mapping functions as they are designed to abstract the decision we were handling in i915. Aside from simpler code, removing another member for the intel_gtt struct is a nice motivation. I ran this by Chris, and he wasn't concerned about the extra kzalloc, and memory references vs. page_to_phys calculation in the case without IOMMU. v2: Update commit message v3: Remove needs_dmar addition from Zhenyu upstream This reverts (and then other stuff) commit 20652097dadd9a7fb4d652f25466299974bc78f9 Author: Zhenyu Wang Date: Thu Dec 13 23:47:47 2012 +0800 drm/i915: Fix missed needs_dmar setting Reviewed-by: Rodrigo Vivi (v2) Cc: Zhenyu Wang Signed-off-by: Ben Widawsky [danvet: Squash in follow-up fix to remove the bogus hunk which deleted the dma_mask configuration for gen6+.] Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 10 +++++---- drivers/gpu/drm/i915/i915_gem_gtt.c | 45 +++++++++++++------------------------ include/drm/intel-gtt.h | 2 -- 3 files changed, 22 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 7fcee5c2e986..b45241479a56 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -77,6 +77,8 @@ static struct _intel_private { struct page *scratch_page; phys_addr_t scratch_page_dma; int refcount; + /* Whether i915 needs to use the dmar apis or not. */ + unsigned int needs_dmar : 1; } intel_private; #define INTEL_GTT_GEN intel_private.driver->gen @@ -292,7 +294,7 @@ static int intel_gtt_setup_scratch_page(void) get_page(page); set_pages_uc(page, 1); - if (intel_private.base.needs_dmar) { + if (intel_private.needs_dmar) { dma_addr = pci_map_page(intel_private.pcidev, page, 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); if (pci_dma_mapping_error(intel_private.pcidev, dma_addr)) @@ -608,7 +610,7 @@ static int intel_gtt_init(void) intel_private.base.stolen_size = intel_gtt_stolen_size(); - intel_private.base.needs_dmar = USE_PCI_DMA_API && INTEL_GTT_GEN > 2; + intel_private.needs_dmar = USE_PCI_DMA_API && INTEL_GTT_GEN > 2; ret = intel_gtt_setup_scratch_page(); if (ret != 0) { @@ -866,7 +868,7 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem, if (!mem->is_flushed) global_cache_flush(); - if (intel_private.base.needs_dmar) { + if (intel_private.needs_dmar) { struct sg_table st; ret = intel_gtt_map_memory(mem->pages, mem->page_count, &st); @@ -907,7 +909,7 @@ static int intel_fake_agp_remove_entries(struct agp_memory *mem, intel_gtt_clear_range(pg_start, mem->page_count); - if (intel_private.base.needs_dmar) { + if (intel_private.needs_dmar) { intel_gtt_unmap_memory(mem->sg_list, mem->num_sg); mem->sg_list = NULL; mem->num_sg = 0; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 3c1f66ebc245..a0ba4a9e53c7 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -138,28 +138,23 @@ int i915_gem_init_aliasing_ppgtt(struct drm_device *dev) goto err_pt_alloc; } - if (dev_priv->mm.gtt->needs_dmar) { - ppgtt->pt_dma_addr = kzalloc(sizeof(dma_addr_t) - *ppgtt->num_pd_entries, - GFP_KERNEL); - if (!ppgtt->pt_dma_addr) - goto err_pt_alloc; + ppgtt->pt_dma_addr = kzalloc(sizeof(dma_addr_t) *ppgtt->num_pd_entries, + GFP_KERNEL); + if (!ppgtt->pt_dma_addr) + goto err_pt_alloc; - for (i = 0; i < ppgtt->num_pd_entries; i++) { - dma_addr_t pt_addr; + for (i = 0; i < ppgtt->num_pd_entries; i++) { + dma_addr_t pt_addr; - pt_addr = pci_map_page(dev->pdev, ppgtt->pt_pages[i], - 0, 4096, - PCI_DMA_BIDIRECTIONAL); + pt_addr = pci_map_page(dev->pdev, ppgtt->pt_pages[i], 0, 4096, + PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(dev->pdev, - pt_addr)) { - ret = -EIO; - goto err_pd_pin; + if (pci_dma_mapping_error(dev->pdev, pt_addr)) { + ret = -EIO; + goto err_pd_pin; - } - ppgtt->pt_dma_addr[i] = pt_addr; } + ppgtt->pt_dma_addr[i] = pt_addr; } ppgtt->scratch_page_dma_addr = dev_priv->gtt.scratch_page_dma; @@ -294,11 +289,7 @@ void i915_gem_init_ppgtt(struct drm_device *dev) for (i = 0; i < ppgtt->num_pd_entries; i++) { dma_addr_t pt_addr; - if (dev_priv->mm.gtt->needs_dmar) - pt_addr = ppgtt->pt_dma_addr[i]; - else - pt_addr = page_to_phys(ppgtt->pt_pages[i]); - + pt_addr = ppgtt->pt_dma_addr[i]; pd_entry = GEN6_PDE_ADDR_ENCODE(pt_addr); pd_entry |= GEN6_PDE_VALID; @@ -730,16 +721,12 @@ int i915_gem_gtt_init(struct drm_device *dev) 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)); -#ifdef CONFIG_INTEL_IOMMU - dev_priv->mm.gtt->needs_dmar = 1; -#endif + dev_priv->mm.gtt = kzalloc(sizeof(*dev_priv->mm.gtt), GFP_KERNEL); + if (!dev_priv->mm.gtt) + return -ENOMEM; /* For GEN6+ the PTEs for the ggtt live at 2MB + BAR0 */ gtt_bus_addr = pci_resource_start(dev->pdev, 0) + (2<<20); diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index c787ee4c4c07..984105cddc57 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -11,8 +11,6 @@ struct intel_gtt { /* Part of the gtt that is mappable by the cpu, for those chips where * this is not the full gtt. */ unsigned int gtt_mappable_entries; - /* Whether i915 needs to use the dmar apis or not. */ - unsigned int needs_dmar : 1; /* needed for ioremap in drm/i915 */ phys_addr_t gma_bus_addr; } *intel_gtt_get(void); -- cgit v1.2.3 From e5c653777986b40e2986d2c918847fddbcba3a34 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Fri, 18 Jan 2013 12:30:34 -0800 Subject: agp/intel: Add gma_bus_addr It is no longer used in the i915 code, so isolate it from the shared struct. This was originally part of: commit 0e275518f325418d559c05327775bff894b237f7 Author: Ben Widawsky Date: Mon Jan 14 13:35:33 2013 -0800 agp/intel: decouple more of the agp-i915 sharing Reviewed-by: Rodrigo Vivi Signed-off-by: Ben Widawsky That commit had some other hunks which can't be used due to issues Daniel found in previous commits. Signed-off-by: Ben Widawsky [danvet: drop squash notice from the commit since it's imo ok to keep this one separate.] Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 5 +++-- include/drm/intel-gtt.h | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index b45241479a56..ff5f3483e8ea 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -79,6 +79,7 @@ static struct _intel_private { int refcount; /* Whether i915 needs to use the dmar apis or not. */ unsigned int needs_dmar : 1; + phys_addr_t gma_bus_addr; } intel_private; #define INTEL_GTT_GEN intel_private.driver->gen @@ -625,7 +626,7 @@ static int intel_gtt_init(void) pci_read_config_dword(intel_private.pcidev, I915_GMADDR, &gma_addr); - intel_private.base.gma_bus_addr = (gma_addr & PCI_BASE_ADDRESS_MEM_MASK); + intel_private.gma_bus_addr = (gma_addr & PCI_BASE_ADDRESS_MEM_MASK); return 0; } @@ -781,7 +782,7 @@ static int intel_fake_agp_configure(void) return -EIO; intel_private.clear_fake_agp = true; - agp_bridge->gart_bus_addr = intel_private.base.gma_bus_addr; + agp_bridge->gart_bus_addr = intel_private.gma_bus_addr; return 0; } diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index 984105cddc57..769b6c79097e 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -11,8 +11,6 @@ struct intel_gtt { /* Part of the gtt that is mappable by the cpu, for those chips where * this is not the full gtt. */ unsigned int gtt_mappable_entries; - /* needed for ioremap in drm/i915 */ - phys_addr_t gma_bus_addr; } *intel_gtt_get(void); int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev, -- cgit v1.2.3 From 4b5aed62121eddfc47fd8f2739ca6b802b97390e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 14 Nov 2012 17:14:03 +0100 Subject: drm/i915: move dev_priv->mm out of line Tha one is really big, since it contains tons of comments explaining how things work. Which is nice ;-) Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 202 ++++++++++++++++++++-------------------- 1 file changed, 102 insertions(+), 100 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 3189034deeb8..ea3226852ea8 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -665,6 +665,107 @@ struct intel_l3_parity { struct work_struct error_work; }; +struct i915_gem_mm { + /** Bridge to intel-gtt-ko */ + struct intel_gtt *gtt; + /** Memory allocator for GTT stolen memory */ + struct drm_mm stolen; + /** Memory allocator for GTT */ + struct drm_mm gtt_space; + /** List of all objects in gtt_space. Used to restore gtt + * mappings on resume */ + struct list_head bound_list; + /** + * List of objects which are not bound to the GTT (thus + * are idle and not used by the GPU) but still have + * (presumably uncached) pages still attached. + */ + struct list_head unbound_list; + + /** Usable portion of the GTT for GEM */ + unsigned long stolen_base; /* limited to low memory (32-bit) */ + + int gtt_mtrr; + + /** PPGTT used for aliasing the PPGTT with the GTT */ + struct i915_hw_ppgtt *aliasing_ppgtt; + + struct shrinker inactive_shrinker; + bool shrinker_no_lock_stealing; + + /** + * List of objects currently involved in rendering. + * + * Includes buffers having the contents of their GPU caches + * flushed, not necessarily primitives. last_rendering_seqno + * represents when the rendering involved will be completed. + * + * A reference is held on the buffer while on this list. + */ + struct list_head active_list; + + /** + * LRU list of objects which are not in the ringbuffer and + * are ready to unbind, but are still in the GTT. + * + * last_rendering_seqno is 0 while an object is in this list. + * + * A reference is not held on the buffer while on this list, + * as merely being GTT-bound shouldn't prevent its being + * freed, and we'll pull it off the list in the free path. + */ + struct list_head inactive_list; + + /** LRU list of objects with fence regs on them. */ + struct list_head fence_list; + + /** + * We leave the user IRQ off as much as possible, + * but this means that requests will finish and never + * be retired once the system goes idle. Set a timer to + * fire periodically while the ring is running. When it + * fires, go retire requests. + */ + struct delayed_work retire_work; + + /** + * Are we in a non-interruptible section of code like + * modesetting? + */ + bool interruptible; + + /** + * Flag if the X Server, and thus DRM, is not currently in + * control of the device. + * + * This is set between LeaveVT and EnterVT. It needs to be + * replaced with a semaphore. It also needs to be + * transitioned away from for kernel modesetting. + */ + int suspended; + + /** + * Flag if the hardware appears to be wedged. + * + * This is set when attempts to idle the device timeout. + * It prevents command submission from occurring and makes + * every pending request fail + */ + atomic_t wedged; + + /** Bit 6 swizzling required for X tiling */ + uint32_t bit_6_swizzle_x; + /** Bit 6 swizzling required for Y tiling */ + uint32_t bit_6_swizzle_y; + + /* storage for physical objects */ + struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT]; + + /* accounting, useful for userland debugging */ + size_t object_memory; + u32 object_count; +}; + typedef struct drm_i915_private { struct drm_device *dev; struct kmem_cache *slab; @@ -806,106 +907,7 @@ typedef struct drm_i915_private { struct i915_gtt gtt; - struct { - /** Bridge to intel-gtt-ko */ - struct intel_gtt *gtt; - /** Memory allocator for GTT stolen memory */ - struct drm_mm stolen; - /** Memory allocator for GTT */ - struct drm_mm gtt_space; - /** List of all objects in gtt_space. Used to restore gtt - * mappings on resume */ - struct list_head bound_list; - /** - * List of objects which are not bound to the GTT (thus - * are idle and not used by the GPU) but still have - * (presumably uncached) pages still attached. - */ - struct list_head unbound_list; - - /** Usable portion of the GTT for GEM */ - unsigned long stolen_base; /* limited to low memory (32-bit) */ - - int gtt_mtrr; - - /** PPGTT used for aliasing the PPGTT with the GTT */ - struct i915_hw_ppgtt *aliasing_ppgtt; - - struct shrinker inactive_shrinker; - bool shrinker_no_lock_stealing; - - /** - * List of objects currently involved in rendering. - * - * Includes buffers having the contents of their GPU caches - * flushed, not necessarily primitives. last_rendering_seqno - * represents when the rendering involved will be completed. - * - * A reference is held on the buffer while on this list. - */ - struct list_head active_list; - - /** - * LRU list of objects which are not in the ringbuffer and - * are ready to unbind, but are still in the GTT. - * - * last_rendering_seqno is 0 while an object is in this list. - * - * A reference is not held on the buffer while on this list, - * as merely being GTT-bound shouldn't prevent its being - * freed, and we'll pull it off the list in the free path. - */ - struct list_head inactive_list; - - /** LRU list of objects with fence regs on them. */ - struct list_head fence_list; - - /** - * We leave the user IRQ off as much as possible, - * but this means that requests will finish and never - * be retired once the system goes idle. Set a timer to - * fire periodically while the ring is running. When it - * fires, go retire requests. - */ - struct delayed_work retire_work; - - /** - * Are we in a non-interruptible section of code like - * modesetting? - */ - bool interruptible; - - /** - * Flag if the X Server, and thus DRM, is not currently in - * control of the device. - * - * This is set between LeaveVT and EnterVT. It needs to be - * replaced with a semaphore. It also needs to be - * transitioned away from for kernel modesetting. - */ - int suspended; - - /** - * Flag if the hardware appears to be wedged. - * - * This is set when attempts to idle the device timeout. - * It prevents command submission from occurring and makes - * every pending request fail - */ - atomic_t wedged; - - /** Bit 6 swizzling required for X tiling */ - uint32_t bit_6_swizzle_x; - /** Bit 6 swizzling required for Y tiling */ - uint32_t bit_6_swizzle_y; - - /* storage for physical objects */ - struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT]; - - /* accounting, useful for userland debugging */ - size_t object_memory; - u32 object_count; - } mm; + struct i915_gem_mm mm; /* Kernel Modesetting */ -- cgit v1.2.3 From 99584db33ba4f864777e2cfef5329ed1bf13f714 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 14 Nov 2012 17:14:04 +0100 Subject: drm/i915: extract hangcheck/reset/error_state state into substruct This has been sprinkled all over the place in dev_priv. I think it'd be good to also move all the code into a separate file like i915_gem_error.c, but that's for another patch. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 10 +++--- drivers/gpu/drm/i915/i915_dma.c | 6 ++-- drivers/gpu/drm/i915/i915_drv.c | 8 ++--- drivers/gpu/drm/i915/i915_drv.h | 39 +++++++++++++--------- drivers/gpu/drm/i915/i915_gem.c | 10 +++--- drivers/gpu/drm/i915/i915_irq.c | 59 ++++++++++++++++++--------------- drivers/gpu/drm/i915/intel_ringbuffer.c | 2 +- 7 files changed, 73 insertions(+), 61 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 90a6fc506dd5..3b1bf4e70d94 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -814,11 +814,11 @@ static int i915_error_state_open(struct inode *inode, struct file *file) error_priv->dev = dev; - spin_lock_irqsave(&dev_priv->error_lock, flags); - error_priv->error = dev_priv->first_error; + spin_lock_irqsave(&dev_priv->gpu_error.lock, flags); + error_priv->error = dev_priv->gpu_error.first_error; if (error_priv->error) kref_get(&error_priv->error->ref); - spin_unlock_irqrestore(&dev_priv->error_lock, flags); + spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags); return single_open(file, i915_error_state, error_priv); } @@ -1727,7 +1727,7 @@ i915_ring_stop_read(struct file *filp, int len; len = snprintf(buf, sizeof(buf), - "0x%08x\n", dev_priv->stop_rings); + "0x%08x\n", dev_priv->gpu_error.stop_rings); if (len > sizeof(buf)) len = sizeof(buf); @@ -1763,7 +1763,7 @@ i915_ring_stop_write(struct file *filp, if (ret) return ret; - dev_priv->stop_rings = val; + dev_priv->gpu_error.stop_rings = val; mutex_unlock(&dev->struct_mutex); return cnt; diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 3f70178c63ca..11c7aa80e29b 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1605,7 +1605,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) pci_enable_msi(dev->pdev); spin_lock_init(&dev_priv->irq_lock); - spin_lock_init(&dev_priv->error_lock); + spin_lock_init(&dev_priv->gpu_error.lock); spin_lock_init(&dev_priv->rps.lock); mutex_init(&dev_priv->dpio_lock); @@ -1725,8 +1725,8 @@ int i915_driver_unload(struct drm_device *dev) } /* Free error state after interrupts are fully disabled. */ - del_timer_sync(&dev_priv->hangcheck_timer); - cancel_work_sync(&dev_priv->error_work); + del_timer_sync(&dev_priv->gpu_error.hangcheck_timer); + cancel_work_sync(&dev_priv->gpu_error.work); i915_destroy_error_state(dev); if (dev->pdev->msi_enabled) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index c8cbc32fe8db..3ff8e73f4341 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -779,9 +779,9 @@ int intel_gpu_reset(struct drm_device *dev) } /* Also reset the gpu hangman. */ - if (dev_priv->stop_rings) { + if (dev_priv->gpu_error.stop_rings) { DRM_DEBUG("Simulated gpu hang, resetting stop_rings\n"); - dev_priv->stop_rings = 0; + dev_priv->gpu_error.stop_rings = 0; if (ret == -ENODEV) { DRM_ERROR("Reset not implemented, but ignoring " "error for simulated gpu hangs\n"); @@ -820,12 +820,12 @@ int i915_reset(struct drm_device *dev) i915_gem_reset(dev); ret = -ENODEV; - if (get_seconds() - dev_priv->last_gpu_reset < 5) + if (get_seconds() - dev_priv->gpu_error.last_reset < 5) DRM_ERROR("GPU hanging too fast, declaring wedged!\n"); else ret = intel_gpu_reset(dev); - dev_priv->last_gpu_reset = get_seconds(); + dev_priv->gpu_error.last_reset = get_seconds(); if (ret) { DRM_ERROR("Failed to reset chip.\n"); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ea3226852ea8..dfe0e747c4f7 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -766,6 +766,28 @@ struct i915_gem_mm { u32 object_count; }; +struct i915_gpu_error { + /* For hangcheck timer */ +#define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */ +#define DRM_I915_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD) + struct timer_list hangcheck_timer; + int hangcheck_count; + uint32_t last_acthd[I915_NUM_RINGS]; + uint32_t prev_instdone[I915_NUM_INSTDONE_REG]; + + /* For reset and error_state handling. */ + spinlock_t lock; + /* Protected by the above dev->gpu_error.lock. */ + struct drm_i915_error_state *first_error; + struct work_struct work; + struct completion completion; + + unsigned long last_reset; + + /* For gpu hang simulation. */ + unsigned int stop_rings; +}; + typedef struct drm_i915_private { struct drm_device *dev; struct kmem_cache *slab; @@ -829,16 +851,6 @@ typedef struct drm_i915_private { int num_pipe; int num_pch_pll; - /* For hangcheck timer */ -#define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */ -#define DRM_I915_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD) - struct timer_list hangcheck_timer; - int hangcheck_count; - uint32_t last_acthd[I915_NUM_RINGS]; - uint32_t prev_instdone[I915_NUM_INSTDONE_REG]; - - unsigned int stop_rings; - unsigned long cfb_size; unsigned int cfb_fb; enum plane cfb_plane; @@ -886,11 +898,6 @@ typedef struct drm_i915_private { unsigned int fsb_freq, mem_freq, is_ddr3; - spinlock_t error_lock; - /* Protected by dev->error_lock. */ - struct drm_i915_error_state *first_error; - struct work_struct error_work; - struct completion error_completion; struct workqueue_struct *wq; /* Display functions */ @@ -949,7 +956,7 @@ typedef struct drm_i915_private { struct drm_mm_node *compressed_fb; struct drm_mm_node *compressed_llb; - unsigned long last_gpu_reset; + struct i915_gpu_error gpu_error; /* list of fbdev register on this device */ struct intel_fbdev *fbdev; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index e4132ffb7609..95e022e7a26e 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -90,7 +90,7 @@ static int i915_gem_wait_for_error(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct completion *x = &dev_priv->error_completion; + struct completion *x = &dev_priv->gpu_error.completion; unsigned long flags; int ret; @@ -943,7 +943,7 @@ i915_gem_check_wedge(struct drm_i915_private *dev_priv, bool interruptible) { if (atomic_read(&dev_priv->mm.wedged)) { - struct completion *x = &dev_priv->error_completion; + struct completion *x = &dev_priv->gpu_error.completion; bool recovery_complete; unsigned long flags; @@ -2045,7 +2045,7 @@ i915_add_request(struct intel_ring_buffer *ring, if (!dev_priv->mm.suspended) { if (i915_enable_hangcheck) { - mod_timer(&dev_priv->hangcheck_timer, + mod_timer(&dev_priv->gpu_error.hangcheck_timer, round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES)); } if (was_empty) { @@ -3803,7 +3803,7 @@ i915_gem_idle(struct drm_device *dev) * And not confound mm.suspended! */ dev_priv->mm.suspended = 1; - del_timer_sync(&dev_priv->hangcheck_timer); + del_timer_sync(&dev_priv->gpu_error.hangcheck_timer); i915_kernel_lost_context(dev); i915_gem_cleanup_ringbuffer(dev); @@ -4064,7 +4064,7 @@ i915_gem_load(struct drm_device *dev) INIT_LIST_HEAD(&dev_priv->fence_regs[i].lru_list); INIT_DELAYED_WORK(&dev_priv->mm.retire_work, i915_gem_retire_work_handler); - init_completion(&dev_priv->error_completion); + init_completion(&dev_priv->gpu_error.completion); /* On GEN3 we really need to make sure the ARB C3 LP bit is set */ if (IS_GEN3(dev)) { diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index cc49a6ddc052..c768ebdf8a27 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -356,8 +356,8 @@ static void notify_ring(struct drm_device *dev, wake_up_all(&ring->irq_queue); if (i915_enable_hangcheck) { - dev_priv->hangcheck_count = 0; - mod_timer(&dev_priv->hangcheck_timer, + dev_priv->gpu_error.hangcheck_count = 0; + mod_timer(&dev_priv->gpu_error.hangcheck_timer, round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES)); } } @@ -863,7 +863,7 @@ done: static void i915_error_work_func(struct work_struct *work) { drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, - error_work); + gpu_error.work); struct drm_device *dev = dev_priv->dev; char *error_event[] = { "ERROR=1", NULL }; char *reset_event[] = { "RESET=1", NULL }; @@ -878,7 +878,7 @@ static void i915_error_work_func(struct work_struct *work) atomic_set(&dev_priv->mm.wedged, 0); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event); } - complete_all(&dev_priv->error_completion); + complete_all(&dev_priv->gpu_error.completion); } } @@ -1255,9 +1255,9 @@ static void i915_capture_error_state(struct drm_device *dev) unsigned long flags; int i, pipe; - spin_lock_irqsave(&dev_priv->error_lock, flags); - error = dev_priv->first_error; - spin_unlock_irqrestore(&dev_priv->error_lock, flags); + spin_lock_irqsave(&dev_priv->gpu_error.lock, flags); + error = dev_priv->gpu_error.first_error; + spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags); if (error) return; @@ -1341,12 +1341,12 @@ static void i915_capture_error_state(struct drm_device *dev) error->overlay = intel_overlay_capture_error_state(dev); error->display = intel_display_capture_error_state(dev); - spin_lock_irqsave(&dev_priv->error_lock, flags); - if (dev_priv->first_error == NULL) { - dev_priv->first_error = error; + spin_lock_irqsave(&dev_priv->gpu_error.lock, flags); + if (dev_priv->gpu_error.first_error == NULL) { + dev_priv->gpu_error.first_error = error; error = NULL; } - spin_unlock_irqrestore(&dev_priv->error_lock, flags); + spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags); if (error) i915_error_state_free(&error->ref); @@ -1358,10 +1358,10 @@ void i915_destroy_error_state(struct drm_device *dev) struct drm_i915_error_state *error; unsigned long flags; - spin_lock_irqsave(&dev_priv->error_lock, flags); - error = dev_priv->first_error; - dev_priv->first_error = NULL; - spin_unlock_irqrestore(&dev_priv->error_lock, flags); + spin_lock_irqsave(&dev_priv->gpu_error.lock, flags); + error = dev_priv->gpu_error.first_error; + dev_priv->gpu_error.first_error = NULL; + spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags); if (error) kref_put(&error->ref, i915_error_state_free); @@ -1482,7 +1482,7 @@ void i915_handle_error(struct drm_device *dev, bool wedged) i915_report_and_clear_eir(dev); if (wedged) { - INIT_COMPLETION(dev_priv->error_completion); + INIT_COMPLETION(dev_priv->gpu_error.completion); atomic_set(&dev_priv->mm.wedged, 1); /* @@ -1492,7 +1492,7 @@ void i915_handle_error(struct drm_device *dev, bool wedged) wake_up_all(&ring->irq_queue); } - queue_work(dev_priv->wq, &dev_priv->error_work); + queue_work(dev_priv->wq, &dev_priv->gpu_error.work); } static void i915_pageflip_stall_check(struct drm_device *dev, int pipe) @@ -1723,7 +1723,7 @@ static bool i915_hangcheck_hung(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; - if (dev_priv->hangcheck_count++ > 1) { + if (dev_priv->gpu_error.hangcheck_count++ > 1) { bool hung = true; DRM_ERROR("Hangcheck timer elapsed... GPU hung\n"); @@ -1782,25 +1782,29 @@ void i915_hangcheck_elapsed(unsigned long data) goto repeat; } - dev_priv->hangcheck_count = 0; + dev_priv->gpu_error.hangcheck_count = 0; return; } i915_get_extra_instdone(dev, instdone); - if (memcmp(dev_priv->last_acthd, acthd, sizeof(acthd)) == 0 && - memcmp(dev_priv->prev_instdone, instdone, sizeof(instdone)) == 0) { + if (memcmp(dev_priv->gpu_error.last_acthd, acthd, + sizeof(acthd)) == 0 && + memcmp(dev_priv->gpu_error.prev_instdone, instdone, + sizeof(instdone)) == 0) { if (i915_hangcheck_hung(dev)) return; } else { - dev_priv->hangcheck_count = 0; + dev_priv->gpu_error.hangcheck_count = 0; - memcpy(dev_priv->last_acthd, acthd, sizeof(acthd)); - memcpy(dev_priv->prev_instdone, instdone, sizeof(instdone)); + memcpy(dev_priv->gpu_error.last_acthd, acthd, + sizeof(acthd)); + memcpy(dev_priv->gpu_error.prev_instdone, instdone, + sizeof(instdone)); } repeat: /* Reset timer case chip hangs without another request being added */ - mod_timer(&dev_priv->hangcheck_timer, + mod_timer(&dev_priv->gpu_error.hangcheck_timer, round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES)); } @@ -2769,11 +2773,12 @@ void intel_irq_init(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); - INIT_WORK(&dev_priv->error_work, i915_error_work_func); + INIT_WORK(&dev_priv->gpu_error.work, i915_error_work_func); INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work); INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work); - setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, + setup_timer(&dev_priv->gpu_error.hangcheck_timer, + i915_hangcheck_elapsed, (unsigned long) dev); pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index ce1d07487402..d6b06aa4c05c 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1491,7 +1491,7 @@ void intel_ring_advance(struct intel_ring_buffer *ring) struct drm_i915_private *dev_priv = ring->dev->dev_private; ring->tail &= ring->size - 1; - if (dev_priv->stop_rings & intel_ring_flag(ring)) + if (dev_priv->gpu_error.stop_rings & intel_ring_flag(ring)) return; ring->write_tail(ring, ring->tail); } -- cgit v1.2.3 From 33196deddacc7790defb9a7e84659e0362d4da7a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 14 Nov 2012 17:14:05 +0100 Subject: drm/i915: move wedged to the other gpu error handling stuff And to make Ben Widawsky happier, use the gpu_error instead of the entire device as the argument in some functions. Drop the outdated comment on ->wedged for now, a follow-up patch will change the semantics and add a proper comment again. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 2 +- drivers/gpu/drm/i915/i915_drv.h | 13 +++---------- drivers/gpu/drm/i915/i915_gem.c | 34 ++++++++++++++++----------------- drivers/gpu/drm/i915/i915_irq.c | 6 +++--- drivers/gpu/drm/i915/intel_display.c | 4 ++-- drivers/gpu/drm/i915/intel_ringbuffer.c | 6 ++++-- 6 files changed, 30 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 3b1bf4e70d94..e1b7eaf60d24 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1672,7 +1672,7 @@ i915_wedged_read(struct file *filp, len = snprintf(buf, sizeof(buf), "wedged : %d\n", - atomic_read(&dev_priv->mm.wedged)); + atomic_read(&dev_priv->gpu_error.wedged)); if (len > sizeof(buf)) len = sizeof(buf); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index dfe0e747c4f7..62da6c7a4884 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -744,15 +744,6 @@ struct i915_gem_mm { */ int suspended; - /** - * Flag if the hardware appears to be wedged. - * - * This is set when attempts to idle the device timeout. - * It prevents command submission from occurring and makes - * every pending request fail - */ - atomic_t wedged; - /** Bit 6 swizzling required for X tiling */ uint32_t bit_6_swizzle_x; /** Bit 6 swizzling required for Y tiling */ @@ -784,6 +775,8 @@ struct i915_gpu_error { unsigned long last_reset; + atomic_t wedged; + /* For gpu hang simulation. */ unsigned int stop_rings; }; @@ -1548,7 +1541,7 @@ i915_gem_object_unpin_fence(struct drm_i915_gem_object *obj) void i915_gem_retire_requests(struct drm_device *dev); void i915_gem_retire_requests_ring(struct intel_ring_buffer *ring); -int __must_check i915_gem_check_wedge(struct drm_i915_private *dev_priv, +int __must_check i915_gem_check_wedge(struct i915_gpu_error *error, bool interruptible); void i915_gem_reset(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 95e022e7a26e..04b2f92eb456 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -87,14 +87,13 @@ static void i915_gem_info_remove_obj(struct drm_i915_private *dev_priv, } static int -i915_gem_wait_for_error(struct drm_device *dev) +i915_gem_wait_for_error(struct i915_gpu_error *error) { - struct drm_i915_private *dev_priv = dev->dev_private; - struct completion *x = &dev_priv->gpu_error.completion; + struct completion *x = &error->completion; unsigned long flags; int ret; - if (!atomic_read(&dev_priv->mm.wedged)) + if (!atomic_read(&error->wedged)) return 0; /* @@ -110,7 +109,7 @@ i915_gem_wait_for_error(struct drm_device *dev) return ret; } - if (atomic_read(&dev_priv->mm.wedged)) { + if (atomic_read(&error->wedged)) { /* GPU is hung, bump the completion count to account for * the token we just consumed so that we never hit zero and * end up waiting upon a subsequent completion event that @@ -125,9 +124,10 @@ i915_gem_wait_for_error(struct drm_device *dev) int i915_mutex_lock_interruptible(struct drm_device *dev) { + struct drm_i915_private *dev_priv = dev->dev_private; int ret; - ret = i915_gem_wait_for_error(dev); + ret = i915_gem_wait_for_error(&dev_priv->gpu_error); if (ret) return ret; @@ -939,11 +939,11 @@ unlock: } int -i915_gem_check_wedge(struct drm_i915_private *dev_priv, +i915_gem_check_wedge(struct i915_gpu_error *error, bool interruptible) { - if (atomic_read(&dev_priv->mm.wedged)) { - struct completion *x = &dev_priv->gpu_error.completion; + if (atomic_read(&error->wedged)) { + struct completion *x = &error->completion; bool recovery_complete; unsigned long flags; @@ -1025,7 +1025,7 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, #define EXIT_COND \ (i915_seqno_passed(ring->get_seqno(ring, false), seqno) || \ - atomic_read(&dev_priv->mm.wedged)) + atomic_read(&dev_priv->gpu_error.wedged)) do { if (interruptible) end = wait_event_interruptible_timeout(ring->irq_queue, @@ -1035,7 +1035,7 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, end = wait_event_timeout(ring->irq_queue, EXIT_COND, timeout_jiffies); - ret = i915_gem_check_wedge(dev_priv, interruptible); + ret = i915_gem_check_wedge(&dev_priv->gpu_error, interruptible); if (ret) end = ret; } while (end == 0 && wait_forever); @@ -1081,7 +1081,7 @@ i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno) BUG_ON(!mutex_is_locked(&dev->struct_mutex)); BUG_ON(seqno == 0); - ret = i915_gem_check_wedge(dev_priv, interruptible); + ret = i915_gem_check_wedge(&dev_priv->gpu_error, interruptible); if (ret) return ret; @@ -1146,7 +1146,7 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj, if (seqno == 0) return 0; - ret = i915_gem_check_wedge(dev_priv, true); + ret = i915_gem_check_wedge(&dev_priv->gpu_error, true); if (ret) return ret; @@ -1379,7 +1379,7 @@ out: /* If this -EIO is due to a gpu hang, give the reset code a * chance to clean up the mess. Otherwise return the proper * SIGBUS. */ - if (!atomic_read(&dev_priv->mm.wedged)) + if (!atomic_read(&dev_priv->gpu_error.wedged)) return VM_FAULT_SIGBUS; case -EAGAIN: /* Give the error handler a chance to run and move the @@ -3390,7 +3390,7 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) u32 seqno = 0; int ret; - if (atomic_read(&dev_priv->mm.wedged)) + if (atomic_read(&dev_priv->gpu_error.wedged)) return -EIO; spin_lock(&file_priv->mm.lock); @@ -3978,9 +3978,9 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data, if (drm_core_check_feature(dev, DRIVER_MODESET)) return 0; - if (atomic_read(&dev_priv->mm.wedged)) { + if (atomic_read(&dev_priv->gpu_error.wedged)) { DRM_ERROR("Reenabling wedged hardware, good luck\n"); - atomic_set(&dev_priv->mm.wedged, 0); + atomic_set(&dev_priv->gpu_error.wedged, 0); } mutex_lock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index c768ebdf8a27..f2c0016fa533 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -871,11 +871,11 @@ static void i915_error_work_func(struct work_struct *work) kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event); - if (atomic_read(&dev_priv->mm.wedged)) { + if (atomic_read(&dev_priv->gpu_error.wedged)) { DRM_DEBUG_DRIVER("resetting chip\n"); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event); if (!i915_reset(dev)) { - atomic_set(&dev_priv->mm.wedged, 0); + atomic_set(&dev_priv->gpu_error.wedged, 0); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event); } complete_all(&dev_priv->gpu_error.completion); @@ -1483,7 +1483,7 @@ void i915_handle_error(struct drm_device *dev, bool wedged) if (wedged) { INIT_COMPLETION(dev_priv->gpu_error.completion); - atomic_set(&dev_priv->mm.wedged, 1); + atomic_set(&dev_priv->gpu_error.wedged, 1); /* * Wakeup waiting processes so they don't hang diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b35902e5d925..160aa5f76118 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2223,7 +2223,7 @@ intel_finish_fb(struct drm_framebuffer *old_fb) WARN_ON(waitqueue_active(&dev_priv->pending_flip_queue)); wait_event(dev_priv->pending_flip_queue, - atomic_read(&dev_priv->mm.wedged) || + atomic_read(&dev_priv->gpu_error.wedged) || atomic_read(&obj->pending_flip) == 0); /* Big Hammer, we also need to ensure that any pending @@ -2871,7 +2871,7 @@ static bool intel_crtc_has_pending_flip(struct drm_crtc *crtc) unsigned long flags; bool pending; - if (atomic_read(&dev_priv->mm.wedged)) + if (atomic_read(&dev_priv->gpu_error.wedged)) return false; spin_lock_irqsave(&dev->event_lock, flags); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index d6b06aa4c05c..9438bcd50678 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1371,7 +1371,8 @@ static int ring_wait_for_space(struct intel_ring_buffer *ring, int n) msleep(1); - ret = i915_gem_check_wedge(dev_priv, dev_priv->mm.interruptible); + ret = i915_gem_check_wedge(&dev_priv->gpu_error, + dev_priv->mm.interruptible); if (ret) return ret; } while (!time_after(jiffies, end)); @@ -1460,7 +1461,8 @@ int intel_ring_begin(struct intel_ring_buffer *ring, drm_i915_private_t *dev_priv = ring->dev->dev_private; int ret; - ret = i915_gem_check_wedge(dev_priv, dev_priv->mm.interruptible); + ret = i915_gem_check_wedge(&dev_priv->gpu_error, + dev_priv->mm.interruptible); if (ret) return ret; -- cgit v1.2.3 From 308887aad14c4ecc3fc10a3c58ec42641c5e4423 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 14 Nov 2012 17:14:06 +0100 Subject: drm/i915: fix reset handling in the throttle ioctl While auditing the code I've noticed one place (the throttle ioctl) which does not yet wait for the reset handler to complete and doesn't properly decode the wedge state into -EAGAIN/-EIO. Fix this up by calling the right helpers. This might explain the oddball "my compositor just died in a successfull gpu reset" reports. Or maybe not, since current mesa doesn't use this ioctl to throttle command submission. The throttle ioctl doesn't take the struct_mutex, so to avoid busy-looping with -EAGAIN while a reset is in process, check for errors first and wait for the handler to complete if a reset is pending by calling i915_gem_wait_for_error. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 04b2f92eb456..c96a501b8205 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3390,8 +3390,13 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) u32 seqno = 0; int ret; - if (atomic_read(&dev_priv->gpu_error.wedged)) - return -EIO; + ret = i915_gem_wait_for_error(&dev_priv->gpu_error); + if (ret) + return ret; + + ret = i915_gem_check_wedge(&dev_priv->gpu_error, false); + if (ret) + return ret; spin_lock(&file_priv->mm.lock); list_for_each_entry(request, &file_priv->mm.request_list, client_list) { -- cgit v1.2.3 From 1f83fee08d625f8d0130f9fe5ef7b17c2e022f3c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Nov 2012 17:17:22 +0100 Subject: drm/i915: clear up wedged transitions We have two important transitions of the wedged state in the current code: - 0 -> 1: This means a hang has been detected, and signals to everyone that they please get of any locks, so that the reset work item can do its job. - 1 -> 0: The reset handler has completed. Now the last transition mixes up two states: "Reset completed and successful" and "Reset failed". To distinguish these two we do some tricks with the reset completion, but I simply could not convince myself that this doesn't race under odd circumstances. Hence split this up, and add a new terminal state indicating that the hw is gone for good. Also add explicit #defines for both states, update comments. v2: Split out the reset handling bugfix for the throttle ioctl. v3: s/tmp/wedged/ sugested by Chris Wilson. Also fixup up a rebase error which prevented this patch from actually compiling. v4: To unify the wedged state with the reset counter, keep the reset-in-progress state just as a flag. The terminally-wedged state is now denoted with a big number. v5: Add a comment to the reset_counter special values explaining that WEDGED & RESET_IN_PROGRESS needs to be true for the code to be correct. v6: Fixup logic errors introduced with the wedged+reset_counter unification. Since WEDGED implies reset-in-progress (in a way we're terminally stuck in the dead-but-reset-not-completed state), we need ensure that we check for this everywhere. The specific bug was in wait_for_error, which would simply have timed out. v7: Extract an inline i915_reset_in_progress helper to make the code more readable. Also annote the reset-in-progress case with an unlikely, to help the compiler optimize the fastpath. Do the same for the terminally wedged case with i915_terminally_wedged. Reviewed-by: Damien Lespiau Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 2 +- drivers/gpu/drm/i915/i915_drv.h | 40 +++++++++++++++++++++++++++-- drivers/gpu/drm/i915/i915_gem.c | 49 +++++++++++++----------------------- drivers/gpu/drm/i915/i915_irq.c | 23 +++++++++++------ drivers/gpu/drm/i915/intel_display.c | 4 +-- 5 files changed, 74 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index e1b7eaf60d24..384f19368a1d 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1672,7 +1672,7 @@ i915_wedged_read(struct file *filp, len = snprintf(buf, sizeof(buf), "wedged : %d\n", - atomic_read(&dev_priv->gpu_error.wedged)); + atomic_read(&dev_priv->gpu_error.reset_counter)); if (len > sizeof(buf)) len = sizeof(buf); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 62da6c7a4884..c84743bb6937 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -771,11 +771,37 @@ struct i915_gpu_error { /* Protected by the above dev->gpu_error.lock. */ struct drm_i915_error_state *first_error; struct work_struct work; - struct completion completion; unsigned long last_reset; - atomic_t wedged; + /** + * State variable controlling the reset flow + * + * Upper bits are for the reset counter. + * + * Lowest bit controls the reset state machine: Set means a reset is in + * progress. This state will (presuming we don't have any bugs) decay + * into either unset (successful reset) or the special WEDGED value (hw + * terminally sour). All waiters on the reset_queue will be woken when + * that happens. + */ + atomic_t reset_counter; + + /** + * Special values/flags for reset_counter + * + * Note that the code relies on + * I915_WEDGED & I915_RESET_IN_PROGRESS_FLAG + * being true. + */ +#define I915_RESET_IN_PROGRESS_FLAG 1 +#define I915_WEDGED 0xffffffff + + /** + * Waitqueue to signal when the reset has completed. Used by clients + * that wait for dev_priv->mm.wedged to settle. + */ + wait_queue_head_t reset_queue; /* For gpu hang simulation. */ unsigned int stop_rings; @@ -1543,6 +1569,16 @@ void i915_gem_retire_requests(struct drm_device *dev); void i915_gem_retire_requests_ring(struct intel_ring_buffer *ring); int __must_check i915_gem_check_wedge(struct i915_gpu_error *error, bool interruptible); +static inline bool i915_reset_in_progress(struct i915_gpu_error *error) +{ + return unlikely(atomic_read(&error->reset_counter) + & I915_RESET_IN_PROGRESS_FLAG); +} + +static inline bool i915_terminally_wedged(struct i915_gpu_error *error) +{ + return atomic_read(&error->reset_counter) == I915_WEDGED; +} void i915_gem_reset(struct drm_device *dev); void i915_gem_clflush_object(struct drm_i915_gem_object *obj); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index c96a501b8205..2ca901194824 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -89,36 +89,32 @@ static void i915_gem_info_remove_obj(struct drm_i915_private *dev_priv, static int i915_gem_wait_for_error(struct i915_gpu_error *error) { - struct completion *x = &error->completion; - unsigned long flags; int ret; - if (!atomic_read(&error->wedged)) +#define EXIT_COND (!i915_reset_in_progress(error)) + if (EXIT_COND) return 0; + /* GPU is already declared terminally dead, give up. */ + if (i915_terminally_wedged(error)) + return -EIO; + /* * Only wait 10 seconds for the gpu reset to complete to avoid hanging * userspace. If it takes that long something really bad is going on and * we should simply try to bail out and fail as gracefully as possible. */ - ret = wait_for_completion_interruptible_timeout(x, 10*HZ); + ret = wait_event_interruptible_timeout(error->reset_queue, + EXIT_COND, + 10*HZ); if (ret == 0) { DRM_ERROR("Timed out waiting for the gpu reset to complete\n"); return -EIO; } else if (ret < 0) { return ret; } +#undef EXIT_COND - if (atomic_read(&error->wedged)) { - /* GPU is hung, bump the completion count to account for - * the token we just consumed so that we never hit zero and - * end up waiting upon a subsequent completion event that - * will never happen. - */ - spin_lock_irqsave(&x->wait.lock, flags); - x->done++; - spin_unlock_irqrestore(&x->wait.lock, flags); - } return 0; } @@ -942,23 +938,14 @@ int i915_gem_check_wedge(struct i915_gpu_error *error, bool interruptible) { - if (atomic_read(&error->wedged)) { - struct completion *x = &error->completion; - bool recovery_complete; - unsigned long flags; - - /* Give the error handler a chance to run. */ - spin_lock_irqsave(&x->wait.lock, flags); - recovery_complete = x->done > 0; - spin_unlock_irqrestore(&x->wait.lock, flags); - + if (i915_reset_in_progress(error)) { /* Non-interruptible callers can't handle -EAGAIN, hence return * -EIO unconditionally for these. */ if (!interruptible) return -EIO; - /* Recovery complete, but still wedged means reset failure. */ - if (recovery_complete) + /* Recovery complete, but the reset failed ... */ + if (i915_terminally_wedged(error)) return -EIO; return -EAGAIN; @@ -1025,7 +1012,7 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, #define EXIT_COND \ (i915_seqno_passed(ring->get_seqno(ring, false), seqno) || \ - atomic_read(&dev_priv->gpu_error.wedged)) + i915_reset_in_progress(&dev_priv->gpu_error)) do { if (interruptible) end = wait_event_interruptible_timeout(ring->irq_queue, @@ -1379,7 +1366,7 @@ out: /* If this -EIO is due to a gpu hang, give the reset code a * chance to clean up the mess. Otherwise return the proper * SIGBUS. */ - if (!atomic_read(&dev_priv->gpu_error.wedged)) + if (i915_terminally_wedged(&dev_priv->gpu_error)) return VM_FAULT_SIGBUS; case -EAGAIN: /* Give the error handler a chance to run and move the @@ -3983,9 +3970,9 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data, if (drm_core_check_feature(dev, DRIVER_MODESET)) return 0; - if (atomic_read(&dev_priv->gpu_error.wedged)) { + if (i915_reset_in_progress(&dev_priv->gpu_error)) { DRM_ERROR("Reenabling wedged hardware, good luck\n"); - atomic_set(&dev_priv->gpu_error.wedged, 0); + atomic_set(&dev_priv->gpu_error.reset_counter, 0); } mutex_lock(&dev->struct_mutex); @@ -4069,7 +4056,7 @@ i915_gem_load(struct drm_device *dev) INIT_LIST_HEAD(&dev_priv->fence_regs[i].lru_list); INIT_DELAYED_WORK(&dev_priv->mm.retire_work, i915_gem_retire_work_handler); - init_completion(&dev_priv->gpu_error.completion); + init_waitqueue_head(&dev_priv->gpu_error.reset_queue); /* On GEN3 we really need to make sure the ARB C3 LP bit is set */ if (IS_GEN3(dev)) { diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index f2c0016fa533..4562c5406ef8 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -862,8 +862,10 @@ done: */ static void i915_error_work_func(struct work_struct *work) { - drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, - gpu_error.work); + struct i915_gpu_error *error = container_of(work, struct i915_gpu_error, + work); + drm_i915_private_t *dev_priv = container_of(error, drm_i915_private_t, + gpu_error); struct drm_device *dev = dev_priv->dev; char *error_event[] = { "ERROR=1", NULL }; char *reset_event[] = { "RESET=1", NULL }; @@ -871,14 +873,18 @@ static void i915_error_work_func(struct work_struct *work) kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event); - if (atomic_read(&dev_priv->gpu_error.wedged)) { + if (i915_reset_in_progress(error)) { DRM_DEBUG_DRIVER("resetting chip\n"); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event); + if (!i915_reset(dev)) { - atomic_set(&dev_priv->gpu_error.wedged, 0); + atomic_set(&error->reset_counter, 0); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event); + } else { + atomic_set(&error->reset_counter, I915_WEDGED); } - complete_all(&dev_priv->gpu_error.completion); + + wake_up_all(&dev_priv->gpu_error.reset_queue); } } @@ -1482,11 +1488,12 @@ void i915_handle_error(struct drm_device *dev, bool wedged) i915_report_and_clear_eir(dev); if (wedged) { - INIT_COMPLETION(dev_priv->gpu_error.completion); - atomic_set(&dev_priv->gpu_error.wedged, 1); + atomic_set(&dev_priv->gpu_error.reset_counter, + I915_RESET_IN_PROGRESS_FLAG); /* - * Wakeup waiting processes so they don't hang + * Wakeup waiting processes so that the reset work item + * doesn't deadlock trying to grab various locks. */ for_each_ring(ring, dev_priv, i) wake_up_all(&ring->irq_queue); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 160aa5f76118..77254460a5cb 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2223,7 +2223,7 @@ intel_finish_fb(struct drm_framebuffer *old_fb) WARN_ON(waitqueue_active(&dev_priv->pending_flip_queue)); wait_event(dev_priv->pending_flip_queue, - atomic_read(&dev_priv->gpu_error.wedged) || + i915_reset_in_progress(&dev_priv->gpu_error) || atomic_read(&obj->pending_flip) == 0); /* Big Hammer, we also need to ensure that any pending @@ -2871,7 +2871,7 @@ static bool intel_crtc_has_pending_flip(struct drm_crtc *crtc) unsigned long flags; bool pending; - if (atomic_read(&dev_priv->gpu_error.wedged)) + if (i915_reset_in_progress(&dev_priv->gpu_error)) return false; spin_lock_irqsave(&dev->event_lock, flags); -- cgit v1.2.3 From d0a57789d5ec807fc218151b2fb2de4da30fbef5 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 9 Oct 2012 19:24:37 +0100 Subject: drm/i915: Only insert the mb() before updating the fence parameter With a fence, we only need to insert a memory barrier around the actual fence alteration for CPU accesses through the GTT. Performing the barrier in flush-fence was inserting unnecessary and expensive barriers for never fenced objects. Note removing the barriers from flush-fence, which was effectively a barrier before every direct access through the GTT, revealed that we where missing a barrier before the first access through the GTT. Lack of that barrier was sufficient to cause GPU hangs. v2: Add a couple more comments to explain the new barriers Signed-off-by: Chris Wilson Reviewed-by: Daniel Vetter Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 2ca901194824..ce706555d011 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2611,9 +2611,22 @@ static void i830_write_fence_reg(struct drm_device *dev, int reg, POSTING_READ(FENCE_REG_830_0 + reg * 4); } +inline static bool i915_gem_object_needs_mb(struct drm_i915_gem_object *obj) +{ + return obj && obj->base.read_domains & I915_GEM_DOMAIN_GTT; +} + static void i915_gem_write_fence(struct drm_device *dev, int reg, struct drm_i915_gem_object *obj) { + struct drm_i915_private *dev_priv = dev->dev_private; + + /* Ensure that all CPU reads are completed before installing a fence + * and all writes before removing the fence. + */ + if (i915_gem_object_needs_mb(dev_priv->fence_regs[reg].obj)) + mb(); + switch (INTEL_INFO(dev)->gen) { case 7: case 6: @@ -2623,6 +2636,12 @@ static void i915_gem_write_fence(struct drm_device *dev, int reg, case 2: i830_write_fence_reg(dev, reg, obj); break; default: BUG(); } + + /* And similarly be paranoid that no direct access to this region + * is reordered to before the fence is installed. + */ + if (i915_gem_object_needs_mb(obj)) + mb(); } static inline int fence_number(struct drm_i915_private *dev_priv, @@ -2652,7 +2671,7 @@ static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj, } static int -i915_gem_object_flush_fence(struct drm_i915_gem_object *obj) +i915_gem_object_wait_fence(struct drm_i915_gem_object *obj) { if (obj->last_fenced_seqno) { int ret = i915_wait_seqno(obj->ring, obj->last_fenced_seqno); @@ -2662,12 +2681,6 @@ i915_gem_object_flush_fence(struct drm_i915_gem_object *obj) obj->last_fenced_seqno = 0; } - /* Ensure that all CPU reads are completed before installing a fence - * and all writes before removing the fence. - */ - if (obj->base.read_domains & I915_GEM_DOMAIN_GTT) - mb(); - obj->fenced_gpu_access = false; return 0; } @@ -2678,7 +2691,7 @@ i915_gem_object_put_fence(struct drm_i915_gem_object *obj) struct drm_i915_private *dev_priv = obj->base.dev->dev_private; int ret; - ret = i915_gem_object_flush_fence(obj); + ret = i915_gem_object_wait_fence(obj); if (ret) return ret; @@ -2752,7 +2765,7 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj) * will need to serialise the write to the associated fence register? */ if (obj->fence_dirty) { - ret = i915_gem_object_flush_fence(obj); + ret = i915_gem_object_wait_fence(obj); if (ret) return ret; } @@ -2773,7 +2786,7 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj) if (reg->obj) { struct drm_i915_gem_object *old = reg->obj; - ret = i915_gem_object_flush_fence(old); + ret = i915_gem_object_wait_fence(old); if (ret) return ret; @@ -3068,6 +3081,13 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write) i915_gem_object_flush_cpu_write_domain(obj); + /* Serialise direct access to this object with the barriers for + * coherent writes from the GPU, by effectively invalidating the + * GTT domain upon first access. + */ + if ((obj->base.read_domains & I915_GEM_DOMAIN_GTT) == 0) + mb(); + old_write_domain = obj->base.write_domain; old_read_domains = obj->base.read_domains; -- cgit v1.2.3 From 97c809fd9cf5e914322b53773ad0d67efe503fde Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 9 Oct 2012 19:24:38 +0100 Subject: drm/i915: Only apply the mb() when flushing the GTT domain during a finish Now that we seem to have brought order to the GTT barriers, the last one to review is the terminal barrier before we unbind the buffer from the GTT. This needs to only be performed if the buffer still resides in the GTT domain, and so we can skip some needless barriers otherwise. Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index ce706555d011..5bb370fdc99c 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2407,15 +2407,15 @@ static void i915_gem_object_finish_gtt(struct drm_i915_gem_object *obj) { u32 old_write_domain, old_read_domains; - /* Act a barrier for all accesses through the GTT */ - mb(); - /* Force a pagefault for domain tracking on next user access */ i915_gem_release_mmap(obj); if ((obj->base.read_domains & I915_GEM_DOMAIN_GTT) == 0) return; + /* Wait for any direct GTT access to complete */ + mb(); + old_read_domains = obj->base.read_domains; old_write_domain = obj->base.write_domain; -- cgit v1.2.3 From ed30933e6f3dbeaaab1de91e1bec25f42d5d32df Mon Sep 17 00:00:00 2001 From: Cong Ding Date: Tue, 15 Jan 2013 01:19:48 +0100 Subject: dma: remove unnecessary null pointer check in mmp_pdma.c the pointer cfg is dereferenced in line 594, so it's no reason to check null again in line 620. Signed-off-by: Cong Ding Signed-off-by: Vinod Koul --- drivers/dma/mmp_pdma.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index 13bdf4a7e1ec..41ad6a62f838 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -617,10 +617,8 @@ static int mmp_pdma_control(struct dma_chan *dchan, enum dma_ctrl_cmd cmd, else if (maxburst == 32) chan->dcmd |= DCMD_BURST32; - if (cfg) { - chan->dir = cfg->direction; - chan->drcmr = cfg->slave_id; - } + chan->dir = cfg->direction; + chan->drcmr = cfg->slave_id; chan->dev_addr = addr; break; default: -- cgit v1.2.3 From 855372c013bbad8369223f7c75242bd3c94f9345 Mon Sep 17 00:00:00 2001 From: Cong Ding Date: Tue, 15 Jan 2013 01:23:48 +0100 Subject: dma: sh/shdma-base.c: remove unnecessary null pointer check the variable chan is dereferenced in line 635, so it is no reason to check null again in line 641. Signed-off-by: Cong Ding Signed-off-by: Vinod Koul --- drivers/dma/sh/shdma-base.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index f4cd946d259d..4acb85a10250 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -638,9 +638,6 @@ static int shdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long flags; int ret; - if (!chan) - return -EINVAL; - switch (cmd) { case DMA_TERMINATE_ALL: spin_lock_irqsave(&schan->chan_lock, flags); -- cgit v1.2.3 From 8faf6b18a2a6bece008de1e6bb80f0c608e58483 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 23:43:11 +0100 Subject: drm: review locking rules in drm_crtc.c - config_cleanup was confused: It claimed that callers need to hold the modeset lock, but the connector|encoder_cleanup helpers grabbed that themselves (note that crtc_cleanup did _not_ grab the modeset lock). Which resulted in all drivers _not_ hodling the lock. Since this is for single-threaded cleanup code, drop the requirement from docs and also drop the lock_grabbing from all _cleanup functions. - Kill the LOCKING section in the doctype, since clearly we're not good enough to keep them up-to-date. And misleading locking documentation is worse than useless (see e.g. the comment in the vmgfx driver about the cleanup mess). And since for most functions the very first line either grabs the lock or has a WARN_ON(!locked) the documentation doesn't really add anything. - Instead put in some effort into explaining the only two special cases a bit better: config_init and config_cleanup are both called from single-threaded setup/teardown code, so don't do any locking. It's the driver's job though to enforce this. - Where lacking, add a WARN_ON(!is_locked). Not many places though, since locking around fbdev setup/teardown is through-roughly screwed up, and so will break almost every single WARN annotation I've tried to add. - Add a drm_modeset_is_locked helper - the Grate Modset Locking Rework will use the compiler to assist in the big reorg by renaming the mode lock, so start encapsulating things. Unfortunately this ended up in the "wrong" header file since it needs the definition of struct drm_device. v2: Drop most WARNS again - we hit them all over the place, mostly in the setup and teardown sequences. And trying to fix it up leads to nice deadlocks, since the locking in the setup code is really inconsistent. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 105 ++++++--------------------------------------- include/drm/drmP.h | 5 +++ 2 files changed, 18 insertions(+), 92 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f2d667b8bee2..b970e4147862 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -208,8 +208,6 @@ char *drm_get_connector_status_name(enum drm_connector_status status) * @ptr: object pointer, used to generate unique ID * @type: object type * - * LOCKING: - * * Create a unique identifier based on @ptr in @dev's identifier space. Used * for tracking modes, CRTCs and connectors. * @@ -247,9 +245,6 @@ again: * @dev: DRM device * @id: ID to free * - * LOCKING: - * Caller must hold DRM mode_config lock. - * * Free @id from @dev's unique identifier pool. */ static void drm_mode_object_put(struct drm_device *dev, @@ -279,9 +274,6 @@ EXPORT_SYMBOL(drm_mode_object_find); * drm_framebuffer_init - initialize a framebuffer * @dev: DRM device * - * LOCKING: - * Caller must hold mode config lock. - * * Allocates an ID for the framebuffer's parent mode object, sets its mode * functions & device file and adds it to the master fd list. * @@ -317,15 +309,12 @@ static void drm_framebuffer_free(struct kref *kref) /** * drm_framebuffer_unreference - unref a framebuffer - * - * LOCKING: - * Caller must hold mode config lock. */ void drm_framebuffer_unreference(struct drm_framebuffer *fb) { struct drm_device *dev = fb->dev; DRM_DEBUG("FB ID: %d\n", fb->base.id); - WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + WARN_ON(!drm_modeset_is_locked(dev)); kref_put(&fb->refcount, drm_framebuffer_free); } EXPORT_SYMBOL(drm_framebuffer_unreference); @@ -344,15 +333,13 @@ EXPORT_SYMBOL(drm_framebuffer_reference); * drm_framebuffer_cleanup - remove a framebuffer object * @fb: framebuffer to remove * - * LOCKING: - * Caller must hold mode config lock. - * * Scans all the CRTCs in @dev's mode_config. If they're using @fb, removes * it, setting it to NULL. */ void drm_framebuffer_cleanup(struct drm_framebuffer *fb) { struct drm_device *dev = fb->dev; + /* * This could be moved to drm_framebuffer_remove(), but for * debugging is nice to keep around the list of fb's that are @@ -370,9 +357,6 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup); * drm_framebuffer_remove - remove and unreference a framebuffer object * @fb: framebuffer to remove * - * LOCKING: - * Caller must hold mode config lock. - * * Scans all the CRTCs and planes in @dev's mode_config. If they're * using @fb, removes it, setting it to NULL. */ @@ -384,6 +368,8 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) struct drm_mode_set set; int ret; + WARN_ON(!drm_modeset_is_locked(dev)); + /* remove from any CRTC */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { if (crtc->fb == fb) { @@ -421,9 +407,6 @@ EXPORT_SYMBOL(drm_framebuffer_remove); * @crtc: CRTC object to init * @funcs: callbacks for the new CRTC * - * LOCKING: - * Takes mode_config lock. - * * Inits a new object created as base part of an driver crtc object. * * RETURNS: @@ -460,9 +443,6 @@ EXPORT_SYMBOL(drm_crtc_init); * drm_crtc_cleanup - Cleans up the core crtc usage. * @crtc: CRTC to cleanup * - * LOCKING: - * Caller must hold mode config lock. - * * Cleanup @crtc. Removes from drm modesetting space * does NOT free object, caller does that. */ @@ -484,9 +464,6 @@ EXPORT_SYMBOL(drm_crtc_cleanup); * @connector: connector the new mode * @mode: mode data * - * LOCKING: - * Caller must hold mode config lock. - * * Add @mode to @connector's mode list for later use. */ void drm_mode_probed_add(struct drm_connector *connector, @@ -501,9 +478,6 @@ EXPORT_SYMBOL(drm_mode_probed_add); * @connector: connector list to modify * @mode: mode to remove * - * LOCKING: - * Caller must hold mode config lock. - * * Remove @mode from @connector's mode list, then free it. */ void drm_mode_remove(struct drm_connector *connector, @@ -521,9 +495,6 @@ EXPORT_SYMBOL(drm_mode_remove); * @funcs: callbacks for this connector * @name: user visible name of the connector * - * LOCKING: - * Takes mode config lock. - * * Initialises a preallocated connector. Connectors should be * subclassed as part of driver connector objects. * @@ -577,9 +548,6 @@ EXPORT_SYMBOL(drm_connector_init); * drm_connector_cleanup - cleans up an initialised connector * @connector: connector to cleanup * - * LOCKING: - * Takes mode config lock. - * * Cleans up the connector but doesn't free the object. */ void drm_connector_cleanup(struct drm_connector *connector) @@ -596,11 +564,9 @@ void drm_connector_cleanup(struct drm_connector *connector) list_for_each_entry_safe(mode, t, &connector->user_modes, head) drm_mode_remove(connector, mode); - mutex_lock(&dev->mode_config.mutex); drm_mode_object_put(dev, &connector->base); list_del(&connector->head); dev->mode_config.num_connector--; - mutex_unlock(&dev->mode_config.mutex); } EXPORT_SYMBOL(drm_connector_cleanup); @@ -721,9 +687,6 @@ EXPORT_SYMBOL(drm_plane_cleanup); * drm_mode_create - create a new display mode * @dev: DRM device * - * LOCKING: - * Caller must hold DRM mode_config lock. - * * Create a new drm_display_mode, give it an ID, and return it. * * RETURNS: @@ -751,9 +714,6 @@ EXPORT_SYMBOL(drm_mode_create); * @dev: DRM device * @mode: mode to remove * - * LOCKING: - * Caller must hold mode config lock. - * * Free @mode's unique identifier, then free it. */ void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) @@ -978,11 +938,13 @@ EXPORT_SYMBOL(drm_mode_create_dirty_info_property); * drm_mode_config_init - initialize DRM mode_configuration structure * @dev: DRM device * - * LOCKING: - * None, should happen single threaded at init time. - * * Initialize @dev's mode_config structure, used for tracking the graphics * configuration of @dev. + * + * Since this initializes the modeset locks, no locking is possible. Which is no + * problem, since this should happen single threaded at init time. It is the + * driver's problem to ensure this guarantee. + * */ void drm_mode_config_init(struct drm_device *dev) { @@ -1057,12 +1019,13 @@ EXPORT_SYMBOL(drm_mode_group_init_legacy_group); * drm_mode_config_cleanup - free up DRM mode_config info * @dev: DRM device * - * LOCKING: - * Caller must hold mode config lock. - * * Free up all the connectors and CRTCs associated with this DRM device, then * free up the framebuffers and associated buffer objects. * + * Note that since this /should/ happen single-threaded at driver/device + * teardown time, no locking is required. It's the driver's job to ensure that + * this guarantee actually holds true. + * * FIXME: cleanup any dangling user buffer objects too */ void drm_mode_config_cleanup(struct drm_device *dev) @@ -1112,9 +1075,6 @@ EXPORT_SYMBOL(drm_mode_config_cleanup); * @out: drm_mode_modeinfo struct to return to the user * @in: drm_display_mode to use * - * LOCKING: - * None. - * * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to * the user. */ @@ -1151,9 +1111,6 @@ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, * @out: drm_display_mode to return to the user * @in: drm_mode_modeinfo to use * - * LOCKING: - * None. - * * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to * the caller. * @@ -1193,9 +1150,6 @@ static int drm_crtc_convert_umode(struct drm_display_mode *out, * @cmd: cmd from ioctl * @arg: arg from ioctl * - * LOCKING: - * Takes mode config lock. - * * Construct a set of configuration description structures and return * them to the user, including CRTC, connector and framebuffer configuration. * @@ -1381,9 +1335,6 @@ out: * @cmd: cmd from ioctl * @arg: arg from ioctl * - * LOCKING: - * Takes mode config lock. - * * Construct a CRTC configuration structure to return to the user. * * Called by the user via ioctl. @@ -1441,9 +1392,6 @@ out: * @cmd: cmd from ioctl * @arg: arg from ioctl * - * LOCKING: - * Takes mode config lock. - * * Construct a connector configuration structure to return to the user. * * Called by the user via ioctl. @@ -1618,9 +1566,6 @@ out: * @data: ioctl data * @file_priv: DRM file info * - * LOCKING: - * Takes mode config lock. - * * Return an plane count and set of IDs. */ int drm_mode_getplane_res(struct drm_device *dev, void *data, @@ -1667,9 +1612,6 @@ out: * @data: ioctl data * @file_priv: DRM file info * - * LOCKING: - * Takes mode config lock. - * * Return plane info, including formats supported, gamma size, any * current fb, etc. */ @@ -1735,9 +1677,6 @@ out: * @data: ioctl data* * @file_prive: DRM file info * - * LOCKING: - * Takes mode config lock. - * * Set plane info, including placement, fb, scaling, and other factors. * Or pass a NULL fb to disable. */ @@ -1867,9 +1806,6 @@ out: * @cmd: cmd from ioctl * @arg: arg from ioctl * - * LOCKING: - * Takes mode config lock. - * * Build a new CRTC configuration based on user request. * * Called by the user via ioctl. @@ -2125,9 +2061,6 @@ EXPORT_SYMBOL(drm_mode_legacy_fb_format); * @cmd: cmd from ioctl * @arg: arg from ioctl * - * LOCKING: - * Takes mode config lock. - * * Add a new FB to the specified CRTC, given a user request. * * Called by the user via ioctl. @@ -2309,9 +2242,6 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) * @cmd: cmd from ioctl * @arg: arg from ioctl * - * LOCKING: - * Takes mode config lock. - * * Add a new FB to the specified CRTC, given a user request with format. * * Called by the user via ioctl. @@ -2375,9 +2305,6 @@ out: * @cmd: cmd from ioctl * @arg: arg from ioctl * - * LOCKING: - * Takes mode config lock. - * * Remove the FB specified by the user. * * Called by the user via ioctl. @@ -2430,9 +2357,6 @@ out: * @cmd: cmd from ioctl * @arg: arg from ioctl * - * LOCKING: - * Takes mode config lock. - * * Lookup the FB given its ID and return info about it. * * Called by the user via ioctl. @@ -2549,9 +2473,6 @@ out_err1: * drm_fb_release - remove and free the FBs on this file * @filp: file * from the ioctl * - * LOCKING: - * Takes mode config lock. - * * Destroy all the FBs associated with @filp. * * Called by the user via ioctl. diff --git a/include/drm/drmP.h b/include/drm/drmP.h index fad21c927a38..3c609abe8c80 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1276,6 +1276,11 @@ static inline int drm_device_is_unplugged(struct drm_device *dev) return ret; } +static inline bool drm_modeset_is_locked(struct drm_device *dev) +{ + return mutex_is_locked(&dev->mode_config.mutex); +} + /******************************************************************/ /** \name Internal function definitions */ /*@{*/ -- cgit v1.2.3 From 065a50ed3ef75cb265e12e3e1b615db0835150bc Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 00:09:18 +0100 Subject: drm/doc: integrate drm_crtc.c kerneldoc And do a quick pass to adjust them to the last few (years?) of changes ... This time actually compile-tested ;-) Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- Documentation/DocBook/drm.tmpl | 4 ++ drivers/gpu/drm/drm_crtc.c | 92 ++++++++++++++++++++---------------------- 2 files changed, 48 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 4ee2304f82f9..caab791b0a6c 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -1609,6 +1609,10 @@ void intel_crt_init(struct drm_device *dev) make its properties available to applications. + + KMS API Functions +!Edrivers/gpu/drm/drm_crtc.c + diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index b970e4147862..81545540b2df 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -203,10 +203,10 @@ char *drm_get_connector_status_name(enum drm_connector_status status) } /** - * drm_mode_object_get - allocate a new identifier + * drm_mode_object_get - allocate a new modeset identifier * @dev: DRM device - * @ptr: object pointer, used to generate unique ID - * @type: object type + * @obj: object pointer, used to generate unique ID + * @obj_type: object type * * Create a unique identifier based on @ptr in @dev's identifier space. Used * for tracking modes, CRTCs and connectors. @@ -241,9 +241,9 @@ again: } /** - * drm_mode_object_put - free an identifer + * drm_mode_object_put - free a modeset identifer * @dev: DRM device - * @id: ID to free + * @object: object to free * * Free @id from @dev's unique identifier pool. */ @@ -273,6 +273,8 @@ EXPORT_SYMBOL(drm_mode_object_find); /** * drm_framebuffer_init - initialize a framebuffer * @dev: DRM device + * @fb: framebuffer to be initialized + * @funcs: ... with these functions * * Allocates an ID for the framebuffer's parent mode object, sets its mode * functions & device file and adds it to the master fd list. @@ -309,6 +311,9 @@ static void drm_framebuffer_free(struct kref *kref) /** * drm_framebuffer_unreference - unref a framebuffer + * @fb: framebuffer to unref + * + * This functions decrements the fb's refcount and frees it if it drops to zero. */ void drm_framebuffer_unreference(struct drm_framebuffer *fb) { @@ -321,6 +326,7 @@ EXPORT_SYMBOL(drm_framebuffer_unreference); /** * drm_framebuffer_reference - incr the fb refcnt + * @fb: framebuffer */ void drm_framebuffer_reference(struct drm_framebuffer *fb) { @@ -493,7 +499,7 @@ EXPORT_SYMBOL(drm_mode_remove); * @dev: DRM device * @connector: the connector to init * @funcs: callbacks for this connector - * @name: user visible name of the connector + * @connector_type: user visible type of the connector * * Initialises a preallocated connector. Connectors should be * subclassed as part of driver connector objects. @@ -1145,10 +1151,9 @@ static int drm_crtc_convert_umode(struct drm_display_mode *out, /** * drm_mode_getresources - get graphics configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Construct a set of configuration description structures and return * them to the user, including CRTC, connector and framebuffer configuration. @@ -1330,10 +1335,9 @@ out: /** * drm_mode_getcrtc - get CRTC configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Construct a CRTC configuration structure to return to the user. * @@ -1387,10 +1391,9 @@ out: /** * drm_mode_getconnector - get connector configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Construct a connector configuration structure to return to the user. * @@ -1675,7 +1678,7 @@ out: * drm_mode_setplane - set up or tear down an plane * @dev: DRM device * @data: ioctl data* - * @file_prive: DRM file info + * @file_priv: DRM file info * * Set plane info, including placement, fb, scaling, and other factors. * Or pass a NULL fb to disable. @@ -1801,10 +1804,9 @@ out: /** * drm_mode_setcrtc - set CRTC configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Build a new CRTC configuration based on user request. * @@ -2056,10 +2058,9 @@ EXPORT_SYMBOL(drm_mode_legacy_fb_format); /** * drm_mode_addfb - add an FB to the graphics configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Add a new FB to the specified CRTC, given a user request. * @@ -2237,10 +2238,9 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) /** * drm_mode_addfb2 - add an FB to the graphics configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Add a new FB to the specified CRTC, given a user request with format. * @@ -2300,10 +2300,9 @@ out: /** * drm_mode_rmfb - remove an FB from the configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Remove the FB specified by the user. * @@ -2352,10 +2351,9 @@ out: /** * drm_mode_getfb - get FB info - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Lookup the FB given its ID and return info about it. * @@ -2471,7 +2469,7 @@ out_err1: /** * drm_fb_release - remove and free the FBs on this file - * @filp: file * from the ioctl + * @priv: drm file for the ioctl * * Destroy all the FBs associated with @filp. * @@ -2581,10 +2579,9 @@ EXPORT_SYMBOL(drm_mode_detachmode_crtc); /** * drm_fb_attachmode - Attach a user mode to an connector - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * This attaches a user specified mode to an connector. * Called by the user via ioctl. @@ -2636,10 +2633,9 @@ out: /** * drm_fb_detachmode - Detach a user specified mode from an connector - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Called by the user via ioctl. * -- cgit v1.2.3 From c7d73f6a8ad71f9d9f58c86981322c6e48093a4f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 13 Dec 2012 23:38:38 +0100 Subject: drm/: reorder framebuffer init sequence With more fine-grained locking we can no longer rely on the big mode_config lock to prevent concurrent access to mode resources like framebuffers. Instead a framebuffer becomes accessible to other threads as soon as it is added to the relevant lookup structures. Hence it needs to be fully set up by the time drivers call drm_framebuffer_init. This patch here is the drivers part of that reorg. Nothing really fancy going on safe for three special cases. - exynos needs to be careful to properly unref all handles. - nouveau gets a resource leak fixed for free: one of the error cases didn't cleanup the framebuffer, which is now moot since the framebuffer is only registered once it is fully set up. - vmwgfx requires a slight reordering of operations, I'm hoping I didn't break anything (but it's refcount management only, so should be safe). v2: Split out exynos, since it's a bit more hairy than expected. v3: Drop bogus cirrus hunk noticed by Richard Wilbur. v4: Split out vmwgfx since there's a small change in return values. Reviewed-by: Rob Clark (core + omapdrm) Signed-off-by: Daniel Vetter --- drivers/gpu/drm/ast/ast_main.c | 4 ++-- drivers/gpu/drm/cirrus/cirrus_main.c | 4 ++-- drivers/gpu/drm/drm_fb_cma_helper.c | 10 +++++----- drivers/gpu/drm/gma500/framebuffer.c | 4 ++-- drivers/gpu/drm/i915/intel_display.c | 5 +++-- drivers/gpu/drm/mgag200/mgag200_main.c | 8 +++++--- drivers/gpu/drm/nouveau/nouveau_display.c | 10 +++++----- drivers/gpu/drm/radeon/radeon_display.c | 2 +- drivers/gpu/drm/udl/udl_fb.c | 2 +- drivers/staging/omapdrm/omap_fb.c | 16 ++++++++-------- 10 files changed, 34 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index f668e6cc0f7a..d5ba7097e5b5 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -266,13 +266,13 @@ int ast_framebuffer_init(struct drm_device *dev, { int ret; + drm_helper_mode_fill_fb_struct(&ast_fb->base, mode_cmd); + ast_fb->obj = obj; ret = drm_framebuffer_init(dev, &ast_fb->base, &ast_fb_funcs); if (ret) { DRM_ERROR("framebuffer init failed %d\n", ret); return ret; } - drm_helper_mode_fill_fb_struct(&ast_fb->base, mode_cmd); - ast_fb->obj = obj; return 0; } diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c index 6a9b12e88d46..364474c66202 100644 --- a/drivers/gpu/drm/cirrus/cirrus_main.c +++ b/drivers/gpu/drm/cirrus/cirrus_main.c @@ -42,13 +42,13 @@ int cirrus_framebuffer_init(struct drm_device *dev, { int ret; + drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); + gfb->obj = obj; ret = drm_framebuffer_init(dev, &gfb->base, &cirrus_fb_funcs); if (ret) { DRM_ERROR("drm_framebuffer_init failed: %d\n", ret); return ret; } - drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); - gfb->obj = obj; return 0; } diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index fd9d0af4d536..e1e0cb0d531a 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -85,6 +85,11 @@ static struct drm_fb_cma *drm_fb_cma_alloc(struct drm_device *dev, if (!fb_cma) return ERR_PTR(-ENOMEM); + drm_helper_mode_fill_fb_struct(&fb_cma->fb, mode_cmd); + + for (i = 0; i < num_planes; i++) + fb_cma->obj[i] = obj[i]; + ret = drm_framebuffer_init(dev, &fb_cma->fb, &drm_fb_cma_funcs); if (ret) { dev_err(dev->dev, "Failed to initalize framebuffer: %d\n", ret); @@ -92,11 +97,6 @@ static struct drm_fb_cma *drm_fb_cma_alloc(struct drm_device *dev, return ERR_PTR(ret); } - drm_helper_mode_fill_fb_struct(&fb_cma->fb, mode_cmd); - - for (i = 0; i < num_planes; i++) - fb_cma->obj[i] = obj[i]; - return fb_cma; } diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index afded54dbb10..38e7e7597de2 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -260,13 +260,13 @@ static int psb_framebuffer_init(struct drm_device *dev, default: return -EINVAL; } + drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd); + fb->gtt = gt; ret = drm_framebuffer_init(dev, &fb->base, &psb_fb_funcs); if (ret) { dev_err(dev->dev, "framebuffer init failed: %d\n", ret); return ret; } - drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd); - fb->gtt = gt; return 0; } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index da1ad9c80bb5..26fa6a795afe 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8666,14 +8666,15 @@ int intel_framebuffer_init(struct drm_device *dev, if (mode_cmd->offsets[0] != 0) return -EINVAL; + drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd); + intel_fb->obj = obj; + ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs); if (ret) { DRM_ERROR("framebuffer init failed %d\n", ret); return ret; } - drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd); - intel_fb->obj = obj; return 0; } diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c index 70dd3c5529d4..266438af61ea 100644 --- a/drivers/gpu/drm/mgag200/mgag200_main.c +++ b/drivers/gpu/drm/mgag200/mgag200_main.c @@ -40,13 +40,15 @@ int mgag200_framebuffer_init(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object *obj) { - int ret = drm_framebuffer_init(dev, &gfb->base, &mga_fb_funcs); + int ret; + + drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); + gfb->obj = obj; + ret = drm_framebuffer_init(dev, &gfb->base, &mga_fb_funcs); if (ret) { DRM_ERROR("drm_framebuffer_init failed: %d\n", ret); return ret; } - drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); - gfb->obj = obj; return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 508b00a2ce0d..d42c9e860c16 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -78,11 +78,6 @@ nouveau_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb = &nv_fb->base; int ret; - ret = drm_framebuffer_init(dev, fb, &nouveau_framebuffer_funcs); - if (ret) { - return ret; - } - drm_helper_mode_fill_fb_struct(fb, mode_cmd); nv_fb->nvbo = nvbo; @@ -125,6 +120,11 @@ nouveau_framebuffer_init(struct drm_device *dev, } } + ret = drm_framebuffer_init(dev, fb, &nouveau_framebuffer_funcs); + if (ret) { + return ret; + } + return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 1da2386d7cf7..12ec3effb409 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -1089,12 +1089,12 @@ radeon_framebuffer_init(struct drm_device *dev, { int ret; rfb->obj = obj; + drm_helper_mode_fill_fb_struct(&rfb->base, mode_cmd); ret = drm_framebuffer_init(dev, &rfb->base, &radeon_fb_funcs); if (ret) { rfb->obj = NULL; return ret; } - drm_helper_mode_fill_fb_struct(&rfb->base, mode_cmd); return 0; } diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index d4ab3beaada0..829c4a76938c 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -435,8 +435,8 @@ udl_framebuffer_init(struct drm_device *dev, int ret; ufb->obj = obj; - ret = drm_framebuffer_init(dev, &ufb->base, &udlfb_funcs); drm_helper_mode_fill_fb_struct(&ufb->base, mode_cmd); + ret = drm_framebuffer_init(dev, &ufb->base, &udlfb_funcs); return ret; } diff --git a/drivers/staging/omapdrm/omap_fb.c b/drivers/staging/omapdrm/omap_fb.c index 09028e9c1093..bf6421f26c40 100644 --- a/drivers/staging/omapdrm/omap_fb.c +++ b/drivers/staging/omapdrm/omap_fb.c @@ -424,14 +424,6 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, } fb = &omap_fb->base; - ret = drm_framebuffer_init(dev, fb, &omap_framebuffer_funcs); - if (ret) { - dev_err(dev->dev, "framebuffer init failed: %d\n", ret); - goto fail; - } - - DBG("create: FB ID: %d (%p)", fb->base.id, fb); - omap_fb->format = format; for (i = 0; i < n; i++) { @@ -462,6 +454,14 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, drm_helper_mode_fill_fb_struct(fb, mode_cmd); + ret = drm_framebuffer_init(dev, fb, &omap_framebuffer_funcs); + if (ret) { + dev_err(dev->dev, "framebuffer init failed: %d\n", ret); + goto fail; + } + + DBG("create: FB ID: %d (%p)", fb->base.id, fb); + return fb; fail: -- cgit v1.2.3 From 80f0b5aff8f49f63eaf08bdae243de3a8ea3f77e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 13 Dec 2012 23:39:01 +0100 Subject: drm/vmwgfx: reorder framebuffer init sequence vmwgfx has an oddity, when failing to reference the surface it'll return 0, since that's what the successfull drm_framebuffer_init will leave behind in ret. Fix this up by returning -EINVAL. Split out from all the other driver updates due to the above tiny semantic change. Shouldn't matter though since the reference grabbing seemingly can't fail. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 54743943d8b3..edc97929c9a3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -681,14 +681,10 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, goto out_err1; } - ret = drm_framebuffer_init(dev, &vfbs->base.base, - &vmw_framebuffer_surface_funcs); - if (ret) - goto out_err2; - if (!vmw_surface_reference(surface)) { DRM_ERROR("failed to reference surface %p\n", surface); - goto out_err3; + ret = -EINVAL; + goto out_err2; } /* XXX get the first 3 from the surface info */ @@ -707,10 +703,15 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, *out = &vfbs->base; + ret = drm_framebuffer_init(dev, &vfbs->base.base, + &vmw_framebuffer_surface_funcs); + if (ret) + goto out_err3; + return 0; out_err3: - drm_framebuffer_cleanup(&vfbs->base.base); + vmw_surface_unreference(&surface); out_err2: kfree(vfbs); out_err1: @@ -1053,14 +1054,10 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, goto out_err1; } - ret = drm_framebuffer_init(dev, &vfbd->base.base, - &vmw_framebuffer_dmabuf_funcs); - if (ret) - goto out_err2; - if (!vmw_dmabuf_reference(dmabuf)) { DRM_ERROR("failed to reference dmabuf %p\n", dmabuf); - goto out_err3; + ret = -EINVAL; + goto out_err2; } vfbd->base.base.bits_per_pixel = mode_cmd->bpp; @@ -1077,10 +1074,15 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, vfbd->base.user_handle = mode_cmd->handle; *out = &vfbd->base; + ret = drm_framebuffer_init(dev, &vfbd->base.base, + &vmw_framebuffer_dmabuf_funcs); + if (ret) + goto out_err3; + return 0; out_err3: - drm_framebuffer_cleanup(&vfbd->base.base); + vmw_dmabuf_unreference(&dmabuf); out_err2: kfree(vfbd); out_err1: -- cgit v1.2.3 From 7147573a5ce499dec3979e6b524691d47e1288d5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 21:55:41 +0100 Subject: drm/gma500: move fbcon restore to lastclose Doing this within the fb->destroy callback leads to a locking nightmare. And all other drm drivers that restore the fbcon do it in lastclose, too. With this adjustments all fb->destroy callbacks optionally drop references to any gem objects used as backing storage, call drm_framebuffer_cleanup and then kfree the struct. Which nicely simplifies the locking for framebuffer unreferencing and freeing, since this doesn't require that we hold the mode_config lock. A slight exception is the vmwgfx surface backed framebuffer, it also calls drm_master_put and removes the object from a device-private framebuffer list. Both seem to have solid locking in place already. Conclusion is that now it is no longer required to hold the mode_config lock while freeing a framebuffer. v2: Drop the corresponding mutex_lock WARN check from drm_framebuffer_unreference. v3: Use just the mode_config lock not modeset_lock_all, due to patch reordering. Acked-by: Alan Cox Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 2 -- drivers/gpu/drm/gma500/framebuffer.c | 24 ------------------------ drivers/gpu/drm/gma500/psb_drv.c | 10 ++++++++++ 3 files changed, 10 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 81545540b2df..8d665fafc15e 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -317,9 +317,7 @@ static void drm_framebuffer_free(struct kref *kref) */ void drm_framebuffer_unreference(struct drm_framebuffer *fb) { - struct drm_device *dev = fb->dev; DRM_DEBUG("FB ID: %d\n", fb->base.id); - WARN_ON(!drm_modeset_is_locked(dev)); kref_put(&fb->refcount, drm_framebuffer_free); } EXPORT_SYMBOL(drm_framebuffer_unreference); diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 38e7e7597de2..49800d2b79dd 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -668,30 +668,6 @@ static void psb_user_framebuffer_destroy(struct drm_framebuffer *fb) { struct psb_framebuffer *psbfb = to_psb_fb(fb); struct gtt_range *r = psbfb->gtt; - struct drm_device *dev = fb->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - struct psb_fbdev *fbdev = dev_priv->fbdev; - struct drm_crtc *crtc; - int reset = 0; - - /* Should never get stolen memory for a user fb */ - WARN_ON(r->stolen); - - /* Check if we are erroneously live */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - if (crtc->fb == fb) - reset = 1; - - if (reset) - /* - * Now force a sane response before we permit the DRM CRTC - * layer to do stupid things like blank the display. Instead - * we reset this framebuffer as if the user had forced a reset. - * We must do this before the cleanup so that the DRM layer - * doesn't get a chance to stick its oar in where it isn't - * wanted. - */ - drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper); /* Let DRM do its clean up */ drm_framebuffer_cleanup(fb); diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index dd1fbfa7e467..dbcefe9f78fa 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -149,6 +149,16 @@ static struct drm_ioctl_desc psb_ioctls[] = { static void psb_lastclose(struct drm_device *dev) { + int ret; + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_fbdev *fbdev = dev_priv->fbdev; + + mutex_lock(&dev->mode_config.mutex); + ret = drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper); + if (ret) + DRM_DEBUG("failed to restore crtc mode\n"); + mutex_unlock(&dev->mode_config.mutex); + return; } -- cgit v1.2.3 From 59ad1465423d968f06f243bc52cc3b1a320a27cf Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 14:49:44 +0100 Subject: drm/nouveau: protect evo_wait/evo_kick sections with a channel mutex With per-crtc locks modeset operations can run in parallel, and the cursor code uses the device-global evo master channel for hw frobbing. But the pageflip code can also sync with the master under some circumstances. Hence just wrap things up in a mutex to ensure that pushbuf access doesn't intermingle. The approach here is a bit overkill since the per-crtc channels used to schedule the pageflips could probably be used without this pushbuf locking, but I'm not familiar enough with the nouveau codebase to be sure of that. v2: Add missing mutex_init to avoid angering lockdep. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/nouveau/nv50_display.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 35874085a61e..d4cbea19b890 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -128,6 +128,11 @@ struct nv50_dmac { struct nv50_chan base; dma_addr_t handle; u32 *ptr; + + /* Protects against concurrent pushbuf access to this channel, lock is + * grabbed by evo_wait (if the pushbuf reservation is successful) and + * dropped again by evo_kick. */ + struct mutex lock; }; static void @@ -271,6 +276,8 @@ nv50_dmac_create(struct nouveau_object *core, u32 bclass, u8 head, u32 pushbuf = *(u32 *)data; int ret; + mutex_init(&dmac->lock); + dmac->ptr = pci_alloc_consistent(nv_device(core)->pdev, PAGE_SIZE, &dmac->handle); if (!dmac->ptr) @@ -395,11 +402,13 @@ evo_wait(void *evoc, int nr) struct nv50_dmac *dmac = evoc; u32 put = nv_ro32(dmac->base.user, 0x0000) / 4; + mutex_lock(&dmac->lock); if (put + nr >= (PAGE_SIZE / 4) - 8) { dmac->ptr[put] = 0x20000000; nv_wo32(dmac->base.user, 0x0000, 0x00000000); if (!nv_wait(dmac->base.user, 0x0004, ~0, 0x00000000)) { + mutex_unlock(&dmac->lock); NV_ERROR(dmac->base.user, "channel stalled\n"); return NULL; } @@ -415,6 +424,7 @@ evo_kick(u32 *push, void *evoc) { struct nv50_dmac *dmac = evoc; nv_wo32(dmac->base.user, 0x0000, (push - dmac->ptr) << 2); + mutex_unlock(&dmac->lock); } #define evo_mthd(p,m,s) *((p)++) = (((s) << 18) | (m)) -- cgit v1.2.3 From 0ae6d7bc0e70dafc1739d50b2b8d9d7c61968395 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 21:52:30 +0100 Subject: drm/nouveau: try to protect nbo->pin_refcount ... by moving the bo_pin/bo_unpin manipulation of the pin_refcount under the protection of the ttm reservation lock. pin/unpin seems to get called from all over the place, so atm this is completely racy. After this patch there are only a few places in cleanup functions left which access ->pin_refcount without locking. But I'm hoping that those are safe and some other code invariant guarantees that this won't blow up. In any case, I only need to fix up pin/unpin to make ->pageflip work safely, so let's keep it at that. Add a comment to the header to explain the new locking rule. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/nouveau/nouveau_bo.c | 22 +++++++++++----------- drivers/gpu/drm/nouveau/nouveau_bo.h | 2 ++ 2 files changed, 13 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 69d7b1d0b9d6..64d6e3047dee 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -300,17 +300,18 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype) struct ttm_buffer_object *bo = &nvbo->bo; int ret; + ret = ttm_bo_reserve(bo, false, false, false, 0); + if (ret) + goto out; + if (nvbo->pin_refcnt && !(memtype & (1 << bo->mem.mem_type))) { NV_ERROR(drm, "bo %p pinned elsewhere: 0x%08x vs 0x%08x\n", bo, 1 << bo->mem.mem_type, memtype); - return -EINVAL; + ret = -EINVAL; + goto out; } if (nvbo->pin_refcnt++) - return 0; - - ret = ttm_bo_reserve(bo, false, false, false, 0); - if (ret) goto out; nouveau_bo_placement_set(nvbo, memtype, 0); @@ -328,10 +329,8 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype) break; } } - ttm_bo_unreserve(bo); out: - if (unlikely(ret)) - nvbo->pin_refcnt--; + ttm_bo_unreserve(bo); return ret; } @@ -342,13 +341,13 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo) struct ttm_buffer_object *bo = &nvbo->bo; int ret; - if (--nvbo->pin_refcnt) - return 0; - ret = ttm_bo_reserve(bo, false, false, false, 0); if (ret) return ret; + if (--nvbo->pin_refcnt) + goto out; + nouveau_bo_placement_set(nvbo, bo->mem.placement, 0); ret = nouveau_bo_validate(nvbo, false, false); @@ -365,6 +364,7 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo) } } +out: ttm_bo_unreserve(bo); return ret; } diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.h b/drivers/gpu/drm/nouveau/nouveau_bo.h index 25ca37989d2c..81d00fe03b56 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.h +++ b/drivers/gpu/drm/nouveau/nouveau_bo.h @@ -28,6 +28,8 @@ struct nouveau_bo { struct nouveau_drm_tile *tile; struct drm_gem_object *gem; + + /* protect by the ttm reservation lock */ int pin_refcnt; struct ttm_bo_kmap_obj dma_buf_vmap; -- cgit v1.2.3 From af26ef3b3978349cfbd864163a6ebb4906b733b5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 13 Dec 2012 23:07:50 +0100 Subject: drm/: Unified handling of unimplemented fb->create_handle Some drivers don't have real ->create_handle callbacks. - cirrus/ast/mga200: Returns either 0 or -EINVAL. - udl: Didn't even bother with a callback, leading to a nice userspace-triggerable OOPS. - vmwgfx: This driver bothered with an implementation to return 0 as the handle (which is the canonical no-obj gem handle). All have in common that ->create_handle doesn't really make too much sense for them - that ioctl is used only for seamless fb takeover in the radeon/nouveau/i915 ddx drivers. So allow drivers to not implement this and return a consistent -ENODEV. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/ast/ast_main.c | 8 -------- drivers/gpu/drm/cirrus/cirrus_main.c | 8 -------- drivers/gpu/drm/drm_crtc.c | 5 ++++- drivers/gpu/drm/mgag200/mgag200_main.c | 8 -------- drivers/gpu/drm/udl/udl_fb.c | 1 - drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 12 ------------ 6 files changed, 4 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index d5ba7097e5b5..f60fd7bd1183 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -246,16 +246,8 @@ static void ast_user_framebuffer_destroy(struct drm_framebuffer *fb) kfree(fb); } -static int ast_user_framebuffer_create_handle(struct drm_framebuffer *fb, - struct drm_file *file, - unsigned int *handle) -{ - return -EINVAL; -} - static const struct drm_framebuffer_funcs ast_fb_funcs = { .destroy = ast_user_framebuffer_destroy, - .create_handle = ast_user_framebuffer_create_handle, }; diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c index 364474c66202..35cbae827771 100644 --- a/drivers/gpu/drm/cirrus/cirrus_main.c +++ b/drivers/gpu/drm/cirrus/cirrus_main.c @@ -23,16 +23,8 @@ static void cirrus_user_framebuffer_destroy(struct drm_framebuffer *fb) kfree(fb); } -static int cirrus_user_framebuffer_create_handle(struct drm_framebuffer *fb, - struct drm_file *file_priv, - unsigned int *handle) -{ - return 0; -} - static const struct drm_framebuffer_funcs cirrus_fb_funcs = { .destroy = cirrus_user_framebuffer_destroy, - .create_handle = cirrus_user_framebuffer_create_handle, }; int cirrus_framebuffer_init(struct drm_device *dev, diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 8d665fafc15e..a9abf49bb3ef 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2384,7 +2384,10 @@ int drm_mode_getfb(struct drm_device *dev, r->depth = fb->depth; r->bpp = fb->bits_per_pixel; r->pitch = fb->pitches[0]; - fb->funcs->create_handle(fb, file_priv, &r->handle); + if (fb->funcs->create_handle) + ret = fb->funcs->create_handle(fb, file_priv, &r->handle); + else + ret = -ENODEV; out: mutex_unlock(&dev->mode_config.mutex); diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c index 266438af61ea..64297c72464f 100644 --- a/drivers/gpu/drm/mgag200/mgag200_main.c +++ b/drivers/gpu/drm/mgag200/mgag200_main.c @@ -23,16 +23,8 @@ static void mga_user_framebuffer_destroy(struct drm_framebuffer *fb) kfree(fb); } -static int mga_user_framebuffer_create_handle(struct drm_framebuffer *fb, - struct drm_file *file_priv, - unsigned int *handle) -{ - return 0; -} - static const struct drm_framebuffer_funcs mga_fb_funcs = { .destroy = mga_user_framebuffer_destroy, - .create_handle = mga_user_framebuffer_create_handle, }; int mgag200_framebuffer_init(struct drm_device *dev, diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index 829c4a76938c..f8904b4e68de 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -422,7 +422,6 @@ static void udl_user_framebuffer_destroy(struct drm_framebuffer *fb) static const struct drm_framebuffer_funcs udlfb_funcs = { .destroy = udl_user_framebuffer_destroy, .dirty = udl_user_framebuffer_dirty, - .create_handle = NULL, }; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index edc97929c9a3..9c0876b908ae 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -373,16 +373,6 @@ void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv) * Generic framebuffer code */ -int vmw_framebuffer_create_handle(struct drm_framebuffer *fb, - struct drm_file *file_priv, - unsigned int *handle) -{ - if (handle) - *handle = 0; - - return 0; -} - /* * Surface framebuffer code */ @@ -610,7 +600,6 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, static struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = { .destroy = vmw_framebuffer_surface_destroy, .dirty = vmw_framebuffer_surface_dirty, - .create_handle = vmw_framebuffer_create_handle, }; static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, @@ -961,7 +950,6 @@ int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, static struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = { .destroy = vmw_framebuffer_dmabuf_destroy, .dirty = vmw_framebuffer_dmabuf_dirty, - .create_handle = vmw_framebuffer_create_handle, }; /** -- cgit v1.2.3 From 2d13b6796e420ed00389b7399a5d5ac7b1fed436 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 13:47:23 +0100 Subject: drm: encapsulate crtc->set_config calls With refcounting we need to adjust framebuffer refcounts at each callsite - much easier to do if they all call the same little helper function. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 19 +++++++++++++++++-- drivers/gpu/drm/drm_fb_helper.c | 6 +++--- drivers/gpu/drm/i2c/ch7006_drv.c | 2 +- drivers/gpu/drm/nouveau/nv04_display.c | 2 +- drivers/gpu/drm/nouveau/nv17_tv.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 2 +- include/drm/drm_crtc.h | 1 + 7 files changed, 25 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index a9abf49bb3ef..7ca2f28348e6 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -381,7 +381,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) memset(&set, 0, sizeof(struct drm_mode_set)); set.crtc = crtc; set.fb = NULL; - ret = crtc->funcs->set_config(&set); + ret = drm_mode_set_config_internal(&set); if (ret) DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); } @@ -1800,6 +1800,21 @@ out: return ret; } +/** + * drm_mode_set_config_internal - helper to call ->set_config + * @set: modeset config to set + * + * This is a little helper to wrap internal calls to the ->set_config driver + * interface. The only thing it adds is correct refcounting dance. + */ +int drm_mode_set_config_internal(struct drm_mode_set *set) +{ + struct drm_crtc *crtc = set->crtc; + + return crtc->funcs->set_config(set); +} +EXPORT_SYMBOL(drm_mode_set_config_internal); + /** * drm_mode_setcrtc - set CRTC configuration * @dev: drm device for the ioctl @@ -1963,7 +1978,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, set.connectors = connector_set; set.num_connectors = crtc_req->count_connectors; set.fb = fb; - ret = crtc->funcs->set_config(&set); + ret = drm_mode_set_config_internal(&set); out: kfree(connector_set); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 954d175bd7fa..82c3a9ff80a5 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -245,7 +245,7 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) int i, ret; for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; - ret = mode_set->crtc->funcs->set_config(mode_set); + ret = drm_mode_set_config_internal(mode_set); if (ret) error = true; } @@ -675,7 +675,7 @@ int drm_fb_helper_set_par(struct fb_info *info) mutex_lock(&dev->mode_config.mutex); for (i = 0; i < fb_helper->crtc_count; i++) { crtc = fb_helper->crtc_info[i].mode_set.crtc; - ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set); + ret = drm_mode_set_config_internal(&fb_helper->crtc_info[i].mode_set); if (ret) { mutex_unlock(&dev->mode_config.mutex); return ret; @@ -711,7 +711,7 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, modeset->y = var->yoffset; if (modeset->num_connectors) { - ret = crtc->funcs->set_config(modeset); + ret = drm_mode_set_config_internal(modeset); if (!ret) { info->var.xoffset = var->xoffset; info->var.yoffset = var->yoffset; diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c index b865d0728e28..51fa32392029 100644 --- a/drivers/gpu/drm/i2c/ch7006_drv.c +++ b/drivers/gpu/drm/i2c/ch7006_drv.c @@ -364,7 +364,7 @@ static int ch7006_encoder_set_property(struct drm_encoder *encoder, .crtc = crtc, }; - crtc->funcs->set_config(&modeset); + drm_mode_set_config_internal(&modeset); } } diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c index 2cd6fb8c548e..4c6e9f83fe82 100644 --- a/drivers/gpu/drm/nouveau/nv04_display.c +++ b/drivers/gpu/drm/nouveau/nv04_display.c @@ -140,7 +140,7 @@ nv04_display_destroy(struct drm_device *dev) .crtc = crtc, }; - crtc->funcs->set_config(&modeset); + drm_mode_set_config_internal(&modeset); } /* Restore state */ diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c index 2ca276ada507..977e42be2050 100644 --- a/drivers/gpu/drm/nouveau/nv17_tv.c +++ b/drivers/gpu/drm/nouveau/nv17_tv.c @@ -768,7 +768,7 @@ static int nv17_tv_set_property(struct drm_encoder *encoder, .crtc = crtc, }; - crtc->funcs->set_config(&modeset); + drm_mode_set_config_internal(&modeset); } } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 161f8b2549aa..07dfd823cc30 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -829,7 +829,7 @@ static void vmw_lastclose(struct drm_device *dev) list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { set.crtc = crtc; - ret = crtc->funcs->set_config(&set); + ret = drm_mode_set_config_internal(&set); WARN_ON(ret != 0); } diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 00d78b5161c0..665289a68cca 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -985,6 +985,7 @@ extern int drm_mode_getcrtc(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_mode_getconnector(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int drm_mode_set_config_internal(struct drm_mode_set *set); extern int drm_mode_setcrtc(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_mode_getplane(struct drm_device *dev, -- cgit v1.2.3 From 848499032504b1defdebdcf930aa70bd01a45ac1 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 00:28:11 +0100 Subject: drm: add drm_modeset_lock|unlock_all This is the first step towards introducing the new modeset locking scheme. The plan is to put helper functions into place at all the right places step-by-step, so that the final patch to switch on the new locking scheme doesn't need to touch every single driver. This helper here will serve as the shotgun solutions for all places where a more fine-grained locking isn't (yet) implemented. v2: Fixup kerneldoc for unlock_all. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 147 ++++++++++++++++++++++---------------- drivers/gpu/drm/drm_crtc_helper.c | 8 +-- drivers/gpu/drm/drm_fb_helper.c | 20 +++--- include/drm/drm_crtc.h | 3 + 4 files changed, 102 insertions(+), 76 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 7ca2f28348e6..a82ec05dee23 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -37,6 +37,29 @@ #include #include +/** + * drm_modeset_lock_all - take all modeset locks + * @dev: drm device + * + * This function takes all modeset locks, suitable where a more fine-grained + * scheme isn't (yet) implemented. + */ +void drm_modeset_lock_all(struct drm_device *dev) +{ + mutex_lock(&dev->mode_config.mutex); +} +EXPORT_SYMBOL(drm_modeset_lock_all); + +/** + * drm_modeset_unlock_all - drop all modeset locks + * @dev: device + */ +void drm_modeset_unlock_all(struct drm_device *dev) +{ + mutex_unlock(&dev->mode_config.mutex); +} +EXPORT_SYMBOL(drm_modeset_unlock_all); + /* Avoid boilerplate. I'm tired of typing. */ #define DRM_ENUM_NAME_FN(fnname, list) \ char *fnname(int val) \ @@ -425,7 +448,7 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, crtc->funcs = funcs; crtc->invert_dimensions = false; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); if (ret) @@ -437,7 +460,7 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, dev->mode_config.num_crtc++; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -512,7 +535,7 @@ int drm_connector_init(struct drm_device *dev, { int ret; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR); if (ret) @@ -542,7 +565,7 @@ int drm_connector_init(struct drm_device *dev, dev->mode_config.dpms_property, 0); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -592,7 +615,7 @@ int drm_encoder_init(struct drm_device *dev, { int ret; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); if (ret) @@ -606,7 +629,7 @@ int drm_encoder_init(struct drm_device *dev, dev->mode_config.num_encoder++; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -615,11 +638,11 @@ EXPORT_SYMBOL(drm_encoder_init); void drm_encoder_cleanup(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); drm_mode_object_put(dev, &encoder->base); list_del(&encoder->head); dev->mode_config.num_encoder--; - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); } EXPORT_SYMBOL(drm_encoder_cleanup); @@ -631,7 +654,7 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, { int ret; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); if (ret) @@ -665,7 +688,7 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, } out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -675,7 +698,7 @@ void drm_plane_cleanup(struct drm_plane *plane) { struct drm_device *dev = plane->dev; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); kfree(plane->format_types); drm_mode_object_put(dev, &plane->base); /* if not added to a list, it must be a private plane */ @@ -683,7 +706,7 @@ void drm_plane_cleanup(struct drm_plane *plane) list_del(&plane->head); dev->mode_config.num_plane--; } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); } EXPORT_SYMBOL(drm_plane_cleanup); @@ -963,9 +986,9 @@ void drm_mode_config_init(struct drm_device *dev) INIT_LIST_HEAD(&dev->mode_config.plane_list); idr_init(&dev->mode_config.crtc_idr); - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); drm_mode_create_standard_connector_properties(dev); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); /* Just to be sure */ dev->mode_config.num_fb = 0; @@ -1185,7 +1208,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); /* * For the non-control nodes we need to limit the list of resources @@ -1327,7 +1350,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data, card_res->count_connectors, card_res->count_encoders); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1355,7 +1378,7 @@ int drm_mode_getcrtc(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, crtc_resp->crtc_id, DRM_MODE_OBJECT_CRTC); @@ -1383,7 +1406,7 @@ int drm_mode_getcrtc(struct drm_device *dev, } out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1426,7 +1449,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id); - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR); @@ -1523,7 +1546,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, out_resp->count_encoders = encoders_count; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1538,7 +1561,7 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, enc_resp->encoder_id, DRM_MODE_OBJECT_ENCODER); if (!obj) { @@ -1557,7 +1580,7 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, enc_resp->possible_clones = encoder->possible_clones; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1581,7 +1604,7 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); config = &dev->mode_config; /* @@ -1603,7 +1626,7 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data, plane_resp->count_planes = config->num_plane; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1628,7 +1651,7 @@ int drm_mode_getplane(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, plane_resp->plane_id, DRM_MODE_OBJECT_PLANE); if (!obj) { @@ -1668,7 +1691,7 @@ int drm_mode_getplane(struct drm_device *dev, void *data, plane_resp->count_format_types = plane->format_count; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1696,7 +1719,7 @@ int drm_mode_setplane(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); /* * First, find the plane, crtc, and fb objects. If not available, @@ -1795,7 +1818,7 @@ int drm_mode_setplane(struct drm_device *dev, void *data, } out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1850,7 +1873,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX) return -ERANGE; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, crtc_req->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { @@ -1983,7 +2006,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, out: kfree(connector_set); drm_mode_destroy(dev, mode); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -2001,7 +2024,7 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); @@ -2029,7 +2052,7 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, } } out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -2108,7 +2131,7 @@ int drm_mode_addfb(struct drm_device *dev, if ((config->min_height > r.height) || (r.height > config->max_height)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); /* TODO check buffer is sufficiently large */ /* TODO setup destructor callback */ @@ -2125,7 +2148,7 @@ int drm_mode_addfb(struct drm_device *dev, DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -2293,7 +2316,7 @@ int drm_mode_addfb2(struct drm_device *dev, if (ret) return ret; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); if (IS_ERR(fb)) { @@ -2307,7 +2330,7 @@ int drm_mode_addfb2(struct drm_device *dev, DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -2337,7 +2360,7 @@ int drm_mode_rmfb(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB); /* TODO check that we really get a framebuffer back. */ if (!obj) { @@ -2358,7 +2381,7 @@ int drm_mode_rmfb(struct drm_device *dev, drm_framebuffer_remove(fb); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -2386,7 +2409,7 @@ int drm_mode_getfb(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); if (!obj) { ret = -EINVAL; @@ -2405,7 +2428,7 @@ int drm_mode_getfb(struct drm_device *dev, ret = -ENODEV; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -2424,7 +2447,7 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); if (!obj) { ret = -EINVAL; @@ -2478,7 +2501,7 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, out_err2: kfree(clips); out_err1: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -2499,11 +2522,11 @@ void drm_fb_release(struct drm_file *priv) struct drm_device *dev = priv->minor->dev; struct drm_framebuffer *fb, *tfb; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { drm_framebuffer_remove(fb); } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); } /** @@ -2618,7 +2641,7 @@ int drm_mode_attachmode_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); if (!obj) { @@ -2642,7 +2665,7 @@ int drm_mode_attachmode_ioctl(struct drm_device *dev, drm_mode_attachmode(dev, connector, mode); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -2671,7 +2694,7 @@ int drm_mode_detachmode_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); if (!obj) { @@ -2688,7 +2711,7 @@ int drm_mode_detachmode_ioctl(struct drm_device *dev, ret = drm_mode_detachmode(dev, connector, &mode); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -2934,7 +2957,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); if (!obj) { ret = -EINVAL; @@ -3012,7 +3035,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, out_resp->count_enum_blobs = blob_count; } done: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -3063,7 +3086,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB); if (!obj) { ret = -EINVAL; @@ -3081,7 +3104,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev, out_resp->length = blob->length; done: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -3223,7 +3246,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); if (!obj) { @@ -3260,7 +3283,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, } arg->count_props = props_count; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -3277,7 +3300,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); if (!arg_obj) @@ -3315,7 +3338,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, } out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -3377,7 +3400,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { ret = -EINVAL; @@ -3418,7 +3441,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -3436,7 +3459,7 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { ret = -EINVAL; @@ -3469,7 +3492,7 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev, goto out; } out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -3489,7 +3512,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, page_flip->reserved != 0) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) goto out; @@ -3567,7 +3590,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, } out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 7b2d378b2576..400ef86a2b43 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -980,7 +980,7 @@ static void output_poll_execute(struct work_struct *work) if (!drm_kms_helper_poll) return; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); list_for_each_entry(connector, &dev->mode_config.connector_list, head) { /* Ignore forced connectors. */ @@ -1010,7 +1010,7 @@ static void output_poll_execute(struct work_struct *work) changed = true; } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); if (changed) drm_kms_helper_hotplug_event(dev); @@ -1070,7 +1070,7 @@ void drm_helper_hpd_irq_event(struct drm_device *dev) if (!dev->mode_config.poll_enabled) return; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); list_for_each_entry(connector, &dev->mode_config.connector_list, head) { /* Only handle HPD capable connectors. */ @@ -1088,7 +1088,7 @@ void drm_helper_hpd_irq_event(struct drm_device *dev) changed = true; } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); if (changed) drm_kms_helper_hotplug_event(dev); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 82c3a9ff80a5..be0f2d65db3a 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -337,7 +337,7 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) /* * For each CRTC in this fb, turn the connectors on/off. */ - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); for (i = 0; i < fb_helper->crtc_count; i++) { crtc = fb_helper->crtc_info[i].mode_set.crtc; @@ -352,7 +352,7 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) dev->mode_config.dpms_property, dpms_mode); } } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); } int drm_fb_helper_blank(int blank, struct fb_info *info) @@ -672,16 +672,16 @@ int drm_fb_helper_set_par(struct fb_info *info) return -EINVAL; } - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); for (i = 0; i < fb_helper->crtc_count; i++) { crtc = fb_helper->crtc_info[i].mode_set.crtc; ret = drm_mode_set_config_internal(&fb_helper->crtc_info[i].mode_set); if (ret) { - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); if (fb_helper->delayed_hotplug) { fb_helper->delayed_hotplug = false; @@ -701,7 +701,7 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, int ret = 0; int i; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); for (i = 0; i < fb_helper->crtc_count; i++) { crtc = fb_helper->crtc_info[i].mode_set.crtc; @@ -718,7 +718,7 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, } } } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } EXPORT_SYMBOL(drm_fb_helper_pan_display); @@ -1375,7 +1375,7 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) if (!fb_helper->fb) return 0; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { if (crtc->fb) crtcs_bound++; @@ -1385,7 +1385,7 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) if (bound < crtcs_bound) { fb_helper->delayed_hotplug = true; - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return 0; } DRM_DEBUG_KMS("\n"); @@ -1397,7 +1397,7 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) count = drm_fb_helper_probe_connector_modes(fb_helper, max_width, max_height); drm_setup_crtcs(fb_helper); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); } diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 665289a68cca..9f0524d507f0 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -842,6 +842,9 @@ struct drm_prop_enum_list { char *name; }; +extern void drm_modeset_lock_all(struct drm_device *dev); +extern void drm_modeset_unlock_all(struct drm_device *dev); + extern int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, const struct drm_crtc_funcs *funcs); -- cgit v1.2.3 From a0e99e68c12ac6dc5d6b1da7942b5e05d5f848af Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 01:05:46 +0100 Subject: drm/i915: use drm_modeset_lock_all Two exceptions: - debugfs files only read information which is not related to crtc, so can stay on the modeset_config lock. - Same holds for the edp vdd work in intel_dp.c. Add a corresponding WARN_ON and a comment next to the intel_dp struct fields for documentation. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 2 ++ drivers/gpu/drm/i915/intel_fb.c | 4 ++-- drivers/gpu/drm/i915/intel_lvds.c | 4 ++-- drivers/gpu/drm/i915/intel_overlay.c | 14 +++++++------- drivers/gpu/drm/i915/intel_sprite.c | 8 ++++---- 5 files changed, 17 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 1b63d55318a0..6912d29f46f3 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1057,6 +1057,8 @@ static void ironlake_panel_vdd_off_sync(struct intel_dp *intel_dp) struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; + WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + if (!intel_dp->want_panel_vdd && ironlake_edp_have_panel_vdd(intel_dp)) { pp = ironlake_get_pp_control(dev_priv); pp &= ~EDP_FORCE_VDD; diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 7b30b5c2c4ee..a9f7280f87fb 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -280,7 +280,7 @@ void intel_fb_restore_mode(struct drm_device *dev) struct drm_mode_config *config = &dev->mode_config; struct drm_plane *plane; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper); if (ret) @@ -290,5 +290,5 @@ void intel_fb_restore_mode(struct drm_device *dev) list_for_each_entry(plane, &config->plane_list, head) plane->funcs->disable_plane(plane); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); } diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 17aee74258ad..279e9d734ece 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -531,9 +531,9 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val, dev_priv->modeset_on_lid = 0; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); intel_modeset_setup_hw_state(dev, true); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return NOTIFY_OK; } diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index d7bc817f51a0..5be09d125509 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -1045,13 +1045,13 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, } if (!(put_image_rec->flags & I915_OVERLAY_ENABLE)) { - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); mutex_lock(&dev->struct_mutex); ret = intel_overlay_switch_off(overlay); mutex_unlock(&dev->struct_mutex); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1075,7 +1075,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, goto out_free; } - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); mutex_lock(&dev->struct_mutex); if (new_bo->tiling_mode) { @@ -1157,7 +1157,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, goto out_unlock; mutex_unlock(&dev->struct_mutex); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); kfree(params); @@ -1165,7 +1165,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, out_unlock: mutex_unlock(&dev->struct_mutex); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); drm_gem_object_unreference_unlocked(&new_bo->base); out_free: kfree(params); @@ -1241,7 +1241,7 @@ int intel_overlay_attrs(struct drm_device *dev, void *data, return -ENODEV; } - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); mutex_lock(&dev->struct_mutex); ret = -EINVAL; @@ -1307,7 +1307,7 @@ int intel_overlay_attrs(struct drm_device *dev, void *data, ret = 0; out_unlock: mutex_unlock(&dev->struct_mutex); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index d7b060e0a231..f8293061d6bd 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -593,7 +593,7 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data, if ((set->flags & (I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE)) == (I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, set->plane_id, DRM_MODE_OBJECT_PLANE); if (!obj) { @@ -606,7 +606,7 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data, ret = intel_plane->update_colorkey(plane, set); out_unlock: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -622,7 +622,7 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -ENODEV; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, get->plane_id, DRM_MODE_OBJECT_PLANE); if (!obj) { @@ -635,7 +635,7 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data, intel_plane->get_colorkey(plane, get); out_unlock: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } -- cgit v1.2.3 From 0a819515fc346b4e79d6e3fc01d837a660452c74 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 01:33:17 +0100 Subject: drm/gma500: use drm_modeset_lock_all Only two places: - suspend/resume - Some really strange mode validation tool with too much funny-lucking hand-rolled conversion code. - The recently-added lastclose fbdev restore code. Better safe than sorry, so convert both places to keep the locking semantics as much as possible. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/gma500/psb_device.c | 8 ++++---- drivers/gpu/drm/gma500/psb_drv.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/gma500/psb_device.c b/drivers/gpu/drm/gma500/psb_device.c index b58c4701c4e8..f6f534b4197e 100644 --- a/drivers/gpu/drm/gma500/psb_device.c +++ b/drivers/gpu/drm/gma500/psb_device.c @@ -194,7 +194,7 @@ static int psb_save_display_registers(struct drm_device *dev) regs->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); /* Save crtc and output state */ - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { if (drm_helper_crtc_in_use(crtc)) crtc->funcs->save(crtc); @@ -204,7 +204,7 @@ static int psb_save_display_registers(struct drm_device *dev) if (connector->funcs->save) connector->funcs->save(connector); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return 0; } @@ -234,7 +234,7 @@ static int psb_restore_display_registers(struct drm_device *dev) /*make sure VGA plane is off. it initializes to on after reset!*/ PSB_WVDC32(0x80000000, VGACNTRL); - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) if (drm_helper_crtc_in_use(crtc)) crtc->funcs->restore(crtc); @@ -243,7 +243,7 @@ static int psb_restore_display_registers(struct drm_device *dev) if (connector->funcs->restore) connector->funcs->restore(connector); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return 0; } diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index dbcefe9f78fa..111e3df9c5de 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -153,11 +153,11 @@ static void psb_lastclose(struct drm_device *dev) struct drm_psb_private *dev_priv = dev->dev_private; struct psb_fbdev *fbdev = dev_priv->fbdev; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); ret = drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper); if (ret) DRM_DEBUG("failed to restore crtc mode\n"); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return; } @@ -486,7 +486,7 @@ static int psb_mode_operation_ioctl(struct drm_device *dev, void *data, case PSB_MODE_OPERATION_MODE_VALID: umode = &arg->mode; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_CONNECTOR); @@ -535,7 +535,7 @@ static int psb_mode_operation_ioctl(struct drm_device *dev, void *data, if (mode) drm_mode_destroy(dev, mode); mode_op_out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; default: -- cgit v1.2.3 From 795f1426b484ddfc136e723a26bfd6a46c8ab085 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 01:34:59 +0100 Subject: drm/ast: use drm_modeset_lock_all Just a call to drm_helper_resume_force_mode, obviously wants full locking for that. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/ast/ast_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index 2d2c2f8d6dc6..df0d0a08097a 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -94,9 +94,9 @@ static int ast_drm_thaw(struct drm_device *dev) ast_post_gpu(dev); drm_mode_config_reset(dev); - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); drm_helper_resume_force_mode(dev); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); console_lock(); ast_fbdev_set_suspend(dev, 0); -- cgit v1.2.3 From b13f59804918ad584a97a639064a24d14e2fd740 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 01:36:59 +0100 Subject: drm/shmobile: use drm_modeset_lock_all Only a resume method to account for. v2: Fixup compilation. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/shmobile/shmob_drm_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c index d1d5306ebf24..f6e0b5395051 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c @@ -313,9 +313,9 @@ static int shmob_drm_pm_resume(struct device *dev) { struct shmob_drm_device *sdev = dev_get_drvdata(dev); - mutex_lock(&sdev->ddev->mode_config.mutex); + drm_modeset_lock_all(sdev->ddev); shmob_drm_crtc_resume(&sdev->crtc); - mutex_unlock(&sdev->ddev->mode_config.mutex); + drm_modeset_unlock_all(sdev->ddev); drm_kms_helper_poll_enable(sdev->ddev); return 0; -- cgit v1.2.3 From bbe4b99ff2443e305598768ae8eac6bc3516b7c9 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 01:48:38 +0100 Subject: drm/vmwgfx: use drm_modeset_lock_all Ok, this one here is a bit more complicated, and I can't really claim to fully understand the locking and lifetime rules of the vmwgfx driver. So just convert ever mutex_lock call, including the interruptible one. Since other places (e.g. in the execbuf ioctl) take the mode_config.mutex without bothering with interruptible handling, I've figured I should be able to get away with this in a few more places ... Signed-off-by: Daniel Vetter --- drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index d9fbbe191071..a135498a1298 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -163,11 +163,7 @@ int vmw_present_ioctl(struct drm_device *dev, void *data, goto out_no_copy; } - ret = mutex_lock_interruptible(&dev->mode_config.mutex); - if (unlikely(ret != 0)) { - ret = -ERESTARTSYS; - goto out_no_mode_mutex; - } + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); if (!obj) { @@ -200,8 +196,7 @@ out_no_surface: ttm_read_unlock(&vmaster->lock); out_no_ttm_lock: out_no_fb: - mutex_unlock(&dev->mode_config.mutex); -out_no_mode_mutex: + drm_modeset_unlock_all(dev); out_no_copy: kfree(clips); out_clips: @@ -251,11 +246,7 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data, goto out_no_copy; } - ret = mutex_lock_interruptible(&dev->mode_config.mutex); - if (unlikely(ret != 0)) { - ret = -ERESTARTSYS; - goto out_no_mode_mutex; - } + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); if (!obj) { @@ -282,8 +273,7 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data, ttm_read_unlock(&vmaster->lock); out_no_ttm_lock: out_no_fb: - mutex_unlock(&dev->mode_config.mutex); -out_no_mode_mutex: + drm_modeset_unlock_all(dev); out_no_copy: kfree(clips); out_clips: -- cgit v1.2.3 From d5d2636ed7990b93c7216f6a4d323f6b0eee08af Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 15:50:41 +0100 Subject: omapdrm: use modeset_lock_all I've left the locking in the debugfs code as-is, it's essentially just used to keep the framebuffer object alive (which won't be necessary any more later on). We don't need fb refcounting either, since the new mode_config.fb_lock ensures that the framebuffers can't disappear (once mode_config.mutex doesn't guarantee this any more later on in the series). The fbcon restore needs all modeset locks. The crtc callbacks seem to only need the crtc locks, but I've quickly discussed things with Rob Clark and he's fine with just using modeset_lock_all for those, too. He'll look into converting things over later. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/staging/omapdrm/omap_crtc.c | 8 ++++---- drivers/staging/omapdrm/omap_drv.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/omapdrm/omap_crtc.c b/drivers/staging/omapdrm/omap_crtc.c index 5c6ed6040eff..510942e67020 100644 --- a/drivers/staging/omapdrm/omap_crtc.c +++ b/drivers/staging/omapdrm/omap_crtc.c @@ -278,13 +278,13 @@ static void page_flip_worker(struct work_struct *work) struct drm_display_mode *mode = &crtc->mode; struct drm_gem_object *bo; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); omap_plane_mode_set(omap_crtc->plane, crtc, crtc->fb, 0, 0, mode->hdisplay, mode->vdisplay, crtc->x << 16, crtc->y << 16, mode->hdisplay << 16, mode->vdisplay << 16, vblank_cb, crtc); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); bo = omap_framebuffer_bo(crtc->fb, 0); drm_gem_object_unreference_unlocked(bo); @@ -417,7 +417,7 @@ static void apply_worker(struct work_struct *work) * the callbacks and list modification all serialized * with respect to modesetting ioctls from userspace. */ - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); dispc_runtime_get(); /* @@ -462,7 +462,7 @@ static void apply_worker(struct work_struct *work) out: dispc_runtime_put(); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); } int omap_crtc_apply(struct drm_crtc *crtc, diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/staging/omapdrm/omap_drv.c index ae5ecc2efbc7..dfdb4ba1e7c6 100644 --- a/drivers/staging/omapdrm/omap_drv.c +++ b/drivers/staging/omapdrm/omap_drv.c @@ -449,9 +449,9 @@ static void dev_lastclose(struct drm_device *dev) } } - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); if (ret) DBG("failed to restore crtc mode"); } -- cgit v1.2.3 From 29494c174dc4793ebd236aa522a2a1ed73b7180e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 02:18:25 +0100 Subject: drm: add per-crtc locks *drumroll* The basic idea is to protect per-crtc state which can change without touching the output configuration with separate mutexes, i.e. all the input side state to a crtc like framebuffers, cursor settings or plane configuration. Holding such a crtc lock gives a read-lock on all the other crtc state which can be changed by e.g. a modeset. All non-crtc state is still protected by the mode_config mutex. Callers that need to change modeset state of a crtc (e.g. dpms or set_mode) need to grab both the mode_config lock and nested within any crtc locks. Note that since there can only ever be one holder of the mode_config lock we can grab the subordinate crtc locks in any order (if we need to grab more than one of them). Lockdep can handle such nesting with the mutex_lock_nest_lock call correctly. With this functions that only touch connectors/encoders but not crtcs only need to take the mode_config lock. The biggest such case is the output probing, which means that we can now pageflip and move cursors while the output probe code is reading an edid. Most cases neatly fall into the three buckets: - Only touches connectors and similar output state and so only needs the mode_config lock. - Touches the global configuration and so needs all locks. - Only touches the crtc input side and so only needs the crtc lock. But a few cases that need special consideration: - Load detection which requires a crtc. The mode_config lock already prevents a modeset change, so we can use any unused crtc as we like to do load detection. The only thing to consider is that such temporary state changes don't leak out to userspace through ioctls that only take the crtc look (like a pageflip). Hence the load detect code needs to grab the crtc of any output pipes it touches (but only if it touches state used by the pageflip or cursor ioctls). - Atomic pageflip when moving planes. The first case is sane hw, where planes have a fixed association with crtcs - nothing needs to be done there. More insane^Wflexible hw needs to have plane->crtc mapping which is separately protect with a lock that nests within the crtc lock. If the plane is unused we can just assign it to the current crtc and continue. But if a plane is already in use by another crtc we can't just reassign it. Two solution present themselves: Either go back to a slow-path which takes all modeset locks, potentially incure quite a hefty delay. Or simply disallowing such changes in one atomic pageflip - in general the vblanks of two crtcs are not synced, so there's no sane way to atomically flip such plane changes accross more than one crtc. I'd heavily favour the later approach, going as far as mandating it as part of the ABI of such a new a nuclear pageflip. And if we _really_ want such semantics, we can always get them by introducing another pageflip mutex between the mode_config.mutex and the individual crtc locks. Pageflips crossing more than one crtc would then need to take that lock first, to lock out concurrent multi-crtc pageflips. - Optimized global modeset operations: We could just take the mode_config lock and then lazily lock all crtc which are affected by a modeset operation. This has the advantage that pageflip could continue unhampered on unaffected crtc. But if e.g. global resources like plls need to be reassigned and so affect unrelated crtcs we can still do that - nested locking works in any order. This patch just adds the locks and takes them in drm_modeset_lock_all, no real locking changes yet. v2: Need to initialize the new lock in crtc_init and lock it righ away, for otherwise the modeset_unlock_all below will try to unlock a not-locked mutex. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 12 ++++++++++++ include/drm/drm_crtc.h | 9 +++++++++ 2 files changed, 21 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index a82ec05dee23..b7c6168fae7e 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -46,7 +46,12 @@ */ void drm_modeset_lock_all(struct drm_device *dev) { + struct drm_crtc *crtc; + mutex_lock(&dev->mode_config.mutex); + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex); } EXPORT_SYMBOL(drm_modeset_lock_all); @@ -56,6 +61,11 @@ EXPORT_SYMBOL(drm_modeset_lock_all); */ void drm_modeset_unlock_all(struct drm_device *dev) { + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + mutex_unlock(&crtc->mutex); + mutex_unlock(&dev->mode_config.mutex); } EXPORT_SYMBOL(drm_modeset_unlock_all); @@ -449,6 +459,8 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, crtc->invert_dimensions = false; drm_modeset_lock_all(dev); + mutex_init(&crtc->mutex); + mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex); ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); if (ret) diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 9f0524d507f0..c89b1161f0be 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -390,6 +390,15 @@ struct drm_crtc { struct drm_device *dev; struct list_head head; + /** + * crtc mutex + * + * This provides a read lock for the overall crtc state (mode, dpms + * state, ...) and a write lock for everything which can be update + * without a full modeset (fb, cursor data, ...) + */ + struct mutex mutex; + struct drm_mode_object base; /* framebuffer the connector is currently bound to */ -- cgit v1.2.3 From bfb899282f500eeb9dff2600729904aad0fd39e7 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 13:48:21 +0100 Subject: drm: only take the crtc lock for ->cursor_set First convert ->cursor_set to only take the crtc lock, since that seems to be the function with the least amount of state - the core ioctl function doesn't check anything which can change at runtime, so we don't have any object lifetime issues to contend. The only thing which is important is that the driver's implementation doesn't touch any state outside of that single crtc which is not yet properly protected by other locking: - ast: access the global ast->cache_kmap. Luckily we only have on crtc on this driver, so this is fine. Add a comment. - gma500: calls gma_power_begin|and and psb_gtt_pin|unpin, both which have their own locking to protect their state. Everything else is crtc-local. - i915: touches a bit of global gem state, all protected by the One Lock to Rule Them All (dev->struct_mutex). - nouveau: Pre-nv50 is all nice, nv50+ uses the evo channels to queue up all display changes. And some of these channels are device global. But this is fine now since the previous patch introduced an evo channel mutex. - radeon: Uses some indirect register access for cursor updates, but with the previous patches to protect these indirect 2-register access patterns with a spinlock, this should be fine now, too. - vmwgfx: I have no idea how that works - update_cursor_position doesn't take any per-crtc argument and I haven't figured out any other place where this could be set in some form of a side-channel. But vmwgfx definitely has more than one crtc (or at least can register more than one), so I have no idea how this is supposed to not fail with the current code already. Hence take the easy way out and simply acquire all locks (which requires dropping the crtc lock the core acquired for us). That way it's not worse off for consistency than the old code. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/ast/ast_drv.h | 2 ++ drivers/gpu/drm/drm_crtc.c | 6 ++++-- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 32 ++++++++++++++++++++++++++------ 3 files changed, 32 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 5ccf984f063a..528429252f0f 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -98,6 +98,8 @@ struct ast_private { struct drm_gem_object *cursor_cache; uint64_t cursor_cache_gpu_addr; + /* Acces to this cache is protected by the crtc->mutex of the only crtc + * we have. */ struct ttm_bo_kmap_obj cache_kmap; int next_cursor; }; diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index b7c6168fae7e..58fa69e5ff4c 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2036,7 +2036,6 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) return -EINVAL; - drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); @@ -2051,20 +2050,23 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, goto out; } /* Turns off the cursor if handle is 0 */ + mutex_lock(&crtc->mutex); ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle, req->width, req->height); + mutex_unlock(&crtc->mutex); } if (req->flags & DRM_MODE_CURSOR_MOVE) { if (crtc->funcs->cursor_move) { + drm_modeset_lock_all(dev); ret = crtc->funcs->cursor_move(crtc, req->x, req->y); + drm_modeset_unlock_all(dev); } else { ret = -EFAULT; goto out; } } out: - drm_modeset_unlock_all(dev); return ret; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 9c0876b908ae..8d82e631c305 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -180,16 +180,29 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, struct vmw_dma_buffer *dmabuf = NULL; int ret; + /* + * FIXME: Unclear whether there's any global state touched by the + * cursor_set function, especially vmw_cursor_update_position looks + * suspicious. For now take the easy route and reacquire all locks. We + * can do this since the caller in the drm core doesn't check anything + * which is protected by any looks. + */ + mutex_unlock(&crtc->mutex); + drm_modeset_lock_all(dev_priv->dev); + /* A lot of the code assumes this */ - if (handle && (width != 64 || height != 64)) - return -EINVAL; + if (handle && (width != 64 || height != 64)) { + ret = -EINVAL; + goto out; + } if (handle) { ret = vmw_user_lookup_handle(dev_priv, tfile, handle, &surface, &dmabuf); if (ret) { DRM_ERROR("failed to find surface or dmabuf: %i\n", ret); - return -EINVAL; + ret = -EINVAL; + goto out; } } @@ -197,7 +210,8 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, if (surface && !surface->snooper.image) { DRM_ERROR("surface not suitable for cursor\n"); vmw_surface_unreference(&surface); - return -EINVAL; + ret = -EINVAL; + goto out; } /* takedown old cursor */ @@ -225,14 +239,20 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, du->hotspot_x, du->hotspot_y); } else { vmw_cursor_update_position(dev_priv, false, 0, 0); - return 0; + ret = 0; + goto out; } vmw_cursor_update_position(dev_priv, true, du->cursor_x + du->hotspot_x, du->cursor_y + du->hotspot_y); - return 0; + ret = 0; +out: + drm_modeset_unlock_all(dev_priv->dev); + mutex_lock(&crtc->mutex); + + return ret; } int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) -- cgit v1.2.3 From dac35663cef4ca7f572d430bb54b14be8f03cb10 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 15:24:10 +0100 Subject: drm: only take the crtc lock for ->cursor_move ->cursor_move uses mostly the same facilities in drivers as ->cursor_set, so pretty much nothing to fix up: - ast/gma500/i915: They all use per-crtc registers to update the cursor position. ast again touches the global cursor cache, but that's ok since there's only one crtc. - nouveau: nv50+ is again special, updates happen through the per-crtc channel (without pushbufs), so it's not protected by the new evo lock introduced earlier. But since this channel is per-crtc, we should be fine anyway. - radeon: A bit a mess: avivo asics need a workaround when both output pipes are enabled, which means it'll access the crtc list. Just reading that flag is ok though as long as radeon _always_ grabs all locks when changing the crtc configuration. Which means with the current scheme it cannot do an optimized modeset which only locks the relevant crtcs. This can be fixed though by introducing a bit of global state with separate locks and ensure in the modeset code that the cursor will be updated appropriately when enabling the 2nd pipe (on affected asics). - vmwgfx: I still don't understand what it's doing exactly, so apply the same trick for now. v2: Fixup unlocking for the error cases, spotted by Richard Wilbur. v3: Another error-case fixup. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 10 ++++------ drivers/gpu/drm/radeon/radeon_cursor.c | 8 +++++++- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 13 +++++++++++++ 3 files changed, 24 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 58fa69e5ff4c..4af6a3d5c9a1 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2039,34 +2039,32 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); - ret = -EINVAL; - goto out; + return -EINVAL; } crtc = obj_to_crtc(obj); + mutex_lock(&crtc->mutex); if (req->flags & DRM_MODE_CURSOR_BO) { if (!crtc->funcs->cursor_set) { ret = -ENXIO; goto out; } /* Turns off the cursor if handle is 0 */ - mutex_lock(&crtc->mutex); ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle, req->width, req->height); - mutex_unlock(&crtc->mutex); } if (req->flags & DRM_MODE_CURSOR_MOVE) { if (crtc->funcs->cursor_move) { - drm_modeset_lock_all(dev); ret = crtc->funcs->cursor_move(crtc, req->x, req->y); - drm_modeset_unlock_all(dev); } else { ret = -EFAULT; goto out; } } out: + mutex_unlock(&crtc->mutex); + return ret; } diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c index ad6df625e8b8..c1680e6d76ad 100644 --- a/drivers/gpu/drm/radeon/radeon_cursor.c +++ b/drivers/gpu/drm/radeon/radeon_cursor.c @@ -245,8 +245,14 @@ int radeon_crtc_cursor_move(struct drm_crtc *crtc, int i = 0; struct drm_crtc *crtc_p; - /* avivo cursor image can't end on 128 pixel boundary or + /* + * avivo cursor image can't end on 128 pixel boundary or * go past the end of the frame if both crtcs are enabled + * + * NOTE: It is safe to access crtc->enabled of other crtcs + * without holding either the mode_config lock or the other + * crtc's lock as long as write access to this flag _always_ + * grabs all locks. */ list_for_each_entry(crtc_p, &crtc->dev->mode_config.crtc_list, head) { if (crtc_p->enabled) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 8d82e631c305..3e3c7ab33ca2 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -264,10 +264,23 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) du->cursor_x = x + crtc->x; du->cursor_y = y + crtc->y; + /* + * FIXME: Unclear whether there's any global state touched by the + * cursor_set function, especially vmw_cursor_update_position looks + * suspicious. For now take the easy route and reacquire all locks. We + * can do this since the caller in the drm core doesn't check anything + * which is protected by any looks. + */ + mutex_unlock(&crtc->mutex); + drm_modeset_lock_all(dev_priv->dev); + vmw_cursor_update_position(dev_priv, shown, du->cursor_x + du->hotspot_x, du->cursor_y + du->hotspot_y); + drm_modeset_unlock_all(dev_priv->dev); + mutex_lock(&crtc->mutex); + return 0; } -- cgit v1.2.3 From 4b096ac10da0b63f09bd123b86fed8deb80646ce Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 10 Dec 2012 21:19:18 +0100 Subject: drm: revamp locking around fb creation/destruction Well, at least step 1. The goal here is that framebuffer objects can survive outside of the mode_config lock, with just a reference held as protection. The first step to get there is to introduce a special fb_lock which protects fb lookup, creation and destruction, to make them appear atomic. This new fb_lock can nest within the mode_config lock. But the idea is (once the reference counting part is completed) that we only quickly take that fb_lock to lookup a framebuffer and grab a reference, without any other locks involved. vmwgfx is the only driver which does framebuffer lookups itself, also wrap those calls to drm_mode_object_find with the new lock. Also protect the fb_list walking in i915 and omapdrm with the new lock. As a slight complication there's also the list of user-created fbs attached to the file private. The problem now is that at fclose() time we need to walk that list, eventually do a modeset call to remove the fb from active usage (and are required to be able to take the mode_config lock), but in the end we need to grab the new fb_lock to remove the fb from the list. The easiest solution is to add another mutex to protect this per-file list. Currently that new fbs_lock nests within the modeset locks and so appears redudant. But later patches will switch around this sequence so that taking the modeset locks in the fb destruction path is optional in the fastpath. Ultimately the goal is that addfb and rmfb do not require the mode_config lock, since otherwise they have the potential to introduce stalls in the pageflip sequence of a compositor (if the compositor e.g. switches to a fullscreen client or if it enables a plane). But that requires a few more steps and hoops to jump through. Note that framebuffer creation/destruction is now double-protected - once by the fb_lock and in parts by the idr_lock. The later would be unnecessariy if framebuffers would have their own idr allocator. But that's material for another patch (series). v2: Properly initialize the fb->filp_head list in _init, otherwise the newly added WARN to check whether the fb isn't on a fpriv list any more will fail for driver-private objects. v3: Fixup two error-case unlock bugs spotted by Richard Wilbur. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 116 +++++++++++++++++++++++---------- drivers/gpu/drm/drm_fops.c | 1 + drivers/gpu/drm/i915/i915_debugfs.c | 5 +- drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c | 4 ++ drivers/staging/omapdrm/omap_debugfs.c | 2 + include/drm/drmP.h | 8 +++ include/drm/drm_crtc.h | 14 ++++ 7 files changed, 113 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 4af6a3d5c9a1..13a3d3426961 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -262,15 +262,21 @@ again: mutex_lock(&dev->mode_config.idr_mutex); ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id); + + if (!ret) { + /* + * Set up the object linking under the protection of the idr + * lock so that other users can't see inconsistent state. + */ + obj->id = new_id; + obj->type = obj_type; + } mutex_unlock(&dev->mode_config.idr_mutex); + if (ret == -EAGAIN) goto again; - else if (ret) - return ret; - obj->id = new_id; - obj->type = obj_type; - return 0; + return ret; } /** @@ -312,6 +318,12 @@ EXPORT_SYMBOL(drm_mode_object_find); * Allocates an ID for the framebuffer's parent mode object, sets its mode * functions & device file and adds it to the master fd list. * + * IMPORTANT: + * This functions publishes the fb and makes it available for concurrent access + * by other users. Which means by this point the fb _must_ be fully set up - + * since all the fb attributes are invariant over its lifetime, no further + * locking but only correct reference counting is required. + * * RETURNS: * Zero on success, error code on failure. */ @@ -320,16 +332,20 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, { int ret; + mutex_lock(&dev->mode_config.fb_lock); kref_init(&fb->refcount); + INIT_LIST_HEAD(&fb->filp_head); + fb->dev = dev; + fb->funcs = funcs; ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB); if (ret) - return ret; + goto out; - fb->dev = dev; - fb->funcs = funcs; dev->mode_config.num_fb++; list_add(&fb->head, &dev->mode_config.fb_list); +out: + mutex_unlock(&dev->mode_config.fb_lock); return 0; } @@ -385,8 +401,10 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb) * this.) */ drm_mode_object_put(dev, &fb->base); + mutex_lock(&dev->mode_config.fb_lock); list_del(&fb->head); dev->mode_config.num_fb--; + mutex_unlock(&dev->mode_config.fb_lock); } EXPORT_SYMBOL(drm_framebuffer_cleanup); @@ -406,6 +424,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) int ret; WARN_ON(!drm_modeset_is_locked(dev)); + WARN_ON(!list_empty(&fb->filp_head)); /* remove from any CRTC */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { @@ -432,8 +451,6 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) } } - list_del(&fb->filp_head); - drm_framebuffer_unreference(fb); } EXPORT_SYMBOL(drm_framebuffer_remove); @@ -989,6 +1006,7 @@ void drm_mode_config_init(struct drm_device *dev) { mutex_init(&dev->mode_config.mutex); mutex_init(&dev->mode_config.idr_mutex); + mutex_init(&dev->mode_config.fb_lock); INIT_LIST_HEAD(&dev->mode_config.fb_list); INIT_LIST_HEAD(&dev->mode_config.crtc_list); INIT_LIST_HEAD(&dev->mode_config.connector_list); @@ -1091,6 +1109,9 @@ void drm_mode_config_cleanup(struct drm_device *dev) drm_property_destroy(dev, property); } + /* Single-threaded teardown context, so it's not requied to grab the + * fb_lock to protect against concurrent fb_list access. Contrary, it + * would actually deadlock with the drm_framebuffer_cleanup function. */ list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { drm_framebuffer_remove(fb); } @@ -1220,8 +1241,8 @@ int drm_mode_getresources(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - drm_modeset_lock_all(dev); + mutex_lock(&file_priv->fbs_lock); /* * For the non-control nodes we need to limit the list of resources * by IDs in the group list for this node @@ -1229,6 +1250,23 @@ int drm_mode_getresources(struct drm_device *dev, void *data, list_for_each(lh, &file_priv->fbs) fb_count++; + /* handle this in 4 parts */ + /* FBs */ + if (card_res->count_fbs >= fb_count) { + copied = 0; + fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr; + list_for_each_entry(fb, &file_priv->fbs, filp_head) { + if (put_user(fb->base.id, fb_id + copied)) { + mutex_unlock(&file_priv->fbs_lock); + return -EFAULT; + } + copied++; + } + } + card_res->count_fbs = fb_count; + mutex_unlock(&file_priv->fbs_lock); + + drm_modeset_lock_all(dev); mode_group = &file_priv->master->minor->mode_group; if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { @@ -1252,21 +1290,6 @@ int drm_mode_getresources(struct drm_device *dev, void *data, card_res->max_width = dev->mode_config.max_width; card_res->min_width = dev->mode_config.min_width; - /* handle this in 4 parts */ - /* FBs */ - if (card_res->count_fbs >= fb_count) { - copied = 0; - fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr; - list_for_each_entry(fb, &file_priv->fbs, filp_head) { - if (put_user(fb->base.id, fb_id + copied)) { - ret = -EFAULT; - goto out; - } - copied++; - } - } - card_res->count_fbs = fb_count; - /* CRTCs */ if (card_res->count_crtcs >= crtc_count) { copied = 0; @@ -1765,8 +1788,10 @@ int drm_mode_setplane(struct drm_device *dev, void *data, } crtc = obj_to_crtc(obj); + mutex_lock(&dev->mode_config.fb_lock); obj = drm_mode_object_find(dev, plane_req->fb_id, DRM_MODE_OBJECT_FB); + mutex_unlock(&dev->mode_config.fb_lock); if (!obj) { DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", plane_req->fb_id); @@ -1908,8 +1933,10 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, } fb = crtc->fb; } else { + mutex_lock(&dev->mode_config.fb_lock); obj = drm_mode_object_find(dev, crtc_req->fb_id, DRM_MODE_OBJECT_FB); + mutex_unlock(&dev->mode_config.fb_lock); if (!obj) { DRM_DEBUG_KMS("Unknown FB ID%d\n", crtc_req->fb_id); @@ -2151,16 +2178,17 @@ int drm_mode_addfb(struct drm_device *dev, fb = dev->mode_config.funcs->fb_create(dev, file_priv, &r); if (IS_ERR(fb)) { DRM_DEBUG_KMS("could not create framebuffer\n"); - ret = PTR_ERR(fb); - goto out; + drm_modeset_unlock_all(dev); + return PTR_ERR(fb); } + mutex_lock(&file_priv->fbs_lock); or->fb_id = fb->base.id; list_add(&fb->filp_head, &file_priv->fbs); DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); - -out: + mutex_unlock(&file_priv->fbs_lock); drm_modeset_unlock_all(dev); + return ret; } @@ -2333,16 +2361,18 @@ int drm_mode_addfb2(struct drm_device *dev, fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); if (IS_ERR(fb)) { DRM_DEBUG_KMS("could not create framebuffer\n"); - ret = PTR_ERR(fb); - goto out; + drm_modeset_unlock_all(dev); + return PTR_ERR(fb); } + mutex_lock(&file_priv->fbs_lock); r->fb_id = fb->base.id; list_add(&fb->filp_head, &file_priv->fbs); DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); + mutex_unlock(&file_priv->fbs_lock); -out: drm_modeset_unlock_all(dev); + return ret; } @@ -2373,27 +2403,34 @@ int drm_mode_rmfb(struct drm_device *dev, return -EINVAL; drm_modeset_lock_all(dev); + mutex_lock(&dev->mode_config.fb_lock); obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB); /* TODO check that we really get a framebuffer back. */ if (!obj) { + mutex_unlock(&dev->mode_config.fb_lock); ret = -EINVAL; goto out; } fb = obj_to_fb(obj); + mutex_unlock(&dev->mode_config.fb_lock); + mutex_lock(&file_priv->fbs_lock); list_for_each_entry(fbl, &file_priv->fbs, filp_head) if (fb == fbl) found = 1; - if (!found) { ret = -EINVAL; + mutex_unlock(&file_priv->fbs_lock); goto out; } - drm_framebuffer_remove(fb); + list_del_init(&fb->filp_head); + mutex_unlock(&file_priv->fbs_lock); + drm_framebuffer_remove(fb); out: drm_modeset_unlock_all(dev); + return ret; } @@ -2422,7 +2459,9 @@ int drm_mode_getfb(struct drm_device *dev, return -EINVAL; drm_modeset_lock_all(dev); + mutex_lock(&dev->mode_config.fb_lock); obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); + mutex_unlock(&dev->mode_config.fb_lock); if (!obj) { ret = -EINVAL; goto out; @@ -2460,7 +2499,9 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, return -EINVAL; drm_modeset_lock_all(dev); + mutex_lock(&dev->mode_config.fb_lock); obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); + mutex_unlock(&dev->mode_config.fb_lock); if (!obj) { ret = -EINVAL; goto out_err1; @@ -2535,9 +2576,12 @@ void drm_fb_release(struct drm_file *priv) struct drm_framebuffer *fb, *tfb; drm_modeset_lock_all(dev); + mutex_lock(&priv->fbs_lock); list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { + list_del_init(&fb->filp_head); drm_framebuffer_remove(fb); } + mutex_unlock(&priv->fbs_lock); drm_modeset_unlock_all(dev); } @@ -3542,7 +3586,9 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if (crtc->funcs->page_flip == NULL) goto out; + mutex_lock(&dev->mode_config.fb_lock); obj = drm_mode_object_find(dev, page_flip->fb_id, DRM_MODE_OBJECT_FB); + mutex_unlock(&dev->mode_config.fb_lock); if (!obj) goto out; fb = obj_to_fb(obj); diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 133b4132983e..13fdcd10a605 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -276,6 +276,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, INIT_LIST_HEAD(&priv->lhead); INIT_LIST_HEAD(&priv->fbs); + mutex_init(&priv->fbs_lock); INIT_LIST_HEAD(&priv->event_list); init_waitqueue_head(&priv->event_wait); priv->event_space = 4096; /* set aside 4k for event buffer */ diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index e6a11ca85eaf..a40c674a57be 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1374,7 +1374,9 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) fb->base.bits_per_pixel); describe_obj(m, fb->obj); seq_printf(m, "\n"); + mutex_unlock(&dev->mode_config.mutex); + mutex_lock(&dev->mode_config.fb_lock); list_for_each_entry(fb, &dev->mode_config.fb_list, base.head) { if (&fb->base == ifbdev->helper.fb) continue; @@ -1387,8 +1389,7 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) describe_obj(m, fb->obj); seq_printf(m, "\n"); } - - mutex_unlock(&dev->mode_config.mutex); + mutex_unlock(&dev->mode_config.fb_lock); return 0; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index a135498a1298..0d6a161b204b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -165,7 +165,9 @@ int vmw_present_ioctl(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); + mutex_lock(&dev->mode_config.fb_lock); obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); + mutex_unlock(&dev->mode_config.fb_lock); if (!obj) { DRM_ERROR("Invalid framebuffer id.\n"); ret = -EINVAL; @@ -248,7 +250,9 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); + mutex_lock(&dev->mode_config.fb_lock); obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); + mutex_unlock(&dev->mode_config.fb_lock); if (!obj) { DRM_ERROR("Invalid framebuffer id.\n"); ret = -EINVAL; diff --git a/drivers/staging/omapdrm/omap_debugfs.c b/drivers/staging/omapdrm/omap_debugfs.c index 2f122e00b51d..e95540b3e2f6 100644 --- a/drivers/staging/omapdrm/omap_debugfs.c +++ b/drivers/staging/omapdrm/omap_debugfs.c @@ -72,6 +72,7 @@ static int fb_show(struct seq_file *m, void *arg) seq_printf(m, "fbcon "); omap_framebuffer_describe(priv->fbdev->fb, m); + mutex_lock(&dev->mode_config.fb_lock); list_for_each_entry(fb, &dev->mode_config.fb_list, head) { if (fb == priv->fbdev->fb) continue; @@ -79,6 +80,7 @@ static int fb_show(struct seq_file *m, void *arg) seq_printf(m, "user "); omap_framebuffer_describe(fb, m); } + mutex_unlock(&dev->mode_config.fb_lock); mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->mode_config.mutex); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 3c609abe8c80..e74731c1a912 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -446,7 +446,15 @@ struct drm_file { int is_master; /* this file private is a master for a minor */ struct drm_master *master; /* master this node is currently associated with N.B. not always minor->master */ + + /** + * fbs - List of framebuffers associated with this file. + * + * Protected by fbs_lock. Note that the fbs list holds a reference on + * the fb object to prevent it from untimely disappearing. + */ struct list_head fbs; + struct mutex fbs_lock; wait_queue_head_t event_wait; struct list_head event_list; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index c89b1161f0be..c35a807d7e5c 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -254,6 +254,10 @@ struct drm_framebuffer { * userspace perspective. */ struct kref refcount; + /* + * Place on the dev->mode_config.fb_list, access protected by + * dev->mode_config.fb_lock. + */ struct list_head head; struct drm_mode_object base; const struct drm_framebuffer_funcs *funcs; @@ -780,8 +784,18 @@ struct drm_mode_config { struct mutex idr_mutex; /* for IDR management */ struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, connector, modes - just makes life easier */ /* this is limited to one for now */ + + + /** + * fb_lock - mutex to protect fb state + * + * Besides the global fb list his also protects the fbs list in the + * file_priv + */ + struct mutex fb_lock; int num_fb; struct list_head fb_list; + int num_connector; struct list_head connector_list; int num_encoder; -- cgit v1.2.3 From 786b99ed13223d8ac58a937dd348aead45eb8191 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 21:53:40 +0100 Subject: drm: create drm_framebuffer_lookup And replace all fb lookups with it. Also add a WARN to drm_mode_object_find since that is now no longer the blessed interface to look up an fb. And add kerneldoc to both functions. This only updates all callsites, but immediately drops the acquired refence again. Hence all callers still rely on the fact that a mode fb can't disappear while they're holding the struct mutex. Subsequent patches will instate proper use of refcounts, and then rework the rmfb and unref code to no longer serialize fb destruction with the mode_config lock. We don't want that since otherwise a compositor might end up stalling for a few frames in rmfb. v2: Don't use kref_get_unless_zero - Greg KH doesn't like that kind of interface. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 109 ++++++++++++++++++++++------------ drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c | 24 ++++---- include/drm/drm_crtc.h | 2 + 3 files changed, 86 insertions(+), 49 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 13a3d3426961..f2ccda85309f 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -294,11 +294,24 @@ static void drm_mode_object_put(struct drm_device *dev, mutex_unlock(&dev->mode_config.idr_mutex); } +/** + * drm_mode_object_find - look up a drm object with static lifetime + * @dev: drm device + * @id: id of the mode object + * @type: type of the mode object + * + * Note that framebuffers cannot be looked up with this functions - since those + * are reference counted, they need special treatment. + */ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type) { struct drm_mode_object *obj = NULL; + /* Framebuffers are reference counted and need their own lookup + * function.*/ + WARN_ON(type == DRM_MODE_OBJECT_FB); + mutex_lock(&dev->mode_config.idr_mutex); obj = idr_find(&dev->mode_config.crtc_idr, id); if (!obj || (obj->type != type) || (obj->id != id)) @@ -358,6 +371,40 @@ static void drm_framebuffer_free(struct kref *kref) fb->funcs->destroy(fb); } +/** + * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference + * @dev: drm device + * @id: id of the fb object + * + * If successful, this grabs an additional reference to the framebuffer - + * callers need to make sure to eventually unreference the returned framebuffer + * again. + */ +struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, + uint32_t id) +{ + struct drm_mode_object *obj = NULL; + struct drm_framebuffer *fb; + + mutex_lock(&dev->mode_config.fb_lock); + + mutex_lock(&dev->mode_config.idr_mutex); + obj = idr_find(&dev->mode_config.crtc_idr, id); + if (!obj || (obj->type != DRM_MODE_OBJECT_FB) || (obj->id != id)) + fb = NULL; + else + fb = obj_to_fb(obj); + mutex_unlock(&dev->mode_config.idr_mutex); + + if (fb) + kref_get(&fb->refcount); + + mutex_unlock(&dev->mode_config.fb_lock); + + return fb; +} +EXPORT_SYMBOL(drm_framebuffer_lookup); + /** * drm_framebuffer_unreference - unref a framebuffer * @fb: framebuffer to unref @@ -1788,17 +1835,15 @@ int drm_mode_setplane(struct drm_device *dev, void *data, } crtc = obj_to_crtc(obj); - mutex_lock(&dev->mode_config.fb_lock); - obj = drm_mode_object_find(dev, plane_req->fb_id, - DRM_MODE_OBJECT_FB); - mutex_unlock(&dev->mode_config.fb_lock); - if (!obj) { + fb = drm_framebuffer_lookup(dev, plane_req->fb_id); + if (!fb) { DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", plane_req->fb_id); ret = -ENOENT; goto out; } - fb = obj_to_fb(obj); + /* fb is protect by the mode_config lock, so drop the ref immediately */ + drm_framebuffer_unreference(fb); /* Check whether this plane supports the fb pixel format. */ for (i = 0; i < plane->format_count; i++) @@ -1933,17 +1978,16 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, } fb = crtc->fb; } else { - mutex_lock(&dev->mode_config.fb_lock); - obj = drm_mode_object_find(dev, crtc_req->fb_id, - DRM_MODE_OBJECT_FB); - mutex_unlock(&dev->mode_config.fb_lock); - if (!obj) { + fb = drm_framebuffer_lookup(dev, crtc_req->fb_id); + if (!fb) { DRM_DEBUG_KMS("Unknown FB ID%d\n", crtc_req->fb_id); ret = -EINVAL; goto out; } - fb = obj_to_fb(obj); + /* fb is protect by the mode_config lock, so drop the + * ref immediately */ + drm_framebuffer_unreference(fb); } mode = drm_mode_create(dev); @@ -2392,7 +2436,6 @@ int drm_mode_addfb2(struct drm_device *dev, int drm_mode_rmfb(struct drm_device *dev, void *data, struct drm_file *file_priv) { - struct drm_mode_object *obj; struct drm_framebuffer *fb = NULL; struct drm_framebuffer *fbl = NULL; uint32_t *id = data; @@ -2403,16 +2446,13 @@ int drm_mode_rmfb(struct drm_device *dev, return -EINVAL; drm_modeset_lock_all(dev); - mutex_lock(&dev->mode_config.fb_lock); - obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB); - /* TODO check that we really get a framebuffer back. */ - if (!obj) { - mutex_unlock(&dev->mode_config.fb_lock); + fb = drm_framebuffer_lookup(dev, *id); + if (!fb) { ret = -EINVAL; goto out; } - fb = obj_to_fb(obj); - mutex_unlock(&dev->mode_config.fb_lock); + /* fb is protect by the mode_config lock, so drop the ref immediately */ + drm_framebuffer_unreference(fb); mutex_lock(&file_priv->fbs_lock); list_for_each_entry(fbl, &file_priv->fbs, filp_head) @@ -2451,7 +2491,6 @@ int drm_mode_getfb(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_fb_cmd *r = data; - struct drm_mode_object *obj; struct drm_framebuffer *fb; int ret = 0; @@ -2459,14 +2498,13 @@ int drm_mode_getfb(struct drm_device *dev, return -EINVAL; drm_modeset_lock_all(dev); - mutex_lock(&dev->mode_config.fb_lock); - obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); - mutex_unlock(&dev->mode_config.fb_lock); - if (!obj) { + fb = drm_framebuffer_lookup(dev, r->fb_id); + if (!fb) { ret = -EINVAL; goto out; } - fb = obj_to_fb(obj); + /* fb is protect by the mode_config lock, so drop the ref immediately */ + drm_framebuffer_unreference(fb); r->height = fb->height; r->width = fb->width; @@ -2489,7 +2527,6 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, struct drm_clip_rect __user *clips_ptr; struct drm_clip_rect *clips = NULL; struct drm_mode_fb_dirty_cmd *r = data; - struct drm_mode_object *obj; struct drm_framebuffer *fb; unsigned flags; int num_clips; @@ -2499,14 +2536,13 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, return -EINVAL; drm_modeset_lock_all(dev); - mutex_lock(&dev->mode_config.fb_lock); - obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); - mutex_unlock(&dev->mode_config.fb_lock); - if (!obj) { + fb = drm_framebuffer_lookup(dev, r->fb_id); + if (!fb) { ret = -EINVAL; goto out_err1; } - fb = obj_to_fb(obj); + /* fb is protect by the mode_config lock, so drop the ref immediately */ + drm_framebuffer_unreference(fb); num_clips = r->num_clips; clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr; @@ -3586,12 +3622,11 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if (crtc->funcs->page_flip == NULL) goto out; - mutex_lock(&dev->mode_config.fb_lock); - obj = drm_mode_object_find(dev, page_flip->fb_id, DRM_MODE_OBJECT_FB); - mutex_unlock(&dev->mode_config.fb_lock); - if (!obj) + fb = drm_framebuffer_lookup(dev, page_flip->fb_id); + if (!fb) goto out; - fb = obj_to_fb(obj); + /* fb is protect by the mode_config lock, so drop the ref immediately */ + drm_framebuffer_unreference(fb); hdisplay = crtc->mode.hdisplay; vdisplay = crtc->mode.vdisplay; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index 0d6a161b204b..1b8f428accae 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -131,7 +131,7 @@ int vmw_present_ioctl(struct drm_device *dev, void *data, struct vmw_master *vmaster = vmw_master(file_priv->master); struct drm_vmw_rect __user *clips_ptr; struct drm_vmw_rect *clips = NULL; - struct drm_mode_object *obj; + struct drm_framebuffer *fb; struct vmw_framebuffer *vfb; struct vmw_resource *res; uint32_t num_clips; @@ -165,15 +165,15 @@ int vmw_present_ioctl(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); - mutex_lock(&dev->mode_config.fb_lock); - obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); - mutex_unlock(&dev->mode_config.fb_lock); - if (!obj) { + fb = drm_framebuffer_lookup(dev, arg->fb_id); + if (!fb) { DRM_ERROR("Invalid framebuffer id.\n"); ret = -EINVAL; goto out_no_fb; } - vfb = vmw_framebuffer_to_vfb(obj_to_fb(obj)); + /* fb is protect by the mode_config lock, so drop the ref immediately */ + drm_framebuffer_unreference(fb); + vfb = vmw_framebuffer_to_vfb(fb); ret = ttm_read_lock(&vmaster->lock, true); if (unlikely(ret != 0)) @@ -217,7 +217,7 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data, struct vmw_master *vmaster = vmw_master(file_priv->master); struct drm_vmw_rect __user *clips_ptr; struct drm_vmw_rect *clips = NULL; - struct drm_mode_object *obj; + struct drm_framebuffer *fb; struct vmw_framebuffer *vfb; uint32_t num_clips; int ret; @@ -250,16 +250,16 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); - mutex_lock(&dev->mode_config.fb_lock); - obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); - mutex_unlock(&dev->mode_config.fb_lock); - if (!obj) { + fb = drm_framebuffer_lookup(dev, arg->fb_id); + if (!fb) { DRM_ERROR("Invalid framebuffer id.\n"); ret = -EINVAL; goto out_no_fb; } + /* fb is protect by the mode_config lock, so drop the ref immediately */ + drm_framebuffer_unreference(fb); - vfb = vmw_framebuffer_to_vfb(obj_to_fb(obj)); + vfb = vmw_framebuffer_to_vfb(fb); if (!vfb->dmabuf) { DRM_ERROR("Framebuffer not dmabuf backed.\n"); ret = -EINVAL; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index c35a807d7e5c..7dc1b31059d4 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -958,6 +958,8 @@ extern void drm_framebuffer_set_object(struct drm_device *dev, extern int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, const struct drm_framebuffer_funcs *funcs); +extern struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, + uint32_t id); extern void drm_framebuffer_unreference(struct drm_framebuffer *fb); extern void drm_framebuffer_reference(struct drm_framebuffer *fb); extern void drm_framebuffer_remove(struct drm_framebuffer *fb); -- cgit v1.2.3 From 362063619cf67c2c2fc2eb90951b2623cbb69a7c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 10 Dec 2012 20:42:17 +0100 Subject: drm: revamp framebuffer cleanup interfaces We have two classes of framebuffer - Created by the driver (atm only for fbdev), and the driver holds onto the last reference count until destruction. - Created by userspace and associated with a given fd. These framebuffers will be reaped when their assoiciated fb is closed. Now these two cases are set up differently, the framebuffers are on different lists and hence destruction needs to clean up different things. Also, for userspace framebuffers we remove them from any current usage, whereas for internal framebuffers it is assumed that the driver has done this already. Long story short, we need two different ways to cleanup such drivers. Three functions are involved in total: - drm_framebuffer_remove: Convenience function which removes the fb from all active usage and then drops the passed-in reference. - drm_framebuffer_unregister_private: Will remove driver-private framebuffers from relevant lists and drop the corresponding references. Should be called for driver-private framebuffers before dropping the last reference (or like for a lot of the drivers where the fbdev is embedded someplace else, before doing the cleanup manually). - drm_framebuffer_cleanup: Final cleanup for both classes of fbs, should be called by the driver's ->destroy callback once the last reference is gone. This patch just rolls out the new interfaces and updates all drivers (by adding calls to drm_framebuffer_unregister_private at all the right places)- no functional changes yet. Follow-on patches will move drm core code around and update the lifetime management for framebuffers, so that we are no longer required to keep framebuffers alive by locking mode_config.mutex. I've also updated the kerneldoc already. vmwgfx seems to again be a bit special, at least I haven't figured out how the fbdev support in that driver works. It smells like it's external though. v2: The i915 driver creates another private framebuffer in the load-detect code. Adjust its cleanup code, too. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/ast/ast_fb.c | 1 + drivers/gpu/drm/cirrus/cirrus_fbdev.c | 1 + drivers/gpu/drm/drm_crtc.c | 31 ++++++++++++++++++++++++++++--- drivers/gpu/drm/drm_fb_cma_helper.c | 5 ++++- drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 4 +++- drivers/gpu/drm/gma500/framebuffer.c | 1 + drivers/gpu/drm/i915/intel_display.c | 6 ++++-- drivers/gpu/drm/i915/intel_fb.c | 1 + drivers/gpu/drm/mgag200/mgag200_fb.c | 1 + drivers/gpu/drm/nouveau/nouveau_fbcon.c | 1 + drivers/gpu/drm/radeon/radeon_fb.c | 2 ++ drivers/gpu/drm/udl/udl_fb.c | 1 + drivers/staging/omapdrm/omap_fbdev.c | 8 ++++++-- include/drm/drm_crtc.h | 1 + 14 files changed, 55 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index d9ec77959dff..3e6584b940dc 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -290,6 +290,7 @@ static void ast_fbdev_destroy(struct drm_device *dev, drm_fb_helper_fini(&afbdev->helper); vfree(afbdev->sysram); + drm_framebuffer_unregister_private(&afb->base); drm_framebuffer_cleanup(&afb->base); } diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index 6c6b4c87d309..3daea0f638c3 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -258,6 +258,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev, vfree(gfbdev->sysram); drm_fb_helper_fini(&gfbdev->helper); + drm_framebuffer_unregister_private(&gfb->base); drm_framebuffer_cleanup(&gfb->base); return 0; diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f2ccda85309f..3eddfabeba96 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -68,6 +68,7 @@ void drm_modeset_unlock_all(struct drm_device *dev) mutex_unlock(&dev->mode_config.mutex); } + EXPORT_SYMBOL(drm_modeset_unlock_all); /* Avoid boilerplate. I'm tired of typing. */ @@ -429,12 +430,35 @@ void drm_framebuffer_reference(struct drm_framebuffer *fb) } EXPORT_SYMBOL(drm_framebuffer_reference); +/** + * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr + * @fb: fb to unregister + * + * Drivers need to call this when cleaning up driver-private framebuffers, e.g. + * those used for fbdev. Note that the caller must hold a reference of it's own, + * i.e. the object may not be destroyed through this call (since it'll lead to a + * locking inversion). + */ +void drm_framebuffer_unregister_private(struct drm_framebuffer *fb) +{ +} +EXPORT_SYMBOL(drm_framebuffer_unregister_private); + /** * drm_framebuffer_cleanup - remove a framebuffer object * @fb: framebuffer to remove * - * Scans all the CRTCs in @dev's mode_config. If they're using @fb, removes - * it, setting it to NULL. + * Cleanup references to a user-created framebuffer. This function is intended + * to be used from the drivers ->destroy callback. + * + * Note that this function does not remove the fb from active usuage - if it is + * still used anywhere, hilarity can ensue since userspace could call getfb on + * the id and get back -EINVAL. Obviously no concern at driver unload time. + * + * Also, the framebuffer will not be removed from the lookup idr - for + * user-created framebuffers this will happen in in the rmfb ioctl. For + * driver-private objects (e.g. for fbdev) drivers need to explicitly call + * drm_framebuffer_unregister_private. */ void drm_framebuffer_cleanup(struct drm_framebuffer *fb) { @@ -460,7 +484,8 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup); * @fb: framebuffer to remove * * Scans all the CRTCs and planes in @dev's mode_config. If they're - * using @fb, removes it, setting it to NULL. + * using @fb, removes it, setting it to NULL. Then drops the reference to the + * passed-in framebuffer. */ void drm_framebuffer_remove(struct drm_framebuffer *fb) { diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index e1e0cb0d531a..3742bc96421e 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -266,6 +266,7 @@ static int drm_fbdev_cma_create(struct drm_fb_helper *helper, return 0; err_drm_fb_cma_destroy: + drm_framebuffer_unregister_private(fb); drm_fb_cma_destroy(fb); err_framebuffer_release: framebuffer_release(fbi); @@ -370,8 +371,10 @@ void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma) framebuffer_release(info); } - if (fbdev_cma->fb) + if (fbdev_cma->fb) { + drm_framebuffer_unregister_private(&fbdev_cma->fb->fb); drm_fb_cma_destroy(&fbdev_cma->fb->fb); + } drm_fb_helper_fini(&fbdev_cma->fb_helper); kfree(fbdev_cma); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 71f867340a88..90d335cfb8c0 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -326,8 +326,10 @@ static void exynos_drm_fbdev_destroy(struct drm_device *dev, /* release drm framebuffer and real buffer */ if (fb_helper->fb && fb_helper->fb->funcs) { fb = fb_helper->fb; - if (fb) + if (fb) { + drm_framebuffer_unregister_private(fb); drm_framebuffer_remove(fb); + } } /* release linux framebuffer */ diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 49800d2b79dd..c1ef37e2efdf 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -590,6 +590,7 @@ static int psb_fbdev_destroy(struct drm_device *dev, struct psb_fbdev *fbdev) framebuffer_release(info); } drm_fb_helper_fini(&fbdev->psb_fb_helper); + drm_framebuffer_unregister_private(&psbfb->base); drm_framebuffer_cleanup(&psbfb->base); if (psbfb->gtt) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 26fa6a795afe..df51203dcebd 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6789,8 +6789,10 @@ void intel_release_load_detect_pipe(struct drm_connector *connector, intel_encoder->new_crtc = NULL; intel_set_mode(crtc, NULL, 0, 0, NULL); - if (old->release_fb) - old->release_fb->funcs->destroy(old->release_fb); + if (old->release_fb) { + drm_framebuffer_unregister_private(old->release_fb); + drm_framebuffer_unreference(old->release_fb); + } return; } diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index a9f7280f87fb..c7b8316137e9 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -212,6 +212,7 @@ static void intel_fbdev_destroy(struct drm_device *dev, drm_fb_helper_fini(&ifbdev->helper); + drm_framebuffer_unregister_private(&ifb->base); drm_framebuffer_cleanup(&ifb->base); if (ifb->obj) { drm_gem_object_unreference_unlocked(&ifb->obj->base); diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index 2f486481d79a..5c69b432f99a 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -247,6 +247,7 @@ static int mga_fbdev_destroy(struct drm_device *dev, } drm_fb_helper_fini(&mfbdev->helper); vfree(mfbdev->sysram); + drm_framebuffer_unregister_private(&mfb->base); drm_framebuffer_cleanup(&mfb->base); return 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 67a1a069de28..d4ecb4deb484 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -433,6 +433,7 @@ nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *fbcon) nouveau_fb->nvbo = NULL; } drm_fb_helper_fini(&fbcon->helper); + drm_framebuffer_unregister_private(&nouveau_fb->base); drm_framebuffer_cleanup(&nouveau_fb->base); return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index cc8489d8c6d1..515e5ee1f9ee 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -293,6 +293,7 @@ out_unref: } if (fb && ret) { drm_gem_object_unreference(gobj); + drm_framebuffer_unregister_private(fb); drm_framebuffer_cleanup(fb); kfree(fb); } @@ -339,6 +340,7 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb rfb->obj = NULL; } drm_fb_helper_fini(&rfbdev->helper); + drm_framebuffer_unregister_private(&rfb->base); drm_framebuffer_cleanup(&rfb->base); return 0; diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index f8904b4e68de..caa84f1de9c1 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -555,6 +555,7 @@ static void udl_fbdev_destroy(struct drm_device *dev, framebuffer_release(info); } drm_fb_helper_fini(&ufbdev->helper); + drm_framebuffer_unregister_private(&ufbdev->ufb.base); drm_framebuffer_cleanup(&ufbdev->ufb.base); drm_gem_object_unreference_unlocked(&ufbdev->ufb.obj->base); } diff --git a/drivers/staging/omapdrm/omap_fbdev.c b/drivers/staging/omapdrm/omap_fbdev.c index 8a027bb77d97..2728e37e02be 100644 --- a/drivers/staging/omapdrm/omap_fbdev.c +++ b/drivers/staging/omapdrm/omap_fbdev.c @@ -275,8 +275,10 @@ fail: if (ret) { if (fbi) framebuffer_release(fbi); - if (fb) + if (fb) { + drm_framebuffer_unregister_private(fb); drm_framebuffer_remove(fb); + } } return ret; @@ -400,8 +402,10 @@ void omap_fbdev_free(struct drm_device *dev) fbdev = to_omap_fbdev(priv->fbdev); /* this will free the backing object */ - if (fbdev->fb) + if (fbdev->fb) { + drm_framebuffer_unregister_private(fbdev->fb); drm_framebuffer_remove(fbdev->fb); + } kfree(fbdev); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 7dc1b31059d4..66b2732f175a 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -964,6 +964,7 @@ extern void drm_framebuffer_unreference(struct drm_framebuffer *fb); extern void drm_framebuffer_reference(struct drm_framebuffer *fb); extern void drm_framebuffer_remove(struct drm_framebuffer *fb); extern void drm_framebuffer_cleanup(struct drm_framebuffer *fb); +extern void drm_framebuffer_unregister_private(struct drm_framebuffer *fb); extern int drmfb_probe(struct drm_device *dev, struct drm_crtc *crtc); 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); -- cgit v1.2.3 From 2b677e8c08eed11e4ebe66a7c334f03e389a19a3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 10 Dec 2012 21:16:05 +0100 Subject: drm: reference framebuffers which are on the idr Since otherwise looking and reference-counting around drm_framebuffer_lookup will be an unmanageable mess. With this change, an object can either be found in the idr and will stay around once we incremented the reference counter. Or it will be gone for good and can't be looked up using its id any more. Atomicity is guaranteed by the dev->mode_config.fb_lock. The newly-introduce fpriv->fbs_lock looks a bit redundant, but the next patch will shuffle the locking order between these two locks and all the modeset locks taken in modeset_lock_all, so we'll need it. Also, since userspace could do really funky stuff and race e.g. a getresources with an rmfb, we need to make sure that the kernel doesn't fall over trying to look-up an inexistent fb, or causing confusion by having two fbs around with the same id. Simply reset the framebuffer id to 0, which marks it as reaped. Any lookups of that id will fail, so the object is really gone for good from userspace's pov. Note that we still need to protect the "remove framebuffer from all use-cases" and the final unreference with the modeset-lock, since most framebuffer use-sites don't implement proper reference counting yet. We can only lift this once _all_ users are converted. With this change, two references are held on alife, but unused framebuffers: - The reference for the idr lookup, created in this patch. - For user-created framebuffers the fpriv->fbs reference, for driver-private fbs the driver is supposed to hold it's own last reference. Note that the dev->mode_config.fb_list itself does _not_ hold a reference onto the framebuffers (this list is essentially only used for debugfs files). Hence if there's anything left there when the driver has cleaned up all it's modeset resources, this is a ref-leak. WARN about it. Now we only need to fix up all other places to properly reference count framebuffers. v2: Fix spelling fail in a comment spotted by Rob Clark. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 118 ++++++++++++++++++++++++++++++--------------- 1 file changed, 80 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 3eddfabeba96..a50f7553b31d 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -356,6 +356,9 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, if (ret) goto out; + /* Grab the idr reference. */ + drm_framebuffer_reference(fb); + dev->mode_config.num_fb++; list_add(&fb->head, &dev->mode_config.fb_list); out: @@ -372,6 +375,23 @@ static void drm_framebuffer_free(struct kref *kref) fb->funcs->destroy(fb); } +static struct drm_framebuffer *__drm_framebuffer_lookup(struct drm_device *dev, + uint32_t id) +{ + struct drm_mode_object *obj = NULL; + struct drm_framebuffer *fb; + + mutex_lock(&dev->mode_config.idr_mutex); + obj = idr_find(&dev->mode_config.crtc_idr, id); + if (!obj || (obj->type != DRM_MODE_OBJECT_FB) || (obj->id != id)) + fb = NULL; + else + fb = obj_to_fb(obj); + mutex_unlock(&dev->mode_config.idr_mutex); + + return fb; +} + /** * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference * @dev: drm device @@ -384,22 +404,12 @@ static void drm_framebuffer_free(struct kref *kref) struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, uint32_t id) { - struct drm_mode_object *obj = NULL; struct drm_framebuffer *fb; mutex_lock(&dev->mode_config.fb_lock); - - mutex_lock(&dev->mode_config.idr_mutex); - obj = idr_find(&dev->mode_config.crtc_idr, id); - if (!obj || (obj->type != DRM_MODE_OBJECT_FB) || (obj->id != id)) - fb = NULL; - else - fb = obj_to_fb(obj); - mutex_unlock(&dev->mode_config.idr_mutex); - + fb = __drm_framebuffer_lookup(dev, id); if (fb) kref_get(&fb->refcount); - mutex_unlock(&dev->mode_config.fb_lock); return fb; @@ -430,6 +440,24 @@ void drm_framebuffer_reference(struct drm_framebuffer *fb) } EXPORT_SYMBOL(drm_framebuffer_reference); +static void drm_framebuffer_free_bug(struct kref *kref) +{ + BUG(); +} + +/* dev->mode_config.fb_lock must be held! */ +static void __drm_framebuffer_unregister(struct drm_device *dev, + struct drm_framebuffer *fb) +{ + mutex_lock(&dev->mode_config.idr_mutex); + idr_remove(&dev->mode_config.crtc_idr, fb->base.id); + mutex_unlock(&dev->mode_config.idr_mutex); + + fb->base.id = 0; + + kref_put(&fb->refcount, drm_framebuffer_free_bug); +} + /** * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr * @fb: fb to unregister @@ -441,6 +469,12 @@ EXPORT_SYMBOL(drm_framebuffer_reference); */ void drm_framebuffer_unregister_private(struct drm_framebuffer *fb) { + struct drm_device *dev = fb->dev; + + mutex_lock(&dev->mode_config.fb_lock); + /* Mark fb as reaped and drop idr ref. */ + __drm_framebuffer_unregister(dev, fb); + mutex_unlock(&dev->mode_config.fb_lock); } EXPORT_SYMBOL(drm_framebuffer_unregister_private); @@ -464,14 +498,6 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb) { struct drm_device *dev = fb->dev; - /* - * This could be moved to drm_framebuffer_remove(), but for - * debugging is nice to keep around the list of fb's that are - * no longer associated w/ a drm_file but are not unreferenced - * yet. (i915 and omapdrm have debugfs files which will show - * this.) - */ - drm_mode_object_put(dev, &fb->base); mutex_lock(&dev->mode_config.fb_lock); list_del(&fb->head); dev->mode_config.num_fb--; @@ -1181,9 +1207,15 @@ void drm_mode_config_cleanup(struct drm_device *dev) drm_property_destroy(dev, property); } - /* Single-threaded teardown context, so it's not requied to grab the + /* + * Single-threaded teardown context, so it's not required to grab the * fb_lock to protect against concurrent fb_list access. Contrary, it - * would actually deadlock with the drm_framebuffer_cleanup function. */ + * would actually deadlock with the drm_framebuffer_cleanup function. + * + * Also, if there are any framebuffers left, that's a driver leak now, + * so politely WARN about this. + */ + WARN_ON(!list_empty(&dev->mode_config.fb_list)); list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { drm_framebuffer_remove(fb); } @@ -2464,39 +2496,41 @@ int drm_mode_rmfb(struct drm_device *dev, struct drm_framebuffer *fb = NULL; struct drm_framebuffer *fbl = NULL; uint32_t *id = data; - int ret = 0; int found = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - drm_modeset_lock_all(dev); - fb = drm_framebuffer_lookup(dev, *id); - if (!fb) { - ret = -EINVAL; - goto out; - } - /* fb is protect by the mode_config lock, so drop the ref immediately */ - drm_framebuffer_unreference(fb); - mutex_lock(&file_priv->fbs_lock); + mutex_lock(&dev->mode_config.fb_lock); + fb = __drm_framebuffer_lookup(dev, *id); + if (!fb) + goto fail_lookup; + list_for_each_entry(fbl, &file_priv->fbs, filp_head) if (fb == fbl) found = 1; - if (!found) { - ret = -EINVAL; - mutex_unlock(&file_priv->fbs_lock); - goto out; - } + if (!found) + goto fail_lookup; + + /* Mark fb as reaped, we still have a ref from fpriv->fbs. */ + __drm_framebuffer_unregister(dev, fb); list_del_init(&fb->filp_head); + mutex_unlock(&dev->mode_config.fb_lock); mutex_unlock(&file_priv->fbs_lock); + drm_modeset_lock_all(dev); drm_framebuffer_remove(fb); -out: drm_modeset_unlock_all(dev); - return ret; + return 0; + +fail_lookup: + mutex_unlock(&dev->mode_config.fb_lock); + mutex_unlock(&file_priv->fbs_lock); + + return -EINVAL; } /** @@ -2639,7 +2673,15 @@ void drm_fb_release(struct drm_file *priv) drm_modeset_lock_all(dev); mutex_lock(&priv->fbs_lock); list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { + + mutex_lock(&dev->mode_config.fb_lock); + /* Mark fb as reaped, we still have a ref from fpriv->fbs. */ + __drm_framebuffer_unregister(dev, fb); + mutex_unlock(&dev->mode_config.fb_lock); + list_del_init(&fb->filp_head); + + /* This will also drop the fpriv->fbs reference. */ drm_framebuffer_remove(fb); } mutex_unlock(&priv->fbs_lock); -- cgit v1.2.3 From 7d331595b05d6f3c38567ad7031b75075557ce2a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 10 Dec 2012 23:44:22 +0100 Subject: drm: nest modeset locks within fpriv->fbs_lock Atm we still need to unconditionally take the modeset locks in the rmfb paths. But eventually we only want to take them if there are other users around as a slow-path. This way sane userspace avoids blocking on edid reads and other stuff in rmfb if it ensures that the fb isn't used anywhere by a crtc/plane. We can do a quick check for such other users once framebuffers are properly refcounting by locking at the refcount - if it's more than 1, there are other users left. Again, rmfb racing against other ioctls isn't a real problem, userspace is allowed to shoot its foot. This patch just prepares this by moving the modeset locks to nest within fpriv->fbs_lock. Now the distinction between the fbs_lock and the device-global fb_lock is clear, since we need to hold the fbs_lock outside of any modeset_locks in fb_release. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index a50f7553b31d..09e02a7023f9 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2282,13 +2282,13 @@ int drm_mode_addfb(struct drm_device *dev, drm_modeset_unlock_all(dev); return PTR_ERR(fb); } + drm_modeset_unlock_all(dev); mutex_lock(&file_priv->fbs_lock); or->fb_id = fb->base.id; list_add(&fb->filp_head, &file_priv->fbs); DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); mutex_unlock(&file_priv->fbs_lock); - drm_modeset_unlock_all(dev); return ret; } @@ -2465,6 +2465,7 @@ int drm_mode_addfb2(struct drm_device *dev, drm_modeset_unlock_all(dev); return PTR_ERR(fb); } + drm_modeset_unlock_all(dev); mutex_lock(&file_priv->fbs_lock); r->fb_id = fb->base.id; @@ -2472,7 +2473,6 @@ int drm_mode_addfb2(struct drm_device *dev, DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); mutex_unlock(&file_priv->fbs_lock); - drm_modeset_unlock_all(dev); return ret; } @@ -2670,7 +2670,6 @@ void drm_fb_release(struct drm_file *priv) struct drm_device *dev = priv->minor->dev; struct drm_framebuffer *fb, *tfb; - drm_modeset_lock_all(dev); mutex_lock(&priv->fbs_lock); list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { @@ -2682,10 +2681,11 @@ void drm_fb_release(struct drm_file *priv) list_del_init(&fb->filp_head); /* This will also drop the fpriv->fbs reference. */ + drm_modeset_lock_all(dev); drm_framebuffer_remove(fb); + drm_modeset_unlock_all(dev); } mutex_unlock(&priv->fbs_lock); - drm_modeset_unlock_all(dev); } /** -- cgit v1.2.3 From 468174f748603497e73dba9b5c6d1d9f71121486 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 00:09:12 +0100 Subject: drm: push modeset_lock_all into ->fb_create driver callbacks And drop it where it's not needed. Most driver just lookup the gem object, allocate an fb struct, fill in all the useful fields and then register it with drm_framebuffer_init. All of these operations are already separately locked, and since we only put the fb into the fpriv->fbs list _after_ having called ->fb_create, we can't also race with rmfb. We can otoh race with other ioctls that put the framebuffer to use, but all drivers have been reorganized already to call drm_framebuffer_init last in the fb creation sequence. So essentially, we can completely remove any modeset locks from the addfb ioctl paths. Yeah! Also, reference-counting is solid - we get a reference from fb_create which we transfer to the fpriv->fbs list. And after unlocking the fpriv->fbs_lock we don't touch the framebuffer any longer. Furthermore drm_framebuffer_init has added a 2nd reference for the idr lookup, and any access through that table will do it's own refcounting. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 09e02a7023f9..2e6103c5d632 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2271,18 +2271,12 @@ int drm_mode_addfb(struct drm_device *dev, if ((config->min_height > r.height) || (r.height > config->max_height)) return -EINVAL; - drm_modeset_lock_all(dev); - - /* TODO check buffer is sufficiently large */ - /* TODO setup destructor callback */ - fb = dev->mode_config.funcs->fb_create(dev, file_priv, &r); if (IS_ERR(fb)) { DRM_DEBUG_KMS("could not create framebuffer\n"); drm_modeset_unlock_all(dev); return PTR_ERR(fb); } - drm_modeset_unlock_all(dev); mutex_lock(&file_priv->fbs_lock); or->fb_id = fb->base.id; @@ -2457,15 +2451,12 @@ int drm_mode_addfb2(struct drm_device *dev, if (ret) return ret; - drm_modeset_lock_all(dev); - fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); if (IS_ERR(fb)) { DRM_DEBUG_KMS("could not create framebuffer\n"); drm_modeset_unlock_all(dev); return PTR_ERR(fb); } - drm_modeset_unlock_all(dev); mutex_lock(&file_priv->fbs_lock); r->fb_id = fb->base.id; -- cgit v1.2.3 From 58c0dca10614117cf4b385e3314e79e3b37fa66b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 13 Dec 2012 23:06:08 +0100 Subject: drm: don't take modeset locks in getfb ioctl We only need to push the fb unreference a bit down. While at it, properly pass the return value from ->create_handle back to userspace. Most drivers either return -ENODEV if they don't have a concept of buffer objects (ast, cirrus, ...) or just install a handle for the underlying gem object (which is ok since we hold a reference on that through the framebuffer). v2: Split out the ->create_handle rework in the individual drivers. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 2e6103c5d632..cba8c8bb789c 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2542,19 +2542,14 @@ int drm_mode_getfb(struct drm_device *dev, { struct drm_mode_fb_cmd *r = data; struct drm_framebuffer *fb; - int ret = 0; + int ret; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - drm_modeset_lock_all(dev); fb = drm_framebuffer_lookup(dev, r->fb_id); - if (!fb) { - ret = -EINVAL; - goto out; - } - /* fb is protect by the mode_config lock, so drop the ref immediately */ - drm_framebuffer_unreference(fb); + if (!fb) + return -EINVAL; r->height = fb->height; r->width = fb->width; @@ -2566,8 +2561,8 @@ int drm_mode_getfb(struct drm_device *dev, else ret = -ENODEV; -out: - drm_modeset_unlock_all(dev); + drm_framebuffer_unreference(fb); + return ret; } -- cgit v1.2.3 From 4ccf097f1935321f03ad36218588a9e446006b6a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 00:38:18 +0100 Subject: drm: fb refcounting for dirtyfb_ioctl We only need to ensure that the fb stays around for long enough. While at it, only grab the modeset locks when we need them (since most drivers don't implement the dirty callback, this should help jitter and stalls when using the generic modeset driver). Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index cba8c8bb789c..d4f8fa5b3c18 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2580,14 +2580,9 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - drm_modeset_lock_all(dev); fb = drm_framebuffer_lookup(dev, r->fb_id); - if (!fb) { - ret = -EINVAL; - goto out_err1; - } - /* fb is protect by the mode_config lock, so drop the ref immediately */ - drm_framebuffer_unreference(fb); + if (!fb) + return -EINVAL; num_clips = r->num_clips; clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr; @@ -2625,17 +2620,19 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, } if (fb->funcs->dirty) { + drm_modeset_lock_all(dev); ret = fb->funcs->dirty(fb, file_priv, flags, r->color, clips, num_clips); + drm_modeset_unlock_all(dev); } else { ret = -ENOSYS; - goto out_err2; } out_err2: kfree(clips); out_err1: - drm_modeset_unlock_all(dev); + drm_framebuffer_unreference(fb); + return ret; } -- cgit v1.2.3 From 6c2a75325c800de286166c693e0cd33c3a1c5ec8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 00:59:24 +0100 Subject: drm: refcounting for sprite framebuffers Now plane->fb holds a reference onto it's framebuffer. Nothing too fancy going on here: - Extract __drm_framebuffer_unreference to be called when we know we're not dropping the last reference, e.g. useful in the fb cleanup code. - Reduce the locked sections in the set_plane ioctl to only protect plane->fb/plane->crtc and the driver callback (i.e. hw state). Everything either doesn't disappear (crtc, plane) or is refcounted (fb), and all the data we check is invariant over the respective object's lifetimes. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index d4f8fa5b3c18..64ef12079528 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -445,6 +445,12 @@ static void drm_framebuffer_free_bug(struct kref *kref) BUG(); } +static void __drm_framebuffer_unreference(struct drm_framebuffer *fb) +{ + DRM_DEBUG("FB ID: %d\n", fb->base.id); + kref_put(&fb->refcount, drm_framebuffer_free_bug); +} + /* dev->mode_config.fb_lock must be held! */ static void __drm_framebuffer_unregister(struct drm_device *dev, struct drm_framebuffer *fb) @@ -455,7 +461,7 @@ static void __drm_framebuffer_unregister(struct drm_device *dev, fb->base.id = 0; - kref_put(&fb->refcount, drm_framebuffer_free_bug); + __drm_framebuffer_unreference(fb); } /** @@ -544,6 +550,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) if (ret) DRM_ERROR("failed to disable plane with busy fb\n"); /* disconnect the plane from the fb and crtc: */ + __drm_framebuffer_unreference(plane->fb); plane->fb = NULL; plane->crtc = NULL; } @@ -1850,7 +1857,7 @@ int drm_mode_setplane(struct drm_device *dev, void *data, struct drm_mode_object *obj; struct drm_plane *plane; struct drm_crtc *crtc; - struct drm_framebuffer *fb; + struct drm_framebuffer *fb = NULL, *old_fb = NULL; int ret = 0; unsigned int fb_width, fb_height; int i; @@ -1858,8 +1865,6 @@ int drm_mode_setplane(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - drm_modeset_lock_all(dev); - /* * First, find the plane, crtc, and fb objects. If not available, * we don't bother to call the driver. @@ -1869,16 +1874,18 @@ int drm_mode_setplane(struct drm_device *dev, void *data, if (!obj) { DRM_DEBUG_KMS("Unknown plane ID %d\n", plane_req->plane_id); - ret = -ENOENT; - goto out; + return -ENOENT; } plane = obj_to_plane(obj); /* No fb means shut it down */ if (!plane_req->fb_id) { + drm_modeset_lock_all(dev); + old_fb = plane->fb; plane->funcs->disable_plane(plane); plane->crtc = NULL; plane->fb = NULL; + drm_modeset_unlock_all(dev); goto out; } @@ -1899,8 +1906,6 @@ int drm_mode_setplane(struct drm_device *dev, void *data, ret = -ENOENT; goto out; } - /* fb is protect by the mode_config lock, so drop the ref immediately */ - drm_framebuffer_unreference(fb); /* Check whether this plane supports the fb pixel format. */ for (i = 0; i < plane->format_count; i++) @@ -1946,18 +1951,25 @@ int drm_mode_setplane(struct drm_device *dev, void *data, goto out; } + drm_modeset_lock_all(dev); ret = plane->funcs->update_plane(plane, crtc, fb, plane_req->crtc_x, plane_req->crtc_y, plane_req->crtc_w, plane_req->crtc_h, plane_req->src_x, plane_req->src_y, plane_req->src_w, plane_req->src_h); if (!ret) { + old_fb = plane->fb; + fb = NULL; plane->crtc = crtc; plane->fb = fb; } + drm_modeset_unlock_all(dev); out: - drm_modeset_unlock_all(dev); + if (fb) + drm_framebuffer_unreference(fb); + if (old_fb) + drm_framebuffer_unreference(old_fb); return ret; } -- cgit v1.2.3 From b0d1232589df5575c5971224ac4cb30e7e525884 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 01:07:12 +0100 Subject: drm: refcounting for crtc framebuffers With the prep patch to encapsulate ->set_crtc calls, this is now rather easy. Hooray for inconsistent semantics between ->set_crtc and ->page_flip, where the driver callback is supposed to update the fb pointer, and ->update_plane, where the drm core does the same. Also, since the drm core functions check crtc->fb before calling into driver callbacks, we can't really reduce the critical sections protected by the mode_config locks. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 64ef12079528..6dc75ee10079 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1984,8 +1984,21 @@ out: int drm_mode_set_config_internal(struct drm_mode_set *set) { struct drm_crtc *crtc = set->crtc; + struct drm_framebuffer *fb, *old_fb; + int ret; + + old_fb = crtc->fb; + fb = set->fb; - return crtc->funcs->set_config(set); + ret = crtc->funcs->set_config(set); + if (ret == 0) { + if (old_fb) + drm_framebuffer_unreference(old_fb); + if (fb) + drm_framebuffer_reference(fb); + } + + return ret; } EXPORT_SYMBOL(drm_mode_set_config_internal); @@ -2046,6 +2059,8 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, goto out; } fb = crtc->fb; + /* Make refcounting symmetric with the lookup path. */ + drm_framebuffer_reference(fb); } else { fb = drm_framebuffer_lookup(dev, crtc_req->fb_id); if (!fb) { @@ -2054,9 +2069,6 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, ret = -EINVAL; goto out; } - /* fb is protect by the mode_config lock, so drop the - * ref immediately */ - drm_framebuffer_unreference(fb); } mode = drm_mode_create(dev); @@ -2156,6 +2168,9 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, ret = drm_mode_set_config_internal(&set); out: + if (fb) + drm_framebuffer_unreference(fb); + kfree(connector_set); drm_mode_destroy(dev, mode); drm_modeset_unlock_all(dev); @@ -3656,7 +3671,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, struct drm_mode_crtc_page_flip *page_flip = data; struct drm_mode_object *obj; struct drm_crtc *crtc; - struct drm_framebuffer *fb; + struct drm_framebuffer *fb = NULL, *old_fb = NULL; struct drm_pending_vblank_event *e = NULL; unsigned long flags; int hdisplay, vdisplay; @@ -3687,8 +3702,6 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, fb = drm_framebuffer_lookup(dev, page_flip->fb_id); if (!fb) goto out; - /* fb is protect by the mode_config lock, so drop the ref immediately */ - drm_framebuffer_unreference(fb); hdisplay = crtc->mode.hdisplay; vdisplay = crtc->mode.vdisplay; @@ -3734,6 +3747,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, (void (*) (struct drm_pending_event *)) kfree; } + old_fb = crtc->fb; ret = crtc->funcs->page_flip(crtc, fb, e); if (ret) { if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { @@ -3742,9 +3756,18 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, spin_unlock_irqrestore(&dev->event_lock, flags); kfree(e); } + /* Keep the old fb, don't unref it. */ + old_fb = NULL; + } else { + /* Unref only the old framebuffer. */ + fb = NULL; } out: + if (fb) + drm_framebuffer_unreference(fb); + if (old_fb) + drm_framebuffer_unreference(old_fb); drm_modeset_unlock_all(dev); return ret; } -- cgit v1.2.3 From 623f9783027ef0a948205f17792c9e1fcedb61c6 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 16:21:38 +0100 Subject: drm/i915: dump refcount into framebuffer debugfs file Useful for checking whether the new refcounting works as advertised. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index a40c674a57be..8a11085e0043 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1367,11 +1367,12 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) ifbdev = dev_priv->fbdev; fb = to_intel_framebuffer(ifbdev->helper.fb); - seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, obj ", + seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, refcount %d, obj ", fb->base.width, fb->base.height, fb->base.depth, - fb->base.bits_per_pixel); + fb->base.bits_per_pixel, + atomic_read(&fb->base.refcount.refcount)); describe_obj(m, fb->obj); seq_printf(m, "\n"); mutex_unlock(&dev->mode_config.mutex); @@ -1381,11 +1382,12 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) if (&fb->base == ifbdev->helper.fb) continue; - seq_printf(m, "user size: %d x %d, depth %d, %d bpp, obj ", + seq_printf(m, "user size: %d x %d, depth %d, %d bpp, refcount %d, obj ", fb->base.width, fb->base.height, fb->base.depth, - fb->base.bits_per_pixel); + fb->base.bits_per_pixel, + atomic_read(&fb->base.refcount.refcount)); describe_obj(m, fb->obj); seq_printf(m, "\n"); } -- cgit v1.2.3 From 2fd5eabab02d9cdade04397eae0bfd49f452cdba Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 16:28:34 +0100 Subject: drm/vmwgfx: add proper framebuffer refcounting Afact vmwgfx already has all the right refcounting implemented on the backing storage, and we only need to ensure that the drm fb doesn't disappear untimely. So holding onto the fb reference from _lookup until vmw_kms_present has completed should be enough. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index 1b8f428accae..c509d40c4897 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -171,8 +171,6 @@ int vmw_present_ioctl(struct drm_device *dev, void *data, ret = -EINVAL; goto out_no_fb; } - /* fb is protect by the mode_config lock, so drop the ref immediately */ - drm_framebuffer_unreference(fb); vfb = vmw_framebuffer_to_vfb(fb); ret = ttm_read_lock(&vmaster->lock, true); @@ -197,6 +195,7 @@ int vmw_present_ioctl(struct drm_device *dev, void *data, out_no_surface: ttm_read_unlock(&vmaster->lock); out_no_ttm_lock: + drm_framebuffer_unreference(fb); out_no_fb: drm_modeset_unlock_all(dev); out_no_copy: @@ -256,14 +255,12 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data, ret = -EINVAL; goto out_no_fb; } - /* fb is protect by the mode_config lock, so drop the ref immediately */ - drm_framebuffer_unreference(fb); vfb = vmw_framebuffer_to_vfb(fb); if (!vfb->dmabuf) { DRM_ERROR("Framebuffer not dmabuf backed.\n"); ret = -EINVAL; - goto out_no_fb; + goto out_no_ttm_lock; } ret = ttm_read_lock(&vmaster->lock, true); @@ -276,6 +273,7 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data, ttm_read_unlock(&vmaster->lock); out_no_ttm_lock: + drm_framebuffer_unreference(fb); out_no_fb: drm_modeset_unlock_all(dev); out_no_copy: -- cgit v1.2.3 From b62584e366ebcb3adffefad373a5abc4c4b677ca Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 16:51:35 +0100 Subject: drm: optimize drm_framebuffer_remove Now that all framebuffer usage is properly refcounted, we are no longer required to hold the modeset locks while dropping the last reference. Hence implemented a fastpath which avoids the potential stalls associated with grabbing mode_config.lock for the case where there's no other reference around. Explain in a big comment why it is safe. Also update kerneldocs with the new locking rules around drm_framebuffer_remove. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 72 +++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 6dc75ee10079..9492e7ec9d9d 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -517,7 +517,11 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup); * * Scans all the CRTCs and planes in @dev's mode_config. If they're * using @fb, removes it, setting it to NULL. Then drops the reference to the - * passed-in framebuffer. + * passed-in framebuffer. Might take the modeset locks. + * + * Note that this function optimizes the cleanup away if the caller holds the + * last reference to the framebuffer. It is also guaranteed to not take the + * modeset locks in this case. */ void drm_framebuffer_remove(struct drm_framebuffer *fb) { @@ -527,33 +531,51 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) struct drm_mode_set set; int ret; - WARN_ON(!drm_modeset_is_locked(dev)); WARN_ON(!list_empty(&fb->filp_head)); - /* remove from any CRTC */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - if (crtc->fb == fb) { - /* should turn off the crtc */ - memset(&set, 0, sizeof(struct drm_mode_set)); - set.crtc = crtc; - set.fb = NULL; - ret = drm_mode_set_config_internal(&set); - if (ret) - DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); + /* + * drm ABI mandates that we remove any deleted framebuffers from active + * useage. But since most sane clients only remove framebuffers they no + * longer need, try to optimize this away. + * + * Since we're holding a reference ourselves, observing a refcount of 1 + * means that we're the last holder and can skip it. Also, the refcount + * can never increase from 1 again, so we don't need any barriers or + * locks. + * + * Note that userspace could try to race with use and instate a new + * usage _after_ we've cleared all current ones. End result will be an + * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot + * in this manner. + */ + if (atomic_read(&fb->refcount.refcount) > 1) { + drm_modeset_lock_all(dev); + /* remove from any CRTC */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (crtc->fb == fb) { + /* should turn off the crtc */ + memset(&set, 0, sizeof(struct drm_mode_set)); + set.crtc = crtc; + set.fb = NULL; + ret = drm_mode_set_config_internal(&set); + if (ret) + DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); + } } - } - list_for_each_entry(plane, &dev->mode_config.plane_list, head) { - if (plane->fb == fb) { - /* should turn off the crtc */ - ret = plane->funcs->disable_plane(plane); - if (ret) - DRM_ERROR("failed to disable plane with busy fb\n"); - /* disconnect the plane from the fb and crtc: */ - __drm_framebuffer_unreference(plane->fb); - plane->fb = NULL; - plane->crtc = NULL; + list_for_each_entry(plane, &dev->mode_config.plane_list, head) { + if (plane->fb == fb) { + /* should turn off the crtc */ + ret = plane->funcs->disable_plane(plane); + if (ret) + DRM_ERROR("failed to disable plane with busy fb\n"); + /* disconnect the plane from the fb and crtc: */ + __drm_framebuffer_unreference(plane->fb); + plane->fb = NULL; + plane->crtc = NULL; + } } + drm_modeset_unlock_all(dev); } drm_framebuffer_unreference(fb); @@ -2538,9 +2560,7 @@ int drm_mode_rmfb(struct drm_device *dev, mutex_unlock(&dev->mode_config.fb_lock); mutex_unlock(&file_priv->fbs_lock); - drm_modeset_lock_all(dev); drm_framebuffer_remove(fb); - drm_modeset_unlock_all(dev); return 0; @@ -2691,9 +2711,7 @@ void drm_fb_release(struct drm_file *priv) list_del_init(&fb->filp_head); /* This will also drop the fpriv->fbs reference. */ - drm_modeset_lock_all(dev); drm_framebuffer_remove(fb); - drm_modeset_unlock_all(dev); } mutex_unlock(&priv->fbs_lock); } -- cgit v1.2.3 From b4d5e7d1dbdd6a758a4f7717beef7bd6b007bd66 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 16:59:31 +0100 Subject: drm: only grab the crtc lock for pageflips The pagelip ioctl itself is rather simply, so the hard work for this patch is auditing all the drivers: - exynos: Pageflip is protect with dev->struct_mutex and ... synchronous. But nothing fancy going on, besides a check whether the crtc is enabled, which should probably be somewhere in the drm core so that we have unified behaviour across all drivers. - i915: hw-state is protected with dev->struct_mutex, the delayed unpin work together with the other stuff the pageflip complete irq handler needs is protected by the event_lock spinlock. - nouveau: With the pin/unpin functions fixed, everything looks safe: A bit of ttm wrestling and refcounting, and a few channel accesses. The later are either already proteced sufficiently, or are now safe with the channel locking introduced to make cursor updates safe. - radeon: The irq_get/put functions look a bit race, since the atomic_inc/dec isn't protect with locks. Otoh they're all per-crtc, so we should be safe with per-crtc locking from the drm core. Then there's tons of per-crtc register access, which could potentially go through the indirect reg acces. But that's fixed to make cursor updates concurrent. Bookeeping for the drm even is also protected with the even_lock, which also protects against the pageflip irq handler since radeon hw seems to have no way to queue these up asynchronously. Otherwise just a bit of ttm-based buffer handling and fencing, which is now safe with the previous patch to hold bdev->fence_lock while grabbing the ttm fence. - shmob: Only one crtc. That's an easy one ... - vmwgfx: As usual a bit special with tons different things: - Flippable check using is_implicit and num_implicit. Changes to those seem to be nicely covered with the global modeset lock, so we should be fine. - Some dirty cliprect handling stuff, or at least that is my guess. Looks like it's fine since either it's per-crtc, invariant or (like the execbuf stuff launched) protected otherwise. - Adding the actual flip to the fence_event list. On a quick look this seems to have solid locking in place, too. ... but generally this is all way over my head. - imx: Impressive display of races between the page_flip implementation and the irq handler. Also, ipu_drm_set_base which gets eventually called from the irq handler to update the display base isn't really protected against concurrent set_config calls from process context. In any case, going for per-crtc locking won't make this worse, so nothing to do. - omap: The new async callback code merged into 3.8 seems to have solid locking in place, and there doesn't seem to be any shared state at risk. Especially since the callbacks still use modeset_lock_all and are so not converted. v2: Update omapdrm analysis to 3.8 code per the discussion with Rob Clark. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 9492e7ec9d9d..fd3e9a13b04f 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3699,12 +3699,12 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, page_flip->reserved != 0) return -EINVAL; - drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) - goto out; + return -EINVAL; crtc = obj_to_crtc(obj); + mutex_lock(&crtc->mutex); if (crtc->fb == NULL) { /* The framebuffer is currently unbound, presumably * due to a hotplug event, that userspace has not @@ -3786,7 +3786,8 @@ out: drm_framebuffer_unreference(fb); if (old_fb) drm_framebuffer_unreference(old_fb); - drm_modeset_unlock_all(dev); + mutex_unlock(&crtc->mutex); + return ret; } -- cgit v1.2.3 From 7b24056be6db7ce907baffdd4cf142ab774ea60c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 12 Dec 2012 00:35:33 +0100 Subject: drm: don't hold crtc mutexes for connector ->detect callbacks The coup de grace of the entire journey. No more dropped frames every 10s on my testbox! I've tried to audit all ->detect and ->get_modes callbacks, but things became a bit fuzzy after trying to piece together the umpteenth implemenation. Afaict most drivers just have bog-standard output register frobbing with a notch of i2c edid reading, nothing which could potentially race with the newly concurrent pageflip/set_cursor code. The big exception is load-detection code which requires a running pipe, but radeon/nouveau seem to to this without touching any state which can be observed from page_flip (e.g. disabled crtcs temporarily getting enabled and so a pageflip succeeding). The only special case I could find is the i915 load detect code. That uses the normal modeset interface to enable the load-detect crtc, and so userspace could try to squeeze in a pageflip on the load-detect pipe. So we need to grab the relevant crtc mutex in there, to avoid the temporary crtc enabling to sneak out and be visible to userspace. Note that the sysfs files already stopped grabbing the per-crtc locks, since I didn't want to bother with doing a interruptible modeset_lock_all. But since there's very little in-between breakage (essentially just the ability for userspace to pageflip on load-detect crtcs when it shouldn't on the i915 driver) I figured I don't need to bother. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 5 +++-- drivers/gpu/drm/drm_crtc_helper.c | 8 ++++---- drivers/gpu/drm/i915/intel_display.c | 10 ++++++++-- 3 files changed, 15 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index fd3e9a13b04f..9c797f6fea75 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1617,7 +1617,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id); - drm_modeset_lock_all(dev); + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR); @@ -1714,7 +1714,8 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, out_resp->count_encoders = encoders_count; out: - drm_modeset_unlock_all(dev); + mutex_unlock(&dev->mode_config.mutex); + return ret; } diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 400ef86a2b43..7b2d378b2576 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -980,7 +980,7 @@ static void output_poll_execute(struct work_struct *work) if (!drm_kms_helper_poll) return; - drm_modeset_lock_all(dev); + mutex_lock(&dev->mode_config.mutex); list_for_each_entry(connector, &dev->mode_config.connector_list, head) { /* Ignore forced connectors. */ @@ -1010,7 +1010,7 @@ static void output_poll_execute(struct work_struct *work) changed = true; } - drm_modeset_unlock_all(dev); + mutex_unlock(&dev->mode_config.mutex); if (changed) drm_kms_helper_hotplug_event(dev); @@ -1070,7 +1070,7 @@ void drm_helper_hpd_irq_event(struct drm_device *dev) if (!dev->mode_config.poll_enabled) return; - drm_modeset_lock_all(dev); + mutex_lock(&dev->mode_config.mutex); list_for_each_entry(connector, &dev->mode_config.connector_list, head) { /* Only handle HPD capable connectors. */ @@ -1088,7 +1088,7 @@ void drm_helper_hpd_irq_event(struct drm_device *dev) changed = true; } - drm_modeset_unlock_all(dev); + mutex_unlock(&dev->mode_config.mutex); if (changed) drm_kms_helper_hotplug_event(dev); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index df51203dcebd..c8f417b34d1a 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6700,6 +6700,8 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, if (encoder->crtc) { crtc = encoder->crtc; + mutex_lock(&crtc->mutex); + old->dpms_mode = connector->dpms; old->load_detect_temp = false; @@ -6729,6 +6731,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, return false; } + mutex_lock(&crtc->mutex); intel_encoder->new_crtc = to_intel_crtc(crtc); to_intel_connector(connector)->new_encoder = intel_encoder; @@ -6756,6 +6759,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, DRM_DEBUG_KMS("reusing fbdev for load-detection framebuffer\n"); if (IS_ERR(fb)) { DRM_DEBUG_KMS("failed to allocate framebuffer for load-detection\n"); + mutex_unlock(&crtc->mutex); return false; } @@ -6763,6 +6767,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n"); if (old->release_fb) old->release_fb->funcs->destroy(old->release_fb); + mutex_unlock(&crtc->mutex); return false; } @@ -6777,14 +6782,13 @@ void intel_release_load_detect_pipe(struct drm_connector *connector, struct intel_encoder *intel_encoder = intel_attached_encoder(connector); struct drm_encoder *encoder = &intel_encoder->base; + struct drm_crtc *crtc = encoder->crtc; DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n", connector->base.id, drm_get_connector_name(connector), encoder->base.id, drm_get_encoder_name(encoder)); if (old->load_detect_temp) { - struct drm_crtc *crtc = encoder->crtc; - to_intel_connector(connector)->new_encoder = NULL; intel_encoder->new_crtc = NULL; intel_set_mode(crtc, NULL, 0, 0, NULL); @@ -6800,6 +6804,8 @@ void intel_release_load_detect_pipe(struct drm_connector *connector, /* Switch crtc and encoder back off if necessary */ if (old->dpms_mode != DRM_MODE_DPMS_ON) connector->funcs->dpms(connector, old->dpms_mode); + + mutex_unlock(&crtc->mutex); } /* Returns the clock of the currently programmed mode of the given pipe. */ -- cgit v1.2.3 From 20c60c35de3285222b3476c3445c66bedf0c449c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 17 Dec 2012 12:13:23 +0100 Subject: drm/fb_helper: check whether fbcon is bound We need to make sure that the fbcon is still bound when touching the hw, since otherwise we might corrupt the modeset state of kms clients. X mostly works around that with VT switching and setting the VT into raw mode, which disables most fbcon events. Raw kms test programs though don't do that dance, and in the future we might want to aim to abolish CONFIG_VT anyway. So improve preventive measures a bit. To do so, extract the existing logic for handling hotplug events (which X can't block with the current set of tricks) and reuse it for the fbdev blanking helper. Long-term we really need to either scrap this all and only have a OOPS console, or come up with a saner model for device ownership sharing between fbdev/fbcon and kms userspace. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index be0f2d65db3a..0c6e25e979dd 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -305,6 +305,24 @@ void drm_fb_helper_restore(void) } EXPORT_SYMBOL(drm_fb_helper_restore); +static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) +{ + struct drm_device *dev = fb_helper->dev; + struct drm_crtc *crtc; + int bound = 0, crtcs_bound = 0; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (crtc->fb) + crtcs_bound++; + if (crtc->fb == fb_helper->fb) + bound++; + } + + if (bound < crtcs_bound) + return false; + return true; +} + #ifdef CONFIG_MAGIC_SYSRQ static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) { @@ -338,6 +356,11 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) * For each CRTC in this fb, turn the connectors on/off. */ drm_modeset_lock_all(dev); + if (!drm_fb_helper_is_bound(fb_helper)) { + drm_modeset_unlock_all(dev); + return; + } + for (i = 0; i < fb_helper->crtc_count; i++) { crtc = fb_helper->crtc_info[i].mode_set.crtc; @@ -702,6 +725,11 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, int i; drm_modeset_lock_all(dev); + if (!drm_fb_helper_is_bound(fb_helper)) { + drm_modeset_unlock_all(dev); + return -EBUSY; + } + for (i = 0; i < fb_helper->crtc_count; i++) { crtc = fb_helper->crtc_info[i].mode_set.crtc; @@ -1369,21 +1397,12 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) struct drm_device *dev = fb_helper->dev; int count = 0; u32 max_width, max_height, bpp_sel; - int bound = 0, crtcs_bound = 0; - struct drm_crtc *crtc; if (!fb_helper->fb) return 0; drm_modeset_lock_all(dev); - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - if (crtc->fb) - crtcs_bound++; - if (crtc->fb == fb_helper->fb) - bound++; - } - - if (bound < crtcs_bound) { + if (!drm_fb_helper_is_bound(fb_helper)) { fb_helper->delayed_hotplug = true; drm_modeset_unlock_all(dev); return 0; -- cgit v1.2.3 From f8122a82d2eae8ef42de48829deed0ca9d9e1f17 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 16 Jan 2013 15:48:50 +0200 Subject: dw_dmac: allocate dma descriptors from DMA_COHERENT memory Currently descriptors are allocated from normal cacheable memory and that slows down filling the descriptors, as we need to call cache_coherency routines afterwards. It would be better to allocate memory for these descriptors from DMA_COHERENT memory. This would make code much cleaner too. Signed-off-by: Andy Shevchenko Tested-by: Mika Westerberg Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 81 ++++++++++------------------------------------ drivers/dma/dw_dmac_regs.h | 1 + 2 files changed, 18 insertions(+), 64 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 28d5f01c350c..76301c4ba1ad 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -91,14 +92,6 @@ static inline unsigned int dwc_get_data_width(struct dma_chan *chan, int master) /*----------------------------------------------------------------------*/ -/* - * Because we're not relying on writeback from the controller (it may not - * even be configured into the core!) we don't need to use dma_pool. These - * descriptors -- and associated data -- are cacheable. We do need to make - * sure their dcache entries are written back before handing them off to - * the controller, though. - */ - static struct device *chan2dev(struct dma_chan *chan) { return &chan->dev->device; @@ -137,19 +130,6 @@ static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) return ret; } -static void dwc_sync_desc_for_cpu(struct dw_dma_chan *dwc, struct dw_desc *desc) -{ - struct dw_desc *child; - - list_for_each_entry(child, &desc->tx_list, desc_node) - dma_sync_single_for_cpu(chan2parent(&dwc->chan), - child->txd.phys, sizeof(child->lli), - DMA_TO_DEVICE); - dma_sync_single_for_cpu(chan2parent(&dwc->chan), - desc->txd.phys, sizeof(desc->lli), - DMA_TO_DEVICE); -} - /* * Move a descriptor, including any children, to the free list. * `desc' must not be on any lists. @@ -161,8 +141,6 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) if (desc) { struct dw_desc *child; - dwc_sync_desc_for_cpu(dwc, desc); - spin_lock_irqsave(&dwc->lock, flags); list_for_each_entry(child, &desc->tx_list, desc_node) dev_vdbg(chan2dev(&dwc->chan), @@ -335,8 +313,6 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, param = txd->callback_param; } - dwc_sync_desc_for_cpu(dwc, desc); - /* async_tx_ack */ list_for_each_entry(child, &desc->tx_list, desc_node) async_tx_ack(&child->txd); @@ -770,25 +746,17 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, first = desc; } else { prev->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, sizeof(prev->lli), - DMA_TO_DEVICE); list_add_tail(&desc->desc_node, &first->tx_list); } prev = desc; } - if (flags & DMA_PREP_INTERRUPT) /* Trigger interrupt after last block */ prev->lli.ctllo |= DWC_CTLL_INT_EN; prev->lli.llp = 0; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, sizeof(prev->lli), - DMA_TO_DEVICE); - first->txd.flags = flags; first->len = len; @@ -876,10 +844,6 @@ slave_sg_todev_fill_desc: first = desc; } else { prev->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, - sizeof(prev->lli), - DMA_TO_DEVICE); list_add_tail(&desc->desc_node, &first->tx_list); } @@ -938,10 +902,6 @@ slave_sg_fromdev_fill_desc: first = desc; } else { prev->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, - sizeof(prev->lli), - DMA_TO_DEVICE); list_add_tail(&desc->desc_node, &first->tx_list); } @@ -961,10 +921,6 @@ slave_sg_fromdev_fill_desc: prev->lli.ctllo |= DWC_CTLL_INT_EN; prev->lli.llp = 0; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, sizeof(prev->lli), - DMA_TO_DEVICE); - first->len = total_len; return &first->txd; @@ -1118,7 +1074,6 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) struct dw_desc *desc; int i; unsigned long flags; - int ret; dev_vdbg(chan2dev(chan), "%s\n", __func__); @@ -1139,21 +1094,21 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) spin_lock_irqsave(&dwc->lock, flags); i = dwc->descs_allocated; while (dwc->descs_allocated < NR_DESCS_PER_CHANNEL) { + dma_addr_t phys; + spin_unlock_irqrestore(&dwc->lock, flags); - desc = kzalloc(sizeof(struct dw_desc), GFP_KERNEL); + desc = dma_pool_alloc(dw->desc_pool, GFP_ATOMIC, &phys); if (!desc) goto err_desc_alloc; + memset(desc, 0, sizeof(struct dw_desc)); + INIT_LIST_HEAD(&desc->tx_list); dma_async_tx_descriptor_init(&desc->txd, chan); desc->txd.tx_submit = dwc_tx_submit; desc->txd.flags = DMA_CTRL_ACK; - desc->txd.phys = dma_map_single(chan2parent(chan), &desc->lli, - sizeof(desc->lli), DMA_TO_DEVICE); - ret = dma_mapping_error(chan2parent(chan), desc->txd.phys); - if (ret) - goto err_desc_alloc; + desc->txd.phys = phys; dwc_desc_put(dwc, desc); @@ -1168,8 +1123,6 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) return i; err_desc_alloc: - kfree(desc); - dev_info(chan2dev(chan), "only allocated %d descriptors\n", i); return i; @@ -1204,9 +1157,7 @@ static void dwc_free_chan_resources(struct dma_chan *chan) list_for_each_entry_safe(desc, _desc, &list, desc_node) { dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc); - dma_unmap_single(chan2parent(chan), desc->txd.phys, - sizeof(desc->lli), DMA_TO_DEVICE); - kfree(desc); + dma_pool_free(dw->desc_pool, desc, desc->txd.phys); } dev_vdbg(chan2dev(chan), "%s: done\n", __func__); @@ -1451,20 +1402,14 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, desc->lli.ctlhi = (period_len >> reg_width); cdesc->desc[i] = desc; - if (last) { + if (last) last->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan2parent(chan), - last->txd.phys, sizeof(last->lli), - DMA_TO_DEVICE); - } last = desc; } /* lets make a cyclic list */ last->lli.llp = cdesc->desc[0]->txd.phys; - dma_sync_single_for_device(chan2parent(chan), last->txd.phys, - sizeof(last->lli), DMA_TO_DEVICE); dev_dbg(chan2dev(&dwc->chan), "cyclic prepared buf 0x%llx len %zu " "period %zu periods %d\n", (unsigned long long)buf_addr, @@ -1722,6 +1667,14 @@ static int dw_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dw); + /* create a pool of consistent memory blocks for hardware descriptors */ + dw->desc_pool = dmam_pool_create("dw_dmac_desc_pool", &pdev->dev, + sizeof(struct dw_desc), 4, 0); + if (!dw->desc_pool) { + dev_err(&pdev->dev, "No memory for descriptors dma pool\n"); + return -ENOMEM; + } + tasklet_init(&dw->tasklet, dw_dma_tasklet, (unsigned long)dw); INIT_LIST_HEAD(&dw->dma.channels); diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 577f2dd35882..fef296de4af1 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -235,6 +235,7 @@ static inline struct dw_dma_chan *to_dw_dma_chan(struct dma_chan *chan) struct dw_dma { struct dma_device dma; void __iomem *regs; + struct dma_pool *desc_pool; struct tasklet_struct tasklet; struct clk *clk; -- cgit v1.2.3 From 5be10f349bc0a2f3dd2ab6417ffe29746403984c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 17 Jan 2013 10:03:01 +0200 Subject: dw_dmac: don't exceed AHB master number in dwc_get_data_width The driver assumes that hardware has two AHB masters which might not be always true. In such cases we must not exceed number of the AHB masters present in the hardware. In the proposed scheme in this patch, we would choose the master with highest possible number whenever we exceed max AHB masters. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 76301c4ba1ad..635a4a5d31ae 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -47,13 +47,29 @@ static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave) return slave ? slave->src_master : 1; } +#define SRC_MASTER 0 +#define DST_MASTER 1 + +static inline unsigned int dwc_get_master(struct dma_chan *chan, int master) +{ + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_dma_slave *dws = chan->private; + unsigned int m; + + if (master == SRC_MASTER) + m = dwc_get_sms(dws); + else + m = dwc_get_dms(dws); + + return min_t(unsigned int, dw->nr_masters - 1, m); +} + #define DWC_DEFAULT_CTLLO(_chan) ({ \ - struct dw_dma_slave *__slave = (_chan->private); \ struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \ struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \ bool _is_slave = is_slave_direction(_dwc->direction); \ - int _dms = dwc_get_dms(__slave); \ - int _sms = dwc_get_sms(__slave); \ + int _dms = dwc_get_master(_chan, DST_MASTER); \ + int _sms = dwc_get_master(_chan, SRC_MASTER); \ u8 _smsize = _is_slave ? _sconfig->src_maxburst : \ DW_DMA_MSIZE_16; \ u8 _dmsize = _is_slave ? _sconfig->dst_maxburst : \ @@ -74,20 +90,11 @@ static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave) */ #define NR_DESCS_PER_CHANNEL 64 -#define SRC_MASTER 0 -#define DST_MASTER 1 - static inline unsigned int dwc_get_data_width(struct dma_chan *chan, int master) { struct dw_dma *dw = to_dw_dma(chan->device); - struct dw_dma_slave *dws = chan->private; - - if (master == SRC_MASTER) - return dw->data_width[dwc_get_sms(dws)]; - else if (master == DST_MASTER) - return dw->data_width[dwc_get_dms(dws)]; - return 0; + return dw->data_width[dwc_get_master(chan, master)]; } /*----------------------------------------------------------------------*/ -- cgit v1.2.3 From 77bcc497c60ec62dbb84abc809a6e218d53409e9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 18 Jan 2013 14:14:15 +0200 Subject: dw_dmac: move soft LLP code from tasklet to dwc_scan_descriptors The proper place for the main logic of the soft LLP mode is dwc_scan_descriptors. It prevents to get the transfer unexpectedly aborted in case the user calls dwc_tx_status. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 635a4a5d31ae..dc3b9558a25c 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -400,6 +400,20 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) if (status_xfer & dwc->mask) { /* Everything we've submitted is done */ dma_writel(dw, CLEAR.XFER, dwc->mask); + + if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { + if (dwc->tx_node_active != dwc->tx_list) { + desc = to_dw_desc(dwc->tx_node_active); + + /* Submit next block */ + dwc_do_single_block(dwc, desc); + spin_unlock_irqrestore(&dwc->lock, flags); + + return; + } + /* We are done here */ + clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); + } spin_unlock_irqrestore(&dwc->lock, flags); dwc_complete_all(dw, dwc); @@ -411,6 +425,12 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) return; } + if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { + dev_vdbg(chan2dev(&dwc->chan), "%s: soft LLP mode\n", __func__); + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + dev_vdbg(chan2dev(&dwc->chan), "%s: llp=0x%llx\n", __func__, (unsigned long long)llp); @@ -596,29 +616,8 @@ static void dw_dma_tasklet(unsigned long data) dwc_handle_cyclic(dw, dwc, status_err, status_xfer); else if (status_err & (1 << i)) dwc_handle_error(dw, dwc); - else if (status_xfer & (1 << i)) { - unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); - if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { - if (dwc->tx_node_active != dwc->tx_list) { - struct dw_desc *desc = - to_dw_desc(dwc->tx_node_active); - - dma_writel(dw, CLEAR.XFER, dwc->mask); - - dwc_do_single_block(dwc, desc); - - spin_unlock_irqrestore(&dwc->lock, flags); - continue; - } - /* we are done here */ - clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); - } - spin_unlock_irqrestore(&dwc->lock, flags); - + else if (status_xfer & (1 << i)) dwc_scan_descriptors(dw, dwc); - } } /* -- cgit v1.2.3 From 373d4d099761cb1f637bed488ab3871945882273 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 21 Jan 2013 17:17:39 +1030 Subject: taint: add explicit flag to show whether lock dep is still OK. Fix up all callers as they were before, with make one change: an unsigned module taints the kernel, but doesn't turn off lockdep. Signed-off-by: Rusty Russell --- arch/alpha/kernel/traps.c | 2 +- arch/arm/kernel/traps.c | 2 +- arch/arm64/kernel/traps.c | 2 +- arch/avr32/kernel/traps.c | 2 +- arch/hexagon/kernel/traps.c | 2 +- arch/ia64/kernel/traps.c | 2 +- arch/m68k/kernel/traps.c | 2 +- arch/mips/kernel/traps.c | 2 +- arch/parisc/kernel/traps.c | 2 +- arch/powerpc/kernel/traps.c | 2 +- arch/s390/kernel/traps.c | 2 +- arch/sh/kernel/traps.c | 2 +- arch/sparc/kernel/setup_64.c | 2 +- arch/sparc/kernel/traps_32.c | 2 +- arch/sparc/kernel/traps_64.c | 2 +- arch/unicore32/kernel/traps.c | 2 +- arch/x86/kernel/cpu/amd.c | 3 +-- arch/x86/kernel/cpu/mcheck/mce.c | 2 +- arch/x86/kernel/cpu/mcheck/p5.c | 2 +- arch/x86/kernel/cpu/mcheck/winchip.c | 2 +- arch/x86/kernel/cpu/mtrr/generic.c | 2 +- arch/x86/kernel/dumpstack.c | 2 +- arch/xtensa/kernel/traps.c | 2 +- drivers/acpi/custom_method.c | 2 +- drivers/acpi/osl.c | 2 +- drivers/base/regmap/regmap-debugfs.c | 2 +- include/linux/kernel.h | 6 +++++- kernel/module.c | 26 +++++++++++++++----------- kernel/panic.c | 34 ++++++++++++++-------------------- kernel/sched/core.c | 2 +- kernel/sysctl.c | 2 +- lib/bug.c | 3 ++- mm/memory.c | 2 +- mm/page_alloc.c | 2 +- mm/slab.c | 2 +- mm/slub.c | 2 +- sound/soc/soc-core.c | 2 +- 37 files changed, 69 insertions(+), 67 deletions(-) (limited to 'drivers') diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c index 272666d006df..4037461a6493 100644 --- a/arch/alpha/kernel/traps.c +++ b/arch/alpha/kernel/traps.c @@ -186,7 +186,7 @@ die_if_kernel(char * str, struct pt_regs *regs, long err, unsigned long *r9_15) #endif printk("%s(%d): %s %ld\n", current->comm, task_pid_nr(current), str, err); dik_show_regs(regs, r9_15); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); dik_show_trace((unsigned long *)(regs+1)); dik_show_code((unsigned int *)regs->pc); diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index b0179b89a04c..1c089119b2d7 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -296,7 +296,7 @@ static void oops_end(unsigned long flags, struct pt_regs *regs, int signr) bust_spinlocks(0); die_owner = -1; - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); die_nest_count--; if (!die_nest_count) /* Nest count reaches zero, release the lock. */ diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index 3883f842434f..b3c5f628bdb4 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -242,7 +242,7 @@ void die(const char *str, struct pt_regs *regs, int err) crash_kexec(regs); bust_spinlocks(0); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); raw_spin_unlock_irq(&die_lock); oops_exit(); diff --git a/arch/avr32/kernel/traps.c b/arch/avr32/kernel/traps.c index 3d760c06f024..682b2478691a 100644 --- a/arch/avr32/kernel/traps.c +++ b/arch/avr32/kernel/traps.c @@ -61,7 +61,7 @@ void die(const char *str, struct pt_regs *regs, long err) show_regs_log_lvl(regs, KERN_EMERG); show_stack_log_lvl(current, regs->sp, regs, KERN_EMERG); bust_spinlocks(0); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); spin_unlock_irq(&die_lock); if (in_interrupt()) diff --git a/arch/hexagon/kernel/traps.c b/arch/hexagon/kernel/traps.c index a41eeb8eeaa1..be5e2dd9c9d3 100644 --- a/arch/hexagon/kernel/traps.c +++ b/arch/hexagon/kernel/traps.c @@ -225,7 +225,7 @@ int die(const char *str, struct pt_regs *regs, long err) do_show_stack(current, ®s->r30, pt_elr(regs)); bust_spinlocks(0); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); spin_unlock_irq(&die.lock); diff --git a/arch/ia64/kernel/traps.c b/arch/ia64/kernel/traps.c index bd42b76000d1..f7f9f9c6caf0 100644 --- a/arch/ia64/kernel/traps.c +++ b/arch/ia64/kernel/traps.c @@ -72,7 +72,7 @@ die (const char *str, struct pt_regs *regs, long err) bust_spinlocks(0); die.lock_owner = -1; - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); spin_unlock_irq(&die.lock); if (!regs) diff --git a/arch/m68k/kernel/traps.c b/arch/m68k/kernel/traps.c index cbc624af4494..f32ab22e7ed3 100644 --- a/arch/m68k/kernel/traps.c +++ b/arch/m68k/kernel/traps.c @@ -1176,7 +1176,7 @@ void die_if_kernel (char *str, struct pt_regs *fp, int nr) console_verbose(); printk("%s: %08x\n",str,nr); show_registers(fp); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); do_exit(SIGSEGV); } diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index cf7ac5483f53..9007966d56d4 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -396,7 +396,7 @@ void __noreturn die(const char *str, struct pt_regs *regs) printk("%s[#%d]:\n", str, ++die_counter); show_registers(regs); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); raw_spin_unlock_irq(&die_lock); oops_exit(); diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c index 45ba99f5080b..aeb8f8f2c07a 100644 --- a/arch/parisc/kernel/traps.c +++ b/arch/parisc/kernel/traps.c @@ -282,7 +282,7 @@ void die_if_kernel(char *str, struct pt_regs *regs, long err) show_regs(regs); dump_stack(); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); if (in_interrupt()) panic("Fatal exception in interrupt"); diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 32518401af68..c579db859388 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -138,7 +138,7 @@ static void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, { bust_spinlocks(0); die_owner = -1; - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); die_nest_count--; oops_exit(); printk("\n"); diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index 70ecfc5fe8f0..13dd63fba367 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -271,7 +271,7 @@ void die(struct pt_regs *regs, const char *str) print_modules(); show_regs(regs); bust_spinlocks(0); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); spin_unlock_irq(&die_lock); if (in_interrupt()) panic("Fatal exception in interrupt"); diff --git a/arch/sh/kernel/traps.c b/arch/sh/kernel/traps.c index 72246bc06884..dfdad72c61ca 100644 --- a/arch/sh/kernel/traps.c +++ b/arch/sh/kernel/traps.c @@ -38,7 +38,7 @@ void die(const char *str, struct pt_regs *regs, long err) notify_die(DIE_OOPS, str, regs, err, 255, SIGSEGV); bust_spinlocks(0); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); spin_unlock_irq(&die_lock); oops_exit(); diff --git a/arch/sparc/kernel/setup_64.c b/arch/sparc/kernel/setup_64.c index 0eaf0059aaef..88a127b9c69e 100644 --- a/arch/sparc/kernel/setup_64.c +++ b/arch/sparc/kernel/setup_64.c @@ -115,7 +115,7 @@ static void __init process_switch(char c) break; } cheetah_pcache_forced_on = 1; - add_taint(TAINT_MACHINE_CHECK); + add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); cheetah_enable_pcache(); break; diff --git a/arch/sparc/kernel/traps_32.c b/arch/sparc/kernel/traps_32.c index a5785ea2a85d..662982946a89 100644 --- a/arch/sparc/kernel/traps_32.c +++ b/arch/sparc/kernel/traps_32.c @@ -58,7 +58,7 @@ void die_if_kernel(char *str, struct pt_regs *regs) printk("%s(%d): %s [#%d]\n", current->comm, task_pid_nr(current), str, ++die_counter); show_regs(regs); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); __SAVE; __SAVE; __SAVE; __SAVE; __SAVE; __SAVE; __SAVE; __SAVE; diff --git a/arch/sparc/kernel/traps_64.c b/arch/sparc/kernel/traps_64.c index e7ecf1507d90..8d38ca97aa23 100644 --- a/arch/sparc/kernel/traps_64.c +++ b/arch/sparc/kernel/traps_64.c @@ -2383,7 +2383,7 @@ void die_if_kernel(char *str, struct pt_regs *regs) notify_die(DIE_OOPS, str, regs, 0, 255, SIGSEGV); __asm__ __volatile__("flushw"); show_regs(regs); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); if (regs->tstate & TSTATE_PRIV) { struct thread_info *tp = current_thread_info(); struct reg_window *rw = (struct reg_window *) diff --git a/arch/unicore32/kernel/traps.c b/arch/unicore32/kernel/traps.c index 2054f0d4db13..0870b68d2ad9 100644 --- a/arch/unicore32/kernel/traps.c +++ b/arch/unicore32/kernel/traps.c @@ -231,7 +231,7 @@ void die(const char *str, struct pt_regs *regs, int err) ret = __die(str, err, thread, regs); bust_spinlocks(0); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); spin_unlock_irq(&die_lock); oops_exit(); diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 15239fffd6fe..5853e57523e5 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -220,8 +220,7 @@ static void __cpuinit amd_k7_smp_check(struct cpuinfo_x86 *c) */ WARN_ONCE(1, "WARNING: This combination of AMD" " processors is not suitable for SMP.\n"); - if (!test_taint(TAINT_UNSAFE_SMP)) - add_taint(TAINT_UNSAFE_SMP); + add_taint(TAINT_UNSAFE_SMP, LOCKDEP_NOW_UNRELIABLE); valid_k7: ; diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index 80dbda84f1c3..6bc15edbc8cd 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c @@ -1085,7 +1085,7 @@ void do_machine_check(struct pt_regs *regs, long error_code) /* * Set taint even when machine check was not enabled. */ - add_taint(TAINT_MACHINE_CHECK); + add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); severity = mce_severity(&m, cfg->tolerant, NULL); diff --git a/arch/x86/kernel/cpu/mcheck/p5.c b/arch/x86/kernel/cpu/mcheck/p5.c index 2d5454cd2c4f..1c044b1ccc59 100644 --- a/arch/x86/kernel/cpu/mcheck/p5.c +++ b/arch/x86/kernel/cpu/mcheck/p5.c @@ -33,7 +33,7 @@ static void pentium_machine_check(struct pt_regs *regs, long error_code) smp_processor_id()); } - add_taint(TAINT_MACHINE_CHECK); + add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); } /* Set up machine check reporting for processors with Intel style MCE: */ diff --git a/arch/x86/kernel/cpu/mcheck/winchip.c b/arch/x86/kernel/cpu/mcheck/winchip.c index 2d7998fb628c..e9a701aecaa1 100644 --- a/arch/x86/kernel/cpu/mcheck/winchip.c +++ b/arch/x86/kernel/cpu/mcheck/winchip.c @@ -15,7 +15,7 @@ static void winchip_machine_check(struct pt_regs *regs, long error_code) { printk(KERN_EMERG "CPU0: Machine Check Exception.\n"); - add_taint(TAINT_MACHINE_CHECK); + add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); } /* Set up machine check reporting on the Winchip C6 series */ diff --git a/arch/x86/kernel/cpu/mtrr/generic.c b/arch/x86/kernel/cpu/mtrr/generic.c index e9fe907cd249..fa72a39e5d46 100644 --- a/arch/x86/kernel/cpu/mtrr/generic.c +++ b/arch/x86/kernel/cpu/mtrr/generic.c @@ -542,7 +542,7 @@ static void generic_get_mtrr(unsigned int reg, unsigned long *base, if (tmp != mask_lo) { printk(KERN_WARNING "mtrr: your BIOS has configured an incorrect mask, fixing it.\n"); - add_taint(TAINT_FIRMWARE_WORKAROUND); + add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK); mask_lo = tmp; } } diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c index ae42418bc50f..c8797d55b245 100644 --- a/arch/x86/kernel/dumpstack.c +++ b/arch/x86/kernel/dumpstack.c @@ -232,7 +232,7 @@ void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr) bust_spinlocks(0); die_owner = -1; - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); die_nest_count--; if (!die_nest_count) /* Nest count reaches zero, release the lock. */ diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index 01e0111bf787..ded955d45155 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c @@ -524,7 +524,7 @@ void die(const char * str, struct pt_regs * regs, long err) if (!user_mode(regs)) show_stack(NULL, (unsigned long*)regs->areg[1]); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); spin_unlock_irq(&die_lock); if (in_interrupt()) diff --git a/drivers/acpi/custom_method.c b/drivers/acpi/custom_method.c index 5d42c2414ae5..cd9a68e2fea9 100644 --- a/drivers/acpi/custom_method.c +++ b/drivers/acpi/custom_method.c @@ -66,7 +66,7 @@ static ssize_t cm_write(struct file *file, const char __user * user_buf, buf = NULL; if (ACPI_FAILURE(status)) return -EINVAL; - add_taint(TAINT_OVERRIDDEN_ACPI_TABLE); + add_taint(TAINT_OVERRIDDEN_ACPI_TABLE, LOCKDEP_NOW_UNRELIABLE); } return count; diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 3ff267861541..535e888bad78 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -661,7 +661,7 @@ static void acpi_table_taint(struct acpi_table_header *table) pr_warn(PREFIX "Override [%4.4s-%8.8s], this is unsafe: tainting kernel\n", table->signature, table->oem_table_id); - add_taint(TAINT_OVERRIDDEN_ACPI_TABLE); + add_taint(TAINT_OVERRIDDEN_ACPI_TABLE, LOCKDEP_NOW_UNRELIABLE); } diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 46a213a596e2..f63c830083ec 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -267,7 +267,7 @@ static ssize_t regmap_map_write_file(struct file *file, return -EINVAL; /* Userspace has been fiddling around behind the kernel's back */ - add_taint(TAINT_USER); + add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); regmap_write(map, reg, value); return buf_size; diff --git a/include/linux/kernel.h b/include/linux/kernel.h index c566927efcbd..80d36874689b 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -398,7 +398,11 @@ extern int panic_on_unrecovered_nmi; extern int panic_on_io_nmi; extern int sysctl_panic_on_stackoverflow; extern const char *print_tainted(void); -extern void add_taint(unsigned flag); +enum lockdep_ok { + LOCKDEP_STILL_OK, + LOCKDEP_NOW_UNRELIABLE +}; +extern void add_taint(unsigned flag, enum lockdep_ok); extern int test_taint(unsigned flag); extern unsigned long get_taint(void); extern int root_mountflags; diff --git a/kernel/module.c b/kernel/module.c index e69a5a68766f..cc000dd6e4a8 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -197,9 +197,10 @@ static inline int strong_try_module_get(struct module *mod) return -ENOENT; } -static inline void add_taint_module(struct module *mod, unsigned flag) +static inline void add_taint_module(struct module *mod, unsigned flag, + enum lockdep_ok lockdep_ok) { - add_taint(flag); + add_taint(flag, lockdep_ok); mod->taints |= (1U << flag); } @@ -727,7 +728,7 @@ static inline int try_force_unload(unsigned int flags) { int ret = (flags & O_TRUNC); if (ret) - add_taint(TAINT_FORCED_RMMOD); + add_taint(TAINT_FORCED_RMMOD, LOCKDEP_NOW_UNRELIABLE); return ret; } #else @@ -1138,7 +1139,7 @@ static int try_to_force_load(struct module *mod, const char *reason) if (!test_taint(TAINT_FORCED_MODULE)) printk(KERN_WARNING "%s: %s: kernel tainted.\n", mod->name, reason); - add_taint_module(mod, TAINT_FORCED_MODULE); + add_taint_module(mod, TAINT_FORCED_MODULE, LOCKDEP_NOW_UNRELIABLE); return 0; #else return -ENOEXEC; @@ -2147,7 +2148,8 @@ static void set_license(struct module *mod, const char *license) if (!test_taint(TAINT_PROPRIETARY_MODULE)) printk(KERN_WARNING "%s: module license '%s' taints " "kernel.\n", mod->name, license); - add_taint_module(mod, TAINT_PROPRIETARY_MODULE); + add_taint_module(mod, TAINT_PROPRIETARY_MODULE, + LOCKDEP_NOW_UNRELIABLE); } } @@ -2700,10 +2702,10 @@ static int check_modinfo(struct module *mod, struct load_info *info, int flags) } if (!get_modinfo(info, "intree")) - add_taint_module(mod, TAINT_OOT_MODULE); + add_taint_module(mod, TAINT_OOT_MODULE, LOCKDEP_STILL_OK); if (get_modinfo(info, "staging")) { - add_taint_module(mod, TAINT_CRAP); + add_taint_module(mod, TAINT_CRAP, LOCKDEP_STILL_OK); printk(KERN_WARNING "%s: module is from the staging directory," " the quality is unknown, you have been warned.\n", mod->name); @@ -2869,15 +2871,17 @@ static int check_module_license_and_versions(struct module *mod) * using GPL-only symbols it needs. */ if (strcmp(mod->name, "ndiswrapper") == 0) - add_taint(TAINT_PROPRIETARY_MODULE); + add_taint(TAINT_PROPRIETARY_MODULE, LOCKDEP_NOW_UNRELIABLE); /* driverloader was caught wrongly pretending to be under GPL */ if (strcmp(mod->name, "driverloader") == 0) - add_taint_module(mod, TAINT_PROPRIETARY_MODULE); + add_taint_module(mod, TAINT_PROPRIETARY_MODULE, + LOCKDEP_NOW_UNRELIABLE); /* lve claims to be GPL but upstream won't provide source */ if (strcmp(mod->name, "lve") == 0) - add_taint_module(mod, TAINT_PROPRIETARY_MODULE); + add_taint_module(mod, TAINT_PROPRIETARY_MODULE, + LOCKDEP_NOW_UNRELIABLE); #ifdef CONFIG_MODVERSIONS if ((mod->num_syms && !mod->crcs) @@ -3197,7 +3201,7 @@ again: "%s: module verification failed: signature and/or" " required key missing - tainting kernel\n", mod->name); - add_taint_module(mod, TAINT_FORCED_MODULE); + add_taint_module(mod, TAINT_FORCED_MODULE, LOCKDEP_STILL_OK); } #endif diff --git a/kernel/panic.c b/kernel/panic.c index e1b2822fff97..7c57cc9eee2c 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -259,26 +259,19 @@ unsigned long get_taint(void) return tainted_mask; } -void add_taint(unsigned flag) +/** + * add_taint: add a taint flag if not already set. + * @flag: one of the TAINT_* constants. + * @lockdep_ok: whether lock debugging is still OK. + * + * If something bad has gone wrong, you'll want @lockdebug_ok = false, but for + * some notewortht-but-not-corrupting cases, it can be set to true. + */ +void add_taint(unsigned flag, enum lockdep_ok lockdep_ok) { - /* - * Can't trust the integrity of the kernel anymore. - * We don't call directly debug_locks_off() because the issue - * is not necessarily serious enough to set oops_in_progress to 1 - * Also we want to keep up lockdep for staging/out-of-tree - * development and post-warning case. - */ - switch (flag) { - case TAINT_CRAP: - case TAINT_OOT_MODULE: - case TAINT_WARN: - case TAINT_FIRMWARE_WORKAROUND: - break; - - default: - if (__debug_locks_off()) - printk(KERN_WARNING "Disabling lock debugging due to kernel taint\n"); - } + if (lockdep_ok == LOCKDEP_NOW_UNRELIABLE && __debug_locks_off()) + printk(KERN_WARNING + "Disabling lock debugging due to kernel taint\n"); set_bit(flag, &tainted_mask); } @@ -421,7 +414,8 @@ static void warn_slowpath_common(const char *file, int line, void *caller, print_modules(); dump_stack(); print_oops_end_marker(); - add_taint(taint); + /* Just a warning, don't kill lockdep. */ + add_taint(taint, LOCKDEP_STILL_OK); } void warn_slowpath_fmt(const char *file, int line, const char *fmt, ...) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 257002c13bb0..662f3d512183 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2785,7 +2785,7 @@ static noinline void __schedule_bug(struct task_struct *prev) if (irqs_disabled()) print_irqtrace_events(prev); dump_stack(); - add_taint(TAINT_WARN); + add_taint(TAINT_WARN, LOCKDEP_STILL_OK); } /* diff --git a/kernel/sysctl.c b/kernel/sysctl.c index c88878db491e..f97f9d75cde8 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -2006,7 +2006,7 @@ static int proc_taint(struct ctl_table *table, int write, int i; for (i = 0; i < BITS_PER_LONG && tmptaint >> i; i++) { if ((tmptaint >> i) & 1) - add_taint(i); + add_taint(i, LOCKDEP_STILL_OK); } } diff --git a/lib/bug.c b/lib/bug.c index d0cdf14c651a..168603477f02 100644 --- a/lib/bug.c +++ b/lib/bug.c @@ -166,7 +166,8 @@ enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs) print_modules(); show_regs(regs); print_oops_end_marker(); - add_taint(BUG_GET_TAINT(bug)); + /* Just a warning, don't kill lockdep. */ + add_taint(BUG_GET_TAINT(bug), LOCKDEP_STILL_OK); return BUG_TRAP_TYPE_WARN; } diff --git a/mm/memory.c b/mm/memory.c index bb1369f7b9b4..bc8bec762db7 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -716,7 +716,7 @@ static void print_bad_pte(struct vm_area_struct *vma, unsigned long addr, print_symbol(KERN_ALERT "vma->vm_file->f_op->mmap: %s\n", (unsigned long)vma->vm_file->f_op->mmap); dump_stack(); - add_taint(TAINT_BAD_PAGE); + add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE); } static inline bool is_cow_mapping(vm_flags_t flags) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index df2022ff0c8a..4c99cb7e276a 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -320,7 +320,7 @@ static void bad_page(struct page *page) out: /* Leave bad fields for debug, except PageBuddy could make trouble */ reset_page_mapcount(page); /* remove PageBuddy */ - add_taint(TAINT_BAD_PAGE); + add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE); } /* diff --git a/mm/slab.c b/mm/slab.c index e7667a3584bc..856e4a192d25 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -812,7 +812,7 @@ static void __slab_error(const char *function, struct kmem_cache *cachep, printk(KERN_ERR "slab error in %s(): cache `%s': %s\n", function, cachep->name, msg); dump_stack(); - add_taint(TAINT_BAD_PAGE); + add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE); } #endif diff --git a/mm/slub.c b/mm/slub.c index ba2ca53f6c3a..7ec3041bdd0d 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -562,7 +562,7 @@ static void slab_bug(struct kmem_cache *s, char *fmt, ...) printk(KERN_ERR "----------------------------------------" "-------------------------------------\n\n"); - add_taint(TAINT_BAD_PAGE); + add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE); } static void slab_fix(struct kmem_cache *s, char *fmt, ...) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 2370063b5824..4c6526c0db03 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -251,7 +251,7 @@ static ssize_t codec_reg_write_file(struct file *file, return -EINVAL; /* Userspace has been fiddling around behind the kernel's back */ - add_taint(TAINT_USER); + add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); snd_soc_write(codec, reg, value); return buf_size; -- cgit v1.2.3 From f69061bedd6ea63f271fe97914364def2f33fc6b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 6 Dec 2012 09:01:42 +0100 Subject: drm/i915: create a race-free reset detection With the previous patch the state transition handling of the reset code itself is now (hopefully) race free and solid. But that still leaves out everyone else - with the various lock-free wait paths we have there's the possibility that the reset happens between the point where we read the seqno we should wait on and the actual wait. And if __wait_seqno then never sees the RESET_IN_PROGRESS state, we'll happily wait for a seqno which will in all likelyhood never signal. In practice this is not a big problem since the X server gets constantly interrupted, and can then submit more work (hopefully) to unblock everyone else: As soon as a new seqno write lands, all waiters will unblock. But running the i-g-t reset testcase ZZ_hangman can expose this race, especially on slower hw with fewer cpu cores. Now looking forward to ARB_robustness and friends that's not the best possible behaviour, hence this patch adds a reset_counter to be able to detect any reset, even if a given thread never observed the in-progress state. The important part is to correctly order things: - The write side needs to increment the counter after any seqno gets reset. Hence we need to do that at the end of the reset work, and again wake everyone up. We also need to place a barrier in between any possible seqno changes and the counter increment, since any unlock operations only guarantee that nothing leaks out, but not that at later load operation gets moved ahead. - On the read side we need to ensure that no reset can sneak in and invalidate the seqno. In all cases we can use the one-sided barrier that unlock operations guarantee (of the lock protecting the respective seqno/ring pair) to ensure correct ordering. Hence it is sufficient to place the atomic read before the mutex/spin_unlock and no additional barriers are required. The end-result of all this is that we need to wake up everyone twice in a reset operation: - First, before the reset starts, to get any lockholders of the locks, so that the reset can proceed. - Second, after the reset is completed, to allow waiters to properly and reliably detect the reset condition and bail out. I admit that this entire reset_counter thing smells a bit like overkill, but I think it's justified since it makes it really explicit what the bail-out condition is. And we need a reset counter anyway to implement ARB_robustness, and imo with finer-grained locking on the horizont this is the most resilient scheme I could think of. v2: Drop spurious change in the wait_for_error EXIT_COND - we only need to wait until we leave the reset-in-progress wedged state. v3: Don't play tricks with barriers in the throttle ioctl, the spin_unlock is barrier enough. I've also considered using a little helper to grab the current reset_counter, but then decided that hiding the atomic_read isn't a great idea, since having it explicitly show up in the code is a nice remainder to reviews to check the memory barriers. v4: Add a comment to explain why we need to fall through in __wait_seqno in the end variable assignments. v5: Review from Damien: - s/smb/smp/ in a comment - don't increment the reset counter after we've set it to WEDGED. Now we (again) properly wedge the gpu when the reset fails. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 11 +++++++++-- drivers/gpu/drm/i915/i915_gem.c | 36 +++++++++++++++++++++++++++++++----- drivers/gpu/drm/i915/i915_irq.c | 30 +++++++++++++++++++++++++----- 3 files changed, 65 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c84743bb6937..56ece50910a8 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -775,9 +775,16 @@ struct i915_gpu_error { unsigned long last_reset; /** - * State variable controlling the reset flow + * State variable and reset counter controlling the reset flow * - * Upper bits are for the reset counter. + * Upper bits are for the reset counter. This counter is used by the + * wait_seqno code to race-free noticed that a reset event happened and + * that it needs to restart the entire ioctl (since most likely the + * seqno it waited for won't ever signal anytime soon). + * + * This is important for lock-free wait paths, where no contended lock + * naturally enforces the correct ordering between the bail-out of the + * waiter and the gpu reset work code. * * Lowest bit controls the reset state machine: Set means a reset is in * progress. This state will (presuming we don't have any bugs) decay diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 5bb370fdc99c..5226ebcac33a 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -976,13 +976,22 @@ i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno) * __wait_seqno - wait until execution of seqno has finished * @ring: the ring expected to report seqno * @seqno: duh! + * @reset_counter: reset sequence associated with the given seqno * @interruptible: do an interruptible wait (normally yes) * @timeout: in - how long to wait (NULL forever); out - how much time remaining * + * Note: It is of utmost importance that the passed in seqno and reset_counter + * values have been read by the caller in an smp safe manner. Where read-side + * locks are involved, it is sufficient to read the reset_counter before + * unlocking the lock that protects the seqno. For lockless tricks, the + * reset_counter _must_ be read before, and an appropriate smp_rmb must be + * inserted. + * * Returns 0 if the seqno was found within the alloted time. Else returns the * errno with remaining time filled in timeout argument. */ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, + unsigned reset_counter, bool interruptible, struct timespec *timeout) { drm_i915_private_t *dev_priv = ring->dev->dev_private; @@ -1012,7 +1021,8 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, #define EXIT_COND \ (i915_seqno_passed(ring->get_seqno(ring, false), seqno) || \ - i915_reset_in_progress(&dev_priv->gpu_error)) + i915_reset_in_progress(&dev_priv->gpu_error) || \ + reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter)) do { if (interruptible) end = wait_event_interruptible_timeout(ring->irq_queue, @@ -1022,6 +1032,13 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, end = wait_event_timeout(ring->irq_queue, EXIT_COND, timeout_jiffies); + /* We need to check whether any gpu reset happened in between + * the caller grabbing the seqno and now ... */ + if (reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter)) + end = -EAGAIN; + + /* ... but upgrade the -EGAIN to an -EIO if the gpu is truely + * gone. */ ret = i915_gem_check_wedge(&dev_priv->gpu_error, interruptible); if (ret) end = ret; @@ -1076,7 +1093,9 @@ i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno) if (ret) return ret; - return __wait_seqno(ring, seqno, interruptible, NULL); + return __wait_seqno(ring, seqno, + atomic_read(&dev_priv->gpu_error.reset_counter), + interruptible, NULL); } /** @@ -1123,6 +1142,7 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj, struct drm_device *dev = obj->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_ring_buffer *ring = obj->ring; + unsigned reset_counter; u32 seqno; int ret; @@ -1141,8 +1161,9 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj, if (ret) return ret; + reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter); mutex_unlock(&dev->struct_mutex); - ret = __wait_seqno(ring, seqno, true, NULL); + ret = __wait_seqno(ring, seqno, reset_counter, true, NULL); mutex_lock(&dev->struct_mutex); i915_gem_retire_requests_ring(ring); @@ -2297,10 +2318,12 @@ i915_gem_object_flush_active(struct drm_i915_gem_object *obj) int i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { + drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_wait *args = data; struct drm_i915_gem_object *obj; struct intel_ring_buffer *ring = NULL; struct timespec timeout_stack, *timeout = NULL; + unsigned reset_counter; u32 seqno = 0; int ret = 0; @@ -2341,9 +2364,10 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file) } drm_gem_object_unreference(&obj->base); + reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter); mutex_unlock(&dev->struct_mutex); - ret = __wait_seqno(ring, seqno, true, timeout); + ret = __wait_seqno(ring, seqno, reset_counter, true, timeout); if (timeout) { WARN_ON(!timespec_valid(timeout)); args->timeout_ns = timespec_to_ns(timeout); @@ -3394,6 +3418,7 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) unsigned long recent_enough = jiffies - msecs_to_jiffies(20); struct drm_i915_gem_request *request; struct intel_ring_buffer *ring = NULL; + unsigned reset_counter; u32 seqno = 0; int ret; @@ -3413,12 +3438,13 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) ring = request->ring; seqno = request->seqno; } + reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter); spin_unlock(&file_priv->mm.lock); if (seqno == 0) return 0; - ret = __wait_seqno(ring, seqno, true, NULL); + ret = __wait_seqno(ring, seqno, reset_counter, true, NULL); if (ret == 0) queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, 0); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 4562c5406ef8..f833f2c155f8 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -867,9 +867,11 @@ static void i915_error_work_func(struct work_struct *work) drm_i915_private_t *dev_priv = container_of(error, drm_i915_private_t, gpu_error); struct drm_device *dev = dev_priv->dev; + struct intel_ring_buffer *ring; char *error_event[] = { "ERROR=1", NULL }; char *reset_event[] = { "RESET=1", NULL }; char *reset_done_event[] = { "ERROR=0", NULL }; + int i, ret; kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event); @@ -877,13 +879,31 @@ static void i915_error_work_func(struct work_struct *work) DRM_DEBUG_DRIVER("resetting chip\n"); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event); - if (!i915_reset(dev)) { - atomic_set(&error->reset_counter, 0); - kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event); + ret = i915_reset(dev); + + if (ret == 0) { + /* + * After all the gem state is reset, increment the reset + * counter and wake up everyone waiting for the reset to + * complete. + * + * Since unlock operations are a one-sided barrier only, + * we need to insert a barrier here to order any seqno + * updates before + * the counter increment. + */ + smp_mb__before_atomic_inc(); + atomic_inc(&dev_priv->gpu_error.reset_counter); + + kobject_uevent_env(&dev->primary->kdev.kobj, + KOBJ_CHANGE, reset_done_event); } else { atomic_set(&error->reset_counter, I915_WEDGED); } + for_each_ring(ring, dev_priv, i) + wake_up_all(&ring->irq_queue); + wake_up_all(&dev_priv->gpu_error.reset_queue); } } @@ -1488,8 +1508,8 @@ void i915_handle_error(struct drm_device *dev, bool wedged) i915_report_and_clear_eir(dev); if (wedged) { - atomic_set(&dev_priv->gpu_error.reset_counter, - I915_RESET_IN_PROGRESS_FLAG); + atomic_set_mask(I915_RESET_IN_PROGRESS_FLAG, + &dev_priv->gpu_error.reset_counter); /* * Wakeup waiting processes so that the reset work item -- cgit v1.2.3 From 7db0ba242b3a7ddcfc1450bc50eb3102c68f0244 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 6 Dec 2012 16:23:37 +0100 Subject: drm/i915: clarify concurrent hang detect/gpu reset consistency Damien Lespiau wondered how race the gpu reset/hang detection code is against concurrent gpu resets/hang detections or combinations thereof. Luckily the single work item is guranteed to never run concurrently, so reset handling is already single-threaded. Hence we only have to worry about concurrent hang detections, or a hang detection firing off while we're still processing an older gpu reset request. Due to the new mechanism of setting the reset in progress flag and the ordering guaranteed by the schedule_work function there's nothing to do but add a comment explaining why we're safe. The only thing I've noticed is that we still try to reset the gpu now, even when it is declared terminally wedged. Add a check for that to avoid continous warnings about failed resets, in case the hangcheck timer ever gets stuck. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index f833f2c155f8..943db1001cfd 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -875,9 +875,20 @@ static void i915_error_work_func(struct work_struct *work) kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event); - if (i915_reset_in_progress(error)) { + /* + * Note that there's only one work item which does gpu resets, so we + * need not worry about concurrent gpu resets potentially incrementing + * error->reset_counter twice. We only need to take care of another + * racing irq/hangcheck declaring the gpu dead for a second time. A + * quick check for that is good enough: schedule_work ensures the + * correct ordering between hang detection and this work item, and since + * the reset in-progress bit is only ever set by code outside of this + * work we don't need to worry about any other races. + */ + if (i915_reset_in_progress(error) && !i915_terminally_wedged(error)) { DRM_DEBUG_DRIVER("resetting chip\n"); - kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event); + kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, + reset_event); ret = i915_reset(dev); -- cgit v1.2.3 From afe759511808cd5bb508b598007cf0c7b0ca8e08 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 15 Jan 2013 17:20:58 +0800 Subject: libata: identify and init ZPODD devices The ODD can be enabled for ZPODD if the following three conditions are satisfied: 1 The ODD supports device attention; 2 The platform can runtime power off the ODD through ACPI; 3 The ODD is either slot type or drawer type. For such ODDs, zpodd_init is called and a new structure is allocated for it to store ZPODD related stuffs. And the zpodd_dev_enabled function is used to test if ZPODD is currently enabled for this ODD. A new config CONFIG_SATA_ZPODD is added to selectively build ZPODD code. Signed-off-by: Aaron Lu Acked-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/Kconfig | 13 ++++++ drivers/ata/Makefile | 1 + drivers/ata/libata-core.c | 4 +- drivers/ata/libata-scsi.c | 2 + drivers/ata/libata-zpodd.c | 100 +++++++++++++++++++++++++++++++++++++++++++++ drivers/ata/libata.h | 14 +++++++ include/linux/libata.h | 3 ++ include/uapi/linux/cdrom.h | 34 +++++++++++++++ 8 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 drivers/ata/libata-zpodd.c (limited to 'drivers') diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index e08d322d01d7..996d16c9c6e5 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -58,6 +58,19 @@ config ATA_ACPI You can disable this at kernel boot time by using the option libata.noacpi=1 +config SATA_ZPODD + bool "SATA Zero Power ODD Support" + depends on ATA_ACPI + default n + help + This option adds support for SATA ZPODD. It requires both + ODD and the platform support, and if enabled, will automatically + power on/off the ODD when certain condition is satisfied. This + does not impact user's experience of the ODD, only power is saved + when ODD is not in use(i.e. no disc inside). + + If unsure, say N. + config SATA_PMP bool "SATA Port Multiplier support" default y diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 9329dafba91b..85e3de463ed1 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -107,3 +107,4 @@ libata-y := libata-core.o libata-scsi.o libata-eh.o libata-transport.o libata-$(CONFIG_ATA_SFF) += libata-sff.o libata-$(CONFIG_SATA_PMP) += libata-pmp.o libata-$(CONFIG_ATA_ACPI) += libata-acpi.o +libata-$(CONFIG_SATA_ZPODD) += libata-zpodd.o diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 275941b576a8..c7ecd8492f1e 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -2401,8 +2401,10 @@ int ata_dev_configure(struct ata_device *dev) dma_dir_string = ", DMADIR"; } - if (ata_id_has_da(dev->id)) + if (ata_id_has_da(dev->id)) { dev->flags |= ATA_DFLAG_DA; + zpodd_init(dev); + } /* print device info to dmesg */ if (ata_msg_drv(ap) && print_info) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 7c337e754dab..1ff018525e3b 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3755,6 +3755,8 @@ static void ata_scsi_remove_dev(struct ata_device *dev) mutex_lock(&ap->scsi_host->scan_mutex); spin_lock_irqsave(ap->lock, flags); + if (zpodd_dev_enabled(dev)) + zpodd_exit(dev); ata_acpi_unbind(dev); /* clearing dev->sdev is protected by host lock */ diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c new file mode 100644 index 000000000000..27eed2f09a8a --- /dev/null +++ b/drivers/ata/libata-zpodd.c @@ -0,0 +1,100 @@ +#include +#include + +#include "libata.h" + +enum odd_mech_type { + ODD_MECH_TYPE_SLOT, + ODD_MECH_TYPE_DRAWER, + ODD_MECH_TYPE_UNSUPPORTED, +}; + +struct zpodd { + enum odd_mech_type mech_type; /* init during probe, RO afterwards */ + struct ata_device *dev; +}; + +/* Per the spec, only slot type and drawer type ODD can be supported */ +static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev) +{ + char buf[16]; + unsigned int ret; + struct rm_feature_desc *desc = (void *)(buf + 8); + struct ata_taskfile tf = {}; + + char cdb[] = { GPCMD_GET_CONFIGURATION, + 2, /* only 1 feature descriptor requested */ + 0, 3, /* 3, removable medium feature */ + 0, 0, 0,/* reserved */ + 0, sizeof(buf), + 0, 0, 0, + }; + + tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + tf.command = ATA_CMD_PACKET; + tf.protocol = ATAPI_PROT_PIO; + tf.lbam = sizeof(buf); + + ret = ata_exec_internal(dev, &tf, cdb, DMA_FROM_DEVICE, + buf, sizeof(buf), 0); + if (ret) + return ODD_MECH_TYPE_UNSUPPORTED; + + if (be16_to_cpu(desc->feature_code) != 3) + return ODD_MECH_TYPE_UNSUPPORTED; + + if (desc->mech_type == 0 && desc->load == 0 && desc->eject == 1) + return ODD_MECH_TYPE_SLOT; + else if (desc->mech_type == 1 && desc->load == 0 && desc->eject == 1) + return ODD_MECH_TYPE_DRAWER; + else + return ODD_MECH_TYPE_UNSUPPORTED; +} + +static bool odd_can_poweroff(struct ata_device *ata_dev) +{ + acpi_handle handle; + acpi_status status; + struct acpi_device *acpi_dev; + + handle = ata_dev_acpi_handle(ata_dev); + if (!handle) + return false; + + status = acpi_bus_get_device(handle, &acpi_dev); + if (ACPI_FAILURE(status)) + return false; + + return acpi_device_can_poweroff(acpi_dev); +} + +void zpodd_init(struct ata_device *dev) +{ + enum odd_mech_type mech_type; + struct zpodd *zpodd; + + if (dev->zpodd) + return; + + if (!odd_can_poweroff(dev)) + return; + + mech_type = zpodd_get_mech_type(dev); + if (mech_type == ODD_MECH_TYPE_UNSUPPORTED) + return; + + zpodd = kzalloc(sizeof(struct zpodd), GFP_KERNEL); + if (!zpodd) + return; + + zpodd->mech_type = mech_type; + + zpodd->dev = dev; + dev->zpodd = zpodd; +} + +void zpodd_exit(struct ata_device *dev) +{ + kfree(dev->zpodd); + dev->zpodd = NULL; +} diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 7148a58020b9..a21740b4ee11 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -230,4 +230,18 @@ static inline void ata_sff_exit(void) { } #endif /* CONFIG_ATA_SFF */ +/* libata-zpodd.c */ +#ifdef CONFIG_SATA_ZPODD +void zpodd_init(struct ata_device *dev); +void zpodd_exit(struct ata_device *dev); +static inline bool zpodd_dev_enabled(struct ata_device *dev) +{ + return dev->zpodd != NULL; +} +#else /* CONFIG_SATA_ZPODD */ +static inline void zpodd_init(struct ata_device *dev) {} +static inline void zpodd_exit(struct ata_device *dev) {} +static inline bool zpodd_dev_enabled(struct ata_device *dev) { return false; } +#endif /* CONFIG_SATA_ZPODD */ + #endif /* __LIBATA_H__ */ diff --git a/include/linux/libata.h b/include/linux/libata.h index 7ae207eb29a0..65ff67e34b77 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -620,6 +620,9 @@ struct ata_device { #ifdef CONFIG_ATA_ACPI union acpi_object *gtf_cache; unsigned int gtf_filter; +#endif +#ifdef CONFIG_SATA_ZPODD + void *zpodd; #endif struct device tdev; /* n_sector is CLEAR_BEGIN, read comment above CLEAR_BEGIN */ diff --git a/include/uapi/linux/cdrom.h b/include/uapi/linux/cdrom.h index 898b866b300c..bd17ad5aa06d 100644 --- a/include/uapi/linux/cdrom.h +++ b/include/uapi/linux/cdrom.h @@ -908,5 +908,39 @@ struct mode_page_header { __be16 desc_length; }; +/* removable medium feature descriptor */ +struct rm_feature_desc { + __be16 feature_code; +#if defined(__BIG_ENDIAN_BITFIELD) + __u8 reserved1:2; + __u8 feature_version:4; + __u8 persistent:1; + __u8 curr:1; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + __u8 curr:1; + __u8 persistent:1; + __u8 feature_version:4; + __u8 reserved1:2; +#endif + __u8 add_len; +#if defined(__BIG_ENDIAN_BITFIELD) + __u8 mech_type:3; + __u8 load:1; + __u8 eject:1; + __u8 pvnt_jmpr:1; + __u8 dbml:1; + __u8 lock:1; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + __u8 lock:1; + __u8 dbml:1; + __u8 pvnt_jmpr:1; + __u8 eject:1; + __u8 load:1; + __u8 mech_type:3; +#endif + __u8 reserved2; + __u8 reserved3; + __u8 reserved4; +}; #endif /* _UAPI_LINUX_CDROM_H */ -- cgit v1.2.3 From f064a20dded807448669426c9bfb7d03aba5659c Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 15 Jan 2013 17:20:59 +0800 Subject: libata: move acpi notification code to zpodd Since the ata acpi notification code introduced in commit 3bd46600a7a7e938c54df8cdbac9910668c7dfb0 is solely for ZPODD, and we now have a dedicated place for it, move these code there. And the ata_acpi_add_pm_notifier code is changed a little bit in that it is now invoked when scsi device is not bound with ACPI yet, so the way to get the acpi handle is different with the previous version. And the ata_acpi_add/remove_pm_notifier is also simplified a little bit in that it doesn't check if the acpi_device for the handle exists or not as the odd_can_poweroff function already checked that. Signed-off-by: Aaron Lu Acked-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-acpi.c | 71 ---------------------------------------------- drivers/ata/libata-zpodd.c | 34 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 71 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index ef01ac07502e..446b4e761af0 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -974,57 +974,6 @@ void ata_acpi_on_disable(struct ata_device *dev) ata_acpi_clear_gtf(dev); } -static void ata_acpi_wake_dev(acpi_handle handle, u32 event, void *context) -{ - struct ata_device *ata_dev = context; - - if (event == ACPI_NOTIFY_DEVICE_WAKE && ata_dev && - pm_runtime_suspended(&ata_dev->sdev->sdev_gendev)) - scsi_autopm_get_device(ata_dev->sdev); -} - -static void ata_acpi_add_pm_notifier(struct ata_device *dev) -{ - struct acpi_device *acpi_dev; - acpi_handle handle; - acpi_status status; - - handle = ata_dev_acpi_handle(dev); - if (!handle) - return; - - status = acpi_bus_get_device(handle, &acpi_dev); - if (ACPI_FAILURE(status)) - return; - - if (dev->sdev->can_power_off) { - acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - ata_acpi_wake_dev, dev); - device_set_run_wake(&dev->sdev->sdev_gendev, true); - } -} - -static void ata_acpi_remove_pm_notifier(struct ata_device *dev) -{ - struct acpi_device *acpi_dev; - acpi_handle handle; - acpi_status status; - - handle = ata_dev_acpi_handle(dev); - if (!handle) - return; - - status = acpi_bus_get_device(handle, &acpi_dev); - if (ACPI_FAILURE(status)) - return; - - if (dev->sdev->can_power_off) { - device_set_run_wake(&dev->sdev->sdev_gendev, false); - acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - ata_acpi_wake_dev); - } -} - static void ata_acpi_register_power_resource(struct ata_device *dev) { struct scsi_device *sdev = dev->sdev; @@ -1057,13 +1006,11 @@ static void ata_acpi_unregister_power_resource(struct ata_device *dev) void ata_acpi_bind(struct ata_device *dev) { - ata_acpi_add_pm_notifier(dev); ata_acpi_register_power_resource(dev); } void ata_acpi_unbind(struct ata_device *dev) { - ata_acpi_remove_pm_notifier(dev); ata_acpi_unregister_power_resource(dev); } @@ -1105,9 +1052,6 @@ static int ata_acpi_bind_device(struct ata_port *ap, struct scsi_device *sdev, acpi_handle *handle) { struct ata_device *ata_dev; - acpi_status status; - struct acpi_device *acpi_dev; - struct acpi_device_power_state *states; if (ap->flags & ATA_FLAG_ACPI_SATA) { if (!sata_pmp_attached(ap)) @@ -1124,21 +1068,6 @@ static int ata_acpi_bind_device(struct ata_port *ap, struct scsi_device *sdev, if (!*handle) return -ENODEV; - status = acpi_bus_get_device(*handle, &acpi_dev); - if (ACPI_FAILURE(status)) - return 0; - - /* - * If firmware has _PS3 or _PR3 for this device, - * and this ata ODD device support device attention, - * it means this device can be powered off - */ - states = acpi_dev->power.states; - if ((states[ACPI_STATE_D3_HOT].flags.valid || - states[ACPI_STATE_D3_COLD].flags.explicit_set) && - ata_dev->flags & ATA_DFLAG_DA) - sdev->can_power_off = 1; - return 0; } diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c index 27eed2f09a8a..9a0d90d09d81 100644 --- a/drivers/ata/libata-zpodd.c +++ b/drivers/ata/libata-zpodd.c @@ -1,5 +1,7 @@ #include #include +#include +#include #include "libata.h" @@ -12,6 +14,10 @@ enum odd_mech_type { struct zpodd { enum odd_mech_type mech_type; /* init during probe, RO afterwards */ struct ata_device *dev; + + /* The following fields are synchronized by PM core. */ + bool from_notify; /* resumed as a result of + * acpi wake notification */ }; /* Per the spec, only slot type and drawer type ODD can be supported */ @@ -68,6 +74,32 @@ static bool odd_can_poweroff(struct ata_device *ata_dev) return acpi_device_can_poweroff(acpi_dev); } +static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context) +{ + struct ata_device *ata_dev = context; + struct zpodd *zpodd = ata_dev->zpodd; + struct device *dev = &ata_dev->sdev->sdev_gendev; + + if (event == ACPI_NOTIFY_DEVICE_WAKE && ata_dev && + pm_runtime_suspended(dev)) { + zpodd->from_notify = true; + pm_runtime_resume(dev); + } +} + +static void ata_acpi_add_pm_notifier(struct ata_device *dev) +{ + acpi_handle handle = ata_dev_acpi_handle(dev); + acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + zpodd_wake_dev, dev); +} + +static void ata_acpi_remove_pm_notifier(struct ata_device *dev) +{ + acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->sdev->sdev_gendev); + acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, zpodd_wake_dev); +} + void zpodd_init(struct ata_device *dev) { enum odd_mech_type mech_type; @@ -89,12 +121,14 @@ void zpodd_init(struct ata_device *dev) zpodd->mech_type = mech_type; + ata_acpi_add_pm_notifier(dev); zpodd->dev = dev; dev->zpodd = zpodd; } void zpodd_exit(struct ata_device *dev) { + ata_acpi_remove_pm_notifier(dev); kfree(dev->zpodd); dev->zpodd = NULL; } -- cgit v1.2.3 From 3dc67440d99b2c718ef5f1eb1424a9066ffa3fb9 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 15 Jan 2013 17:21:00 +0800 Subject: libata: check zero power ready status for ZPODD Per the Mount Fuji spec, the ODD is considered zero power ready when: - For slot type ODD, no media inside; - For tray type ODD, no media inside and tray closed. The information can be retrieved by either the returned information of command GET_EVENT_STATUS_NOTIFICATION(the command is used to poll for media event) or sense code. The information provided by the media status byte is not accurate, it is possible that after a new disc is just inserted, the status byte still returns media not present. So this information can not be used as the deciding factor, we use sense code to decide if zpready status is true. When we first sensed the ODD in the zero power ready state, the zp_sampled will be set and timestamp will be recoreded. And after ODD stayed in this state for some pre-defined period, the ODD is considered as power off ready and the zp_ready flag will be set. The zp_ready flag serves as the deciding factor other code will use to see if power off is OK for the ODD. The Mount Fuji spec suggests a delay should be used here, to avoid the case user ejects the ODD and then instantly inserts a new one again, so that we can avoid a power transition. And some ODDs may be slow to place its head to the home position after disc is ejected, so a delay here is generally a good idea. And the delay time can be changed via the module param zpodd_poweroff_delay. The zero power ready status check is performed in the ata port's runtime suspend code path, when port is not frozen yet, as we need to issue some IOs to the ODD. Signed-off-by: Aaron Lu Signed-off-by: Jeff Garzik --- drivers/ata/libata-eh.c | 14 +++++++-- drivers/ata/libata-zpodd.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/ata/libata.h | 5 ++++ 3 files changed, 92 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index bcf4437214f5..a0dddc3b4924 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1591,7 +1591,7 @@ static int ata_eh_read_log_10h(struct ata_device *dev, * RETURNS: * 0 on success, AC_ERR_* mask on failure. */ -static unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key) +unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key) { u8 cdb[ATAPI_CDB_LEN] = { TEST_UNIT_READY, 0, 0, 0, 0, 0 }; struct ata_taskfile tf; @@ -1624,7 +1624,7 @@ static unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key) * RETURNS: * 0 on success, AC_ERR_* mask on failure */ -static unsigned int atapi_eh_request_sense(struct ata_device *dev, +unsigned int atapi_eh_request_sense(struct ata_device *dev, u8 *sense_buf, u8 dfl_sense_key) { u8 cdb[ATAPI_CDB_LEN] = @@ -4022,6 +4022,7 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap) { unsigned long flags; int rc = 0; + struct ata_device *dev; /* are we suspending? */ spin_lock_irqsave(ap->lock, flags); @@ -4034,6 +4035,15 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap) WARN_ON(ap->pflags & ATA_PFLAG_SUSPENDED); + /* + * If we have a ZPODD attached, check its zero + * power ready status before the port is frozen. + */ + ata_for_each_dev(dev, &ap->link, ENABLED) { + if (zpodd_dev_enabled(dev)) + zpodd_on_suspend(dev); + } + /* tell ACPI we're suspending */ rc = ata_acpi_on_suspend(ap); if (rc) diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c index 9a0d90d09d81..71dd48c94f2c 100644 --- a/drivers/ata/libata-zpodd.c +++ b/drivers/ata/libata-zpodd.c @@ -1,10 +1,15 @@ #include #include #include +#include #include #include "libata.h" +static int zpodd_poweroff_delay = 30; /* 30 seconds for power off delay */ +module_param(zpodd_poweroff_delay, int, 0644); +MODULE_PARM_DESC(zpodd_poweroff_delay, "Poweroff delay for ZPODD in seconds"); + enum odd_mech_type { ODD_MECH_TYPE_SLOT, ODD_MECH_TYPE_DRAWER, @@ -18,6 +23,9 @@ struct zpodd { /* The following fields are synchronized by PM core. */ bool from_notify; /* resumed as a result of * acpi wake notification */ + bool zp_ready; /* ZP ready state */ + unsigned long last_ready; /* last ZP ready timestamp */ + bool zp_sampled; /* ZP ready state sampled */ }; /* Per the spec, only slot type and drawer type ODD can be supported */ @@ -74,6 +82,73 @@ static bool odd_can_poweroff(struct ata_device *ata_dev) return acpi_device_can_poweroff(acpi_dev); } +/* Test if ODD is zero power ready by sense code */ +static bool zpready(struct ata_device *dev) +{ + u8 sense_key, *sense_buf; + unsigned int ret, asc, ascq, add_len; + struct zpodd *zpodd = dev->zpodd; + + ret = atapi_eh_tur(dev, &sense_key); + + if (!ret || sense_key != NOT_READY) + return false; + + sense_buf = dev->link->ap->sector_buf; + ret = atapi_eh_request_sense(dev, sense_buf, sense_key); + if (ret) + return false; + + /* sense valid */ + if ((sense_buf[0] & 0x7f) != 0x70) + return false; + + add_len = sense_buf[7]; + /* has asc and ascq */ + if (add_len < 6) + return false; + + asc = sense_buf[12]; + ascq = sense_buf[13]; + + if (zpodd->mech_type == ODD_MECH_TYPE_SLOT) + /* no media inside */ + return asc == 0x3a; + else + /* no media inside and door closed */ + return asc == 0x3a && ascq == 0x01; +} + +/* + * Update the zpodd->zp_ready field. This field will only be set + * if the ODD has stayed in ZP ready state for zpodd_poweroff_delay + * time, and will be used to decide if power off is allowed. If it + * is set, it will be cleared during resume from powered off state. + */ +void zpodd_on_suspend(struct ata_device *dev) +{ + struct zpodd *zpodd = dev->zpodd; + unsigned long expires; + + if (!zpready(dev)) { + zpodd->zp_sampled = false; + return; + } + + if (!zpodd->zp_sampled) { + zpodd->zp_sampled = true; + zpodd->last_ready = jiffies; + return; + } + + expires = zpodd->last_ready + + msecs_to_jiffies(zpodd_poweroff_delay * 1000); + if (time_before(jiffies, expires)) + return; + + zpodd->zp_ready = true; +} + static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context) { struct ata_device *ata_dev = context; diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index a21740b4ee11..b9b2bb1d5dc6 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -182,6 +182,9 @@ extern void ata_eh_finish(struct ata_port *ap); extern int ata_ering_map(struct ata_ering *ering, int (*map_fn)(struct ata_ering_entry *, void *), void *arg); +extern unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key); +extern unsigned int atapi_eh_request_sense(struct ata_device *dev, + u8 *sense_buf, u8 dfl_sense_key); /* libata-pmp.c */ #ifdef CONFIG_SATA_PMP @@ -238,10 +241,12 @@ static inline bool zpodd_dev_enabled(struct ata_device *dev) { return dev->zpodd != NULL; } +void zpodd_on_suspend(struct ata_device *dev); #else /* CONFIG_SATA_ZPODD */ static inline void zpodd_init(struct ata_device *dev) {} static inline void zpodd_exit(struct ata_device *dev) {} static inline bool zpodd_dev_enabled(struct ata_device *dev) { return false; } +static inline void zpodd_on_suspend(struct ata_device *dev) {} #endif /* CONFIG_SATA_ZPODD */ #endif /* __LIBATA_H__ */ -- cgit v1.2.3 From 213342053db58eabdaddff9c036c2b81ca63c443 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 15 Jan 2013 17:21:01 +0800 Subject: libata: handle power transition of ODD When ata port is runtime suspended, it will check if the ODD attched to it is a zero power(ZP) capable ODD and if the ZP capable ODD is in zero power ready state. And if this is not the case, the highest acpi state will be limited to ACPI_STATE_D3_HOT to avoid powering off the ODD. And if the ODD can be powered off, runtime wake capability needs to be enabled and powered_off flag will be set to let resume code knows that the ODD was in powered off state. And on resume, before it is powered on, if it was powered off during suspend, runtime wake capability needs to be disabled. After it is recovered, the ODD is considered functional, post power on processing like eject tray if the ODD is drawer type is done, and several ZPODD related fields will also be reset. Signed-off-by: Aaron Lu Acked-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-acpi.c | 35 +++++++++++++------ drivers/ata/libata-eh.c | 2 ++ drivers/ata/libata-zpodd.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/ata/libata.h | 8 +++++ 4 files changed, 118 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index 446b4e761af0..6f72c648ea14 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -835,6 +835,22 @@ void ata_acpi_on_resume(struct ata_port *ap) } } +static int ata_acpi_choose_suspend_state(struct ata_device *dev) +{ + int d_max_in = ACPI_STATE_D3_COLD; + + /* + * For ATAPI, runtime D3 cold is only allowed + * for ZPODD in zero power ready state + */ + if (dev->class == ATA_DEV_ATAPI && + !(zpodd_dev_enabled(dev) && zpodd_zpready(dev))) + d_max_in = ACPI_STATE_D3_HOT; + + return acpi_pm_device_sleep_state(&dev->sdev->sdev_gendev, + NULL, d_max_in); +} + /** * ata_acpi_set_state - set the port power state * @ap: target ATA port @@ -861,17 +877,16 @@ void ata_acpi_set_state(struct ata_port *ap, pm_message_t state) continue; if (state.event != PM_EVENT_ON) { - acpi_state = acpi_pm_device_sleep_state( - &dev->sdev->sdev_gendev, NULL, ACPI_STATE_D3); - if (acpi_state > 0) - acpi_bus_set_power(handle, acpi_state); - /* TBD: need to check if it's runtime pm request */ - acpi_pm_device_run_wake( - &dev->sdev->sdev_gendev, true); + acpi_state = ata_acpi_choose_suspend_state(dev); + if (acpi_state == ACPI_STATE_D0) + continue; + if (zpodd_dev_enabled(dev) && + acpi_state == ACPI_STATE_D3_COLD) + zpodd_enable_run_wake(dev); + acpi_bus_set_power(handle, acpi_state); } else { - /* Ditto */ - acpi_pm_device_run_wake( - &dev->sdev->sdev_gendev, false); + if (zpodd_dev_enabled(dev)) + zpodd_disable_run_wake(dev); acpi_bus_set_power(handle, ACPI_STATE_D0); } } diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index a0dddc3b4924..50f3ef04809d 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -3857,6 +3857,8 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, rc = atapi_eh_clear_ua(dev); if (rc) goto rest_fail; + if (zpodd_dev_enabled(dev)) + zpodd_post_poweron(dev); } } diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c index 71dd48c94f2c..1f5d52ae3974 100644 --- a/drivers/ata/libata-zpodd.c +++ b/drivers/ata/libata-zpodd.c @@ -26,8 +26,26 @@ struct zpodd { bool zp_ready; /* ZP ready state */ unsigned long last_ready; /* last ZP ready timestamp */ bool zp_sampled; /* ZP ready state sampled */ + bool powered_off; /* ODD is powered off + * during suspend */ }; +static int eject_tray(struct ata_device *dev) +{ + struct ata_taskfile tf = {}; + const char cdb[] = { GPCMD_START_STOP_UNIT, + 0, 0, 0, + 0x02, /* LoEj */ + 0, 0, 0, 0, 0, 0, 0, + }; + + tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + tf.command = ATA_CMD_PACKET; + tf.protocol = ATAPI_PROT_NODATA; + + return ata_exec_internal(dev, &tf, cdb, DMA_NONE, NULL, 0, 0); +} + /* Per the spec, only slot type and drawer type ODD can be supported */ static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev) { @@ -149,6 +167,71 @@ void zpodd_on_suspend(struct ata_device *dev) zpodd->zp_ready = true; } +bool zpodd_zpready(struct ata_device *dev) +{ + struct zpodd *zpodd = dev->zpodd; + return zpodd->zp_ready; +} + +/* + * Enable runtime wake capability through ACPI and set the powered_off flag, + * this flag will be used during resume to decide what operations are needed + * to take. + */ +void zpodd_enable_run_wake(struct ata_device *dev) +{ + struct zpodd *zpodd = dev->zpodd; + + zpodd->powered_off = true; + device_set_run_wake(&dev->sdev->sdev_gendev, true); + acpi_pm_device_run_wake(&dev->sdev->sdev_gendev, true); +} + +/* Disable runtime wake capability if it is enabled */ +void zpodd_disable_run_wake(struct ata_device *dev) +{ + struct zpodd *zpodd = dev->zpodd; + + if (zpodd->powered_off) { + acpi_pm_device_run_wake(&dev->sdev->sdev_gendev, false); + device_set_run_wake(&dev->sdev->sdev_gendev, false); + } +} + +/* + * Post power on processing after the ODD has been recovered. If the + * ODD wasn't powered off during suspend, it doesn't do anything. + * + * For drawer type ODD, if it is powered on due to user pressed the + * eject button, the tray needs to be ejected. This can only be done + * after the ODD has been recovered, i.e. link is initialized and + * device is able to process NON_DATA PIO command, as eject needs to + * send command for the ODD to process. + * + * The from_notify flag set in wake notification handler function + * zpodd_wake_dev represents if power on is due to user's action. + * + * For both types of ODD, several fields need to be reset. + */ +void zpodd_post_poweron(struct ata_device *dev) +{ + struct zpodd *zpodd = dev->zpodd; + + if (!zpodd->powered_off) + return; + + zpodd->powered_off = false; + + if (zpodd->from_notify) { + zpodd->from_notify = false; + if (zpodd->mech_type == ODD_MECH_TYPE_DRAWER) + eject_tray(dev); + } + + zpodd->zp_sampled = false; + zpodd->zp_ready = false; +} + static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context) { struct ata_device *ata_dev = context; diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index b9b2bb1d5dc6..c949dd311b2e 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -242,11 +242,19 @@ static inline bool zpodd_dev_enabled(struct ata_device *dev) return dev->zpodd != NULL; } void zpodd_on_suspend(struct ata_device *dev); +bool zpodd_zpready(struct ata_device *dev); +void zpodd_enable_run_wake(struct ata_device *dev); +void zpodd_disable_run_wake(struct ata_device *dev); +void zpodd_post_poweron(struct ata_device *dev); #else /* CONFIG_SATA_ZPODD */ static inline void zpodd_init(struct ata_device *dev) {} static inline void zpodd_exit(struct ata_device *dev) {} static inline bool zpodd_dev_enabled(struct ata_device *dev) { return false; } static inline void zpodd_on_suspend(struct ata_device *dev) {} +static inline bool zpodd_zpready(struct ata_device *dev) { return false; } +static inline void zpodd_enable_run_wake(struct ata_device *dev) {} +static inline void zpodd_disable_run_wake(struct ata_device *dev) {} +static inline void zpodd_post_poweron(struct ata_device *dev) {} #endif /* CONFIG_SATA_ZPODD */ #endif /* __LIBATA_H__ */ -- cgit v1.2.3 From a59b9aae230316134597775c6202cf28f6da0333 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 15 Jan 2013 17:21:02 +0800 Subject: libata: expose pm qos flags for ata device Expose pm qos flags to user space so that user has a chance to disable ZPODD feature, if he/she has a broken platform or devices or simply does not like this feature. This flag is exposed to user space only for ZPODD devices. Due to this flag, it is possible the ODD is ZP ready but we didn't power it off. So the zp_ready flag will need to be cleared whenever we found the ODD is not in ZP ready state. Previously, once zp_ready is set, the ODD will always be powered off and the flag will be cleared in post_poweron. But this is no longer the case now. Signed-off-by: Aaron Lu Signed-off-by: Jeff Garzik --- drivers/ata/libata-acpi.c | 3 +++ drivers/ata/libata-zpodd.c | 1 + 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index 6f72c648ea14..97094496127e 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "libata.h" @@ -1022,6 +1023,8 @@ static void ata_acpi_unregister_power_resource(struct ata_device *dev) void ata_acpi_bind(struct ata_device *dev) { ata_acpi_register_power_resource(dev); + if (zpodd_dev_enabled(dev)) + dev_pm_qos_expose_flags(&dev->sdev->sdev_gendev, 0); } void ata_acpi_unbind(struct ata_device *dev) diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c index 1f5d52ae3974..540b0b7904fb 100644 --- a/drivers/ata/libata-zpodd.c +++ b/drivers/ata/libata-zpodd.c @@ -150,6 +150,7 @@ void zpodd_on_suspend(struct ata_device *dev) if (!zpready(dev)) { zpodd->zp_sampled = false; + zpodd->zp_ready = false; return; } -- cgit v1.2.3 From 7e15e9be37eb834aaaca69030064ac97eaf5df2f Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 15 Jan 2013 17:21:04 +0800 Subject: libata: do not suspend port if normal ODD is attached For ODDs, the upper layer will poll for media change every few seconds, which will make it enter and leave suspend state very often. And as each suspend will also cause a hard/soft reset, the gain of runtime suspend is very little while the ODD may malfunction after constantly being reset. So the idle callback here will not proceed to suspend if a non-ZPODD capable ODD is attached to the port. Signed-off-by: Aaron Lu Acked-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index c7ecd8492f1e..b7eed827daae 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5413,8 +5413,27 @@ static int ata_port_resume(struct device *dev) return rc; } +/* + * For ODDs, the upper layer will poll for media change every few seconds, + * which will make it enter and leave suspend state every few seconds. And + * as each suspend will cause a hard/soft reset, the gain of runtime suspend + * is very little and the ODD may malfunction after constantly being reset. + * So the idle callback here will not proceed to suspend if a non-ZPODD capable + * ODD is attached to the port. + */ static int ata_port_runtime_idle(struct device *dev) { + struct ata_port *ap = to_ata_port(dev); + struct ata_link *link; + struct ata_device *adev; + + ata_for_each_link(link, ap, HOST_FIRST) { + ata_for_each_dev(adev, link, ENABLED) + if (adev->class == ATA_DEV_ATAPI && + !zpodd_dev_enabled(adev)) + return -EBUSY; + } + return pm_runtime_suspend(dev); } -- cgit v1.2.3 From 1f8051876a194d7f7fe7834d9853f240d6b4b9ab Mon Sep 17 00:00:00 2001 From: Sjur Brændeland Date: Tue, 22 Jan 2013 09:51:20 +1030 Subject: virtio_console: Let unconnected rproc device receive data. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow rproc serial ports to receive data before the port is connected. Rproc serial ports usually talk to very simple remote devices with no control queue managing open/close events. So we must let remote devices write to the virtio ring even if the device is not yet fully initialized. Signed-off-by: Sjur Brændeland Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 684b0d53764f..95cae778bd73 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1763,8 +1763,11 @@ static void in_intr(struct virtqueue *vq) * tty is spawned) and the host sends out data to console * ports. For generic serial ports, the host won't * (shouldn't) send data till the guest is connected. + * However a remote device might send data before the port is + * connected. So don't remove data from a rproc_serial device. */ - if (!port->guest_connected) + + if (!port->guest_connected && !is_rproc_serial(port->portdev->vdev)) discard_port_data(port); spin_unlock_irqrestore(&port->inbuf_lock, flags); -- cgit v1.2.3 From 6a89a314ab107a12af08c71420c19a37a30fc2d3 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 18 Jan 2013 15:57:46 +0800 Subject: gpio: devm_gpio_* support should not depend on GPIOLIB Some architectures (e.g. blackfin) provide gpio API without requiring GPIOLIB support (ARCH_WANT_OPTIONAL_GPIOLIB). devm_gpio_* functions should also work for these architectures, since they do not really depend on GPIOLIB. Add a new option GPIO_DEVRES (enabled by default) to control the build of devres.c. It also removes the empty version of devm_gpio_* functions for !GENERIC_GPIO build from linux/gpio.h, and moves the function declarations from asm-generic/gpio.h into linux/gpio.h. Signed-off-by: Shawn Guo Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 3 +++ drivers/gpio/Makefile | 3 ++- include/asm-generic/gpio.h | 6 ------ include/linux/gpio.h | 28 ++++++++-------------------- 4 files changed, 13 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 682de754d63f..d9729322b1c7 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -30,6 +30,9 @@ config ARCH_REQUIRE_GPIOLIB Selecting this from the architecture code will cause the gpiolib code to always get built in. +config GPIO_DEVRES + def_bool y + depends on HAS_IOMEM menuconfig GPIOLIB diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index c5aebd008dde..36ca605ff038 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -2,7 +2,8 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG -obj-$(CONFIG_GPIOLIB) += gpiolib.o devres.o +obj-$(CONFIG_GPIO_DEVRES) += devres.o +obj-$(CONFIG_GPIOLIB) += gpiolib.o obj-$(CONFIG_OF_GPIO) += gpiolib-of.o obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 23410147555f..45b8916805f3 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -192,12 +192,6 @@ extern int gpio_request_one(unsigned gpio, unsigned long flags, const char *labe extern int gpio_request_array(const struct gpio *array, size_t num); extern void gpio_free_array(const struct gpio *array, size_t num); -/* bindings for managed devices that want to request gpios */ -int devm_gpio_request(struct device *dev, unsigned gpio, const char *label); -int devm_gpio_request_one(struct device *dev, unsigned gpio, - unsigned long flags, const char *label); -void devm_gpio_free(struct device *dev, unsigned int gpio); - #ifdef CONFIG_GPIO_SYSFS /* diff --git a/include/linux/gpio.h b/include/linux/gpio.h index bfe665621536..f6c7ae3e223b 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -94,24 +94,12 @@ static inline int gpio_request(unsigned gpio, const char *label) return -ENOSYS; } -static inline int devm_gpio_request(struct device *dev, unsigned gpio, - const char *label) -{ - return -ENOSYS; -} - static inline int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) { return -ENOSYS; } -static inline int devm_gpio_request_one(struct device *dev, unsigned gpio, - unsigned long flags, const char *label) -{ - return -ENOSYS; -} - static inline int gpio_request_array(const struct gpio *array, size_t num) { return -ENOSYS; @@ -125,14 +113,6 @@ static inline void gpio_free(unsigned gpio) WARN_ON(1); } -static inline void devm_gpio_free(struct device *dev, unsigned gpio) -{ - might_sleep(); - - /* GPIO can never have been requested */ - WARN_ON(1); -} - static inline void gpio_free_array(const struct gpio *array, size_t num) { might_sleep(); @@ -248,4 +228,12 @@ gpiochip_remove_pin_ranges(struct gpio_chip *chip) #endif /* ! CONFIG_GENERIC_GPIO */ +struct device; + +/* bindings for managed devices that want to request gpios */ +int devm_gpio_request(struct device *dev, unsigned gpio, const char *label); +int devm_gpio_request_one(struct device *dev, unsigned gpio, + unsigned long flags, const char *label); +void devm_gpio_free(struct device *dev, unsigned int gpio); + #endif /* __LINUX_GPIO_H */ -- cgit v1.2.3 From 5985d76cc1b62125301754c9b636a18c346bfc52 Mon Sep 17 00:00:00 2001 From: Haojian Zhuang Date: Fri, 18 Jan 2013 15:31:13 +0800 Subject: gpio: pl061: set initcall level to module init Replace subsys initcall by module initcall level. Since pinctrl driver is already launched before gpio driver. It's unnecessary to set gpio driver in subsys init call level. Signed-off-by: Haojian Zhuang Tested-by: Dinh Nguyen Signed-off-by: Linus Walleij --- drivers/gpio/gpio-pl061.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index c1720de18a4f..b820869ca93c 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -365,7 +365,7 @@ static int __init pl061_gpio_init(void) { return amba_driver_register(&pl061_gpio_driver); } -subsys_initcall(pl061_gpio_init); +module_init(pl061_gpio_init); MODULE_AUTHOR("Baruch Siach "); MODULE_DESCRIPTION("PL061 GPIO driver"); -- cgit v1.2.3 From a77cfcac79c7b171d344e2bc0f05c075bc1fcfb2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 14 Jan 2013 09:26:09 -0300 Subject: [media] mb86a20s: improve error handling at get_frontend The read/write errors are not handled well on get_frontend. Fix it, by letting the frontend cached values to represent the DVB properties that were successfully retrieved. While here, use "c" for dtv_frontend_properties cache, instead of "p", as this is more common. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 138 +++++++++++++++++++-------------- 1 file changed, 80 insertions(+), 58 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index fade566927c3..4ff3a0c9d977 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -1,11 +1,9 @@ /* * Fujitu mb86a20s ISDB-T/ISDB-Tsb Module driver * - * Copyright (C) 2010 Mauro Carvalho Chehab + * Copyright (C) 2010-2013 Mauro Carvalho Chehab * Copyright (C) 2009-2010 Douglas Landgraf * - * FIXME: Need to port to DVB v5.2 API - * * 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 version 2. @@ -360,7 +358,7 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe) /* * FIXME: Properly implement the set frontend properties */ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; #endif dprintk("\n"); @@ -507,93 +505,117 @@ static int mb86a20s_get_segment_count(struct mb86a20s_state *state, return count; } +static void mb86a20s_reset_frontend_cache(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + /* Fixed parameters */ + c->delivery_system = SYS_ISDBT; + c->bandwidth_hz = 6000000; + + /* Initialize values that will be later autodetected */ + c->isdbt_layer_enabled = 0; + c->transmission_mode = TRANSMISSION_MODE_AUTO; + c->guard_interval = GUARD_INTERVAL_AUTO; + c->isdbt_sb_mode = 0; + c->isdbt_sb_segment_count = 0; +} + static int mb86a20s_get_frontend(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; int i, rc; - /* Fixed parameters */ - p->delivery_system = SYS_ISDBT; - p->bandwidth_hz = 6000000; + /* Reset frontend cache to default values */ + mb86a20s_reset_frontend_cache(fe); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); /* Check for partial reception */ rc = mb86a20s_writereg(state, 0x6d, 0x85); - if (rc >= 0) - rc = mb86a20s_readreg(state, 0x6e); - if (rc >= 0) - p->isdbt_partial_reception = (rc & 0x10) ? 1 : 0; + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x6e); + if (rc < 0) + return rc; + c->isdbt_partial_reception = (rc & 0x10) ? 1 : 0; /* Get per-layer data */ - p->isdbt_layer_enabled = 0; + for (i = 0; i < 3; i++) { rc = mb86a20s_get_segment_count(state, i); - if (rc >= 0 && rc < 14) - p->layer[i].segment_count = rc; - if (rc == 0x0f) + if (rc < 0) + goto error; + if (rc >= 0 && rc < 14) + c->layer[i].segment_count = rc; + else { + c->layer[i].segment_count = 0; continue; - p->isdbt_layer_enabled |= 1 << i; + } + c->isdbt_layer_enabled |= 1 << i; rc = mb86a20s_get_modulation(state, i); - if (rc >= 0) - p->layer[i].modulation = rc; + if (rc < 0) + goto error; + c->layer[i].modulation = rc; rc = mb86a20s_get_fec(state, i); - if (rc >= 0) - p->layer[i].fec = rc; + if (rc < 0) + goto error; + c->layer[i].fec = rc; rc = mb86a20s_get_interleaving(state, i); - if (rc >= 0) - p->layer[i].interleaving = rc; + if (rc < 0) + goto error; + c->layer[i].interleaving = rc; } - p->isdbt_sb_mode = 0; rc = mb86a20s_writereg(state, 0x6d, 0x84); - if ((rc >= 0) && ((rc & 0x60) == 0x20)) { - p->isdbt_sb_mode = 1; + if (rc < 0) + return rc; + if ((rc & 0x60) == 0x20) { + c->isdbt_sb_mode = 1; /* At least, one segment should exist */ - if (!p->isdbt_sb_segment_count) - p->isdbt_sb_segment_count = 1; - } else - p->isdbt_sb_segment_count = 0; + if (!c->isdbt_sb_segment_count) + c->isdbt_sb_segment_count = 1; + } /* Get transmission mode and guard interval */ - p->transmission_mode = TRANSMISSION_MODE_AUTO; - p->guard_interval = GUARD_INTERVAL_AUTO; rc = mb86a20s_readreg(state, 0x07); - if (rc >= 0) { - if ((rc & 0x60) == 0x20) { - switch (rc & 0x0c >> 2) { - case 0: - p->transmission_mode = TRANSMISSION_MODE_2K; - break; - case 1: - p->transmission_mode = TRANSMISSION_MODE_4K; - break; - case 2: - p->transmission_mode = TRANSMISSION_MODE_8K; - break; - } + if (rc < 0) + return rc; + if ((rc & 0x60) == 0x20) { + switch (rc & 0x0c >> 2) { + case 0: + c->transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + c->transmission_mode = TRANSMISSION_MODE_4K; + break; + case 2: + c->transmission_mode = TRANSMISSION_MODE_8K; + break; } - if (!(rc & 0x10)) { - switch (rc & 0x3) { - case 0: - p->guard_interval = GUARD_INTERVAL_1_4; - break; - case 1: - p->guard_interval = GUARD_INTERVAL_1_8; - break; - case 2: - p->guard_interval = GUARD_INTERVAL_1_16; - break; - } + } + if (!(rc & 0x10)) { + switch (rc & 0x3) { + case 0: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + case 1: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case 2: + c->guard_interval = GUARD_INTERVAL_1_16; + break; } } +error: if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - return 0; + return rc; + } static int mb86a20s_tune(struct dvb_frontend *fe, -- cgit v1.2.3 From fd53744efea8bf845dc54bd3095be6203b1b07a1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 14 Jan 2013 10:16:07 -0300 Subject: [media] mb86a20s: Fix i2c gate on error If an error happens, restore tuner I2C gate to the right value. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 4ff3a0c9d977..3c8587e38a05 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -262,10 +262,10 @@ static int mb86a20s_initfe(struct dvb_frontend *fe) goto err; } +err: if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); -err: if (rc < 0) { state->need_init = true; printk(KERN_INFO "mb86a20s: Init failed. Will try again later\n"); @@ -363,6 +363,10 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe) dprintk("\n"); + /* + * Gate should already be opened, but it doesn't hurt to + * double-check + */ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); dprintk("Calling tuner set parameters\n"); -- cgit v1.2.3 From ce77d120ed24d44aa020bde61f32bbdabb9ed596 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 14 Jan 2013 14:12:10 -0300 Subject: [media] mb86a20s: make AGC work better It is recommented to change register 0x0440 value to 0, in order to fix some AGC bug. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 3c8587e38a05..40c61838c61d 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -126,7 +126,8 @@ static struct regdata mb86a20s_init[] = { { 0x50, 0xd7 }, { 0x51, 0x3f }, { 0x28, 0x74 }, { 0x29, 0x00 }, { 0x28, 0x74 }, { 0x29, 0x40 }, { 0x28, 0x46 }, { 0x29, 0x2c }, { 0x28, 0x46 }, { 0x29, 0x0c }, - { 0x04, 0x40 }, { 0x05, 0x01 }, + + { 0x04, 0x40 }, { 0x05, 0x00 }, { 0x28, 0x00 }, { 0x29, 0x10 }, { 0x28, 0x05 }, { 0x29, 0x02 }, { 0x1c, 0x01 }, -- cgit v1.2.3 From 04585921ac0fa0f4baaf510cc7e52e3399018fb4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 14 Jan 2013 12:31:13 -0300 Subject: [media] mb86a20s: fix interleaving and FEC retrival Get the proper bits from the TMCC table registers. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 40c61838c61d..8f4fff1f0fa5 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -413,7 +413,7 @@ static int mb86a20s_get_modulation(struct mb86a20s_state *state, rc = mb86a20s_readreg(state, 0x6e); if (rc < 0) return rc; - switch ((rc & 0x70) >> 4) { + switch ((rc >> 4) & 0x07) { case 0: return DQPSK; case 1: @@ -446,7 +446,7 @@ static int mb86a20s_get_fec(struct mb86a20s_state *state, rc = mb86a20s_readreg(state, 0x6e); if (rc < 0) return rc; - switch (rc) { + switch ((rc >> 4) & 0x07) { case 0: return FEC_1_2; case 1: @@ -481,9 +481,21 @@ static int mb86a20s_get_interleaving(struct mb86a20s_state *state, rc = mb86a20s_readreg(state, 0x6e); if (rc < 0) return rc; - if (rc > 3) - return -EINVAL; /* Not used */ - return rc; + + switch ((rc >> 4) & 0x07) { + case 1: + return GUARD_INTERVAL_1_4; + case 2: + return GUARD_INTERVAL_1_8; + case 3: + return GUARD_INTERVAL_1_16; + case 4: + return GUARD_INTERVAL_1_32; + + default: + case 0: + return GUARD_INTERVAL_AUTO; + } } static int mb86a20s_get_segment_count(struct mb86a20s_state *state, -- cgit v1.2.3 From d36e418a7b1eaeb006ee304533054e2720537db7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 22 Jan 2013 08:49:39 -0200 Subject: [media] mb86a20s: Split status read logic from DVB callback Split the logic that reads the status from the DVB callback. That helps to properly return an error code, if status read fails. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 8f4fff1f0fa5..03b74d3afd8f 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -320,16 +320,14 @@ static int mb86a20s_read_signal_strength(struct dvb_frontend *fe, u16 *strength) static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct mb86a20s_state *state = fe->demodulator_priv; - u8 val; + int val; dprintk("\n"); *status = 0; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); val = mb86a20s_readreg(state, 0x0a) & 0xf; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); + if (val < 0) + return val; if (val >= 2) *status |= FE_HAS_SIGNAL; @@ -635,6 +633,25 @@ error: } +static int mb86a20s_read_status_gate(struct dvb_frontend *fe, + fe_status_t *status) +{ + int ret; + + dprintk("\n"); + *status = 0; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + ret = mb86a20s_read_status(fe, status); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + return ret; +} + static int mb86a20s_tune(struct dvb_frontend *fe, bool re_tune, unsigned int mode_flags, @@ -649,7 +666,7 @@ static int mb86a20s_tune(struct dvb_frontend *fe, rc = mb86a20s_set_frontend(fe); if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) - mb86a20s_read_status(fe, status); + mb86a20s_read_status_gate(fe, status); return rc; } @@ -730,7 +747,7 @@ static struct dvb_frontend_ops mb86a20s_ops = { .init = mb86a20s_initfe, .set_frontend = mb86a20s_set_frontend, .get_frontend = mb86a20s_get_frontend, - .read_status = mb86a20s_read_status, + .read_status = mb86a20s_read_status_gate, .read_signal_strength = mb86a20s_read_signal_strength, .tune = mb86a20s_tune, }; -- cgit v1.2.3 From dd4493ef34cb4062d59d87717aaf8a1c27d450c9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 22 Jan 2013 08:53:11 -0200 Subject: [media] mb86a20s: Function reorder Reorder functions to have everything related to stats/status read close. That will make the file more organized as other stats routines will be added. No functional changes. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 215 +++++++++++++++++---------------- 1 file changed, 110 insertions(+), 105 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 03b74d3afd8f..b348f97aa7b5 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -175,6 +175,10 @@ static struct regdata mb86a20s_reset_reception[] = { { 0x08, 0x00 }, }; +/* + * I2C read/write functions and macros + */ + static int mb86a20s_i2c_writereg(struct mb86a20s_state *state, u8 i2c_addr, int reg, int data) { @@ -236,45 +240,36 @@ static int mb86a20s_i2c_readreg(struct mb86a20s_state *state, mb86a20s_i2c_writeregdata(state, state->config->demod_address, \ regdata, ARRAY_SIZE(regdata)) -static int mb86a20s_initfe(struct dvb_frontend *fe) +static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct mb86a20s_state *state = fe->demodulator_priv; - int rc; - u8 regD5 = 1; + int val; dprintk("\n"); + *status = 0; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); + val = mb86a20s_readreg(state, 0x0a) & 0xf; + if (val < 0) + return val; - /* Initialize the frontend */ - rc = mb86a20s_writeregdata(state, mb86a20s_init); - if (rc < 0) - goto err; + if (val >= 2) + *status |= FE_HAS_SIGNAL; - if (!state->config->is_serial) { - regD5 &= ~1; + if (val >= 4) + *status |= FE_HAS_CARRIER; - rc = mb86a20s_writereg(state, 0x50, 0xd5); - if (rc < 0) - goto err; - rc = mb86a20s_writereg(state, 0x51, regD5); - if (rc < 0) - goto err; - } + if (val >= 5) + *status |= FE_HAS_VITERBI; -err: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); + if (val >= 7) + *status |= FE_HAS_SYNC; - if (rc < 0) { - state->need_init = true; - printk(KERN_INFO "mb86a20s: Init failed. Will try again later\n"); - } else { - state->need_init = false; - dprintk("Initialization succeeded.\n"); - } - return rc; + if (val >= 8) /* Maybe 9? */ + *status |= FE_HAS_LOCK; + + dprintk("val = %d, status = 0x%02x\n", val, *status); + + return 0; } static int mb86a20s_read_signal_strength(struct dvb_frontend *fe, u16 *strength) @@ -317,82 +312,6 @@ static int mb86a20s_read_signal_strength(struct dvb_frontend *fe, u16 *strength) return 0; } -static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) -{ - struct mb86a20s_state *state = fe->demodulator_priv; - int val; - - dprintk("\n"); - *status = 0; - - val = mb86a20s_readreg(state, 0x0a) & 0xf; - if (val < 0) - return val; - - if (val >= 2) - *status |= FE_HAS_SIGNAL; - - if (val >= 4) - *status |= FE_HAS_CARRIER; - - if (val >= 5) - *status |= FE_HAS_VITERBI; - - if (val >= 7) - *status |= FE_HAS_SYNC; - - if (val >= 8) /* Maybe 9? */ - *status |= FE_HAS_LOCK; - - dprintk("val = %d, status = 0x%02x\n", val, *status); - - return 0; -} - -static int mb86a20s_set_frontend(struct dvb_frontend *fe) -{ - struct mb86a20s_state *state = fe->demodulator_priv; - int rc; -#if 0 - /* - * FIXME: Properly implement the set frontend properties - */ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; -#endif - - dprintk("\n"); - - /* - * Gate should already be opened, but it doesn't hurt to - * double-check - */ - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - dprintk("Calling tuner set parameters\n"); - fe->ops.tuner_ops.set_params(fe); - - /* - * Make it more reliable: if, for some reason, the initial - * device initialization doesn't happen, initialize it when - * a SBTVD parameters are adjusted. - * - * Unfortunately, due to a hard to track bug at tda829x/tda18271, - * the agc callback logic is not called during DVB attach time, - * causing mb86a20s to not be initialized with Kworld SBTVD. - * So, this hack is needed, in order to make Kworld SBTVD to work. - */ - if (state->need_init) - mb86a20s_initfe(fe); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - return rc; -} - static int mb86a20s_get_modulation(struct mb86a20s_state *state, unsigned layer) { @@ -633,6 +552,92 @@ error: } +static int mb86a20s_initfe(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + int rc; + u8 regD5 = 1; + + dprintk("\n"); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + /* Initialize the frontend */ + rc = mb86a20s_writeregdata(state, mb86a20s_init); + if (rc < 0) + goto err; + + if (!state->config->is_serial) { + regD5 &= ~1; + + rc = mb86a20s_writereg(state, 0x50, 0xd5); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x51, regD5); + if (rc < 0) + goto err; + } + +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (rc < 0) { + state->need_init = true; + printk(KERN_INFO "mb86a20s: Init failed. Will try again later\n"); + } else { + state->need_init = false; + dprintk("Initialization succeeded.\n"); + } + return rc; +} + +static int mb86a20s_set_frontend(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + int rc; +#if 0 + /* + * FIXME: Properly implement the set frontend properties + */ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; +#endif + + dprintk("\n"); + + /* + * Gate should already be opened, but it doesn't hurt to + * double-check + */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + dprintk("Calling tuner set parameters\n"); + fe->ops.tuner_ops.set_params(fe); + + /* + * Make it more reliable: if, for some reason, the initial + * device initialization doesn't happen, initialize it when + * a SBTVD parameters are adjusted. + * + * Unfortunately, due to a hard to track bug at tda829x/tda18271, + * the agc callback logic is not called during DVB attach time, + * causing mb86a20s to not be initialized with Kworld SBTVD. + * So, this hack is needed, in order to make Kworld SBTVD to work. + */ + if (state->need_init) + mb86a20s_initfe(fe); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + return rc; +} + + static int mb86a20s_read_status_gate(struct dvb_frontend *fe, fe_status_t *status) { -- cgit v1.2.3 From f66d81b54dac26d4e601d4d7faca53f3bdc98427 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 22 Jan 2013 09:13:08 -0200 Subject: [media] mb86a20s: convert it to use dev_info/dev_err/dev_dbg Instead of having its own set of macros, use the Kernel default ones for debug, error and info. While here, do some cleanup on the debug printk's. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 96 ++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index b348f97aa7b5..c52ae2ea0bf9 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -24,18 +24,6 @@ static int debug = 1; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); -#define rc(args...) do { \ - printk(KERN_ERR "mb86a20s: " args); \ -} while (0) - -#define dprintk(args...) \ - do { \ - if (debug) { \ - printk(KERN_DEBUG "mb86a20s: %s: ", __func__); \ - printk(args); \ - } \ - } while (0) - struct mb86a20s_state { struct i2c_adapter *i2c; const struct mb86a20s_config *config; @@ -190,8 +178,9 @@ static int mb86a20s_i2c_writereg(struct mb86a20s_state *state, rc = i2c_transfer(state->i2c, &msg, 1); if (rc != 1) { - printk("%s: writereg error (rc == %i, reg == 0x%02x," - " data == 0x%02x)\n", __func__, rc, reg, data); + dev_err(&state->i2c->dev, + "%s: writereg error (rc == %i, reg == 0x%02x, data == 0x%02x)\n", + __func__, rc, reg, data); return rc; } @@ -225,8 +214,9 @@ static int mb86a20s_i2c_readreg(struct mb86a20s_state *state, rc = i2c_transfer(state->i2c, msg, 2); if (rc != 2) { - rc("%s: reg=0x%x (error=%d)\n", __func__, reg, rc); - return rc; + dev_err(&state->i2c->dev, "%s: reg=0x%x (error=%d)\n", + __func__, reg, rc); + return (rc < 0) ? rc : -EIO; } return val; @@ -245,7 +235,6 @@ static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) struct mb86a20s_state *state = fe->demodulator_priv; int val; - dprintk("\n"); *status = 0; val = mb86a20s_readreg(state, 0x0a) & 0xf; @@ -267,7 +256,8 @@ static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) if (val >= 8) /* Maybe 9? */ *status |= FE_HAS_LOCK; - dprintk("val = %d, status = 0x%02x\n", val, *status); + dev_dbg(&state->i2c->dev, "%s: Status = 0x%02x (state = %d)\n", + __func__, *status, val); return 0; } @@ -278,8 +268,6 @@ static int mb86a20s_read_signal_strength(struct dvb_frontend *fe, u16 *strength) unsigned rf_max, rf_min, rf; u8 val; - dprintk("\n"); - if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); @@ -300,12 +288,13 @@ static int mb86a20s_read_signal_strength(struct dvb_frontend *fe, u16 *strength) rf_max = (rf_max + rf_min) / 2; if (rf_max - rf_min < 4) { *strength = (((rf_max + rf_min) / 2) * 65535) / 4095; + dev_dbg(&state->i2c->dev, + "%s: signal strength = %d (%d < RF=%d < %d)\n", + __func__, rf, rf_min, rf >> 4, rf_max); break; } } while (1); - dprintk("signal strength = %d\n", *strength); - if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -419,15 +408,17 @@ static int mb86a20s_get_segment_count(struct mb86a20s_state *state, unsigned layer) { int rc, count; - static unsigned char reg[] = { [0] = 0x89, /* Layer A */ [1] = 0x8d, /* Layer B */ [2] = 0x91, /* Layer C */ }; + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + if (layer >= ARRAY_SIZE(reg)) return -EINVAL; + rc = mb86a20s_writereg(state, 0x6d, reg[layer]); if (rc < 0) return rc; @@ -436,13 +427,18 @@ static int mb86a20s_get_segment_count(struct mb86a20s_state *state, return rc; count = (rc >> 4) & 0x0f; + dev_dbg(&state->i2c->dev, "%s: segments: %d.\n", __func__, count); + return count; } static void mb86a20s_reset_frontend_cache(struct dvb_frontend *fe) { + struct mb86a20s_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + /* Fixed parameters */ c->delivery_system = SYS_ISDBT; c->bandwidth_hz = 6000000; @@ -461,6 +457,8 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) struct dtv_frontend_properties *c = &fe->dtv_property_cache; int i, rc; + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + /* Reset frontend cache to default values */ mb86a20s_reset_frontend_cache(fe); @@ -479,9 +477,12 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) /* Get per-layer data */ for (i = 0; i < 3; i++) { + dev_dbg(&state->i2c->dev, "%s: getting data for layer %c.\n", + __func__, 'A' + i); + rc = mb86a20s_get_segment_count(state, i); if (rc < 0) - goto error; + goto noperlayer_error; if (rc >= 0 && rc < 14) c->layer[i].segment_count = rc; else { @@ -491,15 +492,21 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) c->isdbt_layer_enabled |= 1 << i; rc = mb86a20s_get_modulation(state, i); if (rc < 0) - goto error; + goto noperlayer_error; + dev_dbg(&state->i2c->dev, "%s: modulation %d.\n", + __func__, rc); c->layer[i].modulation = rc; rc = mb86a20s_get_fec(state, i); if (rc < 0) - goto error; + goto noperlayer_error; + dev_dbg(&state->i2c->dev, "%s: FEC %d.\n", + __func__, rc); c->layer[i].fec = rc; rc = mb86a20s_get_interleaving(state, i); if (rc < 0) - goto error; + goto noperlayer_error; + dev_dbg(&state->i2c->dev, "%s: interleaving %d.\n", + __func__, rc); c->layer[i].interleaving = rc; } @@ -544,7 +551,7 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) } } -error: +noperlayer_error: if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -558,7 +565,7 @@ static int mb86a20s_initfe(struct dvb_frontend *fe) int rc; u8 regD5 = 1; - dprintk("\n"); + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); @@ -585,10 +592,11 @@ err: if (rc < 0) { state->need_init = true; - printk(KERN_INFO "mb86a20s: Init failed. Will try again later\n"); + dev_info(&state->i2c->dev, + "mb86a20s: Init failed. Will try again later\n"); } else { state->need_init = false; - dprintk("Initialization succeeded.\n"); + dev_dbg(&state->i2c->dev, "Initialization succeeded.\n"); } return rc; } @@ -603,8 +611,7 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe) */ struct dtv_frontend_properties *c = &fe->dtv_property_cache; #endif - - dprintk("\n"); + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); /* * Gate should already be opened, but it doesn't hurt to @@ -612,7 +619,6 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe) */ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - dprintk("Calling tuner set parameters\n"); fe->ops.tuner_ops.set_params(fe); /* @@ -637,13 +643,11 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe) return rc; } - static int mb86a20s_read_status_gate(struct dvb_frontend *fe, fe_status_t *status) { int ret; - dprintk("\n"); *status = 0; if (fe->ops.i2c_gate_ctrl) @@ -663,9 +667,10 @@ static int mb86a20s_tune(struct dvb_frontend *fe, unsigned int *delay, fe_status_t *status) { + struct mb86a20s_state *state = fe->demodulator_priv; int rc = 0; - dprintk("\n"); + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); if (re_tune) rc = mb86a20s_set_frontend(fe); @@ -680,7 +685,7 @@ static void mb86a20s_release(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; - dprintk("\n"); + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); kfree(state); } @@ -690,15 +695,16 @@ static struct dvb_frontend_ops mb86a20s_ops; struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, struct i2c_adapter *i2c) { + struct mb86a20s_state *state; u8 rev; /* allocate memory for the internal state */ - struct mb86a20s_state *state = - kzalloc(sizeof(struct mb86a20s_state), GFP_KERNEL); + state = kzalloc(sizeof(struct mb86a20s_state), GFP_KERNEL); - dprintk("\n"); + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); if (state == NULL) { - rc("Unable to kzalloc\n"); + dev_err(&state->i2c->dev, + "%s: unable to allocate memory for state\n", __func__); goto error; } @@ -715,9 +721,11 @@ struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, rev = mb86a20s_readreg(state, 0); if (rev == 0x13) { - printk(KERN_INFO "Detected a Fujitsu mb86a20s frontend\n"); + dev_info(&state->i2c->dev, + "Detected a Fujitsu mb86a20s frontend\n"); } else { - printk(KERN_ERR "Frontend revision %d is unknown - aborting.\n", + dev_dbg(&state->i2c->dev, + "Frontend revision %d is unknown - aborting.\n", rev); goto error; } -- cgit v1.2.3 From 1ffff60320879830e469e26062c18f75236822ba Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 22 Jan 2013 12:50:34 +0200 Subject: drm/i915: add quirk to invert brightness on eMachines G725 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=59628 Reported-by: Roland Gruber Signed-off-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 77254460a5cb..44f9d8f73a20 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8599,6 +8599,9 @@ static struct intel_quirk intel_quirks[] = { /* Acer Aspire 5734Z must invert backlight brightness */ { 0x2a42, 0x1025, 0x0459, quirk_invert_brightness }, + + /* Acer/eMachines G725 */ + { 0x2a42, 0x1025, 0x0210, quirk_invert_brightness }, }; static void intel_init_quirks(struct drm_device *dev) -- cgit v1.2.3 From 01e3a8feb40e54b962a20fa7eb595c5efef5e109 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 22 Jan 2013 12:50:35 +0200 Subject: drm/i915: add quirk to invert brightness on eMachines e725 Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=31522#c35 [Note: There are more than one broken setups in the bug. This fixes one.] Reported-by: Martins Signed-off-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 44f9d8f73a20..8575a62d29d3 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8602,6 +8602,9 @@ static struct intel_quirk intel_quirks[] = { /* Acer/eMachines G725 */ { 0x2a42, 0x1025, 0x0210, quirk_invert_brightness }, + + /* Acer/eMachines e725 */ + { 0x2a42, 0x1025, 0x0212, quirk_invert_brightness }, }; static void intel_init_quirks(struct drm_device *dev) -- cgit v1.2.3 From 5559ecadad5a73b27f863e92f4b4f369501dce6f Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 22 Jan 2013 12:50:36 +0200 Subject: drm/i915: add quirk to invert brightness on Packard Bell NCL20 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=44156 Reported-by: Alan Zimmerman Signed-off-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8575a62d29d3..726278668ab6 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8605,6 +8605,9 @@ static struct intel_quirk intel_quirks[] = { /* Acer/eMachines e725 */ { 0x2a42, 0x1025, 0x0212, quirk_invert_brightness }, + + /* Acer/Packard Bell NCL20 */ + { 0x2a42, 0x1025, 0x034b, quirk_invert_brightness }, }; static void intel_init_quirks(struct drm_device *dev) -- cgit v1.2.3 From 99433931950f33039d9e1a52b4ed9af3f1b58e84 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Tue, 22 Jan 2013 14:12:17 +0200 Subject: drm/i915: use gem_set_seqno() on hardware init When machine was rebooted or module was reloaded, gem_hw_init() set last_seqno to be identical to next_seqno. This lead to situation that waits for first ever request always passed immediately regardless if it was actually executed. Use gem_set_seqno() to be consistent how hw is initialized on init, wrap and on resume. Signed-off-by: Mika Kuoppala Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 6 ++++-- drivers/gpu/drm/i915/intel_ringbuffer.c | 2 -- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 5226ebcac33a..801f77ece72e 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3941,8 +3941,6 @@ i915_gem_init_hw(struct drm_device *dev) i915_gem_init_swizzling(dev); - dev_priv->next_seqno = dev_priv->last_seqno = (u32)~0 - 0x1000; - ret = intel_init_render_ring_buffer(dev); if (ret) return ret; @@ -3959,6 +3957,10 @@ i915_gem_init_hw(struct drm_device *dev) goto cleanup_bsd_ring; } + ret = i915_gem_set_seqno(dev, ((u32)~0 - 0x1000)); + if (ret) + return ret; + /* * XXX: There was some w/a described somewhere suggesting loading * contexts before PPGTT. diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 9438bcd50678..dc6ae2fa1cee 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1223,8 +1223,6 @@ static int intel_init_ring_buffer(struct drm_device *dev, if (IS_I830(ring->dev) || IS_845G(ring->dev)) ring->effective_size -= 128; - intel_ring_init_seqno(ring, dev_priv->last_seqno); - return 0; err_unmap: -- cgit v1.2.3 From 7b9f35a6dd72f89452c58bbdbaf063027bf857ec Mon Sep 17 00:00:00 2001 From: Wang Xingchao Date: Tue, 22 Jan 2013 23:25:25 +0800 Subject: drm/i915: HDMI/DP - ELD info refresh support for Haswell MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ELD info should be updated dynamically according to hot plug event. For haswell chip, clear/set the eld valid bit and output enable bit from callback intel_disable/eanble_ddi(). Reviewed-by: Ville Syrjälä Reviewed-by: Rodrigo Vivi Signed-off-by: Wang Xingchao Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 21 +++++++++++++++++++++ drivers/gpu/drm/i915/intel_display.c | 4 ++++ drivers/gpu/drm/i915/intel_drv.h | 1 + 3 files changed, 26 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 2e904a5cd6cb..33b911218023 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -677,6 +677,7 @@ static void intel_ddi_mode_set(struct drm_encoder *encoder, DRM_DEBUG_KMS("Preparing DDI mode for Haswell on port %c, pipe %c\n", port_name(port), pipe_name(pipe)); + intel_crtc->eld_vld = false; if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); @@ -1287,10 +1288,14 @@ static void intel_ddi_post_disable(struct intel_encoder *intel_encoder) static void intel_enable_ddi(struct intel_encoder *intel_encoder) { struct drm_encoder *encoder = &intel_encoder->base; + struct drm_crtc *crtc = encoder->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; enum port port = intel_ddi_get_encoder_port(intel_encoder); int type = intel_encoder->type; + uint32_t tmp; if (type == INTEL_OUTPUT_HDMI) { /* In HDMI/DVI mode, the port width, and swing/emphasis values @@ -1303,18 +1308,34 @@ static void intel_enable_ddi(struct intel_encoder *intel_encoder) ironlake_edp_backlight_on(intel_dp); } + + if (intel_crtc->eld_vld) { + tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); + tmp |= ((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << (pipe * 4)); + I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp); + } } static void intel_disable_ddi(struct intel_encoder *intel_encoder) { struct drm_encoder *encoder = &intel_encoder->base; + struct drm_crtc *crtc = encoder->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; int type = intel_encoder->type; + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t tmp; if (type == INTEL_OUTPUT_EDP) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); ironlake_edp_backlight_off(intel_dp); } + + tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); + tmp &= ~((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << (pipe * 4)); + I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp); } int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 726278668ab6..305be115319e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3721,10 +3721,12 @@ static void intel_crtc_disable(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; struct drm_connector *connector; struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); /* crtc should still be enabled when we disable it. */ WARN_ON(!crtc->enabled); + intel_crtc->eld_vld = false; dev_priv->display.crtc_disable(crtc); intel_crtc_update_sarea(crtc, false); dev_priv->display.off(crtc); @@ -5792,6 +5794,7 @@ static void haswell_write_eld(struct drm_connector *connector, struct drm_i915_private *dev_priv = connector->dev->dev_private; uint8_t *eld = connector->eld; struct drm_device *dev = crtc->dev; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); uint32_t eldv; uint32_t i; int len; @@ -5833,6 +5836,7 @@ static void haswell_write_eld(struct drm_connector *connector, DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(pipe)); eldv = AUDIO_ELD_VALID_A << (pipe * 4); + intel_crtc->eld_vld = true; if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n"); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index aeff0d1067ad..66619d80348f 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -211,6 +211,7 @@ struct intel_crtc { * some outputs connected to this crtc. */ bool active; + bool eld_vld; bool primary_disabled; /* is the crtc obscured by a plane? */ bool lowfreq_avail; struct intel_overlay *overlay; -- cgit v1.2.3 From 6d1aa02f10497b138e01ebe6eafabd6071729334 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 10 Nov 2012 12:06:25 +0100 Subject: omap3isp: Set cam_mclk rate directly Now that the cam_mclk rate changes are back-propagated to dpll4_m5_ck we can set the cam_mclk rate directly instead of manually setting the rate of the parent clock. Signed-off-by: Laurent Pinchart Reviewed-by: Mike Turquette Acked-by: Sakari Ailus Tested-by: Sakari Ailus --- drivers/media/platform/omap3isp/isp.c | 18 ++---------------- drivers/media/platform/omap3isp/isp.h | 8 +++----- 2 files changed, 5 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index e4aaee91201d..e7f5da0296f0 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -1338,28 +1338,15 @@ static int isp_enable_clocks(struct isp_device *isp) { int r; unsigned long rate; - int divisor; - - /* - * cam_mclk clock chain: - * dpll4 -> dpll4_m5 -> dpll4_m5x2 -> cam_mclk - * - * In OMAP3630 dpll4_m5x2 != 2 x dpll4_m5 but both are - * set to the same value. Hence the rate set for dpll4_m5 - * has to be twice of what is set on OMAP3430 to get - * the required value for cam_mclk - */ - divisor = isp->revision == ISP_REVISION_15_0 ? 1 : 2; r = clk_prepare_enable(isp->clock[ISP_CLK_CAM_ICK]); if (r) { dev_err(isp->dev, "failed to enable cam_ick clock\n"); goto out_clk_enable_ick; } - r = clk_set_rate(isp->clock[ISP_CLK_DPLL4_M5_CK], - CM_CAM_MCLK_HZ/divisor); + r = clk_set_rate(isp->clock[ISP_CLK_CAM_MCLK], CM_CAM_MCLK_HZ); if (r) { - dev_err(isp->dev, "clk_set_rate for dpll4_m5_ck failed\n"); + dev_err(isp->dev, "clk_set_rate for cam_mclk failed\n"); goto out_clk_enable_mclk; } r = clk_prepare_enable(isp->clock[ISP_CLK_CAM_MCLK]); @@ -1401,7 +1388,6 @@ static void isp_disable_clocks(struct isp_device *isp) static const char *isp_clocks[] = { "cam_ick", "cam_mclk", - "dpll4_m5_ck", "csi2_96m_fck", "l3_ick", }; diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h index 517d348ce32b..c77e1f2ae5ca 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/omap3isp/isp.h @@ -147,7 +147,6 @@ struct isp_platform_callback { * @ref_count: Reference count for handling multiple ISP requests. * @cam_ick: Pointer to camera interface clock structure. * @cam_mclk: Pointer to camera functional clock structure. - * @dpll4_m5_ck: Pointer to DPLL4 M5 clock structure. * @csi2_fck: Pointer to camera CSI2 complexIO clock structure. * @l3_ick: Pointer to OMAP3 L3 bus interface clock. * @irq: Currently attached ISP ISR callbacks information structure. @@ -189,10 +188,9 @@ struct isp_device { u32 xclk_divisor[2]; /* Two clocks, a and b. */ #define ISP_CLK_CAM_ICK 0 #define ISP_CLK_CAM_MCLK 1 -#define ISP_CLK_DPLL4_M5_CK 2 -#define ISP_CLK_CSI2_FCK 3 -#define ISP_CLK_L3_ICK 4 - struct clk *clock[5]; +#define ISP_CLK_CSI2_FCK 2 +#define ISP_CLK_L3_ICK 3 + struct clk *clock[4]; /* ISP modules */ struct ispstat isp_af; -- cgit v1.2.3 From c00db2463978ffab59d731773aae1a4f4e11d78c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 22 Jan 2013 15:33:27 +0100 Subject: drm/i915: fixup sbi_read/write locking commit 09153000b8ca32a539a1207edebabd0d40b6c61b Author: Daniel Vetter Date: Wed Dec 12 14:06:44 2012 +0100 drm/i915: rework locking for intel_dpio|sbi_read|write reworked the locking around sbi_read/write functions for 3.8-fixes. But commit dde86e2db54545ef981b64805097a7b4c3156d6e Author: Paulo Zanoni Date: Sat Dec 1 12:04:25 2012 -0200 drm/i915: add lpt_init_pch_refcl Added new use-cases in the -next tree which has not been updated in the merge. Fix it up. Reported-by: Ben Widawsky Reviewed-by: Ben Widawsky Tested-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 305be115319e..886124a720e9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4873,6 +4873,8 @@ static void lpt_init_pch_refclk(struct drm_device *dev) if (!has_vga) return; + mutex_lock(&dev_priv->dpio_lock); + /* XXX: Rip out SDV support once Haswell ships for real. */ if (IS_HASWELL(dev) && (dev->pci_device & 0xFF00) == 0x0C00) is_sdv = true; @@ -5015,6 +5017,8 @@ static void lpt_init_pch_refclk(struct drm_device *dev) tmp = intel_sbi_read(dev_priv, SBI_DBUFF0, SBI_ICLK); tmp |= SBI_DBUFF0_ENABLE; intel_sbi_write(dev_priv, SBI_DBUFF0, tmp, SBI_ICLK); + + mutex_unlock(&dev_priv->dpio_lock); } /* -- cgit v1.2.3 From f167e302c6a1321ae9f4d3a24a6e5bac90a5c79d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 23 Jan 2013 13:22:22 -0200 Subject: [media] mb86a20s: don't use state before initializing it As reported by Feng's kbuild test: From: kbuild test robot Subject: drivers/media/dvb-frontends/mb86a20s.c:706 mb86a20s_attach() error: potential null dereference 'state'. (kzalloc returns null) Date: Wed, 23 Jan 2013 19:30:43 +0800 commit: f66d81b54dac26d4e601d4d7faca53f3bdc98427 [media] mb86a20s: convert it to use dev_info/dev_err/dev_dbg drivers/media/dvb-frontends/mb86a20s.c:706 mb86a20s_attach() error: potential null dereference 'state'. (kzalloc returns null) drivers/media/dvb-frontends/mb86a20s.c:706 mb86a20s_attach() error: we previously assumed 'state' could be null (see line 705) As, at mb86a20s_attach(), we have an i2c pointer, use it for all printk messages there, instead of state->i2c. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index c52ae2ea0bf9..4b3ffc418294 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -698,12 +698,12 @@ struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, struct mb86a20s_state *state; u8 rev; + dev_dbg(&i2c->dev, "%s called.\n", __func__); + /* allocate memory for the internal state */ state = kzalloc(sizeof(struct mb86a20s_state), GFP_KERNEL); - - dev_dbg(&state->i2c->dev, "%s called.\n", __func__); if (state == NULL) { - dev_err(&state->i2c->dev, + dev_err(&i2c->dev, "%s: unable to allocate memory for state\n", __func__); goto error; } @@ -721,10 +721,10 @@ struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, rev = mb86a20s_readreg(state, 0); if (rev == 0x13) { - dev_info(&state->i2c->dev, + dev_info(&i2c->dev, "Detected a Fujitsu mb86a20s frontend\n"); } else { - dev_dbg(&state->i2c->dev, + dev_dbg(&i2c->dev, "Frontend revision %d is unknown - aborting.\n", rev); goto error; -- cgit v1.2.3 From 7cd4ece58f9b94372687de820c22cb2eae4a623e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 7 Jan 2013 15:41:35 -0300 Subject: [media] dvb: the core logic to handle the DVBv5 QoS properties Add the logic to poll, reset counters and report the QoS stats to the end user. The idea is that the core will periodically poll the frontend for the stats. The frontend may return -EBUSY, if the previous collect didn't finish, or it may fill the cached data. The value returned to the end user is always the cached data. Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_frontend.c | 35 +++++++++++++++++++++++++++++++++++ drivers/media/dvb-core/dvb_frontend.h | 10 ++++++++++ 2 files changed, 45 insertions(+) (limited to 'drivers') diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index dd35fa972067..0c6f936ffac8 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -1053,6 +1053,16 @@ static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = { _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_B, 0, 0), _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_C, 0, 0), _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_D, 0, 0), + + /* Statistics API */ + _DTV_CMD(DTV_STAT_SIGNAL_STRENGTH, 0, 0), + _DTV_CMD(DTV_STAT_CNR, 0, 0), + _DTV_CMD(DTV_STAT_PRE_ERROR_BIT_COUNT, 0, 0), + _DTV_CMD(DTV_STAT_PRE_TOTAL_BIT_COUNT, 0, 0), + _DTV_CMD(DTV_STAT_POST_ERROR_BIT_COUNT, 0, 0), + _DTV_CMD(DTV_STAT_POST_TOTAL_BIT_COUNT, 0, 0), + _DTV_CMD(DTV_STAT_ERROR_BLOCK_COUNT, 0, 0), + _DTV_CMD(DTV_STAT_TOTAL_BLOCK_COUNT, 0, 0), }; static void dtv_property_dump(struct dvb_frontend *fe, struct dtv_property *tvp) @@ -1443,6 +1453,31 @@ static int dtv_property_process_get(struct dvb_frontend *fe, tvp->u.data = c->lna; break; + /* Fill quality measures */ + case DTV_STAT_SIGNAL_STRENGTH: + tvp->u.st = c->strength; + break; + case DTV_STAT_CNR: + tvp->u.st = c->cnr; + break; + case DTV_STAT_PRE_ERROR_BIT_COUNT: + tvp->u.st = c->pre_bit_error; + break; + case DTV_STAT_PRE_TOTAL_BIT_COUNT: + tvp->u.st = c->pre_bit_count; + break; + case DTV_STAT_POST_ERROR_BIT_COUNT: + tvp->u.st = c->post_bit_error; + break; + case DTV_STAT_POST_TOTAL_BIT_COUNT: + tvp->u.st = c->post_bit_count; + break; + case DTV_STAT_ERROR_BLOCK_COUNT: + tvp->u.st = c->block_error; + break; + case DTV_STAT_TOTAL_BLOCK_COUNT: + tvp->u.st = c->block_count; + break; default: return -EINVAL; } diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h index 97112cd88a17..b34922a08156 100644 --- a/drivers/media/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb-core/dvb_frontend.h @@ -393,6 +393,16 @@ struct dtv_frontend_properties { u8 atscmh_sccc_code_mode_d; u32 lna; + + /* statistics data */ + struct dtv_fe_stats strength; + struct dtv_fe_stats cnr; + struct dtv_fe_stats pre_bit_error; + struct dtv_fe_stats pre_bit_count; + struct dtv_fe_stats post_bit_error; + struct dtv_fe_stats post_bit_count; + struct dtv_fe_stats block_error; + struct dtv_fe_stats block_count; }; struct dvb_frontend { -- cgit v1.2.3 From 09b6d21e100a8dcda7cf5a32ecd52e8008094f72 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 22 Jan 2013 12:28:31 -0300 Subject: [media] mb86a20s: calculate statistics at .read_status() Instead of providing separate callbacks to read the several FE stats properties, the better seems to use just one method that will: - Read lock status; - Read signal strength; - if locked, get TMCC data; - if locked, get DVB statistics. As the DVB frontend thread will call this read_status callback on every 3 seconds, and userspace can even call it earlier, all stats data and layers layout will be updated together if available, with is a good thing. Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 263 ++++++++++++++++++++++++++++----- 1 file changed, 224 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 4b3ffc418294..d7668e6292a0 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -27,6 +27,7 @@ MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); struct mb86a20s_state { struct i2c_adapter *i2c; const struct mb86a20s_config *config; + u32 last_frequency; struct dvb_frontend frontend; @@ -80,17 +81,26 @@ static struct regdata mb86a20s_init[] = { { 0x04, 0x13 }, { 0x05, 0xff }, { 0x04, 0x15 }, { 0x05, 0x4e }, { 0x04, 0x16 }, { 0x05, 0x20 }, - { 0x52, 0x01 }, - { 0x50, 0xa7 }, { 0x51, 0xff }, + + /* + * On this demod, when the bit count reaches the count below, + * it collects the bit error count. The bit counters are initialized + * to 65535 here. This warrants that all of them will be quickly + * calculated when device gets locked. As TMCC is parsed, the values + * can be adjusted later in the driver's code. + */ + { 0x52, 0x01 }, /* Turn on BER before Viterbi */ + { 0x50, 0xa7 }, { 0x51, 0x00 }, { 0x50, 0xa8 }, { 0x51, 0xff }, { 0x50, 0xa9 }, { 0x51, 0xff }, - { 0x50, 0xaa }, { 0x51, 0xff }, + { 0x50, 0xaa }, { 0x51, 0x00 }, { 0x50, 0xab }, { 0x51, 0xff }, { 0x50, 0xac }, { 0x51, 0xff }, - { 0x50, 0xad }, { 0x51, 0xff }, + { 0x50, 0xad }, { 0x51, 0x00 }, { 0x50, 0xae }, { 0x51, 0xff }, { 0x50, 0xaf }, { 0x51, 0xff }, - { 0x5e, 0x07 }, + + { 0x5e, 0x00 }, /* Turn off BER after Viterbi */ { 0x50, 0xdc }, { 0x51, 0x01 }, { 0x50, 0xdd }, { 0x51, 0xf4 }, { 0x50, 0xde }, { 0x51, 0x01 }, @@ -105,8 +115,8 @@ static struct regdata mb86a20s_init[] = { { 0x50, 0xb6 }, { 0x51, 0xff }, { 0x50, 0xb7 }, { 0x51, 0xff }, { 0x50, 0x50 }, { 0x51, 0x02 }, - { 0x50, 0x51 }, { 0x51, 0x04 }, - { 0x45, 0x04 }, + { 0x50, 0x51 }, { 0x51, 0x04 }, /* MER symbol 4 */ + { 0x45, 0x04 }, /* CN symbol 4 */ { 0x48, 0x04 }, { 0x50, 0xd5 }, { 0x51, 0x01 }, /* Serial */ { 0x50, 0xd6 }, { 0x51, 0x1f }, @@ -163,12 +173,23 @@ static struct regdata mb86a20s_reset_reception[] = { { 0x08, 0x00 }, }; +static struct regdata mb86a20s_vber_reset[] = { + { 0x53, 0x00 }, /* VBER Counter reset */ + { 0x53, 0x07 }, +}; + +static struct regdata mb86a20s_per_reset[] = { + { 0x50, 0xb1 }, /* PER Counter reset */ + { 0x51, 0x07 }, + { 0x51, 0x00 }, +}; + /* * I2C read/write functions and macros */ static int mb86a20s_i2c_writereg(struct mb86a20s_state *state, - u8 i2c_addr, int reg, int data) + u8 i2c_addr, u8 reg, u8 data) { u8 buf[] = { reg, data }; struct i2c_msg msg = { @@ -230,6 +251,12 @@ static int mb86a20s_i2c_readreg(struct mb86a20s_state *state, mb86a20s_i2c_writeregdata(state, state->config->demod_address, \ regdata, ARRAY_SIZE(regdata)) +/* + * Ancillary internal routines (likely compiled inlined) + * + * The functions below assume that gateway lock has already obtained + */ + static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct mb86a20s_state *state = fe->demodulator_priv; @@ -262,42 +289,49 @@ static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) return 0; } -static int mb86a20s_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +static int mb86a20s_read_signal_strength(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; + int rc; unsigned rf_max, rf_min, rf; - u8 val; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* Does a binary search to get RF strength */ rf_max = 0xfff; rf_min = 0; do { rf = (rf_max + rf_min) / 2; - mb86a20s_writereg(state, 0x04, 0x1f); - mb86a20s_writereg(state, 0x05, rf >> 8); - mb86a20s_writereg(state, 0x04, 0x20); - mb86a20s_writereg(state, 0x04, rf); + rc = mb86a20s_writereg(state, 0x04, 0x1f); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x05, rf >> 8); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x04, 0x20); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x04, rf); + if (rc < 0) + return rc; - val = mb86a20s_readreg(state, 0x02); - if (val & 0x08) + rc = mb86a20s_readreg(state, 0x02); + if (rc < 0) + return rc; + if (rc & 0x08) rf_min = (rf_max + rf_min) / 2; else rf_max = (rf_max + rf_min) / 2; if (rf_max - rf_min < 4) { - *strength = (((rf_max + rf_min) / 2) * 65535) / 4095; + rf = (rf_max + rf_min) / 2; + + /* Rescale it from 2^12 (4096) to 2^16 */ + rf <<= (16 - 12); dev_dbg(&state->i2c->dev, "%s: signal strength = %d (%d < RF=%d < %d)\n", __func__, rf, rf_min, rf >> 4, rf_max); - break; + return rf; } } while (1); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - return 0; } @@ -462,9 +496,6 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) /* Reset frontend cache to default values */ mb86a20s_reset_frontend_cache(fe); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - /* Check for partial reception */ rc = mb86a20s_writereg(state, 0x6d, 0x85); if (rc < 0) @@ -550,15 +581,119 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) break; } } + return 0; noperlayer_error: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); + /* per-layer info is incomplete; discard all per-layer */ + c->isdbt_layer_enabled = 0; + + return rc; +} + +static int mb86a20s_reset_counters(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int rc, val; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + /* Reset the counters, if the channel changed */ + if (state->last_frequency != c->frequency) { + memset(&c->strength, 0, sizeof(c->strength)); + memset(&c->cnr, 0, sizeof(c->cnr)); + memset(&c->pre_bit_error, 0, sizeof(c->pre_bit_error)); + memset(&c->pre_bit_count, 0, sizeof(c->pre_bit_count)); + memset(&c->block_error, 0, sizeof(c->block_error)); + memset(&c->block_count, 0, sizeof(c->block_count)); + + state->last_frequency = c->frequency; + } + + /* Clear status for most stats */ + + /* BER counter reset */ + rc = mb86a20s_writeregdata(state, mb86a20s_vber_reset); + if (rc < 0) + goto err; + + /* MER, PER counter reset */ + rc = mb86a20s_writeregdata(state, mb86a20s_per_reset); + if (rc < 0) + goto err; + + /* CNR counter reset */ + rc = mb86a20s_readreg(state, 0x45); + if (rc < 0) + goto err; + val = rc; + rc = mb86a20s_writereg(state, 0x45, val | 0x10); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x45, val & 0x6f); + if (rc < 0) + goto err; + + /* MER counter reset */ + rc = mb86a20s_writereg(state, 0x50, 0x50); + if (rc < 0) + goto err; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + goto err; + val = rc; + rc = mb86a20s_writereg(state, 0x51, val | 0x01); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x51, val & 0x06); + if (rc < 0) + goto err; + +err: return rc; +} + +static void mb86a20s_stats_not_ready(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int i; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + /* Fill the length of each status counter */ + + /* Only global stats */ + c->strength.len = 1; + + /* Per-layer stats - 3 layers + global */ + c->cnr.len = 4; + c->pre_bit_error.len = 4; + c->pre_bit_count.len = 4; + c->block_error.len = 4; + c->block_count.len = 4; + + /* Signal is always available */ + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = 0; + + /* Put all of them at FE_SCALE_NOT_AVAILABLE */ + for (i = 0; i < 4; i++) { + c->cnr.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + } } + +/* + * The functions below are called via DVB callbacks, so they need to + * properly use the I2C gate control + */ + static int mb86a20s_initfe(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; @@ -637,30 +772,80 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe) if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception); + mb86a20s_reset_counters(fe); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); return rc; } -static int mb86a20s_read_status_gate(struct dvb_frontend *fe, - fe_status_t *status) +static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe, + fe_status_t *status) { - int ret; + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int rc; - *status = 0; + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); - ret = mb86a20s_read_status(fe, status); + /* Get lock */ + rc = mb86a20s_read_status(fe, status); + if (!(*status & FE_HAS_LOCK)) { + mb86a20s_stats_not_ready(fe); + mb86a20s_reset_frontend_cache(fe); + } + if (rc < 0) + goto error; + + /* Get signal strength */ + rc = mb86a20s_read_signal_strength(fe); + if (rc < 0) { + mb86a20s_stats_not_ready(fe); + mb86a20s_reset_frontend_cache(fe); + goto error; + } + /* Fill signal strength */ + c->strength.stat[0].uvalue = rc; + + if (*status & FE_HAS_LOCK) { + /* Get TMCC info*/ + rc = mb86a20s_get_frontend(fe); + if (rc < 0) + goto error; + } + + mb86a20s_stats_not_ready(fe); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); +error: + return rc; +} + +static int mb86a20s_read_signal_strength_from_cache(struct dvb_frontend *fe, + u16 *strength) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + + *strength = c->strength.stat[0].uvalue; - return ret; + return 0; } +static int mb86a20s_get_frontend_dummy(struct dvb_frontend *fe) +{ + /* + * get_frontend is now handled together with other stats + * retrival, when read_status() is called, as some statistics + * will depend on the layers detection. + */ + return 0; +}; + static int mb86a20s_tune(struct dvb_frontend *fe, bool re_tune, unsigned int mode_flags, @@ -676,7 +861,7 @@ static int mb86a20s_tune(struct dvb_frontend *fe, rc = mb86a20s_set_frontend(fe); if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) - mb86a20s_read_status_gate(fe, status); + mb86a20s_read_status_and_stats(fe, status); return rc; } @@ -759,9 +944,9 @@ static struct dvb_frontend_ops mb86a20s_ops = { .init = mb86a20s_initfe, .set_frontend = mb86a20s_set_frontend, - .get_frontend = mb86a20s_get_frontend, - .read_status = mb86a20s_read_status_gate, - .read_signal_strength = mb86a20s_read_signal_strength, + .get_frontend = mb86a20s_get_frontend_dummy, + .read_status = mb86a20s_read_status_and_stats, + .read_signal_strength = mb86a20s_read_signal_strength_from_cache, .tune = mb86a20s_tune, }; -- cgit v1.2.3 From 149d518ad0fd0d566d4859a4d3f280ee33de8ed7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 22 Jan 2013 12:30:07 -0300 Subject: [media] mb86a20s: add BER measurement Add the methods to read bit error/bit count measurements from mb86a20s. On ISDB-T devices, those reads are done per layer. However, as userspace applications may not be aware of that, add a global measure that will sum the bit errors and bit counts for each layer, storing them into a global value. Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 181 ++++++++++++++++++++++++++++++++- 1 file changed, 178 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index d7668e6292a0..354ea664b713 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -650,10 +650,95 @@ static int mb86a20s_reset_counters(struct dvb_frontend *fe) if (rc < 0) goto err; + goto ok; err: + dev_err(&state->i2c->dev, + "%s: Can't reset FE statistics (error %d).\n", + __func__, rc); +ok: return rc; } +static int mb86a20s_get_ber_before_vterbi(struct dvb_frontend *fe, + unsigned layer, + u32 *error, u32 *count) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + int rc; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + if (layer >= 3) + return -EINVAL; + + /* Check if the BER measures are already available */ + rc = mb86a20s_readreg(state, 0x54); + if (rc < 0) + return rc; + + /* Check if data is available for that layer */ + if (!(rc & (1 << layer))) { + dev_dbg(&state->i2c->dev, + "%s: BER for layer %c is not available yet.\n", + __func__, 'A' + layer); + return -EBUSY; + } + + /* Read Bit Error Count */ + rc = mb86a20s_readreg(state, 0x55 + layer * 3); + if (rc < 0) + return rc; + *error = rc << 16; + rc = mb86a20s_readreg(state, 0x56 + layer * 3); + if (rc < 0) + return rc; + *error |= rc << 8; + rc = mb86a20s_readreg(state, 0x57 + layer * 3); + if (rc < 0) + return rc; + *error |= rc; + + dev_dbg(&state->i2c->dev, + "%s: bit error before Viterbi for layer %c: %d.\n", + __func__, 'A' + layer, *error); + + /* Read Bit Count */ + rc = mb86a20s_writereg(state, 0x50, 0xa7 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + *count = rc << 16; + rc = mb86a20s_writereg(state, 0x50, 0xa8 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + *count |= rc << 8; + rc = mb86a20s_writereg(state, 0x50, 0xa9 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + *count |= rc; + + dev_dbg(&state->i2c->dev, + "%s: bit count before Viterbi for layer %c: %d.\n", + __func__, 'A' + layer, *count); + + + /* Reset counter to collect new data */ + rc = mb86a20s_writereg(state, 0x53, 0x07 & ~(1 << layer)); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x53, 0x07); + + return 0; +} + static void mb86a20s_stats_not_ready(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; @@ -688,6 +773,72 @@ static void mb86a20s_stats_not_ready(struct dvb_frontend *fe) } } +static int mb86a20s_get_stats(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int rc = 0, i; + u32 bit_error = 0, bit_count = 0; + u32 t_pre_bit_error = 0, t_pre_bit_count = 0; + int active_layers = 0, ber_layers = 0; + + /* Get per-layer stats */ + for (i = 0; i < 3; i++) { + if (c->isdbt_layer_enabled & (1 << i)) { + /* Layer is active and has rc segments */ + active_layers++; + + /* Read per-layer BER */ + /* Handle BER before vterbi */ + rc = mb86a20s_get_ber_before_vterbi(fe, i, + &bit_error, + &bit_count); + if (rc >= 0) { + c->pre_bit_error.stat[1 + i].scale = FE_SCALE_COUNTER; + c->pre_bit_error.stat[1 + i].uvalue += bit_error; + c->pre_bit_count.stat[1 + i].scale = FE_SCALE_COUNTER; + c->pre_bit_count.stat[1 + i].uvalue += bit_count; + } else if (rc != -EBUSY) { + /* + * If an I/O error happened, + * measures are now unavailable + */ + c->pre_bit_error.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE; + dev_err(&state->i2c->dev, + "%s: Can't get BER for layer %c (error %d).\n", + __func__, 'A' + i, rc); + } + + if (c->block_error.stat[1 + i].scale != FE_SCALE_NOT_AVAILABLE) + ber_layers++; + + /* Update total BER */ + t_pre_bit_error += c->pre_bit_error.stat[1 + i].uvalue; + t_pre_bit_count += c->pre_bit_count.stat[1 + i].uvalue; + } + } + + /* + * Start showing global count if at least one error count is + * available. + */ + if (ber_layers) { + /* + * At least one per-layer BER measure was read. We can now + * calculate the total BER + * + * Total Bit Error/Count is calculated as the sum of the + * bit errors on all active layers. + */ + c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->pre_bit_error.stat[0].uvalue = t_pre_bit_error; + c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->pre_bit_count.stat[0].uvalue = t_pre_bit_count; + } + + return rc; +} /* * The functions below are called via DVB callbacks, so they need to @@ -797,14 +948,21 @@ static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe, mb86a20s_stats_not_ready(fe); mb86a20s_reset_frontend_cache(fe); } - if (rc < 0) + if (rc < 0) { + dev_err(&state->i2c->dev, + "%s: Can't read frontend lock status\n", __func__); goto error; + } /* Get signal strength */ rc = mb86a20s_read_signal_strength(fe); if (rc < 0) { + dev_err(&state->i2c->dev, + "%s: Can't reset VBER registers.\n", __func__); mb86a20s_stats_not_ready(fe); mb86a20s_reset_frontend_cache(fe); + + rc = 0; /* Status is OK */ goto error; } /* Fill signal strength */ @@ -813,15 +971,32 @@ static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe, if (*status & FE_HAS_LOCK) { /* Get TMCC info*/ rc = mb86a20s_get_frontend(fe); - if (rc < 0) + if (rc < 0) { + dev_err(&state->i2c->dev, + "%s: Can't get FE TMCC data.\n", __func__); + rc = 0; /* Status is OK */ + goto error; + } + + /* Get statistics */ + rc = mb86a20s_get_stats(fe); + if (rc < 0 && rc != -EBUSY) { + dev_err(&state->i2c->dev, + "%s: Can't get FE statistics.\n", __func__); + rc = 0; goto error; + } + rc = 0; /* Don't return EBUSY to userspace */ } + goto ok; +error: mb86a20s_stats_not_ready(fe); +ok: if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); -error: + return rc; } -- cgit v1.2.3 From d01a8ee37afdeb1a00458a79854a1672ada5c9f0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 14 Jan 2013 20:34:55 -0300 Subject: [media] mb86a20s: improve bit error count for BER Do a better job on setting the bit error counters, in order to have all layer measures to happen in a little less than one second. Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 160 ++++++++++++++++++++++++++++++++- 1 file changed, 157 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 354ea664b713..c68e4676e5fd 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -31,6 +31,8 @@ struct mb86a20s_state { struct dvb_frontend frontend; + u32 estimated_rate[3]; + bool need_init; }; @@ -39,6 +41,8 @@ struct regdata { u8 data; }; +#define BER_SAMPLING_RATE 1 /* Seconds */ + /* * Initialization sequence: Use whatevere default values that PV SBTVD * does on its initialisation, obtained via USB snoop @@ -87,7 +91,7 @@ static struct regdata mb86a20s_init[] = { * it collects the bit error count. The bit counters are initialized * to 65535 here. This warrants that all of them will be quickly * calculated when device gets locked. As TMCC is parsed, the values - * can be adjusted later in the driver's code. + * will be adjusted later in the driver's code. */ { 0x52, 0x01 }, /* Turn on BER before Viterbi */ { 0x50, 0xa7 }, { 0x51, 0x00 }, @@ -485,6 +489,113 @@ static void mb86a20s_reset_frontend_cache(struct dvb_frontend *fe) c->isdbt_sb_segment_count = 0; } +/* + * Estimates the bit rate using the per-segment bit rate given by + * ABNT/NBR 15601 spec (table 4). + */ +static u32 isdbt_rate[3][5][4] = { + { /* DQPSK/QPSK */ + { 280850, 312060, 330420, 340430 }, /* 1/2 */ + { 374470, 416080, 440560, 453910 }, /* 2/3 */ + { 421280, 468090, 495630, 510650 }, /* 3/4 */ + { 468090, 520100, 550700, 567390 }, /* 5/6 */ + { 491500, 546110, 578230, 595760 }, /* 7/8 */ + }, { /* QAM16 */ + { 561710, 624130, 660840, 680870 }, /* 1/2 */ + { 748950, 832170, 881120, 907820 }, /* 2/3 */ + { 842570, 936190, 991260, 1021300 }, /* 3/4 */ + { 936190, 1040210, 1101400, 1134780 }, /* 5/6 */ + { 983000, 1092220, 1156470, 1191520 }, /* 7/8 */ + }, { /* QAM64 */ + { 842570, 936190, 991260, 1021300 }, /* 1/2 */ + { 1123430, 1248260, 1321680, 1361740 }, /* 2/3 */ + { 1263860, 1404290, 1486900, 1531950 }, /* 3/4 */ + { 1404290, 1560320, 1652110, 1702170 }, /* 5/6 */ + { 1474500, 1638340, 1734710, 1787280 }, /* 7/8 */ + } +}; + +static void mb86a20s_layer_bitrate(struct dvb_frontend *fe, u32 layer, + u32 modulation, u32 fec, u32 interleaving, + u32 segment) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + u32 rate; + int m, f, i; + + /* + * If modulation/fec/interleaving is not detected, the default is + * to consider the lowest bit rate, to avoid taking too long time + * to get BER. + */ + switch (modulation) { + case DQPSK: + case QPSK: + default: + m = 0; + break; + case QAM_16: + m = 1; + break; + case QAM_64: + m = 2; + break; + } + + switch (fec) { + default: + case FEC_1_2: + case FEC_AUTO: + f = 0; + break; + case FEC_2_3: + f = 1; + break; + case FEC_3_4: + f = 2; + break; + case FEC_5_6: + f = 3; + break; + case FEC_7_8: + f = 4; + break; + } + + switch (interleaving) { + default: + case GUARD_INTERVAL_1_4: + i = 0; + break; + case GUARD_INTERVAL_1_8: + i = 1; + break; + case GUARD_INTERVAL_1_16: + i = 2; + break; + case GUARD_INTERVAL_1_32: + i = 3; + break; + } + + /* Samples BER at BER_SAMPLING_RATE seconds */ + rate = isdbt_rate[m][f][i] * segment * BER_SAMPLING_RATE; + + /* Avoids sampling too quickly or to overflow the register */ + if (rate < 256) + rate = 256; + else if (rate > (1 << 24) - 1) + rate = (1 << 24) - 1; + + dev_dbg(&state->i2c->dev, + "%s: layer %c bitrate: %d kbps; counter = %d (0x%06x)\n", + __func__, 'A' + layer, segment * isdbt_rate[m][f][i]/1000, + rate, rate); + + state->estimated_rate[i] = rate; +} + + static int mb86a20s_get_frontend(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; @@ -514,10 +625,11 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) rc = mb86a20s_get_segment_count(state, i); if (rc < 0) goto noperlayer_error; - if (rc >= 0 && rc < 14) + if (rc >= 0 && rc < 14) { c->layer[i].segment_count = rc; - else { + } else { c->layer[i].segment_count = 0; + state->estimated_rate[i] = 0; continue; } c->isdbt_layer_enabled |= 1 << i; @@ -539,6 +651,10 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) dev_dbg(&state->i2c->dev, "%s: interleaving %d.\n", __func__, rc); c->layer[i].interleaving = rc; + mb86a20s_layer_bitrate(fe, i, c->layer[i].modulation, + c->layer[i].fec, + c->layer[i].interleaving, + c->layer[i].segment_count); } rc = mb86a20s_writereg(state, 0x6d, 0x84); @@ -730,6 +846,42 @@ static int mb86a20s_get_ber_before_vterbi(struct dvb_frontend *fe, __func__, 'A' + layer, *count); + /* + * As we get TMCC data from the frontend, we can better estimate the + * BER bit counters, in order to do the BER measure during a longer + * time. Use those data, if available, to update the bit count + * measure. + */ + + if (state->estimated_rate[layer] + && state->estimated_rate[layer] != *count) { + dev_dbg(&state->i2c->dev, + "%s: updating layer %c counter to %d.\n", + __func__, 'A' + layer, state->estimated_rate[layer]); + rc = mb86a20s_writereg(state, 0x50, 0xa7 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, + state->estimated_rate[layer] >> 16); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x50, 0xa8 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, + state->estimated_rate[layer] >> 8); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x50, 0xa9 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, + state->estimated_rate[layer]); + if (rc < 0) + return rc; + } + + /* Reset counter to collect new data */ rc = mb86a20s_writereg(state, 0x53, 0x07 & ~(1 << layer)); if (rc < 0) @@ -922,8 +1074,10 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe) if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception); mb86a20s_reset_counters(fe); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); -- cgit v1.2.3 From 25188bd0e66f244d8111c4459f2c2f262a13d272 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 16 Jan 2013 15:12:05 -0300 Subject: [media] mb86a20s: add CNR measurement Add Signal/Noise ratio measurement. On this device, a global measure is taken by the demod. It also provides per-layer CNR measurements, based on Modulation Error measures (MER). Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 336 ++++++++++++++++++++++++++++++++- 1 file changed, 334 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index c68e4676e5fd..4f3e222a2bcf 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -118,10 +118,12 @@ static struct regdata mb86a20s_init[] = { { 0x50, 0xb5 }, { 0x51, 0xff }, { 0x50, 0xb6 }, { 0x51, 0xff }, { 0x50, 0xb7 }, { 0x51, 0xff }, - { 0x50, 0x50 }, { 0x51, 0x02 }, + + { 0x50, 0x50 }, { 0x51, 0x02 }, /* MER manual mode */ { 0x50, 0x51 }, { 0x51, 0x04 }, /* MER symbol 4 */ { 0x45, 0x04 }, /* CN symbol 4 */ - { 0x48, 0x04 }, + { 0x48, 0x04 }, /* CN manual mode */ + { 0x50, 0xd5 }, { 0x51, 0x01 }, /* Serial */ { 0x50, 0xd6 }, { 0x51, 0x1f }, { 0x50, 0xd2 }, { 0x51, 0x03 }, @@ -891,6 +893,330 @@ static int mb86a20s_get_ber_before_vterbi(struct dvb_frontend *fe, return 0; } +struct linear_segments { + unsigned x, y; +}; + +/* + * All tables below return a dB/1000 measurement + */ + +static struct linear_segments cnr_to_db_table[] = { + { 19648, 0}, + { 18187, 1000}, + { 16534, 2000}, + { 14823, 3000}, + { 13161, 4000}, + { 11622, 5000}, + { 10279, 6000}, + { 9089, 7000}, + { 8042, 8000}, + { 7137, 9000}, + { 6342, 10000}, + { 5641, 11000}, + { 5030, 12000}, + { 4474, 13000}, + { 3988, 14000}, + { 3556, 15000}, + { 3180, 16000}, + { 2841, 17000}, + { 2541, 18000}, + { 2276, 19000}, + { 2038, 20000}, + { 1800, 21000}, + { 1625, 22000}, + { 1462, 23000}, + { 1324, 24000}, + { 1175, 25000}, + { 1063, 26000}, + { 980, 27000}, + { 907, 28000}, + { 840, 29000}, + { 788, 30000}, +}; + +static struct linear_segments cnr_64qam_table[] = { + { 3922688, 0}, + { 3920384, 1000}, + { 3902720, 2000}, + { 3894784, 3000}, + { 3882496, 4000}, + { 3872768, 5000}, + { 3858944, 6000}, + { 3851520, 7000}, + { 3838976, 8000}, + { 3829248, 9000}, + { 3818240, 10000}, + { 3806976, 11000}, + { 3791872, 12000}, + { 3767040, 13000}, + { 3720960, 14000}, + { 3637504, 15000}, + { 3498496, 16000}, + { 3296000, 17000}, + { 3031040, 18000}, + { 2715392, 19000}, + { 2362624, 20000}, + { 1963264, 21000}, + { 1649664, 22000}, + { 1366784, 23000}, + { 1120768, 24000}, + { 890880, 25000}, + { 723456, 26000}, + { 612096, 27000}, + { 518912, 28000}, + { 448256, 29000}, + { 388864, 30000}, +}; + +static struct linear_segments cnr_16qam_table[] = { + { 5314816, 0}, + { 5219072, 1000}, + { 5118720, 2000}, + { 4998912, 3000}, + { 4875520, 4000}, + { 4736000, 5000}, + { 4604160, 6000}, + { 4458752, 7000}, + { 4300288, 8000}, + { 4092928, 9000}, + { 3836160, 10000}, + { 3521024, 11000}, + { 3155968, 12000}, + { 2756864, 13000}, + { 2347008, 14000}, + { 1955072, 15000}, + { 1593600, 16000}, + { 1297920, 17000}, + { 1043968, 18000}, + { 839680, 19000}, + { 672256, 20000}, + { 523008, 21000}, + { 424704, 22000}, + { 345088, 23000}, + { 280064, 24000}, + { 221440, 25000}, + { 179712, 26000}, + { 151040, 27000}, + { 128512, 28000}, + { 110080, 29000}, + { 95744, 30000}, +}; + +struct linear_segments cnr_qpsk_table[] = { + { 2834176, 0}, + { 2683648, 1000}, + { 2536960, 2000}, + { 2391808, 3000}, + { 2133248, 4000}, + { 1906176, 5000}, + { 1666560, 6000}, + { 1422080, 7000}, + { 1189632, 8000}, + { 976384, 9000}, + { 790272, 10000}, + { 633344, 11000}, + { 505600, 12000}, + { 402944, 13000}, + { 320768, 14000}, + { 255488, 15000}, + { 204032, 16000}, + { 163072, 17000}, + { 130304, 18000}, + { 105216, 19000}, + { 83456, 20000}, + { 65024, 21000}, + { 52480, 22000}, + { 42752, 23000}, + { 34560, 24000}, + { 27136, 25000}, + { 22016, 26000}, + { 18432, 27000}, + { 15616, 28000}, + { 13312, 29000}, + { 11520, 30000}, +}; + +static u32 interpolate_value(u32 value, struct linear_segments *segments, + unsigned len) +{ + u64 tmp64; + u32 dx, dy; + int i, ret; + + if (value >= segments[0].x) + return segments[0].y; + if (value < segments[len-1].x) + return segments[len-1].y; + + for (i = 1; i < len - 1; i++) { + /* If value is identical, no need to interpolate */ + if (value == segments[i].x) + return segments[i].y; + if (value > segments[i].x) + break; + } + + /* Linear interpolation between the two (x,y) points */ + dy = segments[i].y - segments[i - 1].y; + dx = segments[i - 1].x - segments[i].x; + tmp64 = value - segments[i].x; + tmp64 *= dy; + do_div(tmp64, dx); + ret = segments[i].y - tmp64; + + return ret; +} + +static int mb86a20s_get_main_CNR(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 cnr_linear, cnr; + int rc, val; + + /* Check if CNR is available */ + rc = mb86a20s_readreg(state, 0x45); + if (rc < 0) + return rc; + + if (!(rc & 0x40)) { + dev_info(&state->i2c->dev, "%s: CNR is not available yet.\n", + __func__); + return -EBUSY; + } + val = rc; + + rc = mb86a20s_readreg(state, 0x46); + if (rc < 0) + return rc; + cnr_linear = rc << 8; + + rc = mb86a20s_readreg(state, 0x46); + if (rc < 0) + return rc; + cnr_linear |= rc; + + cnr = interpolate_value(cnr_linear, + cnr_to_db_table, ARRAY_SIZE(cnr_to_db_table)); + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = cnr; + + dev_dbg(&state->i2c->dev, "%s: CNR is %d.%03d dB (%d)\n", + __func__, cnr / 1000, cnr % 1000, cnr_linear); + + /* CNR counter reset */ + rc = mb86a20s_writereg(state, 0x45, val | 0x10); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x45, val & 0x6f); + + return rc; +} + +static int mb86a20s_get_per_layer_CNR(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 mer, cnr; + int rc, val, i; + struct linear_segments *segs; + unsigned segs_len; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + /* Check if the measures are already available */ + rc = mb86a20s_writereg(state, 0x50, 0x5b); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + + /* Check if data is available */ + if (!(rc & 0x01)) { + dev_info(&state->i2c->dev, + "%s: MER measures aren't available yet.\n", __func__); + return -EBUSY; + } + + /* Read all layers */ + for (i = 0; i < 3; i++) { + if (!(c->isdbt_layer_enabled & (1 << i))) { + c->cnr.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE; + continue; + } + + rc = mb86a20s_writereg(state, 0x50, 0x52 + i * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + mer = rc << 16; + rc = mb86a20s_writereg(state, 0x50, 0x53 + i * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + mer |= rc << 8; + rc = mb86a20s_writereg(state, 0x50, 0x54 + i * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + mer |= rc; + + switch (c->layer[i].modulation) { + case DQPSK: + case QPSK: + segs = cnr_qpsk_table; + segs_len = ARRAY_SIZE(cnr_qpsk_table); + break; + case QAM_16: + segs = cnr_16qam_table; + segs_len = ARRAY_SIZE(cnr_16qam_table); + break; + default: + case QAM_64: + segs = cnr_64qam_table; + segs_len = ARRAY_SIZE(cnr_64qam_table); + break; + } + cnr = interpolate_value(mer, segs, segs_len); + + c->cnr.stat[1 + i].scale = FE_SCALE_DECIBEL; + c->cnr.stat[1 + i].svalue = cnr; + + dev_dbg(&state->i2c->dev, + "%s: CNR for layer %c is %d.%03d dB (MER = %d).\n", + __func__, 'A' + i, cnr / 1000, cnr % 1000, mer); + + } + + /* Start a new MER measurement */ + /* MER counter reset */ + rc = mb86a20s_writereg(state, 0x50, 0x50); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + val = rc; + + rc = mb86a20s_writereg(state, 0x51, val | 0x01); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, val & 0x06); + if (rc < 0) + return rc; + + return 0; +} + static void mb86a20s_stats_not_ready(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; @@ -934,7 +1260,13 @@ static int mb86a20s_get_stats(struct dvb_frontend *fe) u32 t_pre_bit_error = 0, t_pre_bit_count = 0; int active_layers = 0, ber_layers = 0; + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + mb86a20s_get_main_CNR(fe); + /* Get per-layer stats */ + mb86a20s_get_per_layer_CNR(fe); + for (i = 0; i < 3; i++) { if (c->isdbt_layer_enabled & (1 << i)) { /* Layer is active and has rc segments */ -- cgit v1.2.3 From 94a93e5f85040114d6a77c085457b3943b6da889 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 23 Jan 2013 17:06:02 -0300 Subject: [media] dvb_frontend: print a msg if a property doesn't exist If userspace calls a property that doesn't exist, it currently just returns -EINVAL. However, this is more likely a problem at the userspace application, calling it with a non-existing property. So, add a debug message to help tracking it. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_frontend.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 0c6f936ffac8..b059abf572d6 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -1479,6 +1479,9 @@ static int dtv_property_process_get(struct dvb_frontend *fe, tvp->u.st = c->block_count; break; default: + dev_dbg(fe->dvb->device, + "%s: FE property %d doesn't exist\n", + __func__, tvp->cmd); return -EINVAL; } -- cgit v1.2.3 From ea4f3111ef0daffa1d11fded6f375227febca458 Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Thu, 22 Nov 2012 13:11:32 +0100 Subject: viafb: rename display_timing to via_display_timing The struct display_timing is specific to the via subsystem. The naming leads to collisions with the new struct display_timing, which is supposed to be a shared struct between different subsystems. To clean this up, prepend the existing struct with the subsystem it is specific to. Signed-off-by: Steffen Trumtrar --- drivers/video/via/hw.c | 6 +++--- drivers/video/via/hw.h | 2 +- drivers/video/via/lcd.c | 2 +- drivers/video/via/share.h | 2 +- drivers/video/via/via_modesetting.c | 8 ++++---- drivers/video/via/via_modesetting.h | 6 +++--- 6 files changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/video/via/hw.c b/drivers/video/via/hw.c index 80233dae358a..22450908306c 100644 --- a/drivers/video/via/hw.c +++ b/drivers/video/via/hw.c @@ -1467,10 +1467,10 @@ void viafb_set_vclock(u32 clk, int set_iga) via_write_misc_reg_mask(0x0C, 0x0C); /* select external clock */ } -struct display_timing var_to_timing(const struct fb_var_screeninfo *var, +struct via_display_timing var_to_timing(const struct fb_var_screeninfo *var, u16 cxres, u16 cyres) { - struct display_timing timing; + struct via_display_timing timing; u16 dx = (var->xres - cxres) / 2, dy = (var->yres - cyres) / 2; timing.hor_addr = cxres; @@ -1491,7 +1491,7 @@ struct display_timing var_to_timing(const struct fb_var_screeninfo *var, void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var, u16 cxres, u16 cyres, int iga) { - struct display_timing crt_reg = var_to_timing(var, + struct via_display_timing crt_reg = var_to_timing(var, cxres ? cxres : var->xres, cyres ? cyres : var->yres); if (iga == IGA1) diff --git a/drivers/video/via/hw.h b/drivers/video/via/hw.h index a8205754c736..3be073c58b03 100644 --- a/drivers/video/via/hw.h +++ b/drivers/video/via/hw.h @@ -637,7 +637,7 @@ extern int viafb_LCD_ON; extern int viafb_DVI_ON; extern int viafb_hotplug; -struct display_timing var_to_timing(const struct fb_var_screeninfo *var, +struct via_display_timing var_to_timing(const struct fb_var_screeninfo *var, u16 cxres, u16 cyres); void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var, u16 cxres, u16 cyres, int iga); diff --git a/drivers/video/via/lcd.c b/drivers/video/via/lcd.c index 980ee1b1dcf3..5d21ff436ec8 100644 --- a/drivers/video/via/lcd.c +++ b/drivers/video/via/lcd.c @@ -549,7 +549,7 @@ void viafb_lcd_set_mode(const struct fb_var_screeninfo *var, u16 cxres, int panel_hres = plvds_setting_info->lcd_panel_hres; int panel_vres = plvds_setting_info->lcd_panel_vres; u32 clock; - struct display_timing timing; + struct via_display_timing timing; struct fb_var_screeninfo panel_var; const struct fb_videomode *mode_crt_table, *panel_crt_table; diff --git a/drivers/video/via/share.h b/drivers/video/via/share.h index 3158dfc90bed..65c65c611e0a 100644 --- a/drivers/video/via/share.h +++ b/drivers/video/via/share.h @@ -319,7 +319,7 @@ struct crt_mode_table { int refresh_rate; int h_sync_polarity; int v_sync_polarity; - struct display_timing crtc; + struct via_display_timing crtc; }; struct io_reg { diff --git a/drivers/video/via/via_modesetting.c b/drivers/video/via/via_modesetting.c index 0e431aee17bb..0b414b09b9b4 100644 --- a/drivers/video/via/via_modesetting.c +++ b/drivers/video/via/via_modesetting.c @@ -30,9 +30,9 @@ #include "debug.h" -void via_set_primary_timing(const struct display_timing *timing) +void via_set_primary_timing(const struct via_display_timing *timing) { - struct display_timing raw; + struct via_display_timing raw; raw.hor_total = timing->hor_total / 8 - 5; raw.hor_addr = timing->hor_addr / 8 - 1; @@ -88,9 +88,9 @@ void via_set_primary_timing(const struct display_timing *timing) via_write_reg_mask(VIACR, 0x17, 0x80, 0x80); } -void via_set_secondary_timing(const struct display_timing *timing) +void via_set_secondary_timing(const struct via_display_timing *timing) { - struct display_timing raw; + struct via_display_timing raw; raw.hor_total = timing->hor_total - 1; raw.hor_addr = timing->hor_addr - 1; diff --git a/drivers/video/via/via_modesetting.h b/drivers/video/via/via_modesetting.h index 06e09fe351ae..f6a6503da3b3 100644 --- a/drivers/video/via/via_modesetting.h +++ b/drivers/video/via/via_modesetting.h @@ -33,7 +33,7 @@ #define VIA_PITCH_MAX 0x3FF8 -struct display_timing { +struct via_display_timing { u16 hor_total; u16 hor_addr; u16 hor_blank_start; @@ -49,8 +49,8 @@ struct display_timing { }; -void via_set_primary_timing(const struct display_timing *timing); -void via_set_secondary_timing(const struct display_timing *timing); +void via_set_primary_timing(const struct via_display_timing *timing); +void via_set_secondary_timing(const struct via_display_timing *timing); void via_set_primary_address(u32 addr); void via_set_secondary_address(u32 addr); void via_set_primary_pitch(u32 pitch); -- cgit v1.2.3 From 8714c0cecfc28f7ce73a520be4831f09743c4fd7 Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Mon, 17 Dec 2012 14:20:17 +0100 Subject: video: add display_timing and videomode Add display_timing structure and the according helper functions. This allows the description of a display via its supported timing parameters. Also, add helper functions to convert from display timings to a generic videomode structure. The struct display_timing specifies all needed parameters to describe the signal properties of a display in one mode. This includes - ranges for signals that may have min-, max- and typical values - single integers for signals that can be on, off or are ignored - booleans for signals that are either on or off As a display may support multiple modes like this, a struct display_timings is added, that holds all given struct display_timing pointers and declares the native mode of the display. Although a display may state that a signal can be in a range, it is driven with fixed values that indicate a videomode. Therefore graphic drivers don't need all the information of struct display_timing, but would generate a videomode from the given set of supported signal timings and work with that. The video subsystems all define their own structs that describe a mode and work with that (e.g. fb_videomode or drm_display_mode). To slowly replace all those various structures and allow code reuse across those subsystems, add struct videomode as a generic description. This patch only includes the most basic fields in struct videomode. All missing fields that are needed to have a really generic video mode description can be added at a later stage. Signed-off-by: Steffen Trumtrar Reviewed-by: Thierry Reding Acked-by: Thierry Reding Tested-by: Thierry Reding Tested-by: Philipp Zabel Reviewed-by: Laurent Pinchart Acked-by: Laurent Pinchart Tested-by: Afzal Mohammed Tested-by: Rob Clark Tested-by: Leela Krishna Amudala --- drivers/video/Kconfig | 6 ++ drivers/video/Makefile | 2 + drivers/video/display_timing.c | 24 ++++++++ drivers/video/videomode.c | 39 +++++++++++++ include/video/display_timing.h | 124 +++++++++++++++++++++++++++++++++++++++++ include/video/videomode.h | 48 ++++++++++++++++ 6 files changed, 243 insertions(+) create mode 100644 drivers/video/display_timing.c create mode 100644 drivers/video/videomode.c create mode 100644 include/video/display_timing.h create mode 100644 include/video/videomode.h (limited to 'drivers') diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index e7068c508800..09a8f0d8a3d4 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -33,6 +33,12 @@ config VIDEO_OUTPUT_CONTROL This framework adds support for low-level control of the video output switch. +config DISPLAY_TIMING + bool + +config VIDEOMODE + bool + menuconfig FB tristate "Support for frame buffer devices" ---help--- diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 768a137a1bac..e0dd8202365f 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -168,3 +168,5 @@ obj-$(CONFIG_FB_VIRTUAL) += vfb.o #video output switch sysfs driver obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o +obj-$(CONFIG_DISPLAY_TIMING) += display_timing.o +obj-$(CONFIG_VIDEOMODE) += videomode.o diff --git a/drivers/video/display_timing.c b/drivers/video/display_timing.c new file mode 100644 index 000000000000..5e1822cef571 --- /dev/null +++ b/drivers/video/display_timing.c @@ -0,0 +1,24 @@ +/* + * generic display timing functions + * + * Copyright (c) 2012 Steffen Trumtrar , Pengutronix + * + * This file is released under the GPLv2 + */ + +#include +#include +#include + + V4L2_EVENT_CTRL_CH_RANGE + 0x0004 + This control event was triggered because the minimum, + maximum, step or the default value of the control changed. + diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 7b486ac3f4d9..3f27571b814d 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1158,8 +1158,7 @@ static int new_to_user(struct v4l2_ext_control *c, } /* Copy the new value to the current value. */ -static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, - bool update_inactive) +static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags) { bool changed = false; @@ -1183,8 +1182,8 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, ctrl->cur.val = ctrl->val; break; } - if (update_inactive) { - /* Note: update_inactive can only be true for auto clusters. */ + if (ch_flags & V4L2_EVENT_CTRL_CH_FLAGS) { + /* Note: CH_FLAGS is only set for auto clusters. */ ctrl->flags &= ~(V4L2_CTRL_FLAG_INACTIVE | V4L2_CTRL_FLAG_VOLATILE); if (!is_cur_manual(ctrl->cluster[0])) { @@ -1194,14 +1193,13 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, } fh = NULL; } - if (changed || update_inactive) { + if (changed || ch_flags) { /* If a control was changed that was not one of the controls modified by the application, then send the event to all. */ if (!ctrl->is_new) fh = NULL; send_event(fh, ctrl, - (changed ? V4L2_EVENT_CTRL_CH_VALUE : 0) | - (update_inactive ? V4L2_EVENT_CTRL_CH_FLAGS : 0)); + (changed ? V4L2_EVENT_CTRL_CH_VALUE : 0) | ch_flags); if (ctrl->call_notify && changed && ctrl->handler->notify) ctrl->handler->notify(ctrl, ctrl->handler->notify_priv); } @@ -1257,6 +1255,41 @@ static int cluster_changed(struct v4l2_ctrl *master) return diff; } +/* Control range checking */ +static int check_range(enum v4l2_ctrl_type type, + s32 min, s32 max, u32 step, s32 def) +{ + switch (type) { + case V4L2_CTRL_TYPE_BOOLEAN: + if (step != 1 || max > 1 || min < 0) + return -ERANGE; + /* fall through */ + case V4L2_CTRL_TYPE_INTEGER: + if (step <= 0 || min > max || def < min || def > max) + return -ERANGE; + return 0; + case V4L2_CTRL_TYPE_BITMASK: + if (step || min || !max || (def & ~max)) + return -ERANGE; + return 0; + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: + if (min > max || def < min || def > max) + return -ERANGE; + /* Note: step == menu_skip_mask for menu controls. + So here we check if the default value is masked out. */ + if (step && ((1 << def) & step)) + return -EINVAL; + return 0; + case V4L2_CTRL_TYPE_STRING: + if (min > max || min < 0 || step < 1 || def) + return -ERANGE; + return 0; + default: + return 0; + } +} + /* Validate a new control */ static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c) @@ -1529,30 +1562,21 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, { struct v4l2_ctrl *ctrl; unsigned sz_extra = 0; + int err; if (hdl->error) return NULL; /* Sanity checks */ if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE || - (type == V4L2_CTRL_TYPE_INTEGER && step == 0) || - (type == V4L2_CTRL_TYPE_BITMASK && max == 0) || (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) || - (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL) || - (type == V4L2_CTRL_TYPE_STRING && max == 0)) { - handler_set_err(hdl, -ERANGE); - return NULL; - } - if (type != V4L2_CTRL_TYPE_BITMASK && max < min) { + (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL)) { handler_set_err(hdl, -ERANGE); return NULL; } - if ((type == V4L2_CTRL_TYPE_INTEGER || - type == V4L2_CTRL_TYPE_MENU || - type == V4L2_CTRL_TYPE_INTEGER_MENU || - type == V4L2_CTRL_TYPE_BOOLEAN) && - (def < min || def > max)) { - handler_set_err(hdl, -ERANGE); + err = check_range(type, min, max, step, def); + if (err) { + handler_set_err(hdl, err); return NULL; } if (type == V4L2_CTRL_TYPE_BITMASK && ((def & ~max) || min || step)) { @@ -2426,8 +2450,8 @@ EXPORT_SYMBOL(v4l2_ctrl_g_ctrl_int64); /* Core function that calls try/s_ctrl and ensures that the new value is copied to the current value on a set. Must be called with ctrl->handler->lock held. */ -static int try_or_set_cluster(struct v4l2_fh *fh, - struct v4l2_ctrl *master, bool set) +static int try_or_set_cluster(struct v4l2_fh *fh, struct v4l2_ctrl *master, + bool set, u32 ch_flags) { bool update_flag; int ret; @@ -2465,7 +2489,8 @@ static int try_or_set_cluster(struct v4l2_fh *fh, /* If OK, then make the new values permanent. */ update_flag = is_cur_manual(master) != is_new_manual(master); for (i = 0; i < master->ncontrols; i++) - new_to_cur(fh, master->cluster[i], update_flag && i > 0); + new_to_cur(fh, master->cluster[i], ch_flags | + ((update_flag && i > 0) ? V4L2_EVENT_CTRL_CH_FLAGS : 0)); return 0; } @@ -2592,7 +2617,7 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, } while (!ret && idx); if (!ret) - ret = try_or_set_cluster(fh, master, set); + ret = try_or_set_cluster(fh, master, set, 0); /* Copy the new values back to userspace. */ if (!ret) { @@ -2638,10 +2663,9 @@ EXPORT_SYMBOL(v4l2_subdev_s_ext_ctrls); /* Helper function for VIDIOC_S_CTRL compatibility */ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, - struct v4l2_ext_control *c) + struct v4l2_ext_control *c, u32 ch_flags) { struct v4l2_ctrl *master = ctrl->cluster[0]; - int ret; int i; /* String controls are not supported. The user_to_new() and @@ -2651,12 +2675,6 @@ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, if (ctrl->type == V4L2_CTRL_TYPE_STRING) return -EINVAL; - ret = validate_new(ctrl, c); - if (ret) - return ret; - - v4l2_ctrl_lock(ctrl); - /* Reset the 'is_new' flags of the cluster */ for (i = 0; i < master->ncontrols; i++) if (master->cluster[i]) @@ -2670,10 +2688,22 @@ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, update_from_auto_cluster(master); user_to_new(c, ctrl); - ret = try_or_set_cluster(fh, master, true); - cur_to_user(c, ctrl); + return try_or_set_cluster(fh, master, true, ch_flags); +} - v4l2_ctrl_unlock(ctrl); +/* Helper function for VIDIOC_S_CTRL compatibility */ +static int set_ctrl_lock(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, + struct v4l2_ext_control *c) +{ + int ret = validate_new(ctrl, c); + + if (!ret) { + v4l2_ctrl_lock(ctrl); + ret = set_ctrl(fh, ctrl, c, 0); + if (!ret) + cur_to_user(c, ctrl); + v4l2_ctrl_unlock(ctrl); + } return ret; } @@ -2691,7 +2721,7 @@ int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, return -EACCES; c.value = control->value; - ret = set_ctrl(fh, ctrl, &c); + ret = set_ctrl_lock(fh, ctrl, &c); control->value = c.value; return ret; } @@ -2710,7 +2740,7 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) /* It's a driver bug if this happens. */ WARN_ON(!type_is_int(ctrl)); c.value = val; - return set_ctrl(NULL, ctrl, &c); + return set_ctrl_lock(NULL, ctrl, &c); } EXPORT_SYMBOL(v4l2_ctrl_s_ctrl); @@ -2721,7 +2751,7 @@ int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) /* It's a driver bug if this happens. */ WARN_ON(ctrl->type != V4L2_CTRL_TYPE_INTEGER64); c.value64 = val; - return set_ctrl(NULL, ctrl, &c); + return set_ctrl_lock(NULL, ctrl, &c); } EXPORT_SYMBOL(v4l2_ctrl_s_ctrl_int64); @@ -2741,6 +2771,41 @@ void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void } EXPORT_SYMBOL(v4l2_ctrl_notify); +int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, + s32 min, s32 max, u32 step, s32 def) +{ + int ret = check_range(ctrl->type, min, max, step, def); + struct v4l2_ext_control c; + + switch (ctrl->type) { + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: + case V4L2_CTRL_TYPE_BITMASK: + if (ret) + return ret; + break; + default: + return -EINVAL; + } + v4l2_ctrl_lock(ctrl); + ctrl->minimum = min; + ctrl->maximum = max; + ctrl->step = step; + ctrl->default_value = def; + c.value = ctrl->cur.val; + if (validate_new(ctrl, &c)) + c.value = def; + if (c.value != ctrl->cur.val) + ret = set_ctrl(NULL, ctrl, &c, V4L2_EVENT_CTRL_CH_RANGE); + else + send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_RANGE); + v4l2_ctrl_unlock(ctrl); + return ret; +} +EXPORT_SYMBOL(v4l2_ctrl_modify_range); + static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems) { struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id); diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index c4cc04136074..91125b6f05a5 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -518,6 +518,26 @@ void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active); */ void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed); +/** v4l2_ctrl_modify_range() - Update the range of a control. + * @ctrl: The control to update. + * @min: The control's minimum value. + * @max: The control's maximum value. + * @step: The control's step value + * @def: The control's default value. + * + * Update the range of a control on the fly. This works for control types + * INTEGER, BOOLEAN, MENU, INTEGER MENU and BITMASK. For menu controls the + * @step value is interpreted as a menu_skip_mask. + * + * An error is returned if one of the range arguments is invalid for this + * control type. + * + * This function assumes that the control handler is not locked and will + * take the lock itself. + */ +int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, + s32 min, s32 max, u32 step, s32 def); + /** v4l2_ctrl_lock() - Helper function to lock the handler * associated with the control. * @ctrl: The control to lock. diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 94cbe26e9f00..928799c2e2d9 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1822,6 +1822,7 @@ struct v4l2_event_vsync { /* Payload for V4L2_EVENT_CTRL */ #define V4L2_EVENT_CTRL_CH_VALUE (1 << 0) #define V4L2_EVENT_CTRL_CH_FLAGS (1 << 1) +#define V4L2_EVENT_CTRL_CH_RANGE (1 << 2) struct v4l2_event_ctrl { __u32 changes; -- cgit v1.2.3 From 4f4d14b70a29c679dd53e367b0d9b007a7117ee3 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 22 Jan 2013 18:58:57 -0300 Subject: [media] V4L: Add v4l2_event_subdev_unsubscribe() helper function Add a v4l2 core helper function that can be used as the subdev .unsubscribe_event handler. This allows to eliminate some boilerplate from drivers that are only handling the control events. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-event.c | 7 +++++++ include/media/v4l2-event.h | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/v4l2-core/v4l2-event.c b/drivers/media/v4l2-core/v4l2-event.c index c72009218152..86dcb5483c42 100644 --- a/drivers/media/v4l2-core/v4l2-event.c +++ b/drivers/media/v4l2-core/v4l2-event.c @@ -311,3 +311,10 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh, return 0; } EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe); + +int v4l2_event_subdev_unsubscribe(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + return v4l2_event_unsubscribe(fh, sub); +} +EXPORT_SYMBOL_GPL(v4l2_event_subdev_unsubscribe); diff --git a/include/media/v4l2-event.h b/include/media/v4l2-event.h index eff85f934b24..be05d019de25 100644 --- a/include/media/v4l2-event.h +++ b/include/media/v4l2-event.h @@ -64,6 +64,7 @@ */ struct v4l2_fh; +struct v4l2_subdev; struct v4l2_subscribed_event; struct video_device; @@ -129,5 +130,6 @@ int v4l2_event_subscribe(struct v4l2_fh *fh, int v4l2_event_unsubscribe(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub); void v4l2_event_unsubscribe_all(struct v4l2_fh *fh); - +int v4l2_event_subdev_unsubscribe(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub); #endif /* V4L2_EVENT_H */ -- cgit v1.2.3 From 22fa4279eebc3fa4b3c3dc2b190158dbbafcda9f Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 22 Jan 2013 19:00:23 -0300 Subject: [media] V4L: Add v4l2_ctrl_subdev_subscribe_event() helper function Add a v4l2 core helper function that can be used as the subdev .subscribe_event handler. This allows to eliminate some boilerplate from drivers that are only handling the control events. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 9 +++++++++ include/media/v4l2-ctrls.h | 5 +++++ 2 files changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 3f27571b814d..9051ec53875c 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -2885,6 +2885,15 @@ int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh, } EXPORT_SYMBOL(v4l2_ctrl_subscribe_event); +int v4l2_ctrl_subdev_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + if (!sd->ctrl_handler) + return -EINVAL; + return v4l2_ctrl_subscribe_event(fh, sub); +} +EXPORT_SYMBOL(v4l2_ctrl_subdev_subscribe_event); + unsigned int v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait) { struct v4l2_fh *fh = file->private_data; diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 91125b6f05a5..1e849461fc9d 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -654,4 +654,9 @@ int v4l2_subdev_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs int v4l2_subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl); int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl); +/* Can be used as a subscribe_event function that just subscribes control + events. */ +int v4l2_ctrl_subdev_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub); + #endif -- cgit v1.2.3 From ffa9b9f016a9c97a3cc205d0d634b10d8f72eb36 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 22 Jan 2013 19:01:02 -0300 Subject: [media] V4L: Add v4l2_ctrl_subdev_log_status() helper function This patch adds a v4l2 core helper function that can be used as the log_status handler for subdevs that only need to log state of the v4l2 controls owned by the subdev's control handler. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 7 +++++++ include/media/v4l2-ctrls.h | 3 +++ 2 files changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 9051ec53875c..6b28b5800500 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -2004,6 +2004,13 @@ void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl, } EXPORT_SYMBOL(v4l2_ctrl_handler_log_status); +int v4l2_ctrl_subdev_log_status(struct v4l2_subdev *sd) +{ + v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name); + return 0; +} +EXPORT_SYMBOL(v4l2_ctrl_subdev_log_status); + /* Call s_ctrl for all controls owned by the handler */ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl) { diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 1e849461fc9d..f00d42bc01a6 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -659,4 +659,7 @@ int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl); int v4l2_ctrl_subdev_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, struct v4l2_event_subscription *sub); +/* Log all controls owned by subdev's control handler. */ +int v4l2_ctrl_subdev_log_status(struct v4l2_subdev *sd); + #endif -- cgit v1.2.3 From 84a15ded76ec8ec23d84974238b7864813143655 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 26 Dec 2012 15:50:03 -0300 Subject: [media] V4L: Add driver for OV9650/52 image sensors This patch adds V4L2 sub-device driver for OV9650/OV9652 image sensors. The driver exposes following V4L2 controls: - auto/manual exposure, - auto/manual white balance, - auto/manual gain, - brightness, saturation, sharpness, - horizontal/vertical flip, - color bar test pattern, - banding filter (power line frequency). Frame rate can be configured with g/s_frame_interval pad level ops. Supported resolution are only: SXGA, VGA, QVGA. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 7 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ov9650.c | 1562 ++++++++++++++++++++++++++++++++++++++++++++ include/media/ov9650.h | 27 + 4 files changed, 1597 insertions(+) create mode 100644 drivers/media/i2c/ov9650.c create mode 100644 include/media/ov9650.h (limited to 'drivers') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 1e4b2d0f23ce..d73078cdffec 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -421,6 +421,13 @@ config VIDEO_OV7670 OV7670 VGA camera. It currently only works with the M88ALP01 controller. +config VIDEO_OV9650 + tristate "OmniVision OV9650/OV9652 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + ---help--- + This is a V4L2 sensor-level driver for the Omnivision + OV9650 and OV9652 camera sensors. + config VIDEO_VS6624 tristate "ST VS6624 sensor support" depends on VIDEO_V4L2 && I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index b1d62dfd49b8..8b62e54d3884 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_VIDEO_OV7670) += ov7670.o +obj-$(CONFIG_VIDEO_OV9650) += ov9650.o obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c new file mode 100644 index 000000000000..1dbb8118a285 --- /dev/null +++ b/drivers/media/i2c/ov9650.c @@ -0,0 +1,1562 @@ +/* + * Omnivision OV9650/OV9652 CMOS Image Sensor driver + * + * Copyright (C) 2013, Sylwester Nawrocki + * + * Register definitions and initial settings based on a driver written + * by Vladimir Fonov. + * Copyright (c) 2010, Vladimir Fonov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + +#define DRIVER_NAME "OV9650" + +/* + * OV9650/OV9652 register definitions + */ +#define REG_GAIN 0x00 /* Gain control, AGC[7:0] */ +#define REG_BLUE 0x01 /* AWB - Blue chanel gain */ +#define REG_RED 0x02 /* AWB - Red chanel gain */ +#define REG_VREF 0x03 /* [7:6] - AGC[9:8], [5:3]/[2:0] */ +#define VREF_GAIN_MASK 0xc0 /* - VREF end/start low 3 bits */ +#define REG_COM1 0x04 +#define COM1_CCIR656 0x40 +#define REG_B_AVE 0x05 +#define REG_GB_AVE 0x06 +#define REG_GR_AVE 0x07 +#define REG_R_AVE 0x08 +#define REG_COM2 0x09 +#define REG_PID 0x0a /* Product ID MSB */ +#define REG_VER 0x0b /* Product ID LSB */ +#define REG_COM3 0x0c +#define COM3_SWAP 0x40 +#define COM3_VARIOPIXEL1 0x04 +#define REG_COM4 0x0d /* Vario Pixels */ +#define COM4_VARIOPIXEL2 0x80 +#define REG_COM5 0x0e /* System clock options */ +#define COM5_SLAVE_MODE 0x10 +#define COM5_SYSTEMCLOCK48MHZ 0x80 +#define REG_COM6 0x0f /* HREF & ADBLC options */ +#define REG_AECH 0x10 /* Exposure value, AEC[9:2] */ +#define REG_CLKRC 0x11 /* Clock control */ +#define CLK_EXT 0x40 /* Use external clock directly */ +#define CLK_SCALE 0x3f /* Mask for internal clock scale */ +#define REG_COM7 0x12 /* SCCB reset, output format */ +#define COM7_RESET 0x80 +#define COM7_FMT_MASK 0x38 +#define COM7_FMT_VGA 0x40 +#define COM7_FMT_CIF 0x20 +#define COM7_FMT_QVGA 0x10 +#define COM7_FMT_QCIF 0x08 +#define COM7_RGB 0x04 +#define COM7_YUV 0x00 +#define COM7_BAYER 0x01 +#define COM7_PBAYER 0x05 +#define REG_COM8 0x13 /* AGC/AEC options */ +#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ +#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ +#define COM8_BFILT 0x20 /* Band filter enable */ +#define COM8_AGC 0x04 /* Auto gain enable */ +#define COM8_AWB 0x02 /* White balance enable */ +#define COM8_AEC 0x01 /* Auto exposure enable */ +#define REG_COM9 0x14 /* Gain ceiling */ +#define COM9_GAIN_CEIL_MASK 0x70 /* */ +#define REG_COM10 0x15 /* PCLK, HREF, HSYNC signals polarity */ +#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ +#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ +#define COM10_HREF_REV 0x08 /* Reverse HREF */ +#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ +#define COM10_VS_NEG 0x02 /* VSYNC negative */ +#define COM10_HS_NEG 0x01 /* HSYNC negative */ +#define REG_HSTART 0x17 /* Horiz start high bits */ +#define REG_HSTOP 0x18 /* Horiz stop high bits */ +#define REG_VSTART 0x19 /* Vert start high bits */ +#define REG_VSTOP 0x1a /* Vert stop high bits */ +#define REG_PSHFT 0x1b /* Pixel delay after HREF */ +#define REG_MIDH 0x1c /* Manufacturer ID MSB */ +#define REG_MIDL 0x1d /* Manufufacturer ID LSB */ +#define REG_MVFP 0x1e /* Image mirror/flip */ +#define MVFP_MIRROR 0x20 /* Mirror image */ +#define MVFP_FLIP 0x10 /* Vertical flip */ +#define REG_BOS 0x20 /* B channel Offset */ +#define REG_GBOS 0x21 /* Gb channel Offset */ +#define REG_GROS 0x22 /* Gr channel Offset */ +#define REG_ROS 0x23 /* R channel Offset */ +#define REG_AEW 0x24 /* AGC upper limit */ +#define REG_AEB 0x25 /* AGC lower limit */ +#define REG_VPT 0x26 /* AGC/AEC fast mode op region */ +#define REG_BBIAS 0x27 /* B channel output bias */ +#define REG_GBBIAS 0x28 /* Gb channel output bias */ +#define REG_GRCOM 0x29 /* Analog BLC & regulator */ +#define REG_EXHCH 0x2a /* Dummy pixel insert MSB */ +#define REG_EXHCL 0x2b /* Dummy pixel insert LSB */ +#define REG_RBIAS 0x2c /* R channel output bias */ +#define REG_ADVFL 0x2d /* LSB of dummy line insert */ +#define REG_ADVFH 0x2e /* MSB of dummy line insert */ +#define REG_YAVE 0x2f /* Y/G channel average value */ +#define REG_HSYST 0x30 /* HSYNC rising edge delay LSB*/ +#define REG_HSYEN 0x31 /* HSYNC falling edge delay LSB*/ +#define REG_HREF 0x32 /* HREF pieces */ +#define REG_CHLF 0x33 /* reserved */ +#define REG_ADC 0x37 /* reserved */ +#define REG_ACOM 0x38 /* reserved */ +#define REG_OFON 0x39 /* Power down register */ +#define OFON_PWRDN 0x08 /* Power down bit */ +#define REG_TSLB 0x3a /* YUVU format */ +#define TSLB_YUYV_MASK 0x0c /* UYVY or VYUY - see com13 */ +#define REG_COM11 0x3b /* Night mode, banding filter enable */ +#define COM11_NIGHT 0x80 /* Night mode enable */ +#define COM11_NMFR 0x60 /* Two bit NM frame rate */ +#define COM11_BANDING 0x01 /* Banding filter */ +#define COM11_AEC_REF_MASK 0x18 /* AEC reference area selection */ +#define REG_COM12 0x3c /* HREF option, UV average */ +#define COM12_HREF 0x80 /* HREF always */ +#define REG_COM13 0x3d /* Gamma selection, Color matrix en. */ +#define COM13_GAMMA 0x80 /* Gamma enable */ +#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ +#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ +#define REG_COM14 0x3e /* Edge enhancement options */ +#define COM14_EDGE_EN 0x02 +#define COM14_EEF_X2 0x01 +#define REG_EDGE 0x3f /* Edge enhancement factor */ +#define EDGE_FACTOR_MASK 0x0f +#define REG_COM15 0x40 /* Output range, RGB 555/565 */ +#define COM15_R10F0 0x00 /* Data range 10 to F0 */ +#define COM15_R01FE 0x80 /* 01 to FE */ +#define COM15_R00FF 0xc0 /* 00 to FF */ +#define COM15_RGB565 0x10 /* RGB565 output */ +#define COM15_RGB555 0x30 /* RGB555 output */ +#define COM15_SWAPRB 0x04 /* Swap R&B */ +#define REG_COM16 0x41 /* Color matrix coeff options */ +#define REG_COM17 0x42 /* Single frame out, banding filter */ +/* n = 1...9, 0x4f..0x57 */ +#define REG_MTX(__n) (0x4f + (__n) - 1) +#define REG_MTXS 0x58 +/* Lens Correction Option 1...5, __n = 0...5 */ +#define REG_LCC(__n) (0x62 + (__n) - 1) +#define LCC5_LCC_ENABLE 0x01 /* LCC5, enable lens correction */ +#define LCC5_LCC_COLOR 0x04 +#define REG_MANU 0x67 /* Manual U value */ +#define REG_MANV 0x68 /* Manual V value */ +#define REG_HV 0x69 /* Manual banding filter MSB */ +#define REG_MBD 0x6a /* Manual banding filter value */ +#define REG_DBLV 0x6b /* reserved */ +#define REG_GSP 0x6c /* Gamma curve */ +#define GSP_LEN 15 +#define REG_GST 0x7c /* Gamma curve */ +#define GST_LEN 15 +#define REG_COM21 0x8b +#define REG_COM22 0x8c /* Edge enhancement, denoising */ +#define COM22_WHTPCOR 0x02 /* White pixel correction enable */ +#define COM22_WHTPCOROPT 0x01 /* White pixel correction option */ +#define COM22_DENOISE 0x10 /* White pixel correction option */ +#define REG_COM23 0x8d /* Color bar test, color gain */ +#define COM23_TEST_MODE 0x10 +#define REG_DBLC1 0x8f /* Digital BLC */ +#define REG_DBLC_B 0x90 /* Digital BLC B channel offset */ +#define REG_DBLC_R 0x91 /* Digital BLC R channel offset */ +#define REG_DM_LNL 0x92 /* Dummy line low 8 bits */ +#define REG_DM_LNH 0x93 /* Dummy line high 8 bits */ +#define REG_LCCFB 0x9d /* Lens Correction B channel */ +#define REG_LCCFR 0x9e /* Lens Correction R channel */ +#define REG_DBLC_GB 0x9f /* Digital BLC GB chan offset */ +#define REG_DBLC_GR 0xa0 /* Digital BLC GR chan offset */ +#define REG_AECHM 0xa1 /* Exposure value - bits AEC[15:10] */ +#define REG_BD50ST 0xa2 /* Banding filter value for 50Hz */ +#define REG_BD60ST 0xa3 /* Banding filter value for 60Hz */ +#define REG_NULL 0xff /* Array end token */ + +#define DEF_CLKRC 0x80 + +#define OV965X_ID(_msb, _lsb) ((_msb) << 8 | (_lsb)) +#define OV9650_ID 0x9650 +#define OV9652_ID 0x9652 + +struct ov965x_ctrls { + struct v4l2_ctrl_handler handler; + struct { + struct v4l2_ctrl *auto_exp; + struct v4l2_ctrl *exposure; + }; + struct { + struct v4l2_ctrl *auto_wb; + struct v4l2_ctrl *blue_balance; + struct v4l2_ctrl *red_balance; + }; + struct { + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; + struct { + struct v4l2_ctrl *auto_gain; + struct v4l2_ctrl *gain; + }; + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *sharpness; + struct v4l2_ctrl *light_freq; + u8 update; +}; + +struct ov965x_framesize { + u16 width; + u16 height; + u16 max_exp_lines; + const u8 *regs; +}; + +struct ov965x_interval { + struct v4l2_fract interval; + /* Maximum resolution for this interval */ + struct v4l2_frmsize_discrete size; + u8 clkrc_div; +}; + +enum gpio_id { + GPIO_PWDN, + GPIO_RST, + NUM_GPIOS, +}; + +struct ov965x { + struct v4l2_subdev sd; + struct media_pad pad; + enum v4l2_mbus_type bus_type; + int gpios[NUM_GPIOS]; + /* External master clock frequency */ + unsigned long mclk_frequency; + + /* Protects the struct fields below */ + struct mutex lock; + + struct i2c_client *client; + + /* Exposure row interval in us */ + unsigned int exp_row_interval; + + unsigned short id; + const struct ov965x_framesize *frame_size; + /* YUYV sequence (pixel format) control register */ + u8 tslb_reg; + struct v4l2_mbus_framefmt format; + + struct ov965x_ctrls ctrls; + /* Pointer to frame rate control data structure */ + const struct ov965x_interval *fiv; + + int streaming; + int power; + + u8 apply_frame_fmt; +}; + +struct i2c_rv { + u8 addr; + u8 value; +}; + +static const struct i2c_rv ov965x_init_regs[] = { + { REG_COM2, 0x10 }, /* Set soft sleep mode */ + { REG_COM5, 0x00 }, /* System clock options */ + { REG_COM2, 0x01 }, /* Output drive, soft sleep mode */ + { REG_COM10, 0x00 }, /* Slave mode, HREF vs HSYNC, signals negate */ + { REG_EDGE, 0xa6 }, /* Edge enhancement treshhold and factor */ + { REG_COM16, 0x02 }, /* Color matrix coeff double option */ + { REG_COM17, 0x08 }, /* Single frame out, banding filter */ + { 0x16, 0x06 }, + { REG_CHLF, 0xc0 }, /* Reserved */ + { 0x34, 0xbf }, + { 0xa8, 0x80 }, + { 0x96, 0x04 }, + { 0x8e, 0x00 }, + { REG_COM12, 0x77 }, /* HREF option, UV average */ + { 0x8b, 0x06 }, + { 0x35, 0x91 }, + { 0x94, 0x88 }, + { 0x95, 0x88 }, + { REG_COM15, 0xc1 }, /* Output range, RGB 555/565 */ + { REG_GRCOM, 0x2f }, /* Analog BLC & regulator */ + { REG_COM6, 0x43 }, /* HREF & ADBLC options */ + { REG_COM8, 0xe5 }, /* AGC/AEC options */ + { REG_COM13, 0x90 }, /* Gamma selection, colour matrix, UV delay */ + { REG_HV, 0x80 }, /* Manual banding filter MSB */ + { 0x5c, 0x96 }, /* Reserved up to 0xa5 */ + { 0x5d, 0x96 }, + { 0x5e, 0x10 }, + { 0x59, 0xeb }, + { 0x5a, 0x9c }, + { 0x5b, 0x55 }, + { 0x43, 0xf0 }, + { 0x44, 0x10 }, + { 0x45, 0x55 }, + { 0x46, 0x86 }, + { 0x47, 0x64 }, + { 0x48, 0x86 }, + { 0x5f, 0xe0 }, + { 0x60, 0x8c }, + { 0x61, 0x20 }, + { 0xa5, 0xd9 }, + { 0xa4, 0x74 }, /* reserved */ + { REG_COM23, 0x02 }, /* Color gain analog/_digital_ */ + { REG_COM8, 0xe7 }, /* Enable AEC, AWB, AEC */ + { REG_COM22, 0x23 }, /* Edge enhancement, denoising */ + { 0xa9, 0xb8 }, + { 0xaa, 0x92 }, + { 0xab, 0x0a }, + { REG_DBLC1, 0xdf }, /* Digital BLC */ + { REG_DBLC_B, 0x00 }, /* Digital BLC B chan offset */ + { REG_DBLC_R, 0x00 }, /* Digital BLC R chan offset */ + { REG_DBLC_GB, 0x00 }, /* Digital BLC GB chan offset */ + { REG_DBLC_GR, 0x00 }, + { REG_COM9, 0x3a }, /* Gain ceiling 16x */ + { REG_NULL, 0 } +}; + +#define NUM_FMT_REGS 14 +/* + * COM7, COM3, COM4, HSTART, HSTOP, HREF, VSTART, VSTOP, VREF, + * EXHCH, EXHCL, ADC, OCOM, OFON + */ +static const u8 frame_size_reg_addr[NUM_FMT_REGS] = { + 0x12, 0x0c, 0x0d, 0x17, 0x18, 0x32, 0x19, 0x1a, 0x03, + 0x2a, 0x2b, 0x37, 0x38, 0x39, +}; + +static const u8 ov965x_sxga_regs[NUM_FMT_REGS] = { + 0x00, 0x00, 0x00, 0x1e, 0xbe, 0xbf, 0x01, 0x81, 0x12, + 0x10, 0x34, 0x81, 0x93, 0x51, +}; + +static const u8 ov965x_vga_regs[NUM_FMT_REGS] = { + 0x40, 0x04, 0x80, 0x26, 0xc6, 0xed, 0x01, 0x3d, 0x00, + 0x10, 0x40, 0x91, 0x12, 0x43, +}; + +/* Determined empirically. */ +static const u8 ov965x_qvga_regs[NUM_FMT_REGS] = { + 0x10, 0x04, 0x80, 0x25, 0xc5, 0xbf, 0x00, 0x80, 0x12, + 0x10, 0x40, 0x91, 0x12, 0x43, +}; + +static const struct ov965x_framesize ov965x_framesizes[] = { + { + .width = SXGA_WIDTH, + .height = SXGA_HEIGHT, + .regs = ov965x_sxga_regs, + .max_exp_lines = 1048, + }, { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .regs = ov965x_vga_regs, + .max_exp_lines = 498, + }, { + .width = QVGA_WIDTH, + .height = QVGA_HEIGHT, + .regs = ov965x_qvga_regs, + .max_exp_lines = 248, + }, +}; + +struct ov965x_pixfmt { + enum v4l2_mbus_pixelcode code; + u32 colorspace; + /* REG_TSLB value, only bits [3:2] may be set. */ + u8 tslb_reg; +}; + +static const struct ov965x_pixfmt ov965x_formats[] = { + { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 0x00}, + { V4L2_MBUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG, 0x04}, + { V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG, 0x0c}, + { V4L2_MBUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG, 0x08}, +}; + +/* + * This table specifies possible frame resolution and interval + * combinations. Default CLKRC[5:0] divider values are valid + * only for 24 MHz external clock frequency. + */ +static struct ov965x_interval ov965x_intervals[] = { + {{ 100, 625 }, { SXGA_WIDTH, SXGA_HEIGHT }, 0 }, /* 6.25 fps */ + {{ 10, 125 }, { VGA_WIDTH, VGA_HEIGHT }, 1 }, /* 12.5 fps */ + {{ 10, 125 }, { QVGA_WIDTH, QVGA_HEIGHT }, 3 }, /* 12.5 fps */ + {{ 1, 25 }, { VGA_WIDTH, VGA_HEIGHT }, 0 }, /* 25 fps */ + {{ 1, 25 }, { QVGA_WIDTH, QVGA_HEIGHT }, 1 }, /* 25 fps */ +}; + +static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct ov965x, ctrls.handler)->sd; +} + +static inline struct ov965x *to_ov965x(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ov965x, sd); +} + +static int ov965x_read(struct i2c_client *client, u8 addr, u8 *val) +{ + u8 buf = addr; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &buf + }; + int ret; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret == 1) { + msg.flags = I2C_M_RD; + ret = i2c_transfer(client->adapter, &msg, 1); + + if (ret == 1) + *val = buf; + } + + v4l2_dbg(2, debug, client, "%s: 0x%02x @ 0x%02x. (%d)\n", + __func__, *val, addr, ret); + + return ret == 1 ? 0 : ret; +} + +static int ov965x_write(struct i2c_client *client, u8 addr, u8 val) +{ + u8 buf[2] = { addr, val }; + + int ret = i2c_master_send(client, buf, 2); + + v4l2_dbg(2, debug, client, "%s: 0x%02x @ 0x%02X (%d)\n", + __func__, val, addr, ret); + + return ret == 2 ? 0 : ret; +} + +static int ov965x_write_array(struct i2c_client *client, + const struct i2c_rv *regs) +{ + int i, ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = ov965x_write(client, regs[i].addr, regs[i].value); + + return ret; +} + +static int ov965x_set_default_gamma_curve(struct ov965x *ov965x) +{ + static const u8 gamma_curve[] = { + /* Values taken from OV application note. */ + 0x40, 0x30, 0x4b, 0x60, 0x70, 0x70, 0x70, 0x70, + 0x60, 0x60, 0x50, 0x48, 0x3a, 0x2e, 0x28, 0x22, + 0x04, 0x07, 0x10, 0x28, 0x36, 0x44, 0x52, 0x60, + 0x6c, 0x78, 0x8c, 0x9e, 0xbb, 0xd2, 0xe6 + }; + u8 addr = REG_GSP; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(gamma_curve); i++) { + int ret = ov965x_write(ov965x->client, addr, gamma_curve[i]); + if (ret < 0) + return ret; + addr++; + } + + return 0; +}; + +static int ov965x_set_color_matrix(struct ov965x *ov965x) +{ + static const u8 mtx[] = { + /* MTX1..MTX9, MTXS */ + 0x3a, 0x3d, 0x03, 0x12, 0x26, 0x38, 0x40, 0x40, 0x40, 0x0d + }; + u8 addr = REG_MTX(1); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mtx); i++) { + int ret = ov965x_write(ov965x->client, addr, mtx[i]); + if (ret < 0) + return ret; + addr++; + } + + return 0; +} + +static void ov965x_gpio_set(int gpio, int val) +{ + if (gpio_is_valid(gpio)) + gpio_set_value(gpio, val); +} + +static void __ov965x_set_power(struct ov965x *ov965x, int on) +{ + if (on) { + ov965x_gpio_set(ov965x->gpios[GPIO_PWDN], 0); + ov965x_gpio_set(ov965x->gpios[GPIO_RST], 0); + usleep_range(25000, 26000); + } else { + ov965x_gpio_set(ov965x->gpios[GPIO_RST], 1); + ov965x_gpio_set(ov965x->gpios[GPIO_PWDN], 1); + } + + ov965x->streaming = 0; +} + +static int ov965x_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov965x *ov965x = to_ov965x(sd); + struct i2c_client *client = ov965x->client; + int ret = 0; + + v4l2_dbg(1, debug, client, "%s: on: %d\n", __func__, on); + + mutex_lock(&ov965x->lock); + if (ov965x->power == !on) { + __ov965x_set_power(ov965x, on); + if (on) { + ret = ov965x_write_array(client, + ov965x_init_regs); + ov965x->apply_frame_fmt = 1; + ov965x->ctrls.update = 1; + } + } + if (!ret) + ov965x->power += on ? 1 : -1; + + WARN_ON(ov965x->power < 0); + mutex_unlock(&ov965x->lock); + return ret; +} + +/* + * V4L2 controls + */ + +static void ov965x_update_exposure_ctrl(struct ov965x *ov965x) +{ + struct v4l2_ctrl *ctrl = ov965x->ctrls.exposure; + unsigned long fint, trow; + int min, max, def; + u8 clkrc; + + mutex_lock(&ov965x->lock); + if (WARN_ON(!ctrl || !ov965x->frame_size)) { + mutex_unlock(&ov965x->lock); + return; + } + clkrc = DEF_CLKRC + ov965x->fiv->clkrc_div; + /* Calculate internal clock frequency */ + fint = ov965x->mclk_frequency * ((clkrc >> 7) + 1) / + ((2 * ((clkrc & 0x3f) + 1))); + /* and the row interval (in us). */ + trow = (2 * 1520 * 1000000UL) / fint; + max = ov965x->frame_size->max_exp_lines * trow; + ov965x->exp_row_interval = trow; + mutex_unlock(&ov965x->lock); + + v4l2_dbg(1, debug, &ov965x->sd, "clkrc: %#x, fi: %lu, tr: %lu, %d\n", + clkrc, fint, trow, max); + + /* Update exposure time range to match current frame format. */ + min = (trow + 100) / 100; + max = (max - 100) / 100; + def = min + (max - min) / 2; + + if (v4l2_ctrl_modify_range(ctrl, min, max, 1, def)) + v4l2_err(&ov965x->sd, "Exposure ctrl range update failed\n"); +} + +static int ov965x_set_banding_filter(struct ov965x *ov965x, int value) +{ + unsigned long mbd, light_freq; + int ret; + u8 reg; + + ret = ov965x_read(ov965x->client, REG_COM8, ®); + if (!ret) { + if (value == V4L2_CID_POWER_LINE_FREQUENCY_DISABLED) + reg &= ~COM8_BFILT; + else + reg |= COM8_BFILT; + ret = ov965x_write(ov965x->client, REG_COM8, reg); + } + if (value == V4L2_CID_POWER_LINE_FREQUENCY_DISABLED) + return 0; + if (WARN_ON(ov965x->fiv == NULL)) + return -EINVAL; + /* Set minimal exposure time for 50/60 HZ lighting */ + if (value == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) + light_freq = 50; + else + light_freq = 60; + mbd = (1000UL * ov965x->fiv->interval.denominator * + ov965x->frame_size->max_exp_lines) / + ov965x->fiv->interval.numerator; + mbd = ((mbd / (light_freq * 2)) + 500) / 1000UL; + + return ov965x_write(ov965x->client, REG_MBD, mbd); +} + +static int ov965x_set_white_balance(struct ov965x *ov965x, int awb) +{ + int ret; + u8 reg; + + ret = ov965x_read(ov965x->client, REG_COM8, ®); + if (!ret) { + reg = awb ? reg | REG_COM8 : reg & ~REG_COM8; + ret = ov965x_write(ov965x->client, REG_COM8, reg); + } + if (!ret && !awb) { + ret = ov965x_write(ov965x->client, REG_BLUE, + ov965x->ctrls.blue_balance->val); + if (ret < 0) + return ret; + ret = ov965x_write(ov965x->client, REG_RED, + ov965x->ctrls.red_balance->val); + } + return ret; +} + +#define NUM_BR_LEVELS 7 +#define NUM_BR_REGS 3 + +static int ov965x_set_brightness(struct ov965x *ov965x, int val) +{ + static const u8 regs[NUM_BR_LEVELS + 1][NUM_BR_REGS] = { + { REG_AEW, REG_AEB, REG_VPT }, + { 0x1c, 0x12, 0x50 }, /* -3 */ + { 0x3d, 0x30, 0x71 }, /* -2 */ + { 0x50, 0x44, 0x92 }, /* -1 */ + { 0x70, 0x64, 0xc3 }, /* 0 */ + { 0x90, 0x84, 0xd4 }, /* +1 */ + { 0xc4, 0xbf, 0xf9 }, /* +2 */ + { 0xd8, 0xd0, 0xfa }, /* +3 */ + }; + int i, ret = 0; + + val += (NUM_BR_LEVELS / 2 + 1); + if (val > NUM_BR_LEVELS) + return -EINVAL; + + for (i = 0; i < NUM_BR_REGS && !ret; i++) + ret = ov965x_write(ov965x->client, regs[0][i], + regs[val][i]); + return ret; +} + +static int ov965x_set_gain(struct ov965x *ov965x, int auto_gain) +{ + struct i2c_client *client = ov965x->client; + struct ov965x_ctrls *ctrls = &ov965x->ctrls; + int ret = 0; + u8 reg; + /* + * For manual mode we need to disable AGC first, so + * gain value in REG_VREF, REG_GAIN is not overwritten. + */ + if (ctrls->auto_gain->is_new) { + ret = ov965x_read(client, REG_COM8, ®); + if (ret < 0) + return ret; + if (ctrls->auto_gain->val) + reg |= COM8_AGC; + else + reg &= ~COM8_AGC; + ret = ov965x_write(client, REG_COM8, reg); + if (ret < 0) + return ret; + } + + if (ctrls->gain->is_new && !auto_gain) { + unsigned int gain = ctrls->gain->val; + unsigned int rgain; + int m; + /* + * Convert gain control value to the sensor's gain + * registers (VREF[7:6], GAIN[7:0]) format. + */ + for (m = 6; m >= 0; m--) + if (gain >= (1 << m) * 16) + break; + rgain = (gain - ((1 << m) * 16)) / (1 << m); + rgain |= (((1 << m) - 1) << 4); + + ret = ov965x_write(client, REG_GAIN, rgain & 0xff); + if (ret < 0) + return ret; + ret = ov965x_read(client, REG_VREF, ®); + if (ret < 0) + return ret; + reg &= ~VREF_GAIN_MASK; + reg |= (((rgain >> 8) & 0x3) << 6); + ret = ov965x_write(client, REG_VREF, reg); + if (ret < 0) + return ret; + /* Return updated control's value to userspace */ + ctrls->gain->val = (1 << m) * (16 + (rgain & 0xf)); + } + + return ret; +} + +static int ov965x_set_sharpness(struct ov965x *ov965x, unsigned int value) +{ + u8 com14, edge; + int ret; + + ret = ov965x_read(ov965x->client, REG_COM14, &com14); + if (ret < 0) + return ret; + ret = ov965x_read(ov965x->client, REG_EDGE, &edge); + if (ret < 0) + return ret; + com14 = value ? com14 | COM14_EDGE_EN : com14 & ~COM14_EDGE_EN; + value--; + if (value > 0x0f) { + com14 |= COM14_EEF_X2; + value >>= 1; + } else { + com14 &= ~COM14_EEF_X2; + } + ret = ov965x_write(ov965x->client, REG_COM14, com14); + if (ret < 0) + return ret; + + edge &= ~EDGE_FACTOR_MASK; + edge |= ((u8)value & 0x0f); + + return ov965x_write(ov965x->client, REG_EDGE, edge); +} + +static int ov965x_set_exposure(struct ov965x *ov965x, int exp) +{ + struct i2c_client *client = ov965x->client; + struct ov965x_ctrls *ctrls = &ov965x->ctrls; + bool auto_exposure = (exp == V4L2_EXPOSURE_AUTO); + int ret; + u8 reg; + + if (ctrls->auto_exp->is_new) { + ret = ov965x_read(client, REG_COM8, ®); + if (ret < 0) + return ret; + if (auto_exposure) + reg |= (COM8_AEC | COM8_AGC); + else + reg &= ~(COM8_AEC | COM8_AGC); + ret = ov965x_write(client, REG_COM8, reg); + if (ret < 0) + return ret; + } + + if (!auto_exposure && ctrls->exposure->is_new) { + unsigned int exposure = (ctrls->exposure->val * 100) + / ov965x->exp_row_interval; + /* + * Manual exposure value + * [b15:b0] - AECHM (b15:b10), AECH (b9:b2), COM1 (b1:b0) + */ + ret = ov965x_write(client, REG_COM1, exposure & 0x3); + if (!ret) + ret = ov965x_write(client, REG_AECH, + (exposure >> 2) & 0xff); + if (!ret) + ret = ov965x_write(client, REG_AECHM, + (exposure >> 10) & 0x3f); + /* Update the value to minimize rounding errors */ + ctrls->exposure->val = ((exposure * ov965x->exp_row_interval) + + 50) / 100; + if (ret < 0) + return ret; + } + + v4l2_ctrl_activate(ov965x->ctrls.brightness, !exp); + return 0; +} + +static int ov965x_set_flip(struct ov965x *ov965x) +{ + u8 mvfp = 0; + + if (ov965x->ctrls.hflip->val) + mvfp |= MVFP_MIRROR; + + if (ov965x->ctrls.vflip->val) + mvfp |= MVFP_FLIP; + + return ov965x_write(ov965x->client, REG_MVFP, mvfp); +} + +#define NUM_SAT_LEVELS 5 +#define NUM_SAT_REGS 6 + +static int ov965x_set_saturation(struct ov965x *ov965x, int val) +{ + static const u8 regs[NUM_SAT_LEVELS][NUM_SAT_REGS] = { + /* MTX(1)...MTX(6) */ + { 0x1d, 0x1f, 0x02, 0x09, 0x13, 0x1c }, /* -2 */ + { 0x2e, 0x31, 0x02, 0x0e, 0x1e, 0x2d }, /* -1 */ + { 0x3a, 0x3d, 0x03, 0x12, 0x26, 0x38 }, /* 0 */ + { 0x46, 0x49, 0x04, 0x16, 0x2e, 0x43 }, /* +1 */ + { 0x57, 0x5c, 0x05, 0x1b, 0x39, 0x54 }, /* +2 */ + }; + u8 addr = REG_MTX(1); + int i, ret = 0; + + val += (NUM_SAT_LEVELS / 2); + if (val >= NUM_SAT_LEVELS) + return -EINVAL; + + for (i = 0; i < NUM_SAT_REGS && !ret; i++) + ret = ov965x_write(ov965x->client, addr + i, regs[val][i]); + + return ret; +} + +static int ov965x_set_test_pattern(struct ov965x *ov965x, int value) +{ + int ret; + u8 reg; + + ret = ov965x_read(ov965x->client, REG_COM23, ®); + if (ret < 0) + return ret; + reg = value ? reg | COM23_TEST_MODE : reg & ~COM23_TEST_MODE; + return ov965x_write(ov965x->client, REG_COM23, reg); +} + +static int __g_volatile_ctrl(struct ov965x *ov965x, struct v4l2_ctrl *ctrl) +{ + struct i2c_client *client = ov965x->client; + unsigned int exposure, gain, m; + u8 reg0, reg1, reg2; + int ret; + + if (!ov965x->power) + return 0; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + if (!ctrl->val) + return 0; + ret = ov965x_read(client, REG_GAIN, ®0); + if (ret < 0) + return ret; + ret = ov965x_read(client, REG_VREF, ®1); + if (ret < 0) + return ret; + gain = ((reg1 >> 6) << 8) | reg0; + m = 0x01 << fls(gain >> 4); + ov965x->ctrls.gain->val = m * (16 + (gain & 0xf)); + break; + + case V4L2_CID_EXPOSURE_AUTO: + if (ctrl->val == V4L2_EXPOSURE_MANUAL) + return 0; + ret = ov965x_read(client, REG_COM1, ®0); + if (!ret) + ret = ov965x_read(client, REG_AECH, ®1); + if (!ret) + ret = ov965x_read(client, REG_AECHM, ®2); + if (ret < 0) + return ret; + exposure = ((reg2 & 0x3f) << 10) | (reg1 << 2) | + (reg0 & 0x3); + ov965x->ctrls.exposure->val = ((exposure * + ov965x->exp_row_interval) + 50) / 100; + break; + } + + return 0; +} + +static int ov965x_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sd(ctrl); + struct ov965x *ov965x = to_ov965x(sd); + int ret; + + v4l2_dbg(1, debug, sd, "g_ctrl: %s\n", ctrl->name); + + mutex_lock(&ov965x->lock); + ret = __g_volatile_ctrl(ov965x, ctrl); + mutex_unlock(&ov965x->lock); + return ret; +} + +static int ov965x_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sd(ctrl); + struct ov965x *ov965x = to_ov965x(sd); + int ret = -EINVAL; + + v4l2_dbg(1, debug, sd, "s_ctrl: %s, value: %d. power: %d\n", + ctrl->name, ctrl->val, ov965x->power); + + mutex_lock(&ov965x->lock); + /* + * If the device is not powered up now postpone applying control's + * value to the hardware, until it is ready to accept commands. + */ + if (ov965x->power == 0) { + mutex_unlock(&ov965x->lock); + return 0; + } + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = ov965x_set_white_balance(ov965x, ctrl->val); + break; + + case V4L2_CID_BRIGHTNESS: + ret = ov965x_set_brightness(ov965x, ctrl->val); + break; + + case V4L2_CID_EXPOSURE_AUTO: + ret = ov965x_set_exposure(ov965x, ctrl->val); + break; + + case V4L2_CID_AUTOGAIN: + ret = ov965x_set_gain(ov965x, ctrl->val); + break; + + case V4L2_CID_HFLIP: + ret = ov965x_set_flip(ov965x); + break; + + case V4L2_CID_POWER_LINE_FREQUENCY: + ret = ov965x_set_banding_filter(ov965x, ctrl->val); + break; + + case V4L2_CID_SATURATION: + ret = ov965x_set_saturation(ov965x, ctrl->val); + break; + + case V4L2_CID_SHARPNESS: + ret = ov965x_set_sharpness(ov965x, ctrl->val); + break; + + case V4L2_CID_TEST_PATTERN: + ret = ov965x_set_test_pattern(ov965x, ctrl->val); + break; + } + + mutex_unlock(&ov965x->lock); + return ret; +} + +static const struct v4l2_ctrl_ops ov965x_ctrl_ops = { + .g_volatile_ctrl = ov965x_g_volatile_ctrl, + .s_ctrl = ov965x_s_ctrl, +}; + +static const char * const test_pattern_menu[] = { + "Disabled", + "Color bars", + NULL +}; + +static int ov965x_initialize_controls(struct ov965x *ov965x) +{ + const struct v4l2_ctrl_ops *ops = &ov965x_ctrl_ops; + struct ov965x_ctrls *ctrls = &ov965x->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + int ret; + + ret = v4l2_ctrl_handler_init(hdl, 16); + if (ret < 0) + return ret; + + /* Auto/manual white balance */ + ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 1); + ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE, + 0, 0xff, 1, 0x80); + ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE, + 0, 0xff, 1, 0x80); + /* Auto/manual exposure */ + ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); + /* Exposure time, in 100 us units. min/max is updated dynamically. */ + ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_EXPOSURE_ABSOLUTE, + 2, 1500, 1, 500); + /* Auto/manual gain */ + ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN, + 0, 1, 1, 1); + ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, + 16, 64 * (16 + 15), 1, 64 * 16); + + ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, + -2, 2, 1, 0); + ctrls->brightness = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, + -3, 3, 1, 0); + ctrls->sharpness = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, + 0, 32, 1, 6); + + ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + + ctrls->light_freq = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, ~0x7, + V4L2_CID_POWER_LINE_FREQUENCY_50HZ); + + v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(test_pattern_menu) - 1, 0, 0, + test_pattern_menu); + if (hdl->error) { + ret = hdl->error; + v4l2_ctrl_handler_free(hdl); + return ret; + } + + ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; + ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; + + v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false); + v4l2_ctrl_auto_cluster(3, &ctrls->auto_gain, 0, true); + v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 1, true); + v4l2_ctrl_cluster(2, &ctrls->hflip); + + ov965x->sd.ctrl_handler = hdl; + return 0; +} + +/* + * V4L2 subdev video and pad level operations + */ +static void ov965x_get_default_format(struct v4l2_mbus_framefmt *mf) +{ + mf->width = ov965x_framesizes[0].width; + mf->height = ov965x_framesizes[0].height; + mf->colorspace = ov965x_formats[0].colorspace; + mf->code = ov965x_formats[0].code; + mf->field = V4L2_FIELD_NONE; +} + +static int ov965x_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(ov965x_formats)) + return -EINVAL; + + code->code = ov965x_formats[code->index].code; + return 0; +} + +static int ov965x_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + int i = ARRAY_SIZE(ov965x_formats); + + if (fse->index > ARRAY_SIZE(ov965x_framesizes)) + return -EINVAL; + + while (--i) + if (fse->code == ov965x_formats[i].code) + break; + + fse->code = ov965x_formats[i].code; + + fse->min_width = ov965x_framesizes[fse->index].width; + fse->max_width = fse->min_width; + fse->max_height = ov965x_framesizes[fse->index].height; + fse->min_height = fse->max_height; + + return 0; +} + +static int ov965x_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov965x *ov965x = to_ov965x(sd); + + mutex_lock(&ov965x->lock); + fi->interval = ov965x->fiv->interval; + mutex_unlock(&ov965x->lock); + + return 0; +} + +static int __ov965x_set_frame_interval(struct ov965x *ov965x, + struct v4l2_subdev_frame_interval *fi) +{ + struct v4l2_mbus_framefmt *mbus_fmt = &ov965x->format; + const struct ov965x_interval *fiv = &ov965x_intervals[0]; + u64 req_int, err, min_err = ~0ULL; + unsigned int i; + + + if (fi->interval.denominator == 0) + return -EINVAL; + + req_int = (u64)(fi->interval.numerator * 10000) / + fi->interval.denominator; + + for (i = 0; i < ARRAY_SIZE(ov965x_intervals); i++) { + const struct ov965x_interval *iv = &ov965x_intervals[i]; + + if (mbus_fmt->width != iv->size.width || + mbus_fmt->height != iv->size.height) + continue; + err = abs64((u64)(iv->interval.numerator * 10000) / + iv->interval.denominator - req_int); + if (err < min_err) { + fiv = iv; + min_err = err; + } + } + ov965x->fiv = fiv; + + v4l2_dbg(1, debug, &ov965x->sd, "Changed frame interval to %u us\n", + fiv->interval.numerator * 1000000 / fiv->interval.denominator); + + return 0; +} + +static int ov965x_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov965x *ov965x = to_ov965x(sd); + int ret; + + v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n", + fi->interval.numerator, fi->interval.denominator); + + mutex_lock(&ov965x->lock); + ret = __ov965x_set_frame_interval(ov965x, fi); + ov965x->apply_frame_fmt = 1; + mutex_unlock(&ov965x->lock); + return ret; +} + +static int ov965x_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct ov965x *ov965x = to_ov965x(sd); + struct v4l2_mbus_framefmt *mf; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, 0); + fmt->format = *mf; + return 0; + } + + mutex_lock(&ov965x->lock); + fmt->format = ov965x->format; + mutex_unlock(&ov965x->lock); + + return 0; +} + +static void __ov965x_try_frame_size(struct v4l2_mbus_framefmt *mf, + const struct ov965x_framesize **size) +{ + const struct ov965x_framesize *fsize = &ov965x_framesizes[0], + *match = NULL; + int i = ARRAY_SIZE(ov965x_framesizes); + unsigned int min_err = UINT_MAX; + + while (i--) { + int err = abs(fsize->width - mf->width) + + abs(fsize->height - mf->height); + if (err < min_err) { + min_err = err; + match = fsize; + } + fsize++; + } + if (!match) + match = &ov965x_framesizes[0]; + mf->width = match->width; + mf->height = match->height; + if (size) + *size = match; +} + +static int ov965x_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + unsigned int index = ARRAY_SIZE(ov965x_formats); + struct v4l2_mbus_framefmt *mf = &fmt->format; + struct ov965x *ov965x = to_ov965x(sd); + const struct ov965x_framesize *size = NULL; + int ret = 0; + + __ov965x_try_frame_size(mf, &size); + + while (--index) + if (ov965x_formats[index].code == mf->code) + break; + + mf->colorspace = V4L2_COLORSPACE_JPEG; + mf->code = ov965x_formats[index].code; + mf->field = V4L2_FIELD_NONE; + + mutex_lock(&ov965x->lock); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + if (fh != NULL) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + *mf = fmt->format; + } + } else { + if (ov965x->streaming) { + ret = -EBUSY; + } else { + ov965x->frame_size = size; + ov965x->format = fmt->format; + ov965x->tslb_reg = ov965x_formats[index].tslb_reg; + ov965x->apply_frame_fmt = 1; + } + } + + if (!ret && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + struct v4l2_subdev_frame_interval fiv = { + .interval = { 0, 1 } + }; + /* Reset to minimum possible frame interval */ + __ov965x_set_frame_interval(ov965x, &fiv); + } + mutex_unlock(&ov965x->lock); + + if (!ret) + ov965x_update_exposure_ctrl(ov965x); + + return ret; +} + +static int ov965x_set_frame_size(struct ov965x *ov965x) +{ + int i, ret = 0; + + for (i = 0; ret == 0 && i < NUM_FMT_REGS; i++) + ret = ov965x_write(ov965x->client, frame_size_reg_addr[i], + ov965x->frame_size->regs[i]); + return ret; +} + +static int __ov965x_set_params(struct ov965x *ov965x) +{ + struct i2c_client *client = ov965x->client; + struct ov965x_ctrls *ctrls = &ov965x->ctrls; + int ret = 0; + u8 reg; + + if (ov965x->apply_frame_fmt) { + reg = DEF_CLKRC + ov965x->fiv->clkrc_div; + ret = ov965x_write(client, REG_CLKRC, reg); + if (ret < 0) + return ret; + ret = ov965x_set_frame_size(ov965x); + if (ret < 0) + return ret; + ret = ov965x_read(client, REG_TSLB, ®); + if (ret < 0) + return ret; + reg &= ~TSLB_YUYV_MASK; + reg |= ov965x->tslb_reg; + ret = ov965x_write(client, REG_TSLB, reg); + if (ret < 0) + return ret; + } + ret = ov965x_set_default_gamma_curve(ov965x); + if (ret < 0) + return ret; + ret = ov965x_set_color_matrix(ov965x); + if (ret < 0) + return ret; + /* + * Select manual banding filter, the filter will + * be enabled further if required. + */ + ret = ov965x_read(client, REG_COM11, ®); + if (!ret) + reg |= COM11_BANDING; + ret = ov965x_write(client, REG_COM11, reg); + if (ret < 0) + return ret; + /* + * Banding filter (REG_MBD value) needs to match selected + * resolution and frame rate, so it's always updated here. + */ + return ov965x_set_banding_filter(ov965x, ctrls->light_freq->val); +} + +static int ov965x_s_stream(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov965x *ov965x = to_ov965x(sd); + struct ov965x_ctrls *ctrls = &ov965x->ctrls; + int ret = 0; + + v4l2_dbg(1, debug, client, "%s: on: %d\n", __func__, on); + + mutex_lock(&ov965x->lock); + if (ov965x->streaming == !on) { + if (on) + ret = __ov965x_set_params(ov965x); + + if (!ret && ctrls->update) { + /* + * ov965x_s_ctrl callback takes the mutex + * so it needs to be released here. + */ + mutex_unlock(&ov965x->lock); + ret = v4l2_ctrl_handler_setup(&ctrls->handler); + + mutex_lock(&ov965x->lock); + if (!ret) + ctrls->update = 0; + } + if (!ret) + ret = ov965x_write(client, REG_COM2, + on ? 0x01 : 0x11); + } + if (!ret) + ov965x->streaming += on ? 1 : -1; + + WARN_ON(ov965x->streaming < 0); + mutex_unlock(&ov965x->lock); + + return ret; +} + +/* + * V4L2 subdev internal operations + */ +static int ov965x_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(fh, 0); + + ov965x_get_default_format(mf); + return 0; +} + +static const struct v4l2_subdev_pad_ops ov965x_pad_ops = { + .enum_mbus_code = ov965x_enum_mbus_code, + .enum_frame_size = ov965x_enum_frame_sizes, + .get_fmt = ov965x_get_fmt, + .set_fmt = ov965x_set_fmt, +}; + +static const struct v4l2_subdev_video_ops ov965x_video_ops = { + .s_stream = ov965x_s_stream, + .g_frame_interval = ov965x_g_frame_interval, + .s_frame_interval = ov965x_s_frame_interval, + +}; + +static const struct v4l2_subdev_internal_ops ov965x_sd_internal_ops = { + .open = ov965x_open, +}; + +static const struct v4l2_subdev_core_ops ov965x_core_ops = { + .s_power = ov965x_s_power, + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_ops ov965x_subdev_ops = { + .core = &ov965x_core_ops, + .pad = &ov965x_pad_ops, + .video = &ov965x_video_ops, +}; + +/* + * Reset and power down GPIOs configuration + */ +static int ov965x_configure_gpios(struct ov965x *ov965x, + const struct ov9650_platform_data *pdata) +{ + int ret, i; + + ov965x->gpios[GPIO_PWDN] = pdata->gpio_pwdn; + ov965x->gpios[GPIO_RST] = pdata->gpio_reset; + + for (i = 0; i < ARRAY_SIZE(ov965x->gpios); i++) { + int gpio = ov965x->gpios[i]; + + if (!gpio_is_valid(gpio)) + continue; + ret = devm_gpio_request_one(&ov965x->client->dev, gpio, + GPIOF_OUT_INIT_HIGH, "OV965X"); + if (ret < 0) + return ret; + v4l2_dbg(1, debug, &ov965x->sd, "set gpio %d to 1\n", gpio); + + gpio_set_value(gpio, 1); + gpio_export(gpio, 0); + ov965x->gpios[i] = gpio; + } + + return 0; +} + +static int ov965x_detect_sensor(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov965x *ov965x = to_ov965x(sd); + u8 pid, ver; + int ret; + + mutex_lock(&ov965x->lock); + __ov965x_set_power(ov965x, 1); + usleep_range(25000, 26000); + + /* Check sensor revision */ + ret = ov965x_read(client, REG_PID, &pid); + if (!ret) + ret = ov965x_read(client, REG_VER, &ver); + + __ov965x_set_power(ov965x, 0); + + if (!ret) { + ov965x->id = OV965X_ID(pid, ver); + if (ov965x->id == OV9650_ID || ov965x->id == OV9652_ID) { + v4l2_info(sd, "Found OV%04X sensor\n", ov965x->id); + } else { + v4l2_err(sd, "Sensor detection failed (%04X, %d)\n", + ov965x->id, ret); + ret = -ENODEV; + } + } + mutex_unlock(&ov965x->lock); + + return ret; +} + +static int ov965x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct ov9650_platform_data *pdata = client->dev.platform_data; + struct v4l2_subdev *sd; + struct ov965x *ov965x; + int ret; + + if (pdata == NULL) { + dev_err(&client->dev, "platform data not specified\n"); + return -EINVAL; + } + + if (pdata->mclk_frequency == 0) { + dev_err(&client->dev, "MCLK frequency not specified\n"); + return -EINVAL; + } + + ov965x = devm_kzalloc(&client->dev, sizeof(*ov965x), GFP_KERNEL); + if (!ov965x) + return -ENOMEM; + + mutex_init(&ov965x->lock); + ov965x->client = client; + ov965x->mclk_frequency = pdata->mclk_frequency; + + sd = &ov965x->sd; + v4l2_i2c_subdev_init(sd, client, &ov965x_subdev_ops); + strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name)); + + sd->internal_ops = &ov965x_sd_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + + ret = ov965x_configure_gpios(ov965x, pdata); + if (ret < 0) + return ret; + + ov965x->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + ret = media_entity_init(&sd->entity, 1, &ov965x->pad, 0); + if (ret < 0) + return ret; + + ret = ov965x_initialize_controls(ov965x); + if (ret < 0) + goto err_me; + + ov965x_get_default_format(&ov965x->format); + ov965x->frame_size = &ov965x_framesizes[0]; + ov965x->fiv = &ov965x_intervals[0]; + + ret = ov965x_detect_sensor(sd); + if (ret < 0) + goto err_ctrls; + + /* Update exposure time min/max to match frame format */ + ov965x_update_exposure_ctrl(ov965x); + + return 0; +err_ctrls: + v4l2_ctrl_handler_free(sd->ctrl_handler); +err_me: + media_entity_cleanup(&sd->entity); + return ret; +} + +static int ov965x_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + media_entity_cleanup(&sd->entity); + + return 0; +} + +static const struct i2c_device_id ov965x_id[] = { + { "OV9650", 0 }, + { "OV9652", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, ov965x_id); + +static struct i2c_driver ov965x_i2c_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = ov965x_probe, + .remove = ov965x_remove, + .id_table = ov965x_id, +}; + +module_i2c_driver(ov965x_i2c_driver); + +MODULE_AUTHOR("Sylwester Nawrocki "); +MODULE_DESCRIPTION("OV9650/OV9652 CMOS Image Sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/include/media/ov9650.h b/include/media/ov9650.h new file mode 100644 index 000000000000..d630cf9e028d --- /dev/null +++ b/include/media/ov9650.h @@ -0,0 +1,27 @@ +/* + * OV9650/OV9652 camera sensors driver + * + * Copyright (C) 2013 Sylwester Nawrocki + * + * 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 OV9650_H_ +#define OV9650_H_ + +/** + * struct ov9650_platform_data - ov9650 driver platform data + * @mclk_frequency: the sensor's master clock frequency in Hz + * @gpio_pwdn: number of a GPIO connected to OV965X PWDN pin + * @gpio_reset: number of a GPIO connected to OV965X RESET pin + * + * If any of @gpio_pwdn or @gpio_reset are unused then they should be + * set to a negative value. @mclk_frequency must always be specified. + */ +struct ov9650_platform_data { + unsigned long mclk_frequency; + int gpio_pwdn; + int gpio_reset; +}; +#endif /* OV9650_H_ */ -- cgit v1.2.3 From ba1066d2e9686a5c96c5c0dfcbda7f874fa7b88d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 28 Sep 2012 08:18:07 -0300 Subject: [media] tuner-core: map audmode to STEREO for radio devices Fixes a v4l2-compliance error: setting audmode to a value other than mono or stereo for a radio device should map to MODE_STEREO. The spec specifies that for radio devices only mono and stereo audmodes are valid. If the user specifies another audmode in v4l2_tuner, then that should be mapped to valid audmode. That didn't happen here. Note that tuner drivers might decide to limit the possible audmode even further if it only supports mono. In that case the tuner driver can set audmode to mono. However, that new value wasn't copied back to t->audmode, and that has been fixed as well in this patch. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/tuner-core.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c index b5a819af2b8c..b5a8aac2e126 100644 --- a/drivers/media/v4l2-core/tuner-core.c +++ b/drivers/media/v4l2-core/tuner-core.c @@ -1013,6 +1013,11 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq) t->standby = false; analog_ops->set_params(&t->fe, ¶ms); + /* + * The tuner driver might decide to change the audmode if it only + * supports stereo, so update t->audmode. + */ + t->audmode = params.audmode; } /* @@ -1235,8 +1240,18 @@ static int tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) if (set_mode(t, vt->type)) return 0; - if (t->mode == V4L2_TUNER_RADIO) + if (t->mode == V4L2_TUNER_RADIO) { t->audmode = vt->audmode; + /* + * For radio audmode can only be mono or stereo. Map any + * other values to stereo. The actual tuner driver that is + * called in set_radio_freq can decide to limit the audmode to + * mono if only mono is supported. + */ + if (t->audmode != V4L2_TUNER_MODE_MONO && + t->audmode != V4L2_TUNER_MODE_STEREO) + t->audmode = V4L2_TUNER_MODE_STEREO; + } set_freq(t, 0); return 0; -- cgit v1.2.3 From 33133ea7aca7eedf8b1b4cee514c76dce7654a8c Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Fri, 11 Jan 2013 11:29:32 -0300 Subject: [media] s5p-mfc: Fix a watchdog bug Fixed wrong condition in firmware reload function used by the watchdog. Signed-off-by: Kamil Debski Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c index 1682271c2453..2e5f30b40dea 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c @@ -130,7 +130,7 @@ int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev) release_firmware(fw_blob); return -ENOMEM; } - if (dev->fw_virt_addr) { + if (!dev->fw_virt_addr) { mfc_err("MFC firmware is not allocated\n"); release_firmware(fw_blob); return -EINVAL; -- cgit v1.2.3 From fa8880bece7321d61a7a5e7bf4b67832071ee047 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 11 Jan 2013 06:36:19 -0300 Subject: [media] s5p-fimc: Fix bytesperline value for V4L2_PIX_FMT_YUV420M format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure bytesperline for Cb, Cr planes for V4L2_PIX_FMT_YUV420M format is half of the Y plane value, rather than having same bytesperline for all planes. While at it, simplify the bytesperline parameter handling by storing it when image format is set and returning those values when getting the format, instead of recalculating bytesperline from intermediate parameters. Reported-by: Sebastian Dröge Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-capture.c | 8 +++++--- drivers/media/platform/s5p-fimc/fimc-core.c | 27 ++++++++++++-------------- drivers/media/platform/s5p-fimc/fimc-core.h | 4 +++- drivers/media/platform/s5p-fimc/fimc-m2m.c | 7 ++++--- 4 files changed, 24 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index 18a70e4a0b0f..df6fc6c7eecd 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -950,9 +950,9 @@ static int fimc_cap_g_fmt_mplane(struct file *file, void *fh, struct v4l2_format *f) { struct fimc_dev *fimc = video_drvdata(file); - struct fimc_ctx *ctx = fimc->vid_cap.ctx; - return fimc_fill_format(&ctx->d_frame, f); + __fimc_get_format(&fimc->vid_cap.ctx->d_frame, f); + return 0; } static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, @@ -1074,8 +1074,10 @@ static int __fimc_capture_set_format(struct fimc_dev *fimc, return ret; } - for (i = 0; i < ff->fmt->memplanes; i++) + for (i = 0; i < ff->fmt->memplanes; i++) { + ff->bytesperline[i] = pix->plane_fmt[i].bytesperline; ff->payload[i] = pix->plane_fmt[i].sizeimage; + } set_frame_bounds(ff, pix->width, pix->height); /* Reset the composition rectangle if not yet configured */ diff --git a/drivers/media/platform/s5p-fimc/fimc-core.c b/drivers/media/platform/s5p-fimc/fimc-core.c index 2e86f44edd23..92d477c91811 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.c +++ b/drivers/media/platform/s5p-fimc/fimc-core.c @@ -691,7 +691,7 @@ void fimc_alpha_ctrl_update(struct fimc_ctx *ctx) v4l2_ctrl_unlock(ctrl); } -int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f) +void __fimc_get_format(struct fimc_frame *frame, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; int i; @@ -704,19 +704,9 @@ int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f) pixm->num_planes = frame->fmt->memplanes; for (i = 0; i < pixm->num_planes; ++i) { - int bpl = frame->f_width; - if (frame->fmt->colplanes == 1) /* packed formats */ - bpl = (bpl * frame->fmt->depth[0]) / 8; - pixm->plane_fmt[i].bytesperline = bpl; - - if (frame->fmt->flags & FMT_FLAGS_COMPRESSED) { - pixm->plane_fmt[i].sizeimage = frame->payload[i]; - continue; - } - pixm->plane_fmt[i].sizeimage = (frame->o_width * - frame->o_height * frame->fmt->depth[i]) / 8; + pixm->plane_fmt[i].bytesperline = frame->bytesperline[i]; + pixm->plane_fmt[i].sizeimage = frame->payload[i]; } - return 0; } void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f) @@ -765,9 +755,16 @@ void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height, if (fmt->colplanes == 1 && /* Packed */ (bpl == 0 || ((bpl * 8) / fmt->depth[i]) < pix->width)) bpl = (pix->width * fmt->depth[0]) / 8; - - if (i == 0) /* Same bytesperline for each plane. */ + /* + * Currently bytesperline for each plane is same, except + * V4L2_PIX_FMT_YUV420M format. This calculation may need + * to be changed when other multi-planar formats are added + * to the fimc_formats[] array. + */ + if (i == 0) bytesperline = bpl; + else if (i == 1 && fmt->memplanes == 3) + bytesperline /= 2; plane_fmt->bytesperline = bytesperline; plane_fmt->sizeimage = max((pix->width * pix->height * diff --git a/drivers/media/platform/s5p-fimc/fimc-core.h b/drivers/media/platform/s5p-fimc/fimc-core.h index 424ff960f0d9..cf760c364099 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.h +++ b/drivers/media/platform/s5p-fimc/fimc-core.h @@ -265,6 +265,7 @@ struct fimc_vid_buffer { * @width: image pixel width * @height: image pixel weight * @payload: image size in bytes (w x h x bpp) + * @bytesperline: bytesperline value for each plane * @paddr: image frame buffer physical addresses * @dma_offset: DMA offset in bytes * @fmt: fimc color format pointer @@ -279,6 +280,7 @@ struct fimc_frame { u32 width; u32 height; unsigned int payload[VIDEO_MAX_PLANES]; + unsigned int bytesperline[VIDEO_MAX_PLANES]; struct fimc_addr paddr; struct fimc_dma_offset dma_offset; struct fimc_fmt *fmt; @@ -637,7 +639,7 @@ int fimc_ctrls_create(struct fimc_ctx *ctx); void fimc_ctrls_delete(struct fimc_ctx *ctx); void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active); void fimc_alpha_ctrl_update(struct fimc_ctx *ctx); -int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f); +void __fimc_get_format(struct fimc_frame *frame, struct v4l2_format *f); void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height, struct v4l2_pix_format_mplane *pix); struct fimc_fmt *fimc_find_format(const u32 *pixelformat, const u32 *mbus_code, diff --git a/drivers/media/platform/s5p-fimc/fimc-m2m.c b/drivers/media/platform/s5p-fimc/fimc-m2m.c index 1d57f3b87aa3..1eabd7e74849 100644 --- a/drivers/media/platform/s5p-fimc/fimc-m2m.c +++ b/drivers/media/platform/s5p-fimc/fimc-m2m.c @@ -294,7 +294,8 @@ static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh, if (IS_ERR(frame)) return PTR_ERR(frame); - return fimc_fill_format(frame, f); + __fimc_get_format(frame, f); + return 0; } static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f) @@ -389,8 +390,8 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, fimc_alpha_ctrl_update(ctx); for (i = 0; i < frame->fmt->colplanes; i++) { - frame->payload[i] = - (pix->width * pix->height * frame->fmt->depth[i]) / 8; + frame->bytesperline[i] = pix->plane_fmt[i].bytesperline; + frame->payload[i] = pix->plane_fmt[i].sizeimage; } fimc_fill_frame(frame, f); -- cgit v1.2.3 From 0e5d61d87b3a1ad5591e0dfbe4c548f862e9f5a6 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 14 Jan 2013 06:09:41 -0300 Subject: [media] s5p-mfc: Use NULL instead of 0 for pointer Fixes the following warning: drivers/media/platform/s5p-mfc/s5p_mfc_opr.c:56:27: warning: Using plain integer as NULL pointer Signed-off-by: Sachin Kamat Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc_opr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c index b4c194331d8c..10f8ac37cecd 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c @@ -53,7 +53,7 @@ void s5p_mfc_release_priv_buf(struct device *dev, { if (b->virt) { dma_free_coherent(dev, b->size, b->virt, b->dma); - b->virt = 0; + b->virt = NULL; b->dma = 0; b->size = 0; } -- cgit v1.2.3 From 62ce272d87f09f34f70a9b3f783783af6c1a09d8 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 17 Jan 2013 00:07:18 -0300 Subject: [media] s5p-g2d: Add support for G2D H/W Rev.4.1 Modified the G2D driver (which initially supported only H/W Rev.3) to support H/W Rev.4.1 present on Exynos4x12 and Exynos52x0 SOCs. - Set the SRC and DST type to 'memory' instead of using reset values. - FIMG2D v4.1 H/W uses different logic for stretching(scaling). - Use CACHECTL_REG only with FIMG2D v3. [s.nawrocki: removed empty line at end of file]] Signed-off-by: Ajay Kumar Signed-off-by: Sachin Kamat Acked-by: Kamil Debski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-g2d/g2d-hw.c | 16 ++++++++++++---- drivers/media/platform/s5p-g2d/g2d-regs.h | 7 +++++++ drivers/media/platform/s5p-g2d/g2d.c | 31 +++++++++++++++++++++++++++++-- drivers/media/platform/s5p-g2d/g2d.h | 17 ++++++++++++++--- 4 files changed, 62 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-g2d/g2d-hw.c b/drivers/media/platform/s5p-g2d/g2d-hw.c index 5b86cbe408e2..e87bd93811d4 100644 --- a/drivers/media/platform/s5p-g2d/g2d-hw.c +++ b/drivers/media/platform/s5p-g2d/g2d-hw.c @@ -28,6 +28,7 @@ void g2d_set_src_size(struct g2d_dev *d, struct g2d_frame *f) { u32 n; + w(0, SRC_SELECT_REG); w(f->stride & 0xFFFF, SRC_STRIDE_REG); n = f->o_height & 0xFFF; @@ -52,6 +53,7 @@ void g2d_set_dst_size(struct g2d_dev *d, struct g2d_frame *f) { u32 n; + w(0, DST_SELECT_REG); w(f->stride & 0xFFFF, DST_STRIDE_REG); n = f->o_height & 0xFFF; @@ -82,10 +84,14 @@ void g2d_set_flip(struct g2d_dev *d, u32 r) w(r, SRC_MSK_DIRECT_REG); } -u32 g2d_cmd_stretch(u32 e) +void g2d_set_v41_stretch(struct g2d_dev *d, struct g2d_frame *src, + struct g2d_frame *dst) { - e &= 1; - return e << 4; + w(DEFAULT_SCALE_MODE, SRC_SCALE_CTRL_REG); + + /* inversed scaling factor: src is numerator */ + w((src->c_width << 16) / dst->c_width, SRC_XSCALE_REG); + w((src->c_height << 16) / dst->c_height, SRC_YSCALE_REG); } void g2d_set_cmd(struct g2d_dev *d, u32 c) @@ -96,7 +102,9 @@ void g2d_set_cmd(struct g2d_dev *d, u32 c) void g2d_start(struct g2d_dev *d) { /* Clear cache */ - w(0x7, CACHECTL_REG); + if (d->variant->hw_rev == TYPE_G2D_3X) + w(0x7, CACHECTL_REG); + /* Enable interrupt */ w(1, INTEN_REG); /* Start G2D engine */ diff --git a/drivers/media/platform/s5p-g2d/g2d-regs.h b/drivers/media/platform/s5p-g2d/g2d-regs.h index 02e1cf50da4e..9bf31ad35d47 100644 --- a/drivers/media/platform/s5p-g2d/g2d-regs.h +++ b/drivers/media/platform/s5p-g2d/g2d-regs.h @@ -35,6 +35,9 @@ #define SRC_COLOR_MODE_REG 0x030C /* Src Image Color Mode reg */ #define SRC_LEFT_TOP_REG 0x0310 /* Src Left Top Coordinate reg */ #define SRC_RIGHT_BOTTOM_REG 0x0314 /* Src Right Bottom Coordinate reg */ +#define SRC_SCALE_CTRL_REG 0x0328 /* Src Scaling type select */ +#define SRC_XSCALE_REG 0x032c /* Src X Scaling ratio */ +#define SRC_YSCALE_REG 0x0330 /* Src Y Scaling ratio */ /* Parameter Setting Registers (Dest) */ #define DST_SELECT_REG 0x0400 /* Dest Image Selection reg */ @@ -113,3 +116,7 @@ #define DEFAULT_WIDTH 100 #define DEFAULT_HEIGHT 100 +#define DEFAULT_SCALE_MODE (2 << 0) + +/* Command mode register values */ +#define CMD_V3_ENABLE_STRETCH (1 << 4) diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index dcd53357704d..7e415297e174 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -604,8 +604,13 @@ static void device_run(void *prv) g2d_set_flip(dev, ctx->flip); if (ctx->in.c_width != ctx->out.c_width || - ctx->in.c_height != ctx->out.c_height) - cmd |= g2d_cmd_stretch(1); + ctx->in.c_height != ctx->out.c_height) { + if (dev->variant->hw_rev == TYPE_G2D_3X) + cmd |= CMD_V3_ENABLE_STRETCH; + else + g2d_set_v41_stretch(dev, &ctx->in, &ctx->out); + } + g2d_set_cmd(dev, cmd); g2d_start(dev); @@ -791,6 +796,7 @@ static int g2d_probe(struct platform_device *pdev) } def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; + dev->variant = g2d_get_drv_data(pdev); return 0; @@ -830,9 +836,30 @@ static int g2d_remove(struct platform_device *pdev) return 0; } +static struct g2d_variant g2d_drvdata_v3x = { + .hw_rev = TYPE_G2D_3X, +}; + +static struct g2d_variant g2d_drvdata_v4x = { + .hw_rev = TYPE_G2D_4X, /* Revision 4.1 for Exynos4X12 and Exynos5 */ +}; + +static struct platform_device_id g2d_driver_ids[] = { + { + .name = "s5p-g2d", + .driver_data = (unsigned long)&g2d_drvdata_v3x, + }, { + .name = "s5p-g2d-v4x", + .driver_data = (unsigned long)&g2d_drvdata_v4x, + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, g2d_driver_ids); + static struct platform_driver g2d_pdrv = { .probe = g2d_probe, .remove = g2d_remove, + .id_table = g2d_driver_ids, .driver = { .name = G2D_NAME, .owner = THIS_MODULE, diff --git a/drivers/media/platform/s5p-g2d/g2d.h b/drivers/media/platform/s5p-g2d/g2d.h index 6b765b0216c5..300ca05ba404 100644 --- a/drivers/media/platform/s5p-g2d/g2d.h +++ b/drivers/media/platform/s5p-g2d/g2d.h @@ -10,10 +10,13 @@ * License, or (at your option) any later version */ +#include #include #include #define G2D_NAME "s5p-g2d" +#define TYPE_G2D_3X 3 +#define TYPE_G2D_4X 4 struct g2d_dev { struct v4l2_device v4l2_dev; @@ -27,6 +30,7 @@ struct g2d_dev { struct clk *clk; struct clk *gate; struct g2d_ctx *curr; + struct g2d_variant *variant; int irq; wait_queue_head_t irq_queue; }; @@ -53,7 +57,7 @@ struct g2d_frame { struct g2d_ctx { struct v4l2_fh fh; struct g2d_dev *dev; - struct v4l2_m2m_ctx *m2m_ctx; + struct v4l2_m2m_ctx *m2m_ctx; struct g2d_frame in; struct g2d_frame out; struct v4l2_ctrl *ctrl_hflip; @@ -70,6 +74,9 @@ struct g2d_fmt { u32 hw; }; +struct g2d_variant { + unsigned short hw_rev; +}; void g2d_reset(struct g2d_dev *d); void g2d_set_src_size(struct g2d_dev *d, struct g2d_frame *f); @@ -80,7 +87,11 @@ void g2d_start(struct g2d_dev *d); void g2d_clear_int(struct g2d_dev *d); void g2d_set_rop4(struct g2d_dev *d, u32 r); void g2d_set_flip(struct g2d_dev *d, u32 r); -u32 g2d_cmd_stretch(u32 e); +void g2d_set_v41_stretch(struct g2d_dev *d, + struct g2d_frame *src, struct g2d_frame *dst); void g2d_set_cmd(struct g2d_dev *d, u32 c); - +static inline struct g2d_variant *g2d_get_drv_data(struct platform_device *pdev) +{ + return (struct g2d_variant *)platform_get_device_id(pdev)->driver_data; +} -- cgit v1.2.3 From 23575bf4219c06057632e601524ee66cccf03703 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sun, 13 Jan 2013 14:50:59 -0300 Subject: [media] noon010p30: Remove unneeded v4l2 control compatibility ops All host drivers using this subdev driver are already converted to use the control framework so the compatibility ops can be dropped. Signed-off-by: Sylwester Nawrocki Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/noon010pc30.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/noon010pc30.c b/drivers/media/i2c/noon010pc30.c index 440c12962bae..8554b47f993a 100644 --- a/drivers/media/i2c/noon010pc30.c +++ b/drivers/media/i2c/noon010pc30.c @@ -660,13 +660,6 @@ static const struct v4l2_ctrl_ops noon010_ctrl_ops = { static const struct v4l2_subdev_core_ops noon010_core_ops = { .s_power = noon010_s_power, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, .log_status = noon010_log_status, }; -- cgit v1.2.3 From 97466d0da3b08e087e517725ab56c7a2a2df706f Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 8 Jan 2013 02:48:24 -0300 Subject: [media] s5k6aa: Use devm_regulator_bulk_get API devm_regulator_bulk_get is device managed and saves some cleanup and exit code. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/s5k6aa.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c index 57cd4fa0193d..bdf5e3db31d1 100644 --- a/drivers/media/i2c/s5k6aa.c +++ b/drivers/media/i2c/s5k6aa.c @@ -1598,7 +1598,7 @@ static int s5k6aa_probe(struct i2c_client *client, for (i = 0; i < S5K6AA_NUM_SUPPLIES; i++) s5k6aa->supplies[i].supply = s5k6aa_supply_names[i]; - ret = regulator_bulk_get(&client->dev, S5K6AA_NUM_SUPPLIES, + ret = devm_regulator_bulk_get(&client->dev, S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); if (ret) { dev_err(&client->dev, "Failed to get regulators\n"); @@ -1607,7 +1607,7 @@ static int s5k6aa_probe(struct i2c_client *client, ret = s5k6aa_initialize_ctrls(s5k6aa); if (ret) - goto out_err4; + goto out_err3; s5k6aa_presets_data_init(s5k6aa); @@ -1618,8 +1618,6 @@ static int s5k6aa_probe(struct i2c_client *client, return 0; -out_err4: - regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); out_err3: s5k6aa_free_gpios(s5k6aa); out_err2: @@ -1635,7 +1633,6 @@ static int s5k6aa_remove(struct i2c_client *client) v4l2_device_unregister_subdev(sd); v4l2_ctrl_handler_free(sd->ctrl_handler); media_entity_cleanup(&sd->entity); - regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); s5k6aa_free_gpios(s5k6aa); return 0; -- cgit v1.2.3 From e82564475eab196b2e8a11572fff8e268329530e Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 22 Jan 2013 01:00:06 -0300 Subject: [media] s5p-mfc: Use WARN_ON(condition) directly Use WARN_ON(condition) directly instead of wrapping around an if condition. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 6347f09ca783..5050168ad211 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -582,8 +582,7 @@ static void s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx *ctx, clear_work_bit(ctx); - if (test_and_clear_bit(0, &dev->hw_lock) == 0) - WARN_ON(1); + WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0); s5p_mfc_clock_off(); wake_up(&ctx->queue); -- cgit v1.2.3 From 6e83e6e25eb49dc57a69b3f8ecc1e764c9775101 Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Fri, 18 Jan 2013 15:42:34 -0300 Subject: [media] s5p-mfc: Fix kernel warning on memory init Cleaned up the memory devices allocation code and added missing device_initialize() call to remove the kernel warning during memory allocations. Signed-off-by: Arun Kumar K Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 78 ++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 5050168ad211..3669e3b933ca 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1014,6 +1014,46 @@ static int match_child(struct device *dev, void *data) static void *mfc_get_drv_data(struct platform_device *pdev); +static int s5p_mfc_alloc_memdevs(struct s5p_mfc_dev *dev) +{ + unsigned int mem_info[2]; + + dev->mem_dev_l = devm_kzalloc(&dev->plat_dev->dev, + sizeof(struct device), GFP_KERNEL); + if (!dev->mem_dev_l) { + mfc_err("Not enough memory\n"); + return -ENOMEM; + } + device_initialize(dev->mem_dev_l); + of_property_read_u32_array(dev->plat_dev->dev.of_node, + "samsung,mfc-l", mem_info, 2); + if (dma_declare_coherent_memory(dev->mem_dev_l, mem_info[0], + mem_info[0], mem_info[1], + DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { + mfc_err("Failed to declare coherent memory for\n" + "MFC device\n"); + return -ENOMEM; + } + + dev->mem_dev_r = devm_kzalloc(&dev->plat_dev->dev, + sizeof(struct device), GFP_KERNEL); + if (!dev->mem_dev_r) { + mfc_err("Not enough memory\n"); + return -ENOMEM; + } + device_initialize(dev->mem_dev_r); + of_property_read_u32_array(dev->plat_dev->dev.of_node, + "samsung,mfc-r", mem_info, 2); + if (dma_declare_coherent_memory(dev->mem_dev_r, mem_info[0], + mem_info[0], mem_info[1], + DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { + pr_err("Failed to declare coherent memory for\n" + "MFC device\n"); + return -ENOMEM; + } + return 0; +} + /* MFC probe function */ static int s5p_mfc_probe(struct platform_device *pdev) { @@ -1021,7 +1061,6 @@ static int s5p_mfc_probe(struct platform_device *pdev) struct video_device *vfd; struct resource *res; int ret; - unsigned int mem_info[2]; pr_debug("%s++\n", __func__); dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); @@ -1069,39 +1108,8 @@ static int s5p_mfc_probe(struct platform_device *pdev) } if (pdev->dev.of_node) { - dev->mem_dev_l = kzalloc(sizeof(struct device), GFP_KERNEL); - if (!dev->mem_dev_l) { - mfc_err("Not enough memory\n"); - ret = -ENOMEM; - goto err_res; - } - of_property_read_u32_array(pdev->dev.of_node, "samsung,mfc-l", - mem_info, 2); - if (dma_declare_coherent_memory(dev->mem_dev_l, mem_info[0], - mem_info[0], mem_info[1], - DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { - mfc_err("Failed to declare coherent memory for\n" - "MFC device\n"); - ret = -ENOMEM; + if (s5p_mfc_alloc_memdevs(dev) < 0) goto err_res; - } - - dev->mem_dev_r = kzalloc(sizeof(struct device), GFP_KERNEL); - if (!dev->mem_dev_r) { - mfc_err("Not enough memory\n"); - ret = -ENOMEM; - goto err_res; - } - of_property_read_u32_array(pdev->dev.of_node, "samsung,mfc-r", - mem_info, 2); - if (dma_declare_coherent_memory(dev->mem_dev_r, mem_info[0], - mem_info[0], mem_info[1], - DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { - pr_err("Failed to declare coherent memory for\n" - "MFC device\n"); - ret = -ENOMEM; - goto err_res; - } } else { dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, "s5p-mfc-l", match_child); @@ -1247,6 +1255,10 @@ static int s5p_mfc_remove(struct platform_device *pdev) s5p_mfc_release_firmware(dev); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]); + if (pdev->dev.of_node) { + put_device(dev->mem_dev_l); + put_device(dev->mem_dev_r); + } s5p_mfc_final_pm(dev); return 0; -- cgit v1.2.3 From cac47f1822fcb97018e24b05a7fb31f11a6bc28c Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Thu, 22 Nov 2012 11:39:18 -0300 Subject: [media] V4L: Add S5C73M3 camera driver Add driver for S5C73M3 image sensor. The driver exposes the sensor as two subdevs: pure sensor and output interface. Two subdev architecture supports interleaved UYVY/JPEG image format with separate frame size for both sub-formats, there is a spearate pad for each sub-format. Signed-off-by: Sylwester Nawrocki Signed-off-by: Andrzej Hajda Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 7 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/s5c73m3/Makefile | 2 + drivers/media/i2c/s5c73m3/s5c73m3-core.c | 1706 +++++++++++++++++++++++++++++ drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c | 563 ++++++++++ drivers/media/i2c/s5c73m3/s5c73m3-spi.c | 156 +++ drivers/media/i2c/s5c73m3/s5c73m3.h | 459 ++++++++ include/media/s5c73m3.h | 55 + 8 files changed, 2949 insertions(+) create mode 100644 drivers/media/i2c/s5c73m3/Makefile create mode 100644 drivers/media/i2c/s5c73m3/s5c73m3-core.c create mode 100644 drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c create mode 100644 drivers/media/i2c/s5c73m3/s5c73m3-spi.c create mode 100644 drivers/media/i2c/s5c73m3/s5c73m3.h create mode 100644 include/media/s5c73m3.h (limited to 'drivers') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index d73078cdffec..ecdf7e3fdcf4 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -523,6 +523,13 @@ config VIDEO_S5K4ECGX source "drivers/media/i2c/smiapp/Kconfig" +config VIDEO_S5C73M3 + tristate "Samsung S5C73M3 sensor support" + depends on I2C && SPI && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + ---help--- + This is a V4L2 sensor-level driver for Samsung S5C73M3 + 8 Mpixel camera. + comment "Flash devices" config VIDEO_ADP1653 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 8b62e54d3884..b35e25798cd3 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o +obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o diff --git a/drivers/media/i2c/s5c73m3/Makefile b/drivers/media/i2c/s5c73m3/Makefile new file mode 100644 index 000000000000..fa4df342d1f1 --- /dev/null +++ b/drivers/media/i2c/s5c73m3/Makefile @@ -0,0 +1,2 @@ +s5c73m3-objs := s5c73m3-core.o s5c73m3-spi.o s5c73m3-ctrls.o +obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3.o diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c new file mode 100644 index 000000000000..600909ddb150 --- /dev/null +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -0,0 +1,1706 @@ +/* + * Samsung LSI S5C73M3 8M pixel camera driver + * + * Copyright (C) 2012, Samsung Electronics, Co., Ltd. + * Sylwester Nawrocki + * Andrzej Hajda + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "s5c73m3.h" + +int s5c73m3_dbg; +module_param_named(debug, s5c73m3_dbg, int, 0644); + +int boot_from_rom = 1; +module_param(boot_from_rom, int, 0644); + +int update_fw; +module_param(update_fw, int, 0644); + +#define S5C73M3_EMBEDDED_DATA_MAXLEN SZ_4K + +static const char * const s5c73m3_supply_names[S5C73M3_MAX_SUPPLIES] = { + "vdd-int", /* Digital Core supply (1.2V), CAM_ISP_CORE_1.2V */ + "vdda", /* Analog Core supply (1.2V), CAM_SENSOR_CORE_1.2V */ + "vdd-reg", /* Regulator input supply (2.8V), CAM_SENSOR_A2.8V */ + "vddio-host", /* Digital Host I/O power supply (1.8V...2.8V), + CAM_ISP_SENSOR_1.8V */ + "vddio-cis", /* Digital CIS I/O power (1.2V...1.8V), + CAM_ISP_MIPI_1.2V */ + "vdd-af", /* Lens, CAM_AF_2.8V */ +}; + +static const struct s5c73m3_frame_size s5c73m3_isp_resolutions[] = { + { 320, 240, COMM_CHG_MODE_YUV_320_240 }, + { 352, 288, COMM_CHG_MODE_YUV_352_288 }, + { 640, 480, COMM_CHG_MODE_YUV_640_480 }, + { 880, 720, COMM_CHG_MODE_YUV_880_720 }, + { 960, 720, COMM_CHG_MODE_YUV_960_720 }, + { 1008, 672, COMM_CHG_MODE_YUV_1008_672 }, + { 1184, 666, COMM_CHG_MODE_YUV_1184_666 }, + { 1280, 720, COMM_CHG_MODE_YUV_1280_720 }, + { 1536, 864, COMM_CHG_MODE_YUV_1536_864 }, + { 1600, 1200, COMM_CHG_MODE_YUV_1600_1200 }, + { 1632, 1224, COMM_CHG_MODE_YUV_1632_1224 }, + { 1920, 1080, COMM_CHG_MODE_YUV_1920_1080 }, + { 1920, 1440, COMM_CHG_MODE_YUV_1920_1440 }, + { 2304, 1296, COMM_CHG_MODE_YUV_2304_1296 }, + { 3264, 2448, COMM_CHG_MODE_YUV_3264_2448 }, +}; + +static const struct s5c73m3_frame_size s5c73m3_jpeg_resolutions[] = { + { 640, 480, COMM_CHG_MODE_JPEG_640_480 }, + { 800, 450, COMM_CHG_MODE_JPEG_800_450 }, + { 800, 600, COMM_CHG_MODE_JPEG_800_600 }, + { 1024, 768, COMM_CHG_MODE_JPEG_1024_768 }, + { 1280, 720, COMM_CHG_MODE_JPEG_1280_720 }, + { 1280, 960, COMM_CHG_MODE_JPEG_1280_960 }, + { 1600, 900, COMM_CHG_MODE_JPEG_1600_900 }, + { 1600, 1200, COMM_CHG_MODE_JPEG_1600_1200 }, + { 2048, 1152, COMM_CHG_MODE_JPEG_2048_1152 }, + { 2048, 1536, COMM_CHG_MODE_JPEG_2048_1536 }, + { 2560, 1440, COMM_CHG_MODE_JPEG_2560_1440 }, + { 2560, 1920, COMM_CHG_MODE_JPEG_2560_1920 }, + { 3264, 1836, COMM_CHG_MODE_JPEG_3264_1836 }, + { 3264, 2176, COMM_CHG_MODE_JPEG_3264_2176 }, + { 3264, 2448, COMM_CHG_MODE_JPEG_3264_2448 }, +}; + +static const struct s5c73m3_frame_size * const s5c73m3_resolutions[] = { + [RES_ISP] = s5c73m3_isp_resolutions, + [RES_JPEG] = s5c73m3_jpeg_resolutions +}; + +static const int s5c73m3_resolutions_len[] = { + [RES_ISP] = ARRAY_SIZE(s5c73m3_isp_resolutions), + [RES_JPEG] = ARRAY_SIZE(s5c73m3_jpeg_resolutions) +}; + +static const struct s5c73m3_interval s5c73m3_intervals[] = { + { COMM_FRAME_RATE_FIXED_7FPS, {142857, 1000000}, {3264, 2448} }, + { COMM_FRAME_RATE_FIXED_15FPS, {66667, 1000000}, {3264, 2448} }, + { COMM_FRAME_RATE_FIXED_20FPS, {50000, 1000000}, {2304, 1296} }, + { COMM_FRAME_RATE_FIXED_30FPS, {33333, 1000000}, {2304, 1296} }, +}; + +#define S5C73M3_DEFAULT_FRAME_INTERVAL 3 /* 30 fps */ + +static void s5c73m3_fill_mbus_fmt(struct v4l2_mbus_framefmt *mf, + const struct s5c73m3_frame_size *fs, + u32 code) +{ + mf->width = fs->width; + mf->height = fs->height; + mf->code = code; + mf->colorspace = V4L2_COLORSPACE_JPEG; + mf->field = V4L2_FIELD_NONE; +} + +static int s5c73m3_i2c_write(struct i2c_client *client, u16 addr, u16 data) +{ + u8 buf[4] = { addr >> 8, addr & 0xff, data >> 8, data & 0xff }; + + int ret = i2c_master_send(client, buf, sizeof(buf)); + + v4l_dbg(4, s5c73m3_dbg, client, "%s: addr 0x%04x, data 0x%04x\n", + __func__, addr, data); + + if (ret == 4) + return 0; + + return ret < 0 ? ret : -EREMOTEIO; +} + +static int s5c73m3_i2c_read(struct i2c_client *client, u16 addr, u16 *data) +{ + int ret; + u8 rbuf[2], wbuf[2] = { addr >> 8, addr & 0xff }; + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .flags = 0, + .len = sizeof(wbuf), + .buf = wbuf + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .len = sizeof(rbuf), + .buf = rbuf + } + }; + /* + * Issue repeated START after writing 2 address bytes and + * just one STOP only after reading the data bytes. + */ + ret = i2c_transfer(client->adapter, msg, 2); + if (ret == 2) { + *data = be16_to_cpup((u16 *)rbuf); + v4l2_dbg(4, s5c73m3_dbg, client, + "%s: addr: 0x%04x, data: 0x%04x\n", + __func__, addr, *data); + return 0; + } + + v4l2_err(client, "I2C read failed: addr: %04x, (%d)\n", addr, ret); + + return ret >= 0 ? -EREMOTEIO : ret; +} + +int s5c73m3_write(struct s5c73m3 *state, u32 addr, u16 data) +{ + struct i2c_client *client = state->i2c_client; + int ret; + + if ((addr ^ state->i2c_write_address) & 0xffff0000) { + ret = s5c73m3_i2c_write(client, REG_CMDWR_ADDRH, addr >> 16); + if (ret < 0) { + state->i2c_write_address = 0; + return ret; + } + } + + if ((addr ^ state->i2c_write_address) & 0xffff) { + ret = s5c73m3_i2c_write(client, REG_CMDWR_ADDRL, addr & 0xffff); + if (ret < 0) { + state->i2c_write_address = 0; + return ret; + } + } + + state->i2c_write_address = addr; + + ret = s5c73m3_i2c_write(client, REG_CMDBUF_ADDR, data); + if (ret < 0) + return ret; + + state->i2c_write_address += 2; + + return ret; +} + +int s5c73m3_read(struct s5c73m3 *state, u32 addr, u16 *data) +{ + struct i2c_client *client = state->i2c_client; + int ret; + + if ((addr ^ state->i2c_read_address) & 0xffff0000) { + ret = s5c73m3_i2c_write(client, REG_CMDRD_ADDRH, addr >> 16); + if (ret < 0) { + state->i2c_read_address = 0; + return ret; + } + } + + if ((addr ^ state->i2c_read_address) & 0xffff) { + ret = s5c73m3_i2c_write(client, REG_CMDRD_ADDRL, addr & 0xffff); + if (ret < 0) { + state->i2c_read_address = 0; + return ret; + } + } + + state->i2c_read_address = addr; + + ret = s5c73m3_i2c_read(client, REG_CMDBUF_ADDR, data); + if (ret < 0) + return ret; + + state->i2c_read_address += 2; + + return ret; +} + +static int s5c73m3_check_status(struct s5c73m3 *state, unsigned int value) +{ + unsigned long start = jiffies; + unsigned long end = start + msecs_to_jiffies(2000); + int ret = 0; + u16 status; + int count = 0; + + while (time_is_after_jiffies(end)) { + ret = s5c73m3_read(state, REG_STATUS, &status); + if (ret < 0 || status == value) + break; + usleep_range(500, 1000); + ++count; + } + + if (count > 0) + v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, + "status check took %dms\n", + jiffies_to_msecs(jiffies - start)); + + if (ret == 0 && status != value) { + u16 i2c_status = 0; + u16 i2c_seq_status = 0; + + s5c73m3_read(state, REG_I2C_STATUS, &i2c_status); + s5c73m3_read(state, REG_I2C_SEQ_STATUS, &i2c_seq_status); + + v4l2_err(&state->sensor_sd, + "wrong status %#x, expected: %#x, i2c_status: %#x/%#x\n", + status, value, i2c_status, i2c_seq_status); + + return -ETIMEDOUT; + } + + return ret; +} + +int s5c73m3_isp_command(struct s5c73m3 *state, u16 command, u16 data) +{ + int ret; + + ret = s5c73m3_check_status(state, REG_STATUS_ISP_COMMAND_COMPLETED); + if (ret < 0) + return ret; + + ret = s5c73m3_write(state, 0x00095000, command); + if (ret < 0) + return ret; + + ret = s5c73m3_write(state, 0x00095002, data); + if (ret < 0) + return ret; + + return s5c73m3_write(state, REG_STATUS, 0x0001); +} + +int s5c73m3_isp_comm_result(struct s5c73m3 *state, u16 command, u16 *data) +{ + return s5c73m3_read(state, COMM_RESULT_OFFSET + command, data); +} + +static int s5c73m3_set_af_softlanding(struct s5c73m3 *state) +{ + unsigned long start = jiffies; + u16 af_softlanding; + int count = 0; + int ret; + const char *msg; + + ret = s5c73m3_isp_command(state, COMM_AF_SOFTLANDING, + COMM_AF_SOFTLANDING_ON); + if (ret < 0) { + v4l2_info(&state->sensor_sd, "AF soft-landing failed\n"); + return ret; + } + + for (;;) { + ret = s5c73m3_isp_comm_result(state, COMM_AF_SOFTLANDING, + &af_softlanding); + if (ret < 0) { + msg = "failed"; + break; + } + if (af_softlanding == COMM_AF_SOFTLANDING_RES_COMPLETE) { + msg = "succeeded"; + break; + } + if (++count > 100) { + ret = -ETIME; + msg = "timed out"; + break; + } + msleep(25); + } + + v4l2_info(&state->sensor_sd, "AF soft-landing %s after %dms\n", + msg, jiffies_to_msecs(jiffies - start)); + + return ret; +} + +static int s5c73m3_load_fw(struct v4l2_subdev *sd) +{ + struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); + struct i2c_client *client = state->i2c_client; + const struct firmware *fw; + int ret; + char fw_name[20]; + + snprintf(fw_name, sizeof(fw_name), "SlimISP_%.2s.bin", + state->fw_file_version); + ret = request_firmware(&fw, fw_name, &client->dev); + if (ret < 0) { + v4l2_err(sd, "Firmware request failed (%s)\n", fw_name); + return -EINVAL; + } + + v4l2_info(sd, "Loading firmware (%s, %d B)\n", fw_name, fw->size); + + ret = s5c73m3_spi_write(state, fw->data, fw->size, 64); + + if (ret >= 0) + state->isp_ready = 1; + else + v4l2_err(sd, "SPI write failed\n"); + + release_firmware(fw); + + return ret; +} + +static int s5c73m3_set_frame_size(struct s5c73m3 *state) +{ + const struct s5c73m3_frame_size *prev_size = + state->sensor_pix_size[RES_ISP]; + const struct s5c73m3_frame_size *cap_size = + state->sensor_pix_size[RES_JPEG]; + unsigned int chg_mode; + + v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, + "Preview size: %dx%d, reg_val: 0x%x\n", + prev_size->width, prev_size->height, prev_size->reg_val); + + chg_mode = prev_size->reg_val | COMM_CHG_MODE_NEW; + + if (state->mbus_code == S5C73M3_JPEG_FMT) { + v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, + "Capture size: %dx%d, reg_val: 0x%x\n", + cap_size->width, cap_size->height, cap_size->reg_val); + chg_mode |= cap_size->reg_val; + } + + return s5c73m3_isp_command(state, COMM_CHG_MODE, chg_mode); +} + +static int s5c73m3_set_frame_rate(struct s5c73m3 *state) +{ + int ret; + + if (state->ctrls.stabilization->val) + return 0; + + if (WARN_ON(state->fiv == NULL)) + return -EINVAL; + + ret = s5c73m3_isp_command(state, COMM_FRAME_RATE, state->fiv->fps_reg); + if (!ret) + state->apply_fiv = 0; + + return ret; +} + +static int __s5c73m3_s_stream(struct s5c73m3 *state, struct v4l2_subdev *sd, + int on) +{ + u16 mode; + int ret; + + if (on && state->apply_fmt) { + if (state->mbus_code == S5C73M3_JPEG_FMT) + mode = COMM_IMG_OUTPUT_INTERLEAVED; + else + mode = COMM_IMG_OUTPUT_YUV; + + ret = s5c73m3_isp_command(state, COMM_IMG_OUTPUT, mode); + if (!ret) + ret = s5c73m3_set_frame_size(state); + if (ret) + return ret; + state->apply_fmt = 0; + } + + ret = s5c73m3_isp_command(state, COMM_SENSOR_STREAMING, !!on); + if (ret) + return ret; + + state->streaming = !!on; + + if (!on) + return ret; + + if (state->apply_fiv) { + ret = s5c73m3_set_frame_rate(state); + if (ret < 0) + v4l2_err(sd, "Error setting frame rate(%d)\n", ret); + } + + return s5c73m3_check_status(state, REG_STATUS_ISP_COMMAND_COMPLETED); +} + +static int s5c73m3_oif_s_stream(struct v4l2_subdev *sd, int on) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + int ret; + + mutex_lock(&state->lock); + ret = __s5c73m3_s_stream(state, sd, on); + mutex_unlock(&state->lock); + + return ret; +} + +static int s5c73m3_system_status_wait(struct s5c73m3 *state, u32 value, + unsigned int delay, unsigned int steps) +{ + u16 reg = 0; + + while (steps-- > 0) { + int ret = s5c73m3_read(state, 0x30100010, ®); + if (ret < 0) + return ret; + if (reg == value) + return 0; + usleep_range(delay, delay + 25); + } + return -ETIMEDOUT; +} + +static int s5c73m3_read_fw_version(struct s5c73m3 *state) +{ + struct v4l2_subdev *sd = &state->sensor_sd; + int i, ret; + u16 data[2]; + int offset; + + offset = state->isp_ready ? 0x60 : 0; + + for (i = 0; i < S5C73M3_SENSOR_FW_LEN / 2; i++) { + ret = s5c73m3_read(state, offset + i * 2, data); + if (ret < 0) + return ret; + state->sensor_fw[i * 2] = (char)(*data & 0xff); + state->sensor_fw[i * 2 + 1] = (char)(*data >> 8); + } + state->sensor_fw[S5C73M3_SENSOR_FW_LEN] = '\0'; + + + for (i = 0; i < S5C73M3_SENSOR_TYPE_LEN / 2; i++) { + ret = s5c73m3_read(state, offset + 6 + i * 2, data); + if (ret < 0) + return ret; + state->sensor_type[i * 2] = (char)(*data & 0xff); + state->sensor_type[i * 2 + 1] = (char)(*data >> 8); + } + state->sensor_type[S5C73M3_SENSOR_TYPE_LEN] = '\0'; + + ret = s5c73m3_read(state, offset + 0x14, data); + if (ret >= 0) { + ret = s5c73m3_read(state, offset + 0x16, data + 1); + if (ret >= 0) + state->fw_size = data[0] + (data[1] << 16); + } + + v4l2_info(sd, "Sensor type: %s, FW version: %s\n", + state->sensor_type, state->sensor_fw); + return ret; +} + +static int s5c73m3_fw_update_from(struct s5c73m3 *state) +{ + struct v4l2_subdev *sd = &state->sensor_sd; + u16 status = COMM_FW_UPDATE_NOT_READY; + int ret; + int count = 0; + + v4l2_warn(sd, "Updating F-ROM firmware.\n"); + do { + if (status == COMM_FW_UPDATE_NOT_READY) { + ret = s5c73m3_isp_command(state, COMM_FW_UPDATE, 0); + if (ret < 0) + return ret; + } + + ret = s5c73m3_read(state, 0x00095906, &status); + if (ret < 0) + return ret; + switch (status) { + case COMM_FW_UPDATE_FAIL: + v4l2_warn(sd, "Updating F-ROM firmware failed.\n"); + return -EIO; + case COMM_FW_UPDATE_SUCCESS: + v4l2_warn(sd, "Updating F-ROM firmware finished.\n"); + return 0; + } + ++count; + msleep(20); + } while (count < 500); + + v4l2_warn(sd, "Updating F-ROM firmware timed-out.\n"); + return -ETIMEDOUT; +} + +static int s5c73m3_spi_boot(struct s5c73m3 *state, bool load_fw) +{ + struct v4l2_subdev *sd = &state->sensor_sd; + int ret; + + /* Run ARM MCU */ + ret = s5c73m3_write(state, 0x30000004, 0xffff); + if (ret < 0) + return ret; + + usleep_range(400, 500); + + /* Check booting status */ + ret = s5c73m3_system_status_wait(state, 0x0c, 100, 3); + if (ret < 0) { + v4l2_err(sd, "booting failed: %d\n", ret); + return ret; + } + + /* P,M,S and Boot Mode */ + ret = s5c73m3_write(state, 0x30100014, 0x2146); + if (ret < 0) + return ret; + + ret = s5c73m3_write(state, 0x30100010, 0x210c); + if (ret < 0) + return ret; + + usleep_range(200, 250); + + /* Check SPI status */ + ret = s5c73m3_system_status_wait(state, 0x210d, 100, 300); + if (ret < 0) + v4l2_err(sd, "SPI not ready: %d\n", ret); + + /* Firmware download over SPI */ + if (load_fw) + s5c73m3_load_fw(sd); + + /* MCU reset */ + ret = s5c73m3_write(state, 0x30000004, 0xfffd); + if (ret < 0) + return ret; + + /* Remap */ + ret = s5c73m3_write(state, 0x301000a4, 0x0183); + if (ret < 0) + return ret; + + /* MCU restart */ + ret = s5c73m3_write(state, 0x30000004, 0xffff); + if (ret < 0 || !load_fw) + return ret; + + ret = s5c73m3_read_fw_version(state); + if (ret < 0) + return ret; + + if (load_fw && update_fw) { + ret = s5c73m3_fw_update_from(state); + update_fw = 0; + } + + return ret; +} + +static int s5c73m3_set_timing_register_for_vdd(struct s5c73m3 *state) +{ + static const u32 regs[][2] = { + { 0x30100018, 0x0618 }, + { 0x3010001c, 0x10c1 }, + { 0x30100020, 0x249e } + }; + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + ret = s5c73m3_write(state, regs[i][0], regs[i][1]); + if (ret < 0) + return ret; + } + + return 0; +} + +static void s5c73m3_set_fw_file_version(struct s5c73m3 *state) +{ + switch (state->sensor_fw[0]) { + case 'G': + case 'O': + state->fw_file_version[0] = 'G'; + break; + case 'S': + case 'Z': + state->fw_file_version[0] = 'Z'; + break; + } + + switch (state->sensor_fw[1]) { + case 'C'...'F': + state->fw_file_version[1] = state->sensor_fw[1]; + break; + } +} + +static int s5c73m3_get_fw_version(struct s5c73m3 *state) +{ + struct v4l2_subdev *sd = &state->sensor_sd; + int ret; + + /* Run ARM MCU */ + ret = s5c73m3_write(state, 0x30000004, 0xffff); + if (ret < 0) + return ret; + usleep_range(400, 500); + + /* Check booting status */ + ret = s5c73m3_system_status_wait(state, 0x0c, 100, 3); + if (ret < 0) { + + v4l2_err(sd, "%s: booting failed: %d\n", __func__, ret); + return ret; + } + + /* Change I/O Driver Current in order to read from F-ROM */ + ret = s5c73m3_write(state, 0x30100120, 0x0820); + ret = s5c73m3_write(state, 0x30100124, 0x0820); + + /* Offset Setting */ + ret = s5c73m3_write(state, 0x00010418, 0x0008); + + /* P,M,S and Boot Mode */ + ret = s5c73m3_write(state, 0x30100014, 0x2146); + if (ret < 0) + return ret; + ret = s5c73m3_write(state, 0x30100010, 0x230c); + if (ret < 0) + return ret; + + usleep_range(200, 250); + + /* Check SPI status */ + ret = s5c73m3_system_status_wait(state, 0x230e, 100, 300); + if (ret < 0) + v4l2_err(sd, "SPI not ready: %d\n", ret); + + /* ARM reset */ + ret = s5c73m3_write(state, 0x30000004, 0xfffd); + if (ret < 0) + return ret; + + /* Remap */ + ret = s5c73m3_write(state, 0x301000a4, 0x0183); + if (ret < 0) + return ret; + + s5c73m3_set_timing_register_for_vdd(state); + + ret = s5c73m3_read_fw_version(state); + + s5c73m3_set_fw_file_version(state); + + return ret; +} + +static int s5c73m3_rom_boot(struct s5c73m3 *state, bool load_fw) +{ + static const u32 boot_regs[][2] = { + { 0x3100010c, 0x0044 }, + { 0x31000108, 0x000d }, + { 0x31000304, 0x0001 }, + { 0x00010000, 0x5800 }, + { 0x00010002, 0x0002 }, + { 0x31000000, 0x0001 }, + { 0x30100014, 0x1b85 }, + { 0x30100010, 0x230c } + }; + struct v4l2_subdev *sd = &state->sensor_sd; + int i, ret; + + /* Run ARM MCU */ + ret = s5c73m3_write(state, 0x30000004, 0xffff); + if (ret < 0) + return ret; + usleep_range(400, 450); + + /* Check booting status */ + ret = s5c73m3_system_status_wait(state, 0x0c, 100, 4); + if (ret < 0) { + v4l2_err(sd, "Booting failed: %d\n", ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(boot_regs); i++) { + ret = s5c73m3_write(state, boot_regs[i][0], boot_regs[i][1]); + if (ret < 0) + return ret; + } + msleep(200); + + /* Check the binary read status */ + ret = s5c73m3_system_status_wait(state, 0x230e, 1000, 150); + if (ret < 0) { + v4l2_err(sd, "Binary read failed: %d\n", ret); + return ret; + } + + /* ARM reset */ + ret = s5c73m3_write(state, 0x30000004, 0xfffd); + if (ret < 0) + return ret; + /* Remap */ + ret = s5c73m3_write(state, 0x301000a4, 0x0183); + if (ret < 0) + return ret; + /* MCU re-start */ + ret = s5c73m3_write(state, 0x30000004, 0xffff); + if (ret < 0) + return ret; + + state->isp_ready = 1; + + return s5c73m3_read_fw_version(state); +} + +static int s5c73m3_isp_init(struct s5c73m3 *state) +{ + int ret; + + state->i2c_read_address = 0; + state->i2c_write_address = 0; + + ret = s5c73m3_i2c_write(state->i2c_client, AHB_MSB_ADDR_PTR, 0x3310); + if (ret < 0) + return ret; + + if (boot_from_rom) + return s5c73m3_rom_boot(state, true); + else + return s5c73m3_spi_boot(state, true); +} + +static const struct s5c73m3_frame_size *s5c73m3_find_frame_size( + struct v4l2_mbus_framefmt *fmt, + enum s5c73m3_resolution_types idx) +{ + const struct s5c73m3_frame_size *fs; + const struct s5c73m3_frame_size *best_fs; + int best_dist = INT_MAX; + int i; + + fs = s5c73m3_resolutions[idx]; + best_fs = NULL; + for (i = 0; i < s5c73m3_resolutions_len[idx]; ++i) { + int dist = abs(fs->width - fmt->width) + + abs(fs->height - fmt->height); + if (dist < best_dist) { + best_dist = dist; + best_fs = fs; + } + ++fs; + } + + return best_fs; +} + +static void s5c73m3_oif_try_format(struct s5c73m3 *state, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt, + const struct s5c73m3_frame_size **fs) +{ + u32 code; + + switch (fmt->pad) { + case OIF_ISP_PAD: + *fs = s5c73m3_find_frame_size(&fmt->format, RES_ISP); + code = S5C73M3_ISP_FMT; + break; + case OIF_JPEG_PAD: + *fs = s5c73m3_find_frame_size(&fmt->format, RES_JPEG); + code = S5C73M3_JPEG_FMT; + break; + case OIF_SOURCE_PAD: + default: + if (fmt->format.code == S5C73M3_JPEG_FMT) + code = S5C73M3_JPEG_FMT; + else + code = S5C73M3_ISP_FMT; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + *fs = state->oif_pix_size[RES_ISP]; + else + *fs = s5c73m3_find_frame_size( + v4l2_subdev_get_try_format(fh, + OIF_ISP_PAD), + RES_ISP); + break; + } + + s5c73m3_fill_mbus_fmt(&fmt->format, *fs, code); +} + +static void s5c73m3_try_format(struct s5c73m3 *state, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt, + const struct s5c73m3_frame_size **fs) +{ + u32 code; + + if (fmt->pad == S5C73M3_ISP_PAD) { + *fs = s5c73m3_find_frame_size(&fmt->format, RES_ISP); + code = S5C73M3_ISP_FMT; + } else { + *fs = s5c73m3_find_frame_size(&fmt->format, RES_JPEG); + code = S5C73M3_JPEG_FMT; + } + + s5c73m3_fill_mbus_fmt(&fmt->format, *fs, code); +} + +static int s5c73m3_oif_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + + if (fi->pad != OIF_SOURCE_PAD) + return -EINVAL; + + mutex_lock(&state->lock); + fi->interval = state->fiv->interval; + mutex_unlock(&state->lock); + + return 0; +} + +static int __s5c73m3_set_frame_interval(struct s5c73m3 *state, + struct v4l2_subdev_frame_interval *fi) +{ + const struct s5c73m3_frame_size *prev_size = + state->sensor_pix_size[RES_ISP]; + const struct s5c73m3_interval *fiv = &s5c73m3_intervals[0]; + unsigned int ret, min_err = UINT_MAX; + unsigned int i, fr_time; + + if (fi->interval.denominator == 0) + return -EINVAL; + + fr_time = fi->interval.numerator * 1000 / fi->interval.denominator; + + for (i = 0; i < ARRAY_SIZE(s5c73m3_intervals); i++) { + const struct s5c73m3_interval *iv = &s5c73m3_intervals[i]; + + if (prev_size->width > iv->size.width || + prev_size->height > iv->size.height) + continue; + + ret = abs(iv->interval.numerator / 1000 - fr_time); + if (ret < min_err) { + fiv = iv; + min_err = ret; + } + } + state->fiv = fiv; + + v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, + "Changed frame interval to %u us\n", fiv->interval.numerator); + return 0; +} + +static int s5c73m3_oif_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + int ret; + + if (fi->pad != OIF_SOURCE_PAD) + return -EINVAL; + + v4l2_dbg(1, s5c73m3_dbg, sd, "Setting %d/%d frame interval\n", + fi->interval.numerator, fi->interval.denominator); + + mutex_lock(&state->lock); + + ret = __s5c73m3_set_frame_interval(state, fi); + if (!ret) { + if (state->streaming) + ret = s5c73m3_set_frame_rate(state); + else + state->apply_fiv = 1; + } + mutex_unlock(&state->lock); + return ret; +} + +static int s5c73m3_oif_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + const struct s5c73m3_interval *fi; + int ret = 0; + + if (fie->pad != OIF_SOURCE_PAD) + return -EINVAL; + if (fie->index > ARRAY_SIZE(s5c73m3_intervals)) + return -EINVAL; + + mutex_lock(&state->lock); + fi = &s5c73m3_intervals[fie->index]; + if (fie->width > fi->size.width || fie->height > fi->size.height) + ret = -EINVAL; + else + fie->interval = fi->interval; + mutex_unlock(&state->lock); + + return ret; +} + +static int s5c73m3_oif_get_pad_code(int pad, int index) +{ + if (pad == OIF_SOURCE_PAD) { + if (index > 1) + return -EINVAL; + return (index == 0) ? S5C73M3_ISP_FMT : S5C73M3_JPEG_FMT; + } + + if (index > 0) + return -EINVAL; + + return (pad == OIF_ISP_PAD) ? S5C73M3_ISP_FMT : S5C73M3_JPEG_FMT; +} + +static int s5c73m3_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); + const struct s5c73m3_frame_size *fs; + u32 code; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad); + return 0; + } + + mutex_lock(&state->lock); + + switch (fmt->pad) { + case S5C73M3_ISP_PAD: + code = S5C73M3_ISP_FMT; + fs = state->sensor_pix_size[RES_ISP]; + break; + case S5C73M3_JPEG_PAD: + code = S5C73M3_JPEG_FMT; + fs = state->sensor_pix_size[RES_JPEG]; + break; + default: + mutex_unlock(&state->lock); + return -EINVAL; + } + s5c73m3_fill_mbus_fmt(&fmt->format, fs, code); + + mutex_unlock(&state->lock); + return 0; +} + +static int s5c73m3_oif_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + const struct s5c73m3_frame_size *fs; + u32 code; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad); + return 0; + } + + mutex_lock(&state->lock); + + switch (fmt->pad) { + case OIF_ISP_PAD: + code = S5C73M3_ISP_FMT; + fs = state->oif_pix_size[RES_ISP]; + break; + case OIF_JPEG_PAD: + code = S5C73M3_JPEG_FMT; + fs = state->oif_pix_size[RES_JPEG]; + break; + case OIF_SOURCE_PAD: + code = state->mbus_code; + fs = state->oif_pix_size[RES_ISP]; + break; + default: + mutex_unlock(&state->lock); + return -EINVAL; + } + s5c73m3_fill_mbus_fmt(&fmt->format, fs, code); + + mutex_unlock(&state->lock); + return 0; +} + +static int s5c73m3_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + const struct s5c73m3_frame_size *frame_size = NULL; + struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); + struct v4l2_mbus_framefmt *mf; + int ret = 0; + + mutex_lock(&state->lock); + + s5c73m3_try_format(state, fh, fmt, &frame_size); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + *mf = fmt->format; + } else { + switch (fmt->pad) { + case S5C73M3_ISP_PAD: + state->sensor_pix_size[RES_ISP] = frame_size; + break; + case S5C73M3_JPEG_PAD: + state->sensor_pix_size[RES_JPEG] = frame_size; + break; + default: + ret = -EBUSY; + } + + if (state->streaming) + ret = -EBUSY; + else + state->apply_fmt = 1; + } + + mutex_unlock(&state->lock); + + return ret; +} + +static int s5c73m3_oif_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + const struct s5c73m3_frame_size *frame_size = NULL; + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + struct v4l2_mbus_framefmt *mf; + int ret = 0; + + mutex_lock(&state->lock); + + s5c73m3_oif_try_format(state, fh, fmt, &frame_size); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + *mf = fmt->format; + } else { + switch (fmt->pad) { + case OIF_ISP_PAD: + state->oif_pix_size[RES_ISP] = frame_size; + break; + case OIF_JPEG_PAD: + state->oif_pix_size[RES_JPEG] = frame_size; + break; + case OIF_SOURCE_PAD: + state->mbus_code = fmt->format.code; + break; + default: + ret = -EBUSY; + } + + if (state->streaming) + ret = -EBUSY; + else + state->apply_fmt = 1; + } + + mutex_unlock(&state->lock); + + return ret; +} + +static int s5c73m3_oif_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + int i; + + if (pad != OIF_SOURCE_PAD || fd == NULL) + return -EINVAL; + + mutex_lock(&state->lock); + fd->num_entries = 2; + for (i = 0; i < fd->num_entries; i++) + fd->entry[i] = state->frame_desc.entry[i]; + mutex_unlock(&state->lock); + + return 0; +} + +static int s5c73m3_oif_set_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + struct v4l2_mbus_frame_desc *frame_desc = &state->frame_desc; + int i; + + if (pad != OIF_SOURCE_PAD || fd == NULL) + return -EINVAL; + + fd->entry[0].length = 10 * SZ_1M; + fd->entry[1].length = max_t(u32, fd->entry[1].length, + S5C73M3_EMBEDDED_DATA_MAXLEN); + fd->num_entries = 2; + + mutex_lock(&state->lock); + for (i = 0; i < fd->num_entries; i++) + frame_desc->entry[i] = fd->entry[i]; + mutex_unlock(&state->lock); + + return 0; +} + +static int s5c73m3_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + static const int codes[] = { + [S5C73M3_ISP_PAD] = S5C73M3_ISP_FMT, + [S5C73M3_JPEG_PAD] = S5C73M3_JPEG_FMT}; + + if (code->index > 0 || code->pad >= S5C73M3_NUM_PADS) + return -EINVAL; + + code->code = codes[code->pad]; + + return 0; +} + +static int s5c73m3_oif_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + int ret; + + ret = s5c73m3_oif_get_pad_code(code->pad, code->index); + if (ret < 0) + return ret; + + code->code = ret; + + return 0; +} + +static int s5c73m3_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + int idx; + + if (fse->pad == S5C73M3_ISP_PAD) { + if (fse->code != S5C73M3_ISP_FMT) + return -EINVAL; + idx = RES_ISP; + } else{ + if (fse->code != S5C73M3_JPEG_FMT) + return -EINVAL; + idx = RES_JPEG; + } + + if (fse->index >= s5c73m3_resolutions_len[idx]) + return -EINVAL; + + fse->min_width = s5c73m3_resolutions[idx][fse->index].width; + fse->max_width = fse->min_width; + fse->max_height = s5c73m3_resolutions[idx][fse->index].height; + fse->min_height = fse->max_height; + + return 0; +} + +static int s5c73m3_oif_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + int idx; + + if (fse->pad == OIF_SOURCE_PAD) { + if (fse->index > 0) + return -EINVAL; + + switch (fse->code) { + case S5C73M3_JPEG_FMT: + case S5C73M3_ISP_FMT: { + struct v4l2_mbus_framefmt *mf = + v4l2_subdev_get_try_format(fh, OIF_ISP_PAD); + + fse->max_width = fse->min_width = mf->width; + fse->max_height = fse->min_height = mf->height; + return 0; + } + default: + return -EINVAL; + } + } + + if (fse->code != s5c73m3_oif_get_pad_code(fse->pad, 0)) + return -EINVAL; + + if (fse->pad == OIF_JPEG_PAD) + idx = RES_JPEG; + else + idx = RES_ISP; + + if (fse->index >= s5c73m3_resolutions_len[idx]) + return -EINVAL; + + fse->min_width = s5c73m3_resolutions[idx][fse->index].width; + fse->max_width = fse->min_width; + fse->max_height = s5c73m3_resolutions[idx][fse->index].height; + fse->min_height = fse->max_height; + + return 0; +} + +static int s5c73m3_oif_log_status(struct v4l2_subdev *sd) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + + v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name); + + v4l2_info(sd, "power: %d, apply_fmt: %d\n", state->power, + state->apply_fmt); + + return 0; +} + +static int s5c73m3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *mf; + + mf = v4l2_subdev_get_try_format(fh, S5C73M3_ISP_PAD); + s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], + S5C73M3_ISP_FMT); + + mf = v4l2_subdev_get_try_format(fh, S5C73M3_JPEG_PAD); + s5c73m3_fill_mbus_fmt(mf, &s5c73m3_jpeg_resolutions[1], + S5C73M3_JPEG_FMT); + + return 0; +} + +static int s5c73m3_oif_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *mf; + + mf = v4l2_subdev_get_try_format(fh, OIF_ISP_PAD); + s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], + S5C73M3_ISP_FMT); + + mf = v4l2_subdev_get_try_format(fh, OIF_JPEG_PAD); + s5c73m3_fill_mbus_fmt(mf, &s5c73m3_jpeg_resolutions[1], + S5C73M3_JPEG_FMT); + + mf = v4l2_subdev_get_try_format(fh, OIF_SOURCE_PAD); + s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], + S5C73M3_ISP_FMT); + return 0; +} + +static int s5c73m3_gpio_set_value(struct s5c73m3 *priv, int id, u32 val) +{ + if (!gpio_is_valid(priv->gpio[id].gpio)) + return 0; + gpio_set_value(priv->gpio[id].gpio, !!val); + return 1; +} + +static int s5c73m3_gpio_assert(struct s5c73m3 *priv, int id) +{ + return s5c73m3_gpio_set_value(priv, id, priv->gpio[id].level); +} + +static int s5c73m3_gpio_deassert(struct s5c73m3 *priv, int id) +{ + return s5c73m3_gpio_set_value(priv, id, !priv->gpio[id].level); +} + +static int __s5c73m3_power_on(struct s5c73m3 *state) +{ + int i, ret; + + for (i = 0; i < S5C73M3_MAX_SUPPLIES; i++) { + ret = regulator_enable(state->supplies[i].consumer); + if (ret) + goto err; + } + + s5c73m3_gpio_deassert(state, STBY); + usleep_range(100, 200); + + s5c73m3_gpio_deassert(state, RST); + usleep_range(50, 100); + + return 0; +err: + for (--i; i >= 0; i--) + regulator_disable(state->supplies[i].consumer); + return ret; +} + +static int __s5c73m3_power_off(struct s5c73m3 *state) +{ + int i, ret; + + if (s5c73m3_gpio_assert(state, RST)) + usleep_range(10, 50); + + if (s5c73m3_gpio_assert(state, STBY)) + usleep_range(100, 200); + state->streaming = 0; + state->isp_ready = 0; + + for (i = S5C73M3_MAX_SUPPLIES - 1; i >= 0; i--) { + ret = regulator_disable(state->supplies[i].consumer); + if (ret) + goto err; + } + return 0; +err: + for (++i; i < S5C73M3_MAX_SUPPLIES; i++) + regulator_enable(state->supplies[i].consumer); + + return ret; +} + +static int s5c73m3_oif_set_power(struct v4l2_subdev *sd, int on) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + int ret = 0; + + mutex_lock(&state->lock); + + if (on && !state->power) { + ret = __s5c73m3_power_on(state); + if (!ret) + ret = s5c73m3_isp_init(state); + if (!ret) { + state->apply_fiv = 1; + state->apply_fmt = 1; + } + } else if (!on == state->power) { + ret = s5c73m3_set_af_softlanding(state); + if (!ret) + ret = __s5c73m3_power_off(state); + else + v4l2_err(sd, "Soft landing lens failed\n"); + } + if (!ret) + state->power += on ? 1 : -1; + + v4l2_dbg(1, s5c73m3_dbg, sd, "%s: power: %d\n", + __func__, state->power); + + mutex_unlock(&state->lock); + return ret; +} + +static int s5c73m3_oif_registered(struct v4l2_subdev *sd) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + int ret; + + ret = v4l2_device_register_subdev(sd->v4l2_dev, &state->sensor_sd); + if (ret) { + v4l2_err(sd->v4l2_dev, "Failed to register %s\n", + state->oif_sd.name); + return ret; + } + + ret = media_entity_create_link(&state->sensor_sd.entity, + S5C73M3_ISP_PAD, &state->oif_sd.entity, OIF_ISP_PAD, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + + ret = media_entity_create_link(&state->sensor_sd.entity, + S5C73M3_JPEG_PAD, &state->oif_sd.entity, OIF_JPEG_PAD, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + + mutex_lock(&state->lock); + ret = __s5c73m3_power_on(state); + if (ret == 0) + s5c73m3_get_fw_version(state); + + __s5c73m3_power_off(state); + mutex_unlock(&state->lock); + + v4l2_dbg(1, s5c73m3_dbg, sd, "%s: Booting %s (%d)\n", + __func__, ret ? "failed" : "succeded", ret); + + return ret; +} + +static const struct v4l2_subdev_internal_ops s5c73m3_internal_ops = { + .open = s5c73m3_open, +}; + +static const struct v4l2_subdev_pad_ops s5c73m3_pad_ops = { + .enum_mbus_code = s5c73m3_enum_mbus_code, + .enum_frame_size = s5c73m3_enum_frame_size, + .get_fmt = s5c73m3_get_fmt, + .set_fmt = s5c73m3_set_fmt, +}; + +static const struct v4l2_subdev_ops s5c73m3_subdev_ops = { + .pad = &s5c73m3_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops oif_internal_ops = { + .registered = s5c73m3_oif_registered, + .open = s5c73m3_oif_open, +}; + +static const struct v4l2_subdev_pad_ops s5c73m3_oif_pad_ops = { + .enum_mbus_code = s5c73m3_oif_enum_mbus_code, + .enum_frame_size = s5c73m3_oif_enum_frame_size, + .enum_frame_interval = s5c73m3_oif_enum_frame_interval, + .get_fmt = s5c73m3_oif_get_fmt, + .set_fmt = s5c73m3_oif_set_fmt, + .get_frame_desc = s5c73m3_oif_get_frame_desc, + .set_frame_desc = s5c73m3_oif_set_frame_desc, +}; + +static const struct v4l2_subdev_core_ops s5c73m3_oif_core_ops = { + .s_power = s5c73m3_oif_set_power, + .log_status = s5c73m3_oif_log_status, +}; + +static const struct v4l2_subdev_video_ops s5c73m3_oif_video_ops = { + .s_stream = s5c73m3_oif_s_stream, + .g_frame_interval = s5c73m3_oif_g_frame_interval, + .s_frame_interval = s5c73m3_oif_s_frame_interval, +}; + +static const struct v4l2_subdev_ops oif_subdev_ops = { + .core = &s5c73m3_oif_core_ops, + .pad = &s5c73m3_oif_pad_ops, + .video = &s5c73m3_oif_video_ops, +}; + +static int s5c73m3_configure_gpio(int nr, int val, const char *name) +{ + unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + int ret; + + if (!gpio_is_valid(nr)) + return 0; + ret = gpio_request_one(nr, flags, name); + if (!ret) + gpio_export(nr, 0); + return ret; +} + +static int s5c73m3_free_gpios(struct s5c73m3 *state) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(state->gpio); i++) { + if (!gpio_is_valid(state->gpio[i].gpio)) + continue; + gpio_free(state->gpio[i].gpio); + state->gpio[i].gpio = -EINVAL; + } + return 0; +} + +static int s5c73m3_configure_gpios(struct s5c73m3 *state, + const struct s5c73m3_platform_data *pdata) +{ + const struct s5c73m3_gpio *gpio = &pdata->gpio_stby; + int ret; + + state->gpio[STBY].gpio = -EINVAL; + state->gpio[RST].gpio = -EINVAL; + + ret = s5c73m3_configure_gpio(gpio->gpio, gpio->level, "S5C73M3_STBY"); + if (ret) { + s5c73m3_free_gpios(state); + return ret; + } + state->gpio[STBY] = *gpio; + if (gpio_is_valid(gpio->gpio)) + gpio_set_value(gpio->gpio, 0); + + gpio = &pdata->gpio_reset; + ret = s5c73m3_configure_gpio(gpio->gpio, gpio->level, "S5C73M3_RST"); + if (ret) { + s5c73m3_free_gpios(state); + return ret; + } + state->gpio[RST] = *gpio; + if (gpio_is_valid(gpio->gpio)) + gpio_set_value(gpio->gpio, 0); + + return 0; +} + +static int __devinit s5c73m3_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + const struct s5c73m3_platform_data *pdata = client->dev.platform_data; + struct v4l2_subdev *sd; + struct v4l2_subdev *oif_sd; + struct s5c73m3 *state; + int ret, i; + + if (pdata == NULL) { + dev_err(&client->dev, "Platform data not specified\n"); + return -EINVAL; + } + + state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + mutex_init(&state->lock); + sd = &state->sensor_sd; + oif_sd = &state->oif_sd; + + v4l2_subdev_init(sd, &s5c73m3_subdev_ops); + sd->owner = client->driver->driver.owner; + v4l2_set_subdevdata(sd, state); + strlcpy(sd->name, "S5C73M3", sizeof(sd->name)); + + sd->internal_ops = &s5c73m3_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + state->sensor_pads[S5C73M3_JPEG_PAD].flags = MEDIA_PAD_FL_SOURCE; + state->sensor_pads[S5C73M3_ISP_PAD].flags = MEDIA_PAD_FL_SOURCE; + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + + ret = media_entity_init(&sd->entity, S5C73M3_NUM_PADS, + state->sensor_pads, 0); + if (ret < 0) + return ret; + + v4l2_i2c_subdev_init(oif_sd, client, &oif_subdev_ops); + strcpy(oif_sd->name, "S5C73M3-OIF"); + + oif_sd->internal_ops = &oif_internal_ops; + oif_sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + state->oif_pads[OIF_ISP_PAD].flags = MEDIA_PAD_FL_SINK; + state->oif_pads[OIF_JPEG_PAD].flags = MEDIA_PAD_FL_SINK; + state->oif_pads[OIF_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE; + oif_sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + + ret = media_entity_init(&oif_sd->entity, OIF_NUM_PADS, + state->oif_pads, 0); + if (ret < 0) + return ret; + + state->mclk_frequency = pdata->mclk_frequency; + state->bus_type = pdata->bus_type; + + ret = s5c73m3_configure_gpios(state, pdata); + if (ret) + goto out_err1; + + for (i = 0; i < S5C73M3_MAX_SUPPLIES; i++) + state->supplies[i].supply = s5c73m3_supply_names[i]; + + ret = regulator_bulk_get(dev, S5C73M3_MAX_SUPPLIES, + state->supplies); + if (ret) { + dev_err(dev, "failed to get regulators\n"); + goto out_err2; + } + + ret = s5c73m3_init_controls(state); + if (ret) + goto out_err3; + + state->sensor_pix_size[RES_ISP] = &s5c73m3_isp_resolutions[1]; + state->sensor_pix_size[RES_JPEG] = &s5c73m3_jpeg_resolutions[1]; + state->oif_pix_size[RES_ISP] = state->sensor_pix_size[RES_ISP]; + state->oif_pix_size[RES_JPEG] = state->sensor_pix_size[RES_JPEG]; + + state->mbus_code = S5C73M3_ISP_FMT; + + state->fiv = &s5c73m3_intervals[S5C73M3_DEFAULT_FRAME_INTERVAL]; + + state->fw_file_version[0] = 'G'; + state->fw_file_version[1] = 'C'; + + ret = s5c73m3_register_spi_driver(state); + if (ret < 0) + goto out_err3; + + state->i2c_client = client; + + v4l2_info(sd, "%s: completed succesfully\n", __func__); + return 0; + +out_err3: + regulator_bulk_free(S5C73M3_MAX_SUPPLIES, state->supplies); +out_err2: + s5c73m3_free_gpios(state); +out_err1: + media_entity_cleanup(&sd->entity); + return ret; +} + +static int __devexit s5c73m3_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); + + v4l2_device_unregister_subdev(sd); + + v4l2_ctrl_handler_free(sd->ctrl_handler); + media_entity_cleanup(&sd->entity); + + s5c73m3_unregister_spi_driver(state); + regulator_bulk_free(S5C73M3_MAX_SUPPLIES, state->supplies); + s5c73m3_free_gpios(state); + + return 0; +} + +static const struct i2c_device_id s5c73m3_id[] = { + { DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, s5c73m3_id); + +static struct i2c_driver s5c73m3_i2c_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = s5c73m3_probe, + .remove = __devexit_p(s5c73m3_remove), + .id_table = s5c73m3_id, +}; + +module_i2c_driver(s5c73m3_i2c_driver); + +MODULE_DESCRIPTION("Samsung S5C73M3 camera driver"); +MODULE_AUTHOR("Sylwester Nawrocki "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c new file mode 100644 index 000000000000..8001cde1db1e --- /dev/null +++ b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c @@ -0,0 +1,563 @@ +/* + * Samsung LSI S5C73M3 8M pixel camera driver + * + * Copyright (C) 2012, Samsung Electronics, Co., Ltd. + * Sylwester Nawrocki + * Andrzej Hajda + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "s5c73m3.h" + +static int s5c73m3_get_af_status(struct s5c73m3 *state, struct v4l2_ctrl *ctrl) +{ + u16 reg = REG_AF_STATUS_UNFOCUSED; + + int ret = s5c73m3_read(state, REG_AF_STATUS, ®); + + switch (reg) { + case REG_CAF_STATUS_FIND_SEARCH_DIR: + case REG_AF_STATUS_FOCUSING: + case REG_CAF_STATUS_FOCUSING: + ctrl->val = V4L2_AUTO_FOCUS_STATUS_BUSY; + break; + case REG_CAF_STATUS_FOCUSED: + case REG_AF_STATUS_FOCUSED: + ctrl->val = V4L2_AUTO_FOCUS_STATUS_REACHED; + break; + default: + v4l2_info(&state->sensor_sd, "Unknown AF status %#x\n", reg); + /* Fall through */ + case REG_CAF_STATUS_UNFOCUSED: + case REG_AF_STATUS_UNFOCUSED: + case REG_AF_STATUS_INVALID: + ctrl->val = V4L2_AUTO_FOCUS_STATUS_FAILED; + break; + } + + return ret; +} + +static int s5c73m3_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sensor_sd(ctrl); + struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); + int ret; + + if (state->power == 0) + return -EBUSY; + + switch (ctrl->id) { + case V4L2_CID_FOCUS_AUTO: + ret = s5c73m3_get_af_status(state, state->ctrls.af_status); + if (ret) + return ret; + break; + } + + return 0; +} + +static int s5c73m3_set_colorfx(struct s5c73m3 *state, int val) +{ + static const unsigned short colorfx[][2] = { + { V4L2_COLORFX_NONE, COMM_IMAGE_EFFECT_NONE }, + { V4L2_COLORFX_BW, COMM_IMAGE_EFFECT_MONO }, + { V4L2_COLORFX_SEPIA, COMM_IMAGE_EFFECT_SEPIA }, + { V4L2_COLORFX_NEGATIVE, COMM_IMAGE_EFFECT_NEGATIVE }, + { V4L2_COLORFX_AQUA, COMM_IMAGE_EFFECT_AQUA }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(colorfx); i++) { + if (colorfx[i][0] != val) + continue; + + v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, + "Setting %s color effect\n", + v4l2_ctrl_get_menu(state->ctrls.colorfx->id)[i]); + + return s5c73m3_isp_command(state, COMM_IMAGE_EFFECT, + colorfx[i][1]); + } + return -EINVAL; +} + +/* Set exposure metering/exposure bias */ +static int s5c73m3_set_exposure(struct s5c73m3 *state, int auto_exp) +{ + struct v4l2_subdev *sd = &state->sensor_sd; + struct s5c73m3_ctrls *ctrls = &state->ctrls; + int ret = 0; + + if (ctrls->exposure_metering->is_new) { + u16 metering; + + switch (ctrls->exposure_metering->val) { + case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: + metering = COMM_METERING_CENTER; + break; + case V4L2_EXPOSURE_METERING_SPOT: + metering = COMM_METERING_SPOT; + break; + default: + metering = COMM_METERING_AVERAGE; + break; + } + + ret = s5c73m3_isp_command(state, COMM_METERING, metering); + } + + if (!ret && ctrls->exposure_bias->is_new) { + u16 exp_bias = ctrls->exposure_bias->val; + ret = s5c73m3_isp_command(state, COMM_EV, exp_bias); + } + + v4l2_dbg(1, s5c73m3_dbg, sd, + "%s: exposure bias: %#x, metering: %#x (%d)\n", __func__, + ctrls->exposure_bias->val, ctrls->exposure_metering->val, ret); + + return ret; +} + +static int s5c73m3_set_white_balance(struct s5c73m3 *state, int val) +{ + static const unsigned short wb[][2] = { + { V4L2_WHITE_BALANCE_INCANDESCENT, COMM_AWB_MODE_INCANDESCENT}, + { V4L2_WHITE_BALANCE_FLUORESCENT, COMM_AWB_MODE_FLUORESCENT1}, + { V4L2_WHITE_BALANCE_FLUORESCENT_H, COMM_AWB_MODE_FLUORESCENT2}, + { V4L2_WHITE_BALANCE_CLOUDY, COMM_AWB_MODE_CLOUDY}, + { V4L2_WHITE_BALANCE_DAYLIGHT, COMM_AWB_MODE_DAYLIGHT}, + { V4L2_WHITE_BALANCE_AUTO, COMM_AWB_MODE_AUTO}, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(wb); i++) { + if (wb[i][0] != val) + continue; + + v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, + "Setting white balance to: %s\n", + v4l2_ctrl_get_menu(state->ctrls.auto_wb->id)[i]); + + return s5c73m3_isp_command(state, COMM_AWB_MODE, wb[i][1]); + } + + return -EINVAL; +} + +static int s5c73m3_af_run(struct s5c73m3 *state, bool on) +{ + struct s5c73m3_ctrls *c = &state->ctrls; + + if (!on) + return s5c73m3_isp_command(state, COMM_AF_CON, + COMM_AF_CON_STOP); + + if (c->focus_auto->val) + return s5c73m3_isp_command(state, COMM_AF_MODE, + COMM_AF_MODE_PREVIEW_CAF_START); + + return s5c73m3_isp_command(state, COMM_AF_CON, COMM_AF_CON_START); +} + +static int s5c73m3_3a_lock(struct s5c73m3 *state, struct v4l2_ctrl *ctrl) +{ + bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; + bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; + bool af_lock = ctrl->val & V4L2_LOCK_FOCUS; + int ret = 0; + + if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_EXPOSURE) { + ret = s5c73m3_isp_command(state, COMM_AE_CON, + ae_lock ? COMM_AE_STOP : COMM_AE_START); + if (ret) + return ret; + } + + if (((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_WHITE_BALANCE) + && state->ctrls.auto_wb->val) { + ret = s5c73m3_isp_command(state, COMM_AWB_CON, + awb_lock ? COMM_AWB_STOP : COMM_AWB_START); + if (ret) + return ret; + } + + if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS) + ret = s5c73m3_af_run(state, ~af_lock); + + return ret; +} + +static int s5c73m3_set_auto_focus(struct s5c73m3 *state, int caf) +{ + struct s5c73m3_ctrls *c = &state->ctrls; + int ret = 1; + + if (c->af_distance->is_new) { + u16 mode = (c->af_distance->val == V4L2_AUTO_FOCUS_RANGE_MACRO) + ? COMM_AF_MODE_MACRO : COMM_AF_MODE_NORMAL; + ret = s5c73m3_isp_command(state, COMM_AF_MODE, mode); + if (ret != 0) + return ret; + } + + if (!ret || (c->focus_auto->is_new && c->focus_auto->val) || + c->af_start->is_new) + ret = s5c73m3_af_run(state, 1); + else if ((c->focus_auto->is_new && !c->focus_auto->val) || + c->af_stop->is_new) + ret = s5c73m3_af_run(state, 0); + else + ret = 0; + + return ret; +} + +static int s5c73m3_set_contrast(struct s5c73m3 *state, int val) +{ + u16 reg = (val < 0) ? -val + 2 : val; + return s5c73m3_isp_command(state, COMM_CONTRAST, reg); +} + +static int s5c73m3_set_saturation(struct s5c73m3 *state, int val) +{ + u16 reg = (val < 0) ? -val + 2 : val; + return s5c73m3_isp_command(state, COMM_SATURATION, reg); +} + +static int s5c73m3_set_sharpness(struct s5c73m3 *state, int val) +{ + u16 reg = (val < 0) ? -val + 2 : val; + return s5c73m3_isp_command(state, COMM_SHARPNESS, reg); +} + +static int s5c73m3_set_iso(struct s5c73m3 *state, int val) +{ + u32 iso; + + if (val == V4L2_ISO_SENSITIVITY_MANUAL) + iso = state->ctrls.iso->val + 1; + else + iso = 0; + + return s5c73m3_isp_command(state, COMM_ISO, iso); +} + +static int s5c73m3_set_stabilization(struct s5c73m3 *state, int val) +{ + struct v4l2_subdev *sd = &state->sensor_sd; + + v4l2_dbg(1, s5c73m3_dbg, sd, "Image stabilization: %d\n", val); + + return s5c73m3_isp_command(state, COMM_FRAME_RATE, val ? + COMM_FRAME_RATE_ANTI_SHAKE : COMM_FRAME_RATE_AUTO_SET); +} + +static int s5c73m3_set_jpeg_quality(struct s5c73m3 *state, int quality) +{ + int reg; + + if (quality <= 65) + reg = COMM_IMAGE_QUALITY_NORMAL; + else if (quality <= 75) + reg = COMM_IMAGE_QUALITY_FINE; + else + reg = COMM_IMAGE_QUALITY_SUPERFINE; + + return s5c73m3_isp_command(state, COMM_IMAGE_QUALITY, reg); +} + +static int s5c73m3_set_scene_program(struct s5c73m3 *state, int val) +{ + static const unsigned short scene_lookup[] = { + COMM_SCENE_MODE_NONE, /* V4L2_SCENE_MODE_NONE */ + COMM_SCENE_MODE_AGAINST_LIGHT,/* V4L2_SCENE_MODE_BACKLIGHT */ + COMM_SCENE_MODE_BEACH, /* V4L2_SCENE_MODE_BEACH_SNOW */ + COMM_SCENE_MODE_CANDLE, /* V4L2_SCENE_MODE_CANDLE_LIGHT */ + COMM_SCENE_MODE_DAWN, /* V4L2_SCENE_MODE_DAWN_DUSK */ + COMM_SCENE_MODE_FALL, /* V4L2_SCENE_MODE_FALL_COLORS */ + COMM_SCENE_MODE_FIRE, /* V4L2_SCENE_MODE_FIREWORKS */ + COMM_SCENE_MODE_LANDSCAPE, /* V4L2_SCENE_MODE_LANDSCAPE */ + COMM_SCENE_MODE_NIGHT, /* V4L2_SCENE_MODE_NIGHT */ + COMM_SCENE_MODE_INDOOR, /* V4L2_SCENE_MODE_PARTY_INDOOR */ + COMM_SCENE_MODE_PORTRAIT, /* V4L2_SCENE_MODE_PORTRAIT */ + COMM_SCENE_MODE_SPORTS, /* V4L2_SCENE_MODE_SPORTS */ + COMM_SCENE_MODE_SUNSET, /* V4L2_SCENE_MODE_SUNSET */ + COMM_SCENE_MODE_TEXT, /* V4L2_SCENE_MODE_TEXT */ + }; + + v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, "Setting %s scene mode\n", + v4l2_ctrl_get_menu(state->ctrls.scene_mode->id)[val]); + + return s5c73m3_isp_command(state, COMM_SCENE_MODE, scene_lookup[val]); +} + +static int s5c73m3_set_power_line_freq(struct s5c73m3 *state, int val) +{ + unsigned int pwr_line_freq = COMM_FLICKER_NONE; + + switch (val) { + case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: + pwr_line_freq = COMM_FLICKER_NONE; + break; + case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: + pwr_line_freq = COMM_FLICKER_AUTO_50HZ; + break; + case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: + pwr_line_freq = COMM_FLICKER_AUTO_60HZ; + break; + default: + case V4L2_CID_POWER_LINE_FREQUENCY_AUTO: + pwr_line_freq = COMM_FLICKER_NONE; + } + + return s5c73m3_isp_command(state, COMM_FLICKER_MODE, pwr_line_freq); +} + +static int s5c73m3_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sensor_sd(ctrl); + struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); + int ret = 0; + + v4l2_dbg(1, s5c73m3_dbg, sd, "set_ctrl: %s, value: %d\n", + ctrl->name, ctrl->val); + + mutex_lock(&state->lock); + /* + * If the device is not powered up by the host driver do + * not apply any controls to H/W at this time. Instead + * the controls will be restored right after power-up. + */ + if (state->power == 0) + goto unlock; + + if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) { + ret = -EINVAL; + goto unlock; + } + + switch (ctrl->id) { + case V4L2_CID_3A_LOCK: + ret = s5c73m3_3a_lock(state, ctrl); + break; + + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + ret = s5c73m3_set_white_balance(state, ctrl->val); + break; + + case V4L2_CID_CONTRAST: + ret = s5c73m3_set_contrast(state, ctrl->val); + break; + + case V4L2_CID_COLORFX: + ret = s5c73m3_set_colorfx(state, ctrl->val); + break; + + case V4L2_CID_EXPOSURE_AUTO: + ret = s5c73m3_set_exposure(state, ctrl->val); + break; + + case V4L2_CID_FOCUS_AUTO: + ret = s5c73m3_set_auto_focus(state, ctrl->val); + break; + + case V4L2_CID_IMAGE_STABILIZATION: + ret = s5c73m3_set_stabilization(state, ctrl->val); + break; + + case V4L2_CID_ISO_SENSITIVITY: + ret = s5c73m3_set_iso(state, ctrl->val); + break; + + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + ret = s5c73m3_set_jpeg_quality(state, ctrl->val); + break; + + case V4L2_CID_POWER_LINE_FREQUENCY: + ret = s5c73m3_set_power_line_freq(state, ctrl->val); + break; + + case V4L2_CID_SATURATION: + ret = s5c73m3_set_saturation(state, ctrl->val); + break; + + case V4L2_CID_SCENE_MODE: + ret = s5c73m3_set_scene_program(state, ctrl->val); + break; + + case V4L2_CID_SHARPNESS: + ret = s5c73m3_set_sharpness(state, ctrl->val); + break; + + case V4L2_CID_WIDE_DYNAMIC_RANGE: + ret = s5c73m3_isp_command(state, COMM_WDR, !!ctrl->val); + break; + + case V4L2_CID_ZOOM_ABSOLUTE: + ret = s5c73m3_isp_command(state, COMM_ZOOM_STEP, ctrl->val); + break; + } +unlock: + mutex_unlock(&state->lock); + return ret; +} + +static const struct v4l2_ctrl_ops s5c73m3_ctrl_ops = { + .g_volatile_ctrl = s5c73m3_g_volatile_ctrl, + .s_ctrl = s5c73m3_s_ctrl, +}; + +/* Supported manual ISO values */ +static const s64 iso_qmenu[] = { + /* COMM_ISO: 0x0001...0x0004 */ + 100, 200, 400, 800, +}; + +/* Supported exposure bias values (-2.0EV...+2.0EV) */ +static const s64 ev_bias_qmenu[] = { + /* COMM_EV: 0x0000...0x0008 */ + -2000, -1500, -1000, -500, 0, 500, 1000, 1500, 2000 +}; + +int s5c73m3_init_controls(struct s5c73m3 *state) +{ + const struct v4l2_ctrl_ops *ops = &s5c73m3_ctrl_ops; + struct s5c73m3_ctrls *ctrls = &state->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + + int ret = v4l2_ctrl_handler_init(hdl, 22); + if (ret) + return ret; + + /* White balance */ + ctrls->auto_wb = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, + 9, ~0x15e, V4L2_WHITE_BALANCE_AUTO); + + /* Exposure (only automatic exposure) */ + ctrls->auto_exposure = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_AUTO, 0, ~0x01, V4L2_EXPOSURE_AUTO); + + ctrls->exposure_bias = v4l2_ctrl_new_int_menu(hdl, ops, + V4L2_CID_AUTO_EXPOSURE_BIAS, + ARRAY_SIZE(ev_bias_qmenu) - 1, + ARRAY_SIZE(ev_bias_qmenu)/2 - 1, + ev_bias_qmenu); + + ctrls->exposure_metering = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_METERING, + 2, ~0x7, V4L2_EXPOSURE_METERING_AVERAGE); + + /* Auto focus */ + ctrls->focus_auto = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_FOCUS_AUTO, 0, 1, 1, 0); + + ctrls->af_start = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_AUTO_FOCUS_START, 0, 1, 1, 0); + + ctrls->af_stop = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_AUTO_FOCUS_STOP, 0, 1, 1, 0); + + ctrls->af_status = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_AUTO_FOCUS_STATUS, 0, + (V4L2_AUTO_FOCUS_STATUS_BUSY | + V4L2_AUTO_FOCUS_STATUS_REACHED | + V4L2_AUTO_FOCUS_STATUS_FAILED), + 0, V4L2_AUTO_FOCUS_STATUS_IDLE); + + ctrls->af_distance = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_AUTO_FOCUS_RANGE, + V4L2_AUTO_FOCUS_RANGE_MACRO, + ~(1 << V4L2_AUTO_FOCUS_RANGE_NORMAL | + 1 << V4L2_AUTO_FOCUS_RANGE_MACRO), + V4L2_AUTO_FOCUS_RANGE_NORMAL); + /* ISO sensitivity */ + ctrls->auto_iso = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_ISO_SENSITIVITY_AUTO, 1, 0, + V4L2_ISO_SENSITIVITY_AUTO); + + ctrls->iso = v4l2_ctrl_new_int_menu(hdl, ops, + V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1, + ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu); + + ctrls->contrast = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_CONTRAST, -2, 2, 1, 0); + + ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_SATURATION, -2, 2, 1, 0); + + ctrls->sharpness = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_SHARPNESS, -2, 2, 1, 0); + + ctrls->zoom = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_ZOOM_ABSOLUTE, 0, 30, 1, 0); + + ctrls->colorfx = v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX, + V4L2_COLORFX_AQUA, ~0x40f, V4L2_COLORFX_NONE); + + ctrls->wdr = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_WIDE_DYNAMIC_RANGE, 0, 1, 1, 0); + + ctrls->stabilization = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_IMAGE_STABILIZATION, 0, 1, 1, 0); + + v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO); + + ctrls->jpeg_quality = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 80); + + ctrls->scene_mode = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_SCENE_MODE, V4L2_SCENE_MODE_TEXT, ~0x3fff, + V4L2_SCENE_MODE_NONE); + + ctrls->aaa_lock = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_3A_LOCK, 0, 0x7, 0, 0); + + if (hdl->error) { + ret = hdl->error; + v4l2_ctrl_handler_free(hdl); + return ret; + } + + v4l2_ctrl_auto_cluster(3, &ctrls->auto_exposure, 0, false); + ctrls->auto_iso->flags |= V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_UPDATE; + v4l2_ctrl_auto_cluster(2, &ctrls->auto_iso, 0, false); + ctrls->af_status->flags |= V4L2_CTRL_FLAG_VOLATILE; + v4l2_ctrl_cluster(6, &ctrls->focus_auto); + + state->sensor_sd.ctrl_handler = hdl; + + return 0; +} diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c new file mode 100644 index 000000000000..889139c2ac5e --- /dev/null +++ b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c @@ -0,0 +1,156 @@ +/* + * Samsung LSI S5C73M3 8M pixel camera driver + * + * Copyright (C) 2012, Samsung Electronics, Co., Ltd. + * Sylwester Nawrocki + * Andrzej Hajda + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "s5c73m3.h" + +#define S5C73M3_SPI_DRV_NAME "S5C73M3-SPI" + +enum spi_direction { + SPI_DIR_RX, + SPI_DIR_TX +}; + +static int spi_xmit(struct spi_device *spi_dev, void *addr, const int len, + enum spi_direction dir) +{ + struct spi_message msg; + int r; + struct spi_transfer xfer = { + .len = len, + }; + + if (dir == SPI_DIR_TX) + xfer.tx_buf = addr; + else + xfer.rx_buf = addr; + + if (spi_dev == NULL) { + dev_err(&spi_dev->dev, "SPI device is uninitialized\n"); + return -ENODEV; + } + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + r = spi_sync(spi_dev, &msg); + if (r < 0) + dev_err(&spi_dev->dev, "%s spi_sync failed %d\n", __func__, r); + + return r; +} + +int s5c73m3_spi_write(struct s5c73m3 *state, const void *addr, + const unsigned int len, const unsigned int tx_size) +{ + struct spi_device *spi_dev = state->spi_dev; + u32 count = len / tx_size; + u32 extra = len % tx_size; + unsigned int i, j = 0; + u8 padding[32]; + int r = 0; + + memset(padding, 0, sizeof(padding)); + + for (i = 0; i < count ; i++) { + r = spi_xmit(spi_dev, (void *)addr + j, tx_size, SPI_DIR_TX); + if (r < 0) + return r; + j += tx_size; + } + + if (extra > 0) { + r = spi_xmit(spi_dev, (void *)addr + j, extra, SPI_DIR_TX); + if (r < 0) + return r; + } + + return spi_xmit(spi_dev, padding, sizeof(padding), SPI_DIR_TX); +} + +int s5c73m3_spi_read(struct s5c73m3 *state, void *addr, + const unsigned int len, const unsigned int tx_size) +{ + struct spi_device *spi_dev = state->spi_dev; + u32 count = len / tx_size; + u32 extra = len % tx_size; + unsigned int i, j = 0; + int r = 0; + + for (i = 0; i < count ; i++) { + r = spi_xmit(spi_dev, addr + j, tx_size, SPI_DIR_RX); + if (r < 0) + return r; + j += tx_size; + } + + if (extra > 0) + return spi_xmit(spi_dev, addr + j, extra, SPI_DIR_RX); + + return 0; +} + +static int __devinit s5c73m3_spi_probe(struct spi_device *spi) +{ + int r; + struct s5c73m3 *state = container_of(spi->dev.driver, struct s5c73m3, + spidrv.driver); + spi->bits_per_word = 32; + + r = spi_setup(spi); + if (r < 0) { + dev_err(&spi->dev, "spi_setup() failed\n"); + return r; + } + + mutex_lock(&state->lock); + state->spi_dev = spi; + mutex_unlock(&state->lock); + + v4l2_info(&state->sensor_sd, "S5C73M3 SPI probed successfully\n"); + return 0; +} + +static int __devexit s5c73m3_spi_remove(struct spi_device *spi) +{ + return 0; +} + +int s5c73m3_register_spi_driver(struct s5c73m3 *state) +{ + struct spi_driver *spidrv = &state->spidrv; + + spidrv->remove = __devexit_p(s5c73m3_spi_remove); + spidrv->probe = s5c73m3_spi_probe; + spidrv->driver.name = S5C73M3_SPI_DRV_NAME; + spidrv->driver.bus = &spi_bus_type; + spidrv->driver.owner = THIS_MODULE; + + return spi_register_driver(spidrv); +} + +void s5c73m3_unregister_spi_driver(struct s5c73m3 *state) +{ + spi_unregister_driver(&state->spidrv); +} diff --git a/drivers/media/i2c/s5c73m3/s5c73m3.h b/drivers/media/i2c/s5c73m3/s5c73m3.h new file mode 100644 index 000000000000..9d2c08652246 --- /dev/null +++ b/drivers/media/i2c/s5c73m3/s5c73m3.h @@ -0,0 +1,459 @@ +/* + * Samsung LSI S5C73M3 8M pixel camera driver + * + * Copyright (C) 2012, Samsung Electronics, Co., Ltd. + * Sylwester Nawrocki + * Andrzej Hajda + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef S5C73M3_H_ +#define S5C73M3_H_ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "S5C73M3" + +#define S5C73M3_ISP_FMT V4L2_MBUS_FMT_VYUY8_2X8 +#define S5C73M3_JPEG_FMT V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8 + +/* Subdevs pad index definitions */ +enum s5c73m3_pads { + S5C73M3_ISP_PAD, + S5C73M3_JPEG_PAD, + S5C73M3_NUM_PADS +}; + +enum s5c73m3_oif_pads { + OIF_ISP_PAD, + OIF_JPEG_PAD, + OIF_SOURCE_PAD, + OIF_NUM_PADS +}; + +#define S5C73M3_SENSOR_FW_LEN 6 +#define S5C73M3_SENSOR_TYPE_LEN 12 + +#define S5C73M3_REG(_addrh, _addrl) (((_addrh) << 16) | _addrl) + +#define AHB_MSB_ADDR_PTR 0xfcfc +#define REG_CMDWR_ADDRH 0x0050 +#define REG_CMDWR_ADDRL 0x0054 +#define REG_CMDRD_ADDRH 0x0058 +#define REG_CMDRD_ADDRL 0x005c +#define REG_CMDBUF_ADDR 0x0f14 + +#define REG_I2C_SEQ_STATUS S5C73M3_REG(0x0009, 0x59A6) +#define SEQ_END_PLL (1<<0x0) +#define SEQ_END_SENSOR (1<<0x1) +#define SEQ_END_GPIO (1<<0x2) +#define SEQ_END_FROM (1<<0x3) +#define SEQ_END_STABLE_AE_AWB (1<<0x4) +#define SEQ_END_READY_I2C_CMD (1<<0x5) + +#define REG_I2C_STATUS S5C73M3_REG(0x0009, 0x599E) +#define I2C_STATUS_CIS_I2C (1<<0x0) +#define I2C_STATUS_AF_INIT (1<<0x1) +#define I2C_STATUS_CAL_DATA (1<<0x2) +#define I2C_STATUS_FRAME_COUNT (1<<0x3) +#define I2C_STATUS_FROM_INIT (1<<0x4) +#define I2C_STATUS_I2C_CIS_STREAM_OFF (1<<0x5) +#define I2C_STATUS_I2C_N_CMD_OVER (1<<0x6) +#define I2C_STATUS_I2C_N_CMD_MISMATCH (1<<0x7) +#define I2C_STATUS_CHECK_BIN_CRC (1<<0x8) +#define I2C_STATUS_EXCEPTION (1<<0x9) +#define I2C_STATUS_INIF_INIT_STATE (0x8) + +#define REG_STATUS S5C73M3_REG(0x0009, 0x5080) +#define REG_STATUS_BOOT_SUB_MAIN_ENTER 0xff01 +#define REG_STATUS_BOOT_SRAM_TIMING_OK 0xff02 +#define REG_STATUS_BOOT_INTERRUPTS_EN 0xff03 +#define REG_STATUS_BOOT_R_PLL_DONE 0xff04 +#define REG_STATUS_BOOT_R_PLL_LOCKTIME_DONE 0xff05 +#define REG_STATUS_BOOT_DELAY_COUNT_DONE 0xff06 +#define REG_STATUS_BOOT_I_PLL_DONE 0xff07 +#define REG_STATUS_BOOT_I_PLL_LOCKTIME_DONE 0xff08 +#define REG_STATUS_BOOT_PLL_INIT_OK 0xff09 +#define REG_STATUS_BOOT_SENSOR_INIT_OK 0xff0a +#define REG_STATUS_BOOT_GPIO_SETTING_OK 0xff0b +#define REG_STATUS_BOOT_READ_CAL_DATA_OK 0xff0c +#define REG_STATUS_BOOT_STABLE_AE_AWB_OK 0xff0d +#define REG_STATUS_ISP_COMMAND_COMPLETED 0xffff +#define REG_STATUS_EXCEPTION_OCCURED 0xdead + +#define COMM_RESULT_OFFSET S5C73M3_REG(0x0009, 0x5000) + +#define COMM_IMG_OUTPUT 0x0902 +#define COMM_IMG_OUTPUT_HDR 0x0008 +#define COMM_IMG_OUTPUT_YUV 0x0009 +#define COMM_IMG_OUTPUT_INTERLEAVED 0x000d + +#define COMM_STILL_PRE_FLASH 0x0a00 +#define COMM_STILL_PRE_FLASH_FIRE 0x0000 +#define COMM_STILL_PRE_FLASH_NON_FIRED 0x0000 +#define COMM_STILL_PRE_FLASH_FIRED 0x0001 + +#define COMM_STILL_MAIN_FLASH 0x0a02 +#define COMM_STILL_MAIN_FLASH_CANCEL 0x0001 +#define COMM_STILL_MAIN_FLASH_FIRE 0x0002 + +#define COMM_ZOOM_STEP 0x0b00 + +#define COMM_IMAGE_EFFECT 0x0b0a +#define COMM_IMAGE_EFFECT_NONE 0x0001 +#define COMM_IMAGE_EFFECT_NEGATIVE 0x0002 +#define COMM_IMAGE_EFFECT_AQUA 0x0003 +#define COMM_IMAGE_EFFECT_SEPIA 0x0004 +#define COMM_IMAGE_EFFECT_MONO 0x0005 + +#define COMM_IMAGE_QUALITY 0x0b0c +#define COMM_IMAGE_QUALITY_SUPERFINE 0x0000 +#define COMM_IMAGE_QUALITY_FINE 0x0001 +#define COMM_IMAGE_QUALITY_NORMAL 0x0002 + +#define COMM_FLASH_MODE 0x0b0e +#define COMM_FLASH_MODE_OFF 0x0000 +#define COMM_FLASH_MODE_ON 0x0001 +#define COMM_FLASH_MODE_AUTO 0x0002 + +#define COMM_FLASH_STATUS 0x0b80 +#define COMM_FLASH_STATUS_OFF 0x0001 +#define COMM_FLASH_STATUS_ON 0x0002 +#define COMM_FLASH_STATUS_AUTO 0x0003 + +#define COMM_FLASH_TORCH 0x0b12 +#define COMM_FLASH_TORCH_OFF 0x0000 +#define COMM_FLASH_TORCH_ON 0x0001 + +#define COMM_AE_NEEDS_FLASH 0x0cba +#define COMM_AE_NEEDS_FLASH_OFF 0x0000 +#define COMM_AE_NEEDS_FLASH_ON 0x0001 + +#define COMM_CHG_MODE 0x0b10 +#define COMM_CHG_MODE_NEW 0x8000 +#define COMM_CHG_MODE_SUBSAMPLING_HALF 0x2000 +#define COMM_CHG_MODE_SUBSAMPLING_QUARTER 0x4000 + +#define COMM_CHG_MODE_YUV_320_240 0x0001 +#define COMM_CHG_MODE_YUV_640_480 0x0002 +#define COMM_CHG_MODE_YUV_880_720 0x0003 +#define COMM_CHG_MODE_YUV_960_720 0x0004 +#define COMM_CHG_MODE_YUV_1184_666 0x0005 +#define COMM_CHG_MODE_YUV_1280_720 0x0006 +#define COMM_CHG_MODE_YUV_1536_864 0x0007 +#define COMM_CHG_MODE_YUV_1600_1200 0x0008 +#define COMM_CHG_MODE_YUV_1632_1224 0x0009 +#define COMM_CHG_MODE_YUV_1920_1080 0x000a +#define COMM_CHG_MODE_YUV_1920_1440 0x000b +#define COMM_CHG_MODE_YUV_2304_1296 0x000c +#define COMM_CHG_MODE_YUV_3264_2448 0x000d +#define COMM_CHG_MODE_YUV_352_288 0x000e +#define COMM_CHG_MODE_YUV_1008_672 0x000f + +#define COMM_CHG_MODE_JPEG_640_480 0x0010 +#define COMM_CHG_MODE_JPEG_800_450 0x0020 +#define COMM_CHG_MODE_JPEG_800_600 0x0030 +#define COMM_CHG_MODE_JPEG_1280_720 0x0040 +#define COMM_CHG_MODE_JPEG_1280_960 0x0050 +#define COMM_CHG_MODE_JPEG_1600_900 0x0060 +#define COMM_CHG_MODE_JPEG_1600_1200 0x0070 +#define COMM_CHG_MODE_JPEG_2048_1152 0x0080 +#define COMM_CHG_MODE_JPEG_2048_1536 0x0090 +#define COMM_CHG_MODE_JPEG_2560_1440 0x00a0 +#define COMM_CHG_MODE_JPEG_2560_1920 0x00b0 +#define COMM_CHG_MODE_JPEG_3264_2176 0x00c0 +#define COMM_CHG_MODE_JPEG_1024_768 0x00d0 +#define COMM_CHG_MODE_JPEG_3264_1836 0x00e0 +#define COMM_CHG_MODE_JPEG_3264_2448 0x00f0 + +#define COMM_AF_CON 0x0e00 +#define COMM_AF_CON_STOP 0x0000 +#define COMM_AF_CON_SCAN 0x0001 /* Full Search */ +#define COMM_AF_CON_START 0x0002 /* Fast Search */ + +#define COMM_AF_CAL 0x0e06 +#define COMM_AF_TOUCH_AF 0x0e0a + +#define REG_AF_STATUS S5C73M3_REG(0x0009, 0x5e80) +#define REG_CAF_STATUS_FIND_SEARCH_DIR 0x0001 +#define REG_CAF_STATUS_FOCUSING 0x0002 +#define REG_CAF_STATUS_FOCUSED 0x0003 +#define REG_CAF_STATUS_UNFOCUSED 0x0004 +#define REG_AF_STATUS_INVALID 0x0010 +#define REG_AF_STATUS_FOCUSING 0x0020 +#define REG_AF_STATUS_FOCUSED 0x0030 +#define REG_AF_STATUS_UNFOCUSED 0x0040 + +#define REG_AF_TOUCH_POSITION S5C73M3_REG(0x0009, 0x5e8e) +#define COMM_AF_FACE_ZOOM 0x0e10 + +#define COMM_AF_MODE 0x0e02 +#define COMM_AF_MODE_NORMAL 0x0000 +#define COMM_AF_MODE_MACRO 0x0001 +#define COMM_AF_MODE_MOVIE_CAF_START 0x0002 +#define COMM_AF_MODE_MOVIE_CAF_STOP 0x0003 +#define COMM_AF_MODE_PREVIEW_CAF_START 0x0004 +#define COMM_AF_MODE_PREVIEW_CAF_STOP 0x0005 + +#define COMM_AF_SOFTLANDING 0x0e16 +#define COMM_AF_SOFTLANDING_ON 0x0000 +#define COMM_AF_SOFTLANDING_RES_COMPLETE 0x0001 + +#define COMM_FACE_DET 0x0e0c +#define COMM_FACE_DET_OFF 0x0000 +#define COMM_FACE_DET_ON 0x0001 + +#define COMM_FACE_DET_OSD 0x0e0e +#define COMM_FACE_DET_OSD_OFF 0x0000 +#define COMM_FACE_DET_OSD_ON 0x0001 + +#define COMM_AE_CON 0x0c00 +#define COMM_AE_STOP 0x0000 /* lock */ +#define COMM_AE_START 0x0001 /* unlock */ + +#define COMM_ISO 0x0c02 +#define COMM_ISO_AUTO 0x0000 +#define COMM_ISO_100 0x0001 +#define COMM_ISO_200 0x0002 +#define COMM_ISO_400 0x0003 +#define COMM_ISO_800 0x0004 +#define COMM_ISO_SPORTS 0x0005 +#define COMM_ISO_NIGHT 0x0006 +#define COMM_ISO_INDOOR 0x0007 + +/* 0x00000 (-2.0 EV)...0x0008 (2.0 EV), 0.5EV step */ +#define COMM_EV 0x0c04 + +#define COMM_METERING 0x0c06 +#define COMM_METERING_CENTER 0x0000 +#define COMM_METERING_SPOT 0x0001 +#define COMM_METERING_AVERAGE 0x0002 +#define COMM_METERING_SMART 0x0003 + +#define COMM_WDR 0x0c08 +#define COMM_WDR_OFF 0x0000 +#define COMM_WDR_ON 0x0001 + +#define COMM_FLICKER_MODE 0x0c12 +#define COMM_FLICKER_NONE 0x0000 +#define COMM_FLICKER_MANUAL_50HZ 0x0001 +#define COMM_FLICKER_MANUAL_60HZ 0x0002 +#define COMM_FLICKER_AUTO 0x0003 +#define COMM_FLICKER_AUTO_50HZ 0x0004 +#define COMM_FLICKER_AUTO_60HZ 0x0005 + +#define COMM_FRAME_RATE 0x0c1e +#define COMM_FRAME_RATE_AUTO_SET 0x0000 +#define COMM_FRAME_RATE_FIXED_30FPS 0x0002 +#define COMM_FRAME_RATE_FIXED_20FPS 0x0003 +#define COMM_FRAME_RATE_FIXED_15FPS 0x0004 +#define COMM_FRAME_RATE_FIXED_60FPS 0x0007 +#define COMM_FRAME_RATE_FIXED_120FPS 0x0008 +#define COMM_FRAME_RATE_FIXED_7FPS 0x0009 +#define COMM_FRAME_RATE_FIXED_10FPS 0x000a +#define COMM_FRAME_RATE_FIXED_90FPS 0x000b +#define COMM_FRAME_RATE_ANTI_SHAKE 0x0013 + +/* 0x0000...0x0004 -> sharpness: 0, 1, 2, -1, -2 */ +#define COMM_SHARPNESS 0x0c14 + +/* 0x0000...0x0004 -> saturation: 0, 1, 2, -1, -2 */ +#define COMM_SATURATION 0x0c16 + +/* 0x0000...0x0004 -> contrast: 0, 1, 2, -1, -2 */ +#define COMM_CONTRAST 0x0c18 + +#define COMM_SCENE_MODE 0x0c1a +#define COMM_SCENE_MODE_NONE 0x0000 +#define COMM_SCENE_MODE_PORTRAIT 0x0001 +#define COMM_SCENE_MODE_LANDSCAPE 0x0002 +#define COMM_SCENE_MODE_SPORTS 0x0003 +#define COMM_SCENE_MODE_INDOOR 0x0004 +#define COMM_SCENE_MODE_BEACH 0x0005 +#define COMM_SCENE_MODE_SUNSET 0x0006 +#define COMM_SCENE_MODE_DAWN 0x0007 +#define COMM_SCENE_MODE_FALL 0x0008 +#define COMM_SCENE_MODE_NIGHT 0x0009 +#define COMM_SCENE_MODE_AGAINST_LIGHT 0x000a +#define COMM_SCENE_MODE_FIRE 0x000b +#define COMM_SCENE_MODE_TEXT 0x000c +#define COMM_SCENE_MODE_CANDLE 0x000d + +#define COMM_AE_AUTO_BRACKET 0x0b14 +#define COMM_AE_AUTO_BRAKET_EV05 0x0080 +#define COMM_AE_AUTO_BRAKET_EV10 0x0100 +#define COMM_AE_AUTO_BRAKET_EV15 0x0180 +#define COMM_AE_AUTO_BRAKET_EV20 0x0200 + +#define COMM_SENSOR_STREAMING 0x090a +#define COMM_SENSOR_STREAMING_OFF 0x0000 +#define COMM_SENSOR_STREAMING_ON 0x0001 + +#define COMM_AWB_MODE 0x0d02 +#define COMM_AWB_MODE_INCANDESCENT 0x0000 +#define COMM_AWB_MODE_FLUORESCENT1 0x0001 +#define COMM_AWB_MODE_FLUORESCENT2 0x0002 +#define COMM_AWB_MODE_DAYLIGHT 0x0003 +#define COMM_AWB_MODE_CLOUDY 0x0004 +#define COMM_AWB_MODE_AUTO 0x0005 + +#define COMM_AWB_CON 0x0d00 +#define COMM_AWB_STOP 0x0000 /* lock */ +#define COMM_AWB_START 0x0001 /* unlock */ + +#define COMM_FW_UPDATE 0x0906 +#define COMM_FW_UPDATE_NOT_READY 0x0000 +#define COMM_FW_UPDATE_SUCCESS 0x0005 +#define COMM_FW_UPDATE_FAIL 0x0007 +#define COMM_FW_UPDATE_BUSY 0xffff + + +#define S5C73M3_MAX_SUPPLIES 6 + +struct s5c73m3_ctrls { + struct v4l2_ctrl_handler handler; + struct { + /* exposure/exposure bias cluster */ + struct v4l2_ctrl *auto_exposure; + struct v4l2_ctrl *exposure_bias; + struct v4l2_ctrl *exposure_metering; + }; + struct { + /* iso/auto iso cluster */ + struct v4l2_ctrl *auto_iso; + struct v4l2_ctrl *iso; + }; + struct v4l2_ctrl *auto_wb; + struct { + /* continuous auto focus/auto focus cluster */ + struct v4l2_ctrl *focus_auto; + struct v4l2_ctrl *af_start; + struct v4l2_ctrl *af_stop; + struct v4l2_ctrl *af_status; + struct v4l2_ctrl *af_distance; + }; + + struct v4l2_ctrl *aaa_lock; + struct v4l2_ctrl *colorfx; + struct v4l2_ctrl *contrast; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *sharpness; + struct v4l2_ctrl *zoom; + struct v4l2_ctrl *wdr; + struct v4l2_ctrl *stabilization; + struct v4l2_ctrl *jpeg_quality; + struct v4l2_ctrl *scene_mode; +}; + +enum s5c73m3_gpio_id { + STBY, + RST, + GPIO_NUM, +}; + +enum s5c73m3_resolution_types { + RES_ISP, + RES_JPEG, +}; + +struct s5c73m3_interval { + u16 fps_reg; + struct v4l2_fract interval; + /* Maximum rectangle for the interval */ + struct v4l2_frmsize_discrete size; +}; + +struct s5c73m3 { + struct v4l2_subdev sensor_sd; + struct media_pad sensor_pads[S5C73M3_NUM_PADS]; + + struct v4l2_subdev oif_sd; + struct media_pad oif_pads[OIF_NUM_PADS]; + + struct spi_driver spidrv; + struct spi_device *spi_dev; + struct i2c_client *i2c_client; + u32 i2c_write_address; + u32 i2c_read_address; + + struct regulator_bulk_data supplies[S5C73M3_MAX_SUPPLIES]; + struct s5c73m3_gpio gpio[GPIO_NUM]; + + /* External master clock frequency */ + u32 mclk_frequency; + /* Video bus type - MIPI-CSI2/paralell */ + enum v4l2_mbus_type bus_type; + + const struct s5c73m3_frame_size *sensor_pix_size[2]; + const struct s5c73m3_frame_size *oif_pix_size[2]; + enum v4l2_mbus_pixelcode mbus_code; + + const struct s5c73m3_interval *fiv; + + struct v4l2_mbus_frame_desc frame_desc; + /* protects the struct members below */ + struct mutex lock; + + struct s5c73m3_ctrls ctrls; + + u8 streaming:1; + u8 apply_fmt:1; + u8 apply_fiv:1; + u8 isp_ready:1; + + short power; + + char sensor_fw[S5C73M3_SENSOR_FW_LEN + 2]; + char sensor_type[S5C73M3_SENSOR_TYPE_LEN + 2]; + char fw_file_version[2]; + unsigned int fw_size; +}; + +struct s5c73m3_frame_size { + u32 width; + u32 height; + u8 reg_val; +}; + +extern int s5c73m3_dbg; + +int s5c73m3_register_spi_driver(struct s5c73m3 *state); +void s5c73m3_unregister_spi_driver(struct s5c73m3 *state); +int s5c73m3_spi_write(struct s5c73m3 *state, const void *addr, + const unsigned int len, const unsigned int tx_size); +int s5c73m3_spi_read(struct s5c73m3 *state, void *addr, + const unsigned int len, const unsigned int tx_size); + +int s5c73m3_read(struct s5c73m3 *state, u32 addr, u16 *data); +int s5c73m3_write(struct s5c73m3 *state, u32 addr, u16 data); +int s5c73m3_isp_command(struct s5c73m3 *state, u16 command, u16 data); +int s5c73m3_init_controls(struct s5c73m3 *state); + +static inline struct v4l2_subdev *ctrl_to_sensor_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct s5c73m3, + ctrls.handler)->sensor_sd; +} + +static inline struct s5c73m3 *sensor_sd_to_s5c73m3(struct v4l2_subdev *sd) +{ + return container_of(sd, struct s5c73m3, sensor_sd); +} + +static inline struct s5c73m3 *oif_sd_to_s5c73m3(struct v4l2_subdev *sd) +{ + return container_of(sd, struct s5c73m3, oif_sd); +} +#endif /* S5C73M3_H_ */ diff --git a/include/media/s5c73m3.h b/include/media/s5c73m3.h new file mode 100644 index 000000000000..ccb9e5448762 --- /dev/null +++ b/include/media/s5c73m3.h @@ -0,0 +1,55 @@ +/* + * Samsung LSI S5C73M3 8M pixel camera driver + * + * Copyright (C) 2012, Samsung Electronics, Co., Ltd. + * Sylwester Nawrocki + * Andrzej Hajda + * + * 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. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef MEDIA_S5C73M3__ +#define MEDIA_S5C73M3__ + +#include +#include + +/** + * struct s5c73m3_gpio - data structure describing a GPIO + * @gpio: GPIO number + * @level: indicates active state of the @gpio + */ +struct s5c73m3_gpio { + int gpio; + int level; +}; + +/** + * struct s5c73m3_platform_data - s5c73m3 driver platform data + * @mclk_frequency: sensor's master clock frequency in Hz + * @gpio_reset: GPIO driving RESET pin + * @gpio_stby: GPIO driving STBY pin + * @nlanes: maximum number of MIPI-CSI lanes used + * @horiz_flip: default horizontal image flip value, non zero to enable + * @vert_flip: default vertical image flip value, non zero to enable + */ + +struct s5c73m3_platform_data { + unsigned long mclk_frequency; + + struct s5c73m3_gpio gpio_reset; + struct s5c73m3_gpio gpio_stby; + + enum v4l2_mbus_type bus_type; + u8 nlanes; + u8 horiz_flip; + u8 vert_flip; +}; + +#endif /* MEDIA_S5C73M3__ */ -- cgit v1.2.3 From 81b9f70210b62e256cf63eb8f703042ef44e4cdd Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 18 Jan 2013 12:02:31 -0300 Subject: [media] s5p-fimc: fimc-lite: Remove empty s_power subdev callback The .s_power FIMC-LITE subdev callback is now empty and unneeded. The FIMC-LITE IP power handling will be done by the FIMC-IS driver, so just remove the callback. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-lite.c | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/s5p-fimc/fimc-lite.c index c67dd2ebcff5..fe6cd0c3bdd3 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite.c @@ -1280,18 +1280,6 @@ static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on) return ret; } -static int fimc_lite_subdev_s_power(struct v4l2_subdev *sd, int on) -{ - struct fimc_lite *fimc = v4l2_get_subdevdata(sd); - - if (fimc->out_path == FIMC_IO_DMA) - return -ENOIOCTLCMD; - - /* TODO: */ - - return 0; -} - static int fimc_lite_log_status(struct v4l2_subdev *sd) { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); @@ -1391,7 +1379,6 @@ static const struct v4l2_subdev_video_ops fimc_lite_subdev_video_ops = { }; static const struct v4l2_subdev_core_ops fimc_lite_core_ops = { - .s_power = fimc_lite_subdev_s_power, .log_status = fimc_lite_log_status, }; -- cgit v1.2.3 From 03878bb473bb46cf8514223d8c955420b1ef73bc Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 18 Jan 2013 12:02:32 -0300 Subject: [media] s5p-fimc: fimc-lite: Prevent deadlock at STREAMON/OFF ioctls This patch fixes regression introduced in commit 6319d6a002beb26631 '[media] fimc-lite: Add ISP FIFO output support'. In case of a configuration where video is captured at the video node exposed by the FIMC-LITE driver there is a following video pipeline: sensor -> MIPI-CSIS.n -> FIMC-LITE.n subdev -> FIMC-LITE.n video node In this situation s_stream() handler of the FIMC-LITE.n is called back from within VIDIOC_STREAMON/OFF ioctl of the FIMC-LITE.n video node, through vb2_stream_on/off(), start/stop_streaming and fimc_pipeline_call(set_stream). The fimc->lock mutex is already held then, before invoking vidioc_streamon/off. So it must not be taken again in the s_stream() callback in this case, to avoid a deadlock. This patch makes fimc->out_path atomic_t so the mutex don't need to be taken in the FIMC-LITE subdev s_stream() callback in the DMA output case. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyugmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-lite-reg.c | 2 +- drivers/media/platform/s5p-fimc/fimc-lite.c | 35 ++++++++++++++----------- drivers/media/platform/s5p-fimc/fimc-lite.h | 2 +- 3 files changed, 21 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c b/drivers/media/platform/s5p-fimc/fimc-lite-reg.c index ad63ebf082c5..962652da3b43 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite-reg.c @@ -65,7 +65,7 @@ void flite_hw_set_interrupt_mask(struct fimc_lite *dev) u32 cfg, intsrc; /* Select interrupts to be enabled for each output mode */ - if (dev->out_path == FIMC_IO_DMA) { + if (atomic_read(&dev->out_path) == FIMC_IO_DMA) { intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN | FLITE_REG_CIGCTRL_IRQ_LASTEN | FLITE_REG_CIGCTRL_IRQ_STARTEN; diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/s5p-fimc/fimc-lite.c index fe6cd0c3bdd3..ef3989fe63e5 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite.c @@ -260,7 +260,7 @@ static irqreturn_t flite_irq_handler(int irq, void *priv) wake_up(&fimc->irq_queue); } - if (fimc->out_path != FIMC_IO_DMA) + if (atomic_read(&fimc->out_path) != FIMC_IO_DMA) goto done; if ((intsrc & FLITE_REG_CISTATUS_IRQ_SRC_FRMSTART) && @@ -465,7 +465,7 @@ static int fimc_lite_open(struct file *file) mutex_lock(&me->parent->graph_mutex); mutex_lock(&fimc->lock); - if (fimc->out_path != FIMC_IO_DMA) { + if (atomic_read(&fimc->out_path) != FIMC_IO_DMA) { ret = -EBUSY; goto done; } @@ -479,7 +479,8 @@ static int fimc_lite_open(struct file *file) if (ret < 0) goto done; - if (++fimc->ref_count == 1 && fimc->out_path == FIMC_IO_DMA) { + if (++fimc->ref_count == 1 && + atomic_read(&fimc->out_path) == FIMC_IO_DMA) { ret = fimc_pipeline_call(fimc, open, &fimc->pipeline, &fimc->vfd.entity, true); if (ret < 0) { @@ -504,7 +505,8 @@ static int fimc_lite_close(struct file *file) mutex_lock(&fimc->lock); - if (--fimc->ref_count == 0 && fimc->out_path == FIMC_IO_DMA) { + if (--fimc->ref_count == 0 && + atomic_read(&fimc->out_path) == FIMC_IO_DMA) { clear_bit(ST_FLITE_IN_USE, &fimc->state); fimc_lite_stop_capture(fimc, false); fimc_pipeline_call(fimc, close, &fimc->pipeline); @@ -1034,18 +1036,18 @@ static int fimc_lite_link_setup(struct media_entity *entity, case FLITE_SD_PAD_SOURCE_DMA: if (!(flags & MEDIA_LNK_FL_ENABLED)) - fimc->out_path = FIMC_IO_NONE; + atomic_set(&fimc->out_path, FIMC_IO_NONE); else if (remote_ent_type == MEDIA_ENT_T_DEVNODE) - fimc->out_path = FIMC_IO_DMA; + atomic_set(&fimc->out_path, FIMC_IO_DMA); else ret = -EINVAL; break; case FLITE_SD_PAD_SOURCE_ISP: if (!(flags & MEDIA_LNK_FL_ENABLED)) - fimc->out_path = FIMC_IO_NONE; + atomic_set(&fimc->out_path, FIMC_IO_NONE); else if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV) - fimc->out_path = FIMC_IO_ISP; + atomic_set(&fimc->out_path, FIMC_IO_ISP); else ret = -EINVAL; break; @@ -1054,6 +1056,7 @@ static int fimc_lite_link_setup(struct media_entity *entity, v4l2_err(sd, "Invalid pad index\n"); ret = -EINVAL; } + mb(); mutex_unlock(&fimc->lock); return ret; @@ -1123,8 +1126,10 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, mf->colorspace = V4L2_COLORSPACE_JPEG; mutex_lock(&fimc->lock); - if ((fimc->out_path == FIMC_IO_ISP && sd->entity.stream_count > 0) || - (fimc->out_path == FIMC_IO_DMA && vb2_is_busy(&fimc->vb_queue))) { + if ((atomic_read(&fimc->out_path) == FIMC_IO_ISP && + sd->entity.stream_count > 0) || + (atomic_read(&fimc->out_path) == FIMC_IO_DMA && + vb2_is_busy(&fimc->vb_queue))) { mutex_unlock(&fimc->lock); return -EBUSY; } @@ -1247,12 +1252,10 @@ static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on) */ fimc->sensor = __find_remote_sensor(&sd->entity); - mutex_lock(&fimc->lock); - if (fimc->out_path != FIMC_IO_ISP) { - mutex_unlock(&fimc->lock); + if (atomic_read(&fimc->out_path) != FIMC_IO_ISP) return -ENOIOCTLCMD; - } + mutex_lock(&fimc->lock); if (on) { flite_hw_reset(fimc); ret = fimc_lite_hw_init(fimc, true); @@ -1298,7 +1301,7 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) memset(vfd, 0, sizeof(*vfd)); fimc->fmt = &fimc_lite_formats[0]; - fimc->out_path = FIMC_IO_DMA; + atomic_set(&fimc->out_path, FIMC_IO_DMA); snprintf(vfd->name, sizeof(vfd->name), "fimc-lite.%d.capture", fimc->index); @@ -1589,7 +1592,7 @@ static int fimc_lite_resume(struct device *dev) INIT_LIST_HEAD(&fimc->active_buf_q); fimc_pipeline_call(fimc, open, &fimc->pipeline, &fimc->vfd.entity, false); - fimc_lite_hw_init(fimc, fimc->out_path == FIMC_IO_ISP); + fimc_lite_hw_init(fimc, atomic_read(&fimc->out_path) == FIMC_IO_ISP); clear_bit(ST_FLITE_SUSPENDED, &fimc->state); for (i = 0; i < fimc->reqbufs_count; i++) { diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.h b/drivers/media/platform/s5p-fimc/fimc-lite.h index 4576922952f3..7085761f8c4b 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.h +++ b/drivers/media/platform/s5p-fimc/fimc-lite.h @@ -159,7 +159,7 @@ struct fimc_lite { unsigned long payload[FLITE_MAX_PLANES]; struct flite_frame inp_frame; struct flite_frame out_frame; - enum fimc_datapath out_path; + atomic_t out_path; unsigned int source_subdev_grp_id; unsigned long state; -- cgit v1.2.3 From bed3cd2753d986768a15a6c45b5ae3a56007c1b2 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 8 Jan 2013 02:58:51 -0300 Subject: [media] s5p-csis: Use devm_regulator_bulk_get API devm_regulator_bulk_get is device managed and saves some cleanup and exit code. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/mipi-csis.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index cde510f0eea6..7e36ad92dd94 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -743,7 +743,7 @@ static int s5pcsis_probe(struct platform_device *pdev) for (i = 0; i < CSIS_NUM_SUPPLIES; i++) state->supplies[i].supply = csis_supply_name[i]; - ret = regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES, + ret = devm_regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES, state->supplies); if (ret) return ret; @@ -762,7 +762,7 @@ static int s5pcsis_probe(struct platform_device *pdev) 0, dev_name(&pdev->dev), state); if (ret) { dev_err(&pdev->dev, "Interrupt request failed\n"); - goto e_regput; + goto e_clkput; } v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops); @@ -793,8 +793,6 @@ static int s5pcsis_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); return 0; -e_regput: - regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies); e_clkput: clk_disable(state->clock[CSIS_CLK_MUX]); s5pcsis_clk_put(state); @@ -903,7 +901,6 @@ static int s5pcsis_remove(struct platform_device *pdev) clk_disable(state->clock[CSIS_CLK_MUX]); pm_runtime_set_suspended(&pdev->dev); s5pcsis_clk_put(state); - regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies); media_entity_cleanup(&state->sd.entity); -- cgit v1.2.3 From 969e877cc1e6e91dc76f973d08ad70e2065c56ae Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 7 Dec 2012 16:40:08 -0300 Subject: [media] s5p-fimc: Add missing line breaks Add missing line breaks in the debug traces. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-core.c | 4 ++-- drivers/media/platform/s5p-fimc/fimc-lite.c | 12 ++++++------ drivers/media/platform/s5p-fimc/fimc-mdevice.c | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-core.c b/drivers/media/platform/s5p-fimc/fimc-core.c index 92d477c91811..6d5b03a7b5e5 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.c +++ b/drivers/media/platform/s5p-fimc/fimc-core.c @@ -257,14 +257,14 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx) ty = d_frame->height; } if (tx <= 0 || ty <= 0) { - dev_err(dev, "Invalid target size: %dx%d", tx, ty); + dev_err(dev, "Invalid target size: %dx%d\n", tx, ty); return -EINVAL; } sx = s_frame->width; sy = s_frame->height; if (sx <= 0 || sy <= 0) { - dev_err(dev, "Invalid source size: %dx%d", sx, sy); + dev_err(dev, "Invalid source size: %dx%d\n", sx, sy); return -EINVAL; } sc->real_width = sx; diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/s5p-fimc/fimc-lite.c index ef3989fe63e5..e18babfa89e0 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite.c @@ -611,7 +611,7 @@ static void fimc_lite_try_crop(struct fimc_lite *fimc, struct v4l2_rect *r) r->left = round_down(r->left, fimc->variant->win_hor_offs_align); r->top = clamp_t(u32, r->top, 0, frame->f_height - r->height); - v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, sink fmt: %dx%d", + v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, sink fmt: %dx%d\n", r->left, r->top, r->width, r->height, frame->f_width, frame->f_height); } @@ -631,7 +631,7 @@ static void fimc_lite_try_compose(struct fimc_lite *fimc, struct v4l2_rect *r) r->left = round_down(r->left, fimc->variant->out_hor_offs_align); r->top = clamp_t(u32, r->top, 0, fimc->out_frame.f_height - r->height); - v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, source fmt: %dx%d", + v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, source fmt: %dx%d\n", r->left, r->top, r->width, r->height, frame->f_width, frame->f_height); } @@ -1011,7 +1011,7 @@ static int fimc_lite_link_setup(struct media_entity *entity, if (WARN_ON(fimc == NULL)) return 0; - v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x", + v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x\n", __func__, remote->entity->name, local->entity->name, flags, fimc->source_subdev_grp_id); @@ -1120,7 +1120,7 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, struct flite_frame *source = &fimc->out_frame; const struct fimc_fmt *ffmt; - v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %dx%d", + v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %dx%d\n", fmt->pad, mf->code, mf->width, mf->height); mf->colorspace = V4L2_COLORSPACE_JPEG; @@ -1196,7 +1196,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, } mutex_unlock(&fimc->lock); - v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d", + v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d\n", __func__, f->rect.left, f->rect.top, f->rect.width, f->rect.height, f->f_width, f->f_height); @@ -1230,7 +1230,7 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, } mutex_unlock(&fimc->lock); - v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d", + v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d\n", __func__, f->rect.left, f->rect.top, f->rect.width, f->rect.height, f->f_width, f->f_height); diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 27d346195dbd..52e1aa3c8982 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -556,7 +556,7 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, if (ret) break; - v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]", + v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]\n", source->name, flags ? '=' : '-', sink->name); } return 0; @@ -640,7 +640,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) if (ret) return ret; - v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]", + v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]\n", sensor->entity.name, csis->entity.name); source = NULL; -- cgit v1.2.3 From 7b43a6f3f517109c2912d82f7f666a84420689bc Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 12 Dec 2012 08:16:05 -0300 Subject: [media] s5p-fimc: Change platform subdevs registration method The previous method of registering platform entities into the main driver using driver_find() and then iterating over devices bound to a driver was racy and is being removed here. Nothing was preventing module from unloading during a call to try_module_get(driver->owner). Instead, we look up a device first and then check for its driver while holding device lock. The platform sub-devices are looked up and registered to the top level driver. When any sub-device is not yet initialized and ready the main driver's probe() will be deferred. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-mdevice.c | 203 +++++++++++++------------ 1 file changed, 108 insertions(+), 95 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 52e1aa3c8982..2b0587204868 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -1,8 +1,8 @@ /* * S5P/EXYNOS4 SoC series camera host interface media device driver * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Contact: Sylwester Nawrocki, + * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published @@ -312,138 +312,149 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) } /* - * MIPI CSIS and FIMC platform devices registration. + * MIPI-CSIS, FIMC and FIMC-LITE platform devices registration. */ -static int fimc_register_callback(struct device *dev, void *p) + +static int register_fimc_lite_entity(struct fimc_md *fmd, + struct fimc_lite *fimc_lite) { - struct fimc_dev *fimc = dev_get_drvdata(dev); struct v4l2_subdev *sd; - struct fimc_md *fmd = p; int ret; - if (fimc == NULL || fimc->id >= FIMC_MAX_DEVS) - return 0; + if (WARN_ON(fimc_lite->index >= FIMC_LITE_MAX_DEVS || + fmd->fimc_lite[fimc_lite->index])) + return -EBUSY; - sd = &fimc->vid_cap.subdev; - sd->grp_id = GRP_ID_FIMC; + sd = &fimc_lite->subdev; + sd->grp_id = GRP_ID_FLITE; v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops); ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); - if (ret) { - v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.%d (%d)\n", - fimc->id, ret); - return ret; - } - - fmd->fimc[fimc->id] = fimc; - return 0; + if (!ret) + fmd->fimc_lite[fimc_lite->index] = fimc_lite; + else + v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.LITE%d\n", + fimc_lite->index); + return ret; } -static int fimc_lite_register_callback(struct device *dev, void *p) +static int register_fimc_entity(struct fimc_md *fmd, struct fimc_dev *fimc) { - struct fimc_lite *fimc = dev_get_drvdata(dev); - struct fimc_md *fmd = p; + struct v4l2_subdev *sd; int ret; - if (fimc == NULL || fimc->index >= FIMC_LITE_MAX_DEVS) - return 0; + if (WARN_ON(fimc->id >= FIMC_MAX_DEVS || fmd->fimc[fimc->id])) + return -EBUSY; - fimc->subdev.grp_id = GRP_ID_FLITE; - v4l2_set_subdev_hostdata(&fimc->subdev, (void *)&fimc_pipeline_ops); + sd = &fimc->vid_cap.subdev; + sd->grp_id = GRP_ID_FIMC; + v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops); - ret = v4l2_device_register_subdev(&fmd->v4l2_dev, &fimc->subdev); - if (ret) { - v4l2_err(&fmd->v4l2_dev, - "Failed to register FIMC-LITE.%d (%d)\n", - fimc->index, ret); - return ret; + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); + if (!ret) { + fmd->fimc[fimc->id] = fimc; + fimc->vid_cap.user_subdev_api = fmd->user_subdev_api; + } else { + v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.%d (%d)\n", + fimc->id, ret); } - - fmd->fimc_lite[fimc->index] = fimc; - return 0; + return ret; } -static int csis_register_callback(struct device *dev, void *p) +static int register_csis_entity(struct fimc_md *fmd, + struct platform_device *pdev, + struct v4l2_subdev *sd) { - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct platform_device *pdev; - struct fimc_md *fmd = p; + struct device_node *node = pdev->dev.of_node; int id, ret; - if (!sd) - return 0; - pdev = v4l2_get_subdevdata(sd); - if (!pdev || pdev->id < 0 || pdev->id >= CSIS_MAX_ENTITIES) + id = node ? of_alias_get_id(node, "csis") : max(0, pdev->id); + + if (WARN_ON(id >= CSIS_MAX_ENTITIES || fmd->csis[id].sd)) + return -EBUSY; + + if (WARN_ON(id >= CSIS_MAX_ENTITIES)) return 0; - v4l2_info(sd, "csis%d sd: %s\n", pdev->id, sd->name); - id = pdev->id < 0 ? 0 : pdev->id; sd->grp_id = GRP_ID_CSIS; - ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); if (!ret) fmd->csis[id].sd = sd; else v4l2_err(&fmd->v4l2_dev, - "Failed to register CSIS subdevice: %d\n", ret); + "Failed to register MIPI-CSIS.%d (%d)\n", id, ret); return ret; } -/** - * fimc_md_register_platform_entities - register FIMC and CSIS media entities - */ -static int fimc_md_register_platform_entities(struct fimc_md *fmd) +static int fimc_md_register_platform_entity(struct fimc_md *fmd, + struct platform_device *pdev, + int plat_entity) { - struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data; - struct device_driver *driver; - int ret, i; - - driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type); - if (!driver) { - v4l2_warn(&fmd->v4l2_dev, - "%s driver not found, deffering probe\n", - FIMC_MODULE_NAME); - return -EPROBE_DEFER; - } - - ret = driver_for_each_device(driver, NULL, fmd, - fimc_register_callback); - if (ret) - return ret; - - driver = driver_find(FIMC_LITE_DRV_NAME, &platform_bus_type); - if (driver && try_module_get(driver->owner)) { - ret = driver_for_each_device(driver, NULL, fmd, - fimc_lite_register_callback); - if (ret) - return ret; - module_put(driver->owner); - } - /* - * Check if there is any sensor on the MIPI-CSI2 bus and - * if not skip the s5p-csis module loading. - */ - if (pdata == NULL) - return 0; - for (i = 0; i < pdata->num_clients; i++) { - if (pdata->isp_info[i].bus_type == FIMC_MIPI_CSI2) { - ret = 1; + struct device *dev = &pdev->dev; + int ret = -EPROBE_DEFER; + void *drvdata; + + /* Lock to ensure dev->driver won't change. */ + device_lock(dev); + + if (!dev->driver || !try_module_get(dev->driver->owner)) + goto dev_unlock; + + drvdata = dev_get_drvdata(dev); + /* Some subdev didn't probe succesfully id drvdata is NULL */ + if (drvdata) { + switch (plat_entity) { + case IDX_FIMC: + ret = register_fimc_entity(fmd, drvdata); + break; + case IDX_FLITE: + ret = register_fimc_lite_entity(fmd, drvdata); break; + case IDX_CSIS: + ret = register_csis_entity(fmd, pdev, drvdata); + break; + default: + ret = -ENODEV; } } - if (!ret) - return 0; - driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type); - if (!driver || !try_module_get(driver->owner)) { - v4l2_warn(&fmd->v4l2_dev, - "%s driver not found, deffering probe\n", - CSIS_DRIVER_NAME); - return -EPROBE_DEFER; + module_put(dev->driver->owner); +dev_unlock: + device_unlock(dev); + if (ret == -EPROBE_DEFER) + dev_info(&fmd->pdev->dev, "deferring %s device registration\n", + dev_name(dev)); + else if (ret < 0) + dev_err(&fmd->pdev->dev, "%s device registration failed (%d)\n", + dev_name(dev), ret); + return ret; +} + +static int fimc_md_pdev_match(struct device *dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + int plat_entity = -1; + int ret; + char *p; + + if (!get_device(dev)) + return -ENODEV; + + if (!strcmp(pdev->name, CSIS_DRIVER_NAME)) { + plat_entity = IDX_CSIS; + } else if (!strcmp(pdev->name, FIMC_LITE_DRV_NAME)) { + plat_entity = IDX_FLITE; + } else { + p = strstr(pdev->name, "fimc"); + if (p && *(p + 4) == 0) + plat_entity = IDX_FIMC; } - return driver_for_each_device(driver, NULL, fmd, - csis_register_callback); + if (plat_entity >= 0) + ret = fimc_md_register_platform_entity(data, pdev, + plat_entity); + put_device(dev); + return 0; } static void fimc_md_unregister_entities(struct fimc_md *fmd) @@ -477,6 +488,7 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) fimc_md_unregister_sensor(fmd->sensor[i].subdev); fmd->sensor[i].subdev = NULL; } + v4l2_info(&fmd->v4l2_dev, "Unregistered all entities\n"); } /** @@ -939,7 +951,8 @@ static int fimc_md_probe(struct platform_device *pdev) /* Protect the media graph while we're registering entities */ mutex_lock(&fmd->media_dev.graph_mutex); - ret = fimc_md_register_platform_entities(fmd); + ret = bus_for_each_dev(&platform_bus_type, NULL, fmd, + fimc_md_pdev_match); if (ret) goto err_unlock; -- cgit v1.2.3 From b71b56b264ae27f32784973d15bfdfbc7df6d579 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 29 Jan 2013 06:42:28 -0300 Subject: [media] s5p-fimc: Check return value of clk_enable/clk_set_rate clk_set_rate(), clk_enable() functions can fail, so check the return values to avoid surprises. While at it use ERR_PTR() value to indicate an invalid clock. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-core.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-core.c b/drivers/media/platform/s5p-fimc/fimc-core.c index 6d5b03a7b5e5..a9625414ea95 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.c +++ b/drivers/media/platform/s5p-fimc/fimc-core.c @@ -808,11 +808,11 @@ static void fimc_clk_put(struct fimc_dev *fimc) { int i; for (i = 0; i < MAX_FIMC_CLOCKS; i++) { - if (IS_ERR_OR_NULL(fimc->clock[i])) + if (IS_ERR(fimc->clock[i])) continue; clk_unprepare(fimc->clock[i]); clk_put(fimc->clock[i]); - fimc->clock[i] = NULL; + fimc->clock[i] = ERR_PTR(-EINVAL); } } @@ -820,14 +820,19 @@ static int fimc_clk_get(struct fimc_dev *fimc) { int i, ret; + for (i = 0; i < MAX_FIMC_CLOCKS; i++) + fimc->clock[i] = ERR_PTR(-EINVAL); + for (i = 0; i < MAX_FIMC_CLOCKS; i++) { fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clocks[i]); - if (IS_ERR(fimc->clock[i])) + if (IS_ERR(fimc->clock[i])) { + ret = PTR_ERR(fimc->clock[i]); goto err; + } ret = clk_prepare(fimc->clock[i]); if (ret < 0) { clk_put(fimc->clock[i]); - fimc->clock[i] = NULL; + fimc->clock[i] = ERR_PTR(-EINVAL); goto err; } } @@ -921,8 +926,14 @@ static int fimc_probe(struct platform_device *pdev) ret = fimc_clk_get(fimc); if (ret) return ret; - clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency); - clk_enable(fimc->clock[CLK_BUS]); + + ret = clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency); + if (ret < 0) + return ret; + + ret = clk_enable(fimc->clock[CLK_BUS]); + if (ret < 0) + return ret; ret = devm_request_irq(&pdev->dev, res->start, fimc_irq_handler, 0, dev_name(&pdev->dev), fimc); @@ -956,6 +967,7 @@ err_pm: err_sd: fimc_unregister_capture_subdev(fimc); err_clk: + clk_disable(fimc->clock[CLK_BUS]); fimc_clk_put(fimc); return ret; } -- cgit v1.2.3 From 44e2b09ca468c7c91fa4bb8058fcda38f344974a Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 29 Jan 2013 06:52:29 -0300 Subject: [media] s5p-csis: Check return value of clk_enable/clk_set_rate clk_set_rate(), clk_enable() functions can fail, so check the return values to avoid surprises. While at it use ERR_PTR() value to indicate an invalid clock. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/mipi-csis.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index 7e36ad92dd94..613482fd74a9 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -352,11 +352,11 @@ static void s5pcsis_clk_put(struct csis_state *state) int i; for (i = 0; i < NUM_CSIS_CLOCKS; i++) { - if (IS_ERR_OR_NULL(state->clock[i])) + if (IS_ERR(state->clock[i])) continue; clk_unprepare(state->clock[i]); clk_put(state->clock[i]); - state->clock[i] = NULL; + state->clock[i] = ERR_PTR(-EINVAL); } } @@ -365,14 +365,19 @@ static int s5pcsis_clk_get(struct csis_state *state) struct device *dev = &state->pdev->dev; int i, ret; + for (i = 0; i < NUM_CSIS_CLOCKS; i++) + state->clock[i] = ERR_PTR(-EINVAL); + for (i = 0; i < NUM_CSIS_CLOCKS; i++) { state->clock[i] = clk_get(dev, csi_clock_name[i]); - if (IS_ERR(state->clock[i])) + if (IS_ERR(state->clock[i])) { + ret = PTR_ERR(state->clock[i]); goto err; + } ret = clk_prepare(state->clock[i]); if (ret < 0) { clk_put(state->clock[i]); - state->clock[i] = NULL; + state->clock[i] = ERR_PTR(-EINVAL); goto err; } } @@ -380,7 +385,7 @@ static int s5pcsis_clk_get(struct csis_state *state) err: s5pcsis_clk_put(state); dev_err(dev, "failed to get clock: %s\n", csi_clock_name[i]); - return -ENXIO; + return ret; } static void dump_regs(struct csis_state *state, const char *label) @@ -749,14 +754,20 @@ static int s5pcsis_probe(struct platform_device *pdev) return ret; ret = s5pcsis_clk_get(state); - if (ret) - goto e_clkput; + if (ret < 0) + return ret; - clk_enable(state->clock[CSIS_CLK_MUX]); if (pdata->clk_rate) - clk_set_rate(state->clock[CSIS_CLK_MUX], pdata->clk_rate); + ret = clk_set_rate(state->clock[CSIS_CLK_MUX], + pdata->clk_rate); else dev_WARN(&pdev->dev, "No clock frequency specified!\n"); + if (ret < 0) + goto e_clkput; + + ret = clk_enable(state->clock[CSIS_CLK_MUX]); + if (ret < 0) + goto e_clkput; ret = devm_request_irq(&pdev->dev, state->irq, s5pcsis_irq_handler, 0, dev_name(&pdev->dev), state); -- cgit v1.2.3 From c6c03915b630c2b4e488be4f21ab46703e31c16b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 29 Jan 2013 02:32:30 -0300 Subject: [media] s5c73m3: Staticize some symbols Fixes the following sparse warnings: drivers/media/i2c/s5c73m3/s5c73m3-core.c:42:5: warning: symbol 'boot_from_rom' was not declared. Should it be static? drivers/media/i2c/s5c73m3/s5c73m3-core.c:45:5: warning: symbol 'update_fw' was not declared. Should it be static? drivers/media/i2c/s5c73m3/s5c73m3-core.c:298:5: warning: symbol 's5c73m3_isp_comm_result' was not declared. Should it be static? Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/s5c73m3/s5c73m3-core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index 600909ddb150..b063b4ddf767 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -39,10 +39,10 @@ int s5c73m3_dbg; module_param_named(debug, s5c73m3_dbg, int, 0644); -int boot_from_rom = 1; +static int boot_from_rom = 1; module_param(boot_from_rom, int, 0644); -int update_fw; +static int update_fw; module_param(update_fw, int, 0644); #define S5C73M3_EMBEDDED_DATA_MAXLEN SZ_4K @@ -295,7 +295,8 @@ int s5c73m3_isp_command(struct s5c73m3 *state, u16 command, u16 data) return s5c73m3_write(state, REG_STATUS, 0x0001); } -int s5c73m3_isp_comm_result(struct s5c73m3 *state, u16 command, u16 *data) +static int s5c73m3_isp_comm_result(struct s5c73m3 *state, u16 command, + u16 *data) { return s5c73m3_read(state, COMM_RESULT_OFFSET + command, data); } -- cgit v1.2.3 From 031f515b3d303eca80518ed9a71c79ed420b352a Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 9 Jan 2013 15:09:55 -0300 Subject: [media] s5p-fimc: Avoid null pointer dereference in fimc_capture_ctrls_create() With presence of some faults, e.g. caused by wrong platform data or the device tree structure the IDX_SENSOR entry in the array may be NULL, so make sure it is not dereferenced then. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-capture.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index df6fc6c7eecd..5ec2f48f7db5 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -486,6 +486,7 @@ static struct vb2_ops fimc_capture_qops = { int fimc_capture_ctrls_create(struct fimc_dev *fimc) { struct fimc_vid_cap *vid_cap = &fimc->vid_cap; + struct v4l2_subdev *sensor = fimc->pipeline.subdevs[IDX_SENSOR]; int ret; if (WARN_ON(vid_cap->ctx == NULL)) @@ -494,11 +495,13 @@ int fimc_capture_ctrls_create(struct fimc_dev *fimc) return 0; ret = fimc_ctrls_create(vid_cap->ctx); - if (ret || vid_cap->user_subdev_api || !vid_cap->ctx->ctrls.ready) + + if (ret || vid_cap->user_subdev_api || !sensor || + !vid_cap->ctx->ctrls.ready) return ret; return v4l2_ctrl_add_handler(&vid_cap->ctx->ctrls.handler, - fimc->pipeline.subdevs[IDX_SENSOR]->ctrl_handler, NULL); + sensor->ctrl_handler, NULL); } static int fimc_capture_set_default_format(struct fimc_dev *fimc); -- cgit v1.2.3 From 81619ce1931a1d96e53b455ca2f35757f0c457a5 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 30 Jan 2013 09:54:06 -0300 Subject: [media] s5p-fimc: Set default image format at device open() Make sure a valid image format is initially set on both the CAPTURE and the OUTPUT buffer queue. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-core.c | 20 +---- drivers/media/platform/s5p-fimc/fimc-core.h | 5 +- drivers/media/platform/s5p-fimc/fimc-m2m.c | 131 ++++++++++++++++------------ 3 files changed, 75 insertions(+), 81 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-core.c b/drivers/media/platform/s5p-fimc/fimc-core.c index a9625414ea95..29f7bb71c7e1 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.c +++ b/drivers/media/platform/s5p-fimc/fimc-core.c @@ -525,7 +525,6 @@ static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl) { struct fimc_dev *fimc = ctx->fimc_dev; const struct fimc_variant *variant = fimc->variant; - unsigned int flags = FIMC_DST_FMT | FIMC_SRC_FMT; int ret = 0; if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) @@ -541,8 +540,7 @@ static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl) break; case V4L2_CID_ROTATE: - if (fimc_capture_pending(fimc) || - (ctx->state & flags) == flags) { + if (fimc_capture_pending(fimc)) { ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width, ctx->s_frame.height, ctx->d_frame.width, ctx->d_frame.height, ctrl->val); @@ -709,22 +707,6 @@ void __fimc_get_format(struct fimc_frame *frame, struct v4l2_format *f) } } -void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; - - frame->f_width = pixm->plane_fmt[0].bytesperline; - if (frame->fmt->colplanes == 1) - frame->f_width = (frame->f_width * 8) / frame->fmt->depth[0]; - frame->f_height = pixm->height; - frame->width = pixm->width; - frame->height = pixm->height; - frame->o_width = pixm->width; - frame->o_height = pixm->height; - frame->offs_h = 0; - frame->offs_v = 0; -} - /** * fimc_adjust_mplane_format - adjust bytesperline/sizeimage for each plane * @fmt: fimc pixel format description (input) diff --git a/drivers/media/platform/s5p-fimc/fimc-core.h b/drivers/media/platform/s5p-fimc/fimc-core.h index cf760c364099..412d50708f75 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.h +++ b/drivers/media/platform/s5p-fimc/fimc-core.h @@ -112,9 +112,7 @@ enum fimc_color_fmt { /* The hardware context state. */ #define FIMC_PARAMS (1 << 0) -#define FIMC_SRC_FMT (1 << 3) -#define FIMC_DST_FMT (1 << 4) -#define FIMC_COMPOSE (1 << 5) +#define FIMC_COMPOSE (1 << 1) #define FIMC_CTX_M2M (1 << 16) #define FIMC_CTX_CAP (1 << 17) #define FIMC_CTX_SHUT (1 << 18) @@ -654,7 +652,6 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, struct fimc_frame *frame, struct fimc_addr *paddr); void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f); void fimc_set_yuv_order(struct fimc_ctx *ctx); -void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f); void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf); int fimc_register_m2m_device(struct fimc_dev *fimc, diff --git a/drivers/media/platform/s5p-fimc/fimc-m2m.c b/drivers/media/platform/s5p-fimc/fimc-m2m.c index 1eabd7e74849..f3d535cdd87f 100644 --- a/drivers/media/platform/s5p-fimc/fimc-m2m.c +++ b/drivers/media/platform/s5p-fimc/fimc-m2m.c @@ -1,8 +1,8 @@ /* * Samsung S5P/EXYNOS4 SoC series FIMC (video postprocessor) driver * - * Copyright (C) 2012 Samsung Electronics Co., Ltd. - * Sylwester Nawrocki, + * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published @@ -160,8 +160,7 @@ static void fimc_device_run(void *priv) fimc_hw_set_output_addr(fimc, &df->paddr, -1); fimc_activate_capture(ctx); - ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP | - FIMC_SRC_FMT | FIMC_DST_FMT); + ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP); fimc_hw_activate_input_dma(fimc, true); dma_unlock: @@ -309,8 +308,6 @@ static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f) if (!IS_M2M(f->type)) return -EINVAL; - dbg("w: %d, h: %d", pix->width, pix->height); - fmt = fimc_find_format(&pix->pixelformat, NULL, get_m2m_fmt_flags(f->type), 0); if (WARN(fmt == NULL, "Pixel format lookup failed")) @@ -350,19 +347,39 @@ static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh, struct v4l2_format *f) { struct fimc_ctx *ctx = fh_to_ctx(fh); - return fimc_try_fmt_mplane(ctx, f); } +static void __set_frame_format(struct fimc_frame *frame, struct fimc_fmt *fmt, + struct v4l2_pix_format_mplane *pixm) +{ + int i; + + for (i = 0; i < fmt->colplanes; i++) { + frame->bytesperline[i] = pixm->plane_fmt[i].bytesperline; + frame->payload[i] = pixm->plane_fmt[i].sizeimage; + } + + frame->f_width = pixm->width; + frame->f_height = pixm->height; + frame->o_width = pixm->width; + frame->o_height = pixm->height; + frame->width = pixm->width; + frame->height = pixm->height; + frame->offs_h = 0; + frame->offs_v = 0; + frame->fmt = fmt; +} + static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, struct v4l2_format *f) { struct fimc_ctx *ctx = fh_to_ctx(fh); struct fimc_dev *fimc = ctx->fimc_dev; + struct fimc_fmt *fmt; struct vb2_queue *vq; struct fimc_frame *frame; - struct v4l2_pix_format_mplane *pix; - int i, ret = 0; + int ret; ret = fimc_try_fmt_mplane(ctx, f); if (ret) @@ -380,31 +397,16 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, else frame = &ctx->d_frame; - pix = &f->fmt.pix_mp; - frame->fmt = fimc_find_format(&pix->pixelformat, NULL, - get_m2m_fmt_flags(f->type), 0); - if (!frame->fmt) + fmt = fimc_find_format(&f->fmt.pix_mp.pixelformat, NULL, + get_m2m_fmt_flags(f->type), 0); + if (!fmt) return -EINVAL; + __set_frame_format(frame, fmt, &f->fmt.pix_mp); + /* Update RGB Alpha control state and value range */ fimc_alpha_ctrl_update(ctx); - for (i = 0; i < frame->fmt->colplanes; i++) { - frame->bytesperline[i] = pix->plane_fmt[i].bytesperline; - frame->payload[i] = pix->plane_fmt[i].sizeimage; - } - - fimc_fill_frame(frame, f); - - ctx->scaler.enabled = 1; - - if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - fimc_ctx_state_set(FIMC_PARAMS | FIMC_DST_FMT, ctx); - else - fimc_ctx_state_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx); - - dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height); - return 0; } @@ -412,7 +414,6 @@ static int fimc_m2m_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *reqbufs) { struct fimc_ctx *ctx = fh_to_ctx(fh); - return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); } @@ -420,7 +421,6 @@ static int fimc_m2m_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) { struct fimc_ctx *ctx = fh_to_ctx(fh); - return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); } @@ -428,7 +428,6 @@ static int fimc_m2m_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { struct fimc_ctx *ctx = fh_to_ctx(fh); - return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); } @@ -436,7 +435,6 @@ static int fimc_m2m_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { struct fimc_ctx *ctx = fh_to_ctx(fh); - return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); } @@ -444,7 +442,6 @@ static int fimc_m2m_expbuf(struct file *file, void *fh, struct v4l2_exportbuffer *eb) { struct fimc_ctx *ctx = fh_to_ctx(fh); - return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb); } @@ -453,15 +450,6 @@ static int fimc_m2m_streamon(struct file *file, void *fh, enum v4l2_buf_type type) { struct fimc_ctx *ctx = fh_to_ctx(fh); - - /* The source and target color format need to be set */ - if (V4L2_TYPE_IS_OUTPUT(type)) { - if (!fimc_ctx_state_is_set(FIMC_SRC_FMT, ctx)) - return -EINVAL; - } else if (!fimc_ctx_state_is_set(FIMC_DST_FMT, ctx)) { - return -EINVAL; - } - return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); } @@ -469,7 +457,6 @@ static int fimc_m2m_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) { struct fimc_ctx *ctx = fh_to_ctx(fh); - return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); } @@ -577,20 +564,18 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, const struct v4l2_crop * &ctx->s_frame : &ctx->d_frame; /* Check to see if scaling ratio is within supported range */ - if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) { - if (cr.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - ret = fimc_check_scaler_ratio(ctx, cr.c.width, - cr.c.height, ctx->d_frame.width, - ctx->d_frame.height, ctx->rotation); - } else { - ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width, - ctx->s_frame.height, cr.c.width, - cr.c.height, ctx->rotation); - } - if (ret) { - v4l2_err(&fimc->m2m.vfd, "Out of scaler range\n"); - return -EINVAL; - } + if (cr.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + ret = fimc_check_scaler_ratio(ctx, cr.c.width, + cr.c.height, ctx->d_frame.width, + ctx->d_frame.height, ctx->rotation); + } else { + ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width, + ctx->s_frame.height, cr.c.width, + cr.c.height, ctx->rotation); + } + if (ret) { + v4l2_err(&fimc->m2m.vfd, "Out of scaler range\n"); + return -EINVAL; } f->offs_h = cr.c.left; @@ -653,6 +638,29 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, return vb2_queue_init(dst_vq); } +static int fimc_m2m_set_default_format(struct fimc_ctx *ctx) +{ + struct v4l2_pix_format_mplane pixm = { + .pixelformat = V4L2_PIX_FMT_RGB32, + .width = 800, + .height = 600, + .plane_fmt[0] = { + .bytesperline = 800 * 4, + .sizeimage = 800 * 4 * 600, + }, + }; + struct fimc_fmt *fmt; + + fmt = fimc_find_format(&pixm.pixelformat, NULL, FMT_FLAGS_M2M, 0); + if (!fmt) + return -EINVAL; + + __set_frame_format(&ctx->s_frame, fmt, &pixm); + __set_frame_format(&ctx->d_frame, fmt, &pixm); + + return 0; +} + static int fimc_m2m_open(struct file *file) { struct fimc_dev *fimc = video_drvdata(file); @@ -697,6 +705,7 @@ static int fimc_m2m_open(struct file *file) ctx->flags = 0; ctx->in_path = FIMC_IO_DMA; ctx->out_path = FIMC_IO_DMA; + ctx->scaler.enabled = 1; ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); if (IS_ERR(ctx->m2m_ctx)) { @@ -707,9 +716,15 @@ static int fimc_m2m_open(struct file *file) if (fimc->m2m.refcnt++ == 0) set_bit(ST_M2M_RUN, &fimc->state); + ret = fimc_m2m_set_default_format(ctx); + if (ret < 0) + goto error_m2m_ctx; + mutex_unlock(&fimc->lock); return 0; +error_m2m_ctx: + v4l2_m2m_ctx_release(ctx->m2m_ctx); error_c: fimc_ctrls_delete(ctx); error_fh: -- cgit v1.2.3 From 8b164105d87d1c0101dfbf8d2bacee82c70d91aa Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 30 Jan 2013 09:55:46 -0300 Subject: [media] s5p-fimc: Fix FIMC.n subdev set_selection ioctl handler The V4L2_SEL_TGT_CROP_BOUNDS, V4L2_SEL_TGT_COMPOSE_BOUNDS selection targets are not supposed to be handled in the set_selection ioctl. Remove the code that doesn't do anything sensible now and make sure ctx->state is modified with the spinlock held. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-capture.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index 5ec2f48f7db5..f553cc2a8ee8 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -1688,16 +1688,6 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP); switch (sel->target) { - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - f = &ctx->d_frame; - case V4L2_SEL_TGT_CROP_BOUNDS: - r->width = f->o_width; - r->height = f->o_height; - r->left = 0; - r->top = 0; - mutex_unlock(&fimc->lock); - return 0; - case V4L2_SEL_TGT_CROP: try_sel = v4l2_subdev_get_try_crop(fh, sel->pad); break; @@ -1716,9 +1706,9 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, spin_lock_irqsave(&fimc->slock, flags); set_frame_crop(f, r->left, r->top, r->width, r->height); set_bit(ST_CAPT_APPLY_CFG, &fimc->state); - spin_unlock_irqrestore(&fimc->slock, flags); if (sel->target == V4L2_SEL_TGT_COMPOSE) ctx->state |= FIMC_COMPOSE; + spin_unlock_irqrestore(&fimc->slock, flags); } dbg("target %#x: (%d,%d)/%dx%d", sel->target, r->left, r->top, -- cgit v1.2.3 From 0e23cbbe478809fe8499dab9b2a26bb6154c773b Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 18 Jan 2013 15:34:37 -0300 Subject: [media] s5p-fimc: Add clk_prepare/unprepare for sclk_cam clocks Add clk_prepare(), clk_unprepare() calls for the sclk_cam clocks to ensure the driver works on platforms with the common clocks API enabled. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-mdevice.c | 53 +++++++++++++++++--------- 1 file changed, 36 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 2b0587204868..d940454a0297 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -708,35 +708,54 @@ static int fimc_md_create_links(struct fimc_md *fmd) /* * The peripheral sensor clock management. */ +static void fimc_md_put_clocks(struct fimc_md *fmd) +{ + int i = FIMC_MAX_CAMCLKS; + + while (--i >= 0) { + if (IS_ERR(fmd->camclk[i].clock)) + continue; + clk_unprepare(fmd->camclk[i].clock); + clk_put(fmd->camclk[i].clock); + fmd->camclk[i].clock = ERR_PTR(-EINVAL); + } +} + static int fimc_md_get_clocks(struct fimc_md *fmd) { + struct device *dev = NULL; char clk_name[32]; struct clk *clock; - int i; + int ret, i; + + for (i = 0; i < FIMC_MAX_CAMCLKS; i++) + fmd->camclk[i].clock = ERR_PTR(-EINVAL); + + if (fmd->pdev->dev.of_node) + dev = &fmd->pdev->dev; for (i = 0; i < FIMC_MAX_CAMCLKS; i++) { snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i); - clock = clk_get(NULL, clk_name); + clock = clk_get(dev, clk_name); + if (IS_ERR(clock)) { - v4l2_err(&fmd->v4l2_dev, "Failed to get clock: %s", - clk_name); - return -ENXIO; + dev_err(&fmd->pdev->dev, "Failed to get clock: %s\n", + clk_name); + ret = PTR_ERR(clock); + break; + } + ret = clk_prepare(clock); + if (ret < 0) { + clk_put(clock); + fmd->camclk[i].clock = ERR_PTR(-EINVAL); + break; } fmd->camclk[i].clock = clock; } - return 0; -} - -static void fimc_md_put_clocks(struct fimc_md *fmd) -{ - int i = FIMC_MAX_CAMCLKS; + if (ret) + fimc_md_put_clocks(fmd); - while (--i >= 0) { - if (IS_ERR_OR_NULL(fmd->camclk[i].clock)) - continue; - clk_put(fmd->camclk[i].clock); - fmd->camclk[i].clock = NULL; - } + return ret; } static int __fimc_md_set_camclk(struct fimc_md *fmd, -- cgit v1.2.3 From 33fba5de1e2318d301b84089e6468dedec9ad381 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 31 Jan 2013 01:12:46 -0300 Subject: [media] s5c73m3: Use devm_regulator_bulk_get API devm_regulator_bulk_get saves some cleanup and exit code. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/s5c73m3/s5c73m3-core.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index b063b4ddf767..c143c9ec7ba9 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -1627,7 +1627,7 @@ static int __devinit s5c73m3_probe(struct i2c_client *client, for (i = 0; i < S5C73M3_MAX_SUPPLIES; i++) state->supplies[i].supply = s5c73m3_supply_names[i]; - ret = regulator_bulk_get(dev, S5C73M3_MAX_SUPPLIES, + ret = devm_regulator_bulk_get(dev, S5C73M3_MAX_SUPPLIES, state->supplies); if (ret) { dev_err(dev, "failed to get regulators\n"); @@ -1636,7 +1636,7 @@ static int __devinit s5c73m3_probe(struct i2c_client *client, ret = s5c73m3_init_controls(state); if (ret) - goto out_err3; + goto out_err2; state->sensor_pix_size[RES_ISP] = &s5c73m3_isp_resolutions[1]; state->sensor_pix_size[RES_JPEG] = &s5c73m3_jpeg_resolutions[1]; @@ -1652,15 +1652,13 @@ static int __devinit s5c73m3_probe(struct i2c_client *client, ret = s5c73m3_register_spi_driver(state); if (ret < 0) - goto out_err3; + goto out_err2; state->i2c_client = client; v4l2_info(sd, "%s: completed succesfully\n", __func__); return 0; -out_err3: - regulator_bulk_free(S5C73M3_MAX_SUPPLIES, state->supplies); out_err2: s5c73m3_free_gpios(state); out_err1: @@ -1679,7 +1677,6 @@ static int __devexit s5c73m3_remove(struct i2c_client *client) media_entity_cleanup(&sd->entity); s5c73m3_unregister_spi_driver(state); - regulator_bulk_free(S5C73M3_MAX_SUPPLIES, state->supplies); s5c73m3_free_gpios(state); return 0; -- cgit v1.2.3 From 56bc911ac3b94c731db3a6de20258827f1a61c20 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 1 Feb 2013 15:00:40 -0300 Subject: [media] s5p-fimc: Redefine platform data structure for fimc-is Newer Exynos4 SoC are equipped with a local camera ISP that controls external raw image sensor directly. Such sensors can be connected through FIMC-LITEn (and MIPI-CSISn) IPs to the ISP, which then feeds image data to the FIMCn IP. Thus there can be two busses associated with an image source (sensor). Rename struct s5p_fimc_isp_info describing external image sensor (video decoder) to struct fimc_source_info to avoid confusion. bus_type is split into fimc_bus_type and sensor_bus_type. The bus type enumeration is extended to include both FIMC Writeback input types. The bus_type enumeration and the data structure name in the board files are modified according to the above changes. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Kukjin Kim Signed-off-by: Mauro Carvalho Chehab --- arch/arm/mach-exynos/mach-nuri.c | 8 ++-- arch/arm/mach-exynos/mach-universal_c210.c | 8 ++-- arch/arm/mach-s5pv210/mach-goni.c | 6 +-- drivers/media/platform/s5p-fimc/fimc-lite-reg.c | 8 ++-- drivers/media/platform/s5p-fimc/fimc-lite-reg.h | 4 +- drivers/media/platform/s5p-fimc/fimc-mdevice.c | 16 ++++---- drivers/media/platform/s5p-fimc/fimc-mdevice.h | 2 +- drivers/media/platform/s5p-fimc/fimc-reg.c | 34 +++++++++-------- drivers/media/platform/s5p-fimc/fimc-reg.h | 6 +-- include/media/s5p_fimc.h | 49 ++++++++++++++++--------- 10 files changed, 79 insertions(+), 62 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-exynos/mach-nuri.c b/arch/arm/mach-exynos/mach-nuri.c index 27d4ed8b116e..7c2600e09a91 100644 --- a/arch/arm/mach-exynos/mach-nuri.c +++ b/arch/arm/mach-exynos/mach-nuri.c @@ -1209,25 +1209,25 @@ static struct i2c_board_info m5mols_board_info = { .platform_data = &m5mols_platdata, }; -static struct s5p_fimc_isp_info nuri_camera_sensors[] = { +static struct fimc_source_info nuri_camera_sensors[] = { { .flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_VSYNC_ACTIVE_LOW, - .bus_type = FIMC_ITU_601, + .fimc_bus_type = FIMC_BUS_TYPE_ITU_601, .board_info = &s5k6aa_board_info, .clk_frequency = 24000000UL, .i2c_bus_num = 6, }, { .flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_VSYNC_ACTIVE_LOW, - .bus_type = FIMC_MIPI_CSI2, + .fimc_bus_type = FIMC_BUS_TYPE_MIPI_CSI2, .board_info = &m5mols_board_info, .clk_frequency = 24000000UL, }, }; static struct s5p_platform_fimc fimc_md_platdata = { - .isp_info = nuri_camera_sensors, + .source_info = nuri_camera_sensors, .num_clients = ARRAY_SIZE(nuri_camera_sensors), }; diff --git a/arch/arm/mach-exynos/mach-universal_c210.c b/arch/arm/mach-exynos/mach-universal_c210.c index 9e3340f18950..c09290a8fa2e 100644 --- a/arch/arm/mach-exynos/mach-universal_c210.c +++ b/arch/arm/mach-exynos/mach-universal_c210.c @@ -988,12 +988,12 @@ static struct i2c_board_info m5mols_board_info = { .platform_data = &m5mols_platdata, }; -static struct s5p_fimc_isp_info universal_camera_sensors[] = { +static struct fimc_source_info universal_camera_sensors[] = { { .mux_id = 0, .flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_VSYNC_ACTIVE_LOW, - .bus_type = FIMC_ITU_601, + .fimc_bus_type = FIMC_BUS_TYPE_ITU_601, .board_info = &s5k6aa_board_info, .i2c_bus_num = 0, .clk_frequency = 24000000UL, @@ -1001,7 +1001,7 @@ static struct s5p_fimc_isp_info universal_camera_sensors[] = { .mux_id = 0, .flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_VSYNC_ACTIVE_LOW, - .bus_type = FIMC_MIPI_CSI2, + .fimc_bus_type = FIMC_BUS_TYPE_MIPI_CSI2, .board_info = &m5mols_board_info, .i2c_bus_num = 0, .clk_frequency = 24000000UL, @@ -1009,7 +1009,7 @@ static struct s5p_fimc_isp_info universal_camera_sensors[] = { }; static struct s5p_platform_fimc fimc_md_platdata = { - .isp_info = universal_camera_sensors, + .source_info = universal_camera_sensors, .num_clients = ARRAY_SIZE(universal_camera_sensors), }; diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c index c72b31078c99..423f6b6e8dbe 100644 --- a/arch/arm/mach-s5pv210/mach-goni.c +++ b/arch/arm/mach-s5pv210/mach-goni.c @@ -841,12 +841,12 @@ static struct i2c_board_info noon010pc30_board_info = { .platform_data = &noon010pc30_pldata, }; -static struct s5p_fimc_isp_info goni_camera_sensors[] = { +static struct fimc_source_info goni_camera_sensors[] = { { .mux_id = 0, .flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_VSYNC_ACTIVE_LOW, - .bus_type = FIMC_ITU_601, + .bus_type = FIMC_BUS_TYPE_ITU_601, .board_info = &noon010pc30_board_info, .i2c_bus_num = 0, .clk_frequency = 16000000UL, @@ -854,7 +854,7 @@ static struct s5p_fimc_isp_info goni_camera_sensors[] = { }; static struct s5p_platform_fimc goni_fimc_md_platdata __initdata = { - .isp_info = goni_camera_sensors, + .source_info = goni_camera_sensors, .num_clients = ARRAY_SIZE(goni_camera_sensors), }; diff --git a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c b/drivers/media/platform/s5p-fimc/fimc-lite-reg.c index 962652da3b43..f0af0754a7b4 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite-reg.c @@ -187,12 +187,12 @@ static void flite_hw_set_camera_port(struct fimc_lite *dev, int id) /* Select serial or parallel bus, camera port (A,B) and set signals polarity */ void flite_hw_set_camera_bus(struct fimc_lite *dev, - struct s5p_fimc_isp_info *s_info) + struct fimc_source_info *si) { u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL); - unsigned int flags = s_info->flags; + unsigned int flags = si->flags; - if (s_info->bus_type != FIMC_MIPI_CSI2) { + if (si->sensor_bus_type != FIMC_BUS_TYPE_MIPI_CSI2) { cfg &= ~(FLITE_REG_CIGCTRL_SELCAM_MIPI | FLITE_REG_CIGCTRL_INVPOLPCLK | FLITE_REG_CIGCTRL_INVPOLVSYNC | @@ -212,7 +212,7 @@ void flite_hw_set_camera_bus(struct fimc_lite *dev, writel(cfg, dev->regs + FLITE_REG_CIGCTRL); - flite_hw_set_camera_port(dev, s_info->mux_id); + flite_hw_set_camera_port(dev, si->mux_id); } static void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f) diff --git a/drivers/media/platform/s5p-fimc/fimc-lite-reg.h b/drivers/media/platform/s5p-fimc/fimc-lite-reg.h index adb9e9e6f3c2..0e345844c13a 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite-reg.h +++ b/drivers/media/platform/s5p-fimc/fimc-lite-reg.h @@ -131,9 +131,9 @@ void flite_hw_set_interrupt_mask(struct fimc_lite *dev); void flite_hw_capture_start(struct fimc_lite *dev); void flite_hw_capture_stop(struct fimc_lite *dev); void flite_hw_set_camera_bus(struct fimc_lite *dev, - struct s5p_fimc_isp_info *s_info); + struct fimc_source_info *s_info); void flite_hw_set_camera_polarity(struct fimc_lite *dev, - struct s5p_fimc_isp_info *cam); + struct fimc_source_info *cam); void flite_hw_set_window_offset(struct fimc_lite *dev, struct flite_frame *f); void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f); diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index d940454a0297..f49f6f17a3f7 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -290,7 +290,7 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) for (i = 0; i < num_clients; i++) { struct v4l2_subdev *sd; - fmd->sensor[i].pdata = pdata->isp_info[i]; + fmd->sensor[i].pdata = pdata->source_info[i]; ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true); if (ret) break; @@ -504,7 +504,7 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, struct v4l2_subdev *sensor, int pad, int link_mask) { - struct fimc_sensor_info *s_info; + struct fimc_sensor_info *s_info = NULL; struct media_entity *sink; unsigned int flags = 0; int ret, i; @@ -614,7 +614,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) { struct v4l2_subdev *csi_sensors[CSIS_MAX_ENTITIES] = { NULL }; struct v4l2_subdev *sensor, *csis; - struct s5p_fimc_isp_info *pdata; + struct fimc_source_info *pdata; struct fimc_sensor_info *s_info; struct media_entity *source, *sink; int i, pad, fimc_id = 0, ret = 0; @@ -632,8 +632,8 @@ static int fimc_md_create_links(struct fimc_md *fmd) source = NULL; pdata = &s_info->pdata; - switch (pdata->bus_type) { - case FIMC_MIPI_CSI2: + switch (pdata->sensor_bus_type) { + case FIMC_BUS_TYPE_MIPI_CSI2: if (WARN(pdata->mux_id >= CSIS_MAX_ENTITIES, "Wrong CSI channel id: %d\n", pdata->mux_id)) return -EINVAL; @@ -659,14 +659,14 @@ static int fimc_md_create_links(struct fimc_md *fmd) csi_sensors[pdata->mux_id] = sensor; break; - case FIMC_ITU_601...FIMC_ITU_656: + case FIMC_BUS_TYPE_ITU_601...FIMC_BUS_TYPE_ITU_656: source = &sensor->entity; pad = 0; break; default: v4l2_err(&fmd->v4l2_dev, "Wrong bus_type: %x\n", - pdata->bus_type); + pdata->sensor_bus_type); return -EINVAL; } if (source == NULL) @@ -762,7 +762,7 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, struct fimc_sensor_info *s_info, bool on) { - struct s5p_fimc_isp_info *pdata = &s_info->pdata; + struct fimc_source_info *pdata = &s_info->pdata; struct fimc_camclk_info *camclk; int ret = 0; diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.h b/drivers/media/platform/s5p-fimc/fimc-mdevice.h index da7d9922cf58..06b0d8276fd2 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.h +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.h @@ -53,7 +53,7 @@ struct fimc_camclk_info { * This data structure applies to image sensor and the writeback subdevs. */ struct fimc_sensor_info { - struct s5p_fimc_isp_info pdata; + struct fimc_source_info pdata; struct v4l2_subdev *subdev; struct fimc_dev *host; }; diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.c b/drivers/media/platform/s5p-fimc/fimc-reg.c index c05d0444192f..50b97c75b956 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.c +++ b/drivers/media/platform/s5p-fimc/fimc-reg.c @@ -554,7 +554,7 @@ void fimc_hw_set_output_addr(struct fimc_dev *dev, } int fimc_hw_set_camera_polarity(struct fimc_dev *fimc, - struct s5p_fimc_isp_info *cam) + struct fimc_source_info *cam) { u32 cfg = readl(fimc->regs + FIMC_REG_CIGCTRL); @@ -596,14 +596,15 @@ static const struct mbus_pixfmt_desc pix_desc[] = { }; int fimc_hw_set_camera_source(struct fimc_dev *fimc, - struct s5p_fimc_isp_info *cam) + struct fimc_source_info *source) { struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame; - u32 cfg = 0; - u32 bus_width; + u32 bus_width, cfg = 0; int i; - if (cam->bus_type == FIMC_ITU_601 || cam->bus_type == FIMC_ITU_656) { + switch (source->fimc_bus_type) { + case FIMC_BUS_TYPE_ITU_601: + case FIMC_BUS_TYPE_ITU_656: for (i = 0; i < ARRAY_SIZE(pix_desc); i++) { if (fimc->vid_cap.mf.code == pix_desc[i].pixelcode) { cfg = pix_desc[i].cisrcfmt; @@ -619,15 +620,17 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc, return -EINVAL; } - if (cam->bus_type == FIMC_ITU_601) { + if (source->fimc_bus_type == FIMC_BUS_TYPE_ITU_601) { if (bus_width == 8) cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT; else if (bus_width == 16) cfg |= FIMC_REG_CISRCFMT_ITU601_16BIT; } /* else defaults to ITU-R BT.656 8-bit */ - } else if (cam->bus_type == FIMC_MIPI_CSI2) { + break; + case FIMC_BUS_TYPE_MIPI_CSI2: if (fimc_fmt_is_user_defined(f->fmt->color)) cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT; + break; } cfg |= (f->o_width << 16) | f->o_height; @@ -655,7 +658,7 @@ void fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f) } int fimc_hw_set_camera_type(struct fimc_dev *fimc, - struct s5p_fimc_isp_info *cam) + struct fimc_source_info *source) { u32 cfg, tmp; struct fimc_vid_cap *vid_cap = &fimc->vid_cap; @@ -668,11 +671,11 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, FIMC_REG_CIGCTRL_SELCAM_MIPI | FIMC_REG_CIGCTRL_CAMIF_SELWB | FIMC_REG_CIGCTRL_SELCAM_MIPI_A | FIMC_REG_CIGCTRL_CAM_JPEG); - switch (cam->bus_type) { - case FIMC_MIPI_CSI2: + switch (source->fimc_bus_type) { + case FIMC_BUS_TYPE_MIPI_CSI2: cfg |= FIMC_REG_CIGCTRL_SELCAM_MIPI; - if (cam->mux_id == 0) + if (source->mux_id == 0) cfg |= FIMC_REG_CIGCTRL_SELCAM_MIPI_A; /* TODO: add remaining supported formats. */ @@ -695,15 +698,16 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, writel(tmp, fimc->regs + FIMC_REG_CSIIMGFMT); break; - case FIMC_ITU_601...FIMC_ITU_656: - if (cam->mux_id == 0) /* ITU-A, ITU-B: 0, 1 */ + case FIMC_BUS_TYPE_ITU_601...FIMC_BUS_TYPE_ITU_656: + if (source->mux_id == 0) /* ITU-A, ITU-B: 0, 1 */ cfg |= FIMC_REG_CIGCTRL_SELCAM_ITU_A; break; - case FIMC_LCD_WB: + case FIMC_BUS_TYPE_LCD_WRITEBACK_A: cfg |= FIMC_REG_CIGCTRL_CAMIF_SELWB; break; default: - v4l2_err(&vid_cap->vfd, "Invalid camera bus type selected\n"); + v4l2_err(&vid_cap->vfd, "Invalid FIMC bus type selected: %d\n", + source->fimc_bus_type); return -EINVAL; } writel(cfg, fimc->regs + FIMC_REG_CIGCTRL); diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.h b/drivers/media/platform/s5p-fimc/fimc-reg.h index f3e0b78a3736..1a40df6d1a80 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.h +++ b/drivers/media/platform/s5p-fimc/fimc-reg.h @@ -297,12 +297,12 @@ void fimc_hw_set_input_addr(struct fimc_dev *fimc, struct fimc_addr *paddr); void fimc_hw_set_output_addr(struct fimc_dev *fimc, struct fimc_addr *paddr, int index); int fimc_hw_set_camera_source(struct fimc_dev *fimc, - struct s5p_fimc_isp_info *cam); + struct fimc_source_info *cam); void fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f); int fimc_hw_set_camera_polarity(struct fimc_dev *fimc, - struct s5p_fimc_isp_info *cam); + struct fimc_source_info *cam); int fimc_hw_set_camera_type(struct fimc_dev *fimc, - struct s5p_fimc_isp_info *cam); + struct fimc_source_info *cam); void fimc_hw_clear_irq(struct fimc_dev *dev); void fimc_hw_enable_scaler(struct fimc_dev *dev, bool on); void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on); diff --git a/include/media/s5p_fimc.h b/include/media/s5p_fimc.h index eaea62a382f8..28f3590aa031 100644 --- a/include/media/s5p_fimc.h +++ b/include/media/s5p_fimc.h @@ -1,8 +1,8 @@ /* - * Samsung S5P SoC camera interface driver header + * Samsung S5P/Exynos4 SoC series camera interface driver header * - * Copyright (c) 2010 Samsung Electronics Co., Ltd - * Author: Sylwester Nawrocki, + * Copyright (C) 2010 - 2013 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki * * 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 @@ -14,45 +14,58 @@ #include -enum cam_bus_type { - FIMC_ITU_601 = 1, - FIMC_ITU_656, - FIMC_MIPI_CSI2, - FIMC_LCD_WB, /* FIFO link from LCD mixer */ +/* + * Enumeration of the FIMC data bus types. + */ +enum fimc_bus_type { + /* Camera parallel bus */ + FIMC_BUS_TYPE_ITU_601 = 1, + /* Camera parallel bus with embedded synchronization */ + FIMC_BUS_TYPE_ITU_656, + /* Camera MIPI-CSI2 serial bus */ + FIMC_BUS_TYPE_MIPI_CSI2, + /* FIFO link from LCD controller (WriteBack A) */ + FIMC_BUS_TYPE_LCD_WRITEBACK_A, + /* FIFO link from LCD controller (WriteBack B) */ + FIMC_BUS_TYPE_LCD_WRITEBACK_B, + /* FIFO link from FIMC-IS */ + FIMC_BUS_TYPE_ISP_WRITEBACK = FIMC_BUS_TYPE_LCD_WRITEBACK_B, }; struct i2c_board_info; /** - * struct s5p_fimc_isp_info - image sensor information required for host - * interace configuration. + * struct fimc_source_info - video source description required for the host + * interface configuration * * @board_info: pointer to I2C subdevice's board info * @clk_frequency: frequency of the clock the host interface provides to sensor - * @bus_type: determines bus type, MIPI, ITU-R BT.601 etc. + * @fimc_bus_type: FIMC camera input type + * @sensor_bus_type: image sensor bus type, MIPI, ITU-R BT.601 etc. + * @flags: the parallel sensor bus flags defining signals polarity (V4L2_MBUS_*) * @i2c_bus_num: i2c control bus id the sensor is attached to * @mux_id: FIMC camera interface multiplexer index (separate for MIPI and ITU) * @clk_id: index of the SoC peripheral clock for sensors - * @flags: the parallel bus flags defining signals polarity (V4L2_MBUS_*) */ -struct s5p_fimc_isp_info { +struct fimc_source_info { struct i2c_board_info *board_info; unsigned long clk_frequency; - enum cam_bus_type bus_type; + enum fimc_bus_type fimc_bus_type; + enum fimc_bus_type sensor_bus_type; + u16 flags; u16 i2c_bus_num; u16 mux_id; - u16 flags; u8 clk_id; }; /** * struct s5p_platform_fimc - camera host interface platform data * - * @isp_info: properties of camera sensor required for host interface setup - * @num_clients: the number of attached image sensors + * @source_info: properties of an image source for the host interface setup + * @num_clients: the number of attached image sources */ struct s5p_platform_fimc { - struct s5p_fimc_isp_info *isp_info; + struct fimc_source_info *source_info; int num_clients; }; -- cgit v1.2.3 From ea01a83d5c88a3e0bb124ec4b3abf3aefcf0d719 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Sep 2012 05:42:15 -0300 Subject: [media] mt9v011: convert to the control framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans Verkuil Tested-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9v011.c | 223 +++++++++++++------------------------------- 1 file changed, 67 insertions(+), 156 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c index 6bf01ad62765..73b7688cbebd 100644 --- a/drivers/media/i2c/mt9v011.c +++ b/drivers/media/i2c/mt9v011.c @@ -13,6 +13,7 @@ #include #include #include +#include #include MODULE_DESCRIPTION("Micron mt9v011 sensor driver"); @@ -48,68 +49,9 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)"); #define MT9V011_VERSION 0x8232 #define MT9V011_REV_B_VERSION 0x8243 -/* supported controls */ -static struct v4l2_queryctrl mt9v011_qctrl[] = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = (1 << 12) - 1 - 0x0020, - .step = 1, - .default_value = 0x0020, - .flags = 0, - }, { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0, - .maximum = 2047, - .step = 1, - .default_value = 0x01fc, - .flags = 0, - }, { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = -1 << 9, - .maximum = (1 << 9) - 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = -1 << 9, - .maximum = (1 << 9) - 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Vflip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, { - } -}; - struct mt9v011 { struct v4l2_subdev sd; + struct v4l2_ctrl_handler ctrls; unsigned width, height; unsigned xtal; unsigned hflip:1; @@ -380,99 +322,6 @@ static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) set_res(sd); set_read_mode(sd); - return 0; -}; - -static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct mt9v011 *core = to_mt9v011(sd); - - v4l2_dbg(1, debug, sd, "g_ctrl called\n"); - - switch (ctrl->id) { - case V4L2_CID_GAIN: - ctrl->value = core->global_gain; - return 0; - case V4L2_CID_EXPOSURE: - ctrl->value = core->exposure; - return 0; - case V4L2_CID_RED_BALANCE: - ctrl->value = core->red_bal; - return 0; - case V4L2_CID_BLUE_BALANCE: - ctrl->value = core->blue_bal; - return 0; - case V4L2_CID_HFLIP: - ctrl->value = core->hflip ? 1 : 0; - return 0; - case V4L2_CID_VFLIP: - ctrl->value = core->vflip ? 1 : 0; - return 0; - } - return -EINVAL; -} - -static int mt9v011_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - int i; - - v4l2_dbg(1, debug, sd, "queryctrl called\n"); - - for (i = 0; i < ARRAY_SIZE(mt9v011_qctrl); i++) - if (qc->id && qc->id == mt9v011_qctrl[i].id) { - memcpy(qc, &(mt9v011_qctrl[i]), - sizeof(*qc)); - return 0; - } - - return -EINVAL; -} - - -static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct mt9v011 *core = to_mt9v011(sd); - u8 i, n; - n = ARRAY_SIZE(mt9v011_qctrl); - - for (i = 0; i < n; i++) { - if (ctrl->id != mt9v011_qctrl[i].id) - continue; - if (ctrl->value < mt9v011_qctrl[i].minimum || - ctrl->value > mt9v011_qctrl[i].maximum) - return -ERANGE; - v4l2_dbg(1, debug, sd, "s_ctrl: id=%d, value=%d\n", - ctrl->id, ctrl->value); - break; - } - - switch (ctrl->id) { - case V4L2_CID_GAIN: - core->global_gain = ctrl->value; - break; - case V4L2_CID_EXPOSURE: - core->exposure = ctrl->value; - break; - case V4L2_CID_RED_BALANCE: - core->red_bal = ctrl->value; - break; - case V4L2_CID_BLUE_BALANCE: - core->blue_bal = ctrl->value; - break; - case V4L2_CID_HFLIP: - core->hflip = ctrl->value; - set_read_mode(sd); - return 0; - case V4L2_CID_VFLIP: - core->vflip = ctrl->value; - set_read_mode(sd); - return 0; - default: - return -EINVAL; - } - - set_balance(sd); - return 0; } @@ -599,10 +448,46 @@ static int mt9v011_g_chip_ident(struct v4l2_subdev *sd, version); } -static const struct v4l2_subdev_core_ops mt9v011_core_ops = { - .queryctrl = mt9v011_queryctrl, - .g_ctrl = mt9v011_g_ctrl, +static int mt9v011_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9v011 *core = + container_of(ctrl->handler, struct mt9v011, ctrls); + struct v4l2_subdev *sd = &core->sd; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + core->global_gain = ctrl->val; + break; + case V4L2_CID_EXPOSURE: + core->exposure = ctrl->val; + break; + case V4L2_CID_RED_BALANCE: + core->red_bal = ctrl->val; + break; + case V4L2_CID_BLUE_BALANCE: + core->blue_bal = ctrl->val; + break; + case V4L2_CID_HFLIP: + core->hflip = ctrl->val; + set_read_mode(sd); + return 0; + case V4L2_CID_VFLIP: + core->vflip = ctrl->val; + set_read_mode(sd); + return 0; + default: + return -EINVAL; + } + + set_balance(sd); + return 0; +} + +static struct v4l2_ctrl_ops mt9v011_ctrl_ops = { .s_ctrl = mt9v011_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops mt9v011_core_ops = { .reset = mt9v011_reset, .g_chip_ident = mt9v011_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -658,6 +543,30 @@ static int mt9v011_probe(struct i2c_client *c, return -EINVAL; } + v4l2_ctrl_handler_init(&core->ctrls, 5); + v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops, + V4L2_CID_GAIN, 0, (1 << 12) - 1 - 0x20, 1, 0x20); + v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 2047, 1, 0x01fc); + v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops, + V4L2_CID_RED_BALANCE, -(1 << 9), (1 << 9) - 1, 1, 0); + v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops, + V4L2_CID_BLUE_BALANCE, -(1 << 9), (1 << 9) - 1, 1, 0); + v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + if (core->ctrls.error) { + int ret = core->ctrls.error; + + v4l2_err(sd, "control initialization error %d\n", ret); + v4l2_ctrl_handler_free(&core->ctrls); + kfree(core); + return ret; + } + core->sd.ctrl_handler = &core->ctrls; + core->global_gain = 0x0024; core->exposure = 0x01fc; core->width = 640; @@ -681,12 +590,14 @@ static int mt9v011_probe(struct i2c_client *c, static int mt9v011_remove(struct i2c_client *c) { struct v4l2_subdev *sd = i2c_get_clientdata(c); + struct mt9v011 *core = to_mt9v011(sd); v4l2_dbg(1, debug, sd, "mt9v011.c: removing mt9v011 adapter on address 0x%x\n", c->addr << 1); v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&core->ctrls); kfree(to_mt9v011(sd)); return 0; } -- cgit v1.2.3 From a346caacf31907085e8425bdab7cf5147545439e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 2 Feb 2013 05:57:37 -0300 Subject: [media] tvaudio: fix broken volume/balance calculations The balance control did not do what it is supposed to do due to wrong calculations. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvaudio.c | 65 +++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index 3b24d3fc1866..d5cd2eb95558 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -93,8 +93,8 @@ struct CHIPDESC { /* which register has which value */ int leftreg,rightreg,treblereg,bassreg; - /* initialize with (defaults to 65535/65535/32768/32768 */ - int leftinit,rightinit,trebleinit,bassinit; + /* initialize with (defaults to 65535/32768/32768 */ + int volinit, trebleinit, bassinit; /* functions to convert the values (v4l -> chip) */ getvalue volfunc,treblefunc,bassfunc; @@ -122,7 +122,7 @@ struct CHIPSTATE { audiocmd shadow; /* current settings */ - __u16 left, right, treble, bass, muted; + u16 volume, balance, treble, bass, muted; int prevmode; int radio; int input; @@ -1523,8 +1523,7 @@ static struct CHIPDESC chiplist[] = { .rightreg = TDA9875_MVR, .bassreg = TDA9875_MBA, .treblereg = TDA9875_MTR, - .leftinit = 58880, - .rightinit = 58880, + .volinit = 58880, }, { .name = "tda9850", @@ -1694,20 +1693,13 @@ static int tvaudio_g_ctrl(struct v4l2_subdev *sd, case V4L2_CID_AUDIO_VOLUME: if (!(desc->flags & CHIP_HAS_VOLUME)) break; - ctrl->value = max(chip->left,chip->right); + ctrl->value = chip->volume; return 0; case V4L2_CID_AUDIO_BALANCE: - { - int volume; if (!(desc->flags & CHIP_HAS_VOLUME)) break; - volume = max(chip->left,chip->right); - if (volume) - ctrl->value=(32768*min(chip->left,chip->right))/volume; - else - ctrl->value=32768; + ctrl->value = chip->balance; return 0; - } case V4L2_CID_AUDIO_BASS: if (!(desc->flags & CHIP_HAS_BASSTREBLE)) break; @@ -1744,41 +1736,38 @@ static int tvaudio_s_ctrl(struct v4l2_subdev *sd, return 0; case V4L2_CID_AUDIO_VOLUME: { - int volume,balance; + u32 volume, balance; + u32 left, right; if (!(desc->flags & CHIP_HAS_VOLUME)) break; - volume = max(chip->left,chip->right); - if (volume) - balance=(32768*min(chip->left,chip->right))/volume; - else - balance=32768; - - volume=ctrl->value; - chip->left = (min(65536 - balance,32768) * volume) / 32768; - chip->right = (min(balance,volume *(__u16)32768)) / 32768; - - chip_write(chip,desc->leftreg,desc->volfunc(chip->left)); - chip_write(chip,desc->rightreg,desc->volfunc(chip->right)); + volume = ctrl->value; + chip->volume = volume; + balance = chip->balance; + left = (min(65536U - balance, 32768U) * volume) / 32768U; + right = (min(balance, 32768U) * volume) / 32768U; + chip_write(chip, desc->leftreg, desc->volfunc(left)); + chip_write(chip, desc->rightreg, desc->volfunc(right)); return 0; } case V4L2_CID_AUDIO_BALANCE: { - int volume, balance; + u32 volume, balance; + u32 left, right; if (!(desc->flags & CHIP_HAS_VOLUME)) break; - volume = max(chip->left, chip->right); balance = ctrl->value; - chip->left = (min(65536 - balance, 32768) * volume) / 32768; - chip->right = (min(balance, volume * (__u16)32768)) / 32768; - - chip_write(chip, desc->leftreg, desc->volfunc(chip->left)); - chip_write(chip, desc->rightreg, desc->volfunc(chip->right)); + chip->balance = balance; + volume = chip->volume; + left = (min(65536U - balance, 32768U) * volume) / 32768U; + right = (min(balance, 32768U) * volume) / 32768U; + chip_write(chip, desc->leftreg, desc->volfunc(left)); + chip_write(chip, desc->rightreg, desc->volfunc(right)); return 0; } case V4L2_CID_AUDIO_BASS: @@ -2043,12 +2032,12 @@ static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id * v4l2_info(sd, "volume callback undefined!\n"); desc->flags &= ~CHIP_HAS_VOLUME; } else { - chip->left = desc->leftinit ? desc->leftinit : 65535; - chip->right = desc->rightinit ? desc->rightinit : 65535; + chip->volume = desc->volinit ? desc->volinit : 65535; + chip->balance = 32768; chip_write(chip, desc->leftreg, - desc->volfunc(chip->left)); + desc->volfunc(chip->volume)); chip_write(chip, desc->rightreg, - desc->volfunc(chip->right)); + desc->volfunc(chip->volume)); } } if (desc->flags & CHIP_HAS_BASSTREBLE) { -- cgit v1.2.3 From 71df09bc67f08844a8af4f48966d3bb550a0fb59 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 10 Sep 2012 11:06:57 -0300 Subject: [media] tvaudio: fix two tea6420 errors The inputmask for the tea6420 wasn't set and the wrong mute register value was used. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvaudio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index d5cd2eb95558..df6aaa5fbdec 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -1617,7 +1617,8 @@ static struct CHIPDESC chiplist[] = { .inputreg = -1, .inputmap = { TEA6420_S_SA, TEA6420_S_SB, TEA6420_S_SC }, - .inputmute = TEA6300_S_GMU, + .inputmute = TEA6420_S_GMU, + .inputmask = 0x07, }, { .name = "tda8425", -- cgit v1.2.3 From c9114031d88bb71659d7f0cc74ecf8ddea47e1b7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 2 Feb 2013 06:03:36 -0300 Subject: [media] tvaudio: convert to the control framework Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvaudio.c | 206 +++++++++++++++++--------------------------- 1 file changed, 77 insertions(+), 129 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index df6aaa5fbdec..e3b33b78dd21 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -39,6 +39,7 @@ #include #include #include +#include #include @@ -91,13 +92,13 @@ struct CHIPDESC { audiocmd init; /* which register has which value */ - int leftreg,rightreg,treblereg,bassreg; + int leftreg, rightreg, treblereg, bassreg; /* initialize with (defaults to 65535/32768/32768 */ int volinit, trebleinit, bassinit; /* functions to convert the values (v4l -> chip) */ - getvalue volfunc,treblefunc,bassfunc; + getvalue volfunc, treblefunc, bassfunc; /* get/set mode */ getrxsubchans getrxsubchans; @@ -113,6 +114,12 @@ struct CHIPDESC { /* current state of the chip */ struct CHIPSTATE { struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct { + /* volume/balance cluster */ + struct v4l2_ctrl *volume; + struct v4l2_ctrl *balance; + }; /* chip-specific description - should point to an entry at CHIPDESC table */ @@ -122,7 +129,7 @@ struct CHIPSTATE { audiocmd shadow; /* current settings */ - u16 volume, balance, treble, bass, muted; + u16 muted; int prevmode; int radio; int input; @@ -138,6 +145,11 @@ static inline struct CHIPSTATE *to_state(struct v4l2_subdev *sd) return container_of(sd, struct CHIPSTATE, sd); } +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct CHIPSTATE, hdl)->sd; +} + /* ---------------------------------------------------------------------- */ /* i2c I/O functions */ @@ -1679,91 +1691,27 @@ static struct CHIPDESC chiplist[] = { /* ---------------------------------------------------------------------- */ -static int tvaudio_g_ctrl(struct v4l2_subdev *sd, - struct v4l2_control *ctrl) -{ - struct CHIPSTATE *chip = to_state(sd); - struct CHIPDESC *desc = chip->desc; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (!(desc->flags & CHIP_HAS_INPUTSEL)) - break; - ctrl->value=chip->muted; - return 0; - case V4L2_CID_AUDIO_VOLUME: - if (!(desc->flags & CHIP_HAS_VOLUME)) - break; - ctrl->value = chip->volume; - return 0; - case V4L2_CID_AUDIO_BALANCE: - if (!(desc->flags & CHIP_HAS_VOLUME)) - break; - ctrl->value = chip->balance; - return 0; - case V4L2_CID_AUDIO_BASS: - if (!(desc->flags & CHIP_HAS_BASSTREBLE)) - break; - ctrl->value = chip->bass; - return 0; - case V4L2_CID_AUDIO_TREBLE: - if (!(desc->flags & CHIP_HAS_BASSTREBLE)) - break; - ctrl->value = chip->treble; - return 0; - } - return -EINVAL; -} - -static int tvaudio_s_ctrl(struct v4l2_subdev *sd, - struct v4l2_control *ctrl) +static int tvaudio_s_ctrl(struct v4l2_ctrl *ctrl) { + struct v4l2_subdev *sd = to_sd(ctrl); struct CHIPSTATE *chip = to_state(sd); struct CHIPDESC *desc = chip->desc; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - if (!(desc->flags & CHIP_HAS_INPUTSEL)) - break; - - if (ctrl->value < 0 || ctrl->value >= 2) - return -ERANGE; - chip->muted = ctrl->value; + chip->muted = ctrl->val; if (chip->muted) chip_write_masked(chip,desc->inputreg,desc->inputmute,desc->inputmask); else chip_write_masked(chip,desc->inputreg, desc->inputmap[chip->input],desc->inputmask); return 0; - case V4L2_CID_AUDIO_VOLUME: - { - u32 volume, balance; - u32 left, right; - - if (!(desc->flags & CHIP_HAS_VOLUME)) - break; - - volume = ctrl->value; - chip->volume = volume; - balance = chip->balance; - left = (min(65536U - balance, 32768U) * volume) / 32768U; - right = (min(balance, 32768U) * volume) / 32768U; - - chip_write(chip, desc->leftreg, desc->volfunc(left)); - chip_write(chip, desc->rightreg, desc->volfunc(right)); - return 0; - } - case V4L2_CID_AUDIO_BALANCE: - { + case V4L2_CID_AUDIO_VOLUME: { u32 volume, balance; u32 left, right; - if (!(desc->flags & CHIP_HAS_VOLUME)) - break; - - balance = ctrl->value; - chip->balance = balance; - volume = chip->volume; + volume = chip->volume->val; + balance = chip->balance->val; left = (min(65536U - balance, 32768U) * volume) / 32768U; right = (min(balance, 32768U) * volume) / 32768U; @@ -1772,18 +1720,10 @@ static int tvaudio_s_ctrl(struct v4l2_subdev *sd, return 0; } case V4L2_CID_AUDIO_BASS: - if (!(desc->flags & CHIP_HAS_BASSTREBLE)) - break; - chip->bass = ctrl->value; - chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass)); - + chip_write(chip, desc->bassreg, desc->bassfunc(ctrl->val)); return 0; case V4L2_CID_AUDIO_TREBLE: - if (!(desc->flags & CHIP_HAS_BASSTREBLE)) - break; - chip->treble = ctrl->value; - chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble)); - + chip_write(chip, desc->treblereg, desc->treblefunc(ctrl->val)); return 0; } return -EINVAL; @@ -1802,35 +1742,6 @@ static int tvaudio_s_radio(struct v4l2_subdev *sd) return 0; } -static int tvaudio_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - struct CHIPSTATE *chip = to_state(sd); - struct CHIPDESC *desc = chip->desc; - - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - if (desc->flags & CHIP_HAS_INPUTSEL) - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); - break; - case V4L2_CID_AUDIO_VOLUME: - if (desc->flags & CHIP_HAS_VOLUME) - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); - break; - case V4L2_CID_AUDIO_BALANCE: - if (desc->flags & CHIP_HAS_VOLUME) - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); - break; - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - if (desc->flags & CHIP_HAS_BASSTREBLE) - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); - break; - default: - break; - } - return -EINVAL; -} - static int tvaudio_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) { @@ -1934,13 +1845,32 @@ static int tvaudio_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ide return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVAUDIO, 0); } +static int tvaudio_log_status(struct v4l2_subdev *sd) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + v4l2_info(sd, "Chip: %s\n", desc->name); + v4l2_ctrl_handler_log_status(&chip->hdl, sd->name); + return 0; +} + /* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops tvaudio_ctrl_ops = { + .s_ctrl = tvaudio_s_ctrl, +}; + static const struct v4l2_subdev_core_ops tvaudio_core_ops = { + .log_status = tvaudio_log_status, .g_chip_ident = tvaudio_g_chip_ident, - .queryctrl = tvaudio_queryctrl, - .g_ctrl = tvaudio_g_ctrl, - .s_ctrl = tvaudio_s_ctrl, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, .s_std = tvaudio_s_std, }; @@ -2025,6 +1955,10 @@ static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id * else chip_cmd(chip, "init", &desc->init); + v4l2_ctrl_handler_init(&chip->hdl, 5); + if (desc->flags & CHIP_HAS_INPUTSEL) + v4l2_ctrl_new_std(&chip->hdl, &tvaudio_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); if (desc->flags & CHIP_HAS_VOLUME) { if (!desc->volfunc) { /* This shouldn't be happen. Warn user, but keep working @@ -2033,12 +1967,14 @@ static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id * v4l2_info(sd, "volume callback undefined!\n"); desc->flags &= ~CHIP_HAS_VOLUME; } else { - chip->volume = desc->volinit ? desc->volinit : 65535; - chip->balance = 32768; - chip_write(chip, desc->leftreg, - desc->volfunc(chip->volume)); - chip_write(chip, desc->rightreg, - desc->volfunc(chip->volume)); + chip->volume = v4l2_ctrl_new_std(&chip->hdl, + &tvaudio_ctrl_ops, V4L2_CID_AUDIO_VOLUME, + 0, 65535, 65535 / 100, + desc->volinit ? desc->volinit : 65535); + chip->balance = v4l2_ctrl_new_std(&chip->hdl, + &tvaudio_ctrl_ops, V4L2_CID_AUDIO_BALANCE, + 0, 65535, 65535 / 100, 32768); + v4l2_ctrl_cluster(2, &chip->volume); } } if (desc->flags & CHIP_HAS_BASSTREBLE) { @@ -2049,17 +1985,28 @@ static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id * v4l2_info(sd, "bass/treble callbacks undefined!\n"); desc->flags &= ~CHIP_HAS_BASSTREBLE; } else { - chip->treble = desc->trebleinit ? - desc->trebleinit : 32768; - chip->bass = desc->bassinit ? - desc->bassinit : 32768; - chip_write(chip, desc->bassreg, - desc->bassfunc(chip->bass)); - chip_write(chip, desc->treblereg, - desc->treblefunc(chip->treble)); + v4l2_ctrl_new_std(&chip->hdl, + &tvaudio_ctrl_ops, V4L2_CID_AUDIO_BASS, + 0, 65535, 65535 / 100, + desc->bassinit ? desc->bassinit : 32768); + v4l2_ctrl_new_std(&chip->hdl, + &tvaudio_ctrl_ops, V4L2_CID_AUDIO_TREBLE, + 0, 65535, 65535 / 100, + desc->trebleinit ? desc->trebleinit : 32768); } } + sd->ctrl_handler = &chip->hdl; + if (chip->hdl.error) { + int err = chip->hdl.error; + + v4l2_ctrl_handler_free(&chip->hdl); + kfree(chip); + return err; + } + /* set controls to the default values */ + v4l2_ctrl_handler_setup(&chip->hdl); + chip->thread = NULL; init_timer(&chip->wt); if (desc->flags & CHIP_NEED_CHECKMODE) { @@ -2095,6 +2042,7 @@ static int tvaudio_remove(struct i2c_client *client) } v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&chip->hdl); kfree(chip); return 0; } -- cgit v1.2.3 From f122d9a83e5d1e73f34da9fb832c9b005030b9cb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 11:26:36 -0300 Subject: [media] radio-miropcm20: fix querycap Don't set version (done by the v4l2 core), fill in bus_info, set correct driver name and add device_caps support. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-miropcm20.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 11f76ed4c6fb..3a89e50ade6c 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -79,11 +79,13 @@ static const struct v4l2_file_operations pcm20_fops = { static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *v) { + struct pcm20 *dev = video_drvdata(file); + strlcpy(v->driver, "Miro PCM20", sizeof(v->driver)); strlcpy(v->card, "Miro PCM20", sizeof(v->card)); - strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); - v->version = 0x1; - v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + snprintf(v->bus_info, sizeof(v->bus_info), "ISA:%s", dev->v4l2_dev.name); + v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -229,7 +231,7 @@ static int __init pcm20_init(void) "you must load the snd-miro driver first!\n"); return -ENODEV; } - strlcpy(v4l2_dev->name, "miropcm20", sizeof(v4l2_dev->name)); + strlcpy(v4l2_dev->name, "radio-miropcm20", sizeof(v4l2_dev->name)); mutex_init(&dev->lock); res = v4l2_device_register(NULL, v4l2_dev); -- cgit v1.2.3 From 1a64b63481f6ad5f2db6ee8f3cad31e882ad8a27 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 05:49:53 -0300 Subject: [media] radio-miropcm20: remove input/audio ioctls Such ioctls are not valid for radio devices. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-miropcm20.c | 30 ------------------------------ 1 file changed, 30 deletions(-) (limited to 'drivers') diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 3a89e50ade6c..4b7c1640edb0 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -178,32 +178,6 @@ static int vidioc_s_ctrl(struct file *file, void *priv, return 0; } -static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - return i ? -EINVAL : 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - a->index = 0; - strlcpy(a->name, "Radio", sizeof(a->name)); - a->capability = V4L2_AUDCAP_STEREO; - return 0; -} - -static int vidioc_s_audio(struct file *file, void *priv, - const struct v4l2_audio *a) -{ - return a->index ? -EINVAL : 0; -} - static const struct v4l2_ioctl_ops pcm20_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, @@ -213,10 +187,6 @@ static const struct v4l2_ioctl_ops pcm20_ioctl_ops = { .vidioc_queryctrl = vidioc_queryctrl, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, }; static int __init pcm20_init(void) -- cgit v1.2.3 From 7f51a6108382dcbf2d5e9a268e86255d72318737 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 05:50:28 -0300 Subject: [media] radio-miropcm20: convert to the control framework Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-miropcm20.c | 64 ++++++++++++++--------------------- 1 file changed, 26 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 4b7c1640edb0..061ebcf4876d 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -17,6 +17,7 @@ #include #include #include +#include #include static int radio_nr = -1; @@ -30,6 +31,7 @@ MODULE_PARM_DESC(mono, "Force tuner into mono mode."); struct pcm20 { struct v4l2_device v4l2_dev; struct video_device vdev; + struct v4l2_ctrl_handler ctrl_handler; unsigned long freq; int muted; struct snd_miro_aci *aci; @@ -138,44 +140,16 @@ static int vidioc_s_frequency(struct file *file, void *priv, return 0; } -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) +static int pcm20_s_ctrl(struct v4l2_ctrl *ctrl) { - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - } - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct pcm20 *dev = video_drvdata(file); + struct pcm20 *dev = container_of(ctrl->handler, struct pcm20, ctrl_handler); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - ctrl->value = dev->muted; - break; - default: - return -EINVAL; + pcm20_mute(dev, ctrl->val); + return 0; } - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct pcm20 *dev = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - pcm20_mute(dev, ctrl->value); - break; - default: - return -EINVAL; - } - return 0; + return -EINVAL; } static const struct v4l2_ioctl_ops pcm20_ioctl_ops = { @@ -184,15 +158,17 @@ static const struct v4l2_ioctl_ops pcm20_ioctl_ops = { .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, +}; + +static const struct v4l2_ctrl_ops pcm20_ctrl_ops = { + .s_ctrl = pcm20_s_ctrl, }; static int __init pcm20_init(void) { struct pcm20 *dev = &pcm20_card; struct v4l2_device *v4l2_dev = &dev->v4l2_dev; + struct v4l2_ctrl_handler *hdl; int res; dev->aci = snd_aci_get_aci(); @@ -210,6 +186,16 @@ static int __init pcm20_init(void) return -EINVAL; } + hdl = &dev->ctrl_handler; + v4l2_ctrl_handler_init(hdl, 1); + v4l2_ctrl_new_std(hdl, &pcm20_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + v4l2_dev->ctrl_handler = hdl; + if (hdl->error) { + res = hdl->error; + v4l2_err(v4l2_dev, "Could not register control\n"); + goto err_hdl; + } strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); dev->vdev.v4l2_dev = v4l2_dev; dev->vdev.fops = &pcm20_fops; @@ -219,11 +205,12 @@ static int __init pcm20_init(void) video_set_drvdata(&dev->vdev, dev); if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) - goto fail; + goto err_hdl; v4l2_info(v4l2_dev, "Mirosound PCM20 Radio tuner\n"); return 0; -fail: +err_hdl: + v4l2_ctrl_handler_free(hdl); v4l2_device_unregister(v4l2_dev); return -EINVAL; } @@ -237,6 +224,7 @@ static void __exit pcm20_cleanup(void) struct pcm20 *dev = &pcm20_card; video_unregister_device(&dev->vdev); + v4l2_ctrl_handler_free(&dev->ctrl_handler); v4l2_device_unregister(&dev->v4l2_dev); } -- cgit v1.2.3 From f7c096f73561ba9754d4b13a6dc38e964194d784 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 05:54:33 -0300 Subject: [media] radio-miropcm20: add prio and control event support Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-miropcm20.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 061ebcf4876d..4ac813c5af17 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include static int radio_nr = -1; @@ -75,6 +77,9 @@ static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq) static const struct v4l2_file_operations pcm20_fops = { .owner = THIS_MODULE, + .open = v4l2_fh_open, + .poll = v4l2_ctrl_poll, + .release = v4l2_fh_release, .unlocked_ioctl = video_ioctl2, }; @@ -158,6 +163,9 @@ static const struct v4l2_ioctl_ops pcm20_ioctl_ops = { .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static const struct v4l2_ctrl_ops pcm20_ctrl_ops = { @@ -202,6 +210,7 @@ static int __init pcm20_init(void) dev->vdev.ioctl_ops = &pcm20_ioctl_ops; dev->vdev.release = video_device_release_empty; dev->vdev.lock = &dev->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags); video_set_drvdata(&dev->vdev, dev); if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) -- cgit v1.2.3 From f5e7cc4af36c631edf7dc73111d80af975f526a6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 05:55:50 -0300 Subject: [media] radio-miropcm20: Fix audmode/tuner/frequency handling - instead of a mute module option, use audmode as per the spec. - clamp the frequency before setting it. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-miropcm20.c | 52 ++++++++++++++++------------------- 1 file changed, 24 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 4ac813c5af17..eb6cd86337a8 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -26,44 +26,27 @@ static int radio_nr = -1; module_param(radio_nr, int, 0); MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)"); -static bool mono; -module_param(mono, bool, 0); -MODULE_PARM_DESC(mono, "Force tuner into mono mode."); - struct pcm20 { struct v4l2_device v4l2_dev; struct video_device vdev; struct v4l2_ctrl_handler ctrl_handler; unsigned long freq; - int muted; + u32 audmode; struct snd_miro_aci *aci; struct mutex lock; }; static struct pcm20 pcm20_card = { - .freq = 87*16000, - .muted = 1, + .freq = 87 * 16000, + .audmode = V4L2_TUNER_MODE_STEREO, }; -static int pcm20_mute(struct pcm20 *dev, unsigned char mute) -{ - dev->muted = mute; - return snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, mute, -1); -} - -static int pcm20_stereo(struct pcm20 *dev, unsigned char stereo) -{ - return snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO, !stereo, -1); -} - static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq) { unsigned char freql; unsigned char freqh; struct snd_miro_aci *aci = dev->aci; - dev->freq = freq; - freq /= 160; if (!(aci->aci_version == 0x07 || aci->aci_version >= 0xb0)) freq /= 10; /* I don't know exactly which version @@ -71,7 +54,6 @@ static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq) freql = freq & 0xff; freqh = freq >> 8; - pcm20_stereo(dev, !mono); return snd_aci_cmd(aci, ACI_WRITE_TUNE, freql, freqh); } @@ -99,7 +81,9 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - if (v->index) /* Only 1 tuner */ + struct pcm20 *dev = video_drvdata(file); + + if (v->index) return -EINVAL; strlcpy(v->name, "FM", sizeof(v->name)); v->type = V4L2_TUNER_RADIO; @@ -107,15 +91,23 @@ static int vidioc_g_tuner(struct file *file, void *priv, v->rangehigh = 108*16000; v->signal = 0xffff; v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - v->capability = V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; + v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; + v->audmode = dev->audmode; return 0; } static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - return v->index ? -EINVAL : 0; + struct pcm20 *dev = video_drvdata(file); + + if (v->index) + return -EINVAL; + if (v->audmode > V4L2_TUNER_MODE_STEREO) + v->audmode = V4L2_TUNER_MODE_STEREO; + snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO, + v->audmode == V4L2_TUNER_MODE_MONO, -1); + return 0; } static int vidioc_g_frequency(struct file *file, void *priv, @@ -140,8 +132,8 @@ static int vidioc_s_frequency(struct file *file, void *priv, if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) return -EINVAL; - dev->freq = f->frequency; - pcm20_setfreq(dev, f->frequency); + dev->freq = clamp(f->frequency, 87 * 16000U, 108 * 16000U); + pcm20_setfreq(dev, dev->freq); return 0; } @@ -151,7 +143,7 @@ static int pcm20_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - pcm20_mute(dev, ctrl->val); + snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, ctrl->val, -1); return 0; } return -EINVAL; @@ -212,6 +204,9 @@ static int __init pcm20_init(void) dev->vdev.lock = &dev->lock; set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags); video_set_drvdata(&dev->vdev, dev); + snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO, + dev->audmode == V4L2_TUNER_MODE_MONO, -1); + pcm20_setfreq(dev, dev->freq); if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) goto err_hdl; @@ -233,6 +228,7 @@ static void __exit pcm20_cleanup(void) struct pcm20 *dev = &pcm20_card; video_unregister_device(&dev->vdev); + snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, 1, -1); v4l2_ctrl_handler_free(&dev->ctrl_handler); v4l2_device_unregister(&dev->v4l2_dev); } -- cgit v1.2.3 From 9bbc5820ffe1675c923ed0b82952cc64a610bd5d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 28 May 2012 11:10:01 -0300 Subject: [media] radio-miropcm20: fix signal and stereo indication Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-miropcm20.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index eb6cd86337a8..3d0ff4404d12 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -82,6 +82,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { struct pcm20 *dev = video_drvdata(file); + int res; if (v->index) return -EINVAL; @@ -89,8 +90,13 @@ static int vidioc_g_tuner(struct file *file, void *priv, v->type = V4L2_TUNER_RADIO; v->rangelow = 87*16000; v->rangehigh = 108*16000; - v->signal = 0xffff; - v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + res = snd_aci_cmd(dev->aci, ACI_READ_TUNERSTATION, -1, -1); + v->signal = (res & 0x80) ? 0 : 0xffff; + /* Note: stereo detection does not work if the audio is muted, + it will default to mono in that case. */ + res = snd_aci_cmd(dev->aci, ACI_READ_TUNERSTEREO, -1, -1); + v->rxsubchans = (res & 0x40) ? V4L2_TUNER_SUB_MONO : + V4L2_TUNER_SUB_STEREO; v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; v->audmode = dev->audmode; return 0; -- cgit v1.2.3 From 11d379395291609f95cf1e102f9f23892b83a493 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 11:59:48 -0300 Subject: [media] bw-qcam: zero priv field Fix a v4l2-compliance problem: the priv field of v4l2_pix_format must be cleared by drivers. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/parport/bw-qcam.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/media/parport/bw-qcam.c b/drivers/media/parport/bw-qcam.c index 5b75a64b199b..497b342b0f72 100644 --- a/drivers/media/parport/bw-qcam.c +++ b/drivers/media/parport/bw-qcam.c @@ -693,6 +693,7 @@ static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f pix->sizeimage = pix->width * pix->height; /* Just a guess */ pix->colorspace = V4L2_COLORSPACE_SRGB; + pix->priv = 0; return 0; } @@ -718,6 +719,7 @@ static int qcam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format pix->sizeimage = pix->width * pix->height; /* Just a guess */ pix->colorspace = V4L2_COLORSPACE_SRGB; + pix->priv = 0; return 0; } -- cgit v1.2.3 From 1888e4a9742087df22ba64ea2e0e1064edeb8785 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 14:10:14 -0300 Subject: [media] bw-qcam: convert to videobuf2 I know, nobody really cares about this black-and-white webcam anymore, but it was fun to do. Tested with an actual webcam. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/parport/Kconfig | 1 + drivers/media/parport/bw-qcam.c | 157 ++++++++++++++++++++++++++++------------ 2 files changed, 112 insertions(+), 46 deletions(-) (limited to 'drivers') diff --git a/drivers/media/parport/Kconfig b/drivers/media/parport/Kconfig index ece13dcff07d..948c981d9f05 100644 --- a/drivers/media/parport/Kconfig +++ b/drivers/media/parport/Kconfig @@ -9,6 +9,7 @@ if MEDIA_PARPORT_SUPPORT config VIDEO_BWQCAM tristate "Quickcam BW Video For Linux" depends on PARPORT && VIDEO_V4L2 + select VIDEOBUF2_VMALLOC help Say Y have if you the black and white version of the QuickCam camera. See the next option for the color version. diff --git a/drivers/media/parport/bw-qcam.c b/drivers/media/parport/bw-qcam.c index 497b342b0f72..d3fe34f14c0d 100644 --- a/drivers/media/parport/bw-qcam.c +++ b/drivers/media/parport/bw-qcam.c @@ -80,6 +80,7 @@ OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include /* One from column A... */ #define QC_NOTSET 0 @@ -107,9 +108,11 @@ struct qcam { struct v4l2_device v4l2_dev; struct video_device vdev; struct v4l2_ctrl_handler hdl; + struct vb2_queue vb_vidq; struct pardevice *pdev; struct parport *pport; struct mutex lock; + struct mutex queue_lock; int width, height; int bpp; int mode; @@ -558,7 +561,7 @@ static inline int qc_readbytes(struct qcam *q, char buffer[]) * n=2^(bit depth)-1. Ask me for more details if you don't understand * this. */ -static long qc_capture(struct qcam *q, char __user *buf, unsigned long len) +static long qc_capture(struct qcam *q, u8 *buf, unsigned long len) { int i, j, k, yield; int bytes; @@ -609,7 +612,7 @@ static long qc_capture(struct qcam *q, char __user *buf, unsigned long len) if (o < len) { u8 ch = invert - buffer[k]; got++; - put_user(ch << shift, buf + o); + buf[o] = ch << shift; } } pixels_read += bytes; @@ -639,6 +642,67 @@ static long qc_capture(struct qcam *q, char __user *buf, unsigned long len) return len; } +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct qcam *dev = vb2_get_drv_priv(vq); + + if (0 == *nbuffers) + *nbuffers = 3; + *nplanes = 1; + mutex_lock(&dev->lock); + if (fmt) + sizes[0] = fmt->fmt.pix.width * fmt->fmt.pix.height; + else + sizes[0] = (dev->width / dev->transfer_scale) * + (dev->height / dev->transfer_scale); + mutex_unlock(&dev->lock); + return 0; +} + +static void buffer_queue(struct vb2_buffer *vb) +{ + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); +} + +static int buffer_finish(struct vb2_buffer *vb) +{ + struct qcam *qcam = vb2_get_drv_priv(vb->vb2_queue); + void *vbuf = vb2_plane_vaddr(vb, 0); + int size = vb->vb2_queue->plane_sizes[0]; + int len; + + mutex_lock(&qcam->lock); + parport_claim_or_block(qcam->pdev); + + qc_reset(qcam); + + /* Update the camera parameters if we need to */ + if (qcam->status & QC_PARAM_CHANGE) + qc_set(qcam); + + len = qc_capture(qcam, vbuf, size); + + parport_release(qcam->pdev); + mutex_unlock(&qcam->lock); + if (len != size) + vb->state = VB2_BUF_STATE_ERROR; + vb2_set_plane_payload(vb, 0, len); + return 0; +} + +static struct vb2_ops qcam_video_qops = { + .queue_setup = queue_setup, + .buf_queue = buffer_queue, + .buf_finish = buffer_finish, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + /* * Video4linux interfacing */ @@ -651,7 +715,8 @@ static int qcam_querycap(struct file *file, void *priv, strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver)); strlcpy(vcap->card, "Connectix B&W Quickcam", sizeof(vcap->card)); strlcpy(vcap->bus_info, qcam->pport->name, sizeof(vcap->bus_info)); - vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -731,6 +796,8 @@ static int qcam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f if (ret) return ret; + if (vb2_is_busy(&qcam->vb_vidq)) + return -EBUSY; qcam->width = 320; qcam->height = 240; if (pix->height == 60) @@ -744,12 +811,10 @@ static int qcam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f else qcam->bpp = 4; - mutex_lock(&qcam->lock); qc_setscanmode(qcam); /* We must update the camera before we grab. We could just have changed the grab size */ qcam->status |= QC_PARAM_CHANGE; - mutex_unlock(&qcam->lock); return 0; } @@ -794,41 +859,12 @@ static int qcam_enum_framesizes(struct file *file, void *fh, return 0; } -static ssize_t qcam_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct qcam *qcam = video_drvdata(file); - int len; - parport_claim_or_block(qcam->pdev); - - mutex_lock(&qcam->lock); - - qc_reset(qcam); - - /* Update the camera parameters if we need to */ - if (qcam->status & QC_PARAM_CHANGE) - qc_set(qcam); - - len = qc_capture(qcam, buf, count); - - mutex_unlock(&qcam->lock); - - parport_release(qcam->pdev); - return len; -} - -static unsigned int qcam_poll(struct file *filp, poll_table *wait) -{ - return v4l2_ctrl_poll(filp, wait) | POLLIN | POLLRDNORM; -} - static int qcam_s_ctrl(struct v4l2_ctrl *ctrl) { struct qcam *qcam = container_of(ctrl->handler, struct qcam, hdl); int ret = 0; - mutex_lock(&qcam->lock); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: qcam->brightness = ctrl->val; @@ -847,17 +883,17 @@ static int qcam_s_ctrl(struct v4l2_ctrl *ctrl) qc_setscanmode(qcam); qcam->status |= QC_PARAM_CHANGE; } - mutex_unlock(&qcam->lock); return ret; } static const struct v4l2_file_operations qcam_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, - .release = v4l2_fh_release, - .poll = qcam_poll, + .release = vb2_fop_release, + .poll = vb2_fop_poll, .unlocked_ioctl = video_ioctl2, - .read = qcam_read, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, }; static const struct v4l2_ioctl_ops qcam_ioctl_ops = { @@ -870,6 +906,14 @@ static const struct v4l2_ioctl_ops qcam_ioctl_ops = { .vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap, .vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_log_status = v4l2_ctrl_log_status, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, @@ -886,6 +930,8 @@ static struct qcam *qcam_init(struct parport *port) { struct qcam *qcam; struct v4l2_device *v4l2_dev; + struct vb2_queue *q; + int err; qcam = kzalloc(sizeof(struct qcam), GFP_KERNEL); if (qcam == NULL) @@ -909,31 +955,45 @@ static struct qcam *qcam_init(struct parport *port) V4L2_CID_GAMMA, 0, 255, 1, 105); if (qcam->hdl.error) { v4l2_err(v4l2_dev, "couldn't register controls\n"); - v4l2_ctrl_handler_free(&qcam->hdl); - kfree(qcam); - return NULL; + goto exit; + } + + mutex_init(&qcam->lock); + mutex_init(&qcam->queue_lock); + + /* initialize queue */ + q = &qcam->vb_vidq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + q->drv_priv = qcam; + q->ops = &qcam_video_qops; + q->mem_ops = &vb2_vmalloc_memops; + err = vb2_queue_init(q); + if (err < 0) { + v4l2_err(v4l2_dev, "couldn't init vb2_queue for %s.\n", port->name); + goto exit; } + qcam->vdev.queue = q; + qcam->vdev.queue->lock = &qcam->queue_lock; + qcam->pport = port; qcam->pdev = parport_register_device(port, v4l2_dev->name, NULL, NULL, NULL, 0, NULL); if (qcam->pdev == NULL) { v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name); - v4l2_ctrl_handler_free(&qcam->hdl); - kfree(qcam); - return NULL; + goto exit; } strlcpy(qcam->vdev.name, "Connectix QuickCam", sizeof(qcam->vdev.name)); qcam->vdev.v4l2_dev = v4l2_dev; qcam->vdev.ctrl_handler = &qcam->hdl; qcam->vdev.fops = &qcam_fops; + qcam->vdev.lock = &qcam->lock; qcam->vdev.ioctl_ops = &qcam_ioctl_ops; set_bit(V4L2_FL_USE_FH_PRIO, &qcam->vdev.flags); qcam->vdev.release = video_device_release_empty; video_set_drvdata(&qcam->vdev, qcam); - mutex_init(&qcam->lock); - qcam->port_mode = (QC_ANY | QC_NOTSET); qcam->width = 320; qcam->height = 240; @@ -947,6 +1007,11 @@ static struct qcam *qcam_init(struct parport *port) qcam->mode = -1; qcam->status = QC_PARAM_CHANGE; return qcam; + +exit: + v4l2_ctrl_handler_free(&qcam->hdl); + kfree(qcam); + return NULL; } static int qc_calibrate(struct qcam *q) -- cgit v1.2.3 From 971dfc678114d61c07bd6f8ff8380558b6e12d5d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 14:07:38 -0300 Subject: [media] bw-qcam: remove unnecessary qc_reset and qc_setscanmode calls These are already done elsewhere. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/parport/bw-qcam.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/parport/bw-qcam.c b/drivers/media/parport/bw-qcam.c index d3fe34f14c0d..06231b85e1a9 100644 --- a/drivers/media/parport/bw-qcam.c +++ b/drivers/media/parport/bw-qcam.c @@ -421,8 +421,6 @@ static void qc_set(struct qcam *q) int val; int val2; - qc_reset(q); - /* Set the brightness. Yes, this is repetitive, but it works. * Shorter versions seem to fail subtly. Feel free to try :-). */ /* I think the problem was in qc_command, not here -- bls */ @@ -879,10 +877,8 @@ static int qcam_s_ctrl(struct v4l2_ctrl *ctrl) ret = -EINVAL; break; } - if (ret == 0) { - qc_setscanmode(qcam); + if (ret == 0) qcam->status |= QC_PARAM_CHANGE; - } return ret; } -- cgit v1.2.3 From cd13823f5db3e66552801c04f0e761408ef17eb0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 13:29:02 -0300 Subject: [media] videobuf2: don't return POLLERR when only polling for events If you only poll for events, then vb2_poll will return POLLPRI | POLLERR if no streaming is in progress. That's not right, it's perfectly valid to poll just for events. Cc: Pawel Osciak Cc: Marek Szyprowski Cc: Kyungmin Park Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf2-core.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index d09be38dd377..db1235dcb328 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1965,6 +1965,11 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) poll_wait(file, &fh->wait, wait); } + if (!V4L2_TYPE_IS_OUTPUT(q->type) && !(req_events & (POLLIN | POLLRDNORM))) + return res; + if (V4L2_TYPE_IS_OUTPUT(q->type) && !(req_events & (POLLOUT | POLLWRNORM))) + return res; + /* * Start file I/O emulator only if streaming API has not been used yet. */ -- cgit v1.2.3 From ed986d1fee77bbbb62291a1db1c7edbb00d99515 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 29 Jan 2013 07:21:02 -0300 Subject: [media] meye: convert to the control framework Convert the meye driver to the control framework. Some private controls have been replaced with standardized controls (SHARPNESS and JPEGQUAL). The AGC control looks like it can be replaced by the AUTOGAIN control, but it isn't a boolean so I do not know how to interpret it. The FRAMERATE control looks like it can be replaced by S_PARM, but again, without knowing how to interpret it I decided to leave it alone. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/meye/meye.c | 278 ++++++++++++------------------------- drivers/media/pci/meye/meye.h | 2 + include/uapi/linux/meye.h | 8 +- include/uapi/linux/v4l2-controls.h | 5 + 4 files changed, 99 insertions(+), 194 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c index 3b39deaa8f6b..7859c43479d7 100644 --- a/drivers/media/pci/meye/meye.c +++ b/drivers/media/pci/meye/meye.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include #include #include @@ -865,7 +867,7 @@ static int meye_open(struct file *file) meye.grab_buffer[i].state = MEYE_BUF_UNUSED; kfifo_reset(&meye.grabq); kfifo_reset(&meye.doneq); - return 0; + return v4l2_fh_open(file); } static int meye_release(struct file *file) @@ -873,7 +875,7 @@ static int meye_release(struct file *file) mchip_hic_stop(); mchip_dma_free(); clear_bit(0, &meye.in_use); - return 0; + return v4l2_fh_release(file); } static int meyeioc_g_params(struct meye_params *p) @@ -1032,8 +1034,9 @@ static int vidioc_querycap(struct file *file, void *fh, cap->version = (MEYE_DRIVER_MAJORVERSION << 8) + MEYE_DRIVER_MINORVERSION; - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1063,191 +1066,50 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int i) return 0; } -static int vidioc_queryctrl(struct file *file, void *fh, - struct v4l2_queryctrl *c) -{ - switch (c->id) { - - case V4L2_CID_BRIGHTNESS: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Brightness"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 32; - c->flags = 0; - break; - case V4L2_CID_HUE: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Hue"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 32; - c->flags = 0; - break; - case V4L2_CID_CONTRAST: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Contrast"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 32; - c->flags = 0; - break; - case V4L2_CID_SATURATION: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Saturation"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 32; - c->flags = 0; - break; - case V4L2_CID_AGC: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Agc"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 48; - c->flags = 0; - break; - case V4L2_CID_MEYE_SHARPNESS: - case V4L2_CID_SHARPNESS: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Sharpness"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 32; - - /* Continue to report legacy private SHARPNESS ctrl but - * say it is disabled in preference to ctrl in the spec - */ - c->flags = (c->id == V4L2_CID_SHARPNESS) ? 0 : - V4L2_CTRL_FLAG_DISABLED; - break; - case V4L2_CID_PICTURE: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Picture"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 0; - c->flags = 0; - break; - case V4L2_CID_JPEGQUAL: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "JPEG quality"); - c->minimum = 0; - c->maximum = 10; - c->step = 1; - c->default_value = 8; - c->flags = 0; - break; - case V4L2_CID_FRAMERATE: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Framerate"); - c->minimum = 0; - c->maximum = 31; - c->step = 1; - c->default_value = 0; - c->flags = 0; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) +static int meye_s_ctrl(struct v4l2_ctrl *ctrl) { mutex_lock(&meye.lock); - switch (c->id) { + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, c->value); - meye.brightness = c->value << 10; + SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, ctrl->val); + meye.brightness = ctrl->val << 10; break; case V4L2_CID_HUE: sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERAHUE, c->value); - meye.hue = c->value << 10; + SONY_PIC_COMMAND_SETCAMERAHUE, ctrl->val); + meye.hue = ctrl->val << 10; break; case V4L2_CID_CONTRAST: sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERACONTRAST, c->value); - meye.contrast = c->value << 10; + SONY_PIC_COMMAND_SETCAMERACONTRAST, ctrl->val); + meye.contrast = ctrl->val << 10; break; case V4L2_CID_SATURATION: sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERACOLOR, c->value); - meye.colour = c->value << 10; + SONY_PIC_COMMAND_SETCAMERACOLOR, ctrl->val); + meye.colour = ctrl->val << 10; break; - case V4L2_CID_AGC: + case V4L2_CID_MEYE_AGC: sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERAAGC, c->value); - meye.params.agc = c->value; + SONY_PIC_COMMAND_SETCAMERAAGC, ctrl->val); + meye.params.agc = ctrl->val; break; case V4L2_CID_SHARPNESS: - case V4L2_CID_MEYE_SHARPNESS: sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERASHARPNESS, c->value); - meye.params.sharpness = c->value; + SONY_PIC_COMMAND_SETCAMERASHARPNESS, ctrl->val); + meye.params.sharpness = ctrl->val; break; - case V4L2_CID_PICTURE: + case V4L2_CID_MEYE_PICTURE: sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERAPICTURE, c->value); - meye.params.picture = c->value; + SONY_PIC_COMMAND_SETCAMERAPICTURE, ctrl->val); + meye.params.picture = ctrl->val; break; - case V4L2_CID_JPEGQUAL: - meye.params.quality = c->value; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + meye.params.quality = ctrl->val; break; - case V4L2_CID_FRAMERATE: - meye.params.framerate = c->value; - break; - default: - mutex_unlock(&meye.lock); - return -EINVAL; - } - mutex_unlock(&meye.lock); - - return 0; -} - -static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) -{ - mutex_lock(&meye.lock); - switch (c->id) { - case V4L2_CID_BRIGHTNESS: - c->value = meye.brightness >> 10; - break; - case V4L2_CID_HUE: - c->value = meye.hue >> 10; - break; - case V4L2_CID_CONTRAST: - c->value = meye.contrast >> 10; - break; - case V4L2_CID_SATURATION: - c->value = meye.colour >> 10; - break; - case V4L2_CID_AGC: - c->value = meye.params.agc; - break; - case V4L2_CID_SHARPNESS: - case V4L2_CID_MEYE_SHARPNESS: - c->value = meye.params.sharpness; - break; - case V4L2_CID_PICTURE: - c->value = meye.params.picture; - break; - case V4L2_CID_JPEGQUAL: - c->value = meye.params.quality; - break; - case V4L2_CID_FRAMERATE: - c->value = meye.params.framerate; + case V4L2_CID_MEYE_FRAMERATE: + meye.params.framerate = ctrl->val; break; default: mutex_unlock(&meye.lock); @@ -1577,12 +1439,12 @@ static long vidioc_default(struct file *file, void *fh, bool valid_prio, static unsigned int meye_poll(struct file *file, poll_table *wait) { - unsigned int res = 0; + unsigned int res = v4l2_ctrl_poll(file, wait); mutex_lock(&meye.lock); poll_wait(file, &meye.proc_list, wait); if (kfifo_len(&meye.doneq)) - res = POLLIN | POLLRDNORM; + res |= POLLIN | POLLRDNORM; mutex_unlock(&meye.lock); return res; } @@ -1669,9 +1531,6 @@ static const struct v4l2_ioctl_ops meye_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, @@ -1682,6 +1541,9 @@ static const struct v4l2_ioctl_ops meye_ioctl_ops = { .vidioc_dqbuf = vidioc_dqbuf, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, .vidioc_default = vidioc_default, }; @@ -1692,6 +1554,10 @@ static struct video_device meye_template = { .release = video_device_release, }; +static const struct v4l2_ctrl_ops meye_ctrl_ops = { + .s_ctrl = meye_s_ctrl, +}; + #ifdef CONFIG_PM static int meye_suspend(struct pci_dev *pdev, pm_message_t state) { @@ -1730,6 +1596,32 @@ static int meye_resume(struct pci_dev *pdev) static int meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) { + static const struct v4l2_ctrl_config ctrl_agc = { + .id = V4L2_CID_MEYE_AGC, + .type = V4L2_CTRL_TYPE_INTEGER, + .ops = &meye_ctrl_ops, + .name = "AGC", + .max = 63, + .step = 1, + .def = 48, + .flags = V4L2_CTRL_FLAG_SLIDER, + }; + static const struct v4l2_ctrl_config ctrl_picture = { + .id = V4L2_CID_MEYE_PICTURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .ops = &meye_ctrl_ops, + .name = "Picture", + .max = 63, + .step = 1, + }; + static const struct v4l2_ctrl_config ctrl_framerate = { + .id = V4L2_CID_MEYE_FRAMERATE, + .type = V4L2_CTRL_TYPE_INTEGER, + .ops = &meye_ctrl_ops, + .name = "Framerate", + .max = 31, + .step = 1, + }; struct v4l2_device *v4l2_dev = &meye.v4l2_dev; int ret = -EBUSY; unsigned long mchip_adr; @@ -1833,24 +1725,31 @@ static int meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) mutex_init(&meye.lock); init_waitqueue_head(&meye.proc_list); - meye.brightness = 32 << 10; - meye.hue = 32 << 10; - meye.colour = 32 << 10; - meye.contrast = 32 << 10; - meye.params.subsample = 0; - meye.params.quality = 8; - meye.params.sharpness = 32; - meye.params.agc = 48; - meye.params.picture = 0; - meye.params.framerate = 0; - - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, 32); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAHUE, 32); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACOLOR, 32); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACONTRAST, 32); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, 32); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, 0); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, 48); + + v4l2_ctrl_handler_init(&meye.hdl, 3); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 63, 1, 32); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_HUE, 0, 63, 1, 32); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_CONTRAST, 0, 63, 1, 32); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_SATURATION, 0, 63, 1, 32); + v4l2_ctrl_new_custom(&meye.hdl, &ctrl_agc, NULL); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 63, 1, 32); + v4l2_ctrl_new_custom(&meye.hdl, &ctrl_picture, NULL); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 0, 10, 1, 8); + v4l2_ctrl_new_custom(&meye.hdl, &ctrl_framerate, NULL); + if (meye.hdl.error) { + v4l2_err(v4l2_dev, "couldn't register controls\n"); + goto outvideoreg; + } + + v4l2_ctrl_handler_setup(&meye.hdl); + meye.vdev->ctrl_handler = &meye.hdl; + set_bit(V4L2_FL_USE_FH_PRIO, &meye.vdev->flags); if (video_register_device(meye.vdev, VFL_TYPE_GRABBER, video_nr) < 0) { @@ -1866,6 +1765,7 @@ static int meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) return 0; outvideoreg: + v4l2_ctrl_handler_free(&meye.hdl); free_irq(meye.mchip_irq, meye_irq); outreqirq: iounmap(meye.mchip_mmregs); diff --git a/drivers/media/pci/meye/meye.h b/drivers/media/pci/meye/meye.h index 4bdeb03f1644..6fed9274cfa5 100644 --- a/drivers/media/pci/meye/meye.h +++ b/drivers/media/pci/meye/meye.h @@ -39,6 +39,7 @@ #include #include #include +#include /****************************************************************************/ /* Motion JPEG chip registers */ @@ -290,6 +291,7 @@ struct meye_grab_buffer { /* Motion Eye device structure */ struct meye { struct v4l2_device v4l2_dev; /* Main v4l2_device struct */ + struct v4l2_ctrl_handler hdl; struct pci_dev *mchip_dev; /* pci device */ u8 mchip_irq; /* irq */ u8 mchip_mode; /* actual mchip mode: HIC_MODE... */ diff --git a/include/uapi/linux/meye.h b/include/uapi/linux/meye.h index 0dd49954f746..8ff50fe9e481 100644 --- a/include/uapi/linux/meye.h +++ b/include/uapi/linux/meye.h @@ -57,10 +57,8 @@ struct meye_params { #define MEYEIOC_STILLJCAPT _IOR ('v', BASE_VIDIOC_PRIVATE+5, int) /* V4L2 private controls */ -#define V4L2_CID_AGC V4L2_CID_PRIVATE_BASE -#define V4L2_CID_MEYE_SHARPNESS (V4L2_CID_PRIVATE_BASE + 1) -#define V4L2_CID_PICTURE (V4L2_CID_PRIVATE_BASE + 2) -#define V4L2_CID_JPEGQUAL (V4L2_CID_PRIVATE_BASE + 3) -#define V4L2_CID_FRAMERATE (V4L2_CID_PRIVATE_BASE + 4) +#define V4L2_CID_MEYE_AGC (V4L2_CID_USER_MEYE_BASE + 0) +#define V4L2_CID_MEYE_PICTURE (V4L2_CID_USER_MEYE_BASE + 1) +#define V4L2_CID_MEYE_FRAMERATE (V4L2_CID_USER_MEYE_BASE + 2) #endif diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 0bece06792d7..dcd63745e83a 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -140,6 +140,11 @@ enum v4l2_colorfx { /* last CID + 1 */ #define V4L2_CID_LASTP1 (V4L2_CID_BASE+43) +/* USER-class private control IDs */ + +/* The base for the meye driver controls. See linux/meye.h for the list + * of controls. We reserve 16 controls for this driver. */ +#define V4L2_CID_USER_MEYE_BASE (V4L2_CID_USER_BASE + 0x1000) /* MPEG-class control IDs */ -- cgit v1.2.3 From 2c2a053626cb712d6006cb10f2760a6018a65631 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 11 Sep 2012 07:35:30 -0300 Subject: [media] tm6000: fix querycap and input/tuner compliance issues - add device_caps support - fix bus_info - fix numerous tuner-related problems due to incorrect tests and setting v4l2_tuner fields to wrong values. - remove (audio) input support from the radio: it doesn't belong there. This also fixed a nasty issue where opening the radio would set dev->input to 5 for no good reason. This was never set back to a valid TV input after closing the radio device, thus leaving it at 5 which is out of bounds of the vinput card array. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-video.c | 147 +++++++------------------------- 1 file changed, 31 insertions(+), 116 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index e3c567c27918..7a653b263678 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -948,16 +948,21 @@ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; + struct video_device *vdev = video_devdata(file); strlcpy(cap->driver, "tm6000", sizeof(cap->driver)); strlcpy(cap->card, "Trident TVMaster TM5600/6000/6010", sizeof(cap->card)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_STREAMING | - V4L2_CAP_AUDIO | - V4L2_CAP_READWRITE; - + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); if (dev->tuner_type != TUNER_ABSENT) - cap->capabilities |= V4L2_CAP_TUNER; + cap->device_caps |= V4L2_CAP_TUNER; + if (vdev->vfl_type == VFL_TYPE_GRABBER) + cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + else + cap->device_caps |= V4L2_CAP_RADIO; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS | + V4L2_CAP_RADIO | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; return 0; } @@ -965,7 +970,7 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - if (unlikely(f->index >= ARRAY_SIZE(format))) + if (f->index >= ARRAY_SIZE(format)) return -EINVAL; strlcpy(f->description, format[f->index].name, sizeof(f->description)); @@ -1301,14 +1306,14 @@ static int vidioc_g_tuner(struct file *file, void *priv, struct tm6000_fh *fh = priv; struct tm6000_core *dev = fh->dev; - if (unlikely(UNSET == dev->tuner_type)) - return -EINVAL; + if (UNSET == dev->tuner_type) + return -ENOTTY; if (0 != t->index) return -EINVAL; strcpy(t->name, "Television"); t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM; + t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO; t->rangehigh = 0xffffffffUL; t->rxsubchans = V4L2_TUNER_SUB_STEREO; @@ -1326,11 +1331,14 @@ static int vidioc_s_tuner(struct file *file, void *priv, struct tm6000_core *dev = fh->dev; if (UNSET == dev->tuner_type) - return -EINVAL; + return -ENOTTY; if (0 != t->index) return -EINVAL; - dev->amode = t->audmode; + if (t->audmode > V4L2_TUNER_MODE_STEREO) + dev->amode = V4L2_TUNER_MODE_STEREO; + else + dev->amode = t->audmode; dprintk(dev, 3, "audio mode: %x\n", t->audmode); v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); @@ -1344,10 +1352,11 @@ static int vidioc_g_frequency(struct file *file, void *priv, struct tm6000_fh *fh = priv; struct tm6000_core *dev = fh->dev; - if (unlikely(UNSET == dev->tuner_type)) + if (UNSET == dev->tuner_type) + return -ENOTTY; + if (f->tuner) return -EINVAL; - f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; f->frequency = dev->freq; v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, f); @@ -1361,13 +1370,9 @@ static int vidioc_s_frequency(struct file *file, void *priv, struct tm6000_fh *fh = priv; struct tm6000_core *dev = fh->dev; - if (unlikely(UNSET == dev->tuner_type)) - return -EINVAL; - if (unlikely(f->tuner != 0)) - return -EINVAL; - if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type) - return -EINVAL; - if (1 == fh->radio && V4L2_TUNER_RADIO != f->type) + if (UNSET == dev->tuner_type) + return -ENOTTY; + if (f->tuner != 0) return -EINVAL; dev->freq = f->frequency; @@ -1376,27 +1381,6 @@ static int vidioc_s_frequency(struct file *file, void *priv, return 0; } -static int radio_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct tm6000_fh *fh = file->private_data; - struct tm6000_core *dev = fh->dev; - - strcpy(cap->driver, "tm6000"); - strlcpy(cap->card, dev->name, sizeof(dev->name)); - sprintf(cap->bus_info, "USB%04x:%04x", - le16_to_cpu(dev->udev->descriptor.idVendor), - le16_to_cpu(dev->udev->descriptor.idProduct)); - cap->version = dev->dev_type; - cap->capabilities = V4L2_CAP_TUNER | - V4L2_CAP_AUDIO | - V4L2_CAP_RADIO | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - - return 0; -} - static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { @@ -1409,7 +1393,9 @@ static int radio_g_tuner(struct file *file, void *priv, memset(t, 0, sizeof(*t)); strcpy(t->name, "Radio"); t->type = V4L2_TUNER_RADIO; + t->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; t->rxsubchans = V4L2_TUNER_SUB_STEREO; + t->audmode = V4L2_TUNER_MODE_STEREO; v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); @@ -1424,78 +1410,14 @@ static int radio_s_tuner(struct file *file, void *priv, if (0 != t->index) return -EINVAL; + if (t->audmode > V4L2_TUNER_MODE_STEREO) + t->audmode = V4L2_TUNER_MODE_STEREO; v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); return 0; } -static int radio_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (i->index != 0) - return -EINVAL; - - if (!dev->rinput.type) - return -EINVAL; - - strcpy(i->name, "Radio"); - i->type = V4L2_INPUT_TYPE_TUNER; - - return 0; -} - -static int radio_g_input(struct file *filp, void *priv, unsigned int *i) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (dev->input != 5) - return -EINVAL; - - *i = dev->input - 5; - - return 0; -} - -static int radio_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - memset(a, 0, sizeof(*a)); - strcpy(a->name, "Radio"); - return 0; -} - -static int radio_s_audio(struct file *file, void *priv, - const struct v4l2_audio *a) -{ - return 0; -} - -static int radio_s_input(struct file *filp, void *priv, unsigned int i) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (i) - return -EINVAL; - - if (!dev->rinput.type) - return -EINVAL; - - dev->input = i + 5; - - return 0; -} - -static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm) -{ - return 0; -} - static int radio_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c) { @@ -1599,7 +1521,6 @@ static int __tm6000_open(struct file *file) sizeof(struct tm6000_buffer), fh, &dev->lock); } else { dprintk(dev, V4L2_DEBUG_OPEN, "video_open: setting radio device\n"); - dev->input = 5; tm6000_set_audio_rinput(dev); v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); tm6000_prepare_isoc(dev); @@ -1789,16 +1710,10 @@ static const struct v4l2_file_operations radio_fops = { }; static const struct v4l2_ioctl_ops radio_ioctl_ops = { - .vidioc_querycap = radio_querycap, + .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = radio_g_tuner, - .vidioc_enum_input = radio_enum_input, - .vidioc_g_audio = radio_g_audio, .vidioc_s_tuner = radio_s_tuner, - .vidioc_s_audio = radio_s_audio, - .vidioc_s_input = radio_s_input, - .vidioc_s_std = radio_s_std, .vidioc_queryctrl = radio_queryctrl, - .vidioc_g_input = radio_g_input, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_g_frequency = vidioc_g_frequency, -- cgit v1.2.3 From 9f7473592bd2c8d73657dcc1644de4ab610b0d90 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 31 Jan 2013 08:23:01 -0300 Subject: [media] tm6000: convert to the control framework Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-video.c | 232 +++++++++----------------------- drivers/media/usb/tm6000/tm6000.h | 3 + 2 files changed, 68 insertions(+), 167 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index 7a653b263678..4329fbcf2de2 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -63,71 +63,6 @@ static bool keep_urb; /* keep urb buffers allocated */ int tm6000_debug; EXPORT_SYMBOL_GPL(tm6000_debug); -static const struct v4l2_queryctrl no_ctrl = { - .name = "42", - .flags = V4L2_CTRL_FLAG_DISABLED, -}; - -/* supported controls */ -static struct v4l2_queryctrl tm6000_qctrl[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 54, - .flags = 0, - }, { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = 119, - .flags = 0, - }, { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = 112, - .flags = 0, - }, { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = -128, - .maximum = 127, - .step = 0x1, - .default_value = 0, - .flags = 0, - }, - /* --- audio --- */ - { - .id = V4L2_CID_AUDIO_MUTE, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - }, { - .id = V4L2_CID_AUDIO_VOLUME, - .name = "Volume", - .minimum = -15, - .maximum = 15, - .step = 1, - .default_value = 0, - .type = V4L2_CTRL_TYPE_INTEGER, - } -}; - -static const unsigned int CTRLS = ARRAY_SIZE(tm6000_qctrl); -static int qctl_regs[ARRAY_SIZE(tm6000_qctrl)]; - static struct tm6000_fmt format[] = { { .name = "4:2:2, packed, YVY2", @@ -144,16 +79,6 @@ static struct tm6000_fmt format[] = { } }; -static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id) -{ - unsigned int i; - - for (i = 0; i < CTRLS; i++) - if (tm6000_qctrl[i].id == id) - return tm6000_qctrl+i; - return NULL; -} - /* ------------------------------------------------------------------ * DMA and thread functions * ------------------------------------------------------------------ @@ -1215,79 +1140,40 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) } /* --- controls ---------------------------------------------- */ -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++) - if (qc->id && qc->id == tm6000_qctrl[i].id) { - memcpy(qc, &(tm6000_qctrl[i]), - sizeof(*qc)); - return 0; - } - - return -EINVAL; -} -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) +static int tm6000_s_ctrl(struct v4l2_ctrl *ctrl) { - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - int val; + struct tm6000_core *dev = container_of(ctrl->handler, struct tm6000_core, ctrl_handler); + u8 val = ctrl->val; - /* FIXME: Probably, those won't work! Maybe we need shadow regs */ switch (ctrl->id) { case V4L2_CID_CONTRAST: - val = tm6000_get_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0); - break; + tm6000_set_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, val); + return 0; case V4L2_CID_BRIGHTNESS: - val = tm6000_get_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0); + tm6000_set_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, val); return 0; case V4L2_CID_SATURATION: - val = tm6000_get_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0); + tm6000_set_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, val); return 0; case V4L2_CID_HUE: - val = tm6000_get_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, 0); - return 0; - case V4L2_CID_AUDIO_MUTE: - val = dev->ctl_mute; - return 0; - case V4L2_CID_AUDIO_VOLUME: - val = dev->ctl_volume; + tm6000_set_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, val); return 0; - default: - return -EINVAL; } + return -EINVAL; +} - if (val < 0) - return val; - - ctrl->value = val; +static const struct v4l2_ctrl_ops tm6000_ctrl_ops = { + .s_ctrl = tm6000_s_ctrl, +}; - return 0; -} -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) +static int tm6000_radio_s_ctrl(struct v4l2_ctrl *ctrl) { - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - u8 val = ctrl->value; + struct tm6000_core *dev = container_of(ctrl->handler, + struct tm6000_core, radio_ctrl_handler); + u8 val = ctrl->val; switch (ctrl->id) { - case V4L2_CID_CONTRAST: - tm6000_set_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, val); - return 0; - case V4L2_CID_BRIGHTNESS: - tm6000_set_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, val); - return 0; - case V4L2_CID_SATURATION: - tm6000_set_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, val); - return 0; - case V4L2_CID_HUE: - tm6000_set_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, val); - return 0; case V4L2_CID_AUDIO_MUTE: dev->ctl_mute = val; tm6000_tvaudio_set_mute(dev, val); @@ -1300,6 +1186,10 @@ static int vidioc_s_ctrl(struct file *file, void *priv, return -EINVAL; } +static const struct v4l2_ctrl_ops tm6000_radio_ctrl_ops = { + .s_ctrl = tm6000_radio_s_ctrl, +}; + static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { @@ -1418,23 +1308,6 @@ static int radio_s_tuner(struct file *file, void *priv, return 0; } -static int radio_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *c) -{ - const struct v4l2_queryctrl *ctrl; - - if (c->id < V4L2_CID_BASE || - c->id >= V4L2_CID_LASTP1) - return -EINVAL; - if (c->id == V4L2_CID_AUDIO_MUTE) { - ctrl = ctrl_by_id(c->id); - *c = *ctrl; - } else - *c = no_ctrl; - - return 0; -} - /* ------------------------------------------------------------------ File operations for the device ------------------------------------------------------------------*/ @@ -1445,7 +1318,7 @@ static int __tm6000_open(struct file *file) struct tm6000_core *dev = video_drvdata(file); struct tm6000_fh *fh; enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - int i, rc; + int rc; int radio = 0; dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: open called (dev=%s)\n", @@ -1505,13 +1378,7 @@ static int __tm6000_open(struct file *file) if (rc < 0) return rc; - if (dev->mode != TM6000_MODE_ANALOG) { - /* Put all controls at a sane state */ - for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++) - qctl_regs[i] = tm6000_qctrl[i].default_value; - - dev->mode = TM6000_MODE_ANALOG; - } + dev->mode = TM6000_MODE_ANALOG; if (!fh->radio) { videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops, @@ -1678,9 +1545,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, @@ -1713,9 +1577,6 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = radio_g_tuner, .vidioc_s_tuner = radio_s_tuner, - .vidioc_queryctrl = radio_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, }; @@ -1755,15 +1616,41 @@ static struct video_device *vdev_init(struct tm6000_core *dev, int tm6000_v4l2_register(struct tm6000_core *dev) { - int ret = -1; + int ret = 0; + + v4l2_ctrl_handler_init(&dev->ctrl_handler, 6); + v4l2_ctrl_handler_init(&dev->radio_ctrl_handler, 2); + v4l2_ctrl_new_std(&dev->radio_ctrl_handler, &tm6000_radio_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + v4l2_ctrl_new_std(&dev->radio_ctrl_handler, &tm6000_radio_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, -15, 15, 1, 0); + v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 54); + v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 119); + v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 112); + v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + v4l2_ctrl_add_handler(&dev->ctrl_handler, + &dev->radio_ctrl_handler, NULL); + + if (dev->radio_ctrl_handler.error) + ret = dev->radio_ctrl_handler.error; + if (!ret && dev->ctrl_handler.error) + ret = dev->ctrl_handler.error; + if (ret) + goto free_ctrl; dev->vfd = vdev_init(dev, &tm6000_template, "video"); if (!dev->vfd) { printk(KERN_INFO "%s: can't register video device\n", dev->name); - return -ENOMEM; + ret = -ENOMEM; + goto free_ctrl; } + dev->vfd->ctrl_handler = &dev->ctrl_handler; /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); @@ -1774,7 +1661,9 @@ int tm6000_v4l2_register(struct tm6000_core *dev) if (ret < 0) { printk(KERN_INFO "%s: can't register video device\n", dev->name); - return ret; + video_device_release(dev->vfd); + dev->vfd = NULL; + goto free_ctrl; } printk(KERN_INFO "%s: registered device %s\n", @@ -1787,15 +1676,17 @@ int tm6000_v4l2_register(struct tm6000_core *dev) printk(KERN_INFO "%s: can't register radio device\n", dev->name); ret = -ENXIO; - return ret; /* FIXME release resource */ + goto unreg_video; } + dev->radio_dev->ctrl_handler = &dev->radio_ctrl_handler; ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, radio_nr); if (ret < 0) { printk(KERN_INFO "%s: can't register radio device\n", dev->name); - return ret; /* FIXME release resource */ + video_device_release(dev->radio_dev); + goto unreg_video; } printk(KERN_INFO "%s: registered device %s\n", @@ -1804,6 +1695,13 @@ int tm6000_v4l2_register(struct tm6000_core *dev) printk(KERN_INFO "Trident TVMaster TM5600/TM6000/TM6010 USB2 board (Load status: %d)\n", ret); return ret; + +unreg_video: + video_unregister_device(dev->vfd); +free_ctrl: + v4l2_ctrl_handler_free(&dev->ctrl_handler); + v4l2_ctrl_handler_free(&dev->radio_ctrl_handler); + return ret; } int tm6000_v4l2_unregister(struct tm6000_core *dev) diff --git a/drivers/media/usb/tm6000/tm6000.h b/drivers/media/usb/tm6000/tm6000.h index 173dcd7a7284..a9ac262fcc93 100644 --- a/drivers/media/usb/tm6000/tm6000.h +++ b/drivers/media/usb/tm6000/tm6000.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "dvb_demux.h" @@ -222,6 +223,8 @@ struct tm6000_core { struct video_device *radio_dev; struct tm6000_dmaqueue vidq; struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl_handler radio_ctrl_handler; int input; struct tm6000_input vinput[3]; /* video input */ -- cgit v1.2.3 From 770056c47fbb5c4d892d1cd55ae5ec68cf44c2b4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 11 Sep 2012 11:50:37 -0300 Subject: [media] tm6000: add support for control events and prio handling Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-video.c | 31 +++++++++++++++++++++++-------- drivers/media/usb/tm6000/tm6000.h | 2 ++ 2 files changed, 25 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index 4329fbcf2de2..25202a71d65e 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -1350,6 +1351,7 @@ static int __tm6000_open(struct file *file) return -ENOMEM; } + v4l2_fh_init(&fh->fh, vdev); file->private_data = fh; fh->dev = dev; fh->radio = radio; @@ -1393,6 +1395,7 @@ static int __tm6000_open(struct file *file) tm6000_prepare_isoc(dev); tm6000_start_thread(dev); } + v4l2_fh_add(&fh->fh); return 0; } @@ -1433,29 +1436,35 @@ tm6000_read(struct file *file, char __user *data, size_t count, loff_t *pos) static unsigned int __tm6000_poll(struct file *file, struct poll_table_struct *wait) { + unsigned long req_events = poll_requested_events(wait); struct tm6000_fh *fh = file->private_data; struct tm6000_buffer *buf; + int res = 0; + if (v4l2_event_pending(&fh->fh)) + res = POLLPRI; + else if (req_events & POLLPRI) + poll_wait(file, &fh->fh.wait, wait); if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) - return POLLERR; + return res | POLLERR; if (!!is_res_streaming(fh->dev, fh)) - return POLLERR; + return res | POLLERR; if (!is_res_read(fh->dev, fh)) { /* streaming capture */ if (list_empty(&fh->vb_vidq.stream)) - return POLLERR; + return res | POLLERR; buf = list_entry(fh->vb_vidq.stream.next, struct tm6000_buffer, vb.stream); - } else { + } else if (req_events & (POLLIN | POLLRDNORM)) { /* read() capture */ - return videobuf_poll_stream(file, &fh->vb_vidq, wait); + return res | videobuf_poll_stream(file, &fh->vb_vidq, wait); } poll_wait(file, &buf->vb.done, wait); if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) - return POLLIN | POLLRDNORM; - return 0; + return res | POLLIN | POLLRDNORM; + return res; } static unsigned int tm6000_poll(struct file *file, struct poll_table_struct *wait) @@ -1505,7 +1514,8 @@ static int tm6000_release(struct file *file) if (!fh->radio) videobuf_mmap_free(&fh->vb_vidq); } - + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); kfree(fh); mutex_unlock(&dev->lock); @@ -1555,6 +1565,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device tm6000_template = { @@ -1579,6 +1591,8 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_s_tuner = radio_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device tm6000_radio_template = { @@ -1607,6 +1621,7 @@ static struct video_device *vdev_init(struct tm6000_core *dev, vfd->release = video_device_release; vfd->debug = tm6000_debug; vfd->lock = &dev->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); diff --git a/drivers/media/usb/tm6000/tm6000.h b/drivers/media/usb/tm6000/tm6000.h index a9ac262fcc93..08bd0740dd23 100644 --- a/drivers/media/usb/tm6000/tm6000.h +++ b/drivers/media/usb/tm6000/tm6000.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "dvb_demux.h" @@ -290,6 +291,7 @@ struct tm6000_ops { }; struct tm6000_fh { + struct v4l2_fh fh; struct tm6000_core *dev; unsigned int radio; -- cgit v1.2.3 From e618578dd828458f35c2cd16edfa7a1dba07ce43 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 11 Sep 2012 11:51:02 -0300 Subject: [media] tm6000: set colorspace field Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-video.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index 25202a71d65e..ac2588535f48 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -913,6 +913,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.height = fh->height; f->fmt.pix.field = fh->vb_vidq.field; f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; f->fmt.pix.bytesperline = (f->fmt.pix.width * fh->fmt->depth) >> 3; f->fmt.pix.sizeimage = @@ -967,6 +968,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; } -- cgit v1.2.3 From 52dec548d4244d9ec86c58e5696e1d4ee98c7b3c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 11 Sep 2012 11:53:51 -0300 Subject: [media] tm6000: add poll op for radio device node Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-video.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index ac2588535f48..f41dbb187caf 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -1583,6 +1583,7 @@ static struct video_device tm6000_template = { static const struct v4l2_file_operations radio_fops = { .owner = THIS_MODULE, .open = tm6000_open, + .poll = v4l2_ctrl_poll, .release = tm6000_release, .unlocked_ioctl = video_ioctl2, }; -- cgit v1.2.3 From ed57256f6fe8882cf6dde8d99f5fac5fd84c5a2d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 1 Feb 2013 09:08:46 -0300 Subject: [media] tm6000: fix G/TRY_FMT Two fixes: - the priv field wasn't set to 0. - only V4L2_FIELD_INTERLACED is supported. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-video.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index f41dbb187caf..eab23413a909 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -918,6 +918,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, (f->fmt.pix.width * fh->fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.priv = 0; return 0; } @@ -948,12 +949,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, field = f->fmt.pix.field; - if (field == V4L2_FIELD_ANY) - field = V4L2_FIELD_SEQ_TB; - else if (V4L2_FIELD_INTERLACED != field) { - dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Field type invalid.\n"); - return -EINVAL; - } + field = V4L2_FIELD_INTERLACED; tm6000_get_std_res(dev); @@ -963,6 +959,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.width &= ~0x01; f->fmt.pix.field = field; + f->fmt.pix.priv = 0; f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; -- cgit v1.2.3 From 0b302d88534f0811c5f49bfba7aa46c4e1e032b7 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Tue, 22 Jan 2013 01:19:50 -0300 Subject: [media] media: adv7343: accept configuration through platform data The current code was implemented with some default configurations, this default configuration works on board and doesn't work on other. This patch accepts the configuration through platform data and configures the encoder depending on the data passed. Signed-off-by: Lad, Prabhakar Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7343.c | 36 ++++++++++++++++++++++++++----- include/media/adv7343.h | 52 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c index 432eb5f7a0e5..9fc2b985df0e 100644 --- a/drivers/media/i2c/adv7343.c +++ b/drivers/media/i2c/adv7343.c @@ -43,6 +43,7 @@ MODULE_PARM_DESC(debug, "Debug level 0-1"); struct adv7343_state { struct v4l2_subdev sd; struct v4l2_ctrl_handler hdl; + const struct adv7343_platform_data *pdata; u8 reg00; u8 reg01; u8 reg02; @@ -215,12 +216,23 @@ static int adv7343_setoutput(struct v4l2_subdev *sd, u32 output_type) /* Enable Appropriate DAC */ val = state->reg00 & 0x03; - if (output_type == ADV7343_COMPOSITE_ID) - val |= ADV7343_COMPOSITE_POWER_VALUE; - else if (output_type == ADV7343_COMPONENT_ID) - val |= ADV7343_COMPONENT_POWER_VALUE; + /* configure default configuration */ + if (!state->pdata) + if (output_type == ADV7343_COMPOSITE_ID) + val |= ADV7343_COMPOSITE_POWER_VALUE; + else if (output_type == ADV7343_COMPONENT_ID) + val |= ADV7343_COMPONENT_POWER_VALUE; + else + val |= ADV7343_SVIDEO_POWER_VALUE; else - val |= ADV7343_SVIDEO_POWER_VALUE; + val = state->pdata->mode_config.sleep_mode << 0 | + state->pdata->mode_config.pll_control << 1 | + state->pdata->mode_config.dac_3 << 2 | + state->pdata->mode_config.dac_2 << 3 | + state->pdata->mode_config.dac_1 << 4 | + state->pdata->mode_config.dac_6 << 5 | + state->pdata->mode_config.dac_5 << 6 | + state->pdata->mode_config.dac_4 << 7; err = adv7343_write(sd, ADV7343_POWER_MODE_REG, val); if (err < 0) @@ -238,6 +250,17 @@ static int adv7343_setoutput(struct v4l2_subdev *sd, u32 output_type) /* configure SD DAC Output 2 and SD DAC Output 1 bit to zero */ val = state->reg82 & (SD_DAC_1_DI & SD_DAC_2_DI); + + if (state->pdata && state->pdata->sd_config.sd_dac_out1) + val = val | (state->pdata->sd_config.sd_dac_out1 << 1); + else if (state->pdata && !state->pdata->sd_config.sd_dac_out1) + val = val & ~(state->pdata->sd_config.sd_dac_out1 << 1); + + if (state->pdata && state->pdata->sd_config.sd_dac_out2) + val = val | (state->pdata->sd_config.sd_dac_out2 << 2); + else if (state->pdata && !state->pdata->sd_config.sd_dac_out2) + val = val & ~(state->pdata->sd_config.sd_dac_out2 << 2); + err = adv7343_write(sd, ADV7343_SD_MODE_REG2, val); if (err < 0) goto setoutput_exit; @@ -402,6 +425,9 @@ static int adv7343_probe(struct i2c_client *client, if (state == NULL) return -ENOMEM; + /* Copy board specific information here */ + state->pdata = client->dev.platform_data; + state->reg00 = 0x80; state->reg01 = 0x00; state->reg02 = 0x20; diff --git a/include/media/adv7343.h b/include/media/adv7343.h index d6f8a4e1a1fc..944757be49bb 100644 --- a/include/media/adv7343.h +++ b/include/media/adv7343.h @@ -20,4 +20,56 @@ #define ADV7343_COMPONENT_ID (1) #define ADV7343_SVIDEO_ID (2) +/** + * adv7343_power_mode - power mode configuration. + * @sleep_mode: on enable the current consumption is reduced to micro ampere + * level. All DACs and the internal PLL circuit are disabled. + * Registers can be read from and written in sleep mode. + * @pll_control: PLL and oversampling control. This control allows internal + * PLL 1 circuit to be powered down and the oversampling to be + * switched off. + * @dac_1: power on/off DAC 1. + * @dac_2: power on/off DAC 2. + * @dac_3: power on/off DAC 3. + * @dac_4: power on/off DAC 4. + * @dac_5: power on/off DAC 5. + * @dac_6: power on/off DAC 6. + * + * Power mode register (Register 0x0), for more info refer REGISTER MAP ACCESS + * section of datasheet[1], table 17 page no 30. + * + * [1] http://www.analog.com/static/imported-files/data_sheets/ADV7342_7343.pdf + */ +struct adv7343_power_mode { + bool sleep_mode; + bool pll_control; + bool dac_1; + bool dac_2; + bool dac_3; + bool dac_4; + bool dac_5; + bool dac_6; +}; + +/** + * struct adv7343_sd_config - SD Only Output Configuration. + * @sd_dac_out1: Configure SD DAC Output 1. + * @sd_dac_out2: Configure SD DAC Output 2. + */ +struct adv7343_sd_config { + /* SD only Output Configuration */ + bool sd_dac_out1; + bool sd_dac_out2; +}; + +/** + * struct adv7343_platform_data - Platform data values and access functions. + * @mode_config: Configuration for power mode. + * @sd_config: SD Only Configuration. + */ +struct adv7343_platform_data { + struct adv7343_power_mode mode_config; + struct adv7343_sd_config sd_config; +}; + #endif /* End of #ifndef ADV7343_H */ -- cgit v1.2.3 From 6f2627c29f6619ebdbc6de8934b33c23b73be8e6 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Sun, 6 Jan 2013 13:19:43 -0300 Subject: [media] winbond-cir: only enable higher sample resolution if needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A sample resolution of 2us generates more than 300 interrupts per key and this resolution is not needed unless carrier reports are enabled. Revert to a resolution of 10us unless carrier reports are needed. This generates up to a fifth of the interrupts. Signed-off-by: Sean Young Acked-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/winbond-cir.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index 85424856ae7f..535a18dccbd0 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -154,6 +154,8 @@ #define WBCIR_CNTR_R 0x02 /* Invert TX */ #define WBCIR_IRTX_INV 0x04 +/* Receiver oversampling */ +#define WBCIR_RX_T_OV 0x40 /* Valid banks for the SP3 UART */ enum wbcir_bank { @@ -394,7 +396,8 @@ wbcir_irq_rx(struct wbcir_data *data, struct pnp_dev *device) if (data->rxstate == WBCIR_RXSTATE_ERROR) continue; - duration = ((irdata & 0x7F) + 1) * 2; + duration = ((irdata & 0x7F) + 1) * + (data->carrier_report_enabled ? 2 : 10); rawir.pulse = irdata & 0x80 ? false : true; rawir.duration = US_TO_NS(duration); @@ -550,6 +553,17 @@ wbcir_set_carrier_report(struct rc_dev *dev, int enable) wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL, WBCIR_CNTR_EN, WBCIR_CNTR_EN | WBCIR_CNTR_R); + /* Set a higher sampling resolution if carrier reports are enabled */ + wbcir_select_bank(data, WBCIR_BANK_2); + data->dev->rx_resolution = US_TO_NS(enable ? 2 : 10); + outb(enable ? 0x03 : 0x0f, data->sbase + WBCIR_REG_SP3_BGDL); + outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH); + + /* Enable oversampling if carrier reports are enabled */ + wbcir_select_bank(data, WBCIR_BANK_7); + wbcir_set_bits(data->sbase + WBCIR_REG_SP3_RCCFG, + enable ? WBCIR_RX_T_OV : 0, WBCIR_RX_T_OV); + data->carrier_report_enabled = enable; spin_unlock_irqrestore(&data->spinlock, flags); @@ -931,8 +945,8 @@ wbcir_init_hw(struct wbcir_data *data) /* prescaler 1.0, tx/rx fifo lvl 16 */ outb(0x30, data->sbase + WBCIR_REG_SP3_EXCR2); - /* Set baud divisor to sample every 2 ns */ - outb(0x03, data->sbase + WBCIR_REG_SP3_BGDL); + /* Set baud divisor to sample every 10 us */ + outb(0x0f, data->sbase + WBCIR_REG_SP3_BGDL); outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH); /* Set CEIR mode */ @@ -941,12 +955,9 @@ wbcir_init_hw(struct wbcir_data *data) inb(data->sbase + WBCIR_REG_SP3_LSR); /* Clear LSR */ inb(data->sbase + WBCIR_REG_SP3_MSR); /* Clear MSR */ - /* - * Disable RX demod, enable run-length enc/dec, set freq span and - * enable over-sampling - */ + /* Disable RX demod, enable run-length enc/dec, set freq span */ wbcir_select_bank(data, WBCIR_BANK_7); - outb(0xd0, data->sbase + WBCIR_REG_SP3_RCCFG); + outb(0x90, data->sbase + WBCIR_REG_SP3_RCCFG); /* Disable timer */ wbcir_select_bank(data, WBCIR_BANK_4); -- cgit v1.2.3 From d0aab6564d12add07572141ceb34c60046e93ac3 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Sun, 6 Jan 2013 13:19:44 -0300 Subject: [media] iguanair: ensure transmission mask is initialized Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/iguanair.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c index b99b096d8a8f..b8b3e37e5c57 100644 --- a/drivers/media/rc/iguanair.c +++ b/drivers/media/rc/iguanair.c @@ -512,6 +512,7 @@ static int iguanair_probe(struct usb_interface *intf, rc->rx_resolution = RX_RESOLUTION; iguanair_set_tx_carrier(rc, 38000); + iguanair_set_tx_mask(rc, 0); ret = rc_register_device(rc); if (ret < 0) { -- cgit v1.2.3 From c6a3ea570e5d0ca20069cce5d537c845128b70f4 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Mon, 14 Jan 2013 05:51:44 -0300 Subject: [media] iguanair: intermittent initialization failure On cold boot the device does not initialize until the first packet is received, and that packet is not processed. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/iguanair.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c index b8b3e37e5c57..a4ab2e6b3f82 100644 --- a/drivers/media/rc/iguanair.c +++ b/drivers/media/rc/iguanair.c @@ -58,6 +58,7 @@ struct iguanair { char phys[64]; }; +#define CMD_NOP 0x00 #define CMD_GET_VERSION 0x01 #define CMD_GET_BUFSIZE 0x11 #define CMD_GET_FEATURES 0x10 @@ -196,6 +197,10 @@ static void iguanair_irq_out(struct urb *urb) if (urb->status) dev_dbg(ir->dev, "Error: out urb status = %d\n", urb->status); + + /* if we sent an nop packet, do not expect a response */ + if (urb->status == 0 && ir->packet->header.cmd == CMD_NOP) + complete(&ir->completion); } static int iguanair_send(struct iguanair *ir, unsigned size) @@ -219,10 +224,17 @@ static int iguanair_get_features(struct iguanair *ir) { int rc; + /* + * On cold boot, the iguanair initializes on the first packet + * received but does not process that packet. Send an empty + * packet. + */ ir->packet->header.start = 0; ir->packet->header.direction = DIR_OUT; - ir->packet->header.cmd = CMD_GET_VERSION; + ir->packet->header.cmd = CMD_NOP; + iguanair_send(ir, sizeof(ir->packet->header)); + ir->packet->header.cmd = CMD_GET_VERSION; rc = iguanair_send(ir, sizeof(ir->packet->header)); if (rc) { dev_info(ir->dev, "failed to get version\n"); @@ -255,19 +267,14 @@ static int iguanair_get_features(struct iguanair *ir) ir->packet->header.cmd = CMD_GET_FEATURES; rc = iguanair_send(ir, sizeof(ir->packet->header)); - if (rc) { + if (rc) dev_info(ir->dev, "failed to get features\n"); - goto out; - } - out: return rc; } static int iguanair_receiver(struct iguanair *ir, bool enable) { - int rc; - ir->packet->header.start = 0; ir->packet->header.direction = DIR_OUT; ir->packet->header.cmd = enable ? CMD_RECEIVER_ON : CMD_RECEIVER_OFF; @@ -275,9 +282,7 @@ static int iguanair_receiver(struct iguanair *ir, bool enable) if (enable) ir_raw_event_reset(ir->rc); - rc = iguanair_send(ir, sizeof(ir->packet->header)); - - return rc; + return iguanair_send(ir, sizeof(ir->packet->header)); } /* -- cgit v1.2.3 From 7e20f6bfc47992d93b36f4ed068782f8726b75a3 Mon Sep 17 00:00:00 2001 From: Nickolai Zeldovich Date: Sat, 5 Jan 2013 15:13:05 -0300 Subject: [media] drivers/media/usb/dvb-usb/dib0700_core.c: fix left shift Fix bug introduced in 7757ddda6f4febbc52342d82440dd4f7a7d4f14f, where instead of bit-negating the bitmask, the bit position was bit-negated instead. Signed-off-by: Nickolai Zeldovich Cc: Olivier Grenie Cc: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/dib0700_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c index bf2a908d74cf..bd6a43785d5e 100644 --- a/drivers/media/usb/dvb-usb/dib0700_core.c +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -584,7 +584,7 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) if (onoff) st->channel_state |= 1 << (adap->id); else - st->channel_state |= 1 << ~(adap->id); + st->channel_state &= ~(1 << (adap->id)); } else { if (onoff) st->channel_state |= 1 << (adap->fe_adap[0].stream.props.endpoint-2); -- cgit v1.2.3 From a46edeb0f6098088f316c3543474784f8108508e Mon Sep 17 00:00:00 2001 From: Nickolai Zeldovich Date: Sun, 6 Jan 2013 21:52:03 -0300 Subject: [media] media: cx18, ivtv: eliminate unnecessary array index checks The idx values passed to cx18_i2c_register() and ivtv_i2c_register() by cx18_init_subdevs() and ivtv_load_and_init_modules() respectively are always in-range, based on how the hw_all bitmask is populated. Previously, the checks were already ineffective because arrays were being dereferenced using the index before the check. Acked-by: Andy Walls Signed-off-by: Nickolai Zeldovich Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx18/cx18-i2c.c | 3 --- drivers/media/pci/ivtv/ivtv-i2c.c | 2 -- 2 files changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/cx18/cx18-i2c.c b/drivers/media/pci/cx18/cx18-i2c.c index d61ac6393e7e..4af8cd6df95d 100644 --- a/drivers/media/pci/cx18/cx18-i2c.c +++ b/drivers/media/pci/cx18/cx18-i2c.c @@ -116,9 +116,6 @@ int cx18_i2c_register(struct cx18 *cx, unsigned idx) const char *type = hw_devicenames[idx]; u32 hw = 1 << idx; - if (idx >= ARRAY_SIZE(hw_addrs)) - return -1; - if (hw == CX18_HW_TUNER) { /* special tuner group handling */ sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, diff --git a/drivers/media/pci/ivtv/ivtv-i2c.c b/drivers/media/pci/ivtv/ivtv-i2c.c index a1811054fde4..ceed2d87abfd 100644 --- a/drivers/media/pci/ivtv/ivtv-i2c.c +++ b/drivers/media/pci/ivtv/ivtv-i2c.c @@ -267,8 +267,6 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx) const char *type = hw_devicenames[idx]; u32 hw = 1 << idx; - if (idx >= ARRAY_SIZE(hw_addrs)) - return -1; if (hw == IVTV_HW_TUNER) { /* special tuner handling */ sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, type, 0, -- cgit v1.2.3 From bb71b14d80bde8484ae63ee09d969c72d8615e0c Mon Sep 17 00:00:00 2001 From: Nickolai Zeldovich Date: Mon, 7 Jan 2013 22:28:05 -0300 Subject: [media] drivers/media/pci: use memmove for overlapping regions Change several memcpy() to memmove() in cases when the regions are definitely overlapping; memcpy() of overlapping regions is undefined behavior in C and can produce different results depending on the compiler, the memcpy implementation, etc. Cc: Andy Walls Signed-off-by: Nickolai Zeldovich Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/bt8xx/dst_ca.c | 4 ++-- drivers/media/pci/cx18/cx18-vbi.c | 2 +- drivers/media/pci/ivtv/ivtv-vbi.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/bt8xx/dst_ca.c b/drivers/media/pci/bt8xx/dst_ca.c index 7d96fab7d246..0e788fca992c 100644 --- a/drivers/media/pci/bt8xx/dst_ca.c +++ b/drivers/media/pci/bt8xx/dst_ca.c @@ -180,11 +180,11 @@ static int ca_get_app_info(struct dst_state *state) put_command_and_length(&state->messages[0], CA_APP_INFO, length); // Copy application_type, application_manufacturer and manufacturer_code - memcpy(&state->messages[4], &state->messages[7], 5); + memmove(&state->messages[4], &state->messages[7], 5); // Set string length and copy string state->messages[9] = str_length; - memcpy(&state->messages[10], &state->messages[12], str_length); + memmove(&state->messages[10], &state->messages[12], str_length); return 0; } diff --git a/drivers/media/pci/cx18/cx18-vbi.c b/drivers/media/pci/cx18/cx18-vbi.c index 6d3121ff45a2..add99642f1e2 100644 --- a/drivers/media/pci/cx18/cx18-vbi.c +++ b/drivers/media/pci/cx18/cx18-vbi.c @@ -84,7 +84,7 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp) (the max size of the VBI data is 36 * 43 + 4 bytes). So in this case we use the magic number 'ITV0'. */ memcpy(dst + sd, "ITV0", 4); - memcpy(dst + sd + 4, dst + sd + 12, line * 43); + memmove(dst + sd + 4, dst + sd + 12, line * 43); size = 4 + ((43 * line + 3) & ~3); } else { memcpy(dst + sd, "itv0", 4); diff --git a/drivers/media/pci/ivtv/ivtv-vbi.c b/drivers/media/pci/ivtv/ivtv-vbi.c index 293db806d936..3c156bc70fb4 100644 --- a/drivers/media/pci/ivtv/ivtv-vbi.c +++ b/drivers/media/pci/ivtv/ivtv-vbi.c @@ -224,7 +224,7 @@ static void copy_vbi_data(struct ivtv *itv, int lines, u32 pts_stamp) (the max size of the VBI data is 36 * 43 + 4 bytes). So in this case we use the magic number 'ITV0'. */ memcpy(dst + sd, "ITV0", 4); - memcpy(dst + sd + 4, dst + sd + 12, line * 43); + memmove(dst + sd + 4, dst + sd + 12, line * 43); size = 4 + ((43 * line + 3) & ~3); } else { memcpy(dst + sd, "itv0", 4); @@ -532,7 +532,7 @@ void ivtv_vbi_work_handler(struct ivtv *itv) while (vi->cc_payload_idx) { cc = vi->cc_payload[0]; - memcpy(vi->cc_payload, vi->cc_payload + 1, + memmove(vi->cc_payload, vi->cc_payload + 1, sizeof(vi->cc_payload) - sizeof(vi->cc_payload[0])); vi->cc_payload_idx--; if (vi->cc_payload_idx && cc.odd[0] == 0x80 && cc.odd[1] == 0x80) -- cgit v1.2.3 From f1ec57239f9bf19b2ccc0535cb8bc17dc4aaaab2 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 10 Jan 2013 05:00:57 -0300 Subject: [media] staging: go7007: print the audio input type Smatch complains that the "Audio input:" printk isn't reachable. Hiding the "return 0;" behind another statement is a style violation. It looks like audio_input is normally configured so I've enabled the print statement. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/s2250-board.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/media/go7007/s2250-board.c b/drivers/staging/media/go7007/s2250-board.c index d60e06596375..37400bfa6ccb 100644 --- a/drivers/staging/media/go7007/s2250-board.c +++ b/drivers/staging/media/go7007/s2250-board.c @@ -534,7 +534,7 @@ static int s2250_log_status(struct v4l2_subdev *sd) v4l2_info(sd, "Brightness: %d\n", state->brightness); v4l2_info(sd, "Contrast: %d\n", state->contrast); v4l2_info(sd, "Saturation: %d\n", state->saturation); - v4l2_info(sd, "Hue: %d\n", state->hue); return 0; + v4l2_info(sd, "Hue: %d\n", state->hue); v4l2_info(sd, "Audio input: %s\n", state->audio_input == 0 ? "Line In" : state->audio_input == 1 ? "Mic" : state->audio_input == 2 ? "Mic Boost" : -- cgit v1.2.3 From f62436a96a0678a4e8bc54e7b987be72977af9ce Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 11 Jan 2013 02:48:41 -0300 Subject: [media] cx231xx: add a missing break statement My static checker complains about the fall through here. From the context it looks like we should add a break statement. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/cx231xx-video.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 93dfc182ec51..06376d904c9f 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -1751,6 +1751,7 @@ static int vidioc_s_register(struct file *file, void *priv, 0x02, (u16)reg->reg, 1, value, 1, 2); + break; case 0x322: ret = cx231xx_write_i2c_master(dev, -- cgit v1.2.3 From 0b3966e40c99afeb89849b5cf7b100a3bb4271cd Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 13 Jan 2013 10:15:08 -0300 Subject: [media] em28xx: add missing IR RC slave address to the list of known i2c devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-i2c.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 9ae8f6051d8e..8532c1d4fd46 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -534,6 +534,7 @@ static struct i2c_client em28xx_client_template = { * incomplete list of known devices */ static char *i2c_devs[128] = { + [0x3e >> 1] = "remote IR sensor", [0x4a >> 1] = "saa7113h", [0x52 >> 1] = "drxk", [0x60 >> 1] = "remote IR sensor", -- cgit v1.2.3 From 59cf17d84353e175d40ff10ee9fa221d304d0836 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 13 Jan 2013 10:20:39 -0300 Subject: [media] em28xx-input: remove dead code line from em28xx_get_key_em_haup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Field 'old' of struct IR_i2c is used nowhere in module ir-kbd-i2c. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 07f6030d7455..f554a5291619 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -125,8 +125,6 @@ static int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) if (buf[1] == 0xff) return 0; - ir->old = buf[1]; - /* * Rearranges bits to the right order. * The bit order were determined experimentally by using -- cgit v1.2.3 From 62ec3f86ff86483ae27b9411179b8ded74558c19 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 13 Jan 2013 10:20:40 -0300 Subject: [media] em28xx: remove i2cdprintk() messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't report any key/scan codes or errors inside the key polling functions for internal IR RC devices, just in the key handling fucntions. Do the same for external devices. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index f554a5291619..edcd6978f2e1 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -40,11 +40,6 @@ MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); #define MODULE_NAME "em28xx" -#define i2cdprintk(fmt, arg...) \ - if (ir_debug) { \ - printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ - } - #define dprintk(fmt, arg...) \ if (ir_debug) { \ printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ @@ -86,17 +81,13 @@ static int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char b; /* poll IR chip */ - if (1 != i2c_master_recv(ir->c, &b, 1)) { - i2cdprintk("read error\n"); + if (1 != i2c_master_recv(ir->c, &b, 1)) return -EIO; - } /* it seems that 0xFE indicates that a button is still hold down, while 0xff indicates that no button is hold down. 0xfe sequences are sometimes interrupted by 0xFF */ - i2cdprintk("key %02x\n", b); - if (b == 0xff) return 0; @@ -147,9 +138,6 @@ static int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ((buf[1] & 0x40) ? 0x0200 : 0) | /* 0000 0010 */ ((buf[1] & 0x80) ? 0x0100 : 0); /* 0000 0001 */ - i2cdprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x%02x)\n", - code, buf[1], buf[0]); - /* return key */ *ir_key = code; *ir_raw = code; @@ -163,12 +151,9 @@ static int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, /* poll IR chip */ - if (3 != i2c_master_recv(ir->c, buf, 3)) { - i2cdprintk("read error\n"); + if (3 != i2c_master_recv(ir->c, buf, 3)) return -EIO; - } - i2cdprintk("key %02x\n", buf[2]&0x3f); if (buf[0] != 0x00) return 0; @@ -188,19 +173,15 @@ static int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, { .addr = ir->c->addr, .flags = I2C_M_RD, .buf = &keydetect, .len = 1} }; subaddr = 0x10; - if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { - i2cdprintk("read error\n"); + if (2 != i2c_transfer(ir->c->adapter, msg, 2)) return -EIO; - } if (keydetect == 0x00) return 0; subaddr = 0x00; msg[1].buf = &key; - if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { - i2cdprintk("read error\n"); - return -EIO; - } + if (2 != i2c_transfer(ir->c->adapter, msg, 2)) + return -EIO; if (key == 0x00) return 0; -- cgit v1.2.3 From 768da3dbcf50b697e5e7a921492b7f0d2cd8a8fb Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 13 Jan 2013 10:20:41 -0300 Subject: [media] em28xx: get rid of the dependency on module ir-kbd-i2c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already have the key polling functions and the polling infrastructure in em28xx-input, so we can easily get rid of the dependency on module ir-kbd-i2c. For maximum safety, do not touch the key reporting mechanism for those devices. Code size could be improved further but would have minor peformance impacts. Tested with device "Terratec Cinergy 200 USB" (EM2800_BOARD_TERRATEC_CINERGY_200) [mchehab@redhat.com: Fix two checkpatch.pl warnings: ERROR: "foo * bar" should be "foo *bar" (line 465) WARNING: kfree(NULL) is safe this check is probably not required (line 725)] Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 219 +++++++++++++++++++------------- drivers/media/usb/em28xx/em28xx.h | 3 - 2 files changed, 134 insertions(+), 88 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index edcd6978f2e1..72cb0cfad8d0 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -62,13 +62,17 @@ struct em28xx_IR { char name[32]; char phys[32]; - /* poll external decoder */ + /* poll decoder */ int polling; struct delayed_work work; unsigned int full_code:1; unsigned int last_readcount; u64 rc_type; + /* external device (if used) */ + struct i2c_client *i2c_dev; + + int (*get_key_i2c)(struct i2c_client *, u32 *, u32 *); int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); }; @@ -76,12 +80,13 @@ struct em28xx_IR { I2C IR based get keycodes - should be used with ir-kbd-i2c **********************************************************/ -static int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_terratec(struct i2c_client *i2c_dev, + u32 *ir_key, u32 *ir_raw) { unsigned char b; /* poll IR chip */ - if (1 != i2c_master_recv(ir->c, &b, 1)) + if (1 != i2c_master_recv(i2c_dev, &b, 1)) return -EIO; /* it seems that 0xFE indicates that a button is still hold @@ -100,14 +105,15 @@ static int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } -static int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_em_haup(struct i2c_client *i2c_dev, + u32 *ir_key, u32 *ir_raw) { unsigned char buf[2]; u16 code; int size; /* poll IR chip */ - size = i2c_master_recv(ir->c, buf, sizeof(buf)); + size = i2c_master_recv(i2c_dev, buf, sizeof(buf)); if (size != 2) return -EIO; @@ -144,14 +150,14 @@ static int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } -static int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw) +static int em28xx_get_key_pinnacle_usb_grey(struct i2c_client *i2c_dev, + u32 *ir_key, u32 *ir_raw) { unsigned char buf[3]; /* poll IR chip */ - if (3 != i2c_master_recv(ir->c, buf, 3)) + if (3 != i2c_master_recv(i2c_dev, buf, 3)) return -EIO; if (buf[0] != 0x00) @@ -163,24 +169,24 @@ static int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, return 1; } -static int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw) +static int em28xx_get_key_winfast_usbii_deluxe(struct i2c_client *i2c_dev, + u32 *ir_key, u32 *ir_raw) { unsigned char subaddr, keydetect, key; - struct i2c_msg msg[] = { { .addr = ir->c->addr, .flags = 0, .buf = &subaddr, .len = 1}, + struct i2c_msg msg[] = { { .addr = i2c_dev->addr, .flags = 0, .buf = &subaddr, .len = 1}, - { .addr = ir->c->addr, .flags = I2C_M_RD, .buf = &keydetect, .len = 1} }; + { .addr = i2c_dev->addr, .flags = I2C_M_RD, .buf = &keydetect, .len = 1} }; subaddr = 0x10; - if (2 != i2c_transfer(ir->c->adapter, msg, 2)) + if (2 != i2c_transfer(i2c_dev->adapter, msg, 2)) return -EIO; if (keydetect == 0x00) return 0; subaddr = 0x00; msg[1].buf = &key; - if (2 != i2c_transfer(ir->c->adapter, msg, 2)) + if (2 != i2c_transfer(i2c_dev->adapter, msg, 2)) return -EIO; if (key == 0x00) return 0; @@ -280,6 +286,24 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, Polling code for em28xx **********************************************************/ +static int em28xx_i2c_ir_handle_key(struct em28xx_IR *ir) +{ + static u32 ir_key, ir_raw; + int rc; + + rc = ir->get_key_i2c(ir->i2c_dev, &ir_key, &ir_raw); + if (rc < 0) { + dprintk("ir->get_key_i2c() failed: %d\n", rc); + return rc; + } + + if (rc) { + dprintk("%s: keycode = 0x%04x\n", __func__, ir_key); + rc_keydown(ir->rc, ir_key, 0); + } + return 0; +} + static void em28xx_ir_handle_key(struct em28xx_IR *ir) { int result; @@ -288,7 +312,7 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) /* read the registers containing the IR status */ result = ir->get_key(ir, &poll_result); if (unlikely(result < 0)) { - dprintk("ir->get_key() failed %d\n", result); + dprintk("ir->get_key() failed: %d\n", result); return; } @@ -318,6 +342,14 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) } } +static void em28xx_i2c_ir_work(struct work_struct *work) +{ + struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work); + + em28xx_i2c_ir_handle_key(ir); + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); +} + static void em28xx_ir_work(struct work_struct *work) { struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work); @@ -330,7 +362,10 @@ static int em28xx_ir_start(struct rc_dev *rc) { struct em28xx_IR *ir = rc->priv; - INIT_DELAYED_WORK(&ir->work, em28xx_ir_work); + if (ir->i2c_dev) /* external i2c device */ + INIT_DELAYED_WORK(&ir->work, em28xx_i2c_ir_work); + else /* internal device */ + INIT_DELAYED_WORK(&ir->work, em28xx_ir_work); schedule_delayed_work(&ir->work, 0); return 0; @@ -427,49 +462,33 @@ static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) } } -static void em28xx_register_i2c_ir(struct em28xx *dev) +static struct i2c_client *em28xx_probe_i2c_ir(struct em28xx *dev) { + int i = 0; + struct i2c_client *i2c_dev = NULL; /* Leadtek winfast tv USBII deluxe can find a non working IR-device */ /* at address 0x18, so if that address is needed for another board in */ /* the future, please put it after 0x1f. */ - struct i2c_board_info info; const unsigned short addr_list[] = { 0x1f, 0x30, 0x47, I2C_CLIENT_END }; - memset(&info, 0, sizeof(struct i2c_board_info)); - memset(&dev->init_data, 0, sizeof(dev->init_data)); - strlcpy(info.type, "ir_video", I2C_NAME_SIZE); - - /* detect & configure */ - switch (dev->model) { - case EM2800_BOARD_TERRATEC_CINERGY_200: - case EM2820_BOARD_TERRATEC_CINERGY_250: - dev->init_data.ir_codes = RC_MAP_EM_TERRATEC; - dev->init_data.get_key = em28xx_get_key_terratec; - dev->init_data.name = "Terratec Cinergy 200/250"; - break; - case EM2820_BOARD_PINNACLE_USB_2: - dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; - dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; - dev->init_data.name = "Pinnacle USB2"; - break; - case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: - dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; - dev->init_data.get_key = em28xx_get_key_em_haup; - dev->init_data.name = "WinTV USB2"; - dev->init_data.type = RC_BIT_RC5; - break; - case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: - dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE; - dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; - dev->init_data.name = "Winfast TV USBII Deluxe"; - break; + while (addr_list[i] != I2C_CLIENT_END) { + if (i2c_probe_func_quick_read(&dev->i2c_adap, addr_list[i]) == 1) { + i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL); + if (i2c_dev) { + i2c_dev->addr = addr_list[i]; + i2c_dev->adapter = &dev->i2c_adap; + /* NOTE: as long as we don't register the device + * at the i2c subsystem, no other fields need to + * be set up */ + } + break; + } + i++; } - if (dev->init_data.name) - info.platform_data = &dev->init_data; - i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL); + return i2c_dev; } /********************************************************** @@ -565,19 +584,21 @@ static int em28xx_ir_init(struct em28xx *dev) struct rc_dev *rc; int err = -ENOMEM; u64 rc_type; + struct i2c_client *i2c_rc_dev = NULL; if (dev->board.has_snapshot_button) em28xx_register_snapshot_button(dev); if (dev->board.has_ir_i2c) { - em28xx_register_i2c_ir(dev); -#if defined(CONFIG_MODULES) && defined(MODULE) - request_module("ir-kbd-i2c"); -#endif - return 0; + i2c_rc_dev = em28xx_probe_i2c_ir(dev); + if (!i2c_rc_dev) { + dev->board.has_ir_i2c = 0; + em28xx_warn("No i2c IR remote control device found.\n"); + return -ENODEV; + } } - if (dev->board.ir_codes == NULL) { + if (dev->board.ir_codes == NULL && !dev->board.has_ir_i2c) { /* No remote control support */ em28xx_warn("Remote control support is not available for " "this card.\n"); @@ -594,45 +615,70 @@ static int em28xx_ir_init(struct em28xx *dev) dev->ir = ir; ir->rc = rc; - /* - * em2874 supports more protocols. For now, let's just announce - * the two protocols that were already tested - */ - rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC; rc->priv = ir; - rc->change_protocol = em28xx_ir_change_protocol; rc->open = em28xx_ir_start; rc->close = em28xx_ir_stop; - switch (dev->chip_id) { - case CHIP_ID_EM2860: - case CHIP_ID_EM2883: - rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC; - ir->get_key = default_polling_getkey; - break; - case CHIP_ID_EM2884: - case CHIP_ID_EM2874: - case CHIP_ID_EM28174: - ir->get_key = em2874_polling_getkey; - rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC | RC_BIT_RC6_0; - break; - default: - err = -ENODEV; - goto error; + if (dev->board.has_ir_i2c) { /* external i2c device */ + switch (dev->model) { + case EM2800_BOARD_TERRATEC_CINERGY_200: + case EM2820_BOARD_TERRATEC_CINERGY_250: + rc->map_name = RC_MAP_EM_TERRATEC; + ir->get_key_i2c = em28xx_get_key_terratec; + break; + case EM2820_BOARD_PINNACLE_USB_2: + rc->map_name = RC_MAP_PINNACLE_GREY; + ir->get_key_i2c = em28xx_get_key_pinnacle_usb_grey; + break; + case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: + rc->map_name = RC_MAP_HAUPPAUGE; + ir->get_key_i2c = em28xx_get_key_em_haup; + rc->allowed_protos = RC_BIT_RC5; + break; + case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: + rc->map_name = RC_MAP_WINFAST_USBII_DELUXE; + ir->get_key_i2c = em28xx_get_key_winfast_usbii_deluxe; + break; + default: + err = -ENODEV; + goto error; + } + + ir->i2c_dev = i2c_rc_dev; + } else { /* internal device */ + switch (dev->chip_id) { + case CHIP_ID_EM2860: + case CHIP_ID_EM2883: + rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC; + ir->get_key = default_polling_getkey; + break; + case CHIP_ID_EM2884: + case CHIP_ID_EM2874: + case CHIP_ID_EM28174: + ir->get_key = em2874_polling_getkey; + rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC | + RC_BIT_RC6_0; + break; + default: + err = -ENODEV; + goto error; + } + + rc->change_protocol = em28xx_ir_change_protocol; + rc->map_name = dev->board.ir_codes; + + /* By default, keep protocol field untouched */ + rc_type = RC_BIT_UNKNOWN; + err = em28xx_ir_change_protocol(rc, &rc_type); + if (err) + goto error; } - /* By default, keep protocol field untouched */ - rc_type = RC_BIT_UNKNOWN; - err = em28xx_ir_change_protocol(rc, &rc_type); - if (err) - goto error; - /* This is how often we ask the chip for IR information */ ir->polling = 100; /* ms */ /* init input device */ - snprintf(ir->name, sizeof(ir->name), "em28xx IR (%s)", - dev->name); + snprintf(ir->name, sizeof(ir->name), "em28xx IR (%s)", dev->name); usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); strlcat(ir->phys, "/input0", sizeof(ir->phys)); @@ -644,7 +690,6 @@ static int em28xx_ir_init(struct em28xx *dev) rc->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); rc->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct); rc->dev.parent = &dev->udev->dev; - rc->map_name = dev->board.ir_codes; rc->driver_name = MODULE_NAME; /* all done */ @@ -655,6 +700,8 @@ static int em28xx_ir_init(struct em28xx *dev) return 0; error: + if (ir && ir->i2c_dev) + kfree(ir->i2c_dev); dev->ir = NULL; rc_free_device(rc); kfree(ir); @@ -674,6 +721,8 @@ static int em28xx_ir_fini(struct em28xx *dev) if (ir->rc) rc_unregister_device(ir->rc); + kfree(ir->i2c_dev); + /* done */ kfree(ir); dev->ir = NULL; diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 2aa4b8472a62..5f0b2c59e846 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -640,9 +640,6 @@ struct em28xx { struct delayed_work sbutton_query_work; struct em28xx_dvb *dvb; - - /* I2C keyboard data */ - struct IR_i2c_init_data init_data; }; struct em28xx_ops { -- cgit v1.2.3 From 146b7ee63866cee57620ec08d10250f7fffaf4bc Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 13 Jan 2013 10:20:42 -0300 Subject: [media] em28xx: remove unused parameter ir_raw from i2c RC key polling functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 72cb0cfad8d0..d500e9c2f9e2 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -72,7 +72,7 @@ struct em28xx_IR { /* external device (if used) */ struct i2c_client *i2c_dev; - int (*get_key_i2c)(struct i2c_client *, u32 *, u32 *); + int (*get_key_i2c)(struct i2c_client *, u32 *); int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); }; @@ -80,8 +80,7 @@ struct em28xx_IR { I2C IR based get keycodes - should be used with ir-kbd-i2c **********************************************************/ -static int em28xx_get_key_terratec(struct i2c_client *i2c_dev, - u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_terratec(struct i2c_client *i2c_dev, u32 *ir_key) { unsigned char b; @@ -101,12 +100,10 @@ static int em28xx_get_key_terratec(struct i2c_client *i2c_dev, return 1; *ir_key = b; - *ir_raw = b; return 1; } -static int em28xx_get_key_em_haup(struct i2c_client *i2c_dev, - u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_em_haup(struct i2c_client *i2c_dev, u32 *ir_key) { unsigned char buf[2]; u16 code; @@ -146,12 +143,11 @@ static int em28xx_get_key_em_haup(struct i2c_client *i2c_dev, /* return key */ *ir_key = code; - *ir_raw = code; return 1; } static int em28xx_get_key_pinnacle_usb_grey(struct i2c_client *i2c_dev, - u32 *ir_key, u32 *ir_raw) + u32 *ir_key) { unsigned char buf[3]; @@ -164,13 +160,12 @@ static int em28xx_get_key_pinnacle_usb_grey(struct i2c_client *i2c_dev, return 0; *ir_key = buf[2]&0x3f; - *ir_raw = buf[2]&0x3f; return 1; } static int em28xx_get_key_winfast_usbii_deluxe(struct i2c_client *i2c_dev, - u32 *ir_key, u32 *ir_raw) + u32 *ir_key) { unsigned char subaddr, keydetect, key; @@ -192,7 +187,6 @@ static int em28xx_get_key_winfast_usbii_deluxe(struct i2c_client *i2c_dev, return 0; *ir_key = key; - *ir_raw = key; return 1; } @@ -288,10 +282,10 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, static int em28xx_i2c_ir_handle_key(struct em28xx_IR *ir) { - static u32 ir_key, ir_raw; + static u32 ir_key; int rc; - rc = ir->get_key_i2c(ir->i2c_dev, &ir_key, &ir_raw); + rc = ir->get_key_i2c(ir->i2c_dev, &ir_key); if (rc < 0) { dprintk("ir->get_key_i2c() failed: %d\n", rc); return rc; -- cgit v1.2.3 From 450c7dd65b6c28c8f5b445c75bb55e8b84133bb9 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 13 Jan 2013 10:20:43 -0300 Subject: [media] em28xx: fix a comment and a small coding style issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index d500e9c2f9e2..f3cff2bfb5ba 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -89,8 +89,7 @@ static int em28xx_get_key_terratec(struct i2c_client *i2c_dev, u32 *ir_key) return -EIO; /* it seems that 0xFE indicates that a button is still hold - down, while 0xff indicates that no button is hold - down. 0xfe sequences are sometimes interrupted by 0xFF */ + down, while 0xff indicates that no button is hold down. */ if (b == 0xff) return 0; @@ -170,8 +169,7 @@ static int em28xx_get_key_winfast_usbii_deluxe(struct i2c_client *i2c_dev, unsigned char subaddr, keydetect, key; struct i2c_msg msg[] = { { .addr = i2c_dev->addr, .flags = 0, .buf = &subaddr, .len = 1}, - - { .addr = i2c_dev->addr, .flags = I2C_M_RD, .buf = &keydetect, .len = 1} }; + { .addr = i2c_dev->addr, .flags = I2C_M_RD, .buf = &keydetect, .len = 1} }; subaddr = 0x10; if (2 != i2c_transfer(i2c_dev->adapter, msg, 2)) -- cgit v1.2.3 From 1d968cdaaec2ea994b2656c00b5a4f10d4159fe8 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 13 Jan 2013 10:20:44 -0300 Subject: [media] em28xx: i2c RC devices: minor code size and memory usage optimization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set up the i2c_client locally in em28xx_i2c_ir_handle_key(). Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 42 +++++++++++++-------------------- 1 file changed, 16 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index f3cff2bfb5ba..63b97285ddbd 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -69,8 +69,8 @@ struct em28xx_IR { unsigned int last_readcount; u64 rc_type; - /* external device (if used) */ - struct i2c_client *i2c_dev; + /* i2c slave address of external device (if used) */ + u16 i2c_dev_addr; int (*get_key_i2c)(struct i2c_client *, u32 *); int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); @@ -282,8 +282,12 @@ static int em28xx_i2c_ir_handle_key(struct em28xx_IR *ir) { static u32 ir_key; int rc; + struct i2c_client client; - rc = ir->get_key_i2c(ir->i2c_dev, &ir_key); + client.adapter = &ir->dev->i2c_adap; + client.addr = ir->i2c_dev_addr; + + rc = ir->get_key_i2c(&client, &ir_key); if (rc < 0) { dprintk("ir->get_key_i2c() failed: %d\n", rc); return rc; @@ -354,7 +358,7 @@ static int em28xx_ir_start(struct rc_dev *rc) { struct em28xx_IR *ir = rc->priv; - if (ir->i2c_dev) /* external i2c device */ + if (ir->i2c_dev_addr) /* external i2c device */ INIT_DELAYED_WORK(&ir->work, em28xx_i2c_ir_work); else /* internal device */ INIT_DELAYED_WORK(&ir->work, em28xx_ir_work); @@ -454,10 +458,9 @@ static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) } } -static struct i2c_client *em28xx_probe_i2c_ir(struct em28xx *dev) +static int em28xx_probe_i2c_ir(struct em28xx *dev) { int i = 0; - struct i2c_client *i2c_dev = NULL; /* Leadtek winfast tv USBII deluxe can find a non working IR-device */ /* at address 0x18, so if that address is needed for another board in */ /* the future, please put it after 0x1f. */ @@ -466,21 +469,12 @@ static struct i2c_client *em28xx_probe_i2c_ir(struct em28xx *dev) }; while (addr_list[i] != I2C_CLIENT_END) { - if (i2c_probe_func_quick_read(&dev->i2c_adap, addr_list[i]) == 1) { - i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL); - if (i2c_dev) { - i2c_dev->addr = addr_list[i]; - i2c_dev->adapter = &dev->i2c_adap; - /* NOTE: as long as we don't register the device - * at the i2c subsystem, no other fields need to - * be set up */ - } - break; - } + if (i2c_probe_func_quick_read(&dev->i2c_adap, addr_list[i]) == 1) + return addr_list[i]; i++; } - return i2c_dev; + return -ENODEV; } /********************************************************** @@ -576,14 +570,14 @@ static int em28xx_ir_init(struct em28xx *dev) struct rc_dev *rc; int err = -ENOMEM; u64 rc_type; - struct i2c_client *i2c_rc_dev = NULL; + u16 i2c_rc_dev_addr = 0; if (dev->board.has_snapshot_button) em28xx_register_snapshot_button(dev); if (dev->board.has_ir_i2c) { - i2c_rc_dev = em28xx_probe_i2c_ir(dev); - if (!i2c_rc_dev) { + i2c_rc_dev_addr = em28xx_probe_i2c_ir(dev); + if (!i2c_rc_dev_addr) { dev->board.has_ir_i2c = 0; em28xx_warn("No i2c IR remote control device found.\n"); return -ENODEV; @@ -636,7 +630,7 @@ static int em28xx_ir_init(struct em28xx *dev) goto error; } - ir->i2c_dev = i2c_rc_dev; + ir->i2c_dev_addr = i2c_rc_dev_addr; } else { /* internal device */ switch (dev->chip_id) { case CHIP_ID_EM2860: @@ -692,8 +686,6 @@ static int em28xx_ir_init(struct em28xx *dev) return 0; error: - if (ir && ir->i2c_dev) - kfree(ir->i2c_dev); dev->ir = NULL; rc_free_device(rc); kfree(ir); @@ -713,8 +705,6 @@ static int em28xx_ir_fini(struct em28xx *dev) if (ir->rc) rc_unregister_device(ir->rc); - kfree(ir->i2c_dev); - /* done */ kfree(ir); dev->ir = NULL; -- cgit v1.2.3 From 9b4539bebb86310afdc5563653ec4475ae110088 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 13 Jan 2013 10:20:45 -0300 Subject: [media] em28xx: input: use common work_struct callback function for IR RC key polling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove em28xx_i2c_ir_work() and check the device type in the common callback function em28xx_ir_work() instead. Simplifies em28xx_ir_start(). Reduces the code size with a minor performance drawback. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 63b97285ddbd..1bef990b3f18 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -338,19 +338,14 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) } } -static void em28xx_i2c_ir_work(struct work_struct *work) -{ - struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work); - - em28xx_i2c_ir_handle_key(ir); - schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); -} - static void em28xx_ir_work(struct work_struct *work) { struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work); - em28xx_ir_handle_key(ir); + if (ir->i2c_dev_addr) /* external i2c device */ + em28xx_i2c_ir_handle_key(ir); + else /* internal device */ + em28xx_ir_handle_key(ir); schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); } @@ -358,10 +353,7 @@ static int em28xx_ir_start(struct rc_dev *rc) { struct em28xx_IR *ir = rc->priv; - if (ir->i2c_dev_addr) /* external i2c device */ - INIT_DELAYED_WORK(&ir->work, em28xx_i2c_ir_work); - else /* internal device */ - INIT_DELAYED_WORK(&ir->work, em28xx_ir_work); + INIT_DELAYED_WORK(&ir->work, em28xx_ir_work); schedule_delayed_work(&ir->work, 0); return 0; -- cgit v1.2.3 From 0e3d50bfcbd338254795a700dcff429a96cba1a6 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 5 Feb 2013 11:47:09 -0500 Subject: drm/radeon/dce6: fix display powergating Only enable it when we disable the display rather than at DPMS time since enabling it requires a full modeset to restore the display state. Fixes blank screens in certain cases. Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org --- drivers/gpu/drm/radeon/atombios_crtc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 9175615bbd8a..21a892c6ab9c 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -252,8 +252,6 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode) radeon_crtc->enabled = true; /* adjust pm to dpms changes BEFORE enabling crtcs */ radeon_pm_compute_clocks(rdev); - if (ASIC_IS_DCE6(rdev) && !radeon_crtc->in_mode_set) - atombios_powergate_crtc(crtc, ATOM_DISABLE); atombios_enable_crtc(crtc, ATOM_ENABLE); if (ASIC_IS_DCE3(rdev) && !ASIC_IS_DCE6(rdev)) atombios_enable_crtc_memreq(crtc, ATOM_ENABLE); @@ -271,8 +269,6 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode) atombios_enable_crtc_memreq(crtc, ATOM_DISABLE); atombios_enable_crtc(crtc, ATOM_DISABLE); radeon_crtc->enabled = false; - if (ASIC_IS_DCE6(rdev) && !radeon_crtc->in_mode_set) - atombios_powergate_crtc(crtc, ATOM_ENABLE); /* adjust pm to dpms changes AFTER disabling crtcs */ radeon_pm_compute_clocks(rdev); break; @@ -1844,6 +1840,8 @@ static void atombios_crtc_disable(struct drm_crtc *crtc) int i; atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); + if (ASIC_IS_DCE6(rdev)) + atombios_powergate_crtc(crtc, ATOM_ENABLE); for (i = 0; i < rdev->num_crtc; i++) { if (rdev->mode_info.crtcs[i] && -- cgit v1.2.3 From cf1364b17e94624161775b0e681dd967ef366980 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sun, 13 Jan 2013 15:31:33 -0300 Subject: [media] tuners/xc5000: fix MODE_AIR in xc5000_set_params() There is a missing break so we use XC_RF_MODE_CABLE instead of XC_RF_MODE_AIR. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/xc5000.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index dc93cf338f36..d6be1b613c52 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -785,6 +785,7 @@ static int xc5000_set_params(struct dvb_frontend *fe) return -EINVAL; } priv->rf_mode = XC_RF_MODE_AIR; + break; case SYS_DVBC_ANNEX_A: case SYS_DVBC_ANNEX_C: dprintk(1, "%s() QAM modulation\n", __func__); -- cgit v1.2.3 From d6d71ee4a14ae602db343ec48c491851d7ec5267 Mon Sep 17 00:00:00 2001 From: Jacob Pan Date: Mon, 21 Jan 2013 04:37:57 -0800 Subject: PM: Introduce Intel PowerClamp Driver Intel PowerClamp driver performs synchronized idle injection across all online CPUs. The goal is to maintain a given package level C-state ratio. Compared to other throttling methods already exist in the kernel, such as ACPI PAD (taking CPUs offline) and clock modulation, this is often more efficient in terms of performance per watt. Please refer to Documentation/thermal/intel_powerclamp.txt for more details. Signed-off-by: Arjan van de Ven Signed-off-by: Jacob Pan Signed-off-by: Zhang Rui --- Documentation/thermal/intel_powerclamp.txt | 307 +++++++++++ drivers/thermal/Kconfig | 10 + drivers/thermal/Makefile | 2 + drivers/thermal/intel_powerclamp.c | 794 +++++++++++++++++++++++++++++ 4 files changed, 1113 insertions(+) create mode 100644 Documentation/thermal/intel_powerclamp.txt create mode 100644 drivers/thermal/intel_powerclamp.c (limited to 'drivers') diff --git a/Documentation/thermal/intel_powerclamp.txt b/Documentation/thermal/intel_powerclamp.txt new file mode 100644 index 000000000000..332de4a39b5a --- /dev/null +++ b/Documentation/thermal/intel_powerclamp.txt @@ -0,0 +1,307 @@ + ======================= + INTEL POWERCLAMP DRIVER + ======================= +By: Arjan van de Ven + Jacob Pan + +Contents: + (*) Introduction + - Goals and Objectives + + (*) Theory of Operation + - Idle Injection + - Calibration + + (*) Performance Analysis + - Effectiveness and Limitations + - Power vs Performance + - Scalability + - Calibration + - Comparison with Alternative Techniques + + (*) Usage and Interfaces + - Generic Thermal Layer (sysfs) + - Kernel APIs (TBD) + +============ +INTRODUCTION +============ + +Consider the situation where a system’s power consumption must be +reduced at runtime, due to power budget, thermal constraint, or noise +level, and where active cooling is not preferred. Software managed +passive power reduction must be performed to prevent the hardware +actions that are designed for catastrophic scenarios. + +Currently, P-states, T-states (clock modulation), and CPU offlining +are used for CPU throttling. + +On Intel CPUs, C-states provide effective power reduction, but so far +they’re only used opportunistically, based on workload. With the +development of intel_powerclamp driver, the method of synchronizing +idle injection across all online CPU threads was introduced. The goal +is to achieve forced and controllable C-state residency. + +Test/Analysis has been made in the areas of power, performance, +scalability, and user experience. In many cases, clear advantage is +shown over taking the CPU offline or modulating the CPU clock. + + +=================== +THEORY OF OPERATION +=================== + +Idle Injection +-------------- + +On modern Intel processors (Nehalem or later), package level C-state +residency is available in MSRs, thus also available to the kernel. + +These MSRs are: + #define MSR_PKG_C2_RESIDENCY 0x60D + #define MSR_PKG_C3_RESIDENCY 0x3F8 + #define MSR_PKG_C6_RESIDENCY 0x3F9 + #define MSR_PKG_C7_RESIDENCY 0x3FA + +If the kernel can also inject idle time to the system, then a +closed-loop control system can be established that manages package +level C-state. The intel_powerclamp driver is conceived as such a +control system, where the target set point is a user-selected idle +ratio (based on power reduction), and the error is the difference +between the actual package level C-state residency ratio and the target idle +ratio. + +Injection is controlled by high priority kernel threads, spawned for +each online CPU. + +These kernel threads, with SCHED_FIFO class, are created to perform +clamping actions of controlled duty ratio and duration. Each per-CPU +thread synchronizes its idle time and duration, based on the rounding +of jiffies, so accumulated errors can be prevented to avoid a jittery +effect. Threads are also bound to the CPU such that they cannot be +migrated, unless the CPU is taken offline. In this case, threads +belong to the offlined CPUs will be terminated immediately. + +Running as SCHED_FIFO and relatively high priority, also allows such +scheme to work for both preemptable and non-preemptable kernels. +Alignment of idle time around jiffies ensures scalability for HZ +values. This effect can be better visualized using a Perf timechart. +The following diagram shows the behavior of kernel thread +kidle_inject/cpu. During idle injection, it runs monitor/mwait idle +for a given "duration", then relinquishes the CPU to other tasks, +until the next time interval. + +The NOHZ schedule tick is disabled during idle time, but interrupts +are not masked. Tests show that the extra wakeups from scheduler tick +have a dramatic impact on the effectiveness of the powerclamp driver +on large scale systems (Westmere system with 80 processors). + +CPU0 + ____________ ____________ +kidle_inject/0 | sleep | mwait | sleep | + _________| |________| |_______ + duration +CPU1 + ____________ ____________ +kidle_inject/1 | sleep | mwait | sleep | + _________| |________| |_______ + ^ + | + | + roundup(jiffies, interval) + +Only one CPU is allowed to collect statistics and update global +control parameters. This CPU is referred to as the controlling CPU in +this document. The controlling CPU is elected at runtime, with a +policy that favors BSP, taking into account the possibility of a CPU +hot-plug. + +In terms of dynamics of the idle control system, package level idle +time is considered largely as a non-causal system where its behavior +cannot be based on the past or current input. Therefore, the +intel_powerclamp driver attempts to enforce the desired idle time +instantly as given input (target idle ratio). After injection, +powerclamp moniors the actual idle for a given time window and adjust +the next injection accordingly to avoid over/under correction. + +When used in a causal control system, such as a temperature control, +it is up to the user of this driver to implement algorithms where +past samples and outputs are included in the feedback. For example, a +PID-based thermal controller can use the powerclamp driver to +maintain a desired target temperature, based on integral and +derivative gains of the past samples. + + + +Calibration +----------- +During scalability testing, it is observed that synchronized actions +among CPUs become challenging as the number of cores grows. This is +also true for the ability of a system to enter package level C-states. + +To make sure the intel_powerclamp driver scales well, online +calibration is implemented. The goals for doing such a calibration +are: + +a) determine the effective range of idle injection ratio +b) determine the amount of compensation needed at each target ratio + +Compensation to each target ratio consists of two parts: + + a) steady state error compensation + This is to offset the error occurring when the system can + enter idle without extra wakeups (such as external interrupts). + + b) dynamic error compensation + When an excessive amount of wakeups occurs during idle, an + additional idle ratio can be added to quiet interrupts, by + slowing down CPU activities. + +A debugfs file is provided for the user to examine compensation +progress and results, such as on a Westmere system. +[jacob@nex01 ~]$ cat +/sys/kernel/debug/intel_powerclamp/powerclamp_calib +controlling cpu: 0 +pct confidence steady dynamic (compensation) +0 0 0 0 +1 1 0 0 +2 1 1 0 +3 3 1 0 +4 3 1 0 +5 3 1 0 +6 3 1 0 +7 3 1 0 +8 3 1 0 +... +30 3 2 0 +31 3 2 0 +32 3 1 0 +33 3 2 0 +34 3 1 0 +35 3 2 0 +36 3 1 0 +37 3 2 0 +38 3 1 0 +39 3 2 0 +40 3 3 0 +41 3 1 0 +42 3 2 0 +43 3 1 0 +44 3 1 0 +45 3 2 0 +46 3 3 0 +47 3 0 0 +48 3 2 0 +49 3 3 0 + +Calibration occurs during runtime. No offline method is available. +Steady state compensation is used only when confidence levels of all +adjacent ratios have reached satisfactory level. A confidence level +is accumulated based on clean data collected at runtime. Data +collected during a period without extra interrupts is considered +clean. + +To compensate for excessive amounts of wakeup during idle, additional +idle time is injected when such a condition is detected. Currently, +we have a simple algorithm to double the injection ratio. A possible +enhancement might be to throttle the offending IRQ, such as delaying +EOI for level triggered interrupts. But it is a challenge to be +non-intrusive to the scheduler or the IRQ core code. + + +CPU Online/Offline +------------------ +Per-CPU kernel threads are started/stopped upon receiving +notifications of CPU hotplug activities. The intel_powerclamp driver +keeps track of clamping kernel threads, even after they are migrated +to other CPUs, after a CPU offline event. + + +===================== +Performance Analysis +===================== +This section describes the general performance data collected on +multiple systems, including Westmere (80P) and Ivy Bridge (4P, 8P). + +Effectiveness and Limitations +----------------------------- +The maximum range that idle injection is allowed is capped at 50 +percent. As mentioned earlier, since interrupts are allowed during +forced idle time, excessive interrupts could result in less +effectiveness. The extreme case would be doing a ping -f to generated +flooded network interrupts without much CPU acknowledgement. In this +case, little can be done from the idle injection threads. In most +normal cases, such as scp a large file, applications can be throttled +by the powerclamp driver, since slowing down the CPU also slows down +network protocol processing, which in turn reduces interrupts. + +When control parameters change at runtime by the controlling CPU, it +may take an additional period for the rest of the CPUs to catch up +with the changes. During this time, idle injection is out of sync, +thus not able to enter package C- states at the expected ratio. But +this effect is minor, in that in most cases change to the target +ratio is updated much less frequently than the idle injection +frequency. + +Scalability +----------- +Tests also show a minor, but measurable, difference between the 4P/8P +Ivy Bridge system and the 80P Westmere server under 50% idle ratio. +More compensation is needed on Westmere for the same amount of +target idle ratio. The compensation also increases as the idle ratio +gets larger. The above reason constitutes the need for the +calibration code. + +On the IVB 8P system, compared to an offline CPU, powerclamp can +achieve up to 40% better performance per watt. (measured by a spin +counter summed over per CPU counting threads spawned for all running +CPUs). + +==================== +Usage and Interfaces +==================== +The powerclamp driver is registered to the generic thermal layer as a +cooling device. Currently, it’s not bound to any thermal zones. + +jacob@chromoly:/sys/class/thermal/cooling_device14$ grep . * +cur_state:0 +max_state:50 +type:intel_powerclamp + +Example usage: +- To inject 25% idle time +$ sudo sh -c "echo 25 > /sys/class/thermal/cooling_device80/cur_state +" + +If the system is not busy and has more than 25% idle time already, +then the powerclamp driver will not start idle injection. Using Top +will not show idle injection kernel threads. + +If the system is busy (spin test below) and has less than 25% natural +idle time, powerclamp kernel threads will do idle injection, which +appear running to the scheduler. But the overall system idle is still +reflected. In this example, 24.1% idle is shown. This helps the +system admin or user determine the cause of slowdown, when a +powerclamp driver is in action. + + +Tasks: 197 total, 1 running, 196 sleeping, 0 stopped, 0 zombie +Cpu(s): 71.2%us, 4.7%sy, 0.0%ni, 24.1%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st +Mem: 3943228k total, 1689632k used, 2253596k free, 74960k buffers +Swap: 4087804k total, 0k used, 4087804k free, 945336k cached + + PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND + 3352 jacob 20 0 262m 644 428 S 286 0.0 0:17.16 spin + 3341 root -51 0 0 0 0 D 25 0.0 0:01.62 kidle_inject/0 + 3344 root -51 0 0 0 0 D 25 0.0 0:01.60 kidle_inject/3 + 3342 root -51 0 0 0 0 D 25 0.0 0:01.61 kidle_inject/1 + 3343 root -51 0 0 0 0 D 25 0.0 0:01.60 kidle_inject/2 + 2935 jacob 20 0 696m 125m 35m S 5 3.3 0:31.11 firefox + 1546 root 20 0 158m 20m 6640 S 3 0.5 0:26.97 Xorg + 2100 jacob 20 0 1223m 88m 30m S 3 2.3 0:23.68 compiz + +Tests have shown that by using the powerclamp driver as a cooling +device, a PID based userspace thermal controller can manage to +control CPU temperature effectively, when no other thermal influence +is added. For example, a UltraBook user can compile the kernel under +certain temperature (below most active trip points). diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index c31b9e4451a3..faf38c522fa8 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -131,4 +131,14 @@ config DB8500_CPUFREQ_COOLING bound cpufreq cooling device turns active to set CPU frequency low to cool down the CPU. +config INTEL_POWERCLAMP + tristate "Intel PowerClamp idle injection driver" + depends on THERMAL + depends on X86 + depends on CPU_SUP_INTEL + help + Enable this to enable Intel PowerClamp idle injection driver. This + enforce idle time which results in more package C-state residency. The + user interface is exposed via generic thermal framework. + endif diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index d8da683245fc..574f5f505b9f 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -18,3 +18,5 @@ obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o +obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o + diff --git a/drivers/thermal/intel_powerclamp.c b/drivers/thermal/intel_powerclamp.c new file mode 100644 index 000000000000..a85ff38cb4e8 --- /dev/null +++ b/drivers/thermal/intel_powerclamp.c @@ -0,0 +1,794 @@ +/* + * intel_powerclamp.c - package c-state idle injection + * + * Copyright (c) 2012, Intel Corporation. + * + * Authors: + * Arjan van de Ven + * Jacob Pan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * + * TODO: + * 1. better handle wakeup from external interrupts, currently a fixed + * compensation is added to clamping duration when excessive amount + * of wakeups are observed during idle time. the reason is that in + * case of external interrupts without need for ack, clamping down + * cpu in non-irq context does not reduce irq. for majority of the + * cases, clamping down cpu does help reduce irq as well, we should + * be able to differenciate the two cases and give a quantitative + * solution for the irqs that we can control. perhaps based on + * get_cpu_iowait_time_us() + * + * 2. synchronization with other hw blocks + * + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define MAX_TARGET_RATIO (50U) +/* For each undisturbed clamping period (no extra wake ups during idle time), + * we increment the confidence counter for the given target ratio. + * CONFIDENCE_OK defines the level where runtime calibration results are + * valid. + */ +#define CONFIDENCE_OK (3) +/* Default idle injection duration, driver adjust sleep time to meet target + * idle ratio. Similar to frequency modulation. + */ +#define DEFAULT_DURATION_JIFFIES (6) + +static unsigned int target_mwait; +static struct dentry *debug_dir; + +/* user selected target */ +static unsigned int set_target_ratio; +static unsigned int current_ratio; +static bool should_skip; +static bool reduce_irq; +static atomic_t idle_wakeup_counter; +static unsigned int control_cpu; /* The cpu assigned to collect stat and update + * control parameters. default to BSP but BSP + * can be offlined. + */ +static bool clamping; + + +static struct task_struct * __percpu *powerclamp_thread; +static struct thermal_cooling_device *cooling_dev; +static unsigned long *cpu_clamping_mask; /* bit map for tracking per cpu + * clamping thread + */ + +static unsigned int duration; +static unsigned int pkg_cstate_ratio_cur; +static unsigned int window_size; + +static int duration_set(const char *arg, const struct kernel_param *kp) +{ + int ret = 0; + unsigned long new_duration; + + ret = kstrtoul(arg, 10, &new_duration); + if (ret) + goto exit; + if (new_duration > 25 || new_duration < 6) { + pr_err("Out of recommended range %lu, between 6-25ms\n", + new_duration); + ret = -EINVAL; + } + + duration = clamp(new_duration, 6ul, 25ul); + smp_mb(); + +exit: + + return ret; +} + +static struct kernel_param_ops duration_ops = { + .set = duration_set, + .get = param_get_int, +}; + + +module_param_cb(duration, &duration_ops, &duration, 0644); +MODULE_PARM_DESC(duration, "forced idle time for each attempt in msec."); + +struct powerclamp_calibration_data { + unsigned long confidence; /* used for calibration, basically a counter + * gets incremented each time a clamping + * period is completed without extra wakeups + * once that counter is reached given level, + * compensation is deemed usable. + */ + unsigned long steady_comp; /* steady state compensation used when + * no extra wakeups occurred. + */ + unsigned long dynamic_comp; /* compensate excessive wakeup from idle + * mostly from external interrupts. + */ +}; + +static struct powerclamp_calibration_data cal_data[MAX_TARGET_RATIO]; + +static int window_size_set(const char *arg, const struct kernel_param *kp) +{ + int ret = 0; + unsigned long new_window_size; + + ret = kstrtoul(arg, 10, &new_window_size); + if (ret) + goto exit_win; + if (new_window_size > 10 || new_window_size < 2) { + pr_err("Out of recommended window size %lu, between 2-10\n", + new_window_size); + ret = -EINVAL; + } + + window_size = clamp(new_window_size, 2ul, 10ul); + smp_mb(); + +exit_win: + + return ret; +} + +static struct kernel_param_ops window_size_ops = { + .set = window_size_set, + .get = param_get_int, +}; + +module_param_cb(window_size, &window_size_ops, &window_size, 0644); +MODULE_PARM_DESC(window_size, "sliding window in number of clamping cycles\n" + "\tpowerclamp controls idle ratio within this window. larger\n" + "\twindow size results in slower response time but more smooth\n" + "\tclamping results. default to 2."); + +static void find_target_mwait(void) +{ + unsigned int eax, ebx, ecx, edx; + unsigned int highest_cstate = 0; + unsigned int highest_subcstate = 0; + int i; + + if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF) + return; + + cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx); + + if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) || + !(ecx & CPUID5_ECX_INTERRUPT_BREAK)) + return; + + edx >>= MWAIT_SUBSTATE_SIZE; + for (i = 0; i < 7 && edx; i++, edx >>= MWAIT_SUBSTATE_SIZE) { + if (edx & MWAIT_SUBSTATE_MASK) { + highest_cstate = i; + highest_subcstate = edx & MWAIT_SUBSTATE_MASK; + } + } + target_mwait = (highest_cstate << MWAIT_SUBSTATE_SIZE) | + (highest_subcstate - 1); + +} + +static u64 pkg_state_counter(void) +{ + u64 val; + u64 count = 0; + + static bool skip_c2; + static bool skip_c3; + static bool skip_c6; + static bool skip_c7; + + if (!skip_c2) { + if (!rdmsrl_safe(MSR_PKG_C2_RESIDENCY, &val)) + count += val; + else + skip_c2 = true; + } + + if (!skip_c3) { + if (!rdmsrl_safe(MSR_PKG_C3_RESIDENCY, &val)) + count += val; + else + skip_c3 = true; + } + + if (!skip_c6) { + if (!rdmsrl_safe(MSR_PKG_C6_RESIDENCY, &val)) + count += val; + else + skip_c6 = true; + } + + if (!skip_c7) { + if (!rdmsrl_safe(MSR_PKG_C7_RESIDENCY, &val)) + count += val; + else + skip_c7 = true; + } + + return count; +} + +static void noop_timer(unsigned long foo) +{ + /* empty... just the fact that we get the interrupt wakes us up */ +} + +static unsigned int get_compensation(int ratio) +{ + unsigned int comp = 0; + + /* we only use compensation if all adjacent ones are good */ + if (ratio == 1 && + cal_data[ratio].confidence >= CONFIDENCE_OK && + cal_data[ratio + 1].confidence >= CONFIDENCE_OK && + cal_data[ratio + 2].confidence >= CONFIDENCE_OK) { + comp = (cal_data[ratio].steady_comp + + cal_data[ratio + 1].steady_comp + + cal_data[ratio + 2].steady_comp) / 3; + } else if (ratio == MAX_TARGET_RATIO - 1 && + cal_data[ratio].confidence >= CONFIDENCE_OK && + cal_data[ratio - 1].confidence >= CONFIDENCE_OK && + cal_data[ratio - 2].confidence >= CONFIDENCE_OK) { + comp = (cal_data[ratio].steady_comp + + cal_data[ratio - 1].steady_comp + + cal_data[ratio - 2].steady_comp) / 3; + } else if (cal_data[ratio].confidence >= CONFIDENCE_OK && + cal_data[ratio - 1].confidence >= CONFIDENCE_OK && + cal_data[ratio + 1].confidence >= CONFIDENCE_OK) { + comp = (cal_data[ratio].steady_comp + + cal_data[ratio - 1].steady_comp + + cal_data[ratio + 1].steady_comp) / 3; + } + + /* REVISIT: simple penalty of double idle injection */ + if (reduce_irq) + comp = ratio; + /* do not exceed limit */ + if (comp + ratio >= MAX_TARGET_RATIO) + comp = MAX_TARGET_RATIO - ratio - 1; + + return comp; +} + +static void adjust_compensation(int target_ratio, unsigned int win) +{ + int delta; + struct powerclamp_calibration_data *d = &cal_data[target_ratio]; + + /* + * adjust compensations if confidence level has not been reached or + * there are too many wakeups during the last idle injection period, we + * cannot trust the data for compensation. + */ + if (d->confidence >= CONFIDENCE_OK || + atomic_read(&idle_wakeup_counter) > + win * num_online_cpus()) + return; + + delta = set_target_ratio - current_ratio; + /* filter out bad data */ + if (delta >= 0 && delta <= (1+target_ratio/10)) { + if (d->steady_comp) + d->steady_comp = + roundup(delta+d->steady_comp, 2)/2; + else + d->steady_comp = delta; + d->confidence++; + } +} + +static bool powerclamp_adjust_controls(unsigned int target_ratio, + unsigned int guard, unsigned int win) +{ + static u64 msr_last, tsc_last; + u64 msr_now, tsc_now; + u64 val64; + + /* check result for the last window */ + msr_now = pkg_state_counter(); + rdtscll(tsc_now); + + /* calculate pkg cstate vs tsc ratio */ + if (!msr_last || !tsc_last) + current_ratio = 1; + else if (tsc_now-tsc_last) { + val64 = 100*(msr_now-msr_last); + do_div(val64, (tsc_now-tsc_last)); + current_ratio = val64; + } + + /* update record */ + msr_last = msr_now; + tsc_last = tsc_now; + + adjust_compensation(target_ratio, win); + /* + * too many external interrupts, set flag such + * that we can take measure later. + */ + reduce_irq = atomic_read(&idle_wakeup_counter) >= + 2 * win * num_online_cpus(); + + atomic_set(&idle_wakeup_counter, 0); + /* if we are above target+guard, skip */ + return set_target_ratio + guard <= current_ratio; +} + +static int clamp_thread(void *arg) +{ + int cpunr = (unsigned long)arg; + DEFINE_TIMER(wakeup_timer, noop_timer, 0, 0); + static const struct sched_param param = { + .sched_priority = MAX_USER_RT_PRIO/2, + }; + unsigned int count = 0; + unsigned int target_ratio; + + set_bit(cpunr, cpu_clamping_mask); + set_freezable(); + init_timer_on_stack(&wakeup_timer); + sched_setscheduler(current, SCHED_FIFO, ¶m); + + while (true == clamping && !kthread_should_stop() && + cpu_online(cpunr)) { + int sleeptime; + unsigned long target_jiffies; + unsigned int guard; + unsigned int compensation = 0; + int interval; /* jiffies to sleep for each attempt */ + unsigned int duration_jiffies = msecs_to_jiffies(duration); + unsigned int window_size_now; + + try_to_freeze(); + /* + * make sure user selected ratio does not take effect until + * the next round. adjust target_ratio if user has changed + * target such that we can converge quickly. + */ + target_ratio = set_target_ratio; + guard = 1 + target_ratio/20; + window_size_now = window_size; + count++; + + /* + * systems may have different ability to enter package level + * c-states, thus we need to compensate the injected idle ratio + * to achieve the actual target reported by the HW. + */ + compensation = get_compensation(target_ratio); + interval = duration_jiffies*100/(target_ratio+compensation); + + /* align idle time */ + target_jiffies = roundup(jiffies, interval); + sleeptime = target_jiffies - jiffies; + if (sleeptime <= 0) + sleeptime = 1; + schedule_timeout_interruptible(sleeptime); + /* + * only elected controlling cpu can collect stats and update + * control parameters. + */ + if (cpunr == control_cpu && !(count%window_size_now)) { + should_skip = + powerclamp_adjust_controls(target_ratio, + guard, window_size_now); + smp_mb(); + } + + if (should_skip) + continue; + + target_jiffies = jiffies + duration_jiffies; + mod_timer(&wakeup_timer, target_jiffies); + if (unlikely(local_softirq_pending())) + continue; + /* + * stop tick sched during idle time, interrupts are still + * allowed. thus jiffies are updated properly. + */ + preempt_disable(); + tick_nohz_idle_enter(); + /* mwait until target jiffies is reached */ + while (time_before(jiffies, target_jiffies)) { + unsigned long ecx = 1; + unsigned long eax = target_mwait; + + /* + * REVISIT: may call enter_idle() to notify drivers who + * can save power during cpu idle. same for exit_idle() + */ + local_touch_nmi(); + stop_critical_timings(); + __monitor((void *)¤t_thread_info()->flags, 0, 0); + cpu_relax(); /* allow HT sibling to run */ + __mwait(eax, ecx); + start_critical_timings(); + atomic_inc(&idle_wakeup_counter); + } + tick_nohz_idle_exit(); + preempt_enable_no_resched(); + } + del_timer_sync(&wakeup_timer); + clear_bit(cpunr, cpu_clamping_mask); + + return 0; +} + +/* + * 1 HZ polling while clamping is active, useful for userspace + * to monitor actual idle ratio. + */ +static void poll_pkg_cstate(struct work_struct *dummy); +static DECLARE_DELAYED_WORK(poll_pkg_cstate_work, poll_pkg_cstate); +static void poll_pkg_cstate(struct work_struct *dummy) +{ + static u64 msr_last; + static u64 tsc_last; + static unsigned long jiffies_last; + + u64 msr_now; + unsigned long jiffies_now; + u64 tsc_now; + u64 val64; + + msr_now = pkg_state_counter(); + rdtscll(tsc_now); + jiffies_now = jiffies; + + /* calculate pkg cstate vs tsc ratio */ + if (!msr_last || !tsc_last) + pkg_cstate_ratio_cur = 1; + else { + if (tsc_now - tsc_last) { + val64 = 100 * (msr_now - msr_last); + do_div(val64, (tsc_now - tsc_last)); + pkg_cstate_ratio_cur = val64; + } + } + + /* update record */ + msr_last = msr_now; + jiffies_last = jiffies_now; + tsc_last = tsc_now; + + if (true == clamping) + schedule_delayed_work(&poll_pkg_cstate_work, HZ); +} + +static int start_power_clamp(void) +{ + unsigned long cpu; + struct task_struct *thread; + + /* check if pkg cstate counter is completely 0, abort in this case */ + if (!pkg_state_counter()) { + pr_err("pkg cstate counter not functional, abort\n"); + return -EINVAL; + } + + set_target_ratio = clamp(set_target_ratio, 0U, MAX_TARGET_RATIO); + /* prevent cpu hotplug */ + get_online_cpus(); + + /* prefer BSP */ + control_cpu = 0; + if (!cpu_online(control_cpu)) + control_cpu = smp_processor_id(); + + clamping = true; + schedule_delayed_work(&poll_pkg_cstate_work, 0); + + /* start one thread per online cpu */ + for_each_online_cpu(cpu) { + struct task_struct **p = + per_cpu_ptr(powerclamp_thread, cpu); + + thread = kthread_create_on_node(clamp_thread, + (void *) cpu, + cpu_to_node(cpu), + "kidle_inject/%ld", cpu); + /* bind to cpu here */ + if (likely(!IS_ERR(thread))) { + kthread_bind(thread, cpu); + wake_up_process(thread); + *p = thread; + } + + } + put_online_cpus(); + + return 0; +} + +static void end_power_clamp(void) +{ + int i; + struct task_struct *thread; + + clamping = false; + /* + * make clamping visible to other cpus and give per cpu clamping threads + * sometime to exit, or gets killed later. + */ + smp_mb(); + msleep(20); + if (bitmap_weight(cpu_clamping_mask, num_possible_cpus())) { + for_each_set_bit(i, cpu_clamping_mask, num_possible_cpus()) { + pr_debug("clamping thread for cpu %d alive, kill\n", i); + thread = *per_cpu_ptr(powerclamp_thread, i); + kthread_stop(thread); + } + } +} + +static int powerclamp_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + unsigned long cpu = (unsigned long)hcpu; + struct task_struct *thread; + struct task_struct **percpu_thread = + per_cpu_ptr(powerclamp_thread, cpu); + + if (false == clamping) + goto exit_ok; + + switch (action) { + case CPU_ONLINE: + thread = kthread_create_on_node(clamp_thread, + (void *) cpu, + cpu_to_node(cpu), + "kidle_inject/%lu", cpu); + if (likely(!IS_ERR(thread))) { + kthread_bind(thread, cpu); + wake_up_process(thread); + *percpu_thread = thread; + } + /* prefer BSP as controlling CPU */ + if (cpu == 0) { + control_cpu = 0; + smp_mb(); + } + break; + case CPU_DEAD: + if (test_bit(cpu, cpu_clamping_mask)) { + pr_err("cpu %lu dead but powerclamping thread is not\n", + cpu); + kthread_stop(*percpu_thread); + } + if (cpu == control_cpu) { + control_cpu = smp_processor_id(); + smp_mb(); + } + } + +exit_ok: + return NOTIFY_OK; +} + +static struct notifier_block powerclamp_cpu_notifier = { + .notifier_call = powerclamp_cpu_callback, +}; + +static int powerclamp_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = MAX_TARGET_RATIO; + + return 0; +} + +static int powerclamp_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + if (true == clamping) + *state = pkg_cstate_ratio_cur; + else + /* to save power, do not poll idle ratio while not clamping */ + *state = -1; /* indicates invalid state */ + + return 0; +} + +static int powerclamp_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long new_target_ratio) +{ + int ret = 0; + + new_target_ratio = clamp(new_target_ratio, 0UL, + (unsigned long) (MAX_TARGET_RATIO-1)); + if (set_target_ratio == 0 && new_target_ratio > 0) { + pr_info("Start idle injection to reduce power\n"); + set_target_ratio = new_target_ratio; + ret = start_power_clamp(); + goto exit_set; + } else if (set_target_ratio > 0 && new_target_ratio == 0) { + pr_info("Stop forced idle injection\n"); + set_target_ratio = 0; + end_power_clamp(); + } else /* adjust currently running */ { + set_target_ratio = new_target_ratio; + /* make new set_target_ratio visible to other cpus */ + smp_mb(); + } + +exit_set: + return ret; +} + +/* bind to generic thermal layer as cooling device*/ +static struct thermal_cooling_device_ops powerclamp_cooling_ops = { + .get_max_state = powerclamp_get_max_state, + .get_cur_state = powerclamp_get_cur_state, + .set_cur_state = powerclamp_set_cur_state, +}; + +/* runs on Nehalem and later */ +static const struct x86_cpu_id intel_powerclamp_ids[] = { + { X86_VENDOR_INTEL, 6, 0x1a}, + { X86_VENDOR_INTEL, 6, 0x1c}, + { X86_VENDOR_INTEL, 6, 0x1e}, + { X86_VENDOR_INTEL, 6, 0x1f}, + { X86_VENDOR_INTEL, 6, 0x25}, + { X86_VENDOR_INTEL, 6, 0x26}, + { X86_VENDOR_INTEL, 6, 0x2a}, + { X86_VENDOR_INTEL, 6, 0x2c}, + { X86_VENDOR_INTEL, 6, 0x2d}, + { X86_VENDOR_INTEL, 6, 0x2e}, + { X86_VENDOR_INTEL, 6, 0x2f}, + { X86_VENDOR_INTEL, 6, 0x3a}, + {} +}; +MODULE_DEVICE_TABLE(x86cpu, intel_powerclamp_ids); + +static int powerclamp_probe(void) +{ + if (!x86_match_cpu(intel_powerclamp_ids)) { + pr_err("Intel powerclamp does not run on family %d model %d\n", + boot_cpu_data.x86, boot_cpu_data.x86_model); + return -ENODEV; + } + if (!boot_cpu_has(X86_FEATURE_NONSTOP_TSC) || + !boot_cpu_has(X86_FEATURE_CONSTANT_TSC) || + !boot_cpu_has(X86_FEATURE_MWAIT) || + !boot_cpu_has(X86_FEATURE_ARAT)) + return -ENODEV; + + /* find the deepest mwait value */ + find_target_mwait(); + + return 0; +} + +static int powerclamp_debug_show(struct seq_file *m, void *unused) +{ + int i = 0; + + seq_printf(m, "controlling cpu: %d\n", control_cpu); + seq_printf(m, "pct confidence steady dynamic (compensation)\n"); + for (i = 0; i < MAX_TARGET_RATIO; i++) { + seq_printf(m, "%d\t%lu\t%lu\t%lu\n", + i, + cal_data[i].confidence, + cal_data[i].steady_comp, + cal_data[i].dynamic_comp); + } + + return 0; +} + +static int powerclamp_debug_open(struct inode *inode, + struct file *file) +{ + return single_open(file, powerclamp_debug_show, inode->i_private); +} + +static const struct file_operations powerclamp_debug_fops = { + .open = powerclamp_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static inline void powerclamp_create_debug_files(void) +{ + debug_dir = debugfs_create_dir("intel_powerclamp", NULL); + if (!debug_dir) + return; + + if (!debugfs_create_file("powerclamp_calib", S_IRUGO, debug_dir, + cal_data, &powerclamp_debug_fops)) + goto file_error; + + return; + +file_error: + debugfs_remove_recursive(debug_dir); +} + +static int powerclamp_init(void) +{ + int retval; + int bitmap_size; + + bitmap_size = BITS_TO_LONGS(num_possible_cpus()) * sizeof(long); + cpu_clamping_mask = kzalloc(bitmap_size, GFP_KERNEL); + if (!cpu_clamping_mask) + return -ENOMEM; + + /* probe cpu features and ids here */ + retval = powerclamp_probe(); + if (retval) + return retval; + /* set default limit, maybe adjusted during runtime based on feedback */ + window_size = 2; + register_hotcpu_notifier(&powerclamp_cpu_notifier); + powerclamp_thread = alloc_percpu(struct task_struct *); + cooling_dev = thermal_cooling_device_register("intel_powerclamp", NULL, + &powerclamp_cooling_ops); + if (IS_ERR(cooling_dev)) + return -ENODEV; + + if (!duration) + duration = jiffies_to_msecs(DEFAULT_DURATION_JIFFIES); + powerclamp_create_debug_files(); + + return 0; +} +module_init(powerclamp_init); + +static void powerclamp_exit(void) +{ + unregister_hotcpu_notifier(&powerclamp_cpu_notifier); + end_power_clamp(); + free_percpu(powerclamp_thread); + thermal_cooling_device_unregister(cooling_dev); + kfree(cpu_clamping_mask); + + cancel_delayed_work_sync(&poll_pkg_cstate_work); + debugfs_remove_recursive(debug_dir); +} +module_exit(powerclamp_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Arjan van de Ven "); +MODULE_AUTHOR("Jacob Pan "); +MODULE_DESCRIPTION("Package Level C-state Idle Injection for Intel CPUs"); -- cgit v1.2.3 From 3ad9524a15126c24fc37922f56a0fb5dd03c218f Mon Sep 17 00:00:00 2001 From: Amit Daniel Kachhap Date: Thu, 17 Jan 2013 01:42:18 +0000 Subject: thermal: exynos: Miscellaneous fixes to support falling threshold interrupt Below fixes are done to support falling threshold interrupt, * Falling interrupt status macro corrected according to exynos5 data sheet. * The get trend function modified to calculate trip temperature correctly. * The clearing of interrupt status in the isr is now done after handling the event that caused the interrupt. Signed-off-by: Amit Daniel Kachhap Signed-off-by: Zhang Rui --- drivers/thermal/exynos_thermal.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c index 327102a4596a..cd71e24194b1 100644 --- a/drivers/thermal/exynos_thermal.c +++ b/drivers/thermal/exynos_thermal.c @@ -82,7 +82,7 @@ #define EXYNOS_TRIMINFO_RELOAD 0x1 #define EXYNOS_TMU_CLEAR_RISE_INT 0x111 -#define EXYNOS_TMU_CLEAR_FALL_INT (0x111 << 16) +#define EXYNOS_TMU_CLEAR_FALL_INT (0x111 << 12) #define EXYNOS_MUX_ADDR_VALUE 6 #define EXYNOS_MUX_ADDR_SHIFT 20 #define EXYNOS_TMU_TRIP_MODE_SHIFT 13 @@ -370,7 +370,14 @@ static int exynos_get_temp(struct thermal_zone_device *thermal, static int exynos_get_trend(struct thermal_zone_device *thermal, int trip, enum thermal_trend *trend) { - if (thermal->temperature >= trip) + int ret; + unsigned long trip_temp; + + ret = exynos_get_trip_temp(thermal, trip, &trip_temp); + if (ret < 0) + return ret; + + if (thermal->temperature >= trip_temp) *trend = THERMAL_TREND_RAISING; else *trend = THERMAL_TREND_DROPPING; @@ -705,20 +712,18 @@ static void exynos_tmu_work(struct work_struct *work) struct exynos_tmu_data *data = container_of(work, struct exynos_tmu_data, irq_work); + exynos_report_trigger(); mutex_lock(&data->lock); clk_enable(data->clk); - - if (data->soc == SOC_ARCH_EXYNOS) writel(EXYNOS_TMU_CLEAR_RISE_INT, data->base + EXYNOS_TMU_REG_INTCLEAR); else writel(EXYNOS4210_TMU_INTCLEAR_VAL, data->base + EXYNOS_TMU_REG_INTCLEAR); - clk_disable(data->clk); mutex_unlock(&data->lock); - exynos_report_trigger(); + enable_irq(data->irq); } -- cgit v1.2.3 From c8165dc0ea75855b0bff6e5edbe4957b8a63d021 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 24 Jan 2013 08:51:22 +0000 Subject: PM: intel_powerclamp: off by one in start_power_clamp() This value has already been clamped correctly to 0 through 49 in powerclamp_set_cur_state() so this patch doesn't actually change anything. But we should fix it anyway for consistency. set_target_ratio is used as an offset into an array with MAX_TARGET_RATIO (50) elements. Signed-off-by: Dan Carpenter Signed-off-by: Zhang Rui --- drivers/thermal/intel_powerclamp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/thermal/intel_powerclamp.c b/drivers/thermal/intel_powerclamp.c index a85ff38cb4e8..ab3ed907d2c3 100644 --- a/drivers/thermal/intel_powerclamp.c +++ b/drivers/thermal/intel_powerclamp.c @@ -504,7 +504,7 @@ static int start_power_clamp(void) return -EINVAL; } - set_target_ratio = clamp(set_target_ratio, 0U, MAX_TARGET_RATIO); + set_target_ratio = clamp(set_target_ratio, 0U, MAX_TARGET_RATIO - 1); /* prevent cpu hotplug */ get_online_cpus(); -- cgit v1.2.3 From e6e238c38bd4d42d5e2cddb2165e1a46e0fb1200 Mon Sep 17 00:00:00 2001 From: Amit Daniel Kachhap Date: Mon, 4 Feb 2013 00:30:15 +0000 Subject: thermal: sysfs: Add a new sysfs node emul_temp for thermal emulation This patch adds support to set the emulated temperature method in thermal zone (sensor). After setting this feature thermal zone may report this temperature and not the actual temperature. The emulation implementation may be based on sensor capability through platform specific handler or pure software emulation if no platform handler defined. This is useful in debugging different temperature threshold and its associated cooling action. Critical threshold's cannot be emulated. Writing 0 on this node should disable emulation. Signed-off-by: Amit Daniel Kachhap Acked-by: Kukjin Kim Signed-off-by: Zhang Rui --- Documentation/thermal/sysfs-api.txt | 13 ++++++ drivers/thermal/Kconfig | 8 ++++ drivers/thermal/thermal_sys.c | 79 +++++++++++++++++++++++++++++++------ include/linux/thermal.h | 2 + 4 files changed, 91 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt index 526d4b90d6c1..6859661c9d31 100644 --- a/Documentation/thermal/sysfs-api.txt +++ b/Documentation/thermal/sysfs-api.txt @@ -55,6 +55,8 @@ temperature) and throttle appropriate devices. .get_trip_type: get the type of certain trip point. .get_trip_temp: get the temperature above which the certain trip point will be fired. + .set_emul_temp: set the emulation temperature which helps in debugging + different threshold temperature points. 1.1.2 void thermal_zone_device_unregister(struct thermal_zone_device *tz) @@ -153,6 +155,7 @@ Thermal zone device sys I/F, created once it's registered: |---trip_point_[0-*]_temp: Trip point temperature |---trip_point_[0-*]_type: Trip point type |---trip_point_[0-*]_hyst: Hysteresis value for this trip point + |---emul_temp: Emulated temperature set node Thermal cooling device sys I/F, created once it's registered: /sys/class/thermal/cooling_device[0-*]: @@ -252,6 +255,16 @@ passive Valid values: 0 (disabled) or greater than 1000 RW, Optional +emul_temp + Interface to set the emulated temperature method in thermal zone + (sensor). After setting this temperature, the thermal zone may pass + this temperature to platform emulation function if registered or + cache it locally. This is useful in debugging different temperature + threshold and its associated cooling action. This is write only node + and writing 0 on this node should disable emulation. + Unit: millidegree Celsius + WO, Optional + ***************************** * Cooling device attributes * ***************************** diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index faf38c522fa8..e4cf7fbc3a59 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -78,6 +78,14 @@ config CPU_THERMAL and not the ACPI interface. If you want this support, you should say Y here. +config THERMAL_EMULATION + bool "Thermal emulation mode support" + help + Enable this option to make a emul_temp sysfs node in thermal zone + directory to support temperature emulation. With emulation sysfs node, + user can manually input temperature and test the different trip + threshold behaviour for simulation purpose. + config SPEAR_THERMAL bool "SPEAr thermal sensor driver" depends on PLAT_SPEAR diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 0a1bf6b032ea..0675687c6de8 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -378,24 +378,54 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip) monitor_thermal_zone(tz); } +static int thermal_zone_get_temp(struct thermal_zone_device *tz, + unsigned long *temp) +{ + int ret = 0, count; + unsigned long crit_temp = -1UL; + enum thermal_trip_type type; + + mutex_lock(&tz->lock); + + ret = tz->ops->get_temp(tz, temp); +#ifdef CONFIG_THERMAL_EMULATION + if (!tz->emul_temperature) + goto skip_emul; + + for (count = 0; count < tz->trips; count++) { + ret = tz->ops->get_trip_type(tz, count, &type); + if (!ret && type == THERMAL_TRIP_CRITICAL) { + ret = tz->ops->get_trip_temp(tz, count, &crit_temp); + break; + } + } + + if (ret) + goto skip_emul; + + if (*temp < crit_temp) + *temp = tz->emul_temperature; +skip_emul: +#endif + mutex_unlock(&tz->lock); + return ret; +} + static void update_temperature(struct thermal_zone_device *tz) { long temp; int ret; - mutex_lock(&tz->lock); - - ret = tz->ops->get_temp(tz, &temp); + ret = thermal_zone_get_temp(tz, &temp); if (ret) { dev_warn(&tz->device, "failed to read out thermal zone %d\n", tz->id); - goto exit; + return; } + mutex_lock(&tz->lock); tz->last_temperature = tz->temperature; tz->temperature = temp; - -exit: mutex_unlock(&tz->lock); } @@ -438,10 +468,7 @@ temp_show(struct device *dev, struct device_attribute *attr, char *buf) long temperature; int ret; - if (!tz->ops->get_temp) - return -EPERM; - - ret = tz->ops->get_temp(tz, &temperature); + ret = thermal_zone_get_temp(tz, &temperature); if (ret) return ret; @@ -701,6 +728,31 @@ policy_show(struct device *dev, struct device_attribute *devattr, char *buf) return sprintf(buf, "%s\n", tz->governor->name); } +#ifdef CONFIG_THERMAL_EMULATION +static ssize_t +emul_temp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + int ret = 0; + unsigned long temperature; + + if (kstrtoul(buf, 10, &temperature)) + return -EINVAL; + + if (!tz->ops->set_emul_temp) { + mutex_lock(&tz->lock); + tz->emul_temperature = temperature; + mutex_unlock(&tz->lock); + } else { + ret = tz->ops->set_emul_temp(tz, temperature); + } + + return ret ? ret : count; +} +static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store); +#endif/*CONFIG_THERMAL_EMULATION*/ + static DEVICE_ATTR(type, 0444, type_show, NULL); static DEVICE_ATTR(temp, 0444, temp_show, NULL); static DEVICE_ATTR(mode, 0644, mode_show, mode_store); @@ -843,7 +895,7 @@ temp_input_show(struct device *dev, struct device_attribute *attr, char *buf) temp_input); struct thermal_zone_device *tz = temp->tz; - ret = tz->ops->get_temp(tz, &temperature); + ret = thermal_zone_get_temp(tz, &temperature); if (ret) return ret; @@ -1596,6 +1648,11 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, goto unregister; } +#ifdef CONFIG_THERMAL_EMULATION + result = device_create_file(&tz->device, &dev_attr_emul_temp); + if (result) + goto unregister; +#endif /* Create policy attribute */ result = device_create_file(&tz->device, &dev_attr_policy); if (result) diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 9b78f8c6f773..f0bd7f90a90d 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -123,6 +123,7 @@ struct thermal_zone_device_ops { int (*set_trip_hyst) (struct thermal_zone_device *, int, unsigned long); int (*get_crit_temp) (struct thermal_zone_device *, unsigned long *); + int (*set_emul_temp) (struct thermal_zone_device *, unsigned long); int (*get_trend) (struct thermal_zone_device *, int, enum thermal_trend *); int (*notify) (struct thermal_zone_device *, int, @@ -165,6 +166,7 @@ struct thermal_zone_device { int polling_delay; int temperature; int last_temperature; + int emul_temperature; int passive; unsigned int forced_passive; const struct thermal_zone_device_ops *ops; -- cgit v1.2.3 From 475f41c3ab3dea3a5c60b90197baf57da01fdb5f Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 6 Feb 2013 09:34:32 +0800 Subject: Thermal: fix a wrong comment "level" parameter of get_cpu_frequency equals cooling state of cpu cooling device, and it starts from 0. Fix the misleading comment. Signed-off-by: Zhang Rui --- drivers/thermal/cpu_cooling.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 836828e29a87..455c77a961de 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -118,8 +118,8 @@ static int is_cpufreq_valid(int cpu) /** * get_cpu_frequency - get the absolute value of frequency from level. * @cpu: cpu for which frequency is fetched. - * @level: level of frequency of the CPU - * e.g level=1 --> 1st MAX FREQ, LEVEL=2 ---> 2nd MAX FREQ, .... etc + * @level: level of frequency, equals cooling state of cpu cooling device + * e.g level=0 --> 1st MAX FREQ, level=1 ---> 2nd MAX FREQ, .... etc */ static unsigned int get_cpu_frequency(unsigned int cpu, unsigned long level) { -- cgit v1.2.3 From 5e20b2e51dd13c569ea1ff65c2a57d00a84a81b0 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 6 Feb 2013 14:02:12 +0800 Subject: Thermal: fix a build warning when CONFIG_THERMAL_EMULATION cleared Signed-off-by: Zhang Rui --- drivers/thermal/thermal_sys.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 0675687c6de8..6472e7e9f969 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -381,9 +381,12 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip) static int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp) { - int ret = 0, count; + int ret = 0; +#ifdef CONFIG_THERMAL_EMULATION + int count; unsigned long crit_temp = -1UL; enum thermal_trip_type type; +#endif mutex_lock(&tz->lock); -- cgit v1.2.3 From 9dde8f86085d283042718f88eed017eccad73ab9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:02:51 +0000 Subject: thermal: rcar: use parenthesis on macro Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 89979ff10e27..47b2b227c91e 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -47,7 +47,7 @@ struct rcar_thermal_priv { }; #define MCELSIUS(temp) ((temp) * 1000) -#define rcar_zone_to_priv(zone) (zone->devdata) +#define rcar_zone_to_priv(zone) ((zone)->devdata) /* * basic functions -- cgit v1.2.3 From f8f53e1874c2dfddf4c6dc69008ba85d6de4d944 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:03:11 +0000 Subject: thermal: rcar: enable CPCTL to use hardware TSC deciding If CPCTL was 1 on R-Car thermal, the thermal comparator offset is automatically decided by hardware. And this CPCTL is the conditions which validate interrupt. This patch enabled CPCTL. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 91 ++++++++++++------------------------------ 1 file changed, 26 insertions(+), 65 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 47b2b227c91e..068b2a1c5c15 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -33,7 +33,7 @@ #define THSSR 0x30 /* THSCR */ -#define CPTAP 0xf +#define CPCTL (1 << 12) /* THSSR */ #define CTEMP 0x3f @@ -43,11 +43,11 @@ struct rcar_thermal_priv { void __iomem *base; struct device *dev; spinlock_t lock; - u32 comp; }; #define MCELSIUS(temp) ((temp) * 1000) #define rcar_zone_to_priv(zone) ((zone)->devdata) +#define rcar_priv_to_dev(priv) ((priv)->dev) /* * basic functions @@ -103,79 +103,41 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, unsigned long *temp) { struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); - int val, min, max, tmp; - - tmp = -200; /* default */ - while (1) { - if (priv->comp < 1 || priv->comp > 12) { - dev_err(priv->dev, - "THSSR invalid data (%d)\n", priv->comp); - priv->comp = 4; /* for next thermal */ - return -EINVAL; - } - - /* - * THS comparator offset and the reference temperature - * - * Comparator | reference | Temperature field - * offset | temperature | measurement - * | (degrees C) | (degrees C) - * -------------+---------------+------------------- - * 1 | -45 | -45 to -30 - * 2 | -30 | -30 to -15 - * 3 | -15 | -15 to 0 - * 4 | 0 | 0 to +15 - * 5 | +15 | +15 to +30 - * 6 | +30 | +30 to +45 - * 7 | +45 | +45 to +60 - * 8 | +60 | +60 to +75 - * 9 | +75 | +75 to +90 - * 10 | +90 | +90 to +105 - * 11 | +105 | +105 to +120 - * 12 | +120 | +120 to +135 - */ - - /* calculate thermal limitation */ - min = (priv->comp * 15) - 60; - max = min + 15; - + struct device *dev = rcar_priv_to_dev(priv); + int i; + int ctemp, old, new; + + /* + * TSC decides a value of CPTAP automatically, + * and this is the conditions which validate interrupt. + */ + rcar_thermal_bset(priv, THSCR, CPCTL, CPCTL); + + ctemp = 0; + old = ~0; + for (i = 0; i < 128; i++) { /* * we need to wait 300us after changing comparator offset * to get stable temperature. * see "Usage Notes" on datasheet */ - rcar_thermal_bset(priv, THSCR, CPTAP, priv->comp); udelay(300); - /* calculate current temperature */ - val = rcar_thermal_read(priv, THSSR) & CTEMP; - val = (val * 5) - 65; - - dev_dbg(priv->dev, "comp/min/max/val = %d/%d/%d/%d\n", - priv->comp, min, max, val); - - /* - * If val is same as min/max, then, - * it should try again on next comparator. - * But the val might be correct temperature. - * Keep it on "tmp" and compare with next val. - */ - if (tmp == val) - break; - - if (val <= min) { - tmp = min; - priv->comp--; /* try again */ - } else if (val >= max) { - tmp = max; - priv->comp++; /* try again */ - } else { - tmp = val; + new = rcar_thermal_read(priv, THSSR) & CTEMP; + if (new == old) { + ctemp = new; break; } + old = new; } - *temp = MCELSIUS(tmp); + if (!ctemp) { + dev_err(dev, "thermal sensor was broken\n"); + return -EINVAL; + } + + *temp = MCELSIUS((ctemp * 5) - 65); + return 0; } @@ -262,7 +224,6 @@ static int rcar_thermal_probe(struct platform_device *pdev) return -ENOMEM; } - priv->comp = 4; /* basic setup */ priv->dev = &pdev->dev; spin_lock_init(&priv->lock); priv->base = devm_ioremap_nocache(&pdev->dev, -- cgit v1.2.3 From b2bbc6a2ace78eaca2f6482b58b984519aa783ac Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:03:22 +0000 Subject: thermal: rcar: use mutex lock instead of spin lock Current R-Car thermal driver is using spin lock for each registers read/write, but it is pointless lock. This lock is required while reading temperature, but it needs long wait (= 300ms). So, this patch used mutex lock while reading temperature, instead of spin lock for each registers. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 068b2a1c5c15..e19b267f76d6 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -42,7 +42,7 @@ struct rcar_thermal_priv { void __iomem *base; struct device *dev; - spinlock_t lock; + struct mutex lock; }; #define MCELSIUS(temp) ((temp) * 1000) @@ -54,46 +54,26 @@ struct rcar_thermal_priv { */ static u32 rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) { - unsigned long flags; - u32 ret; - - spin_lock_irqsave(&priv->lock, flags); - - ret = ioread32(priv->base + reg); - - spin_unlock_irqrestore(&priv->lock, flags); - - return ret; + return ioread32(priv->base + reg); } #if 0 /* no user at this point */ static void rcar_thermal_write(struct rcar_thermal_priv *priv, u32 reg, u32 data) { - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - iowrite32(data, priv->base + reg); - - spin_unlock_irqrestore(&priv->lock, flags); } #endif static void rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, u32 mask, u32 data) { - unsigned long flags; u32 val; - spin_lock_irqsave(&priv->lock, flags); - val = ioread32(priv->base + reg); val &= ~mask; val |= (data & mask); iowrite32(val, priv->base + reg); - - spin_unlock_irqrestore(&priv->lock, flags); } /* @@ -107,6 +87,8 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, int i; int ctemp, old, new; + mutex_lock(&priv->lock); + /* * TSC decides a value of CPTAP automatically, * and this is the conditions which validate interrupt. @@ -138,6 +120,8 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, *temp = MCELSIUS((ctemp * 5) - 65); + mutex_unlock(&priv->lock); + return 0; } @@ -225,7 +209,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) } priv->dev = &pdev->dev; - spin_lock_init(&priv->lock); + mutex_init(&priv->lock); priv->base = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); if (!priv->base) { -- cgit v1.2.3 From 3676d1dd3d3069ca70b8075c0e86482cbaa01c2f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:03:33 +0000 Subject: thermal: rcar: multi channel support R-Car thermal sensor will be multi channel sensor in next generation. But "IRQ controlling method" and "register mapping" are different between old/new chip. This patch adds multi sensor support. Then, this driver assumes there is common register if platform has IRQ resource. The IRQ will be supported soon. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 128 ++++++++++++++++++++++++++++++----------- 1 file changed, 94 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index e19b267f76d6..1ba02770153a 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -38,16 +38,27 @@ /* THSSR */ #define CTEMP 0x3f +struct rcar_thermal_common { + void __iomem *base; + struct device *dev; + struct list_head head; +}; struct rcar_thermal_priv { void __iomem *base; - struct device *dev; + struct rcar_thermal_common *common; + struct thermal_zone_device *zone; struct mutex lock; + struct list_head list; }; +#define rcar_thermal_for_each_priv(pos, common) \ + list_for_each_entry(pos, &common->head, list) + #define MCELSIUS(temp) ((temp) * 1000) #define rcar_zone_to_priv(zone) ((zone)->devdata) -#define rcar_priv_to_dev(priv) ((priv)->dev) +#define rcar_priv_to_dev(priv) ((priv)->common->dev) +#define rcar_has_irq_support(priv) ((priv)->common->base) /* * basic functions @@ -129,6 +140,7 @@ static int rcar_thermal_get_trip_type(struct thermal_zone_device *zone, int trip, enum thermal_trip_type *type) { struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + struct device *dev = rcar_priv_to_dev(priv); /* see rcar_thermal_get_temp() */ switch (trip) { @@ -136,7 +148,7 @@ static int rcar_thermal_get_trip_type(struct thermal_zone_device *zone, *type = THERMAL_TRIP_CRITICAL; break; default: - dev_err(priv->dev, "rcar driver trip error\n"); + dev_err(dev, "rcar driver trip error\n"); return -EINVAL; } @@ -147,6 +159,7 @@ static int rcar_thermal_get_trip_temp(struct thermal_zone_device *zone, int trip, unsigned long *temp) { struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + struct device *dev = rcar_priv_to_dev(priv); /* see rcar_thermal_get_temp() */ switch (trip) { @@ -154,7 +167,7 @@ static int rcar_thermal_get_trip_temp(struct thermal_zone_device *zone, *temp = MCELSIUS(90); break; default: - dev_err(priv->dev, "rcar driver trip error\n"); + dev_err(dev, "rcar driver trip error\n"); return -EINVAL; } @@ -165,12 +178,12 @@ static int rcar_thermal_notify(struct thermal_zone_device *zone, int trip, enum thermal_trip_type type) { struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + struct device *dev = rcar_priv_to_dev(priv); switch (type) { case THERMAL_TRIP_CRITICAL: /* FIXME */ - dev_warn(priv->dev, - "Thermal reached to critical temperature\n"); + dev_warn(dev, "Thermal reached to critical temperature\n"); machine_power_off(); break; default: @@ -192,51 +205,98 @@ static struct thermal_zone_device_ops rcar_thermal_zone_ops = { */ static int rcar_thermal_probe(struct platform_device *pdev) { - struct thermal_zone_device *zone; + struct rcar_thermal_common *common; struct rcar_thermal_priv *priv; - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Could not get platform resource\n"); - return -ENODEV; - } + struct device *dev = &pdev->dev; + struct resource *res, *irq; + int mres = 0; + int i; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(&pdev->dev, "Could not allocate priv\n"); + common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL); + if (!common) { + dev_err(dev, "Could not allocate common\n"); return -ENOMEM; } - priv->dev = &pdev->dev; - mutex_init(&priv->lock); - priv->base = devm_ioremap_nocache(&pdev->dev, - res->start, resource_size(res)); - if (!priv->base) { - dev_err(&pdev->dev, "Unable to ioremap thermal register\n"); - return -ENOMEM; + INIT_LIST_HEAD(&common->head); + common->dev = dev; + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (irq) { + /* + * platform has IRQ support. + * Then, drier use common register + */ + res = platform_get_resource(pdev, IORESOURCE_MEM, mres++); + if (!res) { + dev_err(dev, "Could not get platform resource\n"); + return -ENODEV; + } + + /* + * rcar_has_irq_support() will be enabled + */ + common->base = devm_request_and_ioremap(dev, res); + if (!common->base) { + dev_err(dev, "Unable to ioremap thermal register\n"); + return -ENOMEM; + } } - zone = thermal_zone_device_register("rcar_thermal", 1, 0, priv, - &rcar_thermal_zone_ops, NULL, 0, - IDLE_INTERVAL); - if (IS_ERR(zone)) { - dev_err(&pdev->dev, "thermal zone device is NULL\n"); - return PTR_ERR(zone); + for (i = 0;; i++) { + res = platform_get_resource(pdev, IORESOURCE_MEM, mres++); + if (!res) + break; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(dev, "Could not allocate priv\n"); + return -ENOMEM; + } + + priv->base = devm_request_and_ioremap(dev, res); + if (!priv->base) { + dev_err(dev, "Unable to ioremap priv register\n"); + return -ENOMEM; + } + + priv->common = common; + mutex_init(&priv->lock); + INIT_LIST_HEAD(&priv->list); + + priv->zone = thermal_zone_device_register("rcar_thermal", + 1, 0, priv, + &rcar_thermal_zone_ops, NULL, 0, + IDLE_INTERVAL); + if (IS_ERR(priv->zone)) { + dev_err(dev, "can't register thermal zone\n"); + goto error_unregister; + } + + list_move_tail(&priv->list, &common->head); } - platform_set_drvdata(pdev, zone); + platform_set_drvdata(pdev, common); - dev_info(&pdev->dev, "proved\n"); + dev_info(dev, "%d sensor proved\n", i); return 0; + +error_unregister: + rcar_thermal_for_each_priv(priv, common) + thermal_zone_device_unregister(priv->zone); + + return -ENODEV; } static int rcar_thermal_remove(struct platform_device *pdev) { - struct thermal_zone_device *zone = platform_get_drvdata(pdev); + struct rcar_thermal_common *common = platform_get_drvdata(pdev); + struct rcar_thermal_priv *priv; + + rcar_thermal_for_each_priv(priv, common) + thermal_zone_device_unregister(priv->zone); - thermal_zone_device_unregister(zone); platform_set_drvdata(pdev, NULL); return 0; -- cgit v1.2.3 From e9137a582fcbe36d9dedb8d7f6902a059154b14e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:03:46 +0000 Subject: thermal: rcar: add read/write functions for common/priv data R-Car thermal driver will use struct common in next feature (interrupt support). But the register address is different between struct priv and common. This patch adds read/write functions for struct common, and use macro technique to avoid wrong register access. This is preparation patch for next feature (interrupt support), therefore, there is no user to use this common read/write function at this point. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 48 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 1ba02770153a..cf6aa98956b9 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -29,8 +29,8 @@ #define IDLE_INTERVAL 5000 -#define THSCR 0x2c -#define THSSR 0x30 +#define REG_THSCR 0x2c +#define REG_THSSR 0x30 /* THSCR */ #define CPCTL (1 << 12) @@ -63,21 +63,55 @@ struct rcar_thermal_priv { /* * basic functions */ -static u32 rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) +#if 0 +#define rcar_thermal_common_read(c, r) \ + _rcar_thermal_common_read(c, COMMON_ ##r) +static u32 _rcar_thermal_common_read(struct rcar_thermal_common *common, + u32 reg) +{ + return ioread32(common->base + reg); +} + +#define rcar_thermal_common_write(c, r, d) \ + _rcar_thermal_common_write(c, COMMON_ ##r, d) +static void _rcar_thermal_common_write(struct rcar_thermal_common *common, + u32 reg, u32 data) +{ + iowrite32(data, common->base + reg); +} + +#define rcar_thermal_common_bset(c, r, m, d) \ + _rcar_thermal_common_bset(c, COMMON_ ##r, m, d) +static void _rcar_thermal_common_bset(struct rcar_thermal_common *common, + u32 reg, u32 mask, u32 data) +{ + u32 val; + + val = ioread32(common->base + reg); + val &= ~mask; + val |= (data & mask); + iowrite32(val, common->base + reg); +} +#endif + +#define rcar_thermal_read(p, r) _rcar_thermal_read(p, REG_ ##r) +static u32 _rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) { return ioread32(priv->base + reg); } #if 0 /* no user at this point */ -static void rcar_thermal_write(struct rcar_thermal_priv *priv, - u32 reg, u32 data) +#define rcar_thermal_write(p, r, d) _rcar_thermal_write(p, REG_ ##r, d) +static void _rcar_thermal_write(struct rcar_thermal_priv *priv, + u32 reg, u32 data) { iowrite32(data, priv->base + reg); } #endif -static void rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, - u32 mask, u32 data) +#define rcar_thermal_bset(p, r, m, d) _rcar_thermal_bset(p, REG_ ##r, m, d) +static void _rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, + u32 mask, u32 data) { u32 val; -- cgit v1.2.3 From e0a5172e9eec7f0d3c476e013c51dab62f3fc666 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:04:48 +0000 Subject: thermal: rcar: add interrupt support This patch adds interrupt support for R-Car thermal driver. New generation R-Car thermal sensor interrupt controller was different from old generation. This patch supports new generation sensor only, since the old generation interrupt controller had never been used before, and will never be used in the future. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 159 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 150 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index cf6aa98956b9..80aae3cf65eb 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -19,6 +19,8 @@ */ #include #include +#include +#include #include #include #include @@ -29,8 +31,15 @@ #define IDLE_INTERVAL 5000 +#define COMMON_STR 0x00 +#define COMMON_ENR 0x04 +#define COMMON_INTMSK 0x0c + +#define REG_POSNEG 0x20 +#define REG_FILONOFF 0x28 #define REG_THSCR 0x2c #define REG_THSSR 0x30 +#define REG_INTCTRL 0x34 /* THSCR */ #define CPCTL (1 << 12) @@ -42,14 +51,18 @@ struct rcar_thermal_common { void __iomem *base; struct device *dev; struct list_head head; + spinlock_t lock; }; struct rcar_thermal_priv { void __iomem *base; struct rcar_thermal_common *common; struct thermal_zone_device *zone; + struct delayed_work work; struct mutex lock; struct list_head list; + int id; + int ctemp; }; #define rcar_thermal_for_each_priv(pos, common) \ @@ -59,11 +72,17 @@ struct rcar_thermal_priv { #define rcar_zone_to_priv(zone) ((zone)->devdata) #define rcar_priv_to_dev(priv) ((priv)->common->dev) #define rcar_has_irq_support(priv) ((priv)->common->base) +#define rcar_id_to_shift(priv) ((priv)->id * 8) + +#ifdef DEBUG +# define rcar_force_update_temp(priv) 1 +#else +# define rcar_force_update_temp(priv) 0 +#endif /* * basic functions */ -#if 0 #define rcar_thermal_common_read(c, r) \ _rcar_thermal_common_read(c, COMMON_ ##r) static u32 _rcar_thermal_common_read(struct rcar_thermal_common *common, @@ -92,7 +111,6 @@ static void _rcar_thermal_common_bset(struct rcar_thermal_common *common, val |= (data & mask); iowrite32(val, common->base + reg); } -#endif #define rcar_thermal_read(p, r) _rcar_thermal_read(p, REG_ ##r) static u32 _rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) @@ -100,14 +118,12 @@ static u32 _rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) return ioread32(priv->base + reg); } -#if 0 /* no user at this point */ #define rcar_thermal_write(p, r, d) _rcar_thermal_write(p, REG_ ##r, d) static void _rcar_thermal_write(struct rcar_thermal_priv *priv, u32 reg, u32 data) { iowrite32(data, priv->base + reg); } -#endif #define rcar_thermal_bset(p, r, m, d) _rcar_thermal_bset(p, REG_ ##r, m, d) static void _rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, @@ -124,10 +140,8 @@ static void _rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, /* * zone device functions */ -static int rcar_thermal_get_temp(struct thermal_zone_device *zone, - unsigned long *temp) +static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv) { - struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); struct device *dev = rcar_priv_to_dev(priv); int i; int ctemp, old, new; @@ -163,13 +177,42 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, return -EINVAL; } - *temp = MCELSIUS((ctemp * 5) - 65); + /* + * enable IRQ + */ + if (rcar_has_irq_support(priv)) { + rcar_thermal_write(priv, FILONOFF, 0); + + /* enable Rising/Falling edge interrupt */ + rcar_thermal_write(priv, POSNEG, 0x1); + rcar_thermal_write(priv, INTCTRL, (((ctemp - 0) << 8) | + ((ctemp - 1) << 0))); + } + + dev_dbg(dev, "thermal%d %d -> %d\n", priv->id, priv->ctemp, ctemp); + + priv->ctemp = ctemp; mutex_unlock(&priv->lock); return 0; } +static int rcar_thermal_get_temp(struct thermal_zone_device *zone, + unsigned long *temp) +{ + struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + + if (!rcar_has_irq_support(priv) || rcar_force_update_temp(priv)) + rcar_thermal_update_temp(priv); + + mutex_lock(&priv->lock); + *temp = MCELSIUS((priv->ctemp * 5) - 65); + mutex_unlock(&priv->lock); + + return 0; +} + static int rcar_thermal_get_trip_type(struct thermal_zone_device *zone, int trip, enum thermal_trip_type *type) { @@ -234,6 +277,82 @@ static struct thermal_zone_device_ops rcar_thermal_zone_ops = { .notify = rcar_thermal_notify, }; +/* + * interrupt + */ +#define rcar_thermal_irq_enable(p) _rcar_thermal_irq_ctrl(p, 1) +#define rcar_thermal_irq_disable(p) _rcar_thermal_irq_ctrl(p, 0) +static void _rcar_thermal_irq_ctrl(struct rcar_thermal_priv *priv, int enable) +{ + struct rcar_thermal_common *common = priv->common; + unsigned long flags; + u32 mask = 0x3 << rcar_id_to_shift(priv); /* enable Rising/Falling */ + + spin_lock_irqsave(&common->lock, flags); + + rcar_thermal_common_bset(common, INTMSK, mask, enable ? 0 : mask); + + spin_unlock_irqrestore(&common->lock, flags); +} + +static void rcar_thermal_work(struct work_struct *work) +{ + struct rcar_thermal_priv *priv; + + priv = container_of(work, struct rcar_thermal_priv, work.work); + + rcar_thermal_update_temp(priv); + rcar_thermal_irq_enable(priv); + thermal_zone_device_update(priv->zone); +} + +static u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status) +{ + struct device *dev = rcar_priv_to_dev(priv); + + status = (status >> rcar_id_to_shift(priv)) & 0x3; + + if (status & 0x3) { + dev_dbg(dev, "thermal%d %s%s\n", + priv->id, + (status & 0x2) ? "Rising " : "", + (status & 0x1) ? "Falling" : ""); + } + + return status; +} + +static irqreturn_t rcar_thermal_irq(int irq, void *data) +{ + struct rcar_thermal_common *common = data; + struct rcar_thermal_priv *priv; + unsigned long flags; + u32 status, mask; + + spin_lock_irqsave(&common->lock, flags); + + mask = rcar_thermal_common_read(common, INTMSK); + status = rcar_thermal_common_read(common, STR); + rcar_thermal_common_write(common, STR, 0x000F0F0F & mask); + + spin_unlock_irqrestore(&common->lock, flags); + + status = status & ~mask; + + /* + * check the status + */ + rcar_thermal_for_each_priv(priv, common) { + if (rcar_thermal_had_changed(priv, status)) { + rcar_thermal_irq_disable(priv); + schedule_delayed_work(&priv->work, + msecs_to_jiffies(300)); + } + } + + return IRQ_HANDLED; +} + /* * platform functions */ @@ -245,6 +364,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) struct resource *res, *irq; int mres = 0; int i; + int idle = IDLE_INTERVAL; common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL); if (!common) { @@ -253,10 +373,13 @@ static int rcar_thermal_probe(struct platform_device *pdev) } INIT_LIST_HEAD(&common->head); + spin_lock_init(&common->lock); common->dev = dev; irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (irq) { + int ret; + /* * platform has IRQ support. * Then, drier use common register @@ -267,6 +390,13 @@ static int rcar_thermal_probe(struct platform_device *pdev) return -ENODEV; } + ret = devm_request_irq(dev, irq->start, rcar_thermal_irq, 0, + dev_name(dev), common); + if (ret) { + dev_err(dev, "irq request failed\n "); + return ret; + } + /* * rcar_has_irq_support() will be enabled */ @@ -275,6 +405,11 @@ static int rcar_thermal_probe(struct platform_device *pdev) dev_err(dev, "Unable to ioremap thermal register\n"); return -ENOMEM; } + + /* enable temperature comparation */ + rcar_thermal_common_write(common, ENR, 0x00030303); + + idle = 0; /* polling delaye is not needed */ } for (i = 0;; i++) { @@ -295,19 +430,25 @@ static int rcar_thermal_probe(struct platform_device *pdev) } priv->common = common; + priv->id = i; mutex_init(&priv->lock); INIT_LIST_HEAD(&priv->list); + INIT_DELAYED_WORK(&priv->work, rcar_thermal_work); + rcar_thermal_update_temp(priv); priv->zone = thermal_zone_device_register("rcar_thermal", 1, 0, priv, &rcar_thermal_zone_ops, NULL, 0, - IDLE_INTERVAL); + idle); if (IS_ERR(priv->zone)) { dev_err(dev, "can't register thermal zone\n"); goto error_unregister; } list_move_tail(&priv->list, &common->head); + + if (rcar_has_irq_support(priv)) + rcar_thermal_irq_enable(priv); } platform_set_drvdata(pdev, common); -- cgit v1.2.3 From e6e053f4e47634c07993cb31893556d24e18b65e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:26:13 +0000 Subject: thermal: rcar: remove machine_power_off() from rcar_thermal_notify() Machine/System power-off is run in thermal frame work if it become critical temperature. This patch removed pointless machine_power_off() from thermal_zone_device_ops :: .notify Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 80aae3cf65eb..2eebcadb4c99 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -261,7 +261,6 @@ static int rcar_thermal_notify(struct thermal_zone_device *zone, case THERMAL_TRIP_CRITICAL: /* FIXME */ dev_warn(dev, "Thermal reached to critical temperature\n"); - machine_power_off(); break; default: break; -- cgit v1.2.3 From 82bed4d5f8189a2f9db5d1bb6489ac2615426d65 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Wed, 6 Feb 2013 14:03:13 +1100 Subject: block: remove new __devinit/exit annotations on ramsam driver Signed-off-by: Stephen Rothwell Signed-off-by: Jens Axboe --- drivers/block/rsxx/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rsxx/core.c b/drivers/block/rsxx/core.c index f75219140e70..83dadbee0375 100644 --- a/drivers/block/rsxx/core.c +++ b/drivers/block/rsxx/core.c @@ -319,7 +319,7 @@ static int rsxx_compatibility_check(struct rsxx_cardinfo *card) return 0; } -static int __devinit rsxx_pci_probe(struct pci_dev *dev, +static int rsxx_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { struct rsxx_cardinfo *card; @@ -510,7 +510,7 @@ failed_ida_get: return st; } -static void __devexit rsxx_pci_remove(struct pci_dev *dev) +static void rsxx_pci_remove(struct pci_dev *dev) { struct rsxx_cardinfo *card = pci_get_drvdata(dev); unsigned long flags; @@ -608,7 +608,7 @@ static struct pci_driver rsxx_pci_driver = { .name = DRIVER_NAME, .id_table = rsxx_pci_ids, .probe = rsxx_pci_probe, - .remove = __devexit_p(rsxx_pci_remove), + .remove = rsxx_pci_remove, .suspend = rsxx_pci_suspend, .shutdown = rsxx_pci_shutdown, }; -- cgit v1.2.3 From 57886518a8cdd319a04f5ea06a5f7ffcb8a93120 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 29 Jan 2013 13:41:09 +0100 Subject: iommu: Check for valid pgsize_bitmap in iommu_map/unmap In case the page-size bitmap is zero the code path in iommu_map and iommu_unmap is undefined. Make it defined and return -ENODEV in this case. Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 1065a1a19478..4e6d6f857e6f 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -734,7 +734,8 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova, size_t orig_size = size; int ret = 0; - if (unlikely(domain->ops->map == NULL)) + if (unlikely(domain->ops->unmap == NULL || + domain->ops->pgsize_bitmap == 0UL)) return -ENODEV; /* find out the minimum page size supported */ @@ -808,7 +809,8 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) size_t unmapped_page, unmapped = 0; unsigned int min_pagesz; - if (unlikely(domain->ops->unmap == NULL)) + if (unlikely(domain->ops->unmap == NULL || + domain->ops->pgsize_bitmap == 0UL)) return -ENODEV; /* find out the minimum page size supported */ -- cgit v1.2.3 From d2e121601619631517409cba34e50db3cbff5852 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 29 Jan 2013 13:49:04 +0100 Subject: iommu: Implement DOMAIN_ATTR_PAGING attribute This attribute of a domain can be queried to find out if the domain supports setting up page-tables using the iommu_map() and iommu_unmap() functions. Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 5 +++++ include/linux/iommu.h | 1 + 2 files changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 4e6d6f857e6f..0e0e5f2e0ccc 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -869,6 +869,7 @@ int iommu_domain_get_attr(struct iommu_domain *domain, enum iommu_attr attr, void *data) { struct iommu_domain_geometry *geometry; + bool *paging; int ret = 0; switch (attr) { @@ -876,6 +877,10 @@ int iommu_domain_get_attr(struct iommu_domain *domain, geometry = data; *geometry = domain->geometry; + break; + case DOMAIN_ATTR_PAGING: + paging = data; + *paging = (domain->ops->pgsize_bitmap != 0UL); break; default: if (!domain->ops->domain_get_attr) diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 7e6ce7260b1c..26066f54a849 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -59,6 +59,7 @@ struct iommu_domain { enum iommu_attr { DOMAIN_ATTR_GEOMETRY, + DOMAIN_ATTR_PAGING, DOMAIN_ATTR_MAX, }; -- cgit v1.2.3 From d7787d579cbef9f8079104759a2259fc916c688c Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 29 Jan 2013 14:26:20 +0100 Subject: iommu: Add domain window handling functions Add the iommu_domain_window_enable() and iommu_domain_window_disable() functions to the IOMMU-API. These functions will be used to setup domains that are based on subwindows and not on paging. Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 20 ++++++++++++++++++++ include/linux/iommu.h | 22 ++++++++++++++++++++++ 2 files changed, 42 insertions(+) (limited to 'drivers') diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 0e0e5f2e0ccc..b3aced7356cc 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -852,6 +852,26 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) } EXPORT_SYMBOL_GPL(iommu_unmap); + +int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr, + phys_addr_t paddr, u64 size) +{ + if (unlikely(domain->ops->domain_window_enable == NULL)) + return -ENODEV; + + return domain->ops->domain_window_enable(domain, wnd_nr, paddr, size); +} +EXPORT_SYMBOL_GPL(iommu_domain_window_enable); + +void iommu_domain_window_disable(struct iommu_domain *domain, u32 wnd_nr) +{ + if (unlikely(domain->ops->domain_window_disable == NULL)) + return; + + return domain->ops->domain_window_disable(domain, wnd_nr); +} +EXPORT_SYMBOL_GPL(iommu_domain_window_disable); + static int __init iommu_init(void) { iommu_group_kset = kset_create_and_add("iommu_groups", diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 26066f54a849..5ea3d7250917 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -101,6 +101,12 @@ struct iommu_ops { enum iommu_attr attr, void *data); int (*domain_set_attr)(struct iommu_domain *domain, enum iommu_attr attr, void *data); + + /* Window handling functions */ + int (*domain_window_enable)(struct iommu_domain *domain, u32 wnd_nr, + phys_addr_t paddr, u64 size); + void (*domain_window_disable)(struct iommu_domain *domain, u32 wnd_nr); + unsigned long pgsize_bitmap; }; @@ -158,6 +164,10 @@ extern int iommu_domain_get_attr(struct iommu_domain *domain, enum iommu_attr, extern int iommu_domain_set_attr(struct iommu_domain *domain, enum iommu_attr, void *data); +/* Window handling function prototypes */ +extern int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr, + phys_addr_t offset, u64 size); +extern void iommu_domain_window_disable(struct iommu_domain *domain, u32 wnd_nr); /** * report_iommu_fault() - report about an IOMMU fault to the IOMMU framework * @domain: the iommu domain where the fault has happened @@ -240,6 +250,18 @@ static inline int iommu_unmap(struct iommu_domain *domain, unsigned long iova, return -ENODEV; } +static inline int iommu_domain_window_enable(struct iommu_domain *domain, + u32 wnd_nr, phys_addr_t paddr, + u64 size) +{ + return -ENODEV; +} + +static inline void iommu_domain_window_disable(struct iommu_domain *domain, + u32 wnd_nr) +{ +} + static inline phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, unsigned long iova) { -- cgit v1.2.3 From 693567125bde1966a095267a9d8ca1b8d40f59ee Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Mon, 4 Feb 2013 14:00:01 +0100 Subject: iommu: Add DOMAIN_ATTR_WINDOWS domain attribute This attribute can be used to set and get the number of subwindows on IOMMUs that are window-based. Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 33 ++++++++++++++++++++++++++++++--- include/linux/iommu.h | 5 +++++ 2 files changed, 35 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index b3aced7356cc..b972d430d92b 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -891,6 +891,7 @@ int iommu_domain_get_attr(struct iommu_domain *domain, struct iommu_domain_geometry *geometry; bool *paging; int ret = 0; + u32 *count; switch (attr) { case DOMAIN_ATTR_GEOMETRY: @@ -901,6 +902,15 @@ int iommu_domain_get_attr(struct iommu_domain *domain, case DOMAIN_ATTR_PAGING: paging = data; *paging = (domain->ops->pgsize_bitmap != 0UL); + break; + case DOMAIN_ATTR_WINDOWS: + count = data; + + if (domain->ops->domain_get_windows != NULL) + *count = domain->ops->domain_get_windows(domain); + else + ret = -ENODEV; + break; default: if (!domain->ops->domain_get_attr) @@ -916,9 +926,26 @@ EXPORT_SYMBOL_GPL(iommu_domain_get_attr); int iommu_domain_set_attr(struct iommu_domain *domain, enum iommu_attr attr, void *data) { - if (!domain->ops->domain_set_attr) - return -EINVAL; + int ret = 0; + u32 *count; + + switch (attr) { + case DOMAIN_ATTR_WINDOWS: + count = data; + + if (domain->ops->domain_set_windows != NULL) + ret = domain->ops->domain_set_windows(domain, *count); + else + ret = -ENODEV; - return domain->ops->domain_set_attr(domain, attr, data); + break; + default: + if (domain->ops->domain_set_attr == NULL) + return -EINVAL; + + ret = domain->ops->domain_set_attr(domain, attr, data); + } + + return ret; } EXPORT_SYMBOL_GPL(iommu_domain_set_attr); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 5ea3d7250917..ba3b8a98a049 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -60,6 +60,7 @@ struct iommu_domain { enum iommu_attr { DOMAIN_ATTR_GEOMETRY, DOMAIN_ATTR_PAGING, + DOMAIN_ATTR_WINDOWS, DOMAIN_ATTR_MAX, }; @@ -106,6 +107,10 @@ struct iommu_ops { int (*domain_window_enable)(struct iommu_domain *domain, u32 wnd_nr, phys_addr_t paddr, u64 size); void (*domain_window_disable)(struct iommu_domain *domain, u32 wnd_nr); + /* Set the numer of window per domain */ + int (*domain_set_windows)(struct iommu_domain *domain, u32 w_count); + /* Get the numer of window per domain */ + u32 (*domain_get_windows)(struct iommu_domain *domain); unsigned long pgsize_bitmap; }; -- cgit v1.2.3 From c2c460f7c148aa1a59630f61dac2481f1efb4f4e Mon Sep 17 00:00:00 2001 From: Hideki EIRAKU Date: Mon, 21 Jan 2013 19:54:26 +0900 Subject: iommu/shmobile: Add iommu driver for Renesas IPMMU modules This is the Renesas IPMMU driver and IOMMU API implementation. The IPMMU module supports the MMU function and the PMB function. The MMU function provides address translation by pagetable compatible with ARMv6. The PMB function provides address translation including tile-linear translation. This patch implements the MMU function. The iommu driver does not register a platform driver directly because: - the register space of the MMU function and the PMB function have a common register (used for settings flush), so they should ideally have a way to appropriately share this register. - the MMU function uses the IOMMU API while the PMB function does not. - the two functions may be used independently. Signed-off-by: Hideki EIRAKU Signed-off-by: Joerg Roedel --- drivers/iommu/Kconfig | 74 ++++++ drivers/iommu/Makefile | 2 + drivers/iommu/shmobile-iommu.c | 395 +++++++++++++++++++++++++++++++++ drivers/iommu/shmobile-ipmmu.c | 136 ++++++++++++ drivers/iommu/shmobile-ipmmu.h | 34 +++ include/linux/platform_data/sh_ipmmu.h | 18 ++ 6 files changed, 659 insertions(+) create mode 100644 drivers/iommu/shmobile-iommu.c create mode 100644 drivers/iommu/shmobile-ipmmu.c create mode 100644 drivers/iommu/shmobile-ipmmu.h create mode 100644 include/linux/platform_data/sh_ipmmu.h (limited to 'drivers') diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index e39f9dbf297b..d364494c9e7c 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -187,4 +187,78 @@ config EXYNOS_IOMMU_DEBUG Say N unless you need kernel log message for IOMMU debugging +config SHMOBILE_IPMMU + bool + +config SHMOBILE_IPMMU_TLB + bool + +config SHMOBILE_IOMMU + bool "IOMMU for Renesas IPMMU/IPMMUI" + default n + depends on (ARM && ARCH_SHMOBILE) + select IOMMU_API + select ARM_DMA_USE_IOMMU + select SHMOBILE_IPMMU + select SHMOBILE_IPMMU_TLB + help + Support for Renesas IPMMU/IPMMUI. This option enables + remapping of DMA memory accesses from all of the IP blocks + on the ICB. + + Warning: Drivers (including userspace drivers of UIO + devices) of the IP blocks on the ICB *must* use addresses + allocated from the IPMMU (iova) for DMA with this option + enabled. + + If unsure, say N. + +choice + prompt "IPMMU/IPMMUI address space size" + default SHMOBILE_IOMMU_ADDRSIZE_2048MB + depends on SHMOBILE_IOMMU + help + This option sets IPMMU/IPMMUI address space size by + adjusting the 1st level page table size. The page table size + is calculated as follows: + + page table size = number of page table entries * 4 bytes + number of page table entries = address space size / 1 MiB + + For example, when the address space size is 2048 MiB, the + 1st level page table size is 8192 bytes. + + config SHMOBILE_IOMMU_ADDRSIZE_2048MB + bool "2 GiB" + + config SHMOBILE_IOMMU_ADDRSIZE_1024MB + bool "1 GiB" + + config SHMOBILE_IOMMU_ADDRSIZE_512MB + bool "512 MiB" + + config SHMOBILE_IOMMU_ADDRSIZE_256MB + bool "256 MiB" + + config SHMOBILE_IOMMU_ADDRSIZE_128MB + bool "128 MiB" + + config SHMOBILE_IOMMU_ADDRSIZE_64MB + bool "64 MiB" + + config SHMOBILE_IOMMU_ADDRSIZE_32MB + bool "32 MiB" + +endchoice + +config SHMOBILE_IOMMU_L1SIZE + int + default 8192 if SHMOBILE_IOMMU_ADDRSIZE_2048MB + default 4096 if SHMOBILE_IOMMU_ADDRSIZE_1024MB + default 2048 if SHMOBILE_IOMMU_ADDRSIZE_512MB + default 1024 if SHMOBILE_IOMMU_ADDRSIZE_256MB + default 512 if SHMOBILE_IOMMU_ADDRSIZE_128MB + default 256 if SHMOBILE_IOMMU_ADDRSIZE_64MB + default 128 if SHMOBILE_IOMMU_ADDRSIZE_32MB + endif # IOMMU_SUPPORT diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index f66b816d455c..ef0e5207ad69 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -13,3 +13,5 @@ obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o +obj-$(CONFIG_SHMOBILE_IOMMU) += shmobile-iommu.o +obj-$(CONFIG_SHMOBILE_IPMMU) += shmobile-ipmmu.o diff --git a/drivers/iommu/shmobile-iommu.c b/drivers/iommu/shmobile-iommu.c new file mode 100644 index 000000000000..b6e8b57cf0a8 --- /dev/null +++ b/drivers/iommu/shmobile-iommu.c @@ -0,0 +1,395 @@ +/* + * IOMMU for IPMMU/IPMMUI + * Copyright (C) 2012 Hideki EIRAKU + * + * 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; version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "shmobile-ipmmu.h" + +#define L1_SIZE CONFIG_SHMOBILE_IOMMU_L1SIZE +#define L1_LEN (L1_SIZE / 4) +#define L1_ALIGN L1_SIZE +#define L2_SIZE SZ_1K +#define L2_LEN (L2_SIZE / 4) +#define L2_ALIGN L2_SIZE + +struct shmobile_iommu_domain_pgtable { + uint32_t *pgtable; + dma_addr_t handle; +}; + +struct shmobile_iommu_archdata { + struct list_head attached_list; + struct dma_iommu_mapping *iommu_mapping; + spinlock_t attach_lock; + struct shmobile_iommu_domain *attached; + int num_attached_devices; + struct shmobile_ipmmu *ipmmu; +}; + +struct shmobile_iommu_domain { + struct shmobile_iommu_domain_pgtable l1, l2[L1_LEN]; + spinlock_t map_lock; + spinlock_t attached_list_lock; + struct list_head attached_list; +}; + +static struct shmobile_iommu_archdata *ipmmu_archdata; +static struct kmem_cache *l1cache, *l2cache; + +static int pgtable_alloc(struct shmobile_iommu_domain_pgtable *pgtable, + struct kmem_cache *cache, size_t size) +{ + pgtable->pgtable = kmem_cache_zalloc(cache, GFP_ATOMIC); + if (!pgtable->pgtable) + return -ENOMEM; + pgtable->handle = dma_map_single(NULL, pgtable->pgtable, size, + DMA_TO_DEVICE); + return 0; +} + +static void pgtable_free(struct shmobile_iommu_domain_pgtable *pgtable, + struct kmem_cache *cache, size_t size) +{ + dma_unmap_single(NULL, pgtable->handle, size, DMA_TO_DEVICE); + kmem_cache_free(cache, pgtable->pgtable); +} + +static uint32_t pgtable_read(struct shmobile_iommu_domain_pgtable *pgtable, + unsigned int index) +{ + return pgtable->pgtable[index]; +} + +static void pgtable_write(struct shmobile_iommu_domain_pgtable *pgtable, + unsigned int index, unsigned int count, uint32_t val) +{ + unsigned int i; + + for (i = 0; i < count; i++) + pgtable->pgtable[index + i] = val; + dma_sync_single_for_device(NULL, pgtable->handle + index * sizeof(val), + sizeof(val) * count, DMA_TO_DEVICE); +} + +static int shmobile_iommu_domain_init(struct iommu_domain *domain) +{ + struct shmobile_iommu_domain *sh_domain; + int i, ret; + + sh_domain = kmalloc(sizeof(*sh_domain), GFP_KERNEL); + if (!sh_domain) + return -ENOMEM; + ret = pgtable_alloc(&sh_domain->l1, l1cache, L1_SIZE); + if (ret < 0) { + kfree(sh_domain); + return ret; + } + for (i = 0; i < L1_LEN; i++) + sh_domain->l2[i].pgtable = NULL; + spin_lock_init(&sh_domain->map_lock); + spin_lock_init(&sh_domain->attached_list_lock); + INIT_LIST_HEAD(&sh_domain->attached_list); + domain->priv = sh_domain; + return 0; +} + +static void shmobile_iommu_domain_destroy(struct iommu_domain *domain) +{ + struct shmobile_iommu_domain *sh_domain = domain->priv; + int i; + + for (i = 0; i < L1_LEN; i++) { + if (sh_domain->l2[i].pgtable) + pgtable_free(&sh_domain->l2[i], l2cache, L2_SIZE); + } + pgtable_free(&sh_domain->l1, l1cache, L1_SIZE); + kfree(sh_domain); + domain->priv = NULL; +} + +static int shmobile_iommu_attach_device(struct iommu_domain *domain, + struct device *dev) +{ + struct shmobile_iommu_archdata *archdata = dev->archdata.iommu; + struct shmobile_iommu_domain *sh_domain = domain->priv; + int ret = -EBUSY; + + if (!archdata) + return -ENODEV; + spin_lock(&sh_domain->attached_list_lock); + spin_lock(&archdata->attach_lock); + if (archdata->attached != sh_domain) { + if (archdata->attached) + goto err; + ipmmu_tlb_set(archdata->ipmmu, sh_domain->l1.handle, L1_SIZE, + 0); + ipmmu_tlb_flush(archdata->ipmmu); + archdata->attached = sh_domain; + archdata->num_attached_devices = 0; + list_add(&archdata->attached_list, &sh_domain->attached_list); + } + archdata->num_attached_devices++; + ret = 0; +err: + spin_unlock(&archdata->attach_lock); + spin_unlock(&sh_domain->attached_list_lock); + return ret; +} + +static void shmobile_iommu_detach_device(struct iommu_domain *domain, + struct device *dev) +{ + struct shmobile_iommu_archdata *archdata = dev->archdata.iommu; + struct shmobile_iommu_domain *sh_domain = domain->priv; + + if (!archdata) + return; + spin_lock(&sh_domain->attached_list_lock); + spin_lock(&archdata->attach_lock); + archdata->num_attached_devices--; + if (!archdata->num_attached_devices) { + ipmmu_tlb_set(archdata->ipmmu, 0, 0, 0); + ipmmu_tlb_flush(archdata->ipmmu); + archdata->attached = NULL; + list_del(&archdata->attached_list); + } + spin_unlock(&archdata->attach_lock); + spin_unlock(&sh_domain->attached_list_lock); +} + +static void domain_tlb_flush(struct shmobile_iommu_domain *sh_domain) +{ + struct shmobile_iommu_archdata *archdata; + + spin_lock(&sh_domain->attached_list_lock); + list_for_each_entry(archdata, &sh_domain->attached_list, attached_list) + ipmmu_tlb_flush(archdata->ipmmu); + spin_unlock(&sh_domain->attached_list_lock); +} + +static int l2alloc(struct shmobile_iommu_domain *sh_domain, + unsigned int l1index) +{ + int ret; + + if (!sh_domain->l2[l1index].pgtable) { + ret = pgtable_alloc(&sh_domain->l2[l1index], l2cache, L2_SIZE); + if (ret < 0) + return ret; + } + pgtable_write(&sh_domain->l1, l1index, 1, + sh_domain->l2[l1index].handle | 0x1); + return 0; +} + +static void l2realfree(struct shmobile_iommu_domain_pgtable *l2) +{ + if (l2->pgtable) + pgtable_free(l2, l2cache, L2_SIZE); +} + +static void l2free(struct shmobile_iommu_domain *sh_domain, + unsigned int l1index, + struct shmobile_iommu_domain_pgtable *l2) +{ + pgtable_write(&sh_domain->l1, l1index, 1, 0); + if (sh_domain->l2[l1index].pgtable) { + *l2 = sh_domain->l2[l1index]; + sh_domain->l2[l1index].pgtable = NULL; + } +} + +static int shmobile_iommu_map(struct iommu_domain *domain, unsigned long iova, + phys_addr_t paddr, size_t size, int prot) +{ + struct shmobile_iommu_domain_pgtable l2 = { .pgtable = NULL }; + struct shmobile_iommu_domain *sh_domain = domain->priv; + unsigned int l1index, l2index; + int ret; + + l1index = iova >> 20; + switch (size) { + case SZ_4K: + l2index = (iova >> 12) & 0xff; + spin_lock(&sh_domain->map_lock); + ret = l2alloc(sh_domain, l1index); + if (!ret) + pgtable_write(&sh_domain->l2[l1index], l2index, 1, + paddr | 0xff2); + spin_unlock(&sh_domain->map_lock); + break; + case SZ_64K: + l2index = (iova >> 12) & 0xf0; + spin_lock(&sh_domain->map_lock); + ret = l2alloc(sh_domain, l1index); + if (!ret) + pgtable_write(&sh_domain->l2[l1index], l2index, 0x10, + paddr | 0xff1); + spin_unlock(&sh_domain->map_lock); + break; + case SZ_1M: + spin_lock(&sh_domain->map_lock); + l2free(sh_domain, l1index, &l2); + pgtable_write(&sh_domain->l1, l1index, 1, paddr | 0xc02); + spin_unlock(&sh_domain->map_lock); + ret = 0; + break; + default: + ret = -EINVAL; + } + if (!ret) + domain_tlb_flush(sh_domain); + l2realfree(&l2); + return ret; +} + +static size_t shmobile_iommu_unmap(struct iommu_domain *domain, + unsigned long iova, size_t size) +{ + struct shmobile_iommu_domain_pgtable l2 = { .pgtable = NULL }; + struct shmobile_iommu_domain *sh_domain = domain->priv; + unsigned int l1index, l2index; + uint32_t l2entry = 0; + size_t ret = 0; + + l1index = iova >> 20; + if (!(iova & 0xfffff) && size >= SZ_1M) { + spin_lock(&sh_domain->map_lock); + l2free(sh_domain, l1index, &l2); + spin_unlock(&sh_domain->map_lock); + ret = SZ_1M; + goto done; + } + l2index = (iova >> 12) & 0xff; + spin_lock(&sh_domain->map_lock); + if (sh_domain->l2[l1index].pgtable) + l2entry = pgtable_read(&sh_domain->l2[l1index], l2index); + switch (l2entry & 3) { + case 1: + if (l2index & 0xf) + break; + pgtable_write(&sh_domain->l2[l1index], l2index, 0x10, 0); + ret = SZ_64K; + break; + case 2: + pgtable_write(&sh_domain->l2[l1index], l2index, 1, 0); + ret = SZ_4K; + break; + } + spin_unlock(&sh_domain->map_lock); +done: + if (ret) + domain_tlb_flush(sh_domain); + l2realfree(&l2); + return ret; +} + +static phys_addr_t shmobile_iommu_iova_to_phys(struct iommu_domain *domain, + unsigned long iova) +{ + struct shmobile_iommu_domain *sh_domain = domain->priv; + uint32_t l1entry = 0, l2entry = 0; + unsigned int l1index, l2index; + + l1index = iova >> 20; + l2index = (iova >> 12) & 0xff; + spin_lock(&sh_domain->map_lock); + if (sh_domain->l2[l1index].pgtable) + l2entry = pgtable_read(&sh_domain->l2[l1index], l2index); + else + l1entry = pgtable_read(&sh_domain->l1, l1index); + spin_unlock(&sh_domain->map_lock); + switch (l2entry & 3) { + case 1: + return (l2entry & ~0xffff) | (iova & 0xffff); + case 2: + return (l2entry & ~0xfff) | (iova & 0xfff); + default: + if ((l1entry & 3) == 2) + return (l1entry & ~0xfffff) | (iova & 0xfffff); + return 0; + } +} + +static int find_dev_name(struct shmobile_ipmmu *ipmmu, const char *dev_name) +{ + unsigned int i, n = ipmmu->num_dev_names; + + for (i = 0; i < n; i++) { + if (strcmp(ipmmu->dev_names[i], dev_name) == 0) + return 1; + } + return 0; +} + +static int shmobile_iommu_add_device(struct device *dev) +{ + struct shmobile_iommu_archdata *archdata = ipmmu_archdata; + struct dma_iommu_mapping *mapping; + + if (!find_dev_name(archdata->ipmmu, dev_name(dev))) + return 0; + mapping = archdata->iommu_mapping; + if (!mapping) { + mapping = arm_iommu_create_mapping(&platform_bus_type, 0, + L1_LEN << 20, 0); + if (IS_ERR(mapping)) + return PTR_ERR(mapping); + archdata->iommu_mapping = mapping; + } + dev->archdata.iommu = archdata; + if (arm_iommu_attach_device(dev, mapping)) + pr_err("arm_iommu_attach_device failed\n"); + return 0; +} + +static struct iommu_ops shmobile_iommu_ops = { + .domain_init = shmobile_iommu_domain_init, + .domain_destroy = shmobile_iommu_domain_destroy, + .attach_dev = shmobile_iommu_attach_device, + .detach_dev = shmobile_iommu_detach_device, + .map = shmobile_iommu_map, + .unmap = shmobile_iommu_unmap, + .iova_to_phys = shmobile_iommu_iova_to_phys, + .add_device = shmobile_iommu_add_device, + .pgsize_bitmap = SZ_1M | SZ_64K | SZ_4K, +}; + +int ipmmu_iommu_init(struct shmobile_ipmmu *ipmmu) +{ + static struct shmobile_iommu_archdata *archdata; + + l1cache = kmem_cache_create("shmobile-iommu-pgtable1", L1_SIZE, + L1_ALIGN, SLAB_HWCACHE_ALIGN, NULL); + if (!l1cache) + return -ENOMEM; + l2cache = kmem_cache_create("shmobile-iommu-pgtable2", L2_SIZE, + L2_ALIGN, SLAB_HWCACHE_ALIGN, NULL); + if (!l2cache) { + kmem_cache_destroy(l1cache); + return -ENOMEM; + } + archdata = kmalloc(sizeof(*archdata), GFP_KERNEL); + if (!archdata) { + kmem_cache_destroy(l1cache); + kmem_cache_destroy(l2cache); + return -ENOMEM; + } + spin_lock_init(&archdata->attach_lock); + archdata->attached = NULL; + archdata->ipmmu = ipmmu; + ipmmu_archdata = archdata; + bus_set_iommu(&platform_bus_type, &shmobile_iommu_ops); + return 0; +} diff --git a/drivers/iommu/shmobile-ipmmu.c b/drivers/iommu/shmobile-ipmmu.c new file mode 100644 index 000000000000..8321f89596c4 --- /dev/null +++ b/drivers/iommu/shmobile-ipmmu.c @@ -0,0 +1,136 @@ +/* + * IPMMU/IPMMUI + * Copyright (C) 2012 Hideki EIRAKU + * + * 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; version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include "shmobile-ipmmu.h" + +#define IMCTR1 0x000 +#define IMCTR2 0x004 +#define IMASID 0x010 +#define IMTTBR 0x014 +#define IMTTBCR 0x018 + +#define IMCTR1_TLBEN (1 << 0) +#define IMCTR1_FLUSH (1 << 1) + +static void ipmmu_reg_write(struct shmobile_ipmmu *ipmmu, unsigned long reg_off, + unsigned long data) +{ + iowrite32(data, ipmmu->ipmmu_base + reg_off); +} + +void ipmmu_tlb_flush(struct shmobile_ipmmu *ipmmu) +{ + if (!ipmmu) + return; + + mutex_lock(&ipmmu->flush_lock); + if (ipmmu->tlb_enabled) + ipmmu_reg_write(ipmmu, IMCTR1, IMCTR1_FLUSH | IMCTR1_TLBEN); + else + ipmmu_reg_write(ipmmu, IMCTR1, IMCTR1_FLUSH); + mutex_unlock(&ipmmu->flush_lock); +} + +void ipmmu_tlb_set(struct shmobile_ipmmu *ipmmu, unsigned long phys, int size, + int asid) +{ + if (!ipmmu) + return; + + mutex_lock(&ipmmu->flush_lock); + switch (size) { + default: + ipmmu->tlb_enabled = 0; + break; + case 0x2000: + ipmmu_reg_write(ipmmu, IMTTBCR, 1); + ipmmu->tlb_enabled = 1; + break; + case 0x1000: + ipmmu_reg_write(ipmmu, IMTTBCR, 2); + ipmmu->tlb_enabled = 1; + break; + case 0x800: + ipmmu_reg_write(ipmmu, IMTTBCR, 3); + ipmmu->tlb_enabled = 1; + break; + case 0x400: + ipmmu_reg_write(ipmmu, IMTTBCR, 4); + ipmmu->tlb_enabled = 1; + break; + case 0x200: + ipmmu_reg_write(ipmmu, IMTTBCR, 5); + ipmmu->tlb_enabled = 1; + break; + case 0x100: + ipmmu_reg_write(ipmmu, IMTTBCR, 6); + ipmmu->tlb_enabled = 1; + break; + case 0x80: + ipmmu_reg_write(ipmmu, IMTTBCR, 7); + ipmmu->tlb_enabled = 1; + break; + } + ipmmu_reg_write(ipmmu, IMTTBR, phys); + ipmmu_reg_write(ipmmu, IMASID, asid); + mutex_unlock(&ipmmu->flush_lock); +} + +static int ipmmu_probe(struct platform_device *pdev) +{ + struct shmobile_ipmmu *ipmmu; + struct resource *res; + struct shmobile_ipmmu_platform_data *pdata = pdev->dev.platform_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "cannot get platform resources\n"); + return -ENOENT; + } + ipmmu = devm_kzalloc(&pdev->dev, sizeof(*ipmmu), GFP_KERNEL); + if (!ipmmu) { + dev_err(&pdev->dev, "cannot allocate device data\n"); + return -ENOMEM; + } + mutex_init(&ipmmu->flush_lock); + ipmmu->dev = &pdev->dev; + ipmmu->ipmmu_base = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); + if (!ipmmu->ipmmu_base) { + dev_err(&pdev->dev, "ioremap_nocache failed\n"); + return -ENOMEM; + } + ipmmu->dev_names = pdata->dev_names; + ipmmu->num_dev_names = pdata->num_dev_names; + platform_set_drvdata(pdev, ipmmu); + ipmmu_reg_write(ipmmu, IMCTR1, 0x0); /* disable TLB */ + ipmmu_reg_write(ipmmu, IMCTR2, 0x0); /* disable PMB */ + ipmmu_iommu_init(ipmmu); + return 0; +} + +static struct platform_driver ipmmu_driver = { + .probe = ipmmu_probe, + .driver = { + .owner = THIS_MODULE, + .name = "ipmmu", + }, +}; + +static int __init ipmmu_init(void) +{ + return platform_driver_register(&ipmmu_driver); +} +subsys_initcall(ipmmu_init); diff --git a/drivers/iommu/shmobile-ipmmu.h b/drivers/iommu/shmobile-ipmmu.h new file mode 100644 index 000000000000..4d53684673e1 --- /dev/null +++ b/drivers/iommu/shmobile-ipmmu.h @@ -0,0 +1,34 @@ +/* shmobile-ipmmu.h + * + * Copyright (C) 2012 Hideki EIRAKU + * + * 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; version 2 of the License. + */ + +#ifndef __SHMOBILE_IPMMU_H__ +#define __SHMOBILE_IPMMU_H__ + +struct shmobile_ipmmu { + struct device *dev; + void __iomem *ipmmu_base; + int tlb_enabled; + struct mutex flush_lock; + const char * const *dev_names; + unsigned int num_dev_names; +}; + +#ifdef CONFIG_SHMOBILE_IPMMU_TLB +void ipmmu_tlb_flush(struct shmobile_ipmmu *ipmmu); +void ipmmu_tlb_set(struct shmobile_ipmmu *ipmmu, unsigned long phys, int size, + int asid); +int ipmmu_iommu_init(struct shmobile_ipmmu *ipmmu); +#else +static inline int ipmmu_iommu_init(struct shmobile_ipmmu *ipmmu) +{ + return -EINVAL; +} +#endif + +#endif /* __SHMOBILE_IPMMU_H__ */ diff --git a/include/linux/platform_data/sh_ipmmu.h b/include/linux/platform_data/sh_ipmmu.h new file mode 100644 index 000000000000..39f7405cdac5 --- /dev/null +++ b/include/linux/platform_data/sh_ipmmu.h @@ -0,0 +1,18 @@ +/* sh_ipmmu.h + * + * Copyright (C) 2012 Hideki EIRAKU + * + * 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; version 2 of the License. + */ + +#ifndef __SH_IPMMU_H__ +#define __SH_IPMMU_H__ + +struct shmobile_ipmmu_platform_data { + const char * const *dev_names; + unsigned int num_dev_names; +}; + +#endif /* __SH_IPMMU_H__ */ -- cgit v1.2.3 From 09f9408dc394835157397f9a4d714ecb53d03976 Mon Sep 17 00:00:00 2001 From: Eddi De Pieri Date: Mon, 14 Jan 2013 18:21:32 -0300 Subject: [media] Support Digivox Mini HD (rtl2832) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for Digivox Mini HD (rtl2832) The tuner works, but with worst performance then realtek linux driver, due to incomplete implementation of fc2580.c Signed-off-by: Eddi De Pieri Tested-by: Lorenzo Dongarrà Acked-by: Antti Palosaari Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 4ab0dd8dd9a8..e127bd134be0 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1368,6 +1368,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = { &rtl2832u_props, "ASUS My Cinema-U3100Mini Plus V2", NULL) }, { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd393, &rtl2832u_props, "GIGABYTE U7300", NULL) }, + { DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1104, + &rtl2832u_props, "Digivox Micro Hd", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); -- cgit v1.2.3 From f85ed0ceeba78b6b15a857ce48888fdb52de28d0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 6 Feb 2013 08:29:39 -0200 Subject: Revert "[media] drivers/media/usb/dvb-usb/dib0700_core.c: fix left shift" On Wed, 6 Feb 2013 09:04:39 +0000 Olivier GRENIE wrote: > I do not agree with the patch. Let's take an example: adap->id = 0. Then: > * 1 << ~(adap->id) = 1 << ~(0) = 0 > * ~(1 << adap->id) = ~(1 << 0) = 0xFE > > The correct change should be: st->channel_state |= 1 << (1 - adap->id); Indeed, the original source code was not correct. Requested-by: Olivier GRENIE Cc: Patrick Boettcher Cc: Nickolai Zeldovich Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/dib0700_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c index bd6a43785d5e..bf2a908d74cf 100644 --- a/drivers/media/usb/dvb-usb/dib0700_core.c +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -584,7 +584,7 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) if (onoff) st->channel_state |= 1 << (adap->id); else - st->channel_state &= ~(1 << (adap->id)); + st->channel_state |= 1 << ~(adap->id); } else { if (onoff) st->channel_state |= 1 << (adap->fe_adap[0].stream.props.endpoint-2); -- cgit v1.2.3 From 61a2353c70d1f33c52768fb7d3fd7ee173928086 Mon Sep 17 00:00:00 2001 From: Volokh Konstantin Date: Wed, 16 Jan 2013 09:00:48 -0300 Subject: [media] staging: media: go7007: memory clear fix memory clearing for v4l2_subdev allocation Signed-off-by: Volokh Konstantin Acked-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/go7007-driver.c | 2 +- drivers/staging/media/go7007/go7007-v4l2.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c index c9dfc75d1d2c..00014e754c75 100644 --- a/drivers/staging/media/go7007/go7007-driver.c +++ b/drivers/staging/media/go7007/go7007-driver.c @@ -572,7 +572,7 @@ struct go7007 *go7007_alloc(struct go7007_board_info *board, struct device *dev) struct go7007 *go; int i; - go = kmalloc(sizeof(struct go7007), GFP_KERNEL); + go = kzalloc(sizeof(struct go7007), GFP_KERNEL); if (go == NULL) return NULL; go->dev = dev; diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index 0d3713dceba5..94899759229f 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -98,7 +98,7 @@ static int go7007_open(struct file *file) if (go->status != STATUS_ONLINE) return -EBUSY; - gofh = kmalloc(sizeof(struct go7007_file), GFP_KERNEL); + gofh = kzalloc(sizeof(struct go7007_file), GFP_KERNEL); if (gofh == NULL) return -ENOMEM; ++go->ref_count; -- cgit v1.2.3 From 7a295d1289f2be16f80f0a5242db330d542e0037 Mon Sep 17 00:00:00 2001 From: Volokh Konstantin Date: Wed, 16 Jan 2013 09:00:49 -0300 Subject: [media] staging: media: go7007: firmware protection Protection for unfirmware load If no firmware was loaded (no exists,wrong or some error) then rmmod fails with OOPS, so need some protection stuff. Signed-off-by: Volokh Konstantin Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/go7007-usb.c | 2 +- drivers/staging/media/go7007/go7007-v4l2.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/go7007/go7007-usb.c b/drivers/staging/media/go7007/go7007-usb.c index 5443e25086e9..a6cad1589475 100644 --- a/drivers/staging/media/go7007/go7007-usb.c +++ b/drivers/staging/media/go7007/go7007-usb.c @@ -1245,7 +1245,6 @@ static void go7007_usb_disconnect(struct usb_interface *intf) struct urb *vurb, *aurb; int i; - go->status = STATUS_SHUTDOWN; usb_kill_urb(usb->intr_urb); /* Free USB-related structs */ @@ -1269,6 +1268,7 @@ static void go7007_usb_disconnect(struct usb_interface *intf) kfree(go->hpi_context); go7007_remove(go); + go->status = STATUS_SHUTDOWN; } static struct usb_driver go7007_usb_driver = { diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index 94899759229f..39e6749353be 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -1832,5 +1832,6 @@ void go7007_v4l2_remove(struct go7007 *go) mutex_unlock(&go->hw_lock); if (go->video_dev) video_unregister_device(go->video_dev); - v4l2_device_unregister(&go->v4l2_dev); + if (go->status != STATUS_SHUTDOWN) + v4l2_device_unregister(&go->v4l2_dev); } -- cgit v1.2.3 From d18b6acfe2f1c279ab2fb510eabc405cf538fb68 Mon Sep 17 00:00:00 2001 From: Volokh Konstantin Date: Wed, 16 Jan 2013 09:00:50 -0300 Subject: [media] staging: media: go7007: i2c GPIO initialization Reset i2c stuff for GO7007_BOARDID_ADLINK_MPG24 need reset GPIO always when encoder initialize Signed-off-by: Volokh Konstantin Reviewed-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/go7007-driver.c | 5 +++++ drivers/staging/media/go7007/go7007-usb.c | 3 --- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c index 00014e754c75..0aaeb0aeb99e 100644 --- a/drivers/staging/media/go7007/go7007-driver.c +++ b/drivers/staging/media/go7007/go7007-driver.c @@ -173,6 +173,11 @@ static int go7007_init_encoder(struct go7007 *go) go7007_write_addr(go, 0x3c82, 0x0001); go7007_write_addr(go, 0x3c80, 0x00fe); } + if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) { + /* set GPIO5 to be an output, currently low */ + go7007_write_addr(go, 0x3c82, 0x0000); + go7007_write_addr(go, 0x3c80, 0x00df); + } return 0; } diff --git a/drivers/staging/media/go7007/go7007-usb.c b/drivers/staging/media/go7007/go7007-usb.c index a6cad1589475..9dbf5ecd05a2 100644 --- a/drivers/staging/media/go7007/go7007-usb.c +++ b/drivers/staging/media/go7007/go7007-usb.c @@ -1110,9 +1110,6 @@ static int go7007_usb_probe(struct usb_interface *intf, } else { u16 channel; - /* set GPIO5 to be an output, currently low */ - go7007_write_addr(go, 0x3c82, 0x0000); - go7007_write_addr(go, 0x3c80, 0x00df); /* read channel number from GPIO[1:0] */ go7007_read_addr(go, 0x3c81, &channel); channel &= 0x3; -- cgit v1.2.3 From 0a147c3bf75d429fc7922abb582c7c686b028bc4 Mon Sep 17 00:00:00 2001 From: Volokh Konstantin Date: Wed, 16 Jan 2013 09:00:51 -0300 Subject: [media] staging: media: go7007: call_all stream stuff Some Additional stuff for v4l2_subdev stream events partial need for new style framework. Also need for wis_tw2804 notification stuff Signed-off-by: Volokh Konstantin Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/go7007-v4l2.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index 39e6749353be..cb9fe33050c7 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -953,6 +953,7 @@ static int vidioc_streamon(struct file *file, void *priv, } mutex_unlock(&go->hw_lock); mutex_unlock(&gofh->lock); + call_all(&go->v4l2_dev, video, s_stream, 1); return retval; } @@ -968,6 +969,7 @@ static int vidioc_streamoff(struct file *file, void *priv, mutex_lock(&gofh->lock); go7007_streamoff(go); mutex_unlock(&gofh->lock); + call_all(&go->v4l2_dev, video, s_stream, 0); return 0; } -- cgit v1.2.3 From 42f9de6eab3892d7da544be1cc882530eab5b203 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Fri, 25 Jan 2013 19:23:30 -0300 Subject: [media] staging/media/go7007: Use kmemdup rather than duplicating its implementation Found with coccicheck. The semantic patch that makes this change is available in scripts/coccinelle/api/memdup.cocci. Signed-off-by: Peter Huewe Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/go7007-driver.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c index 0aaeb0aeb99e..66950916df23 100644 --- a/drivers/staging/media/go7007/go7007-driver.c +++ b/drivers/staging/media/go7007/go7007-driver.c @@ -108,14 +108,13 @@ static int go7007_load_encoder(struct go7007 *go) return -1; } fw_len = fw_entry->size - 16; - bounce = kmalloc(fw_len, GFP_KERNEL); + bounce = kmemdup(fw_entry->data + 16, fw_len, GFP_KERNEL); if (bounce == NULL) { v4l2_err(go, "unable to allocate %d bytes for " "firmware transfer\n", fw_len); release_firmware(fw_entry); return -1; } - memcpy(bounce, fw_entry->data + 16, fw_len); release_firmware(fw_entry); if (go7007_interface_reset(go) < 0 || go7007_send_firmware(go, bounce, fw_len) < 0 || -- cgit v1.2.3 From fb1e7b053a41807b613247535f269108f3b4fc70 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 28 Jan 2013 10:19:42 -0300 Subject: [media] staging: go7007: fix test for V4L2_STD_SECAM The current test doesn't make a lot of sense. It's likely to be true, which is what we would want in most cases. From looking at how this is handled in other drivers, I think "&" was intended instead of "*". It's an easy mistake to make because they are next to each other on the keyboard. Signed-off-by: Dan Carpenter Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/wis-saa7113.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/media/go7007/wis-saa7113.c b/drivers/staging/media/go7007/wis-saa7113.c index 8810c1e6e1ed..891cde713a47 100644 --- a/drivers/staging/media/go7007/wis-saa7113.c +++ b/drivers/staging/media/go7007/wis-saa7113.c @@ -141,7 +141,7 @@ static int wis_saa7113_command(struct i2c_client *client, } else if (dec->norm & V4L2_STD_PAL) { write_reg(client, 0x0e, 0x01); write_reg(client, 0x10, 0x48); - } else if (dec->norm * V4L2_STD_SECAM) { + } else if (dec->norm & V4L2_STD_SECAM) { write_reg(client, 0x0e, 0x50); write_reg(client, 0x10, 0x48); } -- cgit v1.2.3 From d78a488221059d2bc8627c5175f1d358c57d74a0 Mon Sep 17 00:00:00 2001 From: Scott Jiang Date: Fri, 18 Jan 2013 17:09:48 -0300 Subject: [media] blackfin: add error frame support Mark current frame as error frame when ppi error interrupt report fifo error. Member next_frm in struct bcap_device can be optimized out. Signed-off-by: Scott Jiang Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/blackfin/bfin_capture.c | 37 ++++++++++++++------------ drivers/media/platform/blackfin/ppi.c | 11 ++++++++ include/media/blackfin/ppi.h | 3 ++- 3 files changed, 33 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index 12e0faf53403..5f209d5810dc 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -91,8 +91,6 @@ struct bcap_device { int num_sensor_formats; /* pointing to current video buffer */ struct bcap_buffer *cur_frm; - /* pointing to next video buffer */ - struct bcap_buffer *next_frm; /* buffer queue used in videobuf2 */ struct vb2_queue buffer_queue; /* allocator-specific contexts for each plane */ @@ -455,10 +453,10 @@ static int bcap_stop_streaming(struct vb2_queue *vq) /* release all active buffers */ while (!list_empty(&bcap_dev->dma_queue)) { - bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next, + bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next, struct bcap_buffer, list); - list_del(&bcap_dev->next_frm->list); - vb2_buffer_done(&bcap_dev->next_frm->vb, VB2_BUF_STATE_ERROR); + list_del(&bcap_dev->cur_frm->list); + vb2_buffer_done(&bcap_dev->cur_frm->vb, VB2_BUF_STATE_ERROR); } return 0; } @@ -535,10 +533,21 @@ static irqreturn_t bcap_isr(int irq, void *dev_id) spin_lock(&bcap_dev->lock); - if (bcap_dev->cur_frm != bcap_dev->next_frm) { + if (!list_empty(&bcap_dev->dma_queue)) { v4l2_get_timestamp(&vb->v4l2_buf.timestamp); - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); - bcap_dev->cur_frm = bcap_dev->next_frm; + if (ppi->err) { + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); + ppi->err = false; + } else { + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + } + bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next, + struct bcap_buffer, list); + list_del(&bcap_dev->cur_frm->list); + } else { + /* clear error flag, we will get a new frame */ + if (ppi->err) + ppi->err = false; } ppi->ops->stop(ppi); @@ -546,13 +555,8 @@ static irqreturn_t bcap_isr(int irq, void *dev_id) if (bcap_dev->stop) { complete(&bcap_dev->comp); } else { - if (!list_empty(&bcap_dev->dma_queue)) { - bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next, - struct bcap_buffer, list); - list_del(&bcap_dev->next_frm->list); - addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->next_frm->vb, 0); - ppi->ops->update_addr(ppi, (unsigned long)addr); - } + addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0); + ppi->ops->update_addr(ppi, (unsigned long)addr); ppi->ops->start(ppi); } @@ -586,9 +590,8 @@ static int bcap_streamon(struct file *file, void *priv, } /* get the next frame from the dma queue */ - bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next, + bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next, struct bcap_buffer, list); - bcap_dev->cur_frm = bcap_dev->next_frm; /* remove buffer from the dma queue */ list_del(&bcap_dev->cur_frm->list); addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0); diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c index 1e24584605f2..01b5b501347e 100644 --- a/drivers/media/platform/blackfin/ppi.c +++ b/drivers/media/platform/blackfin/ppi.c @@ -59,19 +59,30 @@ static irqreturn_t ppi_irq_err(int irq, void *dev_id) * others are W1C */ status = bfin_read16(®->status); + if (status & 0x3000) + ppi->err = true; bfin_write16(®->status, 0xff00); break; } case PPI_TYPE_EPPI: { struct bfin_eppi_regs *reg = info->base; + unsigned short status; + + status = bfin_read16(®->status); + if (status & 0x2) + ppi->err = true; bfin_write16(®->status, 0xffff); break; } case PPI_TYPE_EPPI3: { struct bfin_eppi3_regs *reg = info->base; + unsigned long stat; + stat = bfin_read32(®->stat); + if (stat & 0x2) + ppi->err = true; bfin_write32(®->stat, 0xc0ff); break; } diff --git a/include/media/blackfin/ppi.h b/include/media/blackfin/ppi.h index 65c467576b31..d0697f4edf87 100644 --- a/include/media/blackfin/ppi.h +++ b/include/media/blackfin/ppi.h @@ -86,7 +86,8 @@ struct ppi_if { unsigned long ppi_control; const struct ppi_ops *ops; const struct ppi_info *info; - bool err_int; + bool err_int; /* if we need request error interrupt */ + bool err; /* if ppi has fifo error */ void *priv; }; -- cgit v1.2.3 From 8268979a429f6c3f6584d7d650af84444336d468 Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sat, 19 Jan 2013 19:41:08 -0300 Subject: [media] [V2,01/24] pci/cx88/cx88.h: use IS_ENABLED() macro replace: #if defined(CONFIG_VIDEO_CX88_DVB) || \ defined(CONFIG_VIDEO_CX88_DVB_MODULE) with: #if IS_ENABLED(CONFIG_VIDEO_CX88_DVB) This change was made for: CONFIG_VIDEO_CX88_DVB, CONFIG_VIDEO_CX88_BLACKBIRD, CONFIG_VIDEO_CX88_VP3054 Reported-by: Mauro Carvalho Chehab Signed-off-by: Peter Senna Tschudin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx88/cx88.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/cx88/cx88.h b/drivers/media/pci/cx88/cx88.h index ba0dba4a4d22..feff53c0a251 100644 --- a/drivers/media/pci/cx88/cx88.h +++ b/drivers/media/pci/cx88/cx88.h @@ -363,7 +363,7 @@ struct cx88_core { unsigned int tuner_formats; /* config info -- dvb */ -#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE) +#if IS_ENABLED(CONFIG_VIDEO_CX88_DVB) int (*prev_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage); #endif void (*gate_ctrl)(struct cx88_core *core, int open); @@ -562,8 +562,7 @@ struct cx8802_dev { /* for blackbird only */ struct list_head devlist; -#if defined(CONFIG_VIDEO_CX88_BLACKBIRD) || \ - defined(CONFIG_VIDEO_CX88_BLACKBIRD_MODULE) +#if IS_ENABLED(CONFIG_VIDEO_CX88_BLACKBIRD) struct video_device *mpeg_dev; u32 mailbox; int width; @@ -574,13 +573,12 @@ struct cx8802_dev { struct cx2341x_handler cxhdl; #endif -#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE) +#if IS_ENABLED(CONFIG_VIDEO_CX88_DVB) /* for dvb only */ struct videobuf_dvb_frontends frontends; #endif -#if defined(CONFIG_VIDEO_CX88_VP3054) || \ - defined(CONFIG_VIDEO_CX88_VP3054_MODULE) +#if IS_ENABLED(CONFIG_VIDEO_CX88_VP3054) /* For VP3045 secondary I2C bus support */ struct vp3054_i2c_state *vp3054; #endif -- cgit v1.2.3 From 8c31522c64968ae790b0d767d27655c24d913837 Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sat, 19 Jan 2013 19:41:09 -0300 Subject: [media] [V2,02/24] pci/saa7134/saa7134.h: use IS_ENABLED() macro replace: #if defined(CONFIG_VIDEO_SAA7134_DVB) || \ defined(CONFIG_VIDEO_SAA7134_DVB_MODULE) with: #if IS_ENABLED(CONFIG_VIDEO_SAA7134_DVB) This change was made for: CONFIG_VIDEO_SAA7134_DVB Reported-by: Mauro Carvalho Chehab Signed-off-by: Peter Senna Tschudin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/saa7134/saa7134.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index 6eae4321229a..f804324e07fd 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -42,7 +42,7 @@ #include #include #include -#if defined(CONFIG_VIDEO_SAA7134_DVB) || defined(CONFIG_VIDEO_SAA7134_DVB_MODULE) +#if IS_ENABLED(CONFIG_VIDEO_SAA7134_DVB) #include #endif @@ -644,7 +644,7 @@ struct saa7134_dev { struct work_struct empress_workqueue; int empress_started; -#if defined(CONFIG_VIDEO_SAA7134_DVB) || defined(CONFIG_VIDEO_SAA7134_DVB_MODULE) +#if IS_ENABLED(CONFIG_VIDEO_SAA7134_DVB) /* SAA7134_MPEG_DVB only */ struct videobuf_dvb_frontends frontends; int (*original_demod_sleep)(struct dvb_frontend *fe); -- cgit v1.2.3 From b168e810b0e8bef68ccdfb06f741cb4d7142578c Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sat, 19 Jan 2013 19:41:10 -0300 Subject: [media] [V2,03/24] pci/ttpci/av7110.c: use IS_ENABLED() macro replace: #if defined(CONFIG_INPUT_EVDEV) || \ defined(CONFIG_INPUT_EVDEV_MODULE) with: #if IS_ENABLED(CONFIG_INPUT_EVDEV) This change was made for: CONFIG_INPUT_EVDEV, CONFIG_DVB_SP8870 Reported-by: Mauro Carvalho Chehab Signed-off-by: Peter Senna Tschudin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ttpci/av7110.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c index 9f281f1db45e..3dc7aa9b6f40 100644 --- a/drivers/media/pci/ttpci/av7110.c +++ b/drivers/media/pci/ttpci/av7110.c @@ -235,7 +235,7 @@ static void recover_arm(struct av7110 *av7110) restart_feeds(av7110); -#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) +#if IS_ENABLED(CONFIG_INPUT_EVDEV) av7110_check_ir_config(av7110, true); #endif } @@ -268,7 +268,7 @@ static int arm_thread(void *data) if (!av7110->arm_ready) continue; -#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) +#if IS_ENABLED(CONFIG_INPUT_EVDEV) av7110_check_ir_config(av7110, false); #endif @@ -1730,7 +1730,7 @@ static int alps_tdlb7_tuner_set_params(struct dvb_frontend *fe) static int alps_tdlb7_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) { -#if defined(CONFIG_DVB_SP8870) || defined(CONFIG_DVB_SP8870_MODULE) +#if IS_ENABLED(CONFIG_DVB_SP8870) struct av7110* av7110 = fe->dvb->priv; return request_firmware(fw, name, &av7110->dev->pci->dev); @@ -2725,7 +2725,7 @@ static int av7110_attach(struct saa7146_dev* dev, mutex_init(&av7110->ioctl_mutex); -#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) +#if IS_ENABLED(CONFIG_INPUT_EVDEV) av7110_ir_init(av7110); #endif printk(KERN_INFO "dvb-ttpci: found av7110-%d.\n", av7110_num); @@ -2768,7 +2768,7 @@ static int av7110_detach(struct saa7146_dev* saa) struct av7110 *av7110 = saa->ext_priv; dprintk(4, "%p\n", av7110); -#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) +#if IS_ENABLED(CONFIG_INPUT_EVDEV) av7110_ir_exit(av7110); #endif if (budgetpatch || av7110->full_ts) { -- cgit v1.2.3 From f1c16adce0d2a14376dcafd00c90a88c885b4fed Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sat, 19 Jan 2013 19:41:11 -0300 Subject: [media] [V2,04/24] platform/marvell-ccic/mcam-core.h: use IS_ENABLED() macro replace: #if defined(CONFIG_VIDEOBUF2_VMALLOC) || \ defined(CONFIG_VIDEOBUF2_VMALLOC_MODULE) with: #if IS_ENABLED(CONFIG_VIDEOBUF2_VMALLOC) This change was made for: CONFIG_VIDEOBUF2_VMALLOC, CONFIG_VIDEOBUF2_DMA_CONTIG, CONFIG_VIDEOBUF2_DMA_SG Reported-by: Mauro Carvalho Chehab Signed-off-by: Peter Senna Tschudin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/marvell-ccic/mcam-core.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index 5e802c6b398e..6c53ac9681da 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -15,15 +15,15 @@ * Create our own symbols for the supported buffer modes, but, for now, * base them entirely on which videobuf2 options have been selected. */ -#if defined(CONFIG_VIDEOBUF2_VMALLOC) || defined(CONFIG_VIDEOBUF2_VMALLOC_MODULE) +#if IS_ENABLED(CONFIG_VIDEOBUF2_VMALLOC) #define MCAM_MODE_VMALLOC 1 #endif -#if defined(CONFIG_VIDEOBUF2_DMA_CONTIG) || defined(CONFIG_VIDEOBUF2_DMA_CONTIG_MODULE) +#if IS_ENABLED(CONFIG_VIDEOBUF2_DMA_CONTIG) #define MCAM_MODE_DMA_CONTIG 1 #endif -#if defined(CONFIG_VIDEOBUF2_DMA_SG) || defined(CONFIG_VIDEOBUF2_DMA_SG_MODULE) +#if IS_ENABLED(CONFIG_VIDEOBUF2_DMA_SG) #define MCAM_MODE_DMA_SG 1 #endif -- cgit v1.2.3 From 9ecf9b085a0926e07c78c08a07296bbfd1c37d07 Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sat, 19 Jan 2013 19:41:29 -0300 Subject: [media] [V2,22/24] usb/hdpvr/hdpvr-core.c: use IS_ENABLED() macro replace: #if defined(CONFIG_I2C) || \ defined(CONFIG_I2C_MODULE) with: #if IS_ENABLED(CONFIG_I2C) This change was made for: CONFIG_I2C Reported-by: Mauro Carvalho Chehab Signed-off-by: Peter Senna Tschudin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/hdpvr/hdpvr-core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/hdpvr/hdpvr-core.c b/drivers/media/usb/hdpvr/hdpvr-core.c index 84dc26fe80ee..5c6193536399 100644 --- a/drivers/media/usb/hdpvr/hdpvr-core.c +++ b/drivers/media/usb/hdpvr/hdpvr-core.c @@ -391,7 +391,7 @@ static int hdpvr_probe(struct usb_interface *interface, goto error; } -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) retval = hdpvr_register_i2c_adapter(dev); if (retval < 0) { v4l2_err(&dev->v4l2_dev, "i2c adapter register failed\n"); @@ -419,7 +419,7 @@ static int hdpvr_probe(struct usb_interface *interface, return 0; reg_fail: -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_adapter(&dev->i2c_adapter); #endif error: @@ -451,7 +451,7 @@ static void hdpvr_disconnect(struct usb_interface *interface) mutex_lock(&dev->io_mutex); hdpvr_cancel_queue(dev); mutex_unlock(&dev->io_mutex); -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_adapter(&dev->i2c_adapter); #endif video_unregister_device(dev->video_dev); -- cgit v1.2.3 From 6a29b8080bd99793c86060d0dbebfd81c6aa4281 Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sat, 19 Jan 2013 19:41:30 -0300 Subject: [media] [V2,23/24] usb/hdpvr/hdpvr-i2c.c: use IS_ENABLED() macro replace: #if defined(CONFIG_I2C) || \ defined(CONFIG_I2C_MODULE) with: #if IS_ENABLED(CONFIG_I2C) This change was made for: CONFIG_I2C Reported-by: Mauro Carvalho Chehab Signed-off-by: Peter Senna Tschudin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/hdpvr/hdpvr-i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/hdpvr/hdpvr-i2c.c b/drivers/media/usb/hdpvr/hdpvr-i2c.c index 6c5054f3f674..a38f58c4c6bf 100644 --- a/drivers/media/usb/hdpvr/hdpvr-i2c.c +++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c @@ -13,7 +13,7 @@ * */ -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) #include #include -- cgit v1.2.3 From ffe4db06fdd294a3326e2f1009863825c0f6401c Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sat, 19 Jan 2013 19:41:31 -0300 Subject: [media] [V2,24/24] v4l2-core/v4l2-common.c: use IS_ENABLED() macro replace: #if defined(CONFIG_MEDIA_TUNER_TEA5761) || \ defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) with: #if IS_ENABLED(CONFIG_MEDIA_TUNER_TEA5761) This change was made for: CONFIG_MEDIA_TUNER_TEA5761 Also replaced: #if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) with: #if IS_ENABLED(CONFIG_I2C) Reported-by: Mauro Carvalho Chehab Signed-off-by: Peter Senna Tschudin Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 614316f9b7a4..aa044f491666 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -238,7 +238,7 @@ int v4l2_chip_match_host(const struct v4l2_dbg_match *match) } EXPORT_SYMBOL(v4l2_chip_match_host); -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_I2C) int v4l2_chip_match_i2c_client(struct i2c_client *c, const struct v4l2_dbg_match *match) { int len; @@ -384,7 +384,7 @@ EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_addr); const unsigned short *v4l2_i2c_tuner_addrs(enum v4l2_i2c_tuner_type type) { static const unsigned short radio_addrs[] = { -#if defined(CONFIG_MEDIA_TUNER_TEA5761) || defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_TEA5761) 0x10, #endif 0x60, -- cgit v1.2.3 From 7b34be71db533f3e0cf93d53cf62d036cdb5418a Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sun, 20 Jan 2013 01:32:56 -0300 Subject: [media] use IS_ENABLED() macro This patch introduces the use of IS_ENABLED() macro. For example, replacing: #if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) with: #if IS_ENABLED(CONFIG_I2C) All changes made by this patch respect the same replacement pattern. Reported-by: Mauro Carvalho Chehab Signed-off-by: Peter Senna Tschudin Acked-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/bcm3510.h | 2 +- drivers/media/dvb-frontends/cx22700.h | 2 +- drivers/media/dvb-frontends/cx24110.h | 2 +- drivers/media/dvb-frontends/dib0070.h | 2 +- drivers/media/dvb-frontends/dib0090.h | 2 +- drivers/media/dvb-frontends/dib3000.h | 2 +- drivers/media/dvb-frontends/dib8000.h | 2 +- drivers/media/dvb-frontends/dib9000.h | 2 +- drivers/media/dvb-frontends/dvb-pll.h | 2 +- drivers/media/dvb-frontends/isl6405.h | 2 +- drivers/media/dvb-frontends/isl6421.h | 2 +- drivers/media/dvb-frontends/isl6423.h | 2 +- drivers/media/dvb-frontends/itd1000.h | 2 +- drivers/media/dvb-frontends/l64781.h | 2 +- drivers/media/dvb-frontends/lgdt330x.h | 2 +- drivers/media/dvb-frontends/mb86a16.h | 2 +- drivers/media/dvb-frontends/mt312.h | 2 +- drivers/media/dvb-frontends/mt352.h | 2 +- drivers/media/dvb-frontends/nxt200x.h | 2 +- drivers/media/dvb-frontends/nxt6000.h | 2 +- drivers/media/dvb-frontends/or51132.h | 2 +- drivers/media/dvb-frontends/or51211.h | 2 +- drivers/media/dvb-frontends/s5h1420.h | 2 +- drivers/media/dvb-frontends/sp8870.h | 2 +- drivers/media/dvb-frontends/sp887x.h | 2 +- drivers/media/dvb-frontends/stb0899_drv.h | 2 +- drivers/media/dvb-frontends/stb6100.h | 2 +- drivers/media/dvb-frontends/stv0297.h | 2 +- drivers/media/dvb-frontends/stv0299.h | 2 +- drivers/media/dvb-frontends/stv090x.h | 2 +- drivers/media/dvb-frontends/stv6110x.h | 2 +- drivers/media/dvb-frontends/tda1002x.h | 5 ++--- drivers/media/dvb-frontends/tda1004x.h | 2 +- drivers/media/dvb-frontends/tda10086.h | 2 +- drivers/media/dvb-frontends/tda665x.h | 2 +- drivers/media/dvb-frontends/tda8083.h | 2 +- drivers/media/dvb-frontends/tda8261.h | 2 +- drivers/media/dvb-frontends/tda826x.h | 2 +- drivers/media/dvb-frontends/tua6100.h | 2 +- drivers/media/dvb-frontends/ves1820.h | 2 +- drivers/media/dvb-frontends/ves1x93.h | 2 +- drivers/media/dvb-frontends/zl10353.h | 2 +- drivers/media/pci/cx88/cx88-dvb.c | 4 ++-- drivers/media/pci/cx88/cx88-vp3054-i2c.h | 2 +- drivers/media/tuners/mt2060.h | 2 +- drivers/media/tuners/mt2063.h | 2 +- drivers/media/tuners/mt20xx.h | 2 +- drivers/media/tuners/mt2131.h | 2 +- drivers/media/tuners/mt2266.h | 2 +- drivers/media/tuners/mxl5007t.h | 2 +- drivers/media/tuners/qt1010.h | 2 +- drivers/media/tuners/tda18271.h | 2 +- drivers/media/tuners/tda827x.h | 2 +- drivers/media/tuners/tda8290.h | 2 +- drivers/media/tuners/tda9887.h | 2 +- drivers/media/tuners/tea5761.h | 2 +- drivers/media/tuners/tea5767.h | 2 +- drivers/media/tuners/tuner-simple.h | 2 +- drivers/media/tuners/tuner-xc2028.h | 2 +- drivers/media/tuners/xc4000.h | 2 +- drivers/media/v4l2-core/v4l2-device.c | 2 +- 61 files changed, 63 insertions(+), 64 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/bcm3510.h b/drivers/media/dvb-frontends/bcm3510.h index f4575c0cc446..5bd56b1623bf 100644 --- a/drivers/media/dvb-frontends/bcm3510.h +++ b/drivers/media/dvb-frontends/bcm3510.h @@ -34,7 +34,7 @@ struct bcm3510_config int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); }; -#if defined(CONFIG_DVB_BCM3510) || (defined(CONFIG_DVB_BCM3510_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_BCM3510) extern struct dvb_frontend* bcm3510_attach(const struct bcm3510_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/cx22700.h b/drivers/media/dvb-frontends/cx22700.h index 4757a930ca05..382a7b1f3618 100644 --- a/drivers/media/dvb-frontends/cx22700.h +++ b/drivers/media/dvb-frontends/cx22700.h @@ -31,7 +31,7 @@ struct cx22700_config u8 demod_address; }; -#if defined(CONFIG_DVB_CX22700) || (defined(CONFIG_DVB_CX22700_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_CX22700) extern struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/cx24110.h b/drivers/media/dvb-frontends/cx24110.h index fdcceee91f3a..527aff1f2723 100644 --- a/drivers/media/dvb-frontends/cx24110.h +++ b/drivers/media/dvb-frontends/cx24110.h @@ -46,7 +46,7 @@ static inline int cx24110_pll_write(struct dvb_frontend *fe, u32 val) return 0; } -#if defined(CONFIG_DVB_CX24110) || (defined(CONFIG_DVB_CX24110_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_CX24110) extern struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/dib0070.h b/drivers/media/dvb-frontends/dib0070.h index 45c31fae3967..0c6befcc9143 100644 --- a/drivers/media/dvb-frontends/dib0070.h +++ b/drivers/media/dvb-frontends/dib0070.h @@ -48,7 +48,7 @@ struct dib0070_config { u8 vga_filter; }; -#if defined(CONFIG_DVB_TUNER_DIB0070) || (defined(CONFIG_DVB_TUNER_DIB0070_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TUNER_DIB0070) extern struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg); extern u16 dib0070_wbd_offset(struct dvb_frontend *); extern void dib0070_ctrl_agc_filter(struct dvb_frontend *, u8 open); diff --git a/drivers/media/dvb-frontends/dib0090.h b/drivers/media/dvb-frontends/dib0090.h index 781dc49de45b..6a090954fa10 100644 --- a/drivers/media/dvb-frontends/dib0090.h +++ b/drivers/media/dvb-frontends/dib0090.h @@ -75,7 +75,7 @@ struct dib0090_config { u8 force_crystal_mode; }; -#if defined(CONFIG_DVB_TUNER_DIB0090) || (defined(CONFIG_DVB_TUNER_DIB0090_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TUNER_DIB0090) extern struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config); extern struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config); extern void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast); diff --git a/drivers/media/dvb-frontends/dib3000.h b/drivers/media/dvb-frontends/dib3000.h index 404f63a6f26b..9b6c3bbc983a 100644 --- a/drivers/media/dvb-frontends/dib3000.h +++ b/drivers/media/dvb-frontends/dib3000.h @@ -41,7 +41,7 @@ struct dib_fe_xfer_ops int (*tuner_pass_ctrl)(struct dvb_frontend *fe, int onoff, u8 pll_ctrl); }; -#if defined(CONFIG_DVB_DIB3000MB) || (defined(CONFIG_DVB_DIB3000MB_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_DIB3000MB) extern struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config, struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops); #else diff --git a/drivers/media/dvb-frontends/dib8000.h b/drivers/media/dvb-frontends/dib8000.h index 39591bb172c1..9e7a2b170d55 100644 --- a/drivers/media/dvb-frontends/dib8000.h +++ b/drivers/media/dvb-frontends/dib8000.h @@ -37,7 +37,7 @@ struct dib8000_config { #define DEFAULT_DIB8000_I2C_ADDRESS 18 -#if defined(CONFIG_DVB_DIB8000) || (defined(CONFIG_DVB_DIB8000_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_DIB8000) extern struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg); extern struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); diff --git a/drivers/media/dvb-frontends/dib9000.h b/drivers/media/dvb-frontends/dib9000.h index de1cc91fd833..f3639f045ff0 100644 --- a/drivers/media/dvb-frontends/dib9000.h +++ b/drivers/media/dvb-frontends/dib9000.h @@ -27,7 +27,7 @@ struct dib9000_config { #define DEFAULT_DIB9000_I2C_ADDRESS 18 -#if defined(CONFIG_DVB_DIB9000) || (defined(CONFIG_DVB_DIB9000_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_DIB9000) extern struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, const struct dib9000_config *cfg); extern int dib9000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr); extern struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe); diff --git a/drivers/media/dvb-frontends/dvb-pll.h b/drivers/media/dvb-frontends/dvb-pll.h index 4de754f76ce9..f4b5a0601c3a 100644 --- a/drivers/media/dvb-frontends/dvb-pll.h +++ b/drivers/media/dvb-frontends/dvb-pll.h @@ -38,7 +38,7 @@ * @param pll_desc_id dvb_pll_desc to use. * @return Frontend pointer on success, NULL on failure */ -#if defined(CONFIG_DVB_PLL) || (defined(CONFIG_DVB_PLL_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_PLL) extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, diff --git a/drivers/media/dvb-frontends/isl6405.h b/drivers/media/dvb-frontends/isl6405.h index 1c793d37576b..8abb70c26fd9 100644 --- a/drivers/media/dvb-frontends/isl6405.h +++ b/drivers/media/dvb-frontends/isl6405.h @@ -55,7 +55,7 @@ #define ISL6405_ENT2 0x20 #define ISL6405_ISEL2 0x40 -#if defined(CONFIG_DVB_ISL6405) || (defined(CONFIG_DVB_ISL6405_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_ISL6405) /* override_set and override_clear control which system register bits (above) * to always set & clear */ diff --git a/drivers/media/dvb-frontends/isl6421.h b/drivers/media/dvb-frontends/isl6421.h index 47e4518a042d..e7ca7d12b50a 100644 --- a/drivers/media/dvb-frontends/isl6421.h +++ b/drivers/media/dvb-frontends/isl6421.h @@ -39,7 +39,7 @@ #define ISL6421_ISEL1 0x20 #define ISL6421_DCL 0x40 -#if defined(CONFIG_DVB_ISL6421) || (defined(CONFIG_DVB_ISL6421_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_ISL6421) /* override_set and override_clear control which system register bits (above) to always set & clear */ extern struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, u8 override_set, u8 override_clear); diff --git a/drivers/media/dvb-frontends/isl6423.h b/drivers/media/dvb-frontends/isl6423.h index e1a37fba01ca..80dfd9cc4f41 100644 --- a/drivers/media/dvb-frontends/isl6423.h +++ b/drivers/media/dvb-frontends/isl6423.h @@ -42,7 +42,7 @@ struct isl6423_config { u8 mod_extern; }; -#if defined(CONFIG_DVB_ISL6423) || (defined(CONFIG_DVB_ISL6423_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_ISL6423) extern struct dvb_frontend *isl6423_attach(struct dvb_frontend *fe, diff --git a/drivers/media/dvb-frontends/itd1000.h b/drivers/media/dvb-frontends/itd1000.h index 5e18df071b88..edae0902f4fd 100644 --- a/drivers/media/dvb-frontends/itd1000.h +++ b/drivers/media/dvb-frontends/itd1000.h @@ -29,7 +29,7 @@ struct itd1000_config { u8 i2c_address; }; -#if defined(CONFIG_DVB_TUNER_ITD1000) || (defined(CONFIG_DVB_TUNER_ITD1000_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TUNER_ITD1000) extern struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct itd1000_config *cfg); #else static inline struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct itd1000_config *cfg) diff --git a/drivers/media/dvb-frontends/l64781.h b/drivers/media/dvb-frontends/l64781.h index 1305a9e7fb0b..6813b08a774d 100644 --- a/drivers/media/dvb-frontends/l64781.h +++ b/drivers/media/dvb-frontends/l64781.h @@ -31,7 +31,7 @@ struct l64781_config u8 demod_address; }; -#if defined(CONFIG_DVB_L64781) || (defined(CONFIG_DVB_L64781_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_L64781) extern struct dvb_frontend* l64781_attach(const struct l64781_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/lgdt330x.h b/drivers/media/dvb-frontends/lgdt330x.h index 9012504f0f2d..ca0eab562e1e 100644 --- a/drivers/media/dvb-frontends/lgdt330x.h +++ b/drivers/media/dvb-frontends/lgdt330x.h @@ -52,7 +52,7 @@ struct lgdt330x_config int clock_polarity_flip; }; -#if defined(CONFIG_DVB_LGDT330X) || (defined(CONFIG_DVB_LGDT330X_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_LGDT330X) extern struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/mb86a16.h b/drivers/media/dvb-frontends/mb86a16.h index 6ea8c376394f..277ce061acf9 100644 --- a/drivers/media/dvb-frontends/mb86a16.h +++ b/drivers/media/dvb-frontends/mb86a16.h @@ -33,7 +33,7 @@ struct mb86a16_config { -#if defined(CONFIG_DVB_MB86A16) || (defined(CONFIG_DVB_MB86A16_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_MB86A16) extern struct dvb_frontend *mb86a16_attach(const struct mb86a16_config *config, struct i2c_adapter *i2c_adap); diff --git a/drivers/media/dvb-frontends/mt312.h b/drivers/media/dvb-frontends/mt312.h index 29e3bb5496b8..5706621ad79d 100644 --- a/drivers/media/dvb-frontends/mt312.h +++ b/drivers/media/dvb-frontends/mt312.h @@ -36,7 +36,7 @@ struct mt312_config { unsigned int voltage_inverted:1; }; -#if defined(CONFIG_DVB_MT312) || (defined(CONFIG_DVB_MT312_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_MT312) struct dvb_frontend *mt312_attach(const struct mt312_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/mt352.h b/drivers/media/dvb-frontends/mt352.h index ca2562d6f289..451d904e1500 100644 --- a/drivers/media/dvb-frontends/mt352.h +++ b/drivers/media/dvb-frontends/mt352.h @@ -51,7 +51,7 @@ struct mt352_config int (*demod_init)(struct dvb_frontend* fe); }; -#if defined(CONFIG_DVB_MT352) || (defined(CONFIG_DVB_MT352_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_MT352) extern struct dvb_frontend* mt352_attach(const struct mt352_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/nxt200x.h b/drivers/media/dvb-frontends/nxt200x.h index f3c84583770f..b518d545609e 100644 --- a/drivers/media/dvb-frontends/nxt200x.h +++ b/drivers/media/dvb-frontends/nxt200x.h @@ -42,7 +42,7 @@ struct nxt200x_config int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); }; -#if defined(CONFIG_DVB_NXT200X) || (defined(CONFIG_DVB_NXT200X_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_NXT200X) extern struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/nxt6000.h b/drivers/media/dvb-frontends/nxt6000.h index 878eb38a075e..b5867c2ae681 100644 --- a/drivers/media/dvb-frontends/nxt6000.h +++ b/drivers/media/dvb-frontends/nxt6000.h @@ -33,7 +33,7 @@ struct nxt6000_config u8 clock_inversion:1; }; -#if defined(CONFIG_DVB_NXT6000) || (defined(CONFIG_DVB_NXT6000_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_NXT6000) extern struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/or51132.h b/drivers/media/dvb-frontends/or51132.h index 1b8e04d973c8..938958386cb1 100644 --- a/drivers/media/dvb-frontends/or51132.h +++ b/drivers/media/dvb-frontends/or51132.h @@ -34,7 +34,7 @@ struct or51132_config int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); }; -#if defined(CONFIG_DVB_OR51132) || (defined(CONFIG_DVB_OR51132_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_OR51132) extern struct dvb_frontend* or51132_attach(const struct or51132_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/or51211.h b/drivers/media/dvb-frontends/or51211.h index 3ce0508b898e..9a8ae936b62d 100644 --- a/drivers/media/dvb-frontends/or51211.h +++ b/drivers/media/dvb-frontends/or51211.h @@ -37,7 +37,7 @@ struct or51211_config void (*sleep)(struct dvb_frontend * fe); }; -#if defined(CONFIG_DVB_OR51211) || (defined(CONFIG_DVB_OR51211_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_OR51211) extern struct dvb_frontend* or51211_attach(const struct or51211_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/s5h1420.h b/drivers/media/dvb-frontends/s5h1420.h index ff308136d865..210049b5cf30 100644 --- a/drivers/media/dvb-frontends/s5h1420.h +++ b/drivers/media/dvb-frontends/s5h1420.h @@ -40,7 +40,7 @@ struct s5h1420_config u8 serial_mpeg:1; }; -#if defined(CONFIG_DVB_S5H1420) || (defined(CONFIG_DVB_S5H1420_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_S5H1420) extern struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config, struct i2c_adapter *i2c); extern struct i2c_adapter *s5h1420_get_tuner_i2c_adapter(struct dvb_frontend *fe); diff --git a/drivers/media/dvb-frontends/sp8870.h b/drivers/media/dvb-frontends/sp8870.h index a764a793c7d8..065ec67d4e30 100644 --- a/drivers/media/dvb-frontends/sp8870.h +++ b/drivers/media/dvb-frontends/sp8870.h @@ -35,7 +35,7 @@ struct sp8870_config int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); }; -#if defined(CONFIG_DVB_SP8870) || (defined(CONFIG_DVB_SP8870_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_SP8870) extern struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/sp887x.h b/drivers/media/dvb-frontends/sp887x.h index 04eff6e0eef3..2cdc4e8bc9cd 100644 --- a/drivers/media/dvb-frontends/sp887x.h +++ b/drivers/media/dvb-frontends/sp887x.h @@ -17,7 +17,7 @@ struct sp887x_config int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); }; -#if defined(CONFIG_DVB_SP887X) || (defined(CONFIG_DVB_SP887X_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_SP887X) extern struct dvb_frontend* sp887x_attach(const struct sp887x_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/stb0899_drv.h b/drivers/media/dvb-frontends/stb0899_drv.h index 98b200ce0c34..8d26ff6eb1db 100644 --- a/drivers/media/dvb-frontends/stb0899_drv.h +++ b/drivers/media/dvb-frontends/stb0899_drv.h @@ -142,7 +142,7 @@ struct stb0899_config { int (*tuner_set_rfsiggain)(struct dvb_frontend *fe, u32 rf_gain); }; -#if defined(CONFIG_DVB_STB0899) || (defined(CONFIG_DVB_STB0899_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_STB0899) extern struct dvb_frontend *stb0899_attach(struct stb0899_config *config, struct i2c_adapter *i2c); diff --git a/drivers/media/dvb-frontends/stb6100.h b/drivers/media/dvb-frontends/stb6100.h index 2ab096614b3f..3a1e40f3b8be 100644 --- a/drivers/media/dvb-frontends/stb6100.h +++ b/drivers/media/dvb-frontends/stb6100.h @@ -94,7 +94,7 @@ struct stb6100_state { u32 reference; }; -#if defined(CONFIG_DVB_STB6100) || (defined(CONFIG_DVB_STB6100_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_STB6100) extern struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe, const struct stb6100_config *config, diff --git a/drivers/media/dvb-frontends/stv0297.h b/drivers/media/dvb-frontends/stv0297.h index 3f8f9468f387..c8ff3639ce00 100644 --- a/drivers/media/dvb-frontends/stv0297.h +++ b/drivers/media/dvb-frontends/stv0297.h @@ -42,7 +42,7 @@ struct stv0297_config u8 stop_during_read:1; }; -#if defined(CONFIG_DVB_STV0297) || (defined(CONFIG_DVB_STV0297_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_STV0297) extern struct dvb_frontend* stv0297_attach(const struct stv0297_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/stv0299.h b/drivers/media/dvb-frontends/stv0299.h index ba219b767a69..06f70fc8327b 100644 --- a/drivers/media/dvb-frontends/stv0299.h +++ b/drivers/media/dvb-frontends/stv0299.h @@ -95,7 +95,7 @@ struct stv0299_config int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); }; -#if defined(CONFIG_DVB_STV0299) || (defined(CONFIG_DVB_STV0299_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_STV0299) extern struct dvb_frontend *stv0299_attach(const struct stv0299_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/stv090x.h b/drivers/media/dvb-frontends/stv090x.h index 29cdc2b71314..0bd6adcfee8a 100644 --- a/drivers/media/dvb-frontends/stv090x.h +++ b/drivers/media/dvb-frontends/stv090x.h @@ -103,7 +103,7 @@ struct stv090x_config { void (*tuner_i2c_lock) (struct dvb_frontend *fe, int lock); }; -#if defined(CONFIG_DVB_STV090x) || (defined(CONFIG_DVB_STV090x_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_STV090x) extern struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, struct i2c_adapter *i2c, diff --git a/drivers/media/dvb-frontends/stv6110x.h b/drivers/media/dvb-frontends/stv6110x.h index 47516753929a..bc4766db29c5 100644 --- a/drivers/media/dvb-frontends/stv6110x.h +++ b/drivers/media/dvb-frontends/stv6110x.h @@ -53,7 +53,7 @@ struct stv6110x_devctl { }; -#if defined(CONFIG_DVB_STV6110x) || (defined(CONFIG_DVB_STV6110x_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_STV6110x) extern struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, const struct stv6110x_config *config, diff --git a/drivers/media/dvb-frontends/tda1002x.h b/drivers/media/dvb-frontends/tda1002x.h index 04d19418bf20..e404b6e44802 100644 --- a/drivers/media/dvb-frontends/tda1002x.h +++ b/drivers/media/dvb-frontends/tda1002x.h @@ -57,7 +57,7 @@ struct tda10023_config { u16 deltaf; }; -#if defined(CONFIG_DVB_TDA10021) || (defined(CONFIG_DVB_TDA10021_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TDA10021) extern struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, struct i2c_adapter* i2c, u8 pwm); #else @@ -69,8 +69,7 @@ static inline struct dvb_frontend* tda10021_attach(const struct tda1002x_config* } #endif // CONFIG_DVB_TDA10021 -#if defined(CONFIG_DVB_TDA10023) || \ - (defined(CONFIG_DVB_TDA10023_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TDA10023) extern struct dvb_frontend *tda10023_attach( const struct tda10023_config *config, struct i2c_adapter *i2c, u8 pwm); diff --git a/drivers/media/dvb-frontends/tda1004x.h b/drivers/media/dvb-frontends/tda1004x.h index 4e27ffb0f14e..dd283fbb61c0 100644 --- a/drivers/media/dvb-frontends/tda1004x.h +++ b/drivers/media/dvb-frontends/tda1004x.h @@ -117,7 +117,7 @@ struct tda1004x_state { enum tda1004x_demod demod_type; }; -#if defined(CONFIG_DVB_TDA1004X) || (defined(CONFIG_DVB_TDA1004X_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TDA1004X) extern struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, struct i2c_adapter* i2c); diff --git a/drivers/media/dvb-frontends/tda10086.h b/drivers/media/dvb-frontends/tda10086.h index 61148c558d8d..458fe91c1b88 100644 --- a/drivers/media/dvb-frontends/tda10086.h +++ b/drivers/media/dvb-frontends/tda10086.h @@ -46,7 +46,7 @@ struct tda10086_config enum tda10086_xtal xtal_freq; }; -#if defined(CONFIG_DVB_TDA10086) || (defined(CONFIG_DVB_TDA10086_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TDA10086) extern struct dvb_frontend* tda10086_attach(const struct tda10086_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/tda665x.h b/drivers/media/dvb-frontends/tda665x.h index ec7927aa75ae..03a0da6d5cf2 100644 --- a/drivers/media/dvb-frontends/tda665x.h +++ b/drivers/media/dvb-frontends/tda665x.h @@ -31,7 +31,7 @@ struct tda665x_config { u32 ref_divider; }; -#if defined(CONFIG_DVB_TDA665x) || (defined(CONFIG_DVB_TDA665x_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TDA665x) extern struct dvb_frontend *tda665x_attach(struct dvb_frontend *fe, const struct tda665x_config *config, diff --git a/drivers/media/dvb-frontends/tda8083.h b/drivers/media/dvb-frontends/tda8083.h index 5a03c14a10e8..de6b1860dfdd 100644 --- a/drivers/media/dvb-frontends/tda8083.h +++ b/drivers/media/dvb-frontends/tda8083.h @@ -35,7 +35,7 @@ struct tda8083_config u8 demod_address; }; -#if defined(CONFIG_DVB_TDA8083) || (defined(CONFIG_DVB_TDA8083_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TDA8083) extern struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/tda8261.h b/drivers/media/dvb-frontends/tda8261.h index 006e45351b94..55cf4ffcbfdf 100644 --- a/drivers/media/dvb-frontends/tda8261.h +++ b/drivers/media/dvb-frontends/tda8261.h @@ -34,7 +34,7 @@ struct tda8261_config { enum tda8261_step step_size; }; -#if defined(CONFIG_DVB_TDA8261) || (defined(CONFIG_DVB_TDA8261_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TDA8261) extern struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe, const struct tda8261_config *config, diff --git a/drivers/media/dvb-frontends/tda826x.h b/drivers/media/dvb-frontends/tda826x.h index 89e97926ab23..5f0f20e7e4f8 100644 --- a/drivers/media/dvb-frontends/tda826x.h +++ b/drivers/media/dvb-frontends/tda826x.h @@ -35,7 +35,7 @@ * @param has_loopthrough Set to 1 if the card has a loopthrough RF connector. * @return FE pointer on success, NULL on failure. */ -#if defined(CONFIG_DVB_TDA826X) || (defined(CONFIG_DVB_TDA826X_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TDA826X) extern struct dvb_frontend* tda826x_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c, int has_loopthrough); diff --git a/drivers/media/dvb-frontends/tua6100.h b/drivers/media/dvb-frontends/tua6100.h index f83dbd5e42ae..83a9c30e67ca 100644 --- a/drivers/media/dvb-frontends/tua6100.h +++ b/drivers/media/dvb-frontends/tua6100.h @@ -34,7 +34,7 @@ #include #include "dvb_frontend.h" -#if defined(CONFIG_DVB_TUA6100) || (defined(CONFIG_DVB_TUA6100_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TUA6100) extern struct dvb_frontend *tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c); #else static inline struct dvb_frontend* tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c) diff --git a/drivers/media/dvb-frontends/ves1820.h b/drivers/media/dvb-frontends/ves1820.h index e902ed634ec3..c073f353ac38 100644 --- a/drivers/media/dvb-frontends/ves1820.h +++ b/drivers/media/dvb-frontends/ves1820.h @@ -41,7 +41,7 @@ struct ves1820_config u8 selagc:1; }; -#if defined(CONFIG_DVB_VES1820) || (defined(CONFIG_DVB_VES1820_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_VES1820) extern struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, struct i2c_adapter* i2c, u8 pwm); #else diff --git a/drivers/media/dvb-frontends/ves1x93.h b/drivers/media/dvb-frontends/ves1x93.h index 8a5a49e808f6..2307caea6aec 100644 --- a/drivers/media/dvb-frontends/ves1x93.h +++ b/drivers/media/dvb-frontends/ves1x93.h @@ -40,7 +40,7 @@ struct ves1x93_config u8 invert_pwm:1; }; -#if defined(CONFIG_DVB_VES1X93) || (defined(CONFIG_DVB_VES1X93_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_VES1X93) extern struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/zl10353.h b/drivers/media/dvb-frontends/zl10353.h index 6e3ca9eed048..50c1004aef36 100644 --- a/drivers/media/dvb-frontends/zl10353.h +++ b/drivers/media/dvb-frontends/zl10353.h @@ -47,7 +47,7 @@ struct zl10353_config u8 pll_0; /* default: 0x15 */ }; -#if defined(CONFIG_DVB_ZL10353) || (defined(CONFIG_DVB_ZL10353_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_ZL10353) extern struct dvb_frontend* zl10353_attach(const struct zl10353_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c index 50b5ac5b3983..672b267a2d3e 100644 --- a/drivers/media/pci/cx88/cx88-dvb.c +++ b/drivers/media/pci/cx88/cx88-dvb.c @@ -265,7 +265,7 @@ static struct mb86a16_config twinhan_vp1027 = { .demod_address = 0x08, }; -#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_VIDEO_CX88_VP3054) static int dntv_live_dvbt_pro_demod_init(struct dvb_frontend* fe) { static const u8 clock_config [] = { 0x89, 0x38, 0x38 }; @@ -1127,7 +1127,7 @@ static int dvb_register(struct cx8802_dev *dev) } break; case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: -#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_VIDEO_CX88_VP3054) /* MT352 is on a secondary I2C bus made from some GPIO lines */ fe0->dvb.frontend = dvb_attach(mt352_attach, &dntv_live_dvbt_pro_config, &dev->vp3054->adap); diff --git a/drivers/media/pci/cx88/cx88-vp3054-i2c.h b/drivers/media/pci/cx88/cx88-vp3054-i2c.h index be99c931dc3e..95d0c60a35e1 100644 --- a/drivers/media/pci/cx88/cx88-vp3054-i2c.h +++ b/drivers/media/pci/cx88/cx88-vp3054-i2c.h @@ -30,7 +30,7 @@ struct vp3054_i2c_state { }; /* ----------------------------------------------------------------------- */ -#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_VIDEO_CX88_VP3054) int vp3054_i2c_probe(struct cx8802_dev *dev); void vp3054_i2c_remove(struct cx8802_dev *dev); #else diff --git a/drivers/media/tuners/mt2060.h b/drivers/media/tuners/mt2060.h index cb60caffb6b6..c64fc19cb278 100644 --- a/drivers/media/tuners/mt2060.h +++ b/drivers/media/tuners/mt2060.h @@ -30,7 +30,7 @@ struct mt2060_config { u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */ }; -#if defined(CONFIG_MEDIA_TUNER_MT2060) || (defined(CONFIG_MEDIA_TUNER_MT2060_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_MT2060) extern struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1); #else static inline struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1) diff --git a/drivers/media/tuners/mt2063.h b/drivers/media/tuners/mt2063.h index ab24170c1571..e1acfc8e7ae3 100644 --- a/drivers/media/tuners/mt2063.h +++ b/drivers/media/tuners/mt2063.h @@ -8,7 +8,7 @@ struct mt2063_config { u32 refclock; }; -#if defined(CONFIG_MEDIA_TUNER_MT2063) || (defined(CONFIG_MEDIA_TUNER_MT2063_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_MT2063) struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe, struct mt2063_config *config, struct i2c_adapter *i2c); diff --git a/drivers/media/tuners/mt20xx.h b/drivers/media/tuners/mt20xx.h index 259553a24903..f56241ccaa00 100644 --- a/drivers/media/tuners/mt20xx.h +++ b/drivers/media/tuners/mt20xx.h @@ -20,7 +20,7 @@ #include #include "dvb_frontend.h" -#if defined(CONFIG_MEDIA_TUNER_MT20XX) || (defined(CONFIG_MEDIA_TUNER_MT20XX_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_MT20XX) extern struct dvb_frontend *microtune_attach(struct dvb_frontend *fe, struct i2c_adapter* i2c_adap, u8 i2c_addr); diff --git a/drivers/media/tuners/mt2131.h b/drivers/media/tuners/mt2131.h index 6632de640df0..09ceaf68e47c 100644 --- a/drivers/media/tuners/mt2131.h +++ b/drivers/media/tuners/mt2131.h @@ -30,7 +30,7 @@ struct mt2131_config { u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */ }; -#if defined(CONFIG_MEDIA_TUNER_MT2131) || (defined(CONFIG_MEDIA_TUNER_MT2131_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_MT2131) extern struct dvb_frontend* mt2131_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2131_config *cfg, diff --git a/drivers/media/tuners/mt2266.h b/drivers/media/tuners/mt2266.h index 4d083882d044..fad6dd657d77 100644 --- a/drivers/media/tuners/mt2266.h +++ b/drivers/media/tuners/mt2266.h @@ -24,7 +24,7 @@ struct mt2266_config { u8 i2c_address; }; -#if defined(CONFIG_MEDIA_TUNER_MT2266) || (defined(CONFIG_MEDIA_TUNER_MT2266_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_MT2266) extern struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg); #else static inline struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg) diff --git a/drivers/media/tuners/mxl5007t.h b/drivers/media/tuners/mxl5007t.h index aa3eea0b5262..37b0942e2385 100644 --- a/drivers/media/tuners/mxl5007t.h +++ b/drivers/media/tuners/mxl5007t.h @@ -77,7 +77,7 @@ struct mxl5007t_config { unsigned int clk_out_enable:1; }; -#if defined(CONFIG_MEDIA_TUNER_MXL5007T) || (defined(CONFIG_MEDIA_TUNER_MXL5007T_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_MXL5007T) extern struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 addr, struct mxl5007t_config *cfg); diff --git a/drivers/media/tuners/qt1010.h b/drivers/media/tuners/qt1010.h index 807fb7b6146b..8ab5d479749f 100644 --- a/drivers/media/tuners/qt1010.h +++ b/drivers/media/tuners/qt1010.h @@ -36,7 +36,7 @@ struct qt1010_config { * @param cfg tuner hw based configuration * @return fe pointer on success, NULL on failure */ -#if defined(CONFIG_MEDIA_TUNER_QT1010) || (defined(CONFIG_MEDIA_TUNER_QT1010_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_QT1010) extern struct dvb_frontend *qt1010_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct qt1010_config *cfg); diff --git a/drivers/media/tuners/tda18271.h b/drivers/media/tuners/tda18271.h index 89b6c6d93fec..4c418d63f540 100644 --- a/drivers/media/tuners/tda18271.h +++ b/drivers/media/tuners/tda18271.h @@ -121,7 +121,7 @@ enum tda18271_mode { TDA18271_DIGITAL, }; -#if defined(CONFIG_MEDIA_TUNER_TDA18271) || (defined(CONFIG_MEDIA_TUNER_TDA18271_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_TDA18271) extern struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, struct i2c_adapter *i2c, struct tda18271_config *cfg); diff --git a/drivers/media/tuners/tda827x.h b/drivers/media/tuners/tda827x.h index 7d72ce0a0c2d..9432b5b6121b 100644 --- a/drivers/media/tuners/tda827x.h +++ b/drivers/media/tuners/tda827x.h @@ -50,7 +50,7 @@ struct tda827x_config * @param cfg optional callback function pointers. * @return FE pointer on success, NULL on failure. */ -#if defined(CONFIG_MEDIA_TUNER_TDA827X) || (defined(CONFIG_MEDIA_TUNER_TDA827X_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_TDA827X) extern struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c, struct tda827x_config *cfg); diff --git a/drivers/media/tuners/tda8290.h b/drivers/media/tuners/tda8290.h index 7e288b26fcc3..e12ecbaa35a4 100644 --- a/drivers/media/tuners/tda8290.h +++ b/drivers/media/tuners/tda8290.h @@ -28,7 +28,7 @@ struct tda829x_config { #define TDA829X_DONT_PROBE 1 }; -#if defined(CONFIG_MEDIA_TUNER_TDA8290) || (defined(CONFIG_MEDIA_TUNER_TDA8290_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_TDA8290) extern int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr); extern struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe, diff --git a/drivers/media/tuners/tda9887.h b/drivers/media/tuners/tda9887.h index acc419e8c4fc..37a4a1123e0c 100644 --- a/drivers/media/tuners/tda9887.h +++ b/drivers/media/tuners/tda9887.h @@ -21,7 +21,7 @@ #include "dvb_frontend.h" /* ------------------------------------------------------------------------ */ -#if defined(CONFIG_MEDIA_TUNER_TDA9887) || (defined(CONFIG_MEDIA_TUNER_TDA9887_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_TDA9887) extern struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c_adap, u8 i2c_addr); diff --git a/drivers/media/tuners/tea5761.h b/drivers/media/tuners/tea5761.h index 2e2ff82c95a4..933228ffb509 100644 --- a/drivers/media/tuners/tea5761.h +++ b/drivers/media/tuners/tea5761.h @@ -20,7 +20,7 @@ #include #include "dvb_frontend.h" -#if defined(CONFIG_MEDIA_TUNER_TEA5761) || (defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_TEA5761) extern int tea5761_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr); extern struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe, diff --git a/drivers/media/tuners/tea5767.h b/drivers/media/tuners/tea5767.h index d30ab1b483de..c39101199383 100644 --- a/drivers/media/tuners/tea5767.h +++ b/drivers/media/tuners/tea5767.h @@ -39,7 +39,7 @@ struct tea5767_ctrl { enum tea5767_xtal xtal_freq; }; -#if defined(CONFIG_MEDIA_TUNER_TEA5767) || (defined(CONFIG_MEDIA_TUNER_TEA5767_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_TEA5767) extern int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr); extern struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe, diff --git a/drivers/media/tuners/tuner-simple.h b/drivers/media/tuners/tuner-simple.h index 381fa5d35a9b..ffd12cfe650b 100644 --- a/drivers/media/tuners/tuner-simple.h +++ b/drivers/media/tuners/tuner-simple.h @@ -20,7 +20,7 @@ #include #include "dvb_frontend.h" -#if defined(CONFIG_MEDIA_TUNER_SIMPLE) || (defined(CONFIG_MEDIA_TUNER_SIMPLE_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_SIMPLE) extern struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c_adap, u8 i2c_addr, diff --git a/drivers/media/tuners/tuner-xc2028.h b/drivers/media/tuners/tuner-xc2028.h index 9ebfb2d0ff14..181d087faec4 100644 --- a/drivers/media/tuners/tuner-xc2028.h +++ b/drivers/media/tuners/tuner-xc2028.h @@ -56,7 +56,7 @@ struct xc2028_config { #define XC2028_RESET_CLK 1 #define XC2028_I2C_FLUSH 2 -#if defined(CONFIG_MEDIA_TUNER_XC2028) || (defined(CONFIG_MEDIA_TUNER_XC2028_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_XC2028) extern struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, struct xc2028_config *cfg); #else diff --git a/drivers/media/tuners/xc4000.h b/drivers/media/tuners/xc4000.h index e6a44d151cbd..97c23de5296c 100644 --- a/drivers/media/tuners/xc4000.h +++ b/drivers/media/tuners/xc4000.h @@ -50,7 +50,7 @@ struct xc4000_config { * it's passed back to a bridge during tuner_callback(). */ -#if defined(CONFIG_MEDIA_TUNER_XC4000) || (defined(CONFIG_MEDIA_TUNER_XC4000_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_XC4000) extern struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct xc4000_config *cfg); diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c index 98a7f5e9cb1b..8ed5da2170bf 100644 --- a/drivers/media/v4l2-core/v4l2-device.c +++ b/drivers/media/v4l2-core/v4l2-device.c @@ -112,7 +112,7 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev) /* Unregister subdevs */ list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) { v4l2_device_unregister_subdev(sd); -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_I2C) if (sd->flags & V4L2_SUBDEV_FL_IS_I2C) { struct i2c_client *client = v4l2_get_subdevdata(sd); -- cgit v1.2.3 From f23999eccb5f1b6ec858279670307b5b1abe887a Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 21 Jan 2013 06:09:07 -0300 Subject: [media] media: Convert to devm_ioremap_resource() Convert all uses of devm_request_and_ioremap() to the newly introduced devm_ioremap_resource() which provides more consistent error handling. devm_ioremap_resource() provides its own error messages so all explicit error messages can be removed from the failure code paths. Signed-off-by: Thierry Reding Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-core.c | 8 +++----- drivers/media/platform/mx2_emmaprp.c | 6 +++--- drivers/media/platform/s3c-camif/camif-core.c | 8 +++----- drivers/media/platform/s5p-fimc/fimc-core.c | 8 +++----- drivers/media/platform/s5p-fimc/fimc-lite.c | 8 +++----- drivers/media/platform/s5p-fimc/mipi-csis.c | 8 +++----- drivers/media/platform/s5p-g2d/g2d.c | 8 +++----- drivers/media/platform/s5p-jpeg/jpeg-core.c | 8 +++----- drivers/media/platform/s5p-mfc/s5p_mfc.c | 8 +++----- drivers/media/platform/soc_camera/mx2_camera.c | 12 ++++++------ 10 files changed, 33 insertions(+), 49 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index 99b841d2f8fc..82d9f6ac12f3 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -1099,11 +1099,9 @@ static int gsc_probe(struct platform_device *pdev) gsc->clock = ERR_PTR(-EINVAL); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - gsc->regs = devm_request_and_ioremap(dev, res); - if (!gsc->regs) { - dev_err(dev, "failed to map registers\n"); - return -ENOENT; - } + gsc->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(gsc->regs)) + return PTR_ERR(gsc->regs); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index 6b155d7be8e0..4b9e0a28616a 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -941,9 +941,9 @@ static int emmaprp_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pcdev); - pcdev->base_emma = devm_request_and_ioremap(&pdev->dev, res_emma); - if (!pcdev->base_emma) { - ret = -ENXIO; + pcdev->base_emma = devm_ioremap_resource(&pdev->dev, res_emma); + if (IS_ERR(pcdev->base_emma)) { + ret = PTR_ERR(pcdev->base_emma); goto rel_vdev; } diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c index 8f0ca02c29a9..0d0fab1a7b5e 100644 --- a/drivers/media/platform/s3c-camif/camif-core.c +++ b/drivers/media/platform/s3c-camif/camif-core.c @@ -434,11 +434,9 @@ static int s3c_camif_probe(struct platform_device *pdev) mres = platform_get_resource(pdev, IORESOURCE_MEM, 0); - camif->io_base = devm_request_and_ioremap(dev, mres); - if (!camif->io_base) { - dev_err(dev, "failed to obtain I/O memory\n"); - return -ENOENT; - } + camif->io_base = devm_ioremap_resource(dev, mres); + if (IS_ERR(camif->io_base)) + return PTR_ERR(camif->io_base); ret = camif_request_irqs(pdev, camif); if (ret < 0) diff --git a/drivers/media/platform/s5p-fimc/fimc-core.c b/drivers/media/platform/s5p-fimc/fimc-core.c index 29f7bb71c7e1..e3916bde45cf 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.c +++ b/drivers/media/platform/s5p-fimc/fimc-core.c @@ -893,11 +893,9 @@ static int fimc_probe(struct platform_device *pdev) mutex_init(&fimc->lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - fimc->regs = devm_request_and_ioremap(&pdev->dev, res); - if (fimc->regs == NULL) { - dev_err(&pdev->dev, "Failed to obtain io memory\n"); - return -ENOENT; - } + fimc->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fimc->regs)) + return PTR_ERR(fimc->regs); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/s5p-fimc/fimc-lite.c index e18babfa89e0..bfc4206935c8 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite.c @@ -1500,11 +1500,9 @@ static int fimc_lite_probe(struct platform_device *pdev) mutex_init(&fimc->lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - fimc->regs = devm_request_and_ioremap(&pdev->dev, res); - if (fimc->regs == NULL) { - dev_err(&pdev->dev, "Failed to obtain io memory\n"); - return -ENOENT; - } + fimc->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fimc->regs)) + return PTR_ERR(fimc->regs); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index 613482fd74a9..1cc6501c213f 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -733,11 +733,9 @@ static int s5pcsis_probe(struct platform_device *pdev) } mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - state->regs = devm_request_and_ioremap(&pdev->dev, mem_res); - if (state->regs == NULL) { - dev_err(&pdev->dev, "Failed to request and remap io memory\n"); - return -ENXIO; - } + state->regs = devm_ioremap_resource(&pdev->dev, mem_res); + if (IS_ERR(state->regs)) + return PTR_ERR(state->regs); state->irq = platform_get_irq(pdev, 0); if (state->irq < 0) { diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 7e415297e174..aaaf276a5a6c 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -713,11 +713,9 @@ static int g2d_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dev->regs = devm_request_and_ioremap(&pdev->dev, res); - if (dev->regs == NULL) { - dev_err(&pdev->dev, "Failed to obtain io memory\n"); - return -ENOENT; - } + dev->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dev->regs)) + return PTR_ERR(dev->regs); dev->clk = clk_get(&pdev->dev, "sclk_fimg2d"); if (IS_ERR(dev->clk)) { diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 17983c4c9a9a..3b023752bcb4 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1325,11 +1325,9 @@ static int s5p_jpeg_probe(struct platform_device *pdev) /* memory-mapped registers */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - jpeg->regs = devm_request_and_ioremap(&pdev->dev, res); - if (jpeg->regs == NULL) { - dev_err(&pdev->dev, "Failed to obtain io memory\n"); - return -ENOENT; - } + jpeg->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(jpeg->regs)) + return PTR_ERR(jpeg->regs); /* interrupt service routine registration */ jpeg->irq = ret = platform_get_irq(pdev, 0); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 3669e3b933ca..e84703c314ce 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1087,11 +1087,9 @@ static int s5p_mfc_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dev->regs_base = devm_request_and_ioremap(&pdev->dev, res); - if (dev->regs_base == NULL) { - dev_err(&pdev->dev, "Failed to obtain io memory\n"); - return -ENOENT; - } + dev->regs_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dev->regs_base)) + return PTR_ERR(dev->regs_base); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 27b2e96f27a6..ea56b7589211 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -1444,9 +1444,9 @@ static int mx27_camera_emma_init(struct platform_device *pdev) goto out; } - pcdev->base_emma = devm_request_and_ioremap(pcdev->dev, res_emma); - if (!pcdev->base_emma) { - err = -EADDRNOTAVAIL; + pcdev->base_emma = devm_ioremap_resource(pcdev->dev, res_emma); + if (IS_ERR(pcdev->base_emma)) { + err = PTR_ERR(pcdev->base_emma); goto out; } @@ -1547,9 +1547,9 @@ static int mx2_camera_probe(struct platform_device *pdev) INIT_LIST_HEAD(&pcdev->discard); spin_lock_init(&pcdev->lock); - pcdev->base_csi = devm_request_and_ioremap(&pdev->dev, res_csi); - if (!pcdev->base_csi) { - err = -EADDRNOTAVAIL; + pcdev->base_csi = devm_ioremap_resource(&pdev->dev, res_csi); + if (IS_ERR(pcdev->base_csi)) { + err = PTR_ERR(pcdev->base_csi); goto exit; } -- cgit v1.2.3 From 05c79d0d1d0d44c2195d63d1e7c62e358eadeadf Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 21 Jan 2013 06:51:41 -0300 Subject: [media] uvcvideo: Implement videobuf2 .wait_prepare and .wait_finish operations Those optional operations are used to release and reacquire the queue lock when videobuf2 needs to perform operations that sleep for a long time, such as waiting for a buffer to be complete. Implement them to avoid blocking qbuf or streamoff calls when a dqbuf is in progress. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_queue.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c index 778addc5caff..6c233a54ce40 100644 --- a/drivers/media/usb/uvc/uvc_queue.c +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -115,11 +115,27 @@ static int uvc_buffer_finish(struct vb2_buffer *vb) return 0; } +static void uvc_wait_prepare(struct vb2_queue *vq) +{ + struct uvc_video_queue *queue = vb2_get_drv_priv(vq); + + mutex_unlock(&queue->mutex); +} + +static void uvc_wait_finish(struct vb2_queue *vq) +{ + struct uvc_video_queue *queue = vb2_get_drv_priv(vq); + + mutex_lock(&queue->mutex); +} + static struct vb2_ops uvc_queue_qops = { .queue_setup = uvc_queue_setup, .buf_prepare = uvc_buffer_prepare, .buf_queue = uvc_buffer_queue, .buf_finish = uvc_buffer_finish, + .wait_prepare = uvc_wait_prepare, + .wait_finish = uvc_wait_finish, }; int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, -- cgit v1.2.3 From 8cd5d42ac6d75143ab4bf44a15f76245a7f6c884 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 18 Jan 2013 13:52:38 -0300 Subject: [media] s5p-csis: Fix clock handling on error path in probe() Move e_clkput label after the clk_disable() call so a not acquired clock is not attempted to be disabled. This fixes runtime warnings like: s5p-mipi-csis 11880000.csis: failed to get clock: sclk_csis ------------[ cut here ]------------ WARNING: at drivers/clk/clk.c:478 clk_disable+0x24/0x34() Modules linked in: [] (unwind_backtrace+0x0/0x13c) from [] (warn_slowpath_common+0x54/0x64) [] (warn_slowpath_common+0x54/0x64) from [] (warn_slowpath_null+0x1c/0x24) [] (warn_slowpath_null+0x1c/0x24) from [] (clk_disable+0x24/0x34) [] (clk_disable+0x24/0x34) from [] (s5pcsis_probe+0x25c/0x4c8) [] (s5pcsis_probe+0x25c/0x4c8) from [] (platform_drv_probe+0x18/0x1c) [] (platform_drv_probe+0x18/0x1c) from [] (driver_probe_device+0xa4/0x368) [] (driver_probe_device+0xa4/0x368) from [] (__driver_attach+0x8c/0x90) [] (__driver_attach+0x8c/0x90) from [] (bus_for_each_dev+0x60/0x8c) [] (bus_for_each_dev+0x60/0x8c) from [] (bus_add_driver+0x20c/0x2d4) [] (bus_add_driver+0x20c/0x2d4) from [] (driver_register+0x78/0x194) [] (driver_register+0x78/0x194) from [] (do_one_initcall+0x34/0x188) [] (do_one_initcall+0x34/0x188) from [] (kernel_init+0x180/0x2f0) [] (kernel_init+0x180/0x2f0) from [] (ret_from_fork+0x14/0x3c) ---[ end trace 0c5a55345c42530b ]--- Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/mipi-csis.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index 1cc6501c213f..981863d05aaa 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -771,7 +771,7 @@ static int s5pcsis_probe(struct platform_device *pdev) 0, dev_name(&pdev->dev), state); if (ret) { dev_err(&pdev->dev, "Interrupt request failed\n"); - goto e_clkput; + goto e_clkdis; } v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops); @@ -789,7 +789,7 @@ static int s5pcsis_probe(struct platform_device *pdev) ret = media_entity_init(&state->sd.entity, CSIS_PADS_NUM, state->pads, 0); if (ret < 0) - goto e_clkput; + goto e_clkdis; /* This allows to retrieve the platform device id by the host driver */ v4l2_set_subdevdata(&state->sd, pdev); @@ -802,8 +802,9 @@ static int s5pcsis_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); return 0; -e_clkput: +e_clkdis: clk_disable(state->clock[CSIS_CLK_MUX]); +e_clkput: s5pcsis_clk_put(state); return ret; } -- cgit v1.2.3 From eb36209297b7aadbd3144c74cb79d35704c3086e Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Fri, 11 Jan 2013 11:29:34 -0300 Subject: [media] s5p-mfc: end-of-stream handling in encoder bug fix In some circumstances after issuing the V4L2_ENC_CMD_STOP the application could freeze. This patch prevents this behavior. Signed-off-by: Kamil Debski Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc_enc.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index f92f6ddd739f..2356fd52a169 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -1534,6 +1534,8 @@ int vidioc_encoder_cmd(struct file *file, void *priv, if (list_empty(&ctx->src_queue)) { mfc_debug(2, "EOS: empty src queue, entering finishing state"); ctx->state = MFCINST_FINISHING; + if (s5p_mfc_ctx_ready(ctx)) + set_work_bit_irqsave(ctx); spin_unlock_irqrestore(&dev->irqlock, flags); s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); } else { -- cgit v1.2.3 From 248ac368ce4b3cd36515122d888403909d7a2500 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 11 Jan 2013 07:06:28 -0300 Subject: [media] s5p-fimc: Fix fimc-lite entities deregistration Clear the proper array when deregistering FIMC-LITE devices. Now fimc[] array is erroneously accessed instead of fimc_lite[] and fimc_md_unregister_entities() function call can result in an oops from NULL pointer dereference, since fmd->fimc[] is cleared earlier. This might happen in normal conditions when the driver's probing is deferred and then retried. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-mdevice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index f49f6f17a3f7..a17fcb2d5d41 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -472,7 +472,7 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) if (fmd->fimc_lite[i] == NULL) continue; v4l2_device_unregister_subdev(&fmd->fimc_lite[i]->subdev); - fmd->fimc[i]->pipeline_ops = NULL; + fmd->fimc_lite[i]->pipeline_ops = NULL; fmd->fimc_lite[i] = NULL; } for (i = 0; i < CSIS_MAX_ENTITIES; i++) { -- cgit v1.2.3 From 5815d0c4d55a80310138698c9dc516c759b6be87 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Tue, 22 Jan 2013 15:51:55 -0300 Subject: [media] v4l2-core: do not enable the buffer ioctls for radio devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The buffer ioctls (VIDIOC_REQBUFS, VIDIOC_QUERYBUF, VIDIOC_QBUF, VIDIOC_DQBUF, VIDIOC_EXPBUF, VIDIOC_CREATE_BUFS, VIDIOC_PREPARE_BUF) are not applicable for radio devices. Hence, they should be set valid only for non-radio devices in determine_valid_ioctls(). Signed-off-by: Frank Schäfer Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 98dcad9c8a3b..51b3a7713dcd 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -568,11 +568,6 @@ static void determine_valid_ioctls(struct video_device *vdev) if (ops->vidioc_s_priority || test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags)) set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls); - SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs); - SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf); - SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf); - SET_VALID_IOCTL(ops, VIDIOC_EXPBUF, vidioc_expbuf); - SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf); SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon); SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff); /* Note: the control handler can also be passed through the filehandle, @@ -605,8 +600,6 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_DQEVENT, vidioc_subscribe_event); SET_VALID_IOCTL(ops, VIDIOC_SUBSCRIBE_EVENT, vidioc_subscribe_event); SET_VALID_IOCTL(ops, VIDIOC_UNSUBSCRIBE_EVENT, vidioc_unsubscribe_event); - SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs); - SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf); if (ops->vidioc_enum_freq_bands || ops->vidioc_g_tuner || ops->vidioc_g_modulator) set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls); @@ -672,6 +665,13 @@ static void determine_valid_ioctls(struct video_device *vdev) } if (!is_radio) { /* ioctls valid for video or vbi */ + SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs); + SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf); + SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf); + SET_VALID_IOCTL(ops, VIDIOC_EXPBUF, vidioc_expbuf); + SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf); + SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs); + SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf); if (ops->vidioc_s_std) set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls); if (ops->vidioc_g_std || vdev->current_norm) -- cgit v1.2.3 From 8dc97ea20c2bdf406348640abbd35eb89c843957 Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Tue, 5 Feb 2013 14:34:37 -0300 Subject: [media] sta2x11_vip: convert to videobuf2, control framework, file handler This patch re-write the driver and use the videobuf2 interface instead of the old videobuf. Moreover, it uses also the control framework which allows the driver to inherit controls from its subdevice (ADV7180). Finally the driver does not implement custom file operation but it uses the generic ones from videobuf2 and v4l2_fh Signed-off-by: Federico Vaga Acked-by: Giancarlo Asnaghi Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/sta2x11/Kconfig | 2 +- drivers/media/pci/sta2x11/sta2x11_vip.c | 1073 +++++++++++++------------------ 2 files changed, 434 insertions(+), 641 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/sta2x11/Kconfig b/drivers/media/pci/sta2x11/Kconfig index 6749f67cab8a..a94ccad02066 100644 --- a/drivers/media/pci/sta2x11/Kconfig +++ b/drivers/media/pci/sta2x11/Kconfig @@ -2,7 +2,7 @@ config STA2X11_VIP tristate "STA2X11 VIP Video For Linux" depends on STA2X11 select VIDEO_ADV7180 if MEDIA_SUBDRV_AUTOSELECT - select VIDEOBUF_DMA_CONTIG + select VIDEOBUF2_DMA_CONTIG depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS help Say Y for support for STA2X11 VIP (Video Input Port) capture diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index 1352e50e457b..4b703fe8c953 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -1,7 +1,11 @@ /* * This is the driver for the STA2x11 Video Input Port. * + * Copyright (C) 2012 ST Microelectronics + * author: Federico Vaga * Copyright (C) 2010 WindRiver Systems, Inc. + * authors: Andreas Kies + * Vlad Lungu * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -19,36 +23,30 @@ * The full GNU General Public License is included in this distribution in * the file called "COPYING". * - * Author: Andreas Kies - * Vlad Lungu - * */ #include #include #include #include -#include - #include - #include - #include #include -#include #include #include #include #include #include #include +#include #include -#include +#include +#include +#include #include "sta2x11_vip.h" -#define DRV_NAME "sta2x11_vip" #define DRV_VERSION "1.3" #ifndef PCI_DEVICE_ID_STMICRO_VIP @@ -63,8 +61,8 @@ #define DVP_TFS 0x08 #define DVP_BFO 0x0C #define DVP_BFS 0x10 -#define DVP_VTP 0x14 -#define DVP_VBP 0x18 +#define DVP_VTP 0x14 +#define DVP_VBP 0x18 #define DVP_VMP 0x1C #define DVP_ITM 0x98 #define DVP_ITS 0x9C @@ -84,13 +82,21 @@ #define DVP_HLFLN_SD 0x00000001 -#define REG_WRITE(vip, reg, value) iowrite32((value), (vip->iomem)+(reg)) -#define REG_READ(vip, reg) ioread32((vip->iomem)+(reg)) - #define SAVE_COUNT 8 #define AUX_COUNT 3 #define IRQ_COUNT 1 + +struct vip_buffer { + struct vb2_buffer vb; + struct list_head list; + dma_addr_t dma; +}; +static inline struct vip_buffer *to_vip_buffer(struct vb2_buffer *vb2) +{ + return container_of(vb2, struct vip_buffer, vb); +} + /** * struct sta2x11_vip - All internal data for one instance of device * @v4l2_dev: device registered in v4l layer @@ -99,29 +105,26 @@ * @adapter: contains I2C adapter information * @register_save_area: All relevant register are saved here during suspend * @decoder: contains information about video DAC + * @ctrl_hdl: handler for control framework * @format: pixel format, fixed UYVY * @std: video standard (e.g. PAL/NTSC) * @input: input line for video signal ( 0 or 1 ) - * @users: Number of open of device ( max. 1 ) * @disabled: Device is in power down state - * @mutex: ensures exclusive opening of device * @slock: for excluse acces of registers - * @vb_vidq: queue maintained by videobuf layer - * @capture: linked list of capture buffer - * @active: struct videobuf_buffer currently beingg filled - * @started: device is ready to capture frame - * @closing: device will be shut down + * @alloc_ctx: context for videobuf2 + * @vb_vidq: queue maintained by videobuf2 layer + * @buffer_list: list of buffer in use + * @sequence: sequence number of acquired buffer + * @active: current active buffer + * @lock: used in videobuf2 callback * @tcount: Number of top frames * @bcount: Number of bottom frames * @overflow: Number of FIFO overflows - * @mem_spare: small buffer of unused frame - * @dma_spare: dma addres of mem_spare * @iomem: hardware base address * @config: I2C and gpio config from platform * * All non-local data is accessed via this structure. */ - struct sta2x11_vip { struct v4l2_device v4l2_dev; struct video_device *video_dev; @@ -129,21 +132,27 @@ struct sta2x11_vip { struct i2c_adapter *adapter; unsigned int register_save_area[IRQ_COUNT + SAVE_COUNT + AUX_COUNT]; struct v4l2_subdev *decoder; + struct v4l2_ctrl_handler ctrl_hdl; + + struct v4l2_pix_format format; v4l2_std_id std; unsigned int input; - int users; int disabled; - struct mutex mutex; /* exclusive access during open */ - spinlock_t slock; /* spin lock for hardware and queue access */ - struct videobuf_queue vb_vidq; - struct list_head capture; - struct videobuf_buffer *active; - int started, closing, tcount, bcount; + spinlock_t slock; + + struct vb2_alloc_ctx *alloc_ctx; + struct vb2_queue vb_vidq; + struct list_head buffer_list; + unsigned int sequence; + struct vip_buffer *active; /* current active buffer */ + spinlock_t lock; /* Used in videobuf2 callback */ + + /* Interrupt counters */ + int tcount, bcount; int overflow; - void *mem_spare; - dma_addr_t dma_spare; - void *iomem; + + void *iomem; /* I/O Memory */ struct vip_config *config; }; @@ -206,318 +215,195 @@ static struct v4l2_pix_format formats_60[] = { .colorspace = V4L2_COLORSPACE_SMPTE170M}, }; -/** - * buf_setup - Get size and number of video buffer - * @vq: queue in videobuf - * @count: Number of buffers (1..MAX_FRAMES). - * 0 use default value. - * @size: size of buffer in bytes - * - * returns size and number of buffers - * a preset value of 0 returns the default number. - * return value: 0, always succesfull. - */ -static int buf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) +/* Write VIP register */ +static inline void reg_write(struct sta2x11_vip *vip, unsigned int reg, u32 val) { - struct sta2x11_vip *vip = vq->priv_data; - - *size = vip->format.width * vip->format.height * 2; - if (0 == *count || MAX_FRAMES < *count) - *count = MAX_FRAMES; - return 0; -}; - -/** - * buf_prepare - prepare buffer for usage - * @vq: queue in videobuf layer - * @vb: buffer to be prepared - * @field: type of video data (interlaced/non-interlaced) - * - * Allocate or realloc buffer - * return value: 0, successful. - * - * -EINVAL, supplied buffer is too small. - * - * other, buffer could not be locked. - */ -static int buf_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) + iowrite32((val), (vip->iomem)+(reg)); +} +/* Read VIP register */ +static inline u32 reg_read(struct sta2x11_vip *vip, unsigned int reg) { - struct sta2x11_vip *vip = vq->priv_data; - int ret; - - vb->size = vip->format.width * vip->format.height * 2; - if ((0 != vb->baddr) && (vb->bsize < vb->size)) - return -EINVAL; - vb->width = vip->format.width; - vb->height = vip->format.height; - vb->field = field; - - if (VIDEOBUF_NEEDS_INIT == vb->state) { - ret = videobuf_iolock(vq, vb, NULL); - if (ret) - goto fail; - } - vb->state = VIDEOBUF_PREPARED; - return 0; -fail: - videobuf_dma_contig_free(vq, vb); - vb->state = VIDEOBUF_NEEDS_INIT; - return ret; + return ioread32((vip->iomem)+(reg)); } - -/** - * buf_queu - queue buffer for filling - * @vq: queue in videobuf layer - * @vb: buffer to be queued - * - * if capturing is already running, the buffer will be queued. Otherwise - * capture is started and the buffer is used directly. - */ -static void buf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +/* Start DMA acquisition */ +static void start_dma(struct sta2x11_vip *vip, struct vip_buffer *vip_buf) { - struct sta2x11_vip *vip = vq->priv_data; - u32 dma; + unsigned long offset = 0; + + if (vip->format.field == V4L2_FIELD_INTERLACED) + offset = vip->format.width * 2; - vb->state = VIDEOBUF_QUEUED; + spin_lock_irq(&vip->slock); + /* Enable acquisition */ + reg_write(vip, DVP_CTL, reg_read(vip, DVP_CTL) | DVP_CTL_ENA); + /* Set Top and Bottom Field memory address */ + reg_write(vip, DVP_VTP, (u32)vip_buf->dma); + reg_write(vip, DVP_VBP, (u32)vip_buf->dma + offset); + spin_unlock_irq(&vip->slock); +} - if (vip->active) { - list_add_tail(&vb->queue, &vip->capture); +/* Fetch the next buffer to activate */ +static void vip_active_buf_next(struct sta2x11_vip *vip) +{ + /* Get the next buffer */ + spin_lock(&vip->lock); + if (list_empty(&vip->buffer_list)) {/* No available buffer */ + spin_unlock(&vip->lock); return; } - - vip->started = 1; + vip->active = list_first_entry(&vip->buffer_list, + struct vip_buffer, + list); + /* Reset Top and Bottom counter */ vip->tcount = 0; vip->bcount = 0; - vip->active = vb; - vb->state = VIDEOBUF_ACTIVE; + spin_unlock(&vip->lock); + if (vb2_is_streaming(&vip->vb_vidq)) { /* streaming is on */ + start_dma(vip, vip->active); /* start dma capture */ + } +} - dma = videobuf_to_dma_contig(vb); - REG_WRITE(vip, DVP_TFO, (0 << 16) | (0)); - /* despite of interlace mode, upper and lower frames start at zero */ - REG_WRITE(vip, DVP_BFO, (0 << 16) | (0)); +/* Videobuf2 Operations */ +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct sta2x11_vip *vip = vb2_get_drv_priv(vq); - switch (vip->format.field) { - case V4L2_FIELD_INTERLACED: - REG_WRITE(vip, DVP_TFS, - ((vip->format.height / 2 - 1) << 16) | - (2 * vip->format.width - 1)); - REG_WRITE(vip, DVP_BFS, ((vip->format.height / 2 - 1) << 16) | - (2 * vip->format.width - 1)); - REG_WRITE(vip, DVP_VTP, dma); - REG_WRITE(vip, DVP_VBP, dma + vip->format.width * 2); - REG_WRITE(vip, DVP_VMP, 4 * vip->format.width); - break; - case V4L2_FIELD_TOP: - REG_WRITE(vip, DVP_TFS, - ((vip->format.height - 1) << 16) | - (2 * vip->format.width - 1)); - REG_WRITE(vip, DVP_BFS, ((0) << 16) | - (2 * vip->format.width - 1)); - REG_WRITE(vip, DVP_VTP, dma); - REG_WRITE(vip, DVP_VBP, dma); - REG_WRITE(vip, DVP_VMP, 2 * vip->format.width); - break; - case V4L2_FIELD_BOTTOM: - REG_WRITE(vip, DVP_TFS, ((0) << 16) | - (2 * vip->format.width - 1)); - REG_WRITE(vip, DVP_BFS, - ((vip->format.height) << 16) | - (2 * vip->format.width - 1)); - REG_WRITE(vip, DVP_VTP, dma); - REG_WRITE(vip, DVP_VBP, dma); - REG_WRITE(vip, DVP_VMP, 2 * vip->format.width); - break; + if (!(*nbuffers) || *nbuffers < MAX_FRAMES) + *nbuffers = MAX_FRAMES; - default: - pr_warning("VIP: unknown field format\n"); - return; - } + *nplanes = 1; + sizes[0] = vip->format.sizeimage; + alloc_ctxs[0] = vip->alloc_ctx; - REG_WRITE(vip, DVP_CTL, DVP_CTL_ENA); -} + vip->sequence = 0; + vip->active = NULL; + vip->tcount = 0; + vip->bcount = 0; -/** - * buff_release - release buffer - * @vq: queue in videobuf layer - * @vb: buffer to be released - * - * release buffer in videobuf layer - */ -static void buf_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) + return 0; +}; +static int buffer_init(struct vb2_buffer *vb) { + struct vip_buffer *vip_buf = to_vip_buffer(vb); - videobuf_dma_contig_free(vq, vb); - vb->state = VIDEOBUF_NEEDS_INIT; + vip_buf->dma = vb2_dma_contig_plane_dma_addr(vb, 0); + INIT_LIST_HEAD(&vip_buf->list); + return 0; } -static struct videobuf_queue_ops vip_qops = { - .buf_setup = buf_setup, - .buf_prepare = buf_prepare, - .buf_queue = buf_queue, - .buf_release = buf_release, -}; - -/** - * vip_open - open video device - * @file: descriptor of device - * - * open device, make sure it is only opened once. - * return value: 0, no error. - * - * -EBUSY, device is already opened - * - * -ENOMEM, no memory for auxiliary DMA buffer - */ -static int vip_open(struct file *file) +static int buffer_prepare(struct vb2_buffer *vb) { - struct video_device *dev = video_devdata(file); - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue); + struct vip_buffer *vip_buf = to_vip_buffer(vb); + unsigned long size; + + size = vip->format.sizeimage; + if (vb2_plane_size(vb, 0) < size) { + v4l2_err(&vip->v4l2_dev, "buffer too small (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } - mutex_lock(&vip->mutex); - vip->users++; + vb2_set_plane_payload(&vip_buf->vb, 0, size); - if (vip->users > 1) { - vip->users--; - mutex_unlock(&vip->mutex); - return -EBUSY; + return 0; +} +static void buffer_queue(struct vb2_buffer *vb) +{ + struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue); + struct vip_buffer *vip_buf = to_vip_buffer(vb); + + spin_lock(&vip->lock); + list_add_tail(&vip_buf->list, &vip->buffer_list); + if (!vip->active) { /* No active buffer, active the first one */ + vip->active = list_first_entry(&vip->buffer_list, + struct vip_buffer, + list); + if (vb2_is_streaming(&vip->vb_vidq)) /* streaming is on */ + start_dma(vip, vip_buf); /* start dma capture */ } + spin_unlock(&vip->lock); +} +static int buffer_finish(struct vb2_buffer *vb) +{ + struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue); + struct vip_buffer *vip_buf = to_vip_buffer(vb); - file->private_data = dev; - vip->overflow = 0; - vip->started = 0; - vip->closing = 0; - vip->active = NULL; + /* Buffer handled, remove it from the list */ + spin_lock(&vip->lock); + list_del_init(&vip_buf->list); + spin_unlock(&vip->lock); - INIT_LIST_HEAD(&vip->capture); - vip->mem_spare = dma_alloc_coherent(&vip->pdev->dev, 64, - &vip->dma_spare, GFP_KERNEL); - if (!vip->mem_spare) { - vip->users--; - mutex_unlock(&vip->mutex); - return -ENOMEM; - } + vip_active_buf_next(vip); - mutex_unlock(&vip->mutex); - videobuf_queue_dma_contig_init_cached(&vip->vb_vidq, - &vip_qops, - &vip->pdev->dev, - &vip->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct videobuf_buffer), - vip, NULL); - REG_READ(vip, DVP_ITS); - REG_WRITE(vip, DVP_HLFLN, DVP_HLFLN_SD); - REG_WRITE(vip, DVP_ITM, DVP_IT_VSB | DVP_IT_VST); - REG_WRITE(vip, DVP_CTL, DVP_CTL_RST); - REG_WRITE(vip, DVP_CTL, 0); - REG_READ(vip, DVP_ITS); return 0; } -/** - * vip_close - close video device - * @file: descriptor of device - * - * close video device, wait until all pending operations are finished - * ( maximum FRAME_MAX buffers pending ) - * Turn off interrupts. - * - * return value: 0, always succesful. - */ -static int vip_close(struct file *file) +static int start_streaming(struct vb2_queue *vq, unsigned int count) { - struct video_device *dev = video_devdata(file); - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = vb2_get_drv_priv(vq); - vip->closing = 1; - if (vip->active) - videobuf_waiton(&vip->vb_vidq, vip->active, 0, 0); spin_lock_irq(&vip->slock); - - REG_WRITE(vip, DVP_ITM, 0); - REG_WRITE(vip, DVP_CTL, DVP_CTL_RST); - REG_WRITE(vip, DVP_CTL, 0); - REG_READ(vip, DVP_ITS); - - vip->started = 0; - vip->active = NULL; - + /* Enable interrupt VSYNC Top and Bottom*/ + reg_write(vip, DVP_ITM, DVP_IT_VSB | DVP_IT_VST); spin_unlock_irq(&vip->slock); - videobuf_stop(&vip->vb_vidq); - videobuf_mmap_free(&vip->vb_vidq); + if (count) + start_dma(vip, vip->active); - dma_free_coherent(&vip->pdev->dev, 64, vip->mem_spare, vip->dma_spare); - file->private_data = NULL; - mutex_lock(&vip->mutex); - vip->users--; - mutex_unlock(&vip->mutex); return 0; } -/** - * vip_read - read from video input - * @file: descriptor of device - * @data: user buffer - * @count: number of bytes to be read - * @ppos: position within stream - * - * read video data from video device. - * handling is done in generic videobuf layer - * return value: provided by videobuf layer - */ -static ssize_t vip_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) +/* abort streaming and wait for last buffer */ +static int stop_streaming(struct vb2_queue *vq) { - struct video_device *dev = file->private_data; - struct sta2x11_vip *vip = video_get_drvdata(dev); - - return videobuf_read_stream(&vip->vb_vidq, data, count, ppos, 0, - file->f_flags & O_NONBLOCK); + struct sta2x11_vip *vip = vb2_get_drv_priv(vq); + struct vip_buffer *vip_buf, *node; + + /* Disable acquisition */ + reg_write(vip, DVP_CTL, reg_read(vip, DVP_CTL) & ~DVP_CTL_ENA); + /* Disable all interrupts */ + reg_write(vip, DVP_ITM, 0); + + /* Release all active buffers */ + spin_lock(&vip->lock); + list_for_each_entry_safe(vip_buf, node, &vip->buffer_list, list) { + vb2_buffer_done(&vip_buf->vb, VB2_BUF_STATE_ERROR); + list_del(&vip_buf->list); + } + spin_unlock(&vip->lock); + return 0; } -/** - * vip_mmap - map user buffer - * @file: descriptor of device - * @vma: user buffer - * - * map user space buffer into kernel mode, including DMA address. - * handling is done in generic videobuf layer. - * return value: provided by videobuf layer - */ -static int vip_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_device *dev = file->private_data; - struct sta2x11_vip *vip = video_get_drvdata(dev); +static struct vb2_ops vip_video_qops = { + .queue_setup = queue_setup, + .buf_init = buffer_init, + .buf_prepare = buffer_prepare, + .buf_finish = buffer_finish, + .buf_queue = buffer_queue, + .start_streaming = start_streaming, + .stop_streaming = stop_streaming, +}; - return videobuf_mmap_mapper(&vip->vb_vidq, vma); -} -/** - * vip_poll - poll for event - * @file: descriptor of device - * @wait: contains events to be waited for - * - * wait for event related to video device. - * handling is done in generic videobuf layer. - * return value: provided by videobuf layer - */ -static unsigned int vip_poll(struct file *file, struct poll_table_struct *wait) -{ - struct video_device *dev = file->private_data; - struct sta2x11_vip *vip = video_get_drvdata(dev); +/* File Operations */ +static const struct v4l2_file_operations vip_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .unlocked_ioctl = video_ioctl2, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll +}; - return videobuf_poll_stream(file, &vip->vb_vidq, wait); -} /** * vidioc_querycap - return capabilities of device - * @file: descriptor of device (not used) - * @priv: points to current videodevice + * @file: descriptor of device * @cap: contains return values * * the capabilities of the device are returned @@ -527,25 +413,22 @@ static unsigned int vip_poll(struct file *file, struct poll_table_struct *wait) static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = video_drvdata(file); - memset(cap, 0, sizeof(struct v4l2_capability)); - strcpy(cap->driver, DRV_NAME); - strcpy(cap->card, DRV_NAME); - cap->version = 0; + strcpy(cap->driver, KBUILD_MODNAME); + strcpy(cap->card, KBUILD_MODNAME); snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", pci_name(vip->pdev)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } /** * vidioc_s_std - set video standard - * @file: descriptor of device (not used) - * @priv: points to current videodevice + * @file: descriptor of device * @std: contains standard to be set * * the video standard is set @@ -558,8 +441,7 @@ static int vidioc_querycap(struct file *file, void *priv, */ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) { - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = video_drvdata(file); v4l2_std_id oldstd = vip->std, newstd; int status; @@ -592,8 +474,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) /** * vidioc_g_std - get video standard - * @file: descriptor of device (not used) - * @priv: points to current videodevice + * @file: descriptor of device * @std: contains return values * * the current video standard is returned @@ -602,8 +483,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) */ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std) { - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = video_drvdata(file); *std = vip->std; return 0; @@ -611,8 +491,7 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std) /** * vidioc_querystd - get possible video standards - * @file: descriptor of device (not used) - * @priv: points to current videodevice + * @file: descriptor of device * @std: contains return values * * all possible video standards are returned @@ -621,79 +500,11 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std) */ static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std) { - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = video_drvdata(file); return v4l2_subdev_call(vip->decoder, video, querystd, std); - } -/** - * vidioc_queryctl - get possible control settings - * @file: descriptor of device (not used) - * @priv: points to current videodevice - * @ctrl: contains return values - * - * return possible values for a control - * return value: delivered by video DAC routine. - */ -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *ctrl) -{ - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); - - return v4l2_subdev_call(vip->decoder, core, queryctrl, ctrl); -} - -/** - * vidioc_g_ctl - get control value - * @file: descriptor of device (not used) - * @priv: points to current videodevice - * @ctrl: contains return values - * - * return setting for a control value - * return value: delivered by video DAC routine. - */ -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); - - return v4l2_subdev_call(vip->decoder, core, g_ctrl, ctrl); -} - -/** - * vidioc_s_ctl - set control value - * @file: descriptor of device (not used) - * @priv: points to current videodevice - * @ctrl: contains value to be set - * - * set value for a specific control - * return value: delivered by video DAC routine. - */ -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); - - return v4l2_subdev_call(vip->decoder, core, s_ctrl, ctrl); -} - -/** - * vidioc_enum_input - return name of input line - * @file: descriptor of device (not used) - * @priv: points to current videodevice - * @inp: contains return values - * - * the user friendly name of the input line is returned - * - * return value: 0, no error. - * - * -EINVAL, input line number out of range - */ static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *inp) { @@ -709,8 +520,7 @@ static int vidioc_enum_input(struct file *file, void *priv, /** * vidioc_s_input - set input line - * @file: descriptor of device ( not used) - * @priv: points to current videodevice + * @file: descriptor of device * @i: new input line number * * the current active input line is set @@ -721,8 +531,7 @@ static int vidioc_enum_input(struct file *file, void *priv, */ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = video_drvdata(file); int ret; if (i > 1) @@ -737,8 +546,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) /** * vidioc_g_input - return input line - * @file: descriptor of device ( not used) - * @priv: points to current videodevice + * @file: descriptor of device * @i: returned input line number * * the current active input line is returned @@ -747,8 +555,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) */ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) { - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = video_drvdata(file); *i = vip->input; return 0; @@ -756,8 +563,6 @@ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) /** * vidioc_enum_fmt_vid_cap - return video capture format - * @file: descriptor of device ( not used) - * @priv: points to current videodevice * @f: returned format information * * returns name and format of video capture @@ -780,8 +585,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, /** * vidioc_try_fmt_vid_cap - set video capture format - * @file: descriptor of device ( not used) - * @priv: points to current videodevice + * @file: descriptor of device * @f: new format * * new video format is set which includes width and @@ -797,12 +601,13 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = video_drvdata(file); int interlace_lim; - if (V4L2_PIX_FMT_UYVY != f->fmt.pix.pixelformat) + if (V4L2_PIX_FMT_UYVY != f->fmt.pix.pixelformat) { + v4l2_warn(&vip->v4l2_dev, "Invalid format, only UYVY supported\n"); return -EINVAL; + } if (V4L2_STD_525_60 & vip->std) interlace_lim = 240; @@ -810,6 +615,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, interlace_lim = 288; switch (f->fmt.pix.field) { + default: case V4L2_FIELD_ANY: if (interlace_lim < f->fmt.pix.height) f->fmt.pix.field = V4L2_FIELD_INTERLACED; @@ -823,10 +629,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, break; case V4L2_FIELD_INTERLACED: break; - default: - return -EINVAL; } + /* It is the only supported format */ + f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; f->fmt.pix.height &= ~1; if (2 * interlace_lim < f->fmt.pix.height) f->fmt.pix.height = 2 * interlace_lim; @@ -842,8 +648,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, /** * vidioc_s_fmt_vid_cap - set current video format parameters - * @file: descriptor of device ( not used) - * @priv: points to current videodevice + * @file: descriptor of device * @f: returned format information * * set new capture format @@ -854,22 +659,63 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = video_drvdata(file); + unsigned int t_stop, b_stop, pitch; int ret; ret = vidioc_try_fmt_vid_cap(file, priv, f); if (ret) return ret; - memcpy(&vip->format, &f->fmt.pix, sizeof(struct v4l2_pix_format)); + if (vb2_is_busy(&vip->vb_vidq)) { + /* Can't change format during acquisition */ + v4l2_err(&vip->v4l2_dev, "device busy\n"); + return -EBUSY; + } + vip->format = f->fmt.pix; + switch (vip->format.field) { + case V4L2_FIELD_INTERLACED: + t_stop = ((vip->format.height / 2 - 1) << 16) | + (2 * vip->format.width - 1); + b_stop = t_stop; + pitch = 4 * vip->format.width; + break; + case V4L2_FIELD_TOP: + t_stop = ((vip->format.height - 1) << 16) | + (2 * vip->format.width - 1); + b_stop = (0 << 16) | (2 * vip->format.width - 1); + pitch = 2 * vip->format.width; + break; + case V4L2_FIELD_BOTTOM: + t_stop = (0 << 16) | (2 * vip->format.width - 1); + b_stop = (vip->format.height << 16) | + (2 * vip->format.width - 1); + pitch = 2 * vip->format.width; + break; + default: + v4l2_err(&vip->v4l2_dev, "unknown field format\n"); + return -EINVAL; + } + + spin_lock_irq(&vip->slock); + /* Y-X Top Field Offset */ + reg_write(vip, DVP_TFO, 0); + /* Y-X Bottom Field Offset */ + reg_write(vip, DVP_BFO, 0); + /* Y-X Top Field Stop*/ + reg_write(vip, DVP_TFS, t_stop); + /* Y-X Bottom Field Stop */ + reg_write(vip, DVP_BFS, b_stop); + /* Video Memory Pitch */ + reg_write(vip, DVP_VMP, pitch); + spin_unlock_irq(&vip->slock); + return 0; } /** * vidioc_g_fmt_vid_cap - get current video format parameters - * @file: descriptor of device ( not used) - * @priv: points to current videodevice + * @file: descriptor of device * @f: contains format information * * returns current video format parameters @@ -879,150 +725,47 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); - - memcpy(&f->fmt.pix, &vip->format, sizeof(struct v4l2_pix_format)); - return 0; -} - -/** - * vidioc_reqfs - request buffer - * @file: descriptor of device ( not used) - * @priv: points to current videodevice - * @p: video buffer - * - * Handling is done in generic videobuf layer. - */ -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); - - return videobuf_reqbufs(&vip->vb_vidq, p); -} - -/** - * vidioc_querybuf - query buffer - * @file: descriptor of device ( not used) - * @priv: points to current videodevice - * @p: video buffer - * - * query buffer state. - * Handling is done in generic videobuf layer. - */ -static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = video_drvdata(file); - return videobuf_querybuf(&vip->vb_vidq, p); -} + f->fmt.pix = vip->format; -/** - * vidioc_qbuf - queue a buffer - * @file: descriptor of device ( not used) - * @priv: points to current videodevice - * @p: video buffer - * - * Handling is done in generic videobuf layer. - */ -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); - - return videobuf_qbuf(&vip->vb_vidq, p); -} - -/** - * vidioc_dqbuf - dequeue a buffer - * @file: descriptor of device ( not used) - * @priv: points to current videodevice - * @p: video buffer - * - * Handling is done in generic videobuf layer. - */ -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); - - return videobuf_dqbuf(&vip->vb_vidq, p, file->f_flags & O_NONBLOCK); -} - -/** - * vidioc_streamon - turn on streaming - * @file: descriptor of device ( not used) - * @priv: points to current videodevice - * @type: type of capture - * - * turn on streaming. - * Handling is done in generic videobuf layer. - */ -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); - - return videobuf_streamon(&vip->vb_vidq); -} - -/** - * vidioc_streamoff - turn off streaming - * @file: descriptor of device ( not used) - * @priv: points to current videodevice - * @type: type of capture - * - * turn off streaming. - * Handling is done in generic videobuf layer. - */ -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); - - return videobuf_streamoff(&vip->vb_vidq); + return 0; } -static const struct v4l2_file_operations vip_fops = { - .owner = THIS_MODULE, - .open = vip_open, - .release = vip_close, - .ioctl = video_ioctl2, - .read = vip_read, - .mmap = vip_mmap, - .poll = vip_poll -}; - static const struct v4l2_ioctl_ops vip_ioctl_ops = { .vidioc_querycap = vidioc_querycap, - .vidioc_s_std = vidioc_s_std, + /* FMT handling */ + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + /* Buffer handlers */ + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + /* Stream on/off */ + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + /* Standard handling */ .vidioc_g_std = vidioc_g_std, + .vidioc_s_std = vidioc_s_std, .vidioc_querystd = vidioc_querystd, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, + /* Input handling */ .vidioc_enum_input = vidioc_enum_input, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_s_input = vidioc_s_input, .vidioc_g_input = vidioc_g_input, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, + .vidioc_s_input = vidioc_s_input, + /* Log status ioctl */ + .vidioc_log_status = v4l2_ctrl_log_status, + /* Event handling */ + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device video_dev_template = { - .name = DRV_NAME, + .name = KBUILD_MODNAME, .release = video_device_release, .fops = &vip_fops, .ioctl_ops = &vip_ioctl_ops, @@ -1036,9 +779,7 @@ static struct video_device video_dev_template = { * * check for both frame interrupts set ( top and bottom ). * check FIFO overflow, but limit number of log messages after open. - * signal a complete buffer if done. - * dequeue a new buffer if available. - * disable VIP if no buffer available. + * signal a complete buffer if done * * return value: IRQ_NONE, interrupt was not generated by VIP * @@ -1046,88 +787,122 @@ static struct video_device video_dev_template = { */ static irqreturn_t vip_irq(int irq, struct sta2x11_vip *vip) { - u32 status, dma; - unsigned long flags; - struct videobuf_buffer *vb; + unsigned int status; - status = REG_READ(vip, DVP_ITS); + status = reg_read(vip, DVP_ITS); - if (!status) { - pr_debug("VIP: irq ignored\n"); + if (!status) /* No interrupt to handle */ return IRQ_NONE; - } - - if (!vip->started) - return IRQ_HANDLED; - if (status & DVP_IT_VSB) - vip->bcount++; - - if (status & DVP_IT_VST) - vip->tcount++; + if (status & DVP_IT_FIFO) + if (vip->overflow++ > 5) + pr_info("VIP: fifo overflow\n"); - if ((DVP_IT_VSB | DVP_IT_VST) == (status & (DVP_IT_VST | DVP_IT_VSB))) { + if ((status & DVP_IT_VST) && (status & DVP_IT_VSB)) { /* this is bad, we are too slow, hope the condition is gone * on the next frame */ - pr_info("VIP: both irqs\n"); return IRQ_HANDLED; } - if (status & DVP_IT_FIFO) { - if (5 > vip->overflow++) - pr_info("VIP: fifo overflow\n"); + if (status & DVP_IT_VST) + if ((++vip->tcount) < 2) + return IRQ_HANDLED; + if (status & DVP_IT_VSB) { + vip->bcount++; + return IRQ_HANDLED; } - if (2 > vip->tcount) - return IRQ_HANDLED; + if (vip->active) { /* Acquisition is over on this buffer */ + /* Disable acquisition */ + reg_write(vip, DVP_CTL, reg_read(vip, DVP_CTL) & ~DVP_CTL_ENA); + /* Remove the active buffer from the list */ + do_gettimeofday(&vip->active->vb.v4l2_buf.timestamp); + vip->active->vb.v4l2_buf.sequence = vip->sequence++; + vb2_buffer_done(&vip->active->vb, VB2_BUF_STATE_DONE); + } - if (status & DVP_IT_VSB) - return IRQ_HANDLED; + return IRQ_HANDLED; +} - spin_lock_irqsave(&vip->slock, flags); +static void sta2x11_vip_init_register(struct sta2x11_vip *vip) +{ + /* Register initialization */ + spin_lock_irq(&vip->slock); + /* Clean interrupt */ + reg_read(vip, DVP_ITS); + /* Enable Half Line per vertical */ + reg_write(vip, DVP_HLFLN, DVP_HLFLN_SD); + /* Reset VIP control */ + reg_write(vip, DVP_CTL, DVP_CTL_RST); + /* Clear VIP control */ + reg_write(vip, DVP_CTL, 0); + spin_unlock_irq(&vip->slock); +} +static void sta2x11_vip_clear_register(struct sta2x11_vip *vip) +{ + spin_lock_irq(&vip->slock); + /* Disable interrupt */ + reg_write(vip, DVP_ITM, 0); + /* Reset VIP Control */ + reg_write(vip, DVP_CTL, DVP_CTL_RST); + /* Clear VIP Control */ + reg_write(vip, DVP_CTL, 0); + /* Clean VIP Interrupt */ + reg_read(vip, DVP_ITS); + spin_unlock_irq(&vip->slock); +} +static int sta2x11_vip_init_buffer(struct sta2x11_vip *vip) +{ + int err; - REG_WRITE(vip, DVP_CTL, REG_READ(vip, DVP_CTL) & ~DVP_CTL_ENA); - if (vip->active) { - v4l2_get_timestamp(&vip->active->ts); - vip->active->field_count++; - vip->active->state = VIDEOBUF_DONE; - wake_up(&vip->active->done); - vip->active = NULL; + err = dma_set_coherent_mask(&vip->pdev->dev, DMA_BIT_MASK(29)); + if (err) { + v4l2_err(&vip->v4l2_dev, "Cannot configure coherent mask"); + return err; } - if (!vip->closing) { - if (list_empty(&vip->capture)) - goto done; - - vb = list_first_entry(&vip->capture, struct videobuf_buffer, - queue); - if (NULL == vb) { - pr_info("VIP: no buffer\n"); - goto done; - } - vb->state = VIDEOBUF_ACTIVE; - list_del(&vb->queue); - vip->active = vb; - dma = videobuf_to_dma_contig(vb); - switch (vip->format.field) { - case V4L2_FIELD_INTERLACED: - REG_WRITE(vip, DVP_VTP, dma); - REG_WRITE(vip, DVP_VBP, dma + vip->format.width * 2); - break; - case V4L2_FIELD_TOP: - case V4L2_FIELD_BOTTOM: - REG_WRITE(vip, DVP_VTP, dma); - REG_WRITE(vip, DVP_VBP, dma); - break; - default: - pr_warning("VIP: unknown field format\n"); - goto done; - break; - } - REG_WRITE(vip, DVP_CTL, REG_READ(vip, DVP_CTL) | DVP_CTL_ENA); + memset(&vip->vb_vidq, 0, sizeof(struct vb2_queue)); + vip->vb_vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vip->vb_vidq.io_modes = VB2_MMAP | VB2_READ; + vip->vb_vidq.drv_priv = vip; + vip->vb_vidq.buf_struct_size = sizeof(struct vip_buffer); + vip->vb_vidq.ops = &vip_video_qops; + vip->vb_vidq.mem_ops = &vb2_dma_contig_memops; + err = vb2_queue_init(&vip->vb_vidq); + if (err) + return err; + INIT_LIST_HEAD(&vip->buffer_list); + spin_lock_init(&vip->lock); + + + vip->alloc_ctx = vb2_dma_contig_init_ctx(&vip->pdev->dev); + if (IS_ERR(vip->alloc_ctx)) { + v4l2_err(&vip->v4l2_dev, "Can't allocate buffer context"); + return PTR_ERR(vip->alloc_ctx); } -done: - spin_unlock_irqrestore(&vip->slock, flags); - return IRQ_HANDLED; + + return 0; +} +static void sta2x11_vip_release_buffer(struct sta2x11_vip *vip) +{ + vb2_dma_contig_cleanup_ctx(vip->alloc_ctx); +} +static int sta2x11_vip_init_controls(struct sta2x11_vip *vip) +{ + /* + * Inititialize an empty control so VIP can inerithing controls + * from ADV7180 + */ + v4l2_ctrl_handler_init(&vip->ctrl_hdl, 0); + + vip->v4l2_dev.ctrl_handler = &vip->ctrl_hdl; + if (vip->ctrl_hdl.error) { + int err = vip->ctrl_hdl.error; + + v4l2_ctrl_handler_free(&vip->ctrl_hdl); + return err; + } + + return 0; } /** @@ -1212,10 +987,17 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, struct sta2x11_vip *vip; struct vip_config *config; + /* Check if hardware support 26-bit DMA */ + if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(26))) { + dev_err(&pdev->dev, "26-bit DMA addressing not available\n"); + return -EINVAL; + } + /* Enable PCI */ ret = pci_enable_device(pdev); if (ret) return ret; + /* Get VIP platform data */ config = dev_get_platdata(&pdev->dev); if (!config) { dev_info(&pdev->dev, "VIP slot disabled\n"); @@ -1223,6 +1005,7 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, goto disable; } + /* Power configuration */ ret = vip_gpio_reserve(&pdev->dev, config->pwr_pin, 0, config->pwr_name); if (ret) @@ -1237,7 +1020,6 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, goto disable; } } - if (config->pwr_pin != -1) { /* Datasheet says 5ms between PWR and RST */ usleep_range(5000, 25000); @@ -1251,17 +1033,20 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, } usleep_range(5000, 25000); + /* Allocate a new VIP instance */ vip = kzalloc(sizeof(struct sta2x11_vip), GFP_KERNEL); if (!vip) { ret = -ENOMEM; goto release_gpios; } - vip->pdev = pdev; vip->std = V4L2_STD_PAL; vip->format = formats_50[0]; vip->config = config; + ret = sta2x11_vip_init_controls(vip); + if (ret) + goto free_mem; if (v4l2_device_register(&pdev->dev, &vip->v4l2_dev)) goto free_mem; @@ -1271,46 +1056,52 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, pci_set_master(pdev); - ret = pci_request_regions(pdev, DRV_NAME); + ret = pci_request_regions(pdev, KBUILD_MODNAME); if (ret) goto unreg; vip->iomem = pci_iomap(pdev, 0, 0x100); if (!vip->iomem) { - ret = -ENOMEM; /* FIXME */ + ret = -ENOMEM; goto release; } pci_enable_msi(pdev); - INIT_LIST_HEAD(&vip->capture); + /* Initialize buffer */ + ret = sta2x11_vip_init_buffer(vip); + if (ret) + goto unmap; + spin_lock_init(&vip->slock); - mutex_init(&vip->mutex); - vip->started = 0; - vip->disabled = 0; ret = request_irq(pdev->irq, (irq_handler_t) vip_irq, - IRQF_SHARED, DRV_NAME, vip); + IRQF_SHARED, KBUILD_MODNAME, vip); if (ret) { dev_err(&pdev->dev, "request_irq failed\n"); ret = -ENODEV; - goto unmap; + goto release_buf; } + /* Alloc, initialize and register video device */ vip->video_dev = video_device_alloc(); if (!vip->video_dev) { ret = -ENOMEM; goto release_irq; } - *(vip->video_dev) = video_dev_template; + vip->video_dev = &video_dev_template; + vip->video_dev->v4l2_dev = &vip->v4l2_dev; + vip->video_dev->queue = &vip->vb_vidq; + set_bit(V4L2_FL_USE_FH_PRIO, &vip->video_dev->flags); video_set_drvdata(vip->video_dev, vip); ret = video_register_device(vip->video_dev, VFL_TYPE_GRABBER, -1); if (ret) goto vrelease; + /* Get ADV7180 subdevice */ vip->adapter = i2c_get_adapter(vip->config->i2c_id); if (!vip->adapter) { ret = -ENODEV; @@ -1328,10 +1119,11 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, } i2c_put_adapter(vip->adapter); - v4l2_subdev_call(vip->decoder, core, init, 0); - pr_info("STA2X11 Video Input Port (VIP) loaded\n"); + sta2x11_vip_init_register(vip); + + dev_info(&pdev->dev, "STA2X11 Video Input Port (VIP) loaded\n"); return 0; vunreg: @@ -1343,10 +1135,12 @@ vrelease: video_device_release(vip->video_dev); release_irq: free_irq(pdev->irq, vip); +release_buf: + sta2x11_vip_release_buffer(vip); pci_disable_msi(pdev); unmap: + vb2_queue_release(&vip->vb_vidq); pci_iounmap(pdev, vip->iomem); - mutex_destroy(&vip->mutex); release: pci_release_regions(pdev); unreg: @@ -1382,16 +1176,18 @@ static void sta2x11_vip_remove_one(struct pci_dev *pdev) struct sta2x11_vip *vip = container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); + sta2x11_vip_clear_register(vip); + video_set_drvdata(vip->video_dev, NULL); video_unregister_device(vip->video_dev); /*do not call video_device_release() here, is already done */ free_irq(pdev->irq, vip); pci_disable_msi(pdev); + vb2_queue_release(&vip->vb_vidq); pci_iounmap(pdev, vip->iomem); pci_release_regions(pdev); v4l2_device_unregister(&vip->v4l2_dev); - mutex_destroy(&vip->mutex); vip_gpio_release(&pdev->dev, vip->config->pwr_pin, vip->config->pwr_name); @@ -1416,9 +1212,6 @@ static void sta2x11_vip_remove_one(struct pci_dev *pdev) * * return value: 0 always indicate success, * even if device could not be disabled. (workaround for hardware problem) - * - * reurn value : 0, always succesful, even if hardware does not not support - * power down mode. */ static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state) { @@ -1429,15 +1222,15 @@ static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state) int i; spin_lock_irqsave(&vip->slock, flags); - vip->register_save_area[0] = REG_READ(vip, DVP_CTL); - REG_WRITE(vip, DVP_CTL, vip->register_save_area[0] & DVP_CTL_DIS); - vip->register_save_area[SAVE_COUNT] = REG_READ(vip, DVP_ITM); - REG_WRITE(vip, DVP_ITM, 0); + vip->register_save_area[0] = reg_read(vip, DVP_CTL); + reg_write(vip, DVP_CTL, vip->register_save_area[0] & DVP_CTL_DIS); + vip->register_save_area[SAVE_COUNT] = reg_read(vip, DVP_ITM); + reg_write(vip, DVP_ITM, 0); for (i = 1; i < SAVE_COUNT; i++) - vip->register_save_area[i] = REG_READ(vip, 4 * i); + vip->register_save_area[i] = reg_read(vip, 4 * i); for (i = 0; i < AUX_COUNT; i++) vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i] = - REG_READ(vip, registers_to_save[i]); + reg_read(vip, registers_to_save[i]); spin_unlock_irqrestore(&vip->slock, flags); /* save pci state */ pci_save_state(pdev); @@ -1477,7 +1270,7 @@ static int sta2x11_vip_resume(struct pci_dev *pdev) if (vip->disabled) { ret = pci_enable_device(pdev); if (ret) { - pr_warning("VIP: Can't enable device.\n"); + pr_warn("VIP: Can't enable device.\n"); return ret; } vip->disabled = 0; @@ -1488,7 +1281,7 @@ static int sta2x11_vip_resume(struct pci_dev *pdev) * do not call pci_disable_device on sta2x11 because it * break all other Bus masters on this EP */ - pr_warning("VIP: Can't enable device.\n"); + pr_warn("VIP: Can't enable device.\n"); vip->disabled = 1; return ret; } @@ -1497,12 +1290,12 @@ static int sta2x11_vip_resume(struct pci_dev *pdev) spin_lock_irqsave(&vip->slock, flags); for (i = 1; i < SAVE_COUNT; i++) - REG_WRITE(vip, 4 * i, vip->register_save_area[i]); + reg_write(vip, 4 * i, vip->register_save_area[i]); for (i = 0; i < AUX_COUNT; i++) - REG_WRITE(vip, registers_to_save[i], + reg_write(vip, registers_to_save[i], vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i]); - REG_WRITE(vip, DVP_CTL, vip->register_save_area[0]); - REG_WRITE(vip, DVP_ITM, vip->register_save_area[SAVE_COUNT]); + reg_write(vip, DVP_CTL, vip->register_save_area[0]); + reg_write(vip, DVP_ITM, vip->register_save_area[SAVE_COUNT]); spin_unlock_irqrestore(&vip->slock, flags); return 0; } @@ -1515,7 +1308,7 @@ static DEFINE_PCI_DEVICE_TABLE(sta2x11_vip_pci_tbl) = { }; static struct pci_driver sta2x11_vip_driver = { - .name = DRV_NAME, + .name = KBUILD_MODNAME, .probe = sta2x11_vip_init_one, .remove = sta2x11_vip_remove_one, .id_table = sta2x11_vip_pci_tbl, -- cgit v1.2.3 From 9f3b935bfdf0e92b07a6ce0baf0eaa40b98866dc Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Wed, 23 Jan 2013 10:07:07 -0300 Subject: [media] adv7180: remove {query/g_/s_}ctrl All drivers which use this subdevice use also the control framework. The v4l2_subdev_core_ops operations {query/g_/s_}ctrl are useless because device drivers will inherit controls from this subdevice. Signed-off-by: Federico Vaga Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7180.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 64d71fb87a96..34f39d3b3e3e 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -402,9 +402,6 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = { static const struct v4l2_subdev_core_ops adv7180_core_ops = { .g_chip_ident = adv7180_g_chip_ident, .s_std = adv7180_s_std, - .queryctrl = v4l2_subdev_queryctrl, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, }; static const struct v4l2_subdev_ops adv7180_ops = { -- cgit v1.2.3 From ddf289f98b5ef664e4606205cf31d147eab76b18 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 20 Jan 2013 09:26:47 -0300 Subject: [media] em28xx: overhaul em28xx_capture_area_set() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - move the bit shifting of width+height values inside the function - fix the debug message format and output values - add comment about the size limit (e.g. EM277x supports >2MPix) - make void, because error checking is incomplete and we never check the returned value (we would continue anyway) Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 80f87bbbd554..ee00f9e23420 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -733,22 +733,24 @@ static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, return em28xx_write_regs(dev, EM28XX_R2B_YMAX, &ymax, 1); } -static int em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, +static void em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, u16 width, u16 height) { - u8 cwidth = width; - u8 cheight = height; - u8 overflow = (height >> 7 & 0x02) | (width >> 8 & 0x01); + u8 cwidth = width >> 2; + u8 cheight = height >> 2; + u8 overflow = (height >> 9 & 0x02) | (width >> 10 & 0x01); + /* NOTE: size limit: 2047x1023 = 2MPix */ - em28xx_coredbg("em28xx Area Set: (%d,%d)\n", - (width | (overflow & 2) << 7), - (height | (overflow & 1) << 8)); + em28xx_coredbg("capture area set to (%d,%d): %dx%d\n", + hstart, vstart, + ((overflow & 2) << 9 | cwidth << 2), + ((overflow & 1) << 10 | cheight << 2)); em28xx_write_regs(dev, EM28XX_R1C_HSTART, &hstart, 1); em28xx_write_regs(dev, EM28XX_R1D_VSTART, &vstart, 1); em28xx_write_regs(dev, EM28XX_R1E_CWIDTH, &cwidth, 1); em28xx_write_regs(dev, EM28XX_R1F_CHEIGHT, &cheight, 1); - return em28xx_write_regs(dev, EM28XX_R1B_OFLOW, &overflow, 1); + em28xx_write_regs(dev, EM28XX_R1B_OFLOW, &overflow, 1); } static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) @@ -801,9 +803,9 @@ int em28xx_resolution_set(struct em28xx *dev) it out, we end up with the same format as the rest of the VBI region */ if (em28xx_vbi_supported(dev) == 1) - em28xx_capture_area_set(dev, 0, 2, width >> 2, height >> 2); + em28xx_capture_area_set(dev, 0, 2, width, height); else - em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2); + em28xx_capture_area_set(dev, 0, 0, width, height); return em28xx_scaler_set(dev, dev->hscale, dev->vscale); } -- cgit v1.2.3 From 944f7b1dedb859f76a88c8d34ce23a90bf6285a0 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 17:49:46 +0900 Subject: leds-lp55xx: clean up init_device() in lp5521/5523 To make _probe() simple, device initialization code is moved to _init_device() at each driver. This patch is a preceding step for lp55xx common driver architecture. leds-lp5521: When 'lp5521_init_device()' gets failed, error handling should be 'fail1' rather than 'fail2'. fail1: releasing platform resource and return code fail2: releasing allocated LED devices with handling 'fail1' The 'lp5521_init_device()' is called before creating LED devices. Thus, 'goto fail1' is proper error handler of this function. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 102 +++++++++++++++++++++++++-------------------- drivers/leds/leds-lp5523.c | 47 ++++++++++++--------- 2 files changed, 84 insertions(+), 65 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index cb8a5220200b..24f439f78bd8 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -687,6 +687,59 @@ static void lp5521_unregister_sysfs(struct i2c_client *client) &lp5521_led_attribute_group); } +static int lp5521_init_device(struct lp5521_chip *chip) +{ + struct lp5521_platform_data *pdata = chip->pdata; + struct i2c_client *client = chip->client; + int ret; + u8 buf; + + if (pdata->setup_resources) { + ret = pdata->setup_resources(); + if (ret < 0) + return ret; + } + + if (pdata->enable) { + pdata->enable(0); + usleep_range(1000, 2000); /* Keep enable down at least 1ms */ + pdata->enable(1); + usleep_range(1000, 2000); /* 500us abs min. */ + } + + lp5521_write(client, LP5521_REG_RESET, 0xff); + usleep_range(10000, 20000); /* + * Exact value is not available. 10 - 20ms + * appears to be enough for reset. + */ + + /* + * Make sure that the chip is reset by reading back the r channel + * current reg. This is dummy read is required on some platforms - + * otherwise further access to the R G B channels in the + * LP5521_REG_ENABLE register will not have any effect - strange! + */ + ret = lp5521_read(client, LP5521_REG_R_CURRENT, &buf); + if (ret) { + dev_err(&client->dev, "error in resetting chip\n"); + return ret; + } + if (buf != LP5521_REG_R_CURR_DEFAULT) { + dev_err(&client->dev, + "unexpected data in register (expected 0x%x got 0x%x)\n", + LP5521_REG_R_CURR_DEFAULT, buf); + ret = -EINVAL; + return ret; + } + usleep_range(10000, 20000); + + ret = lp5521_detect(client); + if (ret) + dev_err(&client->dev, "Chip not found\n"); + + return ret; +} + static int lp5521_init_led(struct lp5521_led *led, struct i2c_client *client, int chan, struct lp5521_platform_data *pdata) @@ -742,7 +795,6 @@ static int lp5521_probe(struct i2c_client *client, struct lp5521_chip *chip; struct lp5521_platform_data *pdata; int ret, i, led; - u8 buf; chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -762,51 +814,9 @@ static int lp5521_probe(struct i2c_client *client, chip->pdata = pdata; - if (pdata->setup_resources) { - ret = pdata->setup_resources(); - if (ret < 0) - return ret; - } - - if (pdata->enable) { - pdata->enable(0); - usleep_range(1000, 2000); /* Keep enable down at least 1ms */ - pdata->enable(1); - usleep_range(1000, 2000); /* 500us abs min. */ - } - - lp5521_write(client, LP5521_REG_RESET, 0xff); - usleep_range(10000, 20000); /* - * Exact value is not available. 10 - 20ms - * appears to be enough for reset. - */ - - /* - * Make sure that the chip is reset by reading back the r channel - * current reg. This is dummy read is required on some platforms - - * otherwise further access to the R G B channels in the - * LP5521_REG_ENABLE register will not have any effect - strange! - */ - ret = lp5521_read(client, LP5521_REG_R_CURRENT, &buf); - if (ret) { - dev_err(&client->dev, "error in resetting chip\n"); - goto fail2; - } - if (buf != LP5521_REG_R_CURR_DEFAULT) { - dev_err(&client->dev, - "unexpected data in register (expected 0x%x got 0x%x)\n", - LP5521_REG_R_CURR_DEFAULT, buf); - ret = -EINVAL; - goto fail2; - } - usleep_range(10000, 20000); - - ret = lp5521_detect(client); - - if (ret) { - dev_err(&client->dev, "Chip not found\n"); - goto fail2; - } + ret = lp5521_init_device(chip); + if (ret) + goto fail1; dev_info(&client->dev, "%s programmable led chip found\n", id->name); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 7f5be8948cde..491ea725bb0b 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -896,6 +896,33 @@ static int lp5523_init_led(struct lp5523_led *led, struct device *dev, return 0; } +static int lp5523_init_device(struct lp5523_chip *chip) +{ + struct lp5523_platform_data *pdata = chip->pdata; + struct i2c_client *client = chip->client; + int ret; + + if (pdata->setup_resources) { + ret = pdata->setup_resources(); + if (ret < 0) + return ret; + } + + if (pdata->enable) { + pdata->enable(0); + usleep_range(1000, 2000); /* Keep enable down at least 1ms */ + pdata->enable(1); + usleep_range(1000, 2000); /* 500us abs min. */ + } + + lp5523_write(client, LP5523_REG_RESET, 0xff); + usleep_range(10000, 20000); /* + * Exact value is not available. 10 - 20ms + * appears to be enough for reset. + */ + return lp5523_detect(client); +} + static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -921,25 +948,7 @@ static int lp5523_probe(struct i2c_client *client, chip->pdata = pdata; - if (pdata->setup_resources) { - ret = pdata->setup_resources(); - if (ret < 0) - return ret; - } - - if (pdata->enable) { - pdata->enable(0); - usleep_range(1000, 2000); /* Keep enable down at least 1ms */ - pdata->enable(1); - usleep_range(1000, 2000); /* 500us abs min. */ - } - - lp5523_write(client, LP5523_REG_RESET, 0xff); - usleep_range(10000, 20000); /* - * Exact value is not available. 10 - 20ms - * appears to be enough for reset. - */ - ret = lp5523_detect(client); + ret = lp5523_init_device(chip); if (ret) goto fail1; -- cgit v1.2.3 From 1a9914855d2868112257dbc5771ddc6ebc9b2cab Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 17:50:36 +0900 Subject: leds-lp55xx: clean up deinit_device() in lp5521/5523 Device de-initialization code is moved to _deinit_device() at each driver. This patch is a preceding step for lp55xx common driver architecture. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 20 ++++++++++++-------- drivers/leds/leds-lp5523.c | 20 ++++++++++++-------- 2 files changed, 24 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 24f439f78bd8..fd963fe0a944 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -740,6 +740,16 @@ static int lp5521_init_device(struct lp5521_chip *chip) return ret; } +static void lp5521_deinit_device(struct lp5521_chip *chip) +{ + struct lp5521_platform_data *pdata = chip->pdata; + + if (pdata->enable) + pdata->enable(0); + if (pdata->release_resources) + pdata->release_resources(); +} + static int lp5521_init_led(struct lp5521_led *led, struct i2c_client *client, int chan, struct lp5521_platform_data *pdata) @@ -865,10 +875,7 @@ fail2: cancel_work_sync(&chip->leds[i].brightness_work); } fail1: - if (pdata->enable) - pdata->enable(0); - if (pdata->release_resources) - pdata->release_resources(); + lp5521_deinit_device(chip); return ret; } @@ -885,10 +892,7 @@ static int lp5521_remove(struct i2c_client *client) cancel_work_sync(&chip->leds[i].brightness_work); } - if (chip->pdata->enable) - chip->pdata->enable(0); - if (chip->pdata->release_resources) - chip->pdata->release_resources(); + lp5521_deinit_device(chip); return 0; } diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 491ea725bb0b..ddb482aebe14 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -923,6 +923,16 @@ static int lp5523_init_device(struct lp5523_chip *chip) return lp5523_detect(client); } +static void lp5523_deinit_device(struct lp5523_chip *chip) +{ + struct lp5523_platform_data *pdata = chip->pdata; + + if (pdata->enable) + pdata->enable(0); + if (pdata->release_resources) + pdata->release_resources(); +} + static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1009,10 +1019,7 @@ fail2: flush_work(&chip->leds[i].brightness_work); } fail1: - if (pdata->enable) - pdata->enable(0); - if (pdata->release_resources) - pdata->release_resources(); + lp5523_deinit_device(chip); return ret; } @@ -1031,10 +1038,7 @@ static int lp5523_remove(struct i2c_client *client) flush_work(&chip->leds[i].brightness_work); } - if (chip->pdata->enable) - chip->pdata->enable(0); - if (chip->pdata->release_resources) - chip->pdata->release_resources(); + lp5523_deinit_device(chip); return 0; } -- cgit v1.2.3 From f652480802b636f86e194f9680347676b655d856 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 17:53:40 +0900 Subject: leds-lp55xx: clean up init leds in lp5521/5523 To make LED initialization code simple, new function, _register_leds() is added at each driver. This patch is a preceding step for lp55xx common driver architecture. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 69 ++++++++++++++++++++++++++----------------- drivers/leds/leds-lp5523.c | 73 ++++++++++++++++++++++++++++------------------ 2 files changed, 86 insertions(+), 56 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index fd963fe0a944..f4cd0fe67fef 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -799,12 +799,50 @@ static int lp5521_init_led(struct lp5521_led *led, return 0; } +static int lp5521_register_leds(struct lp5521_chip *chip) +{ + struct lp5521_platform_data *pdata = chip->pdata; + struct i2c_client *client = chip->client; + int i; + int led; + int ret; + + /* Initialize leds */ + chip->num_channels = pdata->num_channels; + chip->num_leds = 0; + led = 0; + for (i = 0; i < pdata->num_channels; i++) { + /* Do not initialize channels that are not connected */ + if (pdata->led_config[i].led_current == 0) + continue; + + ret = lp5521_init_led(&chip->leds[led], client, i, pdata); + if (ret) { + dev_err(&client->dev, "error initializing leds\n"); + return ret; + } + chip->num_leds++; + + chip->leds[led].id = led; + /* Set initial LED current */ + lp5521_set_led_current(chip, led, + chip->leds[led].led_current); + + INIT_WORK(&(chip->leds[led].brightness_work), + lp5521_led_brightness_work); + + led++; + } + + return 0; +} + static int lp5521_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct lp5521_chip *chip; struct lp5521_platform_data *pdata; - int ret, i, led; + int ret, i; chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -836,32 +874,9 @@ static int lp5521_probe(struct i2c_client *client, goto fail1; } - /* Initialize leds */ - chip->num_channels = pdata->num_channels; - chip->num_leds = 0; - led = 0; - for (i = 0; i < pdata->num_channels; i++) { - /* Do not initialize channels that are not connected */ - if (pdata->led_config[i].led_current == 0) - continue; - - ret = lp5521_init_led(&chip->leds[led], client, i, pdata); - if (ret) { - dev_err(&client->dev, "error initializing leds\n"); - goto fail2; - } - chip->num_leds++; - - chip->leds[led].id = led; - /* Set initial LED current */ - lp5521_set_led_current(chip, led, - chip->leds[led].led_current); - - INIT_WORK(&(chip->leds[led].brightness_work), - lp5521_led_brightness_work); - - led++; - } + ret = lp5521_register_leds(chip); + if (ret) + goto fail2; ret = lp5521_register_sysfs(client); if (ret) { diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index ddb482aebe14..f5e893289816 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -896,6 +896,46 @@ static int lp5523_init_led(struct lp5523_led *led, struct device *dev, return 0; } +static int lp5523_register_leds(struct lp5523_chip *chip, const char *name) +{ + struct lp5523_platform_data *pdata = chip->pdata; + struct i2c_client *client = chip->client; + int i; + int led; + int ret; + + /* Initialize leds */ + chip->num_channels = pdata->num_channels; + chip->num_leds = 0; + led = 0; + for (i = 0; i < pdata->num_channels; i++) { + /* Do not initialize channels that are not connected */ + if (pdata->led_config[i].led_current == 0) + continue; + + INIT_WORK(&chip->leds[led].brightness_work, + lp5523_led_brightness_work); + + ret = lp5523_init_led(&chip->leds[led], &client->dev, i, pdata, + name); + if (ret) { + dev_err(&client->dev, "error initializing leds\n"); + return ret; + } + chip->num_leds++; + + chip->leds[led].id = led; + /* Set LED current */ + lp5523_write(client, + LP5523_REG_LED_CURRENT_BASE + chip->leds[led].chan_nr, + chip->leds[led].led_current); + + led++; + } + + return 0; +} + static int lp5523_init_device(struct lp5523_chip *chip) { struct lp5523_platform_data *pdata = chip->pdata; @@ -938,7 +978,7 @@ static int lp5523_probe(struct i2c_client *client, { struct lp5523_chip *chip; struct lp5523_platform_data *pdata; - int ret, i, led; + int ret, i; chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -978,34 +1018,9 @@ static int lp5523_probe(struct i2c_client *client, goto fail1; } - /* Initialize leds */ - chip->num_channels = pdata->num_channels; - chip->num_leds = 0; - led = 0; - for (i = 0; i < pdata->num_channels; i++) { - /* Do not initialize channels that are not connected */ - if (pdata->led_config[i].led_current == 0) - continue; - - INIT_WORK(&chip->leds[led].brightness_work, - lp5523_led_brightness_work); - - ret = lp5523_init_led(&chip->leds[led], &client->dev, i, pdata, - id->name); - if (ret) { - dev_err(&client->dev, "error initializing leds\n"); - goto fail2; - } - chip->num_leds++; - - chip->leds[led].id = led; - /* Set LED current */ - lp5523_write(client, - LP5523_REG_LED_CURRENT_BASE + chip->leds[led].chan_nr, - chip->leds[led].led_current); - - led++; - } + ret = lp5523_register_leds(chip, id->name); + if (ret) + goto fail2; ret = lp5523_register_sysfs(client); if (ret) { -- cgit v1.2.3 From 1904f83d568dba794be9de1311bafb5a4424812a Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 17:56:23 +0900 Subject: leds-lp55xx: clean up deinit leds in lp5521/5523 To make LED unregistration code simple, new function, _unregister_leds() is added in each driver. This patch is a preceding step for lp55xx common driver architecture. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 23 +++++++++++++---------- drivers/leds/leds-lp5523.c | 21 ++++++++++++--------- 2 files changed, 25 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index f4cd0fe67fef..ec1ffe6316c1 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -837,12 +837,22 @@ static int lp5521_register_leds(struct lp5521_chip *chip) return 0; } +static void lp5521_unregister_leds(struct lp5521_chip *chip) +{ + int i; + + for (i = 0; i < chip->num_leds; i++) { + led_classdev_unregister(&chip->leds[i].cdev); + cancel_work_sync(&chip->leds[i].brightness_work); + } +} + static int lp5521_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct lp5521_chip *chip; struct lp5521_platform_data *pdata; - int ret, i; + int ret; chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -885,10 +895,7 @@ static int lp5521_probe(struct i2c_client *client, } return ret; fail2: - for (i = 0; i < chip->num_leds; i++) { - led_classdev_unregister(&chip->leds[i].cdev); - cancel_work_sync(&chip->leds[i].brightness_work); - } + lp5521_unregister_leds(chip); fail1: lp5521_deinit_device(chip); return ret; @@ -897,15 +904,11 @@ fail1: static int lp5521_remove(struct i2c_client *client) { struct lp5521_chip *chip = i2c_get_clientdata(client); - int i; lp5521_run_led_pattern(PATTERN_OFF, chip); lp5521_unregister_sysfs(client); - for (i = 0; i < chip->num_leds; i++) { - led_classdev_unregister(&chip->leds[i].cdev); - cancel_work_sync(&chip->leds[i].brightness_work); - } + lp5521_unregister_leds(chip); lp5521_deinit_device(chip); return 0; diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index f5e893289816..2fc19bbddb72 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -936,6 +936,16 @@ static int lp5523_register_leds(struct lp5523_chip *chip, const char *name) return 0; } +static void lp5523_unregister_leds(struct lp5523_chip *chip) +{ + int i; + + for (i = 0; i < chip->num_leds; i++) { + led_classdev_unregister(&chip->leds[i].cdev); + flush_work(&chip->leds[i].brightness_work); + } +} + static int lp5523_init_device(struct lp5523_chip *chip) { struct lp5523_platform_data *pdata = chip->pdata; @@ -1029,10 +1039,7 @@ static int lp5523_probe(struct i2c_client *client, } return ret; fail2: - for (i = 0; i < chip->num_leds; i++) { - led_classdev_unregister(&chip->leds[i].cdev); - flush_work(&chip->leds[i].brightness_work); - } + lp5523_unregister_leds(chip); fail1: lp5523_deinit_device(chip); return ret; @@ -1041,17 +1048,13 @@ fail1: static int lp5523_remove(struct i2c_client *client) { struct lp5523_chip *chip = i2c_get_clientdata(client); - int i; /* Disable engine mode */ lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_DISABLED); lp5523_unregister_sysfs(client); - for (i = 0; i < chip->num_leds; i++) { - led_classdev_unregister(&chip->leds[i].cdev); - flush_work(&chip->leds[i].brightness_work); - } + lp5523_unregister_leds(chip); lp5523_deinit_device(chip); return 0; -- cgit v1.2.3 From 86eb7748cef00faa3eaefc8fc450ed30281a09e7 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 17:57:02 +0900 Subject: leds-lp55xx: add device reset function in lp5521/5523 Use explicit each driver function rather than raw command. These function will be merged into the lp55xx common driver. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 10 +++++++++- drivers/leds/leds-lp5523.c | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index ec1ffe6316c1..ec89ed641005 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -687,6 +687,13 @@ static void lp5521_unregister_sysfs(struct i2c_client *client) &lp5521_led_attribute_group); } +static void lp5521_reset_device(struct lp5521_chip *chip) +{ + struct i2c_client *client = chip->client; + + lp5521_write(client, LP5521_REG_RESET, 0xff); +} + static int lp5521_init_device(struct lp5521_chip *chip) { struct lp5521_platform_data *pdata = chip->pdata; @@ -707,7 +714,8 @@ static int lp5521_init_device(struct lp5521_chip *chip) usleep_range(1000, 2000); /* 500us abs min. */ } - lp5521_write(client, LP5521_REG_RESET, 0xff); + lp5521_reset_device(chip); + usleep_range(10000, 20000); /* * Exact value is not available. 10 - 20ms * appears to be enough for reset. diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 2fc19bbddb72..cac492b0abf3 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -946,6 +946,13 @@ static void lp5523_unregister_leds(struct lp5523_chip *chip) } } +static void lp5523_reset_device(struct lp5523_chip *chip) +{ + struct i2c_client *client = chip->client; + + lp5523_write(client, LP5523_REG_RESET, 0xff); +} + static int lp5523_init_device(struct lp5523_chip *chip) { struct lp5523_platform_data *pdata = chip->pdata; @@ -965,7 +972,8 @@ static int lp5523_init_device(struct lp5523_chip *chip) usleep_range(1000, 2000); /* 500us abs min. */ } - lp5523_write(client, LP5523_REG_RESET, 0xff); + lp5523_reset_device(chip); + usleep_range(10000, 20000); /* * Exact value is not available. 10 - 20ms * appears to be enough for reset. -- cgit v1.2.3 From f6c64c6fc8d793b414446f1a655a37b7bfce68e3 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 17:58:01 +0900 Subject: leds-lp55xx: do chip specific configuration on device init Chip specific function is configured when the device is initialized. So _configure() is moved to each device init function. If chip configuration gets failed, the device is de-initialized in each _init_device(), not probe(). For compile error fix, function type declarations are added. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 26 +++++++++++++++++--------- drivers/leds/leds-lp5523.c | 26 +++++++++++++++++++------- 2 files changed, 36 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index ec89ed641005..e042a094a07f 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -694,6 +694,7 @@ static void lp5521_reset_device(struct lp5521_chip *chip) lp5521_write(client, LP5521_REG_RESET, 0xff); } +static void lp5521_deinit_device(struct lp5521_chip *chip); static int lp5521_init_device(struct lp5521_chip *chip) { struct lp5521_platform_data *pdata = chip->pdata; @@ -742,9 +743,22 @@ static int lp5521_init_device(struct lp5521_chip *chip) usleep_range(10000, 20000); ret = lp5521_detect(client); - if (ret) + if (ret) { dev_err(&client->dev, "Chip not found\n"); + goto err; + } + + ret = lp5521_configure(client); + if (ret < 0) { + dev_err(&client->dev, "error configuring chip\n"); + goto err_config; + } + return 0; + +err_config: + lp5521_deinit_device(chip); +err: return ret; } @@ -882,16 +896,10 @@ static int lp5521_probe(struct i2c_client *client, ret = lp5521_init_device(chip); if (ret) - goto fail1; + goto err_init; dev_info(&client->dev, "%s programmable led chip found\n", id->name); - ret = lp5521_configure(client); - if (ret < 0) { - dev_err(&client->dev, "error configuring chip\n"); - goto fail1; - } - ret = lp5521_register_leds(chip); if (ret) goto fail2; @@ -904,8 +912,8 @@ static int lp5521_probe(struct i2c_client *client, return ret; fail2: lp5521_unregister_leds(chip); -fail1: lp5521_deinit_device(chip); +err_init: return ret; } diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index cac492b0abf3..fefe27c3f377 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -953,6 +953,7 @@ static void lp5523_reset_device(struct lp5523_chip *chip) lp5523_write(client, LP5523_REG_RESET, 0xff); } +static void lp5523_deinit_device(struct lp5523_chip *chip); static int lp5523_init_device(struct lp5523_chip *chip) { struct lp5523_platform_data *pdata = chip->pdata; @@ -978,7 +979,22 @@ static int lp5523_init_device(struct lp5523_chip *chip) * Exact value is not available. 10 - 20ms * appears to be enough for reset. */ - return lp5523_detect(client); + ret = lp5523_detect(client); + if (ret) + goto err; + + ret = lp5523_configure(client); + if (ret < 0) { + dev_err(&client->dev, "error configuring chip\n"); + goto err_config; + } + + return 0; + +err_config: + lp5523_deinit_device(chip); +err: + return ret; } static void lp5523_deinit_device(struct lp5523_chip *chip) @@ -1018,7 +1034,7 @@ static int lp5523_probe(struct i2c_client *client, ret = lp5523_init_device(chip); if (ret) - goto fail1; + goto err_init; dev_info(&client->dev, "%s Programmable led chip found\n", id->name); @@ -1030,11 +1046,6 @@ static int lp5523_probe(struct i2c_client *client, goto fail1; } } - ret = lp5523_configure(client); - if (ret < 0) { - dev_err(&client->dev, "error configuring chip\n"); - goto fail1; - } ret = lp5523_register_leds(chip, id->name); if (ret) @@ -1050,6 +1061,7 @@ fail2: lp5523_unregister_leds(chip); fail1: lp5523_deinit_device(chip); +err_init: return ret; } -- cgit v1.2.3 From c93d08fa75020835741c7b1d0523ff854e8acde1 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:01:23 +0900 Subject: leds-lp55xx: add new common driver for lp5521/5523 This patch supports basic common driver code for LP5521, LP5523/55231 devices. ( Driver Structure Data ) lp55xx_led and lp55xx_chip In lp55xx common driver, two different data structure is used. o lp55xx_led control multi output LED channels such as led current, channel index. o lp55xx_chip general chip control such like the I2C and platform data. For example, LP5521 has maximum 3 LED channels. LP5523/55231 has 9 output channels. lp55xx_chip for LP5521 ... lp55xx_led #1 lp55xx_led #2 lp55xx_led #3 lp55xx_chip for LP5523 ... lp55xx_led #1 lp55xx_led #2 . . lp55xx_led #9 ( Platform Data ) LP5521 and LP5523/55231 have own specific platform data. However, this data can be handled with just one platform data structure. The lp55xx platform data is declared in the header. This structure is derived from leds-lp5521.h and leds-lp5523.h Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/Kconfig | 9 ++++ drivers/leds/Makefile | 1 + drivers/leds/leds-lp55xx-common.c | 59 +++++++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 61 ++++++++++++++++++++++ include/linux/platform_data/leds-lp55xx.h | 87 +++++++++++++++++++++++++++++++ 5 files changed, 217 insertions(+) create mode 100644 drivers/leds/leds-lp55xx-common.c create mode 100644 drivers/leds/leds-lp55xx-common.h create mode 100644 include/linux/platform_data/leds-lp55xx.h (limited to 'drivers') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index b58bc8a14b9c..3d7822b3498f 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -193,9 +193,17 @@ config LEDS_LP3944 To compile this driver as a module, choose M here: the module will be called leds-lp3944. +config LEDS_LP55XX_COMMON + tristate "Common Driver for TI/National LP5521 and LP5523/55231" + depends on LEDS_LP5521 || LEDS_LP5523 + help + This option supports common operations for LP5521 and LP5523/55231 + devices. + config LEDS_LP5521 tristate "LED Support for N.S. LP5521 LED driver chip" depends on LEDS_CLASS && I2C + select LEDS_LP55XX_COMMON help If you say yes here you get support for the National Semiconductor LP5521 LED driver. It is 3 channel chip with programmable engines. @@ -205,6 +213,7 @@ config LEDS_LP5521 config LEDS_LP5523 tristate "LED Support for TI/National LP5523/55231 LED driver chip" depends on LEDS_CLASS && I2C + select LEDS_LP55XX_COMMON help If you say yes here you get support for TI/National Semiconductor LP5523/55231 LED driver. diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 3fb9641b6194..215e7e3b6173 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o obj-$(CONFIG_LEDS_GPIO_REGISTER) += leds-gpio-register.o obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o +obj-$(CONFIG_LEDS_LP55XX_COMMON) += leds-lp55xx-common.o obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c new file mode 100644 index 000000000000..1c716ecfa817 --- /dev/null +++ b/drivers/leds/leds-lp55xx-common.c @@ -0,0 +1,59 @@ +/* + * LP5521/LP5523/LP55231 Common Driver + * + * Copyright 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim + * + * 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. + * + * Derived from leds-lp5521.c, leds-lp5523.c + */ + +#include +#include +#include +#include + +#include "leds-lp55xx-common.h" + +int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(chip->cl, reg, val); +} +EXPORT_SYMBOL_GPL(lp55xx_write); + +int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val) +{ + s32 ret; + + ret = i2c_smbus_read_byte_data(chip->cl, reg); + if (ret < 0) + return ret; + + *val = ret; + return 0; +} +EXPORT_SYMBOL_GPL(lp55xx_read); + +int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val) +{ + int ret; + u8 tmp; + + ret = lp55xx_read(chip, reg, &tmp); + if (ret) + return ret; + + tmp &= ~mask; + tmp |= val & mask; + + return lp55xx_write(chip, reg, tmp); +} +EXPORT_SYMBOL_GPL(lp55xx_update_bits); + +MODULE_AUTHOR("Milo Kim "); +MODULE_DESCRIPTION("LP55xx Common Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h new file mode 100644 index 000000000000..369cb9c91f17 --- /dev/null +++ b/drivers/leds/leds-lp55xx-common.h @@ -0,0 +1,61 @@ +/* + * LP55XX Common Driver Header + * + * Copyright (C) 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim + * + * 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. + * + * Derived from leds-lp5521.c, leds-lp5523.c + */ + +#ifndef _LEDS_LP55XX_COMMON_H +#define _LEDS_LP55XX_COMMON_H + +struct lp55xx_led; +struct lp55xx_chip; + +/* + * struct lp55xx_chip + * @cl : I2C communication for access registers + * @pdata : Platform specific data + * @lock : Lock for user-space interface + * @num_leds : Number of registered LEDs + */ +struct lp55xx_chip { + struct i2c_client *cl; + struct lp55xx_platform_data *pdata; + struct mutex lock; /* lock for user-space interface */ + int num_leds; +}; + +/* + * struct lp55xx_led + * @chan_nr : Channel number + * @cdev : LED class device + * @led_current : Current setting at each led channel + * @max_current : Maximun current at each led channel + * @brightness_work : Workqueue for brightness control + * @brightness : Brightness value + * @chip : The lp55xx chip data + */ +struct lp55xx_led { + int chan_nr; + struct led_classdev cdev; + u8 led_current; + u8 max_current; + struct work_struct brightness_work; + u8 brightness; + struct lp55xx_chip *chip; +}; + +/* register access */ +extern int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val); +extern int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val); +extern int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, + u8 mask, u8 val); + +#endif /* _LEDS_LP55XX_COMMON_H */ diff --git a/include/linux/platform_data/leds-lp55xx.h b/include/linux/platform_data/leds-lp55xx.h new file mode 100644 index 000000000000..1509570d5a3f --- /dev/null +++ b/include/linux/platform_data/leds-lp55xx.h @@ -0,0 +1,87 @@ +/* + * LP55XX Platform Data Header + * + * Copyright (C) 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim + * + * 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. + * + * Derived from leds-lp5521.h, leds-lp5523.h + */ + +#ifndef _LEDS_LP55XX_H +#define _LEDS_LP55XX_H + +/* Clock configuration */ +#define LP55XX_CLOCK_AUTO 0 +#define LP55XX_CLOCK_INT 1 +#define LP55XX_CLOCK_EXT 2 + +/* Bits in LP5521 CONFIG register. 'update_config' in lp55xx_platform_data */ +#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */ +#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */ +#define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */ +#define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */ +#define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */ +#define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */ +#define LP5521_R_TO_BATT 4 /* R out: 0 = CP, 1 = Vbat */ +#define LP5521_CLK_SRC_EXT 0 /* Ext-clk source (CLK_32K) */ +#define LP5521_CLK_INT 1 /* Internal clock */ +#define LP5521_CLK_AUTO 2 /* Automatic clock selection */ + +struct lp55xx_led_config { + const char *name; + u8 chan_nr; + u8 led_current; /* mA x10, 0 if led is not connected */ + u8 max_current; +}; + +struct lp55xx_predef_pattern { + u8 *r; + u8 *g; + u8 *b; + u8 size_r; + u8 size_g; + u8 size_b; +}; + +/* + * struct lp55xx_platform_data + * @led_config : Configurable led class device + * @num_channels : Number of LED channels + * @label : Used for naming LEDs + * @clock_mode : Input clock mode. LP55XX_CLOCK_AUTO or _INT or _EXT + * @setup_resources : Platform specific function before enabling the chip + * @release_resources : Platform specific function after disabling the chip + * @enable : EN pin control by platform side + * @patterns : Predefined pattern data for RGB channels + * @num_patterns : Number of patterns + * @update_config : Value of CONFIG register + */ +struct lp55xx_platform_data { + + /* LED channel configuration */ + struct lp55xx_led_config *led_config; + u8 num_channels; + const char *label; + + /* Clock configuration */ + u8 clock_mode; + + /* Platform specific functions */ + int (*setup_resources)(void); + void (*release_resources)(void); + void (*enable)(bool state); + + /* Predefined pattern data */ + struct lp55xx_predef_pattern *patterns; + unsigned int num_patterns; + + /* _CONFIG register */ + u8 update_config; +}; + +#endif /* _LEDS_LP55XX_H */ -- cgit v1.2.3 From 945c700746cbfa3375bf88123c2cf6c210f4cc2c Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:02:26 +0900 Subject: leds-lp55xx: replace name of data structure Change the name of chip data structure and platform data. This patch is a preceding step for cleaning up lp5521/5523 probe and remove. These data will be replaced with new lp55xx common data structures in next patch. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 36 ++++++++++++++++++------------------ drivers/leds/leds-lp5523.c | 38 +++++++++++++++++++------------------- 2 files changed, 37 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index e042a094a07f..8ef8f44bf86e 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -872,35 +872,35 @@ static void lp5521_unregister_leds(struct lp5521_chip *chip) static int lp5521_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct lp5521_chip *chip; - struct lp5521_platform_data *pdata; + struct lp5521_chip *old_chip; + struct lp5521_platform_data *old_pdata; int ret; - chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); - if (!chip) + old_chip = devm_kzalloc(&client->dev, sizeof(*old_chip), GFP_KERNEL); + if (!old_chip) return -ENOMEM; - i2c_set_clientdata(client, chip); - chip->client = client; + i2c_set_clientdata(client, old_chip); + old_chip->client = client; - pdata = client->dev.platform_data; + old_pdata = client->dev.platform_data; - if (!pdata) { + if (!old_pdata) { dev_err(&client->dev, "no platform data\n"); return -EINVAL; } - mutex_init(&chip->lock); + mutex_init(&old_chip->lock); - chip->pdata = pdata; + old_chip->pdata = old_pdata; - ret = lp5521_init_device(chip); + ret = lp5521_init_device(old_chip); if (ret) goto err_init; dev_info(&client->dev, "%s programmable led chip found\n", id->name); - ret = lp5521_register_leds(chip); + ret = lp5521_register_leds(old_chip); if (ret) goto fail2; @@ -911,22 +911,22 @@ static int lp5521_probe(struct i2c_client *client, } return ret; fail2: - lp5521_unregister_leds(chip); - lp5521_deinit_device(chip); + lp5521_unregister_leds(old_chip); + lp5521_deinit_device(old_chip); err_init: return ret; } static int lp5521_remove(struct i2c_client *client) { - struct lp5521_chip *chip = i2c_get_clientdata(client); + struct lp5521_chip *old_chip = i2c_get_clientdata(client); - lp5521_run_led_pattern(PATTERN_OFF, chip); + lp5521_run_led_pattern(PATTERN_OFF, old_chip); lp5521_unregister_sysfs(client); - lp5521_unregister_leds(chip); + lp5521_unregister_leds(old_chip); - lp5521_deinit_device(chip); + lp5521_deinit_device(old_chip); return 0; } diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index fefe27c3f377..49b976271d96 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -1010,44 +1010,44 @@ static void lp5523_deinit_device(struct lp5523_chip *chip) static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct lp5523_chip *chip; - struct lp5523_platform_data *pdata; + struct lp5523_chip *old_chip; + struct lp5523_platform_data *old_pdata; int ret, i; - chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); - if (!chip) + old_chip = devm_kzalloc(&client->dev, sizeof(*old_chip), GFP_KERNEL); + if (!old_chip) return -ENOMEM; - i2c_set_clientdata(client, chip); - chip->client = client; + i2c_set_clientdata(client, old_chip); + old_chip->client = client; - pdata = client->dev.platform_data; + old_pdata = client->dev.platform_data; - if (!pdata) { + if (!old_pdata) { dev_err(&client->dev, "no platform data\n"); return -EINVAL; } - mutex_init(&chip->lock); + mutex_init(&old_chip->lock); - chip->pdata = pdata; + old_chip->pdata = old_pdata; - ret = lp5523_init_device(chip); + ret = lp5523_init_device(old_chip); if (ret) goto err_init; dev_info(&client->dev, "%s Programmable led chip found\n", id->name); /* Initialize engines */ - for (i = 0; i < ARRAY_SIZE(chip->engines); i++) { - ret = lp5523_init_engine(&chip->engines[i], i + 1); + for (i = 0; i < ARRAY_SIZE(old_chip->engines); i++) { + ret = lp5523_init_engine(&old_chip->engines[i], i + 1); if (ret) { dev_err(&client->dev, "error initializing engine\n"); goto fail1; } } - ret = lp5523_register_leds(chip, id->name); + ret = lp5523_register_leds(old_chip, id->name); if (ret) goto fail2; @@ -1058,25 +1058,25 @@ static int lp5523_probe(struct i2c_client *client, } return ret; fail2: - lp5523_unregister_leds(chip); + lp5523_unregister_leds(old_chip); fail1: - lp5523_deinit_device(chip); + lp5523_deinit_device(old_chip); err_init: return ret; } static int lp5523_remove(struct i2c_client *client) { - struct lp5523_chip *chip = i2c_get_clientdata(client); + struct lp5523_chip *old_chip = i2c_get_clientdata(client); /* Disable engine mode */ lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_DISABLED); lp5523_unregister_sysfs(client); - lp5523_unregister_leds(chip); + lp5523_unregister_leds(old_chip); - lp5523_deinit_device(chip); + lp5523_deinit_device(old_chip); return 0; } -- cgit v1.2.3 From 6a0c9a47963cc72c68713923ead60d1e72e7136c Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:03:08 +0900 Subject: leds-lp55xx: use common lp55xx data structure in _probe() LP5521 and LP5523 data structures have common features. Use common lp55xx data structures rather than chip specific data. Legacy code in probe is replaced with this new data structures. lp55xx_chip : Common data between lp5521_chip and lp5523_chip lp55xx_led : Common LED structure between lp5521_led and lp5523_led lp55xx_platform_data : Common platform data between lp5521_platform_data and lp5523_platform_data Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 36 ++++++++++++++++++++++-------------- drivers/leds/leds-lp5523.c | 36 ++++++++++++++++++++++-------------- 2 files changed, 44 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 8ef8f44bf86e..1eab1557a612 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -34,6 +34,9 @@ #include #include #include +#include + +#include "leds-lp55xx-common.h" #define LP5521_PROGRAM_LENGTH 32 /* in bytes */ @@ -872,27 +875,32 @@ static void lp5521_unregister_leds(struct lp5521_chip *chip) static int lp5521_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct lp5521_chip *old_chip; - struct lp5521_platform_data *old_pdata; + struct lp5521_chip *old_chip = NULL; int ret; + struct lp55xx_chip *chip; + struct lp55xx_led *led; + struct lp55xx_platform_data *pdata = client->dev.platform_data; - old_chip = devm_kzalloc(&client->dev, sizeof(*old_chip), GFP_KERNEL); - if (!old_chip) - return -ENOMEM; - - i2c_set_clientdata(client, old_chip); - old_chip->client = client; - - old_pdata = client->dev.platform_data; - - if (!old_pdata) { + if (!pdata) { dev_err(&client->dev, "no platform data\n"); return -EINVAL; } - mutex_init(&old_chip->lock); + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + led = devm_kzalloc(&client->dev, + sizeof(*led) * pdata->num_channels, GFP_KERNEL); + if (!led) + return -ENOMEM; + + chip->cl = client; + chip->pdata = pdata; + + mutex_init(&chip->lock); - old_chip->pdata = old_pdata; + i2c_set_clientdata(client, led); ret = lp5521_init_device(old_chip); if (ret) diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 49b976271d96..71bda3670ff0 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -34,6 +34,9 @@ #include #include #include +#include + +#include "leds-lp55xx-common.h" #define LP5523_REG_ENABLE 0x00 #define LP5523_REG_OP_MODE 0x01 @@ -1010,27 +1013,32 @@ static void lp5523_deinit_device(struct lp5523_chip *chip) static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct lp5523_chip *old_chip; - struct lp5523_platform_data *old_pdata; + struct lp5523_chip *old_chip = NULL; int ret, i; + struct lp55xx_chip *chip; + struct lp55xx_led *led; + struct lp55xx_platform_data *pdata = client->dev.platform_data; - old_chip = devm_kzalloc(&client->dev, sizeof(*old_chip), GFP_KERNEL); - if (!old_chip) - return -ENOMEM; - - i2c_set_clientdata(client, old_chip); - old_chip->client = client; - - old_pdata = client->dev.platform_data; - - if (!old_pdata) { + if (!pdata) { dev_err(&client->dev, "no platform data\n"); return -EINVAL; } - mutex_init(&old_chip->lock); + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + led = devm_kzalloc(&client->dev, + sizeof(*led) * pdata->num_channels, GFP_KERNEL); + if (!led) + return -ENOMEM; + + chip->cl = client; + chip->pdata = pdata; + + mutex_init(&chip->lock); - old_chip->pdata = old_pdata; + i2c_set_clientdata(client, led); ret = lp5523_init_device(old_chip); if (ret) -- cgit v1.2.3 From 9448217403462c4b17bc56690a0348a0c02e5ba2 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:03:55 +0900 Subject: leds-lp5521: clean up lp5521_configure() This patch is a preceding step for making common lp55xx init function. LP5521_REG_R_CURRENT register code moved: Chip specific code moved from lp5521_init_device() to lp5521_configure(). Remove engine init function: LP5521 has internal program engines which are used for running LED patterns. (blinking, ramp up/down and other emotional visual effects) Engine initialization is done by reset command in lp5521_init_device(). Remove this duplicate code. Return code: Do not use 'OR' arithmetic for the result. If some error occus, just return it. Enable latency: Use explicit named function, lp5521_wait_enable_done(). According to the datasheet, 500us is guaranteed time. Thus wait time is changed from 1000us to 500us. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 78 ++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 1eab1557a612..341a41030fd8 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -125,6 +125,12 @@ struct lp5521_chip { u8 num_leds; }; +static inline void lp5521_wait_enable_done(void) +{ + /* it takes more 488 us to update ENABLE register */ + usleep_range(500, 600); +} + static inline struct lp5521_led *cdev_to_led(struct led_classdev *cdev) { return container_of(cdev, struct lp5521_led, cdev); @@ -229,43 +235,56 @@ static int lp5521_set_led_current(struct lp5521_chip *chip, int led, u8 curr) curr); } -static void lp5521_init_engine(struct lp5521_chip *chip) -{ - int i; - for (i = 0; i < ARRAY_SIZE(chip->engines); i++) { - chip->engines[i].id = i + 1; - chip->engines[i].engine_mask = LP5521_ENG_MASK_BASE >> (i * 2); - chip->engines[i].prog_page = i; - } -} - static int lp5521_configure(struct i2c_client *client) { struct lp5521_chip *chip = i2c_get_clientdata(client); int ret; u8 cfg; + u8 val; - lp5521_init_engine(chip); + /* + * Make sure that the chip is reset by reading back the r channel + * current reg. This is dummy read is required on some platforms - + * otherwise further access to the R G B channels in the + * LP5521_REG_ENABLE register will not have any effect - strange! + */ + ret = lp5521_read(client, LP5521_REG_R_CURRENT, &val); + if (ret) { + dev_err(&client->dev, "error in resetting chip\n"); + return ret; + } + if (val != LP5521_REG_R_CURR_DEFAULT) { + dev_err(&client->dev, + "unexpected data in register (expected 0x%x got 0x%x)\n", + LP5521_REG_R_CURR_DEFAULT, val); + ret = -EINVAL; + return ret; + } + usleep_range(10000, 20000); /* Set all PWMs to direct control mode */ ret = lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); cfg = chip->pdata->update_config ? : (LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT); - ret |= lp5521_write(client, LP5521_REG_CONFIG, cfg); + ret = lp5521_write(client, LP5521_REG_CONFIG, cfg); + if (ret) + return ret; /* Initialize all channels PWM to zero -> leds off */ - ret |= lp5521_write(client, LP5521_REG_R_PWM, 0); - ret |= lp5521_write(client, LP5521_REG_G_PWM, 0); - ret |= lp5521_write(client, LP5521_REG_B_PWM, 0); + lp5521_write(client, LP5521_REG_R_PWM, 0); + lp5521_write(client, LP5521_REG_G_PWM, 0); + lp5521_write(client, LP5521_REG_B_PWM, 0); /* Set engines are set to run state when OP_MODE enables engines */ - ret |= lp5521_write(client, LP5521_REG_ENABLE, + ret = lp5521_write(client, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM); - /* enable takes 500us. 1 - 2 ms leaves some margin */ - usleep_range(1000, 2000); + if (ret) + return ret; - return ret; + lp5521_wait_enable_done(); + + return 0; } static int lp5521_run_selftest(struct lp5521_chip *chip, char *buf) @@ -703,7 +722,6 @@ static int lp5521_init_device(struct lp5521_chip *chip) struct lp5521_platform_data *pdata = chip->pdata; struct i2c_client *client = chip->client; int ret; - u8 buf; if (pdata->setup_resources) { ret = pdata->setup_resources(); @@ -725,26 +743,6 @@ static int lp5521_init_device(struct lp5521_chip *chip) * appears to be enough for reset. */ - /* - * Make sure that the chip is reset by reading back the r channel - * current reg. This is dummy read is required on some platforms - - * otherwise further access to the R G B channels in the - * LP5521_REG_ENABLE register will not have any effect - strange! - */ - ret = lp5521_read(client, LP5521_REG_R_CURRENT, &buf); - if (ret) { - dev_err(&client->dev, "error in resetting chip\n"); - return ret; - } - if (buf != LP5521_REG_R_CURR_DEFAULT) { - dev_err(&client->dev, - "unexpected data in register (expected 0x%x got 0x%x)\n", - LP5521_REG_R_CURR_DEFAULT, buf); - ret = -EINVAL; - return ret; - } - usleep_range(10000, 20000); - ret = lp5521_detect(client); if (ret) { dev_err(&client->dev, "Chip not found\n"); -- cgit v1.2.3 From 632418bf65503405df3f9a6a1616f5a95f91db85 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:04:50 +0900 Subject: leds-lp5523: clean up lp5523_configure() This patch is a preceding step for making common lp55xx init function. Return code: Do not use 'OR' arithmetic for the result. If some error occurs, just return it. Remove engine verification code: To check whether internal engine works or not, many lines of code are executed. However, this job is unnecessary during the chip initialization because the engine usage is not mandatory but optional function. LED engines are enabled when specific LED pattern is loaded. Therefore, this verification code is removed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5523.c | 67 +++++++--------------------------------------- 1 file changed, 9 insertions(+), 58 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 71bda3670ff0..2ca41c5af719 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -199,77 +199,28 @@ static int lp5523_detect(struct i2c_client *client) static int lp5523_configure(struct i2c_client *client) { - struct lp5523_chip *chip = i2c_get_clientdata(client); int ret = 0; - u8 status; - /* one pattern per engine setting led mux start and stop addresses */ - static const u8 pattern[][LP5523_PROGRAM_LENGTH] = { - { 0x9c, 0x30, 0x9c, 0xb0, 0x9d, 0x80, 0xd8, 0x00, 0}, - { 0x9c, 0x40, 0x9c, 0xc0, 0x9d, 0x80, 0xd8, 0x00, 0}, - { 0x9c, 0x50, 0x9c, 0xd0, 0x9d, 0x80, 0xd8, 0x00, 0}, - }; + ret = lp5523_write(client, LP5523_REG_ENABLE, LP5523_ENABLE); + if (ret) + return ret; - ret |= lp5523_write(client, LP5523_REG_ENABLE, LP5523_ENABLE); /* Chip startup time is 500 us, 1 - 2 ms gives some margin */ usleep_range(1000, 2000); - ret |= lp5523_write(client, LP5523_REG_CONFIG, + ret = lp5523_write(client, LP5523_REG_CONFIG, LP5523_AUTO_INC | LP5523_PWR_SAVE | LP5523_CP_AUTO | LP5523_AUTO_CLK | LP5523_PWM_PWR_SAVE); + if (ret) + return ret; /* turn on all leds */ - ret |= lp5523_write(client, LP5523_REG_ENABLE_LEDS_MSB, 0x01); - ret |= lp5523_write(client, LP5523_REG_ENABLE_LEDS_LSB, 0xff); - - /* hardcode 32 bytes of memory for each engine from program memory */ - ret |= lp5523_write(client, LP5523_REG_CH1_PROG_START, 0x00); - ret |= lp5523_write(client, LP5523_REG_CH2_PROG_START, 0x10); - ret |= lp5523_write(client, LP5523_REG_CH3_PROG_START, 0x20); - - /* write led mux address space for each channel */ - ret |= lp5523_load_program(&chip->engines[0], pattern[0]); - ret |= lp5523_load_program(&chip->engines[1], pattern[1]); - ret |= lp5523_load_program(&chip->engines[2], pattern[2]); - - if (ret) { - dev_err(&client->dev, "could not load mux programs\n"); - return -1; - } - - /* set all engines exec state and mode to run 00101010 */ - ret |= lp5523_write(client, LP5523_REG_ENABLE, - (LP5523_CMD_RUN | LP5523_ENABLE)); - - ret |= lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_RUN); - - if (ret) { - dev_err(&client->dev, "could not start mux programs\n"); - return -1; - } - - /* Let the programs run for couple of ms and check the engine status */ - usleep_range(3000, 6000); - ret = lp5523_read(client, LP5523_REG_STATUS, &status); - if (ret < 0) + ret = lp5523_write(client, LP5523_REG_ENABLE_LEDS_MSB, 0x01); + if (ret) return ret; - status &= LP5523_ENG_STATUS_MASK; - - if (status == LP5523_ENG_STATUS_MASK) { - dev_dbg(&client->dev, "all engines configured\n"); - } else { - dev_info(&client->dev, "status == %x\n", status); - dev_err(&client->dev, "cound not configure LED engine\n"); - return -1; - } - - dev_info(&client->dev, "disabling engines\n"); - - ret |= lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_DISABLED); - - return ret; + return lp5523_write(client, LP5523_REG_ENABLE_LEDS_LSB, 0xff); } static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode) -- cgit v1.2.3 From a85908dd7799e4fa242812ce27a8f774c721d1fb Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:07:20 +0900 Subject: leds-lp55xx: use lp55xx common init function - platform data LP5521/5523 platform data functions are moved to lp55xx common driver. New init function, lp55xx_init_device() is created. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 14 -------------- drivers/leds/leds-lp5523.c | 14 -------------- drivers/leds/leds-lp55xx-common.c | 34 ++++++++++++++++++++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 3 +++ 4 files changed, 37 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 341a41030fd8..124ce80fa115 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -719,23 +719,9 @@ static void lp5521_reset_device(struct lp5521_chip *chip) static void lp5521_deinit_device(struct lp5521_chip *chip); static int lp5521_init_device(struct lp5521_chip *chip) { - struct lp5521_platform_data *pdata = chip->pdata; struct i2c_client *client = chip->client; int ret; - if (pdata->setup_resources) { - ret = pdata->setup_resources(); - if (ret < 0) - return ret; - } - - if (pdata->enable) { - pdata->enable(0); - usleep_range(1000, 2000); /* Keep enable down at least 1ms */ - pdata->enable(1); - usleep_range(1000, 2000); /* 500us abs min. */ - } - lp5521_reset_device(chip); usleep_range(10000, 20000); /* diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 2ca41c5af719..8e602047ce35 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -910,23 +910,9 @@ static void lp5523_reset_device(struct lp5523_chip *chip) static void lp5523_deinit_device(struct lp5523_chip *chip); static int lp5523_init_device(struct lp5523_chip *chip) { - struct lp5523_platform_data *pdata = chip->pdata; struct i2c_client *client = chip->client; int ret; - if (pdata->setup_resources) { - ret = pdata->setup_resources(); - if (ret < 0) - return ret; - } - - if (pdata->enable) { - pdata->enable(0); - usleep_range(1000, 2000); /* Keep enable down at least 1ms */ - pdata->enable(1); - usleep_range(1000, 2000); /* 500us abs min. */ - } - lp5523_reset_device(chip); usleep_range(10000, 20000); /* diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 1c716ecfa817..05a854c0d9b2 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -12,6 +12,7 @@ * Derived from leds-lp5521.c, leds-lp5523.c */ +#include #include #include #include @@ -54,6 +55,39 @@ int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val) } EXPORT_SYMBOL_GPL(lp55xx_update_bits); +int lp55xx_init_device(struct lp55xx_chip *chip) +{ + struct lp55xx_platform_data *pdata; + struct device *dev = &chip->cl->dev; + int ret = 0; + + WARN_ON(!chip); + + pdata = chip->pdata; + + if (!pdata) + return -EINVAL; + + if (pdata->setup_resources) { + ret = pdata->setup_resources(); + if (ret < 0) { + dev_err(dev, "setup resoure err: %d\n", ret); + goto err; + } + } + + if (pdata->enable) { + pdata->enable(0); + usleep_range(1000, 2000); /* Keep enable down at least 1ms */ + pdata->enable(1); + usleep_range(1000, 2000); /* 500us abs min. */ + } + +err: + return ret; +} +EXPORT_SYMBOL_GPL(lp55xx_init_device); + MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP55xx Common Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 369cb9c91f17..09d1882ce58e 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -58,4 +58,7 @@ extern int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val); extern int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val); +/* common device init functions */ +extern int lp55xx_init_device(struct lp55xx_chip *chip); + #endif /* _LEDS_LP55XX_COMMON_H */ -- cgit v1.2.3 From 48068d5de16c23c256c085b2cd3ff03bec393900 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:08:49 +0900 Subject: leds-lp55xx: use lp55xx common init function - reset LP5521/5523 reset device functions are moved to lp55xx common driver. Value of register address and value are chip dependent. Those are configured in each driver. In init function, reset command is executed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 26 ++++++++++++-------------- drivers/leds/leds-lp5523.c | 23 ++++++++++------------- drivers/leds/leds-lp55xx-common.c | 22 +++++++++++++++++++++- drivers/leds/leds-lp55xx-common.h | 20 ++++++++++++++++++++ 4 files changed, 63 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 124ce80fa115..e1f1dfcd1547 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -98,6 +98,9 @@ /* Pattern Mode */ #define PATTERN_OFF 0 +/* Reset register value */ +#define LP5521_RESET 0xFF + struct lp5521_engine { int id; u8 mode; @@ -709,26 +712,12 @@ static void lp5521_unregister_sysfs(struct i2c_client *client) &lp5521_led_attribute_group); } -static void lp5521_reset_device(struct lp5521_chip *chip) -{ - struct i2c_client *client = chip->client; - - lp5521_write(client, LP5521_REG_RESET, 0xff); -} - static void lp5521_deinit_device(struct lp5521_chip *chip); static int lp5521_init_device(struct lp5521_chip *chip) { struct i2c_client *client = chip->client; int ret; - lp5521_reset_device(chip); - - usleep_range(10000, 20000); /* - * Exact value is not available. 10 - 20ms - * appears to be enough for reset. - */ - ret = lp5521_detect(client); if (ret) { dev_err(&client->dev, "Chip not found\n"); @@ -856,6 +845,14 @@ static void lp5521_unregister_leds(struct lp5521_chip *chip) } } +/* Chip specific configurations */ +static struct lp55xx_device_config lp5521_cfg = { + .reset = { + .addr = LP5521_REG_RESET, + .val = LP5521_RESET, + }, +}; + static int lp5521_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -881,6 +878,7 @@ static int lp5521_probe(struct i2c_client *client, chip->cl = client; chip->pdata = pdata; + chip->cfg = &lp5521_cfg; mutex_init(&chip->lock); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 8e602047ce35..00547783db77 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -87,6 +87,7 @@ #define LP5523_AUTO_CLK 0x02 #define LP5523_EN_LEDTEST 0x80 #define LP5523_LEDTEST_DONE 0x80 +#define LP5523_RESET 0xFF #define LP5523_DEFAULT_CURRENT 50 /* microAmps */ #define LP5523_PROGRAM_LENGTH 32 /* in bytes */ @@ -900,25 +901,12 @@ static void lp5523_unregister_leds(struct lp5523_chip *chip) } } -static void lp5523_reset_device(struct lp5523_chip *chip) -{ - struct i2c_client *client = chip->client; - - lp5523_write(client, LP5523_REG_RESET, 0xff); -} - static void lp5523_deinit_device(struct lp5523_chip *chip); static int lp5523_init_device(struct lp5523_chip *chip) { struct i2c_client *client = chip->client; int ret; - lp5523_reset_device(chip); - - usleep_range(10000, 20000); /* - * Exact value is not available. 10 - 20ms - * appears to be enough for reset. - */ ret = lp5523_detect(client); if (ret) goto err; @@ -947,6 +935,14 @@ static void lp5523_deinit_device(struct lp5523_chip *chip) pdata->release_resources(); } +/* Chip specific configurations */ +static struct lp55xx_device_config lp5523_cfg = { + .reset = { + .addr = LP5523_REG_RESET, + .val = LP5523_RESET, + }, +}; + static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -972,6 +968,7 @@ static int lp5523_probe(struct i2c_client *client, chip->cl = client; chip->pdata = pdata; + chip->cfg = &lp5523_cfg; mutex_init(&chip->lock); diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 05a854c0d9b2..bbf2bbf03807 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -20,6 +20,16 @@ #include "leds-lp55xx-common.h" +static void lp55xx_reset_device(struct lp55xx_chip *chip) +{ + struct lp55xx_device_config *cfg = chip->cfg; + u8 addr = cfg->reset.addr; + u8 val = cfg->reset.val; + + /* no error checking here because no ACK from the device after reset */ + lp55xx_write(chip, addr, val); +} + int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val) { return i2c_smbus_write_byte_data(chip->cl, reg, val); @@ -58,14 +68,16 @@ EXPORT_SYMBOL_GPL(lp55xx_update_bits); int lp55xx_init_device(struct lp55xx_chip *chip) { struct lp55xx_platform_data *pdata; + struct lp55xx_device_config *cfg; struct device *dev = &chip->cl->dev; int ret = 0; WARN_ON(!chip); pdata = chip->pdata; + cfg = chip->cfg; - if (!pdata) + if (!pdata || !cfg) return -EINVAL; if (pdata->setup_resources) { @@ -83,6 +95,14 @@ int lp55xx_init_device(struct lp55xx_chip *chip) usleep_range(1000, 2000); /* 500us abs min. */ } + lp55xx_reset_device(chip); + + /* + * Exact value is not available. 10 - 20ms + * appears to be enough for reset. + */ + usleep_range(10000, 20000); + err: return ret; } diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 09d1882ce58e..a73ee0b9a0bd 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -18,18 +18,38 @@ struct lp55xx_led; struct lp55xx_chip; +/* + * struct lp55xx_reg + * @addr : Register address + * @val : Register value + */ +struct lp55xx_reg { + u8 addr; + u8 val; +}; + +/* + * struct lp55xx_device_config + * @reset : Chip specific reset command + */ +struct lp55xx_device_config { + const struct lp55xx_reg reset; +}; + /* * struct lp55xx_chip * @cl : I2C communication for access registers * @pdata : Platform specific data * @lock : Lock for user-space interface * @num_leds : Number of registered LEDs + * @cfg : Device specific configuration data */ struct lp55xx_chip { struct i2c_client *cl; struct lp55xx_platform_data *pdata; struct mutex lock; /* lock for user-space interface */ int num_leds; + struct lp55xx_device_config *cfg; }; /* -- cgit v1.2.3 From e3a700d8aae190e09fb06abe0ddd2e172a682508 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:09:56 +0900 Subject: leds-lp55xx: use lp55xx common init function - detect LP5521/5523 chip detection functions are replaced with lp55xx common function, lp55xx_detect_device(). Chip dependent address and values are configurable in each driver. In init function, chip detection is executed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 31 ++++--------------------------- drivers/leds/leds-lp5523.c | 26 ++++---------------------- drivers/leds/leds-lp55xx-common.c | 29 +++++++++++++++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 2 ++ 4 files changed, 39 insertions(+), 49 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index e1f1dfcd1547..0e27f7eb5d09 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -328,26 +328,6 @@ static void lp5521_led_brightness_work(struct work_struct *work) mutex_unlock(&chip->lock); } -/* Detect the chip by setting its ENABLE register and reading it back. */ -static int lp5521_detect(struct i2c_client *client) -{ - int ret; - u8 buf; - - ret = lp5521_write(client, LP5521_REG_ENABLE, LP5521_ENABLE_DEFAULT); - if (ret) - return ret; - /* enable takes 500us. 1 - 2 ms leaves some margin */ - usleep_range(1000, 2000); - ret = lp5521_read(client, LP5521_REG_ENABLE, &buf); - if (ret) - return ret; - if (buf != LP5521_ENABLE_DEFAULT) - return -ENODEV; - - return 0; -} - /* Set engine mode and create appropriate sysfs attributes, if required. */ static int lp5521_set_mode(struct lp5521_engine *engine, u8 mode) { @@ -718,12 +698,6 @@ static int lp5521_init_device(struct lp5521_chip *chip) struct i2c_client *client = chip->client; int ret; - ret = lp5521_detect(client); - if (ret) { - dev_err(&client->dev, "Chip not found\n"); - goto err; - } - ret = lp5521_configure(client); if (ret < 0) { dev_err(&client->dev, "error configuring chip\n"); @@ -734,7 +708,6 @@ static int lp5521_init_device(struct lp5521_chip *chip) err_config: lp5521_deinit_device(chip); -err: return ret; } @@ -851,6 +824,10 @@ static struct lp55xx_device_config lp5521_cfg = { .addr = LP5521_REG_RESET, .val = LP5521_RESET, }, + .enable = { + .addr = LP5521_REG_ENABLE, + .val = LP5521_ENABLE_DEFAULT, + }, }; static int lp5521_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 00547783db77..b3698e85d51f 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -181,23 +181,6 @@ static int lp5523_read(struct i2c_client *client, u8 reg, u8 *buf) return 0; } -static int lp5523_detect(struct i2c_client *client) -{ - int ret; - u8 buf; - - ret = lp5523_write(client, LP5523_REG_ENABLE, LP5523_ENABLE); - if (ret) - return ret; - ret = lp5523_read(client, LP5523_REG_ENABLE, &buf); - if (ret) - return ret; - if (buf == 0x40) - return 0; - else - return -ENODEV; -} - static int lp5523_configure(struct i2c_client *client) { int ret = 0; @@ -907,10 +890,6 @@ static int lp5523_init_device(struct lp5523_chip *chip) struct i2c_client *client = chip->client; int ret; - ret = lp5523_detect(client); - if (ret) - goto err; - ret = lp5523_configure(client); if (ret < 0) { dev_err(&client->dev, "error configuring chip\n"); @@ -921,7 +900,6 @@ static int lp5523_init_device(struct lp5523_chip *chip) err_config: lp5523_deinit_device(chip); -err: return ret; } @@ -941,6 +919,10 @@ static struct lp55xx_device_config lp5523_cfg = { .addr = LP5523_REG_RESET, .val = LP5523_RESET, }, + .enable = { + .addr = LP5523_REG_ENABLE, + .val = LP5523_ENABLE, + }, }; static int lp5523_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index bbf2bbf03807..6fede0b96715 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -30,6 +30,29 @@ static void lp55xx_reset_device(struct lp55xx_chip *chip) lp55xx_write(chip, addr, val); } +static int lp55xx_detect_device(struct lp55xx_chip *chip) +{ + struct lp55xx_device_config *cfg = chip->cfg; + u8 addr = cfg->enable.addr; + u8 val = cfg->enable.val; + int ret; + + ret = lp55xx_write(chip, addr, val); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = lp55xx_read(chip, addr, &val); + if (ret) + return ret; + + if (val != cfg->enable.val) + return -ENODEV; + + return 0; +} + int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val) { return i2c_smbus_write_byte_data(chip->cl, reg, val); @@ -103,6 +126,12 @@ int lp55xx_init_device(struct lp55xx_chip *chip) */ usleep_range(10000, 20000); + ret = lp55xx_detect_device(chip); + if (ret) { + dev_err(dev, "device detection err: %d\n", ret); + goto err; + } + err: return ret; } diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index a73ee0b9a0bd..81753012ba27 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -31,9 +31,11 @@ struct lp55xx_reg { /* * struct lp55xx_device_config * @reset : Chip specific reset command + * @enable : Chip specific enable command */ struct lp55xx_device_config { const struct lp55xx_reg reset; + const struct lp55xx_reg enable; }; /* -- cgit v1.2.3 From ffbdccdbbaee814963a09d25b1cc598cfe131366 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:57:36 +0900 Subject: leds-lp55xx: use lp55xx common init function - post int LP5521/5523 chip configuration is replaced with lp55xx common function, lp55xx_post_init_device(). Name change: lp5521/5523_configure() to lp5521/5523_post_init_device() These are called in init function. Register access function Argument type is changed from 'i2c_client' to 'lp55xx_chip'. Use exported R/W functions of lp55xx common driver. Temporary variables in lp5521/5523_init_device() These functions will be removed but temporary variables are needed for blocking build warnings - incompatible pointer. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 29 ++++++++++++++--------------- drivers/leds/leds-lp5523.c | 16 +++++++++------- drivers/leds/leds-lp55xx-common.c | 15 +++++++++++++++ drivers/leds/leds-lp55xx-common.h | 4 ++++ 4 files changed, 42 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 0e27f7eb5d09..faab44900c23 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -238,11 +238,9 @@ static int lp5521_set_led_current(struct lp5521_chip *chip, int led, u8 curr) curr); } -static int lp5521_configure(struct i2c_client *client) +static int lp5521_post_init_device(struct lp55xx_chip *chip) { - struct lp5521_chip *chip = i2c_get_clientdata(client); int ret; - u8 cfg; u8 val; /* @@ -251,13 +249,13 @@ static int lp5521_configure(struct i2c_client *client) * otherwise further access to the R G B channels in the * LP5521_REG_ENABLE register will not have any effect - strange! */ - ret = lp5521_read(client, LP5521_REG_R_CURRENT, &val); + ret = lp55xx_read(chip, LP5521_REG_R_CURRENT, &val); if (ret) { - dev_err(&client->dev, "error in resetting chip\n"); + dev_err(&chip->cl->dev, "error in resetting chip\n"); return ret; } if (val != LP5521_REG_R_CURR_DEFAULT) { - dev_err(&client->dev, + dev_err(&chip->cl->dev, "unexpected data in register (expected 0x%x got 0x%x)\n", LP5521_REG_R_CURR_DEFAULT, val); ret = -EINVAL; @@ -266,22 +264,21 @@ static int lp5521_configure(struct i2c_client *client) usleep_range(10000, 20000); /* Set all PWMs to direct control mode */ - ret = lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); + ret = lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); - cfg = chip->pdata->update_config ? + val = chip->pdata->update_config ? : (LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT); - ret = lp5521_write(client, LP5521_REG_CONFIG, cfg); + ret = lp55xx_write(chip, LP5521_REG_CONFIG, val); if (ret) return ret; /* Initialize all channels PWM to zero -> leds off */ - lp5521_write(client, LP5521_REG_R_PWM, 0); - lp5521_write(client, LP5521_REG_G_PWM, 0); - lp5521_write(client, LP5521_REG_B_PWM, 0); + lp55xx_write(chip, LP5521_REG_R_PWM, 0); + lp55xx_write(chip, LP5521_REG_G_PWM, 0); + lp55xx_write(chip, LP5521_REG_B_PWM, 0); /* Set engines are set to run state when OP_MODE enables engines */ - ret = lp5521_write(client, LP5521_REG_ENABLE, - LP5521_ENABLE_RUN_PROGRAM); + ret = lp55xx_write(chip, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM); if (ret) return ret; @@ -696,9 +693,10 @@ static void lp5521_deinit_device(struct lp5521_chip *chip); static int lp5521_init_device(struct lp5521_chip *chip) { struct i2c_client *client = chip->client; + struct lp55xx_chip *temp; int ret; - ret = lp5521_configure(client); + ret = lp5521_post_init_device(temp); if (ret < 0) { dev_err(&client->dev, "error configuring chip\n"); goto err_config; @@ -828,6 +826,7 @@ static struct lp55xx_device_config lp5521_cfg = { .addr = LP5521_REG_ENABLE, .val = LP5521_ENABLE_DEFAULT, }, + .post_init_device = lp5521_post_init_device, }; static int lp5521_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index b3698e85d51f..110565b066e2 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -181,18 +181,18 @@ static int lp5523_read(struct i2c_client *client, u8 reg, u8 *buf) return 0; } -static int lp5523_configure(struct i2c_client *client) +static int lp5523_post_init_device(struct lp55xx_chip *chip) { - int ret = 0; + int ret; - ret = lp5523_write(client, LP5523_REG_ENABLE, LP5523_ENABLE); + ret = lp55xx_write(chip, LP5523_REG_ENABLE, LP5523_ENABLE); if (ret) return ret; /* Chip startup time is 500 us, 1 - 2 ms gives some margin */ usleep_range(1000, 2000); - ret = lp5523_write(client, LP5523_REG_CONFIG, + ret = lp55xx_write(chip, LP5523_REG_CONFIG, LP5523_AUTO_INC | LP5523_PWR_SAVE | LP5523_CP_AUTO | LP5523_AUTO_CLK | LP5523_PWM_PWR_SAVE); @@ -200,11 +200,11 @@ static int lp5523_configure(struct i2c_client *client) return ret; /* turn on all leds */ - ret = lp5523_write(client, LP5523_REG_ENABLE_LEDS_MSB, 0x01); + ret = lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_MSB, 0x01); if (ret) return ret; - return lp5523_write(client, LP5523_REG_ENABLE_LEDS_LSB, 0xff); + return lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_LSB, 0xff); } static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode) @@ -888,9 +888,10 @@ static void lp5523_deinit_device(struct lp5523_chip *chip); static int lp5523_init_device(struct lp5523_chip *chip) { struct i2c_client *client = chip->client; + struct lp55xx_chip *temp; int ret; - ret = lp5523_configure(client); + ret = lp5523_post_init_device(temp); if (ret < 0) { dev_err(&client->dev, "error configuring chip\n"); goto err_config; @@ -923,6 +924,7 @@ static struct lp55xx_device_config lp5523_cfg = { .addr = LP5523_REG_ENABLE, .val = LP5523_ENABLE, }, + .post_init_device = lp5523_post_init_device, }; static int lp5523_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 6fede0b96715..74beb363b787 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -53,6 +53,16 @@ static int lp55xx_detect_device(struct lp55xx_chip *chip) return 0; } +static int lp55xx_post_init_device(struct lp55xx_chip *chip) +{ + struct lp55xx_device_config *cfg = chip->cfg; + + if (!cfg->post_init_device) + return 0; + + return cfg->post_init_device(chip); +} + int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val) { return i2c_smbus_write_byte_data(chip->cl, reg, val); @@ -132,6 +142,11 @@ int lp55xx_init_device(struct lp55xx_chip *chip) goto err; } + /* chip specific initialization */ + ret = lp55xx_post_init_device(chip); + + return 0; + err: return ret; } diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 81753012ba27..ffedc7723d84 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -32,10 +32,14 @@ struct lp55xx_reg { * struct lp55xx_device_config * @reset : Chip specific reset command * @enable : Chip specific enable command + * @post_init_device : Chip specific initialization code */ struct lp55xx_device_config { const struct lp55xx_reg reset; const struct lp55xx_reg enable; + + /* define if the device has specific initialization process */ + int (*post_init_device) (struct lp55xx_chip *chip); }; /* -- cgit v1.2.3 From 22ebeb488b3dbbb64b81146b366551107ae34af8 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:58:35 +0900 Subject: leds-lp55xx: clean up init function lp5521/5523_init_device() are replaced with lp55xx common function, lp55xx_init_device(). Error handler in init_device: deinit function are matched with 'err_post_init' section in lp55xx_init_device(). Remove LP5523 engine intialization code: Engine functionality is not mandatory but optional. Moreover engine initialization is done internally with device reset command. Therefore, this code is unnecessary. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 22 +------------------ drivers/leds/leds-lp5523.c | 46 ++------------------------------------- drivers/leds/leds-lp55xx-common.c | 9 ++++++++ 3 files changed, 12 insertions(+), 65 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index faab44900c23..74dc208fb99f 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -689,26 +689,6 @@ static void lp5521_unregister_sysfs(struct i2c_client *client) &lp5521_led_attribute_group); } -static void lp5521_deinit_device(struct lp5521_chip *chip); -static int lp5521_init_device(struct lp5521_chip *chip) -{ - struct i2c_client *client = chip->client; - struct lp55xx_chip *temp; - int ret; - - ret = lp5521_post_init_device(temp); - if (ret < 0) { - dev_err(&client->dev, "error configuring chip\n"); - goto err_config; - } - - return 0; - -err_config: - lp5521_deinit_device(chip); - return ret; -} - static void lp5521_deinit_device(struct lp5521_chip *chip) { struct lp5521_platform_data *pdata = chip->pdata; @@ -860,7 +840,7 @@ static int lp5521_probe(struct i2c_client *client, i2c_set_clientdata(client, led); - ret = lp5521_init_device(old_chip); + ret = lp55xx_init_device(chip); if (ret) goto err_init; diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 110565b066e2..80b7fb4a3ad6 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -773,18 +773,6 @@ static void lp5523_set_mode(struct lp5523_engine *engine, u8 mode) /*--------------------------------------------------------------*/ /* Probe, Attach, Remove */ /*--------------------------------------------------------------*/ -static int __init lp5523_init_engine(struct lp5523_engine *engine, int id) -{ - if (id < 1 || id > LP5523_ENGINES) - return -1; - engine->id = id; - engine->engine_mask = LP5523_ENG_MASK_BASE >> SHIFT_MASK(id); - engine->prog_page = id - 1; - engine->mux_page = id + 2; - - return 0; -} - static int lp5523_init_led(struct lp5523_led *led, struct device *dev, int chan, struct lp5523_platform_data *pdata, const char *chip_name) @@ -884,26 +872,6 @@ static void lp5523_unregister_leds(struct lp5523_chip *chip) } } -static void lp5523_deinit_device(struct lp5523_chip *chip); -static int lp5523_init_device(struct lp5523_chip *chip) -{ - struct i2c_client *client = chip->client; - struct lp55xx_chip *temp; - int ret; - - ret = lp5523_post_init_device(temp); - if (ret < 0) { - dev_err(&client->dev, "error configuring chip\n"); - goto err_config; - } - - return 0; - -err_config: - lp5523_deinit_device(chip); - return ret; -} - static void lp5523_deinit_device(struct lp5523_chip *chip) { struct lp5523_platform_data *pdata = chip->pdata; @@ -931,7 +899,7 @@ static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct lp5523_chip *old_chip = NULL; - int ret, i; + int ret; struct lp55xx_chip *chip; struct lp55xx_led *led; struct lp55xx_platform_data *pdata = client->dev.platform_data; @@ -958,21 +926,12 @@ static int lp5523_probe(struct i2c_client *client, i2c_set_clientdata(client, led); - ret = lp5523_init_device(old_chip); + ret = lp55xx_init_device(chip); if (ret) goto err_init; dev_info(&client->dev, "%s Programmable led chip found\n", id->name); - /* Initialize engines */ - for (i = 0; i < ARRAY_SIZE(old_chip->engines); i++) { - ret = lp5523_init_engine(&old_chip->engines[i], i + 1); - if (ret) { - dev_err(&client->dev, "error initializing engine\n"); - goto fail1; - } - } - ret = lp5523_register_leds(old_chip, id->name); if (ret) goto fail2; @@ -985,7 +944,6 @@ static int lp5523_probe(struct i2c_client *client, return ret; fail2: lp5523_unregister_leds(old_chip); -fail1: lp5523_deinit_device(old_chip); err_init: return ret; diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 74beb363b787..c06745f160c3 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -144,9 +144,18 @@ int lp55xx_init_device(struct lp55xx_chip *chip) /* chip specific initialization */ ret = lp55xx_post_init_device(chip); + if (ret) { + dev_err(dev, "post init device err: %d\n", ret); + goto err_post_init; + } return 0; +err_post_init: + if (pdata->enable) + pdata->enable(0); + if (pdata->release_resources) + pdata->release_resources(); err: return ret; } -- cgit v1.2.3 From 6ce6176263393dd80b9a537c1e1462b8529f240b Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:03:02 +0900 Subject: leds-lp55xx: use lp55xx common deinit function Two separate de-init functions are merged into one common function. And it is used in err_post_init of lp55xx_init_device(). Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 16 ++++------------ drivers/leds/leds-lp5523.c | 16 ++++------------ drivers/leds/leds-lp55xx-common.c | 15 ++++++++++++--- drivers/leds/leds-lp55xx-common.h | 3 ++- 4 files changed, 22 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 74dc208fb99f..dd4526e168fa 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -689,16 +689,6 @@ static void lp5521_unregister_sysfs(struct i2c_client *client) &lp5521_led_attribute_group); } -static void lp5521_deinit_device(struct lp5521_chip *chip) -{ - struct lp5521_platform_data *pdata = chip->pdata; - - if (pdata->enable) - pdata->enable(0); - if (pdata->release_resources) - pdata->release_resources(); -} - static int lp5521_init_led(struct lp5521_led *led, struct i2c_client *client, int chan, struct lp5521_platform_data *pdata) @@ -858,7 +848,7 @@ static int lp5521_probe(struct i2c_client *client, return ret; fail2: lp5521_unregister_leds(old_chip); - lp5521_deinit_device(old_chip); + lp55xx_deinit_device(chip); err_init: return ret; } @@ -866,13 +856,15 @@ err_init: static int lp5521_remove(struct i2c_client *client) { struct lp5521_chip *old_chip = i2c_get_clientdata(client); + struct lp55xx_led *led = i2c_get_clientdata(client); + struct lp55xx_chip *chip = led->chip; lp5521_run_led_pattern(PATTERN_OFF, old_chip); lp5521_unregister_sysfs(client); lp5521_unregister_leds(old_chip); + lp55xx_deinit_device(chip); - lp5521_deinit_device(old_chip); return 0; } diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 80b7fb4a3ad6..3f506e3d4986 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -872,16 +872,6 @@ static void lp5523_unregister_leds(struct lp5523_chip *chip) } } -static void lp5523_deinit_device(struct lp5523_chip *chip) -{ - struct lp5523_platform_data *pdata = chip->pdata; - - if (pdata->enable) - pdata->enable(0); - if (pdata->release_resources) - pdata->release_resources(); -} - /* Chip specific configurations */ static struct lp55xx_device_config lp5523_cfg = { .reset = { @@ -944,7 +934,7 @@ static int lp5523_probe(struct i2c_client *client, return ret; fail2: lp5523_unregister_leds(old_chip); - lp5523_deinit_device(old_chip); + lp55xx_deinit_device(chip); err_init: return ret; } @@ -952,6 +942,8 @@ err_init: static int lp5523_remove(struct i2c_client *client) { struct lp5523_chip *old_chip = i2c_get_clientdata(client); + struct lp55xx_led *led = i2c_get_clientdata(client); + struct lp55xx_chip *chip = led->chip; /* Disable engine mode */ lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_DISABLED); @@ -959,8 +951,8 @@ static int lp5523_remove(struct i2c_client *client) lp5523_unregister_sysfs(client); lp5523_unregister_leds(old_chip); + lp55xx_deinit_device(chip); - lp5523_deinit_device(old_chip); return 0; } diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index c06745f160c3..bcabf2cb949a 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -152,14 +152,23 @@ int lp55xx_init_device(struct lp55xx_chip *chip) return 0; err_post_init: + lp55xx_deinit_device(chip); +err: + return ret; +} +EXPORT_SYMBOL_GPL(lp55xx_init_device); + +void lp55xx_deinit_device(struct lp55xx_chip *chip) +{ + struct lp55xx_platform_data *pdata = chip->pdata; + if (pdata->enable) pdata->enable(0); + if (pdata->release_resources) pdata->release_resources(); -err: - return ret; } -EXPORT_SYMBOL_GPL(lp55xx_init_device); +EXPORT_SYMBOL_GPL(lp55xx_deinit_device); MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP55xx Common Driver"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index ffedc7723d84..908b00a56b7e 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -84,7 +84,8 @@ extern int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val); extern int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val); -/* common device init functions */ +/* common device init/deinit functions */ extern int lp55xx_init_device(struct lp55xx_chip *chip); +extern void lp55xx_deinit_device(struct lp55xx_chip *chip); #endif /* _LEDS_LP55XX_COMMON_H */ -- cgit v1.2.3 From 9e9b3db1b2f725bacaf1b7e8708a0c78265bde97 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:06:27 +0900 Subject: leds-lp55xx: use lp55xx common led registration function LED class devices are registered in lp5521_register_leds() and lp5523_register_leds(). Two separate functions are merged into consolidated lp55xx function, lp55xx_register_leds(). Error handling fix: Unregistering LEDS are handled in lp55xx_register_leds() when LED registration failure occurs. So each driver error handler is changed to 'err_register_leds' Chip dependency: 'brightness_work_fn' and 'set_led_current' To make the structure abstract, both functions are configured in each driver. Those functions should be done by each driver because register control is chip-dependant work. lp55xx_init_led: skeleton Will be filled in next patch Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 43 +++--------------------------- drivers/leds/leds-lp5523.c | 45 +++----------------------------- drivers/leds/leds-lp55xx-common.c | 55 +++++++++++++++++++++++++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 11 ++++++++ 4 files changed, 72 insertions(+), 82 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index dd4526e168fa..dc58f4106d09 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -738,44 +738,6 @@ static int lp5521_init_led(struct lp5521_led *led, return 0; } -static int lp5521_register_leds(struct lp5521_chip *chip) -{ - struct lp5521_platform_data *pdata = chip->pdata; - struct i2c_client *client = chip->client; - int i; - int led; - int ret; - - /* Initialize leds */ - chip->num_channels = pdata->num_channels; - chip->num_leds = 0; - led = 0; - for (i = 0; i < pdata->num_channels; i++) { - /* Do not initialize channels that are not connected */ - if (pdata->led_config[i].led_current == 0) - continue; - - ret = lp5521_init_led(&chip->leds[led], client, i, pdata); - if (ret) { - dev_err(&client->dev, "error initializing leds\n"); - return ret; - } - chip->num_leds++; - - chip->leds[led].id = led; - /* Set initial LED current */ - lp5521_set_led_current(chip, led, - chip->leds[led].led_current); - - INIT_WORK(&(chip->leds[led].brightness_work), - lp5521_led_brightness_work); - - led++; - } - - return 0; -} - static void lp5521_unregister_leds(struct lp5521_chip *chip) { int i; @@ -836,9 +798,9 @@ static int lp5521_probe(struct i2c_client *client, dev_info(&client->dev, "%s programmable led chip found\n", id->name); - ret = lp5521_register_leds(old_chip); + ret = lp55xx_register_leds(led, chip); if (ret) - goto fail2; + goto err_register_leds; ret = lp5521_register_sysfs(client); if (ret) { @@ -848,6 +810,7 @@ static int lp5521_probe(struct i2c_client *client, return ret; fail2: lp5521_unregister_leds(old_chip); +err_register_leds: lp55xx_deinit_device(chip); err_init: return ret; diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 3f506e3d4986..41ac502ff82f 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -822,46 +822,6 @@ static int lp5523_init_led(struct lp5523_led *led, struct device *dev, return 0; } -static int lp5523_register_leds(struct lp5523_chip *chip, const char *name) -{ - struct lp5523_platform_data *pdata = chip->pdata; - struct i2c_client *client = chip->client; - int i; - int led; - int ret; - - /* Initialize leds */ - chip->num_channels = pdata->num_channels; - chip->num_leds = 0; - led = 0; - for (i = 0; i < pdata->num_channels; i++) { - /* Do not initialize channels that are not connected */ - if (pdata->led_config[i].led_current == 0) - continue; - - INIT_WORK(&chip->leds[led].brightness_work, - lp5523_led_brightness_work); - - ret = lp5523_init_led(&chip->leds[led], &client->dev, i, pdata, - name); - if (ret) { - dev_err(&client->dev, "error initializing leds\n"); - return ret; - } - chip->num_leds++; - - chip->leds[led].id = led; - /* Set LED current */ - lp5523_write(client, - LP5523_REG_LED_CURRENT_BASE + chip->leds[led].chan_nr, - chip->leds[led].led_current); - - led++; - } - - return 0; -} - static void lp5523_unregister_leds(struct lp5523_chip *chip) { int i; @@ -922,9 +882,9 @@ static int lp5523_probe(struct i2c_client *client, dev_info(&client->dev, "%s Programmable led chip found\n", id->name); - ret = lp5523_register_leds(old_chip, id->name); + ret = lp55xx_register_leds(led, chip); if (ret) - goto fail2; + goto err_register_leds; ret = lp5523_register_sysfs(client); if (ret) { @@ -934,6 +894,7 @@ static int lp5523_probe(struct i2c_client *client, return ret; fail2: lp5523_unregister_leds(old_chip); +err_register_leds: lp55xx_deinit_device(chip); err_init: return ret; diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index bcabf2cb949a..1fab1d1c4502 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -63,6 +63,12 @@ static int lp55xx_post_init_device(struct lp55xx_chip *chip) return cfg->post_init_device(chip); } +static int lp55xx_init_led(struct lp55xx_led *led, + struct lp55xx_chip *chip, int chan) +{ + return 0; +} + int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val) { return i2c_smbus_write_byte_data(chip->cl, reg, val); @@ -170,6 +176,55 @@ void lp55xx_deinit_device(struct lp55xx_chip *chip) } EXPORT_SYMBOL_GPL(lp55xx_deinit_device); +int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip) +{ + struct lp55xx_platform_data *pdata = chip->pdata; + struct lp55xx_device_config *cfg = chip->cfg; + int num_channels = pdata->num_channels; + struct lp55xx_led *each; + u8 led_current; + int ret; + int i; + + if (!cfg->brightness_work_fn) { + dev_err(&chip->cl->dev, "empty brightness configuration\n"); + return -EINVAL; + } + + for (i = 0; i < num_channels; i++) { + + /* do not initialize channels that are not connected */ + if (pdata->led_config[i].led_current == 0) + continue; + + led_current = pdata->led_config[i].led_current; + each = led + i; + ret = lp55xx_init_led(each, chip, i); + if (ret) + goto err_init_led; + + INIT_WORK(&each->brightness_work, cfg->brightness_work_fn); + + chip->num_leds++; + each->chip = chip; + + /* setting led current at each channel */ + if (cfg->set_led_current) + cfg->set_led_current(each, led_current); + } + + return 0; + +err_init_led: + for (i = 0; i < chip->num_leds; i++) { + each = led + i; + led_classdev_unregister(&each->cdev); + flush_work(&each->brightness_work); + } + return ret; +} +EXPORT_SYMBOL_GPL(lp55xx_register_leds); + MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP55xx Common Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 908b00a56b7e..219780a2d4eb 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -33,6 +33,8 @@ struct lp55xx_reg { * @reset : Chip specific reset command * @enable : Chip specific enable command * @post_init_device : Chip specific initialization code + * @brightness_work_fn : Brightness work function + * @set_led_current : LED current set function */ struct lp55xx_device_config { const struct lp55xx_reg reset; @@ -40,6 +42,12 @@ struct lp55xx_device_config { /* define if the device has specific initialization process */ int (*post_init_device) (struct lp55xx_chip *chip); + + /* access brightness register */ + void (*brightness_work_fn)(struct work_struct *work); + + /* current setting function */ + void (*set_led_current) (struct lp55xx_led *led, u8 led_current); }; /* @@ -88,4 +96,7 @@ extern int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, extern int lp55xx_init_device(struct lp55xx_chip *chip); extern void lp55xx_deinit_device(struct lp55xx_chip *chip); +/* common LED class device functions */ +extern int lp55xx_register_leds(struct lp55xx_led *led, + struct lp55xx_chip *chip); #endif /* _LEDS_LP55XX_COMMON_H */ -- cgit v1.2.3 From 0e2023463a3c9412728cb2c36c79aca0bb731cc8 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:07:34 +0900 Subject: leds-lp55xx: use lp55xx_init_led() common function lp5521_init_led() and lp5523_init_led() are replaced with one common function, lp55xx_init_led(). Max channels is configurable, so it's used in lp55xx_init_led(). 'LP5523_LEDS' are changed to 'LP5523_MAX_LEDS'. lp55xx_set_brightness, lp55xx_led_attributes: skeleton Will be filled in next patches. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 50 +---------------------------- drivers/leds/leds-lp5523.c | 62 +++++------------------------------- drivers/leds/leds-lp55xx-common.c | 66 +++++++++++++++++++++++++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 2 ++ 4 files changed, 76 insertions(+), 104 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index dc58f4106d09..bda03049fb3c 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -689,55 +689,6 @@ static void lp5521_unregister_sysfs(struct i2c_client *client) &lp5521_led_attribute_group); } -static int lp5521_init_led(struct lp5521_led *led, - struct i2c_client *client, - int chan, struct lp5521_platform_data *pdata) -{ - struct device *dev = &client->dev; - char name[32]; - int res; - - if (chan >= LP5521_MAX_LEDS) - return -EINVAL; - - if (pdata->led_config[chan].led_current == 0) - return 0; - - led->led_current = pdata->led_config[chan].led_current; - led->max_current = pdata->led_config[chan].max_current; - led->chan_nr = pdata->led_config[chan].chan_nr; - - if (led->chan_nr >= LP5521_MAX_LEDS) { - dev_err(dev, "Use channel numbers between 0 and %d\n", - LP5521_MAX_LEDS - 1); - return -EINVAL; - } - - led->cdev.brightness_set = lp5521_set_brightness; - if (pdata->led_config[chan].name) { - led->cdev.name = pdata->led_config[chan].name; - } else { - snprintf(name, sizeof(name), "%s:channel%d", - pdata->label ?: client->name, chan); - led->cdev.name = name; - } - - res = led_classdev_register(dev, &led->cdev); - if (res < 0) { - dev_err(dev, "couldn't register led on channel %d\n", chan); - return res; - } - - res = sysfs_create_group(&led->cdev.dev->kobj, - &lp5521_led_attribute_group); - if (res < 0) { - dev_err(dev, "couldn't register current attribute\n"); - led_classdev_unregister(&led->cdev); - return res; - } - return 0; -} - static void lp5521_unregister_leds(struct lp5521_chip *chip) { int i; @@ -758,6 +709,7 @@ static struct lp55xx_device_config lp5521_cfg = { .addr = LP5521_REG_ENABLE, .val = LP5521_ENABLE_DEFAULT, }, + .max_channel = LP5521_MAX_LEDS, .post_init_device = lp5521_post_init_device, }; diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 41ac502ff82f..ca2f8134909f 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -94,7 +94,7 @@ #define LP5523_PROGRAM_PAGES 6 #define LP5523_ADC_SHORTCIRC_LIM 80 -#define LP5523_LEDS 9 +#define LP5523_MAX_LEDS 9 #define LP5523_ENGINES 3 #define LP5523_ENG_MASK_BASE 0x30 /* 00110000 */ @@ -136,7 +136,7 @@ struct lp5523_chip { struct mutex lock; /* Serialize control */ struct i2c_client *client; struct lp5523_engine engines[LP5523_ENGINES]; - struct lp5523_led leds[LP5523_LEDS]; + struct lp5523_led leds[LP5523_MAX_LEDS]; struct lp5523_platform_data *pdata; u8 num_channels; u8 num_leds; @@ -285,7 +285,7 @@ static int lp5523_mux_parse(const char *buf, u16 *mux, size_t len) int i; u16 tmp_mux = 0; - len = min_t(int, len, LP5523_LEDS); + len = min_t(int, len, LP5523_MAX_LEDS); for (i = 0; i < len; i++) { switch (buf[i]) { case '1': @@ -308,7 +308,7 @@ static int lp5523_mux_parse(const char *buf, u16 *mux, size_t len) static void lp5523_mux_to_array(u16 led_mux, char *array) { int i, pos = 0; - for (i = 0; i < LP5523_LEDS; i++) + for (i = 0; i < LP5523_MAX_LEDS; i++) pos += sprintf(array + pos, "%x", LED_ACTIVE(led_mux, i)); array[pos] = '\0'; @@ -324,7 +324,7 @@ static ssize_t show_engine_leds(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct lp5523_chip *chip = i2c_get_clientdata(client); - char mux[LP5523_LEDS + 1]; + char mux[LP5523_MAX_LEDS + 1]; lp5523_mux_to_array(chip->engines[nr - 1].led_mux, mux); @@ -417,7 +417,7 @@ static ssize_t lp5523_selftest(struct device *dev, vdd--; /* There may be some fluctuation in measurement */ - for (i = 0; i < LP5523_LEDS; i++) { + for (i = 0; i < LP5523_MAX_LEDS; i++) { /* Skip non-existing channels */ if (chip->pdata->led_config[i].led_current == 0) continue; @@ -773,55 +773,6 @@ static void lp5523_set_mode(struct lp5523_engine *engine, u8 mode) /*--------------------------------------------------------------*/ /* Probe, Attach, Remove */ /*--------------------------------------------------------------*/ -static int lp5523_init_led(struct lp5523_led *led, struct device *dev, - int chan, struct lp5523_platform_data *pdata, - const char *chip_name) -{ - char name[32]; - int res; - - if (chan >= LP5523_LEDS) - return -EINVAL; - - if (pdata->led_config[chan].led_current) { - led->led_current = pdata->led_config[chan].led_current; - led->max_current = pdata->led_config[chan].max_current; - led->chan_nr = pdata->led_config[chan].chan_nr; - - if (led->chan_nr >= LP5523_LEDS) { - dev_err(dev, "Use channel numbers between 0 and %d\n", - LP5523_LEDS - 1); - return -EINVAL; - } - - if (pdata->led_config[chan].name) { - led->cdev.name = pdata->led_config[chan].name; - } else { - snprintf(name, sizeof(name), "%s:channel%d", - pdata->label ? : chip_name, chan); - led->cdev.name = name; - } - - led->cdev.brightness_set = lp5523_set_brightness; - res = led_classdev_register(dev, &led->cdev); - if (res < 0) { - dev_err(dev, "couldn't register led on channel %d\n", - chan); - return res; - } - res = sysfs_create_group(&led->cdev.dev->kobj, - &lp5523_led_attribute_group); - if (res < 0) { - dev_err(dev, "couldn't register current attribute\n"); - led_classdev_unregister(&led->cdev); - return res; - } - } else { - led->led_current = 0; - } - return 0; -} - static void lp5523_unregister_leds(struct lp5523_chip *chip) { int i; @@ -842,6 +793,7 @@ static struct lp55xx_device_config lp5523_cfg = { .addr = LP5523_REG_ENABLE, .val = LP5523_ENABLE, }, + .max_channel = LP5523_MAX_LEDS, .post_init_device = lp5523_post_init_device, }; diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 1fab1d1c4502..75ab1c3c03ed 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -63,9 +63,75 @@ static int lp55xx_post_init_device(struct lp55xx_chip *chip) return cfg->post_init_device(chip); } +static struct attribute *lp55xx_led_attributes[] = { + NULL, +}; + +static struct attribute_group lp55xx_led_attr_group = { + .attrs = lp55xx_led_attributes +}; + +static void lp55xx_set_brightness(struct led_classdev *cdev, + enum led_brightness brightness) +{ +} + static int lp55xx_init_led(struct lp55xx_led *led, struct lp55xx_chip *chip, int chan) { + struct lp55xx_platform_data *pdata = chip->pdata; + struct lp55xx_device_config *cfg = chip->cfg; + struct device *dev = &chip->cl->dev; + char name[32]; + int ret; + int max_channel = cfg->max_channel; + + if (chan >= max_channel) { + dev_err(dev, "invalid channel: %d / %d\n", chan, max_channel); + return -EINVAL; + } + + if (pdata->led_config[chan].led_current == 0) + return 0; + + led->led_current = pdata->led_config[chan].led_current; + led->max_current = pdata->led_config[chan].max_current; + led->chan_nr = pdata->led_config[chan].chan_nr; + + if (led->chan_nr >= max_channel) { + dev_err(dev, "Use channel numbers between 0 and %d\n", + max_channel - 1); + return -EINVAL; + } + + led->cdev.brightness_set = lp55xx_set_brightness; + + if (pdata->led_config[chan].name) { + led->cdev.name = pdata->led_config[chan].name; + } else { + snprintf(name, sizeof(name), "%s:channel%d", + pdata->label ? : chip->cl->name, chan); + led->cdev.name = name; + } + + /* + * register led class device for each channel and + * add device attributes + */ + + ret = led_classdev_register(dev, &led->cdev); + if (ret) { + dev_err(dev, "led register err: %d\n", ret); + return ret; + } + + ret = sysfs_create_group(&led->cdev.dev->kobj, &lp55xx_led_attr_group); + if (ret) { + dev_err(dev, "led sysfs err: %d\n", ret); + led_classdev_unregister(&led->cdev); + return ret; + } + return 0; } diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 219780a2d4eb..70d2bdf54b8e 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -32,6 +32,7 @@ struct lp55xx_reg { * struct lp55xx_device_config * @reset : Chip specific reset command * @enable : Chip specific enable command + * @max_channel : Maximum number of channels * @post_init_device : Chip specific initialization code * @brightness_work_fn : Brightness work function * @set_led_current : LED current set function @@ -39,6 +40,7 @@ struct lp55xx_reg { struct lp55xx_device_config { const struct lp55xx_reg reset; const struct lp55xx_reg enable; + const int max_channel; /* define if the device has specific initialization process */ int (*post_init_device) (struct lp55xx_chip *chip); -- cgit v1.2.3 From a6e4679a09a0a2bcfa63074272fc9fb2a40f11ad Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:08:40 +0900 Subject: leds-lp55xx: use lp55xx_set_brightness() lp5521_set_brightness() and lp5523_set_brightness() are replaced with common function, lp55xx_set_brightness(). This function is invoked when the brightness of each LED channel is updated. LP5521 and LP5523 have different register address for the brightness control, so this work is done by chip specific brightness_work_fn(). lp5521/5523_led_brightness_work(): use lp55xx_led and lp55xx_chip data structure. use lp55xx write function. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 19 ++++--------------- drivers/leds/leds-lp5523.c | 23 ++++------------------- drivers/leds/leds-lp55xx-common.c | 9 +++++++++ 3 files changed, 17 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index bda03049fb3c..6efbb7ec0e2d 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -151,8 +151,6 @@ static inline struct lp5521_chip *led_to_lp5521(struct lp5521_led *led) leds[led->id]); } -static void lp5521_led_brightness_work(struct work_struct *work); - static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value) { return i2c_smbus_write_byte_data(client, reg, value); @@ -303,24 +301,14 @@ static int lp5521_run_selftest(struct lp5521_chip *chip, char *buf) return 0; } -static void lp5521_set_brightness(struct led_classdev *cdev, - enum led_brightness brightness) -{ - struct lp5521_led *led = cdev_to_led(cdev); - led->brightness = (u8)brightness; - schedule_work(&led->brightness_work); -} - static void lp5521_led_brightness_work(struct work_struct *work) { - struct lp5521_led *led = container_of(work, - struct lp5521_led, + struct lp55xx_led *led = container_of(work, struct lp55xx_led, brightness_work); - struct lp5521_chip *chip = led_to_lp5521(led); - struct i2c_client *client = chip->client; + struct lp55xx_chip *chip = led->chip; mutex_lock(&chip->lock); - lp5521_write(client, LP5521_REG_LED_PWM_BASE + led->chan_nr, + lp55xx_write(chip, LP5521_REG_LED_PWM_BASE + led->chan_nr, led->brightness); mutex_unlock(&chip->lock); } @@ -711,6 +699,7 @@ static struct lp55xx_device_config lp5521_cfg = { }, .max_channel = LP5521_MAX_LEDS, .post_init_device = lp5521_post_init_device, + .brightness_work_fn = lp5521_led_brightness_work, }; static int lp5521_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index ca2f8134909f..43db2429616b 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -163,8 +163,6 @@ static void lp5523_set_mode(struct lp5523_engine *engine, u8 mode); static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode); static int lp5523_load_program(struct lp5523_engine *engine, const u8 *pattern); -static void lp5523_led_brightness_work(struct work_struct *work); - static int lp5523_write(struct i2c_client *client, u8 reg, u8 value) { return i2c_smbus_write_byte_data(client, reg, value); @@ -468,29 +466,15 @@ release_lock: return pos; } -static void lp5523_set_brightness(struct led_classdev *cdev, - enum led_brightness brightness) -{ - struct lp5523_led *led = cdev_to_led(cdev); - - led->brightness = (u8)brightness; - - schedule_work(&led->brightness_work); -} - static void lp5523_led_brightness_work(struct work_struct *work) { - struct lp5523_led *led = container_of(work, - struct lp5523_led, + struct lp55xx_led *led = container_of(work, struct lp55xx_led, brightness_work); - struct lp5523_chip *chip = led_to_lp5523(led); - struct i2c_client *client = chip->client; + struct lp55xx_chip *chip = led->chip; mutex_lock(&chip->lock); - - lp5523_write(client, LP5523_REG_LED_PWM_BASE + led->chan_nr, + lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr, led->brightness); - mutex_unlock(&chip->lock); } @@ -795,6 +779,7 @@ static struct lp55xx_device_config lp5523_cfg = { }, .max_channel = LP5523_MAX_LEDS, .post_init_device = lp5523_post_init_device, + .brightness_work_fn = lp5523_led_brightness_work, }; static int lp5523_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 75ab1c3c03ed..8244d78447f4 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -20,6 +20,11 @@ #include "leds-lp55xx-common.h" +static struct lp55xx_led *cdev_to_lp55xx_led(struct led_classdev *cdev) +{ + return container_of(cdev, struct lp55xx_led, cdev); +} + static void lp55xx_reset_device(struct lp55xx_chip *chip) { struct lp55xx_device_config *cfg = chip->cfg; @@ -74,6 +79,10 @@ static struct attribute_group lp55xx_led_attr_group = { static void lp55xx_set_brightness(struct led_classdev *cdev, enum led_brightness brightness) { + struct lp55xx_led *led = cdev_to_lp55xx_led(cdev); + + led->brightness = (u8)brightness; + schedule_work(&led->brightness_work); } static int lp55xx_init_led(struct lp55xx_led *led, -- cgit v1.2.3 From a96bfa135ddc8045166fc6311ce4d21bfcb8d13d Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:09:32 +0900 Subject: leds-lp55xx: provide common LED current setting LED current is configurable via the sysfs. Max current is a read-only attribute. These attributes code can be shared in lp55xx common driver. Device attributes: 'led_current' and 'max_current' move to lp55xx common driver Replaced functions: show_max_current() => lp55xx_show_max_current() show_current() => lp55xx_show_current() store_current() => lp55xx_store_current() LED setting function: set_led_current() Current registers are device specific, so configurable function is added in each driver. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 74 +++++---------------------------------- drivers/leds/leds-lp5523.c | 69 +++++------------------------------- drivers/leds/leds-lp55xx-common.c | 53 ++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 127 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 6efbb7ec0e2d..7133af824b5e 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -134,6 +134,13 @@ static inline void lp5521_wait_enable_done(void) usleep_range(500, 600); } +static void lp5521_set_led_current(struct lp55xx_led *led, u8 led_current) +{ + led->led_current = led_current; + lp55xx_write(led->chip, LP5521_REG_LED_CURRENT_BASE + led->chan_nr, + led_current); +} + static inline struct lp5521_led *cdev_to_led(struct led_classdev *cdev) { return container_of(cdev, struct lp5521_led, cdev); @@ -229,13 +236,6 @@ static int lp5521_load_program(struct lp5521_engine *eng, const u8 *pattern) return lp5521_write(client, LP5521_REG_OP_MODE, mode); } -static int lp5521_set_led_current(struct lp5521_chip *chip, int led, u8 curr) -{ - return lp5521_write(chip->client, - LP5521_REG_LED_CURRENT_BASE + chip->leds[led].chan_nr, - curr); -} - static int lp5521_post_init_device(struct lp55xx_chip *chip) { int ret; @@ -462,54 +462,6 @@ store_mode(1) store_mode(2) store_mode(3) -static ssize_t show_max_current(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct lp5521_led *led = cdev_to_led(led_cdev); - - return sprintf(buf, "%d\n", led->max_current); -} - -static ssize_t show_current(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct lp5521_led *led = cdev_to_led(led_cdev); - - return sprintf(buf, "%d\n", led->led_current); -} - -static ssize_t store_current(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct lp5521_led *led = cdev_to_led(led_cdev); - struct lp5521_chip *chip = led_to_lp5521(led); - ssize_t ret; - unsigned long curr; - - if (kstrtoul(buf, 0, &curr)) - return -EINVAL; - - if (curr > led->max_current) - return -EINVAL; - - mutex_lock(&chip->lock); - ret = lp5521_set_led_current(chip, led->id, curr); - mutex_unlock(&chip->lock); - - if (ret < 0) - return ret; - - led->led_current = (u8)curr; - - return len; -} - static ssize_t lp5521_selftest(struct device *dev, struct device_attribute *attr, char *buf) @@ -615,18 +567,7 @@ static ssize_t store_led_pattern(struct device *dev, return len; } -/* led class device attributes */ -static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current); -static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); - -static struct attribute *lp5521_led_attributes[] = { - &dev_attr_led_current.attr, - &dev_attr_max_current.attr, - NULL, -}; - static struct attribute_group lp5521_led_attribute_group = { - .attrs = lp5521_led_attributes }; /* device attributes */ @@ -700,6 +641,7 @@ static struct lp55xx_device_config lp5521_cfg = { .max_channel = LP5521_MAX_LEDS, .post_init_device = lp5521_post_init_device, .brightness_work_fn = lp5521_led_brightness_work, + .set_led_current = lp5521_set_led_current, }; static int lp5521_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 43db2429616b..dfb335420568 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -142,6 +142,13 @@ struct lp5523_chip { u8 num_leds; }; +static void lp5523_set_led_current(struct lp55xx_led *led, u8 led_current) +{ + led->led_current = led_current; + lp55xx_write(led->chip, LP5523_REG_LED_CURRENT_BASE + led->chan_nr, + led_current); +} + static inline struct lp5523_led *cdev_to_led(struct led_classdev *cdev) { return container_of(cdev, struct lp5523_led, cdev); @@ -602,68 +609,7 @@ store_mode(1) store_mode(2) store_mode(3) -static ssize_t show_max_current(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct lp5523_led *led = cdev_to_led(led_cdev); - - return sprintf(buf, "%d\n", led->max_current); -} - -static ssize_t show_current(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct lp5523_led *led = cdev_to_led(led_cdev); - - return sprintf(buf, "%d\n", led->led_current); -} - -static ssize_t store_current(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct lp5523_led *led = cdev_to_led(led_cdev); - struct lp5523_chip *chip = led_to_lp5523(led); - ssize_t ret; - unsigned long curr; - - if (kstrtoul(buf, 0, &curr)) - return -EINVAL; - - if (curr > led->max_current) - return -EINVAL; - - mutex_lock(&chip->lock); - ret = lp5523_write(chip->client, - LP5523_REG_LED_CURRENT_BASE + led->chan_nr, - (u8)curr); - mutex_unlock(&chip->lock); - - if (ret < 0) - return ret; - - led->led_current = (u8)curr; - - return len; -} - -/* led class device attributes */ -static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current); -static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); - -static struct attribute *lp5523_led_attributes[] = { - &dev_attr_led_current.attr, - &dev_attr_max_current.attr, - NULL, -}; - static struct attribute_group lp5523_led_attribute_group = { - .attrs = lp5523_led_attributes }; /* device attributes */ @@ -780,6 +726,7 @@ static struct lp55xx_device_config lp5523_cfg = { .max_channel = LP5523_MAX_LEDS, .post_init_device = lp5523_post_init_device, .brightness_work_fn = lp5523_led_brightness_work, + .set_led_current = lp5523_set_led_current, }; static int lp5523_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 8244d78447f4..6b3d03709f5f 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -25,6 +25,11 @@ static struct lp55xx_led *cdev_to_lp55xx_led(struct led_classdev *cdev) return container_of(cdev, struct lp55xx_led, cdev); } +static struct lp55xx_led *dev_to_lp55xx_led(struct device *dev) +{ + return cdev_to_lp55xx_led(dev_get_drvdata(dev)); +} + static void lp55xx_reset_device(struct lp55xx_chip *chip) { struct lp55xx_device_config *cfg = chip->cfg; @@ -68,7 +73,55 @@ static int lp55xx_post_init_device(struct lp55xx_chip *chip) return cfg->post_init_device(chip); } +static ssize_t lp55xx_show_current(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct lp55xx_led *led = dev_to_lp55xx_led(dev); + + return sprintf(buf, "%d\n", led->led_current); +} + +static ssize_t lp55xx_store_current(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct lp55xx_led *led = dev_to_lp55xx_led(dev); + struct lp55xx_chip *chip = led->chip; + unsigned long curr; + + if (kstrtoul(buf, 0, &curr)) + return -EINVAL; + + if (curr > led->max_current) + return -EINVAL; + + if (!chip->cfg->set_led_current) + return len; + + mutex_lock(&chip->lock); + chip->cfg->set_led_current(led, (u8)curr); + mutex_unlock(&chip->lock); + + return len; +} + +static ssize_t lp55xx_show_max_current(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct lp55xx_led *led = dev_to_lp55xx_led(dev); + + return sprintf(buf, "%d\n", led->max_current); +} + +static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, lp55xx_show_current, + lp55xx_store_current); +static DEVICE_ATTR(max_current, S_IRUGO , lp55xx_show_max_current, NULL); + static struct attribute *lp55xx_led_attributes[] = { + &dev_attr_led_current.attr, + &dev_attr_max_current.attr, NULL, }; -- cgit v1.2.3 From c3a68ebfcd22abc186f2328149732c801449b297 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:11:18 +0900 Subject: leds-lp55xx: use lp55xx_unregister_leds() To unregister led class devices and sysfs attributes, LP5521 and LP5523 have each driver function. This patch makes both drivers simple using common driver function, lp55xx_unregister_leds(). And some unused variables are removed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 24 ++---------------------- drivers/leds/leds-lp5523.c | 25 ++----------------------- drivers/leds/leds-lp55xx-common.c | 16 ++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 2 ++ 4 files changed, 22 insertions(+), 45 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 7133af824b5e..cec252eae716 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -567,9 +567,6 @@ static ssize_t store_led_pattern(struct device *dev, return len; } -static struct attribute_group lp5521_led_attribute_group = { -}; - /* device attributes */ static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR, show_engine1_mode, store_engine1_mode); @@ -607,25 +604,9 @@ static int lp5521_register_sysfs(struct i2c_client *client) static void lp5521_unregister_sysfs(struct i2c_client *client) { - struct lp5521_chip *chip = i2c_get_clientdata(client); struct device *dev = &client->dev; - int i; sysfs_remove_group(&dev->kobj, &lp5521_group); - - for (i = 0; i < chip->num_leds; i++) - sysfs_remove_group(&chip->leds[i].cdev.dev->kobj, - &lp5521_led_attribute_group); -} - -static void lp5521_unregister_leds(struct lp5521_chip *chip) -{ - int i; - - for (i = 0; i < chip->num_leds; i++) { - led_classdev_unregister(&chip->leds[i].cdev); - cancel_work_sync(&chip->leds[i].brightness_work); - } } /* Chip specific configurations */ @@ -647,7 +628,6 @@ static struct lp55xx_device_config lp5521_cfg = { static int lp5521_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct lp5521_chip *old_chip = NULL; int ret; struct lp55xx_chip *chip; struct lp55xx_led *led; @@ -692,7 +672,7 @@ static int lp5521_probe(struct i2c_client *client, } return ret; fail2: - lp5521_unregister_leds(old_chip); + lp55xx_unregister_leds(led, chip); err_register_leds: lp55xx_deinit_device(chip); err_init: @@ -708,7 +688,7 @@ static int lp5521_remove(struct i2c_client *client) lp5521_run_led_pattern(PATTERN_OFF, old_chip); lp5521_unregister_sysfs(client); - lp5521_unregister_leds(old_chip); + lp55xx_unregister_leds(led, chip); lp55xx_deinit_device(chip); return 0; diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index dfb335420568..70630156eff9 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -609,9 +609,6 @@ store_mode(1) store_mode(2) store_mode(3) -static struct attribute_group lp5523_led_attribute_group = { -}; - /* device attributes */ static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR, show_engine1_mode, store_engine1_mode); @@ -662,15 +659,9 @@ static int lp5523_register_sysfs(struct i2c_client *client) static void lp5523_unregister_sysfs(struct i2c_client *client) { - struct lp5523_chip *chip = i2c_get_clientdata(client); struct device *dev = &client->dev; - int i; sysfs_remove_group(&dev->kobj, &lp5523_group); - - for (i = 0; i < chip->num_leds; i++) - sysfs_remove_group(&chip->leds[i].cdev.dev->kobj, - &lp5523_led_attribute_group); } /*--------------------------------------------------------------*/ @@ -703,16 +694,6 @@ static void lp5523_set_mode(struct lp5523_engine *engine, u8 mode) /*--------------------------------------------------------------*/ /* Probe, Attach, Remove */ /*--------------------------------------------------------------*/ -static void lp5523_unregister_leds(struct lp5523_chip *chip) -{ - int i; - - for (i = 0; i < chip->num_leds; i++) { - led_classdev_unregister(&chip->leds[i].cdev); - flush_work(&chip->leds[i].brightness_work); - } -} - /* Chip specific configurations */ static struct lp55xx_device_config lp5523_cfg = { .reset = { @@ -732,7 +713,6 @@ static struct lp55xx_device_config lp5523_cfg = { static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct lp5523_chip *old_chip = NULL; int ret; struct lp55xx_chip *chip; struct lp55xx_led *led; @@ -777,7 +757,7 @@ static int lp5523_probe(struct i2c_client *client, } return ret; fail2: - lp5523_unregister_leds(old_chip); + lp55xx_unregister_leds(led, chip); err_register_leds: lp55xx_deinit_device(chip); err_init: @@ -786,7 +766,6 @@ err_init: static int lp5523_remove(struct i2c_client *client) { - struct lp5523_chip *old_chip = i2c_get_clientdata(client); struct lp55xx_led *led = i2c_get_clientdata(client); struct lp55xx_chip *chip = led->chip; @@ -795,7 +774,7 @@ static int lp5523_remove(struct i2c_client *client) lp5523_unregister_sysfs(client); - lp5523_unregister_leds(old_chip); + lp55xx_unregister_leds(led, chip); lp55xx_deinit_device(chip); return 0; diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 6b3d03709f5f..dcd64f5285e8 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -353,6 +353,22 @@ err_init_led: } EXPORT_SYMBOL_GPL(lp55xx_register_leds); +void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip) +{ + int i; + struct lp55xx_led *each; + struct kobject *kobj; + + for (i = 0; i < chip->num_leds; i++) { + each = led + i; + kobj = &led->cdev.dev->kobj; + sysfs_remove_group(kobj, &lp55xx_led_attr_group); + led_classdev_unregister(&each->cdev); + flush_work(&each->brightness_work); + } +} +EXPORT_SYMBOL_GPL(lp55xx_unregister_leds); + MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP55xx Common Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 70d2bdf54b8e..32d96828cbc6 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -101,4 +101,6 @@ extern void lp55xx_deinit_device(struct lp55xx_chip *chip); /* common LED class device functions */ extern int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip); +extern void lp55xx_unregister_leds(struct lp55xx_led *led, + struct lp55xx_chip *chip); #endif /* _LEDS_LP55XX_COMMON_H */ -- cgit v1.2.3 From d9067d28461cb2e817cacb84c727959cbd57d247 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:12:47 +0900 Subject: leds-lp55xx: fix error condition in lp55xx_register_leds() Use lp55xx_unregister_leds() rather than duplicate code. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp55xx-common.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index dcd64f5285e8..cd19027895e5 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -344,11 +344,7 @@ int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip) return 0; err_init_led: - for (i = 0; i < chip->num_leds; i++) { - each = led + i; - led_classdev_unregister(&each->cdev); - flush_work(&each->brightness_work); - } + lp55xx_unregister_leds(led, chip); return ret; } EXPORT_SYMBOL_GPL(lp55xx_register_leds); -- cgit v1.2.3 From b3b6f8119d752c969c6394314dc7ab80e6611111 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:15:27 +0900 Subject: leds-lp55xx: add new lp55xx_register_sysfs() for the firmware interface LP55xx family chips have internal program memory which run various patterns. Using this memory, LEDs continue on blinking/dimming without continuous I2C commands. That means the I2C HOST can be entered into sleep once the memory is updated. An application can get hex data from a file and write them into the program memory through the I2C. This is general firwmare interface. This patch is the initial step for adding the firmware interface. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp55xx-common.c | 16 ++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 4 ++++ 2 files changed, 20 insertions(+) (limited to 'drivers') diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index cd19027895e5..98407ca45e4f 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -197,6 +197,14 @@ static int lp55xx_init_led(struct lp55xx_led *led, return 0; } +static struct attribute *lp55xx_engine_attributes[] = { + NULL, +}; + +static const struct attribute_group lp55xx_engine_attr_group = { + .attrs = lp55xx_engine_attributes, +}; + int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val) { return i2c_smbus_write_byte_data(chip->cl, reg, val); @@ -365,6 +373,14 @@ void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip) } EXPORT_SYMBOL_GPL(lp55xx_unregister_leds); +int lp55xx_register_sysfs(struct lp55xx_chip *chip) +{ + struct device *dev = &chip->cl->dev; + + return sysfs_create_group(&dev->kobj, &lp55xx_engine_attr_group); +} +EXPORT_SYMBOL_GPL(lp55xx_register_sysfs); + MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP55xx Common Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 32d96828cbc6..d0be837643f0 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -103,4 +103,8 @@ extern int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip); extern void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip); + +/* common device attributes functions */ +extern int lp55xx_register_sysfs(struct lp55xx_chip *chip); + #endif /* _LEDS_LP55XX_COMMON_H */ -- cgit v1.2.3 From 10c06d178df11b0b2b746321a80ea14241997127 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:17:20 +0900 Subject: leds-lp55xx: support firmware interface This patch provides additional device attributes which enable loading the firmware. ('select_engine' and 'run_engine') To run a LED pattern, two parts of driver should be enabled. Common features : lp55xx-common =============================== Firmware interface for loading LED patterns Chip specific features : leds-lp5521, leds-lp5523 ================================================= Register addresses for loading firmware data Register addresses for running selected engine Pattern programming sequence ============================ LP55xx chips have three program engines. To load and run a LED pattern, the programming sequence is as follows. (1) Select an engine number (1/2/3) (2) Set engine mode to load (3) Write pattern data into selected area (4) Set engine mode to run This sequence is almost same as the firmware interface. (1) Select an engine number : 'select_engine' dev attribute (2) Mode change to load : 'loading' of firmware class (3) Write pattern data into selected area : 'data' of firmware class (4) Mode change to run : 'run_engine' dev attribute (1) and (4) are device specific features which provide callback functions (2) and (3) are common features. For example, echo 1 or 2 or 3 > /sys/bus/i2c/devices/xxxx/select_engine echo 1 > /sys/class/firmware/lp5521/loading echo "4000600040FF6000" > /sys/class/firmware/lp5521/data echo 0 > /sys/class/firmware/lp5521/loading echo 1 > /sys/bus/i2c/devices/xxxx/run_engine As soon as 'loading' is set to 0, registered callback is called. Inside the callback, the selected engine is loaded and memory is updated. To run programmed pattern, 'run_engine' attribute should be enabled. Device specific data structure ============================== o Firmware callback load selected engine and update program memory o Run engine change the engine mode o 'engine_idx' and firmware data, 'fw' Those are used in the driver internally with callback functions Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/Kconfig | 1 + drivers/leds/leds-lp55xx-common.c | 117 ++++++++++++++++++++++++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 19 +++++++ 3 files changed, 137 insertions(+) (limited to 'drivers') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 3d7822b3498f..fc680a9ed841 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -196,6 +196,7 @@ config LEDS_LP3944 config LEDS_LP55XX_COMMON tristate "Common Driver for TI/National LP5521 and LP5523/55231" depends on LEDS_LP5521 || LEDS_LP5523 + select FW_LOADER help This option supports common operations for LP5521 and LP5523/55231 devices. diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 98407ca45e4f..578902ab604f 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -197,7 +198,123 @@ static int lp55xx_init_led(struct lp55xx_led *led, return 0; } +static void lp55xx_firmware_loaded(const struct firmware *fw, void *context) +{ + struct lp55xx_chip *chip = context; + struct device *dev = &chip->cl->dev; + + if (!fw) { + dev_err(dev, "firmware request failed\n"); + goto out; + } + + /* handling firmware data is chip dependent */ + mutex_lock(&chip->lock); + + chip->fw = fw; + if (chip->cfg->firmware_cb) + chip->cfg->firmware_cb(chip); + + mutex_unlock(&chip->lock); + +out: + /* firmware should be released for other channel use */ + release_firmware(chip->fw); +} + +static int lp55xx_request_firmware(struct lp55xx_chip *chip) +{ + const char *name = chip->cl->name; + struct device *dev = &chip->cl->dev; + + return request_firmware_nowait(THIS_MODULE, true, name, dev, + GFP_KERNEL, chip, lp55xx_firmware_loaded); +} + +static ssize_t lp55xx_show_engine_select(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); + struct lp55xx_chip *chip = led->chip; + + return sprintf(buf, "%d\n", chip->engine_idx); +} + +static ssize_t lp55xx_store_engine_select(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); + struct lp55xx_chip *chip = led->chip; + unsigned long val; + int ret; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + /* select the engine to be run */ + + switch (val) { + case LP55XX_ENGINE_1: + case LP55XX_ENGINE_2: + case LP55XX_ENGINE_3: + mutex_lock(&chip->lock); + chip->engine_idx = val; + ret = lp55xx_request_firmware(chip); + mutex_unlock(&chip->lock); + break; + default: + dev_err(dev, "%lu: invalid engine index. (1, 2, 3)\n", val); + return -EINVAL; + } + + if (ret) { + dev_err(dev, "request firmware err: %d\n", ret); + return ret; + } + + return len; +} + +static inline void lp55xx_run_engine(struct lp55xx_chip *chip, bool start) +{ + if (chip->cfg->run_engine) + chip->cfg->run_engine(chip, start); +} + +static ssize_t lp55xx_store_engine_run(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); + struct lp55xx_chip *chip = led->chip; + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + /* run or stop the selected engine */ + + if (val <= 0) { + lp55xx_run_engine(chip, false); + return len; + } + + mutex_lock(&chip->lock); + lp55xx_run_engine(chip, true); + mutex_unlock(&chip->lock); + + return len; +} + +static DEVICE_ATTR(select_engine, S_IRUGO | S_IWUSR, + lp55xx_show_engine_select, lp55xx_store_engine_select); +static DEVICE_ATTR(run_engine, S_IWUSR, NULL, lp55xx_store_engine_run); + static struct attribute *lp55xx_engine_attributes[] = { + &dev_attr_select_engine.attr, + &dev_attr_run_engine.attr, NULL, }; diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index d0be837643f0..8473abf9830c 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -15,6 +15,13 @@ #ifndef _LEDS_LP55XX_COMMON_H #define _LEDS_LP55XX_COMMON_H +enum lp55xx_engine_index { + LP55XX_ENGINE_INVALID, + LP55XX_ENGINE_1, + LP55XX_ENGINE_2, + LP55XX_ENGINE_3, +}; + struct lp55xx_led; struct lp55xx_chip; @@ -36,6 +43,8 @@ struct lp55xx_reg { * @post_init_device : Chip specific initialization code * @brightness_work_fn : Brightness work function * @set_led_current : LED current set function + * @firmware_cb : Call function when the firmware is loaded + * @run_engine : Run internal engine for pattern */ struct lp55xx_device_config { const struct lp55xx_reg reset; @@ -50,6 +59,12 @@ struct lp55xx_device_config { /* current setting function */ void (*set_led_current) (struct lp55xx_led *led, u8 led_current); + + /* access program memory when the firmware is loaded */ + void (*firmware_cb)(struct lp55xx_chip *chip); + + /* used for running firmware LED patterns */ + void (*run_engine) (struct lp55xx_chip *chip, bool start); }; /* @@ -59,6 +74,8 @@ struct lp55xx_device_config { * @lock : Lock for user-space interface * @num_leds : Number of registered LEDs * @cfg : Device specific configuration data + * @engine_idx : Selected engine number + * @fw : Firmware data for running a LED pattern */ struct lp55xx_chip { struct i2c_client *cl; @@ -66,6 +83,8 @@ struct lp55xx_chip { struct mutex lock; /* lock for user-space interface */ int num_leds; struct lp55xx_device_config *cfg; + enum lp55xx_engine_index engine_idx; + const struct firmware *fw; }; /* -- cgit v1.2.3 From 9ce7cb170f97f83a78dc948bf7d25690f15e1328 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:18:10 +0900 Subject: leds-lp5521: use generic firmware interface LP55xx common driver provides generic firmware interface for running a LED pattern. LP5521 and LP5523 have many device attributes for running patterns. This patch cleans up those complex code. Removed device attributes: engine1_mode engine2_mode engine3_mode engine1_load engine2_load engine3_load led_pattern All device attributes and functions are replaced with two callback functions, 'firmware_cb' and 'run_engine'. New engine functions: lp5521_load/stop/run_engine(), lp5521_update_program_memory() and lp5521_wait_opmode_done() Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 414 ++++++++++++++++++--------------------------- 1 file changed, 163 insertions(+), 251 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index cec252eae716..89371e065c13 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "leds-lp55xx-common.h" @@ -101,12 +102,28 @@ /* Reset register value */ #define LP5521_RESET 0xFF -struct lp5521_engine { - int id; - u8 mode; - u8 prog_page; - u8 engine_mask; -}; +/* Program Memory Operations */ +#define LP5521_MODE_R_M 0x30 /* Operation Mode Register */ +#define LP5521_MODE_G_M 0x0C +#define LP5521_MODE_B_M 0x03 +#define LP5521_LOAD_R 0x10 +#define LP5521_LOAD_G 0x04 +#define LP5521_LOAD_B 0x01 + +#define LP5521_R_IS_LOADING(mode) \ + ((mode & LP5521_MODE_R_M) == LP5521_LOAD_R) +#define LP5521_G_IS_LOADING(mode) \ + ((mode & LP5521_MODE_G_M) == LP5521_LOAD_G) +#define LP5521_B_IS_LOADING(mode) \ + ((mode & LP5521_MODE_B_M) == LP5521_LOAD_B) + +#define LP5521_EXEC_R_M 0x30 /* Enable Register */ +#define LP5521_EXEC_G_M 0x0C +#define LP5521_EXEC_B_M 0x03 +#define LP5521_EXEC_M 0x3F +#define LP5521_RUN_R 0x20 +#define LP5521_RUN_G 0x08 +#define LP5521_RUN_B 0x02 struct lp5521_led { int id; @@ -122,12 +139,17 @@ struct lp5521_chip { struct lp5521_platform_data *pdata; struct mutex lock; /* Serialize control */ struct i2c_client *client; - struct lp5521_engine engines[LP5521_MAX_ENGINES]; struct lp5521_led leds[LP5521_MAX_LEDS]; u8 num_channels; u8 num_leds; }; +static inline void lp5521_wait_opmode_done(void) +{ + /* operation mode change needs to be longer than 153 us */ + usleep_range(200, 300); +} + static inline void lp5521_wait_enable_done(void) { /* it takes more 488 us to update ENABLE register */ @@ -141,23 +163,6 @@ static void lp5521_set_led_current(struct lp55xx_led *led, u8 led_current) led_current); } -static inline struct lp5521_led *cdev_to_led(struct led_classdev *cdev) -{ - return container_of(cdev, struct lp5521_led, cdev); -} - -static inline struct lp5521_chip *engine_to_lp5521(struct lp5521_engine *engine) -{ - return container_of(engine, struct lp5521_chip, - engines[engine->id - 1]); -} - -static inline struct lp5521_chip *led_to_lp5521(struct lp5521_led *led) -{ - return container_of(led, struct lp5521_chip, - leds[led->id]); -} - static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value) { return i2c_smbus_write_byte_data(client, reg, value); @@ -175,65 +180,153 @@ static int lp5521_read(struct i2c_client *client, u8 reg, u8 *buf) return 0; } -static int lp5521_set_engine_mode(struct lp5521_engine *engine, u8 mode) +static void lp5521_load_engine(struct lp55xx_chip *chip) { - struct lp5521_chip *chip = engine_to_lp5521(engine); - struct i2c_client *client = chip->client; - int ret; - u8 engine_state; + enum lp55xx_engine_index idx = chip->engine_idx; + u8 mask[] = { + [LP55XX_ENGINE_1] = LP5521_MODE_R_M, + [LP55XX_ENGINE_2] = LP5521_MODE_G_M, + [LP55XX_ENGINE_3] = LP5521_MODE_B_M, + }; - /* Only transition between RUN and DIRECT mode are handled here */ - if (mode == LP5521_CMD_LOAD) - return 0; + u8 val[] = { + [LP55XX_ENGINE_1] = LP5521_LOAD_R, + [LP55XX_ENGINE_2] = LP5521_LOAD_G, + [LP55XX_ENGINE_3] = LP5521_LOAD_B, + }; - if (mode == LP5521_CMD_DISABLED) - mode = LP5521_CMD_DIRECT; + lp55xx_update_bits(chip, LP5521_REG_OP_MODE, mask[idx], val[idx]); - ret = lp5521_read(client, LP5521_REG_OP_MODE, &engine_state); - if (ret < 0) - return ret; + lp5521_wait_opmode_done(); +} - /* set mode only for this engine */ - engine_state &= ~(engine->engine_mask); - mode &= engine->engine_mask; - engine_state |= mode; - return lp5521_write(client, LP5521_REG_OP_MODE, engine_state); +static void lp5521_stop_engine(struct lp55xx_chip *chip) +{ + lp55xx_write(chip, LP5521_REG_OP_MODE, 0); + lp5521_wait_opmode_done(); } -static int lp5521_load_program(struct lp5521_engine *eng, const u8 *pattern) +static void lp5521_run_engine(struct lp55xx_chip *chip, bool start) { - struct lp5521_chip *chip = engine_to_lp5521(eng); - struct i2c_client *client = chip->client; int ret; - int addr; u8 mode; + u8 exec; - /* move current engine to direct mode and remember the state */ - ret = lp5521_set_engine_mode(eng, LP5521_CMD_DIRECT); + /* stop engine */ + if (!start) { + lp5521_stop_engine(chip); + lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); + lp5521_wait_opmode_done(); + return; + } + + /* + * To run the engine, + * operation mode and enable register should updated at the same time + */ + + ret = lp55xx_read(chip, LP5521_REG_OP_MODE, &mode); if (ret) - return ret; + return; - /* Mode change requires min 500 us delay. 1 - 2 ms with margin */ - usleep_range(1000, 2000); - ret = lp5521_read(client, LP5521_REG_OP_MODE, &mode); + ret = lp55xx_read(chip, LP5521_REG_ENABLE, &exec); if (ret) - return ret; + return; + + /* change operation mode to RUN only when each engine is loading */ + if (LP5521_R_IS_LOADING(mode)) { + mode = (mode & ~LP5521_MODE_R_M) | LP5521_RUN_R; + exec = (exec & ~LP5521_EXEC_R_M) | LP5521_RUN_R; + } + + if (LP5521_G_IS_LOADING(mode)) { + mode = (mode & ~LP5521_MODE_G_M) | LP5521_RUN_G; + exec = (exec & ~LP5521_EXEC_G_M) | LP5521_RUN_G; + } + + if (LP5521_B_IS_LOADING(mode)) { + mode = (mode & ~LP5521_MODE_B_M) | LP5521_RUN_B; + exec = (exec & ~LP5521_EXEC_B_M) | LP5521_RUN_B; + } + + lp55xx_write(chip, LP5521_REG_OP_MODE, mode); + lp5521_wait_opmode_done(); - /* For loading, all the engines to load mode */ - lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); - /* Mode change requires min 500 us delay. 1 - 2 ms with margin */ - usleep_range(1000, 2000); - lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_LOAD); - /* Mode change requires min 500 us delay. 1 - 2 ms with margin */ - usleep_range(1000, 2000); - - addr = LP5521_PROG_MEM_BASE + eng->prog_page * LP5521_PROG_MEM_SIZE; - i2c_smbus_write_i2c_block_data(client, - addr, - LP5521_PROG_MEM_SIZE, - pattern); - - return lp5521_write(client, LP5521_REG_OP_MODE, mode); + lp55xx_update_bits(chip, LP5521_REG_ENABLE, LP5521_EXEC_M, exec); + lp5521_wait_enable_done(); +} + +static int lp5521_update_program_memory(struct lp55xx_chip *chip, + const u8 *data, size_t size) +{ + enum lp55xx_engine_index idx = chip->engine_idx; + u8 pattern[LP5521_PROGRAM_LENGTH] = {0}; + u8 addr[] = { + [LP55XX_ENGINE_1] = LP5521_REG_R_PROG_MEM, + [LP55XX_ENGINE_2] = LP5521_REG_G_PROG_MEM, + [LP55XX_ENGINE_3] = LP5521_REG_B_PROG_MEM, + }; + unsigned cmd; + char c[3]; + int program_size; + int nrchars; + int offset = 0; + int ret; + int i; + + /* clear program memory before updating */ + for (i = 0; i < LP5521_PROGRAM_LENGTH; i++) + lp55xx_write(chip, addr[idx] + i, 0); + + i = 0; + while ((offset < size - 1) && (i < LP5521_PROGRAM_LENGTH)) { + /* separate sscanfs because length is working only for %s */ + ret = sscanf(data + offset, "%2s%n ", c, &nrchars); + if (ret != 1) + goto err; + + ret = sscanf(c, "%2x", &cmd); + if (ret != 1) + goto err; + + pattern[i] = (u8)cmd; + offset += nrchars; + i++; + } + + /* Each instruction is 16bit long. Check that length is even */ + if (i % 2) + goto err; + + program_size = i; + for (i = 0; i < program_size; i++) + lp55xx_write(chip, addr[idx] + i, pattern[i]); + + return 0; + +err: + dev_err(&chip->cl->dev, "wrong pattern format\n"); + return -EINVAL; +} + +static void lp5521_firmware_loaded(struct lp55xx_chip *chip) +{ + const struct firmware *fw = chip->fw; + + if (fw->size > LP5521_PROGRAM_LENGTH) { + dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n", + fw->size); + return; + } + + /* + * Program momery sequence + * 1) set engine mode to "LOAD" + * 2) write firmware data into program memory + */ + + lp5521_load_engine(chip); + lp5521_update_program_memory(chip, fw->data, fw->size); } static int lp5521_post_init_device(struct lp55xx_chip *chip) @@ -313,155 +406,6 @@ static void lp5521_led_brightness_work(struct work_struct *work) mutex_unlock(&chip->lock); } -/* Set engine mode and create appropriate sysfs attributes, if required. */ -static int lp5521_set_mode(struct lp5521_engine *engine, u8 mode) -{ - int ret = 0; - - /* if in that mode already do nothing, except for run */ - if (mode == engine->mode && mode != LP5521_CMD_RUN) - return 0; - - if (mode == LP5521_CMD_RUN) { - ret = lp5521_set_engine_mode(engine, LP5521_CMD_RUN); - } else if (mode == LP5521_CMD_LOAD) { - lp5521_set_engine_mode(engine, LP5521_CMD_DISABLED); - lp5521_set_engine_mode(engine, LP5521_CMD_LOAD); - } else if (mode == LP5521_CMD_DISABLED) { - lp5521_set_engine_mode(engine, LP5521_CMD_DISABLED); - } - - engine->mode = mode; - - return ret; -} - -static int lp5521_do_store_load(struct lp5521_engine *engine, - const char *buf, size_t len) -{ - struct lp5521_chip *chip = engine_to_lp5521(engine); - struct i2c_client *client = chip->client; - int ret, nrchars, offset = 0, i = 0; - char c[3]; - unsigned cmd; - u8 pattern[LP5521_PROGRAM_LENGTH] = {0}; - - while ((offset < len - 1) && (i < LP5521_PROGRAM_LENGTH)) { - /* separate sscanfs because length is working only for %s */ - ret = sscanf(buf + offset, "%2s%n ", c, &nrchars); - if (ret != 2) - goto fail; - ret = sscanf(c, "%2x", &cmd); - if (ret != 1) - goto fail; - pattern[i] = (u8)cmd; - - offset += nrchars; - i++; - } - - /* Each instruction is 16bit long. Check that length is even */ - if (i % 2) - goto fail; - - mutex_lock(&chip->lock); - if (engine->mode == LP5521_CMD_LOAD) - ret = lp5521_load_program(engine, pattern); - else - ret = -EINVAL; - mutex_unlock(&chip->lock); - - if (ret) { - dev_err(&client->dev, "failed loading pattern\n"); - return ret; - } - - return len; -fail: - dev_err(&client->dev, "wrong pattern format\n"); - return -EINVAL; -} - -static ssize_t store_engine_load(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5521_chip *chip = i2c_get_clientdata(client); - return lp5521_do_store_load(&chip->engines[nr - 1], buf, len); -} - -#define store_load(nr) \ -static ssize_t store_engine##nr##_load(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t len) \ -{ \ - return store_engine_load(dev, attr, buf, len, nr); \ -} -store_load(1) -store_load(2) -store_load(3) - -static ssize_t show_engine_mode(struct device *dev, - struct device_attribute *attr, - char *buf, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5521_chip *chip = i2c_get_clientdata(client); - switch (chip->engines[nr - 1].mode) { - case LP5521_CMD_RUN: - return sprintf(buf, "run\n"); - case LP5521_CMD_LOAD: - return sprintf(buf, "load\n"); - case LP5521_CMD_DISABLED: - return sprintf(buf, "disabled\n"); - default: - return sprintf(buf, "disabled\n"); - } -} - -#define show_mode(nr) \ -static ssize_t show_engine##nr##_mode(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_engine_mode(dev, attr, buf, nr); \ -} -show_mode(1) -show_mode(2) -show_mode(3) - -static ssize_t store_engine_mode(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5521_chip *chip = i2c_get_clientdata(client); - struct lp5521_engine *engine = &chip->engines[nr - 1]; - mutex_lock(&chip->lock); - - if (!strncmp(buf, "run", 3)) - lp5521_set_mode(engine, LP5521_CMD_RUN); - else if (!strncmp(buf, "load", 4)) - lp5521_set_mode(engine, LP5521_CMD_LOAD); - else if (!strncmp(buf, "disabled", 8)) - lp5521_set_mode(engine, LP5521_CMD_DISABLED); - - mutex_unlock(&chip->lock); - return len; -} - -#define store_mode(nr) \ -static ssize_t store_engine##nr##_mode(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t len) \ -{ \ - return store_engine_mode(dev, attr, buf, len, nr); \ -} -store_mode(1) -store_mode(2) -store_mode(3) - static ssize_t lp5521_selftest(struct device *dev, struct device_attribute *attr, char *buf) @@ -550,45 +494,11 @@ static void lp5521_run_led_pattern(int mode, struct lp5521_chip *chip) } } -static ssize_t store_led_pattern(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct lp5521_chip *chip = i2c_get_clientdata(to_i2c_client(dev)); - unsigned long val; - int ret; - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - lp5521_run_led_pattern(val, chip); - - return len; -} - /* device attributes */ -static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR, - show_engine1_mode, store_engine1_mode); -static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUSR, - show_engine2_mode, store_engine2_mode); -static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUSR, - show_engine3_mode, store_engine3_mode); -static DEVICE_ATTR(engine1_load, S_IWUSR, NULL, store_engine1_load); -static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load); -static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load); static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); -static DEVICE_ATTR(led_pattern, S_IWUSR, NULL, store_led_pattern); static struct attribute *lp5521_attributes[] = { - &dev_attr_engine1_mode.attr, - &dev_attr_engine2_mode.attr, - &dev_attr_engine3_mode.attr, &dev_attr_selftest.attr, - &dev_attr_engine1_load.attr, - &dev_attr_engine2_load.attr, - &dev_attr_engine3_load.attr, - &dev_attr_led_pattern.attr, NULL }; @@ -623,6 +533,8 @@ static struct lp55xx_device_config lp5521_cfg = { .post_init_device = lp5521_post_init_device, .brightness_work_fn = lp5521_led_brightness_work, .set_led_current = lp5521_set_led_current, + .firmware_cb = lp5521_firmware_loaded, + .run_engine = lp5521_run_engine, }; static int lp5521_probe(struct i2c_client *client, -- cgit v1.2.3 From db6eaf8388a413a5ee1b4547ce78506b9c6456b0 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:18:51 +0900 Subject: leds-lp5523: use generic firmware interface LP55xx common driver provides generic firmware interface for running a LED pattern. LP5521 and LP5523 have many device attributes for running patterns. This patch cleans up those complex code. Removed device attributes: engine1_mode engine2_mode engine3_mode engine1_load engine2_load engine3_load engine1_leds engine2_leds engine3_leds All device attributes and functions are replaced with two callback functions, 'firmware_cb' and 'run_engine'. New engine functions: lp5523_load/stop/run_engine(), lp5523_update_program_memory() and lp5523_wait_opmode_done() Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5523.c | 503 ++++++++++++++------------------------------- 1 file changed, 157 insertions(+), 346 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 70630156eff9..4b1ad9013a51 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "leds-lp55xx-common.h" @@ -108,20 +109,39 @@ #define LED_ACTIVE(mux, led) (!!(mux & (0x0001 << led))) #define SHIFT_MASK(id) (((id) - 1) * 2) +/* Memory Page Selection */ +#define LP5523_PAGE_ENG1 0 +#define LP5523_PAGE_ENG2 1 +#define LP5523_PAGE_ENG3 2 + +/* Program Memory Operations */ +#define LP5523_MODE_ENG1_M 0x30 /* Operation Mode Register */ +#define LP5523_MODE_ENG2_M 0x0C +#define LP5523_MODE_ENG3_M 0x03 +#define LP5523_LOAD_ENG1 0x10 +#define LP5523_LOAD_ENG2 0x04 +#define LP5523_LOAD_ENG3 0x01 + +#define LP5523_ENG1_IS_LOADING(mode) \ + ((mode & LP5523_MODE_ENG1_M) == LP5523_LOAD_ENG1) +#define LP5523_ENG2_IS_LOADING(mode) \ + ((mode & LP5523_MODE_ENG2_M) == LP5523_LOAD_ENG2) +#define LP5523_ENG3_IS_LOADING(mode) \ + ((mode & LP5523_MODE_ENG3_M) == LP5523_LOAD_ENG3) + +#define LP5523_EXEC_ENG1_M 0x30 /* Enable Register */ +#define LP5523_EXEC_ENG2_M 0x0C +#define LP5523_EXEC_ENG3_M 0x03 +#define LP5523_EXEC_M 0x3F +#define LP5523_RUN_ENG1 0x20 +#define LP5523_RUN_ENG2 0x08 +#define LP5523_RUN_ENG3 0x02 + enum lp5523_chip_id { LP5523, LP55231, }; -struct lp5523_engine { - int id; - u8 mode; - u8 prog_page; - u8 mux_page; - u16 led_mux; - u8 engine_mask; -}; - struct lp5523_led { int id; u8 chan_nr; @@ -135,13 +155,17 @@ struct lp5523_led { struct lp5523_chip { struct mutex lock; /* Serialize control */ struct i2c_client *client; - struct lp5523_engine engines[LP5523_ENGINES]; struct lp5523_led leds[LP5523_MAX_LEDS]; struct lp5523_platform_data *pdata; u8 num_channels; u8 num_leds; }; +static inline void lp5523_wait_opmode_done(void) +{ + usleep_range(1000, 2000); +} + static void lp5523_set_led_current(struct lp55xx_led *led, u8 led_current) { led->led_current = led_current; @@ -149,27 +173,6 @@ static void lp5523_set_led_current(struct lp55xx_led *led, u8 led_current) led_current); } -static inline struct lp5523_led *cdev_to_led(struct led_classdev *cdev) -{ - return container_of(cdev, struct lp5523_led, cdev); -} - -static inline struct lp5523_chip *engine_to_lp5523(struct lp5523_engine *engine) -{ - return container_of(engine, struct lp5523_chip, - engines[engine->id - 1]); -} - -static inline struct lp5523_chip *led_to_lp5523(struct lp5523_led *led) -{ - return container_of(led, struct lp5523_chip, - leds[led->id]); -} - -static void lp5523_set_mode(struct lp5523_engine *engine, u8 mode); -static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode); -static int lp5523_load_program(struct lp5523_engine *engine, const u8 *pattern); - static int lp5523_write(struct i2c_client *client, u8 reg, u8 value) { return i2c_smbus_write_byte_data(client, reg, value); @@ -212,177 +215,162 @@ static int lp5523_post_init_device(struct lp55xx_chip *chip) return lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_LSB, 0xff); } -static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode) +static void lp5523_load_engine(struct lp55xx_chip *chip) { - struct lp5523_chip *chip = engine_to_lp5523(engine); - struct i2c_client *client = chip->client; - int ret; - u8 engine_state; - - ret = lp5523_read(client, LP5523_REG_OP_MODE, &engine_state); - if (ret) - goto fail; - - engine_state &= ~(engine->engine_mask); - - /* set mode only for this engine */ - mode &= engine->engine_mask; - - engine_state |= mode; - - ret |= lp5523_write(client, LP5523_REG_OP_MODE, engine_state); -fail: - return ret; + enum lp55xx_engine_index idx = chip->engine_idx; + u8 mask[] = { + [LP55XX_ENGINE_1] = LP5523_MODE_ENG1_M, + [LP55XX_ENGINE_2] = LP5523_MODE_ENG2_M, + [LP55XX_ENGINE_3] = LP5523_MODE_ENG3_M, + }; + + u8 val[] = { + [LP55XX_ENGINE_1] = LP5523_LOAD_ENG1, + [LP55XX_ENGINE_2] = LP5523_LOAD_ENG2, + [LP55XX_ENGINE_3] = LP5523_LOAD_ENG3, + }; + + u8 page_sel[] = { + [LP55XX_ENGINE_1] = LP5523_PAGE_ENG1, + [LP55XX_ENGINE_2] = LP5523_PAGE_ENG2, + [LP55XX_ENGINE_3] = LP5523_PAGE_ENG3, + }; + + lp55xx_update_bits(chip, LP5523_REG_OP_MODE, mask[idx], val[idx]); + + lp5523_wait_opmode_done(); + + lp55xx_write(chip, LP5523_REG_PROG_PAGE_SEL, page_sel[idx]); } -static int lp5523_load_mux(struct lp5523_engine *engine, u16 mux) +static void lp5523_stop_engine(struct lp55xx_chip *chip) { - struct lp5523_chip *chip = engine_to_lp5523(engine); - struct i2c_client *client = chip->client; - int ret = 0; - - ret |= lp5523_set_engine_mode(engine, LP5523_CMD_LOAD); + lp55xx_write(chip, LP5523_REG_OP_MODE, 0); + lp5523_wait_opmode_done(); +} - ret |= lp5523_write(client, LP5523_REG_PROG_PAGE_SEL, engine->mux_page); - ret |= lp5523_write(client, LP5523_REG_PROG_MEM, - (u8)(mux >> 8)); - ret |= lp5523_write(client, LP5523_REG_PROG_MEM + 1, (u8)(mux)); - engine->led_mux = mux; +static void lp5523_turn_off_channels(struct lp55xx_chip *chip) +{ + int i; - return ret; + for (i = 0; i < LP5523_MAX_LEDS; i++) + lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + i, 0); } -static int lp5523_load_program(struct lp5523_engine *engine, const u8 *pattern) +static void lp5523_run_engine(struct lp55xx_chip *chip, bool start) { - struct lp5523_chip *chip = engine_to_lp5523(engine); - struct i2c_client *client = chip->client; + int ret; + u8 mode; + u8 exec; - int ret = 0; + /* stop engine */ + if (!start) { + lp5523_stop_engine(chip); + lp5523_turn_off_channels(chip); + return; + } - ret |= lp5523_set_engine_mode(engine, LP5523_CMD_LOAD); + /* + * To run the engine, + * operation mode and enable register should updated at the same time + */ - ret |= lp5523_write(client, LP5523_REG_PROG_PAGE_SEL, - engine->prog_page); - ret |= i2c_smbus_write_i2c_block_data(client, LP5523_REG_PROG_MEM, - LP5523_PROGRAM_LENGTH, pattern); + ret = lp55xx_read(chip, LP5523_REG_OP_MODE, &mode); + if (ret) + return; - return ret; -} + ret = lp55xx_read(chip, LP5523_REG_ENABLE, &exec); + if (ret) + return; -static int lp5523_run_program(struct lp5523_engine *engine) -{ - struct lp5523_chip *chip = engine_to_lp5523(engine); - struct i2c_client *client = chip->client; - int ret; + /* change operation mode to RUN only when each engine is loading */ + if (LP5523_ENG1_IS_LOADING(mode)) { + mode = (mode & ~LP5523_MODE_ENG1_M) | LP5523_RUN_ENG1; + exec = (exec & ~LP5523_EXEC_ENG1_M) | LP5523_RUN_ENG1; + } - ret = lp5523_write(client, LP5523_REG_ENABLE, - LP5523_CMD_RUN | LP5523_ENABLE); - if (ret) - goto fail; + if (LP5523_ENG2_IS_LOADING(mode)) { + mode = (mode & ~LP5523_MODE_ENG2_M) | LP5523_RUN_ENG2; + exec = (exec & ~LP5523_EXEC_ENG2_M) | LP5523_RUN_ENG2; + } - ret = lp5523_set_engine_mode(engine, LP5523_CMD_RUN); -fail: - return ret; + if (LP5523_ENG3_IS_LOADING(mode)) { + mode = (mode & ~LP5523_MODE_ENG3_M) | LP5523_RUN_ENG3; + exec = (exec & ~LP5523_EXEC_ENG3_M) | LP5523_RUN_ENG3; + } + + lp55xx_write(chip, LP5523_REG_OP_MODE, mode); + lp5523_wait_opmode_done(); + + lp55xx_update_bits(chip, LP5523_REG_ENABLE, LP5523_EXEC_M, exec); } -static int lp5523_mux_parse(const char *buf, u16 *mux, size_t len) +static int lp5523_update_program_memory(struct lp55xx_chip *chip, + const u8 *data, size_t size) { + u8 pattern[LP5523_PROGRAM_LENGTH] = {0}; + unsigned cmd; + char c[3]; + int update_size; + int nrchars; + int offset = 0; + int ret; int i; - u16 tmp_mux = 0; - - len = min_t(int, len, LP5523_MAX_LEDS); - for (i = 0; i < len; i++) { - switch (buf[i]) { - case '1': - tmp_mux |= (1 << i); - break; - case '0': - break; - case '\n': - i = len; - break; - default: - return -1; - } - } - *mux = tmp_mux; - return 0; -} + /* clear program memory before updating */ + for (i = 0; i < LP5523_PROGRAM_LENGTH; i++) + lp55xx_write(chip, LP5523_REG_PROG_MEM + i, 0); -static void lp5523_mux_to_array(u16 led_mux, char *array) -{ - int i, pos = 0; - for (i = 0; i < LP5523_MAX_LEDS; i++) - pos += sprintf(array + pos, "%x", LED_ACTIVE(led_mux, i)); + i = 0; + while ((offset < size - 1) && (i < LP5523_PROGRAM_LENGTH)) { + /* separate sscanfs because length is working only for %s */ + ret = sscanf(data + offset, "%2s%n ", c, &nrchars); + if (ret != 1) + goto err; - array[pos] = '\0'; -} + ret = sscanf(c, "%2x", &cmd); + if (ret != 1) + goto err; -/*--------------------------------------------------------------*/ -/* Sysfs interface */ -/*--------------------------------------------------------------*/ + pattern[i] = (u8)cmd; + offset += nrchars; + i++; + } -static ssize_t show_engine_leds(struct device *dev, - struct device_attribute *attr, - char *buf, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5523_chip *chip = i2c_get_clientdata(client); - char mux[LP5523_MAX_LEDS + 1]; + /* Each instruction is 16bit long. Check that length is even */ + if (i % 2) + goto err; - lp5523_mux_to_array(chip->engines[nr - 1].led_mux, mux); + update_size = i; + for (i = 0; i < update_size; i++) + lp55xx_write(chip, LP5523_REG_PROG_MEM + i, pattern[i]); - return sprintf(buf, "%s\n", mux); -} + return 0; -#define show_leds(nr) \ -static ssize_t show_engine##nr##_leds(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_engine_leds(dev, attr, buf, nr); \ +err: + dev_err(&chip->cl->dev, "wrong pattern format\n"); + return -EINVAL; } -show_leds(1) -show_leds(2) -show_leds(3) -static ssize_t store_engine_leds(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len, int nr) +static void lp5523_firmware_loaded(struct lp55xx_chip *chip) { - struct i2c_client *client = to_i2c_client(dev); - struct lp5523_chip *chip = i2c_get_clientdata(client); - u16 mux = 0; - ssize_t ret; - - if (lp5523_mux_parse(buf, &mux, len)) - return -EINVAL; + const struct firmware *fw = chip->fw; - mutex_lock(&chip->lock); - ret = -EINVAL; - if (chip->engines[nr - 1].mode != LP5523_CMD_LOAD) - goto leave; - - if (lp5523_load_mux(&chip->engines[nr - 1], mux)) - goto leave; + if (fw->size > LP5523_PROGRAM_LENGTH) { + dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n", + fw->size); + return; + } - ret = len; -leave: - mutex_unlock(&chip->lock); - return ret; -} + /* + * Program momery sequence + * 1) set engine mode to "LOAD" + * 2) write firmware data into program memory + */ -#define store_leds(nr) \ -static ssize_t store_engine##nr##_leds(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t len) \ -{ \ - return store_engine_leds(dev, attr, buf, len, nr); \ + lp5523_load_engine(chip); + lp5523_update_program_memory(chip, fw->data, fw->size); } -store_leds(1) -store_leds(2) -store_leds(3) static ssize_t lp5523_selftest(struct device *dev, struct device_attribute *attr, @@ -485,159 +473,10 @@ static void lp5523_led_brightness_work(struct work_struct *work) mutex_unlock(&chip->lock); } -static int lp5523_do_store_load(struct lp5523_engine *engine, - const char *buf, size_t len) -{ - struct lp5523_chip *chip = engine_to_lp5523(engine); - struct i2c_client *client = chip->client; - int ret, nrchars, offset = 0, i = 0; - char c[3]; - unsigned cmd; - u8 pattern[LP5523_PROGRAM_LENGTH] = {0}; - - if (engine->mode != LP5523_CMD_LOAD) - return -EINVAL; - - while ((offset < len - 1) && (i < LP5523_PROGRAM_LENGTH)) { - /* separate sscanfs because length is working only for %s */ - ret = sscanf(buf + offset, "%2s%n ", c, &nrchars); - ret = sscanf(c, "%2x", &cmd); - if (ret != 1) - goto fail; - pattern[i] = (u8)cmd; - - offset += nrchars; - i++; - } - - /* Each instruction is 16bit long. Check that length is even */ - if (i % 2) - goto fail; - - mutex_lock(&chip->lock); - ret = lp5523_load_program(engine, pattern); - mutex_unlock(&chip->lock); - - if (ret) { - dev_err(&client->dev, "failed loading pattern\n"); - return ret; - } - - return len; -fail: - dev_err(&client->dev, "wrong pattern format\n"); - return -EINVAL; -} - -static ssize_t store_engine_load(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5523_chip *chip = i2c_get_clientdata(client); - return lp5523_do_store_load(&chip->engines[nr - 1], buf, len); -} - -#define store_load(nr) \ -static ssize_t store_engine##nr##_load(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t len) \ -{ \ - return store_engine_load(dev, attr, buf, len, nr); \ -} -store_load(1) -store_load(2) -store_load(3) - -static ssize_t show_engine_mode(struct device *dev, - struct device_attribute *attr, - char *buf, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5523_chip *chip = i2c_get_clientdata(client); - switch (chip->engines[nr - 1].mode) { - case LP5523_CMD_RUN: - return sprintf(buf, "run\n"); - case LP5523_CMD_LOAD: - return sprintf(buf, "load\n"); - case LP5523_CMD_DISABLED: - return sprintf(buf, "disabled\n"); - default: - return sprintf(buf, "disabled\n"); - } -} - -#define show_mode(nr) \ -static ssize_t show_engine##nr##_mode(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_engine_mode(dev, attr, buf, nr); \ -} -show_mode(1) -show_mode(2) -show_mode(3) - -static ssize_t store_engine_mode(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5523_chip *chip = i2c_get_clientdata(client); - struct lp5523_engine *engine = &chip->engines[nr - 1]; - mutex_lock(&chip->lock); - - if (!strncmp(buf, "run", 3)) - lp5523_set_mode(engine, LP5523_CMD_RUN); - else if (!strncmp(buf, "load", 4)) - lp5523_set_mode(engine, LP5523_CMD_LOAD); - else if (!strncmp(buf, "disabled", 8)) - lp5523_set_mode(engine, LP5523_CMD_DISABLED); - - mutex_unlock(&chip->lock); - return len; -} - -#define store_mode(nr) \ -static ssize_t store_engine##nr##_mode(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t len) \ -{ \ - return store_engine_mode(dev, attr, buf, len, nr); \ -} -store_mode(1) -store_mode(2) -store_mode(3) - -/* device attributes */ -static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR, - show_engine1_mode, store_engine1_mode); -static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUSR, - show_engine2_mode, store_engine2_mode); -static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUSR, - show_engine3_mode, store_engine3_mode); -static DEVICE_ATTR(engine1_leds, S_IRUGO | S_IWUSR, - show_engine1_leds, store_engine1_leds); -static DEVICE_ATTR(engine2_leds, S_IRUGO | S_IWUSR, - show_engine2_leds, store_engine2_leds); -static DEVICE_ATTR(engine3_leds, S_IRUGO | S_IWUSR, - show_engine3_leds, store_engine3_leds); -static DEVICE_ATTR(engine1_load, S_IWUSR, NULL, store_engine1_load); -static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load); -static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load); static DEVICE_ATTR(selftest, S_IRUGO, lp5523_selftest, NULL); static struct attribute *lp5523_attributes[] = { - &dev_attr_engine1_mode.attr, - &dev_attr_engine2_mode.attr, - &dev_attr_engine3_mode.attr, &dev_attr_selftest.attr, - &dev_attr_engine1_load.attr, - &dev_attr_engine1_leds.attr, - &dev_attr_engine2_load.attr, - &dev_attr_engine2_leds.attr, - &dev_attr_engine3_load.attr, - &dev_attr_engine3_leds.attr, NULL, }; @@ -664,36 +503,6 @@ static void lp5523_unregister_sysfs(struct i2c_client *client) sysfs_remove_group(&dev->kobj, &lp5523_group); } -/*--------------------------------------------------------------*/ -/* Set chip operating mode */ -/*--------------------------------------------------------------*/ -static void lp5523_set_mode(struct lp5523_engine *engine, u8 mode) -{ - /* if in that mode already do nothing, except for run */ - if (mode == engine->mode && mode != LP5523_CMD_RUN) - return; - - switch (mode) { - case LP5523_CMD_RUN: - lp5523_run_program(engine); - break; - case LP5523_CMD_LOAD: - lp5523_set_engine_mode(engine, LP5523_CMD_DISABLED); - lp5523_set_engine_mode(engine, LP5523_CMD_LOAD); - break; - case LP5523_CMD_DISABLED: - lp5523_set_engine_mode(engine, LP5523_CMD_DISABLED); - break; - default: - return; - } - - engine->mode = mode; -} - -/*--------------------------------------------------------------*/ -/* Probe, Attach, Remove */ -/*--------------------------------------------------------------*/ /* Chip specific configurations */ static struct lp55xx_device_config lp5523_cfg = { .reset = { @@ -708,6 +517,8 @@ static struct lp55xx_device_config lp5523_cfg = { .post_init_device = lp5523_post_init_device, .brightness_work_fn = lp5523_led_brightness_work, .set_led_current = lp5523_set_led_current, + .firmware_cb = lp5523_firmware_loaded, + .run_engine = lp5523_run_engine, }; static int lp5523_probe(struct i2c_client *client, -- cgit v1.2.3 From 240085e255cd2818aff2ccde3066b7db1f29076a Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:20:01 +0900 Subject: leds-lp55xx: support device specific attributes To support device specific attributes, new common driver function is added. Eventually those are created on registering the sysfs with common dev attrs. Furthermore, this patch makes adding device attributes simple in each driver. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp55xx-common.c | 13 ++++++++++++- drivers/leds/leds-lp55xx-common.h | 4 ++++ 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 578902ab604f..9638ad4dc635 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -493,8 +493,19 @@ EXPORT_SYMBOL_GPL(lp55xx_unregister_leds); int lp55xx_register_sysfs(struct lp55xx_chip *chip) { struct device *dev = &chip->cl->dev; + struct lp55xx_device_config *cfg = chip->cfg; + int ret; + + if (!cfg->run_engine || !cfg->firmware_cb) + goto dev_specific_attrs; + + ret = sysfs_create_group(&dev->kobj, &lp55xx_engine_attr_group); + if (ret) + return ret; - return sysfs_create_group(&dev->kobj, &lp55xx_engine_attr_group); +dev_specific_attrs: + return cfg->dev_attr_group ? + sysfs_create_group(&dev->kobj, cfg->dev_attr_group) : 0; } EXPORT_SYMBOL_GPL(lp55xx_register_sysfs); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 8473abf9830c..64eb78da1c4b 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -45,6 +45,7 @@ struct lp55xx_reg { * @set_led_current : LED current set function * @firmware_cb : Call function when the firmware is loaded * @run_engine : Run internal engine for pattern + * @dev_attr_group : Device specific attributes */ struct lp55xx_device_config { const struct lp55xx_reg reset; @@ -65,6 +66,9 @@ struct lp55xx_device_config { /* used for running firmware LED patterns */ void (*run_engine) (struct lp55xx_chip *chip, bool start); + + /* additional device specific attributes */ + const struct attribute_group *dev_attr_group; }; /* -- cgit v1.2.3 From e73c0ce6beaa71bee39b2d11bff0253be84c71a9 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:20:45 +0900 Subject: leds-lp55xx: use common device attribute driver function lp5521/5523_register_sysfs() are replaced with lp55xx common driver function, lp55xx_register_sysfs(). Chip specific device attributes are configurable using 'dev_attr_group'. Error condition name is changed: use specific error condition, 'err_register_sysfs' rather than unclear name, 'fail2'. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 17 +++++++---------- drivers/leds/leds-lp5523.c | 23 +++++++---------------- 2 files changed, 14 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 89371e065c13..abc33139e6fa 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -506,12 +506,6 @@ static const struct attribute_group lp5521_group = { .attrs = lp5521_attributes, }; -static int lp5521_register_sysfs(struct i2c_client *client) -{ - struct device *dev = &client->dev; - return sysfs_create_group(&dev->kobj, &lp5521_group); -} - static void lp5521_unregister_sysfs(struct i2c_client *client) { struct device *dev = &client->dev; @@ -535,6 +529,7 @@ static struct lp55xx_device_config lp5521_cfg = { .set_led_current = lp5521_set_led_current, .firmware_cb = lp5521_firmware_loaded, .run_engine = lp5521_run_engine, + .dev_attr_group = &lp5521_group, }; static int lp5521_probe(struct i2c_client *client, @@ -577,13 +572,15 @@ static int lp5521_probe(struct i2c_client *client, if (ret) goto err_register_leds; - ret = lp5521_register_sysfs(client); + ret = lp55xx_register_sysfs(chip); if (ret) { dev_err(&client->dev, "registering sysfs failed\n"); - goto fail2; + goto err_register_sysfs; } - return ret; -fail2: + + return 0; + +err_register_sysfs: lp55xx_unregister_leds(led, chip); err_register_leds: lp55xx_deinit_device(chip); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 4b1ad9013a51..4192a1ec6062 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -484,18 +484,6 @@ static const struct attribute_group lp5523_group = { .attrs = lp5523_attributes, }; -static int lp5523_register_sysfs(struct i2c_client *client) -{ - struct device *dev = &client->dev; - int ret; - - ret = sysfs_create_group(&dev->kobj, &lp5523_group); - if (ret < 0) - return ret; - - return 0; -} - static void lp5523_unregister_sysfs(struct i2c_client *client) { struct device *dev = &client->dev; @@ -519,6 +507,7 @@ static struct lp55xx_device_config lp5523_cfg = { .set_led_current = lp5523_set_led_current, .firmware_cb = lp5523_firmware_loaded, .run_engine = lp5523_run_engine, + .dev_attr_group = &lp5523_group, }; static int lp5523_probe(struct i2c_client *client, @@ -561,13 +550,15 @@ static int lp5523_probe(struct i2c_client *client, if (ret) goto err_register_leds; - ret = lp5523_register_sysfs(client); + ret = lp55xx_register_sysfs(chip); if (ret) { dev_err(&client->dev, "registering sysfs failed\n"); - goto fail2; + goto err_register_sysfs; } - return ret; -fail2: + + return 0; + +err_register_sysfs: lp55xx_unregister_leds(led, chip); err_register_leds: lp55xx_deinit_device(chip); -- cgit v1.2.3 From 9ca3bd8022d76a0d1b386cedcecaf49004a58644 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:21:43 +0900 Subject: leds-lp55xx: code refactoring on selftest function LP5521 and LP5523 have a selftest function which is run via the sysfs. Use lp55xx driver data and R/W functions rather than lp5521/5523 private data and functions. Additionally, if-statements are changed for code simplicity. Unused functions, lp5521/5523_read() are removed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 30 ++++++++++---------------- drivers/leds/leds-lp5523.c | 53 +++++++++++++++++----------------------------- 2 files changed, 31 insertions(+), 52 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index abc33139e6fa..1f6d9c7eb4a2 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -168,18 +168,6 @@ static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value) return i2c_smbus_write_byte_data(client, reg, value); } -static int lp5521_read(struct i2c_client *client, u8 reg, u8 *buf) -{ - s32 ret; - - ret = i2c_smbus_read_byte_data(client, reg); - if (ret < 0) - return ret; - - *buf = ret; - return 0; -} - static void lp5521_load_engine(struct lp55xx_chip *chip) { enum lp55xx_engine_index idx = chip->engine_idx; @@ -378,19 +366,23 @@ static int lp5521_post_init_device(struct lp55xx_chip *chip) return 0; } -static int lp5521_run_selftest(struct lp5521_chip *chip, char *buf) +static int lp5521_run_selftest(struct lp55xx_chip *chip, char *buf) { + struct lp55xx_platform_data *pdata = chip->pdata; int ret; u8 status; - ret = lp5521_read(chip->client, LP5521_REG_STATUS, &status); + ret = lp55xx_read(chip, LP5521_REG_STATUS, &status); if (ret < 0) return ret; + if (pdata->clock_mode != LP55XX_CLOCK_EXT) + return 0; + /* Check that ext clock is really in use if requested */ - if (chip->pdata && chip->pdata->clock_mode == LP5521_CLOCK_EXT) - if ((status & LP5521_EXT_CLK_USED) == 0) - return -EIO; + if ((status & LP5521_EXT_CLK_USED) == 0) + return -EIO; + return 0; } @@ -410,8 +402,8 @@ static ssize_t lp5521_selftest(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lp5521_chip *chip = i2c_get_clientdata(client); + struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); + struct lp55xx_chip *chip = led->chip; int ret; mutex_lock(&chip->lock); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 4192a1ec6062..577059934f58 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -178,17 +178,6 @@ static int lp5523_write(struct i2c_client *client, u8 reg, u8 value) return i2c_smbus_write_byte_data(client, reg, value); } -static int lp5523_read(struct i2c_client *client, u8 reg, u8 *buf) -{ - s32 ret = i2c_smbus_read_byte_data(client, reg); - - if (ret < 0) - return ret; - - *buf = ret; - return 0; -} - static int lp5523_post_init_device(struct lp55xx_chip *chip) { int ret; @@ -376,35 +365,35 @@ static ssize_t lp5523_selftest(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lp5523_chip *chip = i2c_get_clientdata(client); + struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); + struct lp55xx_chip *chip = led->chip; + struct lp55xx_platform_data *pdata = chip->pdata; int i, ret, pos = 0; - int led = 0; u8 status, adc, vdd; mutex_lock(&chip->lock); - ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status); + ret = lp55xx_read(chip, LP5523_REG_STATUS, &status); if (ret < 0) goto fail; /* Check that ext clock is really in use if requested */ - if ((chip->pdata) && (chip->pdata->clock_mode == LP5523_CLOCK_EXT)) + if (pdata->clock_mode == LP55XX_CLOCK_EXT) { if ((status & LP5523_EXT_CLK_USED) == 0) goto fail; + } /* Measure VDD (i.e. VBAT) first (channel 16 corresponds to VDD) */ - lp5523_write(chip->client, LP5523_REG_LED_TEST_CTRL, - LP5523_EN_LEDTEST | 16); + lp55xx_write(chip, LP5523_REG_LED_TEST_CTRL, LP5523_EN_LEDTEST | 16); usleep_range(3000, 6000); /* ADC conversion time is typically 2.7 ms */ - ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status); + ret = lp55xx_read(chip, LP5523_REG_STATUS, &status); if (ret < 0) goto fail; if (!(status & LP5523_LEDTEST_DONE)) usleep_range(3000, 6000); /* Was not ready. Wait little bit */ - ret = lp5523_read(chip->client, LP5523_REG_LED_TEST_ADC, &vdd); + ret = lp55xx_read(chip, LP5523_REG_LED_TEST_ADC, &vdd); if (ret < 0) goto fail; @@ -412,41 +401,39 @@ static ssize_t lp5523_selftest(struct device *dev, for (i = 0; i < LP5523_MAX_LEDS; i++) { /* Skip non-existing channels */ - if (chip->pdata->led_config[i].led_current == 0) + if (pdata->led_config[i].led_current == 0) continue; /* Set default current */ - lp5523_write(chip->client, - LP5523_REG_LED_CURRENT_BASE + i, - chip->pdata->led_config[i].led_current); + lp55xx_write(chip, LP5523_REG_LED_CURRENT_BASE + i, + pdata->led_config[i].led_current); - lp5523_write(chip->client, LP5523_REG_LED_PWM_BASE + i, 0xff); + lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + i, 0xff); /* let current stabilize 2 - 4ms before measurements start */ usleep_range(2000, 4000); - lp5523_write(chip->client, - LP5523_REG_LED_TEST_CTRL, + lp55xx_write(chip, LP5523_REG_LED_TEST_CTRL, LP5523_EN_LEDTEST | i); /* ADC conversion time is 2.7 ms typically */ usleep_range(3000, 6000); - ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status); + ret = lp55xx_read(chip, LP5523_REG_STATUS, &status); if (ret < 0) goto fail; if (!(status & LP5523_LEDTEST_DONE)) usleep_range(3000, 6000);/* Was not ready. Wait. */ - ret = lp5523_read(chip->client, LP5523_REG_LED_TEST_ADC, &adc); + + ret = lp55xx_read(chip, LP5523_REG_LED_TEST_ADC, &adc); if (ret < 0) goto fail; if (adc >= vdd || adc < LP5523_ADC_SHORTCIRC_LIM) pos += sprintf(buf + pos, "LED %d FAIL\n", i); - lp5523_write(chip->client, LP5523_REG_LED_PWM_BASE + i, 0x00); + lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + i, 0x00); /* Restore current */ - lp5523_write(chip->client, - LP5523_REG_LED_CURRENT_BASE + i, - chip->leds[led].led_current); + lp55xx_write(chip, LP5523_REG_LED_CURRENT_BASE + i, + led->led_current); led++; } if (pos == 0) -- cgit v1.2.3 From ba6fa84651ff9a609e0ceb8265e3335ab6ed656d Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:23:04 +0900 Subject: leds-lp55xx: add new function for removing device attribtues lp55xx_unregister_sysfs() is used for removing lp55xx device attributes. Chip specific and engine attributes are removed on unloading the driver. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp55xx-common.c | 12 ++++++++++++ drivers/leds/leds-lp55xx-common.h | 1 + 2 files changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 9638ad4dc635..782ab84fe65f 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -509,6 +509,18 @@ dev_specific_attrs: } EXPORT_SYMBOL_GPL(lp55xx_register_sysfs); +void lp55xx_unregister_sysfs(struct lp55xx_chip *chip) +{ + struct device *dev = &chip->cl->dev; + struct lp55xx_device_config *cfg = chip->cfg; + + if (cfg->dev_attr_group) + sysfs_remove_group(&dev->kobj, cfg->dev_attr_group); + + sysfs_remove_group(&dev->kobj, &lp55xx_engine_attr_group); +} +EXPORT_SYMBOL_GPL(lp55xx_unregister_sysfs); + MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP55xx Common Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 64eb78da1c4b..ece4761a1302 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -129,5 +129,6 @@ extern void lp55xx_unregister_leds(struct lp55xx_led *led, /* common device attributes functions */ extern int lp55xx_register_sysfs(struct lp55xx_chip *chip); +extern void lp55xx_unregister_sysfs(struct lp55xx_chip *chip); #endif /* _LEDS_LP55XX_COMMON_H */ -- cgit v1.2.3 From 87cc4bde2a97cd8acccf34f333d0980dc5c2aa8a Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:23:51 +0900 Subject: leds-lp55xx: clean up _remove() Replace lp5521/5523_unregister_sysfs() with lp55xx_unregister_sysfs(). On unloading the driver, running engines should be stopped. Use explicit driver function, lp5521/5523_stop_engine(). Unused functions are removed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 79 ++-------------------------------------------- drivers/leds/leds-lp5523.c | 19 ++--------- 2 files changed, 4 insertions(+), 94 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 1f6d9c7eb4a2..46721c3a8e8b 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -412,36 +412,6 @@ static ssize_t lp5521_selftest(struct device *dev, return sprintf(buf, "%s\n", ret ? "FAIL" : "OK"); } -static void lp5521_clear_program_memory(struct i2c_client *cl) -{ - int i; - u8 rgb_mem[] = { - LP5521_REG_R_PROG_MEM, - LP5521_REG_G_PROG_MEM, - LP5521_REG_B_PROG_MEM, - }; - - for (i = 0; i < ARRAY_SIZE(rgb_mem); i++) { - lp5521_write(cl, rgb_mem[i], 0); - lp5521_write(cl, rgb_mem[i] + 1, 0); - } -} - -static void lp5521_write_program_memory(struct i2c_client *cl, - u8 base, u8 *rgb, int size) -{ - int i; - - if (!rgb || size <= 0) - return; - - for (i = 0; i < size; i++) - lp5521_write(cl, base + i, *(rgb + i)); - - lp5521_write(cl, base + i, 0); - lp5521_write(cl, base + i + 1, 0); -} - static inline struct lp5521_led_pattern *lp5521_get_pattern (struct lp5521_chip *chip, u8 offset) { @@ -450,42 +420,6 @@ static inline struct lp5521_led_pattern *lp5521_get_pattern return ptn; } -static void lp5521_run_led_pattern(int mode, struct lp5521_chip *chip) -{ - struct lp5521_led_pattern *ptn; - struct i2c_client *cl = chip->client; - int num_patterns = chip->pdata->num_patterns; - - if (mode > num_patterns || !(chip->pdata->patterns)) - return; - - if (mode == PATTERN_OFF) { - lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_DEFAULT); - usleep_range(1000, 2000); - lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); - } else { - ptn = lp5521_get_pattern(chip, mode); - if (!ptn) - return; - - lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_LOAD); - usleep_range(1000, 2000); - - lp5521_clear_program_memory(cl); - - lp5521_write_program_memory(cl, LP5521_REG_R_PROG_MEM, - ptn->r, ptn->size_r); - lp5521_write_program_memory(cl, LP5521_REG_G_PROG_MEM, - ptn->g, ptn->size_g); - lp5521_write_program_memory(cl, LP5521_REG_B_PROG_MEM, - ptn->b, ptn->size_b); - - lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_RUN); - usleep_range(1000, 2000); - lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM); - } -} - /* device attributes */ static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); @@ -498,13 +432,6 @@ static const struct attribute_group lp5521_group = { .attrs = lp5521_attributes, }; -static void lp5521_unregister_sysfs(struct i2c_client *client) -{ - struct device *dev = &client->dev; - - sysfs_remove_group(&dev->kobj, &lp5521_group); -} - /* Chip specific configurations */ static struct lp55xx_device_config lp5521_cfg = { .reset = { @@ -582,13 +509,11 @@ err_init: static int lp5521_remove(struct i2c_client *client) { - struct lp5521_chip *old_chip = i2c_get_clientdata(client); struct lp55xx_led *led = i2c_get_clientdata(client); struct lp55xx_chip *chip = led->chip; - lp5521_run_led_pattern(PATTERN_OFF, old_chip); - lp5521_unregister_sysfs(client); - + lp5521_stop_engine(chip); + lp55xx_unregister_sysfs(chip); lp55xx_unregister_leds(led, chip); lp55xx_deinit_device(chip); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 577059934f58..cf587c1b2c41 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -173,11 +173,6 @@ static void lp5523_set_led_current(struct lp55xx_led *led, u8 led_current) led_current); } -static int lp5523_write(struct i2c_client *client, u8 reg, u8 value) -{ - return i2c_smbus_write_byte_data(client, reg, value); -} - static int lp5523_post_init_device(struct lp55xx_chip *chip) { int ret; @@ -471,13 +466,6 @@ static const struct attribute_group lp5523_group = { .attrs = lp5523_attributes, }; -static void lp5523_unregister_sysfs(struct i2c_client *client) -{ - struct device *dev = &client->dev; - - sysfs_remove_group(&dev->kobj, &lp5523_group); -} - /* Chip specific configurations */ static struct lp55xx_device_config lp5523_cfg = { .reset = { @@ -558,11 +546,8 @@ static int lp5523_remove(struct i2c_client *client) struct lp55xx_led *led = i2c_get_clientdata(client); struct lp55xx_chip *chip = led->chip; - /* Disable engine mode */ - lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_DISABLED); - - lp5523_unregister_sysfs(client); - + lp5523_stop_engine(chip); + lp55xx_unregister_sysfs(chip); lp55xx_unregister_leds(led, chip); lp55xx_deinit_device(chip); -- cgit v1.2.3 From 93ca4093adb757d5140071e72b2e9bfbb519b6c1 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:24:37 +0900 Subject: leds-lp55xx: clean up unused data and functions Old data structures and I2C function are not used any more. Each driver uses the lp55xx common data and functions. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 32 -------------------------------- drivers/leds/leds-lp5523.c | 19 ------------------- 2 files changed, 51 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 46721c3a8e8b..f05eb6e31d58 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -125,25 +125,6 @@ #define LP5521_RUN_G 0x08 #define LP5521_RUN_B 0x02 -struct lp5521_led { - int id; - u8 chan_nr; - u8 led_current; - u8 max_current; - struct led_classdev cdev; - struct work_struct brightness_work; - u8 brightness; -}; - -struct lp5521_chip { - struct lp5521_platform_data *pdata; - struct mutex lock; /* Serialize control */ - struct i2c_client *client; - struct lp5521_led leds[LP5521_MAX_LEDS]; - u8 num_channels; - u8 num_leds; -}; - static inline void lp5521_wait_opmode_done(void) { /* operation mode change needs to be longer than 153 us */ @@ -163,11 +144,6 @@ static void lp5521_set_led_current(struct lp55xx_led *led, u8 led_current) led_current); } -static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value) -{ - return i2c_smbus_write_byte_data(client, reg, value); -} - static void lp5521_load_engine(struct lp55xx_chip *chip) { enum lp55xx_engine_index idx = chip->engine_idx; @@ -412,14 +388,6 @@ static ssize_t lp5521_selftest(struct device *dev, return sprintf(buf, "%s\n", ret ? "FAIL" : "OK"); } -static inline struct lp5521_led_pattern *lp5521_get_pattern - (struct lp5521_chip *chip, u8 offset) -{ - struct lp5521_led_pattern *ptn; - ptn = chip->pdata->patterns + (offset - 1); - return ptn; -} - /* device attributes */ static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index cf587c1b2c41..b14bde2db24f 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -142,25 +142,6 @@ enum lp5523_chip_id { LP55231, }; -struct lp5523_led { - int id; - u8 chan_nr; - u8 led_current; - u8 max_current; - struct led_classdev cdev; - struct work_struct brightness_work; - u8 brightness; -}; - -struct lp5523_chip { - struct mutex lock; /* Serialize control */ - struct i2c_client *client; - struct lp5523_led leds[LP5523_MAX_LEDS]; - struct lp5523_platform_data *pdata; - u8 num_channels; - u8 num_leds; -}; - static inline void lp5523_wait_opmode_done(void) { usleep_range(1000, 2000); -- cgit v1.2.3 From 12f022d27bcdd606527ae1b3c9ad20cc8c90ce98 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:25:26 +0900 Subject: leds-lp55xx: clean up definitions Remove unused definitions and change hex values to capital letters Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 26 +++------------------ drivers/leds/leds-lp5523.c | 56 ++++++++-------------------------------------- 2 files changed, 12 insertions(+), 70 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index f05eb6e31d58..b2a0d877cdc1 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -39,18 +39,9 @@ #include "leds-lp55xx-common.h" -#define LP5521_PROGRAM_LENGTH 32 /* in bytes */ - -#define LP5521_MAX_LEDS 3 /* Maximum number of LEDs */ -#define LP5521_MAX_ENGINES 3 /* Maximum number of engines */ - -#define LP5521_ENG_MASK_BASE 0x30 /* 00110000 */ -#define LP5521_ENG_STATUS_MASK 0x07 /* 00000111 */ - -#define LP5521_CMD_LOAD 0x15 /* 00010101 */ -#define LP5521_CMD_RUN 0x2a /* 00101010 */ -#define LP5521_CMD_DIRECT 0x3f /* 00111111 */ -#define LP5521_CMD_DISABLED 0x00 /* 00000000 */ +#define LP5521_PROGRAM_LENGTH 32 +#define LP5521_MAX_LEDS 3 +#define LP5521_CMD_DIRECT 0x3F /* Registers */ #define LP5521_REG_ENABLE 0x00 @@ -62,22 +53,14 @@ #define LP5521_REG_G_CURRENT 0x06 #define LP5521_REG_B_CURRENT 0x07 #define LP5521_REG_CONFIG 0x08 -#define LP5521_REG_R_CHANNEL_PC 0x09 -#define LP5521_REG_G_CHANNEL_PC 0x0A -#define LP5521_REG_B_CHANNEL_PC 0x0B #define LP5521_REG_STATUS 0x0C #define LP5521_REG_RESET 0x0D -#define LP5521_REG_GPO 0x0E #define LP5521_REG_R_PROG_MEM 0x10 #define LP5521_REG_G_PROG_MEM 0x30 #define LP5521_REG_B_PROG_MEM 0x50 -#define LP5521_PROG_MEM_BASE LP5521_REG_R_PROG_MEM -#define LP5521_PROG_MEM_SIZE 0x20 - /* Base register to set LED current */ #define LP5521_REG_LED_CURRENT_BASE LP5521_REG_R_CURRENT - /* Base register to set the brightness */ #define LP5521_REG_LED_PWM_BASE LP5521_REG_R_PWM @@ -96,9 +79,6 @@ /* default R channel current register value */ #define LP5521_REG_R_CURR_DEFAULT 0xAF -/* Pattern Mode */ -#define PATTERN_OFF 0 - /* Reset register value */ #define LP5521_RESET 0xFF diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index b14bde2db24f..e145ce984b1d 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -39,76 +39,38 @@ #include "leds-lp55xx-common.h" +#define LP5523_PROGRAM_LENGTH 32 +#define LP5523_MAX_LEDS 9 + +/* Registers */ #define LP5523_REG_ENABLE 0x00 #define LP5523_REG_OP_MODE 0x01 -#define LP5523_REG_RATIOMETRIC_MSB 0x02 -#define LP5523_REG_RATIOMETRIC_LSB 0x03 #define LP5523_REG_ENABLE_LEDS_MSB 0x04 #define LP5523_REG_ENABLE_LEDS_LSB 0x05 -#define LP5523_REG_LED_CNTRL_BASE 0x06 #define LP5523_REG_LED_PWM_BASE 0x16 #define LP5523_REG_LED_CURRENT_BASE 0x26 #define LP5523_REG_CONFIG 0x36 -#define LP5523_REG_CHANNEL1_PC 0x37 -#define LP5523_REG_CHANNEL2_PC 0x38 -#define LP5523_REG_CHANNEL3_PC 0x39 -#define LP5523_REG_STATUS 0x3a -#define LP5523_REG_GPO 0x3b -#define LP5523_REG_VARIABLE 0x3c -#define LP5523_REG_RESET 0x3d -#define LP5523_REG_TEMP_CTRL 0x3e -#define LP5523_REG_TEMP_READ 0x3f -#define LP5523_REG_TEMP_WRITE 0x40 +#define LP5523_REG_STATUS 0x3A +#define LP5523_REG_RESET 0x3D #define LP5523_REG_LED_TEST_CTRL 0x41 #define LP5523_REG_LED_TEST_ADC 0x42 -#define LP5523_REG_ENG1_VARIABLE 0x45 -#define LP5523_REG_ENG2_VARIABLE 0x46 -#define LP5523_REG_ENG3_VARIABLE 0x47 -#define LP5523_REG_MASTER_FADER1 0x48 -#define LP5523_REG_MASTER_FADER2 0x49 -#define LP5523_REG_MASTER_FADER3 0x4a -#define LP5523_REG_CH1_PROG_START 0x4c -#define LP5523_REG_CH2_PROG_START 0x4d -#define LP5523_REG_CH3_PROG_START 0x4e -#define LP5523_REG_PROG_PAGE_SEL 0x4f +#define LP5523_REG_PROG_PAGE_SEL 0x4F #define LP5523_REG_PROG_MEM 0x50 -#define LP5523_CMD_LOAD 0x15 /* 00010101 */ -#define LP5523_CMD_RUN 0x2a /* 00101010 */ -#define LP5523_CMD_DISABLED 0x00 /* 00000000 */ - +/* Bit description in registers */ #define LP5523_ENABLE 0x40 #define LP5523_AUTO_INC 0x40 #define LP5523_PWR_SAVE 0x20 #define LP5523_PWM_PWR_SAVE 0x04 -#define LP5523_CP_1 0x08 -#define LP5523_CP_1_5 0x10 #define LP5523_CP_AUTO 0x18 -#define LP5523_INT_CLK 0x01 #define LP5523_AUTO_CLK 0x02 + #define LP5523_EN_LEDTEST 0x80 #define LP5523_LEDTEST_DONE 0x80 #define LP5523_RESET 0xFF - -#define LP5523_DEFAULT_CURRENT 50 /* microAmps */ -#define LP5523_PROGRAM_LENGTH 32 /* in bytes */ -#define LP5523_PROGRAM_PAGES 6 #define LP5523_ADC_SHORTCIRC_LIM 80 - -#define LP5523_MAX_LEDS 9 -#define LP5523_ENGINES 3 - -#define LP5523_ENG_MASK_BASE 0x30 /* 00110000 */ - -#define LP5523_ENG_STATUS_MASK 0x07 /* 00000111 */ - -#define LP5523_IRQ_FLAGS IRQF_TRIGGER_FALLING - #define LP5523_EXT_CLK_USED 0x08 -#define LED_ACTIVE(mux, led) (!!(mux & (0x0001 << led))) -#define SHIFT_MASK(id) (((id) - 1) * 2) - /* Memory Page Selection */ #define LP5523_PAGE_ENG1 0 #define LP5523_PAGE_ENG2 1 -- cgit v1.2.3 From 79bcc10b8cf3db99958743ecb174e7637e1dd932 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:26:14 +0900 Subject: leds-lp55xx: clean up headers Remove unused headers and sort them alphabetically Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 19 ++++++------------- drivers/leds/leds-lp5523.c | 19 ++++++------------- 2 files changed, 12 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index b2a0d877cdc1..3b6dcb0be7e8 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -20,22 +20,15 @@ * 02110-1301 USA */ -#include -#include -#include -#include -#include -#include #include -#include -#include -#include +#include +#include +#include #include -#include -#include -#include +#include +#include #include -#include +#include #include "leds-lp55xx-common.h" diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index e145ce984b1d..d44c001e5e73 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -20,22 +20,15 @@ * 02110-1301 USA */ -#include -#include -#include -#include -#include -#include #include -#include -#include -#include +#include +#include +#include #include -#include -#include -#include +#include +#include #include -#include +#include #include "leds-lp55xx-common.h" -- cgit v1.2.3 From a2387cb9f6974fede9bf2d731a78ca158acb5424 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:28:00 +0900 Subject: leds-lp5521/5523: add author and copyright description Now LP5521 and LP5523 drivers are based on new lp55xx structure. So the author and copyrights are updated. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 3 +++ drivers/leds/leds-lp5523.c | 3 +++ 2 files changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 3b6dcb0be7e8..1001347ba70b 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -2,8 +2,10 @@ * LP5521 LED chip driver. * * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2012 Texas Instruments * * Contact: Samu Onkalo + * Milo(Woogyom) Kim * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -479,5 +481,6 @@ static struct i2c_driver lp5521_driver = { module_i2c_driver(lp5521_driver); MODULE_AUTHOR("Mathias Nyman, Yuri Zaporozhets, Samu Onkalo"); +MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP5521 LED engine"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index d44c001e5e73..229f734040af 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -2,8 +2,10 @@ * lp5523.c - LP5523 LED Driver * * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2012 Texas Instruments * * Contact: Samu Onkalo + * Milo(Woogyom) Kim * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -510,5 +512,6 @@ static struct i2c_driver lp5523_driver = { module_i2c_driver(lp5523_driver); MODULE_AUTHOR("Mathias Nyman "); +MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP5523 LED engine"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 109b833071b44a4a6f5dc56385025543ed15a500 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:28:44 +0900 Subject: leds-lp55xx: fix problem on removing LED attributes LP55XX common device attributes, 'led_current' and 'max_current' are created while loading the driver. Those are LED device attributes which are removed automatically on releasing led class devices - led_classdev_unregister(). Therefore, this duplicate code should be removed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp55xx-common.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 782ab84fe65f..d9eb84157423 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -478,12 +478,9 @@ void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip) { int i; struct lp55xx_led *each; - struct kobject *kobj; for (i = 0; i < chip->num_leds; i++) { each = led + i; - kobj = &led->cdev.dev->kobj; - sysfs_remove_group(kobj, &lp55xx_led_attr_group); led_classdev_unregister(&each->cdev); flush_work(&each->brightness_work); } -- cgit v1.2.3 From ad0ba85fab7da9e634c9da4f96315c8d0b9febcb Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 23 Jan 2013 00:35:04 -0800 Subject: leds: leds-pwm: make it depend on PWM and not HAVE_PWM The correct dependency for the leds-pwm is PWM and not HAVE_PWM since PWM drivers now have their own subsystem. Signed-off-by: Peter Ujfalusi Signed-off-by: Bryan Wu --- drivers/leds/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index fc680a9ed841..78b354f32129 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -320,7 +320,7 @@ config LEDS_DAC124S085 config LEDS_PWM tristate "PWM driven LED Support" depends on LEDS_CLASS - depends on HAVE_PWM + depends on PWM help This option enables support for pwm driven LEDs -- cgit v1.2.3 From ef754e88e35f86d9704f79ac8dace8c66f367164 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 27 Jan 2013 01:14:14 -0800 Subject: leds: tca6507: Use of_get_child_count() Signed-off-by: Axel Lin Signed-off-by: Bryan Wu --- drivers/leds/leds-tca6507.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c index 220fc7fbf1f0..070ba0741b21 100644 --- a/drivers/leds/leds-tca6507.c +++ b/drivers/leds/leds-tca6507.c @@ -674,14 +674,10 @@ tca6507_led_dt_init(struct i2c_client *client) struct device_node *np = client->dev.of_node, *child; struct tca6507_platform_data *pdata; struct led_info *tca_leds; - int count = 0; + int count; - for_each_child_of_node(np, child) - count++; - if (!count) - return ERR_PTR(-ENODEV); - - if (count > NUM_LEDS) + count = of_get_child_count(np); + if (!count || count > NUM_LEDS) return ERR_PTR(-ENODEV); tca_leds = devm_kzalloc(&client->dev, -- cgit v1.2.3 From 61d4eb2724283e85b37ed2fe13390366d7a6db74 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 27 Jan 2013 04:56:57 -0800 Subject: leds: 88pm860x: Add missing of_node_put() of_find_node_by_name() returns a node pointer with refcount incremented, use of_node_put() on it when done. of_find_node_by_name() will call of_node_put() against from parameter, thus we also need to call of_node_get(from) before calling of_find_node_by_name(). Signed-off-by: Axel Lin Signed-off-by: Bryan Wu --- drivers/leds/leds-88pm860x.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c index 6be2edd41173..f5b9ea315790 100644 --- a/drivers/leds/leds-88pm860x.c +++ b/drivers/leds/leds-88pm860x.c @@ -128,8 +128,10 @@ static void pm860x_led_set(struct led_classdev *cdev, static int pm860x_led_dt_init(struct platform_device *pdev, struct pm860x_led *data) { - struct device_node *nproot = pdev->dev.parent->of_node, *np; + struct device_node *nproot, *np; int iset = 0; + + nproot = of_node_get(pdev->dev.parent->of_node); if (!nproot) return -ENODEV; nproot = of_find_node_by_name(nproot, "leds"); @@ -145,6 +147,7 @@ static int pm860x_led_dt_init(struct platform_device *pdev, break; } } + of_node_put(nproot); return 0; } #else -- cgit v1.2.3 From 4b07c5d5123f76487c61cf9dc3f987d0b8c88a94 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 4 Feb 2013 18:01:21 -0800 Subject: leds: leds-sunfire: use dev_err()/pr_err() instead of printk() Fixed the checkpatch errors and warnings as below: ERROR: spaces required around that '=' (ctx:VxW) WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... Signed-off-by: Jingoo Han Signed-off-by: Bryan Wu --- drivers/leds/leds-sunfire.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-sunfire.c b/drivers/leds/leds-sunfire.c index 07ff5a3a6cee..89792990088d 100644 --- a/drivers/leds/leds-sunfire.c +++ b/drivers/leds/leds-sunfire.c @@ -3,6 +3,8 @@ * Copyright (C) 2008 David S. Miller */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -14,9 +16,6 @@ #include #include -#define DRIVER_NAME "leds-sunfire" -#define PFX DRIVER_NAME ": " - MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); MODULE_DESCRIPTION("Sun Fire LED driver"); MODULE_LICENSE("GPL"); @@ -130,14 +129,14 @@ static int sunfire_led_generic_probe(struct platform_device *pdev, int i, err; if (pdev->num_resources != 1) { - printk(KERN_ERR PFX "Wrong number of resources %d, should be 1\n", + dev_err(&pdev->dev, "Wrong number of resources %d, should be 1\n", pdev->num_resources); return -EINVAL; } p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); if (!p) { - printk(KERN_ERR PFX "Could not allocate struct sunfire_drvdata\n"); + dev_err(&pdev->dev, "Could not allocate struct sunfire_drvdata\n"); return -ENOMEM; } @@ -152,7 +151,7 @@ static int sunfire_led_generic_probe(struct platform_device *pdev, err = led_classdev_register(&pdev->dev, lp); if (err) { - printk(KERN_ERR PFX "Could not register %s LED\n", + dev_err(&pdev->dev, "Could not register %s LED\n", lp->name); for (i--; i >= 0; i--) led_classdev_unregister(&p->leds[i].led_cdev); @@ -188,7 +187,7 @@ static struct led_type clockboard_led_types[NUM_LEDS_PER_BOARD] = { { .name = "clockboard-right", .handler = clockboard_right_set, - .default_trigger= "heartbeat", + .default_trigger = "heartbeat", }, }; @@ -209,7 +208,7 @@ static struct led_type fhc_led_types[NUM_LEDS_PER_BOARD] = { { .name = "fhc-right", .handler = fhc_right_set, - .default_trigger= "heartbeat", + .default_trigger = "heartbeat", }, }; @@ -244,13 +243,13 @@ static int __init sunfire_leds_init(void) int err = platform_driver_register(&sunfire_clockboard_led_driver); if (err) { - printk(KERN_ERR PFX "Could not register clock board LED driver\n"); + pr_err("Could not register clock board LED driver\n"); return err; } err = platform_driver_register(&sunfire_fhc_led_driver); if (err) { - printk(KERN_ERR PFX "Could not register FHC LED driver\n"); + pr_err("Could not register FHC LED driver\n"); platform_driver_unregister(&sunfire_clockboard_led_driver); } -- cgit v1.2.3 From 2a2483072393b27f4336ab068a1f48ca19ff1c1e Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 24 Jan 2013 14:14:19 +1000 Subject: vgacon/vt: clear buffer attributes when we load a 512 character font (v2) When we switch from 256->512 byte font rendering mode, it means the current contents of the screen is being reinterpreted. The bit that holds the high bit of the 9-bit font, may have been previously set, and thus the new font misrenders. The problem case we see is grub2 writes spaces with the bit set, so it ends up with data like 0x820, which gets reinterpreted into 0x120 char which the font translates into G with a circumflex. This flashes up on screen at boot and is quite ugly. A current side effect of this patch though is that any rendering on the screen changes color to a slightly darker color, but at least the screen no longer corrupts. v2: as suggested by hpa, always clear the attribute space, whether we are are going to or from 512 chars. Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie --- drivers/tty/vt/vt.c | 2 +- drivers/video/console/vgacon.c | 22 +++++++++++++++------- include/linux/vt_kern.h | 1 + 3 files changed, 17 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 8fd89687d068..c8067aef4b86 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -638,7 +638,7 @@ static inline void save_screen(struct vc_data *vc) * Redrawing of screen */ -static void clear_buffer_attributes(struct vc_data *vc) +void clear_buffer_attributes(struct vc_data *vc) { unsigned short *p = (unsigned short *)vc->vc_origin; int count = vc->vc_screenbuf_size / 2; diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index d449a74d4a31..5855d17d19ac 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -1064,7 +1064,7 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512) unsigned short video_port_status = vga_video_port_reg + 6; int font_select = 0x00, beg, i; char *charmap; - + bool clear_attribs = false; if (vga_video_type != VIDEO_TYPE_EGAM) { charmap = (char *) VGA_MAP_MEM(colourmap, 0); beg = 0x0e; @@ -1169,12 +1169,6 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512) /* if 512 char mode is already enabled don't re-enable it. */ if ((set) && (ch512 != vga_512_chars)) { - /* attribute controller */ - for (i = 0; i < MAX_NR_CONSOLES; i++) { - struct vc_data *c = vc_cons[i].d; - if (c && c->vc_sw == &vga_con) - c->vc_hi_font_mask = ch512 ? 0x0800 : 0; - } vga_512_chars = ch512; /* 256-char: enable intensity bit 512-char: disable intensity bit */ @@ -1185,8 +1179,22 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512) it means, but it works, and it appears necessary */ inb_p(video_port_status); vga_wattr(state->vgabase, VGA_AR_ENABLE_DISPLAY, 0); + clear_attribs = true; } raw_spin_unlock_irq(&vga_lock); + + if (clear_attribs) { + for (i = 0; i < MAX_NR_CONSOLES; i++) { + struct vc_data *c = vc_cons[i].d; + if (c && c->vc_sw == &vga_con) { + /* force hi font mask to 0, so we always clear + the bit on either transition */ + c->vc_hi_font_mask = 0x00; + clear_buffer_attributes(c); + c->vc_hi_font_mask = ch512 ? 0x0800 : 0; + } + } + } return 0; } diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index 50ae7d0c279e..1f55665d7b49 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -47,6 +47,7 @@ int con_set_cmap(unsigned char __user *cmap); int con_get_cmap(unsigned char __user *cmap); void scrollback(struct vc_data *vc, int lines); void scrollfront(struct vc_data *vc, int lines); +void clear_buffer_attributes(struct vc_data *vc); void update_region(struct vc_data *vc, unsigned long start, int count); void redraw_screen(struct vc_data *vc, int is_switch); #define update_screen(x) redraw_screen(x, 0) -- cgit v1.2.3 From ae1287865f5361fa138d4d3b1b6277908b54eac9 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 24 Jan 2013 16:12:41 +1000 Subject: fbcon: don't lose the console font across generic->chip driver switch If grub2 loads efifb/vesafb, then when systemd starts it can set the console font on that framebuffer device, however when we then load the native KMS driver, the first thing it does is tear down the generic framebuffer driver. The thing is the generic code is doing the right thing, it frees the font because otherwise it would leak memory. However we can assume that if you are removing the generic firmware driver (vesa/efi/offb), that a new driver *should* be loading soon after, so we effectively leak the font. However the old code left a dangling pointer in vc->vc_font.data and we can now reuse that dangling pointer to load the font into the new driver, now that we aren't freeing it. Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=892340 Cc: Kay Sievers Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie --- drivers/video/console/fbcon.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index fdefa8fd72c4..f8dbe76a1b6f 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -990,7 +990,7 @@ static const char *fbcon_startup(void) } /* Setup default font */ - if (!p->fontdata) { + if (!p->fontdata && !vc->vc_font.data) { if (!fontname[0] || !(font = find_font(fontname))) font = get_default_font(info->var.xres, info->var.yres, @@ -1000,6 +1000,8 @@ static const char *fbcon_startup(void) vc->vc_font.height = font->height; vc->vc_font.data = (void *)(p->fontdata = font->data); vc->vc_font.charcount = 256; /* FIXME Need to support more fonts */ + } else { + p->fontdata = vc->vc_font.data; } cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); @@ -1159,9 +1161,9 @@ static void fbcon_init(struct vc_data *vc, int init) ops->p = &fb_display[fg_console]; } -static void fbcon_free_font(struct display *p) +static void fbcon_free_font(struct display *p, bool freefont) { - if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0)) + if (freefont && p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0)) kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int)); p->fontdata = NULL; p->userfont = 0; @@ -1173,8 +1175,8 @@ static void fbcon_deinit(struct vc_data *vc) struct fb_info *info; struct fbcon_ops *ops; int idx; + bool free_font = true; - fbcon_free_font(p); idx = con2fb_map[vc->vc_num]; if (idx == -1) @@ -1185,6 +1187,8 @@ static void fbcon_deinit(struct vc_data *vc) if (!info) goto finished; + if (info->flags & FBINFO_MISC_FIRMWARE) + free_font = false; ops = info->fbcon_par; if (!ops) @@ -1196,6 +1200,8 @@ static void fbcon_deinit(struct vc_data *vc) ops->flags &= ~FBCON_FLAGS_INIT; finished: + fbcon_free_font(p, free_font); + if (!con_is_bound(&fb_con)) fbcon_exit(); -- cgit v1.2.3 From 9f23de52b64f7fb801fd76f3dd8651a0dc89187b Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 7 Feb 2013 10:10:04 +1000 Subject: drm/usb: bind driver to correct device While looking at plymouth on udl I noticed that plymouth was trying to use its fb plugin not its drm one, it was trying to drmOpen a driver called usb not udl, noticed that we actually had out driver pointing at the wrong device. Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c index 3cec30611417..34a156f0c336 100644 --- a/drivers/gpu/drm/drm_usb.c +++ b/drivers/gpu/drm/drm_usb.c @@ -18,7 +18,7 @@ int drm_get_usb_dev(struct usb_interface *interface, usbdev = interface_to_usbdev(interface); dev->usbdev = usbdev; - dev->dev = &usbdev->dev; + dev->dev = &interface->dev; mutex_lock(&drm_global_mutex); -- cgit v1.2.3 From bcb39af4486be07e896fc374a2336bad3104ae0a Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 7 Feb 2013 11:19:15 +1000 Subject: drm/udl: make usage as a console safer Okay you don't really want to use udl devices as your console, but if you are unlucky enough to do so, you run into a lot of schedule while atomic due to printk being called from all sorts of funky places. So check if we are in an atomic context, and queue the damage for later, the next printk should cause it to appear. This isn't ideal, but it is simple, and seems to work okay in my testing here. (dirty area idea came from xenfb) fixes a bunch of sleeping while atomic issues running fbcon on udl devices. Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie --- drivers/gpu/drm/udl/udl_drv.h | 2 ++ drivers/gpu/drm/udl/udl_fb.c | 44 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index 87aa5f5d3c88..cc6d90f28c71 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -75,6 +75,8 @@ struct udl_framebuffer { struct drm_framebuffer base; struct udl_gem_object *obj; bool active_16; /* active on the 16-bit channel */ + int x1, y1, x2, y2; /* dirty rect */ + spinlock_t dirty_lock; }; #define to_udl_fb(x) container_of(x, struct udl_framebuffer, base) diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index d4ab3beaada0..c35880ff2207 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -153,6 +153,9 @@ int udl_handle_damage(struct udl_framebuffer *fb, int x, int y, struct urb *urb; int aligned_x; int bpp = (fb->base.bits_per_pixel / 8); + int x2, y2; + bool store_for_later = false; + unsigned long flags; if (!fb->active_16) return 0; @@ -169,8 +172,6 @@ int udl_handle_damage(struct udl_framebuffer *fb, int x, int y, } } - start_cycles = get_cycles(); - aligned_x = DL_ALIGN_DOWN(x, sizeof(unsigned long)); width = DL_ALIGN_UP(width + (x-aligned_x), sizeof(unsigned long)); x = aligned_x; @@ -180,19 +181,53 @@ int udl_handle_damage(struct udl_framebuffer *fb, int x, int y, (y + height > fb->base.height)) return -EINVAL; + /* if we are in atomic just store the info + can't test inside spin lock */ + if (in_atomic()) + store_for_later = true; + + x2 = x + width - 1; + y2 = y + height - 1; + + spin_lock_irqsave(&fb->dirty_lock, flags); + + if (fb->y1 < y) + y = fb->y1; + if (fb->y2 > y2) + y2 = fb->y2; + if (fb->x1 < x) + x = fb->x1; + if (fb->x2 > x2) + x2 = fb->x2; + + if (store_for_later) { + fb->x1 = x; + fb->x2 = x2; + fb->y1 = y; + fb->y2 = y2; + spin_unlock_irqrestore(&fb->dirty_lock, flags); + return 0; + } + + fb->x1 = fb->y1 = INT_MAX; + fb->x2 = fb->y2 = 0; + + spin_unlock_irqrestore(&fb->dirty_lock, flags); + start_cycles = get_cycles(); + urb = udl_get_urb(dev); if (!urb) return 0; cmd = urb->transfer_buffer; - for (i = y; i < y + height ; i++) { + for (i = y; i <= y2 ; i++) { const int line_offset = fb->base.pitches[0] * i; const int byte_offset = line_offset + (x * bpp); const int dev_byte_offset = (fb->base.width * bpp * i) + (x * bpp); if (udl_render_hline(dev, bpp, &urb, (char *) fb->obj->vmapping, &cmd, byte_offset, dev_byte_offset, - width * bpp, + (x2 - x + 1) * bpp, &bytes_identical, &bytes_sent)) goto error; } @@ -434,6 +469,7 @@ udl_framebuffer_init(struct drm_device *dev, { int ret; + spin_lock_init(&ufb->dirty_lock); ufb->obj = obj; ret = drm_framebuffer_init(dev, &ufb->base, &udlfb_funcs); drm_helper_mode_fill_fb_struct(&ufb->base, mode_cmd); -- cgit v1.2.3 From 1262e24a59a052f9a98383e47e7c903712490d5c Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 7 Feb 2013 07:37:29 +0100 Subject: drivers/block/mtip32xx: add missing GENERIC_HARDIRQS dependency The MTIP32XX driver calls devm_request_irq() and therefore needs a GENERIC_HARDIRQS dependency to prevent building it on s390. Signed-off-by: Heiko Carstens Signed-off-by: Jens Axboe --- drivers/block/mtip32xx/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/block/mtip32xx/Kconfig b/drivers/block/mtip32xx/Kconfig index 0ba837fc62a8..1fca1f996b45 100644 --- a/drivers/block/mtip32xx/Kconfig +++ b/drivers/block/mtip32xx/Kconfig @@ -4,6 +4,6 @@ config BLK_DEV_PCIESSD_MTIP32XX tristate "Block Device Driver for Micron PCIe SSDs" - depends on PCI + depends on PCI && GENERIC_HARDIRQS help This enables the block driver for Micron PCIe SSDs. -- cgit v1.2.3 From e5e9fdaad4443ba45ada91c0aae8b36a38ca9c4d Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 7 Feb 2013 10:04:21 +0100 Subject: rsxx: add slab.h include to dma.c kbuild test robot says: tree: git://git.kernel.dk/linux-block.git for-3.9/drivers head: 1262e24a59a052f9a98383e47e7c903712490d5c commit: 8722ff8cdbfac9c1b20e67bb067b455c48cb8e93 [6/8] block: IBM RamSan 70/80 device driver config: make ARCH=alpha allyesconfig All error/warnings: drivers/block/rsxx/dma.c: In function 'rsxx_complete_dma': >> drivers/block/rsxx/dma.c:251:2: error: implicit declaration of function 'kmem_cache_free' [-Werror=implicit-function-declaration] drivers/block/rsxx/dma.c: In function 'rsxx_queue_discard': >> drivers/block/rsxx/dma.c:567:2: error: implicit declaration of function 'kmem_cache_alloc' [-Werror=implicit-function-declaration] >> drivers/block/rsxx/dma.c:567:6: warning: assignment makes pointer from integer without a cast [enabled by default] drivers/block/rsxx/dma.c: In function 'rsxx_queue_dma': >> drivers/block/rsxx/dma.c:601:6: warning: assignment makes pointer from integer without a cast [enabled by default] drivers/block/rsxx/dma.c: In function 'rsxx_dma_init': >> drivers/block/rsxx/dma.c:985:2: error: implicit declaration of function 'KMEM_CACHE' [-Werror=implicit-function-declaration] >> drivers/block/rsxx/dma.c:985:29: error: 'rsxx_dma' undeclared (first use in this function) drivers/block/rsxx/dma.c:985:29: note: each undeclared identifier is reported only once for each function it appears in >> drivers/block/rsxx/dma.c:985:39: error: 'SLAB_HWCACHE_ALIGN' undeclared (first use in this function) drivers/block/rsxx/dma.c: In function 'rsxx_dma_cleanup': >> drivers/block/rsxx/dma.c:995:2: error: implicit declaration of function 'kmem_cache_destroy' [-Werror=implicit-function-declaration] cc1: some warnings being treated as errors Signed-off-by: Jens Axboe --- drivers/block/rsxx/dma.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/block/rsxx/dma.c b/drivers/block/rsxx/dma.c index 08da35ea1d85..872f50358624 100644 --- a/drivers/block/rsxx/dma.c +++ b/drivers/block/rsxx/dma.c @@ -22,6 +22,7 @@ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include "rsxx_priv.h" struct rsxx_dma { -- cgit v1.2.3 From c2668c082ac1b4f30b145762a75ec2b3e016b952 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 6 Feb 2013 17:35:41 -0300 Subject: [media] s5c73m3: Remove __dev* attributes Remove no longer supported __devinit, __devexit attributes. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/s5c73m3/s5c73m3-core.c | 8 ++++---- drivers/media/i2c/s5c73m3/s5c73m3-spi.c | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index c143c9ec7ba9..5dbb65e1f6b7 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -1561,8 +1561,8 @@ static int s5c73m3_configure_gpios(struct s5c73m3 *state, return 0; } -static int __devinit s5c73m3_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int s5c73m3_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct device *dev = &client->dev; const struct s5c73m3_platform_data *pdata = client->dev.platform_data; @@ -1666,7 +1666,7 @@ out_err1: return ret; } -static int __devexit s5c73m3_remove(struct i2c_client *client) +static int s5c73m3_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); @@ -1693,7 +1693,7 @@ static struct i2c_driver s5c73m3_i2c_driver = { .name = DRIVER_NAME, }, .probe = s5c73m3_probe, - .remove = __devexit_p(s5c73m3_remove), + .remove = s5c73m3_remove, .id_table = s5c73m3_id, }; diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c index 889139c2ac5e..6f3a9c00fe65 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c @@ -111,7 +111,7 @@ int s5c73m3_spi_read(struct s5c73m3 *state, void *addr, return 0; } -static int __devinit s5c73m3_spi_probe(struct spi_device *spi) +static int s5c73m3_spi_probe(struct spi_device *spi) { int r; struct s5c73m3 *state = container_of(spi->dev.driver, struct s5c73m3, @@ -132,7 +132,7 @@ static int __devinit s5c73m3_spi_probe(struct spi_device *spi) return 0; } -static int __devexit s5c73m3_spi_remove(struct spi_device *spi) +static int s5c73m3_spi_remove(struct spi_device *spi) { return 0; } @@ -141,7 +141,7 @@ int s5c73m3_register_spi_driver(struct s5c73m3 *state) { struct spi_driver *spidrv = &state->spidrv; - spidrv->remove = __devexit_p(s5c73m3_spi_remove); + spidrv->remove = s5c73m3_spi_remove; spidrv->probe = s5c73m3_spi_probe; spidrv->driver.name = S5C73M3_SPI_DRV_NAME; spidrv->driver.bus = &spi_bus_type; -- cgit v1.2.3 From 77e383504791008cdbab1b33d70b395ca3a361c8 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 6 Feb 2013 13:55:17 +0530 Subject: iommu/exynos: Make exynos_sysmmu_disable static 'exynos_sysmmu_disable' is used only in this file and can be made static. Signed-off-by: Sachin Kamat Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 7fe44f83cc37..238a3caa949a 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -511,7 +511,7 @@ int exynos_sysmmu_enable(struct device *dev, unsigned long pgtable) return ret; } -bool exynos_sysmmu_disable(struct device *dev) +static bool exynos_sysmmu_disable(struct device *dev) { struct sysmmu_drvdata *data = dev_get_drvdata(dev->archdata.iommu); bool disabled; -- cgit v1.2.3 From a3b7256d643dac125f4162cd4f9b603868559b61 Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Wed, 6 Feb 2013 19:38:15 +0200 Subject: iommu/tegra: smmu: Fix incorrect mask for regbase This fixes kernel crash because of BUG() in register address validation. Signed-off-by: Hiroshi Doyu Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-smmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 774728313f51..117427e6004e 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -1191,7 +1191,7 @@ static int tegra_smmu_probe(struct platform_device *pdev) smmu->rege[i] = smmu->regs[i] + resource_size(res) - 1; } /* Same as "mc" 1st regiter block start address */ - smmu->regbase = (void __iomem *)((u32)smmu->regs[0] & ~PAGE_MASK); + smmu->regbase = (void __iomem *)((u32)smmu->regs[0] & PAGE_MASK); err = of_get_dma_window(dev->of_node, NULL, 0, NULL, &base, &size); if (err) -- cgit v1.2.3 From 37a407101e08e444a193837a1e1d4f6f66c8dad4 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Wed, 6 Feb 2013 09:50:10 +0100 Subject: iommu/vt-d: Zero out allocated memory in dmar_enable_qi kmemcheck complained about the use of uninitialized memory. Fix by using kzalloc instead of kmalloc. Cc: David Woodhouse Signed-off-by: Hannes Reinecke Signed-off-by: Joerg Roedel --- drivers/iommu/dmar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 86e2f4a62b9a..2623a570ad2f 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -1040,7 +1040,7 @@ int dmar_enable_qi(struct intel_iommu *iommu) qi->desc = page_address(desc_page); - qi->desc_status = kmalloc(QI_LENGTH * sizeof(int), GFP_ATOMIC); + qi->desc_status = kzalloc(QI_LENGTH * sizeof(int), GFP_ATOMIC); if (!qi->desc_status) { free_page((unsigned long) qi->desc); kfree(qi); -- cgit v1.2.3 From f528d980c17b8714aedc918ba86e058af914d66b Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 6 Feb 2013 12:55:23 +0100 Subject: iommu/amd: Initialize device table after dma_ops When dma_ops are initialized the unity mappings are created. The init_device_table_dma() function makes sure DMA from all devices is blocked by default. This opens a short window in time where DMA to unity mapped regions is blocked by the IOMMU. Make sure this does not happen by initializing the device table after dma_ops. Cc: stable@vger.kernel.org Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu_init.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index faf10ba1ed9a..b6ecddb63cd0 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -1876,11 +1876,6 @@ static int amd_iommu_init_dma(void) struct amd_iommu *iommu; int ret; - init_device_table_dma(); - - for_each_iommu(iommu) - iommu_flush_all_caches(iommu); - if (iommu_pass_through) ret = amd_iommu_init_passthrough(); else @@ -1889,6 +1884,11 @@ static int amd_iommu_init_dma(void) if (ret) return ret; + init_device_table_dma(); + + for_each_iommu(iommu) + iommu_flush_all_caches(iommu); + amd_iommu_init_api(); amd_iommu_init_notifier(); -- cgit v1.2.3 From e90a4ea534b110a43df87a05587c53cd78569467 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 18 Jan 2013 16:31:14 +0000 Subject: drm/udl: Inline memcmp() for RLE compression of xfer As we use a variable length the compiler does not realise that it is a fixed value of either 2 or 4 bytes. Instead of performing the inline comparison itself, the compiler inserts a function call to the generic memcmp routine which is optimised for long comparisons of variable length. That turns out to be quite expensive... Signed-off-by: Chris Wilson Signed-off-by: Dave Airlie --- drivers/gpu/drm/udl/udl_transfer.c | 46 +++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/udl/udl_transfer.c b/drivers/gpu/drm/udl/udl_transfer.c index 142fee5f983f..f343db73e095 100644 --- a/drivers/gpu/drm/udl/udl_transfer.c +++ b/drivers/gpu/drm/udl/udl_transfer.c @@ -75,15 +75,19 @@ static int udl_trim_hline(const u8 *bback, const u8 **bfront, int *width_bytes) } #endif -static inline u16 pixel32_to_be16p(const uint8_t *pixel) +static inline u16 pixel32_to_be16(const uint32_t pixel) { - uint32_t pix = *(uint32_t *)pixel; - u16 retval; + return (((pixel >> 3) & 0x001f) | + ((pixel >> 5) & 0x07e0) | + ((pixel >> 8) & 0xf800)); +} - retval = (((pix >> 3) & 0x001f) | - ((pix >> 5) & 0x07e0) | - ((pix >> 8) & 0xf800)); - return retval; +static bool pixel_repeats(const void *pixel, const uint32_t repeat, int bpp) +{ + if (bpp == 2) + return *(const uint16_t *)pixel == repeat; + else + return *(const uint32_t *)pixel == repeat; } /* @@ -152,29 +156,33 @@ static void udl_compress_hline16( prefetch_range((void *) pixel, (cmd_pixel_end - pixel) * bpp); while (pixel < cmd_pixel_end) { - const u8 * const repeating_pixel = pixel; - - if (bpp == 2) - *(uint16_t *)cmd = cpu_to_be16p((uint16_t *)pixel); - else if (bpp == 4) - *(uint16_t *)cmd = cpu_to_be16(pixel32_to_be16p(pixel)); + const u8 *const start = pixel; + u32 repeating_pixel; + + if (bpp == 2) { + repeating_pixel = *(uint16_t *)pixel; + *(uint16_t *)cmd = cpu_to_be16(repeating_pixel); + } else { + repeating_pixel = *(uint32_t *)pixel; + *(uint16_t *)cmd = cpu_to_be16(pixel32_to_be16(repeating_pixel)); + } cmd += 2; pixel += bpp; if (unlikely((pixel < cmd_pixel_end) && - (!memcmp(pixel, repeating_pixel, bpp)))) { + (pixel_repeats(pixel, repeating_pixel, bpp)))) { /* go back and fill in raw pixel count */ - *raw_pixels_count_byte = (((repeating_pixel - + *raw_pixels_count_byte = (((start - raw_pixel_start) / bpp) + 1) & 0xFF; - while ((pixel < cmd_pixel_end) - && (!memcmp(pixel, repeating_pixel, bpp))) { + while ((pixel < cmd_pixel_end) && + (pixel_repeats(pixel, repeating_pixel, bpp))) { pixel += bpp; } /* immediately after raw data is repeat byte */ - *cmd++ = (((pixel - repeating_pixel) / bpp) - 1) & 0xFF; + *cmd++ = (((pixel - start) / bpp) - 1) & 0xFF; /* Then start another raw pixel span */ raw_pixel_start = pixel; @@ -223,6 +231,8 @@ int udl_render_hline(struct drm_device *dev, int bpp, struct urb **urb_ptr, u8 *cmd = *urb_buf_ptr; u8 *cmd_end = (u8 *) urb->transfer_buffer + urb->transfer_buffer_length; + BUG_ON(!(bpp == 2 || bpp == 4)); + line_start = (u8 *) (front + byte_offset); next_pixel = line_start; line_end = next_pixel + byte_width; -- cgit v1.2.3 From 677d23b70bf949f75746c80cbae92c233c6b5e2a Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 7 Feb 2013 12:30:25 +1000 Subject: drm/udl: disable fb_defio by default There seems to be a bad interaction between gem/shmem and defio on top, I get list corruption on the page lru in the shmem code. Turn it off for now until we get some more digging done. Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie --- drivers/gpu/drm/udl/udl_fb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index c35880ff2207..1eb060cc71d1 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -22,9 +22,9 @@ #include -#define DL_DEFIO_WRITE_DELAY 5 /* fb_deferred_io.delay in jiffies */ +#define DL_DEFIO_WRITE_DELAY (HZ/20) /* fb_deferred_io.delay in jiffies */ -static int fb_defio = 1; /* Optionally enable experimental fb_defio mmap support */ +static int fb_defio = 0; /* Optionally enable experimental fb_defio mmap support */ static int fb_bpp = 16; module_param(fb_bpp, int, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); -- cgit v1.2.3 From 50e244cc793d511b86adea24972f3a7264cae114 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 25 Jan 2013 10:28:15 +1000 Subject: fb: rework locking to fix lock ordering on takeover Adjust the console layer to allow a take over call where the caller already holds the locks. Make the fb layer lock in order. This is partly a band aid, the fb layer is terminally confused about the locking rules it uses for its notifiers it seems. [akpm@linux-foundation.org: remove stray non-ascii char, tidy comment] [akpm@linux-foundation.org: export do_take_over_console()] [airlied: cleanup another non-ascii char] Signed-off-by: Alan Cox Cc: Florian Tobias Schandinat Cc: Stephen Rothwell Cc: Jiri Kosina Cc: stable Tested-by: Sedat Dilek Reviewed-by: Daniel Vetter Signed-off-by: Andrew Morton Signed-off-by: Dave Airlie --- drivers/tty/vt/vt.c | 93 ++++++++++++++++++++++++++++++++----------- drivers/video/console/fbcon.c | 29 +++++++++++++- drivers/video/fbmem.c | 5 +-- drivers/video/fbsysfs.c | 3 ++ include/linux/console.h | 1 + 5 files changed, 104 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 8fd89687d068..c076af0b300b 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -2987,7 +2987,7 @@ int __init vty_init(const struct file_operations *console_fops) static struct class *vtconsole_class; -static int bind_con_driver(const struct consw *csw, int first, int last, +static int do_bind_con_driver(const struct consw *csw, int first, int last, int deflt) { struct module *owner = csw->owner; @@ -2998,7 +2998,7 @@ static int bind_con_driver(const struct consw *csw, int first, int last, if (!try_module_get(owner)) return -ENODEV; - console_lock(); + WARN_CONSOLE_UNLOCKED(); /* check if driver is registered */ for (i = 0; i < MAX_NR_CON_DRIVER; i++) { @@ -3083,11 +3083,22 @@ static int bind_con_driver(const struct consw *csw, int first, int last, retval = 0; err: - console_unlock(); module_put(owner); return retval; }; + +static int bind_con_driver(const struct consw *csw, int first, int last, + int deflt) +{ + int ret; + + console_lock(); + ret = do_bind_con_driver(csw, first, last, deflt); + console_unlock(); + return ret; +} + #ifdef CONFIG_VT_HW_CONSOLE_BINDING static int con_is_graphics(const struct consw *csw, int first, int last) { @@ -3199,9 +3210,9 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) if (!con_is_bound(csw)) con_driver->flag &= ~CON_DRIVER_FLAG_INIT; - console_unlock(); /* ignore return value, binding should not fail */ - bind_con_driver(defcsw, first, last, deflt); + do_bind_con_driver(defcsw, first, last, deflt); + console_unlock(); err: module_put(owner); return retval; @@ -3492,28 +3503,18 @@ int con_debug_leave(void) } EXPORT_SYMBOL_GPL(con_debug_leave); -/** - * register_con_driver - register console driver to console layer - * @csw: console driver - * @first: the first console to take over, minimum value is 0 - * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1 - * - * DESCRIPTION: This function registers a console driver which can later - * bind to a range of consoles specified by @first and @last. It will - * also initialize the console driver by calling con_startup(). - */ -int register_con_driver(const struct consw *csw, int first, int last) +static int do_register_con_driver(const struct consw *csw, int first, int last) { struct module *owner = csw->owner; struct con_driver *con_driver; const char *desc; int i, retval = 0; + WARN_CONSOLE_UNLOCKED(); + if (!try_module_get(owner)) return -ENODEV; - console_lock(); - for (i = 0; i < MAX_NR_CON_DRIVER; i++) { con_driver = ®istered_con_driver[i]; @@ -3566,10 +3567,29 @@ int register_con_driver(const struct consw *csw, int first, int last) } err: - console_unlock(); module_put(owner); return retval; } + +/** + * register_con_driver - register console driver to console layer + * @csw: console driver + * @first: the first console to take over, minimum value is 0 + * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1 + * + * DESCRIPTION: This function registers a console driver which can later + * bind to a range of consoles specified by @first and @last. It will + * also initialize the console driver by calling con_startup(). + */ +int register_con_driver(const struct consw *csw, int first, int last) +{ + int retval; + + console_lock(); + retval = do_register_con_driver(csw, first, last); + console_unlock(); + return retval; +} EXPORT_SYMBOL(register_con_driver); /** @@ -3623,17 +3643,44 @@ EXPORT_SYMBOL(unregister_con_driver); * when a driver wants to take over some existing consoles * and become default driver for newly opened ones. * - * take_over_console is basically a register followed by unbind + * take_over_console is basically a register followed by unbind + */ +int do_take_over_console(const struct consw *csw, int first, int last, int deflt) +{ + int err; + + err = do_register_con_driver(csw, first, last); + /* + * If we get an busy error we still want to bind the console driver + * and return success, as we may have unbound the console driver + * but not unregistered it. + */ + if (err == -EBUSY) + err = 0; + if (!err) + do_bind_con_driver(csw, first, last, deflt); + + return err; +} +EXPORT_SYMBOL_GPL(do_take_over_console); + +/* + * If we support more console drivers, this function is used + * when a driver wants to take over some existing consoles + * and become default driver for newly opened ones. + * + * take_over_console is basically a register followed by unbind */ int take_over_console(const struct consw *csw, int first, int last, int deflt) { int err; err = register_con_driver(csw, first, last); - /* if we get an busy error we still want to bind the console driver + /* + * If we get an busy error we still want to bind the console driver * and return success, as we may have unbound the console driver -  * but not unregistered it. - */ + * but not unregistered it. + */ if (err == -EBUSY) err = 0; if (!err) diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index fdefa8fd72c4..4bd7820cf4d0 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -529,6 +529,33 @@ static int search_for_mapped_con(void) return retval; } +static int do_fbcon_takeover(int show_logo) +{ + int err, i; + + if (!num_registered_fb) + return -ENODEV; + + if (!show_logo) + logo_shown = FBCON_LOGO_DONTSHOW; + + for (i = first_fb_vc; i <= last_fb_vc; i++) + con2fb_map[i] = info_idx; + + err = do_take_over_console(&fb_con, first_fb_vc, last_fb_vc, + fbcon_is_default); + + if (err) { + for (i = first_fb_vc; i <= last_fb_vc; i++) + con2fb_map[i] = -1; + info_idx = -1; + } else { + fbcon_has_console_bind = 1; + } + + return err; +} + static int fbcon_takeover(int show_logo) { int err, i; @@ -3115,7 +3142,7 @@ static int fbcon_fb_registered(struct fb_info *info) } if (info_idx != -1) - ret = fbcon_takeover(1); + ret = do_fbcon_takeover(1); } else { for (i = first_fb_vc; i <= last_fb_vc; i++) { if (con2fb_map_boot[i] == idx) diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 3ff0105a496a..d8d9831b3fdb 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1650,7 +1650,9 @@ static int do_register_framebuffer(struct fb_info *fb_info) event.info = fb_info; if (!lock_fb_info(fb_info)) return -ENODEV; + console_lock(); fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event); + console_unlock(); unlock_fb_info(fb_info); return 0; } @@ -1853,11 +1855,8 @@ int fb_new_modelist(struct fb_info *info) err = 1; if (!list_empty(&info->modelist)) { - if (!lock_fb_info(info)) - return -ENODEV; event.info = info; err = fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event); - unlock_fb_info(info); } return err; diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c index a55e3669d135..ef476b02fbe5 100644 --- a/drivers/video/fbsysfs.c +++ b/drivers/video/fbsysfs.c @@ -177,6 +177,8 @@ static ssize_t store_modes(struct device *device, if (i * sizeof(struct fb_videomode) != count) return -EINVAL; + if (!lock_fb_info(fb_info)) + return -ENODEV; console_lock(); list_splice(&fb_info->modelist, &old_list); fb_videomode_to_modelist((const struct fb_videomode *)buf, i, @@ -188,6 +190,7 @@ static ssize_t store_modes(struct device *device, fb_destroy_modelist(&old_list); console_unlock(); + unlock_fb_info(fb_info); return 0; } diff --git a/include/linux/console.h b/include/linux/console.h index dedb082fe50f..4ef4307dfb82 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -78,6 +78,7 @@ int con_is_bound(const struct consw *csw); int register_con_driver(const struct consw *csw, int first, int last); int unregister_con_driver(const struct consw *csw); int take_over_console(const struct consw *sw, int first, int last, int deflt); +int do_take_over_console(const struct consw *sw, int first, int last, int deflt); void give_up_console(const struct consw *sw); #ifdef CONFIG_HW_CONSOLE int con_debug_enter(struct vc_data *vc); -- cgit v1.2.3 From e93a9a868792ad71cdd09d75e5a02d8067473c4e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 25 Jan 2013 10:28:18 +1000 Subject: fb: Yet another band-aid for fixing lockdep mess I've still got lockdep warnings even after Alan's patch, and it seems that yet more band aids are required to paper over similar paths for unbind_con_driver() and unregister_con_driver(). After this hack, lockdep warnings are finally gone. Signed-off-by: Takashi Iwai Cc: Alan Cox Cc: Florian Tobias Schandinat Cc: Jiri Kosina Cc: stable Tested-by: Sedat Dilek Signed-off-by: Andrew Morton Signed-off-by: Dave Airlie --- drivers/tty/vt/vt.c | 43 ++++++++++++++++++++++++++++--------------- drivers/video/console/fbcon.c | 4 ++-- drivers/video/fbmem.c | 4 ++++ include/linux/console.h | 1 + include/linux/vt_kern.h | 2 ++ 5 files changed, 37 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index c076af0b300b..457c41fe7eb9 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3134,6 +3134,18 @@ static int con_is_graphics(const struct consw *csw, int first, int last) * or 0 on success. */ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) +{ + int retval; + + console_lock(); + retval = do_unbind_con_driver(csw, first, last, deflt); + console_unlock(); + return retval; +} +EXPORT_SYMBOL(unbind_con_driver); + +/* unlocked version of unbind_con_driver() */ +int do_unbind_con_driver(const struct consw *csw, int first, int last, int deflt) { struct module *owner = csw->owner; const struct consw *defcsw = NULL; @@ -3143,7 +3155,7 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) if (!try_module_get(owner)) return -ENODEV; - console_lock(); + WARN_CONSOLE_UNLOCKED(); /* check if driver is registered and if it is unbindable */ for (i = 0; i < MAX_NR_CON_DRIVER; i++) { @@ -3156,10 +3168,8 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) } } - if (retval) { - console_unlock(); + if (retval) goto err; - } retval = -ENODEV; @@ -3175,15 +3185,11 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) } } - if (retval) { - console_unlock(); + if (retval) goto err; - } - if (!con_is_bound(csw)) { - console_unlock(); + if (!con_is_bound(csw)) goto err; - } first = max(first, con_driver->first); last = min(last, con_driver->last); @@ -3212,13 +3218,12 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) /* ignore return value, binding should not fail */ do_bind_con_driver(defcsw, first, last, deflt); - console_unlock(); err: module_put(owner); return retval; } -EXPORT_SYMBOL(unbind_con_driver); +EXPORT_SYMBOL_GPL(do_unbind_con_driver); static int vt_bind(struct con_driver *con) { @@ -3605,9 +3610,18 @@ EXPORT_SYMBOL(register_con_driver); */ int unregister_con_driver(const struct consw *csw) { - int i, retval = -ENODEV; + int retval; console_lock(); + retval = do_unregister_con_driver(csw); + console_unlock(); + return retval; +} +EXPORT_SYMBOL(unregister_con_driver); + +int do_unregister_con_driver(const struct consw *csw) +{ + int i, retval = -ENODEV; /* cannot unregister a bound driver */ if (con_is_bound(csw)) @@ -3633,10 +3647,9 @@ int unregister_con_driver(const struct consw *csw) } } err: - console_unlock(); return retval; } -EXPORT_SYMBOL(unregister_con_driver); +EXPORT_SYMBOL_GPL(do_unregister_con_driver); /* * If we support more console drivers, this function is used diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 4bd7820cf4d0..2aef9cac4d18 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -3004,7 +3004,7 @@ static int fbcon_unbind(void) { int ret; - ret = unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc, + ret = do_unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc, fbcon_is_default); if (!ret) @@ -3077,7 +3077,7 @@ static int fbcon_fb_unregistered(struct fb_info *info) primary_device = -1; if (!num_registered_fb) - unregister_con_driver(&fb_con); + do_unregister_con_driver(&fb_con); return 0; } diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index d8d9831b3fdb..070b9a13d892 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1668,8 +1668,10 @@ static int do_unregister_framebuffer(struct fb_info *fb_info) if (!lock_fb_info(fb_info)) return -ENODEV; + console_lock(); event.info = fb_info; ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event); + console_unlock(); unlock_fb_info(fb_info); if (ret) @@ -1684,7 +1686,9 @@ static int do_unregister_framebuffer(struct fb_info *fb_info) num_registered_fb--; fb_cleanup_device(fb_info); event.info = fb_info; + console_lock(); fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event); + console_unlock(); /* this may free fb info */ put_fb_info(fb_info); diff --git a/include/linux/console.h b/include/linux/console.h index 4ef4307dfb82..47b858cffc47 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -77,6 +77,7 @@ extern const struct consw prom_con; /* SPARC PROM console */ int con_is_bound(const struct consw *csw); int register_con_driver(const struct consw *csw, int first, int last); int unregister_con_driver(const struct consw *csw); +int do_unregister_con_driver(const struct consw *csw); int take_over_console(const struct consw *sw, int first, int last, int deflt); int do_take_over_console(const struct consw *sw, int first, int last, int deflt); void give_up_console(const struct consw *sw); diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index 50ae7d0c279e..dbbc6bfee71f 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -130,6 +130,8 @@ void vt_event_post(unsigned int event, unsigned int old, unsigned int new); int vt_waitactive(int n); void change_console(struct vc_data *new_vc); void reset_vc(struct vc_data *vc); +extern int do_unbind_con_driver(const struct consw *csw, int first, int last, + int deflt); extern int unbind_con_driver(const struct consw *csw, int first, int last, int deflt); int vty_init(const struct file_operations *console_fops); -- cgit v1.2.3 From 054430e773c9a1e26f38e30156eff02dedfffc17 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 25 Jan 2013 11:38:56 +1000 Subject: fbcon: fix locking harder Okay so Alan's patch handled the case where there was no registered fbcon, however the other path entered in set_con2fb_map pit. In there we called fbcon_takeover, but we also took the console lock in a couple of places. So push the console lock out to the callers of set_con2fb_map, this means fbmem and switcheroo needed to take the lock around the fb notifier entry points that lead to this. This should fix the efifb regression seen by Maarten. Tested-by: Maarten Lankhorst Tested-by: Lu Hua Signed-off-by: Dave Airlie --- drivers/gpu/vga/vga_switcheroo.c | 3 +++ drivers/video/console/fbcon.c | 11 ++++++++--- drivers/video/fbmem.c | 2 ++ 3 files changed, 13 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index fa60add0ff63..cf787e1d9322 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -337,8 +338,10 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) if (new_client->fb_info) { struct fb_event event; + console_lock(); event.info = new_client->fb_info; fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event); + console_unlock(); } ret = vgasr_priv.handler->switchto(new_client->id); diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 2aef9cac4d18..2e2d959acae6 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -842,6 +842,8 @@ static void con2fb_init_display(struct vc_data *vc, struct fb_info *info, * * Maps a virtual console @unit to a frame buffer device * @newidx. + * + * This should be called with the console lock held. */ static int set_con2fb_map(int unit, int newidx, int user) { @@ -859,7 +861,7 @@ static int set_con2fb_map(int unit, int newidx, int user) if (!search_for_mapped_con() || !con_is_bound(&fb_con)) { info_idx = newidx; - return fbcon_takeover(0); + return do_fbcon_takeover(0); } if (oldidx != -1) @@ -867,7 +869,6 @@ static int set_con2fb_map(int unit, int newidx, int user) found = search_fb_in_map(newidx); - console_lock(); con2fb_map[unit] = newidx; if (!err && !found) err = con2fb_acquire_newinfo(vc, info, unit, oldidx); @@ -894,7 +895,6 @@ static int set_con2fb_map(int unit, int newidx, int user) if (!search_fb_in_map(info_idx)) info_idx = newidx; - console_unlock(); return err; } @@ -3019,6 +3019,7 @@ static inline int fbcon_unbind(void) } #endif /* CONFIG_VT_HW_CONSOLE_BINDING */ +/* called with console_lock held */ static int fbcon_fb_unbind(int idx) { int i, new_idx = -1, ret = 0; @@ -3045,6 +3046,7 @@ static int fbcon_fb_unbind(int idx) return ret; } +/* called with console_lock held */ static int fbcon_fb_unregistered(struct fb_info *info) { int i, idx; @@ -3082,6 +3084,7 @@ static int fbcon_fb_unregistered(struct fb_info *info) return 0; } +/* called with console_lock held */ static void fbcon_remap_all(int idx) { int i; @@ -3126,6 +3129,7 @@ static inline void fbcon_select_primary(struct fb_info *info) } #endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */ +/* called with console_lock held */ static int fbcon_fb_registered(struct fb_info *info) { int ret = 0, i, idx; @@ -3278,6 +3282,7 @@ static int fbcon_event_notify(struct notifier_block *self, ret = fbcon_fb_unregistered(info); break; case FB_EVENT_SET_CONSOLE_MAP: + /* called with console lock held */ con2fb = event->data; ret = set_con2fb_map(con2fb->console - 1, con2fb->framebuffer, 1); diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 070b9a13d892..dc61c12ecf8c 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1177,8 +1177,10 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, event.data = &con2fb; if (!lock_fb_info(info)) return -ENODEV; + console_lock(); event.info = info; ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event); + console_unlock(); unlock_fb_info(info); break; case FBIOBLANK: -- cgit v1.2.3 From 6504d0d9900a2c05ea1fbab2ec008bf442993d94 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 24 Jan 2013 21:46:07 +0000 Subject: drm/i2c: give i2c it's own Kconfig Move this out of nouveau directory. As we start to add more encoder slaves used by other drivers, it makes sense to put the Kconfig bits in one place. Signed-off-by: Rob Clark Signed-off-by: Dave Airlie --- drivers/gpu/drm/Kconfig | 2 ++ drivers/gpu/drm/i2c/Kconfig | 22 ++++++++++++++++++++++ drivers/gpu/drm/nouveau/Kconfig | 23 ----------------------- 3 files changed, 24 insertions(+), 23 deletions(-) create mode 100644 drivers/gpu/drm/i2c/Kconfig (limited to 'drivers') diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 3399b209260a..ed9e3af17b31 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -69,6 +69,8 @@ config DRM_KMS_CMA_HELPER help Choose this if you need the KMS CMA helper functions +source "drivers/gpu/drm/i2c/Kconfig" + config DRM_TDFX tristate "3dfx Banshee/Voodoo3+" depends on DRM && PCI diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig new file mode 100644 index 000000000000..16118363509a --- /dev/null +++ b/drivers/gpu/drm/i2c/Kconfig @@ -0,0 +1,22 @@ +menu "I2C encoder or helper chips" + depends on DRM && DRM_KMS_HELPER && I2C + +config DRM_I2C_CH7006 + tristate "Chrontel ch7006 TV encoder" + default m if DRM_NOUVEAU + help + Support for Chrontel ch7006 and similar TV encoders, found + on some nVidia video cards. + + This driver is currently only useful if you're also using + the nouveau driver. + +config DRM_I2C_SIL164 + tristate "Silicon Image sil164 TMDS transmitter" + default m if DRM_NOUVEAU + help + Support for sil164 and similar single-link (or dual-link + when used in pairs) TMDS transmitters, used in some nVidia + video cards. + +endmenu diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig index 8a55beeb8bdc..47ccc1ad5405 100644 --- a/drivers/gpu/drm/nouveau/Kconfig +++ b/drivers/gpu/drm/nouveau/Kconfig @@ -52,26 +52,3 @@ config DRM_NOUVEAU_BACKLIGHT help Say Y here if you want to control the backlight of your display (e.g. a laptop panel). - -menu "I2C encoder or helper chips" - depends on DRM && DRM_KMS_HELPER && I2C - -config DRM_I2C_CH7006 - tristate "Chrontel ch7006 TV encoder" - default m if DRM_NOUVEAU - help - Support for Chrontel ch7006 and similar TV encoders, found - on some nVidia video cards. - - This driver is currently only useful if you're also using - the nouveau driver. - -config DRM_I2C_SIL164 - tristate "Silicon Image sil164 TMDS transmitter" - default m if DRM_NOUVEAU - help - Support for sil164 and similar single-link (or dual-link - when used in pairs) TMDS transmitters, used in some nVidia - video cards. - -endmenu -- cgit v1.2.3 From 89177644a7b6306e6084a89eab7e290f4bfef397 Mon Sep 17 00:00:00 2001 From: Aaron Plattner Date: Tue, 15 Jan 2013 20:47:42 +0000 Subject: drm: add prime helpers Instead of reimplementing all of the dma_buf functionality in every driver, create helpers drm_prime_import and drm_prime_export that implement them in terms of new, lower-level hook functions: gem_prime_pin: callback when a buffer is created, used to pin buffers into GTT gem_prime_get_sg_table: convert a drm_gem_object to an sg_table for export gem_prime_import_sg_table: convert an sg_table into a drm_gem_object gem_prime_vmap, gem_prime_vunmap: map and unmap an object These hooks are optional; drivers can opt in by using drm_gem_prime_import and drm_gem_prime_export as the .gem_prime_import and .gem_prime_export fields of struct drm_driver. v2: - Drop .begin_cpu_access. None of the drivers this code replaces implemented it. Having it here was a leftover from when I was trying to include i915 in this rework. - Use mutex_lock instead of mutex_lock_interruptible, as these three drivers did. This patch series shouldn't change that behavior. - Rename helpers to gem_prime_get_sg_table and gem_prime_import_sg_table. Rename struct sg_table* variables to 'sgt' for clarity. - Update drm.tmpl for these new hooks. v3: - Pass the vaddr down to the driver. This lets drivers that just call vunmap on the pointer avoid having to store the pointer in their GEM private structures. - Move documentation into a /** DOC */ comment in drm_prime.c and include it in drm.tmpl with a !P line. I tried to use !F lines to include documentation of the individual functions from drmP.h, but the docproc / kernel-doc scripts barf on that file, so hopefully this is good enough for now. - apply refcount fix from commit be8a42ae60addd8b6092535c11b42d099d6470ec ("drm/prime: drop reference on imported dma-buf come from gem") Signed-off-by: Aaron Plattner Cc: Daniel Vetter Cc: David Airlie Signed-off-by: Dave Airlie --- Documentation/DocBook/drm.tmpl | 4 + drivers/gpu/drm/drm_prime.c | 186 ++++++++++++++++++++++++++++++++++++++++- include/drm/drmP.h | 12 +++ 3 files changed, 201 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 6c11d77c8d02..b26de523ab70 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -743,6 +743,10 @@ char *date; These two operations are mandatory for GEM drivers that support DRM PRIME. + + DRM PRIME Helper Functions Reference +!Pdrivers/gpu/drm/drm_prime.c PRIME Helpers + GEM Objects Mapping diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 7f125738f44e..366910ddcfcb 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -53,7 +53,8 @@ * Self-importing: if userspace is using PRIME as a replacement for flink * then it will get a fd->handle request for a GEM object that it created. * Drivers should detect this situation and return back the gem object - * from the dma-buf private. + * from the dma-buf private. Prime will do this automatically for drivers that + * use the drm_gem_prime_{import,export} helpers. */ struct drm_prime_member { @@ -62,6 +63,137 @@ struct drm_prime_member { uint32_t handle; }; +static struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach, + enum dma_data_direction dir) +{ + struct drm_gem_object *obj = attach->dmabuf->priv; + struct sg_table *sgt; + + mutex_lock(&obj->dev->struct_mutex); + + sgt = obj->dev->driver->gem_prime_get_sg_table(obj); + + if (!IS_ERR_OR_NULL(sgt)) + dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir); + + mutex_unlock(&obj->dev->struct_mutex); + return sgt; +} + +static void drm_gem_unmap_dma_buf(struct dma_buf_attachment *attach, + struct sg_table *sgt, enum dma_data_direction dir) +{ + dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir); + sg_free_table(sgt); + kfree(sgt); +} + +static void drm_gem_dmabuf_release(struct dma_buf *dma_buf) +{ + struct drm_gem_object *obj = dma_buf->priv; + + if (obj->export_dma_buf == dma_buf) { + /* drop the reference on the export fd holds */ + obj->export_dma_buf = NULL; + drm_gem_object_unreference_unlocked(obj); + } +} + +static void *drm_gem_dmabuf_vmap(struct dma_buf *dma_buf) +{ + struct drm_gem_object *obj = dma_buf->priv; + struct drm_device *dev = obj->dev; + + return dev->driver->gem_prime_vmap(obj); +} + +static void drm_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr) +{ + struct drm_gem_object *obj = dma_buf->priv; + struct drm_device *dev = obj->dev; + + dev->driver->gem_prime_vunmap(obj, vaddr); +} + +static void *drm_gem_dmabuf_kmap_atomic(struct dma_buf *dma_buf, + unsigned long page_num) +{ + return NULL; +} + +static void drm_gem_dmabuf_kunmap_atomic(struct dma_buf *dma_buf, + unsigned long page_num, void *addr) +{ + +} +static void *drm_gem_dmabuf_kmap(struct dma_buf *dma_buf, + unsigned long page_num) +{ + return NULL; +} + +static void drm_gem_dmabuf_kunmap(struct dma_buf *dma_buf, + unsigned long page_num, void *addr) +{ + +} + +static int drm_gem_dmabuf_mmap(struct dma_buf *dma_buf, + struct vm_area_struct *vma) +{ + return -EINVAL; +} + +static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = { + .map_dma_buf = drm_gem_map_dma_buf, + .unmap_dma_buf = drm_gem_unmap_dma_buf, + .release = drm_gem_dmabuf_release, + .kmap = drm_gem_dmabuf_kmap, + .kmap_atomic = drm_gem_dmabuf_kmap_atomic, + .kunmap = drm_gem_dmabuf_kunmap, + .kunmap_atomic = drm_gem_dmabuf_kunmap_atomic, + .mmap = drm_gem_dmabuf_mmap, + .vmap = drm_gem_dmabuf_vmap, + .vunmap = drm_gem_dmabuf_vunmap, +}; + +/** + * DOC: PRIME Helpers + * + * Drivers can implement @gem_prime_export and @gem_prime_import in terms of + * simpler APIs by using the helper functions @drm_gem_prime_export and + * @drm_gem_prime_import. These functions implement dma-buf support in terms of + * five lower-level driver callbacks: + * + * Export callbacks: + * + * - @gem_prime_pin (optional): prepare a GEM object for exporting + * + * - @gem_prime_get_sg_table: provide a scatter/gather table of pinned pages + * + * - @gem_prime_vmap: vmap a buffer exported by your driver + * + * - @gem_prime_vunmap: vunmap a buffer exported by your driver + * + * Import callback: + * + * - @gem_prime_import_sg_table (import): produce a GEM object from another + * driver's scatter/gather table + */ + +struct dma_buf *drm_gem_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, int flags) +{ + if (dev->driver->gem_prime_pin) { + int ret = dev->driver->gem_prime_pin(obj); + if (ret) + return ERR_PTR(ret); + } + return dma_buf_export(obj, &drm_gem_prime_dmabuf_ops, obj->size, + 0600); +} +EXPORT_SYMBOL(drm_gem_prime_export); + int drm_gem_prime_handle_to_fd(struct drm_device *dev, struct drm_file *file_priv, uint32_t handle, uint32_t flags, int *prime_fd) @@ -117,6 +249,58 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev, } EXPORT_SYMBOL(drm_gem_prime_handle_to_fd); +struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf) +{ + struct dma_buf_attachment *attach; + struct sg_table *sgt; + struct drm_gem_object *obj; + int ret; + + if (!dev->driver->gem_prime_import_sg_table) + return ERR_PTR(-EINVAL); + + if (dma_buf->ops == &drm_gem_prime_dmabuf_ops) { + obj = dma_buf->priv; + if (obj->dev == dev) { + /* + * Importing dmabuf exported from out own gem increases + * refcount on gem itself instead of f_count of dmabuf. + */ + drm_gem_object_reference(obj); + dma_buf_put(dma_buf); + return obj; + } + } + + attach = dma_buf_attach(dma_buf, dev->dev); + if (IS_ERR(attach)) + return ERR_PTR(PTR_ERR(attach)); + + sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + if (IS_ERR_OR_NULL(sgt)) { + ret = PTR_ERR(sgt); + goto fail_detach; + } + + obj = dev->driver->gem_prime_import_sg_table(dev, dma_buf->size, sgt); + if (IS_ERR(obj)) { + ret = PTR_ERR(obj); + goto fail_unmap; + } + + obj->import_attach = attach; + + return obj; + +fail_unmap: + dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); +fail_detach: + dma_buf_detach(dma_buf, attach); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(drm_gem_prime_import); + int drm_gem_prime_fd_to_handle(struct drm_device *dev, struct drm_file *file_priv, int prime_fd, uint32_t *handle) { diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 3aaa50d64451..2d94d7413d71 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -930,6 +930,14 @@ struct drm_driver { /* import dmabuf -> GEM */ struct drm_gem_object * (*gem_prime_import)(struct drm_device *dev, struct dma_buf *dma_buf); + /* low-level interface used by drm_gem_prime_{import,export} */ + int (*gem_prime_pin)(struct drm_gem_object *obj); + struct sg_table *(*gem_prime_get_sg_table)(struct drm_gem_object *obj); + struct drm_gem_object *(*gem_prime_import_sg_table)( + struct drm_device *dev, size_t size, + struct sg_table *sgt); + void *(*gem_prime_vmap)(struct drm_gem_object *obj); + void (*gem_prime_vunmap)(struct drm_gem_object *obj, void *vaddr); /* vga arb irq handler */ void (*vgaarb_irq)(struct drm_device *dev, bool state); @@ -1562,9 +1570,13 @@ extern int drm_clients_info(struct seq_file *m, void* data); extern int drm_gem_name_info(struct seq_file *m, void *data); +extern struct dma_buf *drm_gem_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, int flags); extern int drm_gem_prime_handle_to_fd(struct drm_device *dev, struct drm_file *file_priv, uint32_t handle, uint32_t flags, int *prime_fd); +extern struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf); extern int drm_gem_prime_fd_to_handle(struct drm_device *dev, struct drm_file *file_priv, int prime_fd, uint32_t *handle); -- cgit v1.2.3 From ab9ccb96a6e6f95bcde6b8b2a524370efdbfdcd6 Mon Sep 17 00:00:00 2001 From: Aaron Plattner Date: Tue, 15 Jan 2013 20:47:43 +0000 Subject: drm/nouveau: use prime helpers Simplify the Nouveau prime implementation by using the default behavior provided by drm_gem_prime_import and drm_gem_prime_export. v2: Rename functions to nouveau_gem_prime_get_sg_table and nouveau_gem_prime_import_sg_table. Signed-off-by: Aaron Plattner Cc: Daniel Vetter Cc: David Airlie Signed-off-by: Dave Airlie --- drivers/gpu/drm/nouveau/nouveau_bo.h | 1 - drivers/gpu/drm/nouveau/nouveau_drm.c | 9 +- drivers/gpu/drm/nouveau/nouveau_gem.c | 2 - drivers/gpu/drm/nouveau/nouveau_gem.h | 10 +- drivers/gpu/drm/nouveau/nouveau_prime.c | 173 ++++---------------------------- 5 files changed, 34 insertions(+), 161 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.h b/drivers/gpu/drm/nouveau/nouveau_bo.h index 81d00fe03b56..653dbbbd4fa1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.h +++ b/drivers/gpu/drm/nouveau/nouveau_bo.h @@ -33,7 +33,6 @@ struct nouveau_bo { int pin_refcnt; struct ttm_bo_kmap_obj dma_buf_vmap; - int vmapping_count; }; static inline struct nouveau_bo * diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 8b090f1eb51d..8e8e8ce75528 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -650,8 +650,13 @@ driver = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, - .gem_prime_export = nouveau_gem_prime_export, - .gem_prime_import = nouveau_gem_prime_import, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_pin = nouveau_gem_prime_pin, + .gem_prime_get_sg_table = nouveau_gem_prime_get_sg_table, + .gem_prime_import_sg_table = nouveau_gem_prime_import_sg_table, + .gem_prime_vmap = nouveau_gem_prime_vmap, + .gem_prime_vunmap = nouveau_gem_prime_vunmap, .gem_init_object = nouveau_gem_object_new, .gem_free_object = nouveau_gem_object_del, diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 8bf695c52f95..24e0aabda03c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -24,8 +24,6 @@ * */ -#include - #include #include "nouveau_drm.h" diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.h b/drivers/gpu/drm/nouveau/nouveau_gem.h index 5c1049236d22..8d7a3f0aeb86 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.h +++ b/drivers/gpu/drm/nouveau/nouveau_gem.h @@ -35,9 +35,11 @@ extern int nouveau_gem_ioctl_cpu_fini(struct drm_device *, void *, extern int nouveau_gem_ioctl_info(struct drm_device *, void *, struct drm_file *); -extern struct dma_buf *nouveau_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, int flags); -extern struct drm_gem_object *nouveau_gem_prime_import(struct drm_device *dev, - struct dma_buf *dma_buf); +extern int nouveau_gem_prime_pin(struct drm_gem_object *); +extern struct sg_table *nouveau_gem_prime_get_sg_table(struct drm_gem_object *); +extern struct drm_gem_object *nouveau_gem_prime_import_sg_table( + struct drm_device *, size_t size, struct sg_table *); +extern void *nouveau_gem_prime_vmap(struct drm_gem_object *); +extern void nouveau_gem_prime_vunmap(struct drm_gem_object *, void *); #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_prime.c b/drivers/gpu/drm/nouveau/nouveau_prime.c index b8e05ae38212..f53e10874cae 100644 --- a/drivers/gpu/drm/nouveau/nouveau_prime.c +++ b/drivers/gpu/drm/nouveau/nouveau_prime.c @@ -22,126 +22,42 @@ * Authors: Dave Airlie */ -#include - #include #include "nouveau_drm.h" #include "nouveau_gem.h" -static struct sg_table *nouveau_gem_map_dma_buf(struct dma_buf_attachment *attachment, - enum dma_data_direction dir) +struct sg_table *nouveau_gem_prime_get_sg_table(struct drm_gem_object *obj) { - struct nouveau_bo *nvbo = attachment->dmabuf->priv; - struct drm_device *dev = nvbo->gem->dev; + struct nouveau_bo *nvbo = nouveau_gem_object(obj); int npages = nvbo->bo.num_pages; - struct sg_table *sg; - int nents; - - mutex_lock(&dev->struct_mutex); - sg = drm_prime_pages_to_sg(nvbo->bo.ttm->pages, npages); - nents = dma_map_sg(attachment->dev, sg->sgl, sg->nents, dir); - mutex_unlock(&dev->struct_mutex); - return sg; -} - -static void nouveau_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, - struct sg_table *sg, enum dma_data_direction dir) -{ - dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir); - sg_free_table(sg); - kfree(sg); -} - -static void nouveau_gem_dmabuf_release(struct dma_buf *dma_buf) -{ - struct nouveau_bo *nvbo = dma_buf->priv; - - if (nvbo->gem->export_dma_buf == dma_buf) { - nvbo->gem->export_dma_buf = NULL; - drm_gem_object_unreference_unlocked(nvbo->gem); - } -} - -static void *nouveau_gem_kmap_atomic(struct dma_buf *dma_buf, unsigned long page_num) -{ - return NULL; -} - -static void nouveau_gem_kunmap_atomic(struct dma_buf *dma_buf, unsigned long page_num, void *addr) -{ -} -static void *nouveau_gem_kmap(struct dma_buf *dma_buf, unsigned long page_num) -{ - return NULL; + return drm_prime_pages_to_sg(nvbo->bo.ttm->pages, npages); } -static void nouveau_gem_kunmap(struct dma_buf *dma_buf, unsigned long page_num, void *addr) +void *nouveau_gem_prime_vmap(struct drm_gem_object *obj) { - -} - -static int nouveau_gem_prime_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma) -{ - return -EINVAL; -} - -static void *nouveau_gem_prime_vmap(struct dma_buf *dma_buf) -{ - struct nouveau_bo *nvbo = dma_buf->priv; - struct drm_device *dev = nvbo->gem->dev; + struct nouveau_bo *nvbo = nouveau_gem_object(obj); int ret; - mutex_lock(&dev->struct_mutex); - if (nvbo->vmapping_count) { - nvbo->vmapping_count++; - goto out_unlock; - } - ret = ttm_bo_kmap(&nvbo->bo, 0, nvbo->bo.num_pages, &nvbo->dma_buf_vmap); - if (ret) { - mutex_unlock(&dev->struct_mutex); + if (ret) return ERR_PTR(ret); - } - nvbo->vmapping_count = 1; -out_unlock: - mutex_unlock(&dev->struct_mutex); + return nvbo->dma_buf_vmap.virtual; } -static void nouveau_gem_prime_vunmap(struct dma_buf *dma_buf, void *vaddr) +void nouveau_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) { - struct nouveau_bo *nvbo = dma_buf->priv; - struct drm_device *dev = nvbo->gem->dev; + struct nouveau_bo *nvbo = nouveau_gem_object(obj); - mutex_lock(&dev->struct_mutex); - nvbo->vmapping_count--; - if (nvbo->vmapping_count == 0) { - ttm_bo_kunmap(&nvbo->dma_buf_vmap); - } - mutex_unlock(&dev->struct_mutex); + ttm_bo_kunmap(&nvbo->dma_buf_vmap); } -static const struct dma_buf_ops nouveau_dmabuf_ops = { - .map_dma_buf = nouveau_gem_map_dma_buf, - .unmap_dma_buf = nouveau_gem_unmap_dma_buf, - .release = nouveau_gem_dmabuf_release, - .kmap = nouveau_gem_kmap, - .kmap_atomic = nouveau_gem_kmap_atomic, - .kunmap = nouveau_gem_kunmap, - .kunmap_atomic = nouveau_gem_kunmap_atomic, - .mmap = nouveau_gem_prime_mmap, - .vmap = nouveau_gem_prime_vmap, - .vunmap = nouveau_gem_prime_vunmap, -}; - -static int -nouveau_prime_new(struct drm_device *dev, - size_t size, - struct sg_table *sg, - struct nouveau_bo **pnvbo) +struct drm_gem_object *nouveau_gem_prime_import_sg_table(struct drm_device *dev, + size_t size, + struct sg_table *sg) { struct nouveau_bo *nvbo; u32 flags = 0; @@ -150,24 +66,22 @@ nouveau_prime_new(struct drm_device *dev, flags = TTM_PL_FLAG_TT; ret = nouveau_bo_new(dev, size, 0, flags, 0, 0, - sg, pnvbo); + sg, &nvbo); if (ret) - return ret; - nvbo = *pnvbo; + return ERR_PTR(ret); nvbo->valid_domains = NOUVEAU_GEM_DOMAIN_GART; nvbo->gem = drm_gem_object_alloc(dev, nvbo->bo.mem.size); if (!nvbo->gem) { - nouveau_bo_ref(NULL, pnvbo); - return -ENOMEM; + nouveau_bo_ref(NULL, &nvbo); + return ERR_PTR(-ENOMEM); } nvbo->gem->driver_private = nvbo; - return 0; + return nvbo->gem; } -struct dma_buf *nouveau_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, int flags) +int nouveau_gem_prime_pin(struct drm_gem_object *obj) { struct nouveau_bo *nvbo = nouveau_gem_object(obj); int ret = 0; @@ -175,52 +89,7 @@ struct dma_buf *nouveau_gem_prime_export(struct drm_device *dev, /* pin buffer into GTT */ ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_TT); if (ret) - return ERR_PTR(-EINVAL); - - return dma_buf_export(nvbo, &nouveau_dmabuf_ops, obj->size, flags); -} - -struct drm_gem_object *nouveau_gem_prime_import(struct drm_device *dev, - struct dma_buf *dma_buf) -{ - struct dma_buf_attachment *attach; - struct sg_table *sg; - struct nouveau_bo *nvbo; - int ret; - - if (dma_buf->ops == &nouveau_dmabuf_ops) { - nvbo = dma_buf->priv; - if (nvbo->gem) { - if (nvbo->gem->dev == dev) { - drm_gem_object_reference(nvbo->gem); - dma_buf_put(dma_buf); - return nvbo->gem; - } - } - } - /* need to attach */ - attach = dma_buf_attach(dma_buf, dev->dev); - if (IS_ERR(attach)) - return ERR_PTR(PTR_ERR(attach)); - - sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); - if (IS_ERR(sg)) { - ret = PTR_ERR(sg); - goto fail_detach; - } - - ret = nouveau_prime_new(dev, dma_buf->size, sg, &nvbo); - if (ret) - goto fail_unmap; - - nvbo->gem->import_attach = attach; - - return nvbo->gem; + return -EINVAL; -fail_unmap: - dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL); -fail_detach: - dma_buf_detach(dma_buf, attach); - return ERR_PTR(ret); + return 0; } - -- cgit v1.2.3 From 1e6d17a5df848cf8e483b689c6295776e9d6d997 Mon Sep 17 00:00:00 2001 From: Aaron Plattner Date: Tue, 15 Jan 2013 20:47:44 +0000 Subject: drm/radeon: use prime helpers Simplify the Radeon prime implementation by using the default behavior provided by drm_gem_prime_import and drm_gem_prime_export. v2: - Rename functions to radeon_gem_prime_get_sg_table and radeon_gem_prime_import_sg_table. - Delete the now-unused vmapping_count variable. Signed-off-by: Aaron Plattner Cc: Daniel Vetter Cc: David Airlie Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/radeon.h | 1 - drivers/gpu/drm/radeon/radeon_drv.c | 21 +++-- drivers/gpu/drm/radeon/radeon_prime.c | 170 +++++----------------------------- 3 files changed, 35 insertions(+), 157 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 59bfbd3868c9..bb43a849759b 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -350,7 +350,6 @@ struct radeon_bo { struct drm_gem_object gem_base; struct ttm_bo_kmap_obj dma_buf_vmap; - int vmapping_count; }; #define gem_to_radeon_bo(gobj) container_of((gobj), struct radeon_bo, gem_base) diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 833484da12d9..167758488ed6 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -118,11 +118,13 @@ int radeon_mode_dumb_create(struct drm_file *file_priv, int radeon_mode_dumb_destroy(struct drm_file *file_priv, struct drm_device *dev, uint32_t handle); -struct dma_buf *radeon_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, - int flags); -struct drm_gem_object *radeon_gem_prime_import(struct drm_device *dev, - struct dma_buf *dma_buf); +struct sg_table *radeon_gem_prime_get_sg_table(struct drm_gem_object *obj); +struct drm_gem_object *radeon_gem_prime_import_sg_table(struct drm_device *dev, + size_t size, + struct sg_table *sg); +int radeon_gem_prime_pin(struct drm_gem_object *obj); +void *radeon_gem_prime_vmap(struct drm_gem_object *obj); +void radeon_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr); extern long radeon_kms_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); @@ -409,8 +411,13 @@ static struct drm_driver kms_driver = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, - .gem_prime_export = radeon_gem_prime_export, - .gem_prime_import = radeon_gem_prime_import, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_pin = radeon_gem_prime_pin, + .gem_prime_get_sg_table = radeon_gem_prime_get_sg_table, + .gem_prime_import_sg_table = radeon_gem_prime_import_sg_table, + .gem_prime_vmap = radeon_gem_prime_vmap, + .gem_prime_vunmap = radeon_gem_prime_vunmap, .name = DRIVER_NAME, .desc = DRIVER_DESC, diff --git a/drivers/gpu/drm/radeon/radeon_prime.c b/drivers/gpu/drm/radeon/radeon_prime.c index 26c23bb651c6..4940af7e75e6 100644 --- a/drivers/gpu/drm/radeon/radeon_prime.c +++ b/drivers/gpu/drm/radeon/radeon_prime.c @@ -28,199 +28,71 @@ #include "radeon.h" #include -#include - -static struct sg_table *radeon_gem_map_dma_buf(struct dma_buf_attachment *attachment, - enum dma_data_direction dir) +struct sg_table *radeon_gem_prime_get_sg_table(struct drm_gem_object *obj) { - struct radeon_bo *bo = attachment->dmabuf->priv; - struct drm_device *dev = bo->rdev->ddev; + struct radeon_bo *bo = gem_to_radeon_bo(obj); int npages = bo->tbo.num_pages; - struct sg_table *sg; - int nents; - - mutex_lock(&dev->struct_mutex); - sg = drm_prime_pages_to_sg(bo->tbo.ttm->pages, npages); - nents = dma_map_sg(attachment->dev, sg->sgl, sg->nents, dir); - mutex_unlock(&dev->struct_mutex); - return sg; -} - -static void radeon_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, - struct sg_table *sg, enum dma_data_direction dir) -{ - dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir); - sg_free_table(sg); - kfree(sg); -} - -static void radeon_gem_dmabuf_release(struct dma_buf *dma_buf) -{ - struct radeon_bo *bo = dma_buf->priv; - - if (bo->gem_base.export_dma_buf == dma_buf) { - DRM_ERROR("unreference dmabuf %p\n", &bo->gem_base); - bo->gem_base.export_dma_buf = NULL; - drm_gem_object_unreference_unlocked(&bo->gem_base); - } -} - -static void *radeon_gem_kmap_atomic(struct dma_buf *dma_buf, unsigned long page_num) -{ - return NULL; -} - -static void radeon_gem_kunmap_atomic(struct dma_buf *dma_buf, unsigned long page_num, void *addr) -{ - -} -static void *radeon_gem_kmap(struct dma_buf *dma_buf, unsigned long page_num) -{ - return NULL; -} - -static void radeon_gem_kunmap(struct dma_buf *dma_buf, unsigned long page_num, void *addr) -{ + return drm_prime_pages_to_sg(bo->tbo.ttm->pages, npages); } -static int radeon_gem_prime_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma) +void *radeon_gem_prime_vmap(struct drm_gem_object *obj) { - return -EINVAL; -} - -static void *radeon_gem_prime_vmap(struct dma_buf *dma_buf) -{ - struct radeon_bo *bo = dma_buf->priv; - struct drm_device *dev = bo->rdev->ddev; + struct radeon_bo *bo = gem_to_radeon_bo(obj); int ret; - mutex_lock(&dev->struct_mutex); - if (bo->vmapping_count) { - bo->vmapping_count++; - goto out_unlock; - } - ret = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages, &bo->dma_buf_vmap); - if (ret) { - mutex_unlock(&dev->struct_mutex); + if (ret) return ERR_PTR(ret); - } - bo->vmapping_count = 1; -out_unlock: - mutex_unlock(&dev->struct_mutex); + return bo->dma_buf_vmap.virtual; } -static void radeon_gem_prime_vunmap(struct dma_buf *dma_buf, void *vaddr) +void radeon_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) { - struct radeon_bo *bo = dma_buf->priv; - struct drm_device *dev = bo->rdev->ddev; + struct radeon_bo *bo = gem_to_radeon_bo(obj); - mutex_lock(&dev->struct_mutex); - bo->vmapping_count--; - if (bo->vmapping_count == 0) { - ttm_bo_kunmap(&bo->dma_buf_vmap); - } - mutex_unlock(&dev->struct_mutex); + ttm_bo_kunmap(&bo->dma_buf_vmap); } -const static struct dma_buf_ops radeon_dmabuf_ops = { - .map_dma_buf = radeon_gem_map_dma_buf, - .unmap_dma_buf = radeon_gem_unmap_dma_buf, - .release = radeon_gem_dmabuf_release, - .kmap = radeon_gem_kmap, - .kmap_atomic = radeon_gem_kmap_atomic, - .kunmap = radeon_gem_kunmap, - .kunmap_atomic = radeon_gem_kunmap_atomic, - .mmap = radeon_gem_prime_mmap, - .vmap = radeon_gem_prime_vmap, - .vunmap = radeon_gem_prime_vunmap, -}; - -static int radeon_prime_create(struct drm_device *dev, - size_t size, - struct sg_table *sg, - struct radeon_bo **pbo) + +struct drm_gem_object *radeon_gem_prime_import_sg_table(struct drm_device *dev, + size_t size, + struct sg_table *sg) { struct radeon_device *rdev = dev->dev_private; struct radeon_bo *bo; int ret; ret = radeon_bo_create(rdev, size, PAGE_SIZE, false, - RADEON_GEM_DOMAIN_GTT, sg, pbo); + RADEON_GEM_DOMAIN_GTT, sg, &bo); if (ret) - return ret; - bo = *pbo; + return ERR_PTR(ret); bo->gem_base.driver_private = bo; mutex_lock(&rdev->gem.mutex); list_add_tail(&bo->list, &rdev->gem.objects); mutex_unlock(&rdev->gem.mutex); - return 0; + return &bo->gem_base; } -struct dma_buf *radeon_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, - int flags) +int radeon_gem_prime_pin(struct drm_gem_object *obj) { struct radeon_bo *bo = gem_to_radeon_bo(obj); int ret = 0; ret = radeon_bo_reserve(bo, false); if (unlikely(ret != 0)) - return ERR_PTR(ret); + return ret; /* pin buffer into GTT */ ret = radeon_bo_pin(bo, RADEON_GEM_DOMAIN_GTT, NULL); if (ret) { radeon_bo_unreserve(bo); - return ERR_PTR(ret); + return ret; } radeon_bo_unreserve(bo); - return dma_buf_export(bo, &radeon_dmabuf_ops, obj->size, flags); -} -struct drm_gem_object *radeon_gem_prime_import(struct drm_device *dev, - struct dma_buf *dma_buf) -{ - struct dma_buf_attachment *attach; - struct sg_table *sg; - struct radeon_bo *bo; - int ret; - - if (dma_buf->ops == &radeon_dmabuf_ops) { - bo = dma_buf->priv; - if (bo->gem_base.dev == dev) { - drm_gem_object_reference(&bo->gem_base); - dma_buf_put(dma_buf); - return &bo->gem_base; - } - } - - /* need to attach */ - attach = dma_buf_attach(dma_buf, dev->dev); - if (IS_ERR(attach)) - return ERR_CAST(attach); - - sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); - if (IS_ERR(sg)) { - ret = PTR_ERR(sg); - goto fail_detach; - } - - ret = radeon_prime_create(dev, dma_buf->size, sg, &bo); - if (ret) - goto fail_unmap; - - bo->gem_base.import_attach = attach; - - return &bo->gem_base; - -fail_unmap: - dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL); -fail_detach: - dma_buf_detach(dma_buf, attach); - return ERR_PTR(ret); + return 0; } -- cgit v1.2.3 From 03f6509df9218c760ae74f41a609233220d33f19 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 14 Jan 2013 16:05:56 +0000 Subject: drm: Allow vblank support without DRIVER_HAVE_IRQ Drivers that register interrupt handlers without the DRM core helpers don't initialize the .irq_enabled field and drm_dev_to_irq() may fail when called on them. This shouldn't preclude them from implementing the vblank IOCTL. Signed-off-by: Thierry Reding Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_irq.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 19c01ca3cc76..71f820565637 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -1218,8 +1218,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data, int ret; unsigned int flags, seq, crtc, high_crtc; - if ((!drm_dev_to_irq(dev)) || (!dev->irq_enabled)) - return -EINVAL; + if (drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) + if ((!drm_dev_to_irq(dev)) || (!dev->irq_enabled)) + return -EINVAL; if (vblwait->request.type & _DRM_VBLANK_SIGNAL) return -EINVAL; -- cgit v1.2.3 From 9fe0423eac2c0781a3ece8779b086acd3e76f2c8 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 4 Jan 2013 19:10:32 +0000 Subject: drm/pci: Use the standard #defines for PCIe Link Capability bits Use the standard #defines rather than bare numbers for the PCIe Link Capabilities speed bits. Signed-off-by: Bjorn Helgaas Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 754bc96e10c7..11c8adde40b0 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -504,9 +504,9 @@ int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask) if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB) *mask |= DRM_PCIE_SPEED_80; } else { - if (lnkcap & 1) + if (lnkcap & PCI_EXP_LNKCAP_SLS_2_5GB) *mask |= DRM_PCIE_SPEED_25; - if (lnkcap & 2) + if (lnkcap & PCI_EXP_LNKCAP_SLS_5_0GB) *mask |= DRM_PCIE_SPEED_50; } -- cgit v1.2.3 From f8acf6f4c8fe1fd4de1f669ac6a3c71e89f13523 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 4 Jan 2013 19:10:37 +0000 Subject: drm/pci: Set all supported speeds in speed cap mask for pre-3.0 devices For devices that conform to PCIe r3.0 and have a Link Capabilities 2 register, we test and report every bit in the Supported Link Speeds Vector field. For a device that supports both 2.5GT/s and 5.0GT/s, we set both DRM_PCIE_SPEED_25 and DRM_PCIE_SPEED_50 in the returned mask. For pre-r3.0 devices, the Link Capabilities 0010b encoding (PCI_EXP_LNKCAP_SLS_5_0GB) means that both 5.0GT/s and 2.5GT/s are supported, so set both DRM_PCIE_SPEED_25 and DRM_PCIE_SPEED_50 in this case as well. Signed-off-by: Bjorn Helgaas Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 11c8adde40b0..50e26f2d198e 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -507,7 +507,7 @@ int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask) if (lnkcap & PCI_EXP_LNKCAP_SLS_2_5GB) *mask |= DRM_PCIE_SPEED_25; if (lnkcap & PCI_EXP_LNKCAP_SLS_5_0GB) - *mask |= DRM_PCIE_SPEED_50; + *mask |= (DRM_PCIE_SPEED_25 | DRM_PCIE_SPEED_50); } DRM_INFO("probing gen 2 caps for device %x:%x = %x/%x\n", root->vendor, root->device, lnkcap, lnkcap2); -- cgit v1.2.3 From dd66cc2e1f4765d0e6f39eb1e7d8d64d3f1cc522 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 4 Jan 2013 19:10:42 +0000 Subject: drm/pci: Use PCI Express Capability accessors Use PCI Express Capability access functions to simplify this code a bit. For non-PCIe devices or pre-PCIe 3.0 devices that don't implement the Link Capabilities 2 register, pcie_capability_read_dword() reads a zero. Since we're only testing whether the bits we care about are set, there's no need to mask out the other bits we *don't* care about. Signed-off-by: Bjorn Helgaas Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_pci.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 50e26f2d198e..86102a08f65c 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -469,41 +469,30 @@ EXPORT_SYMBOL(drm_pci_exit); int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask) { struct pci_dev *root; - int pos; - u32 lnkcap = 0, lnkcap2 = 0; + u32 lnkcap, lnkcap2; *mask = 0; if (!dev->pdev) return -EINVAL; - if (!pci_is_pcie(dev->pdev)) - return -EINVAL; - root = dev->pdev->bus->self; - pos = pci_pcie_cap(root); - if (!pos) - return -EINVAL; - /* we've been informed via and serverworks don't make the cut */ if (root->vendor == PCI_VENDOR_ID_VIA || root->vendor == PCI_VENDOR_ID_SERVERWORKS) return -EINVAL; - pci_read_config_dword(root, pos + PCI_EXP_LNKCAP, &lnkcap); - pci_read_config_dword(root, pos + PCI_EXP_LNKCAP2, &lnkcap2); - - lnkcap &= PCI_EXP_LNKCAP_SLS; - lnkcap2 &= 0xfe; + pcie_capability_read_dword(root, PCI_EXP_LNKCAP, &lnkcap); + pcie_capability_read_dword(root, PCI_EXP_LNKCAP2, &lnkcap2); - if (lnkcap2) { /* PCIE GEN 3.0 */ + if (lnkcap2) { /* PCIe r3.0-compliant */ if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB) *mask |= DRM_PCIE_SPEED_25; if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB) *mask |= DRM_PCIE_SPEED_50; if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB) *mask |= DRM_PCIE_SPEED_80; - } else { + } else { /* pre-r3.0 */ if (lnkcap & PCI_EXP_LNKCAP_SLS_2_5GB) *mask |= DRM_PCIE_SPEED_25; if (lnkcap & PCI_EXP_LNKCAP_SLS_5_0GB) -- cgit v1.2.3 From 85a7ce67f3ebfd5975ffd1febcabfe4999ca911d Mon Sep 17 00:00:00 2001 From: Daniel Kurtz Date: Thu, 27 Dec 2012 01:01:46 +0000 Subject: drm: make frame duration time calculation more precise It is a bit more precise to compute the total number of pixels first and then divide, rather than multiplying the line pixel count by the already-rounded line duration. Signed-off-by: Daniel Kurtz Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_irq.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 71f820565637..38e79927b2d7 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -505,6 +505,7 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc) /* Valid dotclock? */ if (dotclock > 0) { + int frame_size; /* Convert scanline length in pixels and video dot clock to * line duration, frame duration and pixel duration in * nanoseconds: @@ -512,7 +513,10 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc) pixeldur_ns = (s64) div64_u64(1000000000, dotclock); linedur_ns = (s64) div64_u64(((u64) crtc->hwmode.crtc_htotal * 1000000000), dotclock); - framedur_ns = (s64) crtc->hwmode.crtc_vtotal * linedur_ns; + frame_size = crtc->hwmode.crtc_htotal * + crtc->hwmode.crtc_vtotal; + framedur_ns = (s64) div64_u64((u64) frame_size * 1000000000, + dotclock); } else DRM_ERROR("crtc %d: Can't calculate constants, dotclock = 0!\n", crtc->base.id); -- cgit v1.2.3 From 76cc1887496fe80138c6b07c37d7f81e4cf27cde Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:05:26 +0000 Subject: thermal: rcar: add Device Tree support Support for loading the Renesas R-Car thermal module via devicetree. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- .../devicetree/bindings/thermal/rcar-thermal.txt | 29 ++++++++++++++++++++++ drivers/thermal/rcar_thermal.c | 7 ++++++ 2 files changed, 36 insertions(+) create mode 100644 Documentation/devicetree/bindings/thermal/rcar-thermal.txt (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/thermal/rcar-thermal.txt b/Documentation/devicetree/bindings/thermal/rcar-thermal.txt new file mode 100644 index 000000000000..28ef498a66e5 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/rcar-thermal.txt @@ -0,0 +1,29 @@ +* Renesas R-Car Thermal + +Required properties: +- compatible : "renesas,rcar-thermal" +- reg : Address range of the thermal registers. + The 1st reg will be recognized as common register + if it has "interrupts". + +Option properties: + +- interrupts : use interrupt + +Example (non interrupt support): + +thermal@e61f0100 { + compatible = "renesas,rcar-thermal"; + reg = <0xe61f0100 0x38>; +}; + +Example (interrupt support): + +thermal@e61f0000 { + compatible = "renesas,rcar-thermal"; + reg = <0xe61f0000 0x14 + 0xe61f0100 0x38 + 0xe61f0200 0x38 + 0xe61f0300 0x38>; + interrupts = <0 69 4>; +}; diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 2eebcadb4c99..909bb4bb837f 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -476,9 +476,16 @@ static int rcar_thermal_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id rcar_thermal_dt_ids[] __devinitconst = { + { .compatible = "renesas,rcar-thermal", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rcar_thermal_dt_ids); + static struct platform_driver rcar_thermal_driver = { .driver = { .name = "rcar_thermal", + .of_match_table = rcar_thermal_dt_ids, }, .probe = rcar_thermal_probe, .remove = rcar_thermal_remove, -- cgit v1.2.3 From 7060aa36645c51d1205ef0e0cbf7b564f1f52f36 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Wed, 6 Feb 2013 06:35:24 +0000 Subject: thermal: Add support for the thermal sensor on Kirkwood SoCs This patch adds support for Kirkwood 88F6282 and 88F6283 thermal sensor. Signed-off-by: Nobuhiro Iwamatsu Signed-off-by: Andrew Lunn Signed-off-by: Zhang Rui --- .../bindings/thermal/kirkwood-thermal.txt | 15 +++ drivers/thermal/Kconfig | 8 ++ drivers/thermal/Makefile | 1 + drivers/thermal/kirkwood_thermal.c | 134 +++++++++++++++++++++ 4 files changed, 158 insertions(+) create mode 100644 Documentation/devicetree/bindings/thermal/kirkwood-thermal.txt create mode 100644 drivers/thermal/kirkwood_thermal.c (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/thermal/kirkwood-thermal.txt b/Documentation/devicetree/bindings/thermal/kirkwood-thermal.txt new file mode 100644 index 000000000000..8c0f5eb86da7 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/kirkwood-thermal.txt @@ -0,0 +1,15 @@ +* Kirkwood Thermal + +This version is for Kirkwood 88F8262 & 88F6283 SoCs. Other kirkwoods +don't contain a thermal sensor. + +Required properties: +- compatible : "marvell,kirkwood-thermal" +- reg : Address range of the thermal registers + +Example: + + thermal@10078 { + compatible = "marvell,kirkwood-thermal"; + reg = <0x10078 0x4>; + }; diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index e4cf7fbc3a59..5070fbee24ef 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -101,6 +101,14 @@ config RCAR_THERMAL Enable this to plug the R-Car thermal sensor driver into the Linux thermal framework +config KIRKWOOD_THERMAL + tristate "Temperature sensor on Marvell Kirkwood SoCs" + depends on ARCH_KIRKWOOD + depends on OF + help + Support for the Kirkwood thermal sensor driver into the Linux thermal + framework. Only kirkwood 88F6282 and 88F6283 have this sensor. + config EXYNOS_THERMAL tristate "Temperature sensor on Samsung EXYNOS" depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5) diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 574f5f505b9f..b32c35de67e7 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o # platform thermal drivers obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o +obj-$(CONFIG_KIRKWOOD_THERMAL) += kirkwood_thermal.o obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o diff --git a/drivers/thermal/kirkwood_thermal.c b/drivers/thermal/kirkwood_thermal.c new file mode 100644 index 000000000000..65cb4f09e8f6 --- /dev/null +++ b/drivers/thermal/kirkwood_thermal.c @@ -0,0 +1,134 @@ +/* + * Kirkwood thermal sensor driver + * + * Copyright (C) 2012 Nobuhiro Iwamatsu + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define KIRKWOOD_THERMAL_VALID_OFFSET 9 +#define KIRKWOOD_THERMAL_VALID_MASK 0x1 +#define KIRKWOOD_THERMAL_TEMP_OFFSET 10 +#define KIRKWOOD_THERMAL_TEMP_MASK 0x1FF + +/* Kirkwood Thermal Sensor Dev Structure */ +struct kirkwood_thermal_priv { + void __iomem *sensor; +}; + +static int kirkwood_get_temp(struct thermal_zone_device *thermal, + unsigned long *temp) +{ + unsigned long reg; + struct kirkwood_thermal_priv *priv = thermal->devdata; + + reg = readl_relaxed(priv->sensor); + + /* Valid check */ + if (!(reg >> KIRKWOOD_THERMAL_VALID_OFFSET) & + KIRKWOOD_THERMAL_VALID_MASK) { + dev_err(&thermal->device, + "Temperature sensor reading not valid\n"); + return -EIO; + } + + /* + * Calculate temperature. See Section 8.10.1 of the 88AP510, + * datasheet, which has the same sensor. + * Documentation/arm/Marvell/README + */ + reg = (reg >> KIRKWOOD_THERMAL_TEMP_OFFSET) & + KIRKWOOD_THERMAL_TEMP_MASK; + *temp = ((2281638UL - (7298*reg)) / 10); + + return 0; +} + +static struct thermal_zone_device_ops ops = { + .get_temp = kirkwood_get_temp, +}; + +static const struct of_device_id kirkwood_thermal_id_table[] = { + { .compatible = "marvell,kirkwood-thermal" }, + {} +}; + +static int kirkwood_thermal_probe(struct platform_device *pdev) +{ + struct thermal_zone_device *thermal = NULL; + struct kirkwood_thermal_priv *priv; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Failed to get platform resource\n"); + return -ENODEV; + } + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->sensor = devm_request_and_ioremap(&pdev->dev, res); + if (!priv->sensor) { + dev_err(&pdev->dev, "Failed to request_ioremap memory\n"); + return -EADDRNOTAVAIL; + } + + thermal = thermal_zone_device_register("kirkwood_thermal", 0, 0, + priv, &ops, NULL, 0, 0); + if (IS_ERR(thermal)) { + dev_err(&pdev->dev, + "Failed to register thermal zone device\n"); + return PTR_ERR(thermal); + } + + platform_set_drvdata(pdev, thermal); + + return 0; +} + +static int kirkwood_thermal_exit(struct platform_device *pdev) +{ + struct thermal_zone_device *kirkwood_thermal = + platform_get_drvdata(pdev); + + thermal_zone_device_unregister(kirkwood_thermal); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +MODULE_DEVICE_TABLE(of, kirkwood_thermal_id_table); + +static struct platform_driver kirkwood_thermal_driver = { + .probe = kirkwood_thermal_probe, + .remove = kirkwood_thermal_exit, + .driver = { + .name = "kirkwood_thermal", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(kirkwood_thermal_id_table), + }, +}; + +module_platform_driver(kirkwood_thermal_driver); + +MODULE_AUTHOR("Nobuhiro Iwamatsu "); +MODULE_DESCRIPTION("kirkwood thermal driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 74ffa64c23616706381c30064a47888382bba30f Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Feb 2013 06:35:26 +0000 Subject: Thermal: Dove: Add Themal sensor support for Dove. The Marvell Dove SoC has a thermal sensor. Add a driver using the thermal framework. Signed-off-by: Andrew Lunn Signed-off-by: Sebastian Hesselbarth Signed-off-by: Zhang Rui --- .../devicetree/bindings/thermal/dove-thermal.txt | 18 ++ drivers/thermal/Kconfig | 8 + drivers/thermal/Makefile | 1 + drivers/thermal/dove_thermal.c | 209 +++++++++++++++++++++ 4 files changed, 236 insertions(+) create mode 100644 Documentation/devicetree/bindings/thermal/dove-thermal.txt create mode 100644 drivers/thermal/dove_thermal.c (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/thermal/dove-thermal.txt b/Documentation/devicetree/bindings/thermal/dove-thermal.txt new file mode 100644 index 000000000000..6f474677d472 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/dove-thermal.txt @@ -0,0 +1,18 @@ +* Dove Thermal + +This driver is for Dove SoCs which contain a thermal sensor. + +Required properties: +- compatible : "marvell,dove-thermal" +- reg : Address range of the thermal registers + +The reg properties should contain two ranges. The first is for the +three Thermal Manager registers, while the second range contains the +Thermal Diode Control Registers. + +Example: + + thermal@10078 { + compatible = "marvell,dove-thermal"; + reg = <0xd001c 0x0c>, <0xd005c 0x08>; + }; diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 5070fbee24ef..24d913aa1003 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -126,6 +126,14 @@ config EXYNOS_THERMAL_EMUL device directory to support emulation mode. With emulation mode sysfs node, you can manually input temperature to TMU for simulation purpose. +config DOVE_THERMAL + tristate "Temperature sensor on Marvell Dove SoCs" + depends on ARCH_DOVE + depends on OF + help + Support for the Dove thermal sensor driver in the Linux thermal + framework. + config DB8500_THERMAL bool "DB8500 thermal management" depends on ARCH_U8500 diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index b32c35de67e7..f5c01cce7768 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o obj-$(CONFIG_KIRKWOOD_THERMAL) += kirkwood_thermal.o obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o +obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o diff --git a/drivers/thermal/dove_thermal.c b/drivers/thermal/dove_thermal.c new file mode 100644 index 000000000000..7b0bfa0e7a9c --- /dev/null +++ b/drivers/thermal/dove_thermal.c @@ -0,0 +1,209 @@ +/* + * Dove thermal sensor driver + * + * Copyright (C) 2013 Andrew Lunn + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define DOVE_THERMAL_TEMP_OFFSET 1 +#define DOVE_THERMAL_TEMP_MASK 0x1FF + +/* Dove Thermal Manager Control and Status Register */ +#define PMU_TM_DISABLE_OFFS 0 +#define PMU_TM_DISABLE_MASK (0x1 << PMU_TM_DISABLE_OFFS) + +/* Dove Theraml Diode Control 0 Register */ +#define PMU_TDC0_SW_RST_MASK (0x1 << 1) +#define PMU_TDC0_SEL_VCAL_OFFS 5 +#define PMU_TDC0_SEL_VCAL_MASK (0x3 << PMU_TDC0_SEL_VCAL_OFFS) +#define PMU_TDC0_REF_CAL_CNT_OFFS 11 +#define PMU_TDC0_REF_CAL_CNT_MASK (0x1FF << PMU_TDC0_REF_CAL_CNT_OFFS) +#define PMU_TDC0_AVG_NUM_OFFS 25 +#define PMU_TDC0_AVG_NUM_MASK (0x7 << PMU_TDC0_AVG_NUM_OFFS) + +/* Dove Thermal Diode Control 1 Register */ +#define PMU_TEMP_DIOD_CTRL1_REG 0x04 +#define PMU_TDC1_TEMP_VALID_MASK (0x1 << 10) + +/* Dove Thermal Sensor Dev Structure */ +struct dove_thermal_priv { + void __iomem *sensor; + void __iomem *control; +}; + +static int dove_init_sensor(const struct dove_thermal_priv *priv) +{ + u32 reg; + u32 i; + + /* Configure the Diode Control Register #0 */ + reg = readl_relaxed(priv->control); + + /* Use average of 2 */ + reg &= ~PMU_TDC0_AVG_NUM_MASK; + reg |= (0x1 << PMU_TDC0_AVG_NUM_OFFS); + + /* Reference calibration value */ + reg &= ~PMU_TDC0_REF_CAL_CNT_MASK; + reg |= (0x0F1 << PMU_TDC0_REF_CAL_CNT_OFFS); + + /* Set the high level reference for calibration */ + reg &= ~PMU_TDC0_SEL_VCAL_MASK; + reg |= (0x2 << PMU_TDC0_SEL_VCAL_OFFS); + writel(reg, priv->control); + + /* Reset the sensor */ + reg = readl_relaxed(priv->control); + writel((reg | PMU_TDC0_SW_RST_MASK), priv->control); + writel(reg, priv->control); + + /* Enable the sensor */ + reg = readl_relaxed(priv->sensor); + reg &= ~PMU_TM_DISABLE_MASK; + writel(reg, priv->sensor); + + /* Poll the sensor for the first reading */ + for (i = 0; i < 1000000; i++) { + reg = readl_relaxed(priv->sensor); + if (reg & DOVE_THERMAL_TEMP_MASK) + break; + } + + if (i == 1000000) + return -EIO; + + return 0; +} + +static int dove_get_temp(struct thermal_zone_device *thermal, + unsigned long *temp) +{ + unsigned long reg; + struct dove_thermal_priv *priv = thermal->devdata; + + /* Valid check */ + reg = readl_relaxed(priv->control + PMU_TEMP_DIOD_CTRL1_REG); + if ((reg & PMU_TDC1_TEMP_VALID_MASK) == 0x0) { + dev_err(&thermal->device, + "Temperature sensor reading not valid\n"); + return -EIO; + } + + /* + * Calculate temperature. See Section 8.10.1 of 88AP510, + * Documentation/arm/Marvell/README + */ + reg = readl_relaxed(priv->sensor); + reg = (reg >> DOVE_THERMAL_TEMP_OFFSET) & DOVE_THERMAL_TEMP_MASK; + *temp = ((2281638UL - (7298*reg)) / 10); + + return 0; +} + +static struct thermal_zone_device_ops ops = { + .get_temp = dove_get_temp, +}; + +static const struct of_device_id dove_thermal_id_table[] = { + { .compatible = "marvell,dove-thermal" }, + {} +}; + +static int dove_thermal_probe(struct platform_device *pdev) +{ + struct thermal_zone_device *thermal = NULL; + struct dove_thermal_priv *priv; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Failed to get platform resource\n"); + return -ENODEV; + } + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->sensor = devm_request_and_ioremap(&pdev->dev, res); + if (!priv->sensor) { + dev_err(&pdev->dev, "Failed to request_ioremap memory\n"); + return -EADDRNOTAVAIL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(&pdev->dev, "Failed to get platform resource\n"); + return -ENODEV; + } + priv->control = devm_request_and_ioremap(&pdev->dev, res); + if (!priv->control) { + dev_err(&pdev->dev, "Failed to request_ioremap memory\n"); + return -EADDRNOTAVAIL; + } + + ret = dove_init_sensor(priv); + if (ret) { + dev_err(&pdev->dev, "Failed to initialize sensor\n"); + return ret; + } + + thermal = thermal_zone_device_register("dove_thermal", 0, 0, + priv, &ops, NULL, 0, 0); + if (IS_ERR(thermal)) { + dev_err(&pdev->dev, + "Failed to register thermal zone device\n"); + return PTR_ERR(thermal); + } + + platform_set_drvdata(pdev, thermal); + + return 0; +} + +static int dove_thermal_exit(struct platform_device *pdev) +{ + struct thermal_zone_device *dove_thermal = + platform_get_drvdata(pdev); + + thermal_zone_device_unregister(dove_thermal); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +MODULE_DEVICE_TABLE(of, dove_thermal_id_table); + +static struct platform_driver dove_thermal_driver = { + .probe = dove_thermal_probe, + .remove = dove_thermal_exit, + .driver = { + .name = "dove_thermal", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(dove_thermal_id_table), + }, +}; + +module_platform_driver(dove_thermal_driver); + +MODULE_AUTHOR("Andrew Lunn "); +MODULE_DESCRIPTION("Dove thermal driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 4f0a6847815837b63b05fc23878ba391701d8f6a Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Fri, 8 Feb 2013 01:13:06 +0000 Subject: Thermal: exynos: Add support for temperature falling interrupt. This patch introduces using temperature falling interrupt in exynos thermal driver. Former patch, it only use polling way to check whether if system themperature is fallen. However, exynos SOC also provides temperature falling interrupt way to do same things by hw. This feature is not supported in exynos4210. Acked-by: Kukjin Kim Signed-off-by: Jonghwa Lee Signed-off-by: Amit Daniel Kachhap Signed-off-by: Zhang Rui --- drivers/thermal/exynos_thermal.c | 81 ++++++++++++++++------------ include/linux/platform_data/exynos_thermal.h | 3 ++ 2 files changed, 49 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c index cd71e24194b1..f4dd68a25022 100644 --- a/drivers/thermal/exynos_thermal.c +++ b/drivers/thermal/exynos_thermal.c @@ -94,6 +94,7 @@ #define SENSOR_NAME_LEN 16 #define MAX_TRIP_COUNT 8 #define MAX_COOLING_DEVICE 4 +#define MAX_THRESHOLD_LEVS 4 #define ACTIVE_INTERVAL 500 #define IDLE_INTERVAL 10000 @@ -133,6 +134,7 @@ struct exynos_tmu_data { struct thermal_trip_point_conf { int trip_val[MAX_TRIP_COUNT]; int trip_count; + u8 trigger_falling; }; struct thermal_cooling_conf { @@ -182,7 +184,8 @@ static int exynos_set_mode(struct thermal_zone_device *thermal, mutex_lock(&th_zone->therm_dev->lock); - if (mode == THERMAL_DEVICE_ENABLED) + if (mode == THERMAL_DEVICE_ENABLED && + !th_zone->sensor_conf->trip_data.trigger_falling) th_zone->therm_dev->polling_delay = IDLE_INTERVAL; else th_zone->therm_dev->polling_delay = 0; @@ -428,7 +431,8 @@ static void exynos_report_trigger(void) break; } - if (th_zone->mode == THERMAL_DEVICE_ENABLED) { + if (th_zone->mode == THERMAL_DEVICE_ENABLED && + !th_zone->sensor_conf->trip_data.trigger_falling) { if (i > 0) th_zone->therm_dev->polling_delay = ACTIVE_INTERVAL; else @@ -467,7 +471,8 @@ static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf) th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name, EXYNOS_ZONE_COUNT, 0, NULL, &exynos_dev_ops, NULL, 0, - IDLE_INTERVAL); + sensor_conf->trip_data.trigger_falling ? + 0 : IDLE_INTERVAL); if (IS_ERR(th_zone->therm_dev)) { pr_err("Failed to register thermal zone device\n"); @@ -574,8 +579,9 @@ static int exynos_tmu_initialize(struct platform_device *pdev) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); struct exynos_tmu_platform_data *pdata = data->pdata; - unsigned int status, trim_info, rising_threshold; - int ret = 0, threshold_code; + unsigned int status, trim_info; + unsigned int rising_threshold = 0, falling_threshold = 0; + int ret = 0, threshold_code, i, trigger_levs = 0; mutex_lock(&data->lock); clk_enable(data->clk); @@ -600,6 +606,11 @@ static int exynos_tmu_initialize(struct platform_device *pdev) (data->temp_error2 != 0)) data->temp_error1 = pdata->efuse_value; + /* Count trigger levels to be enabled */ + for (i = 0; i < MAX_THRESHOLD_LEVS; i++) + if (pdata->trigger_levels[i]) + trigger_levs++; + if (data->soc == SOC_ARCH_EXYNOS4210) { /* Write temperature code for threshold */ threshold_code = temp_to_code(data, pdata->threshold); @@ -609,44 +620,38 @@ static int exynos_tmu_initialize(struct platform_device *pdev) } writeb(threshold_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP); - - writeb(pdata->trigger_levels[0], - data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0); - writeb(pdata->trigger_levels[1], - data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL1); - writeb(pdata->trigger_levels[2], - data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL2); - writeb(pdata->trigger_levels[3], - data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL3); + for (i = 0; i < trigger_levs; i++) + writeb(pdata->trigger_levels[i], + data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4); writel(EXYNOS4210_TMU_INTCLEAR_VAL, data->base + EXYNOS_TMU_REG_INTCLEAR); } else if (data->soc == SOC_ARCH_EXYNOS) { - /* Write temperature code for threshold */ - threshold_code = temp_to_code(data, pdata->trigger_levels[0]); - if (threshold_code < 0) { - ret = threshold_code; - goto out; - } - rising_threshold = threshold_code; - threshold_code = temp_to_code(data, pdata->trigger_levels[1]); - if (threshold_code < 0) { - ret = threshold_code; - goto out; - } - rising_threshold |= (threshold_code << 8); - threshold_code = temp_to_code(data, pdata->trigger_levels[2]); - if (threshold_code < 0) { - ret = threshold_code; - goto out; + /* Write temperature code for rising and falling threshold */ + for (i = 0; i < trigger_levs; i++) { + threshold_code = temp_to_code(data, + pdata->trigger_levels[i]); + if (threshold_code < 0) { + ret = threshold_code; + goto out; + } + rising_threshold |= threshold_code << 8 * i; + if (pdata->threshold_falling) { + threshold_code = temp_to_code(data, + pdata->trigger_levels[i] - + pdata->threshold_falling); + if (threshold_code > 0) + falling_threshold |= + threshold_code << 8 * i; + } } - rising_threshold |= (threshold_code << 16); writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE); - writel(0, data->base + EXYNOS_THD_TEMP_FALL); + writel(falling_threshold, + data->base + EXYNOS_THD_TEMP_FALL); - writel(EXYNOS_TMU_CLEAR_RISE_INT|EXYNOS_TMU_CLEAR_FALL_INT, + writel(EXYNOS_TMU_CLEAR_RISE_INT | EXYNOS_TMU_CLEAR_FALL_INT, data->base + EXYNOS_TMU_REG_INTCLEAR); } out: @@ -679,6 +684,8 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on) pdata->trigger_level2_en << 8 | pdata->trigger_level1_en << 4 | pdata->trigger_level0_en; + if (pdata->threshold_falling) + interrupt_en |= interrupt_en << 16; } else { con |= EXYNOS_TMU_CORE_OFF; interrupt_en = 0; /* Disable all interrupts */ @@ -716,7 +723,8 @@ static void exynos_tmu_work(struct work_struct *work) mutex_lock(&data->lock); clk_enable(data->clk); if (data->soc == SOC_ARCH_EXYNOS) - writel(EXYNOS_TMU_CLEAR_RISE_INT, + writel(EXYNOS_TMU_CLEAR_RISE_INT | + EXYNOS_TMU_CLEAR_FALL_INT, data->base + EXYNOS_TMU_REG_INTCLEAR); else writel(EXYNOS4210_TMU_INTCLEAR_VAL, @@ -772,6 +780,7 @@ static struct exynos_tmu_platform_data const exynos4210_default_tmu_data = { #if defined(CONFIG_SOC_EXYNOS5250) || defined(CONFIG_SOC_EXYNOS4412) static struct exynos_tmu_platform_data const exynos_default_tmu_data = { + .threshold_falling = 10, .trigger_levels[0] = 85, .trigger_levels[1] = 103, .trigger_levels[2] = 110, @@ -1015,6 +1024,8 @@ static int __devinit exynos_tmu_probe(struct platform_device *pdev) exynos_sensor_conf.trip_data.trip_val[i] = pdata->threshold + pdata->trigger_levels[i]; + exynos_sensor_conf.trip_data.trigger_falling = pdata->threshold_falling; + exynos_sensor_conf.cooling_data.freq_clip_count = pdata->freq_tab_count; for (i = 0; i < pdata->freq_tab_count; i++) { diff --git a/include/linux/platform_data/exynos_thermal.h b/include/linux/platform_data/exynos_thermal.h index a7bdb2f63b73..da7e6274b175 100644 --- a/include/linux/platform_data/exynos_thermal.h +++ b/include/linux/platform_data/exynos_thermal.h @@ -53,6 +53,8 @@ struct freq_clip_table { * struct exynos_tmu_platform_data * @threshold: basic temperature for generating interrupt * 25 <= threshold <= 125 [unit: degree Celsius] + * @threshold_falling: differntial value for setting threshold + * of temperature falling interrupt. * @trigger_levels: array for each interrupt levels * [unit: degree Celsius] * 0: temperature for trigger_level0 interrupt @@ -97,6 +99,7 @@ struct freq_clip_table { */ struct exynos_tmu_platform_data { u8 threshold; + u8 threshold_falling; u8 trigger_levels[4]; bool trigger_level0_en; bool trigger_level1_en; -- cgit v1.2.3 From ce760ed3f440cfa51785c64dad9b4bd87673bb11 Mon Sep 17 00:00:00 2001 From: Amit Daniel Kachhap Date: Fri, 8 Feb 2013 01:13:07 +0000 Subject: thermal: exynos: Use the new thermal trend type for quick cooling action. This patch uses the quick thermal cooling trend type macros. This is needed as exynos5 and other thermal sensors now supports only interrupt method for thresold temperature check. Acked-by: Kukjin Kim Signed-off-by: Amit Daniel Kachhap Signed-off-by: Zhang Rui --- drivers/thermal/exynos_thermal.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c index f4dd68a25022..64aa8b445054 100644 --- a/drivers/thermal/exynos_thermal.c +++ b/drivers/thermal/exynos_thermal.c @@ -295,7 +295,7 @@ static int exynos_bind(struct thermal_zone_device *thermal, case MONITOR_ZONE: case WARN_ZONE: if (thermal_zone_bind_cooling_device(thermal, i, cdev, - level, level)) { + level, 0)) { pr_err("error binding cdev inst %d\n", i); ret = -EINVAL; } @@ -381,9 +381,9 @@ static int exynos_get_trend(struct thermal_zone_device *thermal, return ret; if (thermal->temperature >= trip_temp) - *trend = THERMAL_TREND_RAISING; + *trend = THERMAL_TREND_RAISE_FULL; else - *trend = THERMAL_TREND_DROPPING; + *trend = THERMAL_TREND_DROP_FULL; return 0; } -- cgit v1.2.3 From 9d185d0417e518fd9cfce90ec2ad75f70771bbaa Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Fri, 8 Feb 2013 20:33:42 +0800 Subject: Thermal: rename thermal governor Kconfig option to avoid generic naming Currently, we have three Kconfig options for thermal governors, aka, CONFIG_FAIR_SHARE, CONFIG_USER_SPACE and CONFIG_STEP_WISE. But these names are too generic that may bring confusion to users. Rename them to CONFIG_THERMAL_GOV_FAIR_SHARE, CONFIG_THERMAL_GOV_USER_SPACE, CONFIG_THERMAL_GOV_STEP_WISE to avoid the generic naming. Signed-off-by: Zhang Rui --- drivers/thermal/Kconfig | 12 ++++++------ drivers/thermal/Makefile | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 24d913aa1003..a764f165b589 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -29,14 +29,14 @@ choice config THERMAL_DEFAULT_GOV_STEP_WISE bool "step_wise" - select STEP_WISE + select THERMAL_GOV_STEP_WISE help Use the step_wise governor as default. This throttles the devices one step at a time. config THERMAL_DEFAULT_GOV_FAIR_SHARE bool "fair_share" - select FAIR_SHARE + select THERMAL_GOV_FAIR_SHARE help Use the fair_share governor as default. This throttles the devices based on their 'contribution' to a zone. The @@ -44,24 +44,24 @@ config THERMAL_DEFAULT_GOV_FAIR_SHARE config THERMAL_DEFAULT_GOV_USER_SPACE bool "user_space" - select USER_SPACE + select THERMAL_GOV_USER_SPACE help Select this if you want to let the user space manage the lpatform thermals. endchoice -config FAIR_SHARE +config THERMAL_GOV_FAIR_SHARE bool "Fair-share thermal governor" help Enable this to manage platform thermals using fair-share governor. -config STEP_WISE +config THERMAL_GOV_STEP_WISE bool "Step_wise thermal governor" help Enable this to manage platform thermals using a simple linear -config USER_SPACE +config THERMAL_GOV_USER_SPACE bool "User_space thermal governor" help Enable this to let the user space manage the platform thermals. diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index f5c01cce7768..d3a2b38c31e8 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -5,9 +5,9 @@ obj-$(CONFIG_THERMAL) += thermal_sys.o # governors -obj-$(CONFIG_FAIR_SHARE) += fair_share.o -obj-$(CONFIG_STEP_WISE) += step_wise.o -obj-$(CONFIG_USER_SPACE) += user_space.o +obj-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o +obj-$(CONFIG_THERMAL_GOV_STEP_WISE) += step_wise.o +obj-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o # cpufreq cooling obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o -- cgit v1.2.3 From 9c677b9b74d4314ed7f222bf802d6d1e7585eb65 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Dec 2012 11:23:42 +0000 Subject: mfd: ab8500: prepare to handle AB8500 GPIO's IRQs correctly In an upcoming patch, the gpio-ab8500 driver will relinquish all IRQ handling capability and pass it back into the AB8500 core driver. This will aid in reducing massive code duplication within the kernel. Also, most of the functionality is already in the AB8500 core driver, as the GPIO IRQs are actually sandwiched between lots of other IRQs which the core driver already handles. All we're doing here is providing the core driver with knowledge that each GPIO has two IRQs assigned to it; one for rising and a separate one for falling. Cc: Samuel Ortiz Signed-off-by: Lee Jones Signed-off-by: Linus Walleij --- drivers/mfd/ab8500-core.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 4778bb124efe..3d5da51b6a94 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -368,16 +368,40 @@ static void ab8500_irq_mask(struct irq_data *data) int mask = 1 << (offset % 8); ab8500->mask[index] |= mask; + + /* The AB8500 GPIOs have two interrupts each (rising & falling). */ + if (offset >= AB8500_INT_GPIO6R && offset <= AB8500_INT_GPIO41R) + ab8500->mask[index + 2] |= mask; + if (offset >= AB9540_INT_GPIO50R && offset <= AB9540_INT_GPIO54R) + ab8500->mask[index + 1] |= mask; + if (offset == AB8540_INT_GPIO43R || offset == AB8540_INT_GPIO44R) + ab8500->mask[index] |= (mask >> 1); } static void ab8500_irq_unmask(struct irq_data *data) { struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data); + unsigned int type = irqd_get_trigger_type(data); int offset = data->hwirq; int index = offset / 8; int mask = 1 << (offset % 8); - ab8500->mask[index] &= ~mask; + if (type & IRQ_TYPE_EDGE_RISING) + ab8500->mask[index] &= ~mask; + + /* The AB8500 GPIOs have two interrupts each (rising & falling). */ + if (type & IRQ_TYPE_EDGE_FALLING) { + if (offset >= AB8500_INT_GPIO6R && offset <= AB8500_INT_GPIO41R) + ab8500->mask[index + 2] &= ~mask; + else if (offset >= AB9540_INT_GPIO50R && offset <= AB9540_INT_GPIO54R) + ab8500->mask[index + 1] &= ~mask; + else if (offset == AB8540_INT_GPIO43R || offset == AB8540_INT_GPIO44R) + ab8500->mask[index] &= ~(mask >> 1); + else + ab8500->mask[index] &= ~mask; + } else + /* Satisfies the case where type is not set. */ + ab8500->mask[index] &= ~mask; } static struct irq_chip ab8500_irq_chip = { -- cgit v1.2.3 From e2ddf46ab4ab40a657a1808cf4e358c46ae1ba68 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 6 Feb 2013 21:54:34 +0100 Subject: mfd: ab8500: actually handle the AB8500 GPIO IRQs correctly The patch: "mfd: ab8500: prepare to handle AB8500 GPIO's IRQs correctly" altered the AB8500 IRQ mask/unmask functions such that they would handle masking on/off the falling edge IRQ if this was requested by the consumer. However the bit mask for hwirqs 43 and 44 was shifting the bit mask incorrectly, resulting in the wrong IRQ being mased/unmasked. Further while the patch would mask/unmask the correct line, when the interrupt actually came in, it would still be treated as a valid hwirq. The offsetting applied when masking/unmasking was not applied when handling the IRQ, i.e. the falling edge lines were not routed back to the rising edge lines. This fixes both cases. The end result has been tested with the SIM detect IRQ, GPIO12, hwirq 46 and 62. Cc: Samuel Ortiz Cc: Lee Jones Signed-off-by: Linus Walleij --- drivers/mfd/ab8500-core.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 3d5da51b6a94..6d6666b0f1b2 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -375,7 +375,8 @@ static void ab8500_irq_mask(struct irq_data *data) if (offset >= AB9540_INT_GPIO50R && offset <= AB9540_INT_GPIO54R) ab8500->mask[index + 1] |= mask; if (offset == AB8540_INT_GPIO43R || offset == AB8540_INT_GPIO44R) - ab8500->mask[index] |= (mask >> 1); + /* Here the falling IRQ is one bit lower */ + ab8500->mask[index] |= (mask << 1); } static void ab8500_irq_unmask(struct irq_data *data) @@ -396,12 +397,14 @@ static void ab8500_irq_unmask(struct irq_data *data) else if (offset >= AB9540_INT_GPIO50R && offset <= AB9540_INT_GPIO54R) ab8500->mask[index + 1] &= ~mask; else if (offset == AB8540_INT_GPIO43R || offset == AB8540_INT_GPIO44R) - ab8500->mask[index] &= ~(mask >> 1); + /* Here the falling IRQ is one bit lower */ + ab8500->mask[index] &= ~(mask << 1); else ab8500->mask[index] &= ~mask; - } else + } else { /* Satisfies the case where type is not set. */ ab8500->mask[index] &= ~mask; + } } static struct irq_chip ab8500_irq_chip = { @@ -435,6 +438,19 @@ static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500, line = (i << 3) + int_bit; latch_val &= ~(1 << int_bit); + /* + * This handles the falling edge hwirqs from the GPIO + * lines. Route them back to the line registered for the + * rising IRQ, as this is merely a flag for the same IRQ + * in linux terms. + */ + if (line >= AB8500_INT_GPIO6F && line <= AB8500_INT_GPIO41F) + line -= 16; + if (line >= AB9540_INT_GPIO50F && line <= AB9540_INT_GPIO54F) + line -= 8; + if (line == AB8540_INT_GPIO43F || line == AB8540_INT_GPIO44F) + line += 1; + handle_nested_irq(ab8500->irq_base + line); } while (latch_val); -- cgit v1.2.3 From 40f6e5a2b52eda7864b7167fb5af1c310b464766 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 7 Jan 2013 12:23:48 +0000 Subject: mfd: ab8500: provide a irq_set_type() function In the AB8500 IRQ mask and unmask functions, we rely on testing for IRQ_TYPE_EDGE_RISING and IRQ_TYPE_EDGE_FALLING interrupts to physically mask and unmask the correct interrupt lines. In order for us to do that, the trigger needs to be set in the associated flags. However, unless a irq_set_type() function pointer is passed when registering the IRQ chip, the IRQ subsystem will refuse to do it. For that reason, we're providing one. Cc: Samuel Ortiz Signed-off-by: Lee Jones Signed-off-by: Linus Walleij --- drivers/mfd/ab8500-core.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 6d6666b0f1b2..0f1b9b788d07 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -407,6 +407,11 @@ static void ab8500_irq_unmask(struct irq_data *data) } } +static int ab8500_irq_set_type(struct irq_data *data, unsigned int type) +{ + return 0; +} + static struct irq_chip ab8500_irq_chip = { .name = "ab8500", .irq_bus_lock = ab8500_irq_lock, @@ -414,6 +419,7 @@ static struct irq_chip ab8500_irq_chip = { .irq_mask = ab8500_irq_mask, .irq_disable = ab8500_irq_mask, .irq_unmask = ab8500_irq_unmask, + .irq_set_type = ab8500_irq_set_type, }; static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500, -- cgit v1.2.3 From 7d56a46e876aa89dcac921d3afab5cccace15e63 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 16 Jan 2013 14:49:45 +0000 Subject: mfd: ab8500: ensure new AB8500 pinctrl driver is probed correctly The old, BROKEN AB8500 GPIO driver has been revamped as a shiny new pinctrl driver and has been renamed as such. So, if we would like to make use of it, we need to register it via its new name. Cc: Samuel Ortiz Signed-off-by: Lee Jones Signed-off-by: Linus Walleij --- drivers/mfd/ab8500-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 0f1b9b788d07..4cb716a00178 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1097,7 +1097,7 @@ static struct mfd_cell ab8500_bm_devs[] = { static struct mfd_cell ab8500_devs[] = { { - .name = "ab8500-gpio", + .name = "pinctrl-ab8500", .of_compatible = "stericsson,ab8500-gpio", }, { -- cgit v1.2.3 From e64d905e28031031c52db403826cd3bfe060b181 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 16 Jan 2013 15:09:36 +0000 Subject: mfd: ab8500: allow AB9540 based devices to use ABX500 pinctrl The old AB8500 GPIO driver has been un-BROKEN and converted into a multi-platform pinctrl driver. If any AB9540 based devices wish to request any GPIO pins that it offers, they can after this patch. Cc: Samuel Ortiz Signed-off-by: Lee Jones Signed-off-by: Linus Walleij --- drivers/mfd/ab8500-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 4cb716a00178..e2a45abf726d 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1114,7 +1114,8 @@ static struct mfd_cell ab8500_devs[] = { static struct mfd_cell ab9540_devs[] = { { - .name = "ab8500-gpio", + .name = "pinctrl-ab9540", + .of_compatible = "stericsson,ab9540-gpio", }, { .name = "ab9540-usb", -- cgit v1.2.3 From 7d0ee461fd173fea8ca254924263f189a3f0dc08 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Fri, 25 Jan 2013 19:10:12 -0300 Subject: [media] staging/media/solo6x10: Use PTR_RET rather than if(IS_ERR(...)) + PTR_ERR Found with coccicheck. The semantic patch that makes this change is available in scripts/coccinelle/api/ptr_ret.cocci. Signed-off-by: Peter Huewe Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/solo6x10/v4l2.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/solo6x10/v4l2.c b/drivers/staging/media/solo6x10/v4l2.c index 571c3a348d30..ca774cc57539 100644 --- a/drivers/staging/media/solo6x10/v4l2.c +++ b/drivers/staging/media/solo6x10/v4l2.c @@ -415,10 +415,7 @@ static int solo_start_thread(struct solo_filehandle *fh) { fh->kthread = kthread_run(solo_thread, fh, SOLO6X10_NAME "_disp"); - if (IS_ERR(fh->kthread)) - return PTR_ERR(fh->kthread); - - return 0; + return PTR_RET(fh->kthread); } static void solo_stop_thread(struct solo_filehandle *fh) -- cgit v1.2.3 From d2008b56cfd6fa76ac6990d9c12a045ce5026c85 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 29 Jan 2013 08:19:27 -0300 Subject: [media] ttusbir: do not set led twice on resume led_classdev_resume already sets the led. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ttusbir.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c index 78be8a914225..f9226b83de41 100644 --- a/drivers/media/rc/ttusbir.c +++ b/drivers/media/rc/ttusbir.c @@ -408,9 +408,8 @@ static int ttusbir_resume(struct usb_interface *intf) struct ttusbir *tt = usb_get_intfdata(intf); int i, rc; - led_classdev_resume(&tt->led); tt->is_led_on = true; - ttusbir_set_led(tt); + led_classdev_resume(&tt->led); for (i = 0; i < NUM_URBS; i++) { rc = usb_submit_urb(tt->urb[i], GFP_KERNEL); -- cgit v1.2.3 From 1e801adc7a70c2f67214b2617088a41f4bebe55e Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 29 Jan 2013 08:19:28 -0300 Subject: [media] ttusbir: add missing endian conversion spotted by sparse. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ttusbir.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c index f9226b83de41..cf0d47f57fb2 100644 --- a/drivers/media/rc/ttusbir.c +++ b/drivers/media/rc/ttusbir.c @@ -213,19 +213,20 @@ static int ttusbir_probe(struct usb_interface *intf, /* find the correct alt setting */ for (i = 0; i < intf->num_altsetting && altsetting == -1; i++) { - int bulk_out_endp = -1, iso_in_endp = -1; + int max_packet, bulk_out_endp = -1, iso_in_endp = -1; idesc = &intf->altsetting[i].desc; for (j = 0; j < idesc->bNumEndpoints; j++) { desc = &intf->altsetting[i].endpoint[j].desc; + max_packet = le16_to_cpu(desc->wMaxPacketSize); if (usb_endpoint_dir_in(desc) && usb_endpoint_xfer_isoc(desc) && - desc->wMaxPacketSize == 0x10) + max_packet == 0x10) iso_in_endp = j; else if (usb_endpoint_dir_out(desc) && usb_endpoint_xfer_bulk(desc) && - desc->wMaxPacketSize == 0x20) + max_packet == 0x20) bulk_out_endp = j; if (bulk_out_endp != -1 && iso_in_endp != -1) { -- cgit v1.2.3 From 8dfef674e6c954f9b6476c1b252b385c48c9ee26 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 29 Jan 2013 08:19:29 -0300 Subject: [media] mceusb: make transmit work on the Philips IR transceiver The GET_REVISION command puts the device in an unresponsive state, although it continues to report any IR activity. Note that GET_REVISION command is not documented, nor is any possible response to it parsed. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/mceusb.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 9afb9331217d..14fea35e5d67 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -291,7 +291,8 @@ static struct usb_device_id mceusb_dev_table[] = { /* Philips/Spinel plus IR transceiver for ASUS */ { USB_DEVICE(VENDOR_PHILIPS, 0x2088) }, /* Philips IR transceiver (Dell branded) */ - { USB_DEVICE(VENDOR_PHILIPS, 0x2093) }, + { USB_DEVICE(VENDOR_PHILIPS, 0x2093), + .driver_info = MCE_GEN2_TX_INV }, /* Realtek MCE IR Receiver and card reader */ { USB_DEVICE(VENDOR_REALTEK, 0x0161), .driver_info = MULTIFUNCTION }, @@ -1121,16 +1122,13 @@ static void mceusb_gen1_init(struct mceusb_dev *ir) mce_async_out(ir, GET_REVISION, sizeof(GET_REVISION)); kfree(data); -}; +} static void mceusb_gen2_init(struct mceusb_dev *ir) { /* device resume */ mce_async_out(ir, DEVICE_RESUME, sizeof(DEVICE_RESUME)); - /* get hw/sw revision? */ - mce_async_out(ir, GET_REVISION, sizeof(GET_REVISION)); - /* get wake version (protocol, key, address) */ mce_async_out(ir, GET_WAKEVERSION, sizeof(GET_WAKEVERSION)); -- cgit v1.2.3 From db8ee1064c97879bff614d653158dff1894d2e37 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 29 Jan 2013 08:19:30 -0300 Subject: [media] mceusb: make transmit work on HP transceiver This transceiver expects the set IR TX ports and IR data as seperate packets, like the Windows driver does. Remove unnecessary kzalloc. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/mceusb.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 14fea35e5d67..bdd1ed8e406e 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -62,7 +62,6 @@ #define MCE_PACKET_SIZE 4 /* Normal length of packet (without header) */ #define MCE_IRDATA_HEADER 0x84 /* Actual header format is 0x80 + num_bytes */ #define MCE_IRDATA_TRAILER 0x80 /* End of IR data */ -#define MCE_TX_HEADER_LENGTH 3 /* # of bytes in the initializing tx header */ #define MCE_MAX_CHANNELS 2 /* Two transmitters, hardware dependent? */ #define MCE_DEFAULT_TX_MASK 0x03 /* Vals: TX1=0x01, TX2=0x02, ALL=0x03 */ #define MCE_PULSE_BIT 0x80 /* Pulse bit, MSB set == PULSE else SPACE */ @@ -366,7 +365,8 @@ static struct usb_device_id mceusb_dev_table[] = { /* Formosa Industrial Computing */ { USB_DEVICE(VENDOR_FORMOSA, 0xe042) }, /* Fintek eHome Infrared Transceiver (HP branded) */ - { USB_DEVICE(VENDOR_FINTEK, 0x5168) }, + { USB_DEVICE(VENDOR_FINTEK, 0x5168), + .driver_info = MCE_GEN2_TX_INV }, /* Fintek eHome Infrared Transceiver */ { USB_DEVICE(VENDOR_FINTEK, 0x0602) }, /* Fintek eHome Infrared Transceiver (in the AOpen MP45) */ @@ -789,19 +789,19 @@ static void mce_flush_rx_buffer(struct mceusb_dev *ir, int size) static int mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count) { struct mceusb_dev *ir = dev->priv; - int i, ret = 0; + int i, length, ret = 0; int cmdcount = 0; - unsigned char *cmdbuf; /* MCE command buffer */ - - cmdbuf = kzalloc(sizeof(unsigned) * MCE_CMDBUF_SIZE, GFP_KERNEL); - if (!cmdbuf) - return -ENOMEM; + unsigned char cmdbuf[MCE_CMDBUF_SIZE]; /* MCE tx init header */ cmdbuf[cmdcount++] = MCE_CMD_PORT_IR; cmdbuf[cmdcount++] = MCE_CMD_SETIRTXPORTS; cmdbuf[cmdcount++] = ir->tx_mask; + /* Send the set TX ports command */ + mce_async_out(ir, cmdbuf, cmdcount); + cmdcount = 0; + /* Generate mce packet data */ for (i = 0; (i < count) && (cmdcount < MCE_CMDBUF_SIZE); i++) { txbuf[i] = txbuf[i] / MCE_TIME_UNIT; @@ -810,8 +810,7 @@ static int mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count) /* Insert mce packet header every 4th entry */ if ((cmdcount < MCE_CMDBUF_SIZE) && - (cmdcount - MCE_TX_HEADER_LENGTH) % - MCE_CODE_LENGTH == 0) + (cmdcount % MCE_CODE_LENGTH) == 0) cmdbuf[cmdcount++] = MCE_IRDATA_HEADER; /* Insert mce packet data */ @@ -830,9 +829,8 @@ static int mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count) } /* Fix packet length in last header */ - cmdbuf[cmdcount - (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH] = - MCE_COMMAND_IRDATA + (cmdcount - MCE_TX_HEADER_LENGTH) % - MCE_CODE_LENGTH - 1; + length = cmdcount % MCE_CODE_LENGTH; + cmdbuf[cmdcount - length] -= MCE_CODE_LENGTH - length; /* Check if we have room for the empty packet at the end */ if (cmdcount >= MCE_CMDBUF_SIZE) { @@ -847,7 +845,6 @@ static int mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count) mce_async_out(ir, cmdbuf, cmdcount); out: - kfree(cmdbuf); return ret ? ret : count; } -- cgit v1.2.3 From 06eae25f162e2a0d9e60f0ad3ec3d14c738fbe68 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 29 Jan 2013 08:19:31 -0300 Subject: [media] redrat3: fix transmit return value and overrun If more than 127 different lengths are transmitted then the driver causes an overrun on sample_lens. Try to send as much as possible and return the amount sent. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/redrat3.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c index 1800326f93e6..1b37fe2779f8 100644 --- a/drivers/media/rc/redrat3.c +++ b/drivers/media/rc/redrat3.c @@ -195,9 +195,6 @@ struct redrat3_dev { dma_addr_t dma_in; dma_addr_t dma_out; - /* locks this structure */ - struct mutex lock; - /* rx signal timeout timer */ struct timer_list rx_timeout; u32 hw_timeout; @@ -922,8 +919,7 @@ static int redrat3_transmit_ir(struct rc_dev *rcdev, unsigned *txbuf, return -EAGAIN; } - if (count > (RR3_DRIVER_MAXLENS * 2)) - return -EINVAL; + count = min_t(unsigned, count, RR3_MAX_SIG_SIZE - RR3_TX_TRAILER_LEN); /* rr3 will disable rc detector on transmit */ rr3->det_enabled = false; @@ -936,24 +932,22 @@ static int redrat3_transmit_ir(struct rc_dev *rcdev, unsigned *txbuf, } for (i = 0; i < count; i++) { + cur_sample_len = redrat3_us_to_len(txbuf[i]); for (lencheck = 0; lencheck < curlencheck; lencheck++) { - cur_sample_len = redrat3_us_to_len(txbuf[i]); if (sample_lens[lencheck] == cur_sample_len) break; } if (lencheck == curlencheck) { - cur_sample_len = redrat3_us_to_len(txbuf[i]); rr3_dbg(dev, "txbuf[%d]=%u, pos %d, enc %u\n", i, txbuf[i], curlencheck, cur_sample_len); - if (curlencheck < 255) { + if (curlencheck < RR3_DRIVER_MAXLENS) { /* now convert the value to a proper * rr3 value.. */ sample_lens[curlencheck] = cur_sample_len; curlencheck++; } else { - dev_err(dev, "signal too long\n"); - ret = -EINVAL; - goto out; + count = i - 1; + break; } } } @@ -1087,6 +1081,7 @@ static struct rc_dev *redrat3_init_rc_dev(struct redrat3_dev *rr3) rc->tx_ir = redrat3_transmit_ir; rc->s_tx_carrier = redrat3_set_tx_carrier; rc->driver_name = DRIVER_NAME; + rc->rx_resolution = US_TO_NS(2); rc->map_name = RC_MAP_HAUPPAUGE; ret = rc_register_device(rc); @@ -1202,7 +1197,6 @@ static int redrat3_dev_probe(struct usb_interface *intf, rr3->bulk_out_buf, ep_out->wMaxPacketSize, (usb_complete_t)redrat3_write_bulk_callback, rr3); - mutex_init(&rr3->lock); rr3->udev = udev; redrat3_reset(rr3); -- cgit v1.2.3 From d058e23704ad7e0b6876a94b0d8428dcef510b49 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 29 Jan 2013 07:12:13 -0300 Subject: [media] media: ov7670: add support for ov7675 ov7675 and ov7670 share the same registers but there is no way to distinguish them at runtime. However, they require different tweaks to achieve the desired resolution. For this reason this patch adds a new ov7675 entry to the ov7670_id table. Signed-off-by: Javier Martin Cc: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 101 ++++++++++++++++++++++++++++++++------------- 1 file changed, 72 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 882ddf6f66d3..51b198f79077 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -183,6 +183,27 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); #define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ #define REG_BD60MAX 0xab /* 60hz banding step limit */ +enum ov7670_model { + MODEL_OV7670 = 0, + MODEL_OV7675, +}; + +struct ov7670_win_size { + int width; + int height; + unsigned char com7_bit; + int hstart; /* Start/stop values for the camera. Note */ + int hstop; /* that they do not always make complete */ + int vstart; /* sense to humans, but evidently the sensor */ + int vstop; /* will do the right thing... */ + struct regval_list *regs; /* Regs to tweak */ +}; + +struct ov7670_devtype { + /* formats supported for each model */ + struct ov7670_win_size *win_sizes; + unsigned int n_win_sizes; +}; /* * Information we maintain about a known sensor. @@ -198,6 +219,7 @@ struct ov7670_info { int clock_speed; /* External clock speed (MHz) */ u8 clkrc; /* Clock divider value */ bool use_smbus; /* Use smbus I/O instead of I2C */ + const struct ov7670_devtype *devtype; /* Device specifics */ }; static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) @@ -652,65 +674,70 @@ static struct regval_list ov7670_qcif_regs[] = { { 0xff, 0xff }, }; -static struct ov7670_win_size { - int width; - int height; - unsigned char com7_bit; - int hstart; /* Start/stop values for the camera. Note */ - int hstop; /* that they do not always make complete */ - int vstart; /* sense to humans, but evidently the sensor */ - int vstop; /* will do the right thing... */ - struct regval_list *regs; /* Regs to tweak */ -/* h/vref stuff */ -} ov7670_win_sizes[] = { +static struct ov7670_win_size ov7670_win_sizes[] = { /* VGA */ { .width = VGA_WIDTH, .height = VGA_HEIGHT, .com7_bit = COM7_FMT_VGA, - .hstart = 158, /* These values from */ - .hstop = 14, /* Omnivision */ + .hstart = 158, /* These values from */ + .hstop = 14, /* Omnivision */ .vstart = 10, .vstop = 490, - .regs = NULL, + .regs = NULL, }, /* CIF */ { .width = CIF_WIDTH, .height = CIF_HEIGHT, .com7_bit = COM7_FMT_CIF, - .hstart = 170, /* Empirically determined */ + .hstart = 170, /* Empirically determined */ .hstop = 90, .vstart = 14, .vstop = 494, - .regs = NULL, + .regs = NULL, }, /* QVGA */ { .width = QVGA_WIDTH, .height = QVGA_HEIGHT, .com7_bit = COM7_FMT_QVGA, - .hstart = 168, /* Empirically determined */ + .hstart = 168, /* Empirically determined */ .hstop = 24, .vstart = 12, .vstop = 492, - .regs = NULL, + .regs = NULL, }, /* QCIF */ { .width = QCIF_WIDTH, .height = QCIF_HEIGHT, .com7_bit = COM7_FMT_VGA, /* see comment above */ - .hstart = 456, /* Empirically determined */ + .hstart = 456, /* Empirically determined */ .hstop = 24, .vstart = 14, .vstop = 494, - .regs = ov7670_qcif_regs, - }, + .regs = ov7670_qcif_regs, + } }; -#define N_WIN_SIZES (ARRAY_SIZE(ov7670_win_sizes)) - +static struct ov7670_win_size ov7675_win_sizes[] = { + /* + * Currently, only VGA is supported. Theoretically it could be possible + * to support CIF, QVGA and QCIF too. Taking values for ov7670 as a + * base and tweak them empirically could be required. + */ + { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .com7_bit = COM7_FMT_VGA, + .hstart = 158, /* These values from */ + .hstop = 14, /* Omnivision */ + .vstart = 14, /* Empirically determined */ + .vstop = 494, + .regs = NULL, + } +}; /* * Store a set of start/stop values into the camera. @@ -761,6 +788,8 @@ static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, { int index; struct ov7670_win_size *wsize; + struct ov7670_info *info = to_state(sd); + unsigned int n_win_sizes = info->devtype->n_win_sizes; for (index = 0; index < N_OV7670_FMTS; index++) if (ov7670_formats[index].mbus_code == fmt->code) @@ -780,11 +809,11 @@ static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, * Round requested image size down to the nearest * we support, but not below the smallest. */ - for (wsize = ov7670_win_sizes; wsize < ov7670_win_sizes + N_WIN_SIZES; - wsize++) + for (wsize = info->devtype->win_sizes; + wsize < info->devtype->win_sizes + n_win_sizes; wsize++) if (fmt->width >= wsize->width && fmt->height >= wsize->height) break; - if (wsize >= ov7670_win_sizes + N_WIN_SIZES) + if (wsize >= info->devtype->win_sizes + n_win_sizes) wsize--; /* Take the smallest one */ if (ret_wsize != NULL) *ret_wsize = wsize; @@ -931,13 +960,14 @@ static int ov7670_enum_framesizes(struct v4l2_subdev *sd, int i; int num_valid = -1; __u32 index = fsize->index; + unsigned int n_win_sizes = info->devtype->n_win_sizes; /* * If a minimum width/height was requested, filter out the capture * windows that fall outside that. */ - for (i = 0; i < N_WIN_SIZES; i++) { - struct ov7670_win_size *win = &ov7670_win_sizes[index]; + for (i = 0; i < n_win_sizes; i++) { + struct ov7670_win_size *win = &info->devtype->win_sizes[index]; if (info->min_width && win->width < info->min_width) continue; if (info->min_height && win->height < info->min_height) @@ -1510,6 +1540,17 @@ static const struct v4l2_subdev_ops ov7670_ops = { /* ----------------------------------------------------------------------- */ +static const struct ov7670_devtype ov7670_devdata[] = { + [MODEL_OV7670] = { + .win_sizes = ov7670_win_sizes, + .n_win_sizes = ARRAY_SIZE(ov7670_win_sizes), + }, + [MODEL_OV7675] = { + .win_sizes = ov7675_win_sizes, + .n_win_sizes = ARRAY_SIZE(ov7675_win_sizes), + }, +}; + static int ov7670_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1551,6 +1592,7 @@ static int ov7670_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%02x (%s)\n", client->addr << 1, client->adapter->name); + info->devtype = &ov7670_devdata[id->driver_data]; info->fmt = &ov7670_formats[0]; info->sat = 128; /* Review this */ info->clkrc = info->clock_speed / 30; @@ -1568,7 +1610,8 @@ static int ov7670_remove(struct i2c_client *client) } static const struct i2c_device_id ov7670_id[] = { - { "ov7670", 0 }, + { "ov7670", MODEL_OV7670 }, + { "ov7675", MODEL_OV7675 }, { } }; MODULE_DEVICE_TABLE(i2c, ov7670_id); -- cgit v1.2.3 From f748cd3ec8039a01d260ba0d51687afdf93c6e67 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 29 Jan 2013 07:14:27 -0300 Subject: [media] media: ov7670: make try_fmt() consistent with 'min_height' and 'min_width' 'min_height' and 'min_width' are variables that allow to specify the minimum resolution that the sensor will achieve. This patch make v4l2 fmt callbacks consider this parameters in order to return valid data to user space. Acked-by: Jonathan Corbet Signed-off-by: Javier Martin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 51b198f79077..88e64739fefd 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -786,10 +786,11 @@ static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, struct ov7670_format_struct **ret_fmt, struct ov7670_win_size **ret_wsize) { - int index; + int index, i; struct ov7670_win_size *wsize; struct ov7670_info *info = to_state(sd); unsigned int n_win_sizes = info->devtype->n_win_sizes; + unsigned int win_sizes_limit = n_win_sizes; for (index = 0; index < N_OV7670_FMTS; index++) if (ov7670_formats[index].mbus_code == fmt->code) @@ -805,15 +806,30 @@ static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, * Fields: the OV devices claim to be progressive. */ fmt->field = V4L2_FIELD_NONE; + + /* + * Don't consider values that don't match min_height and min_width + * constraints. + */ + if (info->min_width || info->min_height) + for (i = 0; i < n_win_sizes; i++) { + wsize = info->devtype->win_sizes + i; + + if (wsize->width < info->min_width || + wsize->height < info->min_height) { + win_sizes_limit = i; + break; + } + } /* * Round requested image size down to the nearest * we support, but not below the smallest. */ for (wsize = info->devtype->win_sizes; - wsize < info->devtype->win_sizes + n_win_sizes; wsize++) + wsize < info->devtype->win_sizes + win_sizes_limit; wsize++) if (fmt->width >= wsize->width && fmt->height >= wsize->height) break; - if (wsize >= info->devtype->win_sizes + n_win_sizes) + if (wsize >= info->devtype->win_sizes + win_sizes_limit) wsize--; /* Take the smallest one */ if (ret_wsize != NULL) *ret_wsize = wsize; -- cgit v1.2.3 From f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 29 Jan 2013 07:16:59 -0300 Subject: [media] media: ov7670: calculate framerate properly for ov7675 According to the datasheet ov7675 uses a formula to achieve the desired framerate that is different from the operations done in the current code. In fact, this formula should apply to ov7670 too. This would mean that current code is wrong but, in order to preserve compatibility, the new formula will be used for ov7675 only. Signed-off-by: Javier Martin Cc: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 136 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 118 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 88e64739fefd..dea2917a2b12 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -47,6 +47,8 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); */ #define OV7670_I2C_ADDR 0x42 +#define PLL_FACTOR 4 + /* Registers */ #define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ #define REG_BLUE 0x01 /* blue gain */ @@ -164,6 +166,12 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); #define REG_GFIX 0x69 /* Fix gain control */ +#define REG_DBLV 0x6b /* PLL control an debugging */ +#define DBLV_BYPASS 0x00 /* Bypass PLL */ +#define DBLV_X4 0x01 /* clock x4 */ +#define DBLV_X6 0x10 /* clock x6 */ +#define DBLV_X8 0x11 /* clock x8 */ + #define REG_REG76 0x76 /* OV's name */ #define R76_BLKPCOR 0x80 /* Black pixel correction enable */ #define R76_WHTPCOR 0x40 /* White pixel correction enable */ @@ -203,6 +211,9 @@ struct ov7670_devtype { /* formats supported for each model */ struct ov7670_win_size *win_sizes; unsigned int n_win_sizes; + /* callbacks for frame rate control */ + int (*set_framerate)(struct v4l2_subdev *, struct v4l2_fract *); + void (*get_framerate)(struct v4l2_subdev *, struct v4l2_fract *); }; /* @@ -739,6 +750,98 @@ static struct ov7670_win_size ov7675_win_sizes[] = { } }; +static void ov7675_get_framerate(struct v4l2_subdev *sd, + struct v4l2_fract *tpf) +{ + struct ov7670_info *info = to_state(sd); + u32 clkrc = info->clkrc; + u32 pll_factor = PLL_FACTOR; + + clkrc++; + if (info->fmt->mbus_code == V4L2_MBUS_FMT_SBGGR8_1X8) + clkrc = (clkrc >> 1); + + tpf->numerator = 1; + tpf->denominator = (5 * pll_factor * info->clock_speed) / + (4 * clkrc); +} + +static int ov7675_set_framerate(struct v4l2_subdev *sd, + struct v4l2_fract *tpf) +{ + struct ov7670_info *info = to_state(sd); + u32 clkrc; + u32 pll_factor = PLL_FACTOR; + int ret; + + /* + * The formula is fps = 5/4*pixclk for YUV/RGB and + * fps = 5/2*pixclk for RAW. + * + * pixclk = clock_speed / (clkrc + 1) * PLLfactor + * + */ + if (tpf->numerator == 0 || tpf->denominator == 0) { + clkrc = 0; + } else { + clkrc = (5 * pll_factor * info->clock_speed * tpf->numerator) / + (4 * tpf->denominator); + if (info->fmt->mbus_code == V4L2_MBUS_FMT_SBGGR8_1X8) + clkrc = (clkrc << 1); + clkrc--; + } + + /* + * The datasheet claims that clkrc = 0 will divide the input clock by 1 + * but we've checked with an oscilloscope that it divides by 2 instead. + * So, if clkrc = 0 just bypass the divider. + */ + if (clkrc <= 0) + clkrc = CLK_EXT; + else if (clkrc > CLK_SCALE) + clkrc = CLK_SCALE; + info->clkrc = clkrc; + + /* Recalculate frame rate */ + ov7675_get_framerate(sd, tpf); + + ret = ov7670_write(sd, REG_CLKRC, info->clkrc); + if (ret < 0) + return ret; + return ov7670_write(sd, REG_DBLV, DBLV_X4); +} + +static void ov7670_get_framerate_legacy(struct v4l2_subdev *sd, + struct v4l2_fract *tpf) +{ + struct ov7670_info *info = to_state(sd); + + tpf->numerator = 1; + tpf->denominator = info->clock_speed; + if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) + tpf->denominator /= (info->clkrc & CLK_SCALE); +} + +static int ov7670_set_framerate_legacy(struct v4l2_subdev *sd, + struct v4l2_fract *tpf) +{ + struct ov7670_info *info = to_state(sd); + int div; + + if (tpf->numerator == 0 || tpf->denominator == 0) + div = 1; /* Reset to full rate */ + else + div = (tpf->numerator * info->clock_speed) / tpf->denominator; + if (div == 0) + div = 1; + else if (div > CLK_SCALE) + div = CLK_SCALE; + info->clkrc = (info->clkrc & 0x80) | div; + tpf->numerator = 1; + tpf->denominator = info->clock_speed / div; + return ov7670_write(sd, REG_CLKRC, info->clkrc); +} + /* * Store a set of start/stop values into the camera. */ @@ -913,10 +1016,8 @@ static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) memset(cp, 0, sizeof(struct v4l2_captureparm)); cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->timeperframe.numerator = 1; - cp->timeperframe.denominator = info->clock_speed; - if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) - cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE); + info->devtype->get_framerate(sd, &cp->timeperframe); + return 0; } @@ -925,25 +1026,13 @@ static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) struct v4l2_captureparm *cp = &parms->parm.capture; struct v4l2_fract *tpf = &cp->timeperframe; struct ov7670_info *info = to_state(sd); - int div; if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (cp->extendedmode != 0) return -EINVAL; - if (tpf->numerator == 0 || tpf->denominator == 0) - div = 1; /* Reset to full rate */ - else - div = (tpf->numerator * info->clock_speed) / tpf->denominator; - if (div == 0) - div = 1; - else if (div > CLK_SCALE) - div = CLK_SCALE; - info->clkrc = (info->clkrc & 0x80) | div; - tpf->numerator = 1; - tpf->denominator = info->clock_speed / div; - return ov7670_write(sd, REG_CLKRC, info->clkrc); + return info->devtype->set_framerate(sd, tpf); } @@ -1560,16 +1649,21 @@ static const struct ov7670_devtype ov7670_devdata[] = { [MODEL_OV7670] = { .win_sizes = ov7670_win_sizes, .n_win_sizes = ARRAY_SIZE(ov7670_win_sizes), + .set_framerate = ov7670_set_framerate_legacy, + .get_framerate = ov7670_get_framerate_legacy, }, [MODEL_OV7675] = { .win_sizes = ov7675_win_sizes, .n_win_sizes = ARRAY_SIZE(ov7675_win_sizes), + .set_framerate = ov7675_set_framerate, + .get_framerate = ov7675_get_framerate, }, }; static int ov7670_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct v4l2_fract tpf; struct v4l2_subdev *sd; struct ov7670_info *info; int ret; @@ -1611,7 +1705,13 @@ static int ov7670_probe(struct i2c_client *client, info->devtype = &ov7670_devdata[id->driver_data]; info->fmt = &ov7670_formats[0]; info->sat = 128; /* Review this */ - info->clkrc = info->clock_speed / 30; + info->clkrc = 0; + + /* Set default frame rate to 30 fps */ + tpf.numerator = 1; + tpf.denominator = 30; + info->devtype->set_framerate(sd, &tpf); + return 0; } -- cgit v1.2.3 From 04ee6d92047e1ac68d4eb615119343f4f0fc57db Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 29 Jan 2013 07:23:42 -0300 Subject: [media] media: ov7670: add possibility to bypass pll for ov7675 For a frame rate of 30 fps a pixclk of 24MHz is needed. For those cases where the ov7670 has a clean 24MHz input (xvclk) the PLL can be bypassed. This will result in a value of clkrc of 1, which means that in practice pixclk = xvclk (input clock) Acked-by: Jonathan Corbet Signed-off-by: Javier Martin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 28 ++++++++++++++++++++++++++-- include/media/ov7670.h | 1 + 2 files changed, 27 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index dea2917a2b12..3e503396aaa4 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -230,6 +230,7 @@ struct ov7670_info { int clock_speed; /* External clock speed (MHz) */ u8 clkrc; /* Clock divider value */ bool use_smbus; /* Use smbus I/O instead of I2C */ + bool pll_bypass; const struct ov7670_devtype *devtype; /* Device specifics */ }; @@ -755,7 +756,12 @@ static void ov7675_get_framerate(struct v4l2_subdev *sd, { struct ov7670_info *info = to_state(sd); u32 clkrc = info->clkrc; - u32 pll_factor = PLL_FACTOR; + int pll_factor; + + if (info->pll_bypass) + pll_factor = 1; + else + pll_factor = PLL_FACTOR; clkrc++; if (info->fmt->mbus_code == V4L2_MBUS_FMT_SBGGR8_1X8) @@ -771,7 +777,7 @@ static int ov7675_set_framerate(struct v4l2_subdev *sd, { struct ov7670_info *info = to_state(sd); u32 clkrc; - u32 pll_factor = PLL_FACTOR; + int pll_factor; int ret; /* @@ -781,6 +787,16 @@ static int ov7675_set_framerate(struct v4l2_subdev *sd, * pixclk = clock_speed / (clkrc + 1) * PLLfactor * */ + if (info->pll_bypass) { + pll_factor = 1; + ret = ov7670_write(sd, REG_DBLV, DBLV_BYPASS); + } else { + pll_factor = PLL_FACTOR; + ret = ov7670_write(sd, REG_DBLV, DBLV_X4); + } + if (ret < 0) + return ret; + if (tpf->numerator == 0 || tpf->denominator == 0) { clkrc = 0; } else { @@ -808,6 +824,7 @@ static int ov7675_set_framerate(struct v4l2_subdev *sd, ret = ov7670_write(sd, REG_CLKRC, info->clkrc); if (ret < 0) return ret; + return ov7670_write(sd, REG_DBLV, DBLV_X4); } @@ -1688,6 +1705,13 @@ static int ov7670_probe(struct i2c_client *client, if (config->clock_speed) info->clock_speed = config->clock_speed; + + /* + * It should be allowed for ov7670 too when it is migrated to + * the new frame rate formula. + */ + if (config->pll_bypass && id->driver_data != MODEL_OV7670) + info->pll_bypass = true; } /* Make sure it's an ov7670 */ diff --git a/include/media/ov7670.h b/include/media/ov7670.h index b133bc123031..a68c8bb3ceba 100644 --- a/include/media/ov7670.h +++ b/include/media/ov7670.h @@ -15,6 +15,7 @@ struct ov7670_config { int min_height; /* Filter out smaller sizes */ int clock_speed; /* External clock speed (MHz) */ bool use_smbus; /* Use smbus I/O instead of I2C */ + bool pll_bypass; /* Choose whether to bypass the PLL */ }; #endif -- cgit v1.2.3 From ee95258ed3926f3aa2cf8d62e62cd51be466fe26 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 29 Jan 2013 07:26:38 -0300 Subject: [media] media: ov7670: Add possibility to disable pixclk during hblank Some bridge drivers capture pixels during blanking periods if pixclk is enabled. In order to avoid capturing bogus data we need to disable pixclk in the sensor during those blanking periods. Acked-by: Jonathan Corbet Signed-off-by: Javier Martin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 7 +++++++ include/media/ov7670.h | 1 + 2 files changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 3e503396aaa4..52c024a334df 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -231,6 +231,7 @@ struct ov7670_info { u8 clkrc; /* Clock divider value */ bool use_smbus; /* Use smbus I/O instead of I2C */ bool pll_bypass; + bool pclk_hb_disable; const struct ov7670_devtype *devtype; /* Device specifics */ }; @@ -1712,6 +1713,9 @@ static int ov7670_probe(struct i2c_client *client, */ if (config->pll_bypass && id->driver_data != MODEL_OV7670) info->pll_bypass = true; + + if (config->pclk_hb_disable) + info->pclk_hb_disable = true; } /* Make sure it's an ov7670 */ @@ -1736,6 +1740,9 @@ static int ov7670_probe(struct i2c_client *client, tpf.denominator = 30; info->devtype->set_framerate(sd, &tpf); + if (info->pclk_hb_disable) + ov7670_write(sd, REG_COM10, COM10_PCLK_HB); + return 0; } diff --git a/include/media/ov7670.h b/include/media/ov7670.h index a68c8bb3ceba..1913d5123072 100644 --- a/include/media/ov7670.h +++ b/include/media/ov7670.h @@ -16,6 +16,7 @@ struct ov7670_config { int clock_speed; /* External clock speed (MHz) */ bool use_smbus; /* Use smbus I/O instead of I2C */ bool pll_bypass; /* Choose whether to bypass the PLL */ + bool pclk_hb_disable; /* Disable toggling pixclk during horizontal blanking */ }; #endif -- cgit v1.2.3 From 492959c77f66c2238298115f4fabf1bb9ca997eb Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 29 Jan 2013 07:31:17 -0300 Subject: [media] ov7670: use the control framework Cc: Jonathan Corbet Signed-off-by: Javier Martin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 310 ++++++++++++++++----------------------------- 1 file changed, 112 insertions(+), 198 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 52c024a334df..93a051e8873f 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -222,9 +223,23 @@ struct ov7670_devtype { struct ov7670_format_struct; /* coming later */ struct ov7670_info { struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct { + /* gain cluster */ + struct v4l2_ctrl *auto_gain; + struct v4l2_ctrl *gain; + }; + struct { + /* exposure cluster */ + struct v4l2_ctrl *auto_exposure; + struct v4l2_ctrl *exposure; + }; + struct { + /* saturation/hue cluster */ + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *hue; + }; struct ov7670_format_struct *fmt; /* Current format */ - unsigned char sat; /* Saturation value */ - int hue; /* Hue value */ int min_width; /* Filter out smaller sizes */ int min_height; /* Filter out smaller sizes */ int clock_speed; /* External clock speed (MHz) */ @@ -240,6 +255,11 @@ static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) return container_of(sd, struct ov7670_info, sd); } +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct ov7670_info, hdl)->sd; +} + /* @@ -1195,23 +1215,23 @@ static int ov7670_cosine(int theta) static void ov7670_calc_cmatrix(struct ov7670_info *info, - int matrix[CMATRIX_LEN]) + int matrix[CMATRIX_LEN], int sat, int hue) { int i; /* * Apply the current saturation setting first. */ for (i = 0; i < CMATRIX_LEN; i++) - matrix[i] = (info->fmt->cmatrix[i]*info->sat) >> 7; + matrix[i] = (info->fmt->cmatrix[i] * sat) >> 7; /* * Then, if need be, rotate the hue value. */ - if (info->hue != 0) { + if (hue != 0) { int sinth, costh, tmpmatrix[CMATRIX_LEN]; memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int)); - sinth = ov7670_sine(info->hue); - costh = ov7670_cosine(info->hue); + sinth = ov7670_sine(hue); + costh = ov7670_cosine(hue); matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000; matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000; @@ -1224,60 +1244,21 @@ static void ov7670_calc_cmatrix(struct ov7670_info *info, -static int ov7670_s_sat(struct v4l2_subdev *sd, int value) +static int ov7670_s_sat_hue(struct v4l2_subdev *sd, int sat, int hue) { struct ov7670_info *info = to_state(sd); int matrix[CMATRIX_LEN]; int ret; - info->sat = value; - ov7670_calc_cmatrix(info, matrix); + ov7670_calc_cmatrix(info, matrix, sat, hue); ret = ov7670_store_cmatrix(sd, matrix); return ret; } -static int ov7670_g_sat(struct v4l2_subdev *sd, __s32 *value) -{ - struct ov7670_info *info = to_state(sd); - - *value = info->sat; - return 0; -} - -static int ov7670_s_hue(struct v4l2_subdev *sd, int value) -{ - struct ov7670_info *info = to_state(sd); - int matrix[CMATRIX_LEN]; - int ret; - - if (value < -180 || value > 180) - return -EINVAL; - info->hue = value; - ov7670_calc_cmatrix(info, matrix); - ret = ov7670_store_cmatrix(sd, matrix); - return ret; -} - - -static int ov7670_g_hue(struct v4l2_subdev *sd, __s32 *value) -{ - struct ov7670_info *info = to_state(sd); - - *value = info->hue; - return 0; -} - /* * Some weird registers seem to store values in a sign/magnitude format! */ -static unsigned char ov7670_sm_to_abs(unsigned char v) -{ - if ((v & 0x80) == 0) - return v + 128; - return 128 - (v & 0x7f); -} - static unsigned char ov7670_abs_to_sm(unsigned char v) { @@ -1299,40 +1280,11 @@ static int ov7670_s_brightness(struct v4l2_subdev *sd, int value) return ret; } -static int ov7670_g_brightness(struct v4l2_subdev *sd, __s32 *value) -{ - unsigned char v = 0; - int ret = ov7670_read(sd, REG_BRIGHT, &v); - - *value = ov7670_sm_to_abs(v); - return ret; -} - static int ov7670_s_contrast(struct v4l2_subdev *sd, int value) { return ov7670_write(sd, REG_CONTRAS, (unsigned char) value); } -static int ov7670_g_contrast(struct v4l2_subdev *sd, __s32 *value) -{ - unsigned char v = 0; - int ret = ov7670_read(sd, REG_CONTRAS, &v); - - *value = v; - return ret; -} - -static int ov7670_g_hflip(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char v = 0; - - ret = ov7670_read(sd, REG_MVFP, &v); - *value = (v & MVFP_MIRROR) == MVFP_MIRROR; - return ret; -} - - static int ov7670_s_hflip(struct v4l2_subdev *sd, int value) { unsigned char v = 0; @@ -1348,19 +1300,6 @@ static int ov7670_s_hflip(struct v4l2_subdev *sd, int value) return ret; } - - -static int ov7670_g_vflip(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char v = 0; - - ret = ov7670_read(sd, REG_MVFP, &v); - *value = (v & MVFP_FLIP) == MVFP_FLIP; - return ret; -} - - static int ov7670_s_vflip(struct v4l2_subdev *sd, int value) { unsigned char v = 0; @@ -1409,16 +1348,6 @@ static int ov7670_s_gain(struct v4l2_subdev *sd, int value) /* * Tweak autogain. */ -static int ov7670_g_autogain(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char com8; - - ret = ov7670_read(sd, REG_COM8, &com8); - *value = (com8 & COM8_AGC) != 0; - return ret; -} - static int ov7670_s_autogain(struct v4l2_subdev *sd, int value) { int ret; @@ -1435,22 +1364,6 @@ static int ov7670_s_autogain(struct v4l2_subdev *sd, int value) return ret; } -/* - * Exposure is spread all over the place: top 6 bits in AECHH, middle - * 8 in AECH, and two stashed in COM1 just for the hell of it. - */ -static int ov7670_g_exp(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char com1, aech, aechh; - - ret = ov7670_read(sd, REG_COM1, &com1) + - ov7670_read(sd, REG_AECH, &aech) + - ov7670_read(sd, REG_AECHH, &aechh); - *value = ((aechh & 0x3f) << 10) | (aech << 2) | (com1 & 0x03); - return ret; -} - static int ov7670_s_exp(struct v4l2_subdev *sd, int value) { int ret; @@ -1477,20 +1390,6 @@ static int ov7670_s_exp(struct v4l2_subdev *sd, int value) /* * Tweak autoexposure. */ -static int ov7670_g_autoexp(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char com8; - enum v4l2_exposure_auto_type *atype = (enum v4l2_exposure_auto_type *) value; - - ret = ov7670_read(sd, REG_COM8, &com8); - if (com8 & COM8_AEC) - *atype = V4L2_EXPOSURE_AUTO; - else - *atype = V4L2_EXPOSURE_MANUAL; - return ret; -} - static int ov7670_s_autoexp(struct v4l2_subdev *sd, enum v4l2_exposure_auto_type value) { @@ -1509,90 +1408,60 @@ static int ov7670_s_autoexp(struct v4l2_subdev *sd, } - -static int ov7670_queryctrl(struct v4l2_subdev *sd, - struct v4l2_queryctrl *qc) +static int ov7670_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { - /* Fill in min, max, step and default value for these controls. */ - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); - case V4L2_CID_VFLIP: - case V4L2_CID_HFLIP: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qc, 0, 256, 1, 128); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, -180, 180, 5, 0); - case V4L2_CID_GAIN: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); - case V4L2_CID_AUTOGAIN: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - case V4L2_CID_EXPOSURE: - return v4l2_ctrl_query_fill(qc, 0, 65535, 1, 500); - case V4L2_CID_EXPOSURE_AUTO: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); - } - return -EINVAL; -} + struct v4l2_subdev *sd = to_sd(ctrl); + struct ov7670_info *info = to_state(sd); -static int ov7670_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - return ov7670_g_brightness(sd, &ctrl->value); - case V4L2_CID_CONTRAST: - return ov7670_g_contrast(sd, &ctrl->value); - case V4L2_CID_SATURATION: - return ov7670_g_sat(sd, &ctrl->value); - case V4L2_CID_HUE: - return ov7670_g_hue(sd, &ctrl->value); - case V4L2_CID_VFLIP: - return ov7670_g_vflip(sd, &ctrl->value); - case V4L2_CID_HFLIP: - return ov7670_g_hflip(sd, &ctrl->value); - case V4L2_CID_GAIN: - return ov7670_g_gain(sd, &ctrl->value); case V4L2_CID_AUTOGAIN: - return ov7670_g_autogain(sd, &ctrl->value); - case V4L2_CID_EXPOSURE: - return ov7670_g_exp(sd, &ctrl->value); - case V4L2_CID_EXPOSURE_AUTO: - return ov7670_g_autoexp(sd, &ctrl->value); + return ov7670_g_gain(sd, &info->gain->val); } return -EINVAL; } -static int ov7670_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int ov7670_s_ctrl(struct v4l2_ctrl *ctrl) { + struct v4l2_subdev *sd = to_sd(ctrl); + struct ov7670_info *info = to_state(sd); + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - return ov7670_s_brightness(sd, ctrl->value); + return ov7670_s_brightness(sd, ctrl->val); case V4L2_CID_CONTRAST: - return ov7670_s_contrast(sd, ctrl->value); + return ov7670_s_contrast(sd, ctrl->val); case V4L2_CID_SATURATION: - return ov7670_s_sat(sd, ctrl->value); - case V4L2_CID_HUE: - return ov7670_s_hue(sd, ctrl->value); + return ov7670_s_sat_hue(sd, + info->saturation->val, info->hue->val); case V4L2_CID_VFLIP: - return ov7670_s_vflip(sd, ctrl->value); + return ov7670_s_vflip(sd, ctrl->val); case V4L2_CID_HFLIP: - return ov7670_s_hflip(sd, ctrl->value); - case V4L2_CID_GAIN: - return ov7670_s_gain(sd, ctrl->value); + return ov7670_s_hflip(sd, ctrl->val); case V4L2_CID_AUTOGAIN: - return ov7670_s_autogain(sd, ctrl->value); - case V4L2_CID_EXPOSURE: - return ov7670_s_exp(sd, ctrl->value); + /* Only set manual gain if auto gain is not explicitly + turned on. */ + if (!ctrl->val) { + /* ov7670_s_gain turns off auto gain */ + return ov7670_s_gain(sd, info->gain->val); + } + return ov7670_s_autogain(sd, ctrl->val); case V4L2_CID_EXPOSURE_AUTO: - return ov7670_s_autoexp(sd, - (enum v4l2_exposure_auto_type) ctrl->value); + /* Only set manual exposure if auto exposure is not explicitly + turned on. */ + if (ctrl->val == V4L2_EXPOSURE_MANUAL) { + /* ov7670_s_exp turns off auto exposure */ + return ov7670_s_exp(sd, info->exposure->val); + } + return ov7670_s_autoexp(sd, ctrl->val); } return -EINVAL; } +static const struct v4l2_ctrl_ops ov7670_ctrl_ops = { + .s_ctrl = ov7670_s_ctrl, + .g_volatile_ctrl = ov7670_g_volatile_ctrl, +}; + static int ov7670_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { @@ -1635,9 +1504,13 @@ static int ov7670_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *r static const struct v4l2_subdev_core_ops ov7670_core_ops = { .g_chip_ident = ov7670_g_chip_ident, - .g_ctrl = ov7670_g_ctrl, - .s_ctrl = ov7670_s_ctrl, - .queryctrl = ov7670_queryctrl, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, .reset = ov7670_reset, .init = ov7670_init, #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -1732,7 +1605,6 @@ static int ov7670_probe(struct i2c_client *client, info->devtype = &ov7670_devdata[id->driver_data]; info->fmt = &ov7670_formats[0]; - info->sat = 128; /* Review this */ info->clkrc = 0; /* Set default frame rate to 30 fps */ @@ -1743,6 +1615,46 @@ static int ov7670_probe(struct i2c_client *client, if (info->pclk_hb_disable) ov7670_write(sd, REG_COM10, COM10_PCLK_HB); + v4l2_ctrl_handler_init(&info->hdl, 10); + v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, + V4L2_CID_CONTRAST, 0, 127, 1, 64); + v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + info->saturation = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, + V4L2_CID_SATURATION, 0, 256, 1, 128); + info->hue = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, + V4L2_CID_HUE, -180, 180, 5, 0); + info->gain = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, + V4L2_CID_GAIN, 0, 255, 1, 128); + info->auto_gain = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + info->exposure = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 65535, 1, 500); + info->auto_exposure = v4l2_ctrl_new_std_menu(&info->hdl, &ov7670_ctrl_ops, + V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, + V4L2_EXPOSURE_AUTO); + sd->ctrl_handler = &info->hdl; + if (info->hdl.error) { + int err = info->hdl.error; + + v4l2_ctrl_handler_free(&info->hdl); + kfree(info); + return err; + } + /* + * We have checked empirically that hw allows to read back the gain + * value chosen by auto gain but that's not the case for auto exposure. + */ + v4l2_ctrl_auto_cluster(2, &info->auto_gain, 0, true); + v4l2_ctrl_auto_cluster(2, &info->auto_exposure, + V4L2_EXPOSURE_MANUAL, false); + v4l2_ctrl_cluster(2, &info->saturation); + v4l2_ctrl_handler_setup(&info->hdl); + return 0; } @@ -1750,9 +1662,11 @@ static int ov7670_probe(struct i2c_client *client, static int ov7670_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov7670_info *info = to_state(sd); v4l2_device_unregister_subdev(sd); - kfree(to_state(sd)); + v4l2_ctrl_handler_free(&info->hdl); + kfree(info); return 0; } -- cgit v1.2.3 From 593403c59b2c1977cf22313d4bb7e912b35ee797 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 29 Jan 2013 07:58:48 -0300 Subject: [media] mcam-core: implement the control framework Signed-off-by: Hans Verkuil Signed-off-by: Javier Martin Cc: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/marvell-ccic/mcam-core.c | 54 +++++-------------------- drivers/media/platform/marvell-ccic/mcam-core.h | 2 + 2 files changed, 11 insertions(+), 45 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 7012913f34a2..92a33f081852 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1225,47 +1226,6 @@ static int mcam_vidioc_dqbuf(struct file *filp, void *priv, return ret; } - - -static int mcam_vidioc_queryctrl(struct file *filp, void *priv, - struct v4l2_queryctrl *qc) -{ - struct mcam_camera *cam = priv; - int ret; - - mutex_lock(&cam->s_mutex); - ret = sensor_call(cam, core, queryctrl, qc); - mutex_unlock(&cam->s_mutex); - return ret; -} - - -static int mcam_vidioc_g_ctrl(struct file *filp, void *priv, - struct v4l2_control *ctrl) -{ - struct mcam_camera *cam = priv; - int ret; - - mutex_lock(&cam->s_mutex); - ret = sensor_call(cam, core, g_ctrl, ctrl); - mutex_unlock(&cam->s_mutex); - return ret; -} - - -static int mcam_vidioc_s_ctrl(struct file *filp, void *priv, - struct v4l2_control *ctrl) -{ - struct mcam_camera *cam = priv; - int ret; - - mutex_lock(&cam->s_mutex); - ret = sensor_call(cam, core, s_ctrl, ctrl); - mutex_unlock(&cam->s_mutex); - return ret; -} - - static int mcam_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -1513,9 +1473,6 @@ static const struct v4l2_ioctl_ops mcam_v4l_ioctl_ops = { .vidioc_dqbuf = mcam_vidioc_dqbuf, .vidioc_streamon = mcam_vidioc_streamon, .vidioc_streamoff = mcam_vidioc_streamoff, - .vidioc_queryctrl = mcam_vidioc_queryctrl, - .vidioc_g_ctrl = mcam_vidioc_g_ctrl, - .vidioc_s_ctrl = mcam_vidioc_s_ctrl, .vidioc_g_parm = mcam_vidioc_g_parm, .vidioc_s_parm = mcam_vidioc_s_parm, .vidioc_enum_framesizes = mcam_vidioc_enum_framesizes, @@ -1782,14 +1739,19 @@ int mccic_register(struct mcam_camera *cam) /* * Get the v4l2 setup done. */ + ret = v4l2_ctrl_handler_init(&cam->ctrl_handler, 10); + if (ret) + goto out_unregister; + cam->v4l2_dev.ctrl_handler = &cam->ctrl_handler; + mutex_lock(&cam->s_mutex); cam->vdev = mcam_v4l_template; cam->vdev.debug = 0; cam->vdev.v4l2_dev = &cam->v4l2_dev; + video_set_drvdata(&cam->vdev, cam); ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); if (ret) goto out; - video_set_drvdata(&cam->vdev, cam); /* * If so requested, try to get our DMA buffers now. @@ -1801,6 +1763,7 @@ int mccic_register(struct mcam_camera *cam) } out: + v4l2_ctrl_handler_free(&cam->ctrl_handler); mutex_unlock(&cam->s_mutex); return ret; out_unregister: @@ -1825,6 +1788,7 @@ void mccic_shutdown(struct mcam_camera *cam) if (cam->buffer_mode == B_vmalloc) mcam_free_dma_bufs(cam); video_unregister_device(&cam->vdev); + v4l2_ctrl_handler_free(&cam->ctrl_handler); v4l2_device_unregister(&cam->v4l2_dev); } diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index 6c53ac9681da..01dec9e5fc2b 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -112,6 +113,7 @@ struct mcam_camera { * should not be touched by the platform code. */ struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; enum mcam_state state; unsigned long flags; /* Buffer status, mainly (dev_lock) */ int users; /* How many open FDs */ -- cgit v1.2.3 From dbf8f4e5839869d1ead50b661a2cbab3b7225002 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 29 Jan 2013 08:06:35 -0300 Subject: [media] via-camera: implement the control framework And added a missing kfree to clean up the via_camera struct. Signed-off-by: Hans Verkuil Signed-off-by: Javier Martin Cc: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/via-camera.c | 60 +++++++++---------------------------- 1 file changed, 14 insertions(+), 46 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c index 63e8c3461239..b051c4a28554 100644 --- a/drivers/media/platform/via-camera.c +++ b/drivers/media/platform/via-camera.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -63,6 +64,7 @@ enum viacam_opstate { S_IDLE = 0, S_RUNNING = 1 }; struct via_camera { struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; struct video_device vdev; struct v4l2_subdev *sensor; struct platform_device *platdev; @@ -817,47 +819,6 @@ static int viacam_g_chip_ident(struct file *file, void *priv, return sensor_call(cam, core, g_chip_ident, ident); } -/* - * Control ops are passed through to the sensor. - */ -static int viacam_queryctrl(struct file *filp, void *priv, - struct v4l2_queryctrl *qc) -{ - struct via_camera *cam = priv; - int ret; - - mutex_lock(&cam->lock); - ret = sensor_call(cam, core, queryctrl, qc); - mutex_unlock(&cam->lock); - return ret; -} - - -static int viacam_g_ctrl(struct file *filp, void *priv, - struct v4l2_control *ctrl) -{ - struct via_camera *cam = priv; - int ret; - - mutex_lock(&cam->lock); - ret = sensor_call(cam, core, g_ctrl, ctrl); - mutex_unlock(&cam->lock); - return ret; -} - - -static int viacam_s_ctrl(struct file *filp, void *priv, - struct v4l2_control *ctrl) -{ - struct via_camera *cam = priv; - int ret; - - mutex_lock(&cam->lock); - ret = sensor_call(cam, core, s_ctrl, ctrl); - mutex_unlock(&cam->lock); - return ret; -} - /* * Only one input. */ @@ -1214,9 +1175,6 @@ static int viacam_enum_frameintervals(struct file *filp, void *priv, static const struct v4l2_ioctl_ops viacam_ioctl_ops = { .vidioc_g_chip_ident = viacam_g_chip_ident, - .vidioc_queryctrl = viacam_queryctrl, - .vidioc_g_ctrl = viacam_g_ctrl, - .vidioc_s_ctrl = viacam_s_ctrl, .vidioc_enum_input = viacam_enum_input, .vidioc_g_input = viacam_g_input, .vidioc_s_input = viacam_s_input, @@ -1418,8 +1376,12 @@ static int viacam_probe(struct platform_device *pdev) ret = v4l2_device_register(&pdev->dev, &cam->v4l2_dev); if (ret) { dev_err(&pdev->dev, "Unable to register v4l2 device\n"); - return ret; + goto out_free; } + ret = v4l2_ctrl_handler_init(&cam->ctrl_handler, 10); + if (ret) + goto out_unregister; + cam->v4l2_dev.ctrl_handler = &cam->ctrl_handler; /* * Convince the system that we can do DMA. */ @@ -1436,7 +1398,7 @@ static int viacam_probe(struct platform_device *pdev) */ ret = via_sensor_power_setup(cam); if (ret) - goto out_unregister; + goto out_ctrl_hdl_free; via_sensor_power_up(cam); /* @@ -1485,8 +1447,12 @@ out_irq: free_irq(viadev->pdev->irq, cam); out_power_down: via_sensor_power_release(cam); +out_ctrl_hdl_free: + v4l2_ctrl_handler_free(&cam->ctrl_handler); out_unregister: v4l2_device_unregister(&cam->v4l2_dev); +out_free: + kfree(cam); return ret; } @@ -1499,6 +1465,8 @@ static int viacam_remove(struct platform_device *pdev) v4l2_device_unregister(&cam->v4l2_dev); free_irq(viadev->pdev->irq, cam); via_sensor_power_release(cam); + v4l2_ctrl_handler_free(&cam->ctrl_handler); + kfree(cam); via_cam_info = NULL; return 0; } -- cgit v1.2.3 From de03277d6ae26d09b3af8617f291c4bb3db7d2eb Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 29 Jan 2013 08:21:40 -0300 Subject: [media] ov7670: remove legacy ctrl callbacks via-camera and mcam-core were the only bridge drivers that used ov7670. Since now they have been moved to use the ctrl framework, the old legacy callbacks in the ov7670 can be removed. Signed-off-by: Javier Martin Cc: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 93a051e8873f..05ed5b8e7f88 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -1504,13 +1504,6 @@ static int ov7670_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *r static const struct v4l2_subdev_core_ops ov7670_core_ops = { .g_chip_ident = ov7670_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, .reset = ov7670_reset, .init = ov7670_init, #ifdef CONFIG_VIDEO_ADV_DEBUG -- cgit v1.2.3 From 88b404c435ffb6c103faf85cc1b41077dcd03bf9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 30 Jan 2013 03:03:43 -0300 Subject: [media] tm6000: check an allocation for failure This allocation had no error checking. It didn't need to be under the mutex so I moved it out form there. That makes the error handling easier and is a potential speed up. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-core.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/tm6000/tm6000-core.c b/drivers/media/usb/tm6000/tm6000-core.c index 22cc0116deb6..7c32353c59db 100644 --- a/drivers/media/usb/tm6000/tm6000-core.c +++ b/drivers/media/usb/tm6000/tm6000-core.c @@ -40,10 +40,13 @@ int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req, u8 *data = NULL; int delay = 5000; - mutex_lock(&dev->usb_lock); - - if (len) + if (len) { data = kzalloc(len, GFP_KERNEL); + if (!data) + return -ENOMEM; + } + + mutex_lock(&dev->usb_lock); if (req_type & USB_DIR_IN) pipe = usb_rcvctrlpipe(dev->udev, 0); -- cgit v1.2.3 From 9690fd80d3fb1bbfe04d7e1d8dde0cfd353c7b8d Mon Sep 17 00:00:00 2001 From: Vadim Frolov Date: Wed, 30 Jan 2013 05:14:59 -0300 Subject: [media] saa7134: Add capture card Hawell HW-9004V1 This patch adds new capture board Hawell HW-9004V1. This card has 4 SAA71300 chips. In order to work it is needed to initialize its registers (gpio mask and value). The value of these registers were dumped under Windows using flytest. Signed-off-by: Vadim Frolov Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.saa7134 | 1 + drivers/media/pci/saa7134/saa7134-cards.c | 17 +++++++++++++++++ drivers/media/pci/saa7134/saa7134.h | 1 + 3 files changed, 19 insertions(+) (limited to 'drivers') diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index 94d9025aa82d..b3ad68309109 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -189,3 +189,4 @@ 188 -> Sensoray 811/911 [6000:0811,6000:0911] 189 -> Kworld PC150-U [17de:a134] 190 -> Asus My Cinema PS3-100 [1043:48cd] +191 -> Hawell HW-9004V1 diff --git a/drivers/media/pci/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c index bc08f1dbc293..dc68cf1070f7 100644 --- a/drivers/media/pci/saa7134/saa7134-cards.c +++ b/drivers/media/pci/saa7134/saa7134-cards.c @@ -5773,6 +5773,23 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0000000, }, }, + [SAA7134_BOARD_HAWELL_HW_9004V1] = { + /* Hawell HW-9004V1 */ + /* Vadim Frolov */ + .name = "Hawell HW-9004V1", + .audio_clock = 0x00200000, + .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x618E700, + .inputs = {{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x6010000, + } }, + }, }; diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index f804324e07fd..71eefef5e324 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -333,6 +333,7 @@ struct saa7134_card_ir { #define SAA7134_BOARD_SENSORAY811_911 188 #define SAA7134_BOARD_KWORLD_PC150U 189 #define SAA7134_BOARD_ASUSTeK_PS3_100 190 +#define SAA7134_BOARD_HAWELL_HW_9004V1 191 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 -- cgit v1.2.3 From 47ebe3f93250fce6f4eaa16309ae5eee9d4099b3 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 30 Jan 2013 09:25:25 -0300 Subject: [media] soc-camera: fix compilation breakage in 3 drivers A recent commit broke compilation of 3 camera drivers: for PXA2x0, OMAP1 and MX1 by using a wrong pointer. Fix them. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/mx1_camera.c | 2 +- drivers/media/platform/soc_camera/omap1_camera.c | 4 ++-- drivers/media/platform/soc_camera/pxa_camera.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/soc_camera/mx1_camera.c b/drivers/media/platform/soc_camera/mx1_camera.c index 4b661e87d8b1..25b2a285dc86 100644 --- a/drivers/media/platform/soc_camera/mx1_camera.c +++ b/drivers/media/platform/soc_camera/mx1_camera.c @@ -372,7 +372,7 @@ static void mx1_camera_init_videobuf(struct videobuf_queue *q, videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, icd->parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct mx1_buffer), icd, &icd->host_lock); + sizeof(struct mx1_buffer), icd, &ici->host_lock); } static int mclk_get_divisor(struct mx1_camera_dev *pcdev) diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c index dcf7be814776..2547bf88f79f 100644 --- a/drivers/media/platform/soc_camera/omap1_camera.c +++ b/drivers/media/platform/soc_camera/omap1_camera.c @@ -1383,12 +1383,12 @@ static void omap1_cam_init_videobuf(struct videobuf_queue *q, videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops, icd->parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct omap1_cam_buf), icd, &icd->host_lock); + sizeof(struct omap1_cam_buf), icd, &ici->host_lock); else videobuf_queue_sg_init(q, &omap1_videobuf_ops, icd->parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct omap1_cam_buf), icd, &icd->host_lock); + sizeof(struct omap1_cam_buf), icd, &ici->host_lock); /* use videobuf mode (auto)selected with the module parameter */ pcdev->vb_mode = sg_mode ? OMAP1_CAM_DMA_SG : OMAP1_CAM_DMA_CONTIG; diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c index 1fbec5202e4f..9c97f7e985e1 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/soc_camera/pxa_camera.c @@ -842,7 +842,7 @@ static void pxa_camera_init_videobuf(struct videobuf_queue *q, */ videobuf_queue_sg_init(q, &pxa_videobuf_ops, NULL, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct pxa_buffer), icd, &icd->host_lock); + sizeof(struct pxa_buffer), icd, &ici->host_lock); } static u32 mclk_get_divisor(struct platform_device *pdev, -- cgit v1.2.3 From 0a3237704dec476be3cdfbe8fc9df9cc65b14442 Mon Sep 17 00:00:00 2001 From: Jose Alberto Reguero Date: Sun, 3 Feb 2013 18:30:38 -0300 Subject: [media] [PATH,1/2] mxl5007 move reset to attach This patch move the soft reset to the attach function because with dual tuners, when one tuner do reset, the other one is perturbed, and the stream has errors. Signed-off-by: Jose Alberto Reguero Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/mxl5007t.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/tuners/mxl5007t.c b/drivers/media/tuners/mxl5007t.c index 69e453ef0a1a..eb61304c260d 100644 --- a/drivers/media/tuners/mxl5007t.c +++ b/drivers/media/tuners/mxl5007t.c @@ -531,10 +531,6 @@ static int mxl5007t_tuner_init(struct mxl5007t_state *state, struct reg_pair_t *init_regs; int ret; - ret = mxl5007t_soft_reset(state); - if (mxl_fail(ret)) - goto fail; - /* calculate initialization reg array */ init_regs = mxl5007t_calc_init_regs(state, mode); @@ -900,7 +896,20 @@ struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe, /* existing tuner instance */ break; } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = mxl5007t_soft_reset(state); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (mxl_fail(ret)) + goto fail; + fe->tuner_priv = state; + mutex_unlock(&mxl5007t_list_mutex); memcpy(&fe->ops.tuner_ops, &mxl5007t_tuner_ops, -- cgit v1.2.3 From da92c29340172fddb6ea838cd964614195031071 Mon Sep 17 00:00:00 2001 From: Matti Kurkela Date: Tue, 5 Feb 2013 07:08:42 -0300 Subject: [media] ttusb2: Kconfig patch to auto-select frontends for TechnoTrend CT-3650 The ttusb2 module is already updated to recognize the TechnoTrend CT-3650 CI DVB C/T USB2.0 receiver in addition to the Pinnacle 400e. But if MEDIA_SUBDRV_AUTOSELECT is used, the required tuner and demodulator modules are not automatically selected. Here's a patch to fix that and add a note of the CT-3650 to the online help of the ttusb2 module. This patch applies cleanly to 3.7.6 and other 3.7.x kernels. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/Kconfig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index c423eb80c3a5..c5d95662e2e1 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -202,8 +202,12 @@ config DVB_USB_TTUSB2 select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT help - Say Y here to support the Pinnacle 400e DVB-S USB2.0 receiver. The + Say Y here to support the Pinnacle 400e DVB-S USB2.0 receiver and + the TechnoTrend CT-3650 CI DVB-C/T USB2.0 receiver. The firmware protocol used by this module is similar to the one used by the old ttusb-driver - that's why the module is called dvb-usb-ttusb2. -- cgit v1.2.3 From 625958207752519bd0e49d0dcdde61a20ac081b3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 6 Feb 2013 07:53:31 -0300 Subject: [media] cx2341x: move from media/i2c to media/common The cx2341x module is a helper module for conexant-based MPEG encoders. It isn't an i2c module at all, instead it should be in common since it is used by 7 pci and usb drivers to handle the MPEG setup. It also shouldn't be visible in the config menu as it is always selected automatically by those drivers that need it. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/Kconfig | 3 + drivers/media/common/Makefile | 1 + drivers/media/common/cx2341x.c | 1726 ++++++++++++++++++++++++++++++++++++++++ drivers/media/i2c/Kconfig | 14 - drivers/media/i2c/Makefile | 1 - drivers/media/i2c/cx2341x.c | 1726 ---------------------------------------- 6 files changed, 1730 insertions(+), 1741 deletions(-) create mode 100644 drivers/media/common/cx2341x.c delete mode 100644 drivers/media/i2c/cx2341x.c (limited to 'drivers') diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index d2a436ce77f8..dfac52f312cf 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig @@ -5,6 +5,9 @@ config MEDIA_COMMON_OPTIONS comment "common driver options" depends on MEDIA_COMMON_OPTIONS +config VIDEO_CX2341X + tristate + source "drivers/media/common/b2c2/Kconfig" source "drivers/media/common/saa7146/Kconfig" source "drivers/media/common/siano/Kconfig" diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index b8e2e3a33a31..9b8ea4fae06f 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -1 +1,2 @@ obj-y += b2c2/ saa7146/ siano/ +obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o diff --git a/drivers/media/common/cx2341x.c b/drivers/media/common/cx2341x.c new file mode 100644 index 000000000000..103ef6bad2e2 --- /dev/null +++ b/drivers/media/common/cx2341x.c @@ -0,0 +1,1726 @@ +/* + * cx2341x - generic code for cx23415/6/8 based devices + * + * Copyright (C) 2006 Hans Verkuil + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +MODULE_DESCRIPTION("cx23415/6/8 driver"); +MODULE_AUTHOR("Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/********************** COMMON CODE *********************/ + +/* definitions for audio properties bits 29-28 */ +#define CX2341X_AUDIO_ENCODING_METHOD_MPEG 0 +#define CX2341X_AUDIO_ENCODING_METHOD_AC3 1 +#define CX2341X_AUDIO_ENCODING_METHOD_LPCM 2 + +static const char *cx2341x_get_name(u32 id) +{ + switch (id) { + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + return "Spatial Filter Mode"; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + return "Spatial Filter"; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + return "Spatial Luma Filter Type"; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + return "Spatial Chroma Filter Type"; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + return "Temporal Filter Mode"; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + return "Temporal Filter"; + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + return "Median Filter Type"; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + return "Median Luma Filter Maximum"; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + return "Median Luma Filter Minimum"; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + return "Median Chroma Filter Maximum"; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + return "Median Chroma Filter Minimum"; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + return "Insert Navigation Packets"; + } + return NULL; +} + +static const char **cx2341x_get_menu(u32 id) +{ + static const char *cx2341x_video_spatial_filter_mode_menu[] = { + "Manual", + "Auto", + NULL + }; + + static const char *cx2341x_video_luma_spatial_filter_type_menu[] = { + "Off", + "1D Horizontal", + "1D Vertical", + "2D H/V Separable", + "2D Symmetric non-separable", + NULL + }; + + static const char *cx2341x_video_chroma_spatial_filter_type_menu[] = { + "Off", + "1D Horizontal", + NULL + }; + + static const char *cx2341x_video_temporal_filter_mode_menu[] = { + "Manual", + "Auto", + NULL + }; + + static const char *cx2341x_video_median_filter_type_menu[] = { + "Off", + "Horizontal", + "Vertical", + "Horizontal/Vertical", + "Diagonal", + NULL + }; + + switch (id) { + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + return cx2341x_video_spatial_filter_mode_menu; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + return cx2341x_video_luma_spatial_filter_type_menu; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + return cx2341x_video_chroma_spatial_filter_type_menu; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + return cx2341x_video_temporal_filter_mode_menu; + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + return cx2341x_video_median_filter_type_menu; + } + return NULL; +} + +static void cx2341x_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, + s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags) +{ + *name = cx2341x_get_name(id); + *flags = 0; + + switch (id) { + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + *type = V4L2_CTRL_TYPE_MENU; + *min = 0; + *step = 0; + break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + *type = V4L2_CTRL_TYPE_BOOLEAN; + *min = 0; + *max = *step = 1; + break; + default: + *type = V4L2_CTRL_TYPE_INTEGER; + break; + } + switch (id) { + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + *flags |= V4L2_CTRL_FLAG_UPDATE; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + *flags |= V4L2_CTRL_FLAG_SLIDER; + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + *flags |= V4L2_CTRL_FLAG_READ_ONLY; + break; + } +} + + +/********************** OLD CODE *********************/ + +/* Must be sorted from low to high control ID! */ +const u32 cx2341x_mpeg_ctrls[] = { + V4L2_CID_MPEG_CLASS, + V4L2_CID_MPEG_STREAM_TYPE, + V4L2_CID_MPEG_STREAM_VBI_FMT, + V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, + V4L2_CID_MPEG_AUDIO_ENCODING, + V4L2_CID_MPEG_AUDIO_L2_BITRATE, + V4L2_CID_MPEG_AUDIO_MODE, + V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, + V4L2_CID_MPEG_AUDIO_EMPHASIS, + V4L2_CID_MPEG_AUDIO_CRC, + V4L2_CID_MPEG_AUDIO_MUTE, + V4L2_CID_MPEG_AUDIO_AC3_BITRATE, + V4L2_CID_MPEG_VIDEO_ENCODING, + V4L2_CID_MPEG_VIDEO_ASPECT, + V4L2_CID_MPEG_VIDEO_B_FRAMES, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_CID_MPEG_VIDEO_BITRATE, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, + V4L2_CID_MPEG_VIDEO_MUTE, + V4L2_CID_MPEG_VIDEO_MUTE_YUV, + V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE, + V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE, + V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE, + V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER, + V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP, + V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS, + 0 +}; +EXPORT_SYMBOL(cx2341x_mpeg_ctrls); + +static const struct cx2341x_mpeg_params default_params = { + /* misc */ + .capabilities = 0, + .port = CX2341X_PORT_MEMORY, + .width = 720, + .height = 480, + .is_50hz = 0, + + /* stream */ + .stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS, + .stream_vbi_fmt = V4L2_MPEG_STREAM_VBI_FMT_NONE, + .stream_insert_nav_packets = 0, + + /* audio */ + .audio_sampling_freq = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, + .audio_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + .audio_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_224K, + .audio_ac3_bitrate = V4L2_MPEG_AUDIO_AC3_BITRATE_224K, + .audio_mode = V4L2_MPEG_AUDIO_MODE_STEREO, + .audio_mode_extension = V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, + .audio_emphasis = V4L2_MPEG_AUDIO_EMPHASIS_NONE, + .audio_crc = V4L2_MPEG_AUDIO_CRC_NONE, + .audio_mute = 0, + + /* video */ + .video_encoding = V4L2_MPEG_VIDEO_ENCODING_MPEG_2, + .video_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3, + .video_b_frames = 2, + .video_gop_size = 12, + .video_gop_closure = 1, + .video_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + .video_bitrate = 6000000, + .video_bitrate_peak = 8000000, + .video_temporal_decimation = 0, + .video_mute = 0, + .video_mute_yuv = 0x008080, /* YCbCr value for black */ + + /* encoding filters */ + .video_spatial_filter_mode = + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, + .video_spatial_filter = 0, + .video_luma_spatial_filter_type = + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR, + .video_chroma_spatial_filter_type = + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, + .video_temporal_filter_mode = + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, + .video_temporal_filter = 8, + .video_median_filter_type = + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, + .video_luma_median_filter_top = 255, + .video_luma_median_filter_bottom = 0, + .video_chroma_median_filter_top = 255, + .video_chroma_median_filter_bottom = 0, +}; +/* Map the control ID to the correct field in the cx2341x_mpeg_params + struct. Return -EINVAL if the ID is unknown, else return 0. */ +static int cx2341x_get_ctrl(const struct cx2341x_mpeg_params *params, + struct v4l2_ext_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + ctrl->value = params->audio_sampling_freq; + break; + case V4L2_CID_MPEG_AUDIO_ENCODING: + ctrl->value = params->audio_encoding; + break; + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + ctrl->value = params->audio_l2_bitrate; + break; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + ctrl->value = params->audio_ac3_bitrate; + break; + case V4L2_CID_MPEG_AUDIO_MODE: + ctrl->value = params->audio_mode; + break; + case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: + ctrl->value = params->audio_mode_extension; + break; + case V4L2_CID_MPEG_AUDIO_EMPHASIS: + ctrl->value = params->audio_emphasis; + break; + case V4L2_CID_MPEG_AUDIO_CRC: + ctrl->value = params->audio_crc; + break; + case V4L2_CID_MPEG_AUDIO_MUTE: + ctrl->value = params->audio_mute; + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + ctrl->value = params->video_encoding; + break; + case V4L2_CID_MPEG_VIDEO_ASPECT: + ctrl->value = params->video_aspect; + break; + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + ctrl->value = params->video_b_frames; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + ctrl->value = params->video_gop_size; + break; + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + ctrl->value = params->video_gop_closure; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + ctrl->value = params->video_bitrate_mode; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + ctrl->value = params->video_bitrate; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + ctrl->value = params->video_bitrate_peak; + break; + case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: + ctrl->value = params->video_temporal_decimation; + break; + case V4L2_CID_MPEG_VIDEO_MUTE: + ctrl->value = params->video_mute; + break; + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: + ctrl->value = params->video_mute_yuv; + break; + case V4L2_CID_MPEG_STREAM_TYPE: + ctrl->value = params->stream_type; + break; + case V4L2_CID_MPEG_STREAM_VBI_FMT: + ctrl->value = params->stream_vbi_fmt; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + ctrl->value = params->video_spatial_filter_mode; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + ctrl->value = params->video_spatial_filter; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + ctrl->value = params->video_luma_spatial_filter_type; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + ctrl->value = params->video_chroma_spatial_filter_type; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + ctrl->value = params->video_temporal_filter_mode; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + ctrl->value = params->video_temporal_filter; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + ctrl->value = params->video_median_filter_type; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + ctrl->value = params->video_luma_median_filter_top; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + ctrl->value = params->video_luma_median_filter_bottom; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + ctrl->value = params->video_chroma_median_filter_top; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + ctrl->value = params->video_chroma_median_filter_bottom; + break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + ctrl->value = params->stream_insert_nav_packets; + break; + default: + return -EINVAL; + } + return 0; +} + +/* Map the control ID to the correct field in the cx2341x_mpeg_params + struct. Return -EINVAL if the ID is unknown, else return 0. */ +static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, int busy, + struct v4l2_ext_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + if (busy) + return -EBUSY; + params->audio_sampling_freq = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_ENCODING: + if (busy) + return -EBUSY; + if (params->capabilities & CX2341X_CAP_HAS_AC3) + if (ctrl->value != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 && + ctrl->value != V4L2_MPEG_AUDIO_ENCODING_AC3) + return -ERANGE; + params->audio_encoding = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + if (busy) + return -EBUSY; + params->audio_l2_bitrate = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + if (busy) + return -EBUSY; + if (!(params->capabilities & CX2341X_CAP_HAS_AC3)) + return -EINVAL; + params->audio_ac3_bitrate = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_MODE: + params->audio_mode = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: + params->audio_mode_extension = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_EMPHASIS: + params->audio_emphasis = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_CRC: + params->audio_crc = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_MUTE: + params->audio_mute = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_ASPECT: + params->video_aspect = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_B_FRAMES: { + int b = ctrl->value + 1; + int gop = params->video_gop_size; + params->video_b_frames = ctrl->value; + params->video_gop_size = b * ((gop + b - 1) / b); + /* Max GOP size = 34 */ + while (params->video_gop_size > 34) + params->video_gop_size -= b; + break; + } + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: { + int b = params->video_b_frames + 1; + int gop = ctrl->value; + params->video_gop_size = b * ((gop + b - 1) / b); + /* Max GOP size = 34 */ + while (params->video_gop_size > 34) + params->video_gop_size -= b; + ctrl->value = params->video_gop_size; + break; + } + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + params->video_gop_closure = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + if (busy) + return -EBUSY; + /* MPEG-1 only allows CBR */ + if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1 && + ctrl->value != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) + return -EINVAL; + params->video_bitrate_mode = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + if (busy) + return -EBUSY; + params->video_bitrate = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + if (busy) + return -EBUSY; + params->video_bitrate_peak = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: + params->video_temporal_decimation = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MUTE: + params->video_mute = (ctrl->value != 0); + break; + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: + params->video_mute_yuv = ctrl->value; + break; + case V4L2_CID_MPEG_STREAM_TYPE: + if (busy) + return -EBUSY; + params->stream_type = ctrl->value; + params->video_encoding = + (params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_SS || + params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ? + V4L2_MPEG_VIDEO_ENCODING_MPEG_1 : + V4L2_MPEG_VIDEO_ENCODING_MPEG_2; + if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) + /* MPEG-1 implies CBR */ + params->video_bitrate_mode = + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; + break; + case V4L2_CID_MPEG_STREAM_VBI_FMT: + params->stream_vbi_fmt = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + params->video_spatial_filter_mode = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + params->video_spatial_filter = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + params->video_luma_spatial_filter_type = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + params->video_chroma_spatial_filter_type = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + params->video_temporal_filter_mode = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + params->video_temporal_filter = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + params->video_median_filter_type = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + params->video_luma_median_filter_top = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + params->video_luma_median_filter_bottom = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + params->video_chroma_median_filter_top = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + params->video_chroma_median_filter_bottom = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + params->stream_insert_nav_packets = ctrl->value; + break; + default: + return -EINVAL; + } + return 0; +} + +static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl, + s32 min, s32 max, s32 step, s32 def) +{ + const char *name; + + switch (qctrl->id) { + /* MPEG controls */ + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + cx2341x_ctrl_fill(qctrl->id, &name, &qctrl->type, + &min, &max, &step, &def, &qctrl->flags); + qctrl->minimum = min; + qctrl->maximum = max; + qctrl->step = step; + qctrl->default_value = def; + qctrl->reserved[0] = qctrl->reserved[1] = 0; + strlcpy(qctrl->name, name, sizeof(qctrl->name)); + return 0; + + default: + return v4l2_ctrl_query_fill(qctrl, min, max, step, def); + } +} + +int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, + struct v4l2_queryctrl *qctrl) +{ + int err; + + switch (qctrl->id) { + case V4L2_CID_MPEG_CLASS: + return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); + case V4L2_CID_MPEG_STREAM_TYPE: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_STREAM_TYPE_MPEG2_PS, + V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, 1, + V4L2_MPEG_STREAM_TYPE_MPEG2_PS); + + case V4L2_CID_MPEG_STREAM_VBI_FMT: + if (params->capabilities & CX2341X_CAP_HAS_SLICED_VBI) + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_STREAM_VBI_FMT_NONE, + V4L2_MPEG_STREAM_VBI_FMT_IVTV, 1, + V4L2_MPEG_STREAM_VBI_FMT_NONE); + return cx2341x_ctrl_query_fill(qctrl, + V4L2_MPEG_STREAM_VBI_FMT_NONE, + V4L2_MPEG_STREAM_VBI_FMT_NONE, 1, + default_params.stream_vbi_fmt); + + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 1, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); + + case V4L2_CID_MPEG_AUDIO_ENCODING: + if (params->capabilities & CX2341X_CAP_HAS_AC3) { + /* + * The state of L2 & AC3 bitrate controls can change + * when this control changes, but v4l2_ctrl_query_fill() + * already sets V4L2_CTRL_FLAG_UPDATE for + * V4L2_CID_MPEG_AUDIO_ENCODING, so we don't here. + */ + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + V4L2_MPEG_AUDIO_ENCODING_AC3, 1, + default_params.audio_encoding); + } + + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, 1, + default_params.audio_encoding); + + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_L2_BITRATE_192K, + V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1, + default_params.audio_l2_bitrate); + if (err) + return err; + if (params->capabilities & CX2341X_CAP_HAS_AC3 && + params->audio_encoding != V4L2_MPEG_AUDIO_ENCODING_LAYER_2) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_AUDIO_MODE: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_MODE_STEREO, + V4L2_MPEG_AUDIO_MODE_MONO, 1, + V4L2_MPEG_AUDIO_MODE_STEREO); + + case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 1, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4); + if (err == 0 && + params->audio_mode != V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return err; + + case V4L2_CID_MPEG_AUDIO_EMPHASIS: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_EMPHASIS_NONE, + V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 1, + V4L2_MPEG_AUDIO_EMPHASIS_NONE); + + case V4L2_CID_MPEG_AUDIO_CRC: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_CRC_NONE, + V4L2_MPEG_AUDIO_CRC_CRC16, 1, + V4L2_MPEG_AUDIO_CRC_NONE); + + case V4L2_CID_MPEG_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); + + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_AC3_BITRATE_48K, + V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 1, + default_params.audio_ac3_bitrate); + if (err) + return err; + if (params->capabilities & CX2341X_CAP_HAS_AC3) { + if (params->audio_encoding != + V4L2_MPEG_AUDIO_ENCODING_AC3) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + } else + qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + return 0; + + case V4L2_CID_MPEG_VIDEO_ENCODING: + /* this setting is read-only for the cx2341x since the + V4L2_CID_MPEG_STREAM_TYPE really determines the + MPEG-1/2 setting */ + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_ENCODING_MPEG_1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2); + if (err == 0) + qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + return err; + + case V4L2_CID_MPEG_VIDEO_ASPECT: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_ASPECT_1x1, + V4L2_MPEG_VIDEO_ASPECT_221x100, 1, + V4L2_MPEG_VIDEO_ASPECT_4x3); + + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + return v4l2_ctrl_query_fill(qctrl, 0, 33, 1, 2); + + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + return v4l2_ctrl_query_fill(qctrl, 1, 34, 1, + params->is_50hz ? 12 : 15); + + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 1); + + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); + if (err == 0 && + params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return err; + + case V4L2_CID_MPEG_VIDEO_BITRATE: + return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000); + + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + err = v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000); + if (err == 0 && + params->video_bitrate_mode == + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return err; + + case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: + return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0); + + case V4L2_CID_MPEG_VIDEO_MUTE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); + + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: /* Init YUV (really YCbCr) to black */ + return v4l2_ctrl_query_fill(qctrl, 0, 0xffffff, 1, 0x008080); + + /* CX23415/6 specific */ + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + return cx2341x_ctrl_query_fill(qctrl, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 1, + default_params.video_spatial_filter_mode); + + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + cx2341x_ctrl_query_fill(qctrl, 0, 15, 1, + default_params.video_spatial_filter); + qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; + if (params->video_spatial_filter_mode == + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + cx2341x_ctrl_query_fill(qctrl, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE, + 1, + default_params.video_luma_spatial_filter_type); + if (params->video_spatial_filter_mode == + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + cx2341x_ctrl_query_fill(qctrl, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, + 1, + default_params.video_chroma_spatial_filter_type); + if (params->video_spatial_filter_mode == + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + return cx2341x_ctrl_query_fill(qctrl, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, 1, + default_params.video_temporal_filter_mode); + + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + cx2341x_ctrl_query_fill(qctrl, 0, 31, 1, + default_params.video_temporal_filter); + qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; + if (params->video_temporal_filter_mode == + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + return cx2341x_ctrl_query_fill(qctrl, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, 1, + default_params.video_median_filter_type); + + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, + default_params.video_luma_median_filter_top); + qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; + if (params->video_median_filter_type == + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, + default_params.video_luma_median_filter_bottom); + qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; + if (params->video_median_filter_type == + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, + default_params.video_chroma_median_filter_top); + qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; + if (params->video_median_filter_type == + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, + default_params.video_chroma_median_filter_bottom); + qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; + if (params->video_median_filter_type == + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + return cx2341x_ctrl_query_fill(qctrl, 0, 1, 1, + default_params.stream_insert_nav_packets); + + default: + return -EINVAL; + + } +} +EXPORT_SYMBOL(cx2341x_ctrl_query); + +const char * const *cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id) +{ + static const char * const mpeg_stream_type_without_ts[] = { + "MPEG-2 Program Stream", + "", + "MPEG-1 System Stream", + "MPEG-2 DVD-compatible Stream", + "MPEG-1 VCD-compatible Stream", + "MPEG-2 SVCD-compatible Stream", + NULL + }; + + static const char *mpeg_stream_type_with_ts[] = { + "MPEG-2 Program Stream", + "MPEG-2 Transport Stream", + "MPEG-1 System Stream", + "MPEG-2 DVD-compatible Stream", + "MPEG-1 VCD-compatible Stream", + "MPEG-2 SVCD-compatible Stream", + NULL + }; + + static const char *mpeg_audio_encoding_l2_ac3[] = { + "", + "MPEG-1/2 Layer II", + "", + "", + "AC-3", + NULL + }; + + switch (id) { + case V4L2_CID_MPEG_STREAM_TYPE: + return (p->capabilities & CX2341X_CAP_HAS_TS) ? + mpeg_stream_type_with_ts : mpeg_stream_type_without_ts; + case V4L2_CID_MPEG_AUDIO_ENCODING: + return (p->capabilities & CX2341X_CAP_HAS_AC3) ? + mpeg_audio_encoding_l2_ac3 : v4l2_ctrl_get_menu(id); + case V4L2_CID_MPEG_AUDIO_L1_BITRATE: + case V4L2_CID_MPEG_AUDIO_L3_BITRATE: + return NULL; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + return cx2341x_get_menu(id); + default: + return v4l2_ctrl_get_menu(id); + } +} +EXPORT_SYMBOL(cx2341x_ctrl_get_menu); + +static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params) +{ + params->audio_properties = + (params->audio_sampling_freq << 0) | + (params->audio_mode << 8) | + (params->audio_mode_extension << 10) | + (((params->audio_emphasis == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17) + ? 3 : params->audio_emphasis) << 12) | + (params->audio_crc << 14); + + if ((params->capabilities & CX2341X_CAP_HAS_AC3) && + params->audio_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) { + params->audio_properties |= + /* Not sure if this MPEG Layer II setting is required */ + ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) | + (params->audio_ac3_bitrate << 4) | + (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28); + } else { + /* Assuming MPEG Layer II */ + params->audio_properties |= + ((3 - params->audio_encoding) << 2) | + ((1 + params->audio_l2_bitrate) << 4); + } +} + +int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, int busy, + struct v4l2_ext_controls *ctrls, unsigned int cmd) +{ + int err = 0; + int i; + + if (cmd == VIDIOC_G_EXT_CTRLS) { + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = ctrls->controls + i; + + err = cx2341x_get_ctrl(params, ctrl); + if (err) { + ctrls->error_idx = i; + break; + } + } + return err; + } + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = ctrls->controls + i; + struct v4l2_queryctrl qctrl; + const char * const *menu_items = NULL; + + qctrl.id = ctrl->id; + err = cx2341x_ctrl_query(params, &qctrl); + if (err) + break; + if (qctrl.type == V4L2_CTRL_TYPE_MENU) + menu_items = cx2341x_ctrl_get_menu(params, qctrl.id); + err = v4l2_ctrl_check(ctrl, &qctrl, menu_items); + if (err) + break; + err = cx2341x_set_ctrl(params, busy, ctrl); + if (err) + break; + } + if (err == 0 && + params->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && + params->video_bitrate_peak < params->video_bitrate) { + err = -ERANGE; + ctrls->error_idx = ctrls->count; + } + if (err) + ctrls->error_idx = i; + else + cx2341x_calc_audio_properties(params); + return err; +} +EXPORT_SYMBOL(cx2341x_ext_ctrls); + +void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p) +{ + *p = default_params; + cx2341x_calc_audio_properties(p); +} +EXPORT_SYMBOL(cx2341x_fill_defaults); + +static int cx2341x_api(void *priv, cx2341x_mbox_func func, + u32 cmd, int args, ...) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + va_list vargs; + int i; + + va_start(vargs, args); + + for (i = 0; i < args; i++) + data[i] = va_arg(vargs, int); + va_end(vargs); + return func(priv, cmd, args, 0, data); +} + +#define NEQ(field) (old->field != new->field) + +int cx2341x_update(void *priv, cx2341x_mbox_func func, + const struct cx2341x_mpeg_params *old, + const struct cx2341x_mpeg_params *new) +{ + static int mpeg_stream_type[] = { + 0, /* MPEG-2 PS */ + 1, /* MPEG-2 TS */ + 2, /* MPEG-1 SS */ + 14, /* DVD */ + 11, /* VCD */ + 12, /* SVCD */ + }; + + int err = 0; + int force = (old == NULL); + u16 temporal = new->video_temporal_filter; + + cx2341x_api(priv, func, CX2341X_ENC_SET_OUTPUT_PORT, 2, new->port, 0); + + if (force || NEQ(is_50hz)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_RATE, 1, + new->is_50hz); + if (err) return err; + } + + if (force || NEQ(width) || NEQ(height) || NEQ(video_encoding)) { + u16 w = new->width; + u16 h = new->height; + + if (new->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) { + w /= 2; + h /= 2; + } + err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_SIZE, 2, + h, w); + if (err) return err; + } + if (force || NEQ(stream_type)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_STREAM_TYPE, 1, + mpeg_stream_type[new->stream_type]); + if (err) return err; + } + if (force || NEQ(video_aspect)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_ASPECT_RATIO, 1, + 1 + new->video_aspect); + if (err) return err; + } + if (force || NEQ(video_b_frames) || NEQ(video_gop_size)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_PROPERTIES, 2, + new->video_gop_size, new->video_b_frames + 1); + if (err) return err; + } + if (force || NEQ(video_gop_closure)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_CLOSURE, 1, + new->video_gop_closure); + if (err) return err; + } + if (force || NEQ(audio_properties)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_AUDIO_PROPERTIES, + 1, new->audio_properties); + if (err) return err; + } + if (force || NEQ(audio_mute)) { + err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_AUDIO, 1, + new->audio_mute); + if (err) return err; + } + if (force || NEQ(video_bitrate_mode) || NEQ(video_bitrate) || + NEQ(video_bitrate_peak)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_BIT_RATE, 5, + new->video_bitrate_mode, new->video_bitrate, + new->video_bitrate_peak / 400, 0, 0); + if (err) return err; + } + if (force || NEQ(video_spatial_filter_mode) || + NEQ(video_temporal_filter_mode) || + NEQ(video_median_filter_type)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_MODE, + 2, new->video_spatial_filter_mode | + (new->video_temporal_filter_mode << 1), + new->video_median_filter_type); + if (err) return err; + } + if (force || NEQ(video_luma_median_filter_bottom) || + NEQ(video_luma_median_filter_top) || + NEQ(video_chroma_median_filter_bottom) || + NEQ(video_chroma_median_filter_top)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_CORING_LEVELS, 4, + new->video_luma_median_filter_bottom, + new->video_luma_median_filter_top, + new->video_chroma_median_filter_bottom, + new->video_chroma_median_filter_top); + if (err) return err; + } + if (force || NEQ(video_luma_spatial_filter_type) || + NEQ(video_chroma_spatial_filter_type)) { + err = cx2341x_api(priv, func, + CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, + 2, new->video_luma_spatial_filter_type, + new->video_chroma_spatial_filter_type); + if (err) return err; + } + if (force || NEQ(video_spatial_filter) || + old->video_temporal_filter != temporal) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_PROPS, + 2, new->video_spatial_filter, temporal); + if (err) return err; + } + if (force || NEQ(video_temporal_decimation)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_DROP_RATE, + 1, new->video_temporal_decimation); + if (err) return err; + } + if (force || NEQ(video_mute) || + (new->video_mute && NEQ(video_mute_yuv))) { + err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_VIDEO, 1, + new->video_mute | (new->video_mute_yuv << 8)); + if (err) return err; + } + if (force || NEQ(stream_insert_nav_packets)) { + err = cx2341x_api(priv, func, CX2341X_ENC_MISC, 2, + 7, new->stream_insert_nav_packets); + if (err) return err; + } + return 0; +} +EXPORT_SYMBOL(cx2341x_update); + +static const char *cx2341x_menu_item(const struct cx2341x_mpeg_params *p, u32 id) +{ + const char * const *menu = cx2341x_ctrl_get_menu(p, id); + struct v4l2_ext_control ctrl; + + if (menu == NULL) + goto invalid; + ctrl.id = id; + if (cx2341x_get_ctrl(p, &ctrl)) + goto invalid; + while (ctrl.value-- && *menu) menu++; + if (*menu == NULL) + goto invalid; + return *menu; + +invalid: + return ""; +} + +void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix) +{ + int is_mpeg1 = p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1; + + /* Stream */ + printk(KERN_INFO "%s: Stream: %s", + prefix, + cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_TYPE)); + if (p->stream_insert_nav_packets) + printk(" (with navigation packets)"); + printk("\n"); + printk(KERN_INFO "%s: VBI Format: %s\n", + prefix, + cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_VBI_FMT)); + + /* Video */ + printk(KERN_INFO "%s: Video: %dx%d, %d fps%s\n", + prefix, + p->width / (is_mpeg1 ? 2 : 1), p->height / (is_mpeg1 ? 2 : 1), + p->is_50hz ? 25 : 30, + (p->video_mute) ? " (muted)" : ""); + printk(KERN_INFO "%s: Video: %s, %s, %s, %d", + prefix, + cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ENCODING), + cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ASPECT), + cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_BITRATE_MODE), + p->video_bitrate); + if (p->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) + printk(", Peak %d", p->video_bitrate_peak); + printk("\n"); + printk(KERN_INFO + "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure\n", + prefix, + p->video_gop_size, p->video_b_frames, + p->video_gop_closure ? "" : "No "); + if (p->video_temporal_decimation) + printk(KERN_INFO "%s: Video: Temporal Decimation %d\n", + prefix, p->video_temporal_decimation); + + /* Audio */ + printk(KERN_INFO "%s: Audio: %s, %s, %s, %s%s", + prefix, + cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ), + cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_ENCODING), + cx2341x_menu_item(p, + p->audio_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3 + ? V4L2_CID_MPEG_AUDIO_AC3_BITRATE + : V4L2_CID_MPEG_AUDIO_L2_BITRATE), + cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE), + p->audio_mute ? " (muted)" : ""); + if (p->audio_mode == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) + printk(", %s", cx2341x_menu_item(p, + V4L2_CID_MPEG_AUDIO_MODE_EXTENSION)); + printk(", %s, %s\n", + cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_EMPHASIS), + cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_CRC)); + + /* Encoding filters */ + printk(KERN_INFO "%s: Spatial Filter: %s, Luma %s, Chroma %s, %d\n", + prefix, + cx2341x_menu_item(p, + V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE), + cx2341x_menu_item(p, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE), + cx2341x_menu_item(p, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE), + p->video_spatial_filter); + + printk(KERN_INFO "%s: Temporal Filter: %s, %d\n", + prefix, + cx2341x_menu_item(p, + V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE), + p->video_temporal_filter); + printk(KERN_INFO + "%s: Median Filter: %s, Luma [%d, %d], Chroma [%d, %d]\n", + prefix, + cx2341x_menu_item(p, + V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE), + p->video_luma_median_filter_bottom, + p->video_luma_median_filter_top, + p->video_chroma_median_filter_bottom, + p->video_chroma_median_filter_top); +} +EXPORT_SYMBOL(cx2341x_log_status); + + + +/********************** NEW CODE *********************/ + +static inline struct cx2341x_handler *to_cxhdl(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct cx2341x_handler, hdl); +} + +static int cx2341x_hdl_api(struct cx2341x_handler *hdl, + u32 cmd, int args, ...) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + va_list vargs; + int i; + + va_start(vargs, args); + + for (i = 0; i < args; i++) + data[i] = va_arg(vargs, int); + va_end(vargs); + return hdl->func(hdl->priv, cmd, args, 0, data); +} + +/* ctrl->handler->lock is held, so it is safe to access cur.val */ +static inline int cx2341x_neq(struct v4l2_ctrl *ctrl) +{ + return ctrl && ctrl->val != ctrl->cur.val; +} + +static int cx2341x_try_ctrl(struct v4l2_ctrl *ctrl) +{ + struct cx2341x_handler *hdl = to_cxhdl(ctrl); + s32 val = ctrl->val; + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_B_FRAMES: { + /* video gop cluster */ + int b = val + 1; + int gop = hdl->video_gop_size->val; + + gop = b * ((gop + b - 1) / b); + + /* Max GOP size = 34 */ + while (gop > 34) + gop -= b; + hdl->video_gop_size->val = gop; + break; + } + + case V4L2_CID_MPEG_STREAM_TYPE: + /* stream type cluster */ + hdl->video_encoding->val = + (hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_SS || + hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ? + V4L2_MPEG_VIDEO_ENCODING_MPEG_1 : + V4L2_MPEG_VIDEO_ENCODING_MPEG_2; + if (hdl->video_encoding->val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) + /* MPEG-1 implies CBR */ + hdl->video_bitrate_mode->val = + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; + /* peak bitrate shall be >= normal bitrate */ + if (hdl->video_bitrate_mode->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && + hdl->video_bitrate_peak->val < hdl->video_bitrate->val) + hdl->video_bitrate_peak->val = hdl->video_bitrate->val; + break; + } + return 0; +} + +static int cx2341x_s_ctrl(struct v4l2_ctrl *ctrl) +{ + static const int mpeg_stream_type[] = { + 0, /* MPEG-2 PS */ + 1, /* MPEG-2 TS */ + 2, /* MPEG-1 SS */ + 14, /* DVD */ + 11, /* VCD */ + 12, /* SVCD */ + }; + struct cx2341x_handler *hdl = to_cxhdl(ctrl); + s32 val = ctrl->val; + u32 props; + int err; + + switch (ctrl->id) { + case V4L2_CID_MPEG_STREAM_VBI_FMT: + if (hdl->ops && hdl->ops->s_stream_vbi_fmt) + return hdl->ops->s_stream_vbi_fmt(hdl, val); + return 0; + + case V4L2_CID_MPEG_VIDEO_ASPECT: + return cx2341x_hdl_api(hdl, + CX2341X_ENC_SET_ASPECT_RATIO, 1, val + 1); + + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_CLOSURE, 1, val); + + case V4L2_CID_MPEG_AUDIO_MUTE: + return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_AUDIO, 1, val); + + case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: + return cx2341x_hdl_api(hdl, + CX2341X_ENC_SET_FRAME_DROP_RATE, 1, val); + + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + return cx2341x_hdl_api(hdl, CX2341X_ENC_MISC, 2, 7, val); + + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + /* audio properties cluster */ + props = (hdl->audio_sampling_freq->val << 0) | + (hdl->audio_mode->val << 8) | + (hdl->audio_mode_extension->val << 10) | + (hdl->audio_crc->val << 14); + if (hdl->audio_emphasis->val == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17) + props |= 3 << 12; + else + props |= hdl->audio_emphasis->val << 12; + + if (hdl->audio_encoding->val == V4L2_MPEG_AUDIO_ENCODING_AC3) { + props |= +#if 1 + /* Not sure if this MPEG Layer II setting is required */ + ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) | +#endif + (hdl->audio_ac3_bitrate->val << 4) | + (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28); + } else { + /* Assuming MPEG Layer II */ + props |= + ((3 - hdl->audio_encoding->val) << 2) | + ((1 + hdl->audio_l2_bitrate->val) << 4); + } + err = cx2341x_hdl_api(hdl, + CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, props); + if (err) + return err; + + hdl->audio_properties = props; + if (hdl->audio_ac3_bitrate) { + int is_ac3 = hdl->audio_encoding->val == + V4L2_MPEG_AUDIO_ENCODING_AC3; + + v4l2_ctrl_activate(hdl->audio_ac3_bitrate, is_ac3); + v4l2_ctrl_activate(hdl->audio_l2_bitrate, !is_ac3); + } + v4l2_ctrl_activate(hdl->audio_mode_extension, + hdl->audio_mode->val == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO); + if (cx2341x_neq(hdl->audio_sampling_freq) && + hdl->ops && hdl->ops->s_audio_sampling_freq) + return hdl->ops->s_audio_sampling_freq(hdl, hdl->audio_sampling_freq->val); + if (cx2341x_neq(hdl->audio_mode) && + hdl->ops && hdl->ops->s_audio_mode) + return hdl->ops->s_audio_mode(hdl, hdl->audio_mode->val); + return 0; + + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + /* video gop cluster */ + return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_PROPERTIES, 2, + hdl->video_gop_size->val, + hdl->video_b_frames->val + 1); + + case V4L2_CID_MPEG_STREAM_TYPE: + /* stream type cluster */ + err = cx2341x_hdl_api(hdl, + CX2341X_ENC_SET_STREAM_TYPE, 1, mpeg_stream_type[val]); + if (err) + return err; + + err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_BIT_RATE, 5, + hdl->video_bitrate_mode->val, + hdl->video_bitrate->val, + hdl->video_bitrate_peak->val / 400, 0, 0); + if (err) + return err; + + v4l2_ctrl_activate(hdl->video_bitrate_mode, + hdl->video_encoding->val != V4L2_MPEG_VIDEO_ENCODING_MPEG_1); + v4l2_ctrl_activate(hdl->video_bitrate_peak, + hdl->video_bitrate_mode->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); + if (cx2341x_neq(hdl->video_encoding) && + hdl->ops && hdl->ops->s_video_encoding) + return hdl->ops->s_video_encoding(hdl, hdl->video_encoding->val); + return 0; + + case V4L2_CID_MPEG_VIDEO_MUTE: + /* video mute cluster */ + return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_VIDEO, 1, + hdl->video_mute->val | + (hdl->video_mute_yuv->val << 8)); + + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: { + int active_filter; + + /* video filter mode */ + err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_MODE, 2, + hdl->video_spatial_filter_mode->val | + (hdl->video_temporal_filter_mode->val << 1), + hdl->video_median_filter_type->val); + if (err) + return err; + + active_filter = hdl->video_spatial_filter_mode->val != + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO; + v4l2_ctrl_activate(hdl->video_spatial_filter, active_filter); + v4l2_ctrl_activate(hdl->video_luma_spatial_filter_type, active_filter); + v4l2_ctrl_activate(hdl->video_chroma_spatial_filter_type, active_filter); + active_filter = hdl->video_temporal_filter_mode->val != + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO; + v4l2_ctrl_activate(hdl->video_temporal_filter, active_filter); + active_filter = hdl->video_median_filter_type->val != + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF; + v4l2_ctrl_activate(hdl->video_luma_median_filter_bottom, active_filter); + v4l2_ctrl_activate(hdl->video_luma_median_filter_top, active_filter); + v4l2_ctrl_activate(hdl->video_chroma_median_filter_bottom, active_filter); + v4l2_ctrl_activate(hdl->video_chroma_median_filter_top, active_filter); + return 0; + } + + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + /* video filter type cluster */ + return cx2341x_hdl_api(hdl, + CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 2, + hdl->video_luma_spatial_filter_type->val, + hdl->video_chroma_spatial_filter_type->val); + + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + /* video filter cluster */ + return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2, + hdl->video_spatial_filter->val, + hdl->video_temporal_filter->val); + + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + /* video median cluster */ + return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_CORING_LEVELS, 4, + hdl->video_luma_median_filter_bottom->val, + hdl->video_luma_median_filter_top->val, + hdl->video_chroma_median_filter_bottom->val, + hdl->video_chroma_median_filter_top->val); + } + return -EINVAL; +} + +static const struct v4l2_ctrl_ops cx2341x_ops = { + .try_ctrl = cx2341x_try_ctrl, + .s_ctrl = cx2341x_s_ctrl, +}; + +static struct v4l2_ctrl *cx2341x_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, + u32 id, s32 min, s32 max, s32 step, s32 def) +{ + struct v4l2_ctrl_config cfg; + + cx2341x_ctrl_fill(id, &cfg.name, &cfg.type, &min, &max, &step, &def, &cfg.flags); + cfg.ops = &cx2341x_ops; + cfg.id = id; + cfg.min = min; + cfg.max = max; + cfg.def = def; + if (cfg.type == V4L2_CTRL_TYPE_MENU) { + cfg.step = 0; + cfg.menu_skip_mask = step; + cfg.qmenu = cx2341x_get_menu(id); + } else { + cfg.step = step; + cfg.menu_skip_mask = 0; + } + return v4l2_ctrl_new_custom(hdl, &cfg, NULL); +} + +static struct v4l2_ctrl *cx2341x_ctrl_new_std(struct v4l2_ctrl_handler *hdl, + u32 id, s32 min, s32 max, s32 step, s32 def) +{ + return v4l2_ctrl_new_std(hdl, &cx2341x_ops, id, min, max, step, def); +} + +static struct v4l2_ctrl *cx2341x_ctrl_new_menu(struct v4l2_ctrl_handler *hdl, + u32 id, s32 max, s32 mask, s32 def) +{ + return v4l2_ctrl_new_std_menu(hdl, &cx2341x_ops, id, max, mask, def); +} + +int cx2341x_handler_init(struct cx2341x_handler *cxhdl, + unsigned nr_of_controls_hint) +{ + struct v4l2_ctrl_handler *hdl = &cxhdl->hdl; + u32 caps = cxhdl->capabilities; + int has_sliced_vbi = caps & CX2341X_CAP_HAS_SLICED_VBI; + int has_ac3 = caps & CX2341X_CAP_HAS_AC3; + int has_ts = caps & CX2341X_CAP_HAS_TS; + + cxhdl->width = 720; + cxhdl->height = 480; + + v4l2_ctrl_handler_init(hdl, nr_of_controls_hint); + + /* Add controls in ascending control ID order for fastest + insertion time. */ + cxhdl->stream_type = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_STREAM_TYPE, + V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, has_ts ? 0 : 2, + V4L2_MPEG_STREAM_TYPE_MPEG2_PS); + cxhdl->stream_vbi_fmt = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_STREAM_VBI_FMT, + V4L2_MPEG_STREAM_VBI_FMT_IVTV, has_sliced_vbi ? 0 : 2, + V4L2_MPEG_STREAM_VBI_FMT_NONE); + cxhdl->audio_sampling_freq = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 0, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); + cxhdl->audio_encoding = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_ENCODING, + V4L2_MPEG_AUDIO_ENCODING_AC3, has_ac3 ? ~0x12 : ~0x2, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2); + cxhdl->audio_l2_bitrate = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_L2_BITRATE, + V4L2_MPEG_AUDIO_L2_BITRATE_384K, 0x1ff, + V4L2_MPEG_AUDIO_L2_BITRATE_224K); + cxhdl->audio_mode = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_MODE, + V4L2_MPEG_AUDIO_MODE_MONO, 0, + V4L2_MPEG_AUDIO_MODE_STEREO); + cxhdl->audio_mode_extension = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 0, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4); + cxhdl->audio_emphasis = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_EMPHASIS, + V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 0, + V4L2_MPEG_AUDIO_EMPHASIS_NONE); + cxhdl->audio_crc = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_CRC, + V4L2_MPEG_AUDIO_CRC_CRC16, 0, + V4L2_MPEG_AUDIO_CRC_NONE); + + cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_AUDIO_MUTE, 0, 1, 1, 0); + if (has_ac3) + cxhdl->audio_ac3_bitrate = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_AC3_BITRATE, + V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 0x03, + V4L2_MPEG_AUDIO_AC3_BITRATE_224K); + cxhdl->video_encoding = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_VIDEO_ENCODING, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 0, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2); + cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_VIDEO_ASPECT, + V4L2_MPEG_VIDEO_ASPECT_221x100, 0, + V4L2_MPEG_VIDEO_ASPECT_4x3); + cxhdl->video_b_frames = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 33, 1, 2); + cxhdl->video_gop_size = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + 1, 34, 1, cxhdl->is_50hz ? 12 : 15); + cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, 0, 1, 1, 1); + cxhdl->video_bitrate_mode = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); + cxhdl->video_bitrate = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_BITRATE, + 0, 27000000, 1, 6000000); + cxhdl->video_bitrate_peak = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + 0, 27000000, 1, 8000000); + cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, 0, 255, 1, 0); + cxhdl->video_mute = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_MUTE, 0, 1, 1, 0); + cxhdl->video_mute_yuv = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_MUTE_YUV, 0, 0xffffff, 1, 0x008080); + + /* CX23415/6 specific */ + cxhdl->video_spatial_filter_mode = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 0, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL); + cxhdl->video_spatial_filter = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER, + 0, 15, 1, 0); + cxhdl->video_luma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE, + 0, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR); + cxhdl->video_chroma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, + 0, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR); + cxhdl->video_temporal_filter_mode = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, + 0, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL); + cxhdl->video_temporal_filter = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER, + 0, 31, 1, 8); + cxhdl->video_median_filter_type = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, + 0, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF); + cxhdl->video_luma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM, + 0, 255, 1, 0); + cxhdl->video_luma_median_filter_top = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP, + 0, 255, 1, 255); + cxhdl->video_chroma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, + 0, 255, 1, 0); + cxhdl->video_chroma_median_filter_top = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP, + 0, 255, 1, 255); + cx2341x_ctrl_new_custom(hdl, V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS, + 0, 1, 1, 0); + + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + return err; + } + + v4l2_ctrl_cluster(8, &cxhdl->audio_sampling_freq); + v4l2_ctrl_cluster(2, &cxhdl->video_b_frames); + v4l2_ctrl_cluster(5, &cxhdl->stream_type); + v4l2_ctrl_cluster(2, &cxhdl->video_mute); + v4l2_ctrl_cluster(3, &cxhdl->video_spatial_filter_mode); + v4l2_ctrl_cluster(2, &cxhdl->video_luma_spatial_filter_type); + v4l2_ctrl_cluster(2, &cxhdl->video_spatial_filter); + v4l2_ctrl_cluster(4, &cxhdl->video_luma_median_filter_top); + + return 0; +} +EXPORT_SYMBOL(cx2341x_handler_init); + +void cx2341x_handler_set_50hz(struct cx2341x_handler *cxhdl, int is_50hz) +{ + cxhdl->is_50hz = is_50hz; + cxhdl->video_gop_size->default_value = cxhdl->is_50hz ? 12 : 15; +} +EXPORT_SYMBOL(cx2341x_handler_set_50hz); + +int cx2341x_handler_setup(struct cx2341x_handler *cxhdl) +{ + int h = cxhdl->height; + int w = cxhdl->width; + int err; + + err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_OUTPUT_PORT, 2, cxhdl->port, 0); + if (err) + return err; + err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_RATE, 1, cxhdl->is_50hz); + if (err) + return err; + + if (v4l2_ctrl_g_ctrl(cxhdl->video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) { + w /= 2; + h /= 2; + } + err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_SIZE, 2, h, w); + if (err) + return err; + return v4l2_ctrl_handler_setup(&cxhdl->hdl); +} +EXPORT_SYMBOL(cx2341x_handler_setup); + +void cx2341x_handler_set_busy(struct cx2341x_handler *cxhdl, int busy) +{ + v4l2_ctrl_grab(cxhdl->audio_sampling_freq, busy); + v4l2_ctrl_grab(cxhdl->audio_encoding, busy); + v4l2_ctrl_grab(cxhdl->audio_l2_bitrate, busy); + v4l2_ctrl_grab(cxhdl->audio_ac3_bitrate, busy); + v4l2_ctrl_grab(cxhdl->stream_vbi_fmt, busy); + v4l2_ctrl_grab(cxhdl->stream_type, busy); + v4l2_ctrl_grab(cxhdl->video_bitrate_mode, busy); + v4l2_ctrl_grab(cxhdl->video_bitrate, busy); + v4l2_ctrl_grab(cxhdl->video_bitrate_peak, busy); +} +EXPORT_SYMBOL(cx2341x_handler_set_busy); diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index ecdf7e3fdcf4..97f14c24a40c 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -317,20 +317,6 @@ config VIDEO_SAA717X source "drivers/media/i2c/cx25840/Kconfig" -comment "MPEG video encoders" - -config VIDEO_CX2341X - tristate "Conexant CX2341x MPEG encoders" - depends on VIDEO_V4L2 - ---help--- - Support for the Conexant CX23416 MPEG encoders - and CX23415 MPEG encoder/decoders. - - This module currently supports the encoding functions only. - - To compile this driver as a module, choose M here: the - module will be called cx2341x. - comment "Video encoders" config VIDEO_SAA7127 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index b35e25798cd3..3a8334f205f3 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -64,6 +64,5 @@ obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o -obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o obj-$(CONFIG_VIDEO_AK881X) += ak881x.o obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o diff --git a/drivers/media/i2c/cx2341x.c b/drivers/media/i2c/cx2341x.c deleted file mode 100644 index 103ef6bad2e2..000000000000 --- a/drivers/media/i2c/cx2341x.c +++ /dev/null @@ -1,1726 +0,0 @@ -/* - * cx2341x - generic code for cx23415/6/8 based devices - * - * Copyright (C) 2006 Hans Verkuil - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -MODULE_DESCRIPTION("cx23415/6/8 driver"); -MODULE_AUTHOR("Hans Verkuil"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -/********************** COMMON CODE *********************/ - -/* definitions for audio properties bits 29-28 */ -#define CX2341X_AUDIO_ENCODING_METHOD_MPEG 0 -#define CX2341X_AUDIO_ENCODING_METHOD_AC3 1 -#define CX2341X_AUDIO_ENCODING_METHOD_LPCM 2 - -static const char *cx2341x_get_name(u32 id) -{ - switch (id) { - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - return "Spatial Filter Mode"; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - return "Spatial Filter"; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - return "Spatial Luma Filter Type"; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - return "Spatial Chroma Filter Type"; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - return "Temporal Filter Mode"; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: - return "Temporal Filter"; - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - return "Median Filter Type"; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - return "Median Luma Filter Maximum"; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: - return "Median Luma Filter Minimum"; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: - return "Median Chroma Filter Maximum"; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: - return "Median Chroma Filter Minimum"; - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - return "Insert Navigation Packets"; - } - return NULL; -} - -static const char **cx2341x_get_menu(u32 id) -{ - static const char *cx2341x_video_spatial_filter_mode_menu[] = { - "Manual", - "Auto", - NULL - }; - - static const char *cx2341x_video_luma_spatial_filter_type_menu[] = { - "Off", - "1D Horizontal", - "1D Vertical", - "2D H/V Separable", - "2D Symmetric non-separable", - NULL - }; - - static const char *cx2341x_video_chroma_spatial_filter_type_menu[] = { - "Off", - "1D Horizontal", - NULL - }; - - static const char *cx2341x_video_temporal_filter_mode_menu[] = { - "Manual", - "Auto", - NULL - }; - - static const char *cx2341x_video_median_filter_type_menu[] = { - "Off", - "Horizontal", - "Vertical", - "Horizontal/Vertical", - "Diagonal", - NULL - }; - - switch (id) { - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - return cx2341x_video_spatial_filter_mode_menu; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - return cx2341x_video_luma_spatial_filter_type_menu; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - return cx2341x_video_chroma_spatial_filter_type_menu; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - return cx2341x_video_temporal_filter_mode_menu; - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - return cx2341x_video_median_filter_type_menu; - } - return NULL; -} - -static void cx2341x_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, - s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags) -{ - *name = cx2341x_get_name(id); - *flags = 0; - - switch (id) { - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - *type = V4L2_CTRL_TYPE_MENU; - *min = 0; - *step = 0; - break; - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - *type = V4L2_CTRL_TYPE_BOOLEAN; - *min = 0; - *max = *step = 1; - break; - default: - *type = V4L2_CTRL_TYPE_INTEGER; - break; - } - switch (id) { - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - *flags |= V4L2_CTRL_FLAG_UPDATE; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: - *flags |= V4L2_CTRL_FLAG_SLIDER; - break; - case V4L2_CID_MPEG_VIDEO_ENCODING: - *flags |= V4L2_CTRL_FLAG_READ_ONLY; - break; - } -} - - -/********************** OLD CODE *********************/ - -/* Must be sorted from low to high control ID! */ -const u32 cx2341x_mpeg_ctrls[] = { - V4L2_CID_MPEG_CLASS, - V4L2_CID_MPEG_STREAM_TYPE, - V4L2_CID_MPEG_STREAM_VBI_FMT, - V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, - V4L2_CID_MPEG_AUDIO_ENCODING, - V4L2_CID_MPEG_AUDIO_L2_BITRATE, - V4L2_CID_MPEG_AUDIO_MODE, - V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, - V4L2_CID_MPEG_AUDIO_EMPHASIS, - V4L2_CID_MPEG_AUDIO_CRC, - V4L2_CID_MPEG_AUDIO_MUTE, - V4L2_CID_MPEG_AUDIO_AC3_BITRATE, - V4L2_CID_MPEG_VIDEO_ENCODING, - V4L2_CID_MPEG_VIDEO_ASPECT, - V4L2_CID_MPEG_VIDEO_B_FRAMES, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, - V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, - V4L2_CID_MPEG_VIDEO_BITRATE_MODE, - V4L2_CID_MPEG_VIDEO_BITRATE, - V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, - V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, - V4L2_CID_MPEG_VIDEO_MUTE, - V4L2_CID_MPEG_VIDEO_MUTE_YUV, - V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE, - V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE, - V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE, - V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER, - V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP, - V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS, - 0 -}; -EXPORT_SYMBOL(cx2341x_mpeg_ctrls); - -static const struct cx2341x_mpeg_params default_params = { - /* misc */ - .capabilities = 0, - .port = CX2341X_PORT_MEMORY, - .width = 720, - .height = 480, - .is_50hz = 0, - - /* stream */ - .stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS, - .stream_vbi_fmt = V4L2_MPEG_STREAM_VBI_FMT_NONE, - .stream_insert_nav_packets = 0, - - /* audio */ - .audio_sampling_freq = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, - .audio_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2, - .audio_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_224K, - .audio_ac3_bitrate = V4L2_MPEG_AUDIO_AC3_BITRATE_224K, - .audio_mode = V4L2_MPEG_AUDIO_MODE_STEREO, - .audio_mode_extension = V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, - .audio_emphasis = V4L2_MPEG_AUDIO_EMPHASIS_NONE, - .audio_crc = V4L2_MPEG_AUDIO_CRC_NONE, - .audio_mute = 0, - - /* video */ - .video_encoding = V4L2_MPEG_VIDEO_ENCODING_MPEG_2, - .video_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3, - .video_b_frames = 2, - .video_gop_size = 12, - .video_gop_closure = 1, - .video_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, - .video_bitrate = 6000000, - .video_bitrate_peak = 8000000, - .video_temporal_decimation = 0, - .video_mute = 0, - .video_mute_yuv = 0x008080, /* YCbCr value for black */ - - /* encoding filters */ - .video_spatial_filter_mode = - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, - .video_spatial_filter = 0, - .video_luma_spatial_filter_type = - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR, - .video_chroma_spatial_filter_type = - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, - .video_temporal_filter_mode = - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, - .video_temporal_filter = 8, - .video_median_filter_type = - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, - .video_luma_median_filter_top = 255, - .video_luma_median_filter_bottom = 0, - .video_chroma_median_filter_top = 255, - .video_chroma_median_filter_bottom = 0, -}; -/* Map the control ID to the correct field in the cx2341x_mpeg_params - struct. Return -EINVAL if the ID is unknown, else return 0. */ -static int cx2341x_get_ctrl(const struct cx2341x_mpeg_params *params, - struct v4l2_ext_control *ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: - ctrl->value = params->audio_sampling_freq; - break; - case V4L2_CID_MPEG_AUDIO_ENCODING: - ctrl->value = params->audio_encoding; - break; - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: - ctrl->value = params->audio_l2_bitrate; - break; - case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: - ctrl->value = params->audio_ac3_bitrate; - break; - case V4L2_CID_MPEG_AUDIO_MODE: - ctrl->value = params->audio_mode; - break; - case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: - ctrl->value = params->audio_mode_extension; - break; - case V4L2_CID_MPEG_AUDIO_EMPHASIS: - ctrl->value = params->audio_emphasis; - break; - case V4L2_CID_MPEG_AUDIO_CRC: - ctrl->value = params->audio_crc; - break; - case V4L2_CID_MPEG_AUDIO_MUTE: - ctrl->value = params->audio_mute; - break; - case V4L2_CID_MPEG_VIDEO_ENCODING: - ctrl->value = params->video_encoding; - break; - case V4L2_CID_MPEG_VIDEO_ASPECT: - ctrl->value = params->video_aspect; - break; - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - ctrl->value = params->video_b_frames; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - ctrl->value = params->video_gop_size; - break; - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - ctrl->value = params->video_gop_closure; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - ctrl->value = params->video_bitrate_mode; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: - ctrl->value = params->video_bitrate; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - ctrl->value = params->video_bitrate_peak; - break; - case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: - ctrl->value = params->video_temporal_decimation; - break; - case V4L2_CID_MPEG_VIDEO_MUTE: - ctrl->value = params->video_mute; - break; - case V4L2_CID_MPEG_VIDEO_MUTE_YUV: - ctrl->value = params->video_mute_yuv; - break; - case V4L2_CID_MPEG_STREAM_TYPE: - ctrl->value = params->stream_type; - break; - case V4L2_CID_MPEG_STREAM_VBI_FMT: - ctrl->value = params->stream_vbi_fmt; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - ctrl->value = params->video_spatial_filter_mode; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - ctrl->value = params->video_spatial_filter; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - ctrl->value = params->video_luma_spatial_filter_type; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - ctrl->value = params->video_chroma_spatial_filter_type; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - ctrl->value = params->video_temporal_filter_mode; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: - ctrl->value = params->video_temporal_filter; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - ctrl->value = params->video_median_filter_type; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - ctrl->value = params->video_luma_median_filter_top; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: - ctrl->value = params->video_luma_median_filter_bottom; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: - ctrl->value = params->video_chroma_median_filter_top; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: - ctrl->value = params->video_chroma_median_filter_bottom; - break; - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - ctrl->value = params->stream_insert_nav_packets; - break; - default: - return -EINVAL; - } - return 0; -} - -/* Map the control ID to the correct field in the cx2341x_mpeg_params - struct. Return -EINVAL if the ID is unknown, else return 0. */ -static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, int busy, - struct v4l2_ext_control *ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: - if (busy) - return -EBUSY; - params->audio_sampling_freq = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_ENCODING: - if (busy) - return -EBUSY; - if (params->capabilities & CX2341X_CAP_HAS_AC3) - if (ctrl->value != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 && - ctrl->value != V4L2_MPEG_AUDIO_ENCODING_AC3) - return -ERANGE; - params->audio_encoding = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: - if (busy) - return -EBUSY; - params->audio_l2_bitrate = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: - if (busy) - return -EBUSY; - if (!(params->capabilities & CX2341X_CAP_HAS_AC3)) - return -EINVAL; - params->audio_ac3_bitrate = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_MODE: - params->audio_mode = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: - params->audio_mode_extension = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_EMPHASIS: - params->audio_emphasis = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_CRC: - params->audio_crc = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_MUTE: - params->audio_mute = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_ASPECT: - params->video_aspect = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_B_FRAMES: { - int b = ctrl->value + 1; - int gop = params->video_gop_size; - params->video_b_frames = ctrl->value; - params->video_gop_size = b * ((gop + b - 1) / b); - /* Max GOP size = 34 */ - while (params->video_gop_size > 34) - params->video_gop_size -= b; - break; - } - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: { - int b = params->video_b_frames + 1; - int gop = ctrl->value; - params->video_gop_size = b * ((gop + b - 1) / b); - /* Max GOP size = 34 */ - while (params->video_gop_size > 34) - params->video_gop_size -= b; - ctrl->value = params->video_gop_size; - break; - } - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - params->video_gop_closure = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - if (busy) - return -EBUSY; - /* MPEG-1 only allows CBR */ - if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1 && - ctrl->value != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) - return -EINVAL; - params->video_bitrate_mode = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: - if (busy) - return -EBUSY; - params->video_bitrate = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - if (busy) - return -EBUSY; - params->video_bitrate_peak = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: - params->video_temporal_decimation = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_MUTE: - params->video_mute = (ctrl->value != 0); - break; - case V4L2_CID_MPEG_VIDEO_MUTE_YUV: - params->video_mute_yuv = ctrl->value; - break; - case V4L2_CID_MPEG_STREAM_TYPE: - if (busy) - return -EBUSY; - params->stream_type = ctrl->value; - params->video_encoding = - (params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_SS || - params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ? - V4L2_MPEG_VIDEO_ENCODING_MPEG_1 : - V4L2_MPEG_VIDEO_ENCODING_MPEG_2; - if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) - /* MPEG-1 implies CBR */ - params->video_bitrate_mode = - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; - break; - case V4L2_CID_MPEG_STREAM_VBI_FMT: - params->stream_vbi_fmt = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - params->video_spatial_filter_mode = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - params->video_spatial_filter = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - params->video_luma_spatial_filter_type = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - params->video_chroma_spatial_filter_type = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - params->video_temporal_filter_mode = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: - params->video_temporal_filter = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - params->video_median_filter_type = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - params->video_luma_median_filter_top = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: - params->video_luma_median_filter_bottom = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: - params->video_chroma_median_filter_top = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: - params->video_chroma_median_filter_bottom = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - params->stream_insert_nav_packets = ctrl->value; - break; - default: - return -EINVAL; - } - return 0; -} - -static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl, - s32 min, s32 max, s32 step, s32 def) -{ - const char *name; - - switch (qctrl->id) { - /* MPEG controls */ - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - cx2341x_ctrl_fill(qctrl->id, &name, &qctrl->type, - &min, &max, &step, &def, &qctrl->flags); - qctrl->minimum = min; - qctrl->maximum = max; - qctrl->step = step; - qctrl->default_value = def; - qctrl->reserved[0] = qctrl->reserved[1] = 0; - strlcpy(qctrl->name, name, sizeof(qctrl->name)); - return 0; - - default: - return v4l2_ctrl_query_fill(qctrl, min, max, step, def); - } -} - -int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, - struct v4l2_queryctrl *qctrl) -{ - int err; - - switch (qctrl->id) { - case V4L2_CID_MPEG_CLASS: - return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); - case V4L2_CID_MPEG_STREAM_TYPE: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_STREAM_TYPE_MPEG2_PS, - V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, 1, - V4L2_MPEG_STREAM_TYPE_MPEG2_PS); - - case V4L2_CID_MPEG_STREAM_VBI_FMT: - if (params->capabilities & CX2341X_CAP_HAS_SLICED_VBI) - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_STREAM_VBI_FMT_NONE, - V4L2_MPEG_STREAM_VBI_FMT_IVTV, 1, - V4L2_MPEG_STREAM_VBI_FMT_NONE); - return cx2341x_ctrl_query_fill(qctrl, - V4L2_MPEG_STREAM_VBI_FMT_NONE, - V4L2_MPEG_STREAM_VBI_FMT_NONE, 1, - default_params.stream_vbi_fmt); - - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 1, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); - - case V4L2_CID_MPEG_AUDIO_ENCODING: - if (params->capabilities & CX2341X_CAP_HAS_AC3) { - /* - * The state of L2 & AC3 bitrate controls can change - * when this control changes, but v4l2_ctrl_query_fill() - * already sets V4L2_CTRL_FLAG_UPDATE for - * V4L2_CID_MPEG_AUDIO_ENCODING, so we don't here. - */ - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_ENCODING_LAYER_2, - V4L2_MPEG_AUDIO_ENCODING_AC3, 1, - default_params.audio_encoding); - } - - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_ENCODING_LAYER_2, - V4L2_MPEG_AUDIO_ENCODING_LAYER_2, 1, - default_params.audio_encoding); - - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: - err = v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_L2_BITRATE_192K, - V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1, - default_params.audio_l2_bitrate); - if (err) - return err; - if (params->capabilities & CX2341X_CAP_HAS_AC3 && - params->audio_encoding != V4L2_MPEG_AUDIO_ENCODING_LAYER_2) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_AUDIO_MODE: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_MODE_STEREO, - V4L2_MPEG_AUDIO_MODE_MONO, 1, - V4L2_MPEG_AUDIO_MODE_STEREO); - - case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: - err = v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 1, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4); - if (err == 0 && - params->audio_mode != V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return err; - - case V4L2_CID_MPEG_AUDIO_EMPHASIS: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_EMPHASIS_NONE, - V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 1, - V4L2_MPEG_AUDIO_EMPHASIS_NONE); - - case V4L2_CID_MPEG_AUDIO_CRC: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_CRC_NONE, - V4L2_MPEG_AUDIO_CRC_CRC16, 1, - V4L2_MPEG_AUDIO_CRC_NONE); - - case V4L2_CID_MPEG_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); - - case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: - err = v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_AC3_BITRATE_48K, - V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 1, - default_params.audio_ac3_bitrate); - if (err) - return err; - if (params->capabilities & CX2341X_CAP_HAS_AC3) { - if (params->audio_encoding != - V4L2_MPEG_AUDIO_ENCODING_AC3) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - } else - qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; - return 0; - - case V4L2_CID_MPEG_VIDEO_ENCODING: - /* this setting is read-only for the cx2341x since the - V4L2_CID_MPEG_STREAM_TYPE really determines the - MPEG-1/2 setting */ - err = v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_VIDEO_ENCODING_MPEG_1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2); - if (err == 0) - qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - return err; - - case V4L2_CID_MPEG_VIDEO_ASPECT: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_VIDEO_ASPECT_1x1, - V4L2_MPEG_VIDEO_ASPECT_221x100, 1, - V4L2_MPEG_VIDEO_ASPECT_4x3); - - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - return v4l2_ctrl_query_fill(qctrl, 0, 33, 1, 2); - - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - return v4l2_ctrl_query_fill(qctrl, 1, 34, 1, - params->is_50hz ? 12 : 15); - - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 1); - - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - err = v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1, - V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); - if (err == 0 && - params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return err; - - case V4L2_CID_MPEG_VIDEO_BITRATE: - return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000); - - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - err = v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000); - if (err == 0 && - params->video_bitrate_mode == - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return err; - - case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: - return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0); - - case V4L2_CID_MPEG_VIDEO_MUTE: - return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); - - case V4L2_CID_MPEG_VIDEO_MUTE_YUV: /* Init YUV (really YCbCr) to black */ - return v4l2_ctrl_query_fill(qctrl, 0, 0xffffff, 1, 0x008080); - - /* CX23415/6 specific */ - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - return cx2341x_ctrl_query_fill(qctrl, - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 1, - default_params.video_spatial_filter_mode); - - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - cx2341x_ctrl_query_fill(qctrl, 0, 15, 1, - default_params.video_spatial_filter); - qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; - if (params->video_spatial_filter_mode == - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - cx2341x_ctrl_query_fill(qctrl, - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE, - 1, - default_params.video_luma_spatial_filter_type); - if (params->video_spatial_filter_mode == - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - cx2341x_ctrl_query_fill(qctrl, - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, - 1, - default_params.video_chroma_spatial_filter_type); - if (params->video_spatial_filter_mode == - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - return cx2341x_ctrl_query_fill(qctrl, - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, 1, - default_params.video_temporal_filter_mode); - - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: - cx2341x_ctrl_query_fill(qctrl, 0, 31, 1, - default_params.video_temporal_filter); - qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; - if (params->video_temporal_filter_mode == - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - return cx2341x_ctrl_query_fill(qctrl, - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, 1, - default_params.video_median_filter_type); - - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, - default_params.video_luma_median_filter_top); - qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; - if (params->video_median_filter_type == - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: - cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, - default_params.video_luma_median_filter_bottom); - qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; - if (params->video_median_filter_type == - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: - cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, - default_params.video_chroma_median_filter_top); - qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; - if (params->video_median_filter_type == - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: - cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, - default_params.video_chroma_median_filter_bottom); - qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; - if (params->video_median_filter_type == - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - return cx2341x_ctrl_query_fill(qctrl, 0, 1, 1, - default_params.stream_insert_nav_packets); - - default: - return -EINVAL; - - } -} -EXPORT_SYMBOL(cx2341x_ctrl_query); - -const char * const *cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id) -{ - static const char * const mpeg_stream_type_without_ts[] = { - "MPEG-2 Program Stream", - "", - "MPEG-1 System Stream", - "MPEG-2 DVD-compatible Stream", - "MPEG-1 VCD-compatible Stream", - "MPEG-2 SVCD-compatible Stream", - NULL - }; - - static const char *mpeg_stream_type_with_ts[] = { - "MPEG-2 Program Stream", - "MPEG-2 Transport Stream", - "MPEG-1 System Stream", - "MPEG-2 DVD-compatible Stream", - "MPEG-1 VCD-compatible Stream", - "MPEG-2 SVCD-compatible Stream", - NULL - }; - - static const char *mpeg_audio_encoding_l2_ac3[] = { - "", - "MPEG-1/2 Layer II", - "", - "", - "AC-3", - NULL - }; - - switch (id) { - case V4L2_CID_MPEG_STREAM_TYPE: - return (p->capabilities & CX2341X_CAP_HAS_TS) ? - mpeg_stream_type_with_ts : mpeg_stream_type_without_ts; - case V4L2_CID_MPEG_AUDIO_ENCODING: - return (p->capabilities & CX2341X_CAP_HAS_AC3) ? - mpeg_audio_encoding_l2_ac3 : v4l2_ctrl_get_menu(id); - case V4L2_CID_MPEG_AUDIO_L1_BITRATE: - case V4L2_CID_MPEG_AUDIO_L3_BITRATE: - return NULL; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - return cx2341x_get_menu(id); - default: - return v4l2_ctrl_get_menu(id); - } -} -EXPORT_SYMBOL(cx2341x_ctrl_get_menu); - -static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params) -{ - params->audio_properties = - (params->audio_sampling_freq << 0) | - (params->audio_mode << 8) | - (params->audio_mode_extension << 10) | - (((params->audio_emphasis == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17) - ? 3 : params->audio_emphasis) << 12) | - (params->audio_crc << 14); - - if ((params->capabilities & CX2341X_CAP_HAS_AC3) && - params->audio_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) { - params->audio_properties |= - /* Not sure if this MPEG Layer II setting is required */ - ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) | - (params->audio_ac3_bitrate << 4) | - (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28); - } else { - /* Assuming MPEG Layer II */ - params->audio_properties |= - ((3 - params->audio_encoding) << 2) | - ((1 + params->audio_l2_bitrate) << 4); - } -} - -int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, int busy, - struct v4l2_ext_controls *ctrls, unsigned int cmd) -{ - int err = 0; - int i; - - if (cmd == VIDIOC_G_EXT_CTRLS) { - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - - err = cx2341x_get_ctrl(params, ctrl); - if (err) { - ctrls->error_idx = i; - break; - } - } - return err; - } - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - struct v4l2_queryctrl qctrl; - const char * const *menu_items = NULL; - - qctrl.id = ctrl->id; - err = cx2341x_ctrl_query(params, &qctrl); - if (err) - break; - if (qctrl.type == V4L2_CTRL_TYPE_MENU) - menu_items = cx2341x_ctrl_get_menu(params, qctrl.id); - err = v4l2_ctrl_check(ctrl, &qctrl, menu_items); - if (err) - break; - err = cx2341x_set_ctrl(params, busy, ctrl); - if (err) - break; - } - if (err == 0 && - params->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && - params->video_bitrate_peak < params->video_bitrate) { - err = -ERANGE; - ctrls->error_idx = ctrls->count; - } - if (err) - ctrls->error_idx = i; - else - cx2341x_calc_audio_properties(params); - return err; -} -EXPORT_SYMBOL(cx2341x_ext_ctrls); - -void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p) -{ - *p = default_params; - cx2341x_calc_audio_properties(p); -} -EXPORT_SYMBOL(cx2341x_fill_defaults); - -static int cx2341x_api(void *priv, cx2341x_mbox_func func, - u32 cmd, int args, ...) -{ - u32 data[CX2341X_MBOX_MAX_DATA]; - va_list vargs; - int i; - - va_start(vargs, args); - - for (i = 0; i < args; i++) - data[i] = va_arg(vargs, int); - va_end(vargs); - return func(priv, cmd, args, 0, data); -} - -#define NEQ(field) (old->field != new->field) - -int cx2341x_update(void *priv, cx2341x_mbox_func func, - const struct cx2341x_mpeg_params *old, - const struct cx2341x_mpeg_params *new) -{ - static int mpeg_stream_type[] = { - 0, /* MPEG-2 PS */ - 1, /* MPEG-2 TS */ - 2, /* MPEG-1 SS */ - 14, /* DVD */ - 11, /* VCD */ - 12, /* SVCD */ - }; - - int err = 0; - int force = (old == NULL); - u16 temporal = new->video_temporal_filter; - - cx2341x_api(priv, func, CX2341X_ENC_SET_OUTPUT_PORT, 2, new->port, 0); - - if (force || NEQ(is_50hz)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_RATE, 1, - new->is_50hz); - if (err) return err; - } - - if (force || NEQ(width) || NEQ(height) || NEQ(video_encoding)) { - u16 w = new->width; - u16 h = new->height; - - if (new->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) { - w /= 2; - h /= 2; - } - err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_SIZE, 2, - h, w); - if (err) return err; - } - if (force || NEQ(stream_type)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_STREAM_TYPE, 1, - mpeg_stream_type[new->stream_type]); - if (err) return err; - } - if (force || NEQ(video_aspect)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_ASPECT_RATIO, 1, - 1 + new->video_aspect); - if (err) return err; - } - if (force || NEQ(video_b_frames) || NEQ(video_gop_size)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_PROPERTIES, 2, - new->video_gop_size, new->video_b_frames + 1); - if (err) return err; - } - if (force || NEQ(video_gop_closure)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_CLOSURE, 1, - new->video_gop_closure); - if (err) return err; - } - if (force || NEQ(audio_properties)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_AUDIO_PROPERTIES, - 1, new->audio_properties); - if (err) return err; - } - if (force || NEQ(audio_mute)) { - err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_AUDIO, 1, - new->audio_mute); - if (err) return err; - } - if (force || NEQ(video_bitrate_mode) || NEQ(video_bitrate) || - NEQ(video_bitrate_peak)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_BIT_RATE, 5, - new->video_bitrate_mode, new->video_bitrate, - new->video_bitrate_peak / 400, 0, 0); - if (err) return err; - } - if (force || NEQ(video_spatial_filter_mode) || - NEQ(video_temporal_filter_mode) || - NEQ(video_median_filter_type)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_MODE, - 2, new->video_spatial_filter_mode | - (new->video_temporal_filter_mode << 1), - new->video_median_filter_type); - if (err) return err; - } - if (force || NEQ(video_luma_median_filter_bottom) || - NEQ(video_luma_median_filter_top) || - NEQ(video_chroma_median_filter_bottom) || - NEQ(video_chroma_median_filter_top)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_CORING_LEVELS, 4, - new->video_luma_median_filter_bottom, - new->video_luma_median_filter_top, - new->video_chroma_median_filter_bottom, - new->video_chroma_median_filter_top); - if (err) return err; - } - if (force || NEQ(video_luma_spatial_filter_type) || - NEQ(video_chroma_spatial_filter_type)) { - err = cx2341x_api(priv, func, - CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, - 2, new->video_luma_spatial_filter_type, - new->video_chroma_spatial_filter_type); - if (err) return err; - } - if (force || NEQ(video_spatial_filter) || - old->video_temporal_filter != temporal) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_PROPS, - 2, new->video_spatial_filter, temporal); - if (err) return err; - } - if (force || NEQ(video_temporal_decimation)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_DROP_RATE, - 1, new->video_temporal_decimation); - if (err) return err; - } - if (force || NEQ(video_mute) || - (new->video_mute && NEQ(video_mute_yuv))) { - err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_VIDEO, 1, - new->video_mute | (new->video_mute_yuv << 8)); - if (err) return err; - } - if (force || NEQ(stream_insert_nav_packets)) { - err = cx2341x_api(priv, func, CX2341X_ENC_MISC, 2, - 7, new->stream_insert_nav_packets); - if (err) return err; - } - return 0; -} -EXPORT_SYMBOL(cx2341x_update); - -static const char *cx2341x_menu_item(const struct cx2341x_mpeg_params *p, u32 id) -{ - const char * const *menu = cx2341x_ctrl_get_menu(p, id); - struct v4l2_ext_control ctrl; - - if (menu == NULL) - goto invalid; - ctrl.id = id; - if (cx2341x_get_ctrl(p, &ctrl)) - goto invalid; - while (ctrl.value-- && *menu) menu++; - if (*menu == NULL) - goto invalid; - return *menu; - -invalid: - return ""; -} - -void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix) -{ - int is_mpeg1 = p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1; - - /* Stream */ - printk(KERN_INFO "%s: Stream: %s", - prefix, - cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_TYPE)); - if (p->stream_insert_nav_packets) - printk(" (with navigation packets)"); - printk("\n"); - printk(KERN_INFO "%s: VBI Format: %s\n", - prefix, - cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_VBI_FMT)); - - /* Video */ - printk(KERN_INFO "%s: Video: %dx%d, %d fps%s\n", - prefix, - p->width / (is_mpeg1 ? 2 : 1), p->height / (is_mpeg1 ? 2 : 1), - p->is_50hz ? 25 : 30, - (p->video_mute) ? " (muted)" : ""); - printk(KERN_INFO "%s: Video: %s, %s, %s, %d", - prefix, - cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ENCODING), - cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ASPECT), - cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_BITRATE_MODE), - p->video_bitrate); - if (p->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) - printk(", Peak %d", p->video_bitrate_peak); - printk("\n"); - printk(KERN_INFO - "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure\n", - prefix, - p->video_gop_size, p->video_b_frames, - p->video_gop_closure ? "" : "No "); - if (p->video_temporal_decimation) - printk(KERN_INFO "%s: Video: Temporal Decimation %d\n", - prefix, p->video_temporal_decimation); - - /* Audio */ - printk(KERN_INFO "%s: Audio: %s, %s, %s, %s%s", - prefix, - cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ), - cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_ENCODING), - cx2341x_menu_item(p, - p->audio_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3 - ? V4L2_CID_MPEG_AUDIO_AC3_BITRATE - : V4L2_CID_MPEG_AUDIO_L2_BITRATE), - cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE), - p->audio_mute ? " (muted)" : ""); - if (p->audio_mode == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) - printk(", %s", cx2341x_menu_item(p, - V4L2_CID_MPEG_AUDIO_MODE_EXTENSION)); - printk(", %s, %s\n", - cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_EMPHASIS), - cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_CRC)); - - /* Encoding filters */ - printk(KERN_INFO "%s: Spatial Filter: %s, Luma %s, Chroma %s, %d\n", - prefix, - cx2341x_menu_item(p, - V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE), - cx2341x_menu_item(p, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE), - cx2341x_menu_item(p, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE), - p->video_spatial_filter); - - printk(KERN_INFO "%s: Temporal Filter: %s, %d\n", - prefix, - cx2341x_menu_item(p, - V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE), - p->video_temporal_filter); - printk(KERN_INFO - "%s: Median Filter: %s, Luma [%d, %d], Chroma [%d, %d]\n", - prefix, - cx2341x_menu_item(p, - V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE), - p->video_luma_median_filter_bottom, - p->video_luma_median_filter_top, - p->video_chroma_median_filter_bottom, - p->video_chroma_median_filter_top); -} -EXPORT_SYMBOL(cx2341x_log_status); - - - -/********************** NEW CODE *********************/ - -static inline struct cx2341x_handler *to_cxhdl(struct v4l2_ctrl *ctrl) -{ - return container_of(ctrl->handler, struct cx2341x_handler, hdl); -} - -static int cx2341x_hdl_api(struct cx2341x_handler *hdl, - u32 cmd, int args, ...) -{ - u32 data[CX2341X_MBOX_MAX_DATA]; - va_list vargs; - int i; - - va_start(vargs, args); - - for (i = 0; i < args; i++) - data[i] = va_arg(vargs, int); - va_end(vargs); - return hdl->func(hdl->priv, cmd, args, 0, data); -} - -/* ctrl->handler->lock is held, so it is safe to access cur.val */ -static inline int cx2341x_neq(struct v4l2_ctrl *ctrl) -{ - return ctrl && ctrl->val != ctrl->cur.val; -} - -static int cx2341x_try_ctrl(struct v4l2_ctrl *ctrl) -{ - struct cx2341x_handler *hdl = to_cxhdl(ctrl); - s32 val = ctrl->val; - - switch (ctrl->id) { - case V4L2_CID_MPEG_VIDEO_B_FRAMES: { - /* video gop cluster */ - int b = val + 1; - int gop = hdl->video_gop_size->val; - - gop = b * ((gop + b - 1) / b); - - /* Max GOP size = 34 */ - while (gop > 34) - gop -= b; - hdl->video_gop_size->val = gop; - break; - } - - case V4L2_CID_MPEG_STREAM_TYPE: - /* stream type cluster */ - hdl->video_encoding->val = - (hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_SS || - hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ? - V4L2_MPEG_VIDEO_ENCODING_MPEG_1 : - V4L2_MPEG_VIDEO_ENCODING_MPEG_2; - if (hdl->video_encoding->val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) - /* MPEG-1 implies CBR */ - hdl->video_bitrate_mode->val = - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; - /* peak bitrate shall be >= normal bitrate */ - if (hdl->video_bitrate_mode->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && - hdl->video_bitrate_peak->val < hdl->video_bitrate->val) - hdl->video_bitrate_peak->val = hdl->video_bitrate->val; - break; - } - return 0; -} - -static int cx2341x_s_ctrl(struct v4l2_ctrl *ctrl) -{ - static const int mpeg_stream_type[] = { - 0, /* MPEG-2 PS */ - 1, /* MPEG-2 TS */ - 2, /* MPEG-1 SS */ - 14, /* DVD */ - 11, /* VCD */ - 12, /* SVCD */ - }; - struct cx2341x_handler *hdl = to_cxhdl(ctrl); - s32 val = ctrl->val; - u32 props; - int err; - - switch (ctrl->id) { - case V4L2_CID_MPEG_STREAM_VBI_FMT: - if (hdl->ops && hdl->ops->s_stream_vbi_fmt) - return hdl->ops->s_stream_vbi_fmt(hdl, val); - return 0; - - case V4L2_CID_MPEG_VIDEO_ASPECT: - return cx2341x_hdl_api(hdl, - CX2341X_ENC_SET_ASPECT_RATIO, 1, val + 1); - - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_CLOSURE, 1, val); - - case V4L2_CID_MPEG_AUDIO_MUTE: - return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_AUDIO, 1, val); - - case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: - return cx2341x_hdl_api(hdl, - CX2341X_ENC_SET_FRAME_DROP_RATE, 1, val); - - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - return cx2341x_hdl_api(hdl, CX2341X_ENC_MISC, 2, 7, val); - - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: - /* audio properties cluster */ - props = (hdl->audio_sampling_freq->val << 0) | - (hdl->audio_mode->val << 8) | - (hdl->audio_mode_extension->val << 10) | - (hdl->audio_crc->val << 14); - if (hdl->audio_emphasis->val == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17) - props |= 3 << 12; - else - props |= hdl->audio_emphasis->val << 12; - - if (hdl->audio_encoding->val == V4L2_MPEG_AUDIO_ENCODING_AC3) { - props |= -#if 1 - /* Not sure if this MPEG Layer II setting is required */ - ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) | -#endif - (hdl->audio_ac3_bitrate->val << 4) | - (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28); - } else { - /* Assuming MPEG Layer II */ - props |= - ((3 - hdl->audio_encoding->val) << 2) | - ((1 + hdl->audio_l2_bitrate->val) << 4); - } - err = cx2341x_hdl_api(hdl, - CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, props); - if (err) - return err; - - hdl->audio_properties = props; - if (hdl->audio_ac3_bitrate) { - int is_ac3 = hdl->audio_encoding->val == - V4L2_MPEG_AUDIO_ENCODING_AC3; - - v4l2_ctrl_activate(hdl->audio_ac3_bitrate, is_ac3); - v4l2_ctrl_activate(hdl->audio_l2_bitrate, !is_ac3); - } - v4l2_ctrl_activate(hdl->audio_mode_extension, - hdl->audio_mode->val == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO); - if (cx2341x_neq(hdl->audio_sampling_freq) && - hdl->ops && hdl->ops->s_audio_sampling_freq) - return hdl->ops->s_audio_sampling_freq(hdl, hdl->audio_sampling_freq->val); - if (cx2341x_neq(hdl->audio_mode) && - hdl->ops && hdl->ops->s_audio_mode) - return hdl->ops->s_audio_mode(hdl, hdl->audio_mode->val); - return 0; - - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - /* video gop cluster */ - return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_PROPERTIES, 2, - hdl->video_gop_size->val, - hdl->video_b_frames->val + 1); - - case V4L2_CID_MPEG_STREAM_TYPE: - /* stream type cluster */ - err = cx2341x_hdl_api(hdl, - CX2341X_ENC_SET_STREAM_TYPE, 1, mpeg_stream_type[val]); - if (err) - return err; - - err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_BIT_RATE, 5, - hdl->video_bitrate_mode->val, - hdl->video_bitrate->val, - hdl->video_bitrate_peak->val / 400, 0, 0); - if (err) - return err; - - v4l2_ctrl_activate(hdl->video_bitrate_mode, - hdl->video_encoding->val != V4L2_MPEG_VIDEO_ENCODING_MPEG_1); - v4l2_ctrl_activate(hdl->video_bitrate_peak, - hdl->video_bitrate_mode->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); - if (cx2341x_neq(hdl->video_encoding) && - hdl->ops && hdl->ops->s_video_encoding) - return hdl->ops->s_video_encoding(hdl, hdl->video_encoding->val); - return 0; - - case V4L2_CID_MPEG_VIDEO_MUTE: - /* video mute cluster */ - return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_VIDEO, 1, - hdl->video_mute->val | - (hdl->video_mute_yuv->val << 8)); - - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: { - int active_filter; - - /* video filter mode */ - err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_MODE, 2, - hdl->video_spatial_filter_mode->val | - (hdl->video_temporal_filter_mode->val << 1), - hdl->video_median_filter_type->val); - if (err) - return err; - - active_filter = hdl->video_spatial_filter_mode->val != - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO; - v4l2_ctrl_activate(hdl->video_spatial_filter, active_filter); - v4l2_ctrl_activate(hdl->video_luma_spatial_filter_type, active_filter); - v4l2_ctrl_activate(hdl->video_chroma_spatial_filter_type, active_filter); - active_filter = hdl->video_temporal_filter_mode->val != - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO; - v4l2_ctrl_activate(hdl->video_temporal_filter, active_filter); - active_filter = hdl->video_median_filter_type->val != - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF; - v4l2_ctrl_activate(hdl->video_luma_median_filter_bottom, active_filter); - v4l2_ctrl_activate(hdl->video_luma_median_filter_top, active_filter); - v4l2_ctrl_activate(hdl->video_chroma_median_filter_bottom, active_filter); - v4l2_ctrl_activate(hdl->video_chroma_median_filter_top, active_filter); - return 0; - } - - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - /* video filter type cluster */ - return cx2341x_hdl_api(hdl, - CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 2, - hdl->video_luma_spatial_filter_type->val, - hdl->video_chroma_spatial_filter_type->val); - - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - /* video filter cluster */ - return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2, - hdl->video_spatial_filter->val, - hdl->video_temporal_filter->val); - - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - /* video median cluster */ - return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_CORING_LEVELS, 4, - hdl->video_luma_median_filter_bottom->val, - hdl->video_luma_median_filter_top->val, - hdl->video_chroma_median_filter_bottom->val, - hdl->video_chroma_median_filter_top->val); - } - return -EINVAL; -} - -static const struct v4l2_ctrl_ops cx2341x_ops = { - .try_ctrl = cx2341x_try_ctrl, - .s_ctrl = cx2341x_s_ctrl, -}; - -static struct v4l2_ctrl *cx2341x_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, - u32 id, s32 min, s32 max, s32 step, s32 def) -{ - struct v4l2_ctrl_config cfg; - - cx2341x_ctrl_fill(id, &cfg.name, &cfg.type, &min, &max, &step, &def, &cfg.flags); - cfg.ops = &cx2341x_ops; - cfg.id = id; - cfg.min = min; - cfg.max = max; - cfg.def = def; - if (cfg.type == V4L2_CTRL_TYPE_MENU) { - cfg.step = 0; - cfg.menu_skip_mask = step; - cfg.qmenu = cx2341x_get_menu(id); - } else { - cfg.step = step; - cfg.menu_skip_mask = 0; - } - return v4l2_ctrl_new_custom(hdl, &cfg, NULL); -} - -static struct v4l2_ctrl *cx2341x_ctrl_new_std(struct v4l2_ctrl_handler *hdl, - u32 id, s32 min, s32 max, s32 step, s32 def) -{ - return v4l2_ctrl_new_std(hdl, &cx2341x_ops, id, min, max, step, def); -} - -static struct v4l2_ctrl *cx2341x_ctrl_new_menu(struct v4l2_ctrl_handler *hdl, - u32 id, s32 max, s32 mask, s32 def) -{ - return v4l2_ctrl_new_std_menu(hdl, &cx2341x_ops, id, max, mask, def); -} - -int cx2341x_handler_init(struct cx2341x_handler *cxhdl, - unsigned nr_of_controls_hint) -{ - struct v4l2_ctrl_handler *hdl = &cxhdl->hdl; - u32 caps = cxhdl->capabilities; - int has_sliced_vbi = caps & CX2341X_CAP_HAS_SLICED_VBI; - int has_ac3 = caps & CX2341X_CAP_HAS_AC3; - int has_ts = caps & CX2341X_CAP_HAS_TS; - - cxhdl->width = 720; - cxhdl->height = 480; - - v4l2_ctrl_handler_init(hdl, nr_of_controls_hint); - - /* Add controls in ascending control ID order for fastest - insertion time. */ - cxhdl->stream_type = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_STREAM_TYPE, - V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, has_ts ? 0 : 2, - V4L2_MPEG_STREAM_TYPE_MPEG2_PS); - cxhdl->stream_vbi_fmt = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_STREAM_VBI_FMT, - V4L2_MPEG_STREAM_VBI_FMT_IVTV, has_sliced_vbi ? 0 : 2, - V4L2_MPEG_STREAM_VBI_FMT_NONE); - cxhdl->audio_sampling_freq = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 0, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); - cxhdl->audio_encoding = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_ENCODING, - V4L2_MPEG_AUDIO_ENCODING_AC3, has_ac3 ? ~0x12 : ~0x2, - V4L2_MPEG_AUDIO_ENCODING_LAYER_2); - cxhdl->audio_l2_bitrate = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_L2_BITRATE, - V4L2_MPEG_AUDIO_L2_BITRATE_384K, 0x1ff, - V4L2_MPEG_AUDIO_L2_BITRATE_224K); - cxhdl->audio_mode = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_MODE, - V4L2_MPEG_AUDIO_MODE_MONO, 0, - V4L2_MPEG_AUDIO_MODE_STEREO); - cxhdl->audio_mode_extension = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 0, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4); - cxhdl->audio_emphasis = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_EMPHASIS, - V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 0, - V4L2_MPEG_AUDIO_EMPHASIS_NONE); - cxhdl->audio_crc = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_CRC, - V4L2_MPEG_AUDIO_CRC_CRC16, 0, - V4L2_MPEG_AUDIO_CRC_NONE); - - cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_AUDIO_MUTE, 0, 1, 1, 0); - if (has_ac3) - cxhdl->audio_ac3_bitrate = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_AC3_BITRATE, - V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 0x03, - V4L2_MPEG_AUDIO_AC3_BITRATE_224K); - cxhdl->video_encoding = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_VIDEO_ENCODING, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 0, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2); - cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_VIDEO_ASPECT, - V4L2_MPEG_VIDEO_ASPECT_221x100, 0, - V4L2_MPEG_VIDEO_ASPECT_4x3); - cxhdl->video_b_frames = cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 33, 1, 2); - cxhdl->video_gop_size = cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, - 1, 34, 1, cxhdl->is_50hz ? 12 : 15); - cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, 0, 1, 1, 1); - cxhdl->video_bitrate_mode = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_VIDEO_BITRATE_MODE, - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, - V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); - cxhdl->video_bitrate = cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_BITRATE, - 0, 27000000, 1, 6000000); - cxhdl->video_bitrate_peak = cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, - 0, 27000000, 1, 8000000); - cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, 0, 255, 1, 0); - cxhdl->video_mute = cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_MUTE, 0, 1, 1, 0); - cxhdl->video_mute_yuv = cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_MUTE_YUV, 0, 0xffffff, 1, 0x008080); - - /* CX23415/6 specific */ - cxhdl->video_spatial_filter_mode = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE, - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 0, - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL); - cxhdl->video_spatial_filter = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER, - 0, 15, 1, 0); - cxhdl->video_luma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE, - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE, - 0, - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR); - cxhdl->video_chroma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE, - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, - 0, - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR); - cxhdl->video_temporal_filter_mode = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE, - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, - 0, - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL); - cxhdl->video_temporal_filter = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER, - 0, 31, 1, 8); - cxhdl->video_median_filter_type = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE, - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, - 0, - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF); - cxhdl->video_luma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM, - 0, 255, 1, 0); - cxhdl->video_luma_median_filter_top = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP, - 0, 255, 1, 255); - cxhdl->video_chroma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, - 0, 255, 1, 0); - cxhdl->video_chroma_median_filter_top = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP, - 0, 255, 1, 255); - cx2341x_ctrl_new_custom(hdl, V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS, - 0, 1, 1, 0); - - if (hdl->error) { - int err = hdl->error; - - v4l2_ctrl_handler_free(hdl); - return err; - } - - v4l2_ctrl_cluster(8, &cxhdl->audio_sampling_freq); - v4l2_ctrl_cluster(2, &cxhdl->video_b_frames); - v4l2_ctrl_cluster(5, &cxhdl->stream_type); - v4l2_ctrl_cluster(2, &cxhdl->video_mute); - v4l2_ctrl_cluster(3, &cxhdl->video_spatial_filter_mode); - v4l2_ctrl_cluster(2, &cxhdl->video_luma_spatial_filter_type); - v4l2_ctrl_cluster(2, &cxhdl->video_spatial_filter); - v4l2_ctrl_cluster(4, &cxhdl->video_luma_median_filter_top); - - return 0; -} -EXPORT_SYMBOL(cx2341x_handler_init); - -void cx2341x_handler_set_50hz(struct cx2341x_handler *cxhdl, int is_50hz) -{ - cxhdl->is_50hz = is_50hz; - cxhdl->video_gop_size->default_value = cxhdl->is_50hz ? 12 : 15; -} -EXPORT_SYMBOL(cx2341x_handler_set_50hz); - -int cx2341x_handler_setup(struct cx2341x_handler *cxhdl) -{ - int h = cxhdl->height; - int w = cxhdl->width; - int err; - - err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_OUTPUT_PORT, 2, cxhdl->port, 0); - if (err) - return err; - err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_RATE, 1, cxhdl->is_50hz); - if (err) - return err; - - if (v4l2_ctrl_g_ctrl(cxhdl->video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) { - w /= 2; - h /= 2; - } - err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_SIZE, 2, h, w); - if (err) - return err; - return v4l2_ctrl_handler_setup(&cxhdl->hdl); -} -EXPORT_SYMBOL(cx2341x_handler_setup); - -void cx2341x_handler_set_busy(struct cx2341x_handler *cxhdl, int busy) -{ - v4l2_ctrl_grab(cxhdl->audio_sampling_freq, busy); - v4l2_ctrl_grab(cxhdl->audio_encoding, busy); - v4l2_ctrl_grab(cxhdl->audio_l2_bitrate, busy); - v4l2_ctrl_grab(cxhdl->audio_ac3_bitrate, busy); - v4l2_ctrl_grab(cxhdl->stream_vbi_fmt, busy); - v4l2_ctrl_grab(cxhdl->stream_type, busy); - v4l2_ctrl_grab(cxhdl->video_bitrate_mode, busy); - v4l2_ctrl_grab(cxhdl->video_bitrate, busy); - v4l2_ctrl_grab(cxhdl->video_bitrate_peak, busy); -} -EXPORT_SYMBOL(cx2341x_handler_set_busy); -- cgit v1.2.3 From f1c50f2489f40494658a6b7326bd6d5a26f72711 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 6 Feb 2013 07:57:57 -0300 Subject: [media] btcx-risc: move from media/i2c to media/common The btcx-risc module is a helper module for bttv/conexant based TV cards. It isn't an i2c module at all, instead it should be in common since it is used by 4 pci drivers. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/Kconfig | 4 + drivers/media/common/Makefile | 1 + drivers/media/common/btcx-risc.c | 260 +++++++++++++++++++++++++++++++++++++ drivers/media/common/btcx-risc.h | 34 +++++ drivers/media/i2c/Kconfig | 4 - drivers/media/i2c/Makefile | 1 - drivers/media/i2c/btcx-risc.c | 260 ------------------------------------- drivers/media/i2c/btcx-risc.h | 34 ----- drivers/media/pci/bt8xx/Makefile | 1 + drivers/media/pci/cx23885/Makefile | 1 + drivers/media/pci/cx25821/Makefile | 1 + drivers/media/pci/cx88/Makefile | 1 + 12 files changed, 303 insertions(+), 299 deletions(-) create mode 100644 drivers/media/common/btcx-risc.c create mode 100644 drivers/media/common/btcx-risc.h delete mode 100644 drivers/media/i2c/btcx-risc.c delete mode 100644 drivers/media/i2c/btcx-risc.h (limited to 'drivers') diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index dfac52f312cf..28c8a602e7ef 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig @@ -8,6 +8,10 @@ comment "common driver options" config VIDEO_CX2341X tristate +config VIDEO_BTCX + depends on PCI + tristate + source "drivers/media/common/b2c2/Kconfig" source "drivers/media/common/saa7146/Kconfig" source "drivers/media/common/siano/Kconfig" diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index 9b8ea4fae06f..cfe34499d993 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -1,2 +1,3 @@ obj-y += b2c2/ saa7146/ siano/ obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o +obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o diff --git a/drivers/media/common/btcx-risc.c b/drivers/media/common/btcx-risc.c new file mode 100644 index 000000000000..ac1b2687a20d --- /dev/null +++ b/drivers/media/common/btcx-risc.c @@ -0,0 +1,260 @@ +/* + + btcx-risc.c + + bt848/bt878/cx2388x risc code generator. + + (c) 2000-03 Gerd Knorr [SuSE Labs] + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "btcx-risc.h" + +MODULE_DESCRIPTION("some code shared by bttv and cx88xx drivers"); +MODULE_AUTHOR("Gerd Knorr"); +MODULE_LICENSE("GPL"); + +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug,"debug messages, default is 0 (no)"); + +/* ---------------------------------------------------------- */ +/* allocate/free risc memory */ + +static int memcnt; + +void btcx_riscmem_free(struct pci_dev *pci, + struct btcx_riscmem *risc) +{ + if (NULL == risc->cpu) + return; + if (debug) { + memcnt--; + printk("btcx: riscmem free [%d] dma=%lx\n", + memcnt, (unsigned long)risc->dma); + } + pci_free_consistent(pci, risc->size, risc->cpu, risc->dma); + memset(risc,0,sizeof(*risc)); +} + +int btcx_riscmem_alloc(struct pci_dev *pci, + struct btcx_riscmem *risc, + unsigned int size) +{ + __le32 *cpu; + dma_addr_t dma = 0; + + if (NULL != risc->cpu && risc->size < size) + btcx_riscmem_free(pci,risc); + if (NULL == risc->cpu) { + cpu = pci_alloc_consistent(pci, size, &dma); + if (NULL == cpu) + return -ENOMEM; + risc->cpu = cpu; + risc->dma = dma; + risc->size = size; + if (debug) { + memcnt++; + printk("btcx: riscmem alloc [%d] dma=%lx cpu=%p size=%d\n", + memcnt, (unsigned long)dma, cpu, size); + } + } + memset(risc->cpu,0,risc->size); + return 0; +} + +/* ---------------------------------------------------------- */ +/* screen overlay helpers */ + +int +btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, + struct v4l2_clip *clips, unsigned int n) +{ + if (win->left < 0) { + /* left */ + clips[n].c.left = 0; + clips[n].c.top = 0; + clips[n].c.width = -win->left; + clips[n].c.height = win->height; + n++; + } + if (win->left + win->width > swidth) { + /* right */ + clips[n].c.left = swidth - win->left; + clips[n].c.top = 0; + clips[n].c.width = win->width - clips[n].c.left; + clips[n].c.height = win->height; + n++; + } + if (win->top < 0) { + /* top */ + clips[n].c.left = 0; + clips[n].c.top = 0; + clips[n].c.width = win->width; + clips[n].c.height = -win->top; + n++; + } + if (win->top + win->height > sheight) { + /* bottom */ + clips[n].c.left = 0; + clips[n].c.top = sheight - win->top; + clips[n].c.width = win->width; + clips[n].c.height = win->height - clips[n].c.top; + n++; + } + return n; +} + +int +btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, unsigned int n, int mask) +{ + s32 nx,nw,dx; + unsigned int i; + + /* fixup window */ + nx = (win->left + mask) & ~mask; + nw = (win->width) & ~mask; + if (nx + nw > win->left + win->width) + nw -= mask+1; + dx = nx - win->left; + win->left = nx; + win->width = nw; + if (debug) + printk(KERN_DEBUG "btcx: window align %dx%d+%d+%d [dx=%d]\n", + win->width, win->height, win->left, win->top, dx); + + /* fixup clips */ + for (i = 0; i < n; i++) { + nx = (clips[i].c.left-dx) & ~mask; + nw = (clips[i].c.width) & ~mask; + if (nx + nw < clips[i].c.left-dx + clips[i].c.width) + nw += mask+1; + clips[i].c.left = nx; + clips[i].c.width = nw; + if (debug) + printk(KERN_DEBUG "btcx: clip align %dx%d+%d+%d\n", + clips[i].c.width, clips[i].c.height, + clips[i].c.left, clips[i].c.top); + } + return 0; +} + +void +btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips) +{ + struct v4l2_clip swap; + int i,j,n; + + if (nclips < 2) + return; + for (i = nclips-2; i >= 0; i--) { + for (n = 0, j = 0; j <= i; j++) { + if (clips[j].c.left > clips[j+1].c.left) { + swap = clips[j]; + clips[j] = clips[j+1]; + clips[j+1] = swap; + n++; + } + } + if (0 == n) + break; + } +} + +void +btcx_calc_skips(int line, int width, int *maxy, + struct btcx_skiplist *skips, unsigned int *nskips, + const struct v4l2_clip *clips, unsigned int nclips) +{ + unsigned int clip,skip; + int end, maxline; + + skip=0; + maxline = 9999; + for (clip = 0; clip < nclips; clip++) { + + /* sanity checks */ + if (clips[clip].c.left + clips[clip].c.width <= 0) + continue; + if (clips[clip].c.left > (signed)width) + break; + + /* vertical range */ + if (line > clips[clip].c.top+clips[clip].c.height-1) + continue; + if (line < clips[clip].c.top) { + if (maxline > clips[clip].c.top-1) + maxline = clips[clip].c.top-1; + continue; + } + if (maxline > clips[clip].c.top+clips[clip].c.height-1) + maxline = clips[clip].c.top+clips[clip].c.height-1; + + /* horizontal range */ + if (0 == skip || clips[clip].c.left > skips[skip-1].end) { + /* new one */ + skips[skip].start = clips[clip].c.left; + if (skips[skip].start < 0) + skips[skip].start = 0; + skips[skip].end = clips[clip].c.left + clips[clip].c.width; + if (skips[skip].end > width) + skips[skip].end = width; + skip++; + } else { + /* overlaps -- expand last one */ + end = clips[clip].c.left + clips[clip].c.width; + if (skips[skip-1].end < end) + skips[skip-1].end = end; + if (skips[skip-1].end > width) + skips[skip-1].end = width; + } + } + *nskips = skip; + *maxy = maxline; + + if (debug) { + printk(KERN_DEBUG "btcx: skips line %d-%d:",line,maxline); + for (skip = 0; skip < *nskips; skip++) { + printk(" %d-%d",skips[skip].start,skips[skip].end); + } + printk("\n"); + } +} + +/* ---------------------------------------------------------- */ + +EXPORT_SYMBOL(btcx_riscmem_alloc); +EXPORT_SYMBOL(btcx_riscmem_free); + +EXPORT_SYMBOL(btcx_screen_clips); +EXPORT_SYMBOL(btcx_align); +EXPORT_SYMBOL(btcx_sort_clips); +EXPORT_SYMBOL(btcx_calc_skips); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/common/btcx-risc.h b/drivers/media/common/btcx-risc.h new file mode 100644 index 000000000000..f8bc6e8e7b51 --- /dev/null +++ b/drivers/media/common/btcx-risc.h @@ -0,0 +1,34 @@ +/* + */ +struct btcx_riscmem { + unsigned int size; + __le32 *cpu; + __le32 *jmp; + dma_addr_t dma; +}; + +struct btcx_skiplist { + int start; + int end; +}; + +int btcx_riscmem_alloc(struct pci_dev *pci, + struct btcx_riscmem *risc, + unsigned int size); +void btcx_riscmem_free(struct pci_dev *pci, + struct btcx_riscmem *risc); + +int btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, + struct v4l2_clip *clips, unsigned int n); +int btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, + unsigned int n, int mask); +void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips); +void btcx_calc_skips(int line, int width, int *maxy, + struct btcx_skiplist *skips, unsigned int *nskips, + const struct v4l2_clip *clips, unsigned int nclips); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 97f14c24a40c..cd2b17148ad6 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -2,10 +2,6 @@ # Generic video config states # -config VIDEO_BTCX - depends on PCI - tristate - config VIDEO_TVEEPROM tristate depends on I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 3a8334f205f3..3691e70f4e75 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -63,6 +63,5 @@ obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o -obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o obj-$(CONFIG_VIDEO_AK881X) += ak881x.o obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o diff --git a/drivers/media/i2c/btcx-risc.c b/drivers/media/i2c/btcx-risc.c deleted file mode 100644 index ac1b2687a20d..000000000000 --- a/drivers/media/i2c/btcx-risc.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - - btcx-risc.c - - bt848/bt878/cx2388x risc code generator. - - (c) 2000-03 Gerd Knorr [SuSE Labs] - - 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. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include -#include -#include -#include -#include -#include -#include - -#include "btcx-risc.h" - -MODULE_DESCRIPTION("some code shared by bttv and cx88xx drivers"); -MODULE_AUTHOR("Gerd Knorr"); -MODULE_LICENSE("GPL"); - -static unsigned int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug,"debug messages, default is 0 (no)"); - -/* ---------------------------------------------------------- */ -/* allocate/free risc memory */ - -static int memcnt; - -void btcx_riscmem_free(struct pci_dev *pci, - struct btcx_riscmem *risc) -{ - if (NULL == risc->cpu) - return; - if (debug) { - memcnt--; - printk("btcx: riscmem free [%d] dma=%lx\n", - memcnt, (unsigned long)risc->dma); - } - pci_free_consistent(pci, risc->size, risc->cpu, risc->dma); - memset(risc,0,sizeof(*risc)); -} - -int btcx_riscmem_alloc(struct pci_dev *pci, - struct btcx_riscmem *risc, - unsigned int size) -{ - __le32 *cpu; - dma_addr_t dma = 0; - - if (NULL != risc->cpu && risc->size < size) - btcx_riscmem_free(pci,risc); - if (NULL == risc->cpu) { - cpu = pci_alloc_consistent(pci, size, &dma); - if (NULL == cpu) - return -ENOMEM; - risc->cpu = cpu; - risc->dma = dma; - risc->size = size; - if (debug) { - memcnt++; - printk("btcx: riscmem alloc [%d] dma=%lx cpu=%p size=%d\n", - memcnt, (unsigned long)dma, cpu, size); - } - } - memset(risc->cpu,0,risc->size); - return 0; -} - -/* ---------------------------------------------------------- */ -/* screen overlay helpers */ - -int -btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, - struct v4l2_clip *clips, unsigned int n) -{ - if (win->left < 0) { - /* left */ - clips[n].c.left = 0; - clips[n].c.top = 0; - clips[n].c.width = -win->left; - clips[n].c.height = win->height; - n++; - } - if (win->left + win->width > swidth) { - /* right */ - clips[n].c.left = swidth - win->left; - clips[n].c.top = 0; - clips[n].c.width = win->width - clips[n].c.left; - clips[n].c.height = win->height; - n++; - } - if (win->top < 0) { - /* top */ - clips[n].c.left = 0; - clips[n].c.top = 0; - clips[n].c.width = win->width; - clips[n].c.height = -win->top; - n++; - } - if (win->top + win->height > sheight) { - /* bottom */ - clips[n].c.left = 0; - clips[n].c.top = sheight - win->top; - clips[n].c.width = win->width; - clips[n].c.height = win->height - clips[n].c.top; - n++; - } - return n; -} - -int -btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, unsigned int n, int mask) -{ - s32 nx,nw,dx; - unsigned int i; - - /* fixup window */ - nx = (win->left + mask) & ~mask; - nw = (win->width) & ~mask; - if (nx + nw > win->left + win->width) - nw -= mask+1; - dx = nx - win->left; - win->left = nx; - win->width = nw; - if (debug) - printk(KERN_DEBUG "btcx: window align %dx%d+%d+%d [dx=%d]\n", - win->width, win->height, win->left, win->top, dx); - - /* fixup clips */ - for (i = 0; i < n; i++) { - nx = (clips[i].c.left-dx) & ~mask; - nw = (clips[i].c.width) & ~mask; - if (nx + nw < clips[i].c.left-dx + clips[i].c.width) - nw += mask+1; - clips[i].c.left = nx; - clips[i].c.width = nw; - if (debug) - printk(KERN_DEBUG "btcx: clip align %dx%d+%d+%d\n", - clips[i].c.width, clips[i].c.height, - clips[i].c.left, clips[i].c.top); - } - return 0; -} - -void -btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips) -{ - struct v4l2_clip swap; - int i,j,n; - - if (nclips < 2) - return; - for (i = nclips-2; i >= 0; i--) { - for (n = 0, j = 0; j <= i; j++) { - if (clips[j].c.left > clips[j+1].c.left) { - swap = clips[j]; - clips[j] = clips[j+1]; - clips[j+1] = swap; - n++; - } - } - if (0 == n) - break; - } -} - -void -btcx_calc_skips(int line, int width, int *maxy, - struct btcx_skiplist *skips, unsigned int *nskips, - const struct v4l2_clip *clips, unsigned int nclips) -{ - unsigned int clip,skip; - int end, maxline; - - skip=0; - maxline = 9999; - for (clip = 0; clip < nclips; clip++) { - - /* sanity checks */ - if (clips[clip].c.left + clips[clip].c.width <= 0) - continue; - if (clips[clip].c.left > (signed)width) - break; - - /* vertical range */ - if (line > clips[clip].c.top+clips[clip].c.height-1) - continue; - if (line < clips[clip].c.top) { - if (maxline > clips[clip].c.top-1) - maxline = clips[clip].c.top-1; - continue; - } - if (maxline > clips[clip].c.top+clips[clip].c.height-1) - maxline = clips[clip].c.top+clips[clip].c.height-1; - - /* horizontal range */ - if (0 == skip || clips[clip].c.left > skips[skip-1].end) { - /* new one */ - skips[skip].start = clips[clip].c.left; - if (skips[skip].start < 0) - skips[skip].start = 0; - skips[skip].end = clips[clip].c.left + clips[clip].c.width; - if (skips[skip].end > width) - skips[skip].end = width; - skip++; - } else { - /* overlaps -- expand last one */ - end = clips[clip].c.left + clips[clip].c.width; - if (skips[skip-1].end < end) - skips[skip-1].end = end; - if (skips[skip-1].end > width) - skips[skip-1].end = width; - } - } - *nskips = skip; - *maxy = maxline; - - if (debug) { - printk(KERN_DEBUG "btcx: skips line %d-%d:",line,maxline); - for (skip = 0; skip < *nskips; skip++) { - printk(" %d-%d",skips[skip].start,skips[skip].end); - } - printk("\n"); - } -} - -/* ---------------------------------------------------------- */ - -EXPORT_SYMBOL(btcx_riscmem_alloc); -EXPORT_SYMBOL(btcx_riscmem_free); - -EXPORT_SYMBOL(btcx_screen_clips); -EXPORT_SYMBOL(btcx_align); -EXPORT_SYMBOL(btcx_sort_clips); -EXPORT_SYMBOL(btcx_calc_skips); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/i2c/btcx-risc.h b/drivers/media/i2c/btcx-risc.h deleted file mode 100644 index f8bc6e8e7b51..000000000000 --- a/drivers/media/i2c/btcx-risc.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - */ -struct btcx_riscmem { - unsigned int size; - __le32 *cpu; - __le32 *jmp; - dma_addr_t dma; -}; - -struct btcx_skiplist { - int start; - int end; -}; - -int btcx_riscmem_alloc(struct pci_dev *pci, - struct btcx_riscmem *risc, - unsigned int size); -void btcx_riscmem_free(struct pci_dev *pci, - struct btcx_riscmem *risc); - -int btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, - struct v4l2_clip *clips, unsigned int n); -int btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, - unsigned int n, int mask); -void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips); -void btcx_calc_skips(int line, int width, int *maxy, - struct btcx_skiplist *skips, unsigned int *nskips, - const struct v4l2_clip *clips, unsigned int nclips); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/bt8xx/Makefile b/drivers/media/pci/bt8xx/Makefile index 5f06597c6a6e..f9fe7c4e7d53 100644 --- a/drivers/media/pci/bt8xx/Makefile +++ b/drivers/media/pci/bt8xx/Makefile @@ -8,4 +8,5 @@ obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o dst_ca.o ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends ccflags-y += -Idrivers/media/i2c +ccflags-y += -Idrivers/media/common ccflags-y += -Idrivers/media/tuners diff --git a/drivers/media/pci/cx23885/Makefile b/drivers/media/pci/cx23885/Makefile index a2cbdcf15a8c..2a2cafb8cf5b 100644 --- a/drivers/media/pci/cx23885/Makefile +++ b/drivers/media/pci/cx23885/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_VIDEO_CX23885) += cx23885.o obj-$(CONFIG_MEDIA_ALTERA_CI) += altera-ci.o ccflags-y += -Idrivers/media/i2c +ccflags-y += -Idrivers/media/common ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/pci/cx25821/Makefile b/drivers/media/pci/cx25821/Makefile index 5bf3ea4c1556..caa32b7b51f8 100644 --- a/drivers/media/pci/cx25821/Makefile +++ b/drivers/media/pci/cx25821/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_VIDEO_CX25821) += cx25821.o obj-$(CONFIG_VIDEO_CX25821_ALSA) += cx25821-alsa.o ccflags-y += -Idrivers/media/i2c +ccflags-y += -Idrivers/media/common ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/pci/cx88/Makefile b/drivers/media/pci/cx88/Makefile index d3679c3ee248..8619c1becee2 100644 --- a/drivers/media/pci/cx88/Makefile +++ b/drivers/media/pci/cx88/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o ccflags-y += -Idrivers/media/i2c +ccflags-y += -Idrivers/media/common ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends -- cgit v1.2.3 From a393edad2a1d5a53918bfb5ac1608fb8d17068dc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 6 Feb 2013 08:03:41 -0300 Subject: [media] tveeprom: move from media/i2c to media/common The tveeprom module is a helper module for Hauppauge-based eeproms. It's used by many drivers and the i2c part is actually optional, so this driver is better placed in the media/common directory. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/Kconfig | 4 + drivers/media/common/Makefile | 1 + drivers/media/common/tveeprom.c | 792 ++++++++++++++++++++++++++++++++++++++++ drivers/media/i2c/Kconfig | 8 - drivers/media/i2c/Makefile | 1 - drivers/media/i2c/tveeprom.c | 792 ---------------------------------------- 6 files changed, 797 insertions(+), 801 deletions(-) create mode 100644 drivers/media/common/tveeprom.c delete mode 100644 drivers/media/i2c/tveeprom.c (limited to 'drivers') diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index 28c8a602e7ef..56c25e6299e9 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig @@ -12,6 +12,10 @@ config VIDEO_BTCX depends on PCI tristate +config VIDEO_TVEEPROM + tristate + depends on I2C + source "drivers/media/common/b2c2/Kconfig" source "drivers/media/common/saa7146/Kconfig" source "drivers/media/common/siano/Kconfig" diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index cfe34499d993..8f8d18755d15 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -1,3 +1,4 @@ obj-y += b2c2/ saa7146/ siano/ obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o +obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o diff --git a/drivers/media/common/tveeprom.c b/drivers/media/common/tveeprom.c new file mode 100644 index 000000000000..3b6cf034976a --- /dev/null +++ b/drivers/media/common/tveeprom.c @@ -0,0 +1,792 @@ +/* + * tveeprom - eeprom decoder for tvcard configuration eeproms + * + * Data and decoding routines shamelessly borrowed from bttv-cards.c + * eeprom access routine shamelessly borrowed from bttv-if.c + * which are: + + Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) + & Marcus Metzler (mocm@thp.uni-koeln.de) + (c) 1999-2001 Gerd Knorr + + * Adjustments to fit a more general model and all bugs: + + Copyright (C) 2003 John Klar + + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +MODULE_DESCRIPTION("i2c Hauppauge eeprom decoder driver"); +MODULE_AUTHOR("John Klar"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#define STRM(array, i) \ + (i < sizeof(array) / sizeof(char *) ? array[i] : "unknown") + +#define tveeprom_info(fmt, arg...) \ + v4l_printk(KERN_INFO, "tveeprom", c->adapter, c->addr, fmt , ## arg) +#define tveeprom_warn(fmt, arg...) \ + v4l_printk(KERN_WARNING, "tveeprom", c->adapter, c->addr, fmt , ## arg) +#define tveeprom_dbg(fmt, arg...) do { \ + if (debug) \ + v4l_printk(KERN_DEBUG, "tveeprom", \ + c->adapter, c->addr, fmt , ## arg); \ + } while (0) + +/* + * The Hauppauge eeprom uses an 8bit field to determine which + * tuner formats the tuner supports. + */ +static struct HAUPPAUGE_TUNER_FMT +{ + int id; + char *name; +} +hauppauge_tuner_fmt[] = +{ + { V4L2_STD_UNKNOWN, " UNKNOWN" }, + { V4L2_STD_UNKNOWN, " FM" }, + { V4L2_STD_B|V4L2_STD_GH, " PAL(B/G)" }, + { V4L2_STD_MN, " NTSC(M)" }, + { V4L2_STD_PAL_I, " PAL(I)" }, + { V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC, " SECAM(L/L')" }, + { V4L2_STD_DK, " PAL(D/D1/K)" }, + { V4L2_STD_ATSC, " ATSC/DVB Digital" }, +}; + +/* This is the full list of possible tuners. Many thanks to Hauppauge for + supplying this information. Note that many tuners where only used for + testing and never made it to the outside world. So you will only see + a subset in actual produced cards. */ +static struct HAUPPAUGE_TUNER +{ + int id; + char *name; +} +hauppauge_tuner[] = +{ + /* 0-9 */ + { TUNER_ABSENT, "None" }, + { TUNER_ABSENT, "External" }, + { TUNER_ABSENT, "Unspecified" }, + { TUNER_PHILIPS_PAL, "Philips FI1216" }, + { TUNER_PHILIPS_SECAM, "Philips FI1216MF" }, + { TUNER_PHILIPS_NTSC, "Philips FI1236" }, + { TUNER_PHILIPS_PAL_I, "Philips FI1246" }, + { TUNER_PHILIPS_PAL_DK, "Philips FI1256" }, + { TUNER_PHILIPS_PAL, "Philips FI1216 MK2" }, + { TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2" }, + /* 10-19 */ + { TUNER_PHILIPS_NTSC, "Philips FI1236 MK2" }, + { TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2" }, + { TUNER_PHILIPS_PAL_DK, "Philips FI1256 MK2" }, + { TUNER_TEMIC_NTSC, "Temic 4032FY5" }, + { TUNER_TEMIC_PAL, "Temic 4002FH5" }, + { TUNER_TEMIC_PAL_I, "Temic 4062FY5" }, + { TUNER_PHILIPS_PAL, "Philips FR1216 MK2" }, + { TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2" }, + { TUNER_PHILIPS_NTSC, "Philips FR1236 MK2" }, + { TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2" }, + /* 20-29 */ + { TUNER_PHILIPS_PAL_DK, "Philips FR1256 MK2" }, + { TUNER_PHILIPS_PAL, "Philips FM1216" }, + { TUNER_PHILIPS_SECAM, "Philips FM1216MF" }, + { TUNER_PHILIPS_NTSC, "Philips FM1236" }, + { TUNER_PHILIPS_PAL_I, "Philips FM1246" }, + { TUNER_PHILIPS_PAL_DK, "Philips FM1256" }, + { TUNER_TEMIC_4036FY5_NTSC, "Temic 4036FY5" }, + { TUNER_ABSENT, "Samsung TCPN9082D" }, + { TUNER_ABSENT, "Samsung TCPM9092P" }, + { TUNER_TEMIC_4006FH5_PAL, "Temic 4006FH5" }, + /* 30-39 */ + { TUNER_ABSENT, "Samsung TCPN9085D" }, + { TUNER_ABSENT, "Samsung TCPB9085P" }, + { TUNER_ABSENT, "Samsung TCPL9091P" }, + { TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5" }, + { TUNER_PHILIPS_FQ1216ME, "Philips FQ1216 ME" }, + { TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5" }, + { TUNER_PHILIPS_NTSC, "Philips TD1536" }, + { TUNER_PHILIPS_NTSC, "Philips TD1536D" }, + { TUNER_PHILIPS_NTSC, "Philips FMR1236" }, /* mono radio */ + { TUNER_ABSENT, "Philips FI1256MP" }, + /* 40-49 */ + { TUNER_ABSENT, "Samsung TCPQ9091P" }, + { TUNER_TEMIC_4006FN5_MULTI_PAL, "Temic 4006FN5" }, + { TUNER_TEMIC_4009FR5_PAL, "Temic 4009FR5" }, + { TUNER_TEMIC_4046FM5, "Temic 4046FM5" }, + { TUNER_TEMIC_4009FN5_MULTI_PAL_FM, "Temic 4009FN5" }, + { TUNER_ABSENT, "Philips TD1536D FH 44"}, + { TUNER_LG_NTSC_FM, "LG TP18NSR01F"}, + { TUNER_LG_PAL_FM, "LG TP18PSB01D"}, + { TUNER_LG_PAL, "LG TP18PSB11D"}, + { TUNER_LG_PAL_I_FM, "LG TAPC-I001D"}, + /* 50-59 */ + { TUNER_LG_PAL_I, "LG TAPC-I701D"}, + { TUNER_ABSENT, "Temic 4042FI5"}, + { TUNER_MICROTUNE_4049FM5, "Microtune 4049 FM5"}, + { TUNER_ABSENT, "LG TPI8NSR11F"}, + { TUNER_ABSENT, "Microtune 4049 FM5 Alt I2C"}, + { TUNER_PHILIPS_FM1216ME_MK3, "Philips FQ1216ME MK3"}, + { TUNER_ABSENT, "Philips FI1236 MK3"}, + { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216 ME MK3"}, + { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK3"}, + { TUNER_ABSENT, "Philips FM1216MP MK3"}, + /* 60-69 */ + { TUNER_PHILIPS_FM1216ME_MK3, "LG S001D MK3"}, + { TUNER_ABSENT, "LG M001D MK3"}, + { TUNER_PHILIPS_FM1216ME_MK3, "LG S701D MK3"}, + { TUNER_ABSENT, "LG M701D MK3"}, + { TUNER_ABSENT, "Temic 4146FM5"}, + { TUNER_ABSENT, "Temic 4136FY5"}, + { TUNER_ABSENT, "Temic 4106FH5"}, + { TUNER_ABSENT, "Philips FQ1216LMP MK3"}, + { TUNER_LG_NTSC_TAPE, "LG TAPE H001F MK3"}, + { TUNER_LG_NTSC_TAPE, "LG TAPE H701F MK3"}, + /* 70-79 */ + { TUNER_ABSENT, "LG TALN H200T"}, + { TUNER_ABSENT, "LG TALN H250T"}, + { TUNER_ABSENT, "LG TALN M200T"}, + { TUNER_ABSENT, "LG TALN Z200T"}, + { TUNER_ABSENT, "LG TALN S200T"}, + { TUNER_ABSENT, "Thompson DTT7595"}, + { TUNER_ABSENT, "Thompson DTT7592"}, + { TUNER_ABSENT, "Silicon TDA8275C1 8290"}, + { TUNER_ABSENT, "Silicon TDA8275C1 8290 FM"}, + { TUNER_ABSENT, "Thompson DTT757"}, + /* 80-89 */ + { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK3"}, + { TUNER_LG_PAL_NEW_TAPC, "LG TAPC G701D"}, + { TUNER_LG_NTSC_NEW_TAPC, "LG TAPC H791F"}, + { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MB 3"}, + { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MI 3"}, + { TUNER_TCL_2002N, "TCL 2002N 6A"}, + { TUNER_PHILIPS_FM1236_MK3, "Philips FQ1236 MK3"}, + { TUNER_SAMSUNG_TCPN_2121P30A, "Samsung TCPN 2121P30A"}, + { TUNER_ABSENT, "Samsung TCPE 4121P30A"}, + { TUNER_PHILIPS_FM1216ME_MK3, "TCL MFPE05 2"}, + /* 90-99 */ + { TUNER_ABSENT, "LG TALN H202T"}, + { TUNER_PHILIPS_FQ1216AME_MK4, "Philips FQ1216AME MK4"}, + { TUNER_PHILIPS_FQ1236A_MK4, "Philips FQ1236A MK4"}, + { TUNER_ABSENT, "Philips FQ1286A MK4"}, + { TUNER_ABSENT, "Philips FQ1216ME MK5"}, + { TUNER_ABSENT, "Philips FQ1236 MK5"}, + { TUNER_SAMSUNG_TCPG_6121P30A, "Samsung TCPG 6121P30A"}, + { TUNER_TCL_2002MB, "TCL 2002MB_3H"}, + { TUNER_ABSENT, "TCL 2002MI_3H"}, + { TUNER_TCL_2002N, "TCL 2002N 5H"}, + /* 100-109 */ + { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216ME"}, + { TUNER_TEA5767, "Philips TEA5768HL FM Radio"}, + { TUNER_ABSENT, "Panasonic ENV57H12D5"}, + { TUNER_PHILIPS_FM1236_MK3, "TCL MFNM05-4"}, + { TUNER_PHILIPS_FM1236_MK3, "TCL MNM05-4"}, + { TUNER_PHILIPS_FM1216ME_MK3, "TCL MPE05-2"}, + { TUNER_ABSENT, "TCL MQNM05-4"}, + { TUNER_ABSENT, "LG TAPC-W701D"}, + { TUNER_ABSENT, "TCL 9886P-WM"}, + { TUNER_ABSENT, "TCL 1676NM-WM"}, + /* 110-119 */ + { TUNER_ABSENT, "Thompson DTT75105"}, + { TUNER_ABSENT, "Conexant_CX24109"}, + { TUNER_TCL_2002N, "TCL M2523_5N_E"}, + { TUNER_TCL_2002MB, "TCL M2523_3DB_E"}, + { TUNER_ABSENT, "Philips 8275A"}, + { TUNER_ABSENT, "Microtune MT2060"}, + { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK5"}, + { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216ME MK5"}, + { TUNER_ABSENT, "TCL M2523_3DI_E"}, + { TUNER_ABSENT, "Samsung THPD5222FG30A"}, + /* 120-129 */ + { TUNER_XC2028, "Xceive XC3028"}, + { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK5"}, + { TUNER_ABSENT, "Philips FQD1216LME"}, + { TUNER_ABSENT, "Conexant CX24118A"}, + { TUNER_ABSENT, "TCL DMF11WIP"}, + { TUNER_ABSENT, "TCL MFNM05_4H_E"}, + { TUNER_ABSENT, "TCL MNM05_4H_E"}, + { TUNER_ABSENT, "TCL MPE05_2H_E"}, + { TUNER_ABSENT, "TCL MQNM05_4_U"}, + { TUNER_ABSENT, "TCL M2523_5NH_E"}, + /* 130-139 */ + { TUNER_ABSENT, "TCL M2523_3DBH_E"}, + { TUNER_ABSENT, "TCL M2523_3DIH_E"}, + { TUNER_ABSENT, "TCL MFPE05_2_U"}, + { TUNER_PHILIPS_FMD1216MEX_MK3, "Philips FMD1216MEX"}, + { TUNER_ABSENT, "Philips FRH2036B"}, + { TUNER_ABSENT, "Panasonic ENGF75_01GF"}, + { TUNER_ABSENT, "MaxLinear MXL5005"}, + { TUNER_ABSENT, "MaxLinear MXL5003"}, + { TUNER_ABSENT, "Xceive XC2028"}, + { TUNER_ABSENT, "Microtune MT2131"}, + /* 140-149 */ + { TUNER_ABSENT, "Philips 8275A_8295"}, + { TUNER_ABSENT, "TCL MF02GIP_5N_E"}, + { TUNER_ABSENT, "TCL MF02GIP_3DB_E"}, + { TUNER_ABSENT, "TCL MF02GIP_3DI_E"}, + { TUNER_ABSENT, "Microtune MT2266"}, + { TUNER_ABSENT, "TCL MF10WPP_4N_E"}, + { TUNER_ABSENT, "LG TAPQ_H702F"}, + { TUNER_ABSENT, "TCL M09WPP_4N_E"}, + { TUNER_ABSENT, "MaxLinear MXL5005_v2"}, + { TUNER_PHILIPS_TDA8290, "Philips 18271_8295"}, + /* 150-159 */ + { TUNER_XC5000, "Xceive XC5000"}, + { TUNER_ABSENT, "Xceive XC3028L"}, + { TUNER_ABSENT, "NXP 18271C2_716x"}, + { TUNER_ABSENT, "Xceive XC4000"}, + { TUNER_ABSENT, "Dibcom 7070"}, + { TUNER_PHILIPS_TDA8290, "NXP 18271C2"}, + { TUNER_ABSENT, "Siano SMS1010"}, + { TUNER_ABSENT, "Siano SMS1150"}, + { TUNER_ABSENT, "MaxLinear 5007"}, + { TUNER_ABSENT, "TCL M09WPP_2P_E"}, + /* 160-169 */ + { TUNER_ABSENT, "Siano SMS1180"}, + { TUNER_ABSENT, "Maxim_MAX2165"}, + { TUNER_ABSENT, "Siano SMS1140"}, + { TUNER_ABSENT, "Siano SMS1150 B1"}, + { TUNER_ABSENT, "MaxLinear 111"}, + { TUNER_ABSENT, "Dibcom 7770"}, + { TUNER_ABSENT, "Siano SMS1180VNS"}, + { TUNER_ABSENT, "Siano SMS1184"}, + { TUNER_PHILIPS_FQ1236_MK5, "TCL M30WTP-4N-E"}, + { TUNER_ABSENT, "TCL_M11WPP_2PN_E"}, + /* 170-179 */ + { TUNER_ABSENT, "MaxLinear 301"}, + { TUNER_ABSENT, "Mirics MSi001"}, + { TUNER_ABSENT, "MaxLinear MxL241SF"}, + { TUNER_XC5000C, "Xceive XC5000C"}, + { TUNER_ABSENT, "Montage M68TS2020"}, + { TUNER_ABSENT, "Siano SMS1530"}, + { TUNER_ABSENT, "Dibcom 7090"}, + { TUNER_ABSENT, "Xceive XC5200C"}, + { TUNER_ABSENT, "NXP 18273"}, + { TUNER_ABSENT, "Montage M88TS2022"}, + /* 180-189 */ + { TUNER_ABSENT, "NXP 18272M"}, + { TUNER_ABSENT, "NXP 18272S"}, +}; + +/* Use V4L2_IDENT_AMBIGUOUS for those audio 'chips' that are + * internal to a video chip, i.e. not a separate audio chip. */ +static struct HAUPPAUGE_AUDIOIC +{ + u32 id; + char *name; +} +audioIC[] = +{ + /* 0-4 */ + { V4L2_IDENT_NONE, "None" }, + { V4L2_IDENT_UNKNOWN, "TEA6300" }, + { V4L2_IDENT_UNKNOWN, "TEA6320" }, + { V4L2_IDENT_UNKNOWN, "TDA9850" }, + { V4L2_IDENT_MSPX4XX, "MSP3400C" }, + /* 5-9 */ + { V4L2_IDENT_MSPX4XX, "MSP3410D" }, + { V4L2_IDENT_MSPX4XX, "MSP3415" }, + { V4L2_IDENT_MSPX4XX, "MSP3430" }, + { V4L2_IDENT_MSPX4XX, "MSP3438" }, + { V4L2_IDENT_UNKNOWN, "CS5331" }, + /* 10-14 */ + { V4L2_IDENT_MSPX4XX, "MSP3435" }, + { V4L2_IDENT_MSPX4XX, "MSP3440" }, + { V4L2_IDENT_MSPX4XX, "MSP3445" }, + { V4L2_IDENT_MSPX4XX, "MSP3411" }, + { V4L2_IDENT_MSPX4XX, "MSP3416" }, + /* 15-19 */ + { V4L2_IDENT_MSPX4XX, "MSP3425" }, + { V4L2_IDENT_MSPX4XX, "MSP3451" }, + { V4L2_IDENT_MSPX4XX, "MSP3418" }, + { V4L2_IDENT_UNKNOWN, "Type 0x12" }, + { V4L2_IDENT_UNKNOWN, "OKI7716" }, + /* 20-24 */ + { V4L2_IDENT_MSPX4XX, "MSP4410" }, + { V4L2_IDENT_MSPX4XX, "MSP4420" }, + { V4L2_IDENT_MSPX4XX, "MSP4440" }, + { V4L2_IDENT_MSPX4XX, "MSP4450" }, + { V4L2_IDENT_MSPX4XX, "MSP4408" }, + /* 25-29 */ + { V4L2_IDENT_MSPX4XX, "MSP4418" }, + { V4L2_IDENT_MSPX4XX, "MSP4428" }, + { V4L2_IDENT_MSPX4XX, "MSP4448" }, + { V4L2_IDENT_MSPX4XX, "MSP4458" }, + { V4L2_IDENT_MSPX4XX, "Type 0x1d" }, + /* 30-34 */ + { V4L2_IDENT_AMBIGUOUS, "CX880" }, + { V4L2_IDENT_AMBIGUOUS, "CX881" }, + { V4L2_IDENT_AMBIGUOUS, "CX883" }, + { V4L2_IDENT_AMBIGUOUS, "CX882" }, + { V4L2_IDENT_AMBIGUOUS, "CX25840" }, + /* 35-39 */ + { V4L2_IDENT_AMBIGUOUS, "CX25841" }, + { V4L2_IDENT_AMBIGUOUS, "CX25842" }, + { V4L2_IDENT_AMBIGUOUS, "CX25843" }, + { V4L2_IDENT_AMBIGUOUS, "CX23418" }, + { V4L2_IDENT_AMBIGUOUS, "CX23885" }, + /* 40-44 */ + { V4L2_IDENT_AMBIGUOUS, "CX23888" }, + { V4L2_IDENT_AMBIGUOUS, "SAA7131" }, + { V4L2_IDENT_AMBIGUOUS, "CX23887" }, + { V4L2_IDENT_AMBIGUOUS, "SAA7164" }, + { V4L2_IDENT_AMBIGUOUS, "AU8522" }, +}; + +/* This list is supplied by Hauppauge. Thanks! */ +static const char *decoderIC[] = { + /* 0-4 */ + "None", "BT815", "BT817", "BT819", "BT815A", + /* 5-9 */ + "BT817A", "BT819A", "BT827", "BT829", "BT848", + /* 10-14 */ + "BT848A", "BT849A", "BT829A", "BT827A", "BT878", + /* 15-19 */ + "BT879", "BT880", "VPX3226E", "SAA7114", "SAA7115", + /* 20-24 */ + "CX880", "CX881", "CX883", "SAA7111", "SAA7113", + /* 25-29 */ + "CX882", "TVP5150A", "CX25840", "CX25841", "CX25842", + /* 30-34 */ + "CX25843", "CX23418", "NEC61153", "CX23885", "CX23888", + /* 35-39 */ + "SAA7131", "CX25837", "CX23887", "CX23885A", "CX23887A", + /* 40-42 */ + "SAA7164", "CX23885B", "AU8522" +}; + +static int hasRadioTuner(int tunerType) +{ + switch (tunerType) { + case 18: /* PNPEnv_TUNER_FR1236_MK2 */ + case 23: /* PNPEnv_TUNER_FM1236 */ + case 38: /* PNPEnv_TUNER_FMR1236 */ + case 16: /* PNPEnv_TUNER_FR1216_MK2 */ + case 19: /* PNPEnv_TUNER_FR1246_MK2 */ + case 21: /* PNPEnv_TUNER_FM1216 */ + case 24: /* PNPEnv_TUNER_FM1246 */ + case 17: /* PNPEnv_TUNER_FR1216MF_MK2 */ + case 22: /* PNPEnv_TUNER_FM1216MF */ + case 20: /* PNPEnv_TUNER_FR1256_MK2 */ + case 25: /* PNPEnv_TUNER_FM1256 */ + case 33: /* PNPEnv_TUNER_4039FR5 */ + case 42: /* PNPEnv_TUNER_4009FR5 */ + case 52: /* PNPEnv_TUNER_4049FM5 */ + case 54: /* PNPEnv_TUNER_4049FM5_AltI2C */ + case 44: /* PNPEnv_TUNER_4009FN5 */ + case 31: /* PNPEnv_TUNER_TCPB9085P */ + case 30: /* PNPEnv_TUNER_TCPN9085D */ + case 46: /* PNPEnv_TUNER_TP18NSR01F */ + case 47: /* PNPEnv_TUNER_TP18PSB01D */ + case 49: /* PNPEnv_TUNER_TAPC_I001D */ + case 60: /* PNPEnv_TUNER_TAPE_S001D_MK3 */ + case 57: /* PNPEnv_TUNER_FM1216ME_MK3 */ + case 59: /* PNPEnv_TUNER_FM1216MP_MK3 */ + case 58: /* PNPEnv_TUNER_FM1236_MK3 */ + case 68: /* PNPEnv_TUNER_TAPE_H001F_MK3 */ + case 61: /* PNPEnv_TUNER_TAPE_M001D_MK3 */ + case 78: /* PNPEnv_TUNER_TDA8275C1_8290_FM */ + case 89: /* PNPEnv_TUNER_TCL_MFPE05_2 */ + case 92: /* PNPEnv_TUNER_PHILIPS_FQ1236A_MK4 */ + case 105: + return 1; + } + return 0; +} + +void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, + unsigned char *eeprom_data) +{ + /* ---------------------------------------------- + ** The hauppauge eeprom format is tagged + ** + ** if packet[0] == 0x84, then packet[0..1] == length + ** else length = packet[0] & 3f; + ** if packet[0] & f8 == f8, then EOD and packet[1] == checksum + ** + ** In our (ivtv) case we're interested in the following: + ** tuner type: tag [00].05 or [0a].01 (index into hauppauge_tuner) + ** tuner fmts: tag [00].04 or [0a].00 (bitmask index into + ** hauppauge_tuner_fmt) + ** radio: tag [00].{last} or [0e].00 (bitmask. bit2=FM) + ** audio proc: tag [02].01 or [05].00 (mask with 0x7f) + ** decoder proc: tag [09].01) + + ** Fun info: + ** model: tag [00].07-08 or [06].00-01 + ** revision: tag [00].09-0b or [06].04-06 + ** serial#: tag [01].05-07 or [04].04-06 + + ** # of inputs/outputs ??? + */ + + int i, j, len, done, beenhere, tag, start; + + int tuner1 = 0, t_format1 = 0, audioic = -1; + char *t_name1 = NULL; + const char *t_fmt_name1[8] = { " none", "", "", "", "", "", "", "" }; + + int tuner2 = 0, t_format2 = 0; + char *t_name2 = NULL; + const char *t_fmt_name2[8] = { " none", "", "", "", "", "", "", "" }; + + memset(tvee, 0, sizeof(*tvee)); + tvee->tuner_type = TUNER_ABSENT; + tvee->tuner2_type = TUNER_ABSENT; + + done = len = beenhere = 0; + + /* Different eeprom start offsets for em28xx, cx2388x and cx23418 */ + if (eeprom_data[0] == 0x1a && + eeprom_data[1] == 0xeb && + eeprom_data[2] == 0x67 && + eeprom_data[3] == 0x95) + start = 0xa0; /* Generic em28xx offset */ + else if ((eeprom_data[0] & 0xe1) == 0x01 && + eeprom_data[1] == 0x00 && + eeprom_data[2] == 0x00 && + eeprom_data[8] == 0x84) + start = 8; /* Generic cx2388x offset */ + else if (eeprom_data[1] == 0x70 && + eeprom_data[2] == 0x00 && + eeprom_data[4] == 0x74 && + eeprom_data[8] == 0x84) + start = 8; /* Generic cx23418 offset (models 74xxx) */ + else + start = 0; + + for (i = start; !done && i < 256; i += len) { + if (eeprom_data[i] == 0x84) { + len = eeprom_data[i + 1] + (eeprom_data[i + 2] << 8); + i += 3; + } else if ((eeprom_data[i] & 0xf0) == 0x70) { + if (eeprom_data[i] & 0x08) { + /* verify checksum! */ + done = 1; + break; + } + len = eeprom_data[i] & 0x07; + ++i; + } else { + tveeprom_warn("Encountered bad packet header [%02x]. " + "Corrupt or not a Hauppauge eeprom.\n", + eeprom_data[i]); + return; + } + + if (debug) { + tveeprom_info("Tag [%02x] + %d bytes:", + eeprom_data[i], len - 1); + for (j = 1; j < len; j++) + printk(KERN_CONT " %02x", eeprom_data[i + j]); + printk(KERN_CONT "\n"); + } + + /* process by tag */ + tag = eeprom_data[i]; + switch (tag) { + case 0x00: + /* tag: 'Comprehensive' */ + tuner1 = eeprom_data[i+6]; + t_format1 = eeprom_data[i+5]; + tvee->has_radio = eeprom_data[i+len-1]; + /* old style tag, don't know how to detect + IR presence, mark as unknown. */ + tvee->has_ir = 0; + tvee->model = + eeprom_data[i+8] + + (eeprom_data[i+9] << 8); + tvee->revision = eeprom_data[i+10] + + (eeprom_data[i+11] << 8) + + (eeprom_data[i+12] << 16); + break; + + case 0x01: + /* tag: 'SerialID' */ + tvee->serial_number = + eeprom_data[i+6] + + (eeprom_data[i+7] << 8) + + (eeprom_data[i+8] << 16); + break; + + case 0x02: + /* tag 'AudioInfo' + Note mask with 0x7F, high bit used on some older models + to indicate 4052 mux was removed in favor of using MSP + inputs directly. */ + audioic = eeprom_data[i+2] & 0x7f; + if (audioic < ARRAY_SIZE(audioIC)) + tvee->audio_processor = audioIC[audioic].id; + else + tvee->audio_processor = V4L2_IDENT_UNKNOWN; + break; + + /* case 0x03: tag 'EEInfo' */ + + case 0x04: + /* tag 'SerialID2' */ + tvee->serial_number = + eeprom_data[i+5] + + (eeprom_data[i+6] << 8) + + (eeprom_data[i+7] << 16); + + if ((eeprom_data[i + 8] & 0xf0) && + (tvee->serial_number < 0xffffff)) { + tvee->MAC_address[0] = 0x00; + tvee->MAC_address[1] = 0x0D; + tvee->MAC_address[2] = 0xFE; + tvee->MAC_address[3] = eeprom_data[i + 7]; + tvee->MAC_address[4] = eeprom_data[i + 6]; + tvee->MAC_address[5] = eeprom_data[i + 5]; + tvee->has_MAC_address = 1; + } + break; + + case 0x05: + /* tag 'Audio2' + Note mask with 0x7F, high bit used on some older models + to indicate 4052 mux was removed in favor of using MSP + inputs directly. */ + audioic = eeprom_data[i+1] & 0x7f; + if (audioic < ARRAY_SIZE(audioIC)) + tvee->audio_processor = audioIC[audioic].id; + else + tvee->audio_processor = V4L2_IDENT_UNKNOWN; + + break; + + case 0x06: + /* tag 'ModelRev' */ + tvee->model = + eeprom_data[i + 1] + + (eeprom_data[i + 2] << 8) + + (eeprom_data[i + 3] << 16) + + (eeprom_data[i + 4] << 24); + tvee->revision = + eeprom_data[i + 5] + + (eeprom_data[i + 6] << 8) + + (eeprom_data[i + 7] << 16); + break; + + case 0x07: + /* tag 'Details': according to Hauppauge not interesting + on any PCI-era or later boards. */ + break; + + /* there is no tag 0x08 defined */ + + case 0x09: + /* tag 'Video' */ + tvee->decoder_processor = eeprom_data[i + 1]; + break; + + case 0x0a: + /* tag 'Tuner' */ + if (beenhere == 0) { + tuner1 = eeprom_data[i + 2]; + t_format1 = eeprom_data[i + 1]; + beenhere = 1; + } else { + /* a second (radio) tuner may be present */ + tuner2 = eeprom_data[i + 2]; + t_format2 = eeprom_data[i + 1]; + /* not a TV tuner? */ + if (t_format2 == 0) + tvee->has_radio = 1; /* must be radio */ + } + break; + + case 0x0b: + /* tag 'Inputs': according to Hauppauge this is specific + to each driver family, so no good assumptions can be + made. */ + break; + + /* case 0x0c: tag 'Balun' */ + /* case 0x0d: tag 'Teletext' */ + + case 0x0e: + /* tag: 'Radio' */ + tvee->has_radio = eeprom_data[i+1]; + break; + + case 0x0f: + /* tag 'IRInfo' */ + tvee->has_ir = 1 | (eeprom_data[i+1] << 1); + break; + + /* case 0x10: tag 'VBIInfo' */ + /* case 0x11: tag 'QCInfo' */ + /* case 0x12: tag 'InfoBits' */ + + default: + tveeprom_dbg("Not sure what to do with tag [%02x]\n", + tag); + /* dump the rest of the packet? */ + } + } + + if (!done) { + tveeprom_warn("Ran out of data!\n"); + return; + } + + if (tvee->revision != 0) { + tvee->rev_str[0] = 32 + ((tvee->revision >> 18) & 0x3f); + tvee->rev_str[1] = 32 + ((tvee->revision >> 12) & 0x3f); + tvee->rev_str[2] = 32 + ((tvee->revision >> 6) & 0x3f); + tvee->rev_str[3] = 32 + (tvee->revision & 0x3f); + tvee->rev_str[4] = 0; + } + + if (hasRadioTuner(tuner1) && !tvee->has_radio) { + tveeprom_info("The eeprom says no radio is present, but the tuner type\n"); + tveeprom_info("indicates otherwise. I will assume that radio is present.\n"); + tvee->has_radio = 1; + } + + if (tuner1 < ARRAY_SIZE(hauppauge_tuner)) { + tvee->tuner_type = hauppauge_tuner[tuner1].id; + t_name1 = hauppauge_tuner[tuner1].name; + } else { + t_name1 = "unknown"; + } + + if (tuner2 < ARRAY_SIZE(hauppauge_tuner)) { + tvee->tuner2_type = hauppauge_tuner[tuner2].id; + t_name2 = hauppauge_tuner[tuner2].name; + } else { + t_name2 = "unknown"; + } + + tvee->tuner_hauppauge_model = tuner1; + tvee->tuner2_hauppauge_model = tuner2; + tvee->tuner_formats = 0; + tvee->tuner2_formats = 0; + for (i = j = 0; i < 8; i++) { + if (t_format1 & (1 << i)) { + tvee->tuner_formats |= hauppauge_tuner_fmt[i].id; + t_fmt_name1[j++] = hauppauge_tuner_fmt[i].name; + } + } + for (i = j = 0; i < 8; i++) { + if (t_format2 & (1 << i)) { + tvee->tuner2_formats |= hauppauge_tuner_fmt[i].id; + t_fmt_name2[j++] = hauppauge_tuner_fmt[i].name; + } + } + + tveeprom_info("Hauppauge model %d, rev %s, serial# %d\n", + tvee->model, tvee->rev_str, tvee->serial_number); + if (tvee->has_MAC_address == 1) + tveeprom_info("MAC address is %pM\n", tvee->MAC_address); + tveeprom_info("tuner model is %s (idx %d, type %d)\n", + t_name1, tuner1, tvee->tuner_type); + tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n", + t_fmt_name1[0], t_fmt_name1[1], t_fmt_name1[2], + t_fmt_name1[3], t_fmt_name1[4], t_fmt_name1[5], + t_fmt_name1[6], t_fmt_name1[7], t_format1); + if (tuner2) + tveeprom_info("second tuner model is %s (idx %d, type %d)\n", + t_name2, tuner2, tvee->tuner2_type); + if (t_format2) + tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n", + t_fmt_name2[0], t_fmt_name2[1], t_fmt_name2[2], + t_fmt_name2[3], t_fmt_name2[4], t_fmt_name2[5], + t_fmt_name2[6], t_fmt_name2[7], t_format2); + if (audioic < 0) { + tveeprom_info("audio processor is unknown (no idx)\n"); + tvee->audio_processor = V4L2_IDENT_UNKNOWN; + } else { + if (audioic < ARRAY_SIZE(audioIC)) + tveeprom_info("audio processor is %s (idx %d)\n", + audioIC[audioic].name, audioic); + else + tveeprom_info("audio processor is unknown (idx %d)\n", + audioic); + } + if (tvee->decoder_processor) + tveeprom_info("decoder processor is %s (idx %d)\n", + STRM(decoderIC, tvee->decoder_processor), + tvee->decoder_processor); + if (tvee->has_ir) + tveeprom_info("has %sradio, has %sIR receiver, has %sIR transmitter\n", + tvee->has_radio ? "" : "no ", + (tvee->has_ir & 2) ? "" : "no ", + (tvee->has_ir & 4) ? "" : "no "); + else + tveeprom_info("has %sradio\n", + tvee->has_radio ? "" : "no "); +} +EXPORT_SYMBOL(tveeprom_hauppauge_analog); + +/* ----------------------------------------------------------------------- */ +/* generic helper functions */ + +int tveeprom_read(struct i2c_client *c, unsigned char *eedata, int len) +{ + unsigned char buf; + int err; + + buf = 0; + err = i2c_master_send(c, &buf, 1); + if (err != 1) { + tveeprom_info("Huh, no eeprom present (err=%d)?\n", err); + return -1; + } + err = i2c_master_recv(c, eedata, len); + if (err != len) { + tveeprom_warn("i2c eeprom read error (err=%d)\n", err); + return -1; + } + if (debug) { + int i; + + tveeprom_info("full 256-byte eeprom dump:\n"); + for (i = 0; i < len; i++) { + if (0 == (i % 16)) + tveeprom_info("%02x:", i); + printk(KERN_CONT " %02x", eedata[i]); + if (15 == (i % 16)) + printk(KERN_CONT "\n"); + } + } + return 0; +} +EXPORT_SYMBOL(tveeprom_read); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index cd2b17148ad6..7b771baa2212 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1,11 +1,3 @@ -# -# Generic video config states -# - -config VIDEO_TVEEPROM - tristate - depends on I2C - # # Multimedia Video device configuration # diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 3691e70f4e75..cfefd30cc1bc 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -49,7 +49,6 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_VIDEO_OV7670) += ov7670.o obj-$(CONFIG_VIDEO_OV9650) += ov9650.o obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o -obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o diff --git a/drivers/media/i2c/tveeprom.c b/drivers/media/i2c/tveeprom.c deleted file mode 100644 index 3b6cf034976a..000000000000 --- a/drivers/media/i2c/tveeprom.c +++ /dev/null @@ -1,792 +0,0 @@ -/* - * tveeprom - eeprom decoder for tvcard configuration eeproms - * - * Data and decoding routines shamelessly borrowed from bttv-cards.c - * eeprom access routine shamelessly borrowed from bttv-if.c - * which are: - - Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) - & Marcus Metzler (mocm@thp.uni-koeln.de) - (c) 1999-2001 Gerd Knorr - - * Adjustments to fit a more general model and all bugs: - - Copyright (C) 2003 John Klar - - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -MODULE_DESCRIPTION("i2c Hauppauge eeprom decoder driver"); -MODULE_AUTHOR("John Klar"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -#define STRM(array, i) \ - (i < sizeof(array) / sizeof(char *) ? array[i] : "unknown") - -#define tveeprom_info(fmt, arg...) \ - v4l_printk(KERN_INFO, "tveeprom", c->adapter, c->addr, fmt , ## arg) -#define tveeprom_warn(fmt, arg...) \ - v4l_printk(KERN_WARNING, "tveeprom", c->adapter, c->addr, fmt , ## arg) -#define tveeprom_dbg(fmt, arg...) do { \ - if (debug) \ - v4l_printk(KERN_DEBUG, "tveeprom", \ - c->adapter, c->addr, fmt , ## arg); \ - } while (0) - -/* - * The Hauppauge eeprom uses an 8bit field to determine which - * tuner formats the tuner supports. - */ -static struct HAUPPAUGE_TUNER_FMT -{ - int id; - char *name; -} -hauppauge_tuner_fmt[] = -{ - { V4L2_STD_UNKNOWN, " UNKNOWN" }, - { V4L2_STD_UNKNOWN, " FM" }, - { V4L2_STD_B|V4L2_STD_GH, " PAL(B/G)" }, - { V4L2_STD_MN, " NTSC(M)" }, - { V4L2_STD_PAL_I, " PAL(I)" }, - { V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC, " SECAM(L/L')" }, - { V4L2_STD_DK, " PAL(D/D1/K)" }, - { V4L2_STD_ATSC, " ATSC/DVB Digital" }, -}; - -/* This is the full list of possible tuners. Many thanks to Hauppauge for - supplying this information. Note that many tuners where only used for - testing and never made it to the outside world. So you will only see - a subset in actual produced cards. */ -static struct HAUPPAUGE_TUNER -{ - int id; - char *name; -} -hauppauge_tuner[] = -{ - /* 0-9 */ - { TUNER_ABSENT, "None" }, - { TUNER_ABSENT, "External" }, - { TUNER_ABSENT, "Unspecified" }, - { TUNER_PHILIPS_PAL, "Philips FI1216" }, - { TUNER_PHILIPS_SECAM, "Philips FI1216MF" }, - { TUNER_PHILIPS_NTSC, "Philips FI1236" }, - { TUNER_PHILIPS_PAL_I, "Philips FI1246" }, - { TUNER_PHILIPS_PAL_DK, "Philips FI1256" }, - { TUNER_PHILIPS_PAL, "Philips FI1216 MK2" }, - { TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2" }, - /* 10-19 */ - { TUNER_PHILIPS_NTSC, "Philips FI1236 MK2" }, - { TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2" }, - { TUNER_PHILIPS_PAL_DK, "Philips FI1256 MK2" }, - { TUNER_TEMIC_NTSC, "Temic 4032FY5" }, - { TUNER_TEMIC_PAL, "Temic 4002FH5" }, - { TUNER_TEMIC_PAL_I, "Temic 4062FY5" }, - { TUNER_PHILIPS_PAL, "Philips FR1216 MK2" }, - { TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2" }, - { TUNER_PHILIPS_NTSC, "Philips FR1236 MK2" }, - { TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2" }, - /* 20-29 */ - { TUNER_PHILIPS_PAL_DK, "Philips FR1256 MK2" }, - { TUNER_PHILIPS_PAL, "Philips FM1216" }, - { TUNER_PHILIPS_SECAM, "Philips FM1216MF" }, - { TUNER_PHILIPS_NTSC, "Philips FM1236" }, - { TUNER_PHILIPS_PAL_I, "Philips FM1246" }, - { TUNER_PHILIPS_PAL_DK, "Philips FM1256" }, - { TUNER_TEMIC_4036FY5_NTSC, "Temic 4036FY5" }, - { TUNER_ABSENT, "Samsung TCPN9082D" }, - { TUNER_ABSENT, "Samsung TCPM9092P" }, - { TUNER_TEMIC_4006FH5_PAL, "Temic 4006FH5" }, - /* 30-39 */ - { TUNER_ABSENT, "Samsung TCPN9085D" }, - { TUNER_ABSENT, "Samsung TCPB9085P" }, - { TUNER_ABSENT, "Samsung TCPL9091P" }, - { TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5" }, - { TUNER_PHILIPS_FQ1216ME, "Philips FQ1216 ME" }, - { TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5" }, - { TUNER_PHILIPS_NTSC, "Philips TD1536" }, - { TUNER_PHILIPS_NTSC, "Philips TD1536D" }, - { TUNER_PHILIPS_NTSC, "Philips FMR1236" }, /* mono radio */ - { TUNER_ABSENT, "Philips FI1256MP" }, - /* 40-49 */ - { TUNER_ABSENT, "Samsung TCPQ9091P" }, - { TUNER_TEMIC_4006FN5_MULTI_PAL, "Temic 4006FN5" }, - { TUNER_TEMIC_4009FR5_PAL, "Temic 4009FR5" }, - { TUNER_TEMIC_4046FM5, "Temic 4046FM5" }, - { TUNER_TEMIC_4009FN5_MULTI_PAL_FM, "Temic 4009FN5" }, - { TUNER_ABSENT, "Philips TD1536D FH 44"}, - { TUNER_LG_NTSC_FM, "LG TP18NSR01F"}, - { TUNER_LG_PAL_FM, "LG TP18PSB01D"}, - { TUNER_LG_PAL, "LG TP18PSB11D"}, - { TUNER_LG_PAL_I_FM, "LG TAPC-I001D"}, - /* 50-59 */ - { TUNER_LG_PAL_I, "LG TAPC-I701D"}, - { TUNER_ABSENT, "Temic 4042FI5"}, - { TUNER_MICROTUNE_4049FM5, "Microtune 4049 FM5"}, - { TUNER_ABSENT, "LG TPI8NSR11F"}, - { TUNER_ABSENT, "Microtune 4049 FM5 Alt I2C"}, - { TUNER_PHILIPS_FM1216ME_MK3, "Philips FQ1216ME MK3"}, - { TUNER_ABSENT, "Philips FI1236 MK3"}, - { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216 ME MK3"}, - { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK3"}, - { TUNER_ABSENT, "Philips FM1216MP MK3"}, - /* 60-69 */ - { TUNER_PHILIPS_FM1216ME_MK3, "LG S001D MK3"}, - { TUNER_ABSENT, "LG M001D MK3"}, - { TUNER_PHILIPS_FM1216ME_MK3, "LG S701D MK3"}, - { TUNER_ABSENT, "LG M701D MK3"}, - { TUNER_ABSENT, "Temic 4146FM5"}, - { TUNER_ABSENT, "Temic 4136FY5"}, - { TUNER_ABSENT, "Temic 4106FH5"}, - { TUNER_ABSENT, "Philips FQ1216LMP MK3"}, - { TUNER_LG_NTSC_TAPE, "LG TAPE H001F MK3"}, - { TUNER_LG_NTSC_TAPE, "LG TAPE H701F MK3"}, - /* 70-79 */ - { TUNER_ABSENT, "LG TALN H200T"}, - { TUNER_ABSENT, "LG TALN H250T"}, - { TUNER_ABSENT, "LG TALN M200T"}, - { TUNER_ABSENT, "LG TALN Z200T"}, - { TUNER_ABSENT, "LG TALN S200T"}, - { TUNER_ABSENT, "Thompson DTT7595"}, - { TUNER_ABSENT, "Thompson DTT7592"}, - { TUNER_ABSENT, "Silicon TDA8275C1 8290"}, - { TUNER_ABSENT, "Silicon TDA8275C1 8290 FM"}, - { TUNER_ABSENT, "Thompson DTT757"}, - /* 80-89 */ - { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK3"}, - { TUNER_LG_PAL_NEW_TAPC, "LG TAPC G701D"}, - { TUNER_LG_NTSC_NEW_TAPC, "LG TAPC H791F"}, - { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MB 3"}, - { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MI 3"}, - { TUNER_TCL_2002N, "TCL 2002N 6A"}, - { TUNER_PHILIPS_FM1236_MK3, "Philips FQ1236 MK3"}, - { TUNER_SAMSUNG_TCPN_2121P30A, "Samsung TCPN 2121P30A"}, - { TUNER_ABSENT, "Samsung TCPE 4121P30A"}, - { TUNER_PHILIPS_FM1216ME_MK3, "TCL MFPE05 2"}, - /* 90-99 */ - { TUNER_ABSENT, "LG TALN H202T"}, - { TUNER_PHILIPS_FQ1216AME_MK4, "Philips FQ1216AME MK4"}, - { TUNER_PHILIPS_FQ1236A_MK4, "Philips FQ1236A MK4"}, - { TUNER_ABSENT, "Philips FQ1286A MK4"}, - { TUNER_ABSENT, "Philips FQ1216ME MK5"}, - { TUNER_ABSENT, "Philips FQ1236 MK5"}, - { TUNER_SAMSUNG_TCPG_6121P30A, "Samsung TCPG 6121P30A"}, - { TUNER_TCL_2002MB, "TCL 2002MB_3H"}, - { TUNER_ABSENT, "TCL 2002MI_3H"}, - { TUNER_TCL_2002N, "TCL 2002N 5H"}, - /* 100-109 */ - { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216ME"}, - { TUNER_TEA5767, "Philips TEA5768HL FM Radio"}, - { TUNER_ABSENT, "Panasonic ENV57H12D5"}, - { TUNER_PHILIPS_FM1236_MK3, "TCL MFNM05-4"}, - { TUNER_PHILIPS_FM1236_MK3, "TCL MNM05-4"}, - { TUNER_PHILIPS_FM1216ME_MK3, "TCL MPE05-2"}, - { TUNER_ABSENT, "TCL MQNM05-4"}, - { TUNER_ABSENT, "LG TAPC-W701D"}, - { TUNER_ABSENT, "TCL 9886P-WM"}, - { TUNER_ABSENT, "TCL 1676NM-WM"}, - /* 110-119 */ - { TUNER_ABSENT, "Thompson DTT75105"}, - { TUNER_ABSENT, "Conexant_CX24109"}, - { TUNER_TCL_2002N, "TCL M2523_5N_E"}, - { TUNER_TCL_2002MB, "TCL M2523_3DB_E"}, - { TUNER_ABSENT, "Philips 8275A"}, - { TUNER_ABSENT, "Microtune MT2060"}, - { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK5"}, - { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216ME MK5"}, - { TUNER_ABSENT, "TCL M2523_3DI_E"}, - { TUNER_ABSENT, "Samsung THPD5222FG30A"}, - /* 120-129 */ - { TUNER_XC2028, "Xceive XC3028"}, - { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK5"}, - { TUNER_ABSENT, "Philips FQD1216LME"}, - { TUNER_ABSENT, "Conexant CX24118A"}, - { TUNER_ABSENT, "TCL DMF11WIP"}, - { TUNER_ABSENT, "TCL MFNM05_4H_E"}, - { TUNER_ABSENT, "TCL MNM05_4H_E"}, - { TUNER_ABSENT, "TCL MPE05_2H_E"}, - { TUNER_ABSENT, "TCL MQNM05_4_U"}, - { TUNER_ABSENT, "TCL M2523_5NH_E"}, - /* 130-139 */ - { TUNER_ABSENT, "TCL M2523_3DBH_E"}, - { TUNER_ABSENT, "TCL M2523_3DIH_E"}, - { TUNER_ABSENT, "TCL MFPE05_2_U"}, - { TUNER_PHILIPS_FMD1216MEX_MK3, "Philips FMD1216MEX"}, - { TUNER_ABSENT, "Philips FRH2036B"}, - { TUNER_ABSENT, "Panasonic ENGF75_01GF"}, - { TUNER_ABSENT, "MaxLinear MXL5005"}, - { TUNER_ABSENT, "MaxLinear MXL5003"}, - { TUNER_ABSENT, "Xceive XC2028"}, - { TUNER_ABSENT, "Microtune MT2131"}, - /* 140-149 */ - { TUNER_ABSENT, "Philips 8275A_8295"}, - { TUNER_ABSENT, "TCL MF02GIP_5N_E"}, - { TUNER_ABSENT, "TCL MF02GIP_3DB_E"}, - { TUNER_ABSENT, "TCL MF02GIP_3DI_E"}, - { TUNER_ABSENT, "Microtune MT2266"}, - { TUNER_ABSENT, "TCL MF10WPP_4N_E"}, - { TUNER_ABSENT, "LG TAPQ_H702F"}, - { TUNER_ABSENT, "TCL M09WPP_4N_E"}, - { TUNER_ABSENT, "MaxLinear MXL5005_v2"}, - { TUNER_PHILIPS_TDA8290, "Philips 18271_8295"}, - /* 150-159 */ - { TUNER_XC5000, "Xceive XC5000"}, - { TUNER_ABSENT, "Xceive XC3028L"}, - { TUNER_ABSENT, "NXP 18271C2_716x"}, - { TUNER_ABSENT, "Xceive XC4000"}, - { TUNER_ABSENT, "Dibcom 7070"}, - { TUNER_PHILIPS_TDA8290, "NXP 18271C2"}, - { TUNER_ABSENT, "Siano SMS1010"}, - { TUNER_ABSENT, "Siano SMS1150"}, - { TUNER_ABSENT, "MaxLinear 5007"}, - { TUNER_ABSENT, "TCL M09WPP_2P_E"}, - /* 160-169 */ - { TUNER_ABSENT, "Siano SMS1180"}, - { TUNER_ABSENT, "Maxim_MAX2165"}, - { TUNER_ABSENT, "Siano SMS1140"}, - { TUNER_ABSENT, "Siano SMS1150 B1"}, - { TUNER_ABSENT, "MaxLinear 111"}, - { TUNER_ABSENT, "Dibcom 7770"}, - { TUNER_ABSENT, "Siano SMS1180VNS"}, - { TUNER_ABSENT, "Siano SMS1184"}, - { TUNER_PHILIPS_FQ1236_MK5, "TCL M30WTP-4N-E"}, - { TUNER_ABSENT, "TCL_M11WPP_2PN_E"}, - /* 170-179 */ - { TUNER_ABSENT, "MaxLinear 301"}, - { TUNER_ABSENT, "Mirics MSi001"}, - { TUNER_ABSENT, "MaxLinear MxL241SF"}, - { TUNER_XC5000C, "Xceive XC5000C"}, - { TUNER_ABSENT, "Montage M68TS2020"}, - { TUNER_ABSENT, "Siano SMS1530"}, - { TUNER_ABSENT, "Dibcom 7090"}, - { TUNER_ABSENT, "Xceive XC5200C"}, - { TUNER_ABSENT, "NXP 18273"}, - { TUNER_ABSENT, "Montage M88TS2022"}, - /* 180-189 */ - { TUNER_ABSENT, "NXP 18272M"}, - { TUNER_ABSENT, "NXP 18272S"}, -}; - -/* Use V4L2_IDENT_AMBIGUOUS for those audio 'chips' that are - * internal to a video chip, i.e. not a separate audio chip. */ -static struct HAUPPAUGE_AUDIOIC -{ - u32 id; - char *name; -} -audioIC[] = -{ - /* 0-4 */ - { V4L2_IDENT_NONE, "None" }, - { V4L2_IDENT_UNKNOWN, "TEA6300" }, - { V4L2_IDENT_UNKNOWN, "TEA6320" }, - { V4L2_IDENT_UNKNOWN, "TDA9850" }, - { V4L2_IDENT_MSPX4XX, "MSP3400C" }, - /* 5-9 */ - { V4L2_IDENT_MSPX4XX, "MSP3410D" }, - { V4L2_IDENT_MSPX4XX, "MSP3415" }, - { V4L2_IDENT_MSPX4XX, "MSP3430" }, - { V4L2_IDENT_MSPX4XX, "MSP3438" }, - { V4L2_IDENT_UNKNOWN, "CS5331" }, - /* 10-14 */ - { V4L2_IDENT_MSPX4XX, "MSP3435" }, - { V4L2_IDENT_MSPX4XX, "MSP3440" }, - { V4L2_IDENT_MSPX4XX, "MSP3445" }, - { V4L2_IDENT_MSPX4XX, "MSP3411" }, - { V4L2_IDENT_MSPX4XX, "MSP3416" }, - /* 15-19 */ - { V4L2_IDENT_MSPX4XX, "MSP3425" }, - { V4L2_IDENT_MSPX4XX, "MSP3451" }, - { V4L2_IDENT_MSPX4XX, "MSP3418" }, - { V4L2_IDENT_UNKNOWN, "Type 0x12" }, - { V4L2_IDENT_UNKNOWN, "OKI7716" }, - /* 20-24 */ - { V4L2_IDENT_MSPX4XX, "MSP4410" }, - { V4L2_IDENT_MSPX4XX, "MSP4420" }, - { V4L2_IDENT_MSPX4XX, "MSP4440" }, - { V4L2_IDENT_MSPX4XX, "MSP4450" }, - { V4L2_IDENT_MSPX4XX, "MSP4408" }, - /* 25-29 */ - { V4L2_IDENT_MSPX4XX, "MSP4418" }, - { V4L2_IDENT_MSPX4XX, "MSP4428" }, - { V4L2_IDENT_MSPX4XX, "MSP4448" }, - { V4L2_IDENT_MSPX4XX, "MSP4458" }, - { V4L2_IDENT_MSPX4XX, "Type 0x1d" }, - /* 30-34 */ - { V4L2_IDENT_AMBIGUOUS, "CX880" }, - { V4L2_IDENT_AMBIGUOUS, "CX881" }, - { V4L2_IDENT_AMBIGUOUS, "CX883" }, - { V4L2_IDENT_AMBIGUOUS, "CX882" }, - { V4L2_IDENT_AMBIGUOUS, "CX25840" }, - /* 35-39 */ - { V4L2_IDENT_AMBIGUOUS, "CX25841" }, - { V4L2_IDENT_AMBIGUOUS, "CX25842" }, - { V4L2_IDENT_AMBIGUOUS, "CX25843" }, - { V4L2_IDENT_AMBIGUOUS, "CX23418" }, - { V4L2_IDENT_AMBIGUOUS, "CX23885" }, - /* 40-44 */ - { V4L2_IDENT_AMBIGUOUS, "CX23888" }, - { V4L2_IDENT_AMBIGUOUS, "SAA7131" }, - { V4L2_IDENT_AMBIGUOUS, "CX23887" }, - { V4L2_IDENT_AMBIGUOUS, "SAA7164" }, - { V4L2_IDENT_AMBIGUOUS, "AU8522" }, -}; - -/* This list is supplied by Hauppauge. Thanks! */ -static const char *decoderIC[] = { - /* 0-4 */ - "None", "BT815", "BT817", "BT819", "BT815A", - /* 5-9 */ - "BT817A", "BT819A", "BT827", "BT829", "BT848", - /* 10-14 */ - "BT848A", "BT849A", "BT829A", "BT827A", "BT878", - /* 15-19 */ - "BT879", "BT880", "VPX3226E", "SAA7114", "SAA7115", - /* 20-24 */ - "CX880", "CX881", "CX883", "SAA7111", "SAA7113", - /* 25-29 */ - "CX882", "TVP5150A", "CX25840", "CX25841", "CX25842", - /* 30-34 */ - "CX25843", "CX23418", "NEC61153", "CX23885", "CX23888", - /* 35-39 */ - "SAA7131", "CX25837", "CX23887", "CX23885A", "CX23887A", - /* 40-42 */ - "SAA7164", "CX23885B", "AU8522" -}; - -static int hasRadioTuner(int tunerType) -{ - switch (tunerType) { - case 18: /* PNPEnv_TUNER_FR1236_MK2 */ - case 23: /* PNPEnv_TUNER_FM1236 */ - case 38: /* PNPEnv_TUNER_FMR1236 */ - case 16: /* PNPEnv_TUNER_FR1216_MK2 */ - case 19: /* PNPEnv_TUNER_FR1246_MK2 */ - case 21: /* PNPEnv_TUNER_FM1216 */ - case 24: /* PNPEnv_TUNER_FM1246 */ - case 17: /* PNPEnv_TUNER_FR1216MF_MK2 */ - case 22: /* PNPEnv_TUNER_FM1216MF */ - case 20: /* PNPEnv_TUNER_FR1256_MK2 */ - case 25: /* PNPEnv_TUNER_FM1256 */ - case 33: /* PNPEnv_TUNER_4039FR5 */ - case 42: /* PNPEnv_TUNER_4009FR5 */ - case 52: /* PNPEnv_TUNER_4049FM5 */ - case 54: /* PNPEnv_TUNER_4049FM5_AltI2C */ - case 44: /* PNPEnv_TUNER_4009FN5 */ - case 31: /* PNPEnv_TUNER_TCPB9085P */ - case 30: /* PNPEnv_TUNER_TCPN9085D */ - case 46: /* PNPEnv_TUNER_TP18NSR01F */ - case 47: /* PNPEnv_TUNER_TP18PSB01D */ - case 49: /* PNPEnv_TUNER_TAPC_I001D */ - case 60: /* PNPEnv_TUNER_TAPE_S001D_MK3 */ - case 57: /* PNPEnv_TUNER_FM1216ME_MK3 */ - case 59: /* PNPEnv_TUNER_FM1216MP_MK3 */ - case 58: /* PNPEnv_TUNER_FM1236_MK3 */ - case 68: /* PNPEnv_TUNER_TAPE_H001F_MK3 */ - case 61: /* PNPEnv_TUNER_TAPE_M001D_MK3 */ - case 78: /* PNPEnv_TUNER_TDA8275C1_8290_FM */ - case 89: /* PNPEnv_TUNER_TCL_MFPE05_2 */ - case 92: /* PNPEnv_TUNER_PHILIPS_FQ1236A_MK4 */ - case 105: - return 1; - } - return 0; -} - -void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, - unsigned char *eeprom_data) -{ - /* ---------------------------------------------- - ** The hauppauge eeprom format is tagged - ** - ** if packet[0] == 0x84, then packet[0..1] == length - ** else length = packet[0] & 3f; - ** if packet[0] & f8 == f8, then EOD and packet[1] == checksum - ** - ** In our (ivtv) case we're interested in the following: - ** tuner type: tag [00].05 or [0a].01 (index into hauppauge_tuner) - ** tuner fmts: tag [00].04 or [0a].00 (bitmask index into - ** hauppauge_tuner_fmt) - ** radio: tag [00].{last} or [0e].00 (bitmask. bit2=FM) - ** audio proc: tag [02].01 or [05].00 (mask with 0x7f) - ** decoder proc: tag [09].01) - - ** Fun info: - ** model: tag [00].07-08 or [06].00-01 - ** revision: tag [00].09-0b or [06].04-06 - ** serial#: tag [01].05-07 or [04].04-06 - - ** # of inputs/outputs ??? - */ - - int i, j, len, done, beenhere, tag, start; - - int tuner1 = 0, t_format1 = 0, audioic = -1; - char *t_name1 = NULL; - const char *t_fmt_name1[8] = { " none", "", "", "", "", "", "", "" }; - - int tuner2 = 0, t_format2 = 0; - char *t_name2 = NULL; - const char *t_fmt_name2[8] = { " none", "", "", "", "", "", "", "" }; - - memset(tvee, 0, sizeof(*tvee)); - tvee->tuner_type = TUNER_ABSENT; - tvee->tuner2_type = TUNER_ABSENT; - - done = len = beenhere = 0; - - /* Different eeprom start offsets for em28xx, cx2388x and cx23418 */ - if (eeprom_data[0] == 0x1a && - eeprom_data[1] == 0xeb && - eeprom_data[2] == 0x67 && - eeprom_data[3] == 0x95) - start = 0xa0; /* Generic em28xx offset */ - else if ((eeprom_data[0] & 0xe1) == 0x01 && - eeprom_data[1] == 0x00 && - eeprom_data[2] == 0x00 && - eeprom_data[8] == 0x84) - start = 8; /* Generic cx2388x offset */ - else if (eeprom_data[1] == 0x70 && - eeprom_data[2] == 0x00 && - eeprom_data[4] == 0x74 && - eeprom_data[8] == 0x84) - start = 8; /* Generic cx23418 offset (models 74xxx) */ - else - start = 0; - - for (i = start; !done && i < 256; i += len) { - if (eeprom_data[i] == 0x84) { - len = eeprom_data[i + 1] + (eeprom_data[i + 2] << 8); - i += 3; - } else if ((eeprom_data[i] & 0xf0) == 0x70) { - if (eeprom_data[i] & 0x08) { - /* verify checksum! */ - done = 1; - break; - } - len = eeprom_data[i] & 0x07; - ++i; - } else { - tveeprom_warn("Encountered bad packet header [%02x]. " - "Corrupt or not a Hauppauge eeprom.\n", - eeprom_data[i]); - return; - } - - if (debug) { - tveeprom_info("Tag [%02x] + %d bytes:", - eeprom_data[i], len - 1); - for (j = 1; j < len; j++) - printk(KERN_CONT " %02x", eeprom_data[i + j]); - printk(KERN_CONT "\n"); - } - - /* process by tag */ - tag = eeprom_data[i]; - switch (tag) { - case 0x00: - /* tag: 'Comprehensive' */ - tuner1 = eeprom_data[i+6]; - t_format1 = eeprom_data[i+5]; - tvee->has_radio = eeprom_data[i+len-1]; - /* old style tag, don't know how to detect - IR presence, mark as unknown. */ - tvee->has_ir = 0; - tvee->model = - eeprom_data[i+8] + - (eeprom_data[i+9] << 8); - tvee->revision = eeprom_data[i+10] + - (eeprom_data[i+11] << 8) + - (eeprom_data[i+12] << 16); - break; - - case 0x01: - /* tag: 'SerialID' */ - tvee->serial_number = - eeprom_data[i+6] + - (eeprom_data[i+7] << 8) + - (eeprom_data[i+8] << 16); - break; - - case 0x02: - /* tag 'AudioInfo' - Note mask with 0x7F, high bit used on some older models - to indicate 4052 mux was removed in favor of using MSP - inputs directly. */ - audioic = eeprom_data[i+2] & 0x7f; - if (audioic < ARRAY_SIZE(audioIC)) - tvee->audio_processor = audioIC[audioic].id; - else - tvee->audio_processor = V4L2_IDENT_UNKNOWN; - break; - - /* case 0x03: tag 'EEInfo' */ - - case 0x04: - /* tag 'SerialID2' */ - tvee->serial_number = - eeprom_data[i+5] + - (eeprom_data[i+6] << 8) + - (eeprom_data[i+7] << 16); - - if ((eeprom_data[i + 8] & 0xf0) && - (tvee->serial_number < 0xffffff)) { - tvee->MAC_address[0] = 0x00; - tvee->MAC_address[1] = 0x0D; - tvee->MAC_address[2] = 0xFE; - tvee->MAC_address[3] = eeprom_data[i + 7]; - tvee->MAC_address[4] = eeprom_data[i + 6]; - tvee->MAC_address[5] = eeprom_data[i + 5]; - tvee->has_MAC_address = 1; - } - break; - - case 0x05: - /* tag 'Audio2' - Note mask with 0x7F, high bit used on some older models - to indicate 4052 mux was removed in favor of using MSP - inputs directly. */ - audioic = eeprom_data[i+1] & 0x7f; - if (audioic < ARRAY_SIZE(audioIC)) - tvee->audio_processor = audioIC[audioic].id; - else - tvee->audio_processor = V4L2_IDENT_UNKNOWN; - - break; - - case 0x06: - /* tag 'ModelRev' */ - tvee->model = - eeprom_data[i + 1] + - (eeprom_data[i + 2] << 8) + - (eeprom_data[i + 3] << 16) + - (eeprom_data[i + 4] << 24); - tvee->revision = - eeprom_data[i + 5] + - (eeprom_data[i + 6] << 8) + - (eeprom_data[i + 7] << 16); - break; - - case 0x07: - /* tag 'Details': according to Hauppauge not interesting - on any PCI-era or later boards. */ - break; - - /* there is no tag 0x08 defined */ - - case 0x09: - /* tag 'Video' */ - tvee->decoder_processor = eeprom_data[i + 1]; - break; - - case 0x0a: - /* tag 'Tuner' */ - if (beenhere == 0) { - tuner1 = eeprom_data[i + 2]; - t_format1 = eeprom_data[i + 1]; - beenhere = 1; - } else { - /* a second (radio) tuner may be present */ - tuner2 = eeprom_data[i + 2]; - t_format2 = eeprom_data[i + 1]; - /* not a TV tuner? */ - if (t_format2 == 0) - tvee->has_radio = 1; /* must be radio */ - } - break; - - case 0x0b: - /* tag 'Inputs': according to Hauppauge this is specific - to each driver family, so no good assumptions can be - made. */ - break; - - /* case 0x0c: tag 'Balun' */ - /* case 0x0d: tag 'Teletext' */ - - case 0x0e: - /* tag: 'Radio' */ - tvee->has_radio = eeprom_data[i+1]; - break; - - case 0x0f: - /* tag 'IRInfo' */ - tvee->has_ir = 1 | (eeprom_data[i+1] << 1); - break; - - /* case 0x10: tag 'VBIInfo' */ - /* case 0x11: tag 'QCInfo' */ - /* case 0x12: tag 'InfoBits' */ - - default: - tveeprom_dbg("Not sure what to do with tag [%02x]\n", - tag); - /* dump the rest of the packet? */ - } - } - - if (!done) { - tveeprom_warn("Ran out of data!\n"); - return; - } - - if (tvee->revision != 0) { - tvee->rev_str[0] = 32 + ((tvee->revision >> 18) & 0x3f); - tvee->rev_str[1] = 32 + ((tvee->revision >> 12) & 0x3f); - tvee->rev_str[2] = 32 + ((tvee->revision >> 6) & 0x3f); - tvee->rev_str[3] = 32 + (tvee->revision & 0x3f); - tvee->rev_str[4] = 0; - } - - if (hasRadioTuner(tuner1) && !tvee->has_radio) { - tveeprom_info("The eeprom says no radio is present, but the tuner type\n"); - tveeprom_info("indicates otherwise. I will assume that radio is present.\n"); - tvee->has_radio = 1; - } - - if (tuner1 < ARRAY_SIZE(hauppauge_tuner)) { - tvee->tuner_type = hauppauge_tuner[tuner1].id; - t_name1 = hauppauge_tuner[tuner1].name; - } else { - t_name1 = "unknown"; - } - - if (tuner2 < ARRAY_SIZE(hauppauge_tuner)) { - tvee->tuner2_type = hauppauge_tuner[tuner2].id; - t_name2 = hauppauge_tuner[tuner2].name; - } else { - t_name2 = "unknown"; - } - - tvee->tuner_hauppauge_model = tuner1; - tvee->tuner2_hauppauge_model = tuner2; - tvee->tuner_formats = 0; - tvee->tuner2_formats = 0; - for (i = j = 0; i < 8; i++) { - if (t_format1 & (1 << i)) { - tvee->tuner_formats |= hauppauge_tuner_fmt[i].id; - t_fmt_name1[j++] = hauppauge_tuner_fmt[i].name; - } - } - for (i = j = 0; i < 8; i++) { - if (t_format2 & (1 << i)) { - tvee->tuner2_formats |= hauppauge_tuner_fmt[i].id; - t_fmt_name2[j++] = hauppauge_tuner_fmt[i].name; - } - } - - tveeprom_info("Hauppauge model %d, rev %s, serial# %d\n", - tvee->model, tvee->rev_str, tvee->serial_number); - if (tvee->has_MAC_address == 1) - tveeprom_info("MAC address is %pM\n", tvee->MAC_address); - tveeprom_info("tuner model is %s (idx %d, type %d)\n", - t_name1, tuner1, tvee->tuner_type); - tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n", - t_fmt_name1[0], t_fmt_name1[1], t_fmt_name1[2], - t_fmt_name1[3], t_fmt_name1[4], t_fmt_name1[5], - t_fmt_name1[6], t_fmt_name1[7], t_format1); - if (tuner2) - tveeprom_info("second tuner model is %s (idx %d, type %d)\n", - t_name2, tuner2, tvee->tuner2_type); - if (t_format2) - tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n", - t_fmt_name2[0], t_fmt_name2[1], t_fmt_name2[2], - t_fmt_name2[3], t_fmt_name2[4], t_fmt_name2[5], - t_fmt_name2[6], t_fmt_name2[7], t_format2); - if (audioic < 0) { - tveeprom_info("audio processor is unknown (no idx)\n"); - tvee->audio_processor = V4L2_IDENT_UNKNOWN; - } else { - if (audioic < ARRAY_SIZE(audioIC)) - tveeprom_info("audio processor is %s (idx %d)\n", - audioIC[audioic].name, audioic); - else - tveeprom_info("audio processor is unknown (idx %d)\n", - audioic); - } - if (tvee->decoder_processor) - tveeprom_info("decoder processor is %s (idx %d)\n", - STRM(decoderIC, tvee->decoder_processor), - tvee->decoder_processor); - if (tvee->has_ir) - tveeprom_info("has %sradio, has %sIR receiver, has %sIR transmitter\n", - tvee->has_radio ? "" : "no ", - (tvee->has_ir & 2) ? "" : "no ", - (tvee->has_ir & 4) ? "" : "no "); - else - tveeprom_info("has %sradio\n", - tvee->has_radio ? "" : "no "); -} -EXPORT_SYMBOL(tveeprom_hauppauge_analog); - -/* ----------------------------------------------------------------------- */ -/* generic helper functions */ - -int tveeprom_read(struct i2c_client *c, unsigned char *eedata, int len) -{ - unsigned char buf; - int err; - - buf = 0; - err = i2c_master_send(c, &buf, 1); - if (err != 1) { - tveeprom_info("Huh, no eeprom present (err=%d)?\n", err); - return -1; - } - err = i2c_master_recv(c, eedata, len); - if (err != len) { - tveeprom_warn("i2c eeprom read error (err=%d)\n", err); - return -1; - } - if (debug) { - int i; - - tveeprom_info("full 256-byte eeprom dump:\n"); - for (i = 0; i < len; i++) { - if (0 == (i % 16)) - tveeprom_info("%02x:", i); - printk(KERN_CONT " %02x", eedata[i]); - if (15 == (i % 16)) - printk(KERN_CONT "\n"); - } - } - return 0; -} -EXPORT_SYMBOL(tveeprom_read); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ -- cgit v1.2.3 From cc318180c8ec87ce692d1fbb22923f9e05871197 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 8 Feb 2013 16:17:10 -0200 Subject: [media] tveeprom: Fix lots of bad whitespace While running checkpatch.pl after the last patch, I noticed lots of those: WARNING: please, no space before tabs #151: FILE: drivers/media/common/tveeprom.c:99: +^I{ TUNER_ABSENT, ^I^I"None" },$ (together with other checkpatch.pl errors/warnings) While I won't be fixing everything, as I have already an script to fix the above, let's do it, in order to clean it a little bit. While here, also drop cmacs-specific format text at the end. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tveeprom.c | 290 ++++++++++++++++++++-------------------- 1 file changed, 142 insertions(+), 148 deletions(-) (limited to 'drivers') diff --git a/drivers/media/common/tveeprom.c b/drivers/media/common/tveeprom.c index 3b6cf034976a..cc1e172dfece 100644 --- a/drivers/media/common/tveeprom.c +++ b/drivers/media/common/tveeprom.c @@ -96,170 +96,170 @@ static struct HAUPPAUGE_TUNER hauppauge_tuner[] = { /* 0-9 */ - { TUNER_ABSENT, "None" }, - { TUNER_ABSENT, "External" }, - { TUNER_ABSENT, "Unspecified" }, - { TUNER_PHILIPS_PAL, "Philips FI1216" }, - { TUNER_PHILIPS_SECAM, "Philips FI1216MF" }, - { TUNER_PHILIPS_NTSC, "Philips FI1236" }, - { TUNER_PHILIPS_PAL_I, "Philips FI1246" }, + { TUNER_ABSENT, "None" }, + { TUNER_ABSENT, "External" }, + { TUNER_ABSENT, "Unspecified" }, + { TUNER_PHILIPS_PAL, "Philips FI1216" }, + { TUNER_PHILIPS_SECAM, "Philips FI1216MF" }, + { TUNER_PHILIPS_NTSC, "Philips FI1236" }, + { TUNER_PHILIPS_PAL_I, "Philips FI1246" }, { TUNER_PHILIPS_PAL_DK, "Philips FI1256" }, - { TUNER_PHILIPS_PAL, "Philips FI1216 MK2" }, - { TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2" }, + { TUNER_PHILIPS_PAL, "Philips FI1216 MK2" }, + { TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2" }, /* 10-19 */ - { TUNER_PHILIPS_NTSC, "Philips FI1236 MK2" }, - { TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2" }, + { TUNER_PHILIPS_NTSC, "Philips FI1236 MK2" }, + { TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2" }, { TUNER_PHILIPS_PAL_DK, "Philips FI1256 MK2" }, - { TUNER_TEMIC_NTSC, "Temic 4032FY5" }, - { TUNER_TEMIC_PAL, "Temic 4002FH5" }, - { TUNER_TEMIC_PAL_I, "Temic 4062FY5" }, - { TUNER_PHILIPS_PAL, "Philips FR1216 MK2" }, - { TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2" }, - { TUNER_PHILIPS_NTSC, "Philips FR1236 MK2" }, - { TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2" }, + { TUNER_TEMIC_NTSC, "Temic 4032FY5" }, + { TUNER_TEMIC_PAL, "Temic 4002FH5" }, + { TUNER_TEMIC_PAL_I, "Temic 4062FY5" }, + { TUNER_PHILIPS_PAL, "Philips FR1216 MK2" }, + { TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2" }, + { TUNER_PHILIPS_NTSC, "Philips FR1236 MK2" }, + { TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2" }, /* 20-29 */ { TUNER_PHILIPS_PAL_DK, "Philips FR1256 MK2" }, - { TUNER_PHILIPS_PAL, "Philips FM1216" }, - { TUNER_PHILIPS_SECAM, "Philips FM1216MF" }, - { TUNER_PHILIPS_NTSC, "Philips FM1236" }, - { TUNER_PHILIPS_PAL_I, "Philips FM1246" }, + { TUNER_PHILIPS_PAL, "Philips FM1216" }, + { TUNER_PHILIPS_SECAM, "Philips FM1216MF" }, + { TUNER_PHILIPS_NTSC, "Philips FM1236" }, + { TUNER_PHILIPS_PAL_I, "Philips FM1246" }, { TUNER_PHILIPS_PAL_DK, "Philips FM1256" }, - { TUNER_TEMIC_4036FY5_NTSC, "Temic 4036FY5" }, - { TUNER_ABSENT, "Samsung TCPN9082D" }, - { TUNER_ABSENT, "Samsung TCPM9092P" }, - { TUNER_TEMIC_4006FH5_PAL, "Temic 4006FH5" }, + { TUNER_TEMIC_4036FY5_NTSC, "Temic 4036FY5" }, + { TUNER_ABSENT, "Samsung TCPN9082D" }, + { TUNER_ABSENT, "Samsung TCPM9092P" }, + { TUNER_TEMIC_4006FH5_PAL, "Temic 4006FH5" }, /* 30-39 */ - { TUNER_ABSENT, "Samsung TCPN9085D" }, - { TUNER_ABSENT, "Samsung TCPB9085P" }, - { TUNER_ABSENT, "Samsung TCPL9091P" }, - { TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5" }, - { TUNER_PHILIPS_FQ1216ME, "Philips FQ1216 ME" }, - { TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5" }, - { TUNER_PHILIPS_NTSC, "Philips TD1536" }, - { TUNER_PHILIPS_NTSC, "Philips TD1536D" }, - { TUNER_PHILIPS_NTSC, "Philips FMR1236" }, /* mono radio */ - { TUNER_ABSENT, "Philips FI1256MP" }, + { TUNER_ABSENT, "Samsung TCPN9085D" }, + { TUNER_ABSENT, "Samsung TCPB9085P" }, + { TUNER_ABSENT, "Samsung TCPL9091P" }, + { TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5" }, + { TUNER_PHILIPS_FQ1216ME, "Philips FQ1216 ME" }, + { TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5" }, + { TUNER_PHILIPS_NTSC, "Philips TD1536" }, + { TUNER_PHILIPS_NTSC, "Philips TD1536D" }, + { TUNER_PHILIPS_NTSC, "Philips FMR1236" }, /* mono radio */ + { TUNER_ABSENT, "Philips FI1256MP" }, /* 40-49 */ - { TUNER_ABSENT, "Samsung TCPQ9091P" }, - { TUNER_TEMIC_4006FN5_MULTI_PAL, "Temic 4006FN5" }, - { TUNER_TEMIC_4009FR5_PAL, "Temic 4009FR5" }, - { TUNER_TEMIC_4046FM5, "Temic 4046FM5" }, + { TUNER_ABSENT, "Samsung TCPQ9091P" }, + { TUNER_TEMIC_4006FN5_MULTI_PAL,"Temic 4006FN5" }, + { TUNER_TEMIC_4009FR5_PAL, "Temic 4009FR5" }, + { TUNER_TEMIC_4046FM5, "Temic 4046FM5" }, { TUNER_TEMIC_4009FN5_MULTI_PAL_FM, "Temic 4009FN5" }, - { TUNER_ABSENT, "Philips TD1536D FH 44"}, - { TUNER_LG_NTSC_FM, "LG TP18NSR01F"}, - { TUNER_LG_PAL_FM, "LG TP18PSB01D"}, - { TUNER_LG_PAL, "LG TP18PSB11D"}, - { TUNER_LG_PAL_I_FM, "LG TAPC-I001D"}, + { TUNER_ABSENT, "Philips TD1536D FH 44"}, + { TUNER_LG_NTSC_FM, "LG TP18NSR01F"}, + { TUNER_LG_PAL_FM, "LG TP18PSB01D"}, + { TUNER_LG_PAL, "LG TP18PSB11D"}, + { TUNER_LG_PAL_I_FM, "LG TAPC-I001D"}, /* 50-59 */ - { TUNER_LG_PAL_I, "LG TAPC-I701D"}, - { TUNER_ABSENT, "Temic 4042FI5"}, - { TUNER_MICROTUNE_4049FM5, "Microtune 4049 FM5"}, - { TUNER_ABSENT, "LG TPI8NSR11F"}, - { TUNER_ABSENT, "Microtune 4049 FM5 Alt I2C"}, - { TUNER_PHILIPS_FM1216ME_MK3, "Philips FQ1216ME MK3"}, - { TUNER_ABSENT, "Philips FI1236 MK3"}, - { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216 ME MK3"}, - { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK3"}, - { TUNER_ABSENT, "Philips FM1216MP MK3"}, + { TUNER_LG_PAL_I, "LG TAPC-I701D"}, + { TUNER_ABSENT, "Temic 4042FI5"}, + { TUNER_MICROTUNE_4049FM5, "Microtune 4049 FM5"}, + { TUNER_ABSENT, "LG TPI8NSR11F"}, + { TUNER_ABSENT, "Microtune 4049 FM5 Alt I2C"}, + { TUNER_PHILIPS_FM1216ME_MK3, "Philips FQ1216ME MK3"}, + { TUNER_ABSENT, "Philips FI1236 MK3"}, + { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216 ME MK3"}, + { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK3"}, + { TUNER_ABSENT, "Philips FM1216MP MK3"}, /* 60-69 */ - { TUNER_PHILIPS_FM1216ME_MK3, "LG S001D MK3"}, - { TUNER_ABSENT, "LG M001D MK3"}, - { TUNER_PHILIPS_FM1216ME_MK3, "LG S701D MK3"}, - { TUNER_ABSENT, "LG M701D MK3"}, - { TUNER_ABSENT, "Temic 4146FM5"}, - { TUNER_ABSENT, "Temic 4136FY5"}, - { TUNER_ABSENT, "Temic 4106FH5"}, - { TUNER_ABSENT, "Philips FQ1216LMP MK3"}, - { TUNER_LG_NTSC_TAPE, "LG TAPE H001F MK3"}, - { TUNER_LG_NTSC_TAPE, "LG TAPE H701F MK3"}, + { TUNER_PHILIPS_FM1216ME_MK3, "LG S001D MK3"}, + { TUNER_ABSENT, "LG M001D MK3"}, + { TUNER_PHILIPS_FM1216ME_MK3, "LG S701D MK3"}, + { TUNER_ABSENT, "LG M701D MK3"}, + { TUNER_ABSENT, "Temic 4146FM5"}, + { TUNER_ABSENT, "Temic 4136FY5"}, + { TUNER_ABSENT, "Temic 4106FH5"}, + { TUNER_ABSENT, "Philips FQ1216LMP MK3"}, + { TUNER_LG_NTSC_TAPE, "LG TAPE H001F MK3"}, + { TUNER_LG_NTSC_TAPE, "LG TAPE H701F MK3"}, /* 70-79 */ - { TUNER_ABSENT, "LG TALN H200T"}, - { TUNER_ABSENT, "LG TALN H250T"}, - { TUNER_ABSENT, "LG TALN M200T"}, - { TUNER_ABSENT, "LG TALN Z200T"}, - { TUNER_ABSENT, "LG TALN S200T"}, - { TUNER_ABSENT, "Thompson DTT7595"}, - { TUNER_ABSENT, "Thompson DTT7592"}, - { TUNER_ABSENT, "Silicon TDA8275C1 8290"}, - { TUNER_ABSENT, "Silicon TDA8275C1 8290 FM"}, - { TUNER_ABSENT, "Thompson DTT757"}, + { TUNER_ABSENT, "LG TALN H200T"}, + { TUNER_ABSENT, "LG TALN H250T"}, + { TUNER_ABSENT, "LG TALN M200T"}, + { TUNER_ABSENT, "LG TALN Z200T"}, + { TUNER_ABSENT, "LG TALN S200T"}, + { TUNER_ABSENT, "Thompson DTT7595"}, + { TUNER_ABSENT, "Thompson DTT7592"}, + { TUNER_ABSENT, "Silicon TDA8275C1 8290"}, + { TUNER_ABSENT, "Silicon TDA8275C1 8290 FM"}, + { TUNER_ABSENT, "Thompson DTT757"}, /* 80-89 */ - { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK3"}, - { TUNER_LG_PAL_NEW_TAPC, "LG TAPC G701D"}, - { TUNER_LG_NTSC_NEW_TAPC, "LG TAPC H791F"}, - { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MB 3"}, - { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MI 3"}, - { TUNER_TCL_2002N, "TCL 2002N 6A"}, - { TUNER_PHILIPS_FM1236_MK3, "Philips FQ1236 MK3"}, - { TUNER_SAMSUNG_TCPN_2121P30A, "Samsung TCPN 2121P30A"}, - { TUNER_ABSENT, "Samsung TCPE 4121P30A"}, - { TUNER_PHILIPS_FM1216ME_MK3, "TCL MFPE05 2"}, + { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK3"}, + { TUNER_LG_PAL_NEW_TAPC, "LG TAPC G701D"}, + { TUNER_LG_NTSC_NEW_TAPC, "LG TAPC H791F"}, + { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MB 3"}, + { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MI 3"}, + { TUNER_TCL_2002N, "TCL 2002N 6A"}, + { TUNER_PHILIPS_FM1236_MK3, "Philips FQ1236 MK3"}, + { TUNER_SAMSUNG_TCPN_2121P30A, "Samsung TCPN 2121P30A"}, + { TUNER_ABSENT, "Samsung TCPE 4121P30A"}, + { TUNER_PHILIPS_FM1216ME_MK3, "TCL MFPE05 2"}, /* 90-99 */ - { TUNER_ABSENT, "LG TALN H202T"}, - { TUNER_PHILIPS_FQ1216AME_MK4, "Philips FQ1216AME MK4"}, - { TUNER_PHILIPS_FQ1236A_MK4, "Philips FQ1236A MK4"}, - { TUNER_ABSENT, "Philips FQ1286A MK4"}, - { TUNER_ABSENT, "Philips FQ1216ME MK5"}, - { TUNER_ABSENT, "Philips FQ1236 MK5"}, - { TUNER_SAMSUNG_TCPG_6121P30A, "Samsung TCPG 6121P30A"}, - { TUNER_TCL_2002MB, "TCL 2002MB_3H"}, - { TUNER_ABSENT, "TCL 2002MI_3H"}, - { TUNER_TCL_2002N, "TCL 2002N 5H"}, + { TUNER_ABSENT, "LG TALN H202T"}, + { TUNER_PHILIPS_FQ1216AME_MK4, "Philips FQ1216AME MK4"}, + { TUNER_PHILIPS_FQ1236A_MK4, "Philips FQ1236A MK4"}, + { TUNER_ABSENT, "Philips FQ1286A MK4"}, + { TUNER_ABSENT, "Philips FQ1216ME MK5"}, + { TUNER_ABSENT, "Philips FQ1236 MK5"}, + { TUNER_SAMSUNG_TCPG_6121P30A, "Samsung TCPG 6121P30A"}, + { TUNER_TCL_2002MB, "TCL 2002MB_3H"}, + { TUNER_ABSENT, "TCL 2002MI_3H"}, + { TUNER_TCL_2002N, "TCL 2002N 5H"}, /* 100-109 */ - { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216ME"}, - { TUNER_TEA5767, "Philips TEA5768HL FM Radio"}, - { TUNER_ABSENT, "Panasonic ENV57H12D5"}, - { TUNER_PHILIPS_FM1236_MK3, "TCL MFNM05-4"}, + { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216ME"}, + { TUNER_TEA5767, "Philips TEA5768HL FM Radio"}, + { TUNER_ABSENT, "Panasonic ENV57H12D5"}, + { TUNER_PHILIPS_FM1236_MK3, "TCL MFNM05-4"}, { TUNER_PHILIPS_FM1236_MK3, "TCL MNM05-4"}, - { TUNER_PHILIPS_FM1216ME_MK3, "TCL MPE05-2"}, - { TUNER_ABSENT, "TCL MQNM05-4"}, - { TUNER_ABSENT, "LG TAPC-W701D"}, - { TUNER_ABSENT, "TCL 9886P-WM"}, - { TUNER_ABSENT, "TCL 1676NM-WM"}, + { TUNER_PHILIPS_FM1216ME_MK3, "TCL MPE05-2"}, + { TUNER_ABSENT, "TCL MQNM05-4"}, + { TUNER_ABSENT, "LG TAPC-W701D"}, + { TUNER_ABSENT, "TCL 9886P-WM"}, + { TUNER_ABSENT, "TCL 1676NM-WM"}, /* 110-119 */ - { TUNER_ABSENT, "Thompson DTT75105"}, - { TUNER_ABSENT, "Conexant_CX24109"}, - { TUNER_TCL_2002N, "TCL M2523_5N_E"}, - { TUNER_TCL_2002MB, "TCL M2523_3DB_E"}, - { TUNER_ABSENT, "Philips 8275A"}, - { TUNER_ABSENT, "Microtune MT2060"}, - { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK5"}, - { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216ME MK5"}, - { TUNER_ABSENT, "TCL M2523_3DI_E"}, - { TUNER_ABSENT, "Samsung THPD5222FG30A"}, + { TUNER_ABSENT, "Thompson DTT75105"}, + { TUNER_ABSENT, "Conexant_CX24109"}, + { TUNER_TCL_2002N, "TCL M2523_5N_E"}, + { TUNER_TCL_2002MB, "TCL M2523_3DB_E"}, + { TUNER_ABSENT, "Philips 8275A"}, + { TUNER_ABSENT, "Microtune MT2060"}, + { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK5"}, + { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216ME MK5"}, + { TUNER_ABSENT, "TCL M2523_3DI_E"}, + { TUNER_ABSENT, "Samsung THPD5222FG30A"}, /* 120-129 */ - { TUNER_XC2028, "Xceive XC3028"}, + { TUNER_XC2028, "Xceive XC3028"}, { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK5"}, - { TUNER_ABSENT, "Philips FQD1216LME"}, - { TUNER_ABSENT, "Conexant CX24118A"}, - { TUNER_ABSENT, "TCL DMF11WIP"}, - { TUNER_ABSENT, "TCL MFNM05_4H_E"}, - { TUNER_ABSENT, "TCL MNM05_4H_E"}, - { TUNER_ABSENT, "TCL MPE05_2H_E"}, - { TUNER_ABSENT, "TCL MQNM05_4_U"}, - { TUNER_ABSENT, "TCL M2523_5NH_E"}, + { TUNER_ABSENT, "Philips FQD1216LME"}, + { TUNER_ABSENT, "Conexant CX24118A"}, + { TUNER_ABSENT, "TCL DMF11WIP"}, + { TUNER_ABSENT, "TCL MFNM05_4H_E"}, + { TUNER_ABSENT, "TCL MNM05_4H_E"}, + { TUNER_ABSENT, "TCL MPE05_2H_E"}, + { TUNER_ABSENT, "TCL MQNM05_4_U"}, + { TUNER_ABSENT, "TCL M2523_5NH_E"}, /* 130-139 */ - { TUNER_ABSENT, "TCL M2523_3DBH_E"}, - { TUNER_ABSENT, "TCL M2523_3DIH_E"}, - { TUNER_ABSENT, "TCL MFPE05_2_U"}, + { TUNER_ABSENT, "TCL M2523_3DBH_E"}, + { TUNER_ABSENT, "TCL M2523_3DIH_E"}, + { TUNER_ABSENT, "TCL MFPE05_2_U"}, { TUNER_PHILIPS_FMD1216MEX_MK3, "Philips FMD1216MEX"}, - { TUNER_ABSENT, "Philips FRH2036B"}, - { TUNER_ABSENT, "Panasonic ENGF75_01GF"}, - { TUNER_ABSENT, "MaxLinear MXL5005"}, - { TUNER_ABSENT, "MaxLinear MXL5003"}, - { TUNER_ABSENT, "Xceive XC2028"}, - { TUNER_ABSENT, "Microtune MT2131"}, + { TUNER_ABSENT, "Philips FRH2036B"}, + { TUNER_ABSENT, "Panasonic ENGF75_01GF"}, + { TUNER_ABSENT, "MaxLinear MXL5005"}, + { TUNER_ABSENT, "MaxLinear MXL5003"}, + { TUNER_ABSENT, "Xceive XC2028"}, + { TUNER_ABSENT, "Microtune MT2131"}, /* 140-149 */ - { TUNER_ABSENT, "Philips 8275A_8295"}, - { TUNER_ABSENT, "TCL MF02GIP_5N_E"}, - { TUNER_ABSENT, "TCL MF02GIP_3DB_E"}, - { TUNER_ABSENT, "TCL MF02GIP_3DI_E"}, - { TUNER_ABSENT, "Microtune MT2266"}, - { TUNER_ABSENT, "TCL MF10WPP_4N_E"}, - { TUNER_ABSENT, "LG TAPQ_H702F"}, - { TUNER_ABSENT, "TCL M09WPP_4N_E"}, - { TUNER_ABSENT, "MaxLinear MXL5005_v2"}, - { TUNER_PHILIPS_TDA8290, "Philips 18271_8295"}, + { TUNER_ABSENT, "Philips 8275A_8295"}, + { TUNER_ABSENT, "TCL MF02GIP_5N_E"}, + { TUNER_ABSENT, "TCL MF02GIP_3DB_E"}, + { TUNER_ABSENT, "TCL MF02GIP_3DI_E"}, + { TUNER_ABSENT, "Microtune MT2266"}, + { TUNER_ABSENT, "TCL MF10WPP_4N_E"}, + { TUNER_ABSENT, "LG TAPQ_H702F"}, + { TUNER_ABSENT, "TCL M09WPP_4N_E"}, + { TUNER_ABSENT, "MaxLinear MXL5005_v2"}, + { TUNER_PHILIPS_TDA8290, "Philips 18271_8295"}, /* 150-159 */ { TUNER_XC5000, "Xceive XC5000"}, { TUNER_ABSENT, "Xceive XC3028L"}, @@ -784,9 +784,3 @@ int tveeprom_read(struct i2c_client *c, unsigned char *eedata, int len) return 0; } EXPORT_SYMBOL(tveeprom_read); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ -- cgit v1.2.3 From c2db409cbc8751ccc7e6d2cc2e41af0d12ea637f Mon Sep 17 00:00:00 2001 From: Seth Heasley Date: Wed, 30 Jan 2013 15:25:32 +0000 Subject: i2c: i801: SMBus patch for Intel Avoton DeviceIDs This patch adds the PCU SMBus DeviceID for the Intel Avoton SOC. Signed-off-by: Seth Heasley Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- Documentation/i2c/busses/i2c-i801 | 1 + drivers/i2c/busses/Kconfig | 1 + drivers/i2c/busses/i2c-i801.c | 3 +++ 3 files changed, 5 insertions(+) (limited to 'drivers') diff --git a/Documentation/i2c/busses/i2c-i801 b/Documentation/i2c/busses/i2c-i801 index 157416e78cc4..8d71d5705b2a 100644 --- a/Documentation/i2c/busses/i2c-i801 +++ b/Documentation/i2c/busses/i2c-i801 @@ -22,6 +22,7 @@ Supported adapters: * Intel Panther Point (PCH) * Intel Lynx Point (PCH) * Intel Lynx Point-LP (PCH) + * Intel Avoton (SOC) Datasheets: Publicly available at the Intel website On Intel Patsburg and later chipsets, both the normal host SMBus controller diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index bdca5111eb9d..77d28873b76f 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -106,6 +106,7 @@ config I2C_I801 Panther Point (PCH) Lynx Point (PCH) Lynx Point-LP (PCH) + Avoton (SOC) This driver can also be built as a module. If so, the module will be called i2c-i801. diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 3092387f6ef4..b00c29d8a5f1 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -53,6 +53,7 @@ Panther Point (PCH) 0x1e22 32 hard yes yes yes Lynx Point (PCH) 0x8c22 32 hard yes yes yes Lynx Point-LP (PCH) 0x9c22 32 hard yes yes yes + Avoton (SOC) 0x1f3c 32 hard yes yes yes Features supported by this driver: Software PEC no @@ -162,6 +163,7 @@ #define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1 0x1d71 #define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2 0x1d72 #define PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS 0x1e22 +#define PCI_DEVICE_ID_INTEL_AVOTON_SMBUS 0x1f3c #define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330 #define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22 @@ -798,6 +800,7 @@ static DEFINE_PCI_DEVICE_TABLE(i801_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_AVOTON_SMBUS) }, { 0, } }; -- cgit v1.2.3 From e6066dba444903344c843dbdcb77a4823c51e7e3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 6 Feb 2013 08:14:47 -0300 Subject: [media] [REVIEW] em28xx: fix bytesperline calculation in TRY_FMT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bytesperline calculation was incorrect: it used the old width instead of the provided width. Fixed. Signed-off-by: Hans Verkuil Acked-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 2eabf2a94752..32bd7de5dec1 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -906,7 +906,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.width = width; f->fmt.pix.height = height; f->fmt.pix.pixelformat = fmt->fourcc; - f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3; + f->fmt.pix.bytesperline = (width * fmt->depth + 7) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; if (dev->progressive) -- cgit v1.2.3 From 5853d1acdeb3e40daaac6c465d92fc069272cb6a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 7 Feb 2013 02:55:54 -0300 Subject: [media] s5p-tv: Include missing irqreturn.h header Without this patch we get the following compilation errors: drivers/media/platform/s5p-tv/mixer.h:345:13: error: Expected ; at end of declaration drivers/media/platform/s5p-tv/mixer.h:345:13: error: got mxr_irq_handler Signed-off-by: Sachin Kamat Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/mixer.h | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-tv/mixer.h b/drivers/media/platform/s5p-tv/mixer.h index b671e20e9318..04e6490a45be 100644 --- a/drivers/media/platform/s5p-tv/mixer.h +++ b/drivers/media/platform/s5p-tv/mixer.h @@ -19,6 +19,7 @@ #endif #include +#include #include #include #include -- cgit v1.2.3 From 3e58ac14ad2c443d86c5bed0137a010fe4d16fe2 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 7 Feb 2013 02:55:55 -0300 Subject: [media] s5p-tv: Include missing platform_device.h header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without this patch we get the following build error: drivers/media/platform/s5p-tv/mixer_video.c: In function ‘find_and_register_subdev’: drivers/media/platform/s5p-tv/mixer_video.c:42:34: error: ‘platform_bus_type’ undeclared (first use in this function) Signed-off-by: Sachin Kamat Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/mixer_video.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index c087b66099fd..82142a2d6d93 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include -- cgit v1.2.3 From bf5bbed15c41228ea1abbb8d3931050922bfc37f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 7 Feb 2013 04:24:49 -0300 Subject: [media] dvb-usb: check for invalid length in ttusb_process_muxpack() This patch is driven by a static checker warning. The ttusb_process_muxpack() function is only called from ttusb_process_frame(). Before calling, it verifies that len >= 2. The problem is that len == 2 is not valid and would lead to an array underflow. Odd number values for len are also invalid and would lead to reading past the end of the array. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c index 5b682cc4c814..e40718552850 100644 --- a/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c +++ b/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c @@ -561,6 +561,13 @@ static void ttusb_process_muxpack(struct ttusb *ttusb, const u8 * muxpack, { u16 csum = 0, cc; int i; + + if (len < 4 || len & 0x1) { + pr_warn("%s: muxpack has invalid len %d\n", __func__, len); + numinvalid++; + return; + } + for (i = 0; i < len; i += 2) csum ^= le16_to_cpup((__le16 *) (muxpack + i)); if (csum) { -- cgit v1.2.3 From 82f0efbcd3c4e6bf7cdfeed5c901b812e6d30f92 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2013 07:05:43 -0300 Subject: [media] tm6000: fix an uninitialized variable tm6000_poll could use an uninitialized buf pointer. Move the buf-handling code inside the 'if' that initializes the buf pointer. Signed-off-by: Hans Verkuil Reported-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-video.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index eab23413a909..1a6857929c15 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -1455,14 +1455,14 @@ __tm6000_poll(struct file *file, struct poll_table_struct *wait) if (list_empty(&fh->vb_vidq.stream)) return res | POLLERR; buf = list_entry(fh->vb_vidq.stream.next, struct tm6000_buffer, vb.stream); + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || + buf->vb.state == VIDEOBUF_ERROR) + return res | POLLIN | POLLRDNORM; } else if (req_events & (POLLIN | POLLRDNORM)) { /* read() capture */ return res | videobuf_poll_stream(file, &fh->vb_vidq, wait); } - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == VIDEOBUF_DONE || - buf->vb.state == VIDEOBUF_ERROR) - return res | POLLIN | POLLRDNORM; return res; } -- cgit v1.2.3 From 0917a60430cdd4b5d3505c240a04bb0f5927c74b Mon Sep 17 00:00:00 2001 From: Michael Büsch Date: Thu, 7 Feb 2013 12:13:13 -0300 Subject: [media] fc0011: fp/fa value overflow fix Assign the maximum instead of masking with the maximum on value overflow. Signed-off-by: Michael Buesch Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0011.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/tuners/fc0011.c b/drivers/media/tuners/fc0011.c index e4882546c283..3089f2ebcbfe 100644 --- a/drivers/media/tuners/fc0011.c +++ b/drivers/media/tuners/fc0011.c @@ -247,8 +247,8 @@ static int fc0011_set_params(struct dvb_frontend *fe) fa += 8; } if (fp > 0x1F) { - fp &= 0x1F; - fa &= 0xF; + fp = 0x1F; + fa = 0xF; } if (fa >= fp) { dev_warn(&priv->i2c->dev, -- cgit v1.2.3 From db5c05b2a1c02e401778de348451bae49b65806e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 8 Feb 2013 17:47:01 -0200 Subject: Revert "[media] [PATH,1/2] mxl5007 move reset to attach" This patch was applied by mistake. Michael thinks that it it needs more work than simply moving the soft reset. So, it should be held back for further review. This reverts commit 0a3237704dec476be3cdfbe8fc9df9cc65b14442. Requested by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/mxl5007t.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/media/tuners/mxl5007t.c b/drivers/media/tuners/mxl5007t.c index eb61304c260d..69e453ef0a1a 100644 --- a/drivers/media/tuners/mxl5007t.c +++ b/drivers/media/tuners/mxl5007t.c @@ -531,6 +531,10 @@ static int mxl5007t_tuner_init(struct mxl5007t_state *state, struct reg_pair_t *init_regs; int ret; + ret = mxl5007t_soft_reset(state); + if (mxl_fail(ret)) + goto fail; + /* calculate initialization reg array */ init_regs = mxl5007t_calc_init_regs(state, mode); @@ -896,20 +900,7 @@ struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe, /* existing tuner instance */ break; } - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - ret = mxl5007t_soft_reset(state); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - if (mxl_fail(ret)) - goto fail; - fe->tuner_priv = state; - mutex_unlock(&mxl5007t_list_mutex); memcpy(&fe->ops.tuner_ops, &mxl5007t_tuner_ops, -- cgit v1.2.3 From 03a497d4b4c5205f6a365c4673e21298e681a8a6 Mon Sep 17 00:00:00 2001 From: Michael Büsch Date: Thu, 7 Feb 2013 12:16:55 -0300 Subject: [media] fc0011: Fix xin value clamping Fix the xin value clamping and use clamp_t(). Signed-off-by: Michael Buesch Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0011.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/media/tuners/fc0011.c b/drivers/media/tuners/fc0011.c index 3089f2ebcbfe..f87aa5a8b8ea 100644 --- a/drivers/media/tuners/fc0011.c +++ b/drivers/media/tuners/fc0011.c @@ -183,8 +183,7 @@ static int fc0011_set_params(struct dvb_frontend *fe) unsigned int i, vco_retries; u32 freq = p->frequency / 1000; u32 bandwidth = p->bandwidth_hz / 1000; - u32 fvco, xin, xdiv, xdivr; - u16 frac; + u32 fvco, xin, frac, xdiv, xdivr; u8 fa, fp, vco_sel, vco_cal; u8 regs[FC11_NR_REGS] = { }; @@ -227,12 +226,8 @@ static int fc0011_set_params(struct dvb_frontend *fe) frac += 32786; if (!frac) xin = 0; - else if (frac < 511) - xin = 512; - else if (frac < 65026) - xin = frac; else - xin = 65024; + xin = clamp_t(u32, frac, 512, 65024); regs[FC11_REG_XINHI] = xin >> 8; regs[FC11_REG_XINLO] = xin; -- cgit v1.2.3 From aadb4640109b76cc9d0e1d8ce6b7bc3258a89170 Mon Sep 17 00:00:00 2001 From: Michael Büsch Date: Thu, 7 Feb 2013 12:19:30 -0300 Subject: [media] fc0011: Add some sanity checks and cleanups Add some sanity checks to the calculations and make the REG_16 register write consistent with the other ones. Signed-off-by: Michael Buesch Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0011.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/tuners/fc0011.c b/drivers/media/tuners/fc0011.c index f87aa5a8b8ea..3932aa81e18c 100644 --- a/drivers/media/tuners/fc0011.c +++ b/drivers/media/tuners/fc0011.c @@ -220,6 +220,7 @@ static int fc0011_set_params(struct dvb_frontend *fe) /* Calc XIN. The PLL reference frequency is 18 MHz. */ xdiv = fvco / 18000; + WARN_ON(xdiv > 0xFF); frac = fvco - xdiv * 18000; frac = (frac << 15) / 18000; if (frac >= 16384) @@ -346,6 +347,8 @@ static int fc0011_set_params(struct dvb_frontend *fe) vco_cal &= FC11_VCOCAL_VALUEMASK; switch (vco_sel) { + default: + WARN_ON(1); case 0: if (vco_cal < 8) { regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); @@ -427,7 +430,8 @@ static int fc0011_set_params(struct dvb_frontend *fe) err = fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); if (err) return err; - err = fc0011_writereg(priv, FC11_REG_16, 0xB); + regs[FC11_REG_16] = 0xB; + err = fc0011_writereg(priv, FC11_REG_16, regs[FC11_REG_16]); if (err) return err; -- cgit v1.2.3 From a92591a7112042f92b609be42bc332d989776e9b Mon Sep 17 00:00:00 2001 From: Michael Büsch Date: Thu, 7 Feb 2013 12:21:06 -0300 Subject: [media] fc0011: Return early, if the frequency is already tuned Return early, if we already tuned to a frequency. Signed-off-by: Michael Buesch Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0011.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/media/tuners/fc0011.c b/drivers/media/tuners/fc0011.c index 3932aa81e18c..18caab11c94b 100644 --- a/drivers/media/tuners/fc0011.c +++ b/drivers/media/tuners/fc0011.c @@ -187,6 +187,9 @@ static int fc0011_set_params(struct dvb_frontend *fe) u8 fa, fp, vco_sel, vco_cal; u8 regs[FC11_NR_REGS] = { }; + if (priv->frequency == p->frequency) + return 0; + regs[FC11_REG_7] = 0x0F; regs[FC11_REG_8] = 0x3E; regs[FC11_REG_10] = 0xB8; -- cgit v1.2.3 From 4880f56438ef56457edd5548b257382761591998 Mon Sep 17 00:00:00 2001 From: Cong Ding Date: Sun, 3 Feb 2013 22:34:48 -0300 Subject: [media] stv0900: remove unnecessary null pointer check The address of a variable is impossible to be null, so we remove the check. Signed-off-by: Cong Ding Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/stv0900_core.c | 14 ++++---------- drivers/media/dvb-frontends/stv0900_sw.c | 7 ++----- 2 files changed, 6 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c index 0fb34e1909d1..e5a87b57d855 100644 --- a/drivers/media/dvb-frontends/stv0900_core.c +++ b/drivers/media/dvb-frontends/stv0900_core.c @@ -524,11 +524,8 @@ void stv0900_set_tuner(struct dvb_frontend *fe, u32 frequency, struct dvb_frontend_ops *frontend_ops = NULL; struct dvb_tuner_ops *tuner_ops = NULL; - if (&fe->ops) - frontend_ops = &fe->ops; - - if (&frontend_ops->tuner_ops) - tuner_ops = &frontend_ops->tuner_ops; + frontend_ops = &fe->ops; + tuner_ops = &frontend_ops->tuner_ops; if (tuner_ops->set_frequency) { if ((tuner_ops->set_frequency(fe, frequency)) < 0) @@ -552,11 +549,8 @@ void stv0900_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) struct dvb_frontend_ops *frontend_ops = NULL; struct dvb_tuner_ops *tuner_ops = NULL; - if (&fe->ops) - frontend_ops = &fe->ops; - - if (&frontend_ops->tuner_ops) - tuner_ops = &frontend_ops->tuner_ops; + frontend_ops = &fe->ops; + tuner_ops = &frontend_ops->tuner_ops; if (tuner_ops->set_bandwidth) { if ((tuner_ops->set_bandwidth(fe, bandwidth)) < 0) diff --git a/drivers/media/dvb-frontends/stv0900_sw.c b/drivers/media/dvb-frontends/stv0900_sw.c index 4af20780fb9c..0a40edfad739 100644 --- a/drivers/media/dvb-frontends/stv0900_sw.c +++ b/drivers/media/dvb-frontends/stv0900_sw.c @@ -1167,11 +1167,8 @@ static u32 stv0900_get_tuner_freq(struct dvb_frontend *fe) struct dvb_tuner_ops *tuner_ops = NULL; u32 freq = 0; - if (&fe->ops) - frontend_ops = &fe->ops; - - if (&frontend_ops->tuner_ops) - tuner_ops = &frontend_ops->tuner_ops; + frontend_ops = &fe->ops; + tuner_ops = &frontend_ops->tuner_ops; if (tuner_ops->get_frequency) { if ((tuner_ops->get_frequency(fe, &freq)) < 0) -- cgit v1.2.3 From 1a989d0f1de8f5a150b35e1e8181cc1abc139162 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sun, 3 Feb 2013 01:29:24 +0900 Subject: gpiolib: link all gpio_chips using a list Add a list member to gpio_chip that allows all chips to be parsed quickly. The current method requires parsing the entire GPIO integer space, which is painfully slow. Using a list makes many chip operations that involve lookup or parsing faster, and also simplifies the code. It is also necessary to eventually get rid of the global gpio_desc[] array. The list of gpio_chips is always ordered by base GPIO number to ensure chips traversal is done in the right order. Signed-off-by: Alexandre Courbot Reviewed-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/gpio/gpiolib.c | 51 +++++++++++++++++++++++++++++++++++++++------- include/asm-generic/gpio.h | 2 ++ 2 files changed, 46 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e14eb88bbe8d..453ac77771ca 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -71,6 +72,8 @@ struct gpio_desc { }; static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; +static LIST_HEAD(gpio_chips); + #ifdef CONFIG_GPIO_SYSFS static DEFINE_IDR(dirent_idr); #endif @@ -1014,6 +1017,43 @@ static inline void gpiochip_unexport(struct gpio_chip *chip) #endif /* CONFIG_GPIO_SYSFS */ +/* + * Add a new chip to the global chips list, keeping the list of chips sorted + * by base order. + * + * Return -EBUSY if the new chip overlaps with some other chip's integer + * space. + */ +static int gpiochip_add_to_list(struct gpio_chip *chip) +{ + struct list_head *pos = &gpio_chips; + struct gpio_chip *_chip; + int err = 0; + + /* find where to insert our chip */ + list_for_each(pos, &gpio_chips) { + _chip = list_entry(pos, struct gpio_chip, list); + /* shall we insert before _chip? */ + if (_chip->base >= chip->base + chip->ngpio) + break; + } + + /* are we stepping on the chip right before? */ + if (pos != &gpio_chips && pos->prev != &gpio_chips) { + _chip = list_entry(pos->prev, struct gpio_chip, list); + if (_chip->base + _chip->ngpio > chip->base) { + dev_err(chip->dev, + "GPIO integer space overlap, cannot add chip\n"); + err = -EBUSY; + } + } + + if (!err) + list_add_tail(&chip->list, pos); + + return err; +} + /** * gpiochip_add() - register a gpio_chip * @chip: the chip to register, with chip->base initialized @@ -1055,13 +1095,8 @@ int gpiochip_add(struct gpio_chip *chip) chip->base = base; } - /* these GPIO numbers must not be managed by another gpio_chip */ - for (id = base; id < base + chip->ngpio; id++) { - if (gpio_desc[id].chip != NULL) { - status = -EBUSY; - break; - } - } + status = gpiochip_add_to_list(chip); + if (status == 0) { for (id = base; id < base + chip->ngpio; id++) { gpio_desc[id].chip = chip; @@ -1135,6 +1170,8 @@ int gpiochip_remove(struct gpio_chip *chip) if (status == 0) { for (id = chip->base; id < chip->base + chip->ngpio; id++) gpio_desc[id].chip = NULL; + + list_del(&chip->list); } spin_unlock_irqrestore(&gpio_lock, flags); diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 2034e691c7ab..b562f95cdc2f 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -53,6 +53,7 @@ struct device_node; * @label: for diagnostics * @dev: optional device providing the GPIOs * @owner: helps prevent removal of modules exporting active GPIOs + * @list: links gpio_chips together for traversal * @request: optional hook for chip-specific activation, such as * enabling module power and clock; may sleep * @free: optional hook for chip-specific deactivation, such as @@ -98,6 +99,7 @@ struct gpio_chip { const char *label; struct device *dev; struct module *owner; + struct list_head list; int (*request)(struct gpio_chip *chip, unsigned offset); -- cgit v1.2.3 From 65493e3ac429623df021e0859d97691f4b42615a Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sun, 3 Feb 2013 01:29:25 +0900 Subject: gpiolib: use gpio_chips list in gpiolib_sysfs_init Use the small list of GPIO chips instead of parsing the whole GPIO number space. Signed-off-by: Alexandre Courbot Reviewed-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/gpio/gpiolib.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 453ac77771ca..c1d8f7bdcd8f 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -974,7 +974,7 @@ static int __init gpiolib_sysfs_init(void) { int status; unsigned long flags; - unsigned gpio; + struct gpio_chip *chip; status = class_register(&gpio_class); if (status < 0) @@ -987,10 +987,7 @@ static int __init gpiolib_sysfs_init(void) * registered, and so arch_initcall() can always gpio_export(). */ spin_lock_irqsave(&gpio_lock, flags); - for (gpio = 0; gpio < ARCH_NR_GPIOS; gpio++) { - struct gpio_chip *chip; - - chip = gpio_desc[gpio].chip; + list_for_each_entry(chip, &gpio_chips, list) { if (!chip || chip->exported) continue; -- cgit v1.2.3 From 125eef96f6cfadddbac8f6b9fccc9848988e7c6e Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sun, 3 Feb 2013 01:29:26 +0900 Subject: gpiolib: use gpio_chips list in gpiochip_find Using the GPIO chips list is much faster than parsing the entire GPIO number space. Signed-off-by: Alexandre Courbot Reviewed-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/gpio/gpiolib.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index c1d8f7bdcd8f..5a79cb955d9f 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1195,20 +1195,17 @@ struct gpio_chip *gpiochip_find(void *data, int (*match)(struct gpio_chip *chip, void *data)) { - struct gpio_chip *chip = NULL; + struct gpio_chip *chip; unsigned long flags; - int i; spin_lock_irqsave(&gpio_lock, flags); - for (i = 0; i < ARCH_NR_GPIOS; i++) { - if (!gpio_desc[i].chip) - continue; - - if (match(gpio_desc[i].chip, data)) { - chip = gpio_desc[i].chip; + list_for_each_entry(chip, &gpio_chips, list) + if (match(chip, data)) break; - } - } + + /* No match? */ + if (&chip->list == &gpio_chips) + chip = NULL; spin_unlock_irqrestore(&gpio_lock, flags); return chip; -- cgit v1.2.3 From cb1650d4e0da27e88c1a1bd8fe98c40ae1a5d313 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sun, 3 Feb 2013 01:29:27 +0900 Subject: gpiolib: use gpio_chips list in sysfs ops This makes the code both simpler and faster compared to parsing the GPIO number space. Signed-off-by: Alexandre Courbot Signed-off-by: Grant Likely --- drivers/gpio/gpiolib.c | 37 ++++++++++--------------------------- 1 file changed, 10 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 5a79cb955d9f..585d7c3ce12e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1890,45 +1890,28 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos) { struct gpio_chip *chip = NULL; - unsigned int gpio; - void *ret = NULL; - loff_t index = 0; + loff_t index = *pos; /* REVISIT this isn't locked against gpio_chip removal ... */ - for (gpio = 0; gpio_is_valid(gpio); gpio++) { - if (gpio_desc[gpio].chip == chip) - continue; - - chip = gpio_desc[gpio].chip; - if (!chip) - continue; - - if (index++ >= *pos) { - ret = chip; - break; - } - } - s->private = ""; - return ret; + list_for_each_entry(chip, &gpio_chips, list) + if (index-- == 0) + return chip; + + return NULL; } static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos) { struct gpio_chip *chip = v; - unsigned int gpio; void *ret = NULL; - /* skip GPIOs provided by the current chip */ - for (gpio = chip->base + chip->ngpio; gpio_is_valid(gpio); gpio++) { - chip = gpio_desc[gpio].chip; - if (chip) { - ret = chip; - break; - } - } + if (list_is_last(&chip->list, &gpio_chips)) + ret = NULL; + else + ret = list_entry(chip->list.next, struct gpio_chip, list); s->private = "\n"; ++*pos; -- cgit v1.2.3 From 83cabe33eb05b51a6239a3df344d89cafac2306c Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sun, 3 Feb 2013 01:29:28 +0900 Subject: gpiolib: use gpio_chips list in gpiochip_find_base Re-implement gpiochip_find_base using the list of chips instead of the global gpio_desc[] array. This makes it both simpler and more efficient, and is needed to remove the global descriptors array. The new code should preserve the exact same GPIO number assignment policy as the code it is replacing. There shouldn't be any visible change to the assigned GPIO numbers. Signed-off-by: Alexandre Courbot [grant.likely: Added comment about assignment policy] Signed-off-by: Grant Likely --- drivers/gpio/gpiolib.c | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 585d7c3ce12e..8ccf68bb8a40 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -126,30 +126,25 @@ struct gpio_chip *gpio_to_chip(unsigned gpio) /* dynamic allocation of GPIOs, e.g. on a hotplugged device */ static int gpiochip_find_base(int ngpio) { - int i; - int spare = 0; - int base = -ENOSPC; - - for (i = ARCH_NR_GPIOS - 1; i >= 0 ; i--) { - struct gpio_desc *desc = &gpio_desc[i]; - struct gpio_chip *chip = desc->chip; - - if (!chip) { - spare++; - if (spare == ngpio) { - base = i; - break; - } - } else { - spare = 0; - if (chip) - i -= chip->ngpio - 1; - } + struct gpio_chip *chip; + int base = ARCH_NR_GPIOS - ngpio; + + list_for_each_entry_reverse(chip, &gpio_chips, list) { + /* found a free space? */ + if (chip->base + chip->ngpio <= base) + break; + else + /* nope, check the space right before the chip */ + base = chip->base - ngpio; } - if (gpio_is_valid(base)) + if (gpio_is_valid(base)) { pr_debug("%s: found new base at %d\n", __func__, base); - return base; + return base; + } else { + pr_err("%s: cannot find free range\n", __func__); + return -ENOSPC; + } } /* caller ensures gpio is valid and requested, chip->get_direction may sleep */ -- cgit v1.2.3 From 28c55dc1acc863cb29832b5be2464ebcdafdc3d5 Mon Sep 17 00:00:00 2001 From: Martin Fuzzey Date: Tue, 29 Jan 2013 16:46:10 +0100 Subject: W1: Add device tree support to MXC onewire master. Signed-off-by: Martin Fuzzey Acked-by: Sascha Hauer Acked-by: Evgeniy Polyakov Signed-off-by: Shawn Guo --- .../devicetree/bindings/w1/fsl-imx-owire.txt | 19 +++++++++++++++++++ drivers/w1/masters/mxc_w1.c | 9 ++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/w1/fsl-imx-owire.txt (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/w1/fsl-imx-owire.txt b/Documentation/devicetree/bindings/w1/fsl-imx-owire.txt new file mode 100644 index 000000000000..ecf42c07684d --- /dev/null +++ b/Documentation/devicetree/bindings/w1/fsl-imx-owire.txt @@ -0,0 +1,19 @@ +* Freescale i.MX One wire bus master controller + +Required properties: +- compatible : should be "fsl,imx21-owire" +- reg : Address and length of the register set for the device + +Optional properties: +- clocks : phandle of clock that supplies the module (required if platform + clock bindings use device tree) + +Example: + +- From imx53.dtsi: +owire: owire@63fa4000 { + compatible = "fsl,imx53-owire", "fsl,imx21-owire"; + reg = <0x63fa4000 0x4000>; + clocks = <&clks 159>; + status = "disabled"; +}; diff --git a/drivers/w1/masters/mxc_w1.c b/drivers/w1/masters/mxc_w1.c index 708a25fc9961..949e56669548 100644 --- a/drivers/w1/masters/mxc_w1.c +++ b/drivers/w1/masters/mxc_w1.c @@ -186,9 +186,16 @@ static int mxc_w1_remove(struct platform_device *pdev) return 0; } +static struct of_device_id mxc_w1_dt_ids[] = { + { .compatible = "fsl,imx21-owire" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxc_w1_dt_ids); + static struct platform_driver mxc_w1_driver = { .driver = { - .name = "mxc_w1", + .name = "mxc_w1", + .of_match_table = mxc_w1_dt_ids, }, .probe = mxc_w1_probe, .remove = mxc_w1_remove, -- cgit v1.2.3 From 626f0a2ff6bc9820b17c17958d5862262556892f Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 30 Nov 2012 18:48:35 +0100 Subject: i2c: mxs: Implement arbitrary clock speed derivation algorithm This patch drops the i2c timing tables from this driver and instead derives the timing based from the requested clock sleep. The timing tables were completely wrong anyway when observed on a scope. This new algorithm is also only derived by using a scope, but it seems to produce much more accurate result. Signed-off-by: Marek Vasut Tested-by: Shawn Guo [wsa: changed messages from dev_err to dev_warn] Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mxs.c | 94 ++++++++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index e55736077d23..22d8ad353409 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -93,35 +93,6 @@ #define MXS_CMD_I2C_READ (MXS_I2C_CTRL0_SEND_NAK_ON_LAST | \ MXS_I2C_CTRL0_MASTER_MODE) -struct mxs_i2c_speed_config { - uint32_t timing0; - uint32_t timing1; - uint32_t timing2; -}; - -/* - * Timing values for the default 24MHz clock supplied into the i2c block. - * - * The bus can operate at 95kHz or at 400kHz with the following timing - * register configurations. The 100kHz mode isn't present because it's - * values are not stated in the i.MX233/i.MX28 datasheet. The 95kHz mode - * shall be close enough replacement. Therefore when the bus is configured - * for 100kHz operation, 95kHz timing settings are actually loaded. - * - * For details, see i.MX233 [25.4.2 - 25.4.4] and i.MX28 [27.5.2 - 27.5.4]. - */ -static const struct mxs_i2c_speed_config mxs_i2c_95kHz_config = { - .timing0 = 0x00780030, - .timing1 = 0x00800030, - .timing2 = 0x00300030, -}; - -static const struct mxs_i2c_speed_config mxs_i2c_400kHz_config = { - .timing0 = 0x000f0007, - .timing1 = 0x001f000f, - .timing2 = 0x00300030, -}; - /** * struct mxs_i2c_dev - per device, private MXS-I2C data * @@ -137,7 +108,9 @@ struct mxs_i2c_dev { struct completion cmd_complete; int cmd_err; struct i2c_adapter adapter; - const struct mxs_i2c_speed_config *speed; + + uint32_t timing0; + uint32_t timing1; /* DMA support components */ int dma_channel; @@ -153,9 +126,16 @@ static void mxs_i2c_reset(struct mxs_i2c_dev *i2c) { stmp_reset_block(i2c->regs); - writel(i2c->speed->timing0, i2c->regs + MXS_I2C_TIMING0); - writel(i2c->speed->timing1, i2c->regs + MXS_I2C_TIMING1); - writel(i2c->speed->timing2, i2c->regs + MXS_I2C_TIMING2); + /* + * Configure timing for the I2C block. The I2C TIMING2 register has to + * be programmed with this particular magic number. The rest is derived + * from the XTAL speed and requested I2C speed. + * + * For details, see i.MX233 [25.4.2 - 25.4.4] and i.MX28 [27.5.2 - 27.5.4]. + */ + writel(i2c->timing0, i2c->regs + MXS_I2C_TIMING0); + writel(i2c->timing1, i2c->regs + MXS_I2C_TIMING1); + writel(0x00300030, i2c->regs + MXS_I2C_TIMING2); writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_SET); } @@ -553,6 +533,43 @@ static bool mxs_i2c_dma_filter(struct dma_chan *chan, void *param) return true; } +static void mxs_i2c_derive_timing(struct mxs_i2c_dev *i2c, int speed) +{ + /* The I2C block clock run at 24MHz */ + const uint32_t clk = 24000000; + uint32_t base; + uint16_t high_count, low_count, rcv_count, xmit_count; + struct device *dev = i2c->dev; + + if (speed > 540000) { + dev_warn(dev, "Speed too high (%d Hz), using 540 kHz\n", speed); + speed = 540000; + } else if (speed < 12000) { + dev_warn(dev, "Speed too low (%d Hz), using 12 kHz\n", speed); + speed = 12000; + } + + /* + * The timing derivation algorithm. There is no documentation for this + * algorithm available, it was derived by using the scope and fiddling + * with constants until the result observed on the scope was good enough + * for 20kHz, 50kHz, 100kHz, 200kHz, 300kHz and 400kHz. It should be + * possible to assume the algorithm works for other frequencies as well. + * + * Note it was necessary to cap the frequency on both ends as it's not + * possible to configure completely arbitrary frequency for the I2C bus + * clock. + */ + base = ((clk / speed) - 38) / 2; + high_count = base + 3; + low_count = base - 3; + rcv_count = (high_count * 3) / 4; + xmit_count = low_count / 4; + + i2c->timing0 = (high_count << 16) | rcv_count; + i2c->timing1 = (low_count << 16) | xmit_count; +} + static int mxs_i2c_get_ofdata(struct mxs_i2c_dev *i2c) { uint32_t speed; @@ -572,12 +589,12 @@ static int mxs_i2c_get_ofdata(struct mxs_i2c_dev *i2c) } ret = of_property_read_u32(node, "clock-frequency", &speed); - if (ret) + if (ret) { dev_warn(dev, "No I2C speed selected, using 100kHz\n"); - else if (speed == 400000) - i2c->speed = &mxs_i2c_400kHz_config; - else if (speed != 100000) - dev_warn(dev, "Unsupported I2C speed selected, using 100kHz\n"); + speed = 100000; + } + + mxs_i2c_derive_timing(i2c, speed); return 0; } @@ -621,7 +638,6 @@ static int mxs_i2c_probe(struct platform_device *pdev) return err; i2c->dev = dev; - i2c->speed = &mxs_i2c_95kHz_config; init_completion(&i2c->cmd_complete); -- cgit v1.2.3 From 05cf936846ca6aef1b34ba26054728fdbc154558 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 17 Jan 2013 10:45:54 +0100 Subject: i2c: sh_mobile: cosmetic: trivially simplify 2 functions Reduce 2 boolean functions from "if (condition) return 1; return 0;" to "return condition;" Signed-off-by: Guennadi Liakhovetski Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index b6e7a83a8296..34024f3a19f5 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -349,20 +349,14 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, return ret; } -static int sh_mobile_i2c_is_first_byte(struct sh_mobile_i2c_data *pd) +static bool sh_mobile_i2c_is_first_byte(struct sh_mobile_i2c_data *pd) { - if (pd->pos == -1) - return 1; - - return 0; + return pd->pos == -1; } -static int sh_mobile_i2c_is_last_byte(struct sh_mobile_i2c_data *pd) +static bool sh_mobile_i2c_is_last_byte(struct sh_mobile_i2c_data *pd) { - if (pd->pos == (pd->msg->len - 1)) - return 1; - - return 0; + return pd->pos == pd->msg->len - 1; } static void sh_mobile_i2c_get_data(struct sh_mobile_i2c_data *pd, -- cgit v1.2.3 From 5687265b3127024089dc0b25956772405b9f53d3 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 17 Jan 2013 10:45:55 +0100 Subject: i2c: sh_mobile: fix timeout error handling In a timeout case return an error immediately from the driver's .master_xfer() method, instead of continuing and letting higher layers fail. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 34024f3a19f5..daaf0ebe8177 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -521,8 +521,11 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, k = wait_event_timeout(pd->wait, pd->sr & (ICSR_TACK | SW_DONE), 5 * HZ); - if (!k) + if (!k) { dev_err(pd->dev, "Transfer request timed out\n"); + err = -ETIMEDOUT; + break; + } retry_count = 1000; again: -- cgit v1.2.3 From 4b3823184f80c1d3950e5e63e2303653f2decdf8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 17 Jan 2013 10:45:56 +0100 Subject: i2c: sh_mobile: eliminate an open-coded "goto" loop Eliminate an open-coded "goto" loop by introducing a function. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 60 +++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index daaf0ebe8177..2767ea8e98a7 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -495,6 +495,37 @@ static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg) return 0; } +static int poll_busy(struct sh_mobile_i2c_data *pd) +{ + int i; + + for (i = 1000; i; i--) { + u_int8_t val = iic_rd(pd, ICSR); + + dev_dbg(pd->dev, "val 0x%02x pd->sr 0x%02x\n", val, pd->sr); + + /* the interrupt handler may wake us up before the + * transfer is finished, so poll the hardware + * until we're done. + */ + if (!(val & ICSR_BUSY)) { + /* handle missing acknowledge and arbitration lost */ + if ((val | pd->sr) & (ICSR_TACK | ICSR_AL)) + return -EIO; + break; + } + + udelay(10); + } + + if (!i) { + dev_err(pd->dev, "Polling timed out\n"); + return -ETIMEDOUT; + } + + return 0; +} + static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) @@ -502,8 +533,7 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, struct sh_mobile_i2c_data *pd = i2c_get_adapdata(adapter); struct i2c_msg *msg; int err = 0; - u_int8_t val; - int i, k, retry_count; + int i, k; activate_ch(pd); @@ -527,31 +557,9 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, break; } - retry_count = 1000; -again: - val = iic_rd(pd, ICSR); - - dev_dbg(pd->dev, "val 0x%02x pd->sr 0x%02x\n", val, pd->sr); - - /* the interrupt handler may wake us up before the - * transfer is finished, so poll the hardware - * until we're done. - */ - if (val & ICSR_BUSY) { - udelay(10); - if (retry_count--) - goto again; - - err = -EIO; - dev_err(pd->dev, "Polling timed out\n"); + err = poll_busy(pd); + if (err < 0) break; - } - - /* handle missing acknowledge and arbitration lost */ - if ((val | pd->sr) & (ICSR_TACK | ICSR_AL)) { - err = -EIO; - break; - } } deactivate_ch(pd); -- cgit v1.2.3 From e789029761503f0cce03e8767a56ae099b88e1bd Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 17 Jan 2013 10:45:57 +0100 Subject: i2c: sh_mobile: don't send a stop condition by default inside transfers By default there should be no stop bit on I2C between single messages within transfers. Fix the driver to comply and only send a stop bit at the end of transfers or if I2C_M_STOP is set. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 79 ++++++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 2767ea8e98a7..debf745c0268 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -38,21 +38,21 @@ /* Transmit operation: */ /* */ /* 0 byte transmit */ -/* BUS: S A8 ACK P */ +/* BUS: S A8 ACK P(*) */ /* IRQ: DTE WAIT */ /* ICIC: */ /* ICCR: 0x94 0x90 */ /* ICDR: A8 */ /* */ /* 1 byte transmit */ -/* BUS: S A8 ACK D8(1) ACK P */ +/* BUS: S A8 ACK D8(1) ACK P(*) */ /* IRQ: DTE WAIT WAIT */ /* ICIC: -DTE */ /* ICCR: 0x94 0x90 */ /* ICDR: A8 D8(1) */ /* */ /* 2 byte transmit */ -/* BUS: S A8 ACK D8(1) ACK D8(2) ACK P */ +/* BUS: S A8 ACK D8(1) ACK D8(2) ACK P(*) */ /* IRQ: DTE WAIT WAIT WAIT */ /* ICIC: -DTE */ /* ICCR: 0x94 0x90 */ @@ -66,20 +66,20 @@ /* 0 byte receive - not supported since slave may hold SDA low */ /* */ /* 1 byte receive [TX] | [RX] */ -/* BUS: S A8 ACK | D8(1) ACK P */ +/* BUS: S A8 ACK | D8(1) ACK P(*) */ /* IRQ: DTE WAIT | WAIT DTE */ /* ICIC: -DTE | +DTE */ /* ICCR: 0x94 0x81 | 0xc0 */ /* ICDR: A8 | D8(1) */ /* */ /* 2 byte receive [TX]| [RX] */ -/* BUS: S A8 ACK | D8(1) ACK D8(2) ACK P */ +/* BUS: S A8 ACK | D8(1) ACK D8(2) ACK P(*) */ /* IRQ: DTE WAIT | WAIT WAIT DTE */ /* ICIC: -DTE | +DTE */ /* ICCR: 0x94 0x81 | 0xc0 */ /* ICDR: A8 | D8(1) D8(2) */ /* */ -/* 3 byte receive [TX] | [RX] */ +/* 3 byte receive [TX] | [RX] (*) */ /* BUS: S A8 ACK | D8(1) ACK D8(2) ACK D8(3) ACK P */ /* IRQ: DTE WAIT | WAIT WAIT WAIT DTE */ /* ICIC: -DTE | +DTE */ @@ -94,7 +94,7 @@ /* SDA ___\___XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAAAAAAA___/ */ /* SCL \_/1\_/2\_/3\_/4\_/5\_/6\_/7\_/8\___/9\_____/ */ /* */ -/* S D7 D6 D5 D4 D3 D2 D1 D0 P */ +/* S D7 D6 D5 D4 D3 D2 D1 D0 P(*) */ /* ___ */ /* WAIT IRQ ________________________________/ \___________ */ /* TACK IRQ ____________________________________/ \_______ */ @@ -103,6 +103,11 @@ /* _______________________________________________ */ /* BUSY __/ \_ */ /* */ +/* (*) The STOP condition is only sent by the master at the end of the last */ +/* I2C message or if the I2C_M_STOP flag is set. Similarly, the BUSY bit is */ +/* only cleared after the STOP condition, so, between messages we have to */ +/* poll for the DTE bit. */ +/* */ enum sh_mobile_i2c_op { OP_START = 0, @@ -132,6 +137,7 @@ struct sh_mobile_i2c_data { struct i2c_msg *msg; int pos; int sr; + bool send_stop; }; #define IIC_FLAG_HAS_ICIC67 (1 << 0) @@ -322,7 +328,7 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, break; case OP_TX_STOP: /* write data and issue a stop afterwards */ iic_wr(pd, ICDR, data); - iic_wr(pd, ICCR, 0x90); + iic_wr(pd, ICCR, pd->send_stop ? 0x90 : 0x94); break; case OP_TX_TO_RX: /* select read mode */ iic_wr(pd, ICCR, 0x81); @@ -469,22 +475,25 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg) +static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg, + bool do_init) { if (usr_msg->len == 0 && (usr_msg->flags & I2C_M_RD)) { dev_err(pd->dev, "Unsupported zero length i2c read\n"); return -EIO; } - /* Initialize channel registers */ - iic_set_clr(pd, ICCR, 0, ICCR_ICE); + if (do_init) { + /* Initialize channel registers */ + iic_set_clr(pd, ICCR, 0, ICCR_ICE); - /* Enable channel and configure rx ack */ - iic_set_clr(pd, ICCR, ICCR_ICE, 0); + /* Enable channel and configure rx ack */ + iic_set_clr(pd, ICCR, ICCR_ICE, 0); - /* Set the clock */ - iic_wr(pd, ICCL, pd->iccl & 0xff); - iic_wr(pd, ICCH, pd->icch & 0xff); + /* Set the clock */ + iic_wr(pd, ICCL, pd->iccl & 0xff); + iic_wr(pd, ICCH, pd->icch & 0xff); + } pd->msg = usr_msg; pd->pos = -1; @@ -495,6 +504,30 @@ static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg) return 0; } +static int poll_dte(struct sh_mobile_i2c_data *pd) +{ + int i; + + for (i = 1000; i; i--) { + u_int8_t val = iic_rd(pd, ICSR); + + if (val & ICSR_DTE) + break; + + if (val & ICSR_TACK) + return -EIO; + + udelay(10); + } + + if (!i) { + dev_warn(pd->dev, "Timeout polling for DTE!\n"); + return -ETIMEDOUT; + } + + return 0; +} + static int poll_busy(struct sh_mobile_i2c_data *pd) { int i; @@ -539,13 +572,16 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, /* Process all messages */ for (i = 0; i < num; i++) { + bool do_start = pd->send_stop || !i; msg = &msgs[i]; + pd->send_stop = i == num - 1 || msg->flags & I2C_M_STOP; - err = start_ch(pd, msg); + err = start_ch(pd, msg, do_start); if (err) break; - i2c_op(pd, OP_START, 0); + if (do_start) + i2c_op(pd, OP_START, 0); /* The interrupt handler takes care of the rest... */ k = wait_event_timeout(pd->wait, @@ -557,7 +593,10 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, break; } - err = poll_busy(pd); + if (pd->send_stop) + err = poll_busy(pd); + else + err = poll_dte(pd); if (err < 0) break; } @@ -571,7 +610,7 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, static u32 sh_mobile_i2c_func(struct i2c_adapter *adapter) { - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING; } static struct i2c_algorithm sh_mobile_i2c_algorithm = { -- cgit v1.2.3 From 13f35ac14cd0a9a1c4f0034c4c40d0ae98844ce9 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Mon, 4 Feb 2013 14:54:10 -0500 Subject: i2c: Adding support for Intel iSMT SMBus 2.0 host controller The iSMT (Intel SMBus Message Transport) supports multi-master I2C/SMBus, as well as IPMI. It's operation is DMA-based and utilizes descriptors to initiate transactions on the bus. The iSMT hardware can act as both a master and a target, although this driver only supports being a master. Signed-off-by: Neil Horman Signed-off-by: Bill Brown Tested-by: Seth Heasley Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- Documentation/i2c/busses/i2c-ismt | 36 ++ drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-ismt.c | 963 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 1010 insertions(+) create mode 100644 Documentation/i2c/busses/i2c-ismt create mode 100644 drivers/i2c/busses/i2c-ismt.c (limited to 'drivers') diff --git a/Documentation/i2c/busses/i2c-ismt b/Documentation/i2c/busses/i2c-ismt new file mode 100644 index 000000000000..737355822c0b --- /dev/null +++ b/Documentation/i2c/busses/i2c-ismt @@ -0,0 +1,36 @@ +Kernel driver i2c-ismt + +Supported adapters: + * Intel S12xx series SOCs + +Authors: + Bill Brown + + +Module Parameters +----------------- + +* bus_speed (unsigned int) +Allows changing of the bus speed. Normally, the bus speed is set by the BIOS +and never needs to be changed. However, some SMBus analyzers are too slow for +monitoring the bus during debug, thus the need for this module parameter. +Specify the bus speed in kHz. +Available bus frequency settings: + 0 no change + 80 kHz + 100 kHz + 400 kHz + 1000 kHz + + +Description +----------- + +The S12xx series of SOCs have a pair of integrated SMBus 2.0 controllers +targeted primarily at the microserver and storage markets. + +The S12xx series contain a pair of PCI functions. An output of lspci will show +something similar to the following: + + 00:13.0 System peripheral: Intel Corporation Centerton SMBus 2.0 Controller 0 + 00:13.1 System peripheral: Intel Corporation Centerton SMBus 2.0 Controller 1 diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 77d28873b76f..87df863ba110 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -122,6 +122,16 @@ config I2C_ISCH This driver can also be built as a module. If so, the module will be called i2c-isch. +config I2C_ISMT + tristate "Intel iSMT SMBus Controller" + depends on PCI && X86 + help + If you say yes to this option, support will be included for the Intel + iSMT SMBus host controller interface. + + This driver can also be built as a module. If so, the module will be + called i2c-ismt. + config I2C_PIIX4 tristate "Intel PIIX4 and compatible (ATI/AMD/Serverworks/Broadcom/SMSC)" depends on PCI diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 6181f3ff263f..23c9d55bc880 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_I2C_AMD756_S4882) += i2c-amd756-s4882.o obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o obj-$(CONFIG_I2C_I801) += i2c-i801.o obj-$(CONFIG_I2C_ISCH) += i2c-isch.o +obj-$(CONFIG_I2C_ISMT) += i2c-ismt.o obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o obj-$(CONFIG_I2C_NFORCE2_S4985) += i2c-nforce2-s4985.o obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c new file mode 100644 index 000000000000..3f7a9cb6dfdd --- /dev/null +++ b/drivers/i2c/busses/i2c-ismt.c @@ -0,0 +1,963 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2012 Intel Corporation. All rights reserved. + * + * GPL LICENSE SUMMARY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Supports the SMBus Message Transport (SMT) in the Intel Atom Processor + * S12xx Product Family. + * + * Features supported by this driver: + * Hardware PEC yes + * Block buffer yes + * Block process call transaction no + * Slave mode no + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* PCI Address Constants */ +#define SMBBAR 0 + +/* PCI DIDs for the Intel SMBus Message Transport (SMT) Devices */ +#define PCI_DEVICE_ID_INTEL_S1200_SMT0 0x0c59 +#define PCI_DEVICE_ID_INTEL_S1200_SMT1 0x0c5a + +#define ISMT_DESC_ENTRIES 32 /* number of descriptor entries */ +#define ISMT_MAX_RETRIES 3 /* number of SMBus retries to attempt */ + +/* Hardware Descriptor Constants - Control Field */ +#define ISMT_DESC_CWRL 0x01 /* Command/Write Length */ +#define ISMT_DESC_BLK 0X04 /* Perform Block Transaction */ +#define ISMT_DESC_FAIR 0x08 /* Set fairness flag upon successful arbit. */ +#define ISMT_DESC_PEC 0x10 /* Packet Error Code */ +#define ISMT_DESC_I2C 0x20 /* I2C Enable */ +#define ISMT_DESC_INT 0x40 /* Interrupt */ +#define ISMT_DESC_SOE 0x80 /* Stop On Error */ + +/* Hardware Descriptor Constants - Status Field */ +#define ISMT_DESC_SCS 0x01 /* Success */ +#define ISMT_DESC_DLTO 0x04 /* Data Low Time Out */ +#define ISMT_DESC_NAK 0x08 /* NAK Received */ +#define ISMT_DESC_CRC 0x10 /* CRC Error */ +#define ISMT_DESC_CLTO 0x20 /* Clock Low Time Out */ +#define ISMT_DESC_COL 0x40 /* Collisions */ +#define ISMT_DESC_LPR 0x80 /* Large Packet Received */ + +/* Macros */ +#define ISMT_DESC_ADDR_RW(addr, rw) (((addr) << 1) | (rw)) + +/* iSMT General Register address offsets (SMBBAR + ) */ +#define ISMT_GR_GCTRL 0x000 /* General Control */ +#define ISMT_GR_SMTICL 0x008 /* SMT Interrupt Cause Location */ +#define ISMT_GR_ERRINTMSK 0x010 /* Error Interrupt Mask */ +#define ISMT_GR_ERRAERMSK 0x014 /* Error AER Mask */ +#define ISMT_GR_ERRSTS 0x018 /* Error Status */ +#define ISMT_GR_ERRINFO 0x01c /* Error Information */ + +/* iSMT Master Registers */ +#define ISMT_MSTR_MDBA 0x100 /* Master Descriptor Base Address */ +#define ISMT_MSTR_MCTRL 0x108 /* Master Control */ +#define ISMT_MSTR_MSTS 0x10c /* Master Status */ +#define ISMT_MSTR_MDS 0x110 /* Master Descriptor Size */ +#define ISMT_MSTR_RPOLICY 0x114 /* Retry Policy */ + +/* iSMT Miscellaneous Registers */ +#define ISMT_SPGT 0x300 /* SMBus PHY Global Timing */ + +/* General Control Register (GCTRL) bit definitions */ +#define ISMT_GCTRL_TRST 0x04 /* Target Reset */ +#define ISMT_GCTRL_KILL 0x08 /* Kill */ +#define ISMT_GCTRL_SRST 0x40 /* Soft Reset */ + +/* Master Control Register (MCTRL) bit definitions */ +#define ISMT_MCTRL_SS 0x01 /* Start/Stop */ +#define ISMT_MCTRL_MEIE 0x10 /* Master Error Interrupt Enable */ +#define ISMT_MCTRL_FMHP 0x00ff0000 /* Firmware Master Head Ptr (FMHP) */ + +/* Master Status Register (MSTS) bit definitions */ +#define ISMT_MSTS_HMTP 0xff0000 /* HW Master Tail Pointer (HMTP) */ +#define ISMT_MSTS_MIS 0x20 /* Master Interrupt Status (MIS) */ +#define ISMT_MSTS_MEIS 0x10 /* Master Error Int Status (MEIS) */ +#define ISMT_MSTS_IP 0x01 /* In Progress */ + +/* Master Descriptor Size (MDS) bit definitions */ +#define ISMT_MDS_MASK 0xff /* Master Descriptor Size mask (MDS) */ + +/* SMBus PHY Global Timing Register (SPGT) bit definitions */ +#define ISMT_SPGT_SPD_MASK 0xc0000000 /* SMBus Speed mask */ +#define ISMT_SPGT_SPD_80K 0x00 /* 80 kHz */ +#define ISMT_SPGT_SPD_100K (0x1 << 30) /* 100 kHz */ +#define ISMT_SPGT_SPD_400K (0x2 << 30) /* 400 kHz */ +#define ISMT_SPGT_SPD_1M (0x3 << 30) /* 1 MHz */ + + +/* MSI Control Register (MSICTL) bit definitions */ +#define ISMT_MSICTL_MSIE 0x01 /* MSI Enable */ + +/* iSMT Hardware Descriptor */ +struct ismt_desc { + u8 tgtaddr_rw; /* target address & r/w bit */ + u8 wr_len_cmd; /* write length in bytes or a command */ + u8 rd_len; /* read length */ + u8 control; /* control bits */ + u8 status; /* status bits */ + u8 retry; /* collision retry and retry count */ + u8 rxbytes; /* received bytes */ + u8 txbytes; /* transmitted bytes */ + u32 dptr_low; /* lower 32 bit of the data pointer */ + u32 dptr_high; /* upper 32 bit of the data pointer */ +} __packed; + +struct ismt_priv { + struct i2c_adapter adapter; + void *smba; /* PCI BAR */ + struct pci_dev *pci_dev; + struct ismt_desc *hw; /* descriptor virt base addr */ + dma_addr_t io_rng_dma; /* descriptor HW base addr */ + u8 head; /* ring buffer head pointer */ + struct completion cmp; /* interrupt completion */ + u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 1]; /* temp R/W data buffer */ + bool using_msi; /* type of interrupt flag */ +}; + +/** + * ismt_ids - PCI device IDs supported by this driver + */ +static const DEFINE_PCI_DEVICE_TABLE(ismt_ids) = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT1) }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, ismt_ids); + +/* Bus speed control bits for slow debuggers - refer to the docs for usage */ +static unsigned int bus_speed; +module_param(bus_speed, uint, S_IRUGO); +MODULE_PARM_DESC(bus_speed, "Bus Speed in kHz (0 = BIOS default)"); + +/** + * __ismt_desc_dump() - dump the contents of a specific descriptor + */ +static void __ismt_desc_dump(struct device *dev, const struct ismt_desc *desc) +{ + + dev_dbg(dev, "Descriptor struct: %p\n", desc); + dev_dbg(dev, "\ttgtaddr_rw=0x%02X\n", desc->tgtaddr_rw); + dev_dbg(dev, "\twr_len_cmd=0x%02X\n", desc->wr_len_cmd); + dev_dbg(dev, "\trd_len= 0x%02X\n", desc->rd_len); + dev_dbg(dev, "\tcontrol= 0x%02X\n", desc->control); + dev_dbg(dev, "\tstatus= 0x%02X\n", desc->status); + dev_dbg(dev, "\tretry= 0x%02X\n", desc->retry); + dev_dbg(dev, "\trxbytes= 0x%02X\n", desc->rxbytes); + dev_dbg(dev, "\ttxbytes= 0x%02X\n", desc->txbytes); + dev_dbg(dev, "\tdptr_low= 0x%08X\n", desc->dptr_low); + dev_dbg(dev, "\tdptr_high= 0x%08X\n", desc->dptr_high); +} +/** + * ismt_desc_dump() - dump the contents of a descriptor for debug purposes + * @priv: iSMT private data + */ +static void ismt_desc_dump(struct ismt_priv *priv) +{ + struct device *dev = &priv->pci_dev->dev; + struct ismt_desc *desc = &priv->hw[priv->head]; + + dev_dbg(dev, "Dump of the descriptor struct: 0x%X\n", priv->head); + __ismt_desc_dump(dev, desc); +} + +/** + * ismt_gen_reg_dump() - dump the iSMT General Registers + * @priv: iSMT private data + */ +static void ismt_gen_reg_dump(struct ismt_priv *priv) +{ + struct device *dev = &priv->pci_dev->dev; + + dev_dbg(dev, "Dump of the iSMT General Registers\n"); + dev_dbg(dev, " GCTRL.... : (0x%p)=0x%X\n", + priv->smba + ISMT_GR_GCTRL, + readl(priv->smba + ISMT_GR_GCTRL)); + dev_dbg(dev, " SMTICL... : (0x%p)=0x%016llX\n", + priv->smba + ISMT_GR_SMTICL, + (long long unsigned int)readq(priv->smba + ISMT_GR_SMTICL)); + dev_dbg(dev, " ERRINTMSK : (0x%p)=0x%X\n", + priv->smba + ISMT_GR_ERRINTMSK, + readl(priv->smba + ISMT_GR_ERRINTMSK)); + dev_dbg(dev, " ERRAERMSK : (0x%p)=0x%X\n", + priv->smba + ISMT_GR_ERRAERMSK, + readl(priv->smba + ISMT_GR_ERRAERMSK)); + dev_dbg(dev, " ERRSTS... : (0x%p)=0x%X\n", + priv->smba + ISMT_GR_ERRSTS, + readl(priv->smba + ISMT_GR_ERRSTS)); + dev_dbg(dev, " ERRINFO.. : (0x%p)=0x%X\n", + priv->smba + ISMT_GR_ERRINFO, + readl(priv->smba + ISMT_GR_ERRINFO)); +} + +/** + * ismt_mstr_reg_dump() - dump the iSMT Master Registers + * @priv: iSMT private data + */ +static void ismt_mstr_reg_dump(struct ismt_priv *priv) +{ + struct device *dev = &priv->pci_dev->dev; + + dev_dbg(dev, "Dump of the iSMT Master Registers\n"); + dev_dbg(dev, " MDBA..... : (0x%p)=0x%016llX\n", + priv->smba + ISMT_MSTR_MDBA, + (long long unsigned int)readq(priv->smba + ISMT_MSTR_MDBA)); + dev_dbg(dev, " MCTRL.... : (0x%p)=0x%X\n", + priv->smba + ISMT_MSTR_MCTRL, + readl(priv->smba + ISMT_MSTR_MCTRL)); + dev_dbg(dev, " MSTS..... : (0x%p)=0x%X\n", + priv->smba + ISMT_MSTR_MSTS, + readl(priv->smba + ISMT_MSTR_MSTS)); + dev_dbg(dev, " MDS...... : (0x%p)=0x%X\n", + priv->smba + ISMT_MSTR_MDS, + readl(priv->smba + ISMT_MSTR_MDS)); + dev_dbg(dev, " RPOLICY.. : (0x%p)=0x%X\n", + priv->smba + ISMT_MSTR_RPOLICY, + readl(priv->smba + ISMT_MSTR_RPOLICY)); + dev_dbg(dev, " SPGT..... : (0x%p)=0x%X\n", + priv->smba + ISMT_SPGT, + readl(priv->smba + ISMT_SPGT)); +} + +/** + * ismt_submit_desc() - add a descriptor to the ring + * @priv: iSMT private data + */ +static void ismt_submit_desc(struct ismt_priv *priv) +{ + uint fmhp; + uint val; + + ismt_desc_dump(priv); + ismt_gen_reg_dump(priv); + ismt_mstr_reg_dump(priv); + + /* Set the FMHP (Firmware Master Head Pointer)*/ + fmhp = ((priv->head + 1) % ISMT_DESC_ENTRIES) << 16; + val = readl(priv->smba + ISMT_MSTR_MCTRL); + writel((val & ~ISMT_MCTRL_FMHP) | fmhp, + priv->smba + ISMT_MSTR_MCTRL); + + /* Set the start bit */ + val = readl(priv->smba + ISMT_MSTR_MCTRL); + writel(val | ISMT_MCTRL_SS, + priv->smba + ISMT_MSTR_MCTRL); +} + +/** + * ismt_process_desc() - handle the completion of the descriptor + * @desc: the iSMT hardware descriptor + * @data: data buffer from the upper layer + * @priv: ismt_priv struct holding our dma buffer + * @size: SMBus transaction type + * @read_write: flag to indicate if this is a read or write + */ +static int ismt_process_desc(const struct ismt_desc *desc, + union i2c_smbus_data *data, + struct ismt_priv *priv, int size, + char read_write) +{ + u8 *dma_buffer = priv->dma_buffer; + + dev_dbg(&priv->pci_dev->dev, "Processing completed descriptor\n"); + __ismt_desc_dump(&priv->pci_dev->dev, desc); + + if (desc->status & ISMT_DESC_SCS) { + if (read_write == I2C_SMBUS_WRITE && + size != I2C_SMBUS_PROC_CALL) + return 0; + + switch (size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + data->byte = dma_buffer[0]; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + data->word = dma_buffer[0] | (dma_buffer[1] << 8); + break; + case I2C_SMBUS_BLOCK_DATA: + memcpy(&data->block[1], dma_buffer, desc->rxbytes); + data->block[0] = desc->rxbytes; + break; + } + return 0; + } + + if (likely(desc->status & ISMT_DESC_NAK)) + return -ENXIO; + + if (desc->status & ISMT_DESC_CRC) + return -EBADMSG; + + if (desc->status & ISMT_DESC_COL) + return -EAGAIN; + + if (desc->status & ISMT_DESC_LPR) + return -EPROTO; + + if (desc->status & (ISMT_DESC_DLTO | ISMT_DESC_CLTO)) + return -ETIMEDOUT; + + return -EIO; +} + +/** + * ismt_access() - process an SMBus command + * @adap: the i2c host adapter + * @addr: address of the i2c/SMBus target + * @flags: command options + * @read_write: read from or write to device + * @command: the i2c/SMBus command to issue + * @size: SMBus transaction type + * @data: read/write data buffer + */ +static int ismt_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + int ret; + dma_addr_t dma_addr = 0; /* address of the data buffer */ + u8 dma_size = 0; + enum dma_data_direction dma_direction = 0; + struct ismt_desc *desc; + struct ismt_priv *priv = i2c_get_adapdata(adap); + struct device *dev = &priv->pci_dev->dev; + + desc = &priv->hw[priv->head]; + + /* Initialize the descriptor */ + memset(desc, 0, sizeof(struct ismt_desc)); + desc->tgtaddr_rw = ISMT_DESC_ADDR_RW(addr, read_write); + + /* Initialize common control bits */ + if (likely(priv->using_msi)) + desc->control = ISMT_DESC_INT | ISMT_DESC_FAIR; + else + desc->control = ISMT_DESC_FAIR; + + if ((flags & I2C_CLIENT_PEC) && (size != I2C_SMBUS_QUICK) + && (size != I2C_SMBUS_I2C_BLOCK_DATA)) + desc->control |= ISMT_DESC_PEC; + + switch (size) { + case I2C_SMBUS_QUICK: + dev_dbg(dev, "I2C_SMBUS_QUICK\n"); + break; + + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) { + /* + * Send Byte + * The command field contains the write data + */ + dev_dbg(dev, "I2C_SMBUS_BYTE: WRITE\n"); + desc->control |= ISMT_DESC_CWRL; + desc->wr_len_cmd = command; + } else { + /* Receive Byte */ + dev_dbg(dev, "I2C_SMBUS_BYTE: READ\n"); + dma_size = 1; + dma_direction = DMA_FROM_DEVICE; + desc->rd_len = 1; + } + break; + + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_WRITE) { + /* + * Write Byte + * Command plus 1 data byte + */ + dev_dbg(dev, "I2C_SMBUS_BYTE_DATA: WRITE\n"); + desc->wr_len_cmd = 2; + dma_size = 2; + dma_direction = DMA_TO_DEVICE; + priv->dma_buffer[0] = command; + priv->dma_buffer[1] = data->byte; + } else { + /* Read Byte */ + dev_dbg(dev, "I2C_SMBUS_BYTE_DATA: READ\n"); + desc->control |= ISMT_DESC_CWRL; + desc->wr_len_cmd = command; + desc->rd_len = 1; + dma_size = 1; + dma_direction = DMA_FROM_DEVICE; + } + break; + + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_WRITE) { + /* Write Word */ + dev_dbg(dev, "I2C_SMBUS_WORD_DATA: WRITE\n"); + desc->wr_len_cmd = 3; + dma_size = 3; + dma_direction = DMA_TO_DEVICE; + priv->dma_buffer[0] = command; + priv->dma_buffer[1] = data->word & 0xff; + priv->dma_buffer[2] = data->word >> 8; + } else { + /* Read Word */ + dev_dbg(dev, "I2C_SMBUS_WORD_DATA: READ\n"); + desc->wr_len_cmd = command; + desc->control |= ISMT_DESC_CWRL; + desc->rd_len = 2; + dma_size = 2; + dma_direction = DMA_FROM_DEVICE; + } + break; + + case I2C_SMBUS_PROC_CALL: + dev_dbg(dev, "I2C_SMBUS_PROC_CALL\n"); + desc->wr_len_cmd = 3; + desc->rd_len = 2; + dma_size = 3; + dma_direction = DMA_BIDIRECTIONAL; + priv->dma_buffer[0] = command; + priv->dma_buffer[1] = data->word & 0xff; + priv->dma_buffer[2] = data->word >> 8; + break; + + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_WRITE) { + /* Block Write */ + dev_dbg(dev, "I2C_SMBUS_BLOCK_DATA: WRITE\n"); + dma_size = data->block[0] + 1; + dma_direction = DMA_TO_DEVICE; + desc->wr_len_cmd = dma_size; + desc->control |= ISMT_DESC_BLK; + priv->dma_buffer[0] = command; + memcpy(&priv->dma_buffer[1], &data->block[1], dma_size); + } else { + /* Block Read */ + dev_dbg(dev, "I2C_SMBUS_BLOCK_DATA: READ\n"); + dma_size = I2C_SMBUS_BLOCK_MAX; + dma_direction = DMA_FROM_DEVICE; + desc->rd_len = dma_size; + desc->wr_len_cmd = command; + desc->control |= (ISMT_DESC_BLK | ISMT_DESC_CWRL); + } + break; + + default: + dev_err(dev, "Unsupported transaction %d\n", + size); + return -EOPNOTSUPP; + } + + /* map the data buffer */ + if (dma_size != 0) { + dev_dbg(dev, " dev=%p\n", dev); + dev_dbg(dev, " data=%p\n", data); + dev_dbg(dev, " dma_buffer=%p\n", priv->dma_buffer); + dev_dbg(dev, " dma_size=%d\n", dma_size); + dev_dbg(dev, " dma_direction=%d\n", dma_direction); + + dma_addr = dma_map_single(dev, + priv->dma_buffer, + dma_size, + dma_direction); + + if (dma_mapping_error(dev, dma_addr)) { + dev_err(dev, "Error in mapping dma buffer %p\n", + priv->dma_buffer); + return -EIO; + } + + dev_dbg(dev, " dma_addr = 0x%016llX\n", + dma_addr); + + desc->dptr_low = lower_32_bits(dma_addr); + desc->dptr_high = upper_32_bits(dma_addr); + } + + INIT_COMPLETION(priv->cmp); + + /* Add the descriptor */ + ismt_submit_desc(priv); + + /* Now we wait for interrupt completion, 1s */ + ret = wait_for_completion_timeout(&priv->cmp, HZ*1); + + /* unmap the data buffer */ + if (dma_size != 0) + dma_unmap_single(&adap->dev, dma_addr, dma_size, dma_direction); + + if (unlikely(!ret)) { + dev_err(dev, "completion wait timed out\n"); + ret = -ETIMEDOUT; + goto out; + } + + /* do any post processing of the descriptor here */ + ret = ismt_process_desc(desc, data, priv, size, read_write); + +out: + /* Update the ring pointer */ + priv->head++; + priv->head %= ISMT_DESC_ENTRIES; + + return ret; +} + +/** + * ismt_func() - report which i2c commands are supported by this adapter + * @adap: the i2c host adapter + */ +static u32 ismt_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_QUICK | + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_PROC_CALL | + I2C_FUNC_SMBUS_BLOCK_DATA | + I2C_FUNC_SMBUS_PEC; +} + +/** + * smbus_algorithm - the adapter algorithm and supported functionality + * @smbus_xfer: the adapter algorithm + * @functionality: functionality supported by the adapter + */ +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = ismt_access, + .functionality = ismt_func, +}; + +/** + * ismt_handle_isr() - interrupt handler bottom half + * @priv: iSMT private data + */ +static irqreturn_t ismt_handle_isr(struct ismt_priv *priv) +{ + complete(&priv->cmp); + + return IRQ_HANDLED; +} + + +/** + * ismt_do_interrupt() - IRQ interrupt handler + * @vec: interrupt vector + * @data: iSMT private data + */ +static irqreturn_t ismt_do_interrupt(int vec, void *data) +{ + u32 val; + struct ismt_priv *priv = data; + + /* + * check to see it's our interrupt, return IRQ_NONE if not ours + * since we are sharing interrupt + */ + val = readl(priv->smba + ISMT_MSTR_MSTS); + + if (!(val & (ISMT_MSTS_MIS | ISMT_MSTS_MEIS))) + return IRQ_NONE; + else + writel(val | ISMT_MSTS_MIS | ISMT_MSTS_MEIS, + priv->smba + ISMT_MSTR_MSTS); + + return ismt_handle_isr(priv); +} + +/** + * ismt_do_msi_interrupt() - MSI interrupt handler + * @vec: interrupt vector + * @data: iSMT private data + */ +static irqreturn_t ismt_do_msi_interrupt(int vec, void *data) +{ + return ismt_handle_isr(data); +} + +/** + * ismt_hw_init() - initialize the iSMT hardware + * @priv: iSMT private data + */ +static void ismt_hw_init(struct ismt_priv *priv) +{ + u32 val; + struct device *dev = &priv->pci_dev->dev; + + /* initialize the Master Descriptor Base Address (MDBA) */ + writeq(priv->io_rng_dma, priv->smba + ISMT_MSTR_MDBA); + + /* initialize the Master Control Register (MCTRL) */ + writel(ISMT_MCTRL_MEIE, priv->smba + ISMT_MSTR_MCTRL); + + /* initialize the Master Status Register (MSTS) */ + writel(0, priv->smba + ISMT_MSTR_MSTS); + + /* initialize the Master Descriptor Size (MDS) */ + val = readl(priv->smba + ISMT_MSTR_MDS); + writel((val & ~ISMT_MDS_MASK) | (ISMT_DESC_ENTRIES - 1), + priv->smba + ISMT_MSTR_MDS); + + /* + * Set the SMBus speed (could use this for slow HW debuggers) + */ + + val = readl(priv->smba + ISMT_SPGT); + + switch (bus_speed) { + case 0: + break; + + case 80: + dev_dbg(dev, "Setting SMBus clock to 80 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_80K), + priv->smba + ISMT_SPGT); + break; + + case 100: + dev_dbg(dev, "Setting SMBus clock to 100 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_100K), + priv->smba + ISMT_SPGT); + break; + + case 400: + dev_dbg(dev, "Setting SMBus clock to 400 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_400K), + priv->smba + ISMT_SPGT); + break; + + case 1000: + dev_dbg(dev, "Setting SMBus clock to 1000 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_1M), + priv->smba + ISMT_SPGT); + break; + + default: + dev_warn(dev, "Invalid SMBus clock speed, only 0, 80, 100, 400, and 1000 are valid\n"); + break; + } + + val = readl(priv->smba + ISMT_SPGT); + + switch (val & ISMT_SPGT_SPD_MASK) { + case ISMT_SPGT_SPD_80K: + bus_speed = 80; + break; + case ISMT_SPGT_SPD_100K: + bus_speed = 100; + break; + case ISMT_SPGT_SPD_400K: + bus_speed = 400; + break; + case ISMT_SPGT_SPD_1M: + bus_speed = 1000; + break; + } + dev_dbg(dev, "SMBus clock is running at %d kHz\n", bus_speed); +} + +/** + * ismt_dev_init() - initialize the iSMT data structures + * @priv: iSMT private data + */ +static int ismt_dev_init(struct ismt_priv *priv) +{ + /* allocate memory for the descriptor */ + priv->hw = dmam_alloc_coherent(&priv->pci_dev->dev, + (ISMT_DESC_ENTRIES + * sizeof(struct ismt_desc)), + &priv->io_rng_dma, + GFP_KERNEL); + if (!priv->hw) + return -ENOMEM; + + memset(priv->hw, 0, (ISMT_DESC_ENTRIES * sizeof(struct ismt_desc))); + + priv->head = 0; + init_completion(&priv->cmp); + + return 0; +} + +/** + * ismt_int_init() - initialize interrupts + * @priv: iSMT private data + */ +static int ismt_int_init(struct ismt_priv *priv) +{ + int err; + + /* Try using MSI interrupts */ + err = pci_enable_msi(priv->pci_dev); + if (err) { + dev_warn(&priv->pci_dev->dev, + "Unable to use MSI interrupts, falling back to legacy\n"); + goto intx; + } + + err = devm_request_irq(&priv->pci_dev->dev, + priv->pci_dev->irq, + ismt_do_msi_interrupt, + 0, + "ismt-msi", + priv); + if (err) { + pci_disable_msi(priv->pci_dev); + goto intx; + } + + priv->using_msi = true; + goto done; + + /* Try using legacy interrupts */ +intx: + err = devm_request_irq(&priv->pci_dev->dev, + priv->pci_dev->irq, + ismt_do_interrupt, + IRQF_SHARED, + "ismt-intx", + priv); + if (err) { + dev_err(&priv->pci_dev->dev, "no usable interrupts\n"); + return -ENODEV; + } + + priv->using_msi = false; + +done: + return 0; +} + +static struct pci_driver ismt_driver; + +/** + * ismt_probe() - probe for iSMT devices + * @pdev: PCI-Express device + * @id: PCI-Express device ID + */ +static int +ismt_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int err; + struct ismt_priv *priv; + unsigned long start, len; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + pci_set_drvdata(pdev, priv); + i2c_set_adapdata(&priv->adapter, priv); + priv->adapter.owner = THIS_MODULE; + + priv->adapter.class = I2C_CLASS_HWMON; + + priv->adapter.algo = &smbus_algorithm; + + /* set up the sysfs linkage to our parent device */ + priv->adapter.dev.parent = &pdev->dev; + + /* number of retries on lost arbitration */ + priv->adapter.retries = ISMT_MAX_RETRIES; + + priv->pci_dev = pdev; + + err = pcim_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "Failed to enable SMBus PCI device (%d)\n", + err); + return err; + } + + /* enable bus mastering */ + pci_set_master(pdev); + + /* Determine the address of the SMBus area */ + start = pci_resource_start(pdev, SMBBAR); + len = pci_resource_len(pdev, SMBBAR); + if (!start || !len) { + dev_err(&pdev->dev, + "SMBus base address uninitialized, upgrade BIOS\n"); + return -ENODEV; + } + + snprintf(priv->adapter.name, sizeof(priv->adapter.name), + "SMBus iSMT adapter at %lx", start); + + dev_dbg(&priv->pci_dev->dev, " start=0x%lX\n", start); + dev_dbg(&priv->pci_dev->dev, " len=0x%lX\n", len); + + err = acpi_check_resource_conflict(&pdev->resource[SMBBAR]); + if (err) { + dev_err(&pdev->dev, "ACPI resource conflict!\n"); + return err; + } + + err = pci_request_region(pdev, SMBBAR, ismt_driver.name); + if (err) { + dev_err(&pdev->dev, + "Failed to request SMBus region 0x%lx-0x%lx\n", + start, start + len); + return err; + } + + priv->smba = pcim_iomap(pdev, SMBBAR, len); + if (!priv->smba) { + dev_err(&pdev->dev, "Unable to ioremap SMBus BAR\n"); + err = -ENODEV; + goto fail; + } + + if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) || + (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) != 0)) { + if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) || + (pci_set_consistent_dma_mask(pdev, + DMA_BIT_MASK(32)) != 0)) { + dev_err(&pdev->dev, "pci_set_dma_mask fail %p\n", + pdev); + goto fail; + } + } + + err = ismt_dev_init(priv); + if (err) + goto fail; + + ismt_hw_init(priv); + + err = ismt_int_init(priv); + if (err) + goto fail; + + err = i2c_add_adapter(&priv->adapter); + if (err) { + dev_err(&pdev->dev, "Failed to add SMBus iSMT adapter\n"); + err = -ENODEV; + goto fail; + } + return 0; + +fail: + pci_release_region(pdev, SMBBAR); + return err; +} + +/** + * ismt_remove() - release driver resources + * @pdev: PCI-Express device + */ +static void ismt_remove(struct pci_dev *pdev) +{ + struct ismt_priv *priv = pci_get_drvdata(pdev); + + i2c_del_adapter(&priv->adapter); + pci_release_region(pdev, SMBBAR); +} + +/** + * ismt_suspend() - place the device in suspend + * @pdev: PCI-Express device + * @mesg: PM message + */ +#ifdef CONFIG_PM +static int ismt_suspend(struct pci_dev *pdev, pm_message_t mesg) +{ + pci_save_state(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, mesg)); + return 0; +} + +/** + * ismt_resume() - PCI resume code + * @pdev: PCI-Express device + */ +static int ismt_resume(struct pci_dev *pdev) +{ + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + return pci_enable_device(pdev); +} + +#else + +#define ismt_suspend NULL +#define ismt_resume NULL + +#endif + +static struct pci_driver ismt_driver = { + .name = "ismt_smbus", + .id_table = ismt_ids, + .probe = ismt_probe, + .remove = ismt_remove, + .suspend = ismt_suspend, + .resume = ismt_resume, +}; + +module_pci_driver(ismt_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Bill E. Brown "); +MODULE_DESCRIPTION("Intel SMBus Message Transport (iSMT) driver"); -- cgit v1.2.3 From 4182b434bf760355a1516b1d3d9f73aa419eeeec Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Sat, 9 Feb 2013 19:14:00 +0100 Subject: i2c: at91: fix unsed variable warning when building with !CONFIG_OF Commit 70d46a2 "i2c: at91: add dt support to i2c-at91" added DT only support for at91sam9x5. Building i2c-at91 without CONFIG_OF now warns about at91sam9x5_config as being unused. drivers/i2c/busses/i2c-at91.c:556:30: warning: 'at91sam9x5_config' defined but not used [-Wunused-variable] Move at91sam9x5_config under the defined(CONFIG_OF) guard as new AT91 SoCs will be DT only. Signed-off-by: Joachim Eastwood Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-at91.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index 3e287d838936..3e4ad68e3bd7 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -553,13 +553,6 @@ static struct at91_twi_pdata at91sam9g10_config = { .has_dma_support = false, }; -static struct at91_twi_pdata at91sam9x5_config = { - .clk_max_div = 7, - .clk_offset = 4, - .has_unre_flag = false, - .has_dma_support = true, -}; - static const struct platform_device_id at91_twi_devtypes[] = { { .name = "i2c-at91rm9200", @@ -582,6 +575,13 @@ static const struct platform_device_id at91_twi_devtypes[] = { }; #if defined(CONFIG_OF) +static struct at91_twi_pdata at91sam9x5_config = { + .clk_max_div = 7, + .clk_offset = 4, + .has_unre_flag = false, + .has_dma_support = true, +}; + static const struct of_device_id atmel_twi_dt_ids[] = { { .compatible = "atmel,at91rm9200-i2c", -- cgit v1.2.3 From 9350393239153c4a98cbed4d69b9ed81e37d5e74 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Sun, 10 Feb 2013 15:57:38 +1030 Subject: virtio: make config_ops const It is just a table of function pointers, make it const for cleanliness and security reasons. Signed-off-by: Stephen Hemminger Signed-off-by: Rusty Russell --- drivers/lguest/lguest_device.c | 2 +- drivers/remoteproc/remoteproc_virtio.c | 2 +- drivers/s390/kvm/kvm_virtio.c | 2 +- drivers/virtio/virtio_mmio.c | 2 +- drivers/virtio/virtio_pci.c | 2 +- include/linux/virtio.h | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c index fc92ccbd71dc..b3256ff0d426 100644 --- a/drivers/lguest/lguest_device.c +++ b/drivers/lguest/lguest_device.c @@ -396,7 +396,7 @@ static const char *lg_bus_name(struct virtio_device *vdev) } /* The ops structure which hooks everything together. */ -static struct virtio_config_ops lguest_config_ops = { +static const struct virtio_config_ops lguest_config_ops = { .get_features = lg_get_features, .finalize_features = lg_finalize_features, .get = lg_get, diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index 9e198e590675..afed9b7731c4 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -222,7 +222,7 @@ static void rproc_virtio_finalize_features(struct virtio_device *vdev) rvdev->gfeatures = vdev->features[0]; } -static struct virtio_config_ops rproc_virtio_config_ops = { +static const struct virtio_config_ops rproc_virtio_config_ops = { .get_features = rproc_virtio_get_features, .finalize_features = rproc_virtio_finalize_features, .find_vqs = rproc_virtio_find_vqs, diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index 8491111aec12..7d08fba7542d 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -275,7 +275,7 @@ static const char *kvm_bus_name(struct virtio_device *vdev) /* * The config ops structure as defined by virtio config */ -static struct virtio_config_ops kvm_vq_configspace_ops = { +static const struct virtio_config_ops kvm_vq_configspace_ops = { .get_features = kvm_get_features, .finalize_features = kvm_finalize_features, .get = kvm_get, diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index 833e6dbf38db..1ba0d6831015 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -423,7 +423,7 @@ static const char *vm_bus_name(struct virtio_device *vdev) return vm_dev->pdev->name; } -static struct virtio_config_ops virtio_mmio_config_ops = { +static const struct virtio_config_ops virtio_mmio_config_ops = { .get = vm_get, .set = vm_set, .get_status = vm_get_status, diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index 0c142892c105..52c5ce6aec8d 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -652,7 +652,7 @@ static int vp_set_vq_affinity(struct virtqueue *vq, int cpu) return 0; } -static struct virtio_config_ops virtio_pci_config_ops = { +static const struct virtio_config_ops virtio_pci_config_ops = { .get = vp_get, .set = vp_set, .get_status = vp_get_status, diff --git a/include/linux/virtio.h b/include/linux/virtio.h index cf8adb1f5b2c..eb34cf774473 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -78,7 +78,7 @@ struct virtio_device { int index; struct device dev; struct virtio_device_id id; - struct virtio_config_ops *config; + const struct virtio_config_ops *config; struct list_head vqs; /* Note that this is a Linux set_bit-style bitmap. */ unsigned long features[1]; -- cgit v1.2.3 From 35cdc9eb65837687bdfc9ea1d2515eb03ea5048a Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Sun, 10 Feb 2013 15:57:39 +1030 Subject: virtio: make pci_device_id const Use DEVICE_TABLE macro which also makes table const. Signed-off-by: Stephen Hemminger Signed-off-by: Rusty Russell --- drivers/virtio/virtio_pci.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index 52c5ce6aec8d..a7ce73029f59 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -91,9 +91,9 @@ struct virtio_pci_vq_info }; /* Qumranet donated their vendor ID for devices 0x1000 thru 0x10FF. */ -static struct pci_device_id virtio_pci_id_table[] = { - { 0x1af4, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0 }, +static DEFINE_PCI_DEVICE_TABLE(virtio_pci_id_table) = { + { PCI_DEVICE(0x1af4, PCI_ANY_ID) }, + { 0 } }; MODULE_DEVICE_TABLE(pci, virtio_pci_id_table); -- cgit v1.2.3 From b08369a174a183e88baa98ab5e3566a617a3a7f8 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Mon, 28 Jan 2013 10:44:12 +0100 Subject: i2c: isch: Add module parameter for backbone clock rate if divider is unset It was observed the Host Clock Divider was not written by the driver. It was still set to (default) 0, if not already set by BIOS, which caused garbage on SMBus. This driver adds a parameters which is used to calculate the divider appropriately for a default bitrate of 100 KHz. This new divider is only applied if the clock divider is still default 0. Signed-off-by: Alexander Stein Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-isch.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-isch.c b/drivers/i2c/busses/i2c-isch.c index 4099f79c2280..8c38aaa7417c 100644 --- a/drivers/i2c/busses/i2c-isch.c +++ b/drivers/i2c/busses/i2c-isch.c @@ -40,6 +40,7 @@ /* SCH SMBus address offsets */ #define SMBHSTCNT (0 + sch_smba) #define SMBHSTSTS (1 + sch_smba) +#define SMBHSTCLK (2 + sch_smba) #define SMBHSTADD (4 + sch_smba) /* TSA */ #define SMBHSTCMD (5 + sch_smba) #define SMBHSTDAT0 (6 + sch_smba) @@ -58,6 +59,9 @@ static unsigned short sch_smba; static struct i2c_adapter sch_adapter; +static int backbone_speed = 33000; /* backbone speed in kHz */ +module_param(backbone_speed, int, S_IRUSR | S_IWUSR); +MODULE_PARM_DESC(backbone_speed, "Backbone speed in kHz, (default = 33000)"); /* * Start the i2c transaction -- the i2c_access will prepare the transaction @@ -156,6 +160,19 @@ static s32 sch_access(struct i2c_adapter *adap, u16 addr, dev_dbg(&sch_adapter.dev, "SMBus busy (%02x)\n", temp); return -EAGAIN; } + temp = inw(SMBHSTCLK); + if (!temp) { + /* + * We can't determine if we have 33 or 25 MHz clock for + * SMBus, so expect 33 MHz and calculate a bus clock of + * 100 kHz. If we actually run at 25 MHz the bus will be + * run ~75 kHz instead which should do no harm. + */ + dev_notice(&sch_adapter.dev, + "Clock divider unitialized. Setting defaults\n"); + outw(backbone_speed / (4 * 100), SMBHSTCLK); + } + dev_dbg(&sch_adapter.dev, "access size: %d %s\n", size, (read_write)?"READ":"WRITE"); switch (size) { -- cgit v1.2.3 From 974d6a3797001c88e59ccb78567c6d71ac526c43 Mon Sep 17 00:00:00 2001 From: Amaury Decrême Date: Mon, 28 Jan 2013 22:21:05 +0100 Subject: i2c: sis630: Add SIS964 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Amaury Decrême Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- Documentation/i2c/busses/i2c-sis630 | 9 ++++ drivers/i2c/busses/Kconfig | 4 +- drivers/i2c/busses/i2c-sis630.c | 88 ++++++++++++++++++++++++------------- 3 files changed, 69 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/Documentation/i2c/busses/i2c-sis630 b/Documentation/i2c/busses/i2c-sis630 index 0b9697366930..ee7943631074 100644 --- a/Documentation/i2c/busses/i2c-sis630 +++ b/Documentation/i2c/busses/i2c-sis630 @@ -4,9 +4,11 @@ Supported adapters: * Silicon Integrated Systems Corp (SiS) 630 chipset (Datasheet: available at http://www.sfr-fresh.com/linux) 730 chipset + 964 chipset * Possible other SiS chipsets ? Author: Alexander Malysh + Amaury Decrême - SiS964 support Module Parameters ----------------- @@ -18,6 +20,7 @@ Module Parameters * high_clock = [1|0] Forcibly set Host Master Clock to 56KHz (default, what your BIOS use). DANGEROUS! This should be a bit faster, but freeze some systems (i.e. my Laptop). + SIS630/730 chip only. Description @@ -36,6 +39,12 @@ or like this: 00:00.0 Host bridge: Silicon Integrated Systems [SiS] 730 Host (rev 02) 00:01.0 ISA bridge: Silicon Integrated Systems [SiS] 85C503/5513 +or like this: + +00:00.0 Host bridge: Silicon Integrated Systems [SiS] 760/M760 Host (rev 02) +00:02.0 ISA bridge: Silicon Integrated Systems [SiS] SiS964 [MuTIOL Media IO] + LPC Controller (rev 36) + in your 'lspci' output , then this driver is for your chipset. Thank You diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 87df863ba110..74e18bc0bf62 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -197,11 +197,11 @@ config I2C_SIS5595 will be called i2c-sis5595. config I2C_SIS630 - tristate "SiS 630/730" + tristate "SiS 630/730/964" depends on PCI help If you say yes to this option, support will be included for the - SiS630 and SiS730 SMBus (a subset of I2C) interface. + SiS630, SiS730 and SiS964 SMBus (a subset of I2C) interface. This driver can also be built as a module. If so, the module will be called i2c-sis630. diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index de6dddb9f865..df8e20ab3a09 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -41,6 +41,20 @@ Supports: SIS 630 SIS 730 + SIS 964 + + Notable differences between chips: + +------------------------+--------------------+-------------------+ + | | SIS630/730 | SIS964 | + +------------------------+--------------------+-------------------+ + | Clock | 14kHz/56kHz | 55.56kHz/27.78kHz | + | SMBus registers offset | 0x80 | 0xE0 | + | SMB_CNT | Bit 1 = Slave Busy | Bit 1 = Bus probe | + | (not used yet) | Bit 3 is reserved | Bit 3 = Last byte | + | SMB_PCOUNT | Offset + 0x06 | Offset + 0x14 | + | SMB_COUNT | 4:0 bits | 5:0 bits | + +------------------------+--------------------+-------------------+ + (Other differences don't affect the functions provided by the driver) Note: we assume there can only be one device, with one SMBus interface. */ @@ -55,22 +69,21 @@ #include #include -/* SIS630 SMBus registers */ -#define SMB_STS 0x80 /* status */ -#define SMB_EN 0x81 /* status enable */ -#define SMB_CNT 0x82 -#define SMBHOST_CNT 0x83 -#define SMB_ADDR 0x84 -#define SMB_CMD 0x85 -#define SMB_PCOUNT 0x86 /* processed count */ -#define SMB_COUNT 0x87 -#define SMB_BYTE 0x88 /* ~0x8F data byte field */ -#define SMBDEV_ADDR 0x90 -#define SMB_DB0 0x91 -#define SMB_DB1 0x92 -#define SMB_SAA 0x93 - -/* register count for request_region */ +/* SIS964 id is defined here as we are the only file using it */ +#define PCI_DEVICE_ID_SI_964 0x0964 + +/* SIS630/730/964 SMBus registers */ +#define SMB_STS 0x00 /* status */ +#define SMB_CNT 0x02 /* control */ +#define SMBHOST_CNT 0x03 /* host control */ +#define SMB_ADDR 0x04 /* address */ +#define SMB_CMD 0x05 /* command */ +#define SMB_COUNT 0x07 /* byte count */ +#define SMB_BYTE 0x08 /* ~0x8F data byte field */ + +/* register count for request_region + * As we don't use SMB_PCOUNT, 20 is ok for SiS630 and SiS964 + */ #define SIS630_SMB_IOREGION 20 /* PCI address constants */ @@ -96,28 +109,30 @@ static struct pci_driver sis630_driver; static bool high_clock; static bool force; module_param(high_clock, bool, 0); -MODULE_PARM_DESC(high_clock, "Set Host Master Clock to 56KHz (default 14KHz)."); +MODULE_PARM_DESC(high_clock, + "Set Host Master Clock to 56KHz (default 14KHz) (SIS630/730 only)."); module_param(force, bool, 0); MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!"); -/* acpi base address */ -static unsigned short acpi_base; +/* SMBus base adress */ +static unsigned short smbus_base; /* supported chips */ static int supported[] = { PCI_DEVICE_ID_SI_630, PCI_DEVICE_ID_SI_730, + PCI_DEVICE_ID_SI_760, 0 /* terminates the list */ }; static inline u8 sis630_read(u8 reg) { - return inb(acpi_base + reg); + return inb(smbus_base + reg); } static inline void sis630_write(u8 reg, u8 data) { - outb(data, acpi_base + reg); + outb(data, smbus_base + reg); } static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldclock) @@ -394,6 +409,8 @@ static int sis630_setup(struct pci_dev *sis630_dev) unsigned char b; struct pci_dev *dummy = NULL; int retval, i; + /* acpi base address */ + unsigned short acpi_base; /* check for supported SiS devices */ for (i=0; supported[i] > 0 ; i++) { @@ -438,16 +455,25 @@ static int sis630_setup(struct pci_dev *sis630_dev) dev_dbg(&sis630_dev->dev, "ACPI base at 0x%04x\n", acpi_base); - retval = acpi_check_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION, + if (supported[i] == PCI_DEVICE_ID_SI_760) + smbus_base = acpi_base + 0xE0; + else + smbus_base = acpi_base + 0x80; + + dev_dbg(&sis630_dev->dev, "SMBus base at 0x%04hx\n", smbus_base); + + retval = acpi_check_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION, sis630_driver.name); if (retval) goto exit; /* Everything is happy, let's grab the memory and set things up. */ - if (!request_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION, + if (!request_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION, sis630_driver.name)) { - dev_err(&sis630_dev->dev, "SMBus registers 0x%04x-0x%04x already " - "in use!\n", acpi_base + SMB_STS, acpi_base + SMB_SAA); + dev_err(&sis630_dev->dev, + "I/O Region 0x%04hx-0x%04hx for SMBus already in use.\n", + smbus_base + SMB_STS, + smbus_base + SMB_STS + SIS630_SMB_IOREGION - 1); retval = -EBUSY; goto exit; } @@ -456,7 +482,7 @@ static int sis630_setup(struct pci_dev *sis630_dev) exit: if (retval) - acpi_base = 0; + smbus_base = 0; return retval; } @@ -470,11 +496,13 @@ static struct i2c_adapter sis630_adapter = { .owner = THIS_MODULE, .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, + .retries = 3 }; static DEFINE_PCI_DEVICE_TABLE(sis630_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) }, { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC) }, + { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_964) }, { 0, } }; @@ -491,17 +519,17 @@ static int sis630_probe(struct pci_dev *dev, const struct pci_device_id *id) sis630_adapter.dev.parent = &dev->dev; snprintf(sis630_adapter.name, sizeof(sis630_adapter.name), - "SMBus SIS630 adapter at %04x", acpi_base + SMB_STS); + "SMBus SIS630 adapter at %04hx", smbus_base + SMB_STS); return i2c_add_adapter(&sis630_adapter); } static void sis630_remove(struct pci_dev *dev) { - if (acpi_base) { + if (smbus_base) { i2c_del_adapter(&sis630_adapter); - release_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION); - acpi_base = 0; + release_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION); + smbus_base = 0; } } -- cgit v1.2.3 From aa9e7a39c5a5a77ff02670ef915f4c6712bc7658 Mon Sep 17 00:00:00 2001 From: Amaury Decrême Date: Mon, 28 Jan 2013 22:21:06 +0100 Subject: i2c: sis630: clear sticky bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sticky bits must be cleared at the end of the transaction by writing a 1 to all fields. Datasheet: SMBus Status (SMB_STS) The following registers are all sticky bits and only can be cleared by writing a one to their corresponding fields. Signed-off-by: Amaury Decrême Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sis630.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index df8e20ab3a09..3124d807c05a 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -213,10 +213,8 @@ static int sis630_transaction_wait(struct i2c_adapter *adap, int size) static void sis630_transaction_end(struct i2c_adapter *adap, u8 oldclock) { - int temp = 0; - /* clear all status "sticky" bits */ - sis630_write(SMB_STS, temp); + sis630_write(SMB_STS, 0xFF); dev_dbg(&adap->dev, "SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT)); -- cgit v1.2.3 From 499b9194ad7b7b6d7c06b01005508e5bcf3c8980 Mon Sep 17 00:00:00 2001 From: Amaury Decrême Date: Mon, 28 Jan 2013 22:21:07 +0100 Subject: i2c: sis630: fix behavior after collision MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Datasheet on collision: SMBus Collision (SMBCOL_STS) This bit is set when a SMBus Collision condition occurs and SMBus Host loses in the bus arbitration. The software should clear this bit and re-start SMBus operation. As the status will be cleared in transaction_end, we can remove the sis630_write and prepare to return -EAGAIN to retry. Signed-off-by: Amaury Decrême Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sis630.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index 3124d807c05a..e152d36b94ac 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -200,12 +200,7 @@ static int sis630_transaction_wait(struct i2c_adapter *adap, int size) if (temp & 0x04) { dev_err(&adap->dev, "Bus collision!\n"); - result = -EIO; - /* - TBD: Datasheet say: - the software should clear this bit and restart SMBUS operation. - Should we do it or user start request again? - */ + result = -EAGAIN; } return result; -- cgit v1.2.3 From a6e7d0efe0e21091682f62c4a72707a1adeef8ea Mon Sep 17 00:00:00 2001 From: Amaury Decrême Date: Mon, 28 Jan 2013 22:21:08 +0100 Subject: i2c: sis630: use hex to constants for SMBus commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch replaces hexadecimal values by constants for SMBus commands. Signed-off-by: Amaury Decrême Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sis630.c | 45 ++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index e152d36b94ac..b2fa7415f7e4 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -81,6 +81,21 @@ #define SMB_COUNT 0x07 /* byte count */ #define SMB_BYTE 0x08 /* ~0x8F data byte field */ +/* SMB_STS register */ +#define BYTE_DONE_STS 0x10 /* Byte Done Status / Block Array */ +#define SMBCOL_STS 0x04 /* Collision */ +#define SMBERR_STS 0x02 /* Device error */ + +/* SMB_CNT register */ +#define MSTO_EN 0x40 /* Host Master Timeout Enable */ +#define SMBCLK_SEL 0x20 /* Host master clock selection */ +#define SMB_PROBE 0x02 /* Bus Probe/Slave busy */ +#define SMB_HOSTBUSY 0x01 /* Host Busy */ + +/* SMBHOST_CNT register */ +#define SMB_KILL 0x20 /* Kill */ +#define SMB_START 0x10 /* Start */ + /* register count for request_region * As we don't use SMB_PCOUNT, 20 is ok for SiS630 and SiS964 */ @@ -140,12 +155,14 @@ static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldc int temp; /* Make sure the SMBus host is ready to start transmitting. */ - if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) { - dev_dbg(&adap->dev, "SMBus busy (%02x).Resetting...\n",temp); + temp = sis630_read(SMB_CNT); + if ((temp & (SMB_PROBE | SMB_HOSTBUSY)) != 0x00) { + dev_dbg(&adap->dev, "SMBus busy (%02x). Resetting...\n", temp); /* kill smbus transaction */ - sis630_write(SMBHOST_CNT, 0x20); + sis630_write(SMBHOST_CNT, SMB_KILL); - if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) { + temp = sis630_read(SMB_CNT); + if (temp & (SMB_PROBE | SMB_HOSTBUSY)) { dev_dbg(&adap->dev, "Failed! (%02x)\n", temp); return -EBUSY; } else { @@ -160,16 +177,16 @@ static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldc /* disable timeout interrupt , set Host Master Clock to 56KHz if requested */ if (high_clock) - sis630_write(SMB_CNT, 0x20); + sis630_write(SMB_CNT, SMBCLK_SEL); else - sis630_write(SMB_CNT, (*oldclock & ~0x40)); + sis630_write(SMB_CNT, (*oldclock & ~MSTO_EN)); /* clear all sticky bits */ temp = sis630_read(SMB_STS); sis630_write(SMB_STS, temp & 0x1e); /* start the transaction by setting bit 4 and size */ - sis630_write(SMBHOST_CNT,0x10 | (size & 0x07)); + sis630_write(SMBHOST_CNT, SMB_START | (size & 0x07)); return 0; } @@ -183,7 +200,7 @@ static int sis630_transaction_wait(struct i2c_adapter *adap, int size) msleep(1); temp = sis630_read(SMB_STS); /* check if block transmitted */ - if (size == SIS630_BLOCK_DATA && (temp & 0x10)) + if (size == SIS630_BLOCK_DATA && (temp & BYTE_DONE_STS)) break; } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT)); @@ -193,12 +210,12 @@ static int sis630_transaction_wait(struct i2c_adapter *adap, int size) result = -ETIMEDOUT; } - if (temp & 0x02) { + if (temp & SMBERR_STS) { dev_dbg(&adap->dev, "Error: Failed bus transaction\n"); result = -ENXIO; } - if (temp & 0x04) { + if (temp & SMBCOL_STS) { dev_err(&adap->dev, "Bus collision!\n"); result = -EAGAIN; } @@ -217,8 +234,8 @@ static void sis630_transaction_end(struct i2c_adapter *adap, u8 oldclock) * restore old Host Master Clock if high_clock is set * and oldclock was not 56KHz */ - if (high_clock && !(oldclock & 0x20)) - sis630_write(SMB_CNT,(sis630_read(SMB_CNT) & ~0x20)); + if (high_clock && !(oldclock & SMBCLK_SEL)) + sis630_write(SMB_CNT, sis630_read(SMB_CNT) & ~SMBCLK_SEL); dev_dbg(&adap->dev, "SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT)); } @@ -270,7 +287,7 @@ static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *dat we must clear sticky bit. clear SMBARY_STS */ - sis630_write(SMB_STS,0x10); + sis630_write(SMB_STS, BYTE_DONE_STS); } rc = sis630_transaction_wait(adap, SIS630_BLOCK_DATA); @@ -312,7 +329,7 @@ static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *dat dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i); /* clear SMBARY_STS */ - sis630_write(SMB_STS,0x10); + sis630_write(SMB_STS, BYTE_DONE_STS); } while(len < data->block[0]); } -- cgit v1.2.3 From 97da42dcb48079860b31dc83cb6bc5af15f9fc17 Mon Sep 17 00:00:00 2001 From: Amaury Decrême Date: Mon, 28 Jan 2013 22:21:09 +0100 Subject: i2c: sis630: display unsigned hex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch corrects the display of the acpi_base unsigned hex value. Signed-off-by: Amaury Decrême Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sis630.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index b2fa7415f7e4..424545b50970 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -463,7 +463,7 @@ static int sis630_setup(struct pci_dev *sis630_dev) goto exit; } - dev_dbg(&sis630_dev->dev, "ACPI base at 0x%04x\n", acpi_base); + dev_dbg(&sis630_dev->dev, "ACPI base at 0x%04hx\n", acpi_base); if (supported[i] == PCI_DEVICE_ID_SI_760) smbus_base = acpi_base + 0xE0; -- cgit v1.2.3 From 91991f34d71d4dd1d6333710d1e159db2f95e6e3 Mon Sep 17 00:00:00 2001 From: Amaury Decrême Date: Tue, 29 Jan 2013 21:22:26 +0100 Subject: i2c: sis630: checkpatch cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch corrects checkpatch errors. The changes has also been removed as it has less meaning with version control tools. Signed-off-by: Amaury Decrême Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sis630.c | 214 ++++++++++++++++++++-------------------- 1 file changed, 109 insertions(+), 105 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index 424545b50970..36a9556d7cfa 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -16,25 +16,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* - Changes: - 24.08.2002 - Fixed the typo in sis630_access (Thanks to Mark M. Hoffman) - Changed sis630_transaction.(Thanks to Mark M. Hoffman) - 18.09.2002 - Added SIS730 as supported. - 21.09.2002 - Added high_clock module option.If this option is set - used Host Master Clock 56KHz (default 14KHz).For now we save old Host - Master Clock and after transaction completed restore (otherwise - it's confuse BIOS and hung Machine). - 24.09.2002 - Fixed typo in sis630_access - Fixed logical error by restoring of Host Master Clock - 31.07.2003 - Added block data read/write support. -*/ - /* Status: beta @@ -150,9 +131,10 @@ static inline void sis630_write(u8 reg, u8 data) outb(data, smbus_base + reg); } -static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldclock) +static int sis630_transaction_start(struct i2c_adapter *adap, int size, + u8 *oldclock) { - int temp; + int temp; /* Make sure the SMBus host is ready to start transmitting. */ temp = sis630_read(SMB_CNT); @@ -165,17 +147,18 @@ static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldc if (temp & (SMB_PROBE | SMB_HOSTBUSY)) { dev_dbg(&adap->dev, "Failed! (%02x)\n", temp); return -EBUSY; - } else { + } else { dev_dbg(&adap->dev, "Successful!\n"); } - } + } /* save old clock, so we can prevent machine for hung */ *oldclock = sis630_read(SMB_CNT); dev_dbg(&adap->dev, "saved clock 0x%02x\n", *oldclock); - /* disable timeout interrupt , set Host Master Clock to 56KHz if requested */ + /* disable timeout interrupt, + * set Host Master Clock to 56KHz if requested */ if (high_clock) sis630_write(SMB_CNT, SMBCLK_SEL); else @@ -228,7 +211,8 @@ static void sis630_transaction_end(struct i2c_adapter *adap, u8 oldclock) /* clear all status "sticky" bits */ sis630_write(SMB_STS, 0xFF); - dev_dbg(&adap->dev, "SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT)); + dev_dbg(&adap->dev, + "SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT)); /* * restore old Host Master Clock if high_clock is set @@ -237,7 +221,8 @@ static void sis630_transaction_end(struct i2c_adapter *adap, u8 oldclock) if (high_clock && !(oldclock & SMBCLK_SEL)) sis630_write(SMB_CNT, sis630_read(SMB_CNT) & ~SMBCLK_SEL); - dev_dbg(&adap->dev, "SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT)); + dev_dbg(&adap->dev, + "SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT)); } static int sis630_transaction(struct i2c_adapter *adap, int size) @@ -254,7 +239,8 @@ static int sis630_transaction(struct i2c_adapter *adap, int size) return result; } -static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *data, int read_write) +static int sis630_block_data(struct i2c_adapter *adap, + union i2c_smbus_data *data, int read_write) { int i, len = 0, rc = 0; u8 oldclock = 0; @@ -266,22 +252,26 @@ static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *dat else if (len > 32) len = 32; sis630_write(SMB_COUNT, len); - for (i=1; i <= len; i++) { - dev_dbg(&adap->dev, "set data 0x%02x\n", data->block[i]); + for (i = 1; i <= len; i++) { + dev_dbg(&adap->dev, + "set data 0x%02x\n", data->block[i]); /* set data */ - sis630_write(SMB_BYTE+(i-1)%8, data->block[i]); - if (i==8 || (len<8 && i==len)) { - dev_dbg(&adap->dev, "start trans len=%d i=%d\n",len ,i); + sis630_write(SMB_BYTE + (i - 1) % 8, data->block[i]); + if (i == 8 || (len < 8 && i == len)) { + dev_dbg(&adap->dev, + "start trans len=%d i=%d\n", len, i); /* first transaction */ rc = sis630_transaction_start(adap, SIS630_BLOCK_DATA, &oldclock); if (rc) return rc; - } - else if ((i-1)%8 == 7 || i==len) { - dev_dbg(&adap->dev, "trans_wait len=%d i=%d\n",len,i); - if (i>8) { - dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i); + } else if ((i - 1) % 8 == 7 || i == len) { + dev_dbg(&adap->dev, + "trans_wait len=%d i=%d\n", len, i); + if (i > 8) { + dev_dbg(&adap->dev, + "clear smbary_sts" + " len=%d i=%d\n", len, i); /* If this is not first transaction, we must clear sticky bit. @@ -292,13 +282,13 @@ static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *dat rc = sis630_transaction_wait(adap, SIS630_BLOCK_DATA); if (rc) { - dev_dbg(&adap->dev, "trans_wait failed\n"); + dev_dbg(&adap->dev, + "trans_wait failed\n"); break; } } } - } - else { + } else { /* read request */ data->block[0] = len = 0; rc = sis630_transaction_start(adap, @@ -319,18 +309,22 @@ static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *dat if (data->block[0] > 32) data->block[0] = 32; - dev_dbg(&adap->dev, "block data read len=0x%x\n", data->block[0]); + dev_dbg(&adap->dev, + "block data read len=0x%x\n", data->block[0]); - for (i=0; i < 8 && len < data->block[0]; i++,len++) { - dev_dbg(&adap->dev, "read i=%d len=%d\n", i, len); - data->block[len+1] = sis630_read(SMB_BYTE+i); + for (i = 0; i < 8 && len < data->block[0]; i++, len++) { + dev_dbg(&adap->dev, + "read i=%d len=%d\n", i, len); + data->block[len + 1] = sis630_read(SMB_BYTE + + i); } - dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i); + dev_dbg(&adap->dev, + "clear smbary_sts len=%d i=%d\n", len, i); /* clear SMBARY_STS */ sis630_write(SMB_STS, BYTE_DONE_STS); - } while(len < data->block[0]); + } while (len < data->block[0]); } sis630_transaction_end(adap, oldclock); @@ -346,42 +340,47 @@ static s32 sis630_access(struct i2c_adapter *adap, u16 addr, int status; switch (size) { - case I2C_SMBUS_QUICK: - sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); - size = SIS630_QUICK; - break; - case I2C_SMBUS_BYTE: - sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); - if (read_write == I2C_SMBUS_WRITE) - sis630_write(SMB_CMD, command); - size = SIS630_BYTE; - break; - case I2C_SMBUS_BYTE_DATA: - sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); - sis630_write(SMB_CMD, command); - if (read_write == I2C_SMBUS_WRITE) - sis630_write(SMB_BYTE, data->byte); - size = SIS630_BYTE_DATA; - break; - case I2C_SMBUS_PROC_CALL: - case I2C_SMBUS_WORD_DATA: - sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01)); + case I2C_SMBUS_QUICK: + sis630_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + size = SIS630_QUICK; + break; + case I2C_SMBUS_BYTE: + sis630_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + if (read_write == I2C_SMBUS_WRITE) sis630_write(SMB_CMD, command); - if (read_write == I2C_SMBUS_WRITE) { - sis630_write(SMB_BYTE, data->word & 0xff); - sis630_write(SMB_BYTE + 1,(data->word & 0xff00) >> 8); - } - size = (size == I2C_SMBUS_PROC_CALL ? SIS630_PCALL : SIS630_WORD_DATA); - break; - case I2C_SMBUS_BLOCK_DATA: - sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01)); - sis630_write(SMB_CMD, command); - size = SIS630_BLOCK_DATA; - return sis630_block_data(adap, data, read_write); - default: - dev_warn(&adap->dev, "Unsupported transaction %d\n", - size); - return -EOPNOTSUPP; + size = SIS630_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + sis630_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis630_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) + sis630_write(SMB_BYTE, data->byte); + size = SIS630_BYTE_DATA; + break; + case I2C_SMBUS_PROC_CALL: + case I2C_SMBUS_WORD_DATA: + sis630_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis630_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) { + sis630_write(SMB_BYTE, data->word & 0xff); + sis630_write(SMB_BYTE + 1, (data->word & 0xff00) >> 8); + } + size = (size == I2C_SMBUS_PROC_CALL ? + SIS630_PCALL : SIS630_WORD_DATA); + break; + case I2C_SMBUS_BLOCK_DATA: + sis630_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis630_write(SMB_CMD, command); + size = SIS630_BLOCK_DATA; + return sis630_block_data(adap, data, read_write); + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; } status = sis630_transaction(adap, size); @@ -393,15 +392,16 @@ static s32 sis630_access(struct i2c_adapter *adap, u16 addr, return 0; } - switch(size) { - case SIS630_BYTE: - case SIS630_BYTE_DATA: - data->byte = sis630_read(SMB_BYTE); - break; - case SIS630_PCALL: - case SIS630_WORD_DATA: - data->word = sis630_read(SMB_BYTE) + (sis630_read(SMB_BYTE + 1) << 8); - break; + switch (size) { + case SIS630_BYTE: + case SIS630_BYTE_DATA: + data->byte = sis630_read(SMB_BYTE); + break; + case SIS630_PCALL: + case SIS630_WORD_DATA: + data->word = sis630_read(SMB_BYTE) + + (sis630_read(SMB_BYTE + 1) << 8); + break; } return 0; @@ -409,9 +409,9 @@ static s32 sis630_access(struct i2c_adapter *adap, u16 addr, static u32 sis630_func(struct i2c_adapter *adapter) { - return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL | - I2C_FUNC_SMBUS_BLOCK_DATA; + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_DATA; } static int sis630_setup(struct pci_dev *sis630_dev) @@ -423,19 +423,19 @@ static int sis630_setup(struct pci_dev *sis630_dev) unsigned short acpi_base; /* check for supported SiS devices */ - for (i=0; supported[i] > 0 ; i++) { - if ((dummy = pci_get_device(PCI_VENDOR_ID_SI, supported[i], dummy))) + for (i = 0; supported[i] > 0; i++) { + dummy = pci_get_device(PCI_VENDOR_ID_SI, supported[i], dummy); + if (dummy) break; /* found */ } if (dummy) { pci_dev_put(dummy); - } - else if (force) { - dev_err(&sis630_dev->dev, "WARNING: Can't detect SIS630 compatible device, but " + } else if (force) { + dev_err(&sis630_dev->dev, + "WARNING: Can't detect SIS630 compatible device, but " "loading because of force option enabled\n"); - } - else { + } else { return -ENODEV; } @@ -443,7 +443,7 @@ static int sis630_setup(struct pci_dev *sis630_dev) Enable ACPI first , so we can accsess reg 74-75 in acpi io space and read acpi base addr */ - if (pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG,&b)) { + if (pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG, &b)) { dev_err(&sis630_dev->dev, "Error: Can't read bios ctl reg\n"); retval = -ENODEV; goto exit; @@ -457,8 +457,10 @@ static int sis630_setup(struct pci_dev *sis630_dev) } /* Determine the ACPI base address */ - if (pci_read_config_word(sis630_dev,SIS630_ACPI_BASE_REG,&acpi_base)) { - dev_err(&sis630_dev->dev, "Error: Can't determine ACPI base address\n"); + if (pci_read_config_word(sis630_dev, + SIS630_ACPI_BASE_REG, &acpi_base)) { + dev_err(&sis630_dev->dev, + "Error: Can't determine ACPI base address\n"); retval = -ENODEV; goto exit; } @@ -516,12 +518,14 @@ static DEFINE_PCI_DEVICE_TABLE(sis630_ids) = { { 0, } }; -MODULE_DEVICE_TABLE (pci, sis630_ids); +MODULE_DEVICE_TABLE(pci, sis630_ids); static int sis630_probe(struct pci_dev *dev, const struct pci_device_id *id) { if (sis630_setup(dev)) { - dev_err(&dev->dev, "SIS630 comp. bus not detected, module not inserted.\n"); + dev_err(&dev->dev, + "SIS630 compatible bus not detected, " + "module not inserted.\n"); return -ENODEV; } -- cgit v1.2.3 From 51aa66a58494f869f491eedda86c409c50536c14 Mon Sep 17 00:00:00 2001 From: Subhash Jadavani Date: Tue, 4 Dec 2012 17:06:18 +0530 Subject: mmc: sdio: fix resume failure due to lack of CMD52 reset If SDIO keep power flag (MMC_PM_KEEP_POWER) is not set, card would be reinitialized during resume but as we are not resetting (CMD52 reset) the SDIO card during this reinitialization, card may fail to respond back to subsequent commands (CMD5 etc...). This change resets the card before the reinitialization of card during resume. Signed-off-by: Subhash Jadavani Acked-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/sdio.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 2273ce6b6c1a..34ad4c877c1f 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -937,10 +937,12 @@ static int mmc_sdio_resume(struct mmc_host *host) mmc_claim_host(host); /* No need to reinitialize powered-resumed nonremovable cards */ - if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) + if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) { + sdio_reset(host); + mmc_go_idle(host); err = mmc_sdio_init_card(host, host->ocr, host->card, mmc_card_keep_power(host)); - else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) { + } else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) { /* We may have switched to 1-bit mode during suspend */ err = sdio_enable_4bit_bus(host->card); if (err > 0) { -- cgit v1.2.3 From 41875e388401ad97c33252d5fa39d52e0b70ee9b Mon Sep 17 00:00:00 2001 From: Sujit Reddy Thumma Date: Tue, 4 Dec 2012 17:06:19 +0530 Subject: mmc: sdio: Fix SDIO 3.0 UHS-I initialization sequence According to UHS-I initialization sequence for SDIO 3.0 cards, the host must set bit[24] (S18R) of OCR register during OCR handshake to know whether the SDIO card is capable of doing 1.8V I/O. Signed-off-by: Sujit Reddy Thumma Signed-off-by: Subhash Jadavani Reviewed-by: Johan Rudholm Reviewed-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/sdio.c | 22 +++++++++++----------- include/linux/mmc/host.h | 8 ++++++++ 2 files changed, 19 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 34ad4c877c1f..9565d38d91a4 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -157,10 +157,7 @@ static int sdio_read_cccr(struct mmc_card *card, u32 ocr) if (ret) goto out; - if (card->host->caps & - (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | - MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | - MMC_CAP_UHS_DDR50)) { + if (mmc_host_uhs(card->host)) { if (data & SDIO_UHS_DDR50) card->sw_caps.sd3_bus_mode |= SD_MODE_UHS_DDR50; @@ -478,8 +475,7 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card) * If the host doesn't support any of the UHS-I modes, fallback on * default speed. */ - if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | - MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50))) + if (!mmc_host_uhs(card->host)) return 0; bus_speed = SDIO_SPEED_SDR12; @@ -645,11 +641,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, * systems that claim 1.8v signalling in fact do not support * it. */ - if ((ocr & R4_18V_PRESENT) && - (host->caps & - (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | - MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | - MMC_CAP_UHS_DDR50))) { + if ((ocr & R4_18V_PRESENT) && mmc_host_uhs(host)) { err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, true); if (err) { @@ -1022,6 +1014,10 @@ static int mmc_sdio_power_restore(struct mmc_host *host) goto out; } + if (mmc_host_uhs(host)) + /* to query card if 1.8V signalling is supported */ + host->ocr |= R4_18V_PRESENT; + ret = mmc_sdio_init_card(host, host->ocr, host->card, mmc_card_keep_power(host)); if (!ret && host->sdio_irqs) @@ -1087,6 +1083,10 @@ int mmc_attach_sdio(struct mmc_host *host) /* * Detect and init the card. */ + if (mmc_host_uhs(host)) + /* to query card if 1.8V signalling is supported */ + host->ocr |= R4_18V_PRESENT; + err = mmc_sdio_init_card(host, host->ocr, NULL, 0); if (err) { if (err == -EAGAIN) { diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 61a10c17aabd..c89a1bb87fa5 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -434,6 +434,14 @@ static inline int mmc_boot_partition_access(struct mmc_host *host) return !(host->caps2 & MMC_CAP2_BOOTPART_NOACC); } +static inline int mmc_host_uhs(struct mmc_host *host) +{ + return host->caps & + (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | + MMC_CAP_UHS_DDR50); +} + #ifdef CONFIG_MMC_CLKGATE void mmc_host_clk_hold(struct mmc_host *host); void mmc_host_clk_release(struct mmc_host *host); -- cgit v1.2.3 From 77e2ff08925c7ec7267dc87c27eda2e62d585b57 Mon Sep 17 00:00:00 2001 From: Subhash Jadavani Date: Tue, 4 Dec 2012 17:06:20 +0530 Subject: mmc: sdio: print correct UHS mode during card detection When SDIO3.0 card is detected, incorrect bus speed mode is printed as part of card detection print in kernel logs. This change fixes it so that user won't be confused by looking at incorrect card detection message in logs. Signed-off-by: Subhash Jadavani Tested-by: Jackey Shen Acked-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/sdio.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 9565d38d91a4..3a64933466b8 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -485,23 +485,27 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card) bus_speed = SDIO_SPEED_SDR104; timing = MMC_TIMING_UHS_SDR104; card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR; + card->sd_bus_speed = UHS_SDR104_BUS_SPEED; } else if ((card->host->caps & MMC_CAP_UHS_DDR50) && (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50)) { bus_speed = SDIO_SPEED_DDR50; timing = MMC_TIMING_UHS_DDR50; card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR; + card->sd_bus_speed = UHS_DDR50_BUS_SPEED; } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50)) && (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR50)) { bus_speed = SDIO_SPEED_SDR50; timing = MMC_TIMING_UHS_SDR50; card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR; + card->sd_bus_speed = UHS_SDR50_BUS_SPEED; } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25)) && (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR25)) { bus_speed = SDIO_SPEED_SDR25; timing = MMC_TIMING_UHS_SDR25; card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR; + card->sd_bus_speed = UHS_SDR25_BUS_SPEED; } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR12)) && (card->sw_caps.sd3_bus_mode & @@ -509,6 +513,7 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card) bus_speed = SDIO_SPEED_SDR12; timing = MMC_TIMING_UHS_SDR12; card->sw_caps.uhs_max_dtr = UHS_SDR12_MAX_DTR; + card->sd_bus_speed = UHS_SDR12_BUS_SPEED; } err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed); -- cgit v1.2.3 From 505a8680b78f580245cfb83f37971c7b62bcf991 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 11 Dec 2012 15:23:42 +0800 Subject: mmc: sdhci: query card presence from cd-gpio before asking SDHCI Call mmc_gpio_get_cd() to query card presence from cd-gpio before asking SDHCI. The rationale behind this change is that flag SDHCI_QUIRK_BROKEN_CARD_DETECTION is designed for SDHCI controller to tell that SDHCI_PRESENT_STATE is broken, and it should be used for this case only. So when cd-gpio is being used, the controller should set the flag to tell that SDHCI_PRESENT_STATE is not available. However, the existing code will skip checking cd-gpio as long as flag SDHCI_QUIRK_BROKEN_CARD_DETECTION is set. Change the querying order between cd-gpio and SDHCI to support the rationale above. Signed-off-by: Shawn Guo Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 6f0bfc0c8c9c..1b97fe2d70ab 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1258,7 +1258,7 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power) static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct sdhci_host *host; - bool present; + int present; unsigned long flags; u32 tuning_opcode; @@ -1287,18 +1287,21 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) host->mrq = mrq; - /* If polling, assume that the card is always present. */ - if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) - present = true; - else - present = sdhci_readl(host, SDHCI_PRESENT_STATE) & - SDHCI_CARD_PRESENT; - - /* If we're using a cd-gpio, testing the presence bit might fail. */ - if (!present) { - int ret = mmc_gpio_get_cd(host->mmc); - if (ret > 0) - present = true; + /* + * Firstly check card presence from cd-gpio. The return could + * be one of the following possibilities: + * negative: cd-gpio is not available + * zero: cd-gpio is used, and card is removed + * one: cd-gpio is used, and card is present + */ + present = mmc_gpio_get_cd(host->mmc); + if (present < 0) { + /* If polling, assume that the card is always present. */ + if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) + present = 1; + else + present = sdhci_readl(host, SDHCI_PRESENT_STATE) & + SDHCI_CARD_PRESENT; } if (!present || host->flags & SDHCI_DEVICE_DEAD) { -- cgit v1.2.3 From d65b5ae8dabf48d8e7811a5319ec581e41b04d62 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 11 Dec 2012 22:32:18 +0800 Subject: mmc: slot-gpio: use devm_* managed functions to ease users Use devm_* managed functions, so that slot-gpio users do not have to call mmc_gpio_free_ro/cd to free up resources requested in mmc_gpio_request_ro/cd. Signed-off-by: Shawn Guo Acked-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/core/slot-gpio.c | 57 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 16a1c0b6f264..324235105519 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -92,6 +92,20 @@ int mmc_gpio_get_cd(struct mmc_host *host) } EXPORT_SYMBOL(mmc_gpio_get_cd); +/** + * mmc_gpio_request_ro - request a gpio for write-protection + * @host: mmc host + * @gpio: gpio number requested + * + * As devm_* managed functions are used in mmc_gpio_request_ro(), client + * drivers do not need to explicitly call mmc_gpio_free_ro() for freeing up, + * if the requesting and freeing are only needed at probing and unbinding time + * for once. However, if client drivers do something special like runtime + * switching for write-protection, they are responsible for calling + * mmc_gpio_request_ro() and mmc_gpio_free_ro() as a pair on their own. + * + * Returns zero on success, else an error. + */ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio) { struct mmc_gpio *ctx; @@ -106,7 +120,8 @@ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio) ctx = host->slot.handler_priv; - ret = gpio_request_one(gpio, GPIOF_DIR_IN, ctx->ro_label); + ret = devm_gpio_request_one(&host->class_dev, gpio, GPIOF_DIR_IN, + ctx->ro_label); if (ret < 0) return ret; @@ -116,6 +131,20 @@ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio) } EXPORT_SYMBOL(mmc_gpio_request_ro); +/** + * mmc_gpio_request_cd - request a gpio for card-detection + * @host: mmc host + * @gpio: gpio number requested + * + * As devm_* managed functions are used in mmc_gpio_request_cd(), client + * drivers do not need to explicitly call mmc_gpio_free_cd() for freeing up, + * if the requesting and freeing are only needed at probing and unbinding time + * for once. However, if client drivers do something special like runtime + * switching for card-detection, they are responsible for calling + * mmc_gpio_request_cd() and mmc_gpio_free_cd() as a pair on their own. + * + * Returns zero on success, else an error. + */ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) { struct mmc_gpio *ctx; @@ -128,7 +157,8 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) ctx = host->slot.handler_priv; - ret = gpio_request_one(gpio, GPIOF_DIR_IN, ctx->cd_label); + ret = devm_gpio_request_one(&host->class_dev, gpio, GPIOF_DIR_IN, + ctx->cd_label); if (ret < 0) /* * don't bother freeing memory. It might still get used by other @@ -146,7 +176,8 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) irq = -EINVAL; if (irq >= 0) { - ret = request_threaded_irq(irq, NULL, mmc_gpio_cd_irqt, + ret = devm_request_threaded_irq(&host->class_dev, irq, + NULL, mmc_gpio_cd_irqt, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, ctx->cd_label, host); if (ret < 0) @@ -164,6 +195,13 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) } EXPORT_SYMBOL(mmc_gpio_request_cd); +/** + * mmc_gpio_free_ro - free the write-protection gpio + * @host: mmc host + * + * It's provided only for cases that client drivers need to manually free + * up the write-protection gpio requested by mmc_gpio_request_ro(). + */ void mmc_gpio_free_ro(struct mmc_host *host) { struct mmc_gpio *ctx = host->slot.handler_priv; @@ -175,10 +213,17 @@ void mmc_gpio_free_ro(struct mmc_host *host) gpio = ctx->ro_gpio; ctx->ro_gpio = -EINVAL; - gpio_free(gpio); + devm_gpio_free(&host->class_dev, gpio); } EXPORT_SYMBOL(mmc_gpio_free_ro); +/** + * mmc_gpio_free_cd - free the card-detection gpio + * @host: mmc host + * + * It's provided only for cases that client drivers need to manually free + * up the card-detection gpio requested by mmc_gpio_request_cd(). + */ void mmc_gpio_free_cd(struct mmc_host *host) { struct mmc_gpio *ctx = host->slot.handler_priv; @@ -188,13 +233,13 @@ void mmc_gpio_free_cd(struct mmc_host *host) return; if (host->slot.cd_irq >= 0) { - free_irq(host->slot.cd_irq, host); + devm_free_irq(&host->class_dev, host->slot.cd_irq, host); host->slot.cd_irq = -EINVAL; } gpio = ctx->cd_gpio; ctx->cd_gpio = -EINVAL; - gpio_free(gpio); + devm_gpio_free(&host->class_dev, gpio); } EXPORT_SYMBOL(mmc_gpio_free_cd); -- cgit v1.2.3 From 164cda5236acf833f22a3edafdf8b95a7de7b402 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 11 Dec 2012 22:32:19 +0800 Subject: mmc: remove unncessary mmc_gpio_free_cd() call from slot-gpio users Since slot-gpio uses devm_* managed functions in mmc_gpio_request_cd() now, we can remove those mmc_gpio_free_cd() call from host drivers' .probe() error path and .remove(). Signed-off-by: Shawn Guo Acked-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pxav3.c | 5 ----- drivers/mmc/host/sh_mmcif.c | 6 ------ drivers/mmc/host/tmio_mmc_pio.c | 8 -------- 3 files changed, 19 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index fad0966427fd..b7ee7761bc26 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -316,7 +316,6 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) err_add_host: clk_disable_unprepare(clk); clk_put(clk); - mmc_gpio_free_cd(host->mmc); err_cd_req: err_clk_get: sdhci_pltfm_free(pdev); @@ -329,16 +328,12 @@ static int sdhci_pxav3_remove(struct platform_device *pdev) struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pxa *pxa = pltfm_host->priv; - struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; sdhci_remove_host(host, 1); clk_disable_unprepare(pltfm_host->clk); clk_put(pltfm_host->clk); - if (gpio_is_valid(pdata->ext_cd_gpio)) - mmc_gpio_free_cd(host->mmc); - sdhci_pltfm_free(pdev); kfree(pxa); diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 9a4c151067dd..741aeb95c7a4 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1404,8 +1404,6 @@ static int sh_mmcif_probe(struct platform_device *pdev) return ret; emmcaddh: - if (pd && pd->use_cd_gpio) - mmc_gpio_free_cd(mmc); erqcd: free_irq(irq[1], host); ereqirq1: @@ -1427,7 +1425,6 @@ ealloch: static int sh_mmcif_remove(struct platform_device *pdev) { struct sh_mmcif_host *host = platform_get_drvdata(pdev); - struct sh_mmcif_plat_data *pd = pdev->dev.platform_data; int irq[2]; host->dying = true; @@ -1436,9 +1433,6 @@ static int sh_mmcif_remove(struct platform_device *pdev) dev_pm_qos_hide_latency_limit(&pdev->dev); - if (pd && pd->use_cd_gpio) - mmc_gpio_free_cd(host->mmc); - mmc_remove_host(host->mmc); sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 50bf495a988b..0f992e9ffc73 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -1060,16 +1060,8 @@ EXPORT_SYMBOL(tmio_mmc_host_probe); void tmio_mmc_host_remove(struct tmio_mmc_host *host) { struct platform_device *pdev = host->pdev; - struct tmio_mmc_data *pdata = host->pdata; struct mmc_host *mmc = host->mmc; - if (pdata->flags & TMIO_MMC_USE_GPIO_CD) - /* - * This means we can miss a card-eject, but this is anyway - * possible, because of delayed processing of hotplug events. - */ - mmc_gpio_free_cd(mmc); - if (!host->native_hotplug) pm_runtime_get_sync(&pdev->dev); -- cgit v1.2.3 From fbe5fdd12c4cdae61a8c3d371e564371177da86e Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 11 Dec 2012 22:32:20 +0800 Subject: mmc: sdhci-esdhc-imx: use slot-gpio helpers for CD and WP Use slot-gpio helpers to save some code in the driver. Signed-off-by: Shawn Guo Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 56 +++++++++++--------------------------- 1 file changed, 16 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index e07df812ff1e..dd7fcc137644 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -147,17 +148,16 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) struct pltfm_imx_data *imx_data = pltfm_host->priv; struct esdhc_platform_data *boarddata = &imx_data->boarddata; - /* fake CARD_PRESENT flag */ u32 val = readl(host->ioaddr + reg); - if (unlikely((reg == SDHCI_PRESENT_STATE) - && gpio_is_valid(boarddata->cd_gpio))) { - if (gpio_get_value(boarddata->cd_gpio)) - /* no card, if a valid gpio says so... */ + if (unlikely(reg == SDHCI_PRESENT_STATE)) { + /* + * After SDHCI core gets improved to never query + * SDHCI_CARD_PRESENT state in GPIO case, we can + * remove this check. + */ + if (boarddata->cd_type == ESDHC_CD_GPIO) val &= ~SDHCI_CARD_PRESENT; - else - /* ... in all other cases assume card is present */ - val |= SDHCI_CARD_PRESENT; } if (unlikely(reg == SDHCI_CAPABILITIES)) { @@ -362,8 +362,7 @@ static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host) switch (boarddata->wp_type) { case ESDHC_WP_GPIO: - if (gpio_is_valid(boarddata->wp_gpio)) - return gpio_get_value(boarddata->wp_gpio); + return mmc_gpio_get_ro(host->mmc); case ESDHC_WP_CONTROLLER: return !(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_WRITE_PROTECT); @@ -394,14 +393,6 @@ static struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { .ops = &sdhci_esdhc_ops, }; -static irqreturn_t cd_irq(int irq, void *data) -{ - struct sdhci_host *sdhost = (struct sdhci_host *)data; - - tasklet_schedule(&sdhost->card_tasklet); - return IRQ_HANDLED; -}; - #ifdef CONFIG_OF static int sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, @@ -527,37 +518,22 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) /* write_protect */ if (boarddata->wp_type == ESDHC_WP_GPIO) { - err = devm_gpio_request_one(&pdev->dev, boarddata->wp_gpio, - GPIOF_IN, "ESDHC_WP"); + err = mmc_gpio_request_ro(host->mmc, boarddata->wp_gpio); if (err) { - dev_warn(mmc_dev(host->mmc), - "no write-protect pin available!\n"); - boarddata->wp_gpio = -EINVAL; + dev_err(mmc_dev(host->mmc), + "failed to request write-protect gpio!\n"); + goto disable_clk; } - } else { - boarddata->wp_gpio = -EINVAL; + host->mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; } /* card_detect */ - if (boarddata->cd_type != ESDHC_CD_GPIO) - boarddata->cd_gpio = -EINVAL; - switch (boarddata->cd_type) { case ESDHC_CD_GPIO: - err = devm_gpio_request_one(&pdev->dev, boarddata->cd_gpio, - GPIOF_IN, "ESDHC_CD"); + err = mmc_gpio_request_cd(host->mmc, boarddata->cd_gpio); if (err) { dev_err(mmc_dev(host->mmc), - "no card-detect pin available!\n"); - goto disable_clk; - } - - err = devm_request_irq(&pdev->dev, - gpio_to_irq(boarddata->cd_gpio), cd_irq, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, - mmc_hostname(host->mmc), host); - if (err) { - dev_err(mmc_dev(host->mmc), "request irq error\n"); + "failed to request card-detect gpio!\n"); goto disable_clk; } /* fall through */ -- cgit v1.2.3 From 92ff0c5bc4000d6b2b1bfc8cd40bd1397a03c8ec Mon Sep 17 00:00:00 2001 From: Teppei Kamijou Date: Wed, 12 Dec 2012 15:38:05 +0100 Subject: mmc: sh_mmcif: force to fail CMD52 immediately mmc_rescan() sends CMD52 (SD_IO_RW_DIRECT) to reset SDIO card during card detection. CMD52 should be ignored by SD/eMMC cards, but we can also abort it in the driver immediately, since MMCIF doesn't support SDIO cards anyway. Signed-off-by: Teppei Kamijou Signed-off-by: Shinya Kuribayashi Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 741aeb95c7a4..8b4e98e2b130 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -911,6 +911,7 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) if ((mrq->cmd->flags & MMC_CMD_MASK) != MMC_CMD_BCR) break; case MMC_APP_CMD: + case SD_IO_RW_DIRECT: host->state = STATE_IDLE; mrq->cmd->error = -ETIMEDOUT; mmc_request_done(mmc, mrq); -- cgit v1.2.3 From f8a8ced7f9e79916311c0ef08d1f6de7bf954807 Mon Sep 17 00:00:00 2001 From: Teppei Kamijou Date: Wed, 12 Dec 2012 15:38:06 +0100 Subject: mmc: sh_mmcif: ensure run-time suspend call is processed before suspend With this post-v2.6.35 change applied: commit a0a1a5fd4fb15ec61117c759fe9f5c16c53d9e9c Author: Tejun Heo Date: Tue Jun 29 10:07:12 2010 +0200 workqueue: reimplement workqueue freeze using max_active freeze_workqueues_begin() was introduced and workqueue now gets frozen before device drivers suspend operations. We have to ensure that run-time PM suspend operation completes before system-wide suspend is started. Signed-off-by: Teppei Kamijou Signed-off-by: Shinya Kuribayashi Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 8b4e98e2b130..2ff3e4774b67 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -982,7 +982,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } } if (host->power) { - pm_runtime_put(&host->pd->dev); + pm_runtime_put_sync(&host->pd->dev); clk_disable(host->hclk); host->power = false; if (ios->power_mode == MMC_POWER_OFF) -- cgit v1.2.3 From 2cd5b3e061e3742de6b3a50f45f7d3b96aa50964 Mon Sep 17 00:00:00 2001 From: Shinya Kuribayashi Date: Mon, 14 Jan 2013 14:12:36 -0500 Subject: mmc: sh_mmcif: add support for bundled MMCIF IRQs On newer SoCs like R-Mobile U2, MMCIF interrupts are bundled. Signed-off-by: Shinya Kuribayashi Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 2ff3e4774b67..c7984bad8efc 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1307,10 +1307,11 @@ static int sh_mmcif_probe(struct platform_device *pdev) struct sh_mmcif_plat_data *pd = pdev->dev.platform_data; struct resource *res; void __iomem *reg; + const char *name; irq[0] = platform_get_irq(pdev, 0); irq[1] = platform_get_irq(pdev, 1); - if (irq[0] < 0 || irq[1] < 0) { + if (irq[0] < 0) { dev_err(&pdev->dev, "Get irq error\n"); return -ENXIO; } @@ -1375,15 +1376,19 @@ static int sh_mmcif_probe(struct platform_device *pdev) sh_mmcif_sync_reset(host); sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); - ret = request_threaded_irq(irq[0], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:error", host); + name = irq[1] < 0 ? dev_name(&pdev->dev) : "sh_mmc:error"; + ret = request_threaded_irq(irq[0], sh_mmcif_intr, sh_mmcif_irqt, 0, name, host); if (ret) { - dev_err(&pdev->dev, "request_irq error (sh_mmc:error)\n"); + dev_err(&pdev->dev, "request_irq error (%s)\n", name); goto ereqirq0; } - ret = request_threaded_irq(irq[1], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:int", host); - if (ret) { - dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n"); - goto ereqirq1; + if (irq[1] >= 0) { + ret = request_threaded_irq(irq[1], sh_mmcif_intr, sh_mmcif_irqt, + 0, "sh_mmc:int", host); + if (ret) { + dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n"); + goto ereqirq1; + } } if (pd && pd->use_cd_gpio) { @@ -1406,7 +1411,8 @@ static int sh_mmcif_probe(struct platform_device *pdev) emmcaddh: erqcd: - free_irq(irq[1], host); + if (irq[1] >= 0) + free_irq(irq[1], host); ereqirq1: free_irq(irq[0], host); ereqirq0: @@ -1451,7 +1457,8 @@ static int sh_mmcif_remove(struct platform_device *pdev) irq[1] = platform_get_irq(pdev, 1); free_irq(irq[0], host); - free_irq(irq[1], host); + if (irq[1] >= 0) + free_irq(irq[1], host); platform_set_drvdata(pdev, NULL); -- cgit v1.2.3 From 555061f987787a966b01e62517b9befbd35e2f89 Mon Sep 17 00:00:00 2001 From: Teppei Kamijou Date: Wed, 12 Dec 2012 15:38:08 +0100 Subject: mmc: sh_mmcif: Add support for eMMC Dual Data Rate Some MMCIF implementations support the Dual Data Rate. With this patch, platforms can set the MMC_CAP_UHS_DDR50 capability flag in MMCIF platform data. This will let the MMC core to actually use the DDR mode. Signed-off-by: Teppei Kamijou Signed-off-by: Shinya Kuribayashi Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index c7984bad8efc..6d4328dce320 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -88,6 +88,7 @@ #define CMD_SET_TBIT (1 << 7) /* 1: tran mission bit "Low" */ #define CMD_SET_OPDM (1 << 6) /* 1: open/drain */ #define CMD_SET_CCSH (1 << 5) +#define CMD_SET_DARS (1 << 2) /* Dual Data Rate */ #define CMD_SET_DATW_1 ((0 << 1) | (0 << 0)) /* 1bit */ #define CMD_SET_DATW_4 ((0 << 1) | (1 << 0)) /* 4bit */ #define CMD_SET_DATW_8 ((1 << 1) | (0 << 0)) /* 8bit */ @@ -216,6 +217,7 @@ struct sh_mmcif_host { struct clk *hclk; unsigned int clk; int bus_width; + unsigned char timing; bool sd_error; bool dying; long timeout; @@ -781,6 +783,17 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, dev_err(&host->pd->dev, "Unsupported bus width.\n"); break; } + switch (host->timing) { + case MMC_TIMING_UHS_DDR50: + /* + * MMC core will only set this timing, if the host + * advertises the MMC_CAP_UHS_DDR50 capability. MMCIF + * implementations with this capability, e.g. sh73a0, + * will have to set it in their platform data. + */ + tmp |= CMD_SET_DARS; + break; + } } /* DWEN */ if (opc == MMC_WRITE_BLOCK || opc == MMC_WRITE_MULTIPLE_BLOCK) @@ -1002,6 +1015,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) sh_mmcif_clock_control(host, ios->clock); } + host->timing = ios->timing; host->bus_width = ios->bus_width; host->state = STATE_IDLE; } -- cgit v1.2.3 From f9fd54f22e79046208ff1656afff7b5f9b7433cb Mon Sep 17 00:00:00 2001 From: Teppei Kamijou Date: Wed, 12 Dec 2012 15:38:09 +0100 Subject: mmc: sh_mmcif: Use msecs_to_jiffies() for host->timeout Timeout period should be properly normalized using msecs_to_jiffies(). Signed-off-by: Teppei Kamijou Signed-off-by: Shinya Kuribayashi Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 6d4328dce320..6d3644430877 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1348,7 +1348,7 @@ static int sh_mmcif_probe(struct platform_device *pdev) host = mmc_priv(mmc); host->mmc = mmc; host->addr = reg; - host->timeout = 1000; + host->timeout = msecs_to_jiffies(1000); host->pd = pdev; -- cgit v1.2.3 From a812ba0fd031c14c7680c94a1a22d5c60c7b6e2b Mon Sep 17 00:00:00 2001 From: Teppei Kamijou Date: Wed, 12 Dec 2012 15:38:10 +0100 Subject: mmc: sh_mmcif: Avoid unnecessary mmc_delay() at mmc_card_sleepawake() SH/R-Mobile MMCIF host controller can wait while the card signals busy. Set MMC_CAP_WAIT_WHILE_BUSY to inform an upper layer (core/mmc_ops.c) not to insert unnecessary mmc_delay(). Signed-off-by: Teppei Kamijou Signed-off-by: Shinya Kuribayashi Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 6d3644430877..663b92b364bb 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -758,6 +758,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, } switch (opc) { /* RBSY */ + case MMC_SLEEP_AWAKE: case MMC_SWITCH: case MMC_STOP_TRANSMISSION: case MMC_SET_WRITE_PROT: @@ -851,6 +852,7 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, switch (opc) { /* response busy check */ + case MMC_SLEEP_AWAKE: case MMC_SWITCH: case MMC_STOP_TRANSMISSION: case MMC_SET_WRITE_PROT: @@ -1357,7 +1359,7 @@ static int sh_mmcif_probe(struct platform_device *pdev) mmc->ops = &sh_mmcif_ops; sh_mmcif_init_ocr(host); - mmc->caps = MMC_CAP_MMC_HIGHSPEED; + mmc->caps = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_WAIT_WHILE_BUSY; if (pd && pd->caps) mmc->caps |= pd->caps; mmc->max_segs = 32; -- cgit v1.2.3 From 5df460b15e10ffcf2c9a05d0c55b309568d330ea Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 12 Dec 2012 15:38:11 +0100 Subject: mmc: sh_mmcif: fix missing and consolidate IO completion timeouts Read block and write block operations are currently missing completion timeouts. Add missing timeouts and consolidate them at one location. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 663b92b364bb..f4b10c8f6384 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -564,7 +564,6 @@ static void sh_mmcif_single_read(struct sh_mmcif_host *host, BLOCK_SIZE_MASK) + 3; host->wait_for = MMCIF_WAIT_FOR_READ; - schedule_delayed_work(&host->timeout_work, host->timeout); /* buf read enable */ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); @@ -606,7 +605,7 @@ static void sh_mmcif_multi_read(struct sh_mmcif_host *host, host->sg_idx = 0; host->sg_blkidx = 0; host->pio_ptr = sg_virt(data->sg); - schedule_delayed_work(&host->timeout_work, host->timeout); + sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); } @@ -629,7 +628,6 @@ static bool sh_mmcif_mread_block(struct sh_mmcif_host *host) if (!sh_mmcif_next_block(host, p)) return false; - schedule_delayed_work(&host->timeout_work, host->timeout); sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); return true; @@ -642,7 +640,6 @@ static void sh_mmcif_single_write(struct sh_mmcif_host *host, BLOCK_SIZE_MASK) + 3; host->wait_for = MMCIF_WAIT_FOR_WRITE; - schedule_delayed_work(&host->timeout_work, host->timeout); /* buf write enable */ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); @@ -684,7 +681,7 @@ static void sh_mmcif_multi_write(struct sh_mmcif_host *host, host->sg_idx = 0; host->sg_blkidx = 0; host->pio_ptr = sg_virt(data->sg); - schedule_delayed_work(&host->timeout_work, host->timeout); + sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); } @@ -707,7 +704,6 @@ static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host) if (!sh_mmcif_next_block(host, p)) return false; - schedule_delayed_work(&host->timeout_work, host->timeout); sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); return true; @@ -900,7 +896,6 @@ static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host, } host->wait_for = MMCIF_WAIT_FOR_STOP; - schedule_delayed_work(&host->timeout_work, host->timeout); } static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) @@ -1121,6 +1116,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) { struct sh_mmcif_host *host = dev_id; struct mmc_request *mrq = host->mrq; + bool wait = false; cancel_delayed_work_sync(&host->timeout_work); @@ -1133,29 +1129,24 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) /* We're too late, the timeout has already kicked in */ return IRQ_HANDLED; case MMCIF_WAIT_FOR_CMD: - if (sh_mmcif_end_cmd(host)) - /* Wait for data */ - return IRQ_HANDLED; + /* Wait for data? */ + wait = sh_mmcif_end_cmd(host); break; case MMCIF_WAIT_FOR_MREAD: - if (sh_mmcif_mread_block(host)) - /* Wait for more data */ - return IRQ_HANDLED; + /* Wait for more data? */ + wait = sh_mmcif_mread_block(host); break; case MMCIF_WAIT_FOR_READ: - if (sh_mmcif_read_block(host)) - /* Wait for data end */ - return IRQ_HANDLED; + /* Wait for data end? */ + wait = sh_mmcif_read_block(host); break; case MMCIF_WAIT_FOR_MWRITE: - if (sh_mmcif_mwrite_block(host)) - /* Wait data to write */ - return IRQ_HANDLED; + /* Wait data to write? */ + wait = sh_mmcif_mwrite_block(host); break; case MMCIF_WAIT_FOR_WRITE: - if (sh_mmcif_write_block(host)) - /* Wait for data end */ - return IRQ_HANDLED; + /* Wait for data end? */ + wait = sh_mmcif_write_block(host); break; case MMCIF_WAIT_FOR_STOP: if (host->sd_error) { @@ -1174,6 +1165,12 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) BUG(); } + if (wait) { + schedule_delayed_work(&host->timeout_work, host->timeout); + /* Wait for more data */ + return IRQ_HANDLED; + } + if (host->wait_for != MMCIF_WAIT_FOR_STOP) { struct mmc_data *data = mrq->data; if (!mrq->cmd->error && data && !data->error) @@ -1182,8 +1179,10 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) if (mrq->stop && !mrq->cmd->error && (!data || !data->error)) { sh_mmcif_stop_cmd(host, mrq); - if (!mrq->stop->error) + if (!mrq->stop->error) { + schedule_delayed_work(&host->timeout_work, host->timeout); return IRQ_HANDLED; + } } } -- cgit v1.2.3 From eae309836509496c981ceaebdef57041de86ecd4 Mon Sep 17 00:00:00 2001 From: Teppei Kamijou Date: Wed, 12 Dec 2012 15:38:12 +0100 Subject: mmc: sh_mmcif: Terminate DMA transactions when detecting timeout or error If a DMA transaction fails, terminate all outstanding DMA transfers and unmap buffers. Signed-off-by: Teppei Kamijou Signed-off-by: Shinya Kuribayashi [g.liakhovetski@gmx.de: forward-port, add dma_unmap_sg() in error cases] Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index f4b10c8f6384..8aa7b0e6dec2 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -263,15 +263,6 @@ static void mmcif_dma_complete(void *arg) dev_name(&host->pd->dev))) return; - if (data->flags & MMC_DATA_READ) - dma_unmap_sg(host->chan_rx->device->dev, - data->sg, data->sg_len, - DMA_FROM_DEVICE); - else - dma_unmap_sg(host->chan_tx->device->dev, - data->sg, data->sg_len, - DMA_TO_DEVICE); - complete(&host->dma_complete); } @@ -1088,14 +1079,20 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) /* Running in the IRQ thread, can sleep */ time = wait_for_completion_interruptible_timeout(&host->dma_complete, host->timeout); + + if (data->flags & MMC_DATA_READ) + dma_unmap_sg(host->chan_rx->device->dev, + data->sg, data->sg_len, + DMA_FROM_DEVICE); + else + dma_unmap_sg(host->chan_tx->device->dev, + data->sg, data->sg_len, + DMA_TO_DEVICE); + if (host->sd_error) { dev_err(host->mmc->parent, "Error IRQ while waiting for DMA completion!\n"); /* Woken up by an error IRQ: abort DMA */ - if (data->flags & MMC_DATA_READ) - dmaengine_terminate_all(host->chan_rx); - else - dmaengine_terminate_all(host->chan_tx); data->error = sh_mmcif_error_manage(host); } else if (!time) { data->error = -ETIMEDOUT; @@ -1106,8 +1103,14 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) BUF_ACC_DMAREN | BUF_ACC_DMAWEN); host->dma_active = false; - if (data->error) + if (data->error) { data->bytes_xfered = 0; + /* Abort DMA */ + if (data->flags & MMC_DATA_READ) + dmaengine_terminate_all(host->chan_rx); + else + dmaengine_terminate_all(host->chan_tx); + } return false; } -- cgit v1.2.3 From 99eb9d8df996fc1695d4de687873875fbd8f6719 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 12 Dec 2012 15:38:13 +0100 Subject: mmc: sh_mmcif: (cosmetic) simplify boolean return blocks Use "return condition" instead of "if (condition) return true; return false" in functions, returning bool. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 8aa7b0e6dec2..de4b6d073599 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -542,10 +542,7 @@ static bool sh_mmcif_next_block(struct sh_mmcif_host *host, u32 *p) host->pio_ptr = p; } - if (host->sg_idx == data->sg_len) - return false; - - return true; + return host->sg_idx != data->sg_len; } static void sh_mmcif_single_read(struct sh_mmcif_host *host, @@ -1071,9 +1068,7 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) if (!host->dma_active) { data->error = sh_mmcif_data_trans(host, host->mrq, cmd->opcode); - if (!data->error) - return true; - return false; + return !data->error; } /* Running in the IRQ thread, can sleep */ -- cgit v1.2.3 From 8047310ee984b3efe932ee5b561f2523396466dd Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 12 Dec 2012 15:38:14 +0100 Subject: mmc: sh_mmcif: fix a race, causing an Oops on SMP Oopses have been observed on SMP in the sh-mmcif IRQ thread, when the two IRQ threads run simultaneously on two CPUs. Also take care to guard the timeout work and the DMA completion callback from possible NULL-pointer dereferences and races. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index de4b6d073599..3cfe383dc22b 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -196,6 +197,7 @@ enum mmcif_state { STATE_IDLE, STATE_REQUEST, STATE_IOS, + STATE_TIMEOUT, }; enum mmcif_wait_for { @@ -232,6 +234,7 @@ struct sh_mmcif_host { int sg_blkidx; bool power; bool card_present; + struct mutex thread_lock; /* DMA support */ struct dma_chan *chan_rx; @@ -255,11 +258,11 @@ static inline void sh_mmcif_bitclr(struct sh_mmcif_host *host, static void mmcif_dma_complete(void *arg) { struct sh_mmcif_host *host = arg; - struct mmc_data *data = host->mrq->data; + struct mmc_request *mrq = host->mrq; dev_dbg(&host->pd->dev, "Command completed\n"); - if (WARN(!data, "%s: NULL data in DMA completion!\n", + if (WARN(!mrq || !mrq->data, "%s: NULL data in DMA completion!\n", dev_name(&host->pd->dev))) return; @@ -1113,11 +1116,21 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) { struct sh_mmcif_host *host = dev_id; - struct mmc_request *mrq = host->mrq; + struct mmc_request *mrq; bool wait = false; cancel_delayed_work_sync(&host->timeout_work); + mutex_lock(&host->thread_lock); + + mrq = host->mrq; + if (!mrq) { + dev_dbg(&host->pd->dev, "IRQ thread state %u, wait %u: NULL mrq!\n", + host->state, host->wait_for); + mutex_unlock(&host->thread_lock); + return IRQ_HANDLED; + } + /* * All handlers return true, if processing continues, and false, if the * request has to be completed - successfully or not @@ -1125,6 +1138,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) switch (host->wait_for) { case MMCIF_WAIT_FOR_REQUEST: /* We're too late, the timeout has already kicked in */ + mutex_unlock(&host->thread_lock); return IRQ_HANDLED; case MMCIF_WAIT_FOR_CMD: /* Wait for data? */ @@ -1166,6 +1180,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) if (wait) { schedule_delayed_work(&host->timeout_work, host->timeout); /* Wait for more data */ + mutex_unlock(&host->thread_lock); return IRQ_HANDLED; } @@ -1179,6 +1194,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) sh_mmcif_stop_cmd(host, mrq); if (!mrq->stop->error) { schedule_delayed_work(&host->timeout_work, host->timeout); + mutex_unlock(&host->thread_lock); return IRQ_HANDLED; } } @@ -1189,6 +1205,8 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) host->mrq = NULL; mmc_request_done(host->mmc, mrq); + mutex_unlock(&host->thread_lock); + return IRQ_HANDLED; } @@ -1262,11 +1280,24 @@ static void mmcif_timeout_work(struct work_struct *work) struct delayed_work *d = container_of(work, struct delayed_work, work); struct sh_mmcif_host *host = container_of(d, struct sh_mmcif_host, timeout_work); struct mmc_request *mrq = host->mrq; + unsigned long flags; if (host->dying) /* Don't run after mmc_remove_host() */ return; + dev_dbg(&host->pd->dev, "Timeout waiting for %u, opcode %u\n", + host->wait_for, mrq->cmd->opcode); + + spin_lock_irqsave(&host->lock, flags); + if (host->state == STATE_IDLE) { + spin_unlock_irqrestore(&host->lock, flags); + return; + } + + host->state = STATE_TIMEOUT; + spin_unlock_irqrestore(&host->lock, flags); + /* * Handle races with cancel_delayed_work(), unless * cancel_delayed_work_sync() is used @@ -1410,6 +1441,8 @@ static int sh_mmcif_probe(struct platform_device *pdev) goto erqcd; } + mutex_init(&host->thread_lock); + clk_disable(host->hclk); ret = mmc_add_host(mmc); if (ret < 0) -- cgit v1.2.3 From aba9d646785c8b1decb1a15ee157b0179a15bef9 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 12 Dec 2012 15:38:15 +0100 Subject: mmc: sh_mmcif: reset error code for any opcode If a command execution has produced an error, it has to be reset as a part of the error handling. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 3cfe383dc22b..14fafafc12d3 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1041,7 +1041,6 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) case MMC_SELECT_CARD: case MMC_APP_CMD: cmd->error = -ETIMEDOUT; - host->sd_error = false; break; default: cmd->error = sh_mmcif_error_manage(host); @@ -1049,6 +1048,7 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) cmd->opcode, cmd->error); break; } + host->sd_error = false; return false; } if (!(cmd->flags & MMC_RSP_PRESENT)) { -- cgit v1.2.3 From 90f1cb438e33bb88036649665c2165155561b54f Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 12 Dec 2012 15:38:16 +0100 Subject: mmc: sh_mmcif: reset DMA completion immediately before starting DMA DMA completion can be signalled from the DMA callback and from the error handler. If both are called, the completion struct can enter an inconsistent state. To prevent this move completion initialisation immediately before activating DMA. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 14fafafc12d3..1c37854c0f33 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -419,8 +419,6 @@ static void sh_mmcif_request_dma(struct sh_mmcif_host *host, if (ret < 0) goto ecfgrx; - init_completion(&host->dma_complete); - return; ecfgrx: @@ -1061,6 +1059,12 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) if (!data) return false; + /* + * Completion can be signalled from DMA callback and error, so, have to + * reset here, before setting .dma_active + */ + init_completion(&host->dma_complete); + if (data->flags & MMC_DATA_READ) { if (host->chan_rx) sh_mmcif_start_dma_rx(host); -- cgit v1.2.3 From 276bc96b2abfa2a6bb219aa4eda13c4c331c53eb Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 12 Dec 2012 15:38:17 +0100 Subject: mmc: sh_mmcif: fix I/O errors The INT_BUFWEN IRQ often arrives with other bits set too. If they are not cleared, an additional IRQ can be triggered, sometimes also after the MMC request has already been completed. This leads to block I/O errors. Earlier Teppei Kamijou also observed these additional interrupts and proposed to explicitly wait for them. This patch chooses an alternative approach of clearing all active bits immediately, when processing the main interrupt. Reported-by: Teppei Kamijou Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 1c37854c0f33..e6a6d2363a4d 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1238,7 +1238,9 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFREN); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); } else if (state & INT_BUFWEN) { - sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFWEN); + sh_mmcif_writel(host->addr, MMCIF_CE_INT, + ~(INT_BUFWEN | INT_DTRANE | INT_CMD12DRE | + INT_CMD12RBE | INT_CMD12CRE)); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); } else if (state & INT_CMD12DRE) { sh_mmcif_writel(host->addr, MMCIF_CE_INT, -- cgit v1.2.3 From e475b2702f612901772d12110614ad8c3fb5d89b Mon Sep 17 00:00:00 2001 From: Teppei Kamijou Date: Wed, 12 Dec 2012 15:38:18 +0100 Subject: mmc: sh_mmcif: report all errors Make error reporting in the driver more verbose. This patch is based on an earlier work by Teppei Kamijou, but we try to not add any new error messages to the log in the normal case to avoid confusing the user, and also add a few more dev_dbg() calls. Signed-off-by: Teppei Kamijou Signed-off-by: Shinya Kuribayashi [g.liakhovetski@gmx.de: avoid producing new errors in normal case] Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index e6a6d2363a4d..b4180c7bffc4 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -514,13 +514,16 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host) } if (state2 & STS2_CRC_ERR) { - dev_dbg(&host->pd->dev, ": CRC error\n"); + dev_err(&host->pd->dev, " CRC error: state %u, wait %u\n", + host->state, host->wait_for); ret = -EIO; } else if (state2 & STS2_TIMEOUT_ERR) { - dev_dbg(&host->pd->dev, ": Timeout\n"); + dev_err(&host->pd->dev, " Timeout: state %u, wait %u\n", + host->state, host->wait_for); ret = -ETIMEDOUT; } else { - dev_dbg(&host->pd->dev, ": End/Index error\n"); + dev_dbg(&host->pd->dev, " End/Index error: state %u, wait %u\n", + host->state, host->wait_for); ret = -EIO; } return ret; @@ -566,6 +569,7 @@ static bool sh_mmcif_read_block(struct sh_mmcif_host *host) if (host->sd_error) { data->error = sh_mmcif_error_manage(host); + dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error); return false; } @@ -606,6 +610,7 @@ static bool sh_mmcif_mread_block(struct sh_mmcif_host *host) if (host->sd_error) { data->error = sh_mmcif_error_manage(host); + dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error); return false; } @@ -642,6 +647,7 @@ static bool sh_mmcif_write_block(struct sh_mmcif_host *host) if (host->sd_error) { data->error = sh_mmcif_error_manage(host); + dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error); return false; } @@ -682,6 +688,7 @@ static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host) if (host->sd_error) { data->error = sh_mmcif_error_manage(host); + dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error); return false; } @@ -823,7 +830,7 @@ static int sh_mmcif_data_trans(struct sh_mmcif_host *host, sh_mmcif_single_read(host, mrq); return 0; default: - dev_err(&host->pd->dev, "UNSUPPORTED CMD = d'%08d\n", opc); + dev_err(&host->pd->dev, "Unsupported CMD%d\n", opc); return -EINVAL; } } @@ -894,6 +901,7 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) spin_lock_irqsave(&host->lock, flags); if (host->state != STATE_IDLE) { + dev_dbg(&host->pd->dev, "%s() rejected, state %u\n", __func__, host->state); spin_unlock_irqrestore(&host->lock, flags); mrq->cmd->error = -EAGAIN; mmc_request_done(mmc, mrq); @@ -957,6 +965,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) spin_lock_irqsave(&host->lock, flags); if (host->state != STATE_IDLE) { + dev_dbg(&host->pd->dev, "%s() rejected, state %u\n", __func__, host->state); spin_unlock_irqrestore(&host->lock, flags); return; } @@ -1042,10 +1051,10 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) break; default: cmd->error = sh_mmcif_error_manage(host); - dev_dbg(&host->pd->dev, "Cmd(d'%d) error %d\n", - cmd->opcode, cmd->error); break; } + dev_dbg(&host->pd->dev, "CMD%d error %d\n", + cmd->opcode, cmd->error); host->sd_error = false; return false; } @@ -1097,8 +1106,11 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) /* Woken up by an error IRQ: abort DMA */ data->error = sh_mmcif_error_manage(host); } else if (!time) { + dev_err(host->mmc->parent, "DMA timeout!\n"); data->error = -ETIMEDOUT; } else if (time < 0) { + dev_err(host->mmc->parent, + "wait_for_completion_...() error %ld!\n", time); data->error = time; } sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, @@ -1167,6 +1179,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) case MMCIF_WAIT_FOR_STOP: if (host->sd_error) { mrq->stop->error = sh_mmcif_error_manage(host); + dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, mrq->stop->error); break; } sh_mmcif_get_cmd12response(host, mrq->stop); @@ -1174,8 +1187,10 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) break; case MMCIF_WAIT_FOR_READ_END: case MMCIF_WAIT_FOR_WRITE_END: - if (host->sd_error) + if (host->sd_error) { mrq->data->error = sh_mmcif_error_manage(host); + dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, mrq->data->error); + } break; default: BUG(); @@ -1292,7 +1307,7 @@ static void mmcif_timeout_work(struct work_struct *work) /* Don't run after mmc_remove_host() */ return; - dev_dbg(&host->pd->dev, "Timeout waiting for %u, opcode %u\n", + dev_err(&host->pd->dev, "Timeout waiting for %u on CMD%u\n", host->wait_for, mrq->cmd->opcode); spin_lock_irqsave(&host->lock, flags); -- cgit v1.2.3 From 8af5075088a0aaa64caed5ed212b485b5760bf0b Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 12 Dec 2012 15:45:14 +0100 Subject: mmc: sh_mmcif: simplify IRQ processing The classical way to process IRQs is read out the status, ack all triggered IRQs, possibly mask them, then process them. Follow this simple procesure instead of the current complex custom algorithm. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 65 +++++++++++++-------------------------------- 1 file changed, 19 insertions(+), 46 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index b4180c7bffc4..0189efcb9e12 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -129,6 +129,10 @@ INT_CCSTO | INT_CRCSTO | INT_WDATTO | \ INT_RDATTO | INT_RBSYTO | INT_RSPTO) +#define INT_ALL (INT_RBSYE | INT_CRSPE | INT_BUFREN | \ + INT_BUFWEN | INT_CMD12DRE | INT_BUFRE | \ + INT_DTRANE | INT_CMD12RBE | INT_CMD12CRE) + /* CE_INT_MASK */ #define MASK_ALL 0x00000000 #define MASK_MCCSDE (1 << 29) @@ -160,6 +164,11 @@ MASK_MCCSTO | MASK_MCRCSTO | MASK_MWDATTO | \ MASK_MRDATTO | MASK_MRBSYTO | MASK_MRSPTO) +#define MASK_CLEAN (INT_ERR_STS | MASK_MRBSYE | MASK_MCRSPE | \ + MASK_MBUFREN | MASK_MBUFWEN | \ + MASK_MCMD12DRE | MASK_MBUFRE | MASK_MDTRANE | \ + MASK_MCMD12RBE | MASK_MCMD12CRE) + /* CE_HOST_STS1 */ #define STS1_CMDSEQ (1 << 31) @@ -1233,58 +1242,22 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) { struct sh_mmcif_host *host = dev_id; u32 state; - int err = 0; state = sh_mmcif_readl(host->addr, MMCIF_CE_INT); + sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state); + sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state & MASK_CLEAN); - if (state & INT_ERR_STS) { - /* error interrupts - process first */ - sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state); - err = 1; - } else if (state & INT_RBSYE) { - sh_mmcif_writel(host->addr, MMCIF_CE_INT, - ~(INT_RBSYE | INT_CRSPE)); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MRBSYE); - } else if (state & INT_CRSPE) { - sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_CRSPE); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCRSPE); - } else if (state & INT_BUFREN) { - sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFREN); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); - } else if (state & INT_BUFWEN) { - sh_mmcif_writel(host->addr, MMCIF_CE_INT, - ~(INT_BUFWEN | INT_DTRANE | INT_CMD12DRE | - INT_CMD12RBE | INT_CMD12CRE)); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); - } else if (state & INT_CMD12DRE) { - sh_mmcif_writel(host->addr, MMCIF_CE_INT, - ~(INT_CMD12DRE | INT_CMD12RBE | - INT_CMD12CRE | INT_BUFRE)); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE); - } else if (state & INT_BUFRE) { - sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFRE); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFRE); - } else if (state & INT_DTRANE) { - sh_mmcif_writel(host->addr, MMCIF_CE_INT, - ~(INT_CMD12DRE | INT_CMD12RBE | - INT_CMD12CRE | INT_DTRANE)); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MDTRANE); - } else if (state & INT_CMD12RBE) { - sh_mmcif_writel(host->addr, MMCIF_CE_INT, - ~(INT_CMD12RBE | INT_CMD12CRE)); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE); - } else { - dev_dbg(&host->pd->dev, "Unsupported interrupt: 0x%x\n", state); - sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state); - err = 1; - } - if (err) { + if (state & ~MASK_CLEAN) + dev_dbg(&host->pd->dev, "IRQ state = 0x%08x incompletely cleared\n", + state); + + if (state & INT_ERR_STS || state & ~INT_ALL) { host->sd_error = true; - dev_dbg(&host->pd->dev, "int err state = %08x\n", state); + dev_dbg(&host->pd->dev, "int err state = 0x%08x\n", state); } if (state & ~(INT_CMD12RBE | INT_CMD12CRE)) { + if (!host->mrq) + dev_dbg(&host->pd->dev, "NULL IRQ state = 0x%08x\n", state); if (!host->dma_active) return IRQ_WAKE_THREAD; else if (host->sd_error) -- cgit v1.2.3 From 369d321ed1baa7748e770aaaae4d8effad699633 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Wed, 26 Dec 2012 10:40:17 +0900 Subject: mmc: queue: exclude asynchronous transfer for special request Unlike normal r/w request, special requests(discard, flush) is finished with a one-time issue_fn. Request change to mqrq_prev makes unnecessary call. Signed-off-by: Seungwon Jeon Reviewed-by: Konstantin Dorfman Signed-off-by: Chris Ball --- drivers/mmc/card/queue.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index fadf52eb5d70..d630d9861e7b 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -24,6 +24,8 @@ #define MMC_QUEUE_SUSPENDED (1 << 0) +#define MMC_REQ_SPECIAL_MASK (REQ_DISCARD | REQ_FLUSH) + /* * Prepare a MMC request. This just filters out odd stuff. */ @@ -58,6 +60,7 @@ static int mmc_queue_thread(void *d) do { struct request *req = NULL; struct mmc_queue_req *tmp; + unsigned int cmd_flags = 0; spin_lock_irq(q->queue_lock); set_current_state(TASK_INTERRUPTIBLE); @@ -67,12 +70,19 @@ static int mmc_queue_thread(void *d) if (req || mq->mqrq_prev->req) { set_current_state(TASK_RUNNING); + cmd_flags = req ? req->cmd_flags : 0; mq->issue_fn(mq, req); /* * Current request becomes previous request * and vice versa. + * In case of special requests, current request + * has been finished. Do not assign it to previous + * request. */ + if (cmd_flags & MMC_REQ_SPECIAL_MASK) + mq->mqrq_cur->req = NULL; + mq->mqrq_prev->brq.mrq.data = NULL; mq->mqrq_prev->req = NULL; tmp = mq->mqrq_prev; -- cgit v1.2.3 From 2220eedfd7aea69008173a224975e10284fbe854 Mon Sep 17 00:00:00 2001 From: Konstantin Dorfman Date: Mon, 14 Jan 2013 14:28:17 -0500 Subject: mmc: fix async request mechanism for sequential read scenarios When current request is running on the bus and if next request fetched by mmcqd is NULL, mmc context (mmcqd thread) gets blocked until the current request completes. This means that if new request comes in while the mmcqd thread is blocked, this new request can not be prepared in parallel to current ongoing request. This may result in delaying the new request execution and increase it's latency. This change allows to wake up the MMC thread on new request arrival. Now once the MMC thread is woken up, a new request can be fetched and prepared in parallel to the current running request which means this new request can be started immediately after the current running request completes. With this change read throughput is improved by 16%. Signed-off-by: Konstantin Dorfman Reviewed-by: Seungwon Jeon Signed-off-by: Chris Ball --- drivers/mmc/card/block.c | 30 +++++++----- drivers/mmc/card/queue.c | 22 ++++++++- drivers/mmc/card/queue.h | 3 ++ drivers/mmc/core/bus.c | 1 + drivers/mmc/core/core.c | 121 +++++++++++++++++++++++++++++++++++++++++++++-- drivers/mmc/core/core.h | 1 + include/linux/mmc/card.h | 12 +++++ include/linux/mmc/core.h | 3 +- include/linux/mmc/host.h | 17 +++++++ 9 files changed, 191 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 21056b9ef0a0..f79b4688e471 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -113,17 +113,6 @@ struct mmc_blk_data { static DEFINE_MUTEX(open_lock); -enum mmc_blk_status { - MMC_BLK_SUCCESS = 0, - MMC_BLK_PARTIAL, - MMC_BLK_CMD_ERR, - MMC_BLK_RETRY, - MMC_BLK_ABORT, - MMC_BLK_DATA_ERR, - MMC_BLK_ECC_ERR, - MMC_BLK_NOMEDIUM, -}; - module_param(perdev_minors, int, 0444); MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device"); @@ -1364,8 +1353,11 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) } else areq = NULL; areq = mmc_start_req(card->host, areq, (int *) &status); - if (!areq) + if (!areq) { + if (status == MMC_BLK_NEW_REQUEST) + mq->flags |= MMC_QUEUE_NEW_REQUEST; return 0; + } mq_rq = container_of(areq, struct mmc_queue_req, mmc_active); brq = &mq_rq->brq; @@ -1438,6 +1430,10 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) break; case MMC_BLK_NOMEDIUM: goto cmd_abort; + default: + pr_err("%s: Unhandled return value (%d)", + req->rq_disk->disk_name, status); + goto cmd_abort; } if (ret) { @@ -1472,6 +1468,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) int ret; struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; + struct mmc_host *host = card->host; + unsigned long flags; if (req && !mq->mqrq_prev->req) /* claim host only for the first request */ @@ -1486,6 +1484,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) goto out; } + mq->flags &= ~MMC_QUEUE_NEW_REQUEST; if (req && req->cmd_flags & REQ_DISCARD) { /* complete ongoing async transfer before issuing discard */ if (card->host->areq) @@ -1501,11 +1500,16 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) mmc_blk_issue_rw_rq(mq, NULL); ret = mmc_blk_issue_flush(mq, req); } else { + if (!req && host->areq) { + spin_lock_irqsave(&host->context_info.lock, flags); + host->context_info.is_waiting_last_req = true; + spin_unlock_irqrestore(&host->context_info.lock, flags); + } ret = mmc_blk_issue_rw_rq(mq, req); } out: - if (!req) + if (!req && !(mq->flags & MMC_QUEUE_NEW_REQUEST)) /* release host only when there are no more requests */ mmc_release_host(card->host); return ret; diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index d630d9861e7b..5e0971016ac5 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -22,7 +22,6 @@ #define MMC_QUEUE_BOUNCESZ 65536 -#define MMC_QUEUE_SUSPENDED (1 << 0) #define MMC_REQ_SPECIAL_MASK (REQ_DISCARD | REQ_FLUSH) @@ -72,6 +71,10 @@ static int mmc_queue_thread(void *d) set_current_state(TASK_RUNNING); cmd_flags = req ? req->cmd_flags : 0; mq->issue_fn(mq, req); + if (mq->flags & MMC_QUEUE_NEW_REQUEST) { + mq->flags &= ~MMC_QUEUE_NEW_REQUEST; + continue; /* fetch again */ + } /* * Current request becomes previous request @@ -113,6 +116,8 @@ static void mmc_request_fn(struct request_queue *q) { struct mmc_queue *mq = q->queuedata; struct request *req; + unsigned long flags; + struct mmc_context_info *cntx; if (!mq) { while ((req = blk_fetch_request(q)) != NULL) { @@ -122,7 +127,20 @@ static void mmc_request_fn(struct request_queue *q) return; } - if (!mq->mqrq_cur->req && !mq->mqrq_prev->req) + cntx = &mq->card->host->context_info; + if (!mq->mqrq_cur->req && mq->mqrq_prev->req) { + /* + * New MMC request arrived when MMC thread may be + * blocked on the previous request to be complete + * with no current request fetched + */ + spin_lock_irqsave(&cntx->lock, flags); + if (cntx->is_waiting_last_req) { + cntx->is_new_req = true; + wake_up_interruptible(&cntx->wait); + } + spin_unlock_irqrestore(&cntx->lock, flags); + } else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req) wake_up_process(mq->thread); } diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h index d2a1eb4b9f9f..e20c27b2b8b4 100644 --- a/drivers/mmc/card/queue.h +++ b/drivers/mmc/card/queue.h @@ -27,6 +27,9 @@ struct mmc_queue { struct task_struct *thread; struct semaphore thread_sem; unsigned int flags; +#define MMC_QUEUE_SUSPENDED (1 << 0) +#define MMC_QUEUE_NEW_REQUEST (1 << 1) + int (*issue_fn)(struct mmc_queue *, struct request *); void *data; struct request_queue *queue; diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 420cb6753c1e..e219c97a02a4 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -321,6 +321,7 @@ int mmc_add_card(struct mmc_card *card) #ifdef CONFIG_DEBUG_FS mmc_add_card_debugfs(card); #endif + mmc_init_context_info(card->host); ret = device_add(&card->dev); if (ret) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index aaed7687cf09..8b3a1222e665 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -319,11 +319,44 @@ out: } EXPORT_SYMBOL(mmc_start_bkops); +/* + * mmc_wait_data_done() - done callback for data request + * @mrq: done data request + * + * Wakes up mmc context, passed as a callback to host controller driver + */ +static void mmc_wait_data_done(struct mmc_request *mrq) +{ + mrq->host->context_info.is_done_rcv = true; + wake_up_interruptible(&mrq->host->context_info.wait); +} + static void mmc_wait_done(struct mmc_request *mrq) { complete(&mrq->completion); } +/* + *__mmc_start_data_req() - starts data request + * @host: MMC host to start the request + * @mrq: data request to start + * + * Sets the done callback to be called when request is completed by the card. + * Starts data mmc request execution + */ +static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq) +{ + mrq->done = mmc_wait_data_done; + mrq->host = host; + if (mmc_card_removed(host->card)) { + mrq->cmd->error = -ENOMEDIUM; + return -ENOMEDIUM; + } + mmc_start_request(host, mrq); + + return 0; +} + static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) { init_completion(&mrq->completion); @@ -337,6 +370,62 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) return 0; } +/* + * mmc_wait_for_data_req_done() - wait for request completed + * @host: MMC host to prepare the command. + * @mrq: MMC request to wait for + * + * Blocks MMC context till host controller will ack end of data request + * execution or new request notification arrives from the block layer. + * Handles command retries. + * + * Returns enum mmc_blk_status after checking errors. + */ +static int mmc_wait_for_data_req_done(struct mmc_host *host, + struct mmc_request *mrq, + struct mmc_async_req *next_req) +{ + struct mmc_command *cmd; + struct mmc_context_info *context_info = &host->context_info; + int err; + unsigned long flags; + + while (1) { + wait_event_interruptible(context_info->wait, + (context_info->is_done_rcv || + context_info->is_new_req)); + spin_lock_irqsave(&context_info->lock, flags); + context_info->is_waiting_last_req = false; + spin_unlock_irqrestore(&context_info->lock, flags); + if (context_info->is_done_rcv) { + context_info->is_done_rcv = false; + context_info->is_new_req = false; + cmd = mrq->cmd; + if (!cmd->error || !cmd->retries || + mmc_card_removed(host->card)) { + err = host->areq->err_check(host->card, + host->areq); + break; /* return err */ + } else { + pr_info("%s: req failed (CMD%u): %d, retrying...\n", + mmc_hostname(host), + cmd->opcode, cmd->error); + cmd->retries--; + cmd->error = 0; + host->ops->request(host, mrq); + continue; /* wait for done/new event again */ + } + } else if (context_info->is_new_req) { + context_info->is_new_req = false; + if (!next_req) { + err = MMC_BLK_NEW_REQUEST; + break; /* return err */ + } + } + } + return err; +} + static void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq) { @@ -426,8 +515,17 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, mmc_pre_req(host, areq->mrq, !host->areq); if (host->areq) { - mmc_wait_for_req_done(host, host->areq->mrq); - err = host->areq->err_check(host->card, host->areq); + err = mmc_wait_for_data_req_done(host, host->areq->mrq, + areq); + if (err == MMC_BLK_NEW_REQUEST) { + if (error) + *error = err; + /* + * The previous request was not completed, + * nothing to return + */ + return NULL; + } /* * Check BKOPS urgency for each R1 response */ @@ -439,7 +537,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, } if (!err && areq) - start_err = __mmc_start_req(host, areq->mrq); + start_err = __mmc_start_data_req(host, areq->mrq); if (host->areq) mmc_post_req(host, host->areq->mrq, 0); @@ -2581,6 +2679,23 @@ int mmc_pm_notify(struct notifier_block *notify_block, } #endif +/** + * mmc_init_context_info() - init synchronization context + * @host: mmc host + * + * Init struct context_info needed to implement asynchronous + * request mechanism, used by mmc core, host driver and mmc requests + * supplier. + */ +void mmc_init_context_info(struct mmc_host *host) +{ + spin_lock_init(&host->context_info.lock); + host->context_info.is_new_req = false; + host->context_info.is_done_rcv = false; + host->context_info.is_waiting_last_req = false; + init_waitqueue_head(&host->context_info.wait); +} + static int __init mmc_init(void) { int ret; diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 3bdafbca354f..0272b3284b5e 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -76,5 +76,6 @@ void mmc_remove_host_debugfs(struct mmc_host *host); void mmc_add_card_debugfs(struct mmc_card *card); void mmc_remove_card_debugfs(struct mmc_card *card); +void mmc_init_context_info(struct mmc_host *host); #endif diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 5c69315d60cc..be2500a49925 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -187,6 +187,18 @@ struct sdio_func_tuple; #define SDIO_MAX_FUNCS 7 +enum mmc_blk_status { + MMC_BLK_SUCCESS = 0, + MMC_BLK_PARTIAL, + MMC_BLK_CMD_ERR, + MMC_BLK_RETRY, + MMC_BLK_ABORT, + MMC_BLK_DATA_ERR, + MMC_BLK_ECC_ERR, + MMC_BLK_NOMEDIUM, + MMC_BLK_NEW_REQUEST, +}; + /* The number of MMC physical partitions. These consist of: * boot partitions (2), general purpose partitions (4) in MMC v4.4. */ diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 5bf7c2274fcb..495d1336149c 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -120,6 +120,7 @@ struct mmc_data { s32 host_cookie; /* host private data */ }; +struct mmc_host; struct mmc_request { struct mmc_command *sbc; /* SET_BLOCK_COUNT for multiblock */ struct mmc_command *cmd; @@ -128,9 +129,9 @@ struct mmc_request { struct completion completion; void (*done)(struct mmc_request *);/* completion function */ + struct mmc_host *host; }; -struct mmc_host; struct mmc_card; struct mmc_async_req; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index c89a1bb87fa5..523d570f58ad 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -170,6 +170,22 @@ struct mmc_slot { void *handler_priv; }; +/** + * mmc_context_info - synchronization details for mmc context + * @is_done_rcv wake up reason was done request + * @is_new_req wake up reason was new request + * @is_waiting_last_req mmc context waiting for single running request + * @wait wait queue + * @lock lock to protect data fields + */ +struct mmc_context_info { + bool is_done_rcv; + bool is_new_req; + bool is_waiting_last_req; + wait_queue_head_t wait; + spinlock_t lock; +}; + struct regulator; struct mmc_supply { @@ -331,6 +347,7 @@ struct mmc_host { struct dentry *debugfs_root; struct mmc_async_req *areq; /* active async req */ + struct mmc_context_info context_info; /* async synchronization info */ #ifdef CONFIG_FAIL_MMC_REQUEST struct fault_attr fail_mmc_request; -- cgit v1.2.3 From 1a94715d4db7b71f825cd5585bc8d653eae1faf4 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 2 Jan 2013 22:34:49 -0700 Subject: mmc: add BCM2835 driver Add a very simple driver for the BCM2835 SoC, which is used in the Raspberry Pi board. Signed-off-by: Stephen Warren Signed-off-by: Chris Ball --- .../devicetree/bindings/mmc/brcm,bcm2835-sdhci.txt | 18 ++ drivers/mmc/host/Kconfig | 11 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-bcm2835.c | 227 +++++++++++++++++++++ 4 files changed, 257 insertions(+) create mode 100644 Documentation/devicetree/bindings/mmc/brcm,bcm2835-sdhci.txt create mode 100644 drivers/mmc/host/sdhci-bcm2835.c (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/mmc/brcm,bcm2835-sdhci.txt b/Documentation/devicetree/bindings/mmc/brcm,bcm2835-sdhci.txt new file mode 100644 index 000000000000..59476fbdbfa1 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/brcm,bcm2835-sdhci.txt @@ -0,0 +1,18 @@ +Broadcom BCM2835 SDHCI controller + +This file documents differences between the core properties described +by mmc.txt and the properties that represent the BCM2835 controller. + +Required properties: +- compatible : Should be "brcm,bcm2835-sdhci". +- clocks : The clock feeding the SDHCI controller. + +Example: + +sdhci: sdhci { + compatible = "brcm,bcm2835-sdhci"; + reg = <0x7e300000 0x100>; + interrupts = <2 30>; + clocks = <&clk_mmc>; + bus-width = <4>; +}; diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 8d13c6594520..66a54aa68e25 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -241,6 +241,17 @@ config MMC_SDHCI_S3C_DMA YMMV. +config MMC_SDHCI_BCM2835 + tristate "SDHCI platform support for the BCM2835 SD/MMC Controller" + depends on ARCH_BCM2835 + depends on MMC_SDHCI_PLTFM + select MMC_SDHCI_IO_ACCESSORS + help + This selects the BCM2835 SD/MMC controller. If you have a BCM2835 + platform with SD or MMC devices, say Y or M here. + + If unsure, say N. + config MMC_OMAP tristate "TI OMAP Multimedia Card Interface support" depends on ARCH_OMAP diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index e4e218c930bd..d5ea072207ec 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o +obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o ifeq ($(CONFIG_CB710_DEBUG),y) CFLAGS-cb710-mmc += -DDEBUG diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c new file mode 100644 index 000000000000..453825fcc5cf --- /dev/null +++ b/drivers/mmc/host/sdhci-bcm2835.c @@ -0,0 +1,227 @@ +/* + * BCM2835 SDHCI + * Copyright (C) 2012 Stephen Warren + * Based on U-Boot's MMC driver for the BCM2835 by Oleksandr Tymoshenko & me + * Portions of the code there were obviously based on the Linux kernel at: + * git://github.com/raspberrypi/linux.git rpi-3.6.y + * commit f5b930b "Main bcm2708 linux port" signed-off-by Dom Cobley. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include "sdhci-pltfm.h" + +/* + * 400KHz is max freq for card ID etc. Use that as min card clock. We need to + * know the min to enable static calculation of max BCM2835_SDHCI_WRITE_DELAY. + */ +#define MIN_FREQ 400000 + +/* + * The Arasan has a bugette whereby it may lose the content of successive + * writes to registers that are within two SD-card clock cycles of each other + * (a clock domain crossing problem). It seems, however, that the data + * register does not have this problem, which is just as well - otherwise we'd + * have to nobble the DMA engine too. + * + * This should probably be dynamically calculated based on the actual card + * frequency. However, this is the longest we'll have to wait, and doesn't + * seem to slow access down too much, so the added complexity doesn't seem + * worth it for now. + * + * 1/MIN_FREQ is (max) time per tick of eMMC clock. + * 2/MIN_FREQ is time for two ticks. + * Multiply by 1000000 to get uS per two ticks. + * *1000000 for uSecs. + * +1 for hack rounding. + */ +#define BCM2835_SDHCI_WRITE_DELAY (((2 * 1000000) / MIN_FREQ) + 1) + +struct bcm2835_sdhci { + struct clk *clk; + u32 shadow; +}; + +static void bcm2835_sdhci_writel(struct sdhci_host *host, u32 val, int reg) +{ + writel(val, host->ioaddr + reg); + + udelay(BCM2835_SDHCI_WRITE_DELAY); +} + +static inline u32 bcm2835_sdhci_readl(struct sdhci_host *host, int reg) +{ + u32 val = readl(host->ioaddr + reg); + + if (reg == SDHCI_CAPABILITIES) + val |= SDHCI_CAN_VDD_330; + + return val; +} + +static void bcm2835_sdhci_writew(struct sdhci_host *host, u16 val, int reg) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct bcm2835_sdhci *bcm2835_host = pltfm_host->priv; + u32 oldval = (reg == SDHCI_COMMAND) ? bcm2835_host->shadow : + bcm2835_sdhci_readl(host, reg & ~3); + u32 word_num = (reg >> 1) & 1; + u32 word_shift = word_num * 16; + u32 mask = 0xffff << word_shift; + u32 newval = (oldval & ~mask) | (val << word_shift); + + if (reg == SDHCI_TRANSFER_MODE) + bcm2835_host->shadow = newval; + else + bcm2835_sdhci_writel(host, newval, reg & ~3); +} + +static u16 bcm2835_sdhci_readw(struct sdhci_host *host, int reg) +{ + u32 val = bcm2835_sdhci_readl(host, (reg & ~3)); + u32 word_num = (reg >> 1) & 1; + u32 word_shift = word_num * 16; + u32 word = (val >> word_shift) & 0xffff; + + return word; +} + +static void bcm2835_sdhci_writeb(struct sdhci_host *host, u8 val, int reg) +{ + u32 oldval = bcm2835_sdhci_readl(host, reg & ~3); + u32 byte_num = reg & 3; + u32 byte_shift = byte_num * 8; + u32 mask = 0xff << byte_shift; + u32 newval = (oldval & ~mask) | (val << byte_shift); + + bcm2835_sdhci_writel(host, newval, reg & ~3); +} + +static u8 bcm2835_sdhci_readb(struct sdhci_host *host, int reg) +{ + u32 val = bcm2835_sdhci_readl(host, (reg & ~3)); + u32 byte_num = reg & 3; + u32 byte_shift = byte_num * 8; + u32 byte = (val >> byte_shift) & 0xff; + + return byte; +} + +static unsigned int bcm2835_sdhci_get_max_clock(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct bcm2835_sdhci *bcm2835_host = pltfm_host->priv; + + return clk_get_rate(bcm2835_host->clk); +} + +unsigned int bcm2835_sdhci_get_min_clock(struct sdhci_host *host) +{ + return MIN_FREQ; +} + +unsigned int bcm2835_sdhci_get_timeout_clock(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct bcm2835_sdhci *bcm2835_host = pltfm_host->priv; + + return clk_get_rate(bcm2835_host->clk); +} + +static struct sdhci_ops bcm2835_sdhci_ops = { + .write_l = bcm2835_sdhci_writel, + .write_w = bcm2835_sdhci_writew, + .write_b = bcm2835_sdhci_writeb, + .read_l = bcm2835_sdhci_readl, + .read_w = bcm2835_sdhci_readw, + .read_b = bcm2835_sdhci_readb, + .get_max_clock = bcm2835_sdhci_get_max_clock, + .get_min_clock = bcm2835_sdhci_get_min_clock, + .get_timeout_clock = bcm2835_sdhci_get_timeout_clock, +}; + +static struct sdhci_pltfm_data bcm2835_sdhci_pdata = { + .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION, + .ops = &bcm2835_sdhci_ops, +}; + +static int bcm2835_sdhci_probe(struct platform_device *pdev) +{ + struct sdhci_host *host; + struct bcm2835_sdhci *bcm2835_host; + struct sdhci_pltfm_host *pltfm_host; + int ret; + + host = sdhci_pltfm_init(pdev, &bcm2835_sdhci_pdata); + if (IS_ERR(host)) + return PTR_ERR(host); + + bcm2835_host = devm_kzalloc(&pdev->dev, sizeof(*bcm2835_host), + GFP_KERNEL); + if (!bcm2835_host) { + dev_err(mmc_dev(host->mmc), + "failed to allocate bcm2835_sdhci\n"); + return -ENOMEM; + } + + pltfm_host = sdhci_priv(host); + pltfm_host->priv = bcm2835_host; + + bcm2835_host->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(bcm2835_host->clk)) { + ret = PTR_ERR(bcm2835_host->clk); + goto err; + } + + return sdhci_add_host(host); + +err: + sdhci_pltfm_free(pdev); + return ret; +} + +static int bcm2835_sdhci_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); + + sdhci_remove_host(host, dead); + sdhci_pltfm_free(pdev); + + return 0; +} + +static const struct of_device_id bcm2835_sdhci_of_match[] = { + { .compatible = "brcm,bcm2835-sdhci" }, + { } +}; +MODULE_DEVICE_TABLE(of, bcm2835_sdhci_of_match); + +static struct platform_driver bcm2835_sdhci_driver = { + .driver = { + .name = "sdhci-bcm2835", + .owner = THIS_MODULE, + .of_match_table = bcm2835_sdhci_of_match, + .pm = SDHCI_PLTFM_PMOPS, + }, + .probe = bcm2835_sdhci_probe, + .remove = bcm2835_sdhci_remove, +}; +module_platform_driver(bcm2835_sdhci_driver); + +MODULE_DESCRIPTION("BCM2835 SDHCI driver"); +MODULE_AUTHOR("Stephen Warren"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From b0a8dece55ebe792b394e0c73fa365ad24c3ec32 Mon Sep 17 00:00:00 2001 From: Kevin Liu Date: Sat, 5 Jan 2013 17:18:28 +0800 Subject: mmc: sdhci: disable interrupt before free_irq Current code missed disabling interrupts before free irq which is shared. Notice below comments for function free_irq (kernel/irq/manage.c): On a shared IRQ the caller must ensure the interrupt is disabled on the card it drives before calling this function. Original code has below issue during suspend/resume when multiple SD hosts share the same IRQ: 1. Assume there are two hosts (host1 for emmc while host2 for sd) share the same mmc irq. 2. When system suspend, host2 will be suspended before host1. So the sequence is below: step1: irq handler for host2 removed -> step2: irq handler for host1 removed and irq disabled -> ... system suspended ... ... system resumed ... step3: irq enabled and the irq handler for host1 restored -> step4: irq handler for host2 restored 3. So there is the buggy time slot that the irq is enabled but the irq handler for host2 is removed. Then host2 interrupt can be triggered but can't be handled at that moment. Signed-off-by: Jialing Fu Signed-off-by: Kevin Liu Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1b97fe2d70ab..1165376592b9 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2487,6 +2487,7 @@ int sdhci_suspend_host(struct sdhci_host *host) return ret; } + sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); free_irq(host->irq, host); return ret; @@ -3142,6 +3143,7 @@ int sdhci_add_host(struct sdhci_host *host) #ifdef SDHCI_USE_LEDS_CLASS reset: sdhci_reset(host, SDHCI_RESET_ALL); + sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); free_irq(host->irq, host); #endif untasklet: @@ -3184,6 +3186,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) if (!dead) sdhci_reset(host, SDHCI_RESET_ALL); + sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); free_irq(host->irq, host); del_timer_sync(&host->timer); -- cgit v1.2.3 From ad080d7916a4701fda29a442a69be4fd54d19a2e Mon Sep 17 00:00:00 2001 From: Kevin Liu Date: Sat, 5 Jan 2013 17:21:33 +0800 Subject: mmc: sdhci: add IRQ wake up support Don't disable SD Host IRQ during suspend if it is wake up source. Enable wakeup event during suspend. Signed-off-by: Jialing Fu Signed-off-by: Kevin Liu Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 60 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1165376592b9..b93076764ddd 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2458,6 +2458,32 @@ out: \*****************************************************************************/ #ifdef CONFIG_PM +void sdhci_enable_irq_wakeups(struct sdhci_host *host) +{ + u8 val; + u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE + | SDHCI_WAKE_ON_INT; + + val = sdhci_readb(host, SDHCI_WAKE_UP_CONTROL); + val |= mask ; + /* Avoid fake wake up */ + if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) + val &= ~(SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE); + sdhci_writeb(host, val, SDHCI_WAKE_UP_CONTROL); +} +EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups); + +void sdhci_disable_irq_wakeups(struct sdhci_host *host) +{ + u8 val; + u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE + | SDHCI_WAKE_ON_INT; + + val = sdhci_readb(host, SDHCI_WAKE_UP_CONTROL); + val &= ~mask; + sdhci_writeb(host, val, SDHCI_WAKE_UP_CONTROL); +} +EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups); int sdhci_suspend_host(struct sdhci_host *host) { @@ -2487,9 +2513,13 @@ int sdhci_suspend_host(struct sdhci_host *host) return ret; } - sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); - free_irq(host->irq, host); - + if (!device_may_wakeup(mmc_dev(host->mmc))) { + sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); + free_irq(host->irq, host); + } else { + sdhci_enable_irq_wakeups(host); + enable_irq_wake(host->irq); + } return ret; } @@ -2504,10 +2534,15 @@ int sdhci_resume_host(struct sdhci_host *host) host->ops->enable_dma(host); } - ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, - mmc_hostname(host->mmc), host); - if (ret) - return ret; + if (!device_may_wakeup(mmc_dev(host->mmc))) { + ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, + mmc_hostname(host->mmc), host); + if (ret) + return ret; + } else { + sdhci_disable_irq_wakeups(host); + disable_irq_wake(host->irq); + } if ((host->mmc->pm_flags & MMC_PM_KEEP_POWER) && (host->quirks2 & SDHCI_QUIRK2_HOST_OFF_CARD_ON)) { @@ -2535,17 +2570,6 @@ int sdhci_resume_host(struct sdhci_host *host) } EXPORT_SYMBOL_GPL(sdhci_resume_host); - -void sdhci_enable_irq_wakeups(struct sdhci_host *host) -{ - u8 val; - val = sdhci_readb(host, SDHCI_WAKE_UP_CONTROL); - val |= SDHCI_WAKE_ON_INT; - sdhci_writeb(host, val, SDHCI_WAKE_UP_CONTROL); -} - -EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups); - #endif /* CONFIG_PM */ #ifdef CONFIG_PM_RUNTIME -- cgit v1.2.3 From 740b7a44aedbe8d916ce21b636f78467f5da1c59 Mon Sep 17 00:00:00 2001 From: Kevin Liu Date: Mon, 14 Jan 2013 14:38:53 -0500 Subject: mmc: sdhci-pxav3: add IRQ wake up support [cjb: The MMP3 architecture requires a registered interrupt to retire wfi when waking from suspend.] Signed-off-by: Jialing Fu Signed-off-by: Kevin Liu Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pxav3.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index b7ee7761bc26..3d20c10fc571 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -311,6 +311,13 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); + if (pdata->pm_caps & MMC_PM_KEEP_POWER) { + device_init_wakeup(&pdev->dev, 1); + host->mmc->pm_flags |= MMC_PM_WAKE_SDIO_IRQ; + } else { + device_init_wakeup(&pdev->dev, 0); + } + return 0; err_add_host: -- cgit v1.2.3 From 8213af3b7a4059f13ad8b5beb3e6d69d9301f27d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 7 Jan 2013 16:31:08 +0200 Subject: mmc: sdhci: introduce sdhci_update_clock helper to re-enable clock There are three places where same piece of code is used. Let's split it to a separate function. Signed-off-by: Andy Shevchenko Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index b93076764ddd..336ab06aeb2f 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1189,6 +1189,15 @@ out: host->clock = clock; } +static inline void sdhci_update_clock(struct sdhci_host *host) +{ + unsigned int clock; + + clock = host->clock; + host->clock = 0; + sdhci_set_clock(host, clock); +} + static int sdhci_set_power(struct sdhci_host *host, unsigned short power) { u8 pwr = 0; @@ -1418,7 +1427,6 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) if (host->version >= SDHCI_SPEC_300) { u16 clk, ctrl_2; - unsigned int clock; /* In case of UHS-I modes, set High Speed Enable */ if ((ios->timing == MMC_TIMING_MMC_HS200) || @@ -1458,9 +1466,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); /* Re-enable SD Clock */ - clock = host->clock; - host->clock = 0; - sdhci_set_clock(host, clock); + sdhci_update_clock(host); } @@ -1491,9 +1497,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) } /* Re-enable SD Clock */ - clock = host->clock; - host->clock = 0; - sdhci_set_clock(host, clock); + sdhci_update_clock(host); } else sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); @@ -2083,14 +2087,9 @@ static void sdhci_tasklet_finish(unsigned long param) (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) { /* Some controllers need this kick or reset won't work here */ - if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) { - unsigned int clock; - + if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) /* This is to force an update */ - clock = host->clock; - host->clock = 0; - sdhci_set_clock(host, clock); - } + sdhci_update_clock(host); /* Spec says we should do both at the same time, but Ricoh controllers do not like that. */ -- cgit v1.2.3 From a043859043042e8bc6f68b40782e3b8c9d10a28d Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 15 Jan 2013 23:19:54 +0800 Subject: mmc: sdhci-esdhc-imx: remove ESDHC_CD_GPIO handling from IO accessory With commit 9444e07 (mmc: remove unncessary mmc_gpio_free_cd() call from slot-gpio users) in place, the ESDHC_CD_GPIO handling in IO accessories becomes unnecessary. Remove it. Signed-off-by: Shawn Guo Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index dd7fcc137644..ae68bc965ab5 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -150,16 +150,6 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) u32 val = readl(host->ioaddr + reg); - if (unlikely(reg == SDHCI_PRESENT_STATE)) { - /* - * After SDHCI core gets improved to never query - * SDHCI_CARD_PRESENT state in GPIO case, we can - * remove this check. - */ - if (boarddata->cd_type == ESDHC_CD_GPIO) - val &= ~SDHCI_CARD_PRESENT; - } - if (unlikely(reg == SDHCI_CAPABILITIES)) { /* In FSL esdhc IC module, only bit20 is used to indicate the * ADMA2 capability of esdhc, but this bit is messed up on @@ -192,13 +182,6 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg) u32 data; if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) { - if (boarddata->cd_type == ESDHC_CD_GPIO) - /* - * These interrupts won't work with a custom - * card_detect gpio (only applied to mx25/35) - */ - val &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT); - if (val & SDHCI_INT_CARD_INT) { /* * Clear and then set D3CD bit to avoid missing the -- cgit v1.2.3 From 3724482d4ce4790d4534bcecebfda2c7133244c7 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Wed, 16 Jan 2013 14:13:57 +0100 Subject: mmc: mvsdio: use slot-gpio infrastructure for write protect gpio The MMC core subsystem provides in drivers/mmc/core/slot-gpio.c a nice set of helper functions to simplify the management of the write protect GPIO in MMC host drivers. This patch migrates the mvsdio driver to using those helpers, which will make the ->probe() code simpler, and therefore ease the process of adding a Device Tree binding for this driver. Signed-off-by: Thomas Petazzoni Signed-off-by: Andrew Lunn Tested-by: Stefan Peter Tested-by: Florian Fainelli Signed-off-by: Jason Cooper Signed-off-by: Chris Ball --- drivers/mmc/host/mvsdio.c | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index f8dd36102949..c6dc8fd696ab 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -52,7 +53,6 @@ struct mvsd_host { struct device *dev; struct clk *clk; int gpio_card_detect; - int gpio_write_protect; }; #define mvsd_write(offs, val) writel(val, iobase + (offs)) @@ -564,20 +564,6 @@ static void mvsd_enable_sdio_irq(struct mmc_host *mmc, int enable) spin_unlock_irqrestore(&host->lock, flags); } -static int mvsd_get_ro(struct mmc_host *mmc) -{ - struct mvsd_host *host = mmc_priv(mmc); - - if (host->gpio_write_protect) - return gpio_get_value(host->gpio_write_protect); - - /* - * Board doesn't support read only detection; let the mmc core - * decide what to do. - */ - return -ENOSYS; -} - static void mvsd_power_up(struct mvsd_host *host) { void __iomem *iobase = host->base; @@ -674,7 +660,7 @@ static void mvsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) static const struct mmc_host_ops mvsd_ops = { .request = mvsd_request, - .get_ro = mvsd_get_ro, + .get_ro = mmc_gpio_get_ro, .set_ios = mvsd_set_ios, .enable_sdio_irq = mvsd_enable_sdio_irq, }; @@ -793,15 +779,7 @@ static int __init mvsd_probe(struct platform_device *pdev) if (!host->gpio_card_detect) mmc->caps |= MMC_CAP_NEEDS_POLL; - if (mvsd_data->gpio_write_protect) { - ret = devm_gpio_request_one(&pdev->dev, - mvsd_data->gpio_write_protect, - GPIOF_IN, DRIVER_NAME " wp"); - if (ret == 0) { - host->gpio_write_protect = - mvsd_data->gpio_write_protect; - } - } + mmc_gpio_request_ro(mmc, mvsd_data->gpio_write_protect); setup_timer(&host->timer, mvsd_timeout_timer, (unsigned long)host); platform_set_drvdata(pdev, mmc); @@ -820,6 +798,7 @@ static int __init mvsd_probe(struct platform_device *pdev) out: if (mmc) { + mmc_gpio_free_ro(mmc); if (!IS_ERR(host->clk)) clk_disable_unprepare(host->clk); mmc_free_host(mmc); @@ -834,6 +813,7 @@ static int __exit mvsd_remove(struct platform_device *pdev) struct mvsd_host *host = mmc_priv(mmc); + mmc_gpio_free_ro(mmc); mmc_remove_host(mmc); del_timer_sync(&host->timer); mvsd_power_down(host); -- cgit v1.2.3 From 07728b77c03dc0721daaf551976d95e6f714af1a Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Wed, 16 Jan 2013 14:13:58 +0100 Subject: mmc: mvsdio: use slot-gpio for card detect gpio The MMC core subsystem provides in drivers/mmc/core/slot-gpio.c a nice set of helper functions to simplify the management of the card detect GPIO in MMC host drivers. This patch migrates the mvsdio driver to using those helpers, which will make the ->probe() code simpler, and therefore ease the process of adding a Device Tree binding for this driver. Signed-off-by: Thomas Petazzoni Signed-off-by: Andrew Lunn Tested-by: Stefan Peter Tested-by: Florian Fainelli Signed-off-by: Jason Cooper Signed-off-by: Chris Ball --- drivers/mmc/host/mvsdio.c | 39 +++++++++------------------------------ 1 file changed, 9 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index c6dc8fd696ab..704b7a3fe873 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -52,7 +52,6 @@ struct mvsd_host { struct mmc_host *mmc; struct device *dev; struct clk *clk; - int gpio_card_detect; }; #define mvsd_write(offs, val) writel(val, iobase + (offs)) @@ -538,13 +537,6 @@ static void mvsd_timeout_timer(unsigned long data) mmc_request_done(host->mmc, mrq); } -static irqreturn_t mvsd_card_detect_irq(int irq, void *dev) -{ - struct mvsd_host *host = dev; - mmc_detect_change(host->mmc, msecs_to_jiffies(100)); - return IRQ_HANDLED; -} - static void mvsd_enable_sdio_irq(struct mmc_host *mmc, int enable) { struct mvsd_host *host = mmc_priv(mmc); @@ -757,26 +749,11 @@ static int __init mvsd_probe(struct platform_device *pdev) if (!IS_ERR(host->clk)) clk_prepare_enable(host->clk); - if (mvsd_data->gpio_card_detect) { - ret = devm_gpio_request_one(&pdev->dev, - mvsd_data->gpio_card_detect, - GPIOF_IN, DRIVER_NAME " cd"); - if (ret == 0) { - irq = gpio_to_irq(mvsd_data->gpio_card_detect); - ret = devm_request_irq(&pdev->dev, irq, - mvsd_card_detect_irq, - IRQ_TYPE_EDGE_RISING | - IRQ_TYPE_EDGE_FALLING, - DRIVER_NAME " cd", host); - if (ret == 0) - host->gpio_card_detect = - mvsd_data->gpio_card_detect; - else - devm_gpio_free(&pdev->dev, - mvsd_data->gpio_card_detect); - } - } - if (!host->gpio_card_detect) + if (gpio_is_valid(mvsd_data->gpio_card_detect)) { + ret = mmc_gpio_request_cd(mmc, mvsd_data->gpio_card_detect); + if (ret) + goto out; + } else mmc->caps |= MMC_CAP_NEEDS_POLL; mmc_gpio_request_ro(mmc, mvsd_data->gpio_write_protect); @@ -789,15 +766,16 @@ static int __init mvsd_probe(struct platform_device *pdev) pr_notice("%s: %s driver initialized, ", mmc_hostname(mmc), DRIVER_NAME); - if (host->gpio_card_detect) + if (!(mmc->caps & MMC_CAP_NEEDS_POLL)) printk("using GPIO %d for card detection\n", - host->gpio_card_detect); + mvsd_data->gpio_card_detect); else printk("lacking card detect (fall back to polling)\n"); return 0; out: if (mmc) { + mmc_gpio_free_cd(mmc); mmc_gpio_free_ro(mmc); if (!IS_ERR(host->clk)) clk_disable_unprepare(host->clk); @@ -813,6 +791,7 @@ static int __exit mvsd_remove(struct platform_device *pdev) struct mvsd_host *host = mmc_priv(mmc); + mmc_gpio_free_cd(mmc); mmc_gpio_free_ro(mmc); mmc_remove_host(mmc); del_timer_sync(&host->timer); -- cgit v1.2.3 From 111936ff3bc33585b475c1033fc98cd6b3370a74 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Wed, 16 Jan 2013 14:13:59 +0100 Subject: mmc: mvsdio: implement a Device Tree binding This patch adds a simple Device Tree binding for the mvsdio driver, as well as the necessary documentation for it. Compatibility with non-DT platforms is preserved, by keeping the platform_data based initialization. We introduce a small difference between non-DT and DT platforms: DT platforms are required to provide a clocks = <...> property, which the driver uses to get the frequency of the clock that goes to the SDIO IP. The behaviour on non-DT platforms is kept unchanged: a clock reference is not mandatory, but the clock frequency must be passed in the "clock" field of the mvsdio_platform_data structure. Signed-off-by: Thomas Petazzoni Signed-off-by: Andrew Lunn Tested-by: Stefan Peter Tested-by: Florian Fainelli Signed-off-by: Jason Cooper Signed-off-by: Chris Ball --- .../devicetree/bindings/mmc/orion-sdio.txt | 17 ++++++ drivers/mmc/host/mvsdio.c | 64 +++++++++++++++++----- 2 files changed, 66 insertions(+), 15 deletions(-) create mode 100644 Documentation/devicetree/bindings/mmc/orion-sdio.txt (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/mmc/orion-sdio.txt b/Documentation/devicetree/bindings/mmc/orion-sdio.txt new file mode 100644 index 000000000000..84f0ebd67a13 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/orion-sdio.txt @@ -0,0 +1,17 @@ +* Marvell orion-sdio controller + +This file documents differences between the core properties in mmc.txt +and the properties used by the orion-sdio driver. + +- compatible: Should be "marvell,orion-sdio" +- clocks: reference to the clock of the SDIO interface + +Example: + + mvsdio@d00d4000 { + compatible = "marvell,orion-sdio"; + reg = <0xd00d4000 0x200>; + interrupts = <54>; + clocks = <&gateclk 17>; + status = "disabled"; + }; diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 704b7a3fe873..78d3abf837c2 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -681,17 +683,17 @@ mv_conf_mbus_windows(struct mvsd_host *host, static int __init mvsd_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; struct mmc_host *mmc = NULL; struct mvsd_host *host = NULL; - const struct mvsdio_platform_data *mvsd_data; const struct mbus_dram_target_info *dram; struct resource *r; int ret, irq; + int gpio_card_detect, gpio_write_protect; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); - mvsd_data = pdev->dev.platform_data; - if (!r || irq < 0 || !mvsd_data) + if (!r || irq < 0) return -ENXIO; mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev); @@ -703,8 +705,39 @@ static int __init mvsd_probe(struct platform_device *pdev) host = mmc_priv(mmc); host->mmc = mmc; host->dev = &pdev->dev; - host->base_clock = mvsd_data->clock / 2; - host->clk = ERR_PTR(-EINVAL); + + /* + * Some non-DT platforms do not pass a clock, and the clock + * frequency is passed through platform_data. On DT platforms, + * a clock must always be passed, even if there is no gatable + * clock associated to the SDIO interface (it can simply be a + * fixed rate clock). + */ + host->clk = devm_clk_get(&pdev->dev, NULL); + if (!IS_ERR(host->clk)) + clk_prepare_enable(host->clk); + + if (np) { + if (IS_ERR(host->clk)) { + dev_err(&pdev->dev, "DT platforms must have a clock associated\n"); + ret = -EINVAL; + goto out; + } + + host->base_clock = clk_get_rate(host->clk) / 2; + gpio_card_detect = of_get_named_gpio(np, "cd-gpios", 0); + gpio_write_protect = of_get_named_gpio(np, "wp-gpios", 0); + } else { + const struct mvsdio_platform_data *mvsd_data; + mvsd_data = pdev->dev.platform_data; + if (!mvsd_data) { + ret = -ENXIO; + goto out; + } + host->base_clock = mvsd_data->clock / 2; + gpio_card_detect = mvsd_data->gpio_card_detect; + gpio_write_protect = mvsd_data->gpio_write_protect; + } mmc->ops = &mvsd_ops; @@ -743,20 +776,14 @@ static int __init mvsd_probe(struct platform_device *pdev) goto out; } - /* Not all platforms can gate the clock, so it is not - an error if the clock does not exists. */ - host->clk = devm_clk_get(&pdev->dev, NULL); - if (!IS_ERR(host->clk)) - clk_prepare_enable(host->clk); - - if (gpio_is_valid(mvsd_data->gpio_card_detect)) { - ret = mmc_gpio_request_cd(mmc, mvsd_data->gpio_card_detect); + if (gpio_is_valid(gpio_card_detect)) { + ret = mmc_gpio_request_cd(mmc, gpio_card_detect); if (ret) goto out; } else mmc->caps |= MMC_CAP_NEEDS_POLL; - mmc_gpio_request_ro(mmc, mvsd_data->gpio_write_protect); + mmc_gpio_request_ro(mmc, gpio_write_protect); setup_timer(&host->timer, mvsd_timeout_timer, (unsigned long)host); platform_set_drvdata(pdev, mmc); @@ -768,7 +795,7 @@ static int __init mvsd_probe(struct platform_device *pdev) mmc_hostname(mmc), DRIVER_NAME); if (!(mmc->caps & MMC_CAP_NEEDS_POLL)) printk("using GPIO %d for card detection\n", - mvsd_data->gpio_card_detect); + gpio_card_detect); else printk("lacking card detect (fall back to polling)\n"); return 0; @@ -832,12 +859,19 @@ static int mvsd_resume(struct platform_device *dev) #define mvsd_resume NULL #endif +static const struct of_device_id mvsdio_dt_ids[] = { + { .compatible = "marvell,orion-sdio" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mvsdio_dt_ids); + static struct platform_driver mvsd_driver = { .remove = __exit_p(mvsd_remove), .suspend = mvsd_suspend, .resume = mvsd_resume, .driver = { .name = DRIVER_NAME, + .of_match_table = mvsdio_dt_ids, }, }; -- cgit v1.2.3 From 33f6984ecefb9b84f1b4d1d3b9022731bb8b62d0 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 7 Feb 2013 13:32:46 -0300 Subject: [media] em28xx: fix analog streaming with USB bulk transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the conversion to videobuf2, some unnecessary calls of em28xx_set_alternate() have been removed. It is now called at analog streaming start only. This has unveiled a bug that causes USB bulk transfers to fail with all urbs having status -EVOERFLOW. The reason is, that for bulk transfers usb_set_interface() needs to be called even if the previous alt setting was the same (side note: bulk transfers seem to work only with alt=0). While it seems to be NOT necessary for isoc transfers, it's reasonable to just call usb_set_interface() unconditionally in em28xx_set_alternate(). Also add a comment that explains the issue to prevent regressions in the future. Cc: stable@vger.kernel.org # for 3.8 Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 43 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index ee00f9e23420..aaedd11791f2 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -813,12 +813,12 @@ int em28xx_resolution_set(struct em28xx *dev) /* Set USB alternate setting for analog video */ int em28xx_set_alternate(struct em28xx *dev) { - int errCode, prev_alt = dev->alt; + int errCode; int i; unsigned int min_pkt_size = dev->width * 2 + 4; /* NOTE: for isoc transfers, only alt settings > 0 are allowed - for bulk transfers, use alt=0 as default value */ + bulk transfers seem to work only with alt=0 ! */ dev->alt = 0; if ((alt > 0) && (alt < dev->num_alt)) { em28xx_coredbg("alternate forced to %d\n", dev->alt); @@ -849,25 +849,26 @@ int em28xx_set_alternate(struct em28xx *dev) } set_alt: - if (dev->alt != prev_alt) { - if (dev->analog_xfer_bulk) { - dev->max_pkt_size = 512; /* USB 2.0 spec */ - dev->packet_multiplier = EM28XX_BULK_PACKET_MULTIPLIER; - } else { /* isoc */ - em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", - min_pkt_size, dev->alt); - dev->max_pkt_size = - dev->alt_max_pkt_size_isoc[dev->alt]; - dev->packet_multiplier = EM28XX_NUM_ISOC_PACKETS; - } - em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", - dev->alt, dev->max_pkt_size); - errCode = usb_set_interface(dev->udev, 0, dev->alt); - if (errCode < 0) { - em28xx_errdev("cannot change alternate number to %d (error=%i)\n", - dev->alt, errCode); - return errCode; - } + /* NOTE: for bulk transfers, we need to call usb_set_interface() + * even if the previous settings were the same. Otherwise streaming + * fails with all urbs having status = -EOVERFLOW ! */ + if (dev->analog_xfer_bulk) { + dev->max_pkt_size = 512; /* USB 2.0 spec */ + dev->packet_multiplier = EM28XX_BULK_PACKET_MULTIPLIER; + } else { /* isoc */ + em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", + min_pkt_size, dev->alt); + dev->max_pkt_size = + dev->alt_max_pkt_size_isoc[dev->alt]; + dev->packet_multiplier = EM28XX_NUM_ISOC_PACKETS; + } + em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", + dev->alt, dev->max_pkt_size); + errCode = usb_set_interface(dev->udev, 0, dev->alt); + if (errCode < 0) { + em28xx_errdev("cannot change alternate number to %d (error=%i)\n", + dev->alt, errCode); + return errCode; } return 0; } -- cgit v1.2.3 From cfb046cb800ba306b211fbbe4ac633486e11055f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 9 Feb 2013 05:40:10 -0300 Subject: [media] cx18/ivtv: fix regression: remove __init from a non-init function Commits 5e6e81b2890db3969527772a8350825a85c22d5c (cx18) and 2aebbf6737212265b917ed27c875c59d3037110a (ivtv) added an __init annotation to the cx18-alsa-load and ivtv-alsa-load functions. However, these functions are called *after* initialization by the main cx18/ivtv driver. By that time the memory containing those functions is already freed and your machine goes BOOM. Cc: stable@vger.kernel.org # for 3.8 Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx18/cx18-alsa-main.c | 2 +- drivers/media/pci/cx18/cx18-alsa-pcm.h | 2 +- drivers/media/pci/ivtv/ivtv-alsa-main.c | 2 +- drivers/media/pci/ivtv/ivtv-alsa-pcm.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/cx18/cx18-alsa-main.c b/drivers/media/pci/cx18/cx18-alsa-main.c index 8e971ff60588..b2c8c3439fea 100644 --- a/drivers/media/pci/cx18/cx18-alsa-main.c +++ b/drivers/media/pci/cx18/cx18-alsa-main.c @@ -197,7 +197,7 @@ err_exit: return ret; } -static int __init cx18_alsa_load(struct cx18 *cx) +static int cx18_alsa_load(struct cx18 *cx) { struct v4l2_device *v4l2_dev = &cx->v4l2_dev; struct cx18_stream *s; diff --git a/drivers/media/pci/cx18/cx18-alsa-pcm.h b/drivers/media/pci/cx18/cx18-alsa-pcm.h index d26e51f94577..e2b2c5b01215 100644 --- a/drivers/media/pci/cx18/cx18-alsa-pcm.h +++ b/drivers/media/pci/cx18/cx18-alsa-pcm.h @@ -20,7 +20,7 @@ * 02111-1307 USA */ -int __init snd_cx18_pcm_create(struct snd_cx18_card *cxsc); +int snd_cx18_pcm_create(struct snd_cx18_card *cxsc); /* Used by cx18-mailbox to announce the PCM data to the module */ void cx18_alsa_announce_pcm_data(struct snd_cx18_card *card, u8 *pcm_data, diff --git a/drivers/media/pci/ivtv/ivtv-alsa-main.c b/drivers/media/pci/ivtv/ivtv-alsa-main.c index 4a221c693995..e970cface70e 100644 --- a/drivers/media/pci/ivtv/ivtv-alsa-main.c +++ b/drivers/media/pci/ivtv/ivtv-alsa-main.c @@ -205,7 +205,7 @@ err_exit: return ret; } -static int __init ivtv_alsa_load(struct ivtv *itv) +static int ivtv_alsa_load(struct ivtv *itv) { struct v4l2_device *v4l2_dev = &itv->v4l2_dev; struct ivtv_stream *s; diff --git a/drivers/media/pci/ivtv/ivtv-alsa-pcm.h b/drivers/media/pci/ivtv/ivtv-alsa-pcm.h index 23dfe0d12400..186814e0b2d4 100644 --- a/drivers/media/pci/ivtv/ivtv-alsa-pcm.h +++ b/drivers/media/pci/ivtv/ivtv-alsa-pcm.h @@ -20,4 +20,4 @@ * 02111-1307 USA */ -int __init snd_ivtv_pcm_create(struct snd_ivtv_card *itvsc); +int snd_ivtv_pcm_create(struct snd_ivtv_card *itvsc); -- cgit v1.2.3 From cdc69ae98b3d8ff337c564e16f497c8816d1a51b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 11 Feb 2013 19:38:59 -0200 Subject: Revert "[media] fc0011: Return early, if the frequency is already tuned" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit a92591a7112042f92b609be42bc332d989776e9b. From: Michael Büsch To: linux-media@vger.kernel.org Cc: mchehab@redhat.com Subject: Re: [git:v4l-dvb/for_v3.9] [media] fc0011: Return early, if the frequency is already tuned Date: Mon, 11 Feb 2013 21:59:19 +0100 Can you please revert this one again? It might cause issues if the dvb device is reset/reinitialized. Requested-by: Michael Büsch Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0011.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/tuners/fc0011.c b/drivers/media/tuners/fc0011.c index 18caab11c94b..3932aa81e18c 100644 --- a/drivers/media/tuners/fc0011.c +++ b/drivers/media/tuners/fc0011.c @@ -187,9 +187,6 @@ static int fc0011_set_params(struct dvb_frontend *fe) u8 fa, fp, vco_sel, vco_cal; u8 regs[FC11_NR_REGS] = { }; - if (priv->frequency == p->frequency) - return 0; - regs[FC11_REG_7] = 0x0F; regs[FC11_REG_8] = 0x3E; regs[FC11_REG_10] = 0xB8; -- cgit v1.2.3 From 372e722ea4dd4ca11c3d04845e11cbc15f32144c Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sun, 3 Feb 2013 01:29:29 +0900 Subject: gpiolib: use descriptors internally Make sure gpiolib works internally with descriptors and (chip, offset) pairs instead of using the global integer namespace. This prepares the ground for the removal of the global gpio_desc[] array and the introduction of the descriptor-based GPIO API. Signed-off-by: Alexandre Courbot Reviewed-by: Linus Walleij [grant.likely: Squash in fix for link error when CONFIG_SYSFS=n] Signed-off-by: Grant Likely --- drivers/gpio/gpiolib.c | 514 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 338 insertions(+), 176 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 8ccf68bb8a40..866431f674c3 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -78,6 +78,28 @@ static LIST_HEAD(gpio_chips); static DEFINE_IDR(dirent_idr); #endif +/* + * Internal gpiod_* API using descriptors instead of the integer namespace. + * Most of this should eventually go public. + */ +static int gpiod_request(struct gpio_desc *desc, const char *label); +static void gpiod_free(struct gpio_desc *desc); +static int gpiod_direction_input(struct gpio_desc *desc); +static int gpiod_direction_output(struct gpio_desc *desc, int value); +static int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce); +static int gpiod_get_value_cansleep(struct gpio_desc *desc); +static void gpiod_set_value_cansleep(struct gpio_desc *desc, int value); +static int gpiod_get_value(struct gpio_desc *desc); +static void gpiod_set_value(struct gpio_desc *desc, int value); +static int gpiod_cansleep(struct gpio_desc *desc); +static int gpiod_to_irq(struct gpio_desc *desc); +static int gpiod_export(struct gpio_desc *desc, bool direction_may_change); +static int gpiod_export_link(struct device *dev, const char *name, + struct gpio_desc *desc); +static int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value); +static void gpiod_unexport(struct gpio_desc *desc); + + static inline void desc_set_label(struct gpio_desc *d, const char *label) { #ifdef CONFIG_DEBUG_FS @@ -85,6 +107,36 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label) #endif } +/* + * Return the GPIO number of the passed descriptor relative to its chip + */ +static int gpio_chip_hwgpio(const struct gpio_desc *desc) +{ + return (desc - &gpio_desc[0]) - desc->chip->base; +} + +/** + * Convert a GPIO number to its descriptor + */ +static struct gpio_desc *gpio_to_desc(unsigned gpio) +{ + if (WARN(!gpio_is_valid(gpio), "invalid GPIO %d\n", gpio)) + return NULL; + else + return &gpio_desc[gpio]; +} + +/** + * Convert a GPIO descriptor to the integer namespace. + * This should disappear in the future but is needed since we still + * use GPIO numbers for error messages and sysfs nodes + */ +static int desc_to_gpio(const struct gpio_desc *desc) +{ + return desc - &gpio_desc[0]; +} + + /* Warn when drivers omit gpio_request() calls -- legal but ill-advised * when setting direction, and otherwise illegal. Until board setup code * and drivers use explicit requests everywhere (which won't happen when @@ -96,10 +148,10 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label) * only "legal" in the sense that (old) code using it won't break yet, * but instead only triggers a WARN() stack dump. */ -static int gpio_ensure_requested(struct gpio_desc *desc, unsigned offset) +static int gpio_ensure_requested(struct gpio_desc *desc) { const struct gpio_chip *chip = desc->chip; - const int gpio = chip->base + offset; + const int gpio = desc_to_gpio(desc); if (WARN(test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0, "autorequest GPIO-%d\n", gpio)) { @@ -118,9 +170,14 @@ static int gpio_ensure_requested(struct gpio_desc *desc, unsigned offset) } /* caller holds gpio_lock *OR* gpio is marked as requested */ +static struct gpio_chip *gpiod_to_chip(struct gpio_desc *desc) +{ + return desc->chip; +} + struct gpio_chip *gpio_to_chip(unsigned gpio) { - return gpio_desc[gpio].chip; + return gpiod_to_chip(gpio_to_desc(gpio)); } /* dynamic allocation of GPIOs, e.g. on a hotplugged device */ @@ -148,19 +205,19 @@ static int gpiochip_find_base(int ngpio) } /* caller ensures gpio is valid and requested, chip->get_direction may sleep */ -static int gpio_get_direction(unsigned gpio) +static int gpiod_get_direction(struct gpio_desc *desc) { struct gpio_chip *chip; - struct gpio_desc *desc = &gpio_desc[gpio]; + unsigned offset; int status = -EINVAL; - chip = gpio_to_chip(gpio); - gpio -= chip->base; + chip = gpiod_to_chip(desc); + offset = gpio_chip_hwgpio(desc); if (!chip->get_direction) return status; - status = chip->get_direction(chip, gpio); + status = chip->get_direction(chip, offset); if (status > 0) { /* GPIOF_DIR_IN, or other positive */ status = 1; @@ -204,8 +261,7 @@ static DEFINE_MUTEX(sysfs_lock); static ssize_t gpio_direction_show(struct device *dev, struct device_attribute *attr, char *buf) { - const struct gpio_desc *desc = dev_get_drvdata(dev); - unsigned gpio = desc - gpio_desc; + struct gpio_desc *desc = dev_get_drvdata(dev); ssize_t status; mutex_lock(&sysfs_lock); @@ -213,7 +269,7 @@ static ssize_t gpio_direction_show(struct device *dev, if (!test_bit(FLAG_EXPORT, &desc->flags)) { status = -EIO; } else { - gpio_get_direction(gpio); + gpiod_get_direction(desc); status = sprintf(buf, "%s\n", test_bit(FLAG_IS_OUT, &desc->flags) ? "out" : "in"); @@ -226,8 +282,7 @@ static ssize_t gpio_direction_show(struct device *dev, static ssize_t gpio_direction_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - const struct gpio_desc *desc = dev_get_drvdata(dev); - unsigned gpio = desc - gpio_desc; + struct gpio_desc *desc = dev_get_drvdata(dev); ssize_t status; mutex_lock(&sysfs_lock); @@ -235,11 +290,11 @@ static ssize_t gpio_direction_store(struct device *dev, if (!test_bit(FLAG_EXPORT, &desc->flags)) status = -EIO; else if (sysfs_streq(buf, "high")) - status = gpio_direction_output(gpio, 1); + status = gpiod_direction_output(desc, 1); else if (sysfs_streq(buf, "out") || sysfs_streq(buf, "low")) - status = gpio_direction_output(gpio, 0); + status = gpiod_direction_output(desc, 0); else if (sysfs_streq(buf, "in")) - status = gpio_direction_input(gpio); + status = gpiod_direction_input(desc); else status = -EINVAL; @@ -253,8 +308,7 @@ static /* const */ DEVICE_ATTR(direction, 0644, static ssize_t gpio_value_show(struct device *dev, struct device_attribute *attr, char *buf) { - const struct gpio_desc *desc = dev_get_drvdata(dev); - unsigned gpio = desc - gpio_desc; + struct gpio_desc *desc = dev_get_drvdata(dev); ssize_t status; mutex_lock(&sysfs_lock); @@ -264,7 +318,7 @@ static ssize_t gpio_value_show(struct device *dev, } else { int value; - value = !!gpio_get_value_cansleep(gpio); + value = !!gpiod_get_value_cansleep(desc); if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; @@ -278,8 +332,7 @@ static ssize_t gpio_value_show(struct device *dev, static ssize_t gpio_value_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - const struct gpio_desc *desc = dev_get_drvdata(dev); - unsigned gpio = desc - gpio_desc; + struct gpio_desc *desc = dev_get_drvdata(dev); ssize_t status; mutex_lock(&sysfs_lock); @@ -295,7 +348,7 @@ static ssize_t gpio_value_store(struct device *dev, if (status == 0) { if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; - gpio_set_value_cansleep(gpio, value != 0); + gpiod_set_value_cansleep(desc, value != 0); status = size; } } @@ -325,7 +378,7 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev, if ((desc->flags & GPIO_TRIGGER_MASK) == gpio_flags) return 0; - irq = gpio_to_irq(desc - gpio_desc); + irq = gpiod_to_irq(desc); if (irq < 0) return -EIO; @@ -595,29 +648,32 @@ static ssize_t export_store(struct class *class, struct class_attribute *attr, const char *buf, size_t len) { - long gpio; - int status; + long gpio; + struct gpio_desc *desc; + int status; status = strict_strtol(buf, 0, &gpio); if (status < 0) goto done; + desc = gpio_to_desc(gpio); + /* No extra locking here; FLAG_SYSFS just signifies that the * request and export were done by on behalf of userspace, so * they may be undone on its behalf too. */ - status = gpio_request(gpio, "sysfs"); + status = gpiod_request(desc, "sysfs"); if (status < 0) { if (status == -EPROBE_DEFER) status = -ENODEV; goto done; } - status = gpio_export(gpio, true); + status = gpiod_export(desc, true); if (status < 0) - gpio_free(gpio); + gpiod_free(desc); else - set_bit(FLAG_SYSFS, &gpio_desc[gpio].flags); + set_bit(FLAG_SYSFS, &desc->flags); done: if (status) @@ -629,8 +685,9 @@ static ssize_t unexport_store(struct class *class, struct class_attribute *attr, const char *buf, size_t len) { - long gpio; - int status; + long gpio; + struct gpio_desc *desc; + int status; status = strict_strtol(buf, 0, &gpio); if (status < 0) @@ -638,17 +695,18 @@ static ssize_t unexport_store(struct class *class, status = -EINVAL; + desc = gpio_to_desc(gpio); /* reject bogus commands (gpio_unexport ignores them) */ - if (!gpio_is_valid(gpio)) + if (!desc) goto done; /* No extra locking here; FLAG_SYSFS just signifies that the * request and export were done by on behalf of userspace, so * they may be undone on its behalf too. */ - if (test_and_clear_bit(FLAG_SYSFS, &gpio_desc[gpio].flags)) { + if (test_and_clear_bit(FLAG_SYSFS, &desc->flags)) { status = 0; - gpio_free(gpio); + gpiod_free(desc); } done: if (status) @@ -685,13 +743,13 @@ static struct class gpio_class = { * * Returns zero on success, else an error. */ -int gpio_export(unsigned gpio, bool direction_may_change) +static int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { unsigned long flags; - struct gpio_desc *desc; int status; const char *ioname = NULL; struct device *dev; + int offset; /* can't export until sysfs is available ... */ if (!gpio_class.p) { @@ -699,20 +757,19 @@ int gpio_export(unsigned gpio, bool direction_may_change) return -ENOENT; } - if (!gpio_is_valid(gpio)) { - pr_debug("%s: gpio %d is not valid\n", __func__, gpio); + if (!desc) { + pr_debug("%s: invalid gpio descriptor\n", __func__); return -EINVAL; } mutex_lock(&sysfs_lock); spin_lock_irqsave(&gpio_lock, flags); - desc = &gpio_desc[gpio]; if (!test_bit(FLAG_REQUESTED, &desc->flags) || test_bit(FLAG_EXPORT, &desc->flags)) { spin_unlock_irqrestore(&gpio_lock, flags); pr_debug("%s: gpio %d unavailable (requested=%d, exported=%d)\n", - __func__, gpio, + __func__, desc_to_gpio(desc), test_bit(FLAG_REQUESTED, &desc->flags), test_bit(FLAG_EXPORT, &desc->flags)); status = -EPERM; @@ -723,11 +780,13 @@ int gpio_export(unsigned gpio, bool direction_may_change) direction_may_change = false; spin_unlock_irqrestore(&gpio_lock, flags); - if (desc->chip->names && desc->chip->names[gpio - desc->chip->base]) - ioname = desc->chip->names[gpio - desc->chip->base]; + offset = gpio_chip_hwgpio(desc); + if (desc->chip->names && desc->chip->names[offset]) + ioname = desc->chip->names[offset]; dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0), - desc, ioname ? ioname : "gpio%u", gpio); + desc, ioname ? ioname : "gpio%u", + desc_to_gpio(desc)); if (IS_ERR(dev)) { status = PTR_ERR(dev); goto fail_unlock; @@ -743,7 +802,7 @@ int gpio_export(unsigned gpio, bool direction_may_change) goto fail_unregister_device; } - if (gpio_to_irq(gpio) >= 0 && (direction_may_change || + if (gpiod_to_irq(desc) >= 0 && (direction_may_change || !test_bit(FLAG_IS_OUT, &desc->flags))) { status = device_create_file(dev, &dev_attr_edge); if (status) @@ -758,9 +817,15 @@ fail_unregister_device: device_unregister(dev); fail_unlock: mutex_unlock(&sysfs_lock); - pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); + pr_debug("%s: gpio%d status %d\n", __func__, desc_to_gpio(desc), + status); return status; } + +int gpio_export(unsigned gpio, bool direction_may_change) +{ + return gpiod_export(gpio_to_desc(gpio), direction_may_change); +} EXPORT_SYMBOL_GPL(gpio_export); static int match_export(struct device *dev, void *data) @@ -779,18 +844,16 @@ static int match_export(struct device *dev, void *data) * * Returns zero on success, else an error. */ -int gpio_export_link(struct device *dev, const char *name, unsigned gpio) +static int gpiod_export_link(struct device *dev, const char *name, + struct gpio_desc *desc) { - struct gpio_desc *desc; int status = -EINVAL; - if (!gpio_is_valid(gpio)) + if (!desc) goto done; mutex_lock(&sysfs_lock); - desc = &gpio_desc[gpio]; - if (test_bit(FLAG_EXPORT, &desc->flags)) { struct device *tdev; @@ -807,12 +870,17 @@ int gpio_export_link(struct device *dev, const char *name, unsigned gpio) done: if (status) - pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); + pr_debug("%s: gpio%d status %d\n", __func__, desc_to_gpio(desc), + status); return status; } -EXPORT_SYMBOL_GPL(gpio_export_link); +int gpio_export_link(struct device *dev, const char *name, unsigned gpio) +{ + return gpiod_export_link(dev, name, gpio_to_desc(gpio)); +} +EXPORT_SYMBOL_GPL(gpio_export_link); /** * gpio_sysfs_set_active_low - set the polarity of gpio sysfs value @@ -826,19 +894,16 @@ EXPORT_SYMBOL_GPL(gpio_export_link); * * Returns zero on success, else an error. */ -int gpio_sysfs_set_active_low(unsigned gpio, int value) +static int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value) { - struct gpio_desc *desc; struct device *dev = NULL; int status = -EINVAL; - if (!gpio_is_valid(gpio)) + if (!desc) goto done; mutex_lock(&sysfs_lock); - desc = &gpio_desc[gpio]; - if (test_bit(FLAG_EXPORT, &desc->flags)) { dev = class_find_device(&gpio_class, NULL, desc, match_export); if (dev == NULL) { @@ -854,10 +919,16 @@ unlock: done: if (status) - pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); + pr_debug("%s: gpio%d status %d\n", __func__, desc_to_gpio(desc), + status); return status; } + +int gpio_sysfs_set_active_low(unsigned gpio, int value) +{ + return gpiod_sysfs_set_active_low(gpio_to_desc(gpio), value); +} EXPORT_SYMBOL_GPL(gpio_sysfs_set_active_low); /** @@ -866,21 +937,18 @@ EXPORT_SYMBOL_GPL(gpio_sysfs_set_active_low); * * This is implicit on gpio_free(). */ -void gpio_unexport(unsigned gpio) +static void gpiod_unexport(struct gpio_desc *desc) { - struct gpio_desc *desc; int status = 0; struct device *dev = NULL; - if (!gpio_is_valid(gpio)) { + if (!desc) { status = -EINVAL; goto done; } mutex_lock(&sysfs_lock); - desc = &gpio_desc[gpio]; - if (test_bit(FLAG_EXPORT, &desc->flags)) { dev = class_find_device(&gpio_class, NULL, desc, match_export); @@ -892,13 +960,20 @@ void gpio_unexport(unsigned gpio) } mutex_unlock(&sysfs_lock); + if (dev) { device_unregister(dev); put_device(dev); } done: if (status) - pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); + pr_debug("%s: gpio%d status %d\n", __func__, desc_to_gpio(desc), + status); +} + +void gpio_unexport(unsigned gpio) +{ + gpiod_unexport(gpio_to_desc(gpio)); } EXPORT_SYMBOL_GPL(gpio_unexport); @@ -1007,6 +1082,27 @@ static inline void gpiochip_unexport(struct gpio_chip *chip) { } +static inline int gpiod_export(struct gpio_desc *desc, + bool direction_may_change) +{ + return -ENOSYS; +} + +static inline int gpiod_export_link(struct device *dev, const char *name, + struct gpio_desc *desc) +{ + return -ENOSYS; +} + +static inline int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value) +{ + return -ENOSYS; +} + +static inline void gpiod_unexport(struct gpio_desc *desc) +{ +} + #endif /* CONFIG_GPIO_SYSFS */ /* @@ -1282,20 +1378,18 @@ EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges); * on each other, and help provide better diagnostics in debugfs. * They're called even less than the "set direction" calls. */ -int gpio_request(unsigned gpio, const char *label) +static int gpiod_request(struct gpio_desc *desc, const char *label) { - struct gpio_desc *desc; struct gpio_chip *chip; int status = -EPROBE_DEFER; unsigned long flags; spin_lock_irqsave(&gpio_lock, flags); - if (!gpio_is_valid(gpio)) { + if (!desc) { status = -EINVAL; goto done; } - desc = &gpio_desc[gpio]; chip = desc->chip; if (chip == NULL) goto done; @@ -1319,7 +1413,7 @@ int gpio_request(unsigned gpio, const char *label) if (chip->request) { /* chip->request may sleep */ spin_unlock_irqrestore(&gpio_lock, flags); - status = chip->request(chip, gpio - chip->base); + status = chip->request(chip, gpio_chip_hwgpio(desc)); spin_lock_irqsave(&gpio_lock, flags); if (status < 0) { @@ -1332,42 +1426,46 @@ int gpio_request(unsigned gpio, const char *label) if (chip->get_direction) { /* chip->get_direction may sleep */ spin_unlock_irqrestore(&gpio_lock, flags); - gpio_get_direction(gpio); + gpiod_get_direction(desc); spin_lock_irqsave(&gpio_lock, flags); } done: if (status) - pr_debug("gpio_request: gpio-%d (%s) status %d\n", - gpio, label ? : "?", status); + pr_debug("_gpio_request: gpio-%d (%s) status %d\n", + desc ? desc_to_gpio(desc) : -1, + label ? : "?", status); spin_unlock_irqrestore(&gpio_lock, flags); return status; } + +int gpio_request(unsigned gpio, const char *label) +{ + return gpiod_request(gpio_to_desc(gpio), label); +} EXPORT_SYMBOL_GPL(gpio_request); -void gpio_free(unsigned gpio) +static void gpiod_free(struct gpio_desc *desc) { unsigned long flags; - struct gpio_desc *desc; struct gpio_chip *chip; might_sleep(); - if (!gpio_is_valid(gpio)) { + if (!desc) { WARN_ON(extra_checks); return; } - gpio_unexport(gpio); + gpiod_unexport(desc); spin_lock_irqsave(&gpio_lock, flags); - desc = &gpio_desc[gpio]; chip = desc->chip; if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) { if (chip->free) { spin_unlock_irqrestore(&gpio_lock, flags); might_sleep_if(chip->can_sleep); - chip->free(chip, gpio - chip->base); + chip->free(chip, gpio_chip_hwgpio(desc)); spin_lock_irqsave(&gpio_lock, flags); } desc_set_label(desc, NULL); @@ -1381,6 +1479,11 @@ void gpio_free(unsigned gpio) spin_unlock_irqrestore(&gpio_lock, flags); } + +void gpio_free(unsigned gpio) +{ + gpiod_free(gpio_to_desc(gpio)); +} EXPORT_SYMBOL_GPL(gpio_free); /** @@ -1391,29 +1494,32 @@ EXPORT_SYMBOL_GPL(gpio_free); */ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) { + struct gpio_desc *desc; int err; - err = gpio_request(gpio, label); + desc = gpio_to_desc(gpio); + + err = gpiod_request(desc, label); if (err) return err; if (flags & GPIOF_OPEN_DRAIN) - set_bit(FLAG_OPEN_DRAIN, &gpio_desc[gpio].flags); + set_bit(FLAG_OPEN_DRAIN, &desc->flags); if (flags & GPIOF_OPEN_SOURCE) - set_bit(FLAG_OPEN_SOURCE, &gpio_desc[gpio].flags); + set_bit(FLAG_OPEN_SOURCE, &desc->flags); if (flags & GPIOF_DIR_IN) - err = gpio_direction_input(gpio); + err = gpiod_direction_input(desc); else - err = gpio_direction_output(gpio, + err = gpiod_direction_output(desc, (flags & GPIOF_INIT_HIGH) ? 1 : 0); if (err) goto free_gpio; if (flags & GPIOF_EXPORT) { - err = gpio_export(gpio, flags & GPIOF_EXPORT_CHANGEABLE); + err = gpiod_export(desc, flags & GPIOF_EXPORT_CHANGEABLE); if (err) goto free_gpio; } @@ -1421,7 +1527,7 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) return 0; free_gpio: - gpio_free(gpio); + gpiod_free(desc); return err; } EXPORT_SYMBOL_GPL(gpio_request_one); @@ -1477,13 +1583,14 @@ EXPORT_SYMBOL_GPL(gpio_free_array); const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset) { unsigned gpio = chip->base + offset; + struct gpio_desc *desc = &gpio_desc[gpio]; - if (!gpio_is_valid(gpio) || gpio_desc[gpio].chip != chip) + if (!gpio_is_valid(gpio) || desc->chip != chip) return NULL; - if (test_bit(FLAG_REQUESTED, &gpio_desc[gpio].flags) == 0) + if (test_bit(FLAG_REQUESTED, &desc->flags) == 0) return NULL; #ifdef CONFIG_DEBUG_FS - return gpio_desc[gpio].label; + return desc->label; #else return "?"; #endif @@ -1500,24 +1607,21 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested); * rely on gpio_request() having been called beforehand. */ -int gpio_direction_input(unsigned gpio) +static int gpiod_direction_input(struct gpio_desc *desc) { unsigned long flags; struct gpio_chip *chip; - struct gpio_desc *desc = &gpio_desc[gpio]; int status = -EINVAL; + int offset; spin_lock_irqsave(&gpio_lock, flags); - if (!gpio_is_valid(gpio)) + if (!desc) goto fail; chip = desc->chip; if (!chip || !chip->get || !chip->direction_input) goto fail; - gpio -= chip->base; - if (gpio >= chip->ngpio) - goto fail; - status = gpio_ensure_requested(desc, gpio); + status = gpio_ensure_requested(desc); if (status < 0) goto fail; @@ -1527,11 +1631,12 @@ int gpio_direction_input(unsigned gpio) might_sleep_if(chip->can_sleep); + offset = gpio_chip_hwgpio(desc); if (status) { - status = chip->request(chip, gpio); + status = chip->request(chip, offset); if (status < 0) { pr_debug("GPIO-%d: chip request fail, %d\n", - chip->base + gpio, status); + desc_to_gpio(desc), status); /* and it's not available to anyone else ... * gpio_request() is the fully clean solution. */ @@ -1539,48 +1644,54 @@ int gpio_direction_input(unsigned gpio) } } - status = chip->direction_input(chip, gpio); + status = chip->direction_input(chip, offset); if (status == 0) clear_bit(FLAG_IS_OUT, &desc->flags); - trace_gpio_direction(chip->base + gpio, 1, status); + trace_gpio_direction(desc_to_gpio(desc), 1, status); lose: return status; fail: spin_unlock_irqrestore(&gpio_lock, flags); - if (status) + if (status) { + int gpio = -1; + if (desc) + gpio = desc_to_gpio(desc); pr_debug("%s: gpio-%d status %d\n", __func__, gpio, status); + } return status; } + +int gpio_direction_input(unsigned gpio) +{ + return gpiod_direction_input(gpio_to_desc(gpio)); +} EXPORT_SYMBOL_GPL(gpio_direction_input); -int gpio_direction_output(unsigned gpio, int value) +static int gpiod_direction_output(struct gpio_desc *desc, int value) { unsigned long flags; struct gpio_chip *chip; - struct gpio_desc *desc = &gpio_desc[gpio]; int status = -EINVAL; + int offset; /* Open drain pin should not be driven to 1 */ if (value && test_bit(FLAG_OPEN_DRAIN, &desc->flags)) - return gpio_direction_input(gpio); + return gpiod_direction_input(desc); /* Open source pin should not be driven to 0 */ if (!value && test_bit(FLAG_OPEN_SOURCE, &desc->flags)) - return gpio_direction_input(gpio); + return gpiod_direction_input(desc); spin_lock_irqsave(&gpio_lock, flags); - if (!gpio_is_valid(gpio)) + if (!desc) goto fail; chip = desc->chip; if (!chip || !chip->set || !chip->direction_output) goto fail; - gpio -= chip->base; - if (gpio >= chip->ngpio) - goto fail; - status = gpio_ensure_requested(desc, gpio); + status = gpio_ensure_requested(desc); if (status < 0) goto fail; @@ -1590,11 +1701,12 @@ int gpio_direction_output(unsigned gpio, int value) might_sleep_if(chip->can_sleep); + offset = gpio_chip_hwgpio(desc); if (status) { - status = chip->request(chip, gpio); + status = chip->request(chip, offset); if (status < 0) { pr_debug("GPIO-%d: chip request fail, %d\n", - chip->base + gpio, status); + desc_to_gpio(desc), status); /* and it's not available to anyone else ... * gpio_request() is the fully clean solution. */ @@ -1602,20 +1714,29 @@ int gpio_direction_output(unsigned gpio, int value) } } - status = chip->direction_output(chip, gpio, value); + status = chip->direction_output(chip, offset, value); if (status == 0) set_bit(FLAG_IS_OUT, &desc->flags); - trace_gpio_value(chip->base + gpio, 0, value); - trace_gpio_direction(chip->base + gpio, 0, status); + trace_gpio_value(desc_to_gpio(desc), 0, value); + trace_gpio_direction(desc_to_gpio(desc), 0, status); lose: return status; fail: spin_unlock_irqrestore(&gpio_lock, flags); - if (status) + if (status) { + int gpio = -1; + if (desc) + gpio = desc_to_gpio(desc); pr_debug("%s: gpio-%d status %d\n", __func__, gpio, status); + } return status; } + +int gpio_direction_output(unsigned gpio, int value) +{ + return gpiod_direction_output(gpio_to_desc(gpio), value); +} EXPORT_SYMBOL_GPL(gpio_direction_output); /** @@ -1623,24 +1744,22 @@ EXPORT_SYMBOL_GPL(gpio_direction_output); * @gpio: the gpio to set debounce time * @debounce: debounce time is microseconds */ -int gpio_set_debounce(unsigned gpio, unsigned debounce) +static int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) { unsigned long flags; struct gpio_chip *chip; - struct gpio_desc *desc = &gpio_desc[gpio]; int status = -EINVAL; + int offset; spin_lock_irqsave(&gpio_lock, flags); - if (!gpio_is_valid(gpio)) + if (!desc) goto fail; chip = desc->chip; if (!chip || !chip->set || !chip->set_debounce) goto fail; - gpio -= chip->base; - if (gpio >= chip->ngpio) - goto fail; - status = gpio_ensure_requested(desc, gpio); + + status = gpio_ensure_requested(desc); if (status < 0) goto fail; @@ -1650,16 +1769,26 @@ int gpio_set_debounce(unsigned gpio, unsigned debounce) might_sleep_if(chip->can_sleep); - return chip->set_debounce(chip, gpio, debounce); + offset = gpio_chip_hwgpio(desc); + return chip->set_debounce(chip, offset, debounce); fail: spin_unlock_irqrestore(&gpio_lock, flags); - if (status) + if (status) { + int gpio = -1; + if (desc) + gpio = desc_to_gpio(desc); pr_debug("%s: gpio-%d status %d\n", __func__, gpio, status); + } return status; } + +int gpio_set_debounce(unsigned gpio, unsigned debounce) +{ + return gpiod_set_debounce(gpio_to_desc(gpio), debounce); +} EXPORT_SYMBOL_GPL(gpio_set_debounce); /* I/O calls are only valid after configuration completed; the relevant @@ -1693,18 +1822,25 @@ EXPORT_SYMBOL_GPL(gpio_set_debounce); * It returns the zero or nonzero value provided by the associated * gpio_chip.get() method; or zero if no such method is provided. */ -int __gpio_get_value(unsigned gpio) +static int gpiod_get_value(struct gpio_desc *desc) { struct gpio_chip *chip; int value; + int offset; - chip = gpio_to_chip(gpio); + chip = desc->chip; + offset = gpio_chip_hwgpio(desc); /* Should be using gpio_get_value_cansleep() */ WARN_ON(chip->can_sleep); - value = chip->get ? chip->get(chip, gpio - chip->base) : 0; - trace_gpio_value(gpio, 1, value); + value = chip->get ? chip->get(chip, offset) : 0; + trace_gpio_value(desc_to_gpio(desc), 1, value); return value; } + +int __gpio_get_value(unsigned gpio) +{ + return gpiod_get_value(gpio_to_desc(gpio)); +} EXPORT_SYMBOL_GPL(__gpio_get_value); /* @@ -1713,23 +1849,25 @@ EXPORT_SYMBOL_GPL(__gpio_get_value); * @chip: Gpio chip. * @value: Non-zero for setting it HIGH otherise it will set to LOW. */ -static void _gpio_set_open_drain_value(unsigned gpio, - struct gpio_chip *chip, int value) +static void _gpio_set_open_drain_value(struct gpio_desc *desc, int value) { int err = 0; + struct gpio_chip *chip = desc->chip; + int offset = gpio_chip_hwgpio(desc); + if (value) { - err = chip->direction_input(chip, gpio - chip->base); + err = chip->direction_input(chip, offset); if (!err) - clear_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags); + clear_bit(FLAG_IS_OUT, &desc->flags); } else { - err = chip->direction_output(chip, gpio - chip->base, 0); + err = chip->direction_output(chip, offset, 0); if (!err) - set_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags); + set_bit(FLAG_IS_OUT, &desc->flags); } - trace_gpio_direction(gpio, value, err); + trace_gpio_direction(desc_to_gpio(desc), value, err); if (err < 0) pr_err("%s: Error in set_value for open drain gpio%d err %d\n", - __func__, gpio, err); + __func__, desc_to_gpio(desc), err); } /* @@ -1738,26 +1876,27 @@ static void _gpio_set_open_drain_value(unsigned gpio, * @chip: Gpio chip. * @value: Non-zero for setting it HIGH otherise it will set to LOW. */ -static void _gpio_set_open_source_value(unsigned gpio, - struct gpio_chip *chip, int value) +static void _gpio_set_open_source_value(struct gpio_desc *desc, int value) { int err = 0; + struct gpio_chip *chip = desc->chip; + int offset = gpio_chip_hwgpio(desc); + if (value) { - err = chip->direction_output(chip, gpio - chip->base, 1); + err = chip->direction_output(chip, offset, 1); if (!err) - set_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags); + set_bit(FLAG_IS_OUT, &desc->flags); } else { - err = chip->direction_input(chip, gpio - chip->base); + err = chip->direction_input(chip, offset); if (!err) - clear_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags); + clear_bit(FLAG_IS_OUT, &desc->flags); } - trace_gpio_direction(gpio, !value, err); + trace_gpio_direction(desc_to_gpio(desc), !value, err); if (err < 0) pr_err("%s: Error in set_value for open source gpio%d err %d\n", - __func__, gpio, err); + __func__, desc_to_gpio(desc), err); } - /** * __gpio_set_value() - assign a gpio's value * @gpio: gpio whose value will be assigned @@ -1767,20 +1906,25 @@ static void _gpio_set_open_source_value(unsigned gpio, * This is used directly or indirectly to implement gpio_set_value(). * It invokes the associated gpio_chip.set() method. */ -void __gpio_set_value(unsigned gpio, int value) +static void gpiod_set_value(struct gpio_desc *desc, int value) { struct gpio_chip *chip; - chip = gpio_to_chip(gpio); + chip = desc->chip; /* Should be using gpio_set_value_cansleep() */ WARN_ON(chip->can_sleep); - trace_gpio_value(gpio, 0, value); - if (test_bit(FLAG_OPEN_DRAIN, &gpio_desc[gpio].flags)) - _gpio_set_open_drain_value(gpio, chip, value); - else if (test_bit(FLAG_OPEN_SOURCE, &gpio_desc[gpio].flags)) - _gpio_set_open_source_value(gpio, chip, value); + trace_gpio_value(desc_to_gpio(desc), 0, value); + if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) + _gpio_set_open_drain_value(desc, value); + else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) + _gpio_set_open_source_value(desc, value); else - chip->set(chip, gpio - chip->base, value); + chip->set(chip, gpio_chip_hwgpio(desc), value); +} + +void __gpio_set_value(unsigned gpio, int value) +{ + return gpiod_set_value(gpio_to_desc(gpio), value); } EXPORT_SYMBOL_GPL(__gpio_set_value); @@ -1792,14 +1936,15 @@ EXPORT_SYMBOL_GPL(__gpio_set_value); * This is used directly or indirectly to implement gpio_cansleep(). It * returns nonzero if access reading or writing the GPIO value can sleep. */ -int __gpio_cansleep(unsigned gpio) +static int gpiod_cansleep(struct gpio_desc *desc) { - struct gpio_chip *chip; - /* only call this on GPIOs that are valid! */ - chip = gpio_to_chip(gpio); + return desc->chip->can_sleep; +} - return chip->can_sleep; +int __gpio_cansleep(unsigned gpio) +{ + return gpiod_cansleep(gpio_to_desc(gpio)); } EXPORT_SYMBOL_GPL(__gpio_cansleep); @@ -1812,50 +1957,67 @@ EXPORT_SYMBOL_GPL(__gpio_cansleep); * It returns the number of the IRQ signaled by this (input) GPIO, * or a negative errno. */ -int __gpio_to_irq(unsigned gpio) +static int gpiod_to_irq(struct gpio_desc *desc) { struct gpio_chip *chip; + int offset; - chip = gpio_to_chip(gpio); - return chip->to_irq ? chip->to_irq(chip, gpio - chip->base) : -ENXIO; + chip = desc->chip; + offset = gpio_chip_hwgpio(desc); + return chip->to_irq ? chip->to_irq(chip, offset) : -ENXIO; } -EXPORT_SYMBOL_GPL(__gpio_to_irq); +int __gpio_to_irq(unsigned gpio) +{ + return gpiod_to_irq(gpio_to_desc(gpio)); +} +EXPORT_SYMBOL_GPL(__gpio_to_irq); /* There's no value in making it easy to inline GPIO calls that may sleep. * Common examples include ones connected to I2C or SPI chips. */ -int gpio_get_value_cansleep(unsigned gpio) +static int gpiod_get_value_cansleep(struct gpio_desc *desc) { struct gpio_chip *chip; int value; + int offset; might_sleep_if(extra_checks); - chip = gpio_to_chip(gpio); - value = chip->get ? chip->get(chip, gpio - chip->base) : 0; - trace_gpio_value(gpio, 1, value); + chip = desc->chip; + offset = gpio_chip_hwgpio(desc); + value = chip->get ? chip->get(chip, offset) : 0; + trace_gpio_value(desc_to_gpio(desc), 1, value); return value; } + +int gpio_get_value_cansleep(unsigned gpio) +{ + return gpiod_get_value_cansleep(gpio_to_desc(gpio)); +} EXPORT_SYMBOL_GPL(gpio_get_value_cansleep); -void gpio_set_value_cansleep(unsigned gpio, int value) +static void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) { struct gpio_chip *chip; might_sleep_if(extra_checks); - chip = gpio_to_chip(gpio); - trace_gpio_value(gpio, 0, value); - if (test_bit(FLAG_OPEN_DRAIN, &gpio_desc[gpio].flags)) - _gpio_set_open_drain_value(gpio, chip, value); - else if (test_bit(FLAG_OPEN_SOURCE, &gpio_desc[gpio].flags)) - _gpio_set_open_source_value(gpio, chip, value); + chip = desc->chip; + trace_gpio_value(desc_to_gpio(desc), 0, value); + if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) + _gpio_set_open_drain_value(desc, value); + else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) + _gpio_set_open_source_value(desc, value); else - chip->set(chip, gpio - chip->base, value); + chip->set(chip, gpio_chip_hwgpio(desc), value); } -EXPORT_SYMBOL_GPL(gpio_set_value_cansleep); +void gpio_set_value_cansleep(unsigned gpio, int value) +{ + return gpiod_set_value_cansleep(gpio_to_desc(gpio), value); +} +EXPORT_SYMBOL_GPL(gpio_set_value_cansleep); #ifdef CONFIG_DEBUG_FS @@ -1870,7 +2032,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) if (!test_bit(FLAG_REQUESTED, &gdesc->flags)) continue; - gpio_get_direction(gpio); + gpiod_get_direction(gdesc); is_out = test_bit(FLAG_IS_OUT, &gdesc->flags); seq_printf(s, " gpio-%-3d (%-20.20s) %s %s", gpio, gdesc->label, -- cgit v1.2.3 From 6c0b4e6c85d085bd92966bc2b8da73e2d7f35929 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sun, 3 Feb 2013 01:29:30 +0900 Subject: gpiolib: let gpio_chip reference its descriptors Add a pointer to the gpio_chip structure that references the array of GPIO descriptors belonging to the chip, and update gpiolib code to use this pointer instead of the global gpio_desc[] array. This is another step towards the removal of the gpio_desc[] global array. Signed-off-by: Alexandre Courbot Reviewed-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/gpio/gpiolib.c | 39 +++++++++++++++++++++++---------------- include/asm-generic/gpio.h | 3 +++ 2 files changed, 26 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 866431f674c3..5050693dcc35 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -72,6 +72,8 @@ struct gpio_desc { }; static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; +#define GPIO_OFFSET_VALID(chip, offset) (offset >= 0 && offset < chip->ngpio) + static LIST_HEAD(gpio_chips); #ifdef CONFIG_GPIO_SYSFS @@ -112,7 +114,7 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label) */ static int gpio_chip_hwgpio(const struct gpio_desc *desc) { - return (desc - &gpio_desc[0]) - desc->chip->base; + return desc - &desc->chip->desc[0]; } /** @@ -133,7 +135,7 @@ static struct gpio_desc *gpio_to_desc(unsigned gpio) */ static int desc_to_gpio(const struct gpio_desc *desc) { - return desc - &gpio_desc[0]; + return desc->chip->base + gpio_chip_hwgpio(desc); } @@ -1007,9 +1009,9 @@ static int gpiochip_export(struct gpio_chip *chip) unsigned gpio; spin_lock_irqsave(&gpio_lock, flags); - gpio = chip->base; - while (gpio_desc[gpio].chip == chip) - gpio_desc[gpio++].chip = NULL; + gpio = 0; + while (gpio < chip->ngpio) + chip->desc[gpio++].chip = NULL; spin_unlock_irqrestore(&gpio_lock, flags); pr_debug("%s: chip %s status %d\n", __func__, @@ -1186,8 +1188,11 @@ int gpiochip_add(struct gpio_chip *chip) status = gpiochip_add_to_list(chip); if (status == 0) { - for (id = base; id < base + chip->ngpio; id++) { - gpio_desc[id].chip = chip; + chip->desc = &gpio_desc[chip->base]; + + for (id = 0; id < chip->ngpio; id++) { + struct gpio_desc *desc = &chip->desc[id]; + desc->chip = chip; /* REVISIT: most hardware initializes GPIOs as * inputs (often with pullups enabled) so power @@ -1196,7 +1201,7 @@ int gpiochip_add(struct gpio_chip *chip) * and in case chip->get_direction is not set, * we may expose the wrong direction in sysfs. */ - gpio_desc[id].flags = !chip->direction_input + desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0; } @@ -1249,15 +1254,15 @@ int gpiochip_remove(struct gpio_chip *chip) gpiochip_remove_pin_ranges(chip); of_gpiochip_remove(chip); - for (id = chip->base; id < chip->base + chip->ngpio; id++) { - if (test_bit(FLAG_REQUESTED, &gpio_desc[id].flags)) { + for (id = 0; id < chip->ngpio; id++) { + if (test_bit(FLAG_REQUESTED, &chip->desc[id].flags)) { status = -EBUSY; break; } } if (status == 0) { - for (id = chip->base; id < chip->base + chip->ngpio; id++) - gpio_desc[id].chip = NULL; + for (id = 0; id < chip->ngpio; id++) + chip->desc[id].chip = NULL; list_del(&chip->list); } @@ -1582,11 +1587,13 @@ EXPORT_SYMBOL_GPL(gpio_free_array); */ const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset) { - unsigned gpio = chip->base + offset; - struct gpio_desc *desc = &gpio_desc[gpio]; + struct gpio_desc *desc; - if (!gpio_is_valid(gpio) || desc->chip != chip) + if (!GPIO_OFFSET_VALID(chip, offset)) return NULL; + + desc = &chip->desc[offset]; + if (test_bit(FLAG_REQUESTED, &desc->flags) == 0) return NULL; #ifdef CONFIG_DEBUG_FS @@ -2025,7 +2032,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) { unsigned i; unsigned gpio = chip->base; - struct gpio_desc *gdesc = &gpio_desc[gpio]; + struct gpio_desc *gdesc = &chip->desc[0]; int is_out; for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) { diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index b562f95cdc2f..bde646995d10 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -47,6 +47,7 @@ struct gpio; struct seq_file; struct module; struct device_node; +struct gpio_desc; /** * struct gpio_chip - abstract a GPIO controller @@ -76,6 +77,7 @@ struct device_node; * negative during registration, requests dynamic ID allocation. * @ngpio: the number of GPIOs handled by this controller; the last GPIO * handled is (base + ngpio - 1). + * @desc: array of ngpio descriptors. Private. * @can_sleep: flag must be set iff get()/set() methods sleep, as they * must while accessing GPIO expander chips over I2C or SPI * @names: if set, must be an array of strings to use as alternative @@ -126,6 +128,7 @@ struct gpio_chip { struct gpio_chip *chip); int base; u16 ngpio; + struct gpio_desc *desc; const char *const *names; unsigned can_sleep:1; unsigned exported:1; -- cgit v1.2.3 From 362432aed5e5b497a8cf7b20c268ba71df93c045 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sat, 9 Feb 2013 09:41:49 +0000 Subject: gpiolib: Fix locking on gpio debugfs files The debugfs files really need to hold the gpiolib spinlock before accessing the list. Otherwise chip addition/removal will cause an oops. Cc: Alexandre Courbot Cc: Linus Walleij Signed-off-by: Grant Likely --- drivers/gpio/gpiolib.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 5050693dcc35..4b3f388bcc6d 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2053,29 +2053,35 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos) { + unsigned long flags; struct gpio_chip *chip = NULL; loff_t index = *pos; - /* REVISIT this isn't locked against gpio_chip removal ... */ - s->private = ""; + spin_lock_irqsave(&gpio_lock, flags); list_for_each_entry(chip, &gpio_chips, list) - if (index-- == 0) + if (index-- == 0) { + spin_unlock_irqrestore(&gpio_lock, flags); return chip; + } + spin_unlock_irqrestore(&gpio_lock, flags); return NULL; } static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos) { + unsigned long flags; struct gpio_chip *chip = v; void *ret = NULL; + spin_lock_irqsave(&gpio_lock, flags); if (list_is_last(&chip->list, &gpio_chips)) ret = NULL; else ret = list_entry(chip->list.next, struct gpio_chip, list); + spin_unlock_irqrestore(&gpio_lock, flags); s->private = "\n"; ++*pos; -- cgit v1.2.3 From 9f01d30ee191a74f1e7a2ac0710f064f472d000a Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Fri, 18 Jan 2013 17:58:22 +1300 Subject: gpio/vt8500: memory cleanup missing This driver is missing a .remove callback, and the fail path on probe is incomplete. If an error occurs in vt8500_add_chips, gpio_base is not unmapped. The driver is also ignoring the return value from this function so if a chip fails to register it completes as successful. Replaced pr_err with dev_err in vt8500_add_chips since the device is available. There is also no .remove callback defined so the function is added. Signed-off-by: Tony Prisk Signed-off-by: Grant Likely --- drivers/gpio/gpio-vt8500.c | 61 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-vt8500.c b/drivers/gpio/gpio-vt8500.c index 9a7c434d2947..81683ca35ac1 100644 --- a/drivers/gpio/gpio-vt8500.c +++ b/drivers/gpio/gpio-vt8500.c @@ -127,6 +127,12 @@ struct vt8500_gpio_chip { void __iomem *base; }; +struct vt8500_data { + struct vt8500_gpio_chip *chip; + void __iomem *iobase; + int num_banks; +}; + #define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip) @@ -224,19 +230,32 @@ static int vt8500_of_xlate(struct gpio_chip *gc, static int vt8500_add_chips(struct platform_device *pdev, void __iomem *base, const struct vt8500_gpio_data *data) { + struct vt8500_data *priv; struct vt8500_gpio_chip *vtchip; struct gpio_chip *chip; int i; int pin_cnt = 0; - vtchip = devm_kzalloc(&pdev->dev, + priv = devm_kzalloc(&pdev->dev, sizeof(struct vt8500_data), GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + priv->chip = devm_kzalloc(&pdev->dev, sizeof(struct vt8500_gpio_chip) * data->num_banks, GFP_KERNEL); - if (!vtchip) { - pr_err("%s: failed to allocate chip memory\n", __func__); + if (!priv->chip) { + dev_err(&pdev->dev, "failed to allocate chip memory\n"); return -ENOMEM; } + priv->iobase = base; + priv->num_banks = data->num_banks; + platform_set_drvdata(pdev, priv); + + vtchip = priv->chip; + for (i = 0; i < data->num_banks; i++) { vtchip[i].base = base; vtchip[i].regs = &data->banks[i]; @@ -273,36 +292,54 @@ static struct of_device_id vt8500_gpio_dt_ids[] = { static int vt8500_gpio_probe(struct platform_device *pdev) { + int ret; void __iomem *gpio_base; - struct device_node *np; + struct resource *res; const struct of_device_id *of_id = of_match_device(vt8500_gpio_dt_ids, &pdev->dev); if (!of_id) { - dev_err(&pdev->dev, "Failed to find gpio controller\n"); + dev_err(&pdev->dev, "No matching driver data\n"); return -ENODEV; } - np = pdev->dev.of_node; - if (!np) { - dev_err(&pdev->dev, "Missing GPIO description in devicetree\n"); - return -EFAULT; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Unable to get IO resource\n"); + return -ENODEV; } - gpio_base = of_iomap(np, 0); + gpio_base = devm_request_and_ioremap(&pdev->dev, res); if (!gpio_base) { dev_err(&pdev->dev, "Unable to map GPIO registers\n"); - of_node_put(np); return -ENOMEM; } - vt8500_add_chips(pdev, gpio_base, of_id->data); + ret = vt8500_add_chips(pdev, gpio_base, of_id->data); + + return ret; +} + +static int vt8500_gpio_remove(struct platform_device *pdev) +{ + int i; + int ret; + struct vt8500_data *priv = platform_get_drvdata(pdev); + struct vt8500_gpio_chip *vtchip = priv->chip; + + for (i = 0; i < priv->num_banks; i++) { + ret = gpiochip_remove(&vtchip[i].chip); + if (ret) + dev_warn(&pdev->dev, "gpiochip_remove returned %d\n", + ret); + } return 0; } static struct platform_driver vt8500_gpio_driver = { .probe = vt8500_gpio_probe, + .remove = vt8500_gpio_remove, .driver = { .name = "vt8500-gpio", .owner = THIS_MODULE, -- cgit v1.2.3 From e9a65bb63e5fe45b9d0ecd1983749786858da14d Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Wed, 6 Feb 2013 18:44:32 +0800 Subject: gpio: using common order: let 'static const' instead of 'const static' 'const static ' is not a common order, better to use 'static const' instead. building: make EXTRA_CFLAGS=-W ARCH=arm drivers/gpio/gpio-omap.c:1479: warning: 'static' is not at beginning of declaration drivers/gpio/gpio-omap.c:1485: warning: 'static' is not at beginning of declaration drivers/gpio/gpio-omap.c:1491: warning: 'static' is not at beginning of declaration Signed-off-by: Chen Gang Acked-by: Santosh Shilimkar Signed-off-by: Grant Likely --- drivers/gpio/gpio-omap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index f1fbedb2a6f9..159f5c57eb45 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1476,19 +1476,19 @@ static struct omap_gpio_reg_offs omap4_gpio_regs = { .fallingdetect = OMAP4_GPIO_FALLINGDETECT, }; -const static struct omap_gpio_platform_data omap2_pdata = { +static const struct omap_gpio_platform_data omap2_pdata = { .regs = &omap2_gpio_regs, .bank_width = 32, .dbck_flag = false, }; -const static struct omap_gpio_platform_data omap3_pdata = { +static const struct omap_gpio_platform_data omap3_pdata = { .regs = &omap2_gpio_regs, .bank_width = 32, .dbck_flag = true, }; -const static struct omap_gpio_platform_data omap4_pdata = { +static const struct omap_gpio_platform_data omap4_pdata = { .regs = &omap4_gpio_regs, .bank_width = 32, .dbck_flag = true, -- cgit v1.2.3 From 7897e6022761ace7377f0f784fca059da55f5d71 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Mon, 4 Feb 2013 15:55:58 +0400 Subject: PCI: Disable Bus Master unconditionally in pci_device_shutdown() Commit b566a22c23 ("PCI: disable Bus Master on PCI device shutdown") used pci_disable_device(), but that doesn't disable Bus Mastering unconditionally; we allow nested enable/disable calls, and only the last disable call actually does anything. This uses pci_clear_master() to unconditionally clear the Bus Master bit. Matthew Garrett and Alan Cox said (see LKML link below) that clearing Bus Master for all PCI devices may lead to unpredictable consequences: some devices ignores this bit and continue DMA, some of them hang after that or crash the whole system. But we're already trying to clear Bus Master in general because of b566a22c23; this merely deals with the cases where drivers haven't shut down the device correctly. [bhelgaas: changelog] Link: https://lkml.org/lkml/2012/6/6/278 Signed-off-by: Konstantin Khlebnikov Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/pci/pci-driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index f79cbcd3944b..dc5bdce63f12 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -392,7 +392,7 @@ static void pci_device_shutdown(struct device *dev) * Turn off Bus Master bit on the device to tell it to not * continue to do DMA */ - pci_disable_device(pci_dev); + pci_clear_master(pci_dev); } #ifdef CONFIG_PM -- cgit v1.2.3 From fd6dceab017e6be6c158dc56ec6dacf817f21a5b Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Mon, 4 Feb 2013 15:56:01 +0400 Subject: PCI: Catch attempts to disable already-disabled devices Warn when disabling a device that has already been disabled. [bhelgaas: message wording] Signed-off-by: Konstantin Khlebnikov Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/pci/pci.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 5cb5820fae40..29a09b705f04 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1401,6 +1401,9 @@ pci_disable_device(struct pci_dev *dev) if (dr) dr->enabled = 0; + dev_WARN_ONCE(&dev->dev, atomic_read(&dev->enable_cnt) <= 0, + "disabling already-disabled device"); + if (atomic_sub_return(1, &dev->enable_cnt) != 0) return; -- cgit v1.2.3 From cc7ba39bab126339d6d525ada07dea5633d71521 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 11 Feb 2013 16:47:01 -0700 Subject: PCI: Use atomic_inc_return() rather than atomic_add_return() No functional change; just use atomic_inc_return() rather than the general-purpose atomic_add_return(). Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 29a09b705f04..c746b04afd6a 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1174,7 +1174,7 @@ static int __pci_enable_device_flags(struct pci_dev *dev, dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); } - if (atomic_add_return(1, &dev->enable_cnt) > 1) + if (atomic_inc_return(&dev->enable_cnt) > 1) return 0; /* already enabled */ /* only skip sriov related */ @@ -1404,7 +1404,7 @@ pci_disable_device(struct pci_dev *dev) dev_WARN_ONCE(&dev->dev, atomic_read(&dev->enable_cnt) <= 0, "disabling already-disabled device"); - if (atomic_sub_return(1, &dev->enable_cnt) != 0) + if (atomic_dec_return(&dev->enable_cnt) != 0) return; do_pci_disable_device(dev); -- cgit v1.2.3 From 82fee4d67ab86d6fe5eb0f9a9e988ca9d654d765 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 4 Feb 2013 15:56:05 +0400 Subject: PCI/PM: Clear state_saved during suspend This patch clears pci_dev->state_saved at the beginning of suspending. PCI config state may be saved long before that. Some drivers call pci_save_state() from the ->probe() callback to get snapshot of sane configuration space to use in the ->slot_reset() callback. Signed-off-by: Konstantin Khlebnikov # add comment Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-driver.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index dc5bdce63f12..f9aa311b9f34 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -628,6 +628,7 @@ static int pci_pm_suspend(struct device *dev) goto Fixup; } + pci_dev->state_saved = false; if (pm->suspend) { pci_power_t prev = pci_dev->current_state; int error; @@ -774,6 +775,7 @@ static int pci_pm_freeze(struct device *dev) return 0; } + pci_dev->state_saved = false; if (pm->freeze) { int error; @@ -862,6 +864,7 @@ static int pci_pm_poweroff(struct device *dev) goto Fixup; } + pci_dev->state_saved = false; if (pm->poweroff) { int error; @@ -987,6 +990,7 @@ static int pci_pm_runtime_suspend(struct device *dev) if (!pm || !pm->runtime_suspend) return -ENOSYS; + pci_dev->state_saved = false; pci_dev->no_d3cold = false; error = pm->runtime_suspend(dev); suspend_report_result(pm->runtime_suspend, error); -- cgit v1.2.3 From b2593cbe18c4f50c9acacd88860b051f567654d7 Mon Sep 17 00:00:00 2001 From: Bhanu Prakash Gollapudi Date: Mon, 4 Feb 2013 23:00:20 -0800 Subject: libfcoe: Handle CVL while waiting to select an FCF When a CVL is received while we wait to select best FCF, we drop it without handling it. This causes initiator and the switch to go out-of-sync. Initiator proceeds selecting one of the FCFs and tries to send FIP FLOGI. However the switch may reject the FLOGI, as it has cleared its internal state, and expects the initiator to start FIP discovery protocol. Fix this condition by resetting the fcoe controller. Signed-off-by: Bhanu Prakash Gollapudi Reviewed-by: Yi Zou Signed-off-by: Robert Love --- drivers/scsi/fcoe/fcoe_ctlr.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c index 75834255bf07..aff3c44a1cdc 100644 --- a/drivers/scsi/fcoe/fcoe_ctlr.c +++ b/drivers/scsi/fcoe/fcoe_ctlr.c @@ -1291,8 +1291,16 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, LIBFCOE_FIP_DBG(fip, "Clear Virtual Link received\n"); - if (!fcf || !lport->port_id) + if (!fcf || !lport->port_id) { + /* + * We are yet to select best FCF, but we got CVL in the + * meantime. reset the ctlr and let it rediscover the FCF + */ + mutex_lock(&fip->ctlr_mutex); + fcoe_ctlr_reset(fip); + mutex_unlock(&fip->ctlr_mutex); return; + } /* * mask of required descriptors. Validating each one clears its bit. -- cgit v1.2.3 From dc18f0800f5f16460030a9623d4fcc165d607edf Mon Sep 17 00:00:00 2001 From: Sjur Brændeland Date: Tue, 12 Feb 2013 16:24:59 +1030 Subject: virtio_console: Use virtio device index to generate port name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use virtio device index for creating unique device port names. Current index allocation in virtio is based on a monotonically increasing variable "index". A better handling of this is to use device index which is allocated by ida. Signed-off-by: Sjur Brændeland Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 95cae778bd73..2cfd5a0575ab 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -61,9 +61,6 @@ struct ports_driver_data { /* List of all the devices we're handling */ struct list_head portdevs; - /* Number of devices this driver is handling */ - unsigned int index; - /* * This is used to keep track of the number of hvc consoles * spawned by this driver. This number is given as the first @@ -169,9 +166,6 @@ struct ports_device { /* Array of per-port IO virtqueues */ struct virtqueue **in_vqs, **out_vqs; - /* Used for numbering devices for sysfs and debugfs */ - unsigned int drv_index; - /* Major number for this device. Ports will be created as minors. */ int chr_major; }; @@ -1415,7 +1409,7 @@ static int add_port(struct ports_device *portdev, u32 id) } port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev, devt, port, "vport%up%u", - port->portdev->drv_index, id); + port->portdev->vdev->index, id); if (IS_ERR(port->dev)) { err = PTR_ERR(port->dev); dev_err(&port->portdev->vdev->dev, @@ -1470,7 +1464,7 @@ static int add_port(struct ports_device *portdev, u32 id) * inspect a port's state at any time */ sprintf(debugfs_name, "vport%up%u", - port->portdev->drv_index, id); + port->portdev->vdev->index, id); port->debugfs_file = debugfs_create_file(debugfs_name, 0444, pdrvdata.debugfs_dir, port, @@ -1961,16 +1955,12 @@ static int virtcons_probe(struct virtio_device *vdev) portdev->vdev = vdev; vdev->priv = portdev; - spin_lock_irq(&pdrvdata_lock); - portdev->drv_index = pdrvdata.index++; - spin_unlock_irq(&pdrvdata_lock); - portdev->chr_major = register_chrdev(0, "virtio-portsdev", &portdev_fops); if (portdev->chr_major < 0) { dev_err(&vdev->dev, "Error %d registering chrdev for device %u\n", - portdev->chr_major, portdev->drv_index); + portdev->chr_major, vdev->index); err = portdev->chr_major; goto free; } -- cgit v1.2.3 From f3b54b9a066edeac5c06e1cdcd82e1cb1224aaef Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 11 Feb 2013 19:47:56 -0700 Subject: i2c: add bcm2835 driver This implements a very basic I2C host driver for the BCM2835 SoC. Missing features so far are: * 10-bit addressing. * DMA. Reviewed-by: Grant Likely Signed-off-by: Stephen Warren Signed-off-by: Wolfram Sang --- .../devicetree/bindings/i2c/brcm,bcm2835-i2c.txt | 20 ++ drivers/i2c/busses/Kconfig | 12 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm2835.c | 342 +++++++++++++++++++++ 4 files changed, 375 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt create mode 100644 drivers/i2c/busses/i2c-bcm2835.c (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt b/Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt new file mode 100644 index 000000000000..e9de3756752b --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt @@ -0,0 +1,20 @@ +Broadcom BCM2835 I2C controller + +Required properties: +- compatible : Should be "brcm,bcm2835-i2c". +- reg: Should contain register location and length. +- interrupts: Should contain interrupt. +- clocks : The clock feeding the I2C controller. + +Recommended properties: +- clock-frequency : desired I2C bus clock frequency in Hz. + +Example: + +i2c@20205000 { + compatible = "brcm,bcm2835-i2c"; + reg = <0x7e205000 0x1000>; + interrupts = <2 21>; + clocks = <&clk_i2c>; + clock-frequency = <100000>; +}; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 74e18bc0bf62..8cbd9cd1e0fa 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -330,6 +330,18 @@ config I2C_AU1550 This driver can also be built as a module. If so, the module will be called i2c-au1550. +config I2C_BCM2835 + tristate "Broadcom BCM2835 I2C controller" + depends on ARCH_BCM2835 + help + If you say yes to this option, support will be included for the + BCM2835 I2C controller. + + If you don't know what to do here, say N. + + This support is also available as a module. If so, the module + will be called i2c-bcm2835. + config I2C_BLACKFIN_TWI tristate "Blackfin TWI I2C support" depends on BLACKFIN diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 23c9d55bc880..8f4fc23b85b1 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o # Embedded system I2C/SMBus host controller drivers obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o +obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o obj-$(CONFIG_I2C_CPM) += i2c-cpm.o diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c new file mode 100644 index 000000000000..ea4b08fc3353 --- /dev/null +++ b/drivers/i2c/busses/i2c-bcm2835.c @@ -0,0 +1,342 @@ +/* + * BCM2835 master mode driver + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BCM2835_I2C_C 0x0 +#define BCM2835_I2C_S 0x4 +#define BCM2835_I2C_DLEN 0x8 +#define BCM2835_I2C_A 0xc +#define BCM2835_I2C_FIFO 0x10 +#define BCM2835_I2C_DIV 0x14 +#define BCM2835_I2C_DEL 0x18 +#define BCM2835_I2C_CLKT 0x1c + +#define BCM2835_I2C_C_READ BIT(0) +#define BCM2835_I2C_C_CLEAR BIT(4) /* bits 4 and 5 both clear */ +#define BCM2835_I2C_C_ST BIT(7) +#define BCM2835_I2C_C_INTD BIT(8) +#define BCM2835_I2C_C_INTT BIT(9) +#define BCM2835_I2C_C_INTR BIT(10) +#define BCM2835_I2C_C_I2CEN BIT(15) + +#define BCM2835_I2C_S_TA BIT(0) +#define BCM2835_I2C_S_DONE BIT(1) +#define BCM2835_I2C_S_TXW BIT(2) +#define BCM2835_I2C_S_RXR BIT(3) +#define BCM2835_I2C_S_TXD BIT(4) +#define BCM2835_I2C_S_RXD BIT(5) +#define BCM2835_I2C_S_TXE BIT(6) +#define BCM2835_I2C_S_RXF BIT(7) +#define BCM2835_I2C_S_ERR BIT(8) +#define BCM2835_I2C_S_CLKT BIT(9) +#define BCM2835_I2C_S_LEN BIT(10) /* Fake bit for SW error reporting */ + +#define BCM2835_I2C_TIMEOUT (msecs_to_jiffies(1000)) + +struct bcm2835_i2c_dev { + struct device *dev; + void __iomem *regs; + struct clk *clk; + int irq; + struct i2c_adapter adapter; + struct completion completion; + u32 msg_err; + u8 *msg_buf; + size_t msg_buf_remaining; +}; + +static inline void bcm2835_i2c_writel(struct bcm2835_i2c_dev *i2c_dev, + u32 reg, u32 val) +{ + writel(val, i2c_dev->regs + reg); +} + +static inline u32 bcm2835_i2c_readl(struct bcm2835_i2c_dev *i2c_dev, u32 reg) +{ + return readl(i2c_dev->regs + reg); +} + +static void bcm2835_fill_txfifo(struct bcm2835_i2c_dev *i2c_dev) +{ + u32 val; + + while (i2c_dev->msg_buf_remaining) { + val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); + if (!(val & BCM2835_I2C_S_TXD)) + break; + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_FIFO, + *i2c_dev->msg_buf); + i2c_dev->msg_buf++; + i2c_dev->msg_buf_remaining--; + } +} + +static void bcm2835_drain_rxfifo(struct bcm2835_i2c_dev *i2c_dev) +{ + u32 val; + + while (i2c_dev->msg_buf_remaining) { + val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); + if (!(val & BCM2835_I2C_S_RXD)) + break; + *i2c_dev->msg_buf = bcm2835_i2c_readl(i2c_dev, + BCM2835_I2C_FIFO); + i2c_dev->msg_buf++; + i2c_dev->msg_buf_remaining--; + } +} + +static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data) +{ + struct bcm2835_i2c_dev *i2c_dev = data; + u32 val, err; + + val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_S, val); + + err = val & (BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR); + if (err) { + i2c_dev->msg_err = err; + complete(&i2c_dev->completion); + return IRQ_HANDLED; + } + + if (val & BCM2835_I2C_S_RXD) { + bcm2835_drain_rxfifo(i2c_dev); + if (!(val & BCM2835_I2C_S_DONE)) + return IRQ_HANDLED; + } + + if (val & BCM2835_I2C_S_DONE) { + if (i2c_dev->msg_buf_remaining) + i2c_dev->msg_err = BCM2835_I2C_S_LEN; + else + i2c_dev->msg_err = 0; + complete(&i2c_dev->completion); + return IRQ_HANDLED; + } + + if (val & BCM2835_I2C_S_TXD) { + bcm2835_fill_txfifo(i2c_dev); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int bcm2835_i2c_xfer_msg(struct bcm2835_i2c_dev *i2c_dev, + struct i2c_msg *msg) +{ + u32 c; + int time_left; + + i2c_dev->msg_buf = msg->buf; + i2c_dev->msg_buf_remaining = msg->len; + INIT_COMPLETION(i2c_dev->completion); + + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR); + + if (msg->flags & I2C_M_RD) { + c = BCM2835_I2C_C_READ | BCM2835_I2C_C_INTR; + } else { + c = BCM2835_I2C_C_INTT; + bcm2835_fill_txfifo(i2c_dev); + } + c |= BCM2835_I2C_C_ST | BCM2835_I2C_C_INTD | BCM2835_I2C_C_I2CEN; + + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_A, msg->addr); + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DLEN, msg->len); + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, c); + + time_left = wait_for_completion_timeout(&i2c_dev->completion, + BCM2835_I2C_TIMEOUT); + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR); + if (!time_left) { + dev_err(i2c_dev->dev, "i2c transfer timed out\n"); + return -ETIMEDOUT; + } + + if (likely(!i2c_dev->msg_err)) + return 0; + + if ((i2c_dev->msg_err & BCM2835_I2C_S_ERR) && + (msg->flags & I2C_M_IGNORE_NAK)) + return 0; + + dev_err(i2c_dev->dev, "i2c transfer failed: %x\n", i2c_dev->msg_err); + + if (i2c_dev->msg_err & BCM2835_I2C_S_ERR) + return -EREMOTEIO; + else + return -EIO; +} + +static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], + int num) +{ + struct bcm2835_i2c_dev *i2c_dev = i2c_get_adapdata(adap); + int i; + int ret = 0; + + for (i = 0; i < num; i++) { + ret = bcm2835_i2c_xfer_msg(i2c_dev, &msgs[i]); + if (ret) + break; + } + + return ret ?: i; +} + +static u32 bcm2835_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm bcm2835_i2c_algo = { + .master_xfer = bcm2835_i2c_xfer, + .functionality = bcm2835_i2c_func, +}; + +static int bcm2835_i2c_probe(struct platform_device *pdev) +{ + struct bcm2835_i2c_dev *i2c_dev; + struct resource *mem, *requested, *irq; + u32 bus_clk_rate, divider; + int ret; + struct i2c_adapter *adap; + + i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL); + if (!i2c_dev) { + dev_err(&pdev->dev, "Cannot allocate i2c_dev\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, i2c_dev); + i2c_dev->dev = &pdev->dev; + init_completion(&i2c_dev->completion); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No mem resource\n"); + return -ENODEV; + } + + requested = devm_request_mem_region(&pdev->dev, mem->start, + resource_size(mem), + dev_name(&pdev->dev)); + if (!requested) { + dev_err(&pdev->dev, "Could not claim register region\n"); + return -EBUSY; + } + + i2c_dev->regs = devm_ioremap(&pdev->dev, mem->start, + resource_size(mem)); + if (!i2c_dev->regs) { + dev_err(&pdev->dev, "Could not map registers\n"); + return -ENOMEM; + } + + i2c_dev->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(i2c_dev->clk)) { + dev_err(&pdev->dev, "Could not get clock\n"); + return PTR_ERR(i2c_dev->clk); + } + + ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", + &bus_clk_rate); + if (ret < 0) { + dev_warn(&pdev->dev, + "Could not read clock-frequency property\n"); + bus_clk_rate = 100000; + } + + divider = DIV_ROUND_UP(clk_get_rate(i2c_dev->clk), bus_clk_rate); + /* + * Per the datasheet, the register is always interpreted as an even + * number, by rounding down. In other words, the LSB is ignored. So, + * if the LSB is set, increment the divider to avoid any issue. + */ + if (divider & 1) + divider++; + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DIV, divider); + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "No IRQ resource\n"); + return -ENODEV; + } + i2c_dev->irq = irq->start; + + ret = request_irq(i2c_dev->irq, bcm2835_i2c_isr, IRQF_SHARED, + dev_name(&pdev->dev), i2c_dev); + if (ret) { + dev_err(&pdev->dev, "Could not request IRQ\n"); + return -ENODEV; + } + + adap = &i2c_dev->adapter; + i2c_set_adapdata(adap, i2c_dev); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON; + strlcpy(adap->name, "bcm2835 I2C adapter", sizeof(adap->name)); + adap->algo = &bcm2835_i2c_algo; + adap->dev.parent = &pdev->dev; + + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, 0); + + ret = i2c_add_adapter(adap); + if (ret) + free_irq(i2c_dev->irq, i2c_dev); + + return ret; +} + +static int bcm2835_i2c_remove(struct platform_device *pdev) +{ + struct bcm2835_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + + free_irq(i2c_dev->irq, i2c_dev); + i2c_del_adapter(&i2c_dev->adapter); + + return 0; +} + +static const struct of_device_id bcm2835_i2c_of_match[] = { + { .compatible = "brcm,bcm2835-i2c" }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm2835_i2c_of_match); + +static struct platform_driver bcm2835_i2c_driver = { + .probe = bcm2835_i2c_probe, + .remove = bcm2835_i2c_remove, + .driver = { + .name = "i2c-bcm2835", + .owner = THIS_MODULE, + .of_match_table = bcm2835_i2c_of_match, + }, +}; +module_platform_driver(bcm2835_i2c_driver); + +MODULE_AUTHOR("Stephen Warren "); +MODULE_DESCRIPTION("BCM2835 I2C bus adapter"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:i2c-bcm2835"); -- cgit v1.2.3 From 47f531e8ba3bc3901a0c493f4252826c41dea1a1 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Thu, 31 Jan 2013 19:02:03 +0000 Subject: efivarfs: Validate filenames much more aggressively The only thing that efivarfs does to enforce a valid filename is ensure that the name isn't too short. We need to strongly sanitise any filenames, not least because variable creation is delayed until efivarfs_file_write(), which means we can't rely on the firmware to inform us of an invalid name, because if the file is never written to we'll never know it's invalid. Perform a couple of steps before agreeing to create a new file, * hex_to_bin() returns a value indicating whether or not it was able to convert its arguments to a binary representation - we should check it. * Ensure that the GUID portion of the filename is the correct length and format. * The variable name portion of the filename needs to be at least one character in size. Reported-by: Lingzhu Xiang Cc: Matthew Garrett Cc: Jeremy Kerr Cc: Al Viro Signed-off-by: Matt Fleming --- drivers/firmware/efivars.c | 49 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 371c44129525..868cea5cd4b8 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -79,6 +79,7 @@ #include #include #include +#include #include #include @@ -900,6 +901,48 @@ static struct inode *efivarfs_get_inode(struct super_block *sb, return inode; } +/* + * Return true if 'str' is a valid efivarfs filename of the form, + * + * VariableName-12345678-1234-1234-1234-1234567891bc + */ +static bool efivarfs_valid_name(const char *str, int len) +{ + static const char dashes[GUID_LEN] = { + [8] = 1, [13] = 1, [18] = 1, [23] = 1 + }; + const char *s = str + len - GUID_LEN; + int i; + + /* + * We need a GUID, plus at least one letter for the variable name, + * plus the '-' separator + */ + if (len < GUID_LEN + 2) + return false; + + /* GUID should be right after the first '-' */ + if (s - 1 != strchr(str, '-')) + return false; + + /* + * Validate that 's' is of the correct format, e.g. + * + * 12345678-1234-1234-1234-123456789abc + */ + for (i = 0; i < GUID_LEN; i++) { + if (dashes[i]) { + if (*s++ != '-') + return false; + } else { + if (!isxdigit(*s++)) + return false; + } + } + + return true; +} + static void efivarfs_hex_to_guid(const char *str, efi_guid_t *guid) { guid->b[0] = hex_to_bin(str[6]) << 4 | hex_to_bin(str[7]); @@ -928,11 +971,7 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry, struct efivar_entry *var; int namelen, i = 0, err = 0; - /* - * We need a GUID, plus at least one letter for the variable name, - * plus the '-' separator - */ - if (dentry->d_name.len < GUID_LEN + 2) + if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len)) return -EINVAL; inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0); -- cgit v1.2.3 From da27a24383b2b10bf6ebd0db29b325548aafecb4 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 1 Feb 2013 11:02:28 +0000 Subject: efivarfs: guid part of filenames are case-insensitive It makes no sense to treat the following filenames as unique, VarName-abcdefab-abcd-abcd-abcd-abcdefabcdef VarName-ABCDEFAB-ABCD-ABCD-ABCD-ABCDEFABCDEF VarName-ABcDEfAB-ABcD-ABcD-ABcD-ABcDEfABcDEf VarName-aBcDEfAB-aBcD-aBcD-aBcD-aBcDEfaBcDEf ... etc ... since the guid will be converted into a binary representation, which has no case. Roll our own dentry operations so that we can treat the variable name part of filenames ("VarName" in the above example) as case-sensitive, but the guid portion as case-insensitive. That way, efivarfs will refuse to create the above files if any one already exists. Reported-by: Lingzhu Xiang Cc: Matthew Garrett Cc: Jeremy Kerr Cc: Al Viro Signed-off-by: Matt Fleming --- drivers/firmware/efivars.c | 95 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 868cea5cd4b8..8bcb5958f21a 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -1043,6 +1043,84 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry) return -EINVAL; }; +/* + * Compare two efivarfs file names. + * + * An efivarfs filename is composed of two parts, + * + * 1. A case-sensitive variable name + * 2. A case-insensitive GUID + * + * So we need to perform a case-sensitive match on part 1 and a + * case-insensitive match on part 2. + */ +static int efivarfs_d_compare(const struct dentry *parent, const struct inode *pinode, + const struct dentry *dentry, const struct inode *inode, + unsigned int len, const char *str, + const struct qstr *name) +{ + int guid = len - GUID_LEN; + + if (name->len != len) + return 1; + + /* Case-sensitive compare for the variable name */ + if (memcmp(str, name->name, guid)) + return 1; + + /* Case-insensitive compare for the GUID */ + return strncasecmp(name->name + guid, str + guid, GUID_LEN); +} + +static int efivarfs_d_hash(const struct dentry *dentry, + const struct inode *inode, struct qstr *qstr) +{ + unsigned long hash = init_name_hash(); + const unsigned char *s = qstr->name; + unsigned int len = qstr->len; + + if (!efivarfs_valid_name(s, len)) + return -EINVAL; + + while (len-- > GUID_LEN) + hash = partial_name_hash(*s++, hash); + + /* GUID is case-insensitive. */ + while (len--) + hash = partial_name_hash(tolower(*s++), hash); + + qstr->hash = end_name_hash(hash); + return 0; +} + +/* + * Retaining negative dentries for an in-memory filesystem just wastes + * memory and lookup time: arrange for them to be deleted immediately. + */ +static int efivarfs_delete_dentry(const struct dentry *dentry) +{ + return 1; +} + +static struct dentry_operations efivarfs_d_ops = { + .d_compare = efivarfs_d_compare, + .d_hash = efivarfs_d_hash, + .d_delete = efivarfs_delete_dentry, +}; + +static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name) +{ + struct qstr q; + + q.name = name; + q.len = strlen(name); + + if (efivarfs_d_hash(NULL, NULL, &q)) + return NULL; + + return d_alloc(parent, &q); +} + static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) { struct inode *inode = NULL; @@ -1058,6 +1136,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = EFIVARFS_MAGIC; sb->s_op = &efivarfs_ops; + sb->s_d_op = &efivarfs_d_ops; sb->s_time_gran = 1; inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0); @@ -1098,7 +1177,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) if (!inode) goto fail_name; - dentry = d_alloc_name(root, name); + dentry = efivarfs_alloc_dentry(root, name); if (!dentry) goto fail_inode; @@ -1148,8 +1227,20 @@ static struct file_system_type efivarfs_type = { .kill_sb = efivarfs_kill_sb, }; +/* + * Handle negative dentry. + */ +static struct dentry *efivarfs_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) +{ + if (dentry->d_name.len > NAME_MAX) + return ERR_PTR(-ENAMETOOLONG); + d_add(dentry, NULL); + return NULL; +} + static const struct inode_operations efivarfs_dir_inode_operations = { - .lookup = simple_lookup, + .lookup = efivarfs_lookup, .unlink = efivarfs_unlink, .create = efivarfs_create, }; -- cgit v1.2.3 From c0cf787f10c6ffb050e85c8ac9c0a8161cb06cb9 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Wed, 23 Jan 2013 20:47:08 +0800 Subject: mtd: atmel_nand: avoid to report an error when lookup table offset is 0. Before this patch, we assume the whole ROM code are mapping to memory. So it is wrong if the lookup table offset is 0. After this patch, we can map only the lookup table of ROM code to memory intead of the whole ROM code (about 1M). In this case, one lookup table offset can be 0. Signed-off-by: Josh Wu Signed-off-by: Artem Bityutskiy --- drivers/mtd/nand/atmel_nand.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index c516a9408087..1d989dba4ce5 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -1215,7 +1215,7 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) static int atmel_of_init_port(struct atmel_nand_host *host, struct device_node *np) { - u32 val, table_offset; + u32 val; u32 offset[2]; int ecc_mode; struct atmel_nand_data *board = &host->board; @@ -1288,13 +1288,12 @@ static int atmel_of_init_port(struct atmel_nand_host *host, dev_err(host->dev, "Cannot get PMECC lookup table offset\n"); return -EINVAL; } - table_offset = host->pmecc_sector_size == 512 ? offset[0] : offset[1]; - - if (!table_offset) { + if (!offset[0] && !offset[1]) { dev_err(host->dev, "Invalid PMECC lookup table offset\n"); return -EINVAL; } - host->pmecc_lookup_table_offset = table_offset; + host->pmecc_lookup_table_offset = + (host->pmecc_sector_size == 512) ? offset[0] : offset[1]; return 0; } -- cgit v1.2.3 From e66b4318f8fc0e12421de6287c557cdd92789010 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Wed, 23 Jan 2013 20:47:11 +0800 Subject: mtd: atmel_nand: make pmecc-cap, pmecc-sector-size in dts is optional. If those two are not specified in dts file, driver will report an error. TODO: in this case, driver will find ecc requirement in NAND ONFI parameters. Signed-off-by: Josh Wu Signed-off-by: Artem Bityutskiy --- drivers/mtd/nand/atmel_nand.c | 52 ++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 1d989dba4ce5..f186a371e437 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -101,6 +101,8 @@ struct atmel_nand_host { u8 pmecc_corr_cap; u16 pmecc_sector_size; u32 pmecc_lookup_table_offset; + u32 pmecc_lookup_table_offset_512; + u32 pmecc_lookup_table_offset_1024; int pmecc_bytes_per_sector; int pmecc_sector_number; @@ -916,8 +918,16 @@ static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, struct resource *regs, *regs_pmerr, *regs_rom; int cap, sector_size, err_no; + if (host->pmecc_corr_cap == 0 || host->pmecc_sector_size == 0) + /* TODO: Should use ONFI ecc parameters. */ + return -EINVAL; + cap = host->pmecc_corr_cap; sector_size = host->pmecc_sector_size; + host->pmecc_lookup_table_offset = (sector_size == 512) ? + host->pmecc_lookup_table_offset_512 : + host->pmecc_lookup_table_offset_1024; + dev_info(host->dev, "Initialize PMECC params, cap: %d, sector: %d\n", cap, sector_size); @@ -1259,29 +1269,29 @@ static int atmel_of_init_port(struct atmel_nand_host *host, /* use PMECC, get correction capability, sector size and lookup * table offset. + * If correction bits and sector size are not specified, then find + * them from NAND ONFI parameters. */ - if (of_property_read_u32(np, "atmel,pmecc-cap", &val) != 0) { - dev_err(host->dev, "Cannot decide PMECC Capability\n"); - return -EINVAL; - } else if ((val != 2) && (val != 4) && (val != 8) && (val != 12) && - (val != 24)) { - dev_err(host->dev, - "Unsupported PMECC correction capability: %d; should be 2, 4, 8, 12 or 24\n", - val); - return -EINVAL; + if (of_property_read_u32(np, "atmel,pmecc-cap", &val) == 0) { + if ((val != 2) && (val != 4) && (val != 8) && (val != 12) && + (val != 24)) { + dev_err(host->dev, + "Unsupported PMECC correction capability: %d; should be 2, 4, 8, 12 or 24\n", + val); + return -EINVAL; + } + host->pmecc_corr_cap = (u8)val; } - host->pmecc_corr_cap = (u8)val; - if (of_property_read_u32(np, "atmel,pmecc-sector-size", &val) != 0) { - dev_err(host->dev, "Cannot decide PMECC Sector Size\n"); - return -EINVAL; - } else if ((val != 512) && (val != 1024)) { - dev_err(host->dev, - "Unsupported PMECC sector size: %d; should be 512 or 1024 bytes\n", - val); - return -EINVAL; + if (of_property_read_u32(np, "atmel,pmecc-sector-size", &val) == 0) { + if ((val != 512) && (val != 1024)) { + dev_err(host->dev, + "Unsupported PMECC sector size: %d; should be 512 or 1024 bytes\n", + val); + return -EINVAL; + } + host->pmecc_sector_size = (u16)val; } - host->pmecc_sector_size = (u16)val; if (of_property_read_u32_array(np, "atmel,pmecc-lookup-table-offset", offset, 2) != 0) { @@ -1292,8 +1302,8 @@ static int atmel_of_init_port(struct atmel_nand_host *host, dev_err(host->dev, "Invalid PMECC lookup table offset\n"); return -EINVAL; } - host->pmecc_lookup_table_offset = - (host->pmecc_sector_size == 512) ? offset[0] : offset[1]; + host->pmecc_lookup_table_offset_512 = offset[0]; + host->pmecc_lookup_table_offset_1024 = offset[1]; return 0; } -- cgit v1.2.3 From 84cfbbb85a98c8c89407f108da9d102e5dfe2ae5 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Wed, 23 Jan 2013 20:47:12 +0800 Subject: mtd: at91: atmel_nand: for PMECC, add code to check the ONFI parameter ECC requirement. This patch will check NAND flash's ecc minimum requirement in ONFI parameter. 1. if pmecc-cap, pmecc-sector-size is set in dts. then use it. Driver will print out a WARNING if the values are different from ONFI parameters. 2. if pmecc-cap, pmecc-sector-size is not set in dts, then use the value from ONFI parameters. * If ONFI ECC parameters are in ONFI extended parameter page, since we are not support it, so assume the minimum ecc requirement is 2 bits in 512 bytes. * For non-ONFI support nand flash, also assume the minimum ecc requirement is 2 bits in 512 bytes. Signed-off-by: Josh Wu Signed-off-by: Artem Bityutskiy --- drivers/mtd/nand/atmel_nand.c | 90 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index f186a371e437..ffcbcca2fd2d 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -910,6 +910,84 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd) pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE); } +/* + * Get ECC requirement in ONFI parameters, returns -1 if ONFI + * parameters is not supported. + * return 0 if success to get the ECC requirement. + */ +static int get_onfi_ecc_param(struct nand_chip *chip, + int *ecc_bits, int *sector_size) +{ + *ecc_bits = *sector_size = 0; + + if (chip->onfi_params.ecc_bits == 0xff) + /* TODO: the sector_size and ecc_bits need to be find in + * extended ecc parameter, currently we don't support it. + */ + return -1; + + *ecc_bits = chip->onfi_params.ecc_bits; + + /* The default sector size (ecc codeword size) is 512 */ + *sector_size = 512; + + return 0; +} + +/* + * Get ecc requirement from ONFI parameters ecc requirement. + * If pmecc-cap, pmecc-sector-size in DTS are not specified, this function + * will set them according to ONFI ecc requirement. Otherwise, use the + * value in DTS file. + * return 0 if success. otherwise return error code. + */ +static int pmecc_choose_ecc(struct atmel_nand_host *host, + int *cap, int *sector_size) +{ + /* Get ECC requirement from ONFI parameters */ + *cap = *sector_size = 0; + if (host->nand_chip.onfi_version) { + if (!get_onfi_ecc_param(&host->nand_chip, cap, sector_size)) + dev_info(host->dev, "ONFI params, minimum required ECC: %d bits in %d bytes\n", + *cap, *sector_size); + else + dev_info(host->dev, "NAND chip ECC reqirement is in Extended ONFI parameter, we don't support yet.\n"); + } else { + dev_info(host->dev, "NAND chip is not ONFI compliant, assume ecc_bits is 2 in 512 bytes"); + } + if (*cap == 0 && *sector_size == 0) { + *cap = 2; + *sector_size = 512; + } + + /* If dts file doesn't specify then use the one in ONFI parameters */ + if (host->pmecc_corr_cap == 0) { + /* use the most fitable ecc bits (the near bigger one ) */ + if (*cap <= 2) + host->pmecc_corr_cap = 2; + else if (*cap <= 4) + host->pmecc_corr_cap = 4; + else if (*cap < 8) + host->pmecc_corr_cap = 8; + else if (*cap < 12) + host->pmecc_corr_cap = 12; + else if (*cap < 24) + host->pmecc_corr_cap = 24; + else + return -EINVAL; + } + if (host->pmecc_sector_size == 0) { + /* use the most fitable sector size (the near smaller one ) */ + if (*sector_size >= 1024) + host->pmecc_sector_size = 1024; + else if (*sector_size >= 512) + host->pmecc_sector_size = 512; + else + return -EINVAL; + } + return 0; +} + static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, struct atmel_nand_host *host) { @@ -918,9 +996,15 @@ static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, struct resource *regs, *regs_pmerr, *regs_rom; int cap, sector_size, err_no; - if (host->pmecc_corr_cap == 0 || host->pmecc_sector_size == 0) - /* TODO: Should use ONFI ecc parameters. */ - return -EINVAL; + err_no = pmecc_choose_ecc(host, &cap, §or_size); + if (err_no) { + dev_err(host->dev, "The NAND flash's ECC requirement are not support!"); + return err_no; + } + + if (cap != host->pmecc_corr_cap || + sector_size != host->pmecc_sector_size) + dev_info(host->dev, "WARNING: Be Caution! Using different PMECC parameters from Nand ONFI ECC reqirement.\n"); cap = host->pmecc_corr_cap; sector_size = host->pmecc_sector_size; -- cgit v1.2.3 From eb82038f97f93c5f0ff274fb98a9fff741dc2f5e Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 25 Jan 2013 11:50:27 -0300 Subject: mtd: physmap_of: Convert device allocation to managed devm_kzalloc() Signed-off-by: Ezequiel Garcia Signed-off-by: Artem Bityutskiy --- drivers/mtd/maps/physmap_of.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 263d9e1b8001..500d7f4f8028 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -68,9 +68,6 @@ static int of_flash_remove(struct platform_device *dev) kfree(info->list[i].res); } } - - kfree(info); - return 0; } @@ -199,8 +196,9 @@ static int of_flash_probe(struct platform_device *dev) map_indirect = of_property_read_bool(dp, "no-unaligned-direct-access"); err = -ENOMEM; - info = kzalloc(sizeof(struct of_flash) + - sizeof(struct of_flash_list) * count, GFP_KERNEL); + info = devm_kzalloc(&dev->dev, + sizeof(struct of_flash) + + sizeof(struct of_flash_list) * count, GFP_KERNEL); if (!info) goto err_flash_remove; -- cgit v1.2.3 From cfdf5b6cc5985014a7ce891093f4fd0ae2d27ca6 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 7 Feb 2013 17:36:28 +0200 Subject: dw_dmac: add support for Lynxpoint DMA controllers Intel Lynxpoint PCH Low Power Subsystem has DMA controller to support general purpose serial buses like SPI, I2C, and HSUART. This controller is enumerated from ACPI namespace with ACPI ID INTL9C60. Signed-off-by: Mika Westerberg Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 35c8dd5f1013..6694676c9b82 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1899,6 +1899,11 @@ static const struct of_device_id dw_dma_id_table[] = { MODULE_DEVICE_TABLE(of, dw_dma_id_table); #endif +static const struct platform_device_id dw_dma_ids[] = { + { "INTL9C60", 0 }, + { } +}; + static struct platform_driver dw_driver = { .probe = dw_probe, .remove = dw_remove, @@ -1908,6 +1913,7 @@ static struct platform_driver dw_driver = { .pm = &dw_dev_pm_ops, .of_match_table = of_match_ptr(dw_dma_id_table), }, + .id_table = dw_dma_ids, }; static int __init dw_init(void) -- cgit v1.2.3 From 4dec23d7718e6f1f5e1773698d112025169e7d49 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 7 Feb 2013 14:38:32 -0700 Subject: ioatdma: fix race between updating ioat->head and IOAT_COMPLETION_PENDING There is a race that can hit during __cleanup() when the ioat->head pointer is incremented during descriptor submission. The __cleanup() can clear the PENDING flag when it does not see any active descriptors. This causes new submitted descriptors to be ignored because the COMPLETION_PENDING flag is cleared. This was introduced when code was adapted from ioatdma v1 to ioatdma v2. For v2 and v3, IOAT_COMPLETION_PENDING flag will be abandoned and a new flag IOAT_CHAN_ACTIVE will be utilized. This flag will also be protected under the prep_lock when being modified in order to avoid the race. Signed-off-by: Dave Jiang Reviewed-by: Dan Williams Signed-off-by: Vinod Koul --- drivers/dma/ioat/dma.h | 1 + drivers/dma/ioat/dma_v2.c | 113 ++++++++++++++++++++++++++-------------------- drivers/dma/ioat/dma_v3.c | 111 +++++++++++++++++++++++++-------------------- 3 files changed, 128 insertions(+), 97 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/ioat/dma.h b/drivers/dma/ioat/dma.h index 5e8fe01ba69d..1020598b80d3 100644 --- a/drivers/dma/ioat/dma.h +++ b/drivers/dma/ioat/dma.h @@ -97,6 +97,7 @@ struct ioat_chan_common { #define IOAT_KOBJ_INIT_FAIL 3 #define IOAT_RESHAPE_PENDING 4 #define IOAT_RUN 5 + #define IOAT_CHAN_ACTIVE 6 struct timer_list timer; #define COMPLETION_TIMEOUT msecs_to_jiffies(100) #define IDLE_TIMEOUT msecs_to_jiffies(2000) diff --git a/drivers/dma/ioat/dma_v2.c b/drivers/dma/ioat/dma_v2.c index b9d667851445..e2b2c70f03db 100644 --- a/drivers/dma/ioat/dma_v2.c +++ b/drivers/dma/ioat/dma_v2.c @@ -269,61 +269,22 @@ static void ioat2_restart_channel(struct ioat2_dma_chan *ioat) __ioat2_restart_chan(ioat); } -void ioat2_timer_event(unsigned long data) +static void check_active(struct ioat2_dma_chan *ioat) { - struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); struct ioat_chan_common *chan = &ioat->base; - if (test_bit(IOAT_COMPLETION_PENDING, &chan->state)) { - dma_addr_t phys_complete; - u64 status; - - status = ioat_chansts(chan); - - /* when halted due to errors check for channel - * programming errors before advancing the completion state - */ - if (is_ioat_halted(status)) { - u32 chanerr; - - chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); - dev_err(to_dev(chan), "%s: Channel halted (%x)\n", - __func__, chanerr); - if (test_bit(IOAT_RUN, &chan->state)) - BUG_ON(is_ioat_bug(chanerr)); - else /* we never got off the ground */ - return; - } - - /* if we haven't made progress and we have already - * acknowledged a pending completion once, then be more - * forceful with a restart - */ - spin_lock_bh(&chan->cleanup_lock); - if (ioat_cleanup_preamble(chan, &phys_complete)) { - __cleanup(ioat, phys_complete); - } else if (test_bit(IOAT_COMPLETION_ACK, &chan->state)) { - spin_lock_bh(&ioat->prep_lock); - ioat2_restart_channel(ioat); - spin_unlock_bh(&ioat->prep_lock); - } else { - set_bit(IOAT_COMPLETION_ACK, &chan->state); - mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); - } - spin_unlock_bh(&chan->cleanup_lock); - } else { - u16 active; + if (ioat2_ring_active(ioat)) { + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + return; + } + if (test_and_clear_bit(IOAT_CHAN_ACTIVE, &chan->state)) + mod_timer(&chan->timer, jiffies + IDLE_TIMEOUT); + else if (ioat->alloc_order > ioat_get_alloc_order()) { /* if the ring is idle, empty, and oversized try to step * down the size */ - spin_lock_bh(&chan->cleanup_lock); - spin_lock_bh(&ioat->prep_lock); - active = ioat2_ring_active(ioat); - if (active == 0 && ioat->alloc_order > ioat_get_alloc_order()) - reshape_ring(ioat, ioat->alloc_order-1); - spin_unlock_bh(&ioat->prep_lock); - spin_unlock_bh(&chan->cleanup_lock); + reshape_ring(ioat, ioat->alloc_order - 1); /* keep shrinking until we get back to our minimum * default size @@ -331,6 +292,60 @@ void ioat2_timer_event(unsigned long data) if (ioat->alloc_order > ioat_get_alloc_order()) mod_timer(&chan->timer, jiffies + IDLE_TIMEOUT); } + +} + +void ioat2_timer_event(unsigned long data) +{ + struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); + struct ioat_chan_common *chan = &ioat->base; + dma_addr_t phys_complete; + u64 status; + + status = ioat_chansts(chan); + + /* when halted due to errors check for channel + * programming errors before advancing the completion state + */ + if (is_ioat_halted(status)) { + u32 chanerr; + + chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); + dev_err(to_dev(chan), "%s: Channel halted (%x)\n", + __func__, chanerr); + if (test_bit(IOAT_RUN, &chan->state)) + BUG_ON(is_ioat_bug(chanerr)); + else /* we never got off the ground */ + return; + } + + /* if we haven't made progress and we have already + * acknowledged a pending completion once, then be more + * forceful with a restart + */ + spin_lock_bh(&chan->cleanup_lock); + if (ioat_cleanup_preamble(chan, &phys_complete)) + __cleanup(ioat, phys_complete); + else if (test_bit(IOAT_COMPLETION_ACK, &chan->state)) { + spin_lock_bh(&ioat->prep_lock); + ioat2_restart_channel(ioat); + spin_unlock_bh(&ioat->prep_lock); + spin_unlock_bh(&chan->cleanup_lock); + return; + } else { + set_bit(IOAT_COMPLETION_ACK, &chan->state); + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + } + + + if (ioat2_ring_active(ioat)) + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + else { + spin_lock_bh(&ioat->prep_lock); + check_active(ioat); + spin_unlock_bh(&ioat->prep_lock); + } + spin_unlock_bh(&chan->cleanup_lock); } static int ioat2_reset_hw(struct ioat_chan_common *chan) @@ -404,7 +419,7 @@ static dma_cookie_t ioat2_tx_submit_unlock(struct dma_async_tx_descriptor *tx) cookie = dma_cookie_assign(tx); dev_dbg(to_dev(&ioat->base), "%s: cookie: %d\n", __func__, cookie); - if (!test_and_set_bit(IOAT_COMPLETION_PENDING, &chan->state)) + if (!test_and_set_bit(IOAT_CHAN_ACTIVE, &chan->state)) mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); /* make descriptor updates visible before advancing ioat->head, diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index e52cf1eb6839..570dd6320c32 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -342,61 +342,22 @@ static void ioat3_restart_channel(struct ioat2_dma_chan *ioat) __ioat2_restart_chan(ioat); } -static void ioat3_timer_event(unsigned long data) +static void check_active(struct ioat2_dma_chan *ioat) { - struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); struct ioat_chan_common *chan = &ioat->base; - if (test_bit(IOAT_COMPLETION_PENDING, &chan->state)) { - dma_addr_t phys_complete; - u64 status; - - status = ioat_chansts(chan); - - /* when halted due to errors check for channel - * programming errors before advancing the completion state - */ - if (is_ioat_halted(status)) { - u32 chanerr; - - chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); - dev_err(to_dev(chan), "%s: Channel halted (%x)\n", - __func__, chanerr); - if (test_bit(IOAT_RUN, &chan->state)) - BUG_ON(is_ioat_bug(chanerr)); - else /* we never got off the ground */ - return; - } - - /* if we haven't made progress and we have already - * acknowledged a pending completion once, then be more - * forceful with a restart - */ - spin_lock_bh(&chan->cleanup_lock); - if (ioat_cleanup_preamble(chan, &phys_complete)) - __cleanup(ioat, phys_complete); - else if (test_bit(IOAT_COMPLETION_ACK, &chan->state)) { - spin_lock_bh(&ioat->prep_lock); - ioat3_restart_channel(ioat); - spin_unlock_bh(&ioat->prep_lock); - } else { - set_bit(IOAT_COMPLETION_ACK, &chan->state); - mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); - } - spin_unlock_bh(&chan->cleanup_lock); - } else { - u16 active; + if (ioat2_ring_active(ioat)) { + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + return; + } + if (test_and_clear_bit(IOAT_CHAN_ACTIVE, &chan->state)) + mod_timer(&chan->timer, jiffies + IDLE_TIMEOUT); + else if (ioat->alloc_order > ioat_get_alloc_order()) { /* if the ring is idle, empty, and oversized try to step * down the size */ - spin_lock_bh(&chan->cleanup_lock); - spin_lock_bh(&ioat->prep_lock); - active = ioat2_ring_active(ioat); - if (active == 0 && ioat->alloc_order > ioat_get_alloc_order()) - reshape_ring(ioat, ioat->alloc_order-1); - spin_unlock_bh(&ioat->prep_lock); - spin_unlock_bh(&chan->cleanup_lock); + reshape_ring(ioat, ioat->alloc_order - 1); /* keep shrinking until we get back to our minimum * default size @@ -404,6 +365,60 @@ static void ioat3_timer_event(unsigned long data) if (ioat->alloc_order > ioat_get_alloc_order()) mod_timer(&chan->timer, jiffies + IDLE_TIMEOUT); } + +} + +void ioat3_timer_event(unsigned long data) +{ + struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); + struct ioat_chan_common *chan = &ioat->base; + dma_addr_t phys_complete; + u64 status; + + status = ioat_chansts(chan); + + /* when halted due to errors check for channel + * programming errors before advancing the completion state + */ + if (is_ioat_halted(status)) { + u32 chanerr; + + chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); + dev_err(to_dev(chan), "%s: Channel halted (%x)\n", + __func__, chanerr); + if (test_bit(IOAT_RUN, &chan->state)) + BUG_ON(is_ioat_bug(chanerr)); + else /* we never got off the ground */ + return; + } + + /* if we haven't made progress and we have already + * acknowledged a pending completion once, then be more + * forceful with a restart + */ + spin_lock_bh(&chan->cleanup_lock); + if (ioat_cleanup_preamble(chan, &phys_complete)) + __cleanup(ioat, phys_complete); + else if (test_bit(IOAT_COMPLETION_ACK, &chan->state)) { + spin_lock_bh(&ioat->prep_lock); + ioat3_restart_channel(ioat); + spin_unlock_bh(&ioat->prep_lock); + spin_unlock_bh(&chan->cleanup_lock); + return; + } else { + set_bit(IOAT_COMPLETION_ACK, &chan->state); + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + } + + + if (ioat2_ring_active(ioat)) + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + else { + spin_lock_bh(&ioat->prep_lock); + check_active(ioat); + spin_unlock_bh(&ioat->prep_lock); + } + spin_unlock_bh(&chan->cleanup_lock); } static enum dma_status -- cgit v1.2.3 From b2a17029c29bb6f7dec580feffa715b9fcf51e42 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 13 Feb 2013 16:59:28 +1030 Subject: virtio: use module_virtio_driver. Signed-off-by: Rusty Russell --- drivers/char/hw_random/virtio-rng.c | 13 +------------ drivers/net/virtio_net.c | 12 +----------- drivers/virtio/virtio_balloon.c | 13 +------------ 3 files changed, 3 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index b65c10395959..10fd71ccf587 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -154,18 +154,7 @@ static struct virtio_driver virtio_rng_driver = { #endif }; -static int __init init(void) -{ - return register_virtio_driver(&virtio_rng_driver); -} - -static void __exit fini(void) -{ - unregister_virtio_driver(&virtio_rng_driver); -} -module_init(init); -module_exit(fini); - +module_virtio_driver(virtio_rng_driver); MODULE_DEVICE_TABLE(virtio, id_table); MODULE_DESCRIPTION("Virtio random number driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index a6fcf15adc4f..80fb683d9d3a 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1645,17 +1645,7 @@ static struct virtio_driver virtio_net_driver = { #endif }; -static int __init init(void) -{ - return register_virtio_driver(&virtio_net_driver); -} - -static void __exit fini(void) -{ - unregister_virtio_driver(&virtio_net_driver); -} -module_init(init); -module_exit(fini); +module_virtio_driver(virtio_net_driver); MODULE_DEVICE_TABLE(virtio, id_table); MODULE_DESCRIPTION("Virtio network driver"); diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 797e1c79a104..8dab163c5ef0 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -560,18 +560,7 @@ static struct virtio_driver virtio_balloon_driver = { #endif }; -static int __init init(void) -{ - return register_virtio_driver(&virtio_balloon_driver); -} - -static void __exit fini(void) -{ - unregister_virtio_driver(&virtio_balloon_driver); -} -module_init(init); -module_exit(fini); - +module_virtio_driver(virtio_balloon_driver); MODULE_DEVICE_TABLE(virtio, id_table); MODULE_DESCRIPTION("Virtio balloon driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 8078db789a92b10ff6e2d713231b5367e014c53b Mon Sep 17 00:00:00 2001 From: Sjur Brændeland Date: Wed, 13 Feb 2013 20:57:21 +1030 Subject: virtio_console: Initialize guest_connected=true for rproc_serial MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When rproc_serial is initialized, guest_connected should be set to true. We can then revert the extra checks introduced in commit: "virtio_console: Let unconnected rproc device receive data." Signed-off-by: Sjur Brændeland Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 2cfd5a0575ab..5afc8f614b1c 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1436,7 +1436,7 @@ static int add_port(struct ports_device *portdev, u32 id) * rproc_serial does not want the console port, only * the generic port implementation. */ - port->host_connected = true; + port->host_connected = port->guest_connected = true; else if (!use_multiport(port->portdev)) { /* * If we're not using multiport support, @@ -1757,11 +1757,8 @@ static void in_intr(struct virtqueue *vq) * tty is spawned) and the host sends out data to console * ports. For generic serial ports, the host won't * (shouldn't) send data till the guest is connected. - * However a remote device might send data before the port is - * connected. So don't remove data from a rproc_serial device. */ - - if (!port->guest_connected && !is_rproc_serial(port->portdev->vdev)) + if (!port->guest_connected) discard_port_data(port); spin_unlock_irqrestore(&port->inbuf_lock, flags); -- cgit v1.2.3 From db3985e5ca8f50fc17606855ba394783d11683a5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 13 Feb 2013 20:37:48 +1000 Subject: Revert "drm: Add EDID_QUIRK_FORCE_REDUCED_BLANKING for ASUS VW222S" This reverts commit 6f33814bd4d9cfe76033a31b1c0c76c960cd8e4b. The quirk cause a regression, and it looks like the original bug was simply a lack of FIFO bandwidth on the i915G of the reporter. Which should eventually be fixed as soon as we get around to implemented DSPARB FIFO reassignment on gen 3. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=52281 Cc: stable@vger.kernel.org Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_edid.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index a3a3b61059ff..51324256a657 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -87,9 +87,6 @@ static struct edid_quirk { int product_id; u32 quirks; } edid_quirk_list[] = { - /* ASUS VW222S */ - { "ACI", 0x22a2, EDID_QUIRK_FORCE_REDUCED_BLANKING }, - /* Acer AL1706 */ { "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 }, /* Acer F51 */ -- cgit v1.2.3 From 91457df7733564015f846c501424610318a5b85f Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Tue, 12 Feb 2013 05:01:50 +0100 Subject: iommu/amd: Remove redundant NULL check before dma_ops_domain_free(). dma_ops_domain_free on a NULL pointer is a no-op, so the NULL check in amd_iommu_init_dma_ops() can be removed. Signed-off-by: Cyril Roelandt Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index c1c74e030a58..f6f4a62ddf09 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -3187,8 +3187,7 @@ int __init amd_iommu_init_dma_ops(void) free_domains: for_each_iommu(iommu) { - if (iommu->default_dom) - dma_ops_domain_free(iommu->default_dom); + dma_ops_domain_free(iommu->default_dom); } return ret; -- cgit v1.2.3 From 9d9c6ae79c5e3452721c5eaebdd793edde9d93df Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 13 Feb 2013 13:16:25 +0200 Subject: mfd: omap-usb-host: Consolidate OMAP USB-HS platform data (part 2/3) Let's have a single platform data structure for the OMAP's High-Speed USB host subsystem instead of having 3 separate ones i.e. one for board data, one for USB Host (UHH) module and one for USB-TLL module. This makes the code much simpler and avoids creating multiple copies of platform data. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi For the ehci-omap.c part: Acked-by: Alan Stern --- drivers/mfd/omap-usb-host.c | 63 ++++++++++++++++++-------------------------- drivers/mfd/omap-usb-tll.c | 11 ++++---- drivers/usb/host/ehci-omap.c | 6 ++--- 3 files changed, 34 insertions(+), 46 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 05164d7f054b..d6e6b8ca854c 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -102,7 +102,7 @@ struct usbhs_hcd_omap { void __iomem *uhh_base; - struct usbhs_omap_platform_data platdata; + struct usbhs_omap_platform_data *pdata; u32 usbhs_rev; spinlock_t lock; @@ -184,19 +184,13 @@ err_end: static int omap_usbhs_alloc_children(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct usbhs_hcd_omap *omap; - struct ehci_hcd_omap_platform_data *ehci_data; - struct ohci_hcd_omap_platform_data *ohci_data; + struct usbhs_omap_platform_data *pdata = dev->platform_data; struct platform_device *ehci; struct platform_device *ohci; struct resource *res; struct resource resources[2]; int ret; - omap = platform_get_drvdata(pdev); - ehci_data = omap->platdata.ehci_data; - ohci_data = omap->platdata.ohci_data; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ehci"); if (!res) { dev_err(dev, "EHCI get resource IORESOURCE_MEM failed\n"); @@ -213,8 +207,8 @@ static int omap_usbhs_alloc_children(struct platform_device *pdev) } resources[1] = *res; - ehci = omap_usbhs_alloc_child(OMAP_EHCI_DEVICE, resources, 2, ehci_data, - sizeof(*ehci_data), dev); + ehci = omap_usbhs_alloc_child(OMAP_EHCI_DEVICE, resources, 2, pdata, + sizeof(*pdata), dev); if (!ehci) { dev_err(dev, "omap_usbhs_alloc_child failed\n"); @@ -238,8 +232,8 @@ static int omap_usbhs_alloc_children(struct platform_device *pdev) } resources[1] = *res; - ohci = omap_usbhs_alloc_child(OMAP_OHCI_DEVICE, resources, 2, ohci_data, - sizeof(*ohci_data), dev); + ohci = omap_usbhs_alloc_child(OMAP_OHCI_DEVICE, resources, 2, pdata, + sizeof(*pdata), dev); if (!ohci) { dev_err(dev, "omap_usbhs_alloc_child failed\n"); ret = -ENOMEM; @@ -278,7 +272,7 @@ static bool is_ohci_port(enum usbhs_omap_port_mode pmode) static int usbhs_runtime_resume(struct device *dev) { struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); - struct usbhs_omap_platform_data *pdata = &omap->platdata; + struct usbhs_omap_platform_data *pdata = omap->pdata; unsigned long flags; dev_dbg(dev, "usbhs_runtime_resume\n"); @@ -310,7 +304,7 @@ static int usbhs_runtime_resume(struct device *dev) static int usbhs_runtime_suspend(struct device *dev) { struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); - struct usbhs_omap_platform_data *pdata = &omap->platdata; + struct usbhs_omap_platform_data *pdata = omap->pdata; unsigned long flags; dev_dbg(dev, "usbhs_runtime_suspend\n"); @@ -342,19 +336,19 @@ static int usbhs_runtime_suspend(struct device *dev) static void omap_usbhs_init(struct device *dev) { struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); - struct usbhs_omap_platform_data *pdata = &omap->platdata; + struct usbhs_omap_platform_data *pdata = omap->pdata; unsigned long flags; unsigned reg; dev_dbg(dev, "starting TI HSUSB Controller\n"); - if (pdata->ehci_data->phy_reset) { - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) - gpio_request_one(pdata->ehci_data->reset_gpio_port[0], + if (pdata->phy_reset) { + if (gpio_is_valid(pdata->reset_gpio_port[0])) + gpio_request_one(pdata->reset_gpio_port[0], GPIOF_OUT_INIT_LOW, "USB1 PHY reset"); - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) - gpio_request_one(pdata->ehci_data->reset_gpio_port[1], + if (gpio_is_valid(pdata->reset_gpio_port[1])) + gpio_request_one(pdata->reset_gpio_port[1], GPIOF_OUT_INIT_LOW, "USB2 PHY reset"); /* Hold the PHY in RESET for enough time till DIR is high */ @@ -430,33 +424,33 @@ static void omap_usbhs_init(struct device *dev) spin_unlock_irqrestore(&omap->lock, flags); pm_runtime_put_sync(dev); - if (pdata->ehci_data->phy_reset) { + if (pdata->phy_reset) { /* Hold the PHY in RESET for enough time till * PHY is settled and ready */ udelay(10); - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) + if (gpio_is_valid(pdata->reset_gpio_port[0])) gpio_set_value_cansleep - (pdata->ehci_data->reset_gpio_port[0], 1); + (pdata->reset_gpio_port[0], 1); - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) + if (gpio_is_valid(pdata->reset_gpio_port[1])) gpio_set_value_cansleep - (pdata->ehci_data->reset_gpio_port[1], 1); + (pdata->reset_gpio_port[1], 1); } } static void omap_usbhs_deinit(struct device *dev) { struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); - struct usbhs_omap_platform_data *pdata = &omap->platdata; + struct usbhs_omap_platform_data *pdata = omap->pdata; - if (pdata->ehci_data->phy_reset) { - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) - gpio_free(pdata->ehci_data->reset_gpio_port[0]); + if (pdata->phy_reset) { + if (gpio_is_valid(pdata->reset_gpio_port[0])) + gpio_free(pdata->reset_gpio_port[0]); - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) - gpio_free(pdata->ehci_data->reset_gpio_port[1]); + if (gpio_is_valid(pdata->reset_gpio_port[1])) + gpio_free(pdata->reset_gpio_port[1]); } } @@ -490,15 +484,10 @@ static int usbhs_omap_probe(struct platform_device *pdev) spin_lock_init(&omap->lock); - for (i = 0; i < OMAP3_HS_USB_PORTS; i++) - omap->platdata.port_mode[i] = pdata->port_mode[i]; - - omap->platdata.ehci_data = pdata->ehci_data; - omap->platdata.ohci_data = pdata->ohci_data; + omap->pdata = pdata; pm_runtime_enable(dev); - for (i = 0; i < OMAP3_HS_USB_PORTS; i++) if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) || is_ehci_hsic_mode(i)) { diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index eb869153206d..e45948969907 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -98,7 +98,7 @@ struct usbtll_omap { struct clk *usbtll_p1_fck; struct clk *usbtll_p2_fck; - struct usbtll_omap_platform_data platdata; + struct usbhs_omap_platform_data *pdata; /* secure the register updates */ spinlock_t lock; }; @@ -203,7 +203,7 @@ static unsigned ohci_omap3_fslsmode(enum usbhs_omap_port_mode mode) static int usbtll_omap_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct usbtll_omap_platform_data *pdata = dev->platform_data; + struct usbhs_omap_platform_data *pdata = dev->platform_data; void __iomem *base; struct resource *res; struct usbtll_omap *tll; @@ -223,8 +223,7 @@ static int usbtll_omap_probe(struct platform_device *pdev) spin_lock_init(&tll->lock); - for (i = 0; i < OMAP3_HS_USB_PORTS; i++) - tll->platdata.port_mode[i] = pdata->port_mode[i]; + tll->pdata = pdata; tll->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk"); if (IS_ERR(tll->usbtll_p1_fck)) { @@ -362,7 +361,7 @@ static int usbtll_omap_remove(struct platform_device *pdev) static int usbtll_runtime_resume(struct device *dev) { struct usbtll_omap *tll = dev_get_drvdata(dev); - struct usbtll_omap_platform_data *pdata = &tll->platdata; + struct usbhs_omap_platform_data *pdata = tll->pdata; unsigned long flags; dev_dbg(dev, "usbtll_runtime_resume\n"); @@ -388,7 +387,7 @@ static int usbtll_runtime_resume(struct device *dev) static int usbtll_runtime_suspend(struct device *dev) { struct usbtll_omap *tll = dev_get_drvdata(dev); - struct usbtll_omap_platform_data *pdata = &tll->platdata; + struct usbhs_omap_platform_data *pdata = tll->pdata; unsigned long flags; dev_dbg(dev, "usbtll_runtime_suspend\n"); diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index ac17a7c3a0cd..5d954d7b290d 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -107,7 +107,7 @@ static int omap_ehci_init(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); int rc; - struct ehci_hcd_omap_platform_data *pdata; + struct usbhs_omap_platform_data *pdata; pdata = hcd->self.controller->platform_data; @@ -151,7 +151,7 @@ static int omap_ehci_init(struct usb_hcd *hcd) } static void disable_put_regulator( - struct ehci_hcd_omap_platform_data *pdata) + struct usbhs_omap_platform_data *pdata) { int i; @@ -176,7 +176,7 @@ static void disable_put_regulator( static int ehci_hcd_omap_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct ehci_hcd_omap_platform_data *pdata = dev->platform_data; + struct usbhs_omap_platform_data *pdata = dev->platform_data; struct resource *res; struct usb_hcd *hcd; void __iomem *regs; -- cgit v1.2.3 From 7e0ff1035cb7efe9c3621c6173a123603ca33857 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 26 Nov 2012 12:28:44 +0200 Subject: mfd: omap-usb-tll: Fix channel count detection Fix channel count detecion for REV2. Also, don't give up if we don't recognize the IP Revision. We assume the default number of channels (i.e. 3) for unrecognized IPs. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-tll.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index e45948969907..9658e1843334 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -98,6 +98,7 @@ struct usbtll_omap { struct clk *usbtll_p1_fck; struct clk *usbtll_p2_fck; + int nch; /* num. of channels */ struct usbhs_omap_platform_data *pdata; /* secure the register updates */ spinlock_t lock; @@ -210,7 +211,7 @@ static int usbtll_omap_probe(struct platform_device *pdev) unsigned reg; unsigned long flags; int ret = 0; - int i, ver, count; + int i, ver; dev_dbg(dev, "starting TI HSUSB TLL Controller\n"); @@ -262,16 +263,18 @@ static int usbtll_omap_probe(struct platform_device *pdev) ver = usbtll_read(base, OMAP_USBTLL_REVISION); switch (ver) { case OMAP_USBTLL_REV1: - case OMAP_USBTLL_REV2: - count = OMAP_TLL_CHANNEL_COUNT; + tll->nch = OMAP_TLL_CHANNEL_COUNT; break; + case OMAP_USBTLL_REV2: case OMAP_USBTLL_REV3: - count = OMAP_REV2_TLL_CHANNEL_COUNT; + tll->nch = OMAP_REV2_TLL_CHANNEL_COUNT; break; default: - dev_err(dev, "TLL version failed\n"); - ret = -ENODEV; - goto err_ioremap; + tll->nch = OMAP_TLL_CHANNEL_COUNT; + dev_dbg(dev, + "USB TLL Rev : 0x%x not recognized, assuming %d channels\n", + ver, tll->nch); + break; } if (is_ehci_tll_mode(pdata->port_mode[0]) || @@ -291,7 +294,7 @@ static int usbtll_omap_probe(struct platform_device *pdev) usbtll_write(base, OMAP_TLL_SHARED_CONF, reg); /* Enable channels now */ - for (i = 0; i < count; i++) { + for (i = 0; i < tll->nch; i++) { reg = usbtll_read(base, OMAP_TLL_CHANNEL_CONF(i)); if (is_ohci_port(pdata->port_mode[i])) { @@ -319,7 +322,6 @@ static int usbtll_omap_probe(struct platform_device *pdev) } } -err_ioremap: spin_unlock_irqrestore(&tll->lock, flags); iounmap(base); pm_runtime_put_sync(dev); -- cgit v1.2.3 From 1a7a8d70cddcf4f6847e42b414da1c3113675873 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 26 Nov 2012 12:56:37 +0200 Subject: mfd: omap-usb-tll: Use devm_kzalloc/ioremap and clean up error path Use devm_ variants of kzalloc() and ioremap(). Simplify the error path. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-tll.c | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index 9658e1843334..9a19cc722c76 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -215,11 +215,10 @@ static int usbtll_omap_probe(struct platform_device *pdev) dev_dbg(dev, "starting TI HSUSB TLL Controller\n"); - tll = kzalloc(sizeof(struct usbtll_omap), GFP_KERNEL); + tll = devm_kzalloc(dev, sizeof(struct usbtll_omap), GFP_KERNEL); if (!tll) { dev_err(dev, "Memory allocation failed\n"); - ret = -ENOMEM; - goto end; + return -ENOMEM; } spin_lock_init(&tll->lock); @@ -230,28 +229,22 @@ static int usbtll_omap_probe(struct platform_device *pdev) if (IS_ERR(tll->usbtll_p1_fck)) { ret = PTR_ERR(tll->usbtll_p1_fck); dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret); - goto err_tll; + return ret; } tll->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk"); if (IS_ERR(tll->usbtll_p2_fck)) { ret = PTR_ERR(tll->usbtll_p2_fck); dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret); - goto err_usbtll_p1_fck; + goto err_p2_fck; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "usb tll get resource failed\n"); - ret = -ENODEV; - goto err_usbtll_p2_fck; - } - - base = ioremap(res->start, resource_size(res)); + base = devm_request_and_ioremap(dev, res); if (!base) { - dev_err(dev, "TLL ioremap failed\n"); - ret = -ENOMEM; - goto err_usbtll_p2_fck; + ret = -EADDRNOTAVAIL; + dev_err(dev, "Resource request/ioremap failed:%d\n", ret); + goto err_res; } platform_set_drvdata(pdev, tll); @@ -323,23 +316,17 @@ static int usbtll_omap_probe(struct platform_device *pdev) } spin_unlock_irqrestore(&tll->lock, flags); - iounmap(base); pm_runtime_put_sync(dev); tll_pdev = pdev; - if (!ret) - goto end; - pm_runtime_disable(dev); -err_usbtll_p2_fck: + return 0; + +err_res: clk_put(tll->usbtll_p2_fck); -err_usbtll_p1_fck: +err_p2_fck: clk_put(tll->usbtll_p1_fck); -err_tll: - kfree(tll); - -end: return ret; } @@ -356,7 +343,6 @@ static int usbtll_omap_remove(struct platform_device *pdev) clk_put(tll->usbtll_p2_fck); clk_put(tll->usbtll_p1_fck); pm_runtime_disable(&pdev->dev); - kfree(tll); return 0; } -- cgit v1.2.3 From 0bde3e9fee74b668857b1664a70998d02807fb5b Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 8 Nov 2012 13:07:09 +0200 Subject: mfd: omap-usb-tll: Clean up clock handling Every channel has a functional clock that is similarly named. It makes sense to use a for loop to manage these clocks as OMAPs can come with up to 3 channels. Dynamically allocate and get channel clocks depending on the number of clocks avaiable on the platform. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi Acked-by: Russell King --- drivers/mfd/omap-usb-tll.c | 87 ++++++++++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index 9a19cc722c76..bf7355e1126d 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -96,10 +96,9 @@ #define is_ehci_tll_mode(x) (x == OMAP_EHCI_PORT_MODE_TLL) struct usbtll_omap { - struct clk *usbtll_p1_fck; - struct clk *usbtll_p2_fck; int nch; /* num. of channels */ struct usbhs_omap_platform_data *pdata; + struct clk **ch_clk; /* secure the register updates */ spinlock_t lock; }; @@ -225,26 +224,12 @@ static int usbtll_omap_probe(struct platform_device *pdev) tll->pdata = pdata; - tll->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk"); - if (IS_ERR(tll->usbtll_p1_fck)) { - ret = PTR_ERR(tll->usbtll_p1_fck); - dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret); - return ret; - } - - tll->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk"); - if (IS_ERR(tll->usbtll_p2_fck)) { - ret = PTR_ERR(tll->usbtll_p2_fck); - dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret); - goto err_p2_fck; - } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_request_and_ioremap(dev, res); if (!base) { ret = -EADDRNOTAVAIL; dev_err(dev, "Resource request/ioremap failed:%d\n", ret); - goto err_res; + return ret; } platform_set_drvdata(pdev, tll); @@ -270,6 +255,29 @@ static int usbtll_omap_probe(struct platform_device *pdev) break; } + spin_unlock_irqrestore(&tll->lock, flags); + + tll->ch_clk = devm_kzalloc(dev, sizeof(struct clk * [tll->nch]), + GFP_KERNEL); + if (!tll->ch_clk) { + ret = -ENOMEM; + dev_err(dev, "Couldn't allocate memory for channel clocks\n"); + goto err_clk_alloc; + } + + for (i = 0; i < tll->nch; i++) { + char clkname[] = "usb_tll_hs_usb_chx_clk"; + + snprintf(clkname, sizeof(clkname), + "usb_tll_hs_usb_ch%d_clk", i); + tll->ch_clk[i] = clk_get(dev, clkname); + + if (IS_ERR(tll->ch_clk[i])) + dev_dbg(dev, "can't get clock : %s\n", clkname); + } + + spin_lock_irqsave(&tll->lock, flags); + if (is_ehci_tll_mode(pdata->port_mode[0]) || is_ehci_tll_mode(pdata->port_mode[1]) || is_ehci_tll_mode(pdata->port_mode[2]) || @@ -321,11 +329,9 @@ static int usbtll_omap_probe(struct platform_device *pdev) return 0; -err_res: - clk_put(tll->usbtll_p2_fck); - -err_p2_fck: - clk_put(tll->usbtll_p1_fck); +err_clk_alloc: + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); return ret; } @@ -339,9 +345,12 @@ err_p2_fck: static int usbtll_omap_remove(struct platform_device *pdev) { struct usbtll_omap *tll = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < tll->nch; i++) + if (!IS_ERR(tll->ch_clk[i])) + clk_put(tll->ch_clk[i]); - clk_put(tll->usbtll_p2_fck); - clk_put(tll->usbtll_p1_fck); pm_runtime_disable(&pdev->dev); return 0; } @@ -351,6 +360,7 @@ static int usbtll_runtime_resume(struct device *dev) struct usbtll_omap *tll = dev_get_drvdata(dev); struct usbhs_omap_platform_data *pdata = tll->pdata; unsigned long flags; + int i; dev_dbg(dev, "usbtll_runtime_resume\n"); @@ -361,11 +371,20 @@ static int usbtll_runtime_resume(struct device *dev) spin_lock_irqsave(&tll->lock, flags); - if (is_ehci_tll_mode(pdata->port_mode[0])) - clk_enable(tll->usbtll_p1_fck); + for (i = 0; i < tll->nch; i++) { + if (is_ehci_tll_mode(pdata->port_mode[i])) { + int r; - if (is_ehci_tll_mode(pdata->port_mode[1])) - clk_enable(tll->usbtll_p2_fck); + if (IS_ERR(tll->ch_clk[i])) + continue; + + r = clk_enable(tll->ch_clk[i]); + if (r) { + dev_err(dev, + "Error enabling ch %d clock: %d\n", i, r); + } + } + } spin_unlock_irqrestore(&tll->lock, flags); @@ -377,6 +396,7 @@ static int usbtll_runtime_suspend(struct device *dev) struct usbtll_omap *tll = dev_get_drvdata(dev); struct usbhs_omap_platform_data *pdata = tll->pdata; unsigned long flags; + int i; dev_dbg(dev, "usbtll_runtime_suspend\n"); @@ -387,11 +407,12 @@ static int usbtll_runtime_suspend(struct device *dev) spin_lock_irqsave(&tll->lock, flags); - if (is_ehci_tll_mode(pdata->port_mode[0])) - clk_disable(tll->usbtll_p1_fck); - - if (is_ehci_tll_mode(pdata->port_mode[1])) - clk_disable(tll->usbtll_p2_fck); + for (i = 0; i < tll->nch; i++) { + if (is_ehci_tll_mode(pdata->port_mode[i])) { + if (!IS_ERR(tll->ch_clk[i])) + clk_disable(tll->ch_clk[i]); + } + } spin_unlock_irqrestore(&tll->lock, flags); -- cgit v1.2.3 From 32a51f2a536fd045c472779c13ac09e9e80ac3cc Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 8 Nov 2012 14:40:45 +0200 Subject: mfd: omap-usb-tll: introduce and use mode_needs_tll() This is a handy macro to check if the port requires the USB TLL module or not. Use it to Enable the TLL module and manage the clocks. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-tll.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index bf7355e1126d..af67b96ded0f 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -95,6 +95,10 @@ #define is_ehci_tll_mode(x) (x == OMAP_EHCI_PORT_MODE_TLL) +/* only PHY and UNUSED modes don't need TLL */ +#define omap_usb_mode_needs_tll(x) ((x) != OMAP_USBHS_PORT_MODE_UNUSED &&\ + (x) != OMAP_EHCI_PORT_MODE_PHY) + struct usbtll_omap { int nch; /* num. of channels */ struct usbhs_omap_platform_data *pdata; @@ -211,6 +215,7 @@ static int usbtll_omap_probe(struct platform_device *pdev) unsigned long flags; int ret = 0; int i, ver; + bool needs_tll; dev_dbg(dev, "starting TI HSUSB TLL Controller\n"); @@ -278,12 +283,11 @@ static int usbtll_omap_probe(struct platform_device *pdev) spin_lock_irqsave(&tll->lock, flags); - if (is_ehci_tll_mode(pdata->port_mode[0]) || - is_ehci_tll_mode(pdata->port_mode[1]) || - is_ehci_tll_mode(pdata->port_mode[2]) || - is_ohci_port(pdata->port_mode[0]) || - is_ohci_port(pdata->port_mode[1]) || - is_ohci_port(pdata->port_mode[2])) { + needs_tll = false; + for (i = 0; i < tll->nch; i++) + needs_tll |= omap_usb_mode_needs_tll(pdata->port_mode[i]); + + if (needs_tll) { /* Program Common TLL register */ reg = usbtll_read(base, OMAP_TLL_SHARED_CONF); @@ -372,7 +376,7 @@ static int usbtll_runtime_resume(struct device *dev) spin_lock_irqsave(&tll->lock, flags); for (i = 0; i < tll->nch; i++) { - if (is_ehci_tll_mode(pdata->port_mode[i])) { + if (omap_usb_mode_needs_tll(pdata->port_mode[i])) { int r; if (IS_ERR(tll->ch_clk[i])) @@ -408,7 +412,7 @@ static int usbtll_runtime_suspend(struct device *dev) spin_lock_irqsave(&tll->lock, flags); for (i = 0; i < tll->nch; i++) { - if (is_ehci_tll_mode(pdata->port_mode[i])) { + if (omap_usb_mode_needs_tll(pdata->port_mode[i])) { if (!IS_ERR(tll->ch_clk[i])) clk_disable(tll->ch_clk[i]); } -- cgit v1.2.3 From 8bdbd1549479840293e46bf0f84d7174e77b7b5e Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 26 Nov 2012 15:17:32 +0200 Subject: mfd: omap-usb-tll: Check for missing platform data in probe No need to check for missing platform data in runtime_suspend/resume as it makes more sense to do it in the probe function. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-tll.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index af67b96ded0f..3dbbfea515ad 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -225,6 +225,11 @@ static int usbtll_omap_probe(struct platform_device *pdev) return -ENOMEM; } + if (!pdata) { + dev_err(dev, "Platform data missing\n"); + return -ENODEV; + } + spin_lock_init(&tll->lock); tll->pdata = pdata; @@ -368,11 +373,6 @@ static int usbtll_runtime_resume(struct device *dev) dev_dbg(dev, "usbtll_runtime_resume\n"); - if (!pdata) { - dev_dbg(dev, "missing platform_data\n"); - return -ENODEV; - } - spin_lock_irqsave(&tll->lock, flags); for (i = 0; i < tll->nch; i++) { @@ -404,11 +404,6 @@ static int usbtll_runtime_suspend(struct device *dev) dev_dbg(dev, "usbtll_runtime_suspend\n"); - if (!pdata) { - dev_dbg(dev, "missing platform_data\n"); - return -ENODEV; - } - spin_lock_irqsave(&tll->lock, flags); for (i = 0; i < tll->nch; i++) { -- cgit v1.2.3 From 7ed8619141198191d09f63fa6f172af361ad280d Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 26 Nov 2012 15:26:28 +0200 Subject: mfd: omap-usb-tll: Fix error message omap_enable/disable_tll() can fail if TLL device is not initialized. It could be due to multiple reasons and not only due to missing platform data. Also make local variables static and use 'struct device *' instead of 'struct platform_device *' for global reference. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-tll.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index 3dbbfea515ad..eccc65e97202 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -109,8 +109,8 @@ struct usbtll_omap { /*-------------------------------------------------------------------------*/ -const char usbtll_driver_name[] = USBTLL_DRIVER_NAME; -struct platform_device *tll_pdev; +static const char usbtll_driver_name[] = USBTLL_DRIVER_NAME; +static struct device *tll_dev; /*-------------------------------------------------------------------------*/ @@ -334,7 +334,8 @@ static int usbtll_omap_probe(struct platform_device *pdev) spin_unlock_irqrestore(&tll->lock, flags); pm_runtime_put_sync(dev); - tll_pdev = pdev; + /* only after this can omap_tll_enable/disable work */ + tll_dev = dev; return 0; @@ -356,6 +357,8 @@ static int usbtll_omap_remove(struct platform_device *pdev) struct usbtll_omap *tll = platform_get_drvdata(pdev); int i; + tll_dev = NULL; + for (i = 0; i < tll->nch; i++) if (!IS_ERR(tll->ch_clk[i])) clk_put(tll->ch_clk[i]); @@ -436,21 +439,21 @@ static struct platform_driver usbtll_omap_driver = { int omap_tll_enable(void) { - if (!tll_pdev) { - pr_err("missing omap usbhs tll platform_data\n"); + if (!tll_dev) { + pr_err("%s: OMAP USB TLL not initialized\n", __func__); return -ENODEV; } - return pm_runtime_get_sync(&tll_pdev->dev); + return pm_runtime_get_sync(tll_dev); } EXPORT_SYMBOL_GPL(omap_tll_enable); int omap_tll_disable(void) { - if (!tll_pdev) { - pr_err("missing omap usbhs tll platform_data\n"); + if (!tll_dev) { + pr_err("%s: OMAP USB TLL not initialized\n", __func__); return -ENODEV; } - return pm_runtime_put_sync(&tll_pdev->dev); + return pm_runtime_put_sync(tll_dev); } EXPORT_SYMBOL_GPL(omap_tll_disable); -- cgit v1.2.3 From 6675144668883a506f0c11db668b582bd5a3161d Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 26 Nov 2012 16:56:26 +0200 Subject: mfd: omap-usb-tll: serialize access to TLL device Get rid of the unnecessary spin_lock_irqsave/restore() as there is no interrupt handler for this driver. Instead we serialize access to tll_dev using a global spinlock. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-tll.c | 53 +++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index eccc65e97202..55c85c78ba44 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -103,14 +103,13 @@ struct usbtll_omap { int nch; /* num. of channels */ struct usbhs_omap_platform_data *pdata; struct clk **ch_clk; - /* secure the register updates */ - spinlock_t lock; }; /*-------------------------------------------------------------------------*/ static const char usbtll_driver_name[] = USBTLL_DRIVER_NAME; static struct device *tll_dev; +static DEFINE_SPINLOCK(tll_lock); /* serialize access to tll_dev */ /*-------------------------------------------------------------------------*/ @@ -212,7 +211,6 @@ static int usbtll_omap_probe(struct platform_device *pdev) struct resource *res; struct usbtll_omap *tll; unsigned reg; - unsigned long flags; int ret = 0; int i, ver; bool needs_tll; @@ -230,8 +228,6 @@ static int usbtll_omap_probe(struct platform_device *pdev) return -ENODEV; } - spin_lock_init(&tll->lock); - tll->pdata = pdata; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -246,8 +242,6 @@ static int usbtll_omap_probe(struct platform_device *pdev) pm_runtime_enable(dev); pm_runtime_get_sync(dev); - spin_lock_irqsave(&tll->lock, flags); - ver = usbtll_read(base, OMAP_USBTLL_REVISION); switch (ver) { case OMAP_USBTLL_REV1: @@ -265,8 +259,6 @@ static int usbtll_omap_probe(struct platform_device *pdev) break; } - spin_unlock_irqrestore(&tll->lock, flags); - tll->ch_clk = devm_kzalloc(dev, sizeof(struct clk * [tll->nch]), GFP_KERNEL); if (!tll->ch_clk) { @@ -286,8 +278,6 @@ static int usbtll_omap_probe(struct platform_device *pdev) dev_dbg(dev, "can't get clock : %s\n", clkname); } - spin_lock_irqsave(&tll->lock, flags); - needs_tll = false; for (i = 0; i < tll->nch; i++) needs_tll |= omap_usb_mode_needs_tll(pdata->port_mode[i]); @@ -332,10 +322,11 @@ static int usbtll_omap_probe(struct platform_device *pdev) } } - spin_unlock_irqrestore(&tll->lock, flags); pm_runtime_put_sync(dev); /* only after this can omap_tll_enable/disable work */ + spin_lock(&tll_lock); tll_dev = dev; + spin_unlock(&tll_lock); return 0; @@ -357,7 +348,9 @@ static int usbtll_omap_remove(struct platform_device *pdev) struct usbtll_omap *tll = platform_get_drvdata(pdev); int i; + spin_lock(&tll_lock); tll_dev = NULL; + spin_unlock(&tll_lock); for (i = 0; i < tll->nch; i++) if (!IS_ERR(tll->ch_clk[i])) @@ -371,13 +364,10 @@ static int usbtll_runtime_resume(struct device *dev) { struct usbtll_omap *tll = dev_get_drvdata(dev); struct usbhs_omap_platform_data *pdata = tll->pdata; - unsigned long flags; int i; dev_dbg(dev, "usbtll_runtime_resume\n"); - spin_lock_irqsave(&tll->lock, flags); - for (i = 0; i < tll->nch; i++) { if (omap_usb_mode_needs_tll(pdata->port_mode[i])) { int r; @@ -393,8 +383,6 @@ static int usbtll_runtime_resume(struct device *dev) } } - spin_unlock_irqrestore(&tll->lock, flags); - return 0; } @@ -402,13 +390,10 @@ static int usbtll_runtime_suspend(struct device *dev) { struct usbtll_omap *tll = dev_get_drvdata(dev); struct usbhs_omap_platform_data *pdata = tll->pdata; - unsigned long flags; int i; dev_dbg(dev, "usbtll_runtime_suspend\n"); - spin_lock_irqsave(&tll->lock, flags); - for (i = 0; i < tll->nch; i++) { if (omap_usb_mode_needs_tll(pdata->port_mode[i])) { if (!IS_ERR(tll->ch_clk[i])) @@ -416,8 +401,6 @@ static int usbtll_runtime_suspend(struct device *dev) } } - spin_unlock_irqrestore(&tll->lock, flags); - return 0; } @@ -439,21 +422,39 @@ static struct platform_driver usbtll_omap_driver = { int omap_tll_enable(void) { + int ret; + + spin_lock(&tll_lock); + if (!tll_dev) { pr_err("%s: OMAP USB TLL not initialized\n", __func__); - return -ENODEV; + ret = -ENODEV; + } else { + ret = pm_runtime_get_sync(tll_dev); } - return pm_runtime_get_sync(tll_dev); + + spin_unlock(&tll_lock); + + return ret; } EXPORT_SYMBOL_GPL(omap_tll_enable); int omap_tll_disable(void) { + int ret; + + spin_lock(&tll_lock); + if (!tll_dev) { pr_err("%s: OMAP USB TLL not initialized\n", __func__); - return -ENODEV; + ret = -ENODEV; + } else { + ret = pm_runtime_put_sync(tll_dev); } - return pm_runtime_put_sync(tll_dev); + + spin_unlock(&tll_lock); + + return ret; } EXPORT_SYMBOL_GPL(omap_tll_disable); -- cgit v1.2.3 From 300c2f8ff4745f65d99cf105e9afef0f01b70d09 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 8 Nov 2012 16:10:41 +0200 Subject: mfd: omap-usb-tll: Add OMAP5 revision and HSIC support The TLL module on OMAP5 has 3 channels. HSIC mode requires the TLL channel to be in Transparent UTMI mode. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-tll.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index 55c85c78ba44..0aef1a768880 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -54,10 +54,13 @@ #define OMAP_TLL_CHANNEL_CONF(num) (0x040 + 0x004 * num) #define OMAP_TLL_CHANNEL_CONF_FSLSMODE_SHIFT 24 +#define OMAP_TLL_CHANNEL_CONF_DRVVBUS (1 << 16) +#define OMAP_TLL_CHANNEL_CONF_CHRGVBUS (1 << 15) #define OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF (1 << 11) #define OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE (1 << 10) #define OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE (1 << 9) #define OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE (1 << 8) +#define OMAP_TLL_CHANNEL_CONF_MODE_TRANSPARENT_UTMI (2 << 1) #define OMAP_TLL_CHANNEL_CONF_CHANMODE_FSLS (1 << 1) #define OMAP_TLL_CHANNEL_CONF_CHANEN (1 << 0) @@ -92,6 +95,7 @@ #define OMAP_USBTLL_REV1 0x00000015 /* OMAP3 */ #define OMAP_USBTLL_REV2 0x00000018 /* OMAP 3630 */ #define OMAP_USBTLL_REV3 0x00000004 /* OMAP4 */ +#define OMAP_USBTLL_REV4 0x00000006 /* OMAP5 */ #define is_ehci_tll_mode(x) (x == OMAP_EHCI_PORT_MODE_TLL) @@ -245,6 +249,7 @@ static int usbtll_omap_probe(struct platform_device *pdev) ver = usbtll_read(base, OMAP_USBTLL_REVISION); switch (ver) { case OMAP_USBTLL_REV1: + case OMAP_USBTLL_REV4: tll->nch = OMAP_TLL_CHANNEL_COUNT; break; case OMAP_USBTLL_REV2: @@ -310,6 +315,15 @@ static int usbtll_omap_probe(struct platform_device *pdev) reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF | OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE); + } else if (pdata->port_mode[i] == + OMAP_EHCI_PORT_MODE_HSIC) { + /* + * HSIC Mode requires UTMI port configurations + */ + reg |= OMAP_TLL_CHANNEL_CONF_DRVVBUS + | OMAP_TLL_CHANNEL_CONF_CHRGVBUS + | OMAP_TLL_CHANNEL_CONF_MODE_TRANSPARENT_UTMI + | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF; } else { continue; } -- cgit v1.2.3 From a1f0d7a1f8cede5585c13c2f1c79e86a05d425ec Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 8 Nov 2012 18:41:56 +0200 Subject: mfd: omap_usb_host: Avoid missing platform data checks in suspend/resume Get rid of the unnecessary missing platform data checks in runtime_suspend/resume. We are already checking for missing platform data in probe. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index d6e6b8ca854c..061366dce8f6 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -277,11 +277,6 @@ static int usbhs_runtime_resume(struct device *dev) dev_dbg(dev, "usbhs_runtime_resume\n"); - if (!pdata) { - dev_dbg(dev, "missing platform_data\n"); - return -ENODEV; - } - omap_tll_enable(); spin_lock_irqsave(&omap->lock, flags); @@ -309,11 +304,6 @@ static int usbhs_runtime_suspend(struct device *dev) dev_dbg(dev, "usbhs_runtime_suspend\n"); - if (!pdata) { - dev_dbg(dev, "missing platform_data\n"); - return -ENODEV; - } - spin_lock_irqsave(&omap->lock, flags); if (is_ehci_tll_mode(pdata->port_mode[0])) -- cgit v1.2.3 From 27d4f2c654e39bed13374af9537633de414057b6 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 26 Nov 2012 17:59:22 +0200 Subject: mfd: omap-usb-host: Use devm_kzalloc() and devm_request_and_ioremap() Use devm_ variants of kzalloc and ioremap. Also clean up error path. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 38 +++++++++++--------------------------- 1 file changed, 11 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 061366dce8f6..310aaa9731ee 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -461,15 +461,20 @@ static int usbhs_omap_probe(struct platform_device *pdev) if (!pdata) { dev_err(dev, "Missing platform data\n"); - ret = -ENOMEM; - goto end_probe; + return -ENODEV; } - omap = kzalloc(sizeof(*omap), GFP_KERNEL); + omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL); if (!omap) { dev_err(dev, "Memory allocation failed\n"); - ret = -ENOMEM; - goto end_probe; + return -ENOMEM; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uhh"); + omap->uhh_base = devm_request_and_ioremap(dev, res); + if (!omap->uhh_base) { + dev_err(dev, "Resource request/ioremap failed\n"); + return -EADDRNOTAVAIL; } spin_lock_init(&omap->lock); @@ -568,20 +573,6 @@ static int usbhs_omap_probe(struct platform_device *pdev) "failed error:%d\n", ret); } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uhh"); - if (!res) { - dev_err(dev, "UHH EHCI get resource failed\n"); - ret = -ENODEV; - goto err_init_60m_fclk; - } - - omap->uhh_base = ioremap(res->start, resource_size(res)); - if (!omap->uhh_base) { - dev_err(dev, "UHH ioremap failed\n"); - ret = -ENOMEM; - goto err_init_60m_fclk; - } - platform_set_drvdata(pdev, omap); omap_usbhs_init(dev); @@ -591,13 +582,10 @@ static int usbhs_omap_probe(struct platform_device *pdev) goto err_alloc; } - goto end_probe; + return 0; err_alloc: omap_usbhs_deinit(&pdev->dev); - iounmap(omap->uhh_base); - -err_init_60m_fclk: clk_put(omap->init_60m_fclk); err_usbhost_p2_fck: @@ -621,9 +609,7 @@ err_utmi_p1_fck: err_end: clk_put(omap->ehci_logic_fck); pm_runtime_disable(dev); - kfree(omap); -end_probe: return ret; } @@ -638,7 +624,6 @@ static int usbhs_omap_remove(struct platform_device *pdev) struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev); omap_usbhs_deinit(&pdev->dev); - iounmap(omap->uhh_base); clk_put(omap->init_60m_fclk); clk_put(omap->usbhost_p2_fck); clk_put(omap->usbhost_p1_fck); @@ -648,7 +633,6 @@ static int usbhs_omap_remove(struct platform_device *pdev) clk_put(omap->utmi_p1_fck); clk_put(omap->ehci_logic_fck); pm_runtime_disable(&pdev->dev); - kfree(omap); return 0; } -- cgit v1.2.3 From d7eaf866104757d66ccb6627dccf7fb9d07aae7e Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 8 Nov 2012 18:04:26 +0200 Subject: mfd: omap-usb-host: know about number of ports from revision register The revision register should tell us how many ports are present. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 310aaa9731ee..26319ca72e38 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -91,6 +91,8 @@ struct usbhs_hcd_omap { + int nports; + struct clk *xclk60mhsp1_ck; struct clk *xclk60mhsp2_ck; struct clk *utmi_p1_fck; @@ -347,8 +349,6 @@ static void omap_usbhs_init(struct device *dev) pm_runtime_get_sync(dev); spin_lock_irqsave(&omap->lock, flags); - omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION); - dev_dbg(dev, "OMAP UHH_REVISION 0x%x\n", omap->usbhs_rev); reg = usbhs_read(omap->uhh_base, OMAP_UHH_HOSTCONFIG); /* setup ULPI bypass and burst configurations */ @@ -483,7 +483,32 @@ static int usbhs_omap_probe(struct platform_device *pdev) pm_runtime_enable(dev); - for (i = 0; i < OMAP3_HS_USB_PORTS; i++) + platform_set_drvdata(pdev, omap); + pm_runtime_get_sync(dev); + + omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION); + + /* we need to call runtime suspend before we update omap->nports + * to prevent unbalanced clk_disable() + */ + pm_runtime_put_sync(dev); + + switch (omap->usbhs_rev) { + case OMAP_USBHS_REV1: + omap->nports = 3; + break; + case OMAP_USBHS_REV2: + omap->nports = 2; + break; + default: + omap->nports = OMAP3_HS_USB_PORTS; + dev_dbg(dev, + "USB HOST Rev : 0x%d not recognized, assuming %d ports\n", + omap->usbhs_rev, omap->nports); + break; + } + + for (i = 0; i < omap->nports; i++) if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) || is_ehci_hsic_mode(i)) { omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck"); @@ -573,8 +598,6 @@ static int usbhs_omap_probe(struct platform_device *pdev) "failed error:%d\n", ret); } - platform_set_drvdata(pdev, omap); - omap_usbhs_init(dev); ret = omap_usbhs_alloc_children(pdev); if (ret) { -- cgit v1.2.3 From ccac71a7f063ad31eb99fac37e95b70ff57f1354 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 8 Nov 2012 19:18:08 +0200 Subject: mfd: omap-usb-host: override number of ports from platform data Both OMAP4 and 5 exhibit the same revision ID in the REVISION register but they have different number of ports i.e. 2 and 3 respectively. So we can't rely on REVISION register for number of ports on OMAP5 and depend on platform data (or device tree) instead. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 34 +++++++++++++++++++++------------- include/linux/platform_data/usb-omap.h | 1 + 2 files changed, 22 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 26319ca72e38..779588be8ab2 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -493,19 +493,27 @@ static int usbhs_omap_probe(struct platform_device *pdev) */ pm_runtime_put_sync(dev); - switch (omap->usbhs_rev) { - case OMAP_USBHS_REV1: - omap->nports = 3; - break; - case OMAP_USBHS_REV2: - omap->nports = 2; - break; - default: - omap->nports = OMAP3_HS_USB_PORTS; - dev_dbg(dev, - "USB HOST Rev : 0x%d not recognized, assuming %d ports\n", - omap->usbhs_rev, omap->nports); - break; + /* + * If platform data contains nports then use that + * else make out number of ports from USBHS revision + */ + if (pdata->nports) { + omap->nports = pdata->nports; + } else { + switch (omap->usbhs_rev) { + case OMAP_USBHS_REV1: + omap->nports = 3; + break; + case OMAP_USBHS_REV2: + omap->nports = 2; + break; + default: + omap->nports = OMAP3_HS_USB_PORTS; + dev_dbg(dev, + "USB HOST Rev:0x%d not recognized, assuming %d ports\n", + omap->usbhs_rev, omap->nports); + break; + } } for (i = 0; i < omap->nports; i++) diff --git a/include/linux/platform_data/usb-omap.h b/include/linux/platform_data/usb-omap.h index e697c85ad3bc..fa579b4c666b 100644 --- a/include/linux/platform_data/usb-omap.h +++ b/include/linux/platform_data/usb-omap.h @@ -55,6 +55,7 @@ struct ohci_hcd_omap_platform_data { }; struct usbhs_omap_platform_data { + int nports; enum usbhs_omap_port_mode port_mode[OMAP3_HS_USB_PORTS]; int reset_gpio_port[OMAP3_HS_USB_PORTS]; struct regulator *regulator[OMAP3_HS_USB_PORTS]; -- cgit v1.2.3 From 06ba7dc75fbf1ac731dd3ca08d5f95f552780cb2 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 8 Nov 2012 17:40:25 +0200 Subject: mfd: omap-usb-host: cleanup clock management code All ports have similarly named port clocks so we can bunch them into a port data structure and use for loop to enable/disable the clocks. Dynamically allocate and get clocks based on number of ports available on the platform Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 186 +++++++++++++++++++++++++------------------- 1 file changed, 106 insertions(+), 80 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 779588be8ab2..9fa0215e3df4 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -92,13 +92,12 @@ struct usbhs_hcd_omap { int nports; + struct clk **utmi_clk; struct clk *xclk60mhsp1_ck; struct clk *xclk60mhsp2_ck; - struct clk *utmi_p1_fck; - struct clk *usbhost_p1_fck; - struct clk *utmi_p2_fck; - struct clk *usbhost_p2_fck; + struct clk *utmi_p1_gfclk; + struct clk *utmi_p2_gfclk; struct clk *init_60m_fclk; struct clk *ehci_logic_fck; @@ -276,22 +275,25 @@ static int usbhs_runtime_resume(struct device *dev) struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); struct usbhs_omap_platform_data *pdata = omap->pdata; unsigned long flags; + int i, r; dev_dbg(dev, "usbhs_runtime_resume\n"); omap_tll_enable(); spin_lock_irqsave(&omap->lock, flags); - if (omap->ehci_logic_fck && !IS_ERR(omap->ehci_logic_fck)) + if (!IS_ERR(omap->ehci_logic_fck)) clk_enable(omap->ehci_logic_fck); - if (is_ehci_tll_mode(pdata->port_mode[0])) - clk_enable(omap->usbhost_p1_fck); - if (is_ehci_tll_mode(pdata->port_mode[1])) - clk_enable(omap->usbhost_p2_fck); + for (i = 0; i < omap->nports; i++) { + if (!is_ehci_tll_mode(pdata->port_mode[i]) || + IS_ERR(omap->utmi_clk[i])) + continue; - clk_enable(omap->utmi_p1_fck); - clk_enable(omap->utmi_p2_fck); + r = clk_enable(omap->utmi_clk[i]); + if (r) + dev_err(dev, "Can't enable port %d clk : %d\n", i, r); + } spin_unlock_irqrestore(&omap->lock, flags); @@ -303,20 +305,19 @@ static int usbhs_runtime_suspend(struct device *dev) struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); struct usbhs_omap_platform_data *pdata = omap->pdata; unsigned long flags; + int i; dev_dbg(dev, "usbhs_runtime_suspend\n"); spin_lock_irqsave(&omap->lock, flags); - if (is_ehci_tll_mode(pdata->port_mode[0])) - clk_disable(omap->usbhost_p1_fck); - if (is_ehci_tll_mode(pdata->port_mode[1])) - clk_disable(omap->usbhost_p2_fck); - - clk_disable(omap->utmi_p2_fck); - clk_disable(omap->utmi_p1_fck); + for (i = 0; i < omap->nports; i++) { + if (is_ehci_tll_mode(pdata->port_mode[i]) && + !IS_ERR(omap->utmi_clk[i])) + clk_disable(omap->utmi_clk[i]); + } - if (omap->ehci_logic_fck && !IS_ERR(omap->ehci_logic_fck)) + if (!IS_ERR(omap->ehci_logic_fck)) clk_disable(omap->ehci_logic_fck); spin_unlock_irqrestore(&omap->lock, flags); @@ -458,6 +459,7 @@ static int usbhs_omap_probe(struct platform_device *pdev) struct resource *res; int ret = 0; int i; + bool need_logic_fck; if (!pdata) { dev_err(dev, "Missing platform data\n"); @@ -516,76 +518,91 @@ static int usbhs_omap_probe(struct platform_device *pdev) } } - for (i = 0; i < omap->nports; i++) + i = sizeof(struct clk *) * omap->nports; + omap->utmi_clk = devm_kzalloc(dev, i, GFP_KERNEL); + if (!omap->utmi_clk) { + dev_err(dev, "Memory allocation failed\n"); + ret = -ENOMEM; + goto err_mem; + } + + need_logic_fck = false; + for (i = 0; i < omap->nports; i++) { if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) || - is_ehci_hsic_mode(i)) { - omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck"); - if (IS_ERR(omap->ehci_logic_fck)) { - ret = PTR_ERR(omap->ehci_logic_fck); - dev_warn(dev, "ehci_logic_fck failed:%d\n", - ret); - } - break; + is_ehci_hsic_mode(i)) + need_logic_fck |= true; + } + + omap->ehci_logic_fck = ERR_PTR(-EINVAL); + if (need_logic_fck) { + omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck"); + if (IS_ERR(omap->ehci_logic_fck)) { + ret = PTR_ERR(omap->ehci_logic_fck); + dev_dbg(dev, "ehci_logic_fck failed:%d\n", ret); } + } - omap->utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk"); - if (IS_ERR(omap->utmi_p1_fck)) { - ret = PTR_ERR(omap->utmi_p1_fck); - dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret); - goto err_end; + omap->utmi_p1_gfclk = clk_get(dev, "utmi_p1_gfclk"); + if (IS_ERR(omap->utmi_p1_gfclk)) { + ret = PTR_ERR(omap->utmi_p1_gfclk); + dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret); + goto err_p1_gfclk; + } + + omap->utmi_p2_gfclk = clk_get(dev, "utmi_p2_gfclk"); + if (IS_ERR(omap->utmi_p2_gfclk)) { + ret = PTR_ERR(omap->utmi_p2_gfclk); + dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret); + goto err_p2_gfclk; } omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck"); if (IS_ERR(omap->xclk60mhsp1_ck)) { ret = PTR_ERR(omap->xclk60mhsp1_ck); dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret); - goto err_utmi_p1_fck; - } - - omap->utmi_p2_fck = clk_get(dev, "utmi_p2_gfclk"); - if (IS_ERR(omap->utmi_p2_fck)) { - ret = PTR_ERR(omap->utmi_p2_fck); - dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret); - goto err_xclk60mhsp1_ck; + goto err_xclk60mhsp1; } omap->xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck"); if (IS_ERR(omap->xclk60mhsp2_ck)) { ret = PTR_ERR(omap->xclk60mhsp2_ck); dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret); - goto err_utmi_p2_fck; - } - - omap->usbhost_p1_fck = clk_get(dev, "usb_host_hs_utmi_p1_clk"); - if (IS_ERR(omap->usbhost_p1_fck)) { - ret = PTR_ERR(omap->usbhost_p1_fck); - dev_err(dev, "usbhost_p1_fck failed error:%d\n", ret); - goto err_xclk60mhsp2_ck; - } - - omap->usbhost_p2_fck = clk_get(dev, "usb_host_hs_utmi_p2_clk"); - if (IS_ERR(omap->usbhost_p2_fck)) { - ret = PTR_ERR(omap->usbhost_p2_fck); - dev_err(dev, "usbhost_p2_fck failed error:%d\n", ret); - goto err_usbhost_p1_fck; + goto err_xclk60mhsp2; } omap->init_60m_fclk = clk_get(dev, "init_60m_fclk"); if (IS_ERR(omap->init_60m_fclk)) { ret = PTR_ERR(omap->init_60m_fclk); dev_err(dev, "init_60m_fclk failed error:%d\n", ret); - goto err_usbhost_p2_fck; + goto err_init60m; + } + + for (i = 0; i < omap->nports; i++) { + char clkname[] = "usb_host_hs_utmi_px_clk"; + + /* clock names are indexed from 1*/ + snprintf(clkname, sizeof(clkname), + "usb_host_hs_utmi_p%d_clk", i + 1); + + /* If a clock is not found we won't bail out as not all + * platforms have all clocks and we can function without + * them + */ + omap->utmi_clk[i] = clk_get(dev, clkname); + if (IS_ERR(omap->utmi_clk[i])) + dev_dbg(dev, "Failed to get clock : %s : %ld\n", + clkname, PTR_ERR(omap->utmi_clk[i])); } if (is_ehci_phy_mode(pdata->port_mode[0])) { /* for OMAP3 , the clk set paretn fails */ - ret = clk_set_parent(omap->utmi_p1_fck, + ret = clk_set_parent(omap->utmi_p1_gfclk, omap->xclk60mhsp1_ck); if (ret != 0) dev_err(dev, "xclk60mhsp1_ck set parent" "failed error:%d\n", ret); } else if (is_ehci_tll_mode(pdata->port_mode[0])) { - ret = clk_set_parent(omap->utmi_p1_fck, + ret = clk_set_parent(omap->utmi_p1_gfclk, omap->init_60m_fclk); if (ret != 0) dev_err(dev, "init_60m_fclk set parent" @@ -593,13 +610,13 @@ static int usbhs_omap_probe(struct platform_device *pdev) } if (is_ehci_phy_mode(pdata->port_mode[1])) { - ret = clk_set_parent(omap->utmi_p2_fck, + ret = clk_set_parent(omap->utmi_p2_gfclk, omap->xclk60mhsp2_ck); if (ret != 0) dev_err(dev, "xclk60mhsp2_ck set parent" "failed error:%d\n", ret); } else if (is_ehci_tll_mode(pdata->port_mode[1])) { - ret = clk_set_parent(omap->utmi_p2_fck, + ret = clk_set_parent(omap->utmi_p2_gfclk, omap->init_60m_fclk); if (ret != 0) dev_err(dev, "init_60m_fclk set parent" @@ -617,28 +634,30 @@ static int usbhs_omap_probe(struct platform_device *pdev) err_alloc: omap_usbhs_deinit(&pdev->dev); - clk_put(omap->init_60m_fclk); -err_usbhost_p2_fck: - clk_put(omap->usbhost_p2_fck); + for (i = 0; i < omap->nports; i++) + if (!IS_ERR(omap->utmi_clk[i])) + clk_put(omap->utmi_clk[i]); -err_usbhost_p1_fck: - clk_put(omap->usbhost_p1_fck); + clk_put(omap->init_60m_fclk); -err_xclk60mhsp2_ck: +err_init60m: clk_put(omap->xclk60mhsp2_ck); -err_utmi_p2_fck: - clk_put(omap->utmi_p2_fck); - -err_xclk60mhsp1_ck: +err_xclk60mhsp2: clk_put(omap->xclk60mhsp1_ck); -err_utmi_p1_fck: - clk_put(omap->utmi_p1_fck); +err_xclk60mhsp1: + clk_put(omap->utmi_p2_gfclk); -err_end: - clk_put(omap->ehci_logic_fck); +err_p2_gfclk: + clk_put(omap->utmi_p1_gfclk); + +err_p1_gfclk: + if (!IS_ERR(omap->ehci_logic_fck)) + clk_put(omap->ehci_logic_fck); + +err_mem: pm_runtime_disable(dev); return ret; @@ -653,16 +672,23 @@ err_end: static int usbhs_omap_remove(struct platform_device *pdev) { struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev); + int i; omap_usbhs_deinit(&pdev->dev); + + for (i = 0; i < omap->nports; i++) + if (!IS_ERR(omap->utmi_clk[i])) + clk_put(omap->utmi_clk[i]); + clk_put(omap->init_60m_fclk); - clk_put(omap->usbhost_p2_fck); - clk_put(omap->usbhost_p1_fck); + clk_put(omap->utmi_p1_gfclk); + clk_put(omap->utmi_p2_gfclk); clk_put(omap->xclk60mhsp2_ck); - clk_put(omap->utmi_p2_fck); clk_put(omap->xclk60mhsp1_ck); - clk_put(omap->utmi_p1_fck); - clk_put(omap->ehci_logic_fck); + + if (!IS_ERR(omap->ehci_logic_fck)) + clk_put(omap->ehci_logic_fck); + pm_runtime_disable(&pdev->dev); return 0; -- cgit v1.2.3 From 340c64eabacb2a4331b4357c0e8944027ce37216 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 12 Nov 2012 16:53:16 +0200 Subject: mfd: omap-usb-host: Manage HSIC clocks for HSIC mode Enable the optional HSIC clocks (60MHz and 480MHz) for the ports that are configured in HSIC mode. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 97 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 83 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 9fa0215e3df4..bdfc8b7b5aff 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -93,6 +93,8 @@ struct usbhs_hcd_omap { int nports; struct clk **utmi_clk; + struct clk **hsic60m_clk; + struct clk **hsic480m_clk; struct clk *xclk60mhsp1_ck; struct clk *xclk60mhsp2_ck; @@ -286,13 +288,40 @@ static int usbhs_runtime_resume(struct device *dev) clk_enable(omap->ehci_logic_fck); for (i = 0; i < omap->nports; i++) { - if (!is_ehci_tll_mode(pdata->port_mode[i]) || - IS_ERR(omap->utmi_clk[i])) - continue; - - r = clk_enable(omap->utmi_clk[i]); - if (r) - dev_err(dev, "Can't enable port %d clk : %d\n", i, r); + switch (pdata->port_mode[i]) { + case OMAP_EHCI_PORT_MODE_HSIC: + if (!IS_ERR(omap->hsic60m_clk[i])) { + r = clk_enable(omap->hsic60m_clk[i]); + if (r) { + dev_err(dev, + "Can't enable port %d hsic60m clk:%d\n", + i, r); + } + } + + if (!IS_ERR(omap->hsic480m_clk[i])) { + r = clk_enable(omap->hsic480m_clk[i]); + if (r) { + dev_err(dev, + "Can't enable port %d hsic480m clk:%d\n", + i, r); + } + } + /* Fall through as HSIC mode needs utmi_clk */ + + case OMAP_EHCI_PORT_MODE_TLL: + if (!IS_ERR(omap->utmi_clk[i])) { + r = clk_enable(omap->utmi_clk[i]); + if (r) { + dev_err(dev, + "Can't enable port %d clk : %d\n", + i, r); + } + } + break; + default: + break; + } } spin_unlock_irqrestore(&omap->lock, flags); @@ -312,9 +341,22 @@ static int usbhs_runtime_suspend(struct device *dev) spin_lock_irqsave(&omap->lock, flags); for (i = 0; i < omap->nports; i++) { - if (is_ehci_tll_mode(pdata->port_mode[i]) && - !IS_ERR(omap->utmi_clk[i])) - clk_disable(omap->utmi_clk[i]); + switch (pdata->port_mode[i]) { + case OMAP_EHCI_PORT_MODE_HSIC: + if (!IS_ERR(omap->hsic60m_clk[i])) + clk_disable(omap->hsic60m_clk[i]); + + if (!IS_ERR(omap->hsic480m_clk[i])) + clk_disable(omap->hsic480m_clk[i]); + /* Fall through as utmi_clks were used in HSIC mode */ + + case OMAP_EHCI_PORT_MODE_TLL: + if (!IS_ERR(omap->utmi_clk[i])) + clk_disable(omap->utmi_clk[i]); + break; + default: + break; + } } if (!IS_ERR(omap->ehci_logic_fck)) @@ -520,7 +562,10 @@ static int usbhs_omap_probe(struct platform_device *pdev) i = sizeof(struct clk *) * omap->nports; omap->utmi_clk = devm_kzalloc(dev, i, GFP_KERNEL); - if (!omap->utmi_clk) { + omap->hsic480m_clk = devm_kzalloc(dev, i, GFP_KERNEL); + omap->hsic60m_clk = devm_kzalloc(dev, i, GFP_KERNEL); + + if (!omap->utmi_clk || !omap->hsic480m_clk || !omap->hsic60m_clk) { dev_err(dev, "Memory allocation failed\n"); ret = -ENOMEM; goto err_mem; @@ -578,7 +623,7 @@ static int usbhs_omap_probe(struct platform_device *pdev) } for (i = 0; i < omap->nports; i++) { - char clkname[] = "usb_host_hs_utmi_px_clk"; + char clkname[30]; /* clock names are indexed from 1*/ snprintf(clkname, sizeof(clkname), @@ -592,6 +637,20 @@ static int usbhs_omap_probe(struct platform_device *pdev) if (IS_ERR(omap->utmi_clk[i])) dev_dbg(dev, "Failed to get clock : %s : %ld\n", clkname, PTR_ERR(omap->utmi_clk[i])); + + snprintf(clkname, sizeof(clkname), + "usb_host_hs_hsic480m_p%d_clk", i + 1); + omap->hsic480m_clk[i] = clk_get(dev, clkname); + if (IS_ERR(omap->hsic480m_clk[i])) + dev_dbg(dev, "Failed to get clock : %s : %ld\n", + clkname, PTR_ERR(omap->hsic480m_clk[i])); + + snprintf(clkname, sizeof(clkname), + "usb_host_hs_hsic60m_p%d_clk", i + 1); + omap->hsic60m_clk[i] = clk_get(dev, clkname); + if (IS_ERR(omap->hsic60m_clk[i])) + dev_dbg(dev, "Failed to get clock : %s : %ld\n", + clkname, PTR_ERR(omap->hsic60m_clk[i])); } if (is_ehci_phy_mode(pdata->port_mode[0])) { @@ -635,9 +694,14 @@ static int usbhs_omap_probe(struct platform_device *pdev) err_alloc: omap_usbhs_deinit(&pdev->dev); - for (i = 0; i < omap->nports; i++) + for (i = 0; i < omap->nports; i++) { if (!IS_ERR(omap->utmi_clk[i])) clk_put(omap->utmi_clk[i]); + if (!IS_ERR(omap->hsic60m_clk[i])) + clk_put(omap->hsic60m_clk[i]); + if (!IS_ERR(omap->hsic480m_clk[i])) + clk_put(omap->hsic480m_clk[i]); + } clk_put(omap->init_60m_fclk); @@ -676,9 +740,14 @@ static int usbhs_omap_remove(struct platform_device *pdev) omap_usbhs_deinit(&pdev->dev); - for (i = 0; i < omap->nports; i++) + for (i = 0; i < omap->nports; i++) { if (!IS_ERR(omap->utmi_clk[i])) clk_put(omap->utmi_clk[i]); + if (!IS_ERR(omap->hsic60m_clk[i])) + clk_put(omap->hsic60m_clk[i]); + if (!IS_ERR(omap->hsic480m_clk[i])) + clk_put(omap->hsic480m_clk[i]); + } clk_put(omap->init_60m_fclk); clk_put(omap->utmi_p1_gfclk); -- cgit v1.2.3 From c6cd087ed058f8a7dfe1997cc51fdf8005e25a03 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 15 Nov 2012 11:48:51 +0200 Subject: mfd: omap-usb-host: Get rid of unnecessary spinlock The driver does not have an interrupt handler and we don't really need a spinlock, so get rid of it. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index bdfc8b7b5aff..0740c6856b51 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -108,7 +107,6 @@ struct usbhs_hcd_omap { struct usbhs_omap_platform_data *pdata; u32 usbhs_rev; - spinlock_t lock; }; /*-------------------------------------------------------------------------*/ @@ -276,13 +274,11 @@ static int usbhs_runtime_resume(struct device *dev) { struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); struct usbhs_omap_platform_data *pdata = omap->pdata; - unsigned long flags; int i, r; dev_dbg(dev, "usbhs_runtime_resume\n"); omap_tll_enable(); - spin_lock_irqsave(&omap->lock, flags); if (!IS_ERR(omap->ehci_logic_fck)) clk_enable(omap->ehci_logic_fck); @@ -324,8 +320,6 @@ static int usbhs_runtime_resume(struct device *dev) } } - spin_unlock_irqrestore(&omap->lock, flags); - return 0; } @@ -333,13 +327,10 @@ static int usbhs_runtime_suspend(struct device *dev) { struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); struct usbhs_omap_platform_data *pdata = omap->pdata; - unsigned long flags; int i; dev_dbg(dev, "usbhs_runtime_suspend\n"); - spin_lock_irqsave(&omap->lock, flags); - for (i = 0; i < omap->nports; i++) { switch (pdata->port_mode[i]) { case OMAP_EHCI_PORT_MODE_HSIC: @@ -362,7 +353,6 @@ static int usbhs_runtime_suspend(struct device *dev) if (!IS_ERR(omap->ehci_logic_fck)) clk_disable(omap->ehci_logic_fck); - spin_unlock_irqrestore(&omap->lock, flags); omap_tll_disable(); return 0; @@ -372,7 +362,6 @@ static void omap_usbhs_init(struct device *dev) { struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); struct usbhs_omap_platform_data *pdata = omap->pdata; - unsigned long flags; unsigned reg; dev_dbg(dev, "starting TI HSUSB Controller\n"); @@ -391,7 +380,6 @@ static void omap_usbhs_init(struct device *dev) } pm_runtime_get_sync(dev); - spin_lock_irqsave(&omap->lock, flags); reg = usbhs_read(omap->uhh_base, OMAP_UHH_HOSTCONFIG); /* setup ULPI bypass and burst configurations */ @@ -454,8 +442,6 @@ static void omap_usbhs_init(struct device *dev) usbhs_write(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg); dev_dbg(dev, "UHH setup done, uhh_hostconfig=%x\n", reg); - spin_unlock_irqrestore(&omap->lock, flags); - pm_runtime_put_sync(dev); if (pdata->phy_reset) { /* Hold the PHY in RESET for enough time till @@ -521,8 +507,6 @@ static int usbhs_omap_probe(struct platform_device *pdev) return -EADDRNOTAVAIL; } - spin_lock_init(&omap->lock); - omap->pdata = pdata; pm_runtime_enable(dev); -- cgit v1.2.3 From c4df00aed9e2e6e3ab094b4bb8b9ecb64cf8c70e Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 12 Nov 2012 16:32:01 +0200 Subject: mfd: omap-usb-host: clean up omap_usbhs_init() We split initializing revision 1 and revision 2 into different functions. Initialization is now done dynamically so that only the number of ports available on the system are initialized. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 129 +++++++++++++++++++++++++++----------------- 1 file changed, 81 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 0740c6856b51..7f9c38675d91 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -358,6 +358,75 @@ static int usbhs_runtime_suspend(struct device *dev) return 0; } +static unsigned omap_usbhs_rev1_hostconfig(struct usbhs_hcd_omap *omap, + unsigned reg) +{ + struct usbhs_omap_platform_data *pdata = omap->pdata; + int i; + + for (i = 0; i < omap->nports; i++) { + switch (pdata->port_mode[i]) { + case OMAP_USBHS_PORT_MODE_UNUSED: + reg &= ~(OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS << i); + break; + case OMAP_EHCI_PORT_MODE_PHY: + if (pdata->single_ulpi_bypass) + break; + + if (i == 0) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; + else + reg &= ~(OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS + << (i-1)); + break; + default: + if (pdata->single_ulpi_bypass) + break; + + if (i == 0) + reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; + else + reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS + << (i-1); + break; + } + } + + if (pdata->single_ulpi_bypass) { + /* bypass ULPI only if none of the ports use PHY mode */ + reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; + + for (i = 0; i < omap->nports; i++) { + if (is_ehci_phy_mode(pdata->port_mode[i])) { + reg &= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; + break; + } + } + } + + return reg; +} + +static unsigned omap_usbhs_rev2_hostconfig(struct usbhs_hcd_omap *omap, + unsigned reg) +{ + struct usbhs_omap_platform_data *pdata = omap->pdata; + int i; + + for (i = 0; i < omap->nports; i++) { + /* Clear port mode fields for PHY mode */ + reg &= ~(OMAP4_P1_MODE_CLEAR << 2 * i); + + if (is_ehci_tll_mode(pdata->port_mode[i]) || + (is_ohci_port(pdata->port_mode[i]))) + reg |= OMAP4_P1_MODE_TLL << 2 * i; + else if (is_ehci_hsic_mode(pdata->port_mode[i])) + reg |= OMAP4_P1_MODE_HSIC << 2 * i; + } + + return reg; +} + static void omap_usbhs_init(struct device *dev) { struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); @@ -389,54 +458,18 @@ static void omap_usbhs_init(struct device *dev) reg |= OMAP4_UHH_HOSTCONFIG_APP_START_CLK; reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN; - if (is_omap_usbhs_rev1(omap)) { - if (pdata->port_mode[0] == OMAP_USBHS_PORT_MODE_UNUSED) - reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS; - if (pdata->port_mode[1] == OMAP_USBHS_PORT_MODE_UNUSED) - reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS; - if (pdata->port_mode[2] == OMAP_USBHS_PORT_MODE_UNUSED) - reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS; - - /* Bypass the TLL module for PHY mode operation */ - if (pdata->single_ulpi_bypass) { - dev_dbg(dev, "OMAP3 ES version <= ES2.1\n"); - if (is_ehci_phy_mode(pdata->port_mode[0]) || - is_ehci_phy_mode(pdata->port_mode[1]) || - is_ehci_phy_mode(pdata->port_mode[2])) - reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; - else - reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; - } else { - dev_dbg(dev, "OMAP3 ES version > ES2.1\n"); - if (is_ehci_phy_mode(pdata->port_mode[0])) - reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; - else - reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; - if (is_ehci_phy_mode(pdata->port_mode[1])) - reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; - else - reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; - if (is_ehci_phy_mode(pdata->port_mode[2])) - reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; - else - reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; - } - } else if (is_omap_usbhs_rev2(omap)) { - /* Clear port mode fields for PHY mode*/ - reg &= ~OMAP4_P1_MODE_CLEAR; - reg &= ~OMAP4_P2_MODE_CLEAR; - - if (is_ehci_tll_mode(pdata->port_mode[0]) || - (is_ohci_port(pdata->port_mode[0]))) - reg |= OMAP4_P1_MODE_TLL; - else if (is_ehci_hsic_mode(pdata->port_mode[0])) - reg |= OMAP4_P1_MODE_HSIC; - - if (is_ehci_tll_mode(pdata->port_mode[1]) || - (is_ohci_port(pdata->port_mode[1]))) - reg |= OMAP4_P2_MODE_TLL; - else if (is_ehci_hsic_mode(pdata->port_mode[1])) - reg |= OMAP4_P2_MODE_HSIC; + switch (omap->usbhs_rev) { + case OMAP_USBHS_REV1: + omap_usbhs_rev1_hostconfig(omap, reg); + break; + + case OMAP_USBHS_REV2: + omap_usbhs_rev2_hostconfig(omap, reg); + break; + + default: /* newer revisions */ + omap_usbhs_rev2_hostconfig(omap, reg); + break; } usbhs_write(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg); -- cgit v1.2.3 From a8c4e9e1118f0a12e3a9524d8d597487d7e3476d Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 28 Nov 2012 16:31:29 +0200 Subject: mfd: omap-usb-host: Don't spam console on clk_set_parent failure clk_set_parent is expected to fail on OMAP3 platforms. We don't consider that as fatal so don't spam console. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 7f9c38675d91..b21ca760b1bd 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -671,32 +671,32 @@ static int usbhs_omap_probe(struct platform_device *pdev) } if (is_ehci_phy_mode(pdata->port_mode[0])) { - /* for OMAP3 , the clk set paretn fails */ + /* for OMAP3, clk_set_parent fails */ ret = clk_set_parent(omap->utmi_p1_gfclk, omap->xclk60mhsp1_ck); if (ret != 0) - dev_err(dev, "xclk60mhsp1_ck set parent" - "failed error:%d\n", ret); + dev_dbg(dev, "xclk60mhsp1_ck set parent failed: %d\n", + ret); } else if (is_ehci_tll_mode(pdata->port_mode[0])) { ret = clk_set_parent(omap->utmi_p1_gfclk, omap->init_60m_fclk); if (ret != 0) - dev_err(dev, "init_60m_fclk set parent" - "failed error:%d\n", ret); + dev_dbg(dev, "P0 init_60m_fclk set parent failed: %d\n", + ret); } if (is_ehci_phy_mode(pdata->port_mode[1])) { ret = clk_set_parent(omap->utmi_p2_gfclk, omap->xclk60mhsp2_ck); if (ret != 0) - dev_err(dev, "xclk60mhsp2_ck set parent" - "failed error:%d\n", ret); + dev_dbg(dev, "xclk60mhsp2_ck set parent failed: %d\n", + ret); } else if (is_ehci_tll_mode(pdata->port_mode[1])) { ret = clk_set_parent(omap->utmi_p2_gfclk, omap->init_60m_fclk); if (ret != 0) - dev_err(dev, "init_60m_fclk set parent" - "failed error:%d\n", ret); + dev_dbg(dev, "P1 init_60m_fclk set parent failed: %d\n", + ret); } omap_usbhs_init(dev); -- cgit v1.2.3 From ab3f2a86d17c5c2c1127871d28d1f64baebc5d04 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 2 Jan 2013 15:59:28 +0200 Subject: mfd: omap-usb-host: get rid of build warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the below build warning when driver is built-in. drivers/mfd/omap-usb-host.c:750:12: warning: ‘usbhs_omap_remove’ defined but not used [-Wunused-function] Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index b21ca760b1bd..6b5edf64de2b 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -791,7 +791,7 @@ static struct platform_driver usbhs_omap_driver = { .owner = THIS_MODULE, .pm = &usbhsomap_dev_pm_ops, }, - .remove = __exit_p(usbhs_omap_remove), + .remove = usbhs_omap_remove, }; MODULE_AUTHOR("Keshava Munegowda "); -- cgit v1.2.3 From 221b1bd3d409b14e8764d8f40994b71ae6887d18 Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Tue, 5 Feb 2013 09:08:10 -0500 Subject: mtd: mtd_torturetest can cause stack overflows mtd_torturetest uses the module parm "ebcnt" to control the size of a stack based array of int's. When "ebcnt" is large, Ex: 1000, it causes stack overflows on systems with small kernel stacks. The fix is to move the array from the stack to kmalloc memory. Signed-off-by: Al Cooper Signed-off-by: Artem Bityutskiy --- drivers/mtd/tests/mtd_torturetest.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/tests/mtd_torturetest.c b/drivers/mtd/tests/mtd_torturetest.c index c4cde1e9eddb..3a9f6a6a79f9 100644 --- a/drivers/mtd/tests/mtd_torturetest.c +++ b/drivers/mtd/tests/mtd_torturetest.c @@ -208,7 +208,7 @@ static inline int write_pattern(int ebnum, void *buf) static int __init tort_init(void) { int err = 0, i, infinite = !cycles_count; - int bad_ebs[ebcnt]; + int *bad_ebs; printk(KERN_INFO "\n"); printk(KERN_INFO "=================================================\n"); @@ -250,28 +250,24 @@ static int __init tort_init(void) err = -ENOMEM; patt_5A5 = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!patt_5A5) { - pr_err("error: cannot allocate memory\n"); + if (!patt_5A5) goto out_mtd; - } patt_A5A = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!patt_A5A) { - pr_err("error: cannot allocate memory\n"); + if (!patt_A5A) goto out_patt_5A5; - } patt_FF = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!patt_FF) { - pr_err("error: cannot allocate memory\n"); + if (!patt_FF) goto out_patt_A5A; - } check_buf = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!check_buf) { - pr_err("error: cannot allocate memory\n"); + if (!check_buf) goto out_patt_FF; - } + + bad_ebs = kcalloc(ebcnt, sizeof(*bad_ebs), GFP_KERNEL); + if (!bad_ebs) + goto out_check_buf; err = 0; @@ -290,7 +286,6 @@ static int __init tort_init(void) /* * Check if there is a bad eraseblock among those we are going to test. */ - memset(&bad_ebs[0], 0, sizeof(int) * ebcnt); if (mtd_can_have_bb(mtd)) { for (i = eb; i < eb + ebcnt; i++) { err = mtd_block_isbad(mtd, (loff_t)i * mtd->erasesize); @@ -394,6 +389,8 @@ out: pr_info("finished after %u erase cycles\n", erase_cycles); + kfree(bad_ebs); +out_check_buf: kfree(check_buf); out_patt_FF: kfree(patt_FF); -- cgit v1.2.3 From ef4e0c2145e928b1c165ac88a9aa20038a981488 Mon Sep 17 00:00:00 2001 From: Mrugesh Katepallewar Date: Thu, 7 Feb 2013 16:03:15 +0530 Subject: mtd: davinci_nand: Use managed resources davinci_nand driver currently uses normal kzalloc, ioremap and get_clk routines. This patch replaces these routines with devm_kzalloc, devm_request_and_ioremap and devm_clk_get resp. Signed-off-by: Mrugesh Katepallewar Signed-off-by: Artem Bityutskiy --- drivers/mtd/nand/davinci_nand.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index feae55c7b880..94e17af8e450 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -606,7 +606,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) if (pdev->id < 0 || pdev->id > 3) return -ENODEV; - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) { dev_err(&pdev->dev, "unable to allocate memory\n"); ret = -ENOMEM; @@ -623,11 +623,11 @@ static int __init nand_davinci_probe(struct platform_device *pdev) goto err_nomem; } - vaddr = ioremap(res1->start, resource_size(res1)); - base = ioremap(res2->start, resource_size(res2)); + vaddr = devm_request_and_ioremap(&pdev->dev, res1); + base = devm_request_and_ioremap(&pdev->dev, res2); if (!vaddr || !base) { dev_err(&pdev->dev, "ioremap failed\n"); - ret = -EINVAL; + ret = -EADDRNOTAVAIL; goto err_ioremap; } @@ -717,7 +717,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) } info->chip.ecc.mode = ecc_mode; - info->clk = clk_get(&pdev->dev, "aemif"); + info->clk = devm_clk_get(&pdev->dev, "aemif"); if (IS_ERR(info->clk)) { ret = PTR_ERR(info->clk); dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret); @@ -845,8 +845,6 @@ err_timing: clk_disable_unprepare(info->clk); err_clk_enable: - clk_put(info->clk); - spin_lock_irq(&davinci_nand_lock); if (ecc_mode == NAND_ECC_HW_SYNDROME) ecc4_busy = false; @@ -855,13 +853,7 @@ err_clk_enable: err_ecc: err_clk: err_ioremap: - if (base) - iounmap(base); - if (vaddr) - iounmap(vaddr); - err_nomem: - kfree(info); return ret; } @@ -874,15 +866,9 @@ static int __exit nand_davinci_remove(struct platform_device *pdev) ecc4_busy = false; spin_unlock_irq(&davinci_nand_lock); - iounmap(info->base); - iounmap(info->vaddr); - nand_release(&info->mtd); clk_disable_unprepare(info->clk); - clk_put(info->clk); - - kfree(info); return 0; } -- cgit v1.2.3 From be0638d9e4795254e28e39eff61e38f47d240fd2 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Thu, 7 Feb 2013 11:56:04 +0100 Subject: mtd: bcm47xxnflash: use pr_fmt for module prefix in messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: Artem Bityutskiy --- drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h | 4 ++++ drivers/mtd/nand/bcm47xxnflash/main.c | 4 ++-- drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h b/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h index 0bdb2ce4da75..c005a62330b1 100644 --- a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h +++ b/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h @@ -1,6 +1,10 @@ #ifndef __BCM47XXNFLASH_H #define __BCM47XXNFLASH_H +#ifndef pr_fmt +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#endif + #include #include diff --git a/drivers/mtd/nand/bcm47xxnflash/main.c b/drivers/mtd/nand/bcm47xxnflash/main.c index a52acdccd0c4..7bae569fdc79 100644 --- a/drivers/mtd/nand/bcm47xxnflash/main.c +++ b/drivers/mtd/nand/bcm47xxnflash/main.c @@ -9,14 +9,14 @@ * */ +#include "bcm47xxnflash.h" + #include #include #include #include #include -#include "bcm47xxnflash.h" - MODULE_DESCRIPTION("NAND flash driver for BCMA bus"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Rafał Miłecki"); diff --git a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c index 86c9a79b89b3..37d5b8956694 100644 --- a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c +++ b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c @@ -9,13 +9,13 @@ * */ +#include "bcm47xxnflash.h" + #include #include #include #include -#include "bcm47xxnflash.h" - /* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has * shown 164 retries as maxiumum. */ #define NFLASH_READY_RETRIES 1000 -- cgit v1.2.3 From 5fa422c922c2599dbfd960faf6dfca2411cc3f99 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 12 Feb 2013 09:15:02 -0800 Subject: dmaengine: move drivers/of/dma.c -> drivers/dma/of-dma.c as requested by Rob Suggested-by: Rob Herring Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 4 + drivers/dma/Makefile | 2 + drivers/dma/of-dma.c | 264 +++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/of/Makefile | 2 +- drivers/of/dma.c | 264 --------------------------------------------------- 5 files changed, 271 insertions(+), 265 deletions(-) create mode 100644 drivers/dma/of-dma.c delete mode 100644 drivers/of/dma.c (limited to 'drivers') diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 0b408bbb6a17..e92b5f0f8a5f 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -325,6 +325,10 @@ config DMA_ENGINE config DMA_VIRTUAL_CHANNELS tristate +config DMA_OF + def_bool y + depends on OF + comment "DMA Clients" depends on DMA_ENGINE diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 7428feaa8705..c1ed644be9e2 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -3,6 +3,8 @@ ccflags-$(CONFIG_DMADEVICES_VDEBUG) += -DVERBOSE_DEBUG obj-$(CONFIG_DMA_ENGINE) += dmaengine.o obj-$(CONFIG_DMA_VIRTUAL_CHANNELS) += virt-dma.o +obj-$(CONFIG_DMA_OF) += of-dma.o + obj-$(CONFIG_NET_DMA) += iovlock.o obj-$(CONFIG_INTEL_MID_DMAC) += intel_mid_dma.o obj-$(CONFIG_DMATEST) += dmatest.o diff --git a/drivers/dma/of-dma.c b/drivers/dma/of-dma.c new file mode 100644 index 000000000000..59631b2c4666 --- /dev/null +++ b/drivers/dma/of-dma.c @@ -0,0 +1,264 @@ +/* + * Device tree helpers for DMA request / controller + * + * Based on of_gpio.c + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(of_dma_list); +static DEFINE_SPINLOCK(of_dma_lock); + +/** + * of_dma_get_controller - Get a DMA controller in DT DMA helpers list + * @dma_spec: pointer to DMA specifier as found in the device tree + * + * Finds a DMA controller with matching device node and number for dma cells + * in a list of registered DMA controllers. If a match is found the use_count + * variable is increased and a valid pointer to the DMA data stored is retuned. + * A NULL pointer is returned if no match is found. + */ +static struct of_dma *of_dma_get_controller(struct of_phandle_args *dma_spec) +{ + struct of_dma *ofdma; + + spin_lock(&of_dma_lock); + + if (list_empty(&of_dma_list)) { + spin_unlock(&of_dma_lock); + return NULL; + } + + list_for_each_entry(ofdma, &of_dma_list, of_dma_controllers) + if ((ofdma->of_node == dma_spec->np) && + (ofdma->of_dma_nbcells == dma_spec->args_count)) { + ofdma->use_count++; + spin_unlock(&of_dma_lock); + return ofdma; + } + + spin_unlock(&of_dma_lock); + + pr_debug("%s: can't find DMA controller %s\n", __func__, + dma_spec->np->full_name); + + return NULL; +} + +/** + * of_dma_put_controller - Decrement use count for a registered DMA controller + * @of_dma: pointer to DMA controller data + * + * Decrements the use_count variable in the DMA data structure. This function + * should be called only when a valid pointer is returned from + * of_dma_get_controller() and no further accesses to data referenced by that + * pointer are needed. + */ +static void of_dma_put_controller(struct of_dma *ofdma) +{ + spin_lock(&of_dma_lock); + ofdma->use_count--; + spin_unlock(&of_dma_lock); +} + +/** + * of_dma_controller_register - Register a DMA controller to DT DMA helpers + * @np: device node of DMA controller + * @of_dma_xlate: translation function which converts a phandle + * arguments list into a dma_chan structure + * @data pointer to controller specific data to be used by + * translation function + * + * Returns 0 on success or appropriate errno value on error. + * + * Allocated memory should be freed with appropriate of_dma_controller_free() + * call. + */ +int of_dma_controller_register(struct device_node *np, + struct dma_chan *(*of_dma_xlate) + (struct of_phandle_args *, struct of_dma *), + void *data) +{ + struct of_dma *ofdma; + int nbcells; + + if (!np || !of_dma_xlate) { + pr_err("%s: not enough information provided\n", __func__); + return -EINVAL; + } + + ofdma = kzalloc(sizeof(*ofdma), GFP_KERNEL); + if (!ofdma) + return -ENOMEM; + + nbcells = be32_to_cpup(of_get_property(np, "#dma-cells", NULL)); + if (!nbcells) { + pr_err("%s: #dma-cells property is missing or invalid\n", + __func__); + return -EINVAL; + } + + ofdma->of_node = np; + ofdma->of_dma_nbcells = nbcells; + ofdma->of_dma_xlate = of_dma_xlate; + ofdma->of_dma_data = data; + ofdma->use_count = 0; + + /* Now queue of_dma controller structure in list */ + list_add_tail(&ofdma->of_dma_controllers, &of_dma_list); + + return 0; +} +EXPORT_SYMBOL_GPL(of_dma_controller_register); + +/** + * of_dma_controller_free - Remove a DMA controller from DT DMA helpers list + * @np: device node of DMA controller + * + * Memory allocated by of_dma_controller_register() is freed here. + */ +int of_dma_controller_free(struct device_node *np) +{ + struct of_dma *ofdma; + + spin_lock(&of_dma_lock); + + if (list_empty(&of_dma_list)) { + spin_unlock(&of_dma_lock); + return -ENODEV; + } + + list_for_each_entry(ofdma, &of_dma_list, of_dma_controllers) + if (ofdma->of_node == np) { + if (ofdma->use_count) { + spin_unlock(&of_dma_lock); + return -EBUSY; + } + + list_del(&ofdma->of_dma_controllers); + spin_unlock(&of_dma_lock); + kfree(ofdma); + return 0; + } + + spin_unlock(&of_dma_lock); + return -ENODEV; +} +EXPORT_SYMBOL_GPL(of_dma_controller_free); + +/** + * of_dma_match_channel - Check if a DMA specifier matches name + * @np: device node to look for DMA channels + * @name: channel name to be matched + * @index: index of DMA specifier in list of DMA specifiers + * @dma_spec: pointer to DMA specifier as found in the device tree + * + * Check if the DMA specifier pointed to by the index in a list of DMA + * specifiers, matches the name provided. Returns 0 if the name matches and + * a valid pointer to the DMA specifier is found. Otherwise returns -ENODEV. + */ +static int of_dma_match_channel(struct device_node *np, char *name, int index, + struct of_phandle_args *dma_spec) +{ + const char *s; + + if (of_property_read_string_index(np, "dma-names", index, &s)) + return -ENODEV; + + if (strcmp(name, s)) + return -ENODEV; + + if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", index, + dma_spec)) + return -ENODEV; + + return 0; +} + +/** + * of_dma_request_slave_channel - Get the DMA slave channel + * @np: device node to get DMA request from + * @name: name of desired channel + * + * Returns pointer to appropriate dma channel on success or NULL on error. + */ +struct dma_chan *of_dma_request_slave_channel(struct device_node *np, + char *name) +{ + struct of_phandle_args dma_spec; + struct of_dma *ofdma; + struct dma_chan *chan; + int count, i; + + if (!np || !name) { + pr_err("%s: not enough information provided\n", __func__); + return NULL; + } + + count = of_property_count_strings(np, "dma-names"); + if (count < 0) { + pr_err("%s: dma-names property missing or empty\n", __func__); + return NULL; + } + + for (i = 0; i < count; i++) { + if (of_dma_match_channel(np, name, i, &dma_spec)) + continue; + + ofdma = of_dma_get_controller(&dma_spec); + + if (!ofdma) + continue; + + chan = ofdma->of_dma_xlate(&dma_spec, ofdma); + + of_dma_put_controller(ofdma); + + of_node_put(dma_spec.np); + + if (chan) + return chan; + } + + return NULL; +} + +/** + * of_dma_simple_xlate - Simple DMA engine translation function + * @dma_spec: pointer to DMA specifier as found in the device tree + * @of_dma: pointer to DMA controller data + * + * A simple translation function for devices that use a 32-bit value for the + * filter_param when calling the DMA engine dma_request_channel() function. + * Note that this translation function requires that #dma-cells is equal to 1 + * and the argument of the dma specifier is the 32-bit filter_param. Returns + * pointer to appropriate dma channel on success or NULL on error. + */ +struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + int count = dma_spec->args_count; + struct of_dma_filter_info *info = ofdma->of_dma_data; + + if (!info || !info->filter_fn) + return NULL; + + if (count != 1) + return NULL; + + return dma_request_channel(info->dma_cap, info->filter_fn, + &dma_spec->args[0]); +} +EXPORT_SYMBOL_GPL(of_dma_simple_xlate); diff --git a/drivers/of/Makefile b/drivers/of/Makefile index eafa107aed40..e027f444d10c 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -1,4 +1,4 @@ -obj-y = base.o dma.o +obj-y = base.o obj-$(CONFIG_OF_FLATTREE) += fdt.o obj-$(CONFIG_OF_PROMTREE) += pdt.o obj-$(CONFIG_OF_ADDRESS) += address.o diff --git a/drivers/of/dma.c b/drivers/of/dma.c deleted file mode 100644 index 59631b2c4666..000000000000 --- a/drivers/of/dma.c +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Device tree helpers for DMA request / controller - * - * Based on of_gpio.c - * - * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -static LIST_HEAD(of_dma_list); -static DEFINE_SPINLOCK(of_dma_lock); - -/** - * of_dma_get_controller - Get a DMA controller in DT DMA helpers list - * @dma_spec: pointer to DMA specifier as found in the device tree - * - * Finds a DMA controller with matching device node and number for dma cells - * in a list of registered DMA controllers. If a match is found the use_count - * variable is increased and a valid pointer to the DMA data stored is retuned. - * A NULL pointer is returned if no match is found. - */ -static struct of_dma *of_dma_get_controller(struct of_phandle_args *dma_spec) -{ - struct of_dma *ofdma; - - spin_lock(&of_dma_lock); - - if (list_empty(&of_dma_list)) { - spin_unlock(&of_dma_lock); - return NULL; - } - - list_for_each_entry(ofdma, &of_dma_list, of_dma_controllers) - if ((ofdma->of_node == dma_spec->np) && - (ofdma->of_dma_nbcells == dma_spec->args_count)) { - ofdma->use_count++; - spin_unlock(&of_dma_lock); - return ofdma; - } - - spin_unlock(&of_dma_lock); - - pr_debug("%s: can't find DMA controller %s\n", __func__, - dma_spec->np->full_name); - - return NULL; -} - -/** - * of_dma_put_controller - Decrement use count for a registered DMA controller - * @of_dma: pointer to DMA controller data - * - * Decrements the use_count variable in the DMA data structure. This function - * should be called only when a valid pointer is returned from - * of_dma_get_controller() and no further accesses to data referenced by that - * pointer are needed. - */ -static void of_dma_put_controller(struct of_dma *ofdma) -{ - spin_lock(&of_dma_lock); - ofdma->use_count--; - spin_unlock(&of_dma_lock); -} - -/** - * of_dma_controller_register - Register a DMA controller to DT DMA helpers - * @np: device node of DMA controller - * @of_dma_xlate: translation function which converts a phandle - * arguments list into a dma_chan structure - * @data pointer to controller specific data to be used by - * translation function - * - * Returns 0 on success or appropriate errno value on error. - * - * Allocated memory should be freed with appropriate of_dma_controller_free() - * call. - */ -int of_dma_controller_register(struct device_node *np, - struct dma_chan *(*of_dma_xlate) - (struct of_phandle_args *, struct of_dma *), - void *data) -{ - struct of_dma *ofdma; - int nbcells; - - if (!np || !of_dma_xlate) { - pr_err("%s: not enough information provided\n", __func__); - return -EINVAL; - } - - ofdma = kzalloc(sizeof(*ofdma), GFP_KERNEL); - if (!ofdma) - return -ENOMEM; - - nbcells = be32_to_cpup(of_get_property(np, "#dma-cells", NULL)); - if (!nbcells) { - pr_err("%s: #dma-cells property is missing or invalid\n", - __func__); - return -EINVAL; - } - - ofdma->of_node = np; - ofdma->of_dma_nbcells = nbcells; - ofdma->of_dma_xlate = of_dma_xlate; - ofdma->of_dma_data = data; - ofdma->use_count = 0; - - /* Now queue of_dma controller structure in list */ - list_add_tail(&ofdma->of_dma_controllers, &of_dma_list); - - return 0; -} -EXPORT_SYMBOL_GPL(of_dma_controller_register); - -/** - * of_dma_controller_free - Remove a DMA controller from DT DMA helpers list - * @np: device node of DMA controller - * - * Memory allocated by of_dma_controller_register() is freed here. - */ -int of_dma_controller_free(struct device_node *np) -{ - struct of_dma *ofdma; - - spin_lock(&of_dma_lock); - - if (list_empty(&of_dma_list)) { - spin_unlock(&of_dma_lock); - return -ENODEV; - } - - list_for_each_entry(ofdma, &of_dma_list, of_dma_controllers) - if (ofdma->of_node == np) { - if (ofdma->use_count) { - spin_unlock(&of_dma_lock); - return -EBUSY; - } - - list_del(&ofdma->of_dma_controllers); - spin_unlock(&of_dma_lock); - kfree(ofdma); - return 0; - } - - spin_unlock(&of_dma_lock); - return -ENODEV; -} -EXPORT_SYMBOL_GPL(of_dma_controller_free); - -/** - * of_dma_match_channel - Check if a DMA specifier matches name - * @np: device node to look for DMA channels - * @name: channel name to be matched - * @index: index of DMA specifier in list of DMA specifiers - * @dma_spec: pointer to DMA specifier as found in the device tree - * - * Check if the DMA specifier pointed to by the index in a list of DMA - * specifiers, matches the name provided. Returns 0 if the name matches and - * a valid pointer to the DMA specifier is found. Otherwise returns -ENODEV. - */ -static int of_dma_match_channel(struct device_node *np, char *name, int index, - struct of_phandle_args *dma_spec) -{ - const char *s; - - if (of_property_read_string_index(np, "dma-names", index, &s)) - return -ENODEV; - - if (strcmp(name, s)) - return -ENODEV; - - if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", index, - dma_spec)) - return -ENODEV; - - return 0; -} - -/** - * of_dma_request_slave_channel - Get the DMA slave channel - * @np: device node to get DMA request from - * @name: name of desired channel - * - * Returns pointer to appropriate dma channel on success or NULL on error. - */ -struct dma_chan *of_dma_request_slave_channel(struct device_node *np, - char *name) -{ - struct of_phandle_args dma_spec; - struct of_dma *ofdma; - struct dma_chan *chan; - int count, i; - - if (!np || !name) { - pr_err("%s: not enough information provided\n", __func__); - return NULL; - } - - count = of_property_count_strings(np, "dma-names"); - if (count < 0) { - pr_err("%s: dma-names property missing or empty\n", __func__); - return NULL; - } - - for (i = 0; i < count; i++) { - if (of_dma_match_channel(np, name, i, &dma_spec)) - continue; - - ofdma = of_dma_get_controller(&dma_spec); - - if (!ofdma) - continue; - - chan = ofdma->of_dma_xlate(&dma_spec, ofdma); - - of_dma_put_controller(ofdma); - - of_node_put(dma_spec.np); - - if (chan) - return chan; - } - - return NULL; -} - -/** - * of_dma_simple_xlate - Simple DMA engine translation function - * @dma_spec: pointer to DMA specifier as found in the device tree - * @of_dma: pointer to DMA controller data - * - * A simple translation function for devices that use a 32-bit value for the - * filter_param when calling the DMA engine dma_request_channel() function. - * Note that this translation function requires that #dma-cells is equal to 1 - * and the argument of the dma specifier is the 32-bit filter_param. Returns - * pointer to appropriate dma channel on success or NULL on error. - */ -struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, - struct of_dma *ofdma) -{ - int count = dma_spec->args_count; - struct of_dma_filter_info *info = ofdma->of_dma_data; - - if (!info || !info->filter_fn) - return NULL; - - if (count != 1) - return NULL; - - return dma_request_channel(info->dma_cap, info->filter_fn, - &dma_spec->args[0]); -} -EXPORT_SYMBOL_GPL(of_dma_simple_xlate); -- cgit v1.2.3 From a20702b8d7c2b54a618e203d94b37b4f1d21bbd4 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Wed, 13 Feb 2013 09:40:03 +0800 Subject: dmaengine: ioat - fix spare sparse complain >> drivers/dma/ioat/dma_v3.c:371:6: sparse: symbol 'ioat3_timer_event' was not declared. Reported-by: Fengguang Wu Signed-off-by: Fengguang Wu Acked-by: Dave Jiang Signed-off-by: Vinod Koul --- drivers/dma/ioat/dma_v3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index 570dd6320c32..2acd1ad57fd6 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -368,7 +368,7 @@ static void check_active(struct ioat2_dma_chan *ioat) } -void ioat3_timer_event(unsigned long data) +static void ioat3_timer_event(unsigned long data) { struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); struct ioat_chan_common *chan = &ioat->base; -- cgit v1.2.3 From d6646b8075f8110ac167f335d660b8a544dd324e Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 30 Jan 2013 05:19:56 -0300 Subject: [media] sh-mobile-ceu-camera: fix SHARPNESS control default The V4L2_CID_SHARPNESS control in the sh-mobile-ceu-camera driver, if off, turns the CEU low-pass filter on. This is the opposite to the hardware default and can degrade image quality. Switch default to on to restore the default unfiltered mode. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 602584dc9330..bb08a46432f4 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -1064,7 +1064,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int /* Add our control */ v4l2_ctrl_new_std(&icd->ctrl_handler, &sh_mobile_ceu_ctrl_ops, - V4L2_CID_SHARPNESS, 0, 1, 1, 0); + V4L2_CID_SHARPNESS, 0, 1, 1, 1); if (icd->ctrl_handler.error) return icd->ctrl_handler.error; -- cgit v1.2.3 From ec34e1d579819789ecde888c1d54310824957893 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 30 Jan 2013 08:04:47 -0300 Subject: [media] mt9t112: mt9t111 format set up differs from mt9t112 The original commit, adding the mt9t112 driver said, that mt9t111 and mt9t112 had identical register layouts. This however doesn't seem to be the case. At least pixel format selection in the mt9t111 datasheet is different from the driver implementation. So far only the default YUYV format has been verified to work with mt9t111. Limit the driver to only report one supported format with mt9t111 until more formats are implemented. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/mt9t112.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c index c75d83168700..188e29b03273 100644 --- a/drivers/media/i2c/soc_camera/mt9t112.c +++ b/drivers/media/i2c/soc_camera/mt9t112.c @@ -92,6 +92,7 @@ struct mt9t112_priv { struct v4l2_rect frame; const struct mt9t112_format *format; int model; + int num_formats; u32 flags; /* for flags */ #define INIT_DONE (1 << 0) @@ -859,11 +860,11 @@ static int mt9t112_set_params(struct mt9t112_priv *priv, /* * get color format */ - for (i = 0; i < ARRAY_SIZE(mt9t112_cfmts); i++) + for (i = 0; i < priv->num_formats; i++) if (mt9t112_cfmts[i].code == code) break; - if (i == ARRAY_SIZE(mt9t112_cfmts)) + if (i == priv->num_formats) return -EINVAL; priv->frame = *rect; @@ -955,14 +956,16 @@ static int mt9t112_s_fmt(struct v4l2_subdev *sd, static int mt9t112_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9t112_priv *priv = to_mt9t112(client); unsigned int top, left; int i; - for (i = 0; i < ARRAY_SIZE(mt9t112_cfmts); i++) + for (i = 0; i < priv->num_formats; i++) if (mt9t112_cfmts[i].code == mf->code) break; - if (i == ARRAY_SIZE(mt9t112_cfmts)) { + if (i == priv->num_formats) { mf->code = V4L2_MBUS_FMT_UYVY8_2X8; mf->colorspace = V4L2_COLORSPACE_JPEG; } else { @@ -979,7 +982,10 @@ static int mt9t112_try_fmt(struct v4l2_subdev *sd, static int mt9t112_enum_fmt(struct v4l2_subdev *sd, unsigned int index, enum v4l2_mbus_pixelcode *code) { - if (index >= ARRAY_SIZE(mt9t112_cfmts)) + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9t112_priv *priv = to_mt9t112(client); + + if (index >= priv->num_formats) return -EINVAL; *code = mt9t112_cfmts[index].code; @@ -1056,10 +1062,12 @@ static int mt9t112_camera_probe(struct i2c_client *client) case 0x2680: devname = "mt9t111"; priv->model = V4L2_IDENT_MT9T111; + priv->num_formats = 1; break; case 0x2682: devname = "mt9t112"; priv->model = V4L2_IDENT_MT9T112; + priv->num_formats = ARRAY_SIZE(mt9t112_cfmts); break; default: dev_err(&client->dev, "Product ID error %04x\n", chipid); -- cgit v1.2.3 From 0dfa1c5da3e4b6849d40f4c3fc43212b6359a09d Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 17 Dec 2012 09:53:35 +0100 Subject: target: Export SPC inquiry emulation Some target drivers might need to access the inquiry data directly, without sending out the actual command. So export these functions. Signed-off-by: Hannes Reinecke Cc: Nicholas Bellinger Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_spc.c | 8 +++++--- include/target/target_core_backend.h | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index 2d88f087d961..fa2447004006 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -66,8 +66,8 @@ static void spc_fill_alua_data(struct se_port *port, unsigned char *buf) spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock); } -static sense_reason_t -spc_emulate_inquiry_std(struct se_cmd *cmd, char *buf) +sense_reason_t +spc_emulate_inquiry_std(struct se_cmd *cmd, unsigned char *buf) { struct se_lun *lun = cmd->se_lun; struct se_device *dev = cmd->se_dev; @@ -104,6 +104,7 @@ spc_emulate_inquiry_std(struct se_cmd *cmd, char *buf) return 0; } +EXPORT_SYMBOL(spc_emulate_inquiry_std); /* unit serial number */ static sense_reason_t @@ -160,7 +161,7 @@ static void spc_parse_naa_6h_vendor_specific(struct se_device *dev, * Device identification VPD, for a complete list of * DESIGNATOR TYPEs see spc4r17 Table 459. */ -static sense_reason_t +sense_reason_t spc_emulate_evpd_83(struct se_cmd *cmd, unsigned char *buf) { struct se_device *dev = cmd->se_dev; @@ -404,6 +405,7 @@ check_scsi_name: buf[3] = (len & 0xff); /* Page Length for VPD 0x83 */ return 0; } +EXPORT_SYMBOL(spc_emulate_evpd_83); /* Extended INQUIRY Data VPD Page */ static sense_reason_t diff --git a/include/target/target_core_backend.h b/include/target/target_core_backend.h index 507910992c59..819b0fc45215 100644 --- a/include/target/target_core_backend.h +++ b/include/target/target_core_backend.h @@ -53,6 +53,8 @@ void target_complete_cmd(struct se_cmd *, u8); sense_reason_t spc_parse_cdb(struct se_cmd *cmd, unsigned int *size); sense_reason_t spc_emulate_report_luns(struct se_cmd *cmd); sector_t spc_get_write_same_sectors(struct se_cmd *cmd); +sense_reason_t spc_emulate_inquiry_std(struct se_cmd *, unsigned char *); +sense_reason_t spc_emulate_evpd_83(struct se_cmd *, unsigned char *); sense_reason_t sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops); u32 sbc_get_device_rev(struct se_device *dev); -- cgit v1.2.3 From 6f15667e21e40ef14005699610723a13cfb26155 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Wed, 2 Jan 2013 12:48:00 -0800 Subject: target: Remove useless if statement We do the same thing no matter which way the test goes, so just remove the test and do what we're going to do. The debug messages printed the wrong value of CMD_T_ACTIVE and don't seem particularly useful, remove them too. Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_tmr.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index c6e0293ffdb0..d0b4dd95b91e 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -331,18 +331,6 @@ static void core_tmr_drain_state_list( fe_count = atomic_read(&cmd->t_fe_count); - if (!(cmd->transport_state & CMD_T_ACTIVE)) { - pr_debug("LUN_RESET: got CMD_T_ACTIVE for" - " cdb: %p, t_fe_count: %d dev: %p\n", cmd, - fe_count, dev); - cmd->transport_state |= CMD_T_ABORTED; - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - - core_tmr_handle_tas_abort(tmr_nacl, cmd, tas, fe_count); - continue; - } - pr_debug("LUN_RESET: Got !CMD_T_ACTIVE for cdb: %p," - " t_fe_count: %d dev: %p\n", cmd, fe_count, dev); cmd->transport_state |= CMD_T_ABORTED; spin_unlock_irqrestore(&cmd->t_state_lock, flags); -- cgit v1.2.3 From 8f67835f1e389978bb0809d5e528961986aa2a69 Mon Sep 17 00:00:00 2001 From: Martin Svec Date: Tue, 15 Jan 2013 12:43:35 -0800 Subject: target/rd: improve sg_table lookup scalability Sequential scan of rd_dev->sg_table_array in rd_get_sg_table is a serious I/O performance bottleneck for large rd LUNs. Fix this by computing the sg_table index directly from page offset because all sg_tables (except the last one) have the same number of pages. Tested with 90 GiB rd_mcp LUN, where the patch improved maximal random R/W IOPS by more than 100-150%, depending on actual hardware and SAN setup. Signed-off-by: Martin Svec Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_rd.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_rd.c b/drivers/target/target_core_rd.c index 0457de362e68..b0fff52c990e 100644 --- a/drivers/target/target_core_rd.c +++ b/drivers/target/target_core_rd.c @@ -256,10 +256,12 @@ static void rd_free_device(struct se_device *dev) static struct rd_dev_sg_table *rd_get_sg_table(struct rd_dev *rd_dev, u32 page) { - u32 i; struct rd_dev_sg_table *sg_table; + u32 i, sg_per_table = (RD_MAX_ALLOCATION_SIZE / + sizeof(struct scatterlist)); - for (i = 0; i < rd_dev->sg_table_count; i++) { + i = page / sg_per_table; + if (i < rd_dev->sg_table_count) { sg_table = &rd_dev->sg_table_array[i]; if ((sg_table->page_start_offset <= page) && (sg_table->page_end_offset >= page)) -- cgit v1.2.3 From 703d641d87034629f8b0da94334034ed5d805b36 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 18 Jan 2013 16:05:12 +0300 Subject: target: change sprintf to snprintf in transport_dump_vpd_ident "buf" is 128 characters and "vpd->device_identifier" is 256. It makes the static checkers complain. Also bump VPD_TMP_BUF_SIZE to match INQUIRY_VPD_DEVICE_IDENTIFIER_LEN. Signed-off-by: Dan Carpenter Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_transport.c | 9 ++++++--- include/target/target_core_base.h | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index bd587b70661a..96b64d57ebbb 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -907,15 +907,18 @@ int transport_dump_vpd_ident( switch (vpd->device_identifier_code_set) { case 0x01: /* Binary */ - sprintf(buf, "T10 VPD Binary Device Identifier: %s\n", + snprintf(buf, sizeof(buf), + "T10 VPD Binary Device Identifier: %s\n", &vpd->device_identifier[0]); break; case 0x02: /* ASCII */ - sprintf(buf, "T10 VPD ASCII Device Identifier: %s\n", + snprintf(buf, sizeof(buf), + "T10 VPD ASCII Device Identifier: %s\n", &vpd->device_identifier[0]); break; case 0x03: /* UTF-8 */ - sprintf(buf, "T10 VPD UTF-8 Device Identifier: %s\n", + snprintf(buf, sizeof(buf), + "T10 VPD UTF-8 Device Identifier: %s\n", &vpd->device_identifier[0]); break; default: diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 4fa0f1038360..199b0ad1a55e 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -44,7 +44,7 @@ /* Used by core_alua_store_tg_pt_gp_info() and target_core_alua_tg_pt_gp_show_attr_members() */ #define TG_PT_GROUP_NAME_BUF 256 /* Used to parse VPD into struct t10_vpd */ -#define VPD_TMP_BUF_SIZE 128 +#define VPD_TMP_BUF_SIZE 254 /* Used by transport_generic_cmd_sequencer() */ #define READ_BLOCK_LEN 6 #define READ_CAP_LEN 8 -- cgit v1.2.3 From 9d6064a347a8e15451cbdf6286542d402c9da24c Mon Sep 17 00:00:00 2001 From: Asias He Date: Sun, 6 Jan 2013 14:36:13 +0800 Subject: tcm_vhost: Use llist for cmd completion list This drops the cmd completion list spin lock and makes the cmd completion queue lock-less. Signed-off-by: Asias He Signed-off-by: Nicholas Bellinger --- drivers/vhost/tcm_vhost.c | 46 +++++++++++++--------------------------------- drivers/vhost/tcm_vhost.h | 2 +- 2 files changed, 14 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c index 22321cf84fbe..77d760bb2e44 100644 --- a/drivers/vhost/tcm_vhost.c +++ b/drivers/vhost/tcm_vhost.c @@ -47,6 +47,7 @@ #include #include /* TODO vhost.h currently depends on this */ #include +#include #include "vhost.c" #include "vhost.h" @@ -64,8 +65,7 @@ struct vhost_scsi { struct vhost_virtqueue vqs[3]; struct vhost_work vs_completion_work; /* cmd completion work item */ - struct list_head vs_completion_list; /* cmd completion queue */ - spinlock_t vs_completion_lock; /* protects s_completion_list */ + struct llist_head vs_completion_list; /* cmd completion queue */ }; /* Local pointer to allocated TCM configfs fabric module */ @@ -301,9 +301,7 @@ static void vhost_scsi_complete_cmd(struct tcm_vhost_cmd *tv_cmd) { struct vhost_scsi *vs = tv_cmd->tvc_vhost; - spin_lock_bh(&vs->vs_completion_lock); - list_add_tail(&tv_cmd->tvc_completion_list, &vs->vs_completion_list); - spin_unlock_bh(&vs->vs_completion_lock); + llist_add(&tv_cmd->tvc_completion_list, &vs->vs_completion_list); vhost_work_queue(&vs->dev, &vs->vs_completion_work); } @@ -347,27 +345,6 @@ static void vhost_scsi_free_cmd(struct tcm_vhost_cmd *tv_cmd) kfree(tv_cmd); } -/* Dequeue a command from the completion list */ -static struct tcm_vhost_cmd *vhost_scsi_get_cmd_from_completion( - struct vhost_scsi *vs) -{ - struct tcm_vhost_cmd *tv_cmd = NULL; - - spin_lock_bh(&vs->vs_completion_lock); - if (list_empty(&vs->vs_completion_list)) { - spin_unlock_bh(&vs->vs_completion_lock); - return NULL; - } - - list_for_each_entry(tv_cmd, &vs->vs_completion_list, - tvc_completion_list) { - list_del(&tv_cmd->tvc_completion_list); - break; - } - spin_unlock_bh(&vs->vs_completion_lock); - return tv_cmd; -} - /* Fill in status and signal that we are done processing this command * * This is scheduled in the vhost work queue so we are called with the owner @@ -377,12 +354,18 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) { struct vhost_scsi *vs = container_of(work, struct vhost_scsi, vs_completion_work); + struct virtio_scsi_cmd_resp v_rsp; struct tcm_vhost_cmd *tv_cmd; + struct llist_node *llnode; + struct se_cmd *se_cmd; + int ret; - while ((tv_cmd = vhost_scsi_get_cmd_from_completion(vs))) { - struct virtio_scsi_cmd_resp v_rsp; - struct se_cmd *se_cmd = &tv_cmd->tvc_se_cmd; - int ret; + llnode = llist_del_all(&vs->vs_completion_list); + while (llnode) { + tv_cmd = llist_entry(llnode, struct tcm_vhost_cmd, + tvc_completion_list); + llnode = llist_next(llnode); + se_cmd = &tv_cmd->tvc_se_cmd; pr_debug("%s tv_cmd %p resid %u status %#02x\n", __func__, tv_cmd, se_cmd->residual_count, se_cmd->scsi_status); @@ -426,7 +409,6 @@ static struct tcm_vhost_cmd *vhost_scsi_allocate_cmd( pr_err("Unable to allocate struct tcm_vhost_cmd\n"); return ERR_PTR(-ENOMEM); } - INIT_LIST_HEAD(&tv_cmd->tvc_completion_list); tv_cmd->tvc_tag = v_req->tag; tv_cmd->tvc_task_attr = v_req->task_attr; tv_cmd->tvc_exp_data_len = exp_data_len; @@ -857,8 +839,6 @@ static int vhost_scsi_open(struct inode *inode, struct file *f) return -ENOMEM; vhost_work_init(&s->vs_completion_work, vhost_scsi_complete_cmd_work); - INIT_LIST_HEAD(&s->vs_completion_list); - spin_lock_init(&s->vs_completion_lock); s->vqs[VHOST_SCSI_VQ_CTL].handle_kick = vhost_scsi_ctl_handle_kick; s->vqs[VHOST_SCSI_VQ_EVT].handle_kick = vhost_scsi_evt_handle_kick; diff --git a/drivers/vhost/tcm_vhost.h b/drivers/vhost/tcm_vhost.h index 7e87c63ecbcd..47ee80b3adee 100644 --- a/drivers/vhost/tcm_vhost.h +++ b/drivers/vhost/tcm_vhost.h @@ -34,7 +34,7 @@ struct tcm_vhost_cmd { /* Sense buffer that will be mapped into outgoing status */ unsigned char tvc_sense_buf[TRANSPORT_SENSE_BUFFER]; /* Completed commands list, serviced from vhost worker thread */ - struct list_head tvc_completion_list; + struct llist_node tvc_completion_list; }; struct tcm_vhost_nexus { -- cgit v1.2.3 From 765b34fdc1507add8fd6f1007410e7a375d0f89c Mon Sep 17 00:00:00 2001 From: Asias He Date: Tue, 22 Jan 2013 11:20:25 +0800 Subject: tcm_vhost: Introduce iov_num_pages Add a helper to calculate the number of pages needed for a iov entry. (nab: Drop unnecessary inline) Signed-off-by: Asias He Signed-off-by: Nicholas Bellinger --- drivers/vhost/tcm_vhost.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c index 77d760bb2e44..defc28595656 100644 --- a/drivers/vhost/tcm_vhost.c +++ b/drivers/vhost/tcm_vhost.c @@ -77,6 +77,12 @@ static struct workqueue_struct *tcm_vhost_workqueue; static DEFINE_MUTEX(tcm_vhost_mutex); static LIST_HEAD(tcm_vhost_list); +static int iov_num_pages(struct iovec *iov) +{ + return (PAGE_ALIGN((unsigned long)iov->iov_base + iov->iov_len) - + ((unsigned long)iov->iov_base & PAGE_MASK)) >> PAGE_SHIFT; +} + static int tcm_vhost_check_true(struct se_portal_group *se_tpg) { return 1; -- cgit v1.2.3 From f3158f362ca8b77b24c5de86cbec347ab2bb6430 Mon Sep 17 00:00:00 2001 From: Asias He Date: Tue, 22 Jan 2013 11:20:26 +0800 Subject: tcm_vhost: Use iov_num_pages to calculate sgl_count Signed-off-by: Asias He Signed-off-by: Nicholas Bellinger --- drivers/vhost/tcm_vhost.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c index defc28595656..62058d7a562f 100644 --- a/drivers/vhost/tcm_vhost.c +++ b/drivers/vhost/tcm_vhost.c @@ -479,11 +479,9 @@ static int vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *tv_cmd, * Find out how long sglist needs to be */ sgl_count = 0; - for (i = 0; i < niov; i++) { - sgl_count += (((uintptr_t)iov[i].iov_base + iov[i].iov_len + - PAGE_SIZE - 1) >> PAGE_SHIFT) - - ((uintptr_t)iov[i].iov_base >> PAGE_SHIFT); - } + for (i = 0; i < niov; i++) + sgl_count += iov_num_pages(&iov[i]); + /* TODO overflow checking */ sg = kmalloc(sizeof(tv_cmd->tvc_sgl[0]) * sgl_count, GFP_ATOMIC); -- cgit v1.2.3 From 1810053e8db3fd65f039229bde9e420278994300 Mon Sep 17 00:00:00 2001 From: Asias He Date: Tue, 22 Jan 2013 11:20:27 +0800 Subject: tcm_vhost: Optimize gup in vhost_scsi_map_to_sgl We can get all the pages in one time instead of calling gup N times. Signed-off-by: Asias He Signed-off-by: Nicholas Bellinger --- drivers/vhost/tcm_vhost.c | 54 ++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c index 62058d7a562f..704e4f674776 100644 --- a/drivers/vhost/tcm_vhost.c +++ b/drivers/vhost/tcm_vhost.c @@ -430,40 +430,47 @@ static struct tcm_vhost_cmd *vhost_scsi_allocate_cmd( * Returns the number of scatterlist entries used or -errno on error. */ static int vhost_scsi_map_to_sgl(struct scatterlist *sgl, - unsigned int sgl_count, void __user *ptr, size_t len, int write) + unsigned int sgl_count, struct iovec *iov, int write) { + unsigned int npages = 0, pages_nr, offset, nbytes; struct scatterlist *sg = sgl; - unsigned int npages = 0; - int ret; + void __user *ptr = iov->iov_base; + size_t len = iov->iov_len; + struct page **pages; + int ret, i; - while (len > 0) { - struct page *page; - unsigned int offset = (uintptr_t)ptr & ~PAGE_MASK; - unsigned int nbytes = min_t(unsigned int, - PAGE_SIZE - offset, len); + pages_nr = iov_num_pages(iov); + if (pages_nr > sgl_count) + return -ENOBUFS; - if (npages == sgl_count) { - ret = -ENOBUFS; - goto err; - } + pages = kmalloc(pages_nr * sizeof(struct page *), GFP_KERNEL); + if (!pages) + return -ENOMEM; - ret = get_user_pages_fast((unsigned long)ptr, 1, write, &page); - BUG_ON(ret == 0); /* we should either get our page or fail */ - if (ret < 0) - goto err; + ret = get_user_pages_fast((unsigned long)ptr, pages_nr, write, pages); + /* No pages were pinned */ + if (ret < 0) + goto out; + /* Less pages pinned than wanted */ + if (ret != pages_nr) { + for (i = 0; i < ret; i++) + put_page(pages[i]); + ret = -EFAULT; + goto out; + } - sg_set_page(sg, page, nbytes, offset); + while (len > 0) { + offset = (uintptr_t)ptr & ~PAGE_MASK; + nbytes = min_t(unsigned int, PAGE_SIZE - offset, len); + sg_set_page(sg, pages[npages], nbytes, offset); ptr += nbytes; len -= nbytes; sg++; npages++; } - return npages; -err: - /* Put pages that we hold */ - for (sg = sgl; sg != &sgl[npages]; sg++) - put_page(sg_page(sg)); +out: + kfree(pages); return ret; } @@ -496,8 +503,7 @@ static int vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *tv_cmd, pr_debug("Mapping %u iovecs for %u pages\n", niov, sgl_count); for (i = 0; i < niov; i++) { - ret = vhost_scsi_map_to_sgl(sg, sgl_count, iov[i].iov_base, - iov[i].iov_len, write); + ret = vhost_scsi_map_to_sgl(sg, sgl_count, &iov[i], write); if (ret < 0) { for (i = 0; i < tv_cmd->tvc_sgl_count; i++) put_page(sg_page(&tv_cmd->tvc_sgl[i])); -- cgit v1.2.3 From 47de201c73fbe435e7b635fa0eb812c7ce68be43 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 7 Jan 2013 09:51:21 -0300 Subject: [media] drivers/media/platform/soc_camera/pxa_camera.c: use devm_ functions This patch uses various devm_ functions for data that is allocated in the probe function of a platform driver and is only freed in the remove function. This also fixes a checkpatch warning, removing a space before a \n in a string. Signed-off-by: Julia Lawall Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/pxa_camera.c | 65 ++++++-------------------- 1 file changed, 15 insertions(+), 50 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c index 9c97f7e985e1..395e2e043615 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/soc_camera/pxa_camera.c @@ -1661,23 +1661,18 @@ static int pxa_camera_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); - if (!res || irq < 0) { - err = -ENODEV; - goto exit; - } + if (!res || irq < 0) + return -ENODEV; - pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL); + pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL); if (!pcdev) { dev_err(&pdev->dev, "Could not allocate pcdev\n"); - err = -ENOMEM; - goto exit; + return -ENOMEM; } - pcdev->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(pcdev->clk)) { - err = PTR_ERR(pcdev->clk); - goto exit_kfree; - } + pcdev->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pcdev->clk)) + return PTR_ERR(pcdev->clk); pcdev->res = res; @@ -1715,17 +1710,9 @@ static int pxa_camera_probe(struct platform_device *pdev) /* * Request the regions. */ - if (!request_mem_region(res->start, resource_size(res), - PXA_CAM_DRV_NAME)) { - err = -EBUSY; - goto exit_clk; - } - - base = ioremap(res->start, resource_size(res)); - if (!base) { - err = -ENOMEM; - goto exit_release; - } + base = devm_request_and_ioremap(&pdev->dev, res); + if (!base) + return -ENOMEM; pcdev->irq = irq; pcdev->base = base; @@ -1734,7 +1721,7 @@ static int pxa_camera_probe(struct platform_device *pdev) pxa_camera_dma_irq_y, pcdev); if (err < 0) { dev_err(&pdev->dev, "Can't request DMA for Y\n"); - goto exit_iounmap; + return err; } pcdev->dma_chans[0] = err; dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_chans[0]); @@ -1762,10 +1749,10 @@ static int pxa_camera_probe(struct platform_device *pdev) DRCMR(70) = pcdev->dma_chans[2] | DRCMR_MAPVLD; /* request irq */ - err = request_irq(pcdev->irq, pxa_camera_irq, 0, PXA_CAM_DRV_NAME, - pcdev); + err = devm_request_irq(&pdev->dev, pcdev->irq, pxa_camera_irq, 0, + PXA_CAM_DRV_NAME, pcdev); if (err) { - dev_err(&pdev->dev, "Camera interrupt register failed \n"); + dev_err(&pdev->dev, "Camera interrupt register failed\n"); goto exit_free_dma; } @@ -1777,27 +1764,16 @@ static int pxa_camera_probe(struct platform_device *pdev) err = soc_camera_host_register(&pcdev->soc_host); if (err) - goto exit_free_irq; + goto exit_free_dma; return 0; -exit_free_irq: - free_irq(pcdev->irq, pcdev); exit_free_dma: pxa_free_dma(pcdev->dma_chans[2]); exit_free_dma_u: pxa_free_dma(pcdev->dma_chans[1]); exit_free_dma_y: pxa_free_dma(pcdev->dma_chans[0]); -exit_iounmap: - iounmap(base); -exit_release: - release_mem_region(res->start, resource_size(res)); -exit_clk: - clk_put(pcdev->clk); -exit_kfree: - kfree(pcdev); -exit: return err; } @@ -1806,24 +1782,13 @@ static int pxa_camera_remove(struct platform_device *pdev) struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); struct pxa_camera_dev *pcdev = container_of(soc_host, struct pxa_camera_dev, soc_host); - struct resource *res; - - clk_put(pcdev->clk); pxa_free_dma(pcdev->dma_chans[0]); pxa_free_dma(pcdev->dma_chans[1]); pxa_free_dma(pcdev->dma_chans[2]); - free_irq(pcdev->irq, pcdev); soc_camera_host_unregister(soc_host); - iounmap(pcdev->base); - - res = pcdev->res; - release_mem_region(res->start, resource_size(res)); - - kfree(pcdev); - dev_info(&pdev->dev, "PXA Camera driver unloaded\n"); return 0; -- cgit v1.2.3 From 1be2956d30b2b6200ebb26ffb758ed3c8071303c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 24 Jan 2013 10:06:37 +0300 Subject: iscsi-target: make some temporary buffers larger My static checker complains because we use sprintf() to print some unsigned ints into 10 byte buffers. In theory unsigned ints can take 10 characters and we need another for the terminator. Signed-off-by: Dan Carpenter Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_parameters.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c index d89164287d00..ca2be406f141 100644 --- a/drivers/target/iscsi/iscsi_target_parameters.c +++ b/drivers/target/iscsi/iscsi_target_parameters.c @@ -1095,11 +1095,11 @@ static int iscsi_check_acceptor_state(struct iscsi_param *param, char *value, SET_PSTATE_REPLY_OPTIONAL(param); } } else if (IS_TYPE_NUMBER(param)) { - char *tmpptr, buf[10]; + char *tmpptr, buf[11]; u32 acceptor_value = simple_strtoul(param->value, &tmpptr, 0); u32 proposer_value = simple_strtoul(value, &tmpptr, 0); - memset(buf, 0, 10); + memset(buf, 0, sizeof(buf)); if (!strcmp(param->name, MAXCONNECTIONS) || !strcmp(param->name, MAXBURSTLENGTH) || @@ -1503,8 +1503,8 @@ static int iscsi_enforce_integrity_rules( FirstBurstLength = simple_strtoul(param->value, &tmpptr, 0); if (FirstBurstLength > MaxBurstLength) { - char tmpbuf[10]; - memset(tmpbuf, 0, 10); + char tmpbuf[11]; + memset(tmpbuf, 0, sizeof(tmpbuf)); sprintf(tmpbuf, "%u", MaxBurstLength); if (iscsi_update_param_value(param, tmpbuf)) return -1; -- cgit v1.2.3 From d0c8b259f8970d39354c1966853363345d401330 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Tue, 29 Jan 2013 22:10:06 -0800 Subject: target/iblock: Use backend REQ_FLUSH hint for WriteCacheEnabled status This patch allows IBLOCK to check block hints in request_queue->flush_flags when reporting current backend device WriteCacheEnabled status to a remote SCSI initiator port. This is done via a se_subsystem_api->get_write_cache() call instead of a backend se_device creation time flag, as we expect REQ_FLUSH bits to possibly change from an underlying blk_queue_flush() by the SCSI disk driver, or internal raw struct block_device driver usage. Also go ahead and update iblock_execute_rw() bio I/O path code to use REQ_FLUSH + REQ_FUA hints when determining WRITE_FUA usage, and make SPC emulation code use a spc_check_dev_wce() helper to handle both types of cases for virtual backend subsystem drivers. (asias: Drop unnecessary comparsion operators) Reported-by: majianpeng Cc: majianpeng Cc: Christoph Hellwig Cc: Jens Axboe Cc: James Bottomley Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_device.c | 6 ++++++ drivers/target/target_core_iblock.c | 31 +++++++++++++++++++++++-------- drivers/target/target_core_spc.c | 21 ++++++++++++++++++--- include/target/target_core_backend.h | 1 + 4 files changed, 48 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index f2aa7543d20a..d10cbed23472 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -772,6 +772,12 @@ int se_dev_set_emulate_write_cache(struct se_device *dev, int flag) pr_err("emulate_write_cache not supported for pSCSI\n"); return -EINVAL; } + if (dev->transport->get_write_cache) { + pr_warn("emulate_write_cache cannot be changed when underlying" + " HW reports WriteCacheEnabled, ignoring request\n"); + return 0; + } + dev->dev_attrib.emulate_write_cache = flag; pr_debug("dev[%p]: SE Device WRITE_CACHE_EMULATION flag: %d\n", dev, dev->dev_attrib.emulate_write_cache); diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index b526d23dcd4f..2f74e17ad3e6 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -154,6 +154,7 @@ static int iblock_configure_device(struct se_device *dev) if (blk_queue_nonrot(q)) dev->dev_attrib.is_nonrot = 1; + return 0; out_free_bioset: @@ -654,20 +655,24 @@ iblock_execute_rw(struct se_cmd *cmd) u32 sg_num = sgl_nents; sector_t block_lba; unsigned bio_cnt; - int rw; + int rw = 0; int i; if (data_direction == DMA_TO_DEVICE) { + struct iblock_dev *ib_dev = IBLOCK_DEV(dev); + struct request_queue *q = bdev_get_queue(ib_dev->ibd_bd); /* - * Force data to disk if we pretend to not have a volatile - * write cache, or the initiator set the Force Unit Access bit. + * Force writethrough using WRITE_FUA if a volatile write cache + * is not enabled, or if initiator set the Force Unit Access bit. */ - if (dev->dev_attrib.emulate_write_cache == 0 || - (dev->dev_attrib.emulate_fua_write > 0 && - (cmd->se_cmd_flags & SCF_FUA))) - rw = WRITE_FUA; - else + if (q->flush_flags & REQ_FUA) { + if (cmd->se_cmd_flags & SCF_FUA) + rw = WRITE_FUA; + else if (!(q->flush_flags & REQ_FLUSH)) + rw = WRITE_FUA; + } else { rw = WRITE; + } } else { rw = READ; } @@ -774,6 +779,15 @@ iblock_parse_cdb(struct se_cmd *cmd) return sbc_parse_cdb(cmd, &iblock_sbc_ops); } +bool iblock_get_write_cache(struct se_device *dev) +{ + struct iblock_dev *ib_dev = IBLOCK_DEV(dev); + struct block_device *bd = ib_dev->ibd_bd; + struct request_queue *q = bdev_get_queue(bd); + + return q->flush_flags & REQ_FLUSH; +} + static struct se_subsystem_api iblock_template = { .name = "iblock", .inquiry_prod = "IBLOCK", @@ -790,6 +804,7 @@ static struct se_subsystem_api iblock_template = { .show_configfs_dev_params = iblock_show_configfs_dev_params, .get_device_type = sbc_get_device_type, .get_blocks = iblock_get_blocks, + .get_write_cache = iblock_get_write_cache, }; static int __init iblock_module_init(void) diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index fa2447004006..803339516fb9 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -407,16 +407,31 @@ check_scsi_name: } EXPORT_SYMBOL(spc_emulate_evpd_83); +static bool +spc_check_dev_wce(struct se_device *dev) +{ + bool wce = false; + + if (dev->transport->get_write_cache) + wce = dev->transport->get_write_cache(dev); + else if (dev->dev_attrib.emulate_write_cache > 0) + wce = true; + + return wce; +} + /* Extended INQUIRY Data VPD Page */ static sense_reason_t spc_emulate_evpd_86(struct se_cmd *cmd, unsigned char *buf) { + struct se_device *dev = cmd->se_dev; + buf[3] = 0x3c; /* Set HEADSUP, ORDSUP, SIMPSUP */ buf[5] = 0x07; /* If WriteCache emulation is enabled, set V_SUP */ - if (cmd->se_dev->dev_attrib.emulate_write_cache > 0) + if (spc_check_dev_wce(dev)) buf[6] = 0x01; return 0; } @@ -766,7 +781,7 @@ static int spc_modesense_caching(struct se_device *dev, u8 pc, u8 *p) if (pc == 1) goto out; - if (dev->dev_attrib.emulate_write_cache > 0) + if (spc_check_dev_wce(dev)) p[2] = 0x04; /* Write Cache Enable */ p[12] = 0x20; /* Disabled Read Ahead */ @@ -878,7 +893,7 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd) (cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY))) spc_modesense_write_protect(&buf[length], type); - if ((dev->dev_attrib.emulate_write_cache > 0) && + if ((spc_check_dev_wce(dev)) && (dev->dev_attrib.emulate_fua_write > 0)) spc_modesense_dpofua(&buf[length], type); diff --git a/include/target/target_core_backend.h b/include/target/target_core_backend.h index 819b0fc45215..f9a80169775d 100644 --- a/include/target/target_core_backend.h +++ b/include/target/target_core_backend.h @@ -35,6 +35,7 @@ struct se_subsystem_api { u32 (*get_device_type)(struct se_device *); sector_t (*get_blocks)(struct se_device *); unsigned char *(*get_sense_buffer)(struct se_cmd *); + bool (*get_write_cache)(struct se_device *); }; struct sbc_ops { -- cgit v1.2.3 From 07ea81b6f7d6345e146245c4964640a5a27b5cc5 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 31 Jan 2013 11:17:54 +0300 Subject: target: don't always say "ipv6" as address type "lstat->last_intr_fail_ip_addr" is an array inside the "lstat" struct. It's never NULL so we always print "ipv6\n" here. The test should be "if (lstat->last_intr_fail_ip_family == AF_INET6)". We don't need the temporary buffer either. We could print directly into "page". Signed-off-by: Dan Carpenter Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_stat.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/target/iscsi/iscsi_target_stat.c b/drivers/target/iscsi/iscsi_target_stat.c index 421d6947dc64..54a6f3834cf0 100644 --- a/drivers/target/iscsi/iscsi_target_stat.c +++ b/drivers/target/iscsi/iscsi_target_stat.c @@ -410,14 +410,16 @@ static ssize_t iscsi_stat_tgt_attr_show_attr_fail_intr_addr_type( struct iscsi_tiqn *tiqn = container_of(igrps, struct iscsi_tiqn, tiqn_stat_grps); struct iscsi_login_stats *lstat = &tiqn->login_stats; - unsigned char buf[8]; + int ret; spin_lock(&lstat->lock); - snprintf(buf, 8, "%s", (lstat->last_intr_fail_ip_addr != NULL) ? - "ipv6" : "ipv4"); + if (lstat->last_intr_fail_ip_family == AF_INET6) + ret = snprintf(page, PAGE_SIZE, "ipv6\n"); + else + ret = snprintf(page, PAGE_SIZE, "ipv4\n"); spin_unlock(&lstat->lock); - return snprintf(page, PAGE_SIZE, "%s\n", buf); + return ret; } ISCSI_STAT_TGT_ATTR_RO(fail_intr_addr_type); -- cgit v1.2.3 From 0e48e7a5a345a727d5fd7a06bd6a9e6a67eae2bd Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 31 Jan 2013 11:19:09 +0300 Subject: target: don't truncate the fail intr address The temporary buffer was only 32 characters but ->last_intr_fail_ip_addr is a 48 character buffer. We don't need to use a temporary buffer at all, we can just print directly to "page". Signed-off-by: Dan Carpenter Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_stat.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/target/iscsi/iscsi_target_stat.c b/drivers/target/iscsi/iscsi_target_stat.c index 54a6f3834cf0..464b4206a51e 100644 --- a/drivers/target/iscsi/iscsi_target_stat.c +++ b/drivers/target/iscsi/iscsi_target_stat.c @@ -429,16 +429,19 @@ static ssize_t iscsi_stat_tgt_attr_show_attr_fail_intr_addr( struct iscsi_tiqn *tiqn = container_of(igrps, struct iscsi_tiqn, tiqn_stat_grps); struct iscsi_login_stats *lstat = &tiqn->login_stats; - unsigned char buf[32]; + int ret; spin_lock(&lstat->lock); - if (lstat->last_intr_fail_ip_family == AF_INET6) - snprintf(buf, 32, "[%s]", lstat->last_intr_fail_ip_addr); - else - snprintf(buf, 32, "%s", lstat->last_intr_fail_ip_addr); + if (lstat->last_intr_fail_ip_family == AF_INET6) { + ret = snprintf(page, PAGE_SIZE, "[%s]\n", + lstat->last_intr_fail_ip_addr); + } else { + ret = snprintf(page, PAGE_SIZE, "%s\n", + lstat->last_intr_fail_ip_addr); + } spin_unlock(&lstat->lock); - return snprintf(page, PAGE_SIZE, "%s\n", buf); + return ret; } ISCSI_STAT_TGT_ATTR_RO(fail_intr_addr); -- cgit v1.2.3 From adfa9570a56c3dbfc2a28baab77ff6f0b8f480d3 Mon Sep 17 00:00:00 2001 From: Tregaron Bayly Date: Thu, 31 Jan 2013 15:30:24 -0700 Subject: target: Add device attribute to expose config_item_name for INQUIRY model This patch changes LIO to use the configfs backend device name as the model if you echo '1' to an individual device's emulate_model_alias attribute. This is a valid operation only on devices with an export count of 0. Signed-off-by: Tregaron Bayly Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_configfs.c | 4 ++++ drivers/target/target_core_device.c | 39 +++++++++++++++++++++++++++++++++++ drivers/target/target_core_internal.h | 1 + include/target/target_core_base.h | 3 +++ 4 files changed, 47 insertions(+) (limited to 'drivers') diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index 4efb61b8d001..43b7ac6c5b1c 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -609,6 +609,9 @@ static struct target_core_dev_attrib_attribute \ __CONFIGFS_EATTR_RO(_name, \ target_core_dev_show_attr_##_name); +DEF_DEV_ATTRIB(emulate_model_alias); +SE_DEV_ATTR(emulate_model_alias, S_IRUGO | S_IWUSR); + DEF_DEV_ATTRIB(emulate_dpo); SE_DEV_ATTR(emulate_dpo, S_IRUGO | S_IWUSR); @@ -681,6 +684,7 @@ SE_DEV_ATTR(max_write_same_len, S_IRUGO | S_IWUSR); CONFIGFS_EATTR_OPS(target_core_dev_attrib, se_dev_attrib, da_group); static struct configfs_attribute *target_core_dev_attrib_attrs[] = { + &target_core_dev_attrib_emulate_model_alias.attr, &target_core_dev_attrib_emulate_dpo.attr, &target_core_dev_attrib_emulate_fua_write.attr, &target_core_dev_attrib_emulate_fua_read.attr, diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index d10cbed23472..6fb82f1abe5c 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -713,6 +713,44 @@ int se_dev_set_max_write_same_len( return 0; } +static void dev_set_t10_wwn_model_alias(struct se_device *dev) +{ + const char *configname; + + configname = config_item_name(&dev->dev_group.cg_item); + if (strlen(configname) >= 16) { + pr_warn("dev[%p]: Backstore name '%s' is too long for " + "INQUIRY_MODEL, truncating to 16 bytes\n", dev, + configname); + } + snprintf(&dev->t10_wwn.model[0], 16, "%s", configname); +} + +int se_dev_set_emulate_model_alias(struct se_device *dev, int flag) +{ + if (dev->export_count) { + pr_err("dev[%p]: Unable to change model alias" + " while export_count is %d\n", + dev, dev->export_count); + return -EINVAL; + } + + if (flag != 0 && flag != 1) { + pr_err("Illegal value %d\n", flag); + return -EINVAL; + } + + if (flag) { + dev_set_t10_wwn_model_alias(dev); + } else { + strncpy(&dev->t10_wwn.model[0], + dev->transport->inquiry_prod, 16); + } + dev->dev_attrib.emulate_model_alias = flag; + + return 0; +} + int se_dev_set_emulate_dpo(struct se_device *dev, int flag) { if (flag != 0 && flag != 1) { @@ -1396,6 +1434,7 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name) dev->t10_alua.t10_dev = dev; dev->dev_attrib.da_dev = dev; + dev->dev_attrib.emulate_model_alias = DA_EMULATE_MODEL_ALIAS; dev->dev_attrib.emulate_dpo = DA_EMULATE_DPO; dev->dev_attrib.emulate_fua_write = DA_EMULATE_FUA_WRITE; dev->dev_attrib.emulate_fua_read = DA_EMULATE_FUA_READ; diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h index 93e9c1f580b0..fdc51f8301a1 100644 --- a/drivers/target/target_core_internal.h +++ b/drivers/target/target_core_internal.h @@ -25,6 +25,7 @@ int se_dev_set_max_unmap_block_desc_count(struct se_device *, u32); int se_dev_set_unmap_granularity(struct se_device *, u32); int se_dev_set_unmap_granularity_alignment(struct se_device *, u32); int se_dev_set_max_write_same_len(struct se_device *, u32); +int se_dev_set_emulate_model_alias(struct se_device *, int); int se_dev_set_emulate_dpo(struct se_device *, int); int se_dev_set_emulate_fua_write(struct se_device *, int); int se_dev_set_emulate_fua_read(struct se_device *, int); diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 199b0ad1a55e..df14dce59191 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -75,6 +75,8 @@ #define DA_MAX_WRITE_SAME_LEN 0 /* Default max transfer length */ #define DA_FABRIC_MAX_SECTORS 8192 +/* Use a model alias based on the configfs backend device name */ +#define DA_EMULATE_MODEL_ALIAS 0 /* Emulation for Direct Page Out */ #define DA_EMULATE_DPO 0 /* Emulation for Forced Unit Access WRITEs */ @@ -591,6 +593,7 @@ struct se_dev_entry { }; struct se_dev_attrib { + int emulate_model_alias; int emulate_dpo; int emulate_fua_write; int emulate_fua_read; -- cgit v1.2.3 From fd51625d6331c80cd249bd201b012ed5fe4f0476 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 3 Jan 2013 15:35:56 -0300 Subject: [media] sh_vou: Use video_drvdata() Replace video_devdata() followed by video_get_drvdata() calls with video_drvdata(). Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_vou.c | 57 +++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index ab5bca40a5b3..e969fea3c122 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -420,8 +420,7 @@ static int sh_vou_enum_fmt_vid_out(struct file *file, void *priv, static int sh_vou_g_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *fmt) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); @@ -672,8 +671,7 @@ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std) static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *fmt) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); struct v4l2_pix_format *pix = &fmt->fmt.pix; unsigned int img_height_max; int pix_idx; @@ -830,8 +828,7 @@ static int sh_vou_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) static int sh_vou_streamon(struct file *file, void *priv, enum v4l2_buf_type buftype) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = priv; int ret; @@ -849,8 +846,7 @@ static int sh_vou_streamon(struct file *file, void *priv, static int sh_vou_streamoff(struct file *file, void *priv, enum v4l2_buf_type buftype) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = priv; dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); @@ -882,13 +878,12 @@ static u32 sh_vou_ntsc_mode(enum sh_vou_bus_fmt bus_fmt) static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id *std_id) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); int ret; dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, *std_id); - if (*std_id & ~vdev->tvnorms) + if (*std_id & ~vou_dev->vdev->tvnorms) return -EINVAL; ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, @@ -910,8 +905,7 @@ static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id *std_id) static int sh_vou_g_std(struct file *file, void *priv, v4l2_std_id *std) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); @@ -922,8 +916,7 @@ static int sh_vou_g_std(struct file *file, void *priv, v4l2_std_id *std) static int sh_vou_g_crop(struct file *file, void *fh, struct v4l2_crop *a) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); @@ -937,8 +930,7 @@ static int sh_vou_g_crop(struct file *file, void *fh, struct v4l2_crop *a) static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a) { struct v4l2_crop a_writable = *a; - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); struct v4l2_rect *rect = &a_writable.c; struct v4l2_crop sd_crop = {.type = V4L2_BUF_TYPE_VIDEO_OUTPUT}; struct v4l2_pix_format *pix = &vou_dev->pix; @@ -1161,8 +1153,7 @@ static int sh_vou_hw_init(struct sh_vou_device *vou_dev) /* File operations */ static int sh_vou_open(struct file *file) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = kzalloc(sizeof(struct sh_vou_file), GFP_KERNEL); @@ -1179,11 +1170,11 @@ static int sh_vou_open(struct file *file) int ret; /* First open */ vou_dev->status = SH_VOU_INITIALISING; - pm_runtime_get_sync(vdev->v4l2_dev->dev); + pm_runtime_get_sync(vou_dev->v4l2_dev.dev); ret = sh_vou_hw_init(vou_dev); if (ret < 0) { atomic_dec(&vou_dev->use_count); - pm_runtime_put(vdev->v4l2_dev->dev); + pm_runtime_put(vou_dev->v4l2_dev.dev); vou_dev->status = SH_VOU_IDLE; mutex_unlock(&vou_dev->fop_lock); return ret; @@ -1194,8 +1185,8 @@ static int sh_vou_open(struct file *file) vou_dev->v4l2_dev.dev, &vou_dev->lock, V4L2_BUF_TYPE_VIDEO_OUTPUT, V4L2_FIELD_NONE, - sizeof(struct videobuf_buffer), vdev, - &vou_dev->fop_lock); + sizeof(struct videobuf_buffer), + vou_dev->vdev, &vou_dev->fop_lock); mutex_unlock(&vou_dev->fop_lock); return 0; @@ -1203,8 +1194,7 @@ static int sh_vou_open(struct file *file) static int sh_vou_release(struct file *file) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = file->private_data; dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); @@ -1214,7 +1204,7 @@ static int sh_vou_release(struct file *file) /* Last close */ vou_dev->status = SH_VOU_IDLE; sh_vou_reg_a_set(vou_dev, VOUER, 0, 0x101); - pm_runtime_put(vdev->v4l2_dev->dev); + pm_runtime_put(vou_dev->v4l2_dev.dev); mutex_unlock(&vou_dev->fop_lock); } @@ -1226,8 +1216,7 @@ static int sh_vou_release(struct file *file) static int sh_vou_mmap(struct file *file, struct vm_area_struct *vma) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = file->private_data; int ret; @@ -1242,8 +1231,7 @@ static int sh_vou_mmap(struct file *file, struct vm_area_struct *vma) static unsigned int sh_vou_poll(struct file *file, poll_table *wait) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = file->private_data; unsigned int res; @@ -1258,8 +1246,7 @@ static unsigned int sh_vou_poll(struct file *file, poll_table *wait) static int sh_vou_g_chip_ident(struct file *file, void *fh, struct v4l2_dbg_chip_ident *id) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_chip_ident, id); } @@ -1268,8 +1255,7 @@ static int sh_vou_g_chip_ident(struct file *file, void *fh, static int sh_vou_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_register, reg); } @@ -1277,8 +1263,7 @@ static int sh_vou_g_register(struct file *file, void *fh, static int sh_vou_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, s_register, reg); } -- cgit v1.2.3 From d899eddde548b9a6d3a59d0600feaab377efcd3f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 3 Jan 2013 15:35:57 -0300 Subject: [media] sh_vou: Use vou_dev instead of vou_file wherever possible This prepares for the removal of vou_file. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_vou.c | 57 ++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index e969fea3c122..66c8da18df84 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -254,7 +254,8 @@ static int sh_vou_buf_setup(struct videobuf_queue *vq, unsigned int *count, if (PAGE_ALIGN(*size) * *count > 4 * 1024 * 1024) *count = 4 * 1024 * 1024 / PAGE_ALIGN(*size); - dev_dbg(vq->dev, "%s(): count=%d, size=%d\n", __func__, *count, *size); + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): count=%d, size=%d\n", __func__, + *count, *size); return 0; } @@ -270,7 +271,7 @@ static int sh_vou_buf_prepare(struct videobuf_queue *vq, int bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8; int ret; - dev_dbg(vq->dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); if (vb->width != pix->width || vb->height != pix->height || @@ -300,7 +301,7 @@ static int sh_vou_buf_prepare(struct videobuf_queue *vq, vb->state = VIDEOBUF_PREPARED; } - dev_dbg(vq->dev, + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): fmt #%d, %u bytes per line, phys 0x%x, type %d, state %d\n", __func__, vou_dev->pix_idx, bytes_per_line, videobuf_to_dma_contig(vb), vb->memory, vb->state); @@ -315,7 +316,7 @@ static void sh_vou_buf_queue(struct videobuf_queue *vq, struct video_device *vdev = vq->priv_data; struct sh_vou_device *vou_dev = video_get_drvdata(vdev); - dev_dbg(vq->dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); vb->state = VIDEOBUF_QUEUED; list_add_tail(&vb->queue, &vou_dev->queue); @@ -326,8 +327,8 @@ static void sh_vou_buf_queue(struct videobuf_queue *vq, vou_dev->active = vb; /* Start from side A: we use mirror addresses, so, set B */ sh_vou_reg_a_write(vou_dev, VOURPR, 1); - dev_dbg(vq->dev, "%s: first buffer status 0x%x\n", __func__, - sh_vou_reg_a_read(vou_dev, VOUSTR)); + dev_dbg(vou_dev->v4l2_dev.dev, "%s: first buffer status 0x%x\n", + __func__, sh_vou_reg_a_read(vou_dev, VOUSTR)); sh_vou_schedule_next(vou_dev, vb); /* Only activate VOU after the second buffer */ } else if (vou_dev->active->queue.next == &vb->queue) { @@ -337,8 +338,8 @@ static void sh_vou_buf_queue(struct videobuf_queue *vq, /* Register side switching with frame VSYNC */ sh_vou_reg_a_write(vou_dev, VOURCR, 5); - dev_dbg(vq->dev, "%s: second buffer status 0x%x\n", __func__, - sh_vou_reg_a_read(vou_dev, VOUSTR)); + dev_dbg(vou_dev->v4l2_dev.dev, "%s: second buffer status 0x%x\n", + __func__, sh_vou_reg_a_read(vou_dev, VOUSTR)); /* Enable End-of-Frame (VSYNC) interrupts */ sh_vou_reg_a_write(vou_dev, VOUIR, 0x10004); @@ -356,7 +357,7 @@ static void sh_vou_buf_release(struct videobuf_queue *vq, struct sh_vou_device *vou_dev = video_get_drvdata(vdev); unsigned long flags; - dev_dbg(vq->dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); spin_lock_irqsave(&vou_dev->lock, flags); @@ -389,9 +390,9 @@ static struct videobuf_queue_ops sh_vou_video_qops = { static int sh_vou_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct sh_vou_file *vou_file = priv; + struct sh_vou_device *vou_dev = video_drvdata(file); - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); strlcpy(cap->card, "SuperH VOU", sizeof(cap->card)); cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; @@ -402,12 +403,12 @@ static int sh_vou_querycap(struct file *file, void *priv, static int sh_vou_enum_fmt_vid_out(struct file *file, void *priv, struct v4l2_fmtdesc *fmt) { - struct sh_vou_file *vou_file = priv; + struct sh_vou_device *vou_dev = video_drvdata(file); if (fmt->index >= ARRAY_SIZE(vou_fmt)) return -EINVAL; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; strlcpy(fmt->description, vou_fmt[fmt->index].desc, @@ -763,11 +764,11 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, static int sh_vou_try_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *fmt) { - struct sh_vou_file *vou_file = priv; + struct sh_vou_device *vou_dev = video_drvdata(file); struct v4l2_pix_format *pix = &fmt->fmt.pix; int i; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; pix->field = V4L2_FIELD_NONE; @@ -787,9 +788,10 @@ static int sh_vou_try_fmt_vid_out(struct file *file, void *priv, static int sh_vou_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *req) { + struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = priv; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) return -EINVAL; @@ -800,27 +802,30 @@ static int sh_vou_reqbufs(struct file *file, void *priv, static int sh_vou_querybuf(struct file *file, void *priv, struct v4l2_buffer *b) { + struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = priv; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); return videobuf_querybuf(&vou_file->vbq, b); } static int sh_vou_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) { + struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = priv; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); return videobuf_qbuf(&vou_file->vbq, b); } static int sh_vou_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) { + struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = priv; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); return videobuf_dqbuf(&vou_file->vbq, b, file->f_flags & O_NONBLOCK); } @@ -832,7 +837,7 @@ static int sh_vou_streamon(struct file *file, void *priv, struct sh_vou_file *vou_file = priv; int ret; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, s_stream, 1); @@ -849,7 +854,7 @@ static int sh_vou_streamoff(struct file *file, void *priv, struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = priv; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); /* * This calls buf_release from host driver's videobuf_queue_ops for all @@ -1021,9 +1026,9 @@ static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a) static int sh_vou_cropcap(struct file *file, void *priv, struct v4l2_cropcap *a) { - struct sh_vou_file *vou_file = priv; + struct sh_vou_device *vou_dev = video_drvdata(file); - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; a->bounds.left = 0; @@ -1197,7 +1202,7 @@ static int sh_vou_release(struct file *file) struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = file->private_data; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); if (!atomic_dec_return(&vou_dev->use_count)) { mutex_lock(&vou_dev->fop_lock); @@ -1220,7 +1225,7 @@ static int sh_vou_mmap(struct file *file, struct vm_area_struct *vma) struct sh_vou_file *vou_file = file->private_data; int ret; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); if (mutex_lock_interruptible(&vou_dev->fop_lock)) return -ERESTARTSYS; @@ -1235,7 +1240,7 @@ static unsigned int sh_vou_poll(struct file *file, poll_table *wait) struct sh_vou_file *vou_file = file->private_data; unsigned int res; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); mutex_lock(&vou_dev->fop_lock); res = videobuf_poll_stream(file, &vou_file->vbq, wait); -- cgit v1.2.3 From 67e18cf9ab21648a477e91e0d3cb6dbdb1330262 Mon Sep 17 00:00:00 2001 From: Asias He Date: Tue, 5 Feb 2013 12:31:57 +0800 Subject: tcm_vhost: Multi-target support In order to take advantages of Paolo's multi-queue virito-scsi, we need multi-target support in tcm_vhost first. Otherwise all the requests go to one queue and other queues are idle. This patch makes: 1. All the targets under the wwpn is seen and can be used by guest. 2. No need to pass the tpgt number in struct vhost_scsi_target to tcm_vhost.ko. Only wwpn is needed. 3. We can always pass max_target = 255 to guest now, since we abort the request who's target id does not exist. Changes in v2: - Handle non-contiguous tpgt Changes in v3: - Simplfy lock in vhost_scsi_set_endpoint - Return -EEXIST when does not match Signed-off-by: Asias He Signed-off-by: Nicholas Bellinger --- drivers/vhost/tcm_vhost.c | 131 +++++++++++++++++++++++++++++----------------- drivers/vhost/tcm_vhost.h | 4 +- 2 files changed, 85 insertions(+), 50 deletions(-) (limited to 'drivers') diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c index 704e4f674776..81ecda54b923 100644 --- a/drivers/vhost/tcm_vhost.c +++ b/drivers/vhost/tcm_vhost.c @@ -59,8 +59,14 @@ enum { VHOST_SCSI_VQ_IO = 2, }; +#define VHOST_SCSI_MAX_TARGET 256 + struct vhost_scsi { - struct tcm_vhost_tpg *vs_tpg; /* Protected by vhost_scsi->dev.mutex */ + /* Protected by vhost_scsi->dev.mutex */ + struct tcm_vhost_tpg *vs_tpg[VHOST_SCSI_MAX_TARGET]; + char vs_vhost_wwpn[TRANSPORT_IQN_LEN]; + bool vs_endpoint; + struct vhost_dev dev; struct vhost_virtqueue vqs[3]; @@ -564,10 +570,10 @@ static void vhost_scsi_handle_vq(struct vhost_scsi *vs) u32 exp_data_len, data_first, data_num, data_direction; unsigned out, in, i; int head, ret; + u8 target; /* Must use ioctl VHOST_SCSI_SET_ENDPOINT */ - tv_tpg = vs->vs_tpg; - if (unlikely(!tv_tpg)) + if (unlikely(!vs->vs_endpoint)) return; mutex_lock(&vq->mutex); @@ -635,6 +641,28 @@ static void vhost_scsi_handle_vq(struct vhost_scsi *vs) break; } + /* Extract the tpgt */ + target = v_req.lun[1]; + tv_tpg = vs->vs_tpg[target]; + + /* Target does not exist, fail the request */ + if (unlikely(!tv_tpg)) { + struct virtio_scsi_cmd_resp __user *resp; + struct virtio_scsi_cmd_resp rsp; + + memset(&rsp, 0, sizeof(rsp)); + rsp.response = VIRTIO_SCSI_S_BAD_TARGET; + resp = vq->iov[out].iov_base; + ret = __copy_to_user(resp, &rsp, sizeof(rsp)); + if (!ret) + vhost_add_used_and_signal(&vs->dev, + &vs->vqs[2], head, 0); + else + pr_err("Faulted on virtio_scsi_cmd_resp\n"); + + continue; + } + exp_data_len = 0; for (i = 0; i < data_num; i++) exp_data_len += vq->iov[data_first + i].iov_len; @@ -743,7 +771,8 @@ static int vhost_scsi_set_endpoint( { struct tcm_vhost_tport *tv_tport; struct tcm_vhost_tpg *tv_tpg; - int index; + bool match = false; + int index, ret; mutex_lock(&vs->dev.mutex); /* Verify that ring has been setup correctly. */ @@ -754,7 +783,6 @@ static int vhost_scsi_set_endpoint( return -EFAULT; } } - mutex_unlock(&vs->dev.mutex); mutex_lock(&tcm_vhost_mutex); list_for_each_entry(tv_tpg, &tcm_vhost_list, tv_tpg_list) { @@ -769,30 +797,33 @@ static int vhost_scsi_set_endpoint( } tv_tport = tv_tpg->tport; - if (!strcmp(tv_tport->tport_name, t->vhost_wwpn) && - (tv_tpg->tport_tpgt == t->vhost_tpgt)) { - tv_tpg->tv_tpg_vhost_count++; - mutex_unlock(&tv_tpg->tv_tpg_mutex); - mutex_unlock(&tcm_vhost_mutex); - - mutex_lock(&vs->dev.mutex); - if (vs->vs_tpg) { - mutex_unlock(&vs->dev.mutex); - mutex_lock(&tv_tpg->tv_tpg_mutex); - tv_tpg->tv_tpg_vhost_count--; + if (!strcmp(tv_tport->tport_name, t->vhost_wwpn)) { + if (vs->vs_tpg[tv_tpg->tport_tpgt]) { mutex_unlock(&tv_tpg->tv_tpg_mutex); + mutex_unlock(&tcm_vhost_mutex); + mutex_unlock(&vs->dev.mutex); return -EEXIST; } - - vs->vs_tpg = tv_tpg; + tv_tpg->tv_tpg_vhost_count++; + vs->vs_tpg[tv_tpg->tport_tpgt] = tv_tpg; smp_mb__after_atomic_inc(); - mutex_unlock(&vs->dev.mutex); - return 0; + match = true; } mutex_unlock(&tv_tpg->tv_tpg_mutex); } mutex_unlock(&tcm_vhost_mutex); - return -EINVAL; + + if (match) { + memcpy(vs->vs_vhost_wwpn, t->vhost_wwpn, + sizeof(vs->vs_vhost_wwpn)); + vs->vs_endpoint = true; + ret = 0; + } else { + ret = -EEXIST; + } + + mutex_unlock(&vs->dev.mutex); + return ret; } static int vhost_scsi_clear_endpoint( @@ -801,7 +832,8 @@ static int vhost_scsi_clear_endpoint( { struct tcm_vhost_tport *tv_tport; struct tcm_vhost_tpg *tv_tpg; - int index, ret; + int index, ret, i; + u8 target; mutex_lock(&vs->dev.mutex); /* Verify that ring has been setup correctly. */ @@ -811,27 +843,32 @@ static int vhost_scsi_clear_endpoint( goto err; } } + for (i = 0; i < VHOST_SCSI_MAX_TARGET; i++) { + target = i; - if (!vs->vs_tpg) { - ret = -ENODEV; - goto err; - } - tv_tpg = vs->vs_tpg; - tv_tport = tv_tpg->tport; - - if (strcmp(tv_tport->tport_name, t->vhost_wwpn) || - (tv_tpg->tport_tpgt != t->vhost_tpgt)) { - pr_warn("tv_tport->tport_name: %s, tv_tpg->tport_tpgt: %hu" - " does not match t->vhost_wwpn: %s, t->vhost_tpgt: %hu\n", - tv_tport->tport_name, tv_tpg->tport_tpgt, - t->vhost_wwpn, t->vhost_tpgt); - ret = -EINVAL; - goto err; + tv_tpg = vs->vs_tpg[target]; + if (!tv_tpg) + continue; + + tv_tport = tv_tpg->tport; + if (!tv_tport) { + ret = -ENODEV; + goto err; + } + + if (strcmp(tv_tport->tport_name, t->vhost_wwpn)) { + pr_warn("tv_tport->tport_name: %s, tv_tpg->tport_tpgt: %hu" + " does not match t->vhost_wwpn: %s, t->vhost_tpgt: %hu\n", + tv_tport->tport_name, tv_tpg->tport_tpgt, + t->vhost_wwpn, t->vhost_tpgt); + ret = -EINVAL; + goto err; + } + tv_tpg->tv_tpg_vhost_count--; + vs->vs_tpg[target] = NULL; + vs->vs_endpoint = false; } - tv_tpg->tv_tpg_vhost_count--; - vs->vs_tpg = NULL; mutex_unlock(&vs->dev.mutex); - return 0; err: @@ -866,16 +903,12 @@ static int vhost_scsi_open(struct inode *inode, struct file *f) static int vhost_scsi_release(struct inode *inode, struct file *f) { struct vhost_scsi *s = f->private_data; + struct vhost_scsi_target t; - if (s->vs_tpg && s->vs_tpg->tport) { - struct vhost_scsi_target backend; - - memcpy(backend.vhost_wwpn, s->vs_tpg->tport->tport_name, - sizeof(backend.vhost_wwpn)); - backend.vhost_tpgt = s->vs_tpg->tport_tpgt; - vhost_scsi_clear_endpoint(s, &backend); - } - + mutex_lock(&s->dev.mutex); + memcpy(t.vhost_wwpn, s->vs_vhost_wwpn, sizeof(t.vhost_wwpn)); + mutex_unlock(&s->dev.mutex); + vhost_scsi_clear_endpoint(s, &t); vhost_dev_stop(&s->dev); vhost_dev_cleanup(&s->dev, false); kfree(s); diff --git a/drivers/vhost/tcm_vhost.h b/drivers/vhost/tcm_vhost.h index 47ee80b3adee..519a5504d347 100644 --- a/drivers/vhost/tcm_vhost.h +++ b/drivers/vhost/tcm_vhost.h @@ -93,9 +93,11 @@ struct tcm_vhost_tport { * * ABI Rev 0: July 2012 version starting point for v3.6-rc merge candidate + * RFC-v2 vhost-scsi userspace. Add GET_ABI_VERSION ioctl usage + * ABI Rev 1: January 2013. Ignore vhost_tpgt filed in struct vhost_scsi_target. + * All the targets under vhost_wwpn can be seen and used by guset. */ -#define VHOST_SCSI_ABI_VERSION 0 +#define VHOST_SCSI_ABI_VERSION 1 struct vhost_scsi_target { int abi_version; -- cgit v1.2.3 From 1b7f390eb3bfc197c979c5478eadbc2a90f07667 Mon Sep 17 00:00:00 2001 From: Asias He Date: Wed, 6 Feb 2013 13:20:59 +0800 Subject: tcm_vhost: Multi-queue support This adds virtio-scsi multi-queue support to tcm_vhost. In order to use multi-queue, guest side multi-queue support is need. It can be found here: https://lkml.org/lkml/2012/12/18/166 Currently, only one thread is created by vhost core code for each vhost_scsi instance. Even if there are multi-queues, all the handling of guest kick (vhost_scsi_handle_kick) are processed in one thread. This is not optimal. Luckily, most of the work is offloaded to the tcm_vhost workqueue. Some initial perf numbers: 1 queue, 4 targets, 1 lun per target 4K request size, 50% randread + 50% randwrite: 127K/127k IOPS 4 queues, 4 targets, 1 lun per target 4K request size, 50% randread + 50% randwrite: 181K/181k IOPS Signed-off-by: Asias He Signed-off-by: Nicholas Bellinger --- drivers/vhost/tcm_vhost.c | 46 +++++++++++++++++++++++++++++----------------- drivers/vhost/tcm_vhost.h | 2 ++ 2 files changed, 31 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c index 81ecda54b923..9951297b2427 100644 --- a/drivers/vhost/tcm_vhost.c +++ b/drivers/vhost/tcm_vhost.c @@ -48,6 +48,7 @@ #include /* TODO vhost.h currently depends on this */ #include #include +#include #include "vhost.c" #include "vhost.h" @@ -59,7 +60,8 @@ enum { VHOST_SCSI_VQ_IO = 2, }; -#define VHOST_SCSI_MAX_TARGET 256 +#define VHOST_SCSI_MAX_TARGET 256 +#define VHOST_SCSI_MAX_VQ 128 struct vhost_scsi { /* Protected by vhost_scsi->dev.mutex */ @@ -68,7 +70,7 @@ struct vhost_scsi { bool vs_endpoint; struct vhost_dev dev; - struct vhost_virtqueue vqs[3]; + struct vhost_virtqueue vqs[VHOST_SCSI_MAX_VQ]; struct vhost_work vs_completion_work; /* cmd completion work item */ struct llist_head vs_completion_list; /* cmd completion queue */ @@ -366,12 +368,14 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) { struct vhost_scsi *vs = container_of(work, struct vhost_scsi, vs_completion_work); + DECLARE_BITMAP(signal, VHOST_SCSI_MAX_VQ); struct virtio_scsi_cmd_resp v_rsp; struct tcm_vhost_cmd *tv_cmd; struct llist_node *llnode; struct se_cmd *se_cmd; - int ret; + int ret, vq; + bitmap_zero(signal, VHOST_SCSI_MAX_VQ); llnode = llist_del_all(&vs->vs_completion_list); while (llnode) { tv_cmd = llist_entry(llnode, struct tcm_vhost_cmd, @@ -390,15 +394,20 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) memcpy(v_rsp.sense, tv_cmd->tvc_sense_buf, v_rsp.sense_len); ret = copy_to_user(tv_cmd->tvc_resp, &v_rsp, sizeof(v_rsp)); - if (likely(ret == 0)) - vhost_add_used(&vs->vqs[2], tv_cmd->tvc_vq_desc, 0); - else + if (likely(ret == 0)) { + vhost_add_used(tv_cmd->tvc_vq, tv_cmd->tvc_vq_desc, 0); + vq = tv_cmd->tvc_vq - vs->vqs; + __set_bit(vq, signal); + } else pr_err("Faulted on virtio_scsi_cmd_resp\n"); vhost_scsi_free_cmd(tv_cmd); } - vhost_signal(&vs->dev, &vs->vqs[2]); + vq = -1; + while ((vq = find_next_bit(signal, VHOST_SCSI_MAX_VQ, vq + 1)) + < VHOST_SCSI_MAX_VQ) + vhost_signal(&vs->dev, &vs->vqs[vq]); } static struct tcm_vhost_cmd *vhost_scsi_allocate_cmd( @@ -561,9 +570,9 @@ static void tcm_vhost_submission_work(struct work_struct *work) } } -static void vhost_scsi_handle_vq(struct vhost_scsi *vs) +static void vhost_scsi_handle_vq(struct vhost_scsi *vs, + struct vhost_virtqueue *vq) { - struct vhost_virtqueue *vq = &vs->vqs[2]; struct virtio_scsi_cmd_req v_req; struct tcm_vhost_tpg *tv_tpg; struct tcm_vhost_cmd *tv_cmd; @@ -656,7 +665,7 @@ static void vhost_scsi_handle_vq(struct vhost_scsi *vs) ret = __copy_to_user(resp, &rsp, sizeof(rsp)); if (!ret) vhost_add_used_and_signal(&vs->dev, - &vs->vqs[2], head, 0); + vq, head, 0); else pr_err("Faulted on virtio_scsi_cmd_resp\n"); @@ -678,6 +687,7 @@ static void vhost_scsi_handle_vq(struct vhost_scsi *vs) ": %d\n", tv_cmd, exp_data_len, data_direction); tv_cmd->tvc_vhost = vs; + tv_cmd->tvc_vq = vq; if (unlikely(vq->iov[out].iov_len != sizeof(struct virtio_scsi_cmd_resp))) { @@ -758,7 +768,7 @@ static void vhost_scsi_handle_kick(struct vhost_work *work) poll.work); struct vhost_scsi *vs = container_of(vq->dev, struct vhost_scsi, dev); - vhost_scsi_handle_vq(vs); + vhost_scsi_handle_vq(vs, vq); } /* @@ -879,7 +889,7 @@ err: static int vhost_scsi_open(struct inode *inode, struct file *f) { struct vhost_scsi *s; - int r; + int r, i; s = kzalloc(sizeof(*s), GFP_KERNEL); if (!s) @@ -889,8 +899,9 @@ static int vhost_scsi_open(struct inode *inode, struct file *f) s->vqs[VHOST_SCSI_VQ_CTL].handle_kick = vhost_scsi_ctl_handle_kick; s->vqs[VHOST_SCSI_VQ_EVT].handle_kick = vhost_scsi_evt_handle_kick; - s->vqs[VHOST_SCSI_VQ_IO].handle_kick = vhost_scsi_handle_kick; - r = vhost_dev_init(&s->dev, s->vqs, 3); + for (i = VHOST_SCSI_VQ_IO; i < VHOST_SCSI_MAX_VQ; i++) + s->vqs[i].handle_kick = vhost_scsi_handle_kick; + r = vhost_dev_init(&s->dev, s->vqs, VHOST_SCSI_MAX_VQ); if (r < 0) { kfree(s); return r; @@ -922,9 +933,10 @@ static void vhost_scsi_flush_vq(struct vhost_scsi *vs, int index) static void vhost_scsi_flush(struct vhost_scsi *vs) { - vhost_scsi_flush_vq(vs, VHOST_SCSI_VQ_CTL); - vhost_scsi_flush_vq(vs, VHOST_SCSI_VQ_EVT); - vhost_scsi_flush_vq(vs, VHOST_SCSI_VQ_IO); + int i; + + for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) + vhost_scsi_flush_vq(vs, i); } static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features) diff --git a/drivers/vhost/tcm_vhost.h b/drivers/vhost/tcm_vhost.h index 519a5504d347..1d2ae7a60e11 100644 --- a/drivers/vhost/tcm_vhost.h +++ b/drivers/vhost/tcm_vhost.h @@ -23,6 +23,8 @@ struct tcm_vhost_cmd { struct virtio_scsi_cmd_resp __user *tvc_resp; /* Pointer to vhost_scsi for our device */ struct vhost_scsi *tvc_vhost; + /* Pointer to vhost_virtqueue for the cmd */ + struct vhost_virtqueue *tvc_vq; /* Pointer to vhost nexus memory */ struct tcm_vhost_nexus *tvc_nexus; /* The TCM I/O descriptor that is accessed via container_of() */ -- cgit v1.2.3 From 535ec049e82e391dbe8be55ecd9f392acd71d0d7 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 11 Feb 2013 14:38:05 -0300 Subject: [media] staging: media: Remove unnecessary OOM messages alloc failures already get standardized OOM messages and a dump_stack. Signed-off-by: Joe Perches Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/as102/as102_usb_drv.c | 4 +--- drivers/staging/media/go7007/go7007-fw.c | 24 +++++++----------------- drivers/staging/media/go7007/s2250-loader.c | 5 ++--- drivers/staging/media/lirc/lirc_imon.c | 3 --- drivers/staging/media/lirc/lirc_sasem.c | 6 ------ 5 files changed, 10 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/as102/as102_usb_drv.c b/drivers/staging/media/as102/as102_usb_drv.c index aaf1bc2ad1b2..9f275f020150 100644 --- a/drivers/staging/media/as102/as102_usb_drv.c +++ b/drivers/staging/media/as102/as102_usb_drv.c @@ -374,10 +374,8 @@ static int as102_usb_probe(struct usb_interface *intf, } as102_dev = kzalloc(sizeof(struct as102_dev_t), GFP_KERNEL); - if (as102_dev == NULL) { - dev_err(&intf->dev, "%s: kzalloc failed\n", __func__); + if (as102_dev == NULL) return -ENOMEM; - } /* Assign the user-friendly device name */ for (i = 0; i < ARRAY_SIZE(as102_usb_id_table); i++) { diff --git a/drivers/staging/media/go7007/go7007-fw.c b/drivers/staging/media/go7007/go7007-fw.c index f99c05b454b0..a5ede1c109d0 100644 --- a/drivers/staging/media/go7007/go7007-fw.c +++ b/drivers/staging/media/go7007/go7007-fw.c @@ -381,11 +381,8 @@ static int gen_mjpeghdr_to_package(struct go7007 *go, __le16 *code, int space) int size = 0, i, off = 0, chunk; buf = kzalloc(4096, GFP_KERNEL); - if (buf == NULL) { - dev_err(go->dev, - "unable to allocate 4096 bytes for firmware construction\n"); + if (buf == NULL) return -1; - } for (i = 1; i < 32; ++i) { mjpeg_frame_header(go, buf + size, i); @@ -651,11 +648,9 @@ static int gen_mpeg1hdr_to_package(struct go7007 *go, int i, off = 0, chunk; buf = kzalloc(5120, GFP_KERNEL); - if (buf == NULL) { - dev_err(go->dev, - "unable to allocate 5120 bytes for firmware construction\n"); + if (buf == NULL) return -1; - } + framelen[0] = mpeg1_frame_header(go, buf, 0, 1, PFRAME); if (go->interlace_coding) framelen[0] += mpeg1_frame_header(go, buf + framelen[0] / 8, @@ -838,11 +833,9 @@ static int gen_mpeg4hdr_to_package(struct go7007 *go, int i, off = 0, chunk; buf = kzalloc(5120, GFP_KERNEL); - if (buf == NULL) { - dev_err(go->dev, - "unable to allocate 5120 bytes for firmware construction\n"); + if (buf == NULL) return -1; - } + framelen[0] = mpeg4_frame_header(go, buf, 0, PFRAME); i = 368; framelen[1] = mpeg4_frame_header(go, buf + i, 0, BFRAME_PRE); @@ -1582,12 +1575,9 @@ int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen) return -1; } code = kzalloc(codespace * 2, GFP_KERNEL); - if (code == NULL) { - dev_err(go->dev, - "unable to allocate %d bytes for firmware construction\n", - codespace * 2); + if (code == NULL) goto fw_failed; - } + src = (__le16 *)fw_entry->data; srclen = fw_entry->size / 2; while (srclen >= 2) { diff --git a/drivers/staging/media/go7007/s2250-loader.c b/drivers/staging/media/go7007/s2250-loader.c index f57eb3beb341..72e5175fe7e3 100644 --- a/drivers/staging/media/go7007/s2250-loader.c +++ b/drivers/staging/media/go7007/s2250-loader.c @@ -81,10 +81,9 @@ static int s2250loader_probe(struct usb_interface *interface, /* Allocate dev data structure */ s = kmalloc(sizeof(device_extension_t), GFP_KERNEL); - if (s == NULL) { - dev_err(&interface->dev, "Out of memory\n"); + if (s == NULL) goto failed; - } + s2250_dev_table[minor] = s; dev_info(&interface->dev, diff --git a/drivers/staging/media/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c index 343c622f9387..0a2c45dd4475 100644 --- a/drivers/staging/media/lirc/lirc_imon.c +++ b/drivers/staging/media/lirc/lirc_imon.c @@ -744,7 +744,6 @@ static int imon_probe(struct usb_interface *interface, context = kzalloc(sizeof(struct imon_context), GFP_KERNEL); if (!context) { - dev_err(dev, "%s: kzalloc failed for context\n", __func__); alloc_status = 1; goto alloc_status_switch; } @@ -826,13 +825,11 @@ static int imon_probe(struct usb_interface *interface, driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); if (!driver) { - dev_err(dev, "%s: kzalloc failed for lirc_driver\n", __func__); alloc_status = 2; goto alloc_status_switch; } rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); if (!rbuf) { - dev_err(dev, "%s: kmalloc failed for lirc_buffer\n", __func__); alloc_status = 3; goto alloc_status_switch; } diff --git a/drivers/staging/media/lirc/lirc_sasem.c b/drivers/staging/media/lirc/lirc_sasem.c index b3fe21e7a90d..68acca74ddb1 100644 --- a/drivers/staging/media/lirc/lirc_sasem.c +++ b/drivers/staging/media/lirc/lirc_sasem.c @@ -759,22 +759,16 @@ static int sasem_probe(struct usb_interface *interface, context = kzalloc(sizeof(struct sasem_context), GFP_KERNEL); if (!context) { - dev_err(&interface->dev, - "%s: kzalloc failed for context\n", __func__); alloc_status = 1; goto alloc_status_switch; } driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); if (!driver) { - dev_err(&interface->dev, - "%s: kzalloc failed for lirc_driver\n", __func__); alloc_status = 2; goto alloc_status_switch; } rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); if (!rbuf) { - dev_err(&interface->dev, - "%s: kmalloc failed for lirc_buffer\n", __func__); alloc_status = 3; goto alloc_status_switch; } -- cgit v1.2.3 From b940a2219c9d59171339cc4510462154934fcb49 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 12 Feb 2013 08:22:08 -0300 Subject: [media] mceusb: move check earlier to make smatch happy Smatch complains that "cmdbuf[cmdcount - length]" might go past the end of the array. It's an easy warning to silence by moving the limit check earlier. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/mceusb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index bdd1ed8e406e..5b5b6e6f79e8 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -828,16 +828,16 @@ static int mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count) (txbuf[i] -= MCE_MAX_PULSE_LENGTH)); } - /* Fix packet length in last header */ - length = cmdcount % MCE_CODE_LENGTH; - cmdbuf[cmdcount - length] -= MCE_CODE_LENGTH - length; - /* Check if we have room for the empty packet at the end */ if (cmdcount >= MCE_CMDBUF_SIZE) { ret = -EINVAL; goto out; } + /* Fix packet length in last header */ + length = cmdcount % MCE_CODE_LENGTH; + cmdbuf[cmdcount - length] -= MCE_CODE_LENGTH - length; + /* All mce commands end with an empty packet (0x80) */ cmdbuf[cmdcount++] = MCE_IRDATA_TRAILER; -- cgit v1.2.3 From 2fd7f398d32a0d490d28de75b613263502918e51 Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Fri, 8 Feb 2013 18:47:30 -0300 Subject: [media] media: rc: gpio-ir-recv: add support for device tree parsing This patch adds device tree parsing for gpio_ir_recv platform_data and the mandatory binding documentation. It basically follows what we already have for e.g. gpio_keys. All required device tree properties are OS independent but an optional property allows linux specific support for rc maps. Signed-off-by: Sebastian Hesselbarth Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/gpio-ir-receiver.txt | 16 +++++++ drivers/media/rc/gpio-ir-recv.c | 52 ++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/gpio-ir-receiver.txt (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/media/gpio-ir-receiver.txt b/Documentation/devicetree/bindings/media/gpio-ir-receiver.txt new file mode 100644 index 000000000000..56e726ef4bf2 --- /dev/null +++ b/Documentation/devicetree/bindings/media/gpio-ir-receiver.txt @@ -0,0 +1,16 @@ +Device-Tree bindings for GPIO IR receiver + +Required properties: + - compatible: should be "gpio-ir-receiver". + - gpios: specifies GPIO used for IR signal reception. + +Optional properties: + - linux,rc-map-name: Linux specific remote control map name. + +Example node: + + ir: ir-receiver { + compatible = "gpio-ir-receiver"; + gpios = <&gpio0 19 1>; + linux,rc-map-name = "rc-rc6-mce"; + }; diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c index 382f362b69af..8b82ae9bd686 100644 --- a/drivers/media/rc/gpio-ir-recv.c +++ b/drivers/media/rc/gpio-ir-recv.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,45 @@ struct gpio_rc_dev { bool active_low; }; +#ifdef CONFIG_OF +/* + * Translate OpenFirmware node properties into platform_data + */ +static int gpio_ir_recv_get_devtree_pdata(struct device *dev, + struct gpio_ir_recv_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + enum of_gpio_flags flags; + int gpio; + + gpio = of_get_gpio_flags(np, 0, &flags); + if (gpio < 0) { + if (gpio != -EPROBE_DEFER) + dev_err(dev, "Failed to get gpio flags (%d)\n", gpio); + return gpio; + } + + pdata->gpio_nr = gpio; + pdata->active_low = (flags & OF_GPIO_ACTIVE_LOW); + /* probe() takes care of map_name == NULL or allowed_protos == 0 */ + pdata->map_name = of_get_property(np, "linux,rc-map-name", NULL); + pdata->allowed_protos = 0; + + return 0; +} + +static struct of_device_id gpio_ir_recv_of_match[] = { + { .compatible = "gpio-ir-receiver", }, + { }, +}; +MODULE_DEVICE_TABLE(of, gpio_ir_recv_of_match); + +#else /* !CONFIG_OF */ + +#define gpio_ir_recv_get_devtree_pdata(dev, pdata) (-ENOSYS) + +#endif + static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id) { struct gpio_rc_dev *gpio_dev = dev_id; @@ -66,6 +106,17 @@ static int gpio_ir_recv_probe(struct platform_device *pdev) pdev->dev.platform_data; int rc; + if (pdev->dev.of_node) { + struct gpio_ir_recv_platform_data *dtpdata = + devm_kzalloc(&pdev->dev, sizeof(*dtpdata), GFP_KERNEL); + if (!dtpdata) + return -ENOMEM; + rc = gpio_ir_recv_get_devtree_pdata(&pdev->dev, dtpdata); + if (rc) + return rc; + pdata = dtpdata; + } + if (!pdata) return -EINVAL; @@ -191,6 +242,7 @@ static struct platform_driver gpio_ir_recv_driver = { .driver = { .name = GPIO_IR_DRIVER_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(gpio_ir_recv_of_match), #ifdef CONFIG_PM .pm = &gpio_ir_recv_pm_ops, #endif -- cgit v1.2.3 From 676fa7d4c9fd141a31cba2870e592a597c0bb07f Mon Sep 17 00:00:00 2001 From: Roland Scheidegger Date: Fri, 8 Feb 2013 21:08:55 -0300 Subject: [media] em28xx: add usb id for terratec h5 rev. 3 Seems to work just the same as older revisions. Signed-off-by: Roland Scheidegger Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 0a5aa6223adf..54a03b20de6e 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2080,6 +2080,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2884_BOARD_TERRATEC_H5 }, { USB_DEVICE(0x0ccd, 0x10ad), /* H5 Rev. 2 */ .driver_info = EM2884_BOARD_TERRATEC_H5 }, + { USB_DEVICE(0x0ccd, 0x10b6), /* H5 Rev. 3 */ + .driver_info = EM2884_BOARD_TERRATEC_H5 }, { USB_DEVICE(0x0ccd, 0x0084), .driver_info = EM2860_BOARD_TERRATEC_AV350 }, { USB_DEVICE(0x0ccd, 0x0096), -- cgit v1.2.3 From bbf344e54ed9a76e344d08feedc70ab2c5a8a64c Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Wed, 6 Feb 2013 14:42:28 +0100 Subject: target_core_rd: break out unterminated loop during copy The loop in rd_execute_rw() will never terminate if the sg element has a zero size. Or it'll spill over into outer space if the sg element is larger than the available space. So we need to add some safety catches here. Cc: Nic Bellinger Signed-off-by: Hannes Reinecke Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_rd.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/target/target_core_rd.c b/drivers/target/target_core_rd.c index b0fff52c990e..e0b3c379aa14 100644 --- a/drivers/target/target_core_rd.c +++ b/drivers/target/target_core_rd.c @@ -316,7 +316,19 @@ rd_execute_rw(struct se_cmd *cmd) void *rd_addr; sg_miter_next(&m); + if (!(u32)m.length) { + pr_debug("RD[%u]: invalid sgl %p len %zu\n", + dev->rd_dev_id, m.addr, m.length); + sg_miter_stop(&m); + return TCM_INCORRECT_AMOUNT_OF_DATA; + } len = min((u32)m.length, src_len); + if (len > rd_size) { + pr_debug("RD[%u]: size underrun page %d offset %d " + "size %d\n", dev->rd_dev_id, + rd_page, rd_offset, rd_size); + len = rd_size; + } m.consumed = len; rd_addr = sg_virt(rd_sg) + rd_offset; -- cgit v1.2.3 From 33633676df0d16d0685f2fbc571143801bc16e3b Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Fri, 8 Feb 2013 15:18:38 -0800 Subject: target: Fix sense data for out-of-bounds IO operations We're supposed to return LOGICAL BLOCK ADDRESS OUT OF RANGE, not INVALID FIELD IN CDB. Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_sbc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index a664c664a31a..170f1f75d2d8 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -486,7 +486,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops) */ if (cmd->t_task_lba || sectors) { if (sbc_check_valid_sectors(cmd) < 0) - return TCM_INVALID_CDB_FIELD; + return TCM_ADDRESS_OUT_OF_RANGE; } cmd->execute_cmd = ops->execute_sync_cache; break; -- cgit v1.2.3 From b9e2afff1e6b36d05a0e12b6114eb0aaf8949c09 Mon Sep 17 00:00:00 2001 From: Alistair Buxton Date: Tue, 12 Feb 2013 21:58:47 -0300 Subject: [media] rtl28xxu: Add USB IDs for Compro VideoMate U620F Signed-off-by: Alistair Buxton Acked-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index e127bd134be0..d98387a3c95e 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1370,6 +1370,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = { &rtl2832u_props, "GIGABYTE U7300", NULL) }, { DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1104, &rtl2832u_props, "Digivox Micro Hd", NULL) }, + { DVB_USB_DEVICE(USB_VID_COMPRO, 0x0620, + &rtl2832u_props, "Compro VideoMate U620F", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); -- cgit v1.2.3 From ed72d37a33fdf43dc47787fe220532cdec9da528 Mon Sep 17 00:00:00 2001 From: Christoph Nuscheler Date: Sat, 9 Feb 2013 15:56:23 -0300 Subject: [media] media: Add 0x3009 USB PID to ttusb2 driver (fixed diff) The "Technisat SkyStar USB plus" is a TT-connect S-2400 clone, which the V4L-DVB drivers already support. However, some of these devices (like mine) come with a different USB PID 0x3009 instead of 0x3006. There have already been patches simply overwriting the USB PID in dvb-usb-ids.h. Of course these patches were rejected because they would have disabled the 0x3006 PID. This new patch adds the 0x3009 PID to dvb-usb-ids.h, and adds references to it within the ttusb2.c driver. PID 0x3006 devices will continue to work. The only difference between the two hardware models seems to be the EEPROM chip. In fact, Windows BDA driver names the 0x3009 device with a "(8 kB EEPROM)" suffix. In spite of that, the 0x3009 device works absolutely flawlessly using the existing ttusb2 driver. Signed-off-by: Christoph Nuscheler Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb-usb-ids.h | 1 + drivers/media/usb/dvb-usb/ttusb2.c | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index 7e1597dad568..399e1042d351 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -242,6 +242,7 @@ #define USB_PID_AVERMEDIA_A867 0xa867 #define USB_PID_AVERMEDIA_TWINSTAR 0x0825 #define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006 +#define USB_PID_TECHNOTREND_CONNECT_S2400_8KEEPROM 0x3009 #define USB_PID_TECHNOTREND_CONNECT_CT3650 0x300d #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY_2 0x0081 diff --git a/drivers/media/usb/dvb-usb/ttusb2.c b/drivers/media/usb/dvb-usb/ttusb2.c index bcdac225ebe1..2ce3d19c58ef 100644 --- a/drivers/media/usb/dvb-usb/ttusb2.c +++ b/drivers/media/usb/dvb-usb/ttusb2.c @@ -620,6 +620,8 @@ static struct usb_device_id ttusb2_table [] = { USB_PID_TECHNOTREND_CONNECT_S2400) }, { USB_DEVICE(USB_VID_TECHNOTREND, USB_PID_TECHNOTREND_CONNECT_CT3650) }, + { USB_DEVICE(USB_VID_TECHNOTREND, + USB_PID_TECHNOTREND_CONNECT_S2400_8KEEPROM) }, {} /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, ttusb2_table); @@ -721,12 +723,16 @@ static struct dvb_usb_device_properties ttusb2_properties_s2400 = { .generic_bulk_ctrl_endpoint = 0x01, - .num_device_descs = 1, + .num_device_descs = 2, .devices = { { "Technotrend TT-connect S-2400", { &ttusb2_table[2], NULL }, { NULL }, }, + { "Technotrend TT-connect S-2400 (8kB EEPROM)", + { &ttusb2_table[4], NULL }, + { NULL }, + }, } }; -- cgit v1.2.3 From bb992e72f9b751fceb04afeb7736b6a3e50effcf Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Fri, 8 Feb 2013 15:18:39 -0800 Subject: target: Fix error checking for UNMAP commands SBC-3 (revision 35) says: The PARAMETER LIST LENGTH field specifies the length in bytes of the UNMAP parameter list that is available to be transferred from the Data-Out Buffer. If the parameter list length is greater than zero and less than 0008h (i.e., eight), then the device server shall terminate the command with CHECK CONDITION status with the sense key set to ILLEGAL REQUEST and the additional sense code set to PARAMETER LIST LENGTH ERROR. A PARAMETER LIST LENGTH set to zero specifies that no data shall be sent. so our sense code for too-short descriptors was wrong, and we were incorrectly failing commands that didn't transfer any descriptors. While we're at it, also handle the UNMAP check: If the ANCHOR bit is set to one, and the ANC_SUP bit in the Logical Block Provisioning VPD page (see 6.6.4) is set to zero, then the device server shall terminate the command with CHECK CONDITION status with the sense key set to ILLEGAL REQUEST and the additional sense code set to INVALID FIELD IN CDB. (chris boot: Fix wrong cut+paste comment in transport_send_check_condition_and_sense) Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_iblock.c | 11 ++++++++++- drivers/target/target_core_transport.c | 10 ++++++++++ include/target/target_core_base.h | 1 + 3 files changed, 21 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 2f74e17ad3e6..facee5f74fa4 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -391,10 +391,19 @@ iblock_execute_unmap(struct se_cmd *cmd) sense_reason_t ret = 0; int dl, bd_dl, err; + /* We never set ANC_SUP */ + if (cmd->t_task_cdb[1]) + return TCM_INVALID_CDB_FIELD; + + if (cmd->data_length == 0) { + target_complete_cmd(cmd, SAM_STAT_GOOD); + return 0; + } + if (cmd->data_length < 8) { pr_warn("UNMAP parameter list length %u too small\n", cmd->data_length); - return TCM_INVALID_PARAMETER_LIST; + return TCM_PARAMETER_LIST_LENGTH_ERROR; } buf = transport_kmap_data_sg(cmd); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 96b64d57ebbb..2030b608136d 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1517,6 +1517,7 @@ void transport_generic_request_failure(struct se_cmd *cmd, case TCM_UNSUPPORTED_SCSI_OPCODE: case TCM_INVALID_CDB_FIELD: case TCM_INVALID_PARAMETER_LIST: + case TCM_PARAMETER_LIST_LENGTH_ERROR: case TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE: case TCM_UNKNOWN_MODE_PAGE: case TCM_WRITE_PROTECTED: @@ -2677,6 +2678,15 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd, /* INVALID FIELD IN PARAMETER LIST */ buffer[SPC_ASC_KEY_OFFSET] = 0x26; break; + case TCM_PARAMETER_LIST_LENGTH_ERROR: + /* CURRENT ERROR */ + buffer[0] = 0x70; + buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; + /* ILLEGAL REQUEST */ + buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; + /* PARAMETER LIST LENGTH ERROR */ + buffer[SPC_ASC_KEY_OFFSET] = 0x1a; + break; case TCM_UNEXPECTED_UNSOLICITED_DATA: /* CURRENT ERROR */ buffer[0] = 0x70; diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index df14dce59191..c4af592f7057 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -195,6 +195,7 @@ enum tcm_sense_reason_table { TCM_RESERVATION_CONFLICT = R(0x10), TCM_ADDRESS_OUT_OF_RANGE = R(0x11), TCM_OUT_OF_RESOURCES = R(0x12), + TCM_PARAMETER_LIST_LENGTH_ERROR = R(0x13), #undef R }; -- cgit v1.2.3 From 71f41fe1fafae2e407ef19d8174207f7ff80b387 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Fri, 8 Feb 2013 15:18:40 -0800 Subject: target: Fix parameter list length checking in MODE SELECT An empty parameter list (length == 0) is not an error, so succeed MODE SELECT in this case. If the parameter list length is too small, return the correct sense code of PARAMETER LIST LENGTH ERROR. Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_spc.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index 803339516fb9..4cb667d720a7 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -1000,6 +1000,14 @@ static sense_reason_t spc_emulate_modeselect(struct se_cmd *cmd) int ret = 0; int i; + if (!cmd->data_length) { + target_complete_cmd(cmd, GOOD); + return 0; + } + + if (cmd->data_length < off + 2) + return TCM_PARAMETER_LIST_LENGTH_ERROR; + buf = transport_kmap_data_sg(cmd); if (!buf) return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; @@ -1024,6 +1032,11 @@ static sense_reason_t spc_emulate_modeselect(struct se_cmd *cmd) goto out; check_contents: + if (cmd->data_length < off + length) { + ret = TCM_PARAMETER_LIST_LENGTH_ERROR; + goto out; + } + if (memcmp(buf + off, tbuf, length)) ret = TCM_INVALID_PARAMETER_LIST; -- cgit v1.2.3 From 6aed8ec3f76a22217c9ae183d32b1aa990bed069 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 17:32:21 +0100 Subject: drm: review locking for drm_fb_helper_restore_fbdev_mode ... it's required. Fix up exynos and the cma helper, and add a corresponding WARN_ON to drm_fb_helper_restore_fbdev_mode. Note that tegra calls the fbdev cma helper restore function also from it's driver-load callback. Which is a bit against current practice, since usually the call is only from ->lastclose, and initial setup is done by drm_fb_helper_initial_config. Also add the relevant drm DocBook entry. v2: Add promised WARN to restore_fbdev_mode. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 16 +++++++++++++++- drivers/gpu/drm/drm_fb_cma_helper.c | 2 ++ drivers/gpu/drm/drm_fb_helper.c | 11 +++++++++++ drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 2 ++ include/drm/drm_crtc.h | 1 + 5 files changed, 31 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 9c797f6fea75..f17077307c65 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -68,9 +68,23 @@ void drm_modeset_unlock_all(struct drm_device *dev) mutex_unlock(&dev->mode_config.mutex); } - EXPORT_SYMBOL(drm_modeset_unlock_all); +/** + * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked + * @dev: device + */ +void drm_warn_on_modeset_not_all_locked(struct drm_device *dev) +{ + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + WARN_ON(!mutex_is_locked(&crtc->mutex)); + + WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); +} +EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); + /* Avoid boilerplate. I'm tired of typing. */ #define DRM_ENUM_NAME_FN(fnname, list) \ char *fnname(int val) \ diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 3742bc96421e..1b6ba2d4d60c 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -389,8 +389,10 @@ EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini); */ void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma) { + drm_modeset_lock_all(dev); if (fbdev_cma) drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper); + drm_modeset_unlock_all(dev); } EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 0c6e25e979dd..77378a1e907e 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -239,10 +239,21 @@ int drm_fb_helper_debug_leave(struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_debug_leave); +/** + * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration + * @fb_helper: fbcon to restore + * + * This should be called from driver's drm->lastclose callback when implementing + * an fbcon on top of kms using this helper. This ensures that the user isn't + * greeted with a black screen when e.g. X dies. + */ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) { bool error = false; int i, ret; + + drm_warn_on_modeset_not_all_locked(fb_helper->dev); + for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; ret = drm_mode_set_config_internal(mode_set); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 90d335cfb8c0..086d0f79785f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -376,5 +376,7 @@ void exynos_drm_fbdev_restore_mode(struct drm_device *dev) if (!private || !private->fb_helper) return; + drm_modeset_lock_all(dev); drm_fb_helper_restore_fbdev_mode(private->fb_helper); + drm_modeset_unlock_all(dev); } diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index e90c8dcc028d..8b7762728639 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -867,6 +867,7 @@ struct drm_prop_enum_list { extern void drm_modeset_lock_all(struct drm_device *dev); extern void drm_modeset_unlock_all(struct drm_device *dev); +extern void drm_warn_on_modeset_not_all_locked(struct drm_device *dev); extern int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, -- cgit v1.2.3 From d21bf469d5301d025cd82997bb1529bcdc7086af Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 18:09:52 +0100 Subject: drm/fb-helper: kill drm_fb_helper_restore It's only used internally for the sysrq and panic handlers provided by the drm fb helper implementation. Hence just inline it, kill the export and remove the confusing kerneldoc. Driver's are supposed to call drm_fb_helper_restore_fbdev_mode on lastclose. Note that locking is totally fubar - the sysrq case doesn't take any locks at all. The panic handler probably shouldn't take any locks since it'll only make things worse. Otoh it's probably better to switch things over to the atomic modeset callbacks (and disable the panic handler for those drivers which don't implement it). But that's both better done in separate patches. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 23 ++++++++--------------- include/drm/drm_fb_helper.h | 1 - 2 files changed, 8 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 77378a1e907e..14a81fc3e01a 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -264,6 +264,10 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) } EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode); +/* + * restore fbcon display for all kms driver's using this helper, used for sysrq + * and panic handling. + */ static bool drm_fb_helper_force_kernel_mode(void) { bool ret, error = false; @@ -302,20 +306,6 @@ static struct notifier_block paniced = { .notifier_call = drm_fb_helper_panic, }; -/** - * drm_fb_helper_restore - restore the framebuffer console (kernel) config - * - * Restore's the kernel's fbcon mode, used for lastclose & panic paths. - */ -void drm_fb_helper_restore(void) -{ - bool ret; - ret = drm_fb_helper_force_kernel_mode(); - if (ret == true) - DRM_ERROR("Failed to restore crtc configuration\n"); -} -EXPORT_SYMBOL(drm_fb_helper_restore); - static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) { struct drm_device *dev = fb_helper->dev; @@ -337,7 +327,10 @@ static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) #ifdef CONFIG_MAGIC_SYSRQ static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) { - drm_fb_helper_restore(); + bool ret; + ret = drm_fb_helper_force_kernel_mode(); + if (ret == true) + DRM_ERROR("Failed to restore crtc configuration\n"); } static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn); diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 5120b01c2eeb..ba32505efb02 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -103,7 +103,6 @@ int drm_fb_helper_setcolreg(unsigned regno, struct fb_info *info); bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper); -void drm_fb_helper_restore(void); void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, uint32_t fb_width, uint32_t fb_height); void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, -- cgit v1.2.3 From 43c8a849a1e92fba6ea4493ed950d2a2e31ac87c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 18:18:07 +0100 Subject: drm/fb-helper: unexport drm_fb_helper_panic It doesn't even show up in any header files and only used iternally. Originally it was (ab)used to restore the fbcon on lastclose, but that died with commit e8e7a2b8ccfdae0d4cb6bd25824bbedcd42da316 Author: Dave Airlie Date: Thu Apr 21 22:18:32 2011 +0100 drm/i915: restore only the mode of this driver on lastclose (v2) Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 14a81fc3e01a..eaab092b995d 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -287,7 +287,7 @@ static bool drm_fb_helper_force_kernel_mode(void) return error; } -int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed, +static int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed, void *panic_str) { /* @@ -300,7 +300,6 @@ int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed, pr_err("panic occurred, switching back to text console\n"); return drm_fb_helper_force_kernel_mode(); } -EXPORT_SYMBOL(drm_fb_helper_panic); static struct notifier_block paniced = { .notifier_call = drm_fb_helper_panic, -- cgit v1.2.3 From de1ace5b56d11db26abe69fd19754f91c326763f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 21:50:49 +0100 Subject: drm/fb-helper: unexport drm_fb_helper_single_fb_probe Not called by anyone, and really, shouldn't be. Drivers are supposed either drm_fb_helper_initial_config or drm_fb_helper_hotplug_event. Originally this was done differently, but is now consolidated in the helper functions and no longer done by drivers directly. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 5 ++--- include/drm/drm_fb_helper.h | 3 --- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index eaab092b995d..f5d362680f2b 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -754,8 +754,8 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, } EXPORT_SYMBOL(drm_fb_helper_pan_display); -int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, - int preferred_bpp) +static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, + int preferred_bpp) { int new_fb = 0; int crtc_count = 0; @@ -870,7 +870,6 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, return 0; } -EXPORT_SYMBOL(drm_fb_helper_single_fb_probe); void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, uint32_t depth) diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index ba32505efb02..973402d6effe 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -82,9 +82,6 @@ struct drm_fb_helper { bool delayed_hotplug; }; -int drm_fb_helper_single_fb_probe(struct drm_fb_helper *helper, - int preferred_bpp); - int drm_fb_helper_init(struct drm_device *dev, struct drm_fb_helper *helper, int crtc_count, int max_conn); -- cgit v1.2.3 From 203cb50143029b2bc95736ce10f7defcf59aca44 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 21:56:20 +0100 Subject: drm/tegra: don't set up initial fbcon config twice drm_fbdev_cma_init does the inital fbcon setup by calling down into drm_fb_helper_initial_config, so no need at all to restore the just set up configuration right away ... Signed-off-by: Daniel Vetter --- drivers/gpu/drm/tegra/fb.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 97993c6835fd..03914953cb1c 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -39,10 +39,6 @@ int tegra_drm_fb_init(struct drm_device *drm) if (IS_ERR(fbdev)) return PTR_ERR(fbdev); -#ifndef CONFIG_FRAMEBUFFER_CONSOLE - drm_fbdev_cma_restore_mode(fbdev); -#endif - host1x->fbdev = fbdev; return 0; -- cgit v1.2.3 From 76a39dbfb2d1bc45219839e5a95d4ceaf6ca114f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 23:12:54 +0100 Subject: drm/fb-helper: don't disable everything in initial_config This should be done in the drivers for two reasons: - it gets in the way of fastboot efforts - it links the fb helpers with the crtc helpers instead of going through the real interface vfuncs, forcing i915 to fake all the ->disable callbacks used by the crtc helper to avoid ugly Oopsen v2: Resolve conflicts since drivers still call drm_fb_helper_single_add_all_connectors. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/ast/ast_fb.c | 5 +++++ drivers/gpu/drm/cirrus/cirrus_fbdev.c | 4 ++++ drivers/gpu/drm/drm_fb_cma_helper.c | 3 +++ drivers/gpu/drm/drm_fb_helper.c | 3 --- drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 3 +++ drivers/gpu/drm/gma500/framebuffer.c | 4 ++++ drivers/gpu/drm/i915/intel_fb.c | 3 +++ drivers/gpu/drm/mgag200/mgag200_fb.c | 5 +++++ drivers/gpu/drm/nouveau/nouveau_fbcon.c | 3 +++ drivers/gpu/drm/radeon/radeon_fb.c | 4 ++++ drivers/gpu/drm/udl/udl_fb.c | 4 ++++ drivers/staging/omapdrm/omap_fbdev.c | 4 ++++ 12 files changed, 42 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index 3e6584b940dc..81763cad9940 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "ast_drv.h" static void ast_dirty_update(struct ast_fbdev *afbdev, @@ -314,6 +315,10 @@ int ast_fbdev_init(struct drm_device *dev) } drm_fb_helper_single_add_all_connectors(&afbdev->helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(&afbdev->helper, 32); return 0; } diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index 3daea0f638c3..b96605c6e1b6 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -291,6 +292,9 @@ int cirrus_fbdev_init(struct cirrus_device *cdev) return ret; } drm_fb_helper_single_add_all_connectors(&gfbdev->helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(cdev->dev); drm_fb_helper_initial_config(&gfbdev->helper, bpp_sel); return 0; diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 1b6ba2d4d60c..ef3d33a8a7e2 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -333,6 +333,9 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, } + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + ret = drm_fb_helper_initial_config(helper, preferred_bpp); if (ret < 0) { dev_err(dev->dev, "Failed to set inital hw configuration.\n"); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index f5d362680f2b..d841b68aaa3e 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1360,9 +1360,6 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) struct drm_device *dev = fb_helper->dev; int count = 0; - /* disable all the possible outputs/crtcs before entering KMS mode */ - drm_helper_disable_unused_functions(fb_helper->dev); - drm_fb_helper_parse_command_line(fb_helper); count = drm_fb_helper_probe_connector_modes(fb_helper, diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 086d0f79785f..fe2a0f068af7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -295,6 +295,9 @@ int exynos_drm_fbdev_init(struct drm_device *dev) } + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP); if (ret < 0) { DRM_ERROR("failed to set up hw configuration.\n"); diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index c1ef37e2efdf..fee3bf85af4a 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -616,6 +616,10 @@ int psb_fbdev_init(struct drm_device *dev) INTELFB_CONN_LIMIT); drm_fb_helper_single_add_all_connectors(&fbdev->psb_fb_helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(&fbdev->psb_fb_helper, 32); return 0; } diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 1c510da04d16..e67061249934 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -258,6 +258,9 @@ void intel_fbdev_initial_config(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + /* Due to peculiar init order wrt to hpd handling this is separate. */ drm_fb_helper_initial_config(&dev_priv->fbdev->helper, 32); } diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index 5c69b432f99a..5bded5b74eaf 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -278,6 +279,10 @@ int mgag200_fbdev_init(struct mga_device *mdev) return ret; } drm_fb_helper_single_add_all_connectors(&mfbdev->helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(mdev->dev); + drm_fb_helper_initial_config(&mfbdev->helper, 32); return 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index d4ecb4deb484..b1ebfe30f912 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -491,6 +491,9 @@ nouveau_fbcon_init(struct drm_device *dev) else preferred_bpp = 32; + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(&fbcon->helper, preferred_bpp); return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index 515e5ee1f9ee..b48d1c8cf9eb 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -379,6 +379,10 @@ int radeon_fbdev_init(struct radeon_device *rdev) } drm_fb_helper_single_add_all_connectors(&rfbdev->helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(rdev->ddev); + drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel); return 0; } diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index b9feec9d08d3..cf5d05a0d955 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -619,6 +619,10 @@ int udl_fbdev_init(struct drm_device *dev) } drm_fb_helper_single_add_all_connectors(&ufbdev->helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(&ufbdev->helper, bpp_sel); return 0; } diff --git a/drivers/staging/omapdrm/omap_fbdev.c b/drivers/staging/omapdrm/omap_fbdev.c index 2728e37e02be..7e66eb138315 100644 --- a/drivers/staging/omapdrm/omap_fbdev.c +++ b/drivers/staging/omapdrm/omap_fbdev.c @@ -369,6 +369,10 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev) } drm_fb_helper_single_add_all_connectors(helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(helper, 32); priv->fbdev = helper; -- cgit v1.2.3 From af5676f1f91585cabe811b8f697e32015e2be826 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 23:28:14 +0100 Subject: drm/i915: rip out helper->disable noop functions Now that the driver is in control of whether it needs to disable everything at take-over or not, we can rip this all out. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_crt.c | 1 - drivers/gpu/drm/i915/intel_ddi.c | 1 - drivers/gpu/drm/i915/intel_display.c | 9 --------- drivers/gpu/drm/i915/intel_dp.c | 1 - drivers/gpu/drm/i915/intel_drv.h | 1 - drivers/gpu/drm/i915/intel_dvo.c | 1 - drivers/gpu/drm/i915/intel_fb.c | 3 --- drivers/gpu/drm/i915/intel_hdmi.c | 1 - drivers/gpu/drm/i915/intel_lvds.c | 1 - drivers/gpu/drm/i915/intel_sdvo.c | 1 - drivers/gpu/drm/i915/intel_tv.c | 1 - 11 files changed, 21 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 68e79f32e100..729dd1a3fe72 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -685,7 +685,6 @@ static void intel_crt_reset(struct drm_connector *connector) static const struct drm_encoder_helper_funcs crt_encoder_funcs = { .mode_fixup = intel_crt_mode_fixup, .mode_set = intel_crt_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_crt_connector_funcs = { diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index cedf4ab5ff16..a259e09eb6a8 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1481,7 +1481,6 @@ static const struct drm_encoder_funcs intel_ddi_funcs = { static const struct drm_encoder_helper_funcs intel_ddi_helper_funcs = { .mode_fixup = intel_ddi_mode_fixup, .mode_set = intel_ddi_mode_set, - .disable = intel_encoder_noop, }; void intel_ddi_init(struct drm_device *dev, enum port port) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 0dfecaf599ff..24f2654338d2 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3718,10 +3718,6 @@ void intel_crtc_update_dpms(struct drm_crtc *crtc) intel_crtc_update_sarea(crtc, enable); } -static void intel_crtc_noop(struct drm_crtc *crtc) -{ -} - static void intel_crtc_disable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -3770,10 +3766,6 @@ void intel_modeset_disable(struct drm_device *dev) } } -void intel_encoder_noop(struct drm_encoder *encoder) -{ -} - void intel_encoder_destroy(struct drm_encoder *encoder) { struct intel_encoder *intel_encoder = to_intel_encoder(encoder); @@ -7277,7 +7269,6 @@ free_work: static struct drm_crtc_helper_funcs intel_helper_funcs = { .mode_set_base_atomic = intel_pipe_set_base_atomic, .load_lut = intel_crtc_load_lut, - .disable = intel_crtc_noop, }; bool intel_encoder_check_is_cloned(struct intel_encoder *encoder) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 56e408b2e3c8..13c1536a8bb2 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2561,7 +2561,6 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder) static const struct drm_encoder_helper_funcs intel_dp_helper_funcs = { .mode_fixup = intel_dp_mode_fixup, .mode_set = intel_dp_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_dp_connector_funcs = { diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 13afb37d8dec..a2ec01c49f40 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -521,7 +521,6 @@ extern void intel_modeset_disable(struct drm_device *dev); extern void intel_crtc_restore_mode(struct drm_crtc *crtc); extern void intel_crtc_load_lut(struct drm_crtc *crtc); extern void intel_crtc_update_dpms(struct drm_crtc *crtc); -extern void intel_encoder_noop(struct drm_encoder *encoder); extern void intel_encoder_destroy(struct drm_encoder *encoder); extern void intel_encoder_dpms(struct intel_encoder *encoder, int mode); extern bool intel_encoder_check_is_cloned(struct intel_encoder *encoder); diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index 15da99533e5b..00e70dbe82da 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -345,7 +345,6 @@ static void intel_dvo_destroy(struct drm_connector *connector) static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = { .mode_fixup = intel_dvo_mode_fixup, .mode_set = intel_dvo_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_dvo_connector_funcs = { diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index e67061249934..1c510da04d16 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -258,9 +258,6 @@ void intel_fbdev_initial_config(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; - /* disable all the possible outputs/crtcs before entering KMS mode */ - drm_helper_disable_unused_functions(dev); - /* Due to peculiar init order wrt to hpd handling this is separate. */ drm_fb_helper_initial_config(&dev_priv->fbdev->helper, 32); } diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 5b4efd64c2f9..3883bed80faa 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -972,7 +972,6 @@ static void intel_hdmi_destroy(struct drm_connector *connector) static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = { .mode_fixup = intel_hdmi_mode_fixup, .mode_set = intel_hdmi_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_hdmi_connector_funcs = { diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 5e3f08e3fd8b..feb43fd7debf 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -656,7 +656,6 @@ static int intel_lvds_set_property(struct drm_connector *connector, static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = { .mode_fixup = intel_lvds_mode_fixup, .mode_set = intel_lvds_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = { diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index f01063a2323a..33b46d9694ea 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -2043,7 +2043,6 @@ done: static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = { .mode_fixup = intel_sdvo_mode_fixup, .mode_set = intel_sdvo_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_sdvo_connector_funcs = { diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 984a113c5d13..d808421c1c80 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1487,7 +1487,6 @@ out: static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = { .mode_fixup = intel_tv_mode_fixup, .mode_set = intel_tv_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_tv_connector_funcs = { -- cgit v1.2.3 From 7e53f3a423146745a4e4bb93362d488dfad502a8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 21 Jan 2013 10:52:17 +0100 Subject: drm/fb-helper: fixup set_config semantics While doing the modeset rework for drm/i915 I've noticed that the fb helper is very liberal with the semantics of the ->set_config interface: - It doesn't bother clearing stale modes (e.g. when unplugging a screen). - It unconditionally sets the fb, even if no mode will be set on a given crtc. - The initial setup is a bit fun since we need to pick crtcs to decide the desired fb size, but also should set the modeset->fb pointer. Explain what's going on in the fixup code after the fb is allocated. The crtc helper didn't really care, but the new i915 modeset infrastructure did, so I've had to add a bunch of special-cases to catch this. Fix this all up and enforce the interface by converting the checks in drm/i915/intel_display.c to BUG_ONs. v2: Fix commit message spell fail spotted by Rob Clark. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 27 +++++++++++++++++++++++---- drivers/gpu/drm/i915/intel_display.c | 11 +++-------- 2 files changed, 26 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index d841b68aaa3e..809ef99f910e 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -689,7 +689,6 @@ int drm_fb_helper_set_par(struct fb_info *info) struct drm_fb_helper *fb_helper = info->par; struct drm_device *dev = fb_helper->dev; struct fb_var_screeninfo *var = &info->var; - struct drm_crtc *crtc; int ret; int i; @@ -700,7 +699,6 @@ int drm_fb_helper_set_par(struct fb_info *info) drm_modeset_lock_all(dev); for (i = 0; i < fb_helper->crtc_count; i++) { - crtc = fb_helper->crtc_info[i].mode_set.crtc; ret = drm_mode_set_config_internal(&fb_helper->crtc_info[i].mode_set); if (ret) { drm_modeset_unlock_all(dev); @@ -841,9 +839,17 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, info = fb_helper->fbdev; - /* set the fb pointer */ + /* + * Set the fb pointer - usually drm_setup_crtcs does this for hotplug + * events, but at init time drm_setup_crtcs needs to be called before + * the fb is allocated (since we need to figure out the desired size of + * the fb before we can allocate it ...). Hence we need to fix things up + * here again. + */ for (i = 0; i < fb_helper->crtc_count; i++) - fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; + if (fb_helper->crtc_info[i].mode_set.num_connectors) + fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; + if (new_fb) { info->var.pixclock = 0; @@ -1314,6 +1320,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) for (i = 0; i < fb_helper->crtc_count; i++) { modeset = &fb_helper->crtc_info[i].mode_set; modeset->num_connectors = 0; + modeset->fb = NULL; } for (i = 0; i < fb_helper->connector_count; i++) { @@ -1330,9 +1337,21 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) modeset->mode = drm_mode_duplicate(dev, fb_crtc->desired_mode); modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector; + modeset->fb = fb_helper->fb; } } + /* Clear out any old modes if there are no more connected outputs. */ + for (i = 0; i < fb_helper->crtc_count; i++) { + modeset = &fb_helper->crtc_info[i].mode_set; + if (modeset->num_connectors == 0) { + BUG_ON(modeset->fb); + BUG_ON(modeset->num_connectors); + if (modeset->mode) + drm_mode_destroy(dev, modeset->mode); + modeset->mode = NULL; + } + } out: kfree(crtcs); kfree(modes); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 24f2654338d2..ca8d5929063e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7978,14 +7978,9 @@ static int intel_crtc_set_config(struct drm_mode_set *set) BUG_ON(!set->crtc); BUG_ON(!set->crtc->helper_private); - if (!set->mode) - set->fb = NULL; - - /* The fb helper likes to play gross jokes with ->mode_set_config. - * Unfortunately the crtc helper doesn't do much at all for this case, - * so we have to cope with this madness until the fb helper is fixed up. */ - if (set->fb && set->num_connectors == 0) - return 0; + /* Enforce sane interface api - has been abused by the fb helper. */ + BUG_ON(!set->mode && set->fb); + BUG_ON(set->fb && set->num_connectors == 0); if (set->fb) { DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n", -- cgit v1.2.3 From 2180c3c7e76536ce8ff0fd957a11dbfaa1e93403 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 21 Jan 2013 23:12:36 +0100 Subject: drm/fb-helper: directly call set_par from the hotplug handler The idea behind calling down into the driver's ->fb_probe function on each hotplug seems to be able to reallocate the backing storage (if e.g. a screen with higher resolution gets added). But that requires quite a bit of work in the fb helper itself, since currently we limit new screens to the currently allocated fb. An no kms driver supports fbdev fb resizing. So don't bother and start to simplify the code by calling drm_fb_helper_set_par directly from the fbdev hotplug function, since that's the only thing left in drm_fb_helper_single_fb_probe which does not concern itself with fb allocation and initial setup. Follow-on patches will streamline the initial setup code. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 809ef99f910e..535c2cadf4d1 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -859,8 +859,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n", info->node, info->fix.id); - } else { - drm_fb_helper_set_par(info); } /* Switch back to kernel console on panic */ @@ -1436,7 +1434,9 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) drm_setup_crtcs(fb_helper); drm_modeset_unlock_all(dev); - return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); + drm_fb_helper_set_par(fb_helper->fbdev); + + return 0; } EXPORT_SYMBOL(drm_fb_helper_hotplug_event); -- cgit v1.2.3 From 8acf658ad6246be7913647e6e76da6a539bff425 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 21 Jan 2013 23:38:37 +0100 Subject: drm/fb-helper: streamline drm_fb_helper_single_fb_probe No need to check whether we've allocated a new fb since we're not always doing that. Also, we always need to register the fbdev and add it to the panic notifier. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 535c2cadf4d1..e0d4b04c0eb5 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -752,10 +752,14 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, } EXPORT_SYMBOL(drm_fb_helper_pan_display); +/* + * Allocates the backing storage through the ->fb_probe callback and then + * registers the fbdev and sets up the panic notifier. + */ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, int preferred_bpp) { - int new_fb = 0; + int ret = 0; int crtc_count = 0; int i; struct fb_info *info; @@ -833,9 +837,9 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, } /* push down into drivers */ - new_fb = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes); - if (new_fb < 0) - return new_fb; + ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes); + if (ret < 0) + return ret; info = fb_helper->fbdev; @@ -851,15 +855,12 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; - if (new_fb) { - info->var.pixclock = 0; - if (register_framebuffer(info) < 0) - return -EINVAL; - - dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n", - info->node, info->fix.id); + info->var.pixclock = 0; + if (register_framebuffer(info) < 0) + return -EINVAL; - } + dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n", + info->node, info->fix.id); /* Switch back to kernel console on panic */ /* multi card linked list maybe */ @@ -869,8 +870,8 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, &paniced); register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); } - if (new_fb) - list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list); + + list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list); return 0; } -- cgit v1.2.3 From cd5428a5447cc6ca77ec6547d6f86834b205eac7 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 21 Jan 2013 23:42:49 +0100 Subject: drm/: simplify ->fb_probe callback The fb helper lost its support for reallocating an fb completely, so no need to return special success values any more. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/ast/ast_fb.c | 21 +++------------------ drivers/gpu/drm/cirrus/cirrus_fbdev.c | 22 +++------------------- drivers/gpu/drm/drm_fb_cma_helper.c | 17 +---------------- drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 30 +----------------------------- drivers/gpu/drm/gma500/framebuffer.c | 10 +--------- drivers/gpu/drm/i915/intel_fb.c | 21 +++------------------ drivers/gpu/drm/mgag200/mgag200_fb.c | 22 +++------------------- drivers/gpu/drm/nouveau/nouveau_fbcon.c | 22 +++------------------- drivers/gpu/drm/radeon/radeon_fb.c | 21 +++------------------ drivers/gpu/drm/udl/udl_fb.c | 22 +++------------------- drivers/staging/omapdrm/omap_fbdev.c | 17 +---------------- 11 files changed, 25 insertions(+), 200 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index 81763cad9940..34931fe7d2c5 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -146,9 +146,10 @@ static int astfb_create_object(struct ast_fbdev *afbdev, return ret; } -static int astfb_create(struct ast_fbdev *afbdev, +static int astfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct ast_fbdev *afbdev = (struct ast_fbdev *)helper; struct drm_device *dev = afbdev->helper.dev; struct drm_mode_fb_cmd2 mode_cmd; struct drm_framebuffer *fb; @@ -249,26 +250,10 @@ static void ast_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, *blue = ast_crtc->lut_b[regno] << 8; } -static int ast_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct ast_fbdev *afbdev = (struct ast_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = astfb_create(afbdev, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - static struct drm_fb_helper_funcs ast_fb_helper_funcs = { .gamma_set = ast_fb_gamma_set, .gamma_get = ast_fb_gamma_get, - .fb_probe = ast_find_or_create_single, + .fb_probe = astfb_create, }; static void ast_fbdev_destroy(struct drm_device *dev, diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index b96605c6e1b6..e25afccaf85b 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -121,9 +121,10 @@ static int cirrusfb_create_object(struct cirrus_fbdev *afbdev, return ret; } -static int cirrusfb_create(struct cirrus_fbdev *gfbdev, +static int cirrusfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct cirrus_fbdev *gfbdev = (struct cirrus_fbdev *)helper; struct drm_device *dev = gfbdev->helper.dev; struct cirrus_device *cdev = gfbdev->helper.dev->dev_private; struct fb_info *info; @@ -220,23 +221,6 @@ out_iounmap: return ret; } -static int cirrus_fb_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size - *sizes) -{ - struct cirrus_fbdev *gfbdev = (struct cirrus_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = cirrusfb_create(gfbdev, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - static int cirrus_fbdev_destroy(struct drm_device *dev, struct cirrus_fbdev *gfbdev) { @@ -268,7 +252,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev, static struct drm_fb_helper_funcs cirrus_fb_helper_funcs = { .gamma_set = cirrus_crtc_fb_gamma_set, .gamma_get = cirrus_crtc_fb_gamma_get, - .fb_probe = cirrus_fb_find_or_create_single, + .fb_probe = cirrusfb_create, }; int cirrus_fbdev_init(struct cirrus_device *cdev) diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index ef3d33a8a7e2..e851658f87d5 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -275,23 +275,8 @@ err_drm_gem_cma_free_object: return ret; } -static int drm_fbdev_cma_probe(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - int ret = 0; - - if (!helper->fb) { - ret = drm_fbdev_cma_create(helper, sizes); - if (ret < 0) - return ret; - ret = 1; - } - - return ret; -} - static struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = { - .fb_probe = drm_fbdev_cma_probe, + .fb_probe = drm_fbdev_cma_create, }; /** diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index fe2a0f068af7..68f0045f86b8 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -226,36 +226,8 @@ out: return ret; } -static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - int ret = 0; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* - * with !helper->fb, it means that this funcion is called first time - * and after that, the helper->fb would be used as clone mode. - */ - if (!helper->fb) { - ret = exynos_drm_fbdev_create(helper, sizes); - if (ret < 0) { - DRM_ERROR("failed to create fbdev.\n"); - return ret; - } - - /* - * fb_helper expects a value more than 1 if succeed - * because register_framebuffer() should be called. - */ - ret = 1; - } - - return ret; -} - static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = { - .fb_probe = exynos_drm_fbdev_probe, + .fb_probe = exynos_drm_fbdev_create, }; int exynos_drm_fbdev_init(struct drm_device *dev) diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index fee3bf85af4a..2590cac84257 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -545,9 +545,7 @@ static int psbfb_probe(struct drm_fb_helper *helper, struct psb_fbdev *psb_fbdev = (struct psb_fbdev *)helper; struct drm_device *dev = psb_fbdev->psb_fb_helper.dev; struct drm_psb_private *dev_priv = dev->dev_private; - int new_fb = 0; int bytespp; - int ret; bytespp = sizes->surface_bpp / 8; if (bytespp == 3) /* no 24bit packed */ @@ -562,13 +560,7 @@ static int psbfb_probe(struct drm_fb_helper *helper, sizes->surface_depth = 16; } - if (!helper->fb) { - ret = psbfb_create(psb_fbdev, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; + return psbfb_create(psb_fbdev, sizes); } static struct drm_fb_helper_funcs psb_fb_helper_funcs = { diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 1c510da04d16..981bdce3634e 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -57,9 +57,10 @@ static struct fb_ops intelfb_ops = { .fb_debug_leave = drm_fb_helper_debug_leave, }; -static int intelfb_create(struct intel_fbdev *ifbdev, +static int intelfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct intel_fbdev *ifbdev = (struct intel_fbdev *)helper; struct drm_device *dev = ifbdev->helper.dev; struct drm_i915_private *dev_priv = dev->dev_private; struct fb_info *info; @@ -181,26 +182,10 @@ out: return ret; } -static int intel_fb_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct intel_fbdev *ifbdev = (struct intel_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = intelfb_create(ifbdev, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - static struct drm_fb_helper_funcs intel_fb_helper_funcs = { .gamma_set = intel_crtc_fb_gamma_set, .gamma_get = intel_crtc_fb_gamma_get, - .fb_probe = intel_fb_find_or_create_single, + .fb_probe = intelfb_create, }; static void intel_fbdev_destroy(struct drm_device *dev, diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index 5bded5b74eaf..d2253f639481 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -121,9 +121,10 @@ static int mgag200fb_create_object(struct mga_fbdev *afbdev, return ret; } -static int mgag200fb_create(struct mga_fbdev *mfbdev, +static int mgag200fb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct mga_fbdev *mfbdev = (struct mga_fbdev *)helper; struct drm_device *dev = mfbdev->helper.dev; struct drm_mode_fb_cmd2 mode_cmd; struct mga_device *mdev = dev->dev_private; @@ -210,23 +211,6 @@ out: return ret; } -static int mga_fb_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size - *sizes) -{ - struct mga_fbdev *mfbdev = (struct mga_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = mgag200fb_create(mfbdev, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - static int mga_fbdev_destroy(struct drm_device *dev, struct mga_fbdev *mfbdev) { @@ -257,7 +241,7 @@ static int mga_fbdev_destroy(struct drm_device *dev, static struct drm_fb_helper_funcs mga_fb_helper_funcs = { .gamma_set = mga_crtc_fb_gamma_set, .gamma_get = mga_crtc_fb_gamma_get, - .fb_probe = mga_fb_find_or_create_single, + .fb_probe = mgag200fb_create, }; int mgag200_fbdev_init(struct mga_device *mdev) diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index b1ebfe30f912..b03531781580 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -251,9 +251,10 @@ nouveau_fbcon_zfill(struct drm_device *dev, struct nouveau_fbdev *fbcon) } static int -nouveau_fbcon_create(struct nouveau_fbdev *fbcon, +nouveau_fbcon_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct nouveau_fbdev *fbcon = (struct nouveau_fbdev *)helper; struct drm_device *dev = fbcon->dev; struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_device *device = nv_device(drm->device); @@ -388,23 +389,6 @@ out: return ret; } -static int -nouveau_fbcon_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct nouveau_fbdev *fbcon = (struct nouveau_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = nouveau_fbcon_create(fbcon, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - void nouveau_fbcon_output_poll_changed(struct drm_device *dev) { @@ -450,7 +434,7 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info) static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = { .gamma_set = nouveau_fbcon_gamma_set, .gamma_get = nouveau_fbcon_gamma_get, - .fb_probe = nouveau_fbcon_find_or_create_single, + .fb_probe = nouveau_fbcon_create, }; diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index b48d1c8cf9eb..b1746741bc59 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -187,9 +187,10 @@ out_unref: return ret; } -static int radeonfb_create(struct radeon_fbdev *rfbdev, +static int radeonfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct radeon_fbdev *rfbdev = (struct radeon_fbdev *)helper; struct radeon_device *rdev = rfbdev->rdev; struct fb_info *info; struct drm_framebuffer *fb = NULL; @@ -300,22 +301,6 @@ out_unref: return ret; } -static int radeon_fb_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct radeon_fbdev *rfbdev = (struct radeon_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = radeonfb_create(rfbdev, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - void radeon_fb_output_poll_changed(struct radeon_device *rdev) { drm_fb_helper_hotplug_event(&rdev->mode_info.rfbdev->helper); @@ -349,7 +334,7 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb static struct drm_fb_helper_funcs radeon_fb_helper_funcs = { .gamma_set = radeon_crtc_fb_gamma_set, .gamma_get = radeon_crtc_fb_gamma_get, - .fb_probe = radeon_fb_find_or_create_single, + .fb_probe = radeonfb_create, }; int radeon_fbdev_init(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index cf5d05a0d955..9f4be3d4a02e 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -476,9 +476,10 @@ udl_framebuffer_init(struct drm_device *dev, } -static int udlfb_create(struct udl_fbdev *ufbdev, +static int udlfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct udl_fbdev *ufbdev = (struct udl_fbdev *)helper; struct drm_device *dev = ufbdev->helper.dev; struct fb_info *info; struct device *device = &dev->usbdev->dev; @@ -556,27 +557,10 @@ out: return ret; } -static int udl_fb_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct udl_fbdev *ufbdev = (struct udl_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = udlfb_create(ufbdev, sizes); - if (ret) - return ret; - - new_fb = 1; - } - return new_fb; -} - static struct drm_fb_helper_funcs udl_fb_helper_funcs = { .gamma_set = udl_crtc_fb_gamma_set, .gamma_get = udl_crtc_fb_gamma_get, - .fb_probe = udl_fb_find_or_create_single, + .fb_probe = udlfb_create, }; static void udl_fbdev_destroy(struct drm_device *dev, diff --git a/drivers/staging/omapdrm/omap_fbdev.c b/drivers/staging/omapdrm/omap_fbdev.c index 7e66eb138315..caefdf9430f8 100644 --- a/drivers/staging/omapdrm/omap_fbdev.c +++ b/drivers/staging/omapdrm/omap_fbdev.c @@ -296,25 +296,10 @@ static void omap_crtc_fb_gamma_get(struct drm_crtc *crtc, DBG("fbdev: get gamma"); } -static int omap_fbdev_probe(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = omap_fbdev_create(helper, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - static struct drm_fb_helper_funcs omap_fb_helper_funcs = { .gamma_set = omap_crtc_fb_gamma_set, .gamma_get = omap_crtc_fb_gamma_get, - .fb_probe = omap_fbdev_probe, + .fb_probe = omap_fbdev_create, }; static struct drm_fb_helper *get_fb(struct fb_info *fbi) -- cgit v1.2.3 From 207fd32970b1def91b11ae28f6bebffc792db714 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 22:13:14 +0100 Subject: drm/fb-helper: improve kerneldoc Now that the fbdev helper interface for drivers is trimmed down, update the kerneldoc for all the remaining exported functions. I've tried to beat the DocBook a bit by reordering the function references a bit into a more sensible ordering. But that didn't work out at all. Hence just extend the in-code DOC: section a bit. Also remove the LOCKING: sections - especially for the setup functions they're totally bogus. But that's not a documentation problem, but simply an artifact of the current rather hazardous locking around drm init and even more so around fbdev setup ... v2: Some further improvements: - Also add documentation for drm_fb_helper_single_add_all_connectors, Dave Airlie didn't want me to kill this one from the fb helper interface. - Update docs for drm_fb_helper_fill_var/fix - they should be used from the driver's ->fb_probe callback to setup the fbdev info structure. - Clarify what the ->fb_probe callback should all do - it needs to setup both the fbdev info and allocate the drm framebuffer used as backing storage. - Add basic documentaation for the drm_fb_helper_funcs driver callback vfunc. v3: Implement clarifications Laurent Pinchart suggested in his review. v4: Fix another mispelling Laurent spotted. Cc: Laurent Pinchart Acked-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- Documentation/DocBook/drm.tmpl | 1 + drivers/gpu/drm/drm_fb_helper.c | 148 ++++++++++++++++++++++++++++++++++++---- include/drm/drm_fb_helper.h | 12 ++++ 3 files changed, 146 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index b26de523ab70..51e1904ac4c7 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -2143,6 +2143,7 @@ void intel_crt_init(struct drm_device *dev) fbdev Helper Functions Reference !Pdrivers/gpu/drm/drm_fb_helper.c fbdev helpers !Edrivers/gpu/drm/drm_fb_helper.c +!Iinclude/drm/drm_fb_helper.h Display Port Helper Functions Reference diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index e0d4b04c0eb5..94f304d8b795 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -52,9 +52,36 @@ static LIST_HEAD(kernel_fb_helper_list); * mode setting driver. They can be used mostly independantely from the crtc * helper functions used by many drivers to implement the kernel mode setting * interfaces. + * + * Initialization is done as a three-step process with drm_fb_helper_init(), + * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config(). + * Drivers with fancier requirements than the default beheviour can override the + * second step with their own code. Teardown is done with drm_fb_helper_fini(). + * + * At runtime drivers should restore the fbdev console by calling + * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They + * should also notify the fb helper code from updates to the output + * configuration by calling drm_fb_helper_hotplug_event(). For easier + * integration with the output polling code in drm_crtc_helper.c the modeset + * code proves a ->output_poll_changed callback. + * + * All other functions exported by the fb helper library can be used to + * implement the fbdev driver interface by the driver. */ -/* simple single crtc case helper function */ +/** + * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev + * emulation helper + * @fb_helper: fbdev initialized with drm_fb_helper_init + * + * This functions adds all the available connectors for use with the given + * fb_helper. This is a separate step to allow drivers to freely assign + * connectors to the fbdev, e.g. if some are reserved for special purposes or + * not adequate to be used for the fbcon. + * + * Since this is part of the initial setup before the fbdev is published, no + * locking is required. + */ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) { struct drm_device *dev = fb_helper->dev; @@ -163,6 +190,10 @@ static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); } +/** + * drm_fb_helper_debug_enter - implementation for ->fb_debug_enter + * @info: fbdev registered by the helper + */ int drm_fb_helper_debug_enter(struct fb_info *info) { struct drm_fb_helper *helper = info->par; @@ -208,6 +239,10 @@ static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc) return NULL; } +/** + * drm_fb_helper_debug_leave - implementation for ->fb_debug_leave + * @info: fbdev registered by the helper + */ int drm_fb_helper_debug_leave(struct fb_info *info) { struct drm_fb_helper *helper = info->par; @@ -243,9 +278,9 @@ EXPORT_SYMBOL(drm_fb_helper_debug_leave); * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration * @fb_helper: fbcon to restore * - * This should be called from driver's drm->lastclose callback when implementing - * an fbcon on top of kms using this helper. This ensures that the user isn't - * greeted with a black screen when e.g. X dies. + * This should be called from driver's drm ->lastclose callback + * when implementing an fbcon on top of kms using this helper. This ensures that + * the user isn't greeted with a black screen when e.g. X dies. */ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) { @@ -381,6 +416,11 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) drm_modeset_unlock_all(dev); } +/** + * drm_fb_helper_blank - implementation for ->fb_blank + * @blank: desired blanking state + * @info: fbdev registered by the helper + */ int drm_fb_helper_blank(int blank, struct fb_info *info) { switch (blank) { @@ -424,6 +464,24 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) kfree(helper->crtc_info); } +/** + * drm_fb_helper_init - initialize a drm_fb_helper structure + * @dev: drm device + * @fb_helper: driver-allocated fbdev helper structure to initialize + * @crtc_count: maximum number of crtcs to support in this fbdev emulation + * @max_conn_count: max connector count + * + * This allocates the structures for the fbdev helper with the given limits. + * Note that this won't yet touch the hardware (through the driver interfaces) + * nor register the fbdev. This is only done in drm_fb_helper_initial_config() + * to allow driver writes more control over the exact init sequence. + * + * Drivers must set fb_helper->funcs before calling + * drm_fb_helper_initial_config(). + * + * RETURNS: + * Zero if everything went ok, nonzero otherwise. + */ int drm_fb_helper_init(struct drm_device *dev, struct drm_fb_helper *fb_helper, int crtc_count, int max_conn_count) @@ -552,6 +610,11 @@ static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, return 0; } +/** + * drm_fb_helper_setcmap - implementation for ->fb_setcmap + * @cmap: cmap to set + * @info: fbdev registered by the helper + */ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; @@ -591,6 +654,11 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_setcmap); +/** + * drm_fb_helper_check_var - implementation for ->fb_check_var + * @var: screeninfo to check + * @info: fbdev registered by the helper + */ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { @@ -683,7 +751,14 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, } EXPORT_SYMBOL(drm_fb_helper_check_var); -/* this will let fbcon do the mode init */ +/** + * drm_fb_helper_set_par - implementation for ->fb_set_par + * @info: fbdev registered by the helper + * + * This will let fbcon do the mode init and is called at initialization time by + * the fbdev core when registering the driver, and later on through the hotplug + * callback. + */ int drm_fb_helper_set_par(struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; @@ -715,6 +790,11 @@ int drm_fb_helper_set_par(struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_set_par); +/** + * drm_fb_helper_pan_display - implementation for ->fb_pan_display + * @var: updated screen information + * @info: fbdev registered by the helper + */ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) { @@ -753,8 +833,9 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, EXPORT_SYMBOL(drm_fb_helper_pan_display); /* - * Allocates the backing storage through the ->fb_probe callback and then - * registers the fbdev and sets up the panic notifier. + * Allocates the backing storage and sets up the fbdev info structure through + * the ->fb_probe callback and then registers the fbdev and sets up the panic + * notifier. */ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, int preferred_bpp) @@ -876,6 +957,19 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, return 0; } +/** + * drm_fb_helper_fill_fix - initializes fixed fbdev information + * @info: fbdev registered by the helper + * @pitch: desired pitch + * @depth: desired depth + * + * Helper to fill in the fixed fbdev information useful for a non-accelerated + * fbdev emulations. Drivers which support acceleration methods which impose + * additional constraints need to set up their own limits. + * + * Drivers should call this (or their equivalent setup code) from their + * ->fb_probe callback. + */ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, uint32_t depth) { @@ -896,6 +990,20 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, } EXPORT_SYMBOL(drm_fb_helper_fill_fix); +/** + * drm_fb_helper_fill_var - initalizes variable fbdev information + * @info: fbdev instance to set up + * @fb_helper: fb helper instance to use as template + * @fb_width: desired fb width + * @fb_height: desired fb height + * + * Sets up the variable fbdev metainformation from the given fb helper instance + * and the drm framebuffer allocated in fb_helper->fb. + * + * Drivers should call this (or their equivalent setup code) from their + * ->fb_probe callback after having allocated the fbdev backing + * storage framebuffer. + */ void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, uint32_t fb_width, uint32_t fb_height) { @@ -1358,18 +1466,23 @@ out: } /** - * drm_helper_initial_config - setup a sane initial connector configuration + * drm_fb_helper_initial_config - setup a sane initial connector configuration * @fb_helper: fb_helper device struct * @bpp_sel: bpp value to use for the framebuffer configuration * - * LOCKING: - * Called at init time by the driver to set up the @fb_helper initial - * configuration, must take the mode config lock. - * * Scans the CRTCs and connectors and tries to put together an initial setup. * At the moment, this is a cloned configuration across all heads with * a new framebuffer object as the backing store. * + * Note that this also registers the fbdev and so allows userspace to call into + * the driver through the fbdev interfaces. + * + * This function will call down into the ->fb_probe callback to let + * the driver allocate and initialize the fbdev info structure and the drm + * framebuffer used to back the fbdev. drm_fb_helper_fill_var() and + * drm_fb_helper_fill_fix() are provided as helpers to setup simple default + * values for the fbdev info structure. + * * RETURNS: * Zero if everything went ok, nonzero otherwise. */ @@ -1400,12 +1513,17 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config); * probing all the outputs attached to the fb * @fb_helper: the drm_fb_helper * - * LOCKING: - * Called at runtime, must take mode config lock. - * * Scan the connectors attached to the fb_helper and try to put together a * setup after *notification of a change in output configuration. * + * Called at runtime, takes the mode config locks to be able to check/change the + * modeset configuration. Must be run from process context (which usually means + * either the output polling work or a work item launched from the driver's + * hotplug interrupt). + * + * Note that the driver must ensure that this is only called _after_ the fb has + * been fully set up, i.e. after the call to drm_fb_helper_initial_config. + * * RETURNS: * 0 on success and a non-zero error code otherwise. */ diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 973402d6effe..31e5b97c2e89 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -48,6 +48,18 @@ struct drm_fb_helper_surface_size { u32 surface_depth; }; +/** + * struct drm_fb_helper_funcs - driver callbacks for the fbdev emulation library + * @gamma_set: - Set the given gamma lut register on the given crtc. + * @gamma_get: - Read the given gamma lut register on the given crtc, used to + * save the current lut when force-restoring the fbdev for e.g. + * kdbg. + * @fb_probe: - Driver callback to allocate and initialize the fbdev info + * structure. Futhermore it also needs to allocate the drm + * framebuffer used to back the fbdev. + * + * Driver callbacks used by the fbdev emulation helper library. + */ struct drm_fb_helper_funcs { void (*gamma_set)(struct drm_crtc *crtc, u16 red, u16 green, u16 blue, int regno); -- cgit v1.2.3 From 1b1d5397058f06bc5bd87d43ed93f34b28546ea4 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 24 Jan 2013 16:42:07 +0100 Subject: drm/fb-helper: don't sleep for screen unblank when an oopps is in progress Otherwise the system will burn even brighter and worse, leave the user wondering what's going on exactly. Since we already have a panic handler which will (try) to restore the entire fbdev console mode, we can just bail out. Inspired by a patch from Konstantin Khlebnikov. The callchain leading to this, cut&pasted from Konstantin's original patch: callstack: panic() bust_spinlocks(1) unblank_screen() vc->vc_sw->con_blank() fbcon_blank() fb_blank() info->fbops->fb_blank() drm_fb_helper_blank() drm_fb_helper_dpms() drm_modeset_lock_all() mutex_lock(&dev->mode_config.mutex) Note that the entire locking in the fb helper around panic/sysrq and kdbg is ... non-existant. So we have a decent change of blowing up everything. But since reworking this ties in with funny concepts like the fbdev notifier chain or the impressive things which happen around console_lock while oopsing, I'll leave that as an exercise for braver souls than me. v2: Drop the -EBUSY return value I've copied, we don't need it since the we'll take care of things ourselves anyway. Cc: Konstantin Khlebnikov Cc: Andrew Morton References: https://patchwork.kernel.org/patch/1878181/ Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 94f304d8b795..59d6b9bf204b 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -390,6 +390,14 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) struct drm_connector *connector; int i, j; + /* + * fbdev->blank can be called from irq context in case of a panic. + * Since we already have our own special panic handler which will + * restore the fbdev console mode completely, just bail out early. + */ + if (oops_in_progress) + return; + /* * For each CRTC in this fb, turn the connectors on/off. */ -- cgit v1.2.3 From a36516b016f52f0f6e5284025e3487f63b4be33b Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 3 Jan 2013 16:16:57 +0530 Subject: mfd: palmas: Add rtc irq number as irq resource for palmas-rtc Palma RTC is capable of generating alarm interrupt. Pass the alarm interrupt as IRQ_RESOURCE for palmas-rtc sub device driver so that rtc driver can get irq as platform_get_irq(). Also pass the irq domain in mfd_add_devices() to properly offset the irqs for sub devices. This is needed when adding device through DT. Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/mfd/palmas.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c index 6ffd7a2affdc..bbdbc50a3cca 100644 --- a/drivers/mfd/palmas.c +++ b/drivers/mfd/palmas.c @@ -39,6 +39,14 @@ enum palmas_ids { PALMAS_USB_ID, }; +static struct resource palmas_rtc_resources[] = { + { + .start = PALMAS_RTC_ALARM_IRQ, + .end = PALMAS_RTC_ALARM_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + static const struct mfd_cell palmas_children[] = { { .name = "palmas-pmic", @@ -59,6 +67,8 @@ static const struct mfd_cell palmas_children[] = { { .name = "palmas-rtc", .id = PALMAS_RTC_ID, + .resources = &palmas_rtc_resources[0], + .num_resources = ARRAY_SIZE(palmas_rtc_resources), }, { .name = "palmas-pwrbutton", @@ -456,8 +466,8 @@ static int palmas_i2c_probe(struct i2c_client *i2c, ret = mfd_add_devices(palmas->dev, -1, children, ARRAY_SIZE(palmas_children), - NULL, regmap_irq_chip_get_base(palmas->irq_data), - NULL); + NULL, 0, + regmap_irq_get_domain(palmas->irq_data)); kfree(children); if (ret < 0) -- cgit v1.2.3 From 3d50a278527154974f4240584da691db7424956d Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 3 Jan 2013 16:16:59 +0530 Subject: gpio: palmas: Add support for Palmas GPIO Add gpio driver for TI Palmas series PMIC. This has 8 gpio which can work as input/output. Signed-off-by: Laxman Dewangan Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/gpio/Kconfig | 7 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-palmas.c | 184 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 drivers/gpio/gpio-palmas.c (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 682de754d63f..40a0ec3b7c40 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -663,6 +663,13 @@ config GPIO_AB8500 help Select this to enable the AB8500 IC GPIO driver +config GPIO_PALMAS + bool "TI PALMAS series PMICs GPIO" + depends on MFD_PALMAS + help + Select this option to enable GPIO driver for the TI PALMAS + series chip family. + config GPIO_TPS6586X bool "TPS6586X GPIO" depends on MFD_TPS6586X diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index c5aebd008dde..8962c5f7b026 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o obj-$(CONFIG_ARCH_DAVINCI_TNETV107X) += gpio-tnetv107x.o +obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c new file mode 100644 index 000000000000..e3a4e56f5a42 --- /dev/null +++ b/drivers/gpio/gpio-palmas.c @@ -0,0 +1,184 @@ +/* + * TI Palma series PMIC's GPIO driver. + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * Author: Laxman Dewangan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +struct palmas_gpio { + struct gpio_chip gpio_chip; + struct palmas *palmas; +}; + +static inline struct palmas_gpio *to_palmas_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct palmas_gpio, gpio_chip); +} + +static int palmas_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + struct palmas_gpio *pg = to_palmas_gpio(gc); + struct palmas *palmas = pg->palmas; + unsigned int val; + int ret; + + ret = palmas_read(palmas, PALMAS_GPIO_BASE, PALMAS_GPIO_DATA_IN, &val); + if (ret < 0) { + dev_err(gc->dev, "GPIO_DATA_IN read failed, err = %d\n", ret); + return ret; + } + return !!(val & BIT(offset)); +} + +static void palmas_gpio_set(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct palmas_gpio *pg = to_palmas_gpio(gc); + struct palmas *palmas = pg->palmas; + int ret; + + if (value) + ret = palmas_write(palmas, PALMAS_GPIO_BASE, + PALMAS_GPIO_SET_DATA_OUT, BIT(offset)); + else + ret = palmas_write(palmas, PALMAS_GPIO_BASE, + PALMAS_GPIO_CLEAR_DATA_OUT, BIT(offset)); + if (ret < 0) + dev_err(gc->dev, "%s write failed, err = %d\n", + (value) ? "GPIO_SET_DATA_OUT" : "GPIO_CLEAR_DATA_OUT", + ret); +} + +static int palmas_gpio_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct palmas_gpio *pg = to_palmas_gpio(gc); + struct palmas *palmas = pg->palmas; + int ret; + + /* Set the initial value */ + palmas_gpio_set(gc, offset, value); + + ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE, + PALMAS_GPIO_DATA_DIR, BIT(offset), BIT(offset)); + if (ret < 0) + dev_err(gc->dev, "GPIO_DATA_DIR write failed, err = %d\n", ret); + return ret; +} + +static int palmas_gpio_input(struct gpio_chip *gc, unsigned offset) +{ + struct palmas_gpio *pg = to_palmas_gpio(gc); + struct palmas *palmas = pg->palmas; + int ret; + + ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE, + PALMAS_GPIO_DATA_DIR, BIT(offset), 0); + if (ret < 0) + dev_err(gc->dev, "GPIO_DATA_DIR write failed, err = %d\n", ret); + return ret; +} + +static int palmas_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct palmas_gpio *pg = to_palmas_gpio(gc); + struct palmas *palmas = pg->palmas; + + return palmas_irq_get_virq(palmas, PALMAS_GPIO_0_IRQ + offset); +} + +static int palmas_gpio_probe(struct platform_device *pdev) +{ + struct palmas *palmas = dev_get_drvdata(pdev->dev.parent); + struct palmas_platform_data *palmas_pdata; + struct palmas_gpio *palmas_gpio; + int ret; + + palmas_gpio = devm_kzalloc(&pdev->dev, + sizeof(*palmas_gpio), GFP_KERNEL); + if (!palmas_gpio) { + dev_err(&pdev->dev, "Could not allocate palmas_gpio\n"); + return -ENOMEM; + } + + palmas_gpio->palmas = palmas; + palmas_gpio->gpio_chip.owner = THIS_MODULE; + palmas_gpio->gpio_chip.label = dev_name(&pdev->dev); + palmas_gpio->gpio_chip.ngpio = 8; + palmas_gpio->gpio_chip.can_sleep = 1; + palmas_gpio->gpio_chip.direction_input = palmas_gpio_input; + palmas_gpio->gpio_chip.direction_output = palmas_gpio_output; + palmas_gpio->gpio_chip.to_irq = palmas_gpio_to_irq; + palmas_gpio->gpio_chip.set = palmas_gpio_set; + palmas_gpio->gpio_chip.get = palmas_gpio_get; + palmas_gpio->gpio_chip.dev = &pdev->dev; +#ifdef CONFIG_OF_GPIO + palmas_gpio->gpio_chip.of_node = palmas->dev->of_node; +#endif + palmas_pdata = dev_get_platdata(palmas->dev); + if (palmas_pdata && palmas_pdata->gpio_base) + palmas_gpio->gpio_chip.base = palmas_pdata->gpio_base; + else + palmas_gpio->gpio_chip.base = -1; + + ret = gpiochip_add(&palmas_gpio->gpio_chip); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, palmas_gpio); + return ret; +} + +static int palmas_gpio_remove(struct platform_device *pdev) +{ + struct palmas_gpio *palmas_gpio = platform_get_drvdata(pdev); + + return gpiochip_remove(&palmas_gpio->gpio_chip); +} + +static struct platform_driver palmas_gpio_driver = { + .driver.name = "palmas-gpio", + .driver.owner = THIS_MODULE, + .probe = palmas_gpio_probe, + .remove = palmas_gpio_remove, +}; + +static int __init palmas_gpio_init(void) +{ + return platform_driver_register(&palmas_gpio_driver); +} +subsys_initcall(palmas_gpio_init); + +static void __exit palmas_gpio_exit(void) +{ + platform_driver_unregister(&palmas_gpio_driver); +} +module_exit(palmas_gpio_exit); + +MODULE_ALIAS("platform:palmas-gpio"); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_DESCRIPTION("GPIO driver for TI Palmas series PMICs"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 0101e53cbb1c653738de8dfddc80c0faa5a4fd39 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 3 Jan 2013 16:17:00 +0530 Subject: rtc: palmas: Add RTC driver Palmas series PMIC TI Palmas series PMIC support the RTC and alarm functionality. Add RTC driver with alarm support for this device. Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/rtc/Kconfig | 10 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-palmas.c | 339 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 350 insertions(+) create mode 100644 drivers/rtc/rtc-palmas.c (limited to 'drivers') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 923a9da9c829..f11f746f7a35 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -269,6 +269,16 @@ config RTC_DRV_X1205 This driver can also be built as a module. If so, the module will be called rtc-x1205. +config RTC_DRV_PALMAS + tristate "TI Palmas RTC driver" + depends on MFD_PALMAS + help + If you say yes here you get support for the RTC of TI PALMA series PMIC + chips. + + This driver can also be built as a module. If so, the module + will be called rtc-palma. + config RTC_DRV_PCF8523 tristate "NXP PCF8523" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 4418ef3f9ecc..3b66c75f6c76 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o +obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o diff --git a/drivers/rtc/rtc-palmas.c b/drivers/rtc/rtc-palmas.c new file mode 100644 index 000000000000..59c42986254e --- /dev/null +++ b/drivers/rtc/rtc-palmas.c @@ -0,0 +1,339 @@ +/* + * rtc-palmas.c -- Palmas Real Time Clock driver. + + * RTC driver for TI Palma series devices like TPS65913, + * TPS65914 power management IC. + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * Author: Laxman Dewangan + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct palmas_rtc { + struct rtc_device *rtc; + struct device *dev; + unsigned int irq; +}; + +/* Total number of RTC registers needed to set time*/ +#define PALMAS_NUM_TIME_REGS (PALMAS_YEARS_REG - PALMAS_SECONDS_REG + 1) + +static int palmas_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char rtc_data[PALMAS_NUM_TIME_REGS]; + struct palmas *palmas = dev_get_drvdata(dev->parent); + int ret; + + /* Copy RTC counting registers to static registers or latches */ + ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, + PALMAS_RTC_CTRL_REG_GET_TIME, PALMAS_RTC_CTRL_REG_GET_TIME); + if (ret < 0) { + dev_err(dev, "RTC CTRL reg update failed, err: %d\n", ret); + return ret; + } + + ret = palmas_bulk_read(palmas, PALMAS_RTC_BASE, PALMAS_SECONDS_REG, + rtc_data, PALMAS_NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "RTC_SECONDS reg read failed, err = %d\n", ret); + return ret; + } + + tm->tm_sec = bcd2bin(rtc_data[0]); + tm->tm_min = bcd2bin(rtc_data[1]); + tm->tm_hour = bcd2bin(rtc_data[2]); + tm->tm_mday = bcd2bin(rtc_data[3]); + tm->tm_mon = bcd2bin(rtc_data[4]) - 1; + tm->tm_year = bcd2bin(rtc_data[5]) + 100; + + return ret; +} + +static int palmas_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char rtc_data[PALMAS_NUM_TIME_REGS]; + struct palmas *palmas = dev_get_drvdata(dev->parent); + int ret; + + rtc_data[0] = bin2bcd(tm->tm_sec); + rtc_data[1] = bin2bcd(tm->tm_min); + rtc_data[2] = bin2bcd(tm->tm_hour); + rtc_data[3] = bin2bcd(tm->tm_mday); + rtc_data[4] = bin2bcd(tm->tm_mon + 1); + rtc_data[5] = bin2bcd(tm->tm_year - 100); + + /* Stop RTC while updating the RTC time registers */ + ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, + PALMAS_RTC_CTRL_REG_STOP_RTC, 0); + if (ret < 0) { + dev_err(dev, "RTC stop failed, err = %d\n", ret); + return ret; + } + + ret = palmas_bulk_write(palmas, PALMAS_RTC_BASE, PALMAS_SECONDS_REG, + rtc_data, PALMAS_NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "RTC_SECONDS reg write failed, err = %d\n", ret); + return ret; + } + + /* Start back RTC */ + ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, + PALMAS_RTC_CTRL_REG_STOP_RTC, PALMAS_RTC_CTRL_REG_STOP_RTC); + if (ret < 0) + dev_err(dev, "RTC start failed, err = %d\n", ret); + return ret; +} + +static int palmas_rtc_alarm_irq_enable(struct device *dev, unsigned enabled) +{ + struct palmas *palmas = dev_get_drvdata(dev->parent); + u8 val; + + val = enabled ? PALMAS_RTC_INTERRUPTS_REG_IT_ALARM : 0; + return palmas_write(palmas, PALMAS_RTC_BASE, + PALMAS_RTC_INTERRUPTS_REG, val); +} + +static int palmas_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned char alarm_data[PALMAS_NUM_TIME_REGS]; + u32 int_val; + struct palmas *palmas = dev_get_drvdata(dev->parent); + int ret; + + ret = palmas_bulk_read(palmas, PALMAS_RTC_BASE, + PALMAS_ALARM_SECONDS_REG, + alarm_data, PALMAS_NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "RTC_ALARM_SECONDS read failed, err = %d\n", ret); + return ret; + } + + alm->time.tm_sec = bcd2bin(alarm_data[0]); + alm->time.tm_min = bcd2bin(alarm_data[1]); + alm->time.tm_hour = bcd2bin(alarm_data[2]); + alm->time.tm_mday = bcd2bin(alarm_data[3]); + alm->time.tm_mon = bcd2bin(alarm_data[4]) - 1; + alm->time.tm_year = bcd2bin(alarm_data[5]) + 100; + + ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_INTERRUPTS_REG, + &int_val); + if (ret < 0) { + dev_err(dev, "RTC_INTERRUPTS reg read failed, err = %d\n", ret); + return ret; + } + + if (int_val & PALMAS_RTC_INTERRUPTS_REG_IT_ALARM) + alm->enabled = 1; + return ret; +} + +static int palmas_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned char alarm_data[PALMAS_NUM_TIME_REGS]; + struct palmas *palmas = dev_get_drvdata(dev->parent); + int ret; + + ret = palmas_rtc_alarm_irq_enable(dev, 0); + if (ret < 0) { + dev_err(dev, "Disable RTC alarm failed\n"); + return ret; + } + + alarm_data[0] = bin2bcd(alm->time.tm_sec); + alarm_data[1] = bin2bcd(alm->time.tm_min); + alarm_data[2] = bin2bcd(alm->time.tm_hour); + alarm_data[3] = bin2bcd(alm->time.tm_mday); + alarm_data[4] = bin2bcd(alm->time.tm_mon + 1); + alarm_data[5] = bin2bcd(alm->time.tm_year - 100); + + ret = palmas_bulk_write(palmas, PALMAS_RTC_BASE, + PALMAS_ALARM_SECONDS_REG, alarm_data, PALMAS_NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "ALARM_SECONDS_REG write failed, err = %d\n", ret); + return ret; + } + + if (alm->enabled) + ret = palmas_rtc_alarm_irq_enable(dev, 1); + return ret; +} + +static int palmas_clear_interrupts(struct device *dev) +{ + struct palmas *palmas = dev_get_drvdata(dev->parent); + unsigned int rtc_reg; + int ret; + + ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_STATUS_REG, + &rtc_reg); + if (ret < 0) { + dev_err(dev, "RTC_STATUS read failed, err = %d\n", ret); + return ret; + } + + ret = palmas_write(palmas, PALMAS_RTC_BASE, PALMAS_RTC_STATUS_REG, + rtc_reg); + if (ret < 0) { + dev_err(dev, "RTC_STATUS write failed, err = %d\n", ret); + return ret; + } + return 0; +} + +static irqreturn_t palmas_rtc_interrupt(int irq, void *context) +{ + struct palmas_rtc *palmas_rtc = context; + struct device *dev = palmas_rtc->dev; + int ret; + + ret = palmas_clear_interrupts(dev); + if (ret < 0) { + dev_err(dev, "RTC interrupt clear failed, err = %d\n", ret); + return IRQ_NONE; + } + + rtc_update_irq(palmas_rtc->rtc, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; +} + +static struct rtc_class_ops palmas_rtc_ops = { + .read_time = palmas_rtc_read_time, + .set_time = palmas_rtc_set_time, + .read_alarm = palmas_rtc_read_alarm, + .set_alarm = palmas_rtc_set_alarm, + .alarm_irq_enable = palmas_rtc_alarm_irq_enable, +}; + +static int palmas_rtc_probe(struct platform_device *pdev) +{ + struct palmas *palmas = dev_get_drvdata(pdev->dev.parent); + struct palmas_rtc *palmas_rtc = NULL; + int ret; + + palmas_rtc = devm_kzalloc(&pdev->dev, sizeof(struct palmas_rtc), + GFP_KERNEL); + if (!palmas_rtc) + return -ENOMEM; + + /* Clear pending interrupts */ + ret = palmas_clear_interrupts(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "clear RTC int failed, err = %d\n", ret); + return ret; + } + + palmas_rtc->dev = &pdev->dev; + platform_set_drvdata(pdev, palmas_rtc); + + /* Start RTC */ + ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, + PALMAS_RTC_CTRL_REG_STOP_RTC, + PALMAS_RTC_CTRL_REG_STOP_RTC); + if (ret < 0) { + dev_err(&pdev->dev, "RTC_CTRL write failed, err = %d\n", ret); + return ret; + } + + palmas_rtc->irq = platform_get_irq(pdev, 0); + + palmas_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, + &palmas_rtc_ops, THIS_MODULE); + if (IS_ERR(palmas_rtc->rtc)) { + ret = PTR_ERR(palmas_rtc->rtc); + dev_err(&pdev->dev, "RTC register failed, err = %d\n", ret); + return ret; + } + + ret = request_threaded_irq(palmas_rtc->irq, NULL, + palmas_rtc_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT | + IRQF_EARLY_RESUME, + dev_name(&pdev->dev), palmas_rtc); + if (ret < 0) { + dev_err(&pdev->dev, "IRQ request failed, err = %d\n", ret); + rtc_device_unregister(palmas_rtc->rtc); + return ret; + } + + device_set_wakeup_capable(&pdev->dev, 1); + return 0; +} + +static int palmas_rtc_remove(struct platform_device *pdev) +{ + struct palmas_rtc *palmas_rtc = platform_get_drvdata(pdev); + + palmas_rtc_alarm_irq_enable(&pdev->dev, 0); + free_irq(palmas_rtc->irq, palmas_rtc); + rtc_device_unregister(palmas_rtc->rtc); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int palmas_rtc_suspend(struct device *dev) +{ + struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(palmas_rtc->irq); + return 0; +} + +static int palmas_rtc_resume(struct device *dev) +{ + struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(palmas_rtc->irq); + return 0; +} +#endif + +static const struct dev_pm_ops palmas_rtc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(palmas_rtc_suspend, palmas_rtc_resume) +}; + +static struct platform_driver palmas_rtc_driver = { + .probe = palmas_rtc_probe, + .remove = palmas_rtc_remove, + .driver = { + .owner = THIS_MODULE, + .name = "palmas-rtc", + .pm = &palmas_rtc_pm_ops, + }, +}; + +module_platform_driver(palmas_rtc_driver); + +MODULE_ALIAS("platform:palmas_rtc"); +MODULE_DESCRIPTION("TI PALMAS series RTC driver"); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 025d982079502b1d6c7c428504eb64c5059a2e80 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 11 Dec 2012 16:51:39 +0900 Subject: mfd: wm5102: Mark only extant DSP registers volatile Since regmap sometimes uses volatile as a proxy for readable simply having a blanket condition can mark too many registers as readable. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm5102-tables.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 1133a64c2dc9..ee0a109f5ce9 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -1831,9 +1831,6 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) static bool wm5102_volatile_register(struct device *dev, unsigned int reg) { - if (reg > 0xffff) - return true; - switch (reg) { case ARIZONA_SOFTWARE_RESET: case ARIZONA_DEVICE_REVISION: @@ -1878,7 +1875,13 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_MIC_DETECT_3: return true; default: - return false; + if ((reg >= 0x100000 && reg < 0x106000) || + (reg >= 0x180000 && reg < 0x180800) || + (reg >= 0x190000 && reg < 0x194800) || + (reg >= 0x1a8000 && reg < 0x1a9800)) + return true; + else + return false; } } -- cgit v1.2.3 From d7768111a98bcf865e1e14d8c663789f6e21393d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 20 Dec 2012 15:38:03 +0000 Subject: mfd: arizona: Register MICVDD supply first to ensure no retries Not strictly required as probe deferral will take care of everything but it makes boot a little smoother. Reported-by: Ryo Tsutsui Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/arizona-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 222c03a5ddc0..1ab02a7d2b05 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -275,19 +275,19 @@ static struct mfd_cell early_devs[] = { }; static struct mfd_cell wm5102_devs[] = { + { .name = "arizona-micsupp" }, { .name = "arizona-extcon" }, { .name = "arizona-gpio" }, { .name = "arizona-haptics" }, - { .name = "arizona-micsupp" }, { .name = "arizona-pwm" }, { .name = "wm5102-codec" }, }; static struct mfd_cell wm5110_devs[] = { + { .name = "arizona-micsupp" }, { .name = "arizona-extcon" }, { .name = "arizona-gpio" }, { .name = "arizona-haptics" }, - { .name = "arizona-micsupp" }, { .name = "arizona-pwm" }, { .name = "wm5110-codec" }, }; -- cgit v1.2.3 From 71f39e5c087418fb63a57f74478c3e32899592cd Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Jan 2013 14:30:18 +0000 Subject: mfd: wm5102: Update rev B patch for latest evaluation The latest evaluation of the revision B silicon suggests some changes to the tuning applied for optimal performance. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm5102-tables.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index ee0a109f5ce9..74f9853b6dc4 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -59,12 +59,13 @@ static const struct reg_default wm5102_reva_patch[] = { static const struct reg_default wm5102_revb_patch[] = { { 0x80, 0x0003 }, { 0x081, 0xE022 }, - { 0x410, 0x6080 }, - { 0x418, 0x6080 }, - { 0x420, 0x6080 }, + { 0x410, 0x4080 }, + { 0x418, 0x4080 }, + { 0x420, 0x4080 }, { 0x428, 0xC000 }, - { 0x441, 0x8014 }, + { 0x4B0, 0x0066 }, { 0x458, 0x000b }, + { 0x212, 0x0000 }, { 0x80, 0x0000 }, }; -- cgit v1.2.3 From f1c68e4dd455eeaf30647a52595a7daf62fc5492 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 8 Jan 2013 14:01:22 +0530 Subject: mfd: wm8994: Use devm_regulator_bulk_get API devm_regulator_bulk_get is device managed and saves some cleanup and exit code. Signed-off-by: Sachin Kamat Acked-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8994-core.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 57c488d42d3e..803e93fae56a 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -467,7 +467,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) goto err; } - ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies, + ret = devm_regulator_bulk_get(wm8994->dev, wm8994->num_supplies, wm8994->supplies); if (ret != 0) { dev_err(wm8994->dev, "Failed to get supplies: %d\n", ret); @@ -478,7 +478,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) wm8994->supplies); if (ret != 0) { dev_err(wm8994->dev, "Failed to enable supplies: %d\n", ret); - goto err_get; + goto err; } ret = wm8994_reg_read(wm8994, WM8994_SOFTWARE_RESET); @@ -658,8 +658,6 @@ err_irq: err_enable: regulator_bulk_disable(wm8994->num_supplies, wm8994->supplies); -err_get: - regulator_bulk_free(wm8994->num_supplies, wm8994->supplies); err: mfd_remove_devices(wm8994->dev); return ret; @@ -672,7 +670,6 @@ static void wm8994_device_exit(struct wm8994 *wm8994) wm8994_irq_exit(wm8994); regulator_bulk_disable(wm8994->num_supplies, wm8994->supplies); - regulator_bulk_free(wm8994->num_supplies, wm8994->supplies); } static const struct of_device_id wm8994_of_match[] = { -- cgit v1.2.3 From 0cc9d08d3dca174ee712044e299732f26fdab22d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 27 Jan 2013 12:07:23 +0800 Subject: mfd: wm5102: Mark DSP memory regions as volatile and readable We can cache some of them but this is simpler for now. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm5102-tables.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 74f9853b6dc4..6655c9e89e92 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -1826,7 +1826,13 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_DSP1_STATUS_3: return true; default: - return false; + if ((reg >= 0x100000 && reg < 0x106000) || + (reg >= 0x180000 && reg < 0x180800) || + (reg >= 0x190000 && reg < 0x194800) || + (reg >= 0x1a8000 && reg < 0x1a9800)) + return true; + else + return false; } } -- cgit v1.2.3 From 595e5bf75cea0664eeb63d8df20716f66f4aa459 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 27 Jan 2013 12:07:30 +0800 Subject: mfd: wm5102: Refresh register defaults The WM5102 register defaults are not up to date with the current register map, synchronise them with those for current devices. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm5102-tables.c | 95 ++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 62 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 6655c9e89e92..ff8f0804dde3 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -225,11 +225,9 @@ const struct regmap_irq_chip wm5102_irq = { static const struct reg_default wm5102_reg_default[] = { { 0x00000008, 0x0019 }, /* R8 - Ctrl IF SPI CFG 1 */ { 0x00000009, 0x0001 }, /* R9 - Ctrl IF I2C1 CFG 1 */ - { 0x0000000D, 0x0000 }, /* R13 - Ctrl IF Status 1 */ { 0x00000016, 0x0000 }, /* R22 - Write Sequencer Ctrl 0 */ { 0x00000017, 0x0000 }, /* R23 - Write Sequencer Ctrl 1 */ { 0x00000018, 0x0000 }, /* R24 - Write Sequencer Ctrl 2 */ - { 0x0000001A, 0x0000 }, /* R26 - Write Sequencer PROM */ { 0x00000020, 0x0000 }, /* R32 - Tone Generator 1 */ { 0x00000021, 0x1000 }, /* R33 - Tone Generator 2 */ { 0x00000022, 0x0000 }, /* R34 - Tone Generator 3 */ @@ -244,12 +242,14 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000062, 0x01FF }, /* R98 - Sample Rate Sequence Select 2 */ { 0x00000063, 0x01FF }, /* R99 - Sample Rate Sequence Select 3 */ { 0x00000064, 0x01FF }, /* R100 - Sample Rate Sequence Select 4 */ - { 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 1 */ - { 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 2 */ - { 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 3 */ - { 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 4 */ - { 0x0000006C, 0x01FF }, /* R108 - Always On Triggers Sequence Select 5 */ - { 0x0000006D, 0x01FF }, /* R109 - Always On Triggers Sequence Select 6 */ + { 0x00000066, 0x01FF }, /* R102 - Always On Triggers Sequence Select 1 */ + { 0x00000067, 0x01FF }, /* R103 - Always On Triggers Sequence Select 2 */ + { 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 3 */ + { 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 4 */ + { 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 5 */ + { 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 6 */ + { 0x0000006E, 0x01FF }, /* R110 - Trigger Sequence Select 32 */ + { 0x0000006F, 0x01FF }, /* R111 - Trigger Sequence Select 33 */ { 0x00000070, 0x0000 }, /* R112 - Comfort Noise Generator */ { 0x00000090, 0x0000 }, /* R144 - Haptics Control 1 */ { 0x00000091, 0x7FFF }, /* R145 - Haptics Control 2 */ @@ -259,13 +259,14 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000095, 0x0000 }, /* R149 - Haptics phase 2 duration */ { 0x00000096, 0x0000 }, /* R150 - Haptics phase 3 intensity */ { 0x00000097, 0x0000 }, /* R151 - Haptics phase 3 duration */ - { 0x00000100, 0x0001 }, /* R256 - Clock 32k 1 */ + { 0x00000100, 0x0002 }, /* R256 - Clock 32k 1 */ { 0x00000101, 0x0304 }, /* R257 - System Clock 1 */ { 0x00000102, 0x0011 }, /* R258 - Sample rate 1 */ { 0x00000103, 0x0011 }, /* R259 - Sample rate 2 */ { 0x00000104, 0x0011 }, /* R260 - Sample rate 3 */ { 0x00000112, 0x0305 }, /* R274 - Async clock 1 */ { 0x00000113, 0x0011 }, /* R275 - Async sample rate 1 */ + { 0x00000114, 0x0011 }, /* R276 - Async sample rate 2 */ { 0x00000149, 0x0000 }, /* R329 - Output system clock */ { 0x0000014A, 0x0000 }, /* R330 - Output async clock */ { 0x00000152, 0x0000 }, /* R338 - Rate Estimator 1 */ @@ -274,13 +275,14 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000155, 0x0000 }, /* R341 - Rate Estimator 4 */ { 0x00000156, 0x0000 }, /* R342 - Rate Estimator 5 */ { 0x00000161, 0x0000 }, /* R353 - Dynamic Frequency Scaling 1 */ - { 0x00000171, 0x0000 }, /* R369 - FLL1 Control 1 */ + { 0x00000171, 0x0002 }, /* R369 - FLL1 Control 1 */ { 0x00000172, 0x0008 }, /* R370 - FLL1 Control 2 */ { 0x00000173, 0x0018 }, /* R371 - FLL1 Control 3 */ { 0x00000174, 0x007D }, /* R372 - FLL1 Control 4 */ { 0x00000175, 0x0004 }, /* R373 - FLL1 Control 5 */ { 0x00000176, 0x0000 }, /* R374 - FLL1 Control 6 */ { 0x00000177, 0x0181 }, /* R375 - FLL1 Loop Filter Test 1 */ + { 0x00000178, 0x0000 }, /* R376 - FLL1 NCO Test 0 */ { 0x00000181, 0x0000 }, /* R385 - FLL1 Synchroniser 1 */ { 0x00000182, 0x0000 }, /* R386 - FLL1 Synchroniser 2 */ { 0x00000183, 0x0000 }, /* R387 - FLL1 Synchroniser 3 */ @@ -296,6 +298,7 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000195, 0x0004 }, /* R405 - FLL2 Control 5 */ { 0x00000196, 0x0000 }, /* R406 - FLL2 Control 6 */ { 0x00000197, 0x0000 }, /* R407 - FLL2 Loop Filter Test 1 */ + { 0x00000198, 0x0000 }, /* R408 - FLL2 NCO Test 0 */ { 0x000001A1, 0x0000 }, /* R417 - FLL2 Synchroniser 1 */ { 0x000001A2, 0x0000 }, /* R418 - FLL2 Synchroniser 2 */ { 0x000001A3, 0x0000 }, /* R419 - FLL2 Synchroniser 3 */ @@ -311,8 +314,13 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000218, 0x01A6 }, /* R536 - Mic Bias Ctrl 1 */ { 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */ { 0x0000021A, 0x01A6 }, /* R538 - Mic Bias Ctrl 3 */ + { 0x00000225, 0x0400 }, /* R549 - HP Ctrl 1L */ + { 0x00000226, 0x0400 }, /* R550 - HP Ctrl 1R */ { 0x00000293, 0x0000 }, /* R659 - Accessory Detect Mode 1 */ { 0x0000029B, 0x0020 }, /* R667 - Headphone Detect 1 */ + { 0x0000029C, 0x0000 }, /* R668 - Headphone Detect 2 */ + { 0x0000029F, 0x0000 }, /* R671 - Headphone Detect Test */ + { 0x000002A2, 0x0000 }, /* R674 - Micd Clamp control */ { 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */ { 0x000002A4, 0x009F }, /* R676 - Mic Detect 2 */ { 0x000002A5, 0x0000 }, /* R677 - Mic Detect 3 */ @@ -343,53 +351,44 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000400, 0x0000 }, /* R1024 - Output Enables 1 */ { 0x00000408, 0x0000 }, /* R1032 - Output Rate 1 */ { 0x00000409, 0x0022 }, /* R1033 - Output Volume Ramp */ - { 0x00000410, 0x0080 }, /* R1040 - Output Path Config 1L */ + { 0x00000410, 0x4080 }, /* R1040 - Output Path Config 1L */ { 0x00000411, 0x0180 }, /* R1041 - DAC Digital Volume 1L */ - { 0x00000412, 0x0080 }, /* R1042 - DAC Volume Limit 1L */ + { 0x00000412, 0x0081 }, /* R1042 - DAC Volume Limit 1L */ { 0x00000413, 0x0001 }, /* R1043 - Noise Gate Select 1L */ { 0x00000414, 0x0080 }, /* R1044 - Output Path Config 1R */ { 0x00000415, 0x0180 }, /* R1045 - DAC Digital Volume 1R */ - { 0x00000416, 0x0080 }, /* R1046 - DAC Volume Limit 1R */ + { 0x00000416, 0x0081 }, /* R1046 - DAC Volume Limit 1R */ { 0x00000417, 0x0002 }, /* R1047 - Noise Gate Select 1R */ - { 0x00000418, 0x0080 }, /* R1048 - Output Path Config 2L */ + { 0x00000418, 0x4080 }, /* R1048 - Output Path Config 2L */ { 0x00000419, 0x0180 }, /* R1049 - DAC Digital Volume 2L */ - { 0x0000041A, 0x0080 }, /* R1050 - DAC Volume Limit 2L */ + { 0x0000041A, 0x0081 }, /* R1050 - DAC Volume Limit 2L */ { 0x0000041B, 0x0004 }, /* R1051 - Noise Gate Select 2L */ { 0x0000041C, 0x0080 }, /* R1052 - Output Path Config 2R */ { 0x0000041D, 0x0180 }, /* R1053 - DAC Digital Volume 2R */ - { 0x0000041E, 0x0080 }, /* R1054 - DAC Volume Limit 2R */ + { 0x0000041E, 0x0081 }, /* R1054 - DAC Volume Limit 2R */ { 0x0000041F, 0x0008 }, /* R1055 - Noise Gate Select 2R */ - { 0x00000420, 0x0080 }, /* R1056 - Output Path Config 3L */ + { 0x00000420, 0x4080 }, /* R1056 - Output Path Config 3L */ { 0x00000421, 0x0180 }, /* R1057 - DAC Digital Volume 3L */ - { 0x00000422, 0x0080 }, /* R1058 - DAC Volume Limit 3L */ + { 0x00000422, 0x0081 }, /* R1058 - DAC Volume Limit 3L */ { 0x00000423, 0x0010 }, /* R1059 - Noise Gate Select 3L */ - { 0x00000424, 0x0080 }, /* R1060 - Output Path Config 3R */ - { 0x00000425, 0x0180 }, /* R1061 - DAC Digital Volume 3R */ - { 0x00000426, 0x0080 }, /* R1062 - DAC Volume Limit 3R */ - { 0x00000428, 0x0000 }, /* R1064 - Output Path Config 4L */ + { 0x00000428, 0xC000 }, /* R1064 - Output Path Config 4L */ { 0x00000429, 0x0180 }, /* R1065 - DAC Digital Volume 4L */ - { 0x0000042A, 0x0080 }, /* R1066 - Out Volume 4L */ + { 0x0000042A, 0x0081 }, /* R1066 - Out Volume 4L */ { 0x0000042B, 0x0040 }, /* R1067 - Noise Gate Select 4L */ - { 0x0000042C, 0x0000 }, /* R1068 - Output Path Config 4R */ { 0x0000042D, 0x0180 }, /* R1069 - DAC Digital Volume 4R */ - { 0x0000042E, 0x0080 }, /* R1070 - Out Volume 4R */ + { 0x0000042E, 0x0081 }, /* R1070 - Out Volume 4R */ { 0x0000042F, 0x0080 }, /* R1071 - Noise Gate Select 4R */ { 0x00000430, 0x0000 }, /* R1072 - Output Path Config 5L */ { 0x00000431, 0x0180 }, /* R1073 - DAC Digital Volume 5L */ - { 0x00000432, 0x0080 }, /* R1074 - DAC Volume Limit 5L */ + { 0x00000432, 0x0081 }, /* R1074 - DAC Volume Limit 5L */ { 0x00000433, 0x0100 }, /* R1075 - Noise Gate Select 5L */ - { 0x00000434, 0x0000 }, /* R1076 - Output Path Config 5R */ { 0x00000435, 0x0180 }, /* R1077 - DAC Digital Volume 5R */ - { 0x00000436, 0x0080 }, /* R1078 - DAC Volume Limit 5R */ - { 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */ + { 0x00000436, 0x0081 }, /* R1078 - DAC Volume Limit 5R */ + { 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */ { 0x00000450, 0x0000 }, /* R1104 - DAC AEC Control 1 */ { 0x00000458, 0x0001 }, /* R1112 - Noise Gate Control */ { 0x00000490, 0x0069 }, /* R1168 - PDM SPK1 CTRL 1 */ { 0x00000491, 0x0000 }, /* R1169 - PDM SPK1 CTRL 2 */ - { 0x000004DC, 0x0000 }, /* R1244 - DAC comp 1 */ - { 0x000004DD, 0x0000 }, /* R1245 - DAC comp 2 */ - { 0x000004DE, 0x0000 }, /* R1246 - DAC comp 3 */ - { 0x000004DF, 0x0000 }, /* R1247 - DAC comp 4 */ { 0x00000500, 0x000C }, /* R1280 - AIF1 BCLK Ctrl */ { 0x00000501, 0x0008 }, /* R1281 - AIF1 Tx Pin Ctrl */ { 0x00000502, 0x0000 }, /* R1282 - AIF1 Rx Pin Ctrl */ @@ -417,7 +416,6 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000518, 0x0007 }, /* R1304 - AIF1 Frame Ctrl 18 */ { 0x00000519, 0x0000 }, /* R1305 - AIF1 Tx Enables */ { 0x0000051A, 0x0000 }, /* R1306 - AIF1 Rx Enables */ - { 0x0000051B, 0x0000 }, /* R1307 - AIF1 Force Write */ { 0x00000540, 0x000C }, /* R1344 - AIF2 BCLK Ctrl */ { 0x00000541, 0x0008 }, /* R1345 - AIF2 Tx Pin Ctrl */ { 0x00000542, 0x0000 }, /* R1346 - AIF2 Rx Pin Ctrl */ @@ -433,7 +431,6 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000552, 0x0001 }, /* R1362 - AIF2 Frame Ctrl 12 */ { 0x00000559, 0x0000 }, /* R1369 - AIF2 Tx Enables */ { 0x0000055A, 0x0000 }, /* R1370 - AIF2 Rx Enables */ - { 0x0000055B, 0x0000 }, /* R1371 - AIF2 Force Write */ { 0x00000580, 0x000C }, /* R1408 - AIF3 BCLK Ctrl */ { 0x00000581, 0x0008 }, /* R1409 - AIF3 Tx Pin Ctrl */ { 0x00000582, 0x0000 }, /* R1410 - AIF3 Rx Pin Ctrl */ @@ -449,7 +446,6 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000592, 0x0001 }, /* R1426 - AIF3 Frame Ctrl 12 */ { 0x00000599, 0x0000 }, /* R1433 - AIF3 Tx Enables */ { 0x0000059A, 0x0000 }, /* R1434 - AIF3 Rx Enables */ - { 0x0000059B, 0x0000 }, /* R1435 - AIF3 Force Write */ { 0x000005E3, 0x0004 }, /* R1507 - SLIMbus Framer Ref Gear */ { 0x000005E5, 0x0000 }, /* R1509 - SLIMbus Rates 1 */ { 0x000005E6, 0x0000 }, /* R1510 - SLIMbus Rates 2 */ @@ -773,22 +769,6 @@ static const struct reg_default wm5102_reg_default[] = { { 0x000008CD, 0x0080 }, /* R2253 - DRC1RMIX Input 3 Volume */ { 0x000008CE, 0x0000 }, /* R2254 - DRC1RMIX Input 4 Source */ { 0x000008CF, 0x0080 }, /* R2255 - DRC1RMIX Input 4 Volume */ - { 0x000008D0, 0x0000 }, /* R2256 - DRC2LMIX Input 1 Source */ - { 0x000008D1, 0x0080 }, /* R2257 - DRC2LMIX Input 1 Volume */ - { 0x000008D2, 0x0000 }, /* R2258 - DRC2LMIX Input 2 Source */ - { 0x000008D3, 0x0080 }, /* R2259 - DRC2LMIX Input 2 Volume */ - { 0x000008D4, 0x0000 }, /* R2260 - DRC2LMIX Input 3 Source */ - { 0x000008D5, 0x0080 }, /* R2261 - DRC2LMIX Input 3 Volume */ - { 0x000008D6, 0x0000 }, /* R2262 - DRC2LMIX Input 4 Source */ - { 0x000008D7, 0x0080 }, /* R2263 - DRC2LMIX Input 4 Volume */ - { 0x000008D8, 0x0000 }, /* R2264 - DRC2RMIX Input 1 Source */ - { 0x000008D9, 0x0080 }, /* R2265 - DRC2RMIX Input 1 Volume */ - { 0x000008DA, 0x0000 }, /* R2266 - DRC2RMIX Input 2 Source */ - { 0x000008DB, 0x0080 }, /* R2267 - DRC2RMIX Input 2 Volume */ - { 0x000008DC, 0x0000 }, /* R2268 - DRC2RMIX Input 3 Source */ - { 0x000008DD, 0x0080 }, /* R2269 - DRC2RMIX Input 3 Volume */ - { 0x000008DE, 0x0000 }, /* R2270 - DRC2RMIX Input 4 Source */ - { 0x000008DF, 0x0080 }, /* R2271 - DRC2RMIX Input 4 Volume */ { 0x00000900, 0x0000 }, /* R2304 - HPLP1MIX Input 1 Source */ { 0x00000901, 0x0080 }, /* R2305 - HPLP1MIX Input 1 Volume */ { 0x00000902, 0x0000 }, /* R2306 - HPLP1MIX Input 2 Source */ @@ -880,7 +860,7 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000D1B, 0xFFFF }, /* R3355 - IRQ2 Status 4 Mask */ { 0x00000D1C, 0xFFFF }, /* R3356 - IRQ2 Status 5 Mask */ { 0x00000D1F, 0x0000 }, /* R3359 - IRQ2 Control */ - { 0x00000D41, 0x0000 }, /* R3393 - ADSP2 IRQ0 */ + { 0x00000D50, 0x0000 }, /* R3408 - AOD wkup and trig */ { 0x00000D53, 0xFFFF }, /* R3411 - AOD IRQ Mask IRQ1 */ { 0x00000D54, 0xFFFF }, /* R3412 - AOD IRQ Mask IRQ2 */ { 0x00000D56, 0x0000 }, /* R3414 - Jack detect debounce */ @@ -975,11 +955,6 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000E82, 0x0018 }, /* R3714 - DRC1 ctrl3 */ { 0x00000E83, 0x0000 }, /* R3715 - DRC1 ctrl4 */ { 0x00000E84, 0x0000 }, /* R3716 - DRC1 ctrl5 */ - { 0x00000E89, 0x0018 }, /* R3721 - DRC2 ctrl1 */ - { 0x00000E8A, 0x0933 }, /* R3722 - DRC2 ctrl2 */ - { 0x00000E8B, 0x0018 }, /* R3723 - DRC2 ctrl3 */ - { 0x00000E8C, 0x0000 }, /* R3724 - DRC2 ctrl4 */ - { 0x00000E8D, 0x0000 }, /* R3725 - DRC2 ctrl5 */ { 0x00000EC0, 0x0000 }, /* R3776 - HPLPF1_1 */ { 0x00000EC1, 0x0000 }, /* R3777 - HPLPF1_2 */ { 0x00000EC4, 0x0000 }, /* R3780 - HPLPF2_1 */ @@ -990,16 +965,12 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000ECD, 0x0000 }, /* R3789 - HPLPF4_2 */ { 0x00000EE0, 0x0000 }, /* R3808 - ASRC_ENABLE */ { 0x00000EE2, 0x0000 }, /* R3810 - ASRC_RATE1 */ - { 0x00000EE3, 0x4000 }, /* R3811 - ASRC_RATE2 */ { 0x00000EF0, 0x0000 }, /* R3824 - ISRC 1 CTRL 1 */ { 0x00000EF1, 0x0000 }, /* R3825 - ISRC 1 CTRL 2 */ { 0x00000EF2, 0x0000 }, /* R3826 - ISRC 1 CTRL 3 */ { 0x00000EF3, 0x0000 }, /* R3827 - ISRC 2 CTRL 1 */ { 0x00000EF4, 0x0000 }, /* R3828 - ISRC 2 CTRL 2 */ { 0x00000EF5, 0x0000 }, /* R3829 - ISRC 2 CTRL 3 */ - { 0x00000EF6, 0x0000 }, /* R3830 - ISRC 3 CTRL 1 */ - { 0x00000EF7, 0x0000 }, /* R3831 - ISRC 3 CTRL 2 */ - { 0x00000EF8, 0x0000 }, /* R3832 - ISRC 3 CTRL 3 */ { 0x00001100, 0x0010 }, /* R4352 - DSP1 Control 1 */ { 0x00001101, 0x0000 }, /* R4353 - DSP1 Clocking 1 */ }; -- cgit v1.2.3 From dc781d0e10fca29123ddde45429d777725c0fde5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 27 Jan 2013 12:07:32 +0800 Subject: mfd: arizona: Disable interrupts during resume Runtime power management does not function during system suspend but the Arizona devices need to use runtime power management to power up the device in order to handle interrupts. Try to avoid interrupts firing during resume by disabling the primary IRQ before interrupts are reenabled on resume and only reenabling it again during main resume. The goal is to avoid issues in the situation where an interrupt is asserted during resume (eg, due to it being the wake source) and the interrupt handling gets scheduled prior to the device being able to handle runtime PM. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/arizona-core.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 1ab02a7d2b05..9c994b69b063 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -263,10 +263,36 @@ static int arizona_runtime_suspend(struct device *dev) } #endif +#ifdef CONFIG_PM_SLEEP +static int arizona_resume_noirq(struct device *dev) +{ + struct arizona *arizona = dev_get_drvdata(dev); + + dev_dbg(arizona->dev, "Early resume, disabling IRQ\n"); + disable_irq(arizona->irq); + + return 0; +} + +static int arizona_resume(struct device *dev) +{ + struct arizona *arizona = dev_get_drvdata(dev); + + dev_dbg(arizona->dev, "Late resume, reenabling IRQ\n"); + enable_irq(arizona->irq); + + return 0; +} +#endif + const struct dev_pm_ops arizona_pm_ops = { SET_RUNTIME_PM_OPS(arizona_runtime_suspend, arizona_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(NULL, arizona_resume) +#ifdef CONFIG_PM_SLEEP + .resume_noirq = arizona_resume_noirq, +#endif }; EXPORT_SYMBOL_GPL(arizona_pm_ops); -- cgit v1.2.3 From 648a98808c6319dde03b64550dc64a61aaccc2b4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 28 Jan 2013 00:32:53 +0800 Subject: mfd: arizona: Clarify mixer underclocking error If the mixer is underclocked it will drop a sample so log that error more directly. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/arizona-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 9c994b69b063..317ba0a4ea25 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -115,7 +115,7 @@ static irqreturn_t arizona_underclocked(int irq, void *data) if (val & ARIZONA_ADC_UNDERCLOCKED_STS) dev_err(arizona->dev, "ADC underclocked\n"); if (val & ARIZONA_MIXER_UNDERCLOCKED_STS) - dev_err(arizona->dev, "Mixer underclocked\n"); + dev_err(arizona->dev, "Mixer dropped sample\n"); return IRQ_HANDLED; } -- cgit v1.2.3 From 3d91f8282c66d9edafa3980385324ce6a48edcda Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 29 Jan 2013 00:47:37 +0800 Subject: mfd: arizona: Provide platform data for MICBIAS configuration Allow the MICBIAS voltages and other attributes to be configured by the platform. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/arizona-core.c | 23 +++++++++++++++++++++++ include/linux/mfd/arizona/pdata.h | 12 ++++++++++++ 2 files changed, 35 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 317ba0a4ea25..b562c7bf8a46 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -510,6 +510,29 @@ int arizona_dev_init(struct arizona *arizona) goto err_reset; } + for (i = 0; i < ARIZONA_MAX_MICBIAS; i++) { + if (!arizona->pdata.micbias[i].mV) + continue; + + val = (arizona->pdata.micbias[i].mV - 1500) / 100; + val <<= ARIZONA_MICB1_LVL_SHIFT; + + if (arizona->pdata.micbias[i].ext_cap) + val |= ARIZONA_MICB1_EXT_CAP; + + if (arizona->pdata.micbias[i].discharge) + val |= ARIZONA_MICB1_DISCH; + + if (arizona->pdata.micbias[i].fast_start) + val |= ARIZONA_MICB1_RATE; + + regmap_update_bits(arizona->regmap, + ARIZONA_MIC_BIAS_CTRL_1 + i, + ARIZONA_MICB1_LVL_MASK | + ARIZONA_MICB1_DISCH | + ARIZONA_MICB1_RATE, val); + } + for (i = 0; i < ARIZONA_MAX_INPUT; i++) { /* Default for both is 0 so noop with defaults */ val = arizona->pdata.dmic_ref[i] diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 8b1d1daaae16..37894d6a4f6f 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -56,6 +56,8 @@ #define ARIZONA_DMIC_MICBIAS2 2 #define ARIZONA_DMIC_MICBIAS3 3 +#define ARIZONA_MAX_MICBIAS 3 + #define ARIZONA_INMODE_DIFF 0 #define ARIZONA_INMODE_SE 1 #define ARIZONA_INMODE_DMIC 2 @@ -69,6 +71,13 @@ struct regulator_init_data; +struct arizona_micbias { + int mV; /** Regulated voltage */ + unsigned int ext_cap:1; /** External capacitor fitted */ + unsigned int discharge:1; /** Actively discharge */ + unsigned int fast_start:1; /** Enable aggressive startup ramp rate */ +}; + struct arizona_micd_config { unsigned int src; unsigned int bias; @@ -106,6 +115,9 @@ struct arizona_pdata { /** Reference voltage for DMIC inputs */ int dmic_ref[ARIZONA_MAX_INPUT]; + /** MICBIAS configurations */ + struct arizona_micbias micbias[ARIZONA_MAX_MICBIAS]; + /** Mode of input structures */ int inmode[ARIZONA_MAX_INPUT]; -- cgit v1.2.3 From 5d4e9bd79a5ab5bd4695d3becaa71da447a76a94 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:50 +0100 Subject: mfd: twl-core: Clean up module id lookup and definitions Use enums for all module definitions: twl_module_ids for common functionality among twl4030/twl6030 twl4030_module_ids for twl4030 specific ids twl6030_module_ids for twl6030 specific ids In this way the list can be managed easier when new functionality going to be implemented. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 105 +++++++++++++++++++++++------------------------- include/linux/i2c/twl.h | 66 ++++++++++++++++-------------- 2 files changed, 86 insertions(+), 85 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 4f3baadd0038..b781cdd0629d 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -74,8 +74,6 @@ #define SUB_CHIP_ID3 3 #define SUB_CHIP_ID_INVAL 0xff -#define TWL_MODULE_LAST TWL4030_MODULE_LAST - /* Base Address defns for twl4030_map[] */ /* subchip/slave 0 - USB ID */ @@ -94,10 +92,7 @@ #define TWL4030_BASEADD_MADC 0x0000 #define TWL4030_BASEADD_MAIN_CHARGE 0x0074 #define TWL4030_BASEADD_PRECHARGE 0x00AA -#define TWL4030_BASEADD_PWM0 0x00F8 -#define TWL4030_BASEADD_PWM1 0x00FB -#define TWL4030_BASEADD_PWMA 0x00EF -#define TWL4030_BASEADD_PWMB 0x00F1 +#define TWL4030_BASEADD_PWM 0x00F8 #define TWL4030_BASEADD_KEYPAD 0x00D2 #define TWL5031_BASEADD_ACCESSORY 0x0074 /* Replaces Main Charge */ @@ -117,7 +112,7 @@ /* subchip/slave 0 0x48 - POWER */ #define TWL6030_BASEADD_RTC 0x0000 -#define TWL6030_BASEADD_MEM 0x0017 +#define TWL6030_BASEADD_SECURED_REG 0x0017 #define TWL6030_BASEADD_PM_MASTER 0x001F #define TWL6030_BASEADD_PM_SLAVE_MISC 0x0030 /* PM_RECEIVER */ #define TWL6030_BASEADD_PM_MISC 0x00E2 @@ -132,6 +127,7 @@ #define TWL6030_BASEADD_PIH 0x00D0 #define TWL6030_BASEADD_CHARGER 0x00E0 #define TWL6025_BASEADD_CHARGER 0x00DA +#define TWL6030_BASEADD_LED 0x00F4 /* subchip/slave 2 0x4A - DFT */ #define TWL6030_BASEADD_DIEID 0x00C0 @@ -188,34 +184,33 @@ static struct twl_mapping twl4030_map[] = { * so they continue to match the order in this table. */ + /* Common IPs */ { 0, TWL4030_BASEADD_USB }, + { 1, TWL4030_BASEADD_PIH }, + { 2, TWL4030_BASEADD_MAIN_CHARGE }, + { 3, TWL4030_BASEADD_PM_MASTER }, + { 3, TWL4030_BASEADD_PM_RECEIVER }, + + { 3, TWL4030_BASEADD_RTC }, + { 2, TWL4030_BASEADD_PWM }, + { 2, TWL4030_BASEADD_LED }, + { 3, TWL4030_BASEADD_SECURED_REG }, + + /* TWL4030 specific IPs */ { 1, TWL4030_BASEADD_AUDIO_VOICE }, { 1, TWL4030_BASEADD_GPIO }, { 1, TWL4030_BASEADD_INTBR }, - { 1, TWL4030_BASEADD_PIH }, - { 1, TWL4030_BASEADD_TEST }, { 2, TWL4030_BASEADD_KEYPAD }, + { 2, TWL4030_BASEADD_MADC }, { 2, TWL4030_BASEADD_INTERRUPTS }, - { 2, TWL4030_BASEADD_LED }, - - { 2, TWL4030_BASEADD_MAIN_CHARGE }, { 2, TWL4030_BASEADD_PRECHARGE }, - { 2, TWL4030_BASEADD_PWM0 }, - { 2, TWL4030_BASEADD_PWM1 }, - { 2, TWL4030_BASEADD_PWMA }, - - { 2, TWL4030_BASEADD_PWMB }, - { 2, TWL5031_BASEADD_ACCESSORY }, - { 2, TWL5031_BASEADD_INTERRUPTS }, { 3, TWL4030_BASEADD_BACKUP }, { 3, TWL4030_BASEADD_INT }, - { 3, TWL4030_BASEADD_PM_MASTER }, - { 3, TWL4030_BASEADD_PM_RECEIVER }, - { 3, TWL4030_BASEADD_RTC }, - { 3, TWL4030_BASEADD_SECURED_REG }, + { 2, TWL5031_BASEADD_ACCESSORY }, + { 2, TWL5031_BASEADD_INTERRUPTS }, }; static struct regmap_config twl4030_regmap_config[4] = { @@ -251,35 +246,25 @@ static struct twl_mapping twl6030_map[] = { * defines for TWL4030_MODULE_* * so they continue to match the order in this table. */ - { SUB_CHIP_ID1, TWL6030_BASEADD_USB }, - { SUB_CHIP_ID_INVAL, TWL6030_BASEADD_AUDIO }, - { SUB_CHIP_ID2, TWL6030_BASEADD_DIEID }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - { SUB_CHIP_ID1, TWL6030_BASEADD_PIH }, - - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - { SUB_CHIP_ID1, TWL6030_BASEADD_GPADC_CTRL }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - - { SUB_CHIP_ID1, TWL6030_BASEADD_CHARGER }, - { SUB_CHIP_ID1, TWL6030_BASEADD_GASGAUGE }, - { SUB_CHIP_ID1, TWL6030_BASEADD_PWM }, - { SUB_CHIP_ID0, TWL6030_BASEADD_ZERO }, - { SUB_CHIP_ID1, TWL6030_BASEADD_ZERO }, - - { SUB_CHIP_ID2, TWL6030_BASEADD_ZERO }, - { SUB_CHIP_ID2, TWL6030_BASEADD_ZERO }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - - { SUB_CHIP_ID0, TWL6030_BASEADD_PM_MASTER }, - { SUB_CHIP_ID0, TWL6030_BASEADD_PM_SLAVE_MISC }, - { SUB_CHIP_ID0, TWL6030_BASEADD_RTC }, - { SUB_CHIP_ID0, TWL6030_BASEADD_MEM }, - { SUB_CHIP_ID1, TWL6025_BASEADD_CHARGER }, + + /* Common IPs */ + { 1, TWL6030_BASEADD_USB }, + { 1, TWL6030_BASEADD_PIH }, + { 1, TWL6030_BASEADD_CHARGER }, + { 0, TWL6030_BASEADD_PM_MASTER }, + { 0, TWL6030_BASEADD_PM_SLAVE_MISC }, + + { 0, TWL6030_BASEADD_RTC }, + { 1, TWL6030_BASEADD_PWM }, + { 1, TWL6030_BASEADD_LED }, + { 0, TWL6030_BASEADD_SECURED_REG }, + + /* TWL6030 specific IPs */ + { 0, TWL6030_BASEADD_ZERO }, + { 1, TWL6030_BASEADD_ZERO }, + { 2, TWL6030_BASEADD_ZERO }, + { 1, TWL6030_BASEADD_GPADC_CTRL }, + { 1, TWL6030_BASEADD_GASGAUGE }, }; static struct regmap_config twl6030_regmap_config[3] = { @@ -305,6 +290,14 @@ static struct regmap_config twl6030_regmap_config[3] = { /*----------------------------------------------------------------------*/ +static inline int twl_get_last_module(void) +{ + if (twl_class_is_4030()) + return TWL4030_MODULE_LAST; + else + return TWL6030_MODULE_LAST; +} + /* Exported Functions */ /** @@ -325,7 +318,7 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) int sid; struct twl_client *twl; - if (unlikely(mod_no >= TWL_MODULE_LAST)) { + if (unlikely(mod_no >= twl_get_last_module())) { pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } @@ -367,7 +360,7 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) int sid; struct twl_client *twl; - if (unlikely(mod_no >= TWL_MODULE_LAST)) { + if (unlikely(mod_no >= twl_get_last_module())) { pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } @@ -1228,6 +1221,10 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) if ((id->driver_data) & TWL6030_CLASS) { twl_id = TWL6030_CLASS_ID; twl_map = &twl6030_map[0]; + /* The charger base address is different in twl6025 */ + if ((id->driver_data) & TWL6025_SUBCLASS) + twl_map[TWL_MODULE_MAIN_CHARGE].base = + TWL6025_BASEADD_CHARGER; twl_regmap_config = twl6030_regmap_config; num_slaves = TWL_NUM_SLAVES - 1; } else { diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 1ff54b114efc..72adc8807912 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -39,51 +39,55 @@ * address each module uses within a given i2c slave. */ +/* Module IDs for similar functionalities found in twl4030/twl6030 */ +enum twl_module_ids { + TWL_MODULE_USB, + TWL_MODULE_PIH, + TWL_MODULE_MAIN_CHARGE, + TWL_MODULE_PM_MASTER, + TWL_MODULE_PM_RECEIVER, + + TWL_MODULE_RTC, + TWL_MODULE_PWM, + TWL_MODULE_LED, + TWL_MODULE_SECURED_REG, + + TWL_MODULE_LAST, +}; + +/* Modules only available in twl4030 series */ enum twl4030_module_ids { - TWL4030_MODULE_USB = 0, /* Slave 0 (i2c address 0x48) */ - TWL4030_MODULE_AUDIO_VOICE, /* Slave 1 (i2c address 0x49) */ + TWL4030_MODULE_AUDIO_VOICE = TWL_MODULE_LAST, TWL4030_MODULE_GPIO, TWL4030_MODULE_INTBR, - TWL4030_MODULE_PIH, - TWL4030_MODULE_TEST, - TWL4030_MODULE_KEYPAD, /* Slave 2 (i2c address 0x4a) */ + TWL4030_MODULE_KEYPAD, + TWL4030_MODULE_MADC, TWL4030_MODULE_INTERRUPTS, - TWL4030_MODULE_LED, - - TWL4030_MODULE_MAIN_CHARGE, TWL4030_MODULE_PRECHARGE, - TWL4030_MODULE_PWM0, - TWL4030_MODULE_PWM1, - TWL4030_MODULE_PWMA, + TWL4030_MODULE_BACKUP, + TWL4030_MODULE_INT, - TWL4030_MODULE_PWMB, TWL5031_MODULE_ACCESSORY, TWL5031_MODULE_INTERRUPTS, - TWL4030_MODULE_BACKUP, /* Slave 3 (i2c address 0x4b) */ - TWL4030_MODULE_INT, - TWL4030_MODULE_PM_MASTER, - TWL4030_MODULE_PM_RECEIVER, - TWL4030_MODULE_RTC, - TWL4030_MODULE_SECURED_REG, TWL4030_MODULE_LAST, }; -/* Similar functionalities implemented in TWL4030/6030 */ -#define TWL_MODULE_USB TWL4030_MODULE_USB -#define TWL_MODULE_PIH TWL4030_MODULE_PIH -#define TWL_MODULE_MAIN_CHARGE TWL4030_MODULE_MAIN_CHARGE -#define TWL_MODULE_PM_MASTER TWL4030_MODULE_PM_MASTER -#define TWL_MODULE_PM_RECEIVER TWL4030_MODULE_PM_RECEIVER -#define TWL_MODULE_RTC TWL4030_MODULE_RTC -#define TWL_MODULE_PWM TWL4030_MODULE_PWM0 -#define TWL_MODULE_LED TWL4030_MODULE_LED - -#define TWL6030_MODULE_ID0 13 -#define TWL6030_MODULE_ID1 14 -#define TWL6030_MODULE_ID2 15 +/* Modules only available in twl6030 series */ +enum twl6030_module_ids { + TWL6030_MODULE_ID0 = TWL_MODULE_LAST, + TWL6030_MODULE_ID1, + TWL6030_MODULE_ID2, + TWL6030_MODULE_GPADC, + TWL6030_MODULE_GASGAUGE, + + TWL6030_MODULE_LAST, +}; + +/* Until the clients has been converted to use TWL_MODULE_LED */ +#define TWL4030_MODULE_LED TWL_MODULE_LED #define GPIO_INTR_OFFSET 0 #define KEYPAD_INTR_OFFSET 1 -- cgit v1.2.3 From 050cde1363a3fc59f2a2f1cac6ef050393a00c99 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:51 +0100 Subject: mfd: twl-core: No need to check for invalid subchip ID The module id table no longer can have invalid/unused entries. No need for checking the ID for validity. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index b781cdd0629d..fa1a5a0fa759 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -72,7 +72,6 @@ #define SUB_CHIP_ID1 1 #define SUB_CHIP_ID2 2 #define SUB_CHIP_ID3 3 -#define SUB_CHIP_ID_INVAL 0xff /* Base Address defns for twl4030_map[] */ @@ -326,12 +325,8 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) pr_err("%s: not initialized\n", DRIVER_NAME); return -EPERM; } + sid = twl_map[mod_no].sid; - if (unlikely(sid == SUB_CHIP_ID_INVAL)) { - pr_err("%s: module %d is not part of the pmic\n", - DRIVER_NAME, mod_no); - return -EINVAL; - } twl = &twl_modules[sid]; ret = regmap_bulk_write(twl->regmap, twl_map[mod_no].base + reg, @@ -368,12 +363,8 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) pr_err("%s: not initialized\n", DRIVER_NAME); return -EPERM; } + sid = twl_map[mod_no].sid; - if (unlikely(sid == SUB_CHIP_ID_INVAL)) { - pr_err("%s: module %d is not part of the pmic\n", - DRIVER_NAME, mod_no); - return -EINVAL; - } twl = &twl_modules[sid]; ret = regmap_bulk_read(twl->regmap, twl_map[mod_no].base + reg, -- cgit v1.2.3 From 3c3302794cc79b363779a762051ebe8670812791 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:52 +0100 Subject: mfd: twl-core: Use the lookup table to find the correct subchip for the modules Instead of using SUB_CHIP_ID* or magic numbers use the twl_mapping table to look for the subchip ID. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 56 ++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index fa1a5a0fa759..f07317b35e4a 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -68,11 +68,6 @@ #define TWL_NUM_SLAVES 4 -#define SUB_CHIP_ID0 0 -#define SUB_CHIP_ID1 1 -#define SUB_CHIP_ID2 2 -#define SUB_CHIP_ID3 3 - /* Base Address defns for twl4030_map[] */ /* subchip/slave 0 - USB ID */ @@ -493,13 +488,20 @@ int twl_get_hfclk_rate(void) EXPORT_SYMBOL_GPL(twl_get_hfclk_rate); static struct device * -add_numbered_child(unsigned chip, const char *name, int num, +add_numbered_child(unsigned mod_no, const char *name, int num, void *pdata, unsigned pdata_len, bool can_wakeup, int irq0, int irq1) { struct platform_device *pdev; - struct twl_client *twl = &twl_modules[chip]; - int status; + struct twl_client *twl; + int status, sid; + + if (unlikely(mod_no >= twl_get_last_module())) { + pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); + return ERR_PTR(-EPERM); + } + sid = twl_map[mod_no].sid; + twl = &twl_modules[sid]; pdev = platform_device_alloc(name, num); if (!pdev) { @@ -544,11 +546,11 @@ err: return &pdev->dev; } -static inline struct device *add_child(unsigned chip, const char *name, +static inline struct device *add_child(unsigned mod_no, const char *name, void *pdata, unsigned pdata_len, bool can_wakeup, int irq0, int irq1) { - return add_numbered_child(chip, name, -1, pdata, pdata_len, + return add_numbered_child(mod_no, name, -1, pdata, pdata_len, can_wakeup, irq0, irq1); } @@ -557,7 +559,6 @@ add_regulator_linked(int num, struct regulator_init_data *pdata, struct regulator_consumer_supply *consumers, unsigned num_consumers, unsigned long features) { - unsigned sub_chip_id; struct twl_regulator_driver_data drv_data; /* regulator framework demands init_data ... */ @@ -584,8 +585,7 @@ add_regulator_linked(int num, struct regulator_init_data *pdata, } /* NOTE: we currently ignore regulator IRQs, e.g. for short circuits */ - sub_chip_id = twl_map[TWL_MODULE_PM_MASTER].sid; - return add_numbered_child(sub_chip_id, "twl_reg", num, + return add_numbered_child(TWL_MODULE_PM_MASTER, "twl_reg", num, pdata, sizeof(*pdata), false, 0, 0); } @@ -607,10 +607,9 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, unsigned long features) { struct device *child; - unsigned sub_chip_id; if (IS_ENABLED(CONFIG_GPIO_TWL4030) && pdata->gpio) { - child = add_child(SUB_CHIP_ID1, "twl4030_gpio", + child = add_child(TWL4030_MODULE_GPIO, "twl4030_gpio", pdata->gpio, sizeof(*pdata->gpio), false, irq_base + GPIO_INTR_OFFSET, 0); if (IS_ERR(child)) @@ -618,7 +617,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, } if (IS_ENABLED(CONFIG_KEYBOARD_TWL4030) && pdata->keypad) { - child = add_child(SUB_CHIP_ID2, "twl4030_keypad", + child = add_child(TWL4030_MODULE_KEYPAD, "twl4030_keypad", pdata->keypad, sizeof(*pdata->keypad), true, irq_base + KEYPAD_INTR_OFFSET, 0); if (IS_ERR(child)) @@ -627,7 +626,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, if (IS_ENABLED(CONFIG_TWL4030_MADC) && pdata->madc && twl_class_is_4030()) { - child = add_child(SUB_CHIP_ID2, "twl4030_madc", + child = add_child(TWL4030_MODULE_MADC, "twl4030_madc", pdata->madc, sizeof(*pdata->madc), true, irq_base + MADC_INTR_OFFSET, 0); if (IS_ERR(child)) @@ -642,22 +641,21 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, * Eventually, Linux might become more aware of such * HW security concerns, and "least privilege". */ - sub_chip_id = twl_map[TWL_MODULE_RTC].sid; - child = add_child(sub_chip_id, "twl_rtc", NULL, 0, + child = add_child(TWL_MODULE_RTC, "twl_rtc", NULL, 0, true, irq_base + RTC_INTR_OFFSET, 0); if (IS_ERR(child)) return PTR_ERR(child); } if (IS_ENABLED(CONFIG_PWM_TWL)) { - child = add_child(SUB_CHIP_ID1, "twl-pwm", NULL, 0, + child = add_child(TWL_MODULE_PWM, "twl-pwm", NULL, 0, false, 0, 0); if (IS_ERR(child)) return PTR_ERR(child); } if (IS_ENABLED(CONFIG_PWM_TWL_LED)) { - child = add_child(SUB_CHIP_ID1, "twl-pwmled", NULL, 0, + child = add_child(TWL_MODULE_LED, "twl-pwmled", NULL, 0, false, 0, 0); if (IS_ERR(child)) return PTR_ERR(child); @@ -709,7 +707,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, } - child = add_child(SUB_CHIP_ID0, "twl4030_usb", + child = add_child(TWL_MODULE_USB, "twl4030_usb", pdata->usb, sizeof(*pdata->usb), true, /* irq0 = USB_PRES, irq1 = USB */ irq_base + USB_PRES_INTR_OFFSET, @@ -758,7 +756,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, pdata->usb->features = features; - child = add_child(SUB_CHIP_ID0, "twl6030_usb", + child = add_child(TWL_MODULE_USB, "twl6030_usb", pdata->usb, sizeof(*pdata->usb), true, /* irq1 = VBUS_PRES, irq0 = USB ID */ irq_base + USBOTG_INTR_OFFSET, @@ -783,22 +781,22 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, } if (IS_ENABLED(CONFIG_TWL4030_WATCHDOG) && twl_class_is_4030()) { - child = add_child(SUB_CHIP_ID3, "twl4030_wdt", NULL, 0, - false, 0, 0); + child = add_child(TWL_MODULE_PM_RECEIVER, "twl4030_wdt", NULL, + 0, false, 0, 0); if (IS_ERR(child)) return PTR_ERR(child); } if (IS_ENABLED(CONFIG_INPUT_TWL4030_PWRBUTTON) && twl_class_is_4030()) { - child = add_child(SUB_CHIP_ID3, "twl4030_pwrbutton", NULL, 0, - true, irq_base + 8 + 0, 0); + child = add_child(TWL_MODULE_PM_MASTER, "twl4030_pwrbutton", + NULL, 0, true, irq_base + 8 + 0, 0); if (IS_ERR(child)) return PTR_ERR(child); } if (IS_ENABLED(CONFIG_MFD_TWL4030_AUDIO) && pdata->audio && twl_class_is_4030()) { - child = add_child(SUB_CHIP_ID1, "twl4030-audio", + child = add_child(TWL4030_MODULE_AUDIO_VOICE, "twl4030-audio", pdata->audio, sizeof(*pdata->audio), false, 0, 0); if (IS_ERR(child)) @@ -1038,7 +1036,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, if (IS_ENABLED(CONFIG_CHARGER_TWL4030) && pdata->bci && !(features & (TPS_SUBSET | TWL5031))) { - child = add_child(SUB_CHIP_ID3, "twl4030_bci", + child = add_child(TWL_MODULE_MAIN_CHARGE, "twl4030_bci", pdata->bci, sizeof(*pdata->bci), false, /* irq0 = CHG_PRES, irq1 = BCI */ irq_base + BCI_PRES_INTR_OFFSET, -- cgit v1.2.3 From 6dd810b5e6fa688010dcb6d386c61589e850aaaa Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:53 +0100 Subject: mfd: twl-core: Allocate twl_modules dynamically At boot time we can allocate the twl_modules array dynamically based on the twl class we are using with devm_kzalloc() instead of the static twl_modules[] array. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index f07317b35e4a..fbff8301dfca 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -66,8 +66,6 @@ /* Triton Core internal information (BEGIN) */ -#define TWL_NUM_SLAVES 4 - /* Base Address defns for twl4030_map[] */ /* subchip/slave 0 - USB ID */ @@ -162,7 +160,7 @@ struct twl_client { struct regmap *regmap; }; -static struct twl_client twl_modules[TWL_NUM_SLAVES]; +static struct twl_client *twl_modules; /* mapping the module id to slave id and base address */ struct twl_mapping { @@ -284,6 +282,14 @@ static struct regmap_config twl6030_regmap_config[3] = { /*----------------------------------------------------------------------*/ +static inline int twl_get_num_slaves(void) +{ + if (twl_class_is_4030()) + return 4; /* TWL4030 class have four slave address */ + else + return 3; /* TWL6030 class have three slave address */ +} + static inline int twl_get_last_module(void) { if (twl_class_is_4030()) @@ -1127,17 +1133,15 @@ static int twl_remove(struct i2c_client *client) unsigned i, num_slaves; int status; - if (twl_class_is_4030()) { + if (twl_class_is_4030()) status = twl4030_exit_irq(); - num_slaves = TWL_NUM_SLAVES; - } else { + else status = twl6030_exit_irq(); - num_slaves = TWL_NUM_SLAVES - 1; - } if (status < 0) return status; + num_slaves = twl_get_num_slaves(); for (i = 0; i < num_slaves; i++) { struct twl_client *twl = &twl_modules[i]; @@ -1215,12 +1219,19 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) twl_map[TWL_MODULE_MAIN_CHARGE].base = TWL6025_BASEADD_CHARGER; twl_regmap_config = twl6030_regmap_config; - num_slaves = TWL_NUM_SLAVES - 1; } else { twl_id = TWL4030_CLASS_ID; twl_map = &twl4030_map[0]; twl_regmap_config = twl4030_regmap_config; - num_slaves = TWL_NUM_SLAVES; + } + + num_slaves = twl_get_num_slaves(); + twl_modules = devm_kzalloc(&client->dev, + sizeof(struct twl_client) * num_slaves, + GFP_KERNEL); + if (!twl_modules) { + status = -ENOMEM; + goto free; } for (i = 0; i < num_slaves; i++) { -- cgit v1.2.3 From e581238f2817d8ca8948340a2bc26dd9504bb812 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:54 +0100 Subject: mfd: twl-core: Do not try to call legacy mfd add_children() when booted with DT There is really no point to retry to add children devices in case the of_platform_populate() fails. We do not have any information provided via pdata in this case anyways. Depending on the boot type (legacy or DT) only execute either one. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index fbff8301dfca..86cca9e09380 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -1305,10 +1305,9 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1); } - status = -ENODEV; if (node) status = of_platform_populate(node, NULL, NULL, &client->dev); - if (status) + else status = add_children(pdata, irq_base, id->driver_data); fail: -- cgit v1.2.3 From 7e2e6c5758de94ec22686b30e7b906a3ddcd9896 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:55 +0100 Subject: mfd: twl-core: Do not create dummy pdata when booted with DT When booted with DT we can manage without the dummy pdata. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 86cca9e09380..547fed540ef7 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -1165,6 +1165,11 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) int status; unsigned i, num_slaves; + if (!node && !pdata) { + dev_err(&client->dev, "no platform data\n"); + return -EINVAL; + } + pdev = platform_device_alloc(DRIVER_NAME, -1); if (!pdev) { dev_err(&client->dev, "can't alloc pdev\n"); @@ -1177,28 +1182,6 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) return status; } - if (node && !pdata) { - /* - * XXX: Temporary pdata until the information is correctly - * retrieved by every TWL modules from DT. - */ - pdata = devm_kzalloc(&client->dev, - sizeof(struct twl4030_platform_data), - GFP_KERNEL); - if (!pdata) { - status = -ENOMEM; - goto free; - } - } - - if (!pdata) { - dev_dbg(&client->dev, "no platform data?\n"); - status = -EINVAL; - goto free; - } - - platform_set_drvdata(pdev, pdata); - if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) { dev_dbg(&client->dev, "can't talk I2C?\n"); status = -EIO; @@ -1264,7 +1247,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) inuse = true; /* setup clock framework */ - clocks_init(&pdev->dev, pdata->clock); + clocks_init(&pdev->dev, pdata ? pdata->clock : NULL); /* read TWL IDCODE Register */ if (twl_id == TWL4030_CLASS_ID) { @@ -1273,7 +1256,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) } /* load power event scripts */ - if (IS_ENABLED(CONFIG_TWL4030_POWER) && pdata->power) + if (IS_ENABLED(CONFIG_TWL4030_POWER) && pdata && pdata->power) twl4030_power_init(pdata->power); /* Maybe init the T2 Interrupt subsystem */ -- cgit v1.2.3 From 6382a0614144901af1cbbfdf9b9a618f5dfb8548 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:56 +0100 Subject: mfd: twl-core: Move 'inuse' check early at probe time We can fail earlier in case multiple instance of the twl-core is tried to be loaded. The twl-core by design only supports one instance. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 547fed540ef7..182708811065 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -1170,6 +1170,12 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) return -EINVAL; } + if (inuse) { + dev_dbg(&client->dev, "only one instance of %s allowed\n", + DRIVER_NAME); + return -EBUSY; + } + pdev = platform_device_alloc(DRIVER_NAME, -1); if (!pdev) { dev_err(&client->dev, "can't alloc pdev\n"); @@ -1188,12 +1194,6 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) goto free; } - if (inuse) { - dev_dbg(&client->dev, "driver is already in use\n"); - status = -EBUSY; - goto free; - } - if ((id->driver_data) & TWL6030_CLASS) { twl_id = TWL6030_CLASS_ID; twl_map = &twl6030_map[0]; -- cgit v1.2.3 From 80a97ccd33be9c79018d3d4983eefa3e6d6bfb6d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:57 +0100 Subject: mfd: twl-core: Collect global variables behind one private structure (global) Gather the global variables under a single structure and allocate it with devm_kzalloc(). It is easier to see them and if in the future we try to add support for multiple instance of twl in the system it is going to be much simpler. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 104 +++++++++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 47 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 182708811065..e2895a4026a1 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -141,33 +141,28 @@ /*----------------------------------------------------------------------*/ -/* is driver active, bound to a chip? */ -static bool inuse; - -/* TWL IDCODE Register value */ -static u32 twl_idcode; - -static unsigned int twl_id; -unsigned int twl_rev(void) -{ - return twl_id; -} -EXPORT_SYMBOL(twl_rev); - /* Structure for each TWL4030/TWL6030 Slave */ struct twl_client { struct i2c_client *client; struct regmap *regmap; }; -static struct twl_client *twl_modules; - /* mapping the module id to slave id and base address */ struct twl_mapping { unsigned char sid; /* Slave ID */ unsigned char base; /* base address */ }; -static struct twl_mapping *twl_map; + +struct twl_private { + bool ready; /* The core driver is ready to be used */ + u32 twl_idcode; /* TWL IDCODE Register value */ + unsigned int twl_id; + + struct twl_mapping *twl_map; + struct twl_client *twl_modules; +}; + +static struct twl_private *twl_priv; static struct twl_mapping twl4030_map[] = { /* @@ -300,6 +295,12 @@ static inline int twl_get_last_module(void) /* Exported Functions */ +unsigned int twl_rev(void) +{ + return twl_priv ? twl_priv->twl_id : 0; +} +EXPORT_SYMBOL(twl_rev); + /** * twl_i2c_write - Writes a n bit register in TWL4030/TWL5030/TWL60X0 * @mod_no: module number @@ -322,16 +323,17 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } - if (unlikely(!inuse)) { + if (unlikely(!twl_priv->ready)) { pr_err("%s: not initialized\n", DRIVER_NAME); return -EPERM; } - sid = twl_map[mod_no].sid; - twl = &twl_modules[sid]; + sid = twl_priv->twl_map[mod_no].sid; + twl = &twl_priv->twl_modules[sid]; - ret = regmap_bulk_write(twl->regmap, twl_map[mod_no].base + reg, - value, num_bytes); + ret = regmap_bulk_write(twl->regmap, + twl_priv->twl_map[mod_no].base + reg, value, + num_bytes); if (ret) pr_err("%s: Write failed (mod %d, reg 0x%02x count %d)\n", @@ -360,16 +362,17 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } - if (unlikely(!inuse)) { + if (unlikely(!twl_priv->ready)) { pr_err("%s: not initialized\n", DRIVER_NAME); return -EPERM; } - sid = twl_map[mod_no].sid; - twl = &twl_modules[sid]; + sid = twl_priv->twl_map[mod_no].sid; + twl = &twl_priv->twl_modules[sid]; - ret = regmap_bulk_read(twl->regmap, twl_map[mod_no].base + reg, - value, num_bytes); + ret = regmap_bulk_read(twl->regmap, + twl_priv->twl_map[mod_no].base + reg, value, + num_bytes); if (ret) pr_err("%s: Read failed (mod %d, reg 0x%02x count %d)\n", @@ -425,7 +428,7 @@ static int twl_read_idcode_register(void) goto fail; } - err = twl_i2c_read(TWL4030_MODULE_INTBR, (u8 *)(&twl_idcode), + err = twl_i2c_read(TWL4030_MODULE_INTBR, (u8 *)(&twl_priv->twl_idcode), REG_IDCODE_7_0, 4); if (err) { pr_err("TWL4030: unable to read IDCODE -%d\n", err); @@ -446,7 +449,7 @@ fail: */ int twl_get_type(void) { - return TWL_SIL_TYPE(twl_idcode); + return TWL_SIL_TYPE(twl_priv->twl_idcode); } EXPORT_SYMBOL_GPL(twl_get_type); @@ -457,7 +460,7 @@ EXPORT_SYMBOL_GPL(twl_get_type); */ int twl_get_version(void) { - return TWL_SIL_REV(twl_idcode); + return TWL_SIL_REV(twl_priv->twl_idcode); } EXPORT_SYMBOL_GPL(twl_get_version); @@ -506,8 +509,8 @@ add_numbered_child(unsigned mod_no, const char *name, int num, pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return ERR_PTR(-EPERM); } - sid = twl_map[mod_no].sid; - twl = &twl_modules[sid]; + sid = twl_priv->twl_map[mod_no].sid; + twl = &twl_priv->twl_modules[sid]; pdev = platform_device_alloc(name, num); if (!pdev) { @@ -1143,13 +1146,13 @@ static int twl_remove(struct i2c_client *client) num_slaves = twl_get_num_slaves(); for (i = 0; i < num_slaves; i++) { - struct twl_client *twl = &twl_modules[i]; + struct twl_client *twl = &twl_priv->twl_modules[i]; if (twl->client && twl->client != client) i2c_unregister_device(twl->client); - twl_modules[i].client = NULL; + twl->client = NULL; } - inuse = false; + twl_priv->ready = false; return 0; } @@ -1170,7 +1173,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) return -EINVAL; } - if (inuse) { + if (twl_priv) { dev_dbg(&client->dev, "only one instance of %s allowed\n", DRIVER_NAME); return -EBUSY; @@ -1194,31 +1197,38 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) goto free; } + twl_priv = devm_kzalloc(&client->dev, sizeof(struct twl_private), + GFP_KERNEL); + if (!twl_priv) { + status = -ENOMEM; + goto free; + } + if ((id->driver_data) & TWL6030_CLASS) { - twl_id = TWL6030_CLASS_ID; - twl_map = &twl6030_map[0]; + twl_priv->twl_id = TWL6030_CLASS_ID; + twl_priv->twl_map = &twl6030_map[0]; /* The charger base address is different in twl6025 */ if ((id->driver_data) & TWL6025_SUBCLASS) - twl_map[TWL_MODULE_MAIN_CHARGE].base = + twl_priv->twl_map[TWL_MODULE_MAIN_CHARGE].base = TWL6025_BASEADD_CHARGER; twl_regmap_config = twl6030_regmap_config; } else { - twl_id = TWL4030_CLASS_ID; - twl_map = &twl4030_map[0]; + twl_priv->twl_id = TWL4030_CLASS_ID; + twl_priv->twl_map = &twl4030_map[0]; twl_regmap_config = twl4030_regmap_config; } num_slaves = twl_get_num_slaves(); - twl_modules = devm_kzalloc(&client->dev, - sizeof(struct twl_client) * num_slaves, - GFP_KERNEL); - if (!twl_modules) { + twl_priv->twl_modules = devm_kzalloc(&client->dev, + sizeof(struct twl_client) * num_slaves, + GFP_KERNEL); + if (!twl_priv->twl_modules) { status = -ENOMEM; goto free; } for (i = 0; i < num_slaves; i++) { - struct twl_client *twl = &twl_modules[i]; + struct twl_client *twl = &twl_priv->twl_modules[i]; if (i == 0) { twl->client = client; @@ -1244,13 +1254,13 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) } } - inuse = true; + twl_priv->ready = true; /* setup clock framework */ clocks_init(&pdev->dev, pdata ? pdata->clock : NULL); /* read TWL IDCODE Register */ - if (twl_id == TWL4030_CLASS_ID) { + if (twl_class_is_4030()) { status = twl_read_idcode_register(); WARN(status < 0, "Error: reading twl_idcode register value\n"); } -- cgit v1.2.3 From ac7bc5a953c38c32513d1825decf336c4909ce3b Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:58 +0100 Subject: mfd: twl-core: Remove no longer valid comment regarding to write buffer size With the regmap conversion there is no longeer a need to allocate bigger buffer for writes Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 3 --- include/linux/i2c/twl.h | 3 --- 2 files changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index e2895a4026a1..9661204fb159 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -308,9 +308,6 @@ EXPORT_SYMBOL(twl_rev); * @reg: register address (just offset will do) * @num_bytes: number of bytes to transfer * - * IMPORTANT: for 'value' parameter: Allocate value num_bytes+1 and - * valid data starts at Offset 1. - * * Returns the result of operation - 0 is success */ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 72adc8807912..e441fd8e7c86 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -182,9 +182,6 @@ int twl_i2c_read_u8(u8 mod_no, u8 *val, u8 reg); /* * Read and write several 8-bit registers at once. - * - * IMPORTANT: For twl_i2c_write(), allocate num_bytes + 1 - * for the value, and populate your data starting at offset 1. */ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); -- cgit v1.2.3 From fbc6ae363e5e589a28135c051a2ff835e6236d5f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:59 +0100 Subject: mfd: twl-core: Move twl_i2c_read/write_u8 to header as inline function twl_i2c_read/write_u8 become as a simple wrapper over the twl_i2c_read/write. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 28 ---------------------------- include/linux/i2c/twl.h | 17 +++++++++++------ 2 files changed, 11 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 9661204fb159..557f9ee5ec18 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -379,34 +379,6 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) } EXPORT_SYMBOL(twl_i2c_read); -/** - * twl_i2c_write_u8 - Writes a 8 bit register in TWL4030/TWL5030/TWL60X0 - * @mod_no: module number - * @value: the value to be written 8 bit - * @reg: register address (just offset will do) - * - * Returns result of operation - 0 is success - */ -int twl_i2c_write_u8(u8 mod_no, u8 value, u8 reg) -{ - return twl_i2c_write(mod_no, &value, reg, 1); -} -EXPORT_SYMBOL(twl_i2c_write_u8); - -/** - * twl_i2c_read_u8 - Reads a 8 bit register from TWL4030/TWL5030/TWL60X0 - * @mod_no: module number - * @value: the value read 8 bit - * @reg: register address (just offset will do) - * - * Returns result of operation - 0 is success - */ -int twl_i2c_read_u8(u8 mod_no, u8 *value, u8 reg) -{ - return twl_i2c_read(mod_no, value, reg, 1); -} -EXPORT_SYMBOL(twl_i2c_read_u8); - /*----------------------------------------------------------------------*/ /** diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index e441fd8e7c86..488debbef895 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -174,18 +174,23 @@ static inline int twl_class_is_ ##class(void) \ TWL_CLASS_IS(4030, TWL4030_CLASS_ID) TWL_CLASS_IS(6030, TWL6030_CLASS_ID) -/* - * Read and write single 8-bit registers - */ -int twl_i2c_write_u8(u8 mod_no, u8 val, u8 reg); -int twl_i2c_read_u8(u8 mod_no, u8 *val, u8 reg); - /* * Read and write several 8-bit registers at once. */ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); +/* + * Read and write single 8-bit registers + */ +static inline int twl_i2c_write_u8(u8 mod_no, u8 val, u8 reg) { + return twl_i2c_write(mod_no, &val, reg, 1); +} + +static inline int twl_i2c_read_u8(u8 mod_no, u8 *val, u8 reg) { + return twl_i2c_read(mod_no, val, reg, 1); +} + int twl_get_type(void); int twl_get_version(void); int twl_get_hfclk_rate(void); -- cgit v1.2.3 From 01560f6bb958b821ceec98590a7147d610a62625 Mon Sep 17 00:00:00 2001 From: Aaron Sierra Date: Thu, 24 Jan 2013 14:52:39 -0600 Subject: mfd: lpc_ich: Fix gpio base and control offsets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In ICH5 and earlier the GPIOBASE and GPIOCTRL registers are found at offsets 0x58 and 0x5C, respectively. This patch allows GPIO access to properly be enabled (and disabled) for these chipsets. Signed-off-by: Agócs Pál Signed-off-by: Aaron Sierra Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/lpc_ich.c | 109 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 76 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index d9d930302e98..a0cfdf980748 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -75,8 +75,10 @@ #define ACPIBASE_GCS_OFF 0x3410 #define ACPIBASE_GCS_END 0x3414 -#define GPIOBASE 0x48 -#define GPIOCTRL 0x4C +#define GPIOBASE_ICH0 0x58 +#define GPIOCTRL_ICH0 0x5C +#define GPIOBASE_ICH6 0x48 +#define GPIOCTRL_ICH6 0x4C #define RCBABASE 0xf0 @@ -84,8 +86,17 @@ #define wdt_mem_res(i) wdt_res(ICH_RES_MEM_OFF, i) #define wdt_res(b, i) (&wdt_ich_res[(b) + (i)]) -static int lpc_ich_acpi_save = -1; -static int lpc_ich_gpio_save = -1; +struct lpc_ich_cfg { + int base; + int ctrl; + int save; +}; + +struct lpc_ich_priv { + int chipset; + struct lpc_ich_cfg acpi; + struct lpc_ich_cfg gpio; +}; static struct resource wdt_ich_res[] = { /* ACPI - TCO */ @@ -661,39 +672,44 @@ MODULE_DEVICE_TABLE(pci, lpc_ich_ids); static void lpc_ich_restore_config_space(struct pci_dev *dev) { - if (lpc_ich_acpi_save >= 0) { - pci_write_config_byte(dev, ACPICTRL, lpc_ich_acpi_save); - lpc_ich_acpi_save = -1; + struct lpc_ich_priv *priv = pci_get_drvdata(dev); + + if (priv->acpi.save >= 0) { + pci_write_config_byte(dev, priv->acpi.ctrl, priv->acpi.save); + priv->acpi.save = -1; } - if (lpc_ich_gpio_save >= 0) { - pci_write_config_byte(dev, GPIOCTRL, lpc_ich_gpio_save); - lpc_ich_gpio_save = -1; + if (priv->gpio.save >= 0) { + pci_write_config_byte(dev, priv->gpio.ctrl, priv->gpio.save); + priv->gpio.save = -1; } } static void lpc_ich_enable_acpi_space(struct pci_dev *dev) { + struct lpc_ich_priv *priv = pci_get_drvdata(dev); u8 reg_save; - pci_read_config_byte(dev, ACPICTRL, ®_save); - pci_write_config_byte(dev, ACPICTRL, reg_save | 0x10); - lpc_ich_acpi_save = reg_save; + pci_read_config_byte(dev, priv->acpi.ctrl, ®_save); + pci_write_config_byte(dev, priv->acpi.ctrl, reg_save | 0x10); + priv->acpi.save = reg_save; } static void lpc_ich_enable_gpio_space(struct pci_dev *dev) { + struct lpc_ich_priv *priv = pci_get_drvdata(dev); u8 reg_save; - pci_read_config_byte(dev, GPIOCTRL, ®_save); - pci_write_config_byte(dev, GPIOCTRL, reg_save | 0x10); - lpc_ich_gpio_save = reg_save; + pci_read_config_byte(dev, priv->gpio.ctrl, ®_save); + pci_write_config_byte(dev, priv->gpio.ctrl, reg_save | 0x10); + priv->gpio.save = reg_save; } -static void lpc_ich_finalize_cell(struct mfd_cell *cell, - const struct pci_device_id *id) +static void lpc_ich_finalize_cell(struct pci_dev *dev, struct mfd_cell *cell) { - cell->platform_data = &lpc_chipset_info[id->driver_data]; + struct lpc_ich_priv *priv = pci_get_drvdata(dev); + + cell->platform_data = &lpc_chipset_info[priv->chipset]; cell->pdata_size = sizeof(struct lpc_ich_info); } @@ -721,9 +737,9 @@ static int lpc_ich_check_conflict_gpio(struct resource *res) return use_gpio ? use_gpio : ret; } -static int lpc_ich_init_gpio(struct pci_dev *dev, - const struct pci_device_id *id) +static int lpc_ich_init_gpio(struct pci_dev *dev) { + struct lpc_ich_priv *priv = pci_get_drvdata(dev); u32 base_addr_cfg; u32 base_addr; int ret; @@ -731,7 +747,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev, struct resource *res; /* Setup power management base register */ - pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg); + pci_read_config_dword(dev, priv->acpi.base, &base_addr_cfg); base_addr = base_addr_cfg & 0x0000ff80; if (!base_addr) { dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n"); @@ -757,7 +773,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev, gpe0_done: /* Setup GPIO base register */ - pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg); + pci_read_config_dword(dev, priv->gpio.base, &base_addr_cfg); base_addr = base_addr_cfg & 0x0000ff80; if (!base_addr) { dev_notice(&dev->dev, "I/O space for GPIO uninitialized\n"); @@ -768,7 +784,7 @@ gpe0_done: /* Older devices provide fewer GPIO and have a smaller resource size. */ res = &gpio_ich_res[ICH_RES_GPIO]; res->start = base_addr; - switch (lpc_chipset_info[id->driver_data].gpio_version) { + switch (lpc_chipset_info[priv->chipset].gpio_version) { case ICH_V5_GPIO: case ICH_V10CORP_GPIO: res->end = res->start + 128 - 1; @@ -784,10 +800,10 @@ gpe0_done: acpi_conflict = true; goto gpio_done; } - lpc_chipset_info[id->driver_data].use_gpio = ret; + lpc_chipset_info[priv->chipset].use_gpio = ret; lpc_ich_enable_gpio_space(dev); - lpc_ich_finalize_cell(&lpc_ich_cells[LPC_GPIO], id); + lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_GPIO]); ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_GPIO], 1, NULL, 0, NULL); @@ -798,16 +814,16 @@ gpio_done: return ret; } -static int lpc_ich_init_wdt(struct pci_dev *dev, - const struct pci_device_id *id) +static int lpc_ich_init_wdt(struct pci_dev *dev) { + struct lpc_ich_priv *priv = pci_get_drvdata(dev); u32 base_addr_cfg; u32 base_addr; int ret; struct resource *res; /* Setup power management base register */ - pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg); + pci_read_config_dword(dev, priv->acpi.base, &base_addr_cfg); base_addr = base_addr_cfg & 0x0000ff80; if (!base_addr) { dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n"); @@ -830,7 +846,7 @@ static int lpc_ich_init_wdt(struct pci_dev *dev, * we have to read RCBA from PCI Config space 0xf0 and use * it as base. GCS = RCBA + ICH6_GCS(0x3410). */ - if (lpc_chipset_info[id->driver_data].iTCO_version == 1) { + if (lpc_chipset_info[priv->chipset].iTCO_version == 1) { /* Don't register iomem for TCO ver 1 */ lpc_ich_cells[LPC_WDT].num_resources--; } else { @@ -847,7 +863,7 @@ static int lpc_ich_init_wdt(struct pci_dev *dev, res->end = base_addr + ACPIBASE_GCS_END; } - lpc_ich_finalize_cell(&lpc_ich_cells[LPC_WDT], id); + lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_WDT]); ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_WDT], 1, NULL, 0, NULL); @@ -858,14 +874,35 @@ wdt_done: static int lpc_ich_probe(struct pci_dev *dev, const struct pci_device_id *id) { + struct lpc_ich_priv *priv; int ret; bool cell_added = false; - ret = lpc_ich_init_wdt(dev, id); + priv = kmalloc(GFP_KERNEL, sizeof(struct lpc_ich_priv)); + if (!priv) + return -ENOMEM; + + priv->chipset = id->driver_data; + priv->acpi.save = -1; + priv->acpi.base = ACPIBASE; + priv->acpi.ctrl = ACPICTRL; + + priv->gpio.save = -1; + if (priv->chipset <= LPC_ICH5) { + priv->gpio.base = GPIOBASE_ICH0; + priv->gpio.ctrl = GPIOCTRL_ICH0; + } else { + priv->gpio.base = GPIOBASE_ICH6; + priv->gpio.ctrl = GPIOCTRL_ICH6; + } + + pci_set_drvdata(dev, priv); + + ret = lpc_ich_init_wdt(dev); if (!ret) cell_added = true; - ret = lpc_ich_init_gpio(dev, id); + ret = lpc_ich_init_gpio(dev); if (!ret) cell_added = true; @@ -876,6 +913,8 @@ static int lpc_ich_probe(struct pci_dev *dev, if (!cell_added) { dev_warn(&dev->dev, "No MFD cells added\n"); lpc_ich_restore_config_space(dev); + pci_set_drvdata(dev, NULL); + kfree(priv); return -ENODEV; } @@ -884,8 +923,12 @@ static int lpc_ich_probe(struct pci_dev *dev, static void lpc_ich_remove(struct pci_dev *dev) { + void *priv = pci_get_drvdata(dev); + mfd_remove_devices(&dev->dev); lpc_ich_restore_config_space(dev); + pci_set_drvdata(dev, NULL); + kfree(priv); } static struct pci_driver lpc_ich_driver = { -- cgit v1.2.3 From 6f8cfa99845f12ab98990baef739e7e93565de87 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Fri, 18 Jan 2013 12:40:12 +0100 Subject: mfd: dbx500-prcmu: Export a9wdog functions Add EXPORT_SYMBOL to db500_prcmu_*_a9wdog functions to allow usage from module. Acked-by: Lee Jones Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 268f45d42394..7a63fa86bf01 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2212,21 +2212,25 @@ int db8500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off) sleep_auto_off ? A9WDOG_AUTO_OFF_EN : A9WDOG_AUTO_OFF_DIS); } +EXPORT_SYMBOL(db8500_prcmu_config_a9wdog); int db8500_prcmu_enable_a9wdog(u8 id) { return prcmu_a9wdog(MB4H_A9WDOG_EN, id, 0, 0, 0); } +EXPORT_SYMBOL(db8500_prcmu_enable_a9wdog); int db8500_prcmu_disable_a9wdog(u8 id) { return prcmu_a9wdog(MB4H_A9WDOG_DIS, id, 0, 0, 0); } +EXPORT_SYMBOL(db8500_prcmu_disable_a9wdog); int db8500_prcmu_kick_a9wdog(u8 id) { return prcmu_a9wdog(MB4H_A9WDOG_KICK, id, 0, 0, 0); } +EXPORT_SYMBOL(db8500_prcmu_kick_a9wdog); /* * timeout is 28 bit, in ms. @@ -2244,6 +2248,7 @@ int db8500_prcmu_load_a9wdog(u8 id, u32 timeout) (u8)((timeout >> 12) & 0xff), (u8)((timeout >> 20) & 0xff)); } +EXPORT_SYMBOL(db8500_prcmu_load_a9wdog); /** * prcmu_abb_read() - Read register value(s) from the ABB. -- cgit v1.2.3 From f0e5bd412fde30de3839c8dfa93a3e19e71ee462 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Tue, 29 Jan 2013 09:57:19 +0100 Subject: watchdog: Add support for ux500_wdt watchdog This patch adds support for the ux500_wdt watchdog that is found in ST-Ericsson Ux500 platform. The driver is based on PRCMU APIs. Acked-by: Linus Walleij Acked-by: Lee Jones Acked-by: Wim Van Sebroeck Signed-off-by: Fabio Baltieri Signed-off-by: Samuel Ortiz --- drivers/watchdog/Kconfig | 12 +++ drivers/watchdog/Makefile | 1 + drivers/watchdog/ux500_wdt.c | 171 ++++++++++++++++++++++++++++++++ include/linux/platform_data/ux500_wdt.h | 19 ++++ 4 files changed, 203 insertions(+) create mode 100644 drivers/watchdog/ux500_wdt.c create mode 100644 include/linux/platform_data/ux500_wdt.h (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 7f809fd4a57f..26e1fdbddf69 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -364,6 +364,18 @@ config IMX2_WDT To compile this driver as a module, choose M here: the module will be called imx2_wdt. +config UX500_WATCHDOG + tristate "ST-Ericsson Ux500 watchdog" + depends on MFD_DB8500_PRCMU + select WATCHDOG_CORE + default y + help + Say Y here to include Watchdog timer support for the watchdog + existing in the prcmu of ST-Ericsson Ux500 series platforms. + + To compile this driver as a module, choose M here: the + module will be called ux500_wdt. + # AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 97bbdb3a4648..bec86ee6e9e3 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o +obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/ux500_wdt.c b/drivers/watchdog/ux500_wdt.c new file mode 100644 index 000000000000..a614d84121c3 --- /dev/null +++ b/drivers/watchdog/ux500_wdt.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) ST-Ericsson SA 2011-2013 + * + * License Terms: GNU General Public License v2 + * + * Author: Mathieu Poirier for ST-Ericsson + * Author: Jonas Aaberg for ST-Ericsson + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define WATCHDOG_TIMEOUT 600 /* 10 minutes */ + +#define WATCHDOG_MIN 0 +#define WATCHDOG_MAX28 268435 /* 28 bit resolution in ms == 268435.455 s */ +#define WATCHDOG_MAX32 4294967 /* 32 bit resolution in ms == 4294967.295 s */ + +static unsigned int timeout = WATCHDOG_TIMEOUT; +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. default=" + __MODULE_STRING(WATCHDOG_TIMEOUT) "."); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static int ux500_wdt_start(struct watchdog_device *wdd) +{ + return prcmu_enable_a9wdog(PRCMU_WDOG_ALL); +} + +static int ux500_wdt_stop(struct watchdog_device *wdd) +{ + return prcmu_disable_a9wdog(PRCMU_WDOG_ALL); +} + +static int ux500_wdt_keepalive(struct watchdog_device *wdd) +{ + return prcmu_kick_a9wdog(PRCMU_WDOG_ALL); +} + +static int ux500_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + ux500_wdt_stop(wdd); + prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); + ux500_wdt_start(wdd); + + return 0; +} + +static const struct watchdog_info ux500_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "Ux500 WDT", + .firmware_version = 1, +}; + +static const struct watchdog_ops ux500_wdt_ops = { + .owner = THIS_MODULE, + .start = ux500_wdt_start, + .stop = ux500_wdt_stop, + .ping = ux500_wdt_keepalive, + .set_timeout = ux500_wdt_set_timeout, +}; + +static struct watchdog_device ux500_wdt = { + .info = &ux500_wdt_info, + .ops = &ux500_wdt_ops, + .min_timeout = WATCHDOG_MIN, + .max_timeout = WATCHDOG_MAX32, +}; + +static int ux500_wdt_probe(struct platform_device *pdev) +{ + int ret; + struct ux500_wdt_data *pdata = pdev->dev.platform_data; + + if (pdata) { + if (pdata->timeout > 0) + timeout = pdata->timeout; + if (pdata->has_28_bits_resolution) + ux500_wdt.max_timeout = WATCHDOG_MAX28; + } + + watchdog_set_nowayout(&ux500_wdt, nowayout); + + /* disable auto off on sleep */ + prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); + + /* set HW initial value */ + prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); + + ret = watchdog_register_device(&ux500_wdt); + if (ret) + return ret; + + dev_info(&pdev->dev, "initialized\n"); + + return 0; +} + +static int ux500_wdt_remove(struct platform_device *dev) +{ + watchdog_unregister_device(&ux500_wdt); + + return 0; +} + +#ifdef CONFIG_PM +static int ux500_wdt_suspend(struct platform_device *pdev, + pm_message_t state) +{ + if (watchdog_active(&ux500_wdt)) { + ux500_wdt_stop(&ux500_wdt); + prcmu_config_a9wdog(PRCMU_WDOG_CPU1, true); + + prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); + ux500_wdt_start(&ux500_wdt); + } + return 0; +} + +static int ux500_wdt_resume(struct platform_device *pdev) +{ + if (watchdog_active(&ux500_wdt)) { + ux500_wdt_stop(&ux500_wdt); + prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); + + prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); + ux500_wdt_start(&ux500_wdt); + } + return 0; +} +#else +#define ux500_wdt_suspend NULL +#define ux500_wdt_resume NULL +#endif + +static struct platform_driver ux500_wdt_driver = { + .probe = ux500_wdt_probe, + .remove = ux500_wdt_remove, + .suspend = ux500_wdt_suspend, + .resume = ux500_wdt_resume, + .driver = { + .owner = THIS_MODULE, + .name = "ux500_wdt", + }, +}; + +module_platform_driver(ux500_wdt_driver); + +MODULE_AUTHOR("Jonas Aaberg "); +MODULE_DESCRIPTION("Ux500 Watchdog Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); +MODULE_ALIAS("platform:ux500_wdt"); diff --git a/include/linux/platform_data/ux500_wdt.h b/include/linux/platform_data/ux500_wdt.h new file mode 100644 index 000000000000..1689ff4c3bfd --- /dev/null +++ b/include/linux/platform_data/ux500_wdt.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) ST Ericsson SA 2011 + * + * License Terms: GNU General Public License v2 + * + * STE Ux500 Watchdog platform data + */ +#ifndef __UX500_WDT_H +#define __UX500_WDT_H + +/** + * struct ux500_wdt_data + */ +struct ux500_wdt_data { + unsigned int timeout; + bool has_28_bits_resolution; +}; + +#endif /* __UX500_WDT_H */ -- cgit v1.2.3 From b3aac62bbb1c3f8e71c88e6e477836def3058fe8 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Fri, 18 Jan 2013 12:40:14 +0100 Subject: mfd: db8500-prcmu: Add ux500_wdt mfd_cell This patch adds the necessary structures to use the watchdog functionality of PRCMU. The watchdog driver is named ux500_wdt. Acked-by: Lee Jones Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 7a63fa86bf01..e42a417adc5f 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -3074,6 +3075,11 @@ static struct resource ab8500_resources[] = { } }; +static struct ux500_wdt_data db8500_wdt_pdata = { + .timeout = 600, /* 10 minutes */ + .has_28_bits_resolution = true, +}; + static struct mfd_cell db8500_prcmu_devs[] = { { .name = "db8500-prcmu-regulators", @@ -3087,6 +3093,12 @@ static struct mfd_cell db8500_prcmu_devs[] = { .platform_data = &db8500_cpufreq_table, .pdata_size = sizeof(db8500_cpufreq_table), }, + { + .name = "ux500_wdt", + .platform_data = &db8500_wdt_pdata, + .pdata_size = sizeof(db8500_wdt_pdata), + .id = -1, + }, { .name = "ab8500-core", .of_compatible = "stericsson,ab8500", -- cgit v1.2.3 From 306df798507d8e009a7d4a5e8ce238a3b107de20 Mon Sep 17 00:00:00 2001 From: Yi Zhang Date: Tue, 22 Jan 2013 10:43:45 +0800 Subject: mfd: 88pm80x: Remove redundant devm_* calls devm_* functions are device managed and make error handling and code simpler; it also fix error exit paths Signed-off-by: Yi Zhang Signed-off-by: Samuel Ortiz --- drivers/mfd/88pm800.c | 10 +++------- drivers/mfd/88pm805.c | 4 ++-- drivers/mfd/88pm80x.c | 22 ++++------------------ include/linux/mfd/88pm80x.h | 2 +- 4 files changed, 10 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/88pm800.c b/drivers/mfd/88pm800.c index 391e23e6a647..582bda543520 100644 --- a/drivers/mfd/88pm800.c +++ b/drivers/mfd/88pm800.c @@ -531,7 +531,7 @@ static int pm800_probe(struct i2c_client *client, ret = device_800_init(chip, pdata); if (ret) { dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id); - goto err_800_init; + goto err_subchip_alloc; } ret = pm800_pages_init(chip); @@ -546,10 +546,8 @@ static int pm800_probe(struct i2c_client *client, err_page_init: mfd_remove_devices(chip->dev); device_irq_exit_800(chip); -err_800_init: - devm_kfree(&client->dev, subchip); err_subchip_alloc: - pm80x_deinit(client); + pm80x_deinit(); out_init: return ret; } @@ -562,9 +560,7 @@ static int pm800_remove(struct i2c_client *client) device_irq_exit_800(chip); pm800_pages_exit(chip); - devm_kfree(&client->dev, chip->subchip); - - pm80x_deinit(client); + pm80x_deinit(); return 0; } diff --git a/drivers/mfd/88pm805.c b/drivers/mfd/88pm805.c index e671230be2b1..65d7ac099b20 100644 --- a/drivers/mfd/88pm805.c +++ b/drivers/mfd/88pm805.c @@ -257,7 +257,7 @@ static int pm805_probe(struct i2c_client *client, pdata->plat_config(chip, pdata); err_805_init: - pm80x_deinit(client); + pm80x_deinit(); out_init: return ret; } @@ -269,7 +269,7 @@ static int pm805_remove(struct i2c_client *client) mfd_remove_devices(chip->dev); device_irq_exit_805(chip); - pm80x_deinit(client); + pm80x_deinit(); return 0; } diff --git a/drivers/mfd/88pm80x.c b/drivers/mfd/88pm80x.c index 1adb355d86d1..f736a46eb8c0 100644 --- a/drivers/mfd/88pm80x.c +++ b/drivers/mfd/88pm80x.c @@ -48,14 +48,12 @@ int pm80x_init(struct i2c_client *client, ret = PTR_ERR(map); dev_err(&client->dev, "Failed to allocate register map: %d\n", ret); - goto err_regmap_init; + return ret; } chip->id = id->driver_data; - if (chip->id < CHIP_PM800 || chip->id > CHIP_PM805) { - ret = -EINVAL; - goto err_chip_id; - } + if (chip->id < CHIP_PM800 || chip->id > CHIP_PM805) + return -EINVAL; chip->client = client; chip->regmap = map; @@ -82,19 +80,11 @@ int pm80x_init(struct i2c_client *client, } return 0; - -err_chip_id: - regmap_exit(map); -err_regmap_init: - devm_kfree(&client->dev, chip); - return ret; } EXPORT_SYMBOL_GPL(pm80x_init); -int pm80x_deinit(struct i2c_client *client) +int pm80x_deinit(void) { - struct pm80x_chip *chip = i2c_get_clientdata(client); - /* * workaround: clear the dependency between pm800 and pm805. * would remove it after HW chip fixes the issue. @@ -103,10 +93,6 @@ int pm80x_deinit(struct i2c_client *client) g_pm80x_chip->companion = NULL; else g_pm80x_chip = NULL; - - regmap_exit(chip->regmap); - devm_kfree(&client->dev, chip); - return 0; } EXPORT_SYMBOL_GPL(pm80x_deinit); diff --git a/include/linux/mfd/88pm80x.h b/include/linux/mfd/88pm80x.h index 478672ed0c3d..e94537befabd 100644 --- a/include/linux/mfd/88pm80x.h +++ b/include/linux/mfd/88pm80x.h @@ -365,5 +365,5 @@ static inline int pm80x_dev_resume(struct device *dev) extern int pm80x_init(struct i2c_client *client, const struct i2c_device_id *id); -extern int pm80x_deinit(struct i2c_client *client); +extern int pm80x_deinit(void); #endif /* __LINUX_MFD_88PM80X_H */ -- cgit v1.2.3 From 154c4c7b613049168e2923d096b7c2d100ba8a6a Mon Sep 17 00:00:00 2001 From: "Vishwanathrao Badarkhe, Manish" Date: Thu, 24 Jan 2013 16:25:17 +0530 Subject: mfd: tps6507x: Add DT support Add device tree based initialization support for TI's tps6507x mfd device. Signed-off-by: Vishwanathrao Badarkhe, Manish Signed-off-by: Samuel Ortiz --- drivers/mfd/tps6507x.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/tps6507x.c b/drivers/mfd/tps6507x.c index 409afa23d5dc..5ad4b772b097 100644 --- a/drivers/mfd/tps6507x.c +++ b/drivers/mfd/tps6507x.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -116,11 +117,19 @@ static const struct i2c_device_id tps6507x_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, tps6507x_i2c_id); +#ifdef CONFIG_OF +static struct of_device_id tps6507x_of_match[] = { + {.compatible = "ti,tps6507x", }, + {}, +}; +MODULE_DEVICE_TABLE(of, tps6507x_of_match); +#endif static struct i2c_driver tps6507x_i2c_driver = { .driver = { .name = "tps6507x", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tps6507x_of_match), }, .probe = tps6507x_i2c_probe, .remove = tps6507x_i2c_remove, -- cgit v1.2.3 From 5f384c1f8be19487f904731d7232120dcfeca8e1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 25 Jan 2013 17:09:23 +0800 Subject: mfd: wm5102: Make DSP scratch registers readable Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm5102-tables.c | 8 ++++++++ include/linux/mfd/arizona/registers.h | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index ff8f0804dde3..d754596eb942 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -1795,6 +1795,10 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_DSP1_STATUS_1: case ARIZONA_DSP1_STATUS_2: case ARIZONA_DSP1_STATUS_3: + case ARIZONA_DSP1_SCRATCH_0: + case ARIZONA_DSP1_SCRATCH_1: + case ARIZONA_DSP1_SCRATCH_2: + case ARIZONA_DSP1_SCRATCH_3: return true; default: if ((reg >= 0x100000 && reg < 0x106000) || @@ -1849,6 +1853,10 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_DSP1_STATUS_1: case ARIZONA_DSP1_STATUS_2: case ARIZONA_DSP1_STATUS_3: + case ARIZONA_DSP1_SCRATCH_0: + case ARIZONA_DSP1_SCRATCH_1: + case ARIZONA_DSP1_SCRATCH_2: + case ARIZONA_DSP1_SCRATCH_3: case ARIZONA_HEADPHONE_DETECT_2: case ARIZONA_MIC_DETECT_3: return true; diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index 1f6fe31a4d5c..718126084ad1 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -982,18 +982,34 @@ #define ARIZONA_DSP1_STATUS_1 0x1104 #define ARIZONA_DSP1_STATUS_2 0x1105 #define ARIZONA_DSP1_STATUS_3 0x1106 +#define ARIZONA_DSP1_SCRATCH_0 0x1140 +#define ARIZONA_DSP1_SCRATCH_1 0x1141 +#define ARIZONA_DSP1_SCRATCH_2 0x1142 +#define ARIZONA_DSP1_SCRATCH_3 0x1143 #define ARIZONA_DSP2_CONTROL_1 0x1200 #define ARIZONA_DSP2_CLOCKING_1 0x1201 #define ARIZONA_DSP2_STATUS_1 0x1204 #define ARIZONA_DSP2_STATUS_2 0x1205 +#define ARIZONA_DSP2_SCRATCH_0 0x1240 +#define ARIZONA_DSP2_SCRATCH_1 0x1241 +#define ARIZONA_DSP2_SCRATCH_2 0x1242 +#define ARIZONA_DSP2_SCRATCH_3 0x1243 #define ARIZONA_DSP3_CONTROL_1 0x1300 #define ARIZONA_DSP3_CLOCKING_1 0x1301 #define ARIZONA_DSP3_STATUS_1 0x1304 #define ARIZONA_DSP3_STATUS_2 0x1305 +#define ARIZONA_DSP3_SCRATCH_0 0x1340 +#define ARIZONA_DSP3_SCRATCH_1 0x1341 +#define ARIZONA_DSP3_SCRATCH_2 0x1342 +#define ARIZONA_DSP3_SCRATCH_3 0x1343 #define ARIZONA_DSP4_CONTROL_1 0x1400 #define ARIZONA_DSP4_CLOCKING_1 0x1401 #define ARIZONA_DSP4_STATUS_1 0x1404 #define ARIZONA_DSP4_STATUS_2 0x1405 +#define ARIZONA_DSP4_SCRATCH_0 0x1440 +#define ARIZONA_DSP4_SCRATCH_1 0x1441 +#define ARIZONA_DSP4_SCRATCH_2 0x1442 +#define ARIZONA_DSP4_SCRATCH_3 0x1443 /* * Field Definitions. -- cgit v1.2.3 From 40719314f259ab9409ca3d48551c17aa23bc2b4d Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 29 Jan 2013 14:35:15 +0530 Subject: mfd: tps65090: add DT support for tps65090 Add device tree support for the TI PMIC TPS65090. The device can be registered through platform or DT. Add device tree binding document for this device. Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- .../devicetree/bindings/regulator/tps65090.txt | 122 +++++++++++++++++++++ drivers/mfd/tps65090.c | 22 +++- 2 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/regulator/tps65090.txt (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/regulator/tps65090.txt b/Documentation/devicetree/bindings/regulator/tps65090.txt new file mode 100644 index 000000000000..313a60ba61d8 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/tps65090.txt @@ -0,0 +1,122 @@ +TPS65090 regulators + +Required properties: +- compatible: "ti,tps65090" +- reg: I2C slave address +- interrupts: the interrupt outputs of the controller +- regulators: A node that houses a sub-node for each regulator within the + device. Each sub-node is identified using the node's name, with valid + values listed below. The content of each sub-node is defined by the + standard binding for regulators; see regulator.txt. + dcdc[1-3], fet[1-7] and ldo[1-2] respectively. +- vsys[1-3]-supply: The input supply for DCDC[1-3] respectively. +- infet[1-7]-supply: The input supply for FET[1-7] respectively. +- vsys-l[1-2]-supply: The input supply for LDO[1-2] respectively. + +Optional properties: +- ti,enable-ext-control: This is applicable for DCDC1, DCDC2 and DCDC3. + If DCDCs are externally controlled then this property should be there. +- "dcdc-ext-control-gpios: This is applicable for DCDC1, DCDC2 and DCDC3. + If DCDCs are externally controlled and if it is from GPIO then GPIO + number should be provided. If it is externally controlled and no GPIO + entry then driver will just configure this rails as external control + and will not provide any enable/disable APIs. + +Each regulator is defined using the standard binding for regulators. + +Example: + + tps65090@48 { + compatible = "ti,tps65090"; + reg = <0x48>; + interrupts = <0 88 0x4>; + + vsys1-supply = <&some_reg>; + vsys2-supply = <&some_reg>; + vsys3-supply = <&some_reg>; + infet1-supply = <&some_reg>; + infet2-supply = <&some_reg>; + infet3-supply = <&some_reg>; + infet4-supply = <&some_reg>; + infet5-supply = <&some_reg>; + infet6-supply = <&some_reg>; + infet7-supply = <&some_reg>; + vsys_l1-supply = <&some_reg>; + vsys_l2-supply = <&some_reg>; + + regulators { + dcdc1 { + regulator-name = "dcdc1"; + regulator-boot-on; + regulator-always-on; + ti,enable-ext-control; + dcdc-ext-control-gpios = <&gpio 10 0>; + }; + + dcdc2 { + regulator-name = "dcdc2"; + regulator-boot-on; + regulator-always-on; + }; + + dcdc3 { + regulator-name = "dcdc3"; + regulator-boot-on; + regulator-always-on; + }; + + fet1 { + regulator-name = "fet1"; + regulator-boot-on; + regulator-always-on; + }; + + fet2 { + regulator-name = "fet2"; + regulator-boot-on; + regulator-always-on; + }; + + fet3 { + regulator-name = "fet3"; + regulator-boot-on; + regulator-always-on; + }; + + fet4 { + regulator-name = "fet4"; + regulator-boot-on; + regulator-always-on; + }; + + fet5 { + regulator-name = "fet5"; + regulator-boot-on; + regulator-always-on; + }; + + fet6 { + regulator-name = "fet6"; + regulator-boot-on; + regulator-always-on; + }; + + fet7 { + regulator-name = "fet7"; + regulator-boot-on; + regulator-always-on; + }; + + ldo1 { + regulator-name = "ldo1"; + regulator-boot-on; + regulator-always-on; + }; + + ldo2 { + regulator-name = "ldo2"; + regulator-boot-on; + regulator-always-on; + }; + }; + }; diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index 8d12a8e00d9c..b49654587ce5 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #define NUM_INT_REG 2 @@ -148,18 +150,31 @@ static const struct regmap_config tps65090_regmap_config = { .volatile_reg = is_volatile_reg, }; +#ifdef CONFIG_OF +static const struct of_device_id tps65090_of_match[] = { + { .compatible = "ti,tps65090",}, + {}, +}; +MODULE_DEVICE_TABLE(of, tps65090_of_match); +#endif + static int tps65090_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct tps65090_platform_data *pdata = client->dev.platform_data; + int irq_base = 0; struct tps65090 *tps65090; int ret; - if (!pdata) { - dev_err(&client->dev, "tps65090 requires platform data\n"); + if (!pdata && !client->dev.of_node) { + dev_err(&client->dev, + "tps65090 requires platform data or of_node\n"); return -EINVAL; } + if (pdata) + irq_base = pdata->irq_base; + tps65090 = devm_kzalloc(&client->dev, sizeof(*tps65090), GFP_KERNEL); if (!tps65090) { dev_err(&client->dev, "mem alloc for tps65090 failed\n"); @@ -178,7 +193,7 @@ static int tps65090_i2c_probe(struct i2c_client *client, if (client->irq) { ret = regmap_add_irq_chip(tps65090->rmap, client->irq, - IRQF_ONESHOT | IRQF_TRIGGER_LOW, pdata->irq_base, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, irq_base, &tps65090_irq_chip, &tps65090->irq_data); if (ret) { dev_err(&client->dev, @@ -247,6 +262,7 @@ static struct i2c_driver tps65090_driver = { .driver = { .name = "tps65090", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tps65090_of_match), .pm = &tps65090_pm_ops, }, .probe = tps65090_i2c_probe, -- cgit v1.2.3 From 4f979ed5e2656570f433101bfc5bc116a919316b Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 29 Jan 2013 14:35:17 +0530 Subject: mfd: tps65090: Pass irq domain when adding mfd sub devices When device is get added through DT then irq_base is 0 (zero) and in this case regmap_irq_chip_get_base() generates warning. The interrupt of this device get added through irq_domain_add_linear() when irq_base is 0. Hence pass the irq domain in place of base_irq when calling mfd_add_devices(). Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65090.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index b49654587ce5..2ad0a1528650 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -204,7 +204,7 @@ static int tps65090_i2c_probe(struct i2c_client *client, ret = mfd_add_devices(tps65090->dev, -1, tps65090s, ARRAY_SIZE(tps65090s), NULL, - regmap_irq_chip_get_base(tps65090->irq_data), NULL); + 0, regmap_irq_get_domain(tps65090->irq_data)); if (ret) { dev_err(&client->dev, "add mfd devices failed with err: %d\n", ret); -- cgit v1.2.3 From 5fd86d34c1fad19a570e7f787d5e4ea94c6f38f7 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 29 Jan 2013 14:35:18 +0530 Subject: mfd: tps65090: remove suspend/resume callbacks The tps65090 mfd driver implement the suspend/resume callbacks which just disable and enable irqs in suspend/resume respectively. This operation is already done in irq suspend and irq_resume and hence it is not require to implement the same in the driver. Remove this non-require code. Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65090.c | 23 ----------------------- 1 file changed, 23 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index 2ad0a1528650..98edb5be85c6 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -230,28 +230,6 @@ static int tps65090_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP -static int tps65090_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - if (client->irq) - disable_irq(client->irq); - return 0; -} - -static int tps65090_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - if (client->irq) - enable_irq(client->irq); - return 0; -} -#endif - -static const struct dev_pm_ops tps65090_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(tps65090_suspend, tps65090_resume) -}; - static const struct i2c_device_id tps65090_id_table[] = { { "tps65090", 0 }, { }, @@ -263,7 +241,6 @@ static struct i2c_driver tps65090_driver = { .name = "tps65090", .owner = THIS_MODULE, .of_match_table = of_match_ptr(tps65090_of_match), - .pm = &tps65090_pm_ops, }, .probe = tps65090_i2c_probe, .remove = tps65090_i2c_remove, -- cgit v1.2.3 From 9871c799d15dfb1609582bf8b2868217802a2100 Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Tue, 29 Jan 2013 15:21:33 +0800 Subject: mfd: rtsx: Remove redundant code In function rtsx_pci_add_sg_tbl, the statement "ptr++" is useless. Signed-off-by: Wei WANG Acked-by: Borislav Petkov Signed-off-by: Samuel Ortiz --- drivers/mfd/rtsx_pcr.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 9fc57009e228..c021ce3df955 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -325,7 +325,6 @@ static void rtsx_pci_add_sg_tbl(struct rtsx_pcr *pcr, val = ((u64)addr << 32) | ((u64)len << 12) | option; put_unaligned_le64(val, ptr); - ptr++; pcr->sgi++; } -- cgit v1.2.3 From f84ef04227d8983c8f76ac0f5cf8b0a15e0c67af Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Tue, 29 Jan 2013 15:21:34 +0800 Subject: mfd: rtsx: Declare that the DMA address limitation is 32bit explicitly Realtek PCIe card reader only supports 32bit DMA. This declaration can improve the readability. Signed-off-by: Wei WANG Signed-off-by: Samuel Ortiz --- drivers/mfd/rtsx_pcr.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index c021ce3df955..b2cfd2ed616c 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -1029,6 +1029,10 @@ static int rtsx_pci_probe(struct pci_dev *pcidev, pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device, (int)pcidev->revision); + ret = pci_set_dma_mask(pcidev, DMA_BIT_MASK(32)); + if (ret < 0) + return ret; + ret = pci_enable_device(pcidev); if (ret) return ret; -- cgit v1.2.3 From 678cacdfda800b0cdbfdd01350dcf5e3b767f6ed Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Tue, 29 Jan 2013 15:21:35 +0800 Subject: mfd: rtsx: Fix checkpatch warning WARNING: Avoid CamelCase: + u8 N, min_N, max_N, clk_divider; WARNING: Avoid CamelCase: + u8 N, min_N, max_N, clk_divider; Signed-off-by: Wei WANG Signed-off-by: Samuel Ortiz --- drivers/mfd/rtsx_pcr.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index b2cfd2ed616c..4897c39cfb7b 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -590,7 +590,7 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk) { int err, clk; - u8 N, min_N, max_N, clk_divider; + u8 n, min_n, max_n, clk_divider; u8 mcu_cnt, div, max_div; u8 depth[] = { [RTSX_SSC_DEPTH_4M] = SSC_DEPTH_4M, @@ -615,8 +615,8 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, card_clock /= 1000000; dev_dbg(&(pcr->pci->dev), "Switch card clock to %dMHz\n", card_clock); - min_N = 80; - max_N = 208; + min_n = 80; + max_n = 208; max_div = CLK_DIV_8; clk = card_clock; @@ -630,30 +630,30 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, return 0; if (pcr->ops->conv_clk_and_div_n) - N = (u8)pcr->ops->conv_clk_and_div_n(clk, CLK_TO_DIV_N); + n = (u8)pcr->ops->conv_clk_and_div_n(clk, CLK_TO_DIV_N); else - N = (u8)(clk - 2); - if ((clk <= 2) || (N > max_N)) + n = (u8)(clk - 2); + if ((clk <= 2) || (n > max_n)) return -EINVAL; mcu_cnt = (u8)(125/clk + 3); if (mcu_cnt > 15) mcu_cnt = 15; - /* Make sure that the SSC clock div_n is equal or greater than min_N */ + /* Make sure that the SSC clock div_n is equal or greater than min_n */ div = CLK_DIV_1; - while ((N < min_N) && (div < max_div)) { + while ((n < min_n) && (div < max_div)) { if (pcr->ops->conv_clk_and_div_n) { - int dbl_clk = pcr->ops->conv_clk_and_div_n(N, + int dbl_clk = pcr->ops->conv_clk_and_div_n(n, DIV_N_TO_CLK) * 2; - N = (u8)pcr->ops->conv_clk_and_div_n(dbl_clk, + n = (u8)pcr->ops->conv_clk_and_div_n(dbl_clk, CLK_TO_DIV_N); } else { - N = (N + 2) * 2 - 2; + n = (n + 2) * 2 - 2; } div++; } - dev_dbg(&(pcr->pci->dev), "N = %d, div = %d\n", N, div); + dev_dbg(&(pcr->pci->dev), "n = %d, div = %d\n", n, div); ssc_depth = depth[ssc_depth]; if (double_clk) @@ -670,7 +670,7 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, SSC_DEPTH_MASK, ssc_depth); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, N); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB); if (vpclk) { rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, -- cgit v1.2.3 From eebbe2541684da99bf0b179d5182dc8025f5f5b6 Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Tue, 29 Jan 2013 15:21:36 +0800 Subject: mfd: rtsx: Use macros to replace some variables In function rtsx_pci_switch_clock, some variables, such as min_n, max_n, and max_div, are not necessary. And those assigned values look very obscure for others. It's more proper to use macro definitions here to replace these variables. Signed-off-by: Wei WANG Acked-by: Borislav Petkov Signed-off-by: Samuel Ortiz --- drivers/mfd/rtsx_pcr.c | 13 ++++--------- drivers/mfd/rtsx_pcr.h | 3 +++ 2 files changed, 7 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 4897c39cfb7b..67bb34ef0ebd 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -590,8 +590,7 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk) { int err, clk; - u8 n, min_n, max_n, clk_divider; - u8 mcu_cnt, div, max_div; + u8 n, clk_divider, mcu_cnt, div; u8 depth[] = { [RTSX_SSC_DEPTH_4M] = SSC_DEPTH_4M, [RTSX_SSC_DEPTH_2M] = SSC_DEPTH_2M, @@ -615,10 +614,6 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, card_clock /= 1000000; dev_dbg(&(pcr->pci->dev), "Switch card clock to %dMHz\n", card_clock); - min_n = 80; - max_n = 208; - max_div = CLK_DIV_8; - clk = card_clock; if (!initial_mode && double_clk) clk = card_clock * 2; @@ -633,16 +628,16 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, n = (u8)pcr->ops->conv_clk_and_div_n(clk, CLK_TO_DIV_N); else n = (u8)(clk - 2); - if ((clk <= 2) || (n > max_n)) + if ((clk <= 2) || (n > MAX_DIV_N_PCR)) return -EINVAL; mcu_cnt = (u8)(125/clk + 3); if (mcu_cnt > 15) mcu_cnt = 15; - /* Make sure that the SSC clock div_n is equal or greater than min_n */ + /* Make sure that the SSC clock div_n is not less than MIN_DIV_N_PCR */ div = CLK_DIV_1; - while ((n < min_n) && (div < max_div)) { + while ((n < MIN_DIV_N_PCR) && (div < CLK_DIV_8)) { if (pcr->ops->conv_clk_and_div_n) { int dbl_clk = pcr->ops->conv_clk_and_div_n(n, DIV_N_TO_CLK) * 2; diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h index 12462c1df1a9..33c210be1daa 100644 --- a/drivers/mfd/rtsx_pcr.h +++ b/drivers/mfd/rtsx_pcr.h @@ -25,6 +25,9 @@ #include +#define MIN_DIV_N_PCR 80 +#define MAX_DIV_N_PCR 208 + void rts5209_init_params(struct rtsx_pcr *pcr); void rts5229_init_params(struct rtsx_pcr *pcr); void rtl8411_init_params(struct rtsx_pcr *pcr); -- cgit v1.2.3 From 504decc0a063e6a09a1e5b203ca68bc21dfffde9 Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Tue, 29 Jan 2013 15:21:37 +0800 Subject: mfd: rtsx: Optimize card detect flow 1. Schedule card detect work at the end of the ISR 2. Callback function ops->cd_deglitch may delay for a period of time. It is not proper to call this callback when local irq disabled. 3. Card detect flow can't be executed in parallel with other card reader operations, so it's better to be protected by mutex. Signed-off-by: Wei WANG Signed-off-by: Samuel Ortiz --- drivers/mfd/rtsx_pcr.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 67bb34ef0ebd..9016932f0267 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -752,7 +752,7 @@ static void rtsx_pci_card_detect(struct work_struct *work) struct delayed_work *dwork; struct rtsx_pcr *pcr; unsigned long flags; - unsigned int card_detect = 0; + unsigned int card_detect = 0, card_inserted, card_removed; u32 irq_status; dwork = to_delayed_work(work); @@ -760,25 +760,32 @@ static void rtsx_pci_card_detect(struct work_struct *work) dev_dbg(&(pcr->pci->dev), "--> %s\n", __func__); + mutex_lock(&pcr->pcr_mutex); spin_lock_irqsave(&pcr->lock, flags); irq_status = rtsx_pci_readl(pcr, RTSX_BIPR); dev_dbg(&(pcr->pci->dev), "irq_status: 0x%08x\n", irq_status); - if (pcr->card_inserted || pcr->card_removed) { + irq_status &= CARD_EXIST; + card_inserted = pcr->card_inserted & irq_status; + card_removed = pcr->card_removed; + pcr->card_inserted = 0; + pcr->card_removed = 0; + + spin_unlock_irqrestore(&pcr->lock, flags); + + if (card_inserted || card_removed) { dev_dbg(&(pcr->pci->dev), "card_inserted: 0x%x, card_removed: 0x%x\n", - pcr->card_inserted, pcr->card_removed); + card_inserted, card_removed); if (pcr->ops->cd_deglitch) - pcr->card_inserted = pcr->ops->cd_deglitch(pcr); + card_inserted = pcr->ops->cd_deglitch(pcr); - card_detect = pcr->card_inserted | pcr->card_removed; - pcr->card_inserted = 0; - pcr->card_removed = 0; + card_detect = card_inserted | card_removed; } - spin_unlock_irqrestore(&pcr->lock, flags); + mutex_unlock(&pcr->pcr_mutex); if ((card_detect & SD_EXIST) && pcr->slots[RTSX_SD_CARD].card_event) pcr->slots[RTSX_SD_CARD].card_event( @@ -830,10 +837,6 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id) } } - if (pcr->card_inserted || pcr->card_removed) - schedule_delayed_work(&pcr->carddet_work, - msecs_to_jiffies(200)); - if (int_reg & (NEED_COMPLETE_INT | DELINK_INT)) { if (int_reg & (TRANS_FAIL_INT | DELINK_INT)) { pcr->trans_result = TRANS_RESULT_FAIL; @@ -846,6 +849,10 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id) } } + if (pcr->card_inserted || pcr->card_removed) + schedule_delayed_work(&pcr->carddet_work, + msecs_to_jiffies(200)); + spin_unlock(&pcr->lock); return IRQ_HANDLED; } -- cgit v1.2.3 From 151621a704fc7b8eaa1d6905bec0c6388b0a57af Mon Sep 17 00:00:00 2001 From: Hongbo Zhang Date: Wed, 30 Jan 2013 18:21:27 +0800 Subject: mfd: ab8500: Rename ab8500 to abx500 for hwmon driver We are using a generic abx500 hwmon layer, so rename specific ab8500 to generic abx500 for hwmon device and driver matching. Signed-off-by: Hongbo Zhang Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 4778bb124efe..cdcaa5b78c4d 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -923,7 +923,7 @@ static struct resource ab8505_iddet_resources[] = { static struct resource ab8500_temp_resources[] = { { - .name = "AB8500_TEMP_WARM", + .name = "ABX500_TEMP_WARM", .start = AB8500_INT_TEMP_WARM, .end = AB8500_INT_TEMP_WARM, .flags = IORESOURCE_IRQ, @@ -999,8 +999,8 @@ static struct mfd_cell abx500_common_devs[] = { .of_compatible = "stericsson,ab8500-denc", }, { - .name = "ab8500-temp", - .of_compatible = "stericsson,ab8500-temp", + .name = "abx500-temp", + .of_compatible = "stericsson,abx500-temp", .num_resources = ARRAY_SIZE(ab8500_temp_resources), .resources = ab8500_temp_resources, }, -- cgit v1.2.3 From 8ea402f5646e6e36c8cd0a62053ba8939204dceb Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Wed, 30 Jan 2013 10:33:16 +0000 Subject: mfd: vexpress: Add pseudo-GPIO based LEDs The LEDs on the Versatile Express motherboard are controlled through simple memory-mapped register. This patch extends the pseudo-GPIO controller definition for these lines and creates generic "leds-gpio" device using them Signed-off-by: Pawel Moll Signed-off-by: Samuel Ortiz --- drivers/mfd/vexpress-sysreg.c | 73 ++++++++++++++++++++++++++++++++----------- include/linux/vexpress.h | 8 +++++ 2 files changed, 63 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index 77048b18439e..51c3ca263bf5 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -49,6 +49,8 @@ #define SYS_ID_HBI_SHIFT 16 #define SYS_PROCIDx_HBI_SHIFT 0 +#define SYS_LED_LED(n) (1 << (n)) + #define SYS_MCI_CARDIN (1 << 0) #define SYS_MCI_WPROT (1 << 1) @@ -348,22 +350,27 @@ void __init vexpress_sysreg_of_early_init(void) } +#define VEXPRESS_SYSREG_GPIO(_name, _reg, _value) \ + [VEXPRESS_GPIO_##_name] = { \ + .reg = _reg, \ + .value = _reg##_##_value, \ + } + static struct vexpress_sysreg_gpio { unsigned long reg; u32 value; } vexpress_sysreg_gpios[] = { - [VEXPRESS_GPIO_MMC_CARDIN] = { - .reg = SYS_MCI, - .value = SYS_MCI_CARDIN, - }, - [VEXPRESS_GPIO_MMC_WPROT] = { - .reg = SYS_MCI, - .value = SYS_MCI_WPROT, - }, - [VEXPRESS_GPIO_FLASH_WPn] = { - .reg = SYS_FLASH, - .value = SYS_FLASH_WPn, - }, + VEXPRESS_SYSREG_GPIO(MMC_CARDIN, SYS_MCI, CARDIN), + VEXPRESS_SYSREG_GPIO(MMC_WPROT, SYS_MCI, WPROT), + VEXPRESS_SYSREG_GPIO(FLASH_WPn, SYS_FLASH, WPn), + VEXPRESS_SYSREG_GPIO(LED0, SYS_LED, LED(0)), + VEXPRESS_SYSREG_GPIO(LED1, SYS_LED, LED(1)), + VEXPRESS_SYSREG_GPIO(LED2, SYS_LED, LED(2)), + VEXPRESS_SYSREG_GPIO(LED3, SYS_LED, LED(3)), + VEXPRESS_SYSREG_GPIO(LED4, SYS_LED, LED(4)), + VEXPRESS_SYSREG_GPIO(LED5, SYS_LED, LED(5)), + VEXPRESS_SYSREG_GPIO(LED6, SYS_LED, LED(6)), + VEXPRESS_SYSREG_GPIO(LED7, SYS_LED, LED(7)), }; static int vexpress_sysreg_gpio_direction_input(struct gpio_chip *chip, @@ -372,12 +379,6 @@ static int vexpress_sysreg_gpio_direction_input(struct gpio_chip *chip, return 0; } -static int vexpress_sysreg_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - return 0; -} - static int vexpress_sysreg_gpio_get(struct gpio_chip *chip, unsigned offset) { @@ -401,6 +402,14 @@ static void vexpress_sysreg_gpio_set(struct gpio_chip *chip, writel(reg_value, vexpress_sysreg_base + gpio->reg); } +static int vexpress_sysreg_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + vexpress_sysreg_gpio_set(chip, offset, value); + + return 0; +} + static struct gpio_chip vexpress_sysreg_gpio_chip = { .label = "vexpress-sysreg", .direction_input = vexpress_sysreg_gpio_direction_input, @@ -412,6 +421,30 @@ static struct gpio_chip vexpress_sysreg_gpio_chip = { }; +#define VEXPRESS_SYSREG_GREEN_LED(_name, _default_trigger, _gpio) \ + { \ + .name = "v2m:green:"_name, \ + .default_trigger = _default_trigger, \ + .gpio = VEXPRESS_GPIO_##_gpio, \ + } + +struct gpio_led vexpress_sysreg_leds[] = { + VEXPRESS_SYSREG_GREEN_LED("user1", "heartbeat", LED0), + VEXPRESS_SYSREG_GREEN_LED("user2", "mmc0", LED1), + VEXPRESS_SYSREG_GREEN_LED("user3", "cpu0", LED2), + VEXPRESS_SYSREG_GREEN_LED("user4", "cpu1", LED3), + VEXPRESS_SYSREG_GREEN_LED("user5", "cpu2", LED4), + VEXPRESS_SYSREG_GREEN_LED("user6", "cpu3", LED5), + VEXPRESS_SYSREG_GREEN_LED("user7", "cpu4", LED6), + VEXPRESS_SYSREG_GREEN_LED("user8", "cpu5", LED7), +}; + +struct gpio_led_platform_data vexpress_sysreg_leds_pdata = { + .num_leds = ARRAY_SIZE(vexpress_sysreg_leds), + .leds = vexpress_sysreg_leds, +}; + + static ssize_t vexpress_sysreg_sys_id_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -456,6 +489,10 @@ static int vexpress_sysreg_probe(struct platform_device *pdev) return err; } + platform_device_register_data(vexpress_sysreg_dev, "leds-gpio", + PLATFORM_DEVID_AUTO, &vexpress_sysreg_leds_pdata, + sizeof(vexpress_sysreg_leds_pdata)); + vexpress_sysreg_dev = &pdev->dev; device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id); diff --git a/include/linux/vexpress.h b/include/linux/vexpress.h index c52215ff4245..75818744ab59 100644 --- a/include/linux/vexpress.h +++ b/include/linux/vexpress.h @@ -27,6 +27,14 @@ #define VEXPRESS_GPIO_MMC_CARDIN 0 #define VEXPRESS_GPIO_MMC_WPROT 1 #define VEXPRESS_GPIO_FLASH_WPn 2 +#define VEXPRESS_GPIO_LED0 3 +#define VEXPRESS_GPIO_LED1 4 +#define VEXPRESS_GPIO_LED2 5 +#define VEXPRESS_GPIO_LED3 6 +#define VEXPRESS_GPIO_LED4 7 +#define VEXPRESS_GPIO_LED5 8 +#define VEXPRESS_GPIO_LED6 9 +#define VEXPRESS_GPIO_LED7 10 #define VEXPRESS_RES_FUNC(_site, _func) \ { \ -- cgit v1.2.3 From 88a7ee37f3c5c73b000f7ba2000b27c5002a5286 Mon Sep 17 00:00:00 2001 From: Roger Tseng Date: Mon, 4 Feb 2013 15:45:58 +0800 Subject: mfd: rtsx: Implement driving adjustment to device-dependent callbacks Implement different ways of selecting driving capability(a necessary adjustment along with voltage change). It was origionally in device-independent mmc/host/rtsx_pci_sdmmc.c. Moving it here to support devices which may have a different way of adjustment. Signed-off-by: Roger Tseng Reviewed-by: Wei WANG Signed-off-by: Samuel Ortiz --- drivers/mfd/rtl8411.c | 16 +++++++++++++--- drivers/mfd/rts5209.c | 8 ++++++++ drivers/mfd/rts5229.c | 8 ++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/rtl8411.c b/drivers/mfd/rtl8411.c index 3d3b4addf81a..2a2d31687b72 100644 --- a/drivers/mfd/rtl8411.c +++ b/drivers/mfd/rtl8411.c @@ -115,14 +115,24 @@ static int rtl8411_card_power_off(struct rtsx_pcr *pcr, int card) static int rtl8411_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) { u8 mask, val; + int err; mask = (BPP_REG_TUNED18 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_MASK; - if (voltage == OUTPUT_3V3) + if (voltage == OUTPUT_3V3) { + err = rtsx_pci_write_register(pcr, + SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_D); + if (err < 0) + return err; val = (BPP_ASIC_3V3 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_3V3; - else if (voltage == OUTPUT_1V8) + } else if (voltage == OUTPUT_1V8) { + err = rtsx_pci_write_register(pcr, + SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B); + if (err < 0) + return err; val = (BPP_ASIC_1V8 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_1V8; - else + } else { return -EINVAL; + } return rtsx_pci_write_register(pcr, LDO_CTL, mask, val); } diff --git a/drivers/mfd/rts5209.c b/drivers/mfd/rts5209.c index 98fe0f39463e..ec78d9fb0879 100644 --- a/drivers/mfd/rts5209.c +++ b/drivers/mfd/rts5209.c @@ -149,10 +149,18 @@ static int rts5209_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) int err; if (voltage == OUTPUT_3V3) { + err = rtsx_pci_write_register(pcr, + SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_D); + if (err < 0) + return err; err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24); if (err < 0) return err; } else if (voltage == OUTPUT_1V8) { + err = rtsx_pci_write_register(pcr, + SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B); + if (err < 0) + return err; err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24); if (err < 0) return err; diff --git a/drivers/mfd/rts5229.c b/drivers/mfd/rts5229.c index 29d889cbb9c5..58af4dbe3586 100644 --- a/drivers/mfd/rts5229.c +++ b/drivers/mfd/rts5229.c @@ -119,10 +119,18 @@ static int rts5229_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) int err; if (voltage == OUTPUT_3V3) { + err = rtsx_pci_write_register(pcr, + SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_D); + if (err < 0) + return err; err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24); if (err < 0) return err; } else if (voltage == OUTPUT_1V8) { + err = rtsx_pci_write_register(pcr, + SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B); + if (err < 0) + return err; err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24); if (err < 0) return err; -- cgit v1.2.3 From e12379320b2e1ceffc4211ad174989bc042149d9 Mon Sep 17 00:00:00 2001 From: Roger Tseng Date: Mon, 4 Feb 2013 15:45:59 +0800 Subject: mfd: rtsx: Support RTS5227 Support new model RTS5227. Signed-off-by: Roger Tseng Reviewed-by: Wei WANG Signed-off-by: Samuel Ortiz --- drivers/mfd/Makefile | 2 +- drivers/mfd/rts5227.c | 234 +++++++++++++++++++++++++++++++++++++++++++ drivers/mfd/rtsx_pcr.c | 5 + drivers/mfd/rtsx_pcr.h | 1 + include/linux/mfd/rtsx_pci.h | 5 + 5 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 drivers/mfd/rts5227.c (limited to 'drivers') diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 8b977f8045ae..b90409c23664 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o obj-$(CONFIG_MFD_SM501) += sm501.o obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o -rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o +rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o diff --git a/drivers/mfd/rts5227.c b/drivers/mfd/rts5227.c new file mode 100644 index 000000000000..fc831dcb1480 --- /dev/null +++ b/drivers/mfd/rts5227.c @@ -0,0 +1,234 @@ +/* Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * + * 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, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + * + * Author: + * Wei WANG + * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China + * + * Roger Tseng + * No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan + */ + +#include +#include +#include + +#include "rtsx_pcr.h" + +static int rts5227_extra_init_hw(struct rtsx_pcr *pcr) +{ + u16 cap; + + rtsx_pci_init_cmd(pcr); + + /* Configure GPIO as output */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, GPIO_CTL, 0x02, 0x02); + /* Switch LDO3318 source from DV33 to card_3v3 */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x00); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x01); + /* LED shine disabled, set initial shine cycle period */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OLT_LED_CTL, 0x0F, 0x02); + /* Configure LTR */ + pcie_capability_read_word(pcr->pci, PCI_EXP_DEVCTL2, &cap); + if (cap & PCI_EXP_LTR_EN) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LTR_CTL, 0xFF, 0xA3); + /* Configure OBFF */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OBFF_CFG, 0x03, 0x03); + /* Configure force_clock_req + * Maybe We should define 0xFF03 as some name + */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, 0xFF03, 0x08, 0x08); + /* Correct driving */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + SD30_CLK_DRIVE_SEL, 0xFF, 0x96); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + SD30_CMD_DRIVE_SEL, 0xFF, 0x96); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + SD30_DAT_DRIVE_SEL, 0xFF, 0x96); + + return rtsx_pci_send_cmd(pcr, 100); +} + +static int rts5227_optimize_phy(struct rtsx_pcr *pcr) +{ + /* Optimize RX sensitivity */ + return rtsx_pci_write_phy_register(pcr, 0x00, 0xBA42); +} + +static int rts5227_turn_on_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x02); +} + +static int rts5227_turn_off_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x00); +} + +static int rts5227_enable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x08); +} + +static int rts5227_disable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x00); +} + +static int rts5227_card_power_on(struct rtsx_pcr *pcr, int card) +{ + int err; + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, + SD_POWER_MASK, SD_PARTIAL_POWER_ON); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, + LDO3318_PWR_MASK, 0x02); + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + /* To avoid too large in-rush current */ + udelay(150); + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, + SD_POWER_MASK, SD_POWER_ON); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, + LDO3318_PWR_MASK, 0x06); + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + return 0; +} + +static int rts5227_card_power_off(struct rtsx_pcr *pcr, int card) +{ + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, + SD_POWER_MASK | PMOS_STRG_MASK, + SD_POWER_OFF | PMOS_STRG_400mA); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, + LDO3318_PWR_MASK, 0X00); + return rtsx_pci_send_cmd(pcr, 100); +} + +static int rts5227_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) +{ + int err; + u8 drive_sel; + + if (voltage == OUTPUT_3V3) { + err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24); + if (err < 0) + return err; + drive_sel = 0x96; + } else if (voltage == OUTPUT_1V8) { + err = rtsx_pci_write_phy_register(pcr, 0x11, 0x3C02); + if (err < 0) + return err; + err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C80 | 0x24); + if (err < 0) + return err; + drive_sel = 0xB3; + } else { + return -EINVAL; + } + + /* set pad drive */ + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CLK_DRIVE_SEL, + 0xFF, drive_sel); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CMD_DRIVE_SEL, + 0xFF, drive_sel); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DAT_DRIVE_SEL, + 0xFF, drive_sel); + return rtsx_pci_send_cmd(pcr, 100); +} + +static const struct pcr_ops rts5227_pcr_ops = { + .extra_init_hw = rts5227_extra_init_hw, + .optimize_phy = rts5227_optimize_phy, + .turn_on_led = rts5227_turn_on_led, + .turn_off_led = rts5227_turn_off_led, + .enable_auto_blink = rts5227_enable_auto_blink, + .disable_auto_blink = rts5227_disable_auto_blink, + .card_power_on = rts5227_card_power_on, + .card_power_off = rts5227_card_power_off, + .switch_output_voltage = rts5227_switch_output_voltage, + .cd_deglitch = NULL, + .conv_clk_and_div_n = NULL, +}; + +/* SD Pull Control Enable: + * SD_DAT[3:0] ==> pull up + * SD_CD ==> pull up + * SD_WP ==> pull up + * SD_CMD ==> pull up + * SD_CLK ==> pull down + */ +static const u32 rts5227_sd_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9), + 0, +}; + +/* SD Pull Control Disable: + * SD_DAT[3:0] ==> pull down + * SD_CD ==> pull up + * SD_WP ==> pull down + * SD_CMD ==> pull down + * SD_CLK ==> pull down + */ +static const u32 rts5227_sd_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5), + 0, +}; + +/* MS Pull Control Enable: + * MS CD ==> pull up + * others ==> pull down + */ +static const u32 rts5227_ms_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), + 0, +}; + +/* MS Pull Control Disable: + * MS CD ==> pull up + * others ==> pull down + */ +static const u32 rts5227_ms_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), + 0, +}; + +void rts5227_init_params(struct rtsx_pcr *pcr) +{ + pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104; + pcr->num_slots = 2; + pcr->ops = &rts5227_pcr_ops; + + pcr->sd_pull_ctl_enable_tbl = rts5227_sd_pull_ctl_enable_tbl; + pcr->sd_pull_ctl_disable_tbl = rts5227_sd_pull_ctl_disable_tbl; + pcr->ms_pull_ctl_enable_tbl = rts5227_ms_pull_ctl_enable_tbl; + pcr->ms_pull_ctl_disable_tbl = rts5227_ms_pull_ctl_disable_tbl; +} diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 9016932f0267..822237e322ba 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -55,6 +55,7 @@ static DEFINE_PCI_DEVICE_TABLE(rtsx_pci_ids) = { { PCI_DEVICE(0x10EC, 0x5209), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5229), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5289), PCI_CLASS_OTHERS << 16, 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5227), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { 0, } }; @@ -998,6 +999,10 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr) case 0x5289: rtl8411_init_params(pcr); break; + + case 0x5227: + rts5227_init_params(pcr); + break; } dev_dbg(&(pcr->pci->dev), "PID: 0x%04x, IC version: 0x%02x\n", diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h index 33c210be1daa..2b3ab8a04823 100644 --- a/drivers/mfd/rtsx_pcr.h +++ b/drivers/mfd/rtsx_pcr.h @@ -31,5 +31,6 @@ void rts5209_init_params(struct rtsx_pcr *pcr); void rts5229_init_params(struct rtsx_pcr *pcr); void rtl8411_init_params(struct rtsx_pcr *pcr); +void rts5227_init_params(struct rtsx_pcr *pcr); #endif diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 3f2bf26ca0d1..5d9b81e8aff4 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -581,8 +581,11 @@ #define CARD_GPIO_DIR 0xFD57 #define CARD_GPIO 0xFD58 #define CARD_DATA_SOURCE 0xFD5B +#define SD30_CLK_DRIVE_SEL 0xFD5A #define CARD_SELECT 0xFD5C #define SD30_DRIVE_SEL 0xFD5E +#define SD30_CMD_DRIVE_SEL 0xFD5E +#define SD30_DAT_DRIVE_SEL 0xFD5F #define CARD_CLK_EN 0xFD69 #define SDIO_CTRL 0xFD6B #define CD_PAD_CTL 0xFD73 @@ -655,6 +658,8 @@ #define MSGTXDATA3 0xFE47 #define MSGTXCTL 0xFE48 #define PETXCFG 0xFE49 +#define LTR_CTL 0xFE4A +#define OBFF_CFG 0xFE4C #define CDRESUMECTL 0xFE52 #define WAKE_SEL_CTL 0xFE54 -- cgit v1.2.3 From dcd560c8587171bb22c75c41ac2a70986bbbde7f Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Mon, 4 Feb 2013 18:08:02 +0000 Subject: mfd: vexpress: Allow vexpress-sysreg to self-initialise The vexpress_sysreg_init() is a core_initcall() already and it can trigger the early initialisation if a matching node is found. This patch allows the SoC code to avoid calling vexpress_sysreg_of_early_init() explicitly. Signed-off-by: Catalin Marinas Acked-by: Arnd Bergmann Acked-by: Pawel Moll Signed-off-by: Samuel Ortiz --- drivers/mfd/vexpress-sysreg.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index 51c3ca263bf5..a4a43230abcd 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -338,14 +338,15 @@ void __init vexpress_sysreg_early_init(void __iomem *base) void __init vexpress_sysreg_of_early_init(void) { - struct device_node *node = of_find_compatible_node(NULL, NULL, - "arm,vexpress-sysreg"); + struct device_node *node; + if (vexpress_sysreg_base) + return; + + node = of_find_compatible_node(NULL, NULL, "arm,vexpress-sysreg"); if (node) { vexpress_sysreg_base = of_iomap(node, 0); vexpress_sysreg_setup(node); - } else { - pr_info("vexpress-sysreg: No Device Tree node found."); } } @@ -515,6 +516,7 @@ static struct platform_driver vexpress_sysreg_driver = { static int __init vexpress_sysreg_init(void) { + vexpress_sysreg_of_early_init(); return platform_driver_register(&vexpress_sysreg_driver); } core_initcall(vexpress_sysreg_init); -- cgit v1.2.3 From 4e405ae256b7e04f7c1213136f3bfd9fb76e2023 Mon Sep 17 00:00:00 2001 From: Qing Xu Date: Mon, 4 Feb 2013 23:40:42 +0800 Subject: mfd: max8925: Add irqdomain for dt Add irqdomains for max8925's main irq, wrap irq register operations into irqdomain's map func. it is necessary for dt support. Also, add dt support for max8925 driver. Signed-off-by: Qing Xu Signed-off-by: Haojian Zhuang Signed-off-by: Samuel Ortiz --- drivers/mfd/max8925-core.c | 73 ++++++++++++++++++++++++++------------------- drivers/mfd/max8925-i2c.c | 36 ++++++++++++++++++++-- include/linux/mfd/max8925.h | 3 +- 3 files changed, 78 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c index e32466e865b9..0ad8d9a7c15a 100644 --- a/drivers/mfd/max8925-core.c +++ b/drivers/mfd/max8925-core.c @@ -14,10 +14,13 @@ #include #include #include +#include #include #include #include #include +#include +#include static struct resource bk_resources[] = { { 0x84, 0x84, "mode control", IORESOURCE_REG, }, @@ -639,17 +642,33 @@ static struct irq_chip max8925_irq_chip = { .irq_disable = max8925_irq_disable, }; +static int max8925_irq_domain_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_data(virq, d->host_data); + irq_set_chip_and_handler(virq, &max8925_irq_chip, handle_edge_irq); + irq_set_nested_thread(virq, 1); +#ifdef CONFIG_ARM + set_irq_flags(virq, IRQF_VALID); +#else + irq_set_noprobe(virq); +#endif + return 0; +} + +static struct irq_domain_ops max8925_irq_domain_ops = { + .map = max8925_irq_domain_map, + .xlate = irq_domain_xlate_onetwocell, +}; + + static int max8925_irq_init(struct max8925_chip *chip, int irq, struct max8925_platform_data *pdata) { unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; - int i, ret; - int __irq; + int ret; + struct device_node *node = chip->dev->of_node; - if (!pdata || !pdata->irq_base) { - dev_warn(chip->dev, "No interrupt support on IRQ base\n"); - return -EINVAL; - } /* clear all interrupts */ max8925_reg_read(chip->i2c, MAX8925_CHG_IRQ1); max8925_reg_read(chip->i2c, MAX8925_CHG_IRQ2); @@ -667,35 +686,30 @@ static int max8925_irq_init(struct max8925_chip *chip, int irq, max8925_reg_write(chip->rtc, MAX8925_RTC_IRQ_MASK, 0xff); mutex_init(&chip->irq_lock); - chip->core_irq = irq; - chip->irq_base = pdata->irq_base; - - /* register with genirq */ - for (i = 0; i < ARRAY_SIZE(max8925_irqs); i++) { - __irq = i + chip->irq_base; - irq_set_chip_data(__irq, chip); - irq_set_chip_and_handler(__irq, &max8925_irq_chip, - handle_edge_irq); - irq_set_nested_thread(__irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(__irq, IRQF_VALID); -#else - irq_set_noprobe(__irq); -#endif - } - if (!irq) { - dev_warn(chip->dev, "No interrupt support on core IRQ\n"); - goto tsc_irq; + chip->irq_base = irq_alloc_descs(-1, 0, MAX8925_NR_IRQS, 0); + if (chip->irq_base < 0) { + dev_err(chip->dev, "Failed to allocate interrupts, ret:%d\n", + chip->irq_base); + return -EBUSY; } + irq_domain_add_legacy(node, MAX8925_NR_IRQS, chip->irq_base, 0, + &max8925_irq_domain_ops, chip); + + /* request irq handler for pmic main irq*/ + chip->core_irq = irq; + if (!chip->core_irq) + return -EBUSY; ret = request_threaded_irq(irq, NULL, max8925_irq, flags | IRQF_ONESHOT, "max8925", chip); if (ret) { dev_err(chip->dev, "Failed to request core IRQ: %d\n", ret); chip->core_irq = 0; + return -EBUSY; } -tsc_irq: + /* request irq handler for pmic tsc irq*/ + /* mask TSC interrupt */ max8925_reg_write(chip->adc, MAX8925_TSC_IRQ_MASK, 0x0f); @@ -704,7 +718,6 @@ tsc_irq: return 0; } chip->tsc_irq = pdata->tsc_irq; - ret = request_threaded_irq(chip->tsc_irq, NULL, max8925_tsc_irq, flags | IRQF_ONESHOT, "max8925-tsc", chip); if (ret) { @@ -875,11 +888,11 @@ int max8925_device_init(struct max8925_chip *chip, if (pdata && pdata->power) { ret = mfd_add_devices(chip->dev, 0, &power_devs[0], - ARRAY_SIZE(power_devs), + ARRAY_SIZE(power_devs), &power_supply_resources[0], 0, NULL); if (ret < 0) { - dev_err(chip->dev, "Failed to add power supply " - "subdev\n"); + dev_err(chip->dev, + "Failed to add power supply subdev\n"); goto out_dev; } } diff --git a/drivers/mfd/max8925-i2c.c b/drivers/mfd/max8925-i2c.c index 00b5b456063d..92bbebd31598 100644 --- a/drivers/mfd/max8925-i2c.c +++ b/drivers/mfd/max8925-i2c.c @@ -135,13 +135,37 @@ static const struct i2c_device_id max8925_id_table[] = { }; MODULE_DEVICE_TABLE(i2c, max8925_id_table); +static int max8925_dt_init(struct device_node *np, struct device *dev, + struct max8925_platform_data *pdata) +{ + int ret; + + ret = of_property_read_u32(np, "maxim,tsc-irq", &pdata->tsc_irq); + if (ret) { + dev_err(dev, "Not found maxim,tsc-irq property\n"); + return -EINVAL; + } + return 0; +} + static int max8925_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct max8925_platform_data *pdata = client->dev.platform_data; static struct max8925_chip *chip; - - if (!pdata) { + struct device_node *node = client->dev.of_node; + + if (node && !pdata) { + /* parse DT to get platform data */ + pdata = devm_kzalloc(&client->dev, + sizeof(struct max8925_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (max8925_dt_init(node, &client->dev, pdata)) + return -EINVAL; + } else if (!pdata) { pr_info("%s: platform data is missing\n", __func__); return -EINVAL; } @@ -203,11 +227,18 @@ static int max8925_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(max8925_pm_ops, max8925_suspend, max8925_resume); +static const struct of_device_id max8925_dt_ids[] = { + { .compatible = "maxim,max8925", }, + {}, +}; +MODULE_DEVICE_TABLE(of, max8925_dt_ids); + static struct i2c_driver max8925_driver = { .driver = { .name = "max8925", .owner = THIS_MODULE, .pm = &max8925_pm_ops, + .of_match_table = of_match_ptr(max8925_dt_ids), }, .probe = max8925_probe, .remove = max8925_remove, @@ -217,7 +248,6 @@ static struct i2c_driver max8925_driver = { static int __init max8925_i2c_init(void) { int ret; - ret = i2c_add_driver(&max8925_driver); if (ret != 0) pr_err("Failed to register MAX8925 I2C driver: %d\n", ret); diff --git a/include/linux/mfd/max8925.h b/include/linux/mfd/max8925.h index 74d8e2969630..ce8502e9e7dc 100644 --- a/include/linux/mfd/max8925.h +++ b/include/linux/mfd/max8925.h @@ -190,6 +190,8 @@ enum { MAX8925_NR_IRQS, }; + + struct max8925_chip { struct device *dev; struct i2c_client *i2c; @@ -201,7 +203,6 @@ struct max8925_chip { int irq_base; int core_irq; int tsc_irq; - unsigned int wakeup_flag; }; -- cgit v1.2.3 From f9ed143180ffe94a617671aa4ed4106ae183407c Mon Sep 17 00:00:00 2001 From: Qing Xu Date: Mon, 4 Feb 2013 23:40:43 +0800 Subject: mfd: max8925: Fix mfd device register failure we encounter rtc/power/touch driver registry failure, root cause it is resources confilict in insert_resouce, solved by changing mfd_add_devices 5th parameter to NULL Signed-off-by: Qing Xu Signed-off-by: Samuel Ortiz --- drivers/mfd/max8925-core.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c index 0ad8d9a7c15a..d7abbb354acd 100644 --- a/drivers/mfd/max8925-core.c +++ b/drivers/mfd/max8925-core.c @@ -859,7 +859,7 @@ int max8925_device_init(struct max8925_chip *chip, ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0], ARRAY_SIZE(rtc_devs), - &rtc_resources[0], chip->irq_base, NULL); + NULL, chip->irq_base, NULL); if (ret < 0) { dev_err(chip->dev, "Failed to add rtc subdev\n"); goto out; @@ -867,7 +867,7 @@ int max8925_device_init(struct max8925_chip *chip, ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0], ARRAY_SIZE(onkey_devs), - &onkey_resources[0], 0, NULL); + NULL, 0, NULL); if (ret < 0) { dev_err(chip->dev, "Failed to add onkey subdev\n"); goto out_dev; @@ -886,21 +886,19 @@ int max8925_device_init(struct max8925_chip *chip, goto out_dev; } - if (pdata && pdata->power) { - ret = mfd_add_devices(chip->dev, 0, &power_devs[0], - ARRAY_SIZE(power_devs), - &power_supply_resources[0], 0, NULL); - if (ret < 0) { - dev_err(chip->dev, - "Failed to add power supply subdev\n"); - goto out_dev; - } + ret = mfd_add_devices(chip->dev, 0, &power_devs[0], + ARRAY_SIZE(power_devs), + NULL, 0, NULL); + if (ret < 0) { + dev_err(chip->dev, + "Failed to add power supply subdev, err = %d\n", ret); + goto out_dev; } if (pdata && pdata->touch) { ret = mfd_add_devices(chip->dev, 0, &touch_devs[0], ARRAY_SIZE(touch_devs), - &touch_resources[0], 0, NULL); + NULL, chip->tsc_irq, NULL); if (ret < 0) { dev_err(chip->dev, "Failed to add touch subdev\n"); goto out_dev; -- cgit v1.2.3 From 678e8cb50647a7580b8122937e8e6e50d949267d Mon Sep 17 00:00:00 2001 From: Qing Xu Date: Mon, 4 Feb 2013 23:40:44 +0800 Subject: mfd: max8925: Fix onkey driver irq base update onkey driver's irq base, it should get from max8925, but not save in a private value Signed-off-by: Qing Xu Acked-by: Dmitry Torokhov Signed-off-by: Samuel Ortiz --- drivers/input/misc/max8925_onkey.c | 3 --- drivers/mfd/max8925-core.c | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/input/misc/max8925_onkey.c b/drivers/input/misc/max8925_onkey.c index 369a39de4ff3..f9179b2585a9 100644 --- a/drivers/input/misc/max8925_onkey.c +++ b/drivers/input/misc/max8925_onkey.c @@ -100,9 +100,6 @@ static int max8925_onkey_probe(struct platform_device *pdev) input->dev.parent = &pdev->dev; input_set_capability(input, EV_KEY, KEY_POWER); - irq[0] += chip->irq_base; - irq[1] += chip->irq_base; - error = request_threaded_irq(irq[0], NULL, max8925_onkey_handler, IRQF_ONESHOT, "onkey-down", info); if (error < 0) { diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c index d7abbb354acd..f0cc40296d8c 100644 --- a/drivers/mfd/max8925-core.c +++ b/drivers/mfd/max8925-core.c @@ -867,7 +867,7 @@ int max8925_device_init(struct max8925_chip *chip, ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0], ARRAY_SIZE(onkey_devs), - NULL, 0, NULL); + NULL, chip->irq_base, NULL); if (ret < 0) { dev_err(chip->dev, "Failed to add onkey subdev\n"); goto out_dev; -- cgit v1.2.3 From 47ec340cb8e232671e7c4a4689ff32c3bdf329da Mon Sep 17 00:00:00 2001 From: Qing Xu Date: Mon, 4 Feb 2013 23:40:45 +0800 Subject: mfd: max8925: Support dt for backlight Add device tree support in max8925 backlight. Signed-off-by: Qing Xu Signed-off-by: Haojian Zhuang Signed-off-by: Samuel Ortiz --- drivers/video/backlight/max8925_bl.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/backlight/max8925_bl.c b/drivers/video/backlight/max8925_bl.c index 2c9bce050aa9..5ca11b066b7e 100644 --- a/drivers/video/backlight/max8925_bl.c +++ b/drivers/video/backlight/max8925_bl.c @@ -101,6 +101,29 @@ static const struct backlight_ops max8925_backlight_ops = { .get_brightness = max8925_backlight_get_brightness, }; +#ifdef CONFIG_OF +static int max8925_backlight_dt_init(struct platform_device *pdev, + struct max8925_backlight_pdata *pdata) +{ + struct device_node *nproot = pdev->dev.parent->of_node, *np; + int dual_string; + + if (!nproot) + return -ENODEV; + np = of_find_node_by_name(nproot, "backlight"); + if (!np) { + dev_err(&pdev->dev, "failed to find backlight node\n"); + return -ENODEV; + } + + of_property_read_u32(np, "maxim,max8925-dual-string", &dual_string); + pdata->dual_string = dual_string; + return 0; +} +#else +#define max8925_backlight_dt_init(x, y) (-1) +#endif + static int max8925_backlight_probe(struct platform_device *pdev) { struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); @@ -147,6 +170,13 @@ static int max8925_backlight_probe(struct platform_device *pdev) platform_set_drvdata(pdev, bl); value = 0; + if (pdev->dev.parent->of_node && !pdata) { + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct max8925_backlight_pdata), + GFP_KERNEL); + max8925_backlight_dt_init(pdev, pdata); + } + if (pdata) { if (pdata->lxw_scl) value |= (1 << 7); @@ -158,7 +188,6 @@ static int max8925_backlight_probe(struct platform_device *pdev) ret = max8925_set_bits(chip->i2c, data->reg_mode_cntl, 0xfe, value); if (ret < 0) goto out_brt; - backlight_update_status(bl); return 0; out_brt: -- cgit v1.2.3 From 21f792cde17f71091252540b79e9312a8d1a7ac2 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 6 Feb 2013 17:23:51 +0100 Subject: mfd: Add missing GENERIC_HARDIRQS dependecies A lot of mfd drivers select MFD_CORE which however depends on GENERIC_HARDIRQS support. So add the missing dependency to all drivers to get rid of this link error: ERROR: "irq_create_mapping" [drivers/mfd/mfd-core.ko] undefined! Signed-off-by: Heiko Carstens Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 72 ++++++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index ff553babf455..671f5b171c73 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -65,7 +65,7 @@ config MFD_SM501_GPIO config MFD_RTSX_PCI tristate "Support for Realtek PCI-E card reader" - depends on PCI + depends on PCI && GENERIC_HARDIRQS select MFD_CORE help This supports for Realtek PCI-Express card reader including rts5209, @@ -95,7 +95,7 @@ config MFD_DM355EVM_MSP config MFD_TI_SSP tristate "TI Sequencer Serial Port support" - depends on ARCH_DAVINCI_TNETV107X + depends on ARCH_DAVINCI_TNETV107X && GENERIC_HARDIRQS select MFD_CORE ---help--- Say Y here if you want support for the Sequencer Serial Port @@ -109,6 +109,7 @@ config MFD_TI_AM335X_TSCADC select MFD_CORE select REGMAP select REGMAP_MMIO + depends on GENERIC_HARDIRQS help If you say yes here you get support for Texas Instruments series of Touch Screen /ADC chips. @@ -126,6 +127,7 @@ config HTC_EGPIO config HTC_PASIC3 tristate "HTC PASIC3 LED/DS1WM chip support" select MFD_CORE + depends on GENERIC_HARDIRQS help This core driver provides register access for the LED/DS1WM chips labeled "AIC2" and "AIC3", found on HTC Blueangel and @@ -157,6 +159,7 @@ config MFD_LM3533 depends on I2C select MFD_CORE select REGMAP_I2C + depends on GENERIC_HARDIRQS help Say yes here to enable support for National Semiconductor / TI LM3533 Lighting Power chips. @@ -171,6 +174,7 @@ config TPS6105X select REGULATOR select MFD_CORE select REGULATOR_FIXED_VOLTAGE + depends on GENERIC_HARDIRQS help This option enables a driver for the TP61050/TPS61052 high-power "white LED driver". This boost converter is @@ -193,7 +197,7 @@ config TPS65010 config TPS6507X tristate "TPS6507x Power Management / Touch Screen chips" select MFD_CORE - depends on I2C + depends on I2C && GENERIC_HARDIRQS help If you say yes here you get support for the TPS6507x series of Power Management / Touch Screen chips. These include voltage @@ -204,7 +208,7 @@ config TPS6507X config MFD_TPS65217 tristate "TPS65217 Power Management / White LED chips" - depends on I2C + depends on I2C && GENERIC_HARDIRQS select MFD_CORE select REGMAP_I2C help @@ -234,7 +238,7 @@ config MFD_TPS6586X config MFD_TPS65910 bool "TPS65910 Power Management chip" - depends on I2C=y && GPIOLIB + depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS select MFD_CORE select REGMAP_I2C select REGMAP_IRQ @@ -251,7 +255,7 @@ config MFD_TPS65912_I2C bool "TPS65912 Power Management chip with I2C" select MFD_CORE select MFD_TPS65912 - depends on I2C=y && GPIOLIB + depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS help If you say yes here you get support for the TPS65912 series of PM chips with I2C interface. @@ -260,7 +264,7 @@ config MFD_TPS65912_SPI bool "TPS65912 Power Management chip with SPI" select MFD_CORE select MFD_TPS65912 - depends on SPI_MASTER && GPIOLIB + depends on SPI_MASTER && GPIOLIB && GENERIC_HARDIRQS help If you say yes here you get support for the TPS65912 series of PM chips with SPI interface. @@ -330,13 +334,13 @@ config TWL4030_POWER config MFD_TWL4030_AUDIO bool - depends on TWL4030_CORE + depends on TWL4030_CORE && GENERIC_HARDIRQS select MFD_CORE default n config TWL6040_CORE bool "Support for TWL6040 audio codec" - depends on I2C=y + depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE select REGMAP_I2C select REGMAP_IRQ @@ -405,7 +409,7 @@ config MFD_TMIO config MFD_T7L66XB bool "Support Toshiba T7L66XB" - depends on ARM && HAVE_CLK + depends on ARM && HAVE_CLK && GENERIC_HARDIRQS select MFD_CORE select MFD_TMIO help @@ -413,7 +417,7 @@ config MFD_T7L66XB config MFD_SMSC bool "Support for the SMSC ECE1099 series chips" - depends on I2C=y + depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE select REGMAP_I2C help @@ -460,7 +464,7 @@ config MFD_DA9052_SPI select REGMAP_SPI select REGMAP_IRQ select PMIC_DA9052 - depends on SPI_MASTER=y + depends on SPI_MASTER=y && GENERIC_HARDIRQS help Support for the Dialog Semiconductor DA9052 PMIC when controlled using SPI. This driver provides common support @@ -472,7 +476,7 @@ config MFD_DA9052_I2C select REGMAP_I2C select REGMAP_IRQ select PMIC_DA9052 - depends on I2C=y + depends on I2C=y && GENERIC_HARDIRQS help Support for the Dialog Semiconductor DA9052 PMIC when controlled using I2C. This driver provides common support @@ -485,7 +489,7 @@ config MFD_DA9055 select REGMAP_IRQ select PMIC_DA9055 select MFD_CORE - depends on I2C=y + depends on I2C=y && GENERIC_HARDIRQS help Say yes here for support of Dialog Semiconductor DA9055. This is a Power Management IC. This driver provides common support for @@ -508,7 +512,7 @@ config PMIC_ADP5520 config MFD_LP8788 bool "Texas Instruments LP8788 Power Management Unit Driver" - depends on I2C=y + depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE select REGMAP_I2C select IRQ_DOMAIN @@ -611,7 +615,7 @@ config MFD_ARIZONA_I2C select MFD_ARIZONA select MFD_CORE select REGMAP_I2C - depends on I2C + depends on I2C && GENERIC_HARDIRQS help Support for the Wolfson Microelectronics Arizona platform audio SoC core functionality controlled via I2C. @@ -621,7 +625,7 @@ config MFD_ARIZONA_SPI select MFD_ARIZONA select MFD_CORE select REGMAP_SPI - depends on SPI_MASTER + depends on SPI_MASTER && GENERIC_HARDIRQS help Support for the Wolfson Microelectronics Arizona platform audio SoC core functionality controlled via I2C. @@ -641,7 +645,7 @@ config MFD_WM5110 config MFD_WM8400 bool "Support Wolfson Microelectronics WM8400" select MFD_CORE - depends on I2C=y + depends on I2C=y && GENERIC_HARDIRQS select REGMAP_I2C help Support for the Wolfson Microelecronics WM8400 PMIC and audio @@ -785,7 +789,7 @@ config MFD_MC13783 config MFD_MC13XXX tristate - depends on SPI_MASTER || I2C + depends on (SPI_MASTER || I2C) && GENERIC_HARDIRQS select MFD_CORE select MFD_MC13783 help @@ -796,7 +800,7 @@ config MFD_MC13XXX config MFD_MC13XXX_SPI tristate "Freescale MC13783 and MC13892 SPI interface" - depends on SPI_MASTER + depends on SPI_MASTER && GENERIC_HARDIRQS select REGMAP_SPI select MFD_MC13XXX help @@ -804,7 +808,7 @@ config MFD_MC13XXX_SPI config MFD_MC13XXX_I2C tristate "Freescale MC13892 I2C interface" - depends on I2C + depends on I2C && GENERIC_HARDIRQS select REGMAP_I2C select MFD_MC13XXX help @@ -822,7 +826,7 @@ config ABX500_CORE config AB3100_CORE bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions" - depends on I2C=y && ABX500_CORE + depends on I2C=y && ABX500_CORE && GENERIC_HARDIRQS select MFD_CORE default y if ARCH_U300 help @@ -909,7 +913,7 @@ config MFD_TIMBERDALE config LPC_SCH tristate "Intel SCH LPC" - depends on PCI + depends on PCI && GENERIC_HARDIRQS select MFD_CORE help LPC bridge function of the Intel SCH provides support for @@ -917,7 +921,7 @@ config LPC_SCH config LPC_ICH tristate "Intel ICH LPC" - depends on PCI + depends on PCI && GENERIC_HARDIRQS select MFD_CORE help The LPC bridge function of the Intel ICH provides support for @@ -928,7 +932,7 @@ config LPC_ICH config MFD_RDC321X tristate "Support for RDC-R321x southbridge" select MFD_CORE - depends on PCI + depends on PCI && GENERIC_HARDIRQS help Say yes here if you want to have support for the RDC R-321x SoC southbridge which provides access to GPIOs and Watchdog using the @@ -937,7 +941,7 @@ config MFD_RDC321X config MFD_JANZ_CMODIO tristate "Support for Janz CMOD-IO PCI MODULbus Carrier Board" select MFD_CORE - depends on PCI + depends on PCI && GENERIC_HARDIRQS help This is the core driver for the Janz CMOD-IO PCI MODULbus carrier board. This device is a PCI to MODULbus bridge which may @@ -955,7 +959,7 @@ config MFD_JZ4740_ADC config MFD_VX855 tristate "Support for VIA VX855/VX875 integrated south bridge" - depends on PCI + depends on PCI && GENERIC_HARDIRQS select MFD_CORE help Say yes here to enable support for various functions of the @@ -964,7 +968,7 @@ config MFD_VX855 config MFD_WL1273_CORE tristate "Support for TI WL1273 FM radio." - depends on I2C + depends on I2C && GENERIC_HARDIRQS select MFD_CORE default n help @@ -1028,7 +1032,7 @@ config MFD_TPS65090 config MFD_AAT2870_CORE bool "Support for the AnalogicTech AAT2870" select MFD_CORE - depends on I2C=y && GPIOLIB + depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS help If you say yes here you get support for the AAT2870. This driver provides common support for accessing the device, @@ -1060,7 +1064,7 @@ config MFD_RC5T583 config MFD_STA2X11 bool "STA2X11 multi function device support" - depends on STA2X11 + depends on STA2X11 && GENERIC_HARDIRQS select MFD_CORE select REGMAP_MMIO @@ -1077,7 +1081,7 @@ config MFD_PALMAS select MFD_CORE select REGMAP_I2C select REGMAP_IRQ - depends on I2C=y + depends on I2C=y && GENERIC_HARDIRQS help If you say yes here you get support for the Palmas series of PMIC chips from Texas Instruments. @@ -1085,7 +1089,7 @@ config MFD_PALMAS config MFD_VIPERBOARD tristate "Support for Nano River Technologies Viperboard" select MFD_CORE - depends on USB + depends on USB && GENERIC_HARDIRQS default n help Say yes here if you want support for Nano River Technologies @@ -1099,7 +1103,7 @@ config MFD_VIPERBOARD config MFD_RETU tristate "Support for Retu multi-function device" select MFD_CORE - depends on I2C + depends on I2C && GENERIC_HARDIRQS select REGMAP_IRQ help Retu is a multi-function device found on Nokia Internet Tablets @@ -1110,7 +1114,7 @@ config MFD_AS3711 select MFD_CORE select REGMAP_I2C select REGMAP_IRQ - depends on I2C=y + depends on I2C=y && GENERIC_HARDIRQS help Support for the AS3711 PMIC from AMS -- cgit v1.2.3 From 0cd5b6d08c7bf8b2d81eb1413ea1463cc72487b1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 6 Feb 2013 23:23:01 +0100 Subject: mfd: ab8500: Fix compile error When compiling the AB8500 core driver in the latest MFD tree the following happens: CC drivers/mfd/ab8500-debugfs.o /home/elinwal/linux-next/drivers/mfd/ab8500-debugfs.c:157:3: error: 'AB8500_SYS_CTRL1_BLOCK' undeclared here (not in a function) /home/elinwal/linux-next/drivers/mfd/ab8500-debugfs.c:157:2: error: array index in initializer not of integer type /home/elinwal/linux-next/drivers/mfd/ab8500-debugfs.c:157:2: error: (near initialization for 'debug_ranges') (...) This is due to a missing include statement, so fix it up. Cc: Lee Jones Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-debugfs.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index ba25f95e1677..45fe3c50eb03 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -82,6 +82,7 @@ #include #include +#include #include #ifdef CONFIG_DEBUG_FS -- cgit v1.2.3 From c3481955f6c78c8dd99921759306d7469c999ec2 Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Fri, 8 Feb 2013 15:24:27 +0800 Subject: mfd: rtsx: Fix issue that booting OS with SD card inserted Realtek card reader supports both SD and MS card. According to the settings of rtsx MFD driver, SD host will be probed before MS host. If we boot/reboot Linux with SD card inserted, the resetting flow of SD card will succeed, and the following resetting flow of MS is sure to fail. Then MS upper-level driver will ask rtsx driver to turn power off. This request leads to the result that the following SD commands fail and SD card can't be accessed again. In this commit, Realtek's SD and MS host driver will check whether the card that upper driver requesting is the one existing in the slot. If not, Realtek's host driver will refuse the operation to make sure the exlusive accessing at the same time. Signed-off-by: Wei WANG Signed-off-by: Samuel Ortiz --- drivers/memstick/host/rtsx_pci_ms.c | 7 +++++++ drivers/mfd/rtsx_pcr.c | 30 ++++++++++++++++++++++++++++++ drivers/mmc/host/rtsx_pci_sdmmc.c | 18 ++++++++++++++++++ include/linux/mfd/rtsx_pci.h | 2 ++ 4 files changed, 57 insertions(+) (limited to 'drivers') diff --git a/drivers/memstick/host/rtsx_pci_ms.c b/drivers/memstick/host/rtsx_pci_ms.c index f5ddb82dadb7..64a779c58a74 100644 --- a/drivers/memstick/host/rtsx_pci_ms.c +++ b/drivers/memstick/host/rtsx_pci_ms.c @@ -426,6 +426,9 @@ static void rtsx_pci_ms_request(struct memstick_host *msh) dev_dbg(ms_dev(host), "--> %s\n", __func__); + if (rtsx_pci_card_exclusive_check(host->pcr, RTSX_MS_CARD)) + return; + schedule_work(&host->handle_req); } @@ -441,6 +444,10 @@ static int rtsx_pci_ms_set_param(struct memstick_host *msh, dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n", __func__, param, value); + err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_MS_CARD); + if (err) + return err; + switch (param) { case MEMSTICK_POWER: if (value == MEMSTICK_POWER_ON) diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 822237e322ba..481a98a10ecd 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -708,6 +708,25 @@ int rtsx_pci_card_power_off(struct rtsx_pcr *pcr, int card) } EXPORT_SYMBOL_GPL(rtsx_pci_card_power_off); +int rtsx_pci_card_exclusive_check(struct rtsx_pcr *pcr, int card) +{ + unsigned int cd_mask[] = { + [RTSX_SD_CARD] = SD_EXIST, + [RTSX_MS_CARD] = MS_EXIST + }; + + if (!pcr->ms_pmos) { + /* When using single PMOS, accessing card is not permitted + * if the existing card is not the designated one. + */ + if (pcr->card_exist & (~cd_mask[card])) + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL_GPL(rtsx_pci_card_exclusive_check); + int rtsx_pci_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) { if (pcr->ops->switch_output_voltage) @@ -784,6 +803,9 @@ static void rtsx_pci_card_detect(struct work_struct *work) card_inserted = pcr->ops->cd_deglitch(pcr); card_detect = card_inserted | card_removed; + + pcr->card_exist |= card_inserted; + pcr->card_exist &= ~card_removed; } mutex_unlock(&pcr->pcr_mutex); @@ -976,6 +998,14 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) return err; } + /* No CD interrupt if probing driver with card inserted. + * So we need to initialize pcr->card_exist here. + */ + if (pcr->ops->cd_deglitch) + pcr->card_exist = pcr->ops->cd_deglitch(pcr); + else + pcr->card_exist = rtsx_pci_readl(pcr, RTSX_BIPR) & CARD_EXIST; + return 0; } diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index f74b5adca642..468c92303167 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -678,12 +678,19 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) struct mmc_command *cmd = mrq->cmd; struct mmc_data *data = mrq->data; unsigned int data_size = 0; + int err; if (host->eject) { cmd->error = -ENOMEDIUM; goto finish; } + err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD); + if (err) { + cmd->error = err; + goto finish; + } + mutex_lock(&pcr->pcr_mutex); rtsx_pci_start_run(pcr); @@ -901,6 +908,9 @@ static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (host->eject) return; + if (rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD)) + return; + mutex_lock(&pcr->pcr_mutex); rtsx_pci_start_run(pcr); @@ -1073,6 +1083,10 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) if (host->eject) return -ENOMEDIUM; + err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD); + if (err) + return err; + mutex_lock(&pcr->pcr_mutex); rtsx_pci_start_run(pcr); @@ -1122,6 +1136,10 @@ static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode) if (host->eject) return -ENOMEDIUM; + err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD); + if (err) + return err; + mutex_lock(&pcr->pcr_mutex); rtsx_pci_start_run(pcr); diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 5d9b81e8aff4..26ea7f1b7caf 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -740,6 +740,7 @@ struct rtsx_pcr { unsigned int card_inserted; unsigned int card_removed; + unsigned int card_exist; struct delayed_work carddet_work; struct delayed_work idle_work; @@ -804,6 +805,7 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk); int rtsx_pci_card_power_on(struct rtsx_pcr *pcr, int card); int rtsx_pci_card_power_off(struct rtsx_pcr *pcr, int card); +int rtsx_pci_card_exclusive_check(struct rtsx_pcr *pcr, int card); int rtsx_pci_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage); unsigned int rtsx_pci_card_exist(struct rtsx_pcr *pcr); void rtsx_pci_complete_unfinished_transfer(struct rtsx_pcr *pcr); -- cgit v1.2.3 From 1765dbccaa5aa2db7b53dc765f0e636591876c03 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Mon, 11 Feb 2013 14:26:19 -0600 Subject: mfd: twl-core: Fix kernel panic on boot Commit 8a6aaa3 (mfd: twl-core: Collect global variables behind one private structure (global)) removed the variable "inuse" that is used to determine if the device has been initialised and now use the twl_priv structure instead. This is causing the kernel to panic on OMAP3+ devices using the twl driver, because we try to access the twl_priv->ready member before checking if twl_priv is initialised. Fix this and move this test to the beginning of the twl_i2c_read/write function because twl_get_last_module() also uses the twl_priv structure. Signed-off-by: Jon Hunter Acked-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 557f9ee5ec18..89ab4d970643 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -316,12 +316,12 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) int sid; struct twl_client *twl; - if (unlikely(mod_no >= twl_get_last_module())) { - pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); + if (unlikely(!twl_priv || !twl_priv->ready)) { + pr_err("%s: not initialized\n", DRIVER_NAME); return -EPERM; } - if (unlikely(!twl_priv->ready)) { - pr_err("%s: not initialized\n", DRIVER_NAME); + if (unlikely(mod_no >= twl_get_last_module())) { + pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } @@ -355,12 +355,12 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) int sid; struct twl_client *twl; - if (unlikely(mod_no >= twl_get_last_module())) { - pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); + if (unlikely(!twl_priv || !twl_priv->ready)) { + pr_err("%s: not initialized\n", DRIVER_NAME); return -EPERM; } - if (unlikely(!twl_priv->ready)) { - pr_err("%s: not initialized\n", DRIVER_NAME); + if (unlikely(mod_no >= twl_get_last_module())) { + pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } -- cgit v1.2.3 From 75177deee91c17b374b827a832c0d2061ce0f07e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 11 Feb 2013 18:48:00 -0200 Subject: mfd: syscon: Fix sparse warning Fix the following sparse warnings: drivers/mfd/syscon.c:40:15: warning: symbol 'syscon_node_to_regmap' was not declared. Should it be static? drivers/mfd/syscon.c:56:15: warning: symbol 'syscon_regmap_lookup_by_compatible' was not declared. Should it be static? drivers/mfd/syscon.c:72:15: warning: symbol 'syscon_regmap_lookup_by_phandle' was not declared. Should it be static? Cc: Dong Aisheng Signed-off-by: Fabio Estevam Signed-off-by: Samuel Ortiz --- drivers/mfd/syscon.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index 3f10591ea94e..61aea6381cdf 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -20,6 +20,7 @@ #include #include #include +#include static struct platform_driver syscon_driver; -- cgit v1.2.3 From c3e9e6b67db23ae678550f861b5679880481cfa3 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 11 Feb 2013 18:48:01 -0200 Subject: mfd: da9052-i2c: Staticize da9052_i2c_fix() da9052_i2c_fix() is only used locally, so let it be static. Fix the following sparse warning: drivers/mfd/da9052-i2c.c:63:5: warning: symbol 'da9052_i2c_fix' was not declared. Should it be static? Cc: Ashish Jangam Signed-off-by: Fabio Estevam Signed-off-by: Samuel Ortiz --- drivers/mfd/da9052-i2c.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c index 885e56780358..6a9fec40d018 100644 --- a/drivers/mfd/da9052-i2c.c +++ b/drivers/mfd/da9052-i2c.c @@ -60,7 +60,7 @@ static inline bool i2c_safe_reg(unsigned char reg) * This fix is to follow any read or write with a dummy read to a safe * register. */ -int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg) +static int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg) { int val; @@ -85,7 +85,6 @@ int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg) return 0; } -EXPORT_SYMBOL(da9052_i2c_fix); static int da9052_i2c_enable_multiwrite(struct da9052 *da9052) { -- cgit v1.2.3 From 5829e9b64e657560e840dc0ecfee177cb002cd69 Mon Sep 17 00:00:00 2001 From: Darren Hart Date: Fri, 8 Feb 2013 15:20:36 -0800 Subject: mfd: lpc_sch: Accomodate partial population of the MFD devices The current probe aborts if any of the 3 base address registers are disabled. On a TunnelCreek system I am working on, this resulted in the SMBIOS and GPIO devices being removed when it couldn't read the base address for the watchdog timer. This patch accommodates partial population of the lpc_sch_cells array and only aborts if all the base address registers are disabled. A max size array is allocated and the individual device cells are added to it after their base addresses are successfully determined. This simplifies the code a bit by removing the need for the separate tunnelcreek cells array and combining some of the add/remove logic. Cc: Grant Likely , Cc: Denis Turischev , Cc: Greg Kroah-Hartman , Cc: Linus Walleij Signed-off-by: Darren Hart Signed-off-by: Samuel Ortiz --- drivers/mfd/lpc_sch.c | 147 ++++++++++++++++++++++++-------------------------- 1 file changed, 71 insertions(+), 76 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c index 5624fcbba69b..8cc6aac27cb2 100644 --- a/drivers/mfd/lpc_sch.c +++ b/drivers/mfd/lpc_sch.c @@ -45,34 +45,32 @@ static struct resource smbus_sch_resource = { .flags = IORESOURCE_IO, }; - static struct resource gpio_sch_resource = { .flags = IORESOURCE_IO, }; -static struct mfd_cell lpc_sch_cells[] = { - { - .name = "isch_smbus", - .num_resources = 1, - .resources = &smbus_sch_resource, - }, - { - .name = "sch_gpio", - .num_resources = 1, - .resources = &gpio_sch_resource, - }, -}; - static struct resource wdt_sch_resource = { .flags = IORESOURCE_IO, }; -static struct mfd_cell tunnelcreek_cells[] = { - { - .name = "ie6xx_wdt", - .num_resources = 1, - .resources = &wdt_sch_resource, - }, +static struct mfd_cell lpc_sch_cells[3]; + +static struct mfd_cell isch_smbus_cell = { + .name = "isch_smbus", + .num_resources = 1, + .resources = &smbus_sch_resource, +}; + +static struct mfd_cell sch_gpio_cell = { + .name = "sch_gpio", + .num_resources = 1, + .resources = &gpio_sch_resource, +}; + +static struct mfd_cell wdt_sch_cell = { + .name = "ie6xx_wdt", + .num_resources = 1, + .resources = &wdt_sch_resource, }; static DEFINE_PCI_DEVICE_TABLE(lpc_sch_ids) = { @@ -88,79 +86,76 @@ static int lpc_sch_probe(struct pci_dev *dev, { unsigned int base_addr_cfg; unsigned short base_addr; - int i; + int i, cells = 0; int ret; pci_read_config_dword(dev, SMBASE, &base_addr_cfg); - if (!(base_addr_cfg & (1 << 31))) { - dev_err(&dev->dev, "Decode of the SMBus I/O range disabled\n"); - return -ENODEV; - } - base_addr = (unsigned short)base_addr_cfg; - if (base_addr == 0) { - dev_err(&dev->dev, "I/O space for SMBus uninitialized\n"); - return -ENODEV; - } - - smbus_sch_resource.start = base_addr; - smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1; + base_addr = 0; + if (!(base_addr_cfg & (1 << 31))) + dev_warn(&dev->dev, "Decode of the SMBus I/O range disabled\n"); + else + base_addr = (unsigned short)base_addr_cfg; - pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg); - if (!(base_addr_cfg & (1 << 31))) { - dev_err(&dev->dev, "Decode of the GPIO I/O range disabled\n"); - return -ENODEV; - } - base_addr = (unsigned short)base_addr_cfg; if (base_addr == 0) { - dev_err(&dev->dev, "I/O space for GPIO uninitialized\n"); - return -ENODEV; + dev_warn(&dev->dev, "I/O space for SMBus uninitialized\n"); + } else { + lpc_sch_cells[cells++] = isch_smbus_cell; + smbus_sch_resource.start = base_addr; + smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1; } - gpio_sch_resource.start = base_addr; - - if (id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) - gpio_sch_resource.end = base_addr + GPIO_IO_SIZE_CENTERTON - 1; + pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg); + base_addr = 0; + if (!(base_addr_cfg & (1 << 31))) + dev_warn(&dev->dev, "Decode of the GPIO I/O range disabled\n"); else - gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1; - - for (i=0; i < ARRAY_SIZE(lpc_sch_cells); i++) - lpc_sch_cells[i].id = id->device; + base_addr = (unsigned short)base_addr_cfg; - ret = mfd_add_devices(&dev->dev, 0, - lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL, - 0, NULL); - if (ret) - goto out_dev; + if (base_addr == 0) { + dev_warn(&dev->dev, "I/O space for GPIO uninitialized\n"); + } else { + lpc_sch_cells[cells++] = sch_gpio_cell; + gpio_sch_resource.start = base_addr; + if (id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) + gpio_sch_resource.end = base_addr + GPIO_IO_SIZE_CENTERTON - 1; + else + gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1; + } if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC - || id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) { + || id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) { pci_read_config_dword(dev, WDTBASE, &base_addr_cfg); - if (!(base_addr_cfg & (1 << 31))) { - dev_err(&dev->dev, "Decode of the WDT I/O range disabled\n"); - ret = -ENODEV; - goto out_dev; + base_addr = 0; + if (!(base_addr_cfg & (1 << 31))) + dev_warn(&dev->dev, "Decode of the WDT I/O range disabled\n"); + else + base_addr = (unsigned short)base_addr_cfg; + if (base_addr == 0) + dev_warn(&dev->dev, "I/O space for WDT uninitialized\n"); + else { + lpc_sch_cells[cells++] = wdt_sch_cell; + wdt_sch_resource.start = base_addr; + wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1; } - base_addr = (unsigned short)base_addr_cfg; - if (base_addr == 0) { - dev_err(&dev->dev, "I/O space for WDT uninitialized\n"); - ret = -ENODEV; - goto out_dev; - } - - wdt_sch_resource.start = base_addr; - wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1; + } - for (i = 0; i < ARRAY_SIZE(tunnelcreek_cells); i++) - tunnelcreek_cells[i].id = id->device; + if (WARN_ON(cells > ARRAY_SIZE(lpc_sch_cells))) { + dev_err(&dev->dev, "Cell count exceeds array size"); + return -ENODEV; + } - ret = mfd_add_devices(&dev->dev, 0, tunnelcreek_cells, - ARRAY_SIZE(tunnelcreek_cells), NULL, - 0, NULL); + if (cells == 0) { + dev_err(&dev->dev, "All decode registers disabled.\n"); + return -ENODEV; } - return ret; -out_dev: - mfd_remove_devices(&dev->dev); + for (i = 0; i < cells; i++) + lpc_sch_cells[i].id = id->device; + + ret = mfd_add_devices(&dev->dev, 0, lpc_sch_cells, cells, NULL, 0, NULL); + if (ret) + mfd_remove_devices(&dev->dev); + return ret; } -- cgit v1.2.3 From 6e6680e3effbf7cff444405a990dbc355dc3a96f Mon Sep 17 00:00:00 2001 From: James Ralston Date: Fri, 8 Feb 2013 17:33:38 -0800 Subject: mfd: lpc_ich: Add Device IDs for Intel Wellsburg PCH This patch adds the Watchdog Timer Device IDs for the Intel Wellsburg PCH Signed-off-by: James Ralston Acked-by: Peter Tyser Signed-off-by: Samuel Ortiz --- drivers/mfd/lpc_ich.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index a0cfdf980748..5c2ef41fa24c 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -50,6 +50,7 @@ * document number TBD : Panther Point * document number TBD : Lynx Point * document number TBD : Lynx Point-LP + * document number TBD : Wellsburg */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -205,6 +206,7 @@ enum lpc_chipsets { LPC_PPT, /* Panther Point */ LPC_LPT, /* Lynx Point */ LPC_LPT_LP, /* Lynx Point-LP */ + LPC_WBG, /* Wellsburg */ }; struct lpc_ich_info lpc_chipset_info[] = { @@ -485,6 +487,10 @@ struct lpc_ich_info lpc_chipset_info[] = { .name = "Lynx Point_LP", .iTCO_version = 2, }, + [LPC_WBG] = { + .name = "Wellsburg", + .iTCO_version = 2, + }, }; /* @@ -666,6 +672,38 @@ static DEFINE_PCI_DEVICE_TABLE(lpc_ich_ids) = { { PCI_VDEVICE(INTEL, 0x9c45), LPC_LPT_LP}, { PCI_VDEVICE(INTEL, 0x9c46), LPC_LPT_LP}, { PCI_VDEVICE(INTEL, 0x9c47), LPC_LPT_LP}, + { PCI_VDEVICE(INTEL, 0x8d40), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d41), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d42), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d43), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d44), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d45), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d46), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d47), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d48), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d49), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d4a), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d4b), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d4c), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d4d), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d4e), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d4f), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d50), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d51), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d52), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d53), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d54), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d55), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d56), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d57), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d58), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d59), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d5a), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d5b), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d5c), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d5d), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d5e), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d5f), LPC_WBG}, { 0, }, /* End of list */ }; MODULE_DEVICE_TABLE(pci, lpc_ich_ids); -- cgit v1.2.3 From 7083e05072b88d503d257b6f012ce56367f3ac97 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Fri, 1 Feb 2013 16:41:14 -0800 Subject: drm/i915: Fix RC6VIDS encode/decode The RC6 VIDS has a linear ramp starting at 250mv, which means any values below 250 are invalid. The old buggy macros tried to adjust for this to be more flexible, but there is no need. As Dan pointed out the ENCODE only ever has one value. The only invalid value for decode is an input of 0 which means something is really wonky, and the cases where DECODE are used either don't matter (debug values), or would be implicitly correct (the check for less than 450). This patch makes simpler, easier to read macros which are actually correct. Maybe this patch can actually fix some bugs now. Thanks to Dan for catching this. /me hides Cc: stable@kernel.org Reported-by: Dan Carpenter Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 15f5e7f9cded..8754f9160aef 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4272,8 +4272,8 @@ #define GEN6_PCODE_READ_MIN_FREQ_TABLE 0x9 #define GEN6_PCODE_WRITE_RC6VIDS 0x4 #define GEN6_PCODE_READ_RC6VIDS 0x5 -#define GEN6_ENCODE_RC6_VID(mv) (((mv) / 5) - 245) < 0 ?: 0 -#define GEN6_DECODE_RC6_VID(vids) (((vids) * 5) > 0 ? ((vids) * 5) + 245 : 0) +#define GEN6_ENCODE_RC6_VID(mv) (((mv) - 245) / 5) +#define GEN6_DECODE_RC6_VID(vids) (((vids) * 5) + 245) #define GEN6_PCODE_DATA 0x138128 #define GEN6_PCODE_FREQ_IA_RATIO_SHIFT 8 -- cgit v1.2.3 From b8efb17b3d687695b81485f606fc4e6c35a50f9a Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Tue, 5 Feb 2013 15:41:53 +0800 Subject: i915: ignore lid open event when resuming i915 driver needs to do modeset when 1. system resumes from sleep 2. lid is opened In PM_SUSPEND_MEM state, all the GPEs are cleared when system resumes, thus it is the i915_resume code does the modeset rather than intel_lid_notify(). But in PM_SUSPEND_FREEZE state, this will be broken because system is still responsive to the lid events. 1. When we close the lid in Freeze state, intel_lid_notify() sets modeset_on_lid. 2. When we reopen the lid, intel_lid_notify() will do a modeset, before the system is resumed. here is the error log, [92146.548074] WARNING: at drivers/gpu/drm/i915/intel_display.c:1028 intel_wait_for_pipe_off+0x184/0x190 [i915]() [92146.548076] Hardware name: VGN-Z540N [92146.548078] pipe_off wait timed out [92146.548167] Modules linked in: hid_generic usbhid hid snd_hda_codec_realtek snd_hda_intel snd_hda_codec parport_pc snd_hwdep ppdev snd_pcm_oss i915 snd_mixer_oss snd_pcm arc4 iwldvm snd_seq_dummy mac80211 snd_seq_oss snd_seq_midi fbcon tileblit font bitblit softcursor drm_kms_helper snd_rawmidi snd_seq_midi_event coretemp drm snd_seq kvm btusb bluetooth snd_timer iwlwifi pcmcia tpm_infineon i2c_algo_bit joydev snd_seq_device intel_agp cfg80211 snd intel_gtt yenta_socket pcmcia_rsrc sony_laptop agpgart microcode psmouse tpm_tis serio_raw mxm_wmi soundcore snd_page_alloc tpm acpi_cpufreq lpc_ich pcmcia_core tpm_bios mperf processor lp parport firewire_ohci firewire_core crc_itu_t sdhci_pci sdhci thermal e1000e [92146.548173] Pid: 4304, comm: kworker/0:0 Tainted: G W 3.8.0-rc3-s0i3-v3-test+ #9 [92146.548175] Call Trace: [92146.548189] [] warn_slowpath_common+0x72/0xa0 [92146.548227] [] ? intel_wait_for_pipe_off+0x184/0x190 [i915] [92146.548263] [] ? intel_wait_for_pipe_off+0x184/0x190 [i915] [92146.548270] [] warn_slowpath_fmt+0x33/0x40 [92146.548307] [] intel_wait_for_pipe_off+0x184/0x190 [i915] [92146.548344] [] intel_disable_pipe+0x102/0x190 [i915] [92146.548380] [] ? intel_disable_plane+0x64/0x80 [i915] [92146.548417] [] i9xx_crtc_disable+0xbc/0x150 [i915] [92146.548456] [] intel_crtc_update_dpms+0x5e/0x90 [i915] [92146.548493] [] intel_modeset_setup_hw_state+0x42f/0x8f0 [i915] [92146.548535] [] intel_lid_notify+0x9b/0xc0 [i915] [92146.548543] [] notifier_call_chain+0x43/0x60 [92146.548550] [] __blocking_notifier_call_chain+0x41/0x80 [92146.548556] [] blocking_notifier_call_chain+0x1f/0x30 [92146.548563] [] acpi_lid_send_state+0x78/0xa4 [92146.548569] [] acpi_button_notify+0x3b/0xf1 [92146.548577] [] ? acpi_os_execute+0x17/0x19 [92146.548582] [] ? acpi_ec_sync_query+0xa5/0xbc [92146.548589] [] acpi_device_notify+0x16/0x18 [92146.548595] [] acpi_ev_notify_dispatch+0x38/0x4f [92146.548600] [] acpi_os_execute_deferred+0x20/0x2b [92146.548607] [] process_one_work+0x128/0x3f0 [92146.548613] [] ? common_interrupt+0x33/0x38 [92146.548618] [] ? wake_up_worker+0x30/0x30 [92146.548624] [] ? acpi_os_wait_events_complete+0x1e/0x1e [92146.548629] [] worker_thread+0x119/0x3b0 [92146.548634] [] ? manage_workers+0x240/0x240 [92146.548640] [] kthread+0x94/0xa0 [92146.548647] [] ? ftrace_raw_output_sched_stat_runtime+0x70/0xf0 [92146.548652] [] ret_from_kernel_thread+0x1b/0x28 [92146.548658] [] ? kthread_create_on_node+0xc0/0xc0 three different modeset flags are introduced in this patch MODESET_ON_LID_OPEN: do modeset on next lid open event MODESET_DONE: modeset already done MODESET_SUSPENDED: suspended, only do modeset when system is resumed In this way, 1. when lid is closed, MODESET_ON_LID_OPEN is set so that we'll do modeset on next lid open event. 2. when lid is opened, MODESET_DONE is set so that duplicate lid open events will be ignored. 3. when system suspends, MODESET_SUSPENDED is set. In this case, we will not do modeset on any lid events. Plus, locking mechanism is also introduced to avoid racing. Signed-off-by: Zhang Rui Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 1 + drivers/gpu/drm/i915/i915_drv.c | 13 ++++++++----- drivers/gpu/drm/i915/i915_drv.h | 10 ++++++++-- drivers/gpu/drm/i915/intel_lvds.c | 33 ++++++++++++++++++++------------- 4 files changed, 37 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index cf0610330135..4fa6beb14c77 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1610,6 +1610,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) mutex_init(&dev_priv->dpio_lock); mutex_init(&dev_priv->rps.hw_lock); + mutex_init(&dev_priv->modeset_restore_lock); if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) dev_priv->num_pipe = 3; diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index d159d7a402e9..c5b8c81b9440 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -470,6 +470,11 @@ static int i915_drm_freeze(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + /* ignore lid events during suspend */ + mutex_lock(&dev_priv->modeset_restore_lock); + dev_priv->modeset_restore = MODESET_SUSPENDED; + mutex_unlock(&dev_priv->modeset_restore_lock); + intel_set_power_well(dev, true); drm_kms_helper_poll_disable(dev); @@ -496,9 +501,6 @@ static int i915_drm_freeze(struct drm_device *dev) intel_opregion_fini(dev); - /* Modeset on resume, not lid events */ - dev_priv->modeset_on_lid = 0; - console_lock(); intel_fbdev_set_suspend(dev, 1); console_unlock(); @@ -574,8 +576,6 @@ static int __i915_drm_thaw(struct drm_device *dev) intel_opregion_init(dev); - dev_priv->modeset_on_lid = 0; - /* * The console lock can be pretty contented on resume due * to all the printk activity. Try to keep it out of the hot @@ -588,6 +588,9 @@ static int __i915_drm_thaw(struct drm_device *dev) schedule_work(&dev_priv->console_resume_work); } + mutex_lock(&dev_priv->modeset_restore_lock); + dev_priv->modeset_restore = MODESET_DONE; + mutex_unlock(&dev_priv->modeset_restore_lock); return error; } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c338b4443fd9..4e5a3776a3ea 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -846,6 +846,12 @@ struct i915_gpu_error { unsigned int stop_rings; }; +enum modeset_restore { + MODESET_ON_LID_OPEN, + MODESET_DONE, + MODESET_SUSPENDED, +}; + typedef struct drm_i915_private { struct drm_device *dev; struct kmem_cache *slab; @@ -967,8 +973,8 @@ typedef struct drm_i915_private { unsigned long quirks; - /* Register state */ - bool modeset_on_lid; + enum modeset_restore modeset_restore; + struct mutex modeset_restore_lock; struct i915_gtt gtt; diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 5e3f08e3fd8b..bff78a3ec37b 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -547,13 +547,14 @@ static const struct dmi_system_id intel_no_modeset_on_lid[] = { }; /* - * Lid events. Note the use of 'modeset_on_lid': - * - we set it on lid close, and reset it on open + * Lid events. Note the use of 'modeset': + * - we set it to MODESET_ON_LID_OPEN on lid close, + * and set it to MODESET_DONE on open * - we use it as a "only once" bit (ie we ignore - * duplicate events where it was already properly - * set/reset) - * - the suspend/resume paths will also set it to - * zero, since they restore the mode ("lid open"). + * duplicate events where it was already properly set) + * - the suspend/resume paths will set it to + * MODESET_SUSPENDED and ignore the lid open event, + * because they restore the mode ("lid open"). */ static int intel_lid_notify(struct notifier_block *nb, unsigned long val, void *unused) @@ -567,6 +568,9 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val, if (dev->switch_power_state != DRM_SWITCH_POWER_ON) return NOTIFY_OK; + mutex_lock(&dev_priv->modeset_restore_lock); + if (dev_priv->modeset_restore == MODESET_SUSPENDED) + goto exit; /* * check and update the status of LVDS connector after receiving * the LID nofication event. @@ -575,21 +579,24 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val, /* Don't force modeset on machines where it causes a GPU lockup */ if (dmi_check_system(intel_no_modeset_on_lid)) - return NOTIFY_OK; + goto exit; if (!acpi_lid_open()) { - dev_priv->modeset_on_lid = 1; - return NOTIFY_OK; + /* do modeset on next lid open event */ + dev_priv->modeset_restore = MODESET_ON_LID_OPEN; + goto exit; } - if (!dev_priv->modeset_on_lid) - return NOTIFY_OK; - - dev_priv->modeset_on_lid = 0; + if (dev_priv->modeset_restore == MODESET_DONE) + goto exit; drm_modeset_lock_all(dev); intel_modeset_setup_hw_state(dev, true); drm_modeset_unlock_all(dev); + dev_priv->modeset_restore = MODESET_DONE; + +exit: + mutex_unlock(&dev_priv->modeset_restore_lock); return NOTIFY_OK; } -- cgit v1.2.3 From bf0d5f503dc11d6314c0503591d258d60ee9c944 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 22 Nov 2012 00:00:08 -0600 Subject: rbd: new request tracking code This patch fully implements the new request tracking code for rbd I/O requests. Each I/O request to an rbd image will get an rbd_image_request structure allocated to track it. This provides access to all information about the original request, as well as access to the set of one or more object requests that are initiated as a result of the image request. An rbd_obj_request structure defines a request sent to a single osd object (possibly) as part of an rbd image request. An rbd object request refers to a ceph_osd_request structure built up to represent the request; for now it will contain a single osd operation. It also provides space to hold the result status and the version of the object when the osd request completes. An rbd_obj_request structure can also stand on its own. This will be used for reading the version 1 header object, for issuing acknowledgements to event notifications, and for making object method calls. All rbd object requests now complete asynchronously with respect to the osd client--they supply a common callback routine. This resolves: http://tracker.newdream.net/issues/3741 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 621 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 619 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 668936381ab0..daa0f18f7089 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -181,6 +181,67 @@ struct rbd_req_coll { struct rbd_req_status status[0]; }; +struct rbd_img_request; +typedef void (*rbd_img_callback_t)(struct rbd_img_request *); + +#define BAD_WHICH U32_MAX /* Good which or bad which, which? */ + +struct rbd_obj_request; +typedef void (*rbd_obj_callback_t)(struct rbd_obj_request *); + +enum obj_request_type { OBJ_REQUEST_BIO }; /* More types to come */ + +struct rbd_obj_request { + const char *object_name; + u64 offset; /* object start byte */ + u64 length; /* bytes from offset */ + + struct rbd_img_request *img_request; + struct list_head links; /* img_request->obj_requests */ + u32 which; /* posn image request list */ + + enum obj_request_type type; + struct bio *bio_list; + + struct ceph_osd_request *osd_req; + + u64 xferred; /* bytes transferred */ + u64 version; + s32 result; + atomic_t done; + + rbd_obj_callback_t callback; + + struct kref kref; +}; + +struct rbd_img_request { + struct request *rq; + struct rbd_device *rbd_dev; + u64 offset; /* starting image byte offset */ + u64 length; /* byte count from offset */ + bool write_request; /* false for read */ + union { + struct ceph_snap_context *snapc; /* for writes */ + u64 snap_id; /* for reads */ + }; + spinlock_t completion_lock;/* protects next_completion */ + u32 next_completion; + rbd_img_callback_t callback; + + u32 obj_request_count; + struct list_head obj_requests; /* rbd_obj_request structs */ + + struct kref kref; +}; + +#define for_each_obj_request(ireq, oreq) \ + list_for_each_entry(oreq, &ireq->obj_requests, links) +#define for_each_obj_request_from(ireq, oreq) \ + list_for_each_entry_from(oreq, &ireq->obj_requests, links) +#define for_each_obj_request_safe(ireq, oreq, n) \ + list_for_each_entry_safe_reverse(oreq, n, &ireq->obj_requests, links) + /* * a single io request */ @@ -1031,6 +1092,62 @@ out_err: return NULL; } +static void rbd_obj_request_get(struct rbd_obj_request *obj_request) +{ + kref_get(&obj_request->kref); +} + +static void rbd_obj_request_destroy(struct kref *kref); +static void rbd_obj_request_put(struct rbd_obj_request *obj_request) +{ + rbd_assert(obj_request != NULL); + kref_put(&obj_request->kref, rbd_obj_request_destroy); +} + +static void rbd_img_request_get(struct rbd_img_request *img_request) +{ + kref_get(&img_request->kref); +} + +static void rbd_img_request_destroy(struct kref *kref); +static void rbd_img_request_put(struct rbd_img_request *img_request) +{ + rbd_assert(img_request != NULL); + kref_put(&img_request->kref, rbd_img_request_destroy); +} + +static inline void rbd_img_obj_request_add(struct rbd_img_request *img_request, + struct rbd_obj_request *obj_request) +{ + rbd_obj_request_get(obj_request); + obj_request->img_request = img_request; + list_add_tail(&obj_request->links, &img_request->obj_requests); + obj_request->which = img_request->obj_request_count++; + rbd_assert(obj_request->which != BAD_WHICH); +} + +static inline void rbd_img_obj_request_del(struct rbd_img_request *img_request, + struct rbd_obj_request *obj_request) +{ + rbd_assert(obj_request->which != BAD_WHICH); + obj_request->which = BAD_WHICH; + list_del(&obj_request->links); + rbd_assert(obj_request->img_request == img_request); + obj_request->callback = NULL; + obj_request->img_request = NULL; + rbd_obj_request_put(obj_request); +} + +static bool obj_request_type_valid(enum obj_request_type type) +{ + switch (type) { + case OBJ_REQUEST_BIO: + return true; + default: + return false; + } +} + struct ceph_osd_req_op *rbd_osd_req_op_create(u16 opcode, ...) { struct ceph_osd_req_op *op; @@ -1395,6 +1512,26 @@ done: return ret; } +static int rbd_obj_request_submit(struct ceph_osd_client *osdc, + struct rbd_obj_request *obj_request) +{ + return ceph_osdc_start_request(osdc, obj_request->osd_req, false); +} + +static void rbd_img_request_complete(struct rbd_img_request *img_request) +{ + if (img_request->callback) + img_request->callback(img_request); + else + rbd_img_request_put(img_request); +} + +static void rbd_obj_request_complete(struct rbd_obj_request *obj_request) +{ + if (obj_request->callback) + obj_request->callback(obj_request); +} + /* * Request sync osd read */ @@ -1618,6 +1755,486 @@ static int rbd_dev_do_request(struct request *rq, return 0; } +static void rbd_osd_read_callback(struct rbd_obj_request *obj_request, + struct ceph_osd_op *op) +{ + u64 xferred; + + /* + * We support a 64-bit length, but ultimately it has to be + * passed to blk_end_request(), which takes an unsigned int. + */ + xferred = le64_to_cpu(op->extent.length); + rbd_assert(xferred < (u64) UINT_MAX); + if (obj_request->result == (s32) -ENOENT) { + zero_bio_chain(obj_request->bio_list, 0); + obj_request->result = 0; + } else if (xferred < obj_request->length && !obj_request->result) { + zero_bio_chain(obj_request->bio_list, xferred); + xferred = obj_request->length; + } + obj_request->xferred = xferred; + atomic_set(&obj_request->done, 1); +} + +static void rbd_osd_write_callback(struct rbd_obj_request *obj_request, + struct ceph_osd_op *op) +{ + obj_request->xferred = le64_to_cpu(op->extent.length); + atomic_set(&obj_request->done, 1); +} + +static void rbd_osd_req_callback(struct ceph_osd_request *osd_req, + struct ceph_msg *msg) +{ + struct rbd_obj_request *obj_request = osd_req->r_priv; + struct ceph_osd_reply_head *reply_head; + struct ceph_osd_op *op; + u32 num_ops; + u16 opcode; + + rbd_assert(osd_req == obj_request->osd_req); + rbd_assert(!!obj_request->img_request ^ + (obj_request->which == BAD_WHICH)); + + obj_request->xferred = le32_to_cpu(msg->hdr.data_len); + reply_head = msg->front.iov_base; + obj_request->result = (s32) le32_to_cpu(reply_head->result); + obj_request->version = le64_to_cpu(osd_req->r_reassert_version.version); + + num_ops = le32_to_cpu(reply_head->num_ops); + WARN_ON(num_ops != 1); /* For now */ + + op = &reply_head->ops[0]; + opcode = le16_to_cpu(op->op); + switch (opcode) { + case CEPH_OSD_OP_READ: + rbd_osd_read_callback(obj_request, op); + break; + case CEPH_OSD_OP_WRITE: + rbd_osd_write_callback(obj_request, op); + break; + default: + rbd_warn(NULL, "%s: unsupported op %hu\n", + obj_request->object_name, (unsigned short) opcode); + break; + } + + if (atomic_read(&obj_request->done)) + rbd_obj_request_complete(obj_request); +} + +static struct ceph_osd_request *rbd_osd_req_create( + struct rbd_device *rbd_dev, + bool write_request, + struct rbd_obj_request *obj_request, + struct ceph_osd_req_op *op) +{ + struct rbd_img_request *img_request = obj_request->img_request; + struct ceph_snap_context *snapc = NULL; + struct ceph_osd_client *osdc; + struct ceph_osd_request *osd_req; + struct timespec now; + struct timespec *mtime; + u64 snap_id = CEPH_NOSNAP; + u64 offset = obj_request->offset; + u64 length = obj_request->length; + + if (img_request) { + rbd_assert(img_request->write_request == write_request); + if (img_request->write_request) + snapc = img_request->snapc; + else + snap_id = img_request->snap_id; + } + + /* Allocate and initialize the request, for the single op */ + + osdc = &rbd_dev->rbd_client->client->osdc; + osd_req = ceph_osdc_alloc_request(osdc, snapc, 1, false, GFP_ATOMIC); + if (!osd_req) + return NULL; /* ENOMEM */ + + rbd_assert(obj_request_type_valid(obj_request->type)); + switch (obj_request->type) { + case OBJ_REQUEST_BIO: + rbd_assert(obj_request->bio_list != NULL); + osd_req->r_bio = obj_request->bio_list; + bio_get(osd_req->r_bio); + /* osd client requires "num pages" even for bio */ + osd_req->r_num_pages = calc_pages_for(offset, length); + break; + } + + if (write_request) { + osd_req->r_flags = CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK; + now = CURRENT_TIME; + mtime = &now; + } else { + osd_req->r_flags = CEPH_OSD_FLAG_READ; + mtime = NULL; /* not needed for reads */ + offset = 0; /* These are not used... */ + length = 0; /* ...for osd read requests */ + } + + osd_req->r_callback = rbd_osd_req_callback; + osd_req->r_priv = obj_request; + + osd_req->r_oid_len = strlen(obj_request->object_name); + rbd_assert(osd_req->r_oid_len < sizeof (osd_req->r_oid)); + memcpy(osd_req->r_oid, obj_request->object_name, osd_req->r_oid_len); + + osd_req->r_file_layout = rbd_dev->layout; /* struct */ + + /* osd_req will get its own reference to snapc (if non-null) */ + + ceph_osdc_build_request(osd_req, offset, length, 1, op, + snapc, snap_id, mtime); + + return osd_req; +} + +static void rbd_osd_req_destroy(struct ceph_osd_request *osd_req) +{ + ceph_osdc_put_request(osd_req); +} + +/* object_name is assumed to be a non-null pointer and NUL-terminated */ + +static struct rbd_obj_request *rbd_obj_request_create(const char *object_name, + u64 offset, u64 length, + enum obj_request_type type) +{ + struct rbd_obj_request *obj_request; + size_t size; + char *name; + + rbd_assert(obj_request_type_valid(type)); + + size = strlen(object_name) + 1; + obj_request = kzalloc(sizeof (*obj_request) + size, GFP_KERNEL); + if (!obj_request) + return NULL; + + name = (char *)(obj_request + 1); + obj_request->object_name = memcpy(name, object_name, size); + obj_request->offset = offset; + obj_request->length = length; + obj_request->which = BAD_WHICH; + obj_request->type = type; + INIT_LIST_HEAD(&obj_request->links); + atomic_set(&obj_request->done, 0); + kref_init(&obj_request->kref); + + return obj_request; +} + +static void rbd_obj_request_destroy(struct kref *kref) +{ + struct rbd_obj_request *obj_request; + + obj_request = container_of(kref, struct rbd_obj_request, kref); + + rbd_assert(obj_request->img_request == NULL); + rbd_assert(obj_request->which == BAD_WHICH); + + if (obj_request->osd_req) + rbd_osd_req_destroy(obj_request->osd_req); + + rbd_assert(obj_request_type_valid(obj_request->type)); + switch (obj_request->type) { + case OBJ_REQUEST_BIO: + if (obj_request->bio_list) + bio_chain_put(obj_request->bio_list); + break; + } + + kfree(obj_request); +} + +/* + * Caller is responsible for filling in the list of object requests + * that comprises the image request, and the Linux request pointer + * (if there is one). + */ +struct rbd_img_request *rbd_img_request_create(struct rbd_device *rbd_dev, + u64 offset, u64 length, + bool write_request) +{ + struct rbd_img_request *img_request; + struct ceph_snap_context *snapc = NULL; + + img_request = kmalloc(sizeof (*img_request), GFP_ATOMIC); + if (!img_request) + return NULL; + + if (write_request) { + down_read(&rbd_dev->header_rwsem); + snapc = ceph_get_snap_context(rbd_dev->header.snapc); + up_read(&rbd_dev->header_rwsem); + if (WARN_ON(!snapc)) { + kfree(img_request); + return NULL; /* Shouldn't happen */ + } + } + + img_request->rq = NULL; + img_request->rbd_dev = rbd_dev; + img_request->offset = offset; + img_request->length = length; + img_request->write_request = write_request; + if (write_request) + img_request->snapc = snapc; + else + img_request->snap_id = rbd_dev->spec->snap_id; + spin_lock_init(&img_request->completion_lock); + img_request->next_completion = 0; + img_request->callback = NULL; + img_request->obj_request_count = 0; + INIT_LIST_HEAD(&img_request->obj_requests); + kref_init(&img_request->kref); + + rbd_img_request_get(img_request); /* Avoid a warning */ + rbd_img_request_put(img_request); /* TEMPORARY */ + + return img_request; +} + +static void rbd_img_request_destroy(struct kref *kref) +{ + struct rbd_img_request *img_request; + struct rbd_obj_request *obj_request; + struct rbd_obj_request *next_obj_request; + + img_request = container_of(kref, struct rbd_img_request, kref); + + for_each_obj_request_safe(img_request, obj_request, next_obj_request) + rbd_img_obj_request_del(img_request, obj_request); + + if (img_request->write_request) + ceph_put_snap_context(img_request->snapc); + + kfree(img_request); +} + +static int rbd_img_request_fill_bio(struct rbd_img_request *img_request, + struct bio *bio_list) +{ + struct rbd_device *rbd_dev = img_request->rbd_dev; + struct rbd_obj_request *obj_request = NULL; + struct rbd_obj_request *next_obj_request; + unsigned int bio_offset; + u64 image_offset; + u64 resid; + u16 opcode; + + opcode = img_request->write_request ? CEPH_OSD_OP_WRITE + : CEPH_OSD_OP_READ; + bio_offset = 0; + image_offset = img_request->offset; + rbd_assert(image_offset == bio_list->bi_sector << SECTOR_SHIFT); + resid = img_request->length; + while (resid) { + const char *object_name; + unsigned int clone_size; + struct ceph_osd_req_op *op; + u64 offset; + u64 length; + + object_name = rbd_segment_name(rbd_dev, image_offset); + if (!object_name) + goto out_unwind; + offset = rbd_segment_offset(rbd_dev, image_offset); + length = rbd_segment_length(rbd_dev, image_offset, resid); + obj_request = rbd_obj_request_create(object_name, + offset, length, + OBJ_REQUEST_BIO); + kfree(object_name); /* object request has its own copy */ + if (!obj_request) + goto out_unwind; + + rbd_assert(length <= (u64) UINT_MAX); + clone_size = (unsigned int) length; + obj_request->bio_list = bio_chain_clone_range(&bio_list, + &bio_offset, clone_size, + GFP_ATOMIC); + if (!obj_request->bio_list) + goto out_partial; + + /* + * Build up the op to use in building the osd + * request. Note that the contents of the op are + * copied by rbd_osd_req_create(). + */ + op = rbd_osd_req_op_create(opcode, offset, length); + if (!op) + goto out_partial; + obj_request->osd_req = rbd_osd_req_create(rbd_dev, + img_request->write_request, + obj_request, op); + rbd_osd_req_op_destroy(op); + if (!obj_request->osd_req) + goto out_partial; + /* status and version are initially zero-filled */ + + rbd_img_obj_request_add(img_request, obj_request); + + image_offset += length; + resid -= length; + } + + return 0; + +out_partial: + rbd_obj_request_put(obj_request); +out_unwind: + for_each_obj_request_safe(img_request, obj_request, next_obj_request) + rbd_obj_request_put(obj_request); + + return -ENOMEM; +} + +static void rbd_img_obj_callback(struct rbd_obj_request *obj_request) +{ + struct rbd_img_request *img_request; + u32 which = obj_request->which; + bool more = true; + + img_request = obj_request->img_request; + rbd_assert(img_request != NULL); + rbd_assert(img_request->rq != NULL); + rbd_assert(which != BAD_WHICH); + rbd_assert(which < img_request->obj_request_count); + rbd_assert(which >= img_request->next_completion); + + spin_lock_irq(&img_request->completion_lock); + if (which != img_request->next_completion) + goto out; + + for_each_obj_request_from(img_request, obj_request) { + unsigned int xferred; + int result; + + rbd_assert(more); + rbd_assert(which < img_request->obj_request_count); + + if (!atomic_read(&obj_request->done)) + break; + + rbd_assert(obj_request->xferred <= (u64) UINT_MAX); + xferred = (unsigned int) obj_request->xferred; + result = (int) obj_request->result; + if (result) + rbd_warn(NULL, "obj_request %s result %d xferred %u\n", + img_request->write_request ? "write" : "read", + result, xferred); + + more = blk_end_request(img_request->rq, result, xferred); + which++; + } + rbd_assert(more ^ (which == img_request->obj_request_count)); + img_request->next_completion = which; +out: + spin_unlock_irq(&img_request->completion_lock); + + if (!more) + rbd_img_request_complete(img_request); +} + +static int rbd_img_request_submit(struct rbd_img_request *img_request) +{ + struct rbd_device *rbd_dev = img_request->rbd_dev; + struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc; + struct rbd_obj_request *obj_request; + + for_each_obj_request(img_request, obj_request) { + int ret; + + obj_request->callback = rbd_img_obj_callback; + ret = rbd_obj_request_submit(osdc, obj_request); + if (ret) + return ret; + /* + * The image request has its own reference to each + * of its object requests, so we can safely drop the + * initial one here. + */ + rbd_obj_request_put(obj_request); + } + + return 0; +} + +static void rbd_request_fn(struct request_queue *q) +{ + struct rbd_device *rbd_dev = q->queuedata; + bool read_only = rbd_dev->mapping.read_only; + struct request *rq; + int result; + + while ((rq = blk_fetch_request(q))) { + bool write_request = rq_data_dir(rq) == WRITE; + struct rbd_img_request *img_request; + u64 offset; + u64 length; + + /* Ignore any non-FS requests that filter through. */ + + if (rq->cmd_type != REQ_TYPE_FS) { + __blk_end_request_all(rq, 0); + continue; + } + + spin_unlock_irq(q->queue_lock); + + /* Disallow writes to a read-only device */ + + if (write_request) { + result = -EROFS; + if (read_only) + goto end_request; + rbd_assert(rbd_dev->spec->snap_id == CEPH_NOSNAP); + } + + /* Quit early if the snapshot has disappeared */ + + if (!atomic_read(&rbd_dev->exists)) { + dout("request for non-existent snapshot"); + rbd_assert(rbd_dev->spec->snap_id != CEPH_NOSNAP); + result = -ENXIO; + goto end_request; + } + + offset = (u64) blk_rq_pos(rq) << SECTOR_SHIFT; + length = (u64) blk_rq_bytes(rq); + + result = -EINVAL; + if (WARN_ON(offset && length > U64_MAX - offset + 1)) + goto end_request; /* Shouldn't happen */ + + result = -ENOMEM; + img_request = rbd_img_request_create(rbd_dev, offset, length, + write_request); + if (!img_request) + goto end_request; + + img_request->rq = rq; + + result = rbd_img_request_fill_bio(img_request, rq->bio); + if (!result) + result = rbd_img_request_submit(img_request); + if (result) + rbd_img_request_put(img_request); +end_request: + spin_lock_irq(q->queue_lock); + if (result < 0) { + rbd_warn(rbd_dev, "obj_request %s result %d\n", + write_request ? "write" : "read", result); + __blk_end_request_all(rq, result); + } + } +} + /* * block device queue callback */ @@ -1929,8 +2546,8 @@ static int rbd_init_disk(struct rbd_device *rbd_dev) disk->fops = &rbd_bd_ops; disk->private_data = rbd_dev; - /* init rq */ - q = blk_init_queue(rbd_rq_fn, &rbd_dev->lock); + (void) rbd_rq_fn; /* avoid a warning */ + q = blk_init_queue(rbd_request_fn, &rbd_dev->lock); if (!q) goto out_disk; -- cgit v1.2.3 From 2250a71b591728092db9adcc51629401deb2f9f8 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 30 Nov 2012 17:53:04 -0600 Subject: rbd: kill rbd_rq_fn() and all other related code Now that the request function has been replaced by one using the new request management data structures the old one can go away. Deleting it makes rbd_dev_do_request() no longer needed, and deleting that makes other functions unneeded, and so on. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 319 ---------------------------------------------------- 1 file changed, 319 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index daa0f18f7089..4c175a7d3f7e 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -621,18 +621,6 @@ static void rbd_put_client(struct rbd_client *rbdc) kref_put(&rbdc->kref, rbd_client_release); } -/* - * Destroy requests collection - */ -static void rbd_coll_release(struct kref *kref) -{ - struct rbd_req_coll *coll = - container_of(kref, struct rbd_req_coll, kref); - - dout("rbd_coll_release %p\n", coll); - kfree(coll); -} - static bool rbd_image_format_valid(u32 image_format) { return image_format == 1 || image_format == 2; @@ -876,28 +864,6 @@ static u64 rbd_segment_length(struct rbd_device *rbd_dev, return length; } -static int rbd_get_num_segments(struct rbd_image_header *header, - u64 ofs, u64 len) -{ - u64 start_seg; - u64 end_seg; - u64 result; - - if (!len) - return 0; - if (len - 1 > U64_MAX - ofs) - return -ERANGE; - - start_seg = ofs >> header->obj_order; - end_seg = (ofs + len - 1) >> header->obj_order; - - result = end_seg - start_seg + 1; - if (result > (u64) INT_MAX) - return -ERANGE; - - return (int) result; -} - /* * returns the size of an object in the image */ @@ -1216,52 +1182,6 @@ static void rbd_osd_req_op_destroy(struct ceph_osd_req_op *op) kfree(op); } -static void rbd_coll_end_req_index(struct request *rq, - struct rbd_req_coll *coll, - int index, - s32 ret, u64 len) -{ - struct request_queue *q; - int min, max, i; - - dout("rbd_coll_end_req_index %p index %d ret %d len %llu\n", - coll, index, (int)ret, (unsigned long long)len); - - if (!rq) - return; - - if (!coll) { - blk_end_request(rq, ret, len); - return; - } - - q = rq->q; - - spin_lock_irq(q->queue_lock); - coll->status[index].done = 1; - coll->status[index].rc = ret; - coll->status[index].bytes = len; - max = min = coll->num_done; - while (max < coll->total && coll->status[max].done) - max++; - - for (i = min; istatus[i].rc, - coll->status[i].bytes); - coll->num_done++; - kref_put(&coll->kref, rbd_coll_release); - } - spin_unlock_irq(q->queue_lock); -} - -static void rbd_coll_end_req(struct rbd_request *rbd_req, - s32 ret, u64 len) -{ - rbd_coll_end_req_index(rbd_req->rq, - rbd_req->coll, rbd_req->coll_index, - ret, len); -} - /* * Send ceph osd request */ @@ -1361,46 +1281,6 @@ done_osd_req: return ret; } -/* - * Ceph osd op callback - */ -static void rbd_req_cb(struct ceph_osd_request *osd_req, struct ceph_msg *msg) -{ - struct rbd_request *rbd_req = osd_req->r_priv; - struct ceph_osd_reply_head *replyhead; - struct ceph_osd_op *op; - s32 rc; - u64 bytes; - int read_op; - - /* parse reply */ - replyhead = msg->front.iov_base; - WARN_ON(le32_to_cpu(replyhead->num_ops) == 0); - op = (void *)(replyhead + 1); - rc = (s32)le32_to_cpu(replyhead->result); - bytes = le64_to_cpu(op->extent.length); - read_op = (le16_to_cpu(op->op) == CEPH_OSD_OP_READ); - - dout("rbd_req_cb bytes=%llu readop=%d rc=%d\n", - (unsigned long long) bytes, read_op, (int) rc); - - if (rc == (s32)-ENOENT && read_op) { - zero_bio_chain(rbd_req->bio, 0); - rc = 0; - } else if (rc == 0 && read_op && bytes < rbd_req->len) { - zero_bio_chain(rbd_req->bio, bytes); - bytes = rbd_req->len; - } - - rbd_coll_end_req(rbd_req, rc, bytes); - - if (rbd_req->bio) - bio_chain_put(rbd_req->bio); - - ceph_osdc_put_request(osd_req); - kfree(rbd_req); -} - static void rbd_simple_req_cb(struct ceph_osd_request *osd_req, struct ceph_msg *msg) { @@ -1448,70 +1328,6 @@ done: return ret; } -/* - * Do an asynchronous ceph osd operation - */ -static int rbd_do_op(struct request *rq, - struct rbd_device *rbd_dev, - struct ceph_snap_context *snapc, - u64 ofs, u64 len, - struct bio *bio, - struct rbd_req_coll *coll, - int coll_index) -{ - const char *seg_name; - u64 seg_ofs; - u64 seg_len; - int ret; - struct ceph_osd_req_op *op; - int opcode; - int flags; - u64 snapid; - - seg_name = rbd_segment_name(rbd_dev, ofs); - if (!seg_name) - return -ENOMEM; - seg_len = rbd_segment_length(rbd_dev, ofs, len); - seg_ofs = rbd_segment_offset(rbd_dev, ofs); - - if (rq_data_dir(rq) == WRITE) { - opcode = CEPH_OSD_OP_WRITE; - flags = CEPH_OSD_FLAG_WRITE|CEPH_OSD_FLAG_ONDISK; - snapid = CEPH_NOSNAP; - } else { - opcode = CEPH_OSD_OP_READ; - flags = CEPH_OSD_FLAG_READ; - rbd_assert(!snapc); - snapid = rbd_dev->spec->snap_id; - } - - ret = -ENOMEM; - op = rbd_osd_req_op_create(opcode, seg_ofs, seg_len); - if (!op) - goto done; - - /* we've taken care of segment sizes earlier when we - cloned the bios. We should never have a segment - truncated at this point */ - rbd_assert(seg_len == len); - - ret = rbd_do_request(rq, rbd_dev, snapc, snapid, - seg_name, seg_ofs, seg_len, - bio, - NULL, 0, - flags, - op, - coll, coll_index, - rbd_req_cb, NULL); - if (ret < 0) - rbd_coll_end_req_index(rq, coll, coll_index, - (s32)ret, seg_len); - rbd_osd_req_op_destroy(op); -done: - kfree(seg_name); - return ret; -} - static int rbd_obj_request_submit(struct ceph_osd_client *osdc, struct rbd_obj_request *obj_request) { @@ -1683,78 +1499,6 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, return ret; } -static struct rbd_req_coll *rbd_alloc_coll(int num_reqs) -{ - struct rbd_req_coll *coll = - kzalloc(sizeof(struct rbd_req_coll) + - sizeof(struct rbd_req_status) * num_reqs, - GFP_ATOMIC); - - if (!coll) - return NULL; - coll->total = num_reqs; - kref_init(&coll->kref); - return coll; -} - -static int rbd_dev_do_request(struct request *rq, - struct rbd_device *rbd_dev, - struct ceph_snap_context *snapc, - u64 ofs, unsigned int size, - struct bio *bio_chain) -{ - int num_segs; - struct rbd_req_coll *coll; - unsigned int bio_offset; - int cur_seg = 0; - - dout("%s 0x%x bytes at 0x%llx\n", - rq_data_dir(rq) == WRITE ? "write" : "read", - size, (unsigned long long) blk_rq_pos(rq) * SECTOR_SIZE); - - num_segs = rbd_get_num_segments(&rbd_dev->header, ofs, size); - if (num_segs <= 0) - return num_segs; - - coll = rbd_alloc_coll(num_segs); - if (!coll) - return -ENOMEM; - - bio_offset = 0; - do { - u64 limit = rbd_segment_length(rbd_dev, ofs, size); - unsigned int clone_size; - struct bio *bio_clone; - - BUG_ON(limit > (u64)UINT_MAX); - clone_size = (unsigned int)limit; - dout("bio_chain->bi_vcnt=%hu\n", bio_chain->bi_vcnt); - - kref_get(&coll->kref); - - /* Pass a cloned bio chain via an osd request */ - - bio_clone = bio_chain_clone_range(&bio_chain, - &bio_offset, clone_size, - GFP_ATOMIC); - if (bio_clone) - (void)rbd_do_op(rq, rbd_dev, snapc, - ofs, clone_size, - bio_clone, coll, cur_seg); - else - rbd_coll_end_req_index(rq, coll, cur_seg, - (s32)-ENOMEM, - clone_size); - size -= clone_size; - ofs += clone_size; - - cur_seg++; - } while (size > 0); - kref_put(&coll->kref, rbd_coll_release); - - return 0; -} - static void rbd_osd_read_callback(struct rbd_obj_request *obj_request, struct ceph_osd_op *op) { @@ -2235,68 +1979,6 @@ end_request: } } -/* - * block device queue callback - */ -static void rbd_rq_fn(struct request_queue *q) -{ - struct rbd_device *rbd_dev = q->queuedata; - bool read_only = rbd_dev->mapping.read_only; - struct request *rq; - - while ((rq = blk_fetch_request(q))) { - struct ceph_snap_context *snapc = NULL; - unsigned int size = 0; - int result; - - dout("fetched request\n"); - - /* Filter out block requests we don't understand */ - - if ((rq->cmd_type != REQ_TYPE_FS)) { - __blk_end_request_all(rq, 0); - continue; - } - spin_unlock_irq(q->queue_lock); - - /* Write requests need a reference to the snapshot context */ - - if (rq_data_dir(rq) == WRITE) { - result = -EROFS; - if (read_only) /* Can't write to a read-only device */ - goto out_end_request; - - /* - * Note that each osd request will take its - * own reference to the snapshot context - * supplied. The reference we take here - * just guarantees the one we provide stays - * valid. - */ - down_read(&rbd_dev->header_rwsem); - snapc = ceph_get_snap_context(rbd_dev->header.snapc); - up_read(&rbd_dev->header_rwsem); - rbd_assert(snapc != NULL); - } else if (!atomic_read(&rbd_dev->exists)) { - rbd_assert(rbd_dev->spec->snap_id != CEPH_NOSNAP); - dout("request for non-existent snapshot"); - result = -ENXIO; - goto out_end_request; - } - - size = blk_rq_bytes(rq); - result = rbd_dev_do_request(rq, rbd_dev, snapc, - blk_rq_pos(rq) * SECTOR_SIZE, - size, rq->bio); -out_end_request: - if (snapc) - ceph_put_snap_context(snapc); - spin_lock_irq(q->queue_lock); - if (!size || result < 0) - __blk_end_request_all(rq, result); - } -} - /* * a queue callback. Makes sure that we don't create a bio that spans across * multiple osd objects. One exception would be with a single page bios, @@ -2546,7 +2228,6 @@ static int rbd_init_disk(struct rbd_device *rbd_dev) disk->fops = &rbd_bd_ops; disk->private_data = rbd_dev; - (void) rbd_rq_fn; /* avoid a warning */ q = blk_init_queue(rbd_request_fn, &rbd_dev->lock); if (!q) goto out_disk; -- cgit v1.2.3 From 7d250b949a33c8a658a2ad4ab390d8394b842224 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 30 Nov 2012 17:53:04 -0600 Subject: rbd: kill rbd_req_coll and rbd_request The two remaining callers of rbd_do_request() always pass a null collection pointer, so the "coll" and "coll_index" parameters are not needed. There is no other use of that data structure, so it can be eliminated. Deleting them means there is no need to allocate a rbd_request structure for the callback function. And since that's the only use of *that* structure, it too can be eliminated. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 58 +++-------------------------------------------------- 1 file changed, 3 insertions(+), 55 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 4c175a7d3f7e..c1bb649b4ad1 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -162,25 +162,6 @@ struct rbd_client { struct list_head node; }; -/* - * a request completion status - */ -struct rbd_req_status { - int done; - s32 rc; - u64 bytes; -}; - -/* - * a collection of requests - */ -struct rbd_req_coll { - int total; - int num_done; - struct kref kref; - struct rbd_req_status status[0]; -}; - struct rbd_img_request; typedef void (*rbd_img_callback_t)(struct rbd_img_request *); @@ -242,18 +223,6 @@ struct rbd_img_request { #define for_each_obj_request_safe(ireq, oreq, n) \ list_for_each_entry_safe_reverse(oreq, n, &ireq->obj_requests, links) -/* - * a single io request - */ -struct rbd_request { - struct request *rq; /* blk layer request */ - struct bio *bio; /* cloned bio */ - struct page **pages; /* list of used pages */ - u64 len; - int coll_index; - struct rbd_req_coll *coll; -}; - struct rbd_snap { struct device dev; const char *name; @@ -1195,21 +1164,18 @@ static int rbd_do_request(struct request *rq, int num_pages, int flags, struct ceph_osd_req_op *op, - struct rbd_req_coll *coll, - int coll_index, void (*rbd_cb)(struct ceph_osd_request *, struct ceph_msg *), u64 *ver) { struct ceph_osd_client *osdc; struct ceph_osd_request *osd_req; - struct rbd_request *rbd_req = NULL; struct timespec mtime = CURRENT_TIME; int ret; - dout("rbd_do_request object_name=%s ofs=%llu len=%llu coll=%p[%d]\n", + dout("rbd_do_request object_name=%s ofs=%llu len=%llu\n", object_name, (unsigned long long) ofs, - (unsigned long long) len, coll, coll_index); + (unsigned long long) len); osdc = &rbd_dev->rbd_client->client->osdc; osd_req = ceph_osdc_alloc_request(osdc, snapc, 1, false, GFP_NOIO); @@ -1223,22 +1189,8 @@ static int rbd_do_request(struct request *rq, bio_get(osd_req->r_bio); } - if (coll) { - ret = -ENOMEM; - rbd_req = kmalloc(sizeof(*rbd_req), GFP_NOIO); - if (!rbd_req) - goto done_osd_req; - - rbd_req->rq = rq; - rbd_req->bio = bio; - rbd_req->pages = pages; - rbd_req->len = len; - rbd_req->coll = coll; - rbd_req->coll_index = coll_index; - } - osd_req->r_callback = rbd_cb; - osd_req->r_priv = rbd_req; + osd_req->r_priv = NULL; strncpy(osd_req->r_oid, object_name, sizeof(osd_req->r_oid)); osd_req->r_oid_len = strlen(osd_req->r_oid); @@ -1274,8 +1226,6 @@ static int rbd_do_request(struct request *rq, done_err: if (bio) bio_chain_put(osd_req->r_bio); - kfree(rbd_req); -done_osd_req: ceph_osdc_put_request(osd_req); return ret; @@ -1314,7 +1264,6 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, pages, num_pages, flags, op, - NULL, 0, NULL, ver); if (ret < 0) @@ -1390,7 +1339,6 @@ static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, NULL, 0, CEPH_OSD_FLAG_READ, op, - NULL, 0, rbd_simple_req_cb, NULL); rbd_osd_req_op_destroy(op); -- cgit v1.2.3 From 788e2df3b92e30f1fff74139bb53e68ec13fe2a5 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 17 Jan 2013 12:25:27 -0600 Subject: rbd: implement sync object read with new code Reimplement the synchronous read operation used for reading a version 1 header using the new request tracking code. Name the resulting function rbd_obj_read_sync() to better reflect that it's a full object operation, not an object request. To do this, implement a new OBJ_REQUEST_PAGES object request type. This implements a new mechanism to allow the caller to wait for completion for an rbd_obj_request by calling rbd_obj_request_wait(). This partially resolves: http://tracker.newdream.net/issues/3755 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index c1bb649b4ad1..3f5eaea444a0 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -170,7 +170,7 @@ typedef void (*rbd_img_callback_t)(struct rbd_img_request *); struct rbd_obj_request; typedef void (*rbd_obj_callback_t)(struct rbd_obj_request *); -enum obj_request_type { OBJ_REQUEST_BIO }; /* More types to come */ +enum obj_request_type { OBJ_REQUEST_BIO, OBJ_REQUEST_PAGES }; struct rbd_obj_request { const char *object_name; @@ -182,7 +182,13 @@ struct rbd_obj_request { u32 which; /* posn image request list */ enum obj_request_type type; - struct bio *bio_list; + union { + struct bio *bio_list; + struct { + struct page **pages; + u32 page_count; + }; + }; struct ceph_osd_request *osd_req; @@ -192,6 +198,7 @@ struct rbd_obj_request { atomic_t done; rbd_obj_callback_t callback; + struct completion completion; struct kref kref; }; @@ -1077,6 +1084,7 @@ static bool obj_request_type_valid(enum obj_request_type type) { switch (type) { case OBJ_REQUEST_BIO: + case OBJ_REQUEST_PAGES: return true; default: return false; @@ -1291,14 +1299,23 @@ static void rbd_img_request_complete(struct rbd_img_request *img_request) rbd_img_request_put(img_request); } +/* Caller is responsible for rbd_obj_request_destroy(obj_request) */ + +static int rbd_obj_request_wait(struct rbd_obj_request *obj_request) +{ + return wait_for_completion_interruptible(&obj_request->completion); +} + static void rbd_obj_request_complete(struct rbd_obj_request *obj_request) { if (obj_request->callback) obj_request->callback(obj_request); + else + complete_all(&obj_request->completion); } /* - * Request sync osd read + * Synchronously read a range from an object into a provided buffer */ static int rbd_req_sync_read(struct rbd_device *rbd_dev, const char *object_name, @@ -1556,6 +1573,11 @@ static struct ceph_osd_request *rbd_osd_req_create( /* osd client requires "num pages" even for bio */ osd_req->r_num_pages = calc_pages_for(offset, length); break; + case OBJ_REQUEST_PAGES: + osd_req->r_pages = obj_request->pages; + osd_req->r_num_pages = obj_request->page_count; + osd_req->r_page_alignment = offset & ~PAGE_MASK; + break; } if (write_request) { @@ -1616,6 +1638,7 @@ static struct rbd_obj_request *rbd_obj_request_create(const char *object_name, obj_request->type = type; INIT_LIST_HEAD(&obj_request->links); atomic_set(&obj_request->done, 0); + init_completion(&obj_request->completion); kref_init(&obj_request->kref); return obj_request; @@ -1639,6 +1662,11 @@ static void rbd_obj_request_destroy(struct kref *kref) if (obj_request->bio_list) bio_chain_put(obj_request->bio_list); break; + case OBJ_REQUEST_PAGES: + if (obj_request->pages) + ceph_release_page_vector(obj_request->pages, + obj_request->page_count); + break; } kfree(obj_request); @@ -1987,6 +2015,65 @@ static void rbd_free_disk(struct rbd_device *rbd_dev) put_disk(disk); } +static int rbd_obj_read_sync(struct rbd_device *rbd_dev, + const char *object_name, + u64 offset, u64 length, + char *buf, u64 *version) + +{ + struct ceph_osd_req_op *op; + struct rbd_obj_request *obj_request; + struct ceph_osd_client *osdc; + struct page **pages = NULL; + u32 page_count; + int ret; + + page_count = (u32) calc_pages_for(offset, length); + pages = ceph_alloc_page_vector(page_count, GFP_KERNEL); + if (IS_ERR(pages)) + ret = PTR_ERR(pages); + + ret = -ENOMEM; + obj_request = rbd_obj_request_create(object_name, offset, length, + OBJ_REQUEST_PAGES); + if (!obj_request) + goto out; + + obj_request->pages = pages; + obj_request->page_count = page_count; + + op = rbd_osd_req_op_create(CEPH_OSD_OP_READ, offset, length); + if (!op) + goto out; + obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, + obj_request, op); + rbd_osd_req_op_destroy(op); + if (!obj_request->osd_req) + goto out; + + osdc = &rbd_dev->rbd_client->client->osdc; + ret = rbd_obj_request_submit(osdc, obj_request); + if (ret) + goto out; + ret = rbd_obj_request_wait(obj_request); + if (ret) + goto out; + + ret = obj_request->result; + if (ret < 0) + goto out; + ret = ceph_copy_from_page_vector(pages, buf, 0, obj_request->xferred); + if (version) + *version = obj_request->version; +out: + if (obj_request) + rbd_obj_request_put(obj_request); + else + ceph_release_page_vector(pages, page_count); + + return ret; +} + /* * Read the complete header for the given rbd device. * @@ -2025,7 +2112,8 @@ rbd_dev_v1_header_read(struct rbd_device *rbd_dev, u64 *version) if (!ondisk) return ERR_PTR(-ENOMEM); - ret = rbd_req_sync_read(rbd_dev, rbd_dev->header_name, + (void) rbd_req_sync_read; /* avoid a warning */ + ret = rbd_obj_read_sync(rbd_dev, rbd_dev->header_name, 0, size, (char *) ondisk, version); -- cgit v1.2.3 From 86ea43bfcbeae61858b0fee4971e5b1e894d7174 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sun, 20 Jan 2013 14:44:42 -0600 Subject: rbd: get rid of rbd_req_sync_read() Delete rbd_req_sync_read() is no longer used, so get rid of it. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 24 ------------------------ 1 file changed, 24 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 3f5eaea444a0..5e7110a50513 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1314,29 +1314,6 @@ static void rbd_obj_request_complete(struct rbd_obj_request *obj_request) complete_all(&obj_request->completion); } -/* - * Synchronously read a range from an object into a provided buffer - */ -static int rbd_req_sync_read(struct rbd_device *rbd_dev, - const char *object_name, - u64 ofs, u64 len, - char *buf, - u64 *ver) -{ - struct ceph_osd_req_op *op; - int ret; - - op = rbd_osd_req_op_create(CEPH_OSD_OP_READ, ofs, len); - if (!op) - return -ENOMEM; - - ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, - op, object_name, ofs, len, buf, ver); - rbd_osd_req_op_destroy(op); - - return ret; -} - /* * Request sync osd watch */ @@ -2112,7 +2089,6 @@ rbd_dev_v1_header_read(struct rbd_device *rbd_dev, u64 *version) if (!ondisk) return ERR_PTR(-ENOMEM); - (void) rbd_req_sync_read; /* avoid a warning */ ret = rbd_obj_read_sync(rbd_dev, rbd_dev->header_name, 0, size, (char *) ondisk, version); -- cgit v1.2.3 From 9969ebc5af93028c21f2614621737f0d6ff6fc06 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 18 Jan 2013 12:31:10 -0600 Subject: rbd: implement watch/unwatch with new code Implement a new function to set up or tear down a watch event for an mapped rbd image header using the new request code. Create a new object request type "nodata" to handle this. And define rbd_osd_trivial_callback() which simply marks a request done. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 5e7110a50513..60b68512fa93 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -170,7 +170,9 @@ typedef void (*rbd_img_callback_t)(struct rbd_img_request *); struct rbd_obj_request; typedef void (*rbd_obj_callback_t)(struct rbd_obj_request *); -enum obj_request_type { OBJ_REQUEST_BIO, OBJ_REQUEST_PAGES }; +enum obj_request_type { + OBJ_REQUEST_NODATA, OBJ_REQUEST_BIO, OBJ_REQUEST_PAGES +}; struct rbd_obj_request { const char *object_name; @@ -1083,6 +1085,7 @@ static inline void rbd_img_obj_request_del(struct rbd_img_request *img_request, static bool obj_request_type_valid(enum obj_request_type type) { switch (type) { + case OBJ_REQUEST_NODATA: case OBJ_REQUEST_BIO: case OBJ_REQUEST_PAGES: return true; @@ -1306,6 +1309,12 @@ static int rbd_obj_request_wait(struct rbd_obj_request *obj_request) return wait_for_completion_interruptible(&obj_request->completion); } +static void rbd_osd_trivial_callback(struct rbd_obj_request *obj_request, + struct ceph_osd_op *op) +{ + atomic_set(&obj_request->done, 1); +} + static void rbd_obj_request_complete(struct rbd_obj_request *obj_request) { if (obj_request->callback) @@ -1500,6 +1509,9 @@ static void rbd_osd_req_callback(struct ceph_osd_request *osd_req, case CEPH_OSD_OP_WRITE: rbd_osd_write_callback(obj_request, op); break; + case CEPH_OSD_OP_WATCH: + rbd_osd_trivial_callback(obj_request, op); + break; default: rbd_warn(NULL, "%s: unsupported op %hu\n", obj_request->object_name, (unsigned short) opcode); @@ -1543,6 +1555,8 @@ static struct ceph_osd_request *rbd_osd_req_create( rbd_assert(obj_request_type_valid(obj_request->type)); switch (obj_request->type) { + case OBJ_REQUEST_NODATA: + break; /* Nothing to do */ case OBJ_REQUEST_BIO: rbd_assert(obj_request->bio_list != NULL); osd_req->r_bio = obj_request->bio_list; @@ -1635,6 +1649,8 @@ static void rbd_obj_request_destroy(struct kref *kref) rbd_assert(obj_request_type_valid(obj_request->type)); switch (obj_request->type) { + case OBJ_REQUEST_NODATA: + break; /* Nothing to do */ case OBJ_REQUEST_BIO: if (obj_request->bio_list) bio_chain_put(obj_request->bio_list); @@ -1862,6 +1878,72 @@ static int rbd_img_request_submit(struct rbd_img_request *img_request) return 0; } +/* + * Request sync osd watch/unwatch. The value of "start" determines + * whether a watch request is being initiated or torn down. + */ +static int rbd_dev_header_watch_sync(struct rbd_device *rbd_dev, int start) +{ + struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc; + struct rbd_obj_request *obj_request; + struct ceph_osd_req_op *op; + int ret; + + rbd_assert(start ^ !!rbd_dev->watch_event); + rbd_assert(start ^ !!rbd_dev->watch_request); + + if (start) { + ret = ceph_osdc_create_event(osdc, rbd_watch_cb, 0, rbd_dev, + &rbd_dev->watch_event); + if (ret < 0) + return ret; + } + + ret = -ENOMEM; + obj_request = rbd_obj_request_create(rbd_dev->header_name, 0, 0, + OBJ_REQUEST_NODATA); + if (!obj_request) + goto out_cancel; + + op = rbd_osd_req_op_create(CEPH_OSD_OP_WATCH, + rbd_dev->watch_event->cookie, + rbd_dev->header.obj_version, start); + if (!op) + goto out_cancel; + obj_request->osd_req = rbd_osd_req_create(rbd_dev, true, + obj_request, op); + rbd_osd_req_op_destroy(op); + if (!obj_request->osd_req) + goto out_cancel; + + if (start) { + rbd_dev->watch_request = obj_request->osd_req; + ceph_osdc_set_request_linger(osdc, rbd_dev->watch_request); + } + ret = rbd_obj_request_submit(osdc, obj_request); + if (ret) + goto out_cancel; + ret = rbd_obj_request_wait(obj_request); + if (ret) + goto out_cancel; + + ret = obj_request->result; + if (ret) + goto out_cancel; + + if (start) + goto done; /* Done if setting up the watch request */ +out_cancel: + /* Cancel the event if we're tearing down, or on error */ + ceph_osdc_cancel_event(rbd_dev->watch_event); + rbd_dev->watch_event = NULL; +done: + if (obj_request) + rbd_obj_request_put(obj_request); + + return ret; +} + static void rbd_request_fn(struct request_queue *q) { struct rbd_device *rbd_dev = q->queuedata; @@ -3879,7 +3961,8 @@ static int rbd_dev_probe_finish(struct rbd_device *rbd_dev) if (ret) goto err_out_bus; - ret = rbd_req_sync_watch(rbd_dev, 1); + (void) rbd_req_sync_watch; /* avoid a warning */ + ret = rbd_dev_header_watch_sync(rbd_dev, 1); if (ret) goto err_out_bus; @@ -4042,7 +4125,7 @@ static void rbd_dev_release(struct device *dev) rbd_dev->watch_request); } if (rbd_dev->watch_event) - rbd_req_sync_watch(rbd_dev, 0); + rbd_dev_header_watch_sync(rbd_dev, 0); /* clean up and free blkdev */ rbd_free_disk(rbd_dev); -- cgit v1.2.3 From ecf7a0318b1b026fb147623c36324fa8c73447a9 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sun, 20 Jan 2013 14:44:42 -0600 Subject: rbd: get rid of rbd_req_sync_watch() Get rid of rbd_req_sync_watch(), because it is no longer used. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 42 ------------------------------------------ 1 file changed, 42 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 60b68512fa93..a6a85271380a 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1369,47 +1369,6 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) rbd_req_sync_notify_ack(rbd_dev, hver, notify_id); } -/* - * Request sync osd watch/unwatch. The value of "start" determines - * whether a watch request is being initiated or torn down. - */ -static int rbd_req_sync_watch(struct rbd_device *rbd_dev, int start) -{ - struct ceph_osd_req_op *op; - int ret = 0; - - rbd_assert(start ^ !!rbd_dev->watch_event); - rbd_assert(start ^ !!rbd_dev->watch_request); - - if (start) { - struct ceph_osd_client *osdc; - - osdc = &rbd_dev->rbd_client->client->osdc; - ret = ceph_osdc_create_event(osdc, rbd_watch_cb, 0, rbd_dev, - &rbd_dev->watch_event); - if (ret < 0) - return ret; - } - - op = rbd_osd_req_op_create(CEPH_OSD_OP_WATCH, - rbd_dev->watch_event->cookie, - rbd_dev->header.obj_version, start); - if (op) - ret = rbd_req_sync_op(rbd_dev, - CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, - op, rbd_dev->header_name, - 0, 0, NULL, NULL); - - /* Cancel the event if we're tearing down, or on error */ - - if (!start || !op || ret < 0) { - ceph_osdc_cancel_event(rbd_dev->watch_event); - rbd_dev->watch_event = NULL; - } - rbd_osd_req_op_destroy(op); - - return ret; -} /* * Synchronous osd object method call @@ -3961,7 +3920,6 @@ static int rbd_dev_probe_finish(struct rbd_device *rbd_dev) if (ret) goto err_out_bus; - (void) rbd_req_sync_watch; /* avoid a warning */ ret = rbd_dev_header_watch_sync(rbd_dev, 1); if (ret) goto err_out_bus; -- cgit v1.2.3 From b8d70035b35dc12135d5835b659b229bcd6d4f94 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 30 Nov 2012 17:53:04 -0600 Subject: rbd: use new code for notify ack Use the new object request tracking mechanism for handling a notify_ack request. Move the callback function below the definition of this so we don't have to do a pre-declaration. This resolves: http://tracker.newdream.net/issues/3754 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 76 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index a6a85271380a..1428795571c9 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1349,27 +1349,6 @@ static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, return ret; } -static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) -{ - struct rbd_device *rbd_dev = (struct rbd_device *)data; - u64 hver; - int rc; - - if (!rbd_dev) - return; - - dout("rbd_watch_cb %s notify_id=%llu opcode=%u\n", - rbd_dev->header_name, (unsigned long long) notify_id, - (unsigned int) opcode); - rc = rbd_dev_refresh(rbd_dev, &hver); - if (rc) - rbd_warn(rbd_dev, "got notification but failed to " - " update snaps: %d\n", rc); - - rbd_req_sync_notify_ack(rbd_dev, hver, notify_id); -} - - /* * Synchronous osd object method call */ @@ -1468,6 +1447,7 @@ static void rbd_osd_req_callback(struct ceph_osd_request *osd_req, case CEPH_OSD_OP_WRITE: rbd_osd_write_callback(obj_request, op); break; + case CEPH_OSD_OP_NOTIFY_ACK: case CEPH_OSD_OP_WATCH: rbd_osd_trivial_callback(obj_request, op); break; @@ -1837,6 +1817,60 @@ static int rbd_img_request_submit(struct rbd_img_request *img_request) return 0; } +static int rbd_obj_notify_ack_sync(struct rbd_device *rbd_dev, + u64 ver, u64 notify_id) +{ + struct rbd_obj_request *obj_request; + struct ceph_osd_req_op *op; + struct ceph_osd_client *osdc; + int ret; + + obj_request = rbd_obj_request_create(rbd_dev->header_name, 0, 0, + OBJ_REQUEST_NODATA); + if (!obj_request) + return -ENOMEM; + + ret = -ENOMEM; + op = rbd_osd_req_op_create(CEPH_OSD_OP_NOTIFY_ACK, notify_id, ver); + if (!op) + goto out; + obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, + obj_request, op); + rbd_osd_req_op_destroy(op); + if (!obj_request->osd_req) + goto out; + + osdc = &rbd_dev->rbd_client->client->osdc; + ret = rbd_obj_request_submit(osdc, obj_request); + if (!ret) + ret = rbd_obj_request_wait(obj_request); +out: + rbd_obj_request_put(obj_request); + + return ret; +} + +static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) +{ + struct rbd_device *rbd_dev = (struct rbd_device *)data; + u64 hver; + int rc; + + if (!rbd_dev) + return; + + dout("rbd_watch_cb %s notify_id=%llu opcode=%u\n", + rbd_dev->header_name, (unsigned long long) notify_id, + (unsigned int) opcode); + rc = rbd_dev_refresh(rbd_dev, &hver); + if (rc) + rbd_warn(rbd_dev, "got notification but failed to " + " update snaps: %d\n", rc); + + (void) rbd_req_sync_notify_ack; /* avoid a warning */ + rbd_obj_notify_ack_sync(rbd_dev, hver, notify_id); +} + /* * Request sync osd watch/unwatch. The value of "start" determines * whether a watch request is being initiated or torn down. -- cgit v1.2.3 From 5ae9db81b45c2d95554c665043afffd5e9a7d5ac Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sun, 20 Jan 2013 14:44:42 -0600 Subject: rbd: get rid of rbd_req_sync_notify_ack() Get rid rbd_req_sync_notify_ack() because it is no longer used. As a result rbd_simple_req_cb() becomes unreferenced, so get rid of that too. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 33 --------------------------------- 1 file changed, 33 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 1428795571c9..7a6694d08874 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1242,12 +1242,6 @@ done_err: return ret; } -static void rbd_simple_req_cb(struct ceph_osd_request *osd_req, - struct ceph_msg *msg) -{ - ceph_osdc_put_request(osd_req); -} - /* * Do a synchronous ceph osd operation */ @@ -1323,32 +1317,6 @@ static void rbd_obj_request_complete(struct rbd_obj_request *obj_request) complete_all(&obj_request->completion); } -/* - * Request sync osd watch - */ -static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, - u64 ver, - u64 notify_id) -{ - struct ceph_osd_req_op *op; - int ret; - - op = rbd_osd_req_op_create(CEPH_OSD_OP_NOTIFY_ACK, notify_id, ver); - if (!op) - return -ENOMEM; - - ret = rbd_do_request(NULL, rbd_dev, NULL, CEPH_NOSNAP, - rbd_dev->header_name, 0, 0, NULL, - NULL, 0, - CEPH_OSD_FLAG_READ, - op, - rbd_simple_req_cb, NULL); - - rbd_osd_req_op_destroy(op); - - return ret; -} - /* * Synchronous osd object method call */ @@ -1867,7 +1835,6 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) rbd_warn(rbd_dev, "got notification but failed to " " update snaps: %d\n", rc); - (void) rbd_req_sync_notify_ack; /* avoid a warning */ rbd_obj_notify_ack_sync(rbd_dev, hver, notify_id); } -- cgit v1.2.3 From cf81b60e4bbd4a1281fe2640f9c0c40fe3a85fdf Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 17 Jan 2013 12:18:46 -0600 Subject: rbd: send notify ack asynchronously When we receive notification of a change to an rbd image's header object we need to refresh our information about the image (its size and snapshot context). Once we have refreshed our rbd image we need to acknowledge the notification. This acknowledgement was previously done synchronously, but there's really no need to wait for it to complete. Change it so the caller doesn't wait for the notify acknowledgement request to complete. And change the name to reflect it's no longer synchronous. This resolves: http://tracker.newdream.net/issues/3877 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 7a6694d08874..76917cc3e5a1 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1785,7 +1785,7 @@ static int rbd_img_request_submit(struct rbd_img_request *img_request) return 0; } -static int rbd_obj_notify_ack_sync(struct rbd_device *rbd_dev, +static int rbd_obj_notify_ack(struct rbd_device *rbd_dev, u64 ver, u64 notify_id) { struct rbd_obj_request *obj_request; @@ -1809,11 +1809,11 @@ static int rbd_obj_notify_ack_sync(struct rbd_device *rbd_dev, goto out; osdc = &rbd_dev->rbd_client->client->osdc; + obj_request->callback = rbd_obj_request_put; ret = rbd_obj_request_submit(osdc, obj_request); - if (!ret) - ret = rbd_obj_request_wait(obj_request); out: - rbd_obj_request_put(obj_request); + if (ret) + rbd_obj_request_put(obj_request); return ret; } @@ -1835,7 +1835,7 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) rbd_warn(rbd_dev, "got notification but failed to " " update snaps: %d\n", rc); - rbd_obj_notify_ack_sync(rbd_dev, hver, notify_id); + rbd_obj_notify_ack(rbd_dev, hver, notify_id); } /* -- cgit v1.2.3 From 36be9a761844e186f629f463b665945df4f67766 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sat, 19 Jan 2013 00:30:28 -0600 Subject: rbd: implement sync method with new code Reimplement synchronous object method calls using the new request tracking code. Use the name rbd_obj_method_sync() Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 114 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 96 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 76917cc3e5a1..d10649f2e346 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1415,6 +1415,7 @@ static void rbd_osd_req_callback(struct ceph_osd_request *osd_req, case CEPH_OSD_OP_WRITE: rbd_osd_write_callback(obj_request, op); break; + case CEPH_OSD_OP_CALL: case CEPH_OSD_OP_NOTIFY_ACK: case CEPH_OSD_OP_WATCH: rbd_osd_trivial_callback(obj_request, op); @@ -1904,6 +1905,82 @@ done: return ret; } +/* + * Synchronous osd object method call + */ +static int rbd_obj_method_sync(struct rbd_device *rbd_dev, + const char *object_name, + const char *class_name, + const char *method_name, + const char *outbound, + size_t outbound_size, + char *inbound, + size_t inbound_size, + u64 *version) +{ + struct rbd_obj_request *obj_request; + struct ceph_osd_client *osdc; + struct ceph_osd_req_op *op; + struct page **pages; + u32 page_count; + int ret; + + /* + * Method calls are ultimately read operations but they + * don't involve object data (so no offset or length). + * The result should placed into the inbound buffer + * provided. They also supply outbound data--parameters for + * the object method. Currently if this is present it will + * be a snapshot id. + */ + page_count = (u32) calc_pages_for(0, inbound_size); + pages = ceph_alloc_page_vector(page_count, GFP_KERNEL); + if (IS_ERR(pages)) + return PTR_ERR(pages); + + ret = -ENOMEM; + obj_request = rbd_obj_request_create(object_name, 0, 0, + OBJ_REQUEST_PAGES); + if (!obj_request) + goto out; + + obj_request->pages = pages; + obj_request->page_count = page_count; + + op = rbd_osd_req_op_create(CEPH_OSD_OP_CALL, class_name, + method_name, outbound, outbound_size); + if (!op) + goto out; + obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, + obj_request, op); + rbd_osd_req_op_destroy(op); + if (!obj_request->osd_req) + goto out; + + osdc = &rbd_dev->rbd_client->client->osdc; + ret = rbd_obj_request_submit(osdc, obj_request); + if (ret) + goto out; + ret = rbd_obj_request_wait(obj_request); + if (ret) + goto out; + + ret = obj_request->result; + if (ret < 0) + goto out; + ret = ceph_copy_from_page_vector(pages, inbound, 0, + obj_request->xferred); + if (version) + *version = obj_request->version; +out: + if (obj_request) + rbd_obj_request_put(obj_request); + else + ceph_release_page_vector(pages, page_count); + + return ret; +} + static void rbd_request_fn(struct request_queue *q) { struct rbd_device *rbd_dev = q->queuedata; @@ -2054,7 +2131,7 @@ static int rbd_obj_read_sync(struct rbd_device *rbd_dev, ret = -ENOMEM; obj_request = rbd_obj_request_create(object_name, offset, length, - OBJ_REQUEST_PAGES); + OBJ_REQUEST_PAGES); if (!obj_request) goto out; @@ -2754,11 +2831,12 @@ static int _rbd_dev_v2_snap_size(struct rbd_device *rbd_dev, u64 snap_id, __le64 size; } __attribute__ ((packed)) size_buf = { 0 }; - ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, + (void) rbd_req_sync_exec; /* Avoid a warning */ + ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name, "rbd", "get_size", (char *) &snapid, sizeof (snapid), (char *) &size_buf, sizeof (size_buf), NULL); - dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); + dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) return ret; @@ -2789,14 +2867,14 @@ static int rbd_dev_v2_object_prefix(struct rbd_device *rbd_dev) if (!reply_buf) return -ENOMEM; - ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, + ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name, "rbd", "get_object_prefix", NULL, 0, reply_buf, RBD_OBJ_PREFIX_LEN_MAX, NULL); - dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); + dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) goto out; - ret = 0; /* rbd_req_sync_exec() can return positive */ + ret = 0; /* rbd_obj_method_sync() can return positive */ p = reply_buf; rbd_dev->header.object_prefix = ceph_extract_encoded_string(&p, @@ -2827,12 +2905,12 @@ static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id, u64 incompat; int ret; - ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, + ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name, "rbd", "get_features", (char *) &snapid, sizeof (snapid), (char *) &features_buf, sizeof (features_buf), NULL); - dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); + dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) return ret; @@ -2883,11 +2961,11 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) } snapid = cpu_to_le64(CEPH_NOSNAP); - ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, + ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name, "rbd", "get_parent", (char *) &snapid, sizeof (snapid), (char *) reply_buf, size, NULL); - dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); + dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) goto out_err; @@ -2954,7 +3032,7 @@ static char *rbd_dev_image_name(struct rbd_device *rbd_dev) if (!reply_buf) goto out; - ret = rbd_req_sync_exec(rbd_dev, RBD_DIRECTORY, + ret = rbd_obj_method_sync(rbd_dev, RBD_DIRECTORY, "rbd", "dir_get_name", image_id, image_id_size, (char *) reply_buf, size, NULL); @@ -3060,11 +3138,11 @@ static int rbd_dev_v2_snap_context(struct rbd_device *rbd_dev, u64 *ver) if (!reply_buf) return -ENOMEM; - ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, + ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name, "rbd", "get_snapcontext", NULL, 0, reply_buf, size, ver); - dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); + dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) goto out; @@ -3129,11 +3207,11 @@ static char *rbd_dev_v2_snap_name(struct rbd_device *rbd_dev, u32 which) return ERR_PTR(-ENOMEM); snap_id = cpu_to_le64(rbd_dev->header.snapc->snaps[which]); - ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, + ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name, "rbd", "get_snapshot_name", (char *) &snap_id, sizeof (snap_id), reply_buf, size, NULL); - dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); + dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) goto out; @@ -3721,14 +3799,14 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev) goto out; } - ret = rbd_req_sync_exec(rbd_dev, object_name, + ret = rbd_obj_method_sync(rbd_dev, object_name, "rbd", "get_id", NULL, 0, response, RBD_IMAGE_ID_LEN_MAX, NULL); - dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); + dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) goto out; - ret = 0; /* rbd_req_sync_exec() can return positive */ + ret = 0; /* rbd_obj_method_sync() can return positive */ p = response; rbd_dev->spec->image_id = ceph_extract_encoded_string(&p, -- cgit v1.2.3 From 9f20e02a53b944a54a35b9f0db1243cd64872f7d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sun, 20 Jan 2013 14:44:42 -0600 Subject: rbd: get rid of rbd_req_sync_exec() Get rid rbd_req_sync_exec() because it is no longer used. That eliminates the last use of rbd_req_sync_op(), so get rid of that too. And finally, that leaves rbd_do_request() unreferenced, so get rid of that. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 160 ---------------------------------------------------- 1 file changed, 160 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index d10649f2e346..4ea89917bb92 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1162,126 +1162,6 @@ static void rbd_osd_req_op_destroy(struct ceph_osd_req_op *op) kfree(op); } -/* - * Send ceph osd request - */ -static int rbd_do_request(struct request *rq, - struct rbd_device *rbd_dev, - struct ceph_snap_context *snapc, - u64 snapid, - const char *object_name, u64 ofs, u64 len, - struct bio *bio, - struct page **pages, - int num_pages, - int flags, - struct ceph_osd_req_op *op, - void (*rbd_cb)(struct ceph_osd_request *, - struct ceph_msg *), - u64 *ver) -{ - struct ceph_osd_client *osdc; - struct ceph_osd_request *osd_req; - struct timespec mtime = CURRENT_TIME; - int ret; - - dout("rbd_do_request object_name=%s ofs=%llu len=%llu\n", - object_name, (unsigned long long) ofs, - (unsigned long long) len); - - osdc = &rbd_dev->rbd_client->client->osdc; - osd_req = ceph_osdc_alloc_request(osdc, snapc, 1, false, GFP_NOIO); - if (!osd_req) - return -ENOMEM; - - osd_req->r_flags = flags; - osd_req->r_pages = pages; - if (bio) { - osd_req->r_bio = bio; - bio_get(osd_req->r_bio); - } - - osd_req->r_callback = rbd_cb; - osd_req->r_priv = NULL; - - strncpy(osd_req->r_oid, object_name, sizeof(osd_req->r_oid)); - osd_req->r_oid_len = strlen(osd_req->r_oid); - - osd_req->r_file_layout = rbd_dev->layout; /* struct */ - osd_req->r_num_pages = calc_pages_for(ofs, len); - osd_req->r_page_alignment = ofs & ~PAGE_MASK; - - ceph_osdc_build_request(osd_req, ofs, len, 1, op, - snapc, snapid, &mtime); - - if (op->op == CEPH_OSD_OP_WATCH && op->watch.flag) { - ceph_osdc_set_request_linger(osdc, osd_req); - rbd_dev->watch_request = osd_req; - } - - ret = ceph_osdc_start_request(osdc, osd_req, false); - if (ret < 0) - goto done_err; - - if (!rbd_cb) { - u64 version; - - ret = ceph_osdc_wait_request(osdc, osd_req); - version = le64_to_cpu(osd_req->r_reassert_version.version); - if (ver) - *ver = version; - dout("reassert_ver=%llu\n", (unsigned long long) version); - ceph_osdc_put_request(osd_req); - } - return ret; - -done_err: - if (bio) - bio_chain_put(osd_req->r_bio); - ceph_osdc_put_request(osd_req); - - return ret; -} - -/* - * Do a synchronous ceph osd operation - */ -static int rbd_req_sync_op(struct rbd_device *rbd_dev, - int flags, - struct ceph_osd_req_op *op, - const char *object_name, - u64 ofs, u64 inbound_size, - char *inbound, - u64 *ver) -{ - int ret; - struct page **pages; - int num_pages; - - rbd_assert(op != NULL); - - num_pages = calc_pages_for(ofs, inbound_size); - pages = ceph_alloc_page_vector(num_pages, GFP_KERNEL); - if (IS_ERR(pages)) - return PTR_ERR(pages); - - ret = rbd_do_request(NULL, rbd_dev, NULL, CEPH_NOSNAP, - object_name, ofs, inbound_size, NULL, - pages, num_pages, - flags, - op, - NULL, - ver); - if (ret < 0) - goto done; - - if ((flags & CEPH_OSD_FLAG_READ) && inbound) - ret = ceph_copy_from_page_vector(pages, inbound, ofs, ret); - -done: - ceph_release_page_vector(pages, num_pages); - return ret; -} - static int rbd_obj_request_submit(struct ceph_osd_client *osdc, struct rbd_obj_request *obj_request) { @@ -1317,45 +1197,6 @@ static void rbd_obj_request_complete(struct rbd_obj_request *obj_request) complete_all(&obj_request->completion); } -/* - * Synchronous osd object method call - */ -static int rbd_req_sync_exec(struct rbd_device *rbd_dev, - const char *object_name, - const char *class_name, - const char *method_name, - const char *outbound, - size_t outbound_size, - char *inbound, - size_t inbound_size, - u64 *ver) -{ - struct ceph_osd_req_op *op; - int ret; - - /* - * Any input parameters required by the method we're calling - * will be sent along with the class and method names as - * part of the message payload. That data and its size are - * supplied via the indata and indata_len fields (named from - * the perspective of the server side) in the OSD request - * operation. - */ - op = rbd_osd_req_op_create(CEPH_OSD_OP_CALL, class_name, - method_name, outbound, outbound_size); - if (!op) - return -ENOMEM; - - ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, op, - object_name, 0, inbound_size, inbound, - ver); - - rbd_osd_req_op_destroy(op); - - dout("cls_exec returned %d\n", ret); - return ret; -} - static void rbd_osd_read_callback(struct rbd_obj_request *obj_request, struct ceph_osd_op *op) { @@ -2831,7 +2672,6 @@ static int _rbd_dev_v2_snap_size(struct rbd_device *rbd_dev, u64 snap_id, __le64 size; } __attribute__ ((packed)) size_buf = { 0 }; - (void) rbd_req_sync_exec; /* Avoid a warning */ ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name, "rbd", "get_size", (char *) &snapid, sizeof (snapid), -- cgit v1.2.3 From 6977c3f983b0d2b481a65b1fa3e85683fd1318af Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 25 Jan 2013 17:08:55 -0600 Subject: rbd: unregister linger in watch sync routine Move the code that unregisters an rbd device's lingering header object watch request into rbd_dev_header_watch_sync(), so it occurs in the same function that originally sets up that request. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 4ea89917bb92..5593def33ce5 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1721,6 +1721,10 @@ static int rbd_dev_header_watch_sync(struct rbd_device *rbd_dev, int start) if (start) { rbd_dev->watch_request = obj_request->osd_req; ceph_osdc_set_request_linger(osdc, rbd_dev->watch_request); + } else { + ceph_osdc_unregister_linger_request(osdc, + rbd_dev->watch_request); + rbd_dev->watch_request = NULL; } ret = rbd_obj_request_submit(osdc, obj_request); if (ret) @@ -3995,12 +3999,6 @@ static void rbd_dev_release(struct device *dev) { struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); - if (rbd_dev->watch_request) { - struct ceph_client *client = rbd_dev->rbd_client->client; - - ceph_osdc_unregister_linger_request(&client->osdc, - rbd_dev->watch_request); - } if (rbd_dev->watch_event) rbd_dev_header_watch_sync(rbd_dev, 0); -- cgit v1.2.3 From 975241afcbba82ab1ddc6ebf8a02246d1e9314fd Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 25 Jan 2013 17:08:55 -0600 Subject: rbd: track object rather than osd request for watch Switch to keeping track of the object request pointer rather than the osd request used to watch the rbd image header object. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 5593def33ce5..fc1a045cee4d 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -272,7 +272,7 @@ struct rbd_device { struct ceph_file_layout layout; struct ceph_osd_event *watch_event; - struct ceph_osd_request *watch_request; + struct rbd_obj_request *watch_request; struct rbd_spec *parent_spec; u64 parent_overlap; @@ -1719,11 +1719,11 @@ static int rbd_dev_header_watch_sync(struct rbd_device *rbd_dev, int start) goto out_cancel; if (start) { - rbd_dev->watch_request = obj_request->osd_req; - ceph_osdc_set_request_linger(osdc, rbd_dev->watch_request); + ceph_osdc_set_request_linger(osdc, obj_request->osd_req); + rbd_dev->watch_request = obj_request; } else { ceph_osdc_unregister_linger_request(osdc, - rbd_dev->watch_request); + rbd_dev->watch_request->osd_req); rbd_dev->watch_request = NULL; } ret = rbd_obj_request_submit(osdc, obj_request); -- cgit v1.2.3 From 25dcf954c3230946b5f3e18db9f91d7640eff76e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 25 Jan 2013 17:08:55 -0600 Subject: rbd: decrement obj request count when deleting Decrement the obj_request_count value when deleting an object request from its image request's list. Rearrange a few lines in the surrounding code. This resolves: http://tracker.ceph.com/issues/3940 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index fc1a045cee4d..d3d15d06abc0 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1063,22 +1063,29 @@ static void rbd_img_request_put(struct rbd_img_request *img_request) static inline void rbd_img_obj_request_add(struct rbd_img_request *img_request, struct rbd_obj_request *obj_request) { + rbd_assert(obj_request->img_request == NULL); + rbd_obj_request_get(obj_request); obj_request->img_request = img_request; - list_add_tail(&obj_request->links, &img_request->obj_requests); - obj_request->which = img_request->obj_request_count++; + obj_request->which = img_request->obj_request_count; rbd_assert(obj_request->which != BAD_WHICH); + img_request->obj_request_count++; + list_add_tail(&obj_request->links, &img_request->obj_requests); } static inline void rbd_img_obj_request_del(struct rbd_img_request *img_request, struct rbd_obj_request *obj_request) { rbd_assert(obj_request->which != BAD_WHICH); - obj_request->which = BAD_WHICH; + list_del(&obj_request->links); + rbd_assert(img_request->obj_request_count > 0); + img_request->obj_request_count--; + rbd_assert(obj_request->which == img_request->obj_request_count); + obj_request->which = BAD_WHICH; rbd_assert(obj_request->img_request == img_request); - obj_request->callback = NULL; obj_request->img_request = NULL; + obj_request->callback = NULL; rbd_obj_request_put(obj_request); } @@ -1472,6 +1479,7 @@ static void rbd_img_request_destroy(struct kref *kref) for_each_obj_request_safe(img_request, obj_request, next_obj_request) rbd_img_obj_request_del(img_request, obj_request); + rbd_assert(img_request->obj_request_count == 0); if (img_request->write_request) ceph_put_snap_context(img_request->snapc); -- cgit v1.2.3 From 8eb87565306cf40a32f5d0883d008675cd2dd510 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 25 Jan 2013 17:08:55 -0600 Subject: rbd: don't drop watch requests on completion When we register an osd request to linger, it means that request will stay around (under control of the osd client) until we've unregistered it. We do that for an rbd image's header object, and we keep a pointer to the object request associated with it. Keep a reference to the watch object request for as long as it is registered to linger. Drop it again after we've removed the linger registration. This resolves: http://tracker.ceph.com/issues/3937 (Note: this originally came about because the osd client was issuing a callback more than once. But that behavior will be changing soon, documented in tracker issue 3967.) Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index d3d15d06abc0..fd9656b5fdb9 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1707,6 +1707,7 @@ static int rbd_dev_header_watch_sync(struct rbd_device *rbd_dev, int start) &rbd_dev->watch_event); if (ret < 0) return ret; + rbd_assert(rbd_dev->watch_event != NULL); } ret = -ENOMEM; @@ -1726,32 +1727,43 @@ static int rbd_dev_header_watch_sync(struct rbd_device *rbd_dev, int start) if (!obj_request->osd_req) goto out_cancel; - if (start) { + if (start) ceph_osdc_set_request_linger(osdc, obj_request->osd_req); - rbd_dev->watch_request = obj_request; - } else { + else ceph_osdc_unregister_linger_request(osdc, rbd_dev->watch_request->osd_req); - rbd_dev->watch_request = NULL; - } ret = rbd_obj_request_submit(osdc, obj_request); if (ret) goto out_cancel; ret = rbd_obj_request_wait(obj_request); if (ret) goto out_cancel; - ret = obj_request->result; if (ret) goto out_cancel; - if (start) - goto done; /* Done if setting up the watch request */ + /* + * A watch request is set to linger, so the underlying osd + * request won't go away until we unregister it. We retain + * a pointer to the object request during that time (in + * rbd_dev->watch_request), so we'll keep a reference to + * it. We'll drop that reference (below) after we've + * unregistered it. + */ + if (start) { + rbd_dev->watch_request = obj_request; + + return 0; + } + + /* We have successfully torn down the watch request */ + + rbd_obj_request_put(rbd_dev->watch_request); + rbd_dev->watch_request = NULL; out_cancel: /* Cancel the event if we're tearing down, or on error */ ceph_osdc_cancel_event(rbd_dev->watch_event); rbd_dev->watch_event = NULL; -done: if (obj_request) rbd_obj_request_put(obj_request); -- cgit v1.2.3 From 6d292906f80170f4647079dd503df18b737750af Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 14 Jan 2013 12:43:31 -0600 Subject: rbd: define flags field, use it for exists flag Define a new rbd device flags field, manipulated using bit operations. Replace the use of the current "exists" flag with a bit in this new "flags" field. Add a little commentary about the "exists" flag, which does not need to be manipulated atomically. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index fd9656b5fdb9..8c90a39c2a91 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -264,7 +264,7 @@ struct rbd_device { spinlock_t lock; /* queue lock */ struct rbd_image_header header; - atomic_t exists; + unsigned long flags; struct rbd_spec *spec; char *header_name; @@ -292,6 +292,12 @@ struct rbd_device { unsigned long open_count; }; +/* Flag bits for rbd_dev->flags */ + +enum rbd_dev_flags { + RBD_DEV_FLAG_EXISTS, /* mapped snapshot has not been deleted */ +}; + static DEFINE_MUTEX(ctl_mutex); /* Serialize open/close/setup/teardown */ static LIST_HEAD(rbd_dev_list); /* devices */ @@ -782,7 +788,8 @@ static int rbd_dev_set_mapping(struct rbd_device *rbd_dev) goto done; rbd_dev->mapping.read_only = true; } - atomic_set(&rbd_dev->exists, 1); + set_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags); + done: return ret; } @@ -1877,9 +1884,14 @@ static void rbd_request_fn(struct request_queue *q) rbd_assert(rbd_dev->spec->snap_id == CEPH_NOSNAP); } - /* Quit early if the snapshot has disappeared */ - - if (!atomic_read(&rbd_dev->exists)) { + /* + * Quit early if the mapped snapshot no longer + * exists. It's still possible the snapshot will + * have disappeared by the time our request arrives + * at the osd, but there's no sense in sending it if + * we already know. + */ + if (!test_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags)) { dout("request for non-existent snapshot"); rbd_assert(rbd_dev->spec->snap_id != CEPH_NOSNAP); result = -ENXIO; @@ -2571,7 +2583,7 @@ struct rbd_device *rbd_dev_create(struct rbd_client *rbdc, return NULL; spin_lock_init(&rbd_dev->lock); - atomic_set(&rbd_dev->exists, 0); + rbd_dev->flags = 0; INIT_LIST_HEAD(&rbd_dev->node); INIT_LIST_HEAD(&rbd_dev->snaps); init_rwsem(&rbd_dev->header_rwsem); @@ -3200,10 +3212,17 @@ static int rbd_dev_snaps_update(struct rbd_device *rbd_dev) if (snap_id == CEPH_NOSNAP || (snap && snap->id > snap_id)) { struct list_head *next = links->next; - /* Existing snapshot not in the new snap context */ - + /* + * A previously-existing snapshot is not in + * the new snap context. + * + * If the now missing snapshot is the one the + * image is mapped to, clear its exists flag + * so we can avoid sending any more requests + * to it. + */ if (rbd_dev->spec->snap_id == snap->id) - atomic_set(&rbd_dev->exists, 0); + clear_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags); rbd_remove_snap_dev(snap); dout("%ssnap id %llu has been removed\n", rbd_dev->spec->snap_id == snap->id ? -- cgit v1.2.3 From b82d167be64b3e88d9434d8a98ce83c83a07aa48 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 14 Jan 2013 12:43:31 -0600 Subject: rbd: prevent open for image being removed An open request for a mapped rbd image can arrive while removal of that mapping is underway. We need to prevent such an open request from succeeding. (It appears that Maciej Galkiewicz ran into this problem.) Define and use a "removing" flag to indicate a mapping is getting removed. Set it in the remove path after verifying nothing holds the device open. And check it in the open path before allowing the open to proceed. Acquire the rbd device's lock around each of these spots to avoid any races accessing the flags and open_count fields. This addresses: http://tracker.newdream.net/issues/3427 Reported-by: Maciej Galkiewicz Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 8c90a39c2a91..ed0c91d81063 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -261,10 +261,10 @@ struct rbd_device { char name[DEV_NAME_LEN]; /* blkdev name, e.g. rbd3 */ - spinlock_t lock; /* queue lock */ + spinlock_t lock; /* queue, flags, open_count */ struct rbd_image_header header; - unsigned long flags; + unsigned long flags; /* possibly lock protected */ struct rbd_spec *spec; char *header_name; @@ -289,13 +289,19 @@ struct rbd_device { /* sysfs related */ struct device dev; - unsigned long open_count; + unsigned long open_count; /* protected by lock */ }; -/* Flag bits for rbd_dev->flags */ - +/* + * Flag bits for rbd_dev->flags. If atomicity is required, + * rbd_dev->lock is used to protect access. + * + * Currently, only the "removing" flag (which is coupled with the + * "open_count" field) requires atomic access. + */ enum rbd_dev_flags { RBD_DEV_FLAG_EXISTS, /* mapped snapshot has not been deleted */ + RBD_DEV_FLAG_REMOVING, /* this mapping is being removed */ }; static DEFINE_MUTEX(ctl_mutex); /* Serialize open/close/setup/teardown */ @@ -383,14 +389,23 @@ static int rbd_dev_v2_refresh(struct rbd_device *rbd_dev, u64 *hver); static int rbd_open(struct block_device *bdev, fmode_t mode) { struct rbd_device *rbd_dev = bdev->bd_disk->private_data; + bool removing = false; if ((mode & FMODE_WRITE) && rbd_dev->mapping.read_only) return -EROFS; + spin_lock(&rbd_dev->lock); + if (test_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags)) + removing = true; + else + rbd_dev->open_count++; + spin_unlock(&rbd_dev->lock); + if (removing) + return -ENOENT; + mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); (void) get_device(&rbd_dev->dev); set_device_ro(bdev, rbd_dev->mapping.read_only); - rbd_dev->open_count++; mutex_unlock(&ctl_mutex); return 0; @@ -399,10 +414,14 @@ static int rbd_open(struct block_device *bdev, fmode_t mode) static int rbd_release(struct gendisk *disk, fmode_t mode) { struct rbd_device *rbd_dev = disk->private_data; + unsigned long open_count_before; + + spin_lock(&rbd_dev->lock); + open_count_before = rbd_dev->open_count--; + spin_unlock(&rbd_dev->lock); + rbd_assert(open_count_before > 0); mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - rbd_assert(rbd_dev->open_count > 0); - rbd_dev->open_count--; put_device(&rbd_dev->dev); mutex_unlock(&ctl_mutex); @@ -4083,10 +4102,14 @@ static ssize_t rbd_remove(struct bus_type *bus, goto done; } - if (rbd_dev->open_count) { + spin_lock(&rbd_dev->lock); + if (rbd_dev->open_count) ret = -EBUSY; + else + set_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags); + spin_unlock(&rbd_dev->lock); + if (ret < 0) goto done; - } rbd_remove_all_snaps(rbd_dev); rbd_bus_del_dev(rbd_dev); -- cgit v1.2.3 From 1e32d34cfa6759df58b5f4002664241f2a0fef6a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 30 Jan 2013 11:13:33 -0600 Subject: rbd: don't take extra bio reference for osd client Currently, if the OSD client finds an osd request has had a bio list attached to it, it drops a reference to it (or rather, to the first entry on that list) when the request is released. The code that added that reference (i.e., the rbd client) is therefore required to take an extra reference to that first bio structure. The osd client doesn't really do anything with the bio pointer other than transfer it from the osd request structure to outgoing (for writes) and ingoing (for reads) messages. So it really isn't the right place to be taking or dropping references. Furthermore, the rbd client already holds references to all bio structures it passes to the osd client, and holds them until the request is completed. So there's no need for this extra reference whatsoever. So remove the bio_put() call in ceph_osdc_release_request(), as well as its matching bio_get() call in rbd_osd_req_create(). This change could lead to a crash if old libceph.ko was used with new rbd.ko. Add a compatibility check at rbd initialization time to avoid this possibilty. This resolves: http://tracker.ceph.com/issues/3798 and http://tracker.ceph.com/issues/3799 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 6 +++++- net/ceph/ceph_common.c | 2 +- net/ceph/osd_client.c | 4 ---- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index ed0c91d81063..3ba4836f024c 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1342,7 +1342,6 @@ static struct ceph_osd_request *rbd_osd_req_create( case OBJ_REQUEST_BIO: rbd_assert(obj_request->bio_list != NULL); osd_req->r_bio = obj_request->bio_list; - bio_get(osd_req->r_bio); /* osd client requires "num pages" even for bio */ osd_req->r_num_pages = calc_pages_for(offset, length); break; @@ -4149,6 +4148,11 @@ int __init rbd_init(void) { int rc; + if (!libceph_compatible(NULL)) { + rbd_warn(NULL, "libceph incompatibility (quitting)"); + + return -EINVAL; + } rc = rbd_sysfs_init(); if (rc) return rc; diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c index a98c03ff853f..c236c235c4a2 100644 --- a/net/ceph/ceph_common.c +++ b/net/ceph/ceph_common.c @@ -39,7 +39,7 @@ */ bool libceph_compatible(void *data) { - return false; + return true; } EXPORT_SYMBOL(libceph_compatible); diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 500ae8b49321..ba03648533c0 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -147,10 +147,6 @@ void ceph_osdc_release_request(struct kref *kref) if (req->r_own_pages) ceph_release_page_vector(req->r_pages, req->r_num_pages); -#ifdef CONFIG_BLOCK - if (req->r_bio) - bio_put(req->r_bio); -#endif ceph_put_snap_context(req->r_snapc); ceph_pagelist_release(&req->r_trail); if (req->r_mempool) -- cgit v1.2.3 From 9cbb1d7268afa997a7f96d779470cc57d28e1a13 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 31 Jan 2013 16:02:00 -0600 Subject: libceph: don't require r_num_pages for bio requests There is a check in the completion path for osd requests that ensures the number of pages allocated is enough to hold the amount of incoming data expected. For bio requests coming from rbd the "number of pages" is not really meaningful (although total length would be). So stop requiring that nr_pages be supplied for bio requests. This is done by checking whether the pages pointer is null before checking the value of nr_pages. Note that this value is passed on to the messenger, but there it's only used for debugging--it's never used for validation. While here, change another spot that used r_pages in a debug message inappropriately, and also invalidate the r_con_filling_msg pointer after dropping a reference to it. This resolves: http://tracker.ceph.com/issues/3875 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 2 -- net/ceph/osd_client.c | 7 ++++--- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 3ba4836f024c..14a6967291d3 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1342,8 +1342,6 @@ static struct ceph_osd_request *rbd_osd_req_create( case OBJ_REQUEST_BIO: rbd_assert(obj_request->bio_list != NULL); osd_req->r_bio = obj_request->bio_list; - /* osd client requires "num pages" even for bio */ - osd_req->r_num_pages = calc_pages_for(offset, length); break; case OBJ_REQUEST_PAGES: osd_req->r_pages = obj_request->pages; diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index ba03648533c0..d9d58bbe9f9a 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -137,10 +137,11 @@ void ceph_osdc_release_request(struct kref *kref) if (req->r_request) ceph_msg_put(req->r_request); if (req->r_con_filling_msg) { - dout("%s revoking pages %p from con %p\n", __func__, - req->r_pages, req->r_con_filling_msg); + dout("%s revoking msg %p from con %p\n", __func__, + req->r_reply, req->r_con_filling_msg); ceph_msg_revoke_incoming(req->r_reply); req->r_con_filling_msg->ops->put(req->r_con_filling_msg); + req->r_con_filling_msg = NULL; } if (req->r_reply) ceph_msg_put(req->r_reply); @@ -1981,7 +1982,7 @@ static struct ceph_msg *get_reply(struct ceph_connection *con, if (data_len > 0) { int want = calc_pages_for(req->r_page_alignment, data_len); - if (unlikely(req->r_num_pages < want)) { + if (req->r_pages && unlikely(req->r_num_pages < want)) { pr_warning("tid %lld reply has %d bytes %d pages, we" " had only %d pages ready\n", tid, data_len, want, req->r_num_pages); -- cgit v1.2.3 From a14ea269dd6b5e48a2941ba73b202cd7cd5d716d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 5 Feb 2013 13:23:12 -0600 Subject: rbd: turn off interrupts for open/remove locking This commit: bc7a62ee5 rbd: prevent open for image being removed added checking for removing rbd before allowing an open, and used the same request spinlock for protecting that and updating the open count as is used for the request queue. However it used the non-irq protected version of the spinlocks. Fix that. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 14a6967291d3..91983a60487b 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -394,12 +394,12 @@ static int rbd_open(struct block_device *bdev, fmode_t mode) if ((mode & FMODE_WRITE) && rbd_dev->mapping.read_only) return -EROFS; - spin_lock(&rbd_dev->lock); + spin_lock_irq(&rbd_dev->lock); if (test_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags)) removing = true; else rbd_dev->open_count++; - spin_unlock(&rbd_dev->lock); + spin_unlock_irq(&rbd_dev->lock); if (removing) return -ENOENT; @@ -416,9 +416,9 @@ static int rbd_release(struct gendisk *disk, fmode_t mode) struct rbd_device *rbd_dev = disk->private_data; unsigned long open_count_before; - spin_lock(&rbd_dev->lock); + spin_lock_irq(&rbd_dev->lock); open_count_before = rbd_dev->open_count--; - spin_unlock(&rbd_dev->lock); + spin_unlock_irq(&rbd_dev->lock); rbd_assert(open_count_before > 0); mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); @@ -4099,12 +4099,12 @@ static ssize_t rbd_remove(struct bus_type *bus, goto done; } - spin_lock(&rbd_dev->lock); + spin_lock_irq(&rbd_dev->lock); if (rbd_dev->open_count) ret = -EBUSY; else set_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags); - spin_unlock(&rbd_dev->lock); + spin_unlock_irq(&rbd_dev->lock); if (ret < 0) goto done; -- cgit v1.2.3 From 077413082f9ade9ca4d9774dbdc81ee7256d8089 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 5 Feb 2013 23:41:50 -0600 Subject: rbd: add barriers near done flag operations Somehow, I missed this little item in Documentation/atomic_ops.txt: *** WARNING: atomic_read() and atomic_set() DO NOT IMPLY BARRIERS! *** Create and use some helper functions that include the proper memory barriers for manipulating the done field. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 91983a60487b..982963ec2607 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1216,10 +1216,28 @@ static int rbd_obj_request_wait(struct rbd_obj_request *obj_request) return wait_for_completion_interruptible(&obj_request->completion); } +static void obj_request_done_init(struct rbd_obj_request *obj_request) +{ + atomic_set(&obj_request->done, 0); + smp_wmb(); +} + +static void obj_request_done_set(struct rbd_obj_request *obj_request) +{ + atomic_set(&obj_request->done, 1); + smp_wmb(); +} + +static bool obj_request_done_test(struct rbd_obj_request *obj_request) +{ + smp_rmb(); + return atomic_read(&obj_request->done) != 0; +} + static void rbd_osd_trivial_callback(struct rbd_obj_request *obj_request, struct ceph_osd_op *op) { - atomic_set(&obj_request->done, 1); + obj_request_done_set(obj_request); } static void rbd_obj_request_complete(struct rbd_obj_request *obj_request) @@ -1249,14 +1267,14 @@ static void rbd_osd_read_callback(struct rbd_obj_request *obj_request, xferred = obj_request->length; } obj_request->xferred = xferred; - atomic_set(&obj_request->done, 1); + obj_request_done_set(obj_request); } static void rbd_osd_write_callback(struct rbd_obj_request *obj_request, struct ceph_osd_op *op) { obj_request->xferred = le64_to_cpu(op->extent.length); - atomic_set(&obj_request->done, 1); + obj_request_done_set(obj_request); } static void rbd_osd_req_callback(struct ceph_osd_request *osd_req, @@ -1300,7 +1318,7 @@ static void rbd_osd_req_callback(struct ceph_osd_request *osd_req, break; } - if (atomic_read(&obj_request->done)) + if (obj_request_done_test(obj_request)) rbd_obj_request_complete(obj_request); } @@ -1407,7 +1425,7 @@ static struct rbd_obj_request *rbd_obj_request_create(const char *object_name, obj_request->which = BAD_WHICH; obj_request->type = type; INIT_LIST_HEAD(&obj_request->links); - atomic_set(&obj_request->done, 0); + obj_request_done_init(obj_request); init_completion(&obj_request->completion); kref_init(&obj_request->kref); @@ -1611,7 +1629,7 @@ static void rbd_img_obj_callback(struct rbd_obj_request *obj_request) rbd_assert(more); rbd_assert(which < img_request->obj_request_count); - if (!atomic_read(&obj_request->done)) + if (!obj_request_done_test(obj_request)) break; rbd_assert(obj_request->xferred <= (u64) UINT_MAX); -- cgit v1.2.3 From 877e86f28385407f05c5aa4e397d4ccb3233f01a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 10:41:09 +0200 Subject: dw_dmac: apply default dma_mask if needed In some cases we got the device without dma_mask configured. We have to apply the default value to avoid crashes during memory mapping. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 6694676c9b82..4c83f18803f1 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1661,6 +1661,12 @@ static int dw_probe(struct platform_device *pdev) if (!regs) return -EBUSY; + /* Apply default dma_mask if needed */ + if (!pdev->dev.dma_mask) { + pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + } + dw_params = dma_read_byaddr(regs, DW_PARAMS); autocfg = dw_params >> DW_PARAMS_EN & 0x1; -- cgit v1.2.3 From e68b1130dfbb834a4a028df01133591aeb59d241 Mon Sep 17 00:00:00 2001 From: Cong Ding Date: Thu, 14 Feb 2013 11:16:10 +0100 Subject: dma: of-dma.c: fix memory leakage The memory allocated to ofdma might be a leakage when error occurs. Signed-off-by: Cong Ding Signed-off-by: Vinod Koul --- drivers/dma/of-dma.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/dma/of-dma.c b/drivers/dma/of-dma.c index 59631b2c4666..583e50e3d47c 100644 --- a/drivers/dma/of-dma.c +++ b/drivers/dma/of-dma.c @@ -107,6 +107,7 @@ int of_dma_controller_register(struct device_node *np, if (!nbcells) { pr_err("%s: #dma-cells property is missing or invalid\n", __func__); + kfree(ofdma); return -EINVAL; } -- cgit v1.2.3 From 7dd145252574e34d92ad574e5168e4115639c0be Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Thu, 14 Feb 2013 10:03:10 +0100 Subject: dmaengine: ste_dma40: do not remove descriptors for cyclic transfers Fix dma_tc_handle() to call d40_desc_remove() and d40_desc_done() only for non-cyclic transfers, as this was breaking ux500_pcm since introduced in: d49278e dmaengine: dma40: Add support to split up large elements Reported-by: Shreshtha Kumar Sahu Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Vinod Koul --- drivers/dma/ste_dma40.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index ad860a221c33..1734feec47b1 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -1570,10 +1570,10 @@ static void dma_tc_handle(struct d40_chan *d40c) d40c->busy = false; pm_runtime_mark_last_busy(d40c->base->dev); pm_runtime_put_autosuspend(d40c->base->dev); - } - d40_desc_remove(d40d); - d40_desc_done(d40c, d40d); + d40_desc_remove(d40d); + d40_desc_done(d40c, d40d); + } d40c->pending_tx++; tasklet_schedule(&d40c->tasklet); -- cgit v1.2.3 From 88b386c0a7c278581d2b8e2c2761d12f6475938d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 11:00:15 +0200 Subject: dma: of-dma: protect list write operation by spin_lock It's possible to have an inconsistency in the list due to unprotected operation on it. The patch adds a proper locking on the list operation. Signed-off-by: Andy Shevchenko Acked-by: Rob Herring Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/of-dma.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/dma/of-dma.c b/drivers/dma/of-dma.c index 583e50e3d47c..69d04d28b1ef 100644 --- a/drivers/dma/of-dma.c +++ b/drivers/dma/of-dma.c @@ -118,7 +118,9 @@ int of_dma_controller_register(struct device_node *np, ofdma->use_count = 0; /* Now queue of_dma controller structure in list */ + spin_lock(&of_dma_lock); list_add_tail(&ofdma->of_dma_controllers, &of_dma_list); + spin_unlock(&of_dma_lock); return 0; } -- cgit v1.2.3 From 4168d0d9d304f184f786b1f00750557b8e09453c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 11:00:17 +0200 Subject: dma: coh901318: avoid unbalanced locking In case the len is 0 we must return without trying to unlock the lock that was not locked. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Cc: Linus Walleij Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Vinod Koul --- drivers/dma/coh901318_lli.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/coh901318_lli.c b/drivers/dma/coh901318_lli.c index 780e0429b38c..fe9cc5eeddae 100644 --- a/drivers/dma/coh901318_lli.c +++ b/drivers/dma/coh901318_lli.c @@ -61,7 +61,7 @@ coh901318_lli_alloc(struct coh901318_pool *pool, unsigned int len) dma_addr_t phy; if (len == 0) - goto err; + return NULL; spin_lock(&pool->lock); -- cgit v1.2.3 From 9b562639a1dbef847dfc9daa807bd3e7e02ef24f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 11:00:18 +0200 Subject: dma: coh901318: set residue only if dma is in progress When status is DMA_SUCCESS the residue should be zero. Otherwise it's a bug. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Cc: Linus Walleij Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Vinod Koul --- drivers/dma/coh901318.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index aa384e53b7ac..671e75962370 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -1147,7 +1147,9 @@ coh901318_tx_status(struct dma_chan *chan, dma_cookie_t cookie, enum dma_status ret; ret = dma_cookie_status(chan, cookie, txstate); - /* FIXME: should be conditional on ret != DMA_SUCCESS? */ + if (ret == DMA_SUCCESS) + return ret; + dma_set_residue(txstate, coh901318_get_bytes_left(chan)); if (ret == DMA_IN_PROGRESS && cohc->stopped) -- cgit v1.2.3 From 373459eee0cd79f775ef9874395bb37638154777 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 11:00:19 +0200 Subject: edma: do not waste memory for dma_mask Accordingly to commentary in the platform_device_register_full the memory allocated for dma_mask will not going to be freed. That's why is better to assign dma_mask afterwards. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 82c8672f26e8..af44cadb8de1 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -620,13 +620,11 @@ static struct platform_device *pdev0, *pdev1; static const struct platform_device_info edma_dev_info0 = { .name = "edma-dma-engine", .id = 0, - .dma_mask = DMA_BIT_MASK(32), }; static const struct platform_device_info edma_dev_info1 = { .name = "edma-dma-engine", .id = 1, - .dma_mask = DMA_BIT_MASK(32), }; static int edma_init(void) @@ -640,6 +638,8 @@ static int edma_init(void) ret = PTR_ERR(pdev0); goto out; } + pdev0->dev.dma_mask = &pdev0->dev.coherent_dma_mask; + pdev0->dev.coherent_dma_mask = DMA_BIT_MASK(32); } if (EDMA_CTLRS == 2) { @@ -649,6 +649,8 @@ static int edma_init(void) platform_device_unregister(pdev0); ret = PTR_ERR(pdev1); } + pdev1->dev.dma_mask = &pdev1->dev.coherent_dma_mask; + pdev1->dev.coherent_dma_mask = DMA_BIT_MASK(32); } out: -- cgit v1.2.3 From a72208733f7866972bcba7995f7e598ad07f1158 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 11:00:20 +0200 Subject: dma: tegra20-apb-dma: remove unnecessary assignment There is no need to assign 0 to residue, because dma_cookie_status() does this for us. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Acked-by: Laxman Dewangan Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 6c144814a896..c71812f8d9c1 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -767,7 +767,6 @@ static enum dma_status tegra_dma_tx_status(struct dma_chan *dc, ret = dma_cookie_status(dc, cookie, txstate); if (ret == DMA_SUCCESS) { - dma_set_residue(txstate, 0); spin_unlock_irqrestore(&tdc->lock, flags); return ret; } -- cgit v1.2.3 From 34d19355b84adde9eebc1d6771231c15dff891e6 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Thu, 14 Feb 2013 09:10:05 +0530 Subject: DMA: PL330: Add new pl330 filter for DT case. This patch adds a new pl330_dt_filter for DT case to filter the required channel based on the new filter params and modifies the old filter only for non-DT case as suggested by Arnd Bergmann. Signed-off-by: Padmavathi Venna Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index f7edb6f0ee87..40e97528244b 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -606,6 +606,11 @@ struct dma_pl330_desc { struct dma_pl330_chan *pchan; }; +struct dma_pl330_filter_args { + struct dma_pl330_dmac *pdmac; + unsigned int chan_id; +}; + static inline void _callback(struct pl330_req *r, enum pl330_op_err err) { if (r && r->xfer_cb) @@ -2352,6 +2357,16 @@ static void dma_pl330_rqcb(void *token, enum pl330_op_err err) tasklet_schedule(&pch->task); } +static bool pl330_dt_filter(struct dma_chan *chan, void *param) +{ + struct dma_pl330_filter_args *fargs = param; + + if (chan->device != &fargs->pdmac->ddma) + return false; + + return (chan->chan_id == fargs->chan_id); +} + bool pl330_filter(struct dma_chan *chan, void *param) { u8 *peri_id; @@ -2359,20 +2374,6 @@ bool pl330_filter(struct dma_chan *chan, void *param) if (chan->device->dev->driver != &pl330_driver.drv) return false; -#ifdef CONFIG_OF - if (chan->device->dev->of_node) { - const __be32 *prop_value; - phandle phandle; - struct device_node *node; - - prop_value = ((struct property *)param)->value; - phandle = be32_to_cpup(prop_value++); - node = of_find_node_by_phandle(phandle); - return ((chan->private == node) && - (chan->chan_id == be32_to_cpup(prop_value))); - } -#endif - peri_id = chan->private; return *peri_id == (unsigned)param; } -- cgit v1.2.3 From a80258f9b2ac81e72ff680d273df9544a1307a32 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Thu, 14 Feb 2013 09:10:06 +0530 Subject: DMA: PL330: Add xlate function Add xlate to translate the device-tree binding information into the appropriate format. The filter function requires the dma controller device and dma channel number as filter_params. Signed-off-by: Padmavathi Venna Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers') diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 40e97528244b..f5d47e617df2 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "dmaengine.h" #define PL330_MAX_CHAN 8 @@ -2379,6 +2380,30 @@ bool pl330_filter(struct dma_chan *chan, void *param) } EXPORT_SYMBOL(pl330_filter); +static struct dma_chan *of_dma_pl330_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + int count = dma_spec->args_count; + struct dma_pl330_dmac *pdmac = ofdma->of_dma_data; + struct dma_pl330_filter_args fargs; + dma_cap_mask_t cap; + + if (!pdmac) + return NULL; + + if (count != 1) + return NULL; + + fargs.pdmac = pdmac; + fargs.chan_id = dma_spec->args[0]; + + dma_cap_zero(cap); + dma_cap_set(DMA_SLAVE, cap); + dma_cap_set(DMA_CYCLIC, cap); + + return dma_request_channel(cap, pl330_dt_filter, &fargs); +} + static int pl330_alloc_chan_resources(struct dma_chan *chan) { struct dma_pl330_chan *pch = to_pchan(chan); -- cgit v1.2.3 From 421da89aadd1b24f4a3bc1d60c9de9825ec2debc Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Thu, 14 Feb 2013 09:10:07 +0530 Subject: DMA: PL330: Register the DMA controller with the generic DMA helpers This patch registers the pl330 dma controller driver with the generic device tree dma helper functions. Signed-off-by: Padmavathi Venna Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index f5d47e617df2..fc9c80017378 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2995,6 +2995,14 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pi->pcfg.data_bus_width / 8, pi->pcfg.num_chan, pi->pcfg.num_peri, pi->pcfg.num_events); + ret = of_dma_controller_register(adev->dev.of_node, + of_dma_pl330_xlate, pdmac); + if (ret) { + dev_err(&adev->dev, + "unable to register DMA to the generic DT DMA helpers\n"); + goto probe_err2; + } + return 0; probe_err2: @@ -3015,6 +3023,8 @@ static int __devexit pl330_remove(struct amba_device *adev) if (!pdmac) return 0; + of_dma_controller_free(adev->dev.of_node); + amba_set_drvdata(adev, NULL); /* Idle the DMAC */ -- cgit v1.2.3 From 736c9fd2902d919b075cf9cf371d1733c5ff635d Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Tue, 8 Jan 2013 15:15:12 +0100 Subject: s390/3270: readd tty3270_open Reintroduce the tty3270_open function which has been removed by git commit 20cda6f2 "TTY: tty3270, add tty install". Without the open function in the tty_operations tty_open will return -ENODEV and the 3270 tty will not work. Signed-off-by: Martin Schwidefsky --- drivers/s390/char/tty3270.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 43ea0593bdb0..964018402b36 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -925,6 +925,20 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) return 0; } +/* + * This routine is called whenever a 3270 tty is opened. + */ +static int +tty3270_open(struct tty_struct *tty, struct file *filp) +{ + struct tty3270 *tp = tty->driver_data; + struct tty_port *port = &tp->port; + + port->count++; + tty_port_tty_set(port, tty); + return 0; +} + /* * This routine is called when the 3270 tty is closed. We wait * for the remaining request to be completed. Then we clean up. @@ -1753,6 +1767,7 @@ static long tty3270_compat_ioctl(struct tty_struct *tty, static const struct tty_operations tty3270_ops = { .install = tty3270_install, .cleanup = tty3270_cleanup, + .open = tty3270_open, .close = tty3270_close, .write = tty3270_write, .put_char = tty3270_put_char, -- cgit v1.2.3 From 57985d7e1e48f16548aa6904264e21bca15af0fc Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Tue, 8 Jan 2013 15:20:05 +0100 Subject: s390/3270: fix initialization order in tty3270_alloc_view Corrects the order of tasklet_init vs. the allocation of the read request which has been broken by git commit 9d2ae233 "TTY: tty3270, move initialization to allocation". Signed-off-by: Martin Schwidefsky --- drivers/s390/char/tty3270.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 964018402b36..5e4b6fc49ae3 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -683,12 +683,6 @@ tty3270_alloc_view(void) INIT_LIST_HEAD(&tp->update); INIT_LIST_HEAD(&tp->rcl_lines); tp->rcl_max = 20; - tty_port_init(&tp->port); - setup_timer(&tp->timer, (void (*)(unsigned long)) tty3270_update, - (unsigned long) tp); - tasklet_init(&tp->readlet, - (void (*)(unsigned long)) tty3270_read_tasklet, - (unsigned long) tp->read); for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) { tp->freemem_pages[pages] = (void *) @@ -710,6 +704,14 @@ tty3270_alloc_view(void) tp->kbd = kbd_alloc(); if (!tp->kbd) goto out_reset; + + tty_port_init(&tp->port); + setup_timer(&tp->timer, (void (*)(unsigned long)) tty3270_update, + (unsigned long) tp); + tasklet_init(&tp->readlet, + (void (*)(unsigned long)) tty3270_read_tasklet, + (unsigned long) tp->read); + return tp; out_reset: -- cgit v1.2.3 From c95571e68086d36e8e3369597b03ec29c63abec9 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Tue, 8 Jan 2013 15:31:11 +0100 Subject: s390/3270: introduce device notifier Add a notifier to create / destroy the device nodes for the tty view and the fullscreen view. Only device nodes for online devices are created and the device names will follow the convention as outlined in Documentation/devices.txt: 3270/tty for the tty nodes, 3270/tub for hte fullscreen nodes and 3270/tub for the fullscreen control node. Signed-off-by: Martin Schwidefsky --- drivers/s390/char/fs3270.c | 29 +++++++++++++++-- drivers/s390/char/raw3270.c | 76 +++++++++------------------------------------ drivers/s390/char/raw3270.h | 11 +++++-- drivers/s390/char/tty3270.c | 49 +++++++++++++++++++---------- 4 files changed, 83 insertions(+), 82 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 911704571b9c..230697aac94b 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -443,7 +443,7 @@ fs3270_open(struct inode *inode, struct file *filp) tty_kref_put(tty); return -ENODEV; } - minor = tty->index + RAW3270_FIRSTMINOR; + minor = tty->index; tty_kref_put(tty); } mutex_lock(&fs3270_mutex); @@ -524,6 +524,25 @@ static const struct file_operations fs3270_fops = { .llseek = no_llseek, }; +void fs3270_create_cb(int minor) +{ + __register_chrdev(IBM_FS3270_MAJOR, minor, 1, "tub", &fs3270_fops); + device_create(class3270, NULL, MKDEV(IBM_FS3270_MAJOR, minor), + NULL, "3270/tub%d", minor); +} + +void fs3270_destroy_cb(int minor) +{ + device_destroy(class3270, MKDEV(IBM_FS3270_MAJOR, minor)); + __unregister_chrdev(IBM_FS3270_MAJOR, minor, 1, "tub"); +} + +struct raw3270_notifier fs3270_notifier = +{ + .create = fs3270_create_cb, + .destroy = fs3270_destroy_cb, +}; + /* * 3270 fullscreen driver initialization. */ @@ -532,16 +551,20 @@ fs3270_init(void) { int rc; - rc = register_chrdev(IBM_FS3270_MAJOR, "fs3270", &fs3270_fops); + rc = __register_chrdev(IBM_FS3270_MAJOR, 0, 1, "fs3270", &fs3270_fops); if (rc) return rc; + device_create(class3270, NULL, MKDEV(IBM_FS3270_MAJOR, 0), + NULL, "3270/tub"); + raw3270_register_notifier(&fs3270_notifier); return 0; } static void __exit fs3270_exit(void) { - unregister_chrdev(IBM_FS3270_MAJOR, "fs3270"); + raw3270_unregister_notifier(&fs3270_notifier); + __unregister_chrdev(IBM_FS3270_MAJOR, 0, 1, "fs3270"); } MODULE_LICENSE("GPL"); diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 9a6c140c5f07..72f69fda9d01 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -28,7 +28,7 @@ #include #include -static struct class *class3270; +struct class *class3270; /* The main 3270 data structure. */ struct raw3270 { @@ -46,8 +46,6 @@ struct raw3270 { struct timer_list timer; /* Device timer. */ unsigned char *ascebc; /* ascii -> ebcdic table */ - struct device *clttydev; /* 3270-class tty device ptr */ - struct device *cltubdev; /* 3270-class tub device ptr */ struct raw3270_request init_request; unsigned char init_data[256]; @@ -1072,10 +1070,6 @@ raw3270_delete_device(struct raw3270 *rp) /* Remove from device chain. */ mutex_lock(&raw3270_mutex); - if (rp->clttydev && !IS_ERR(rp->clttydev)) - device_destroy(class3270, MKDEV(IBM_TTY3270_MAJOR, rp->minor)); - if (rp->cltubdev && !IS_ERR(rp->cltubdev)) - device_destroy(class3270, MKDEV(IBM_FS3270_MAJOR, rp->minor)); list_del_init(&rp->list); mutex_unlock(&raw3270_mutex); @@ -1139,75 +1133,34 @@ static struct attribute_group raw3270_attr_group = { static int raw3270_create_attributes(struct raw3270 *rp) { - int rc; - - rc = sysfs_create_group(&rp->cdev->dev.kobj, &raw3270_attr_group); - if (rc) - goto out; - - rp->clttydev = device_create(class3270, &rp->cdev->dev, - MKDEV(IBM_TTY3270_MAJOR, rp->minor), NULL, - "tty%s", dev_name(&rp->cdev->dev)); - if (IS_ERR(rp->clttydev)) { - rc = PTR_ERR(rp->clttydev); - goto out_ttydev; - } - - rp->cltubdev = device_create(class3270, &rp->cdev->dev, - MKDEV(IBM_FS3270_MAJOR, rp->minor), NULL, - "tub%s", dev_name(&rp->cdev->dev)); - if (!IS_ERR(rp->cltubdev)) - goto out; - - rc = PTR_ERR(rp->cltubdev); - device_destroy(class3270, MKDEV(IBM_TTY3270_MAJOR, rp->minor)); - -out_ttydev: - sysfs_remove_group(&rp->cdev->dev.kobj, &raw3270_attr_group); -out: - return rc; + return sysfs_create_group(&rp->cdev->dev.kobj, &raw3270_attr_group); } /* * Notifier for device addition/removal */ -struct raw3270_notifier { - struct list_head list; - void (*notifier)(int, int); -}; - static LIST_HEAD(raw3270_notifier); -int raw3270_register_notifier(void (*notifier)(int, int)) +int raw3270_register_notifier(struct raw3270_notifier *notifier) { - struct raw3270_notifier *np; struct raw3270 *rp; - np = kmalloc(sizeof(struct raw3270_notifier), GFP_KERNEL); - if (!np) - return -ENOMEM; - np->notifier = notifier; mutex_lock(&raw3270_mutex); - list_add_tail(&np->list, &raw3270_notifier); - list_for_each_entry(rp, &raw3270_devices, list) { - get_device(&rp->cdev->dev); - notifier(rp->minor, 1); - } + list_add_tail(¬ifier->list, &raw3270_notifier); + list_for_each_entry(rp, &raw3270_devices, list) + notifier->create(rp->minor); mutex_unlock(&raw3270_mutex); return 0; } -void raw3270_unregister_notifier(void (*notifier)(int, int)) +void raw3270_unregister_notifier(struct raw3270_notifier *notifier) { - struct raw3270_notifier *np; + struct raw3270 *rp; mutex_lock(&raw3270_mutex); - list_for_each_entry(np, &raw3270_notifier, list) - if (np->notifier == notifier) { - list_del(&np->list); - kfree(np); - break; - } + list_for_each_entry(rp, &raw3270_devices, list) + notifier->destroy(rp->minor); + list_del(¬ifier->list); mutex_unlock(&raw3270_mutex); } @@ -1217,8 +1170,8 @@ void raw3270_unregister_notifier(void (*notifier)(int, int)) static int raw3270_set_online (struct ccw_device *cdev) { - struct raw3270 *rp; struct raw3270_notifier *np; + struct raw3270 *rp; int rc; rp = raw3270_create_device(cdev); @@ -1239,7 +1192,7 @@ raw3270_set_online (struct ccw_device *cdev) set_bit(RAW3270_FLAGS_READY, &rp->flags); mutex_lock(&raw3270_mutex); list_for_each_entry(np, &raw3270_notifier, list) - np->notifier(rp->minor, 1); + np->create(rp->minor); mutex_unlock(&raw3270_mutex); return 0; @@ -1290,7 +1243,7 @@ raw3270_remove (struct ccw_device *cdev) mutex_lock(&raw3270_mutex); list_for_each_entry(np, &raw3270_notifier, list) - np->notifier(rp->minor, 0); + np->destroy(rp->minor); mutex_unlock(&raw3270_mutex); /* Reset 3270 device. */ @@ -1434,6 +1387,7 @@ MODULE_LICENSE("GPL"); module_init(raw3270_init); module_exit(raw3270_exit); +EXPORT_SYMBOL(class3270); EXPORT_SYMBOL(raw3270_request_alloc); EXPORT_SYMBOL(raw3270_request_free); EXPORT_SYMBOL(raw3270_request_reset); diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index ed34eb2199cc..a4c79d043cd3 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -91,6 +91,7 @@ struct raw3270_iocb { struct raw3270; struct raw3270_view; +extern struct class *class3270; /* 3270 CCW request */ struct raw3270_request { @@ -192,8 +193,14 @@ struct raw3270 *raw3270_setup_console(struct ccw_device *cdev); void raw3270_wait_cons_dev(struct raw3270 *); /* Notifier for device addition/removal */ -int raw3270_register_notifier(void (*notifier)(int, int)); -void raw3270_unregister_notifier(void (*notifier)(int, int)); +struct raw3270_notifier { + struct list_head list; + void (*create)(int minor); + void (*destroy)(int minor); +}; + +int raw3270_register_notifier(struct raw3270_notifier *); +void raw3270_unregister_notifier(struct raw3270_notifier *); void raw3270_pm_unfreeze(struct raw3270_view *); /* diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 5e4b6fc49ae3..48767e6bab9d 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -829,9 +829,8 @@ tty3270_del_views(void) { int i; - for (i = 0; i < tty3270_max_index; i++) { - struct raw3270_view *view = - raw3270_find_view(&tty3270_fn, i + RAW3270_FIRSTMINOR); + for (i = RAW3270_FIRSTMINOR; i <= tty3270_max_index; i++) { + struct raw3270_view *view = raw3270_find_view(&tty3270_fn, i); if (!IS_ERR(view)) raw3270_del_view(view); } @@ -855,8 +854,7 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) int i, rc; /* Check if the tty3270 is already there. */ - view = raw3270_find_view(&tty3270_fn, - tty->index + RAW3270_FIRSTMINOR); + view = raw3270_find_view(&tty3270_fn, tty->index); if (!IS_ERR(view)) { tp = container_of(view, struct tty3270, view); tty->driver_data = tp; @@ -868,8 +866,8 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) tp->inattr = TF_INPUT; return tty_port_install(&tp->port, driver, tty); } - if (tty3270_max_index < tty->index + 1) - tty3270_max_index = tty->index + 1; + if (tty3270_max_index < tty->index) + tty3270_max_index = tty->index; /* Quick exit if there is no device for tty->index. */ if (PTR_ERR(view) == -ENODEV) @@ -880,8 +878,7 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) if (IS_ERR(tp)) return PTR_ERR(tp); - rc = raw3270_add_view(&tp->view, &tty3270_fn, - tty->index + RAW3270_FIRSTMINOR); + rc = raw3270_add_view(&tp->view, &tty3270_fn, tty->index); if (rc) { tty3270_free_view(tp); return rc; @@ -1788,6 +1785,22 @@ static const struct tty_operations tty3270_ops = { .set_termios = tty3270_set_termios }; +void tty3270_create_cb(int minor) +{ + tty_register_device(tty3270_driver, minor, NULL); +} + +void tty3270_destroy_cb(int minor) +{ + tty_unregister_device(tty3270_driver, minor); +} + +struct raw3270_notifier tty3270_notifier = +{ + .create = tty3270_create_cb, + .destroy = tty3270_destroy_cb, +}; + /* * 3270 tty registration code called from tty_init(). * Most kernel services (incl. kmalloc) are available at this poimt. @@ -1797,23 +1810,25 @@ static int __init tty3270_init(void) struct tty_driver *driver; int ret; - driver = alloc_tty_driver(RAW3270_MAXDEVS); - if (!driver) - return -ENOMEM; + driver = tty_alloc_driver(RAW3270_MAXDEVS, + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV | + TTY_DRIVER_RESET_TERMIOS); + if (IS_ERR(driver)) + return PTR_ERR(driver); /* * Initialize the tty_driver structure * Entries in tty3270_driver that are NOT initialized: * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc */ - driver->driver_name = "ttyTUB"; - driver->name = "ttyTUB"; + driver->driver_name = "tty3270"; + driver->name = "3270/tty"; driver->major = IBM_TTY3270_MAJOR; - driver->minor_start = RAW3270_FIRSTMINOR; + driver->minor_start = 0; driver->type = TTY_DRIVER_TYPE_SYSTEM; driver->subtype = SYSTEM_TYPE_TTY; driver->init_termios = tty_std_termios; - driver->flags = TTY_DRIVER_RESET_TERMIOS; tty_set_operations(driver, &tty3270_ops); ret = tty_register_driver(driver); if (ret) { @@ -1821,6 +1836,7 @@ static int __init tty3270_init(void) return ret; } tty3270_driver = driver; + raw3270_register_notifier(&tty3270_notifier); return 0; } @@ -1829,6 +1845,7 @@ tty3270_exit(void) { struct tty_driver *driver; + raw3270_unregister_notifier(&tty3270_notifier); driver = tty3270_driver; tty3270_driver = NULL; tty_unregister_driver(driver); -- cgit v1.2.3 From 4d334fd155b53adfe78393e66850ff4bb0aa8406 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Fri, 4 Jan 2013 14:55:13 +0100 Subject: s390/3270: asynchronous size sensing Convert the synchronous size sense code to an interrupt driven approach. This allows to set the device online even if the terminal is not connected. With the new code views can be registered without a connected terminal, the tty can be opened as soon as the device is online. After the terminal has been connected and the size has been determined the tty is resized to match the device characteristics.. Signed-off-by: Martin Schwidefsky --- drivers/s390/char/raw3270.c | 535 ++++++++++++++++++++------------------------ drivers/s390/char/raw3270.h | 1 + drivers/s390/char/tty3270.c | 109 ++++++--- drivers/tty/tty_io.c | 1 + 4 files changed, 332 insertions(+), 314 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 72f69fda9d01..4c9030a5b9f2 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -37,6 +37,7 @@ struct raw3270 { int minor; short model, rows, cols; + unsigned int state; unsigned long flags; struct list_head req_queue; /* Request queue. */ @@ -47,17 +48,25 @@ struct raw3270 { unsigned char *ascebc; /* ascii -> ebcdic table */ - struct raw3270_request init_request; + struct raw3270_view init_view; + struct raw3270_request init_reset; + struct raw3270_request init_readpart; + struct raw3270_request init_readmod; unsigned char init_data[256]; }; +/* raw3270->state */ +#define RAW3270_STATE_INIT 0 /* Initial state */ +#define RAW3270_STATE_RESET 1 /* Reset command is pending */ +#define RAW3270_STATE_W4ATTN 2 /* Wait for attention interrupt */ +#define RAW3270_STATE_READMOD 3 /* Read partition is pending */ +#define RAW3270_STATE_READY 4 /* Device is usable by views */ + /* raw3270->flags */ #define RAW3270_FLAGS_14BITADDR 0 /* 14-bit buffer addresses */ #define RAW3270_FLAGS_BUSY 1 /* Device busy, leave it alone */ -#define RAW3270_FLAGS_ATTN 2 /* Device sent an ATTN interrupt */ -#define RAW3270_FLAGS_READY 4 /* Device is useable by views */ -#define RAW3270_FLAGS_CONSOLE 8 /* Device is the console. */ -#define RAW3270_FLAGS_FROZEN 16 /* set if 3270 is frozen for suspend */ +#define RAW3270_FLAGS_CONSOLE 2 /* Device is the console. */ +#define RAW3270_FLAGS_FROZEN 3 /* set if 3270 is frozen for suspend */ /* Semaphore to protect global data of raw3270 (devices, views, etc). */ static DEFINE_MUTEX(raw3270_mutex); @@ -95,6 +104,17 @@ static unsigned char raw3270_ebcgraf[64] = { 0xf8, 0xf9, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f }; +static inline int raw3270_state_ready(struct raw3270 *rp) +{ + return rp->state == RAW3270_STATE_READY; +} + +static inline int raw3270_state_final(struct raw3270 *rp) +{ + return rp->state == RAW3270_STATE_INIT || + rp->state == RAW3270_STATE_READY; +} + void raw3270_buffer_address(struct raw3270 *rp, char *cp, unsigned short addr) { @@ -212,7 +232,7 @@ raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib) * Stop running ccw. */ static int -raw3270_halt_io_nolock(struct raw3270 *rp, struct raw3270_request *rq) +__raw3270_halt_io(struct raw3270 *rp, struct raw3270_request *rq) { int retries; int rc; @@ -231,18 +251,6 @@ raw3270_halt_io_nolock(struct raw3270 *rp, struct raw3270_request *rq) return rc; } -static int -raw3270_halt_io(struct raw3270 *rp, struct raw3270_request *rq) -{ - unsigned long flags; - int rc; - - spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); - rc = raw3270_halt_io_nolock(rp, rq); - spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); - return rc; -} - /* * Add the request to the request queue, try to start it if the * 3270 device is idle. Return without waiting for end of i/o. @@ -279,8 +287,8 @@ raw3270_start(struct raw3270_view *view, struct raw3270_request *rq) if (!rp || rp->view != view || test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) rc = -EACCES; - else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) - rc = -ENODEV; + else if (!raw3270_state_ready(rp)) + rc = -EBUSY; else rc = __raw3270_start(rp, view, rq); spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags); @@ -297,8 +305,8 @@ raw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq) if (!rp || rp->view != view || test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) rc = -EACCES; - else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) - rc = -ENODEV; + else if (!raw3270_state_ready(rp)) + rc = -EBUSY; else rc = __raw3270_start(rp, view, rq); return rc; @@ -376,7 +384,7 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) case RAW3270_IO_STOP: if (!rq) break; - raw3270_halt_io_nolock(rp, rq); + __raw3270_halt_io(rp, rq); rq->rc = -EIO; break; default: @@ -411,9 +419,14 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) } /* - * Size sensing. + * To determine the size of the 3270 device we need to do: + * 1) send a 'read partition' data stream to the device + * 2) wait for the attn interrupt that precedes the query reply + * 3) do a read modified to get the query reply + * To make things worse we have to cope with intervention + * required (3270 device switched to 'stand-by') and command + * rejects (old devices that can't do 'read partition'). */ - struct raw3270_ua { /* Query Reply structure for Usable Area */ struct { /* Usable Area Query Reply Base */ short l; /* Length of this structured field */ @@ -449,117 +462,21 @@ struct raw3270_ua { /* Query Reply structure for Usable Area */ } __attribute__ ((packed)) aua; } __attribute__ ((packed)); -static struct diag210 raw3270_init_diag210; -static DEFINE_MUTEX(raw3270_init_mutex); - -static int -raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, - struct irb *irb) -{ - /* - * Unit-Check Processing: - * Expect Command Reject or Intervention Required. - */ - if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { - /* Request finished abnormally. */ - if (irb->ecw[0] & SNS0_INTERVENTION_REQ) { - set_bit(RAW3270_FLAGS_BUSY, &view->dev->flags); - return RAW3270_IO_BUSY; - } - } - if (rq) { - if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { - if (irb->ecw[0] & SNS0_CMD_REJECT) - rq->rc = -EOPNOTSUPP; - else - rq->rc = -EIO; - } else - /* Request finished normally. Copy residual count. */ - rq->rescnt = irb->scsw.cmd.count; - } - if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { - set_bit(RAW3270_FLAGS_ATTN, &view->dev->flags); - wake_up(&raw3270_wait_queue); - } - return RAW3270_IO_DONE; -} - -static struct raw3270_fn raw3270_init_fn = { - .intv = raw3270_init_irq -}; - -static struct raw3270_view raw3270_init_view = { - .fn = &raw3270_init_fn -}; - -/* - * raw3270_wait/raw3270_wait_interruptible/__raw3270_wakeup - * Wait for end of request. The request must have been started - * with raw3270_start, rc = 0. The device lock may NOT have been - * released between calling raw3270_start and raw3270_wait. - */ static void -raw3270_wake_init(struct raw3270_request *rq, void *data) -{ - wake_up((wait_queue_head_t *) data); -} - -/* - * Special wait function that can cope with console initialization. - */ -static int -raw3270_start_init(struct raw3270 *rp, struct raw3270_view *view, - struct raw3270_request *rq) -{ - unsigned long flags; - int rc; - -#ifdef CONFIG_TN3270_CONSOLE - if (raw3270_registered == 0) { - spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags); - rq->callback = NULL; - rc = __raw3270_start(rp, view, rq); - if (rc == 0) - while (!raw3270_request_final(rq)) { - wait_cons_dev(); - barrier(); - } - spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags); - return rq->rc; - } -#endif - rq->callback = raw3270_wake_init; - rq->callback_data = &raw3270_wait_queue; - spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags); - rc = __raw3270_start(rp, view, rq); - spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags); - if (rc) - return rc; - /* Now wait for the completion. */ - rc = wait_event_interruptible(raw3270_wait_queue, - raw3270_request_final(rq)); - if (rc == -ERESTARTSYS) { /* Interrupted by a signal. */ - raw3270_halt_io(view->dev, rq); - /* No wait for the halt to complete. */ - wait_event(raw3270_wait_queue, raw3270_request_final(rq)); - return -ERESTARTSYS; - } - return rq->rc; -} - -static int -__raw3270_size_device_vm(struct raw3270 *rp) +raw3270_size_device_vm(struct raw3270 *rp) { int rc, model; struct ccw_dev_id dev_id; + struct diag210 diag_data; ccw_device_get_id(rp->cdev, &dev_id); - raw3270_init_diag210.vrdcdvno = dev_id.devno; - raw3270_init_diag210.vrdclen = sizeof(struct diag210); - rc = diag210(&raw3270_init_diag210); - if (rc) - return rc; - model = raw3270_init_diag210.vrdccrmd; + diag_data.vrdcdvno = dev_id.devno; + diag_data.vrdclen = sizeof(struct diag210); + rc = diag210(&diag_data); + model = diag_data.vrdccrmd; + /* Use default model 2 if the size could not be detected */ + if (rc || model < 2 || model > 5) + model = 2; switch (model) { case 2: rp->model = model; @@ -581,77 +498,25 @@ __raw3270_size_device_vm(struct raw3270 *rp) rp->rows = 27; rp->cols = 132; break; - default: - rc = -EOPNOTSUPP; - break; } - return rc; } -static int -__raw3270_size_device(struct raw3270 *rp) +static void +raw3270_size_device(struct raw3270 *rp) { - static const unsigned char wbuf[] = - { 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 }; struct raw3270_ua *uap; - int rc; - /* - * To determine the size of the 3270 device we need to do: - * 1) send a 'read partition' data stream to the device - * 2) wait for the attn interrupt that precedes the query reply - * 3) do a read modified to get the query reply - * To make things worse we have to cope with intervention - * required (3270 device switched to 'stand-by') and command - * rejects (old devices that can't do 'read partition'). - */ - memset(&rp->init_request, 0, sizeof(rp->init_request)); - memset(&rp->init_data, 0, 256); - /* Store 'read partition' data stream to init_data */ - memcpy(&rp->init_data, wbuf, sizeof(wbuf)); - INIT_LIST_HEAD(&rp->init_request.list); - rp->init_request.ccw.cmd_code = TC_WRITESF; - rp->init_request.ccw.flags = CCW_FLAG_SLI; - rp->init_request.ccw.count = sizeof(wbuf); - rp->init_request.ccw.cda = (__u32) __pa(&rp->init_data); - - rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request); - if (rc) - /* Check error cases: -ERESTARTSYS, -EIO and -EOPNOTSUPP */ - return rc; - - /* Wait for attention interrupt. */ -#ifdef CONFIG_TN3270_CONSOLE - if (raw3270_registered == 0) { - unsigned long flags; - - spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); - while (!test_and_clear_bit(RAW3270_FLAGS_ATTN, &rp->flags)) - wait_cons_dev(); - spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); - } else -#endif - rc = wait_event_interruptible(raw3270_wait_queue, - test_and_clear_bit(RAW3270_FLAGS_ATTN, &rp->flags)); - if (rc) - return rc; - - /* - * The device accepted the 'read partition' command. Now - * set up a read ccw and issue it. - */ - rp->init_request.ccw.cmd_code = TC_READMOD; - rp->init_request.ccw.flags = CCW_FLAG_SLI; - rp->init_request.ccw.count = sizeof(rp->init_data); - rp->init_request.ccw.cda = (__u32) __pa(rp->init_data); - rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request); - if (rc) - return rc; /* Got a Query Reply */ uap = (struct raw3270_ua *) (rp->init_data + 1); /* Paranoia check. */ - if (rp->init_data[0] != 0x88 || uap->uab.qcode != 0x81) - return -EOPNOTSUPP; + if (rp->init_readmod.rc || rp->init_data[0] != 0x88 || + uap->uab.qcode != 0x81) { + /* Couldn't detect size. Use default model 2. */ + rp->model = 2; + rp->rows = 24; + rp->cols = 80; + return; + } /* Copy rows/columns of default Usable Area */ rp->rows = uap->uab.h; rp->cols = uap->uab.w; @@ -664,66 +529,131 @@ __raw3270_size_device(struct raw3270 *rp) rp->rows = uap->aua.hauai; rp->cols = uap->aua.wauai; } - return 0; + /* Try to find a model. */ + rp->model = 0; + if (rp->rows == 24 && rp->cols == 80) + rp->model = 2; + if (rp->rows == 32 && rp->cols == 80) + rp->model = 3; + if (rp->rows == 43 && rp->cols == 80) + rp->model = 4; + if (rp->rows == 27 && rp->cols == 132) + rp->model = 5; } -static int -raw3270_size_device(struct raw3270 *rp) +static void +raw3270_size_device_done(struct raw3270 *rp) { - int rc; + struct raw3270_view *view; - mutex_lock(&raw3270_init_mutex); - rp->view = &raw3270_init_view; - raw3270_init_view.dev = rp; - if (MACHINE_IS_VM) - rc = __raw3270_size_device_vm(rp); - else - rc = __raw3270_size_device(rp); - raw3270_init_view.dev = NULL; rp->view = NULL; - mutex_unlock(&raw3270_init_mutex); - if (rc == 0) { /* Found something. */ - /* Try to find a model. */ - rp->model = 0; - if (rp->rows == 24 && rp->cols == 80) - rp->model = 2; - if (rp->rows == 32 && rp->cols == 80) - rp->model = 3; - if (rp->rows == 43 && rp->cols == 80) - rp->model = 4; - if (rp->rows == 27 && rp->cols == 132) - rp->model = 5; - } else { - /* Couldn't detect size. Use default model 2. */ - rp->model = 2; - rp->rows = 24; - rp->cols = 80; - return 0; + rp->state = RAW3270_STATE_READY; + /* Notify views about new size */ + list_for_each_entry(view, &rp->view_list, list) + if (view->fn->resize) + view->fn->resize(view, rp->model, rp->rows, rp->cols); + /* Setup processing done, now activate a view */ + list_for_each_entry(view, &rp->view_list, list) { + rp->view = view; + if (view->fn->activate(view) == 0) + break; + rp->view = NULL; } - return rc; +} + +static void +raw3270_read_modified_cb(struct raw3270_request *rq, void *data) +{ + struct raw3270 *rp = rq->view->dev; + + raw3270_size_device(rp); + raw3270_size_device_done(rp); +} + +static void +raw3270_read_modified(struct raw3270 *rp) +{ + if (rp->state != RAW3270_STATE_W4ATTN) + return; + /* Use 'read modified' to get the result of a read partition. */ + memset(&rp->init_readmod, 0, sizeof(rp->init_readmod)); + memset(&rp->init_data, 0, sizeof(rp->init_data)); + rp->init_readmod.ccw.cmd_code = TC_READMOD; + rp->init_readmod.ccw.flags = CCW_FLAG_SLI; + rp->init_readmod.ccw.count = sizeof(rp->init_data); + rp->init_readmod.ccw.cda = (__u32) __pa(rp->init_data); + rp->init_readmod.callback = raw3270_read_modified_cb; + rp->state = RAW3270_STATE_READMOD; + raw3270_start_irq(&rp->init_view, &rp->init_readmod); +} + +static void +raw3270_writesf_readpart(struct raw3270 *rp) +{ + static const unsigned char wbuf[] = + { 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 }; + + /* Store 'read partition' data stream to init_data */ + memset(&rp->init_readpart, 0, sizeof(rp->init_readpart)); + memset(&rp->init_data, 0, sizeof(rp->init_data)); + memcpy(&rp->init_data, wbuf, sizeof(wbuf)); + rp->init_readpart.ccw.cmd_code = TC_WRITESF; + rp->init_readpart.ccw.flags = CCW_FLAG_SLI; + rp->init_readpart.ccw.count = sizeof(wbuf); + rp->init_readpart.ccw.cda = (__u32) __pa(&rp->init_data); + rp->state = RAW3270_STATE_W4ATTN; + raw3270_start_irq(&rp->init_view, &rp->init_readpart); +} + +/* + * Device reset + */ +static void +raw3270_reset_device_cb(struct raw3270_request *rq, void *data) +{ + struct raw3270 *rp = rq->view->dev; + + if (rp->state != RAW3270_STATE_RESET) + return; + if (rq && rq->rc) { + /* Reset command failed. */ + rp->state = RAW3270_STATE_INIT; + } else if (0 && MACHINE_IS_VM) { + raw3270_size_device_vm(rp); + raw3270_size_device_done(rp); + } else + raw3270_writesf_readpart(rp); } static int -raw3270_reset_device(struct raw3270 *rp) +__raw3270_reset_device(struct raw3270 *rp) { int rc; - mutex_lock(&raw3270_init_mutex); - memset(&rp->init_request, 0, sizeof(rp->init_request)); + /* Store reset data stream to init_data/init_reset */ + memset(&rp->init_reset, 0, sizeof(rp->init_reset)); memset(&rp->init_data, 0, sizeof(rp->init_data)); - /* Store reset data stream to init_data/init_request */ rp->init_data[0] = TW_KR; - INIT_LIST_HEAD(&rp->init_request.list); - rp->init_request.ccw.cmd_code = TC_EWRITEA; - rp->init_request.ccw.flags = CCW_FLAG_SLI; - rp->init_request.ccw.count = 1; - rp->init_request.ccw.cda = (__u32) __pa(rp->init_data); - rp->view = &raw3270_init_view; - raw3270_init_view.dev = rp; - rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request); - raw3270_init_view.dev = NULL; - rp->view = NULL; - mutex_unlock(&raw3270_init_mutex); + rp->init_reset.ccw.cmd_code = TC_EWRITEA; + rp->init_reset.ccw.flags = CCW_FLAG_SLI; + rp->init_reset.ccw.count = 1; + rp->init_reset.ccw.cda = (__u32) __pa(rp->init_data); + rp->init_reset.callback = raw3270_reset_device_cb; + rc = __raw3270_start(rp, &rp->init_view, &rp->init_reset); + if (rc == 0 && rp->state == RAW3270_STATE_INIT) + rp->state = RAW3270_STATE_RESET; + return rc; +} + +static int +raw3270_reset_device(struct raw3270 *rp) +{ + unsigned long flags; + int rc; + + spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); + rc = __raw3270_reset_device(rp); + spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); return rc; } @@ -737,13 +667,50 @@ raw3270_reset(struct raw3270_view *view) if (!rp || rp->view != view || test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) rc = -EACCES; - else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) - rc = -ENODEV; + else if (!raw3270_state_ready(rp)) + rc = -EBUSY; else rc = raw3270_reset_device(view->dev); return rc; } +static int +raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, + struct irb *irb) +{ + struct raw3270 *rp; + + /* + * Unit-Check Processing: + * Expect Command Reject or Intervention Required. + */ + if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { + /* Request finished abnormally. */ + if (irb->ecw[0] & SNS0_INTERVENTION_REQ) { + set_bit(RAW3270_FLAGS_BUSY, &view->dev->flags); + return RAW3270_IO_BUSY; + } + } + if (rq) { + if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { + if (irb->ecw[0] & SNS0_CMD_REJECT) + rq->rc = -EOPNOTSUPP; + else + rq->rc = -EIO; + } + } + if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { + /* Queue read modified after attention interrupt */ + rp = view->dev; + raw3270_read_modified(rp); + } + return RAW3270_IO_DONE; +} + +static struct raw3270_fn raw3270_init_fn = { + .intv = raw3270_init_irq +}; + /* * Setup new 3270 device. */ @@ -772,6 +739,10 @@ raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc) INIT_LIST_HEAD(&rp->req_queue); INIT_LIST_HEAD(&rp->view_list); + rp->init_view.dev = rp; + rp->init_view.fn = &raw3270_init_fn; + rp->view = &rp->init_view; + /* * Add device to list and find the smallest unused minor * number for it. Note: there is no device with minor 0, @@ -810,6 +781,7 @@ raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc) */ struct raw3270 __init *raw3270_setup_console(struct ccw_device *cdev) { + unsigned long flags; struct raw3270 *rp; char *ascebc; int rc; @@ -820,16 +792,15 @@ struct raw3270 __init *raw3270_setup_console(struct ccw_device *cdev) if (rc) return ERR_PTR(rc); set_bit(RAW3270_FLAGS_CONSOLE, &rp->flags); - rc = raw3270_reset_device(rp); - if (rc) - return ERR_PTR(rc); - rc = raw3270_size_device(rp); - if (rc) - return ERR_PTR(rc); - rc = raw3270_reset_device(rp); - if (rc) - return ERR_PTR(rc); - set_bit(RAW3270_FLAGS_READY, &rp->flags); + spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); + do { + __raw3270_reset_device(rp); + while (!raw3270_state_final(rp)) { + wait_cons_dev(); + barrier(); + } + } while (rp->state != RAW3270_STATE_READY); + spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); return rp; } @@ -891,13 +862,13 @@ raw3270_activate_view(struct raw3270_view *view) spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); if (rp->view == view) rc = 0; - else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) - rc = -ENODEV; + else if (!raw3270_state_ready(rp)) + rc = -EBUSY; else if (test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) rc = -EACCES; else { oldview = NULL; - if (rp->view) { + if (rp->view && rp->view->fn->deactivate) { oldview = rp->view; oldview->fn->deactivate(oldview); } @@ -942,7 +913,7 @@ raw3270_deactivate_view(struct raw3270_view *view) list_del_init(&view->list); list_add_tail(&view->list, &rp->view_list); /* Try to activate another view. */ - if (test_bit(RAW3270_FLAGS_READY, &rp->flags) && + if (raw3270_state_ready(rp) && !test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) { list_for_each_entry(view, &rp->view_list, list) { rp->view = view; @@ -973,18 +944,16 @@ raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, int minor) if (rp->minor != minor) continue; spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); - if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) { - atomic_set(&view->ref_count, 2); - view->dev = rp; - view->fn = fn; - view->model = rp->model; - view->rows = rp->rows; - view->cols = rp->cols; - view->ascebc = rp->ascebc; - spin_lock_init(&view->lock); - list_add(&view->list, &rp->view_list); - rc = 0; - } + atomic_set(&view->ref_count, 2); + view->dev = rp; + view->fn = fn; + view->model = rp->model; + view->rows = rp->rows; + view->cols = rp->cols; + view->ascebc = rp->ascebc; + spin_lock_init(&view->lock); + list_add(&view->list, &rp->view_list); + rc = 0; spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); break; } @@ -1008,14 +977,11 @@ raw3270_find_view(struct raw3270_fn *fn, int minor) if (rp->minor != minor) continue; spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); - if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) { - view = ERR_PTR(-ENOENT); - list_for_each_entry(tmp, &rp->view_list, list) { - if (tmp->fn == fn) { - raw3270_get_view(tmp); - view = tmp; - break; - } + list_for_each_entry(tmp, &rp->view_list, list) { + if (tmp->fn == fn) { + raw3270_get_view(tmp); + view = tmp; + break; } } spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); @@ -1042,7 +1008,7 @@ raw3270_del_view(struct raw3270_view *view) rp->view = NULL; } list_del_init(&view->list); - if (!rp->view && test_bit(RAW3270_FLAGS_READY, &rp->flags) && + if (!rp->view && raw3270_state_ready(rp) && !test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) { /* Try to activate another view. */ list_for_each_entry(nv, &rp->view_list, list) { @@ -1177,19 +1143,10 @@ raw3270_set_online (struct ccw_device *cdev) rp = raw3270_create_device(cdev); if (IS_ERR(rp)) return PTR_ERR(rp); - rc = raw3270_reset_device(rp); - if (rc) - goto failure; - rc = raw3270_size_device(rp); - if (rc) - goto failure; - rc = raw3270_reset_device(rp); - if (rc) - goto failure; rc = raw3270_create_attributes(rp); if (rc) goto failure; - set_bit(RAW3270_FLAGS_READY, &rp->flags); + raw3270_reset_device(rp); mutex_lock(&raw3270_mutex); list_for_each_entry(np, &raw3270_notifier, list) np->create(rp->minor); @@ -1221,14 +1178,14 @@ raw3270_remove (struct ccw_device *cdev) */ if (rp == NULL) return; - clear_bit(RAW3270_FLAGS_READY, &rp->flags); sysfs_remove_group(&cdev->dev.kobj, &raw3270_attr_group); /* Deactivate current view and remove all views. */ spin_lock_irqsave(get_ccwdev_lock(cdev), flags); if (rp->view) { - rp->view->fn->deactivate(rp->view); + if (rp->view->fn->deactivate) + rp->view->fn->deactivate(rp->view); rp->view = NULL; } while (!list_empty(&rp->view_list)) { @@ -1277,7 +1234,7 @@ static int raw3270_pm_stop(struct ccw_device *cdev) if (!rp) return 0; spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); - if (rp->view) + if (rp->view && rp->view->fn->deactivate) rp->view->fn->deactivate(rp->view); if (!test_bit(RAW3270_FLAGS_CONSOLE, &rp->flags)) { /* @@ -1304,7 +1261,7 @@ static int raw3270_pm_start(struct ccw_device *cdev) return 0; spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); clear_bit(RAW3270_FLAGS_FROZEN, &rp->flags); - if (rp->view) + if (rp->view && rp->view->fn->activate) rp->view->fn->activate(rp->view); spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); return 0; diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index a4c79d043cd3..7b73ff8c1bd7 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -141,6 +141,7 @@ struct raw3270_fn { struct raw3270_request *, struct irb *); void (*release)(struct raw3270_view *); void (*free)(struct raw3270_view *); + void (*resize)(struct raw3270_view *, int, int, int); }; /* diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 48767e6bab9d..8f1e02543ada 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -80,6 +81,8 @@ struct tty3270 { unsigned int highlight; /* Blink/reverse/underscore */ unsigned int f_color; /* Foreground color */ struct tty3270_line *screen; + unsigned int n_model, n_cols, n_rows; /* New model & size */ + struct work_struct resize_work; /* Input stuff. */ struct string *prompt; /* Output string for input area. */ @@ -115,6 +118,7 @@ struct tty3270 { #define TTY_UPDATE_ALL 16 /* Recreate screen. */ static void tty3270_update(struct tty3270 *); +static void tty3270_resize_work(struct work_struct *work); /* * Setup timeout for a device. On timeout trigger an update. @@ -711,6 +715,7 @@ tty3270_alloc_view(void) tasklet_init(&tp->readlet, (void (*)(unsigned long)) tty3270_read_tasklet, (unsigned long) tp->read); + INIT_WORK(&tp->resize_work, tty3270_resize_work); return tp; @@ -754,42 +759,96 @@ tty3270_free_view(struct tty3270 *tp) /* * Allocate tty3270 screen. */ -static int -tty3270_alloc_screen(struct tty3270 *tp) +static struct tty3270_line * +tty3270_alloc_screen(unsigned int rows, unsigned int cols) { + struct tty3270_line *screen; unsigned long size; int lines; - size = sizeof(struct tty3270_line) * (tp->view.rows - 2); - tp->screen = kzalloc(size, GFP_KERNEL); - if (!tp->screen) + size = sizeof(struct tty3270_line) * (rows - 2); + screen = kzalloc(size, GFP_KERNEL); + if (!screen) goto out_err; - for (lines = 0; lines < tp->view.rows - 2; lines++) { - size = sizeof(struct tty3270_cell) * tp->view.cols; - tp->screen[lines].cells = kzalloc(size, GFP_KERNEL); - if (!tp->screen[lines].cells) + for (lines = 0; lines < rows - 2; lines++) { + size = sizeof(struct tty3270_cell) * cols; + screen[lines].cells = kzalloc(size, GFP_KERNEL); + if (!screen[lines].cells) goto out_screen; } - return 0; + return screen; out_screen: while (lines--) - kfree(tp->screen[lines].cells); - kfree(tp->screen); + kfree(screen[lines].cells); + kfree(screen); out_err: - return -ENOMEM; + return ERR_PTR(-ENOMEM); } /* * Free tty3270 screen. */ static void -tty3270_free_screen(struct tty3270 *tp) +tty3270_free_screen(struct tty3270_line *screen, unsigned int rows) { int lines; - for (lines = 0; lines < tp->view.rows - 2; lines++) - kfree(tp->screen[lines].cells); - kfree(tp->screen); + for (lines = 0; lines < rows - 2; lines++) + kfree(screen[lines].cells); + kfree(screen); +} + +/* + * Resize tty3270 screen + */ +static void tty3270_resize_work(struct work_struct *work) +{ + struct tty3270 *tp = container_of(work, struct tty3270, resize_work); + struct tty3270_line *screen, *oscreen; + struct tty_struct *tty; + unsigned int orows; + struct winsize ws; + + screen = tty3270_alloc_screen(tp->n_rows, tp->n_cols); + if (!screen) + return; + /* Switch to new output size */ + spin_lock_bh(&tp->view.lock); + oscreen = tp->screen; + orows = tp->view.rows; + tp->view.model = tp->n_model; + tp->view.rows = tp->n_rows; + tp->view.cols = tp->n_cols; + tp->screen = screen; + free_string(&tp->freemem, tp->prompt); + free_string(&tp->freemem, tp->status); + tty3270_create_prompt(tp); + tty3270_create_status(tp); + tp->nr_up = 0; + while (tp->nr_lines < tp->view.rows - 2) + tty3270_blank_line(tp); + tp->update_flags = TTY_UPDATE_ALL; + spin_unlock_bh(&tp->view.lock); + tty3270_free_screen(oscreen, orows); + tty3270_set_timer(tp, 1); + /* Informat tty layer about new size */ + tty = tty_port_tty_get(&tp->port); + if (!tty) + return; + ws.ws_row = tp->view.rows - 2; + ws.ws_col = tp->view.cols; + tty_do_resize(tty, &ws); +} + +static void +tty3270_resize(struct raw3270_view *view, int model, int rows, int cols) +{ + struct tty3270 *tp = container_of(view, struct tty3270, view); + + tp->n_model = model; + tp->n_rows = rows; + tp->n_cols = cols; + schedule_work(&tp->resize_work); } /* @@ -817,7 +876,8 @@ static void tty3270_free(struct raw3270_view *view) { struct tty3270 *tp = container_of(view, struct tty3270, view); - tty3270_free_screen(tp); + + tty3270_free_screen(tp->screen, tp->view.rows); tty3270_free_view(tp); } @@ -841,7 +901,8 @@ static struct raw3270_fn tty3270_fn = { .deactivate = tty3270_deactivate, .intv = (void *) tty3270_irq, .release = tty3270_release, - .free = tty3270_free + .free = tty3270_free, + .resize = tty3270_resize }; /* @@ -869,10 +930,6 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) if (tty3270_max_index < tty->index) tty3270_max_index = tty->index; - /* Quick exit if there is no device for tty->index. */ - if (PTR_ERR(view) == -ENODEV) - return -ENODEV; - /* Allocate tty3270 structure on first open. */ tp = tty3270_alloc_view(); if (IS_ERR(tp)) @@ -884,10 +941,12 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) return rc; } - rc = tty3270_alloc_screen(tp); - if (rc) { + tp->screen = tty3270_alloc_screen(tp->view.cols, tp->view.rows); + if (IS_ERR(tp->screen)) { + rc = PTR_ERR(tp->screen); raw3270_put_view(&tp->view); raw3270_del_view(&tp->view); + tty3270_free_view(tp); return rc; } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index da9fde850754..3f00f9e8daf2 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2203,6 +2203,7 @@ done: mutex_unlock(&tty->termios_mutex); return 0; } +EXPORT_SYMBOL(tty_do_resize); /** * tiocswinsz - implement window size set ioctl -- cgit v1.2.3 From 9a17e972529e07d6e2531e6b6712bf29687df8a6 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 15 Jan 2013 19:04:39 +0100 Subject: s390/chsc: cleanup SEI helper functions Cleanup the functions used to call SEI. Also provide !CONFIG_PCI dummys for pci error handling. Reviewed-by: Peter Oberparleiter Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/pci.h | 5 ++++ drivers/s390/cio/chsc.c | 68 +++++++++++++++++++++------------------------ 2 files changed, 37 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h index b1fa93c606ad..23d6a245e8a7 100644 --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h @@ -160,9 +160,14 @@ void zpci_teardown_msi_irq(struct zpci_dev *, struct msi_desc *); int zpci_msihash_init(void); void zpci_msihash_exit(void); +#ifdef CONFIG_PCI /* Error handling and recovery */ void zpci_event_error(void *); void zpci_event_availability(void *); +#else /* CONFIG_PCI */ +static inline void zpci_event_error(void *e) {} +static inline void zpci_event_availability(void *e) {} +#endif /* CONFIG_PCI */ /* Helpers */ struct zpci_dev *get_zdev(struct pci_dev *); diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 10729bbceced..31ceef1beb8b 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -435,7 +435,6 @@ static void chsc_process_sei_scm_change(struct chsc_sei_nt0_area *sei_area) static void chsc_process_sei_nt2(struct chsc_sei_nt2_area *sei_area) { -#ifdef CONFIG_PCI switch (sei_area->cc) { case 1: zpci_event_error(sei_area->ccdf); @@ -444,11 +443,10 @@ static void chsc_process_sei_nt2(struct chsc_sei_nt2_area *sei_area) zpci_event_availability(sei_area->ccdf); break; default: - CIO_CRW_EVENT(2, "chsc: unhandled sei content code %d\n", + CIO_CRW_EVENT(2, "chsc: sei nt2 unhandled cc=%d\n", sei_area->cc); break; } -#endif } static void chsc_process_sei_nt0(struct chsc_sei_nt0_area *sei_area) @@ -471,13 +469,19 @@ static void chsc_process_sei_nt0(struct chsc_sei_nt0_area *sei_area) chsc_process_sei_scm_change(sei_area); break; default: /* other stuff */ - CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n", + CIO_CRW_EVENT(2, "chsc: sei nt0 unhandled cc=%d\n", sei_area->cc); break; } + + /* Check if we might have lost some information. */ + if (sei_area->flags & 0x40) { + CIO_CRW_EVENT(2, "chsc: event overflow\n"); + css_schedule_eval_all(); + } } -static int __chsc_process_crw(struct chsc_sei *sei, u64 ntsm) +static void chsc_process_event_information(struct chsc_sei *sei, u64 ntsm) { do { memset(sei, 0, sizeof(*sei)); @@ -488,40 +492,37 @@ static int __chsc_process_crw(struct chsc_sei *sei, u64 ntsm) if (chsc(sei)) break; - if (sei->response.code == 0x0001) { - CIO_CRW_EVENT(2, "chsc: sei successful\n"); - - /* Check if we might have lost some information. */ - if (sei->u.nt0_area.flags & 0x40) { - CIO_CRW_EVENT(2, "chsc: event overflow\n"); - css_schedule_eval_all(); - } - - switch (sei->nt) { - case 0: - chsc_process_sei_nt0(&sei->u.nt0_area); - break; - case 2: - chsc_process_sei_nt2(&sei->u.nt2_area); - break; - default: - CIO_CRW_EVENT(2, "chsc: unhandled nt=%d\n", - sei->nt); - break; - } - } else { + if (sei->response.code != 0x0001) { CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n", sei->response.code); break; } - } while (sei->u.nt0_area.flags & 0x80); - return 0; + CIO_CRW_EVENT(2, "chsc: sei successful (nt=%d)\n", sei->nt); + switch (sei->nt) { + case 0: + chsc_process_sei_nt0(&sei->u.nt0_area); + break; + case 2: + chsc_process_sei_nt2(&sei->u.nt2_area); + break; + default: + CIO_CRW_EVENT(2, "chsc: unhandled nt: %d\n", sei->nt); + break; + } + } while (sei->u.nt0_area.flags & 0x80); } +/* + * Handle channel subsystem related CRWs. + * Use store event information to find out what's going on. + * + * Note: Access to sei_page is serialized through machine check handler + * thread, so no need for locking. + */ static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow) { - struct chsc_sei *sei; + struct chsc_sei *sei = sei_page; if (overflow) { css_schedule_eval_all(); @@ -531,14 +532,9 @@ static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow) "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n", crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc, crw0->erc, crw0->rsid); - if (!sei_page) - return; - /* Access to sei_page is serialized through machine check handler - * thread, so no need for locking. */ - sei = sei_page; CIO_TRACE_EVENT(2, "prcss"); - __chsc_process_crw(sei, CHSC_SEI_NT0 | CHSC_SEI_NT2); + chsc_process_event_information(sei, CHSC_SEI_NT0 | CHSC_SEI_NT2); } void chsc_chp_online(struct chp_id chpid) -- cgit v1.2.3 From b4b3d128c821d70112ac0096d5c1440f5ed9f718 Mon Sep 17 00:00:00 2001 From: Michael Holzheu Date: Mon, 21 Jan 2013 18:37:41 +0100 Subject: s390/zcore: Add hsa file Under LPAR the zfcpdump HSA is a shared resource. Up to now the HSA memory is released when the zcore file is closed. Dump programs that know that they do not need the HSA memory any more (e.g. because they already dumped it) could release it earlier. This would allow other LPARs to use it again. To achieve this a new debugfs file "hsa" is added that can be used to read the HSA size and to release the HSA by writing "0" into the file. Signed-off-by: Michael Holzheu Signed-off-by: Martin Schwidefsky --- drivers/s390/char/zcore.c | 62 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index e3b9308b0fe3..681749e7f6dd 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -62,6 +62,7 @@ static struct dentry *zcore_dir; static struct dentry *zcore_file; static struct dentry *zcore_memmap_file; static struct dentry *zcore_reipl_file; +static struct dentry *zcore_hsa_file; static struct ipl_parameter_block *ipl_block; /* @@ -77,6 +78,8 @@ static int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode) int offs, blk_num; static char buf[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); + if (!hsa_available) + return -ENODATA; if (count == 0) return 0; @@ -277,6 +280,15 @@ next: return 0; } +/* + * Release the HSA + */ +static void release_hsa(void) +{ + diag308(DIAG308_REL_HSA, NULL); + hsa_available = 0; +} + /* * Read routine for zcore character device * First 4K are dump header @@ -363,8 +375,8 @@ static int zcore_open(struct inode *inode, struct file *filp) static int zcore_release(struct inode *inode, struct file *filep) { - diag308(DIAG308_REL_HSA, NULL); - hsa_available = 0; + if (hsa_available) + release_hsa(); return 0; } @@ -474,6 +486,41 @@ static const struct file_operations zcore_reipl_fops = { .llseek = no_llseek, }; +static ssize_t zcore_hsa_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + static char str[18]; + + if (hsa_available) + snprintf(str, sizeof(str), "%lx\n", ZFCPDUMP_HSA_SIZE); + else + snprintf(str, sizeof(str), "0\n"); + return simple_read_from_buffer(buf, count, ppos, str, strlen(str)); +} + +static ssize_t zcore_hsa_write(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) +{ + char value; + + if (*ppos != 0) + return -EPIPE; + if (copy_from_user(&value, buf, 1)) + return -EFAULT; + if (value != '0') + return -EINVAL; + release_hsa(); + return count; +} + +static const struct file_operations zcore_hsa_fops = { + .owner = THIS_MODULE, + .write = zcore_hsa_write, + .read = zcore_hsa_read, + .open = nonseekable_open, + .llseek = no_llseek, +}; + #ifdef CONFIG_32BIT static void __init set_lc_mask(struct save_area *map) @@ -658,6 +705,7 @@ static int __init zcore_init(void) rc = check_sdias(); if (rc) goto fail; + hsa_available = 1; rc = memcpy_hsa_kernel(&arch, __LC_AR_MODE_ID, 1); if (rc) @@ -714,9 +762,16 @@ static int __init zcore_init(void) rc = -ENOMEM; goto fail_memmap_file; } - hsa_available = 1; + zcore_hsa_file = debugfs_create_file("hsa", S_IRUSR|S_IWUSR, zcore_dir, + NULL, &zcore_hsa_fops); + if (!zcore_hsa_file) { + rc = -ENOMEM; + goto fail_reipl_file; + } return 0; +fail_reipl_file: + debugfs_remove(zcore_reipl_file); fail_memmap_file: debugfs_remove(zcore_memmap_file); fail_file: @@ -733,6 +788,7 @@ static void __exit zcore_exit(void) debug_unregister(zcore_dbf); sclp_sdias_exit(); free_page((unsigned long) ipl_block); + debugfs_remove(zcore_hsa_file); debugfs_remove(zcore_reipl_file); debugfs_remove(zcore_memmap_file); debugfs_remove(zcore_file); -- cgit v1.2.3 From 69f5576f6c8c9d0f0b3670ee7c807a194b4c40f4 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 28 Jan 2013 19:29:43 +0100 Subject: s390/cio: dont abort verification after missing irq Do not abort path verification when waiting for an interrupt timed out. Use path_noirq_mask to keep track of the paths used for this (also maintain a path_notoper_mask for debugging purposes). If the timeout happend to be during an operation where we query or alter the state of path groups set the pgid_unknown flag. With this change we allow usage of devices which have such ill-behaved paths (if at least one path is operational). Reviewed-by: Peter Oberparleiter Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_pgid.c | 48 +++++++++++++++++++++++++++++++++--------- drivers/s390/cio/io_sch.h | 5 +++++ 2 files changed, 43 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 908d287f66c1..6f2987d8da99 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -102,10 +102,20 @@ static void nop_callback(struct ccw_device *cdev, void *data, int rc) struct subchannel *sch = to_subchannel(cdev->dev.parent); struct ccw_request *req = &cdev->private->req; - if (rc == 0) + switch (rc) { + case 0: sch->vpm |= req->lpm; - else if (rc != -EACCES) + break; + case -ETIME: + cdev->private->path_noirq_mask |= req->lpm; + break; + case -EACCES: + cdev->private->path_notoper_mask |= req->lpm; + break; + default: goto err; + } + /* Continue on the next path. */ req->lpm >>= 1; nop_do(cdev); return; @@ -174,7 +184,12 @@ static void spid_callback(struct ccw_device *cdev, void *data, int rc) case 0: sch->vpm |= req->lpm & sch->opm; break; + case -ETIME: + cdev->private->flags.pgid_unknown = 1; + cdev->private->path_noirq_mask |= req->lpm; + break; case -EACCES: + cdev->private->path_notoper_mask |= req->lpm; break; case -EOPNOTSUPP: if (cdev->private->flags.mpath) { @@ -404,10 +419,21 @@ static void snid_callback(struct ccw_device *cdev, void *data, int rc) { struct ccw_request *req = &cdev->private->req; - if (rc == 0) + switch (rc) { + case 0: cdev->private->pgid_valid_mask |= req->lpm; - else if (rc != -EACCES) + break; + case -ETIME: + cdev->private->flags.pgid_unknown = 1; + cdev->private->path_noirq_mask |= req->lpm; + break; + case -EACCES: + cdev->private->path_notoper_mask |= req->lpm; + break; + default: goto err; + } + /* Continue on the next path. */ req->lpm >>= 1; snid_do(cdev); return; @@ -427,6 +453,13 @@ static void verify_start(struct ccw_device *cdev) sch->vpm = 0; sch->lpm = sch->schib.pmcw.pam; + + /* Initialize PGID data. */ + memset(cdev->private->pgid, 0, sizeof(cdev->private->pgid)); + cdev->private->pgid_valid_mask = 0; + cdev->private->pgid_todo_mask = sch->schib.pmcw.pam; + cdev->private->path_notoper_mask = 0; + /* Initialize request data. */ memset(req, 0, sizeof(*req)); req->timeout = PGID_TIMEOUT; @@ -459,14 +492,8 @@ static void verify_start(struct ccw_device *cdev) */ void ccw_device_verify_start(struct ccw_device *cdev) { - struct subchannel *sch = to_subchannel(cdev->dev.parent); - CIO_TRACE_EVENT(4, "vrfy"); CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); - /* Initialize PGID data. */ - memset(cdev->private->pgid, 0, sizeof(cdev->private->pgid)); - cdev->private->pgid_valid_mask = 0; - cdev->private->pgid_todo_mask = sch->schib.pmcw.pam; /* * Initialize pathgroup and multipath state with target values. * They may change in the course of path verification. @@ -474,6 +501,7 @@ void ccw_device_verify_start(struct ccw_device *cdev) cdev->private->flags.pgroup = cdev->private->options.pgroup; cdev->private->flags.mpath = cdev->private->options.mpath; cdev->private->flags.doverify = 0; + cdev->private->path_noirq_mask = 0; verify_start(cdev); } diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index 76253dfcc1be..b108f4a5c7dd 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -126,6 +126,10 @@ struct ccw_device_private { u8 pgid_valid_mask; /* mask of valid PGIDs */ u8 pgid_todo_mask; /* mask of PGIDs to be adjusted */ u8 pgid_reset_mask; /* mask of PGIDs which were reset */ + u8 path_noirq_mask; /* mask of paths for which no irq was + received */ + u8 path_notoper_mask; /* mask of paths which were found + not operable */ u8 path_gone_mask; /* mask of paths, that became unavailable */ u8 path_new_mask; /* mask of paths, that became available */ struct { @@ -145,6 +149,7 @@ struct ccw_device_private { unsigned int resuming:1; /* recognition while resume */ unsigned int pgroup:1; /* pathgroup is set up */ unsigned int mpath:1; /* multipathing is set up */ + unsigned int pgid_unknown:1;/* unknown pgid state */ unsigned int initialized:1; /* set if initial reference held */ } __attribute__((packed)) flags; unsigned long intparm; /* user interruption parameter */ -- cgit v1.2.3 From e6a0b7c90f9f2663f470bbfaf83afcf52f8459e8 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 28 Jan 2013 19:31:50 +0100 Subject: s390/cio: skip broken paths Omit known to be broken paths (those set in path_noirq_mask) for the sense/set PGID and nop IO commands. Note: path_noirq_mask will be reset in ccw_device_verify_start (the paths could be healthy again). However if we restart a path verification via verify_start this mask will not be reset (there is no need to let the wait for an interrupt time out again - plus we do not want to loop once we deal with the paths in unknown path group state). Reviewed-by: Peter Oberparleiter Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_pgid.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 6f2987d8da99..f12beb72f263 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -70,8 +70,8 @@ static void nop_do(struct ccw_device *cdev) struct subchannel *sch = to_subchannel(cdev->dev.parent); struct ccw_request *req = &cdev->private->req; - /* Adjust lpm. */ - req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & sch->opm); + req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & sch->opm & + ~cdev->private->path_noirq_mask); if (!req->lpm) goto out_nopath; nop_build_cp(cdev); @@ -345,8 +345,9 @@ static void snid_done(struct ccw_device *cdev, int rc) else { donepm = pgid_to_donepm(cdev); sch->vpm = donepm & sch->opm; - cdev->private->pgid_todo_mask &= ~donepm; cdev->private->pgid_reset_mask |= reset; + cdev->private->pgid_todo_mask &= + ~(donepm | cdev->private->path_noirq_mask); pgid_fill(cdev, pgid); } out: @@ -400,8 +401,8 @@ static void snid_do(struct ccw_device *cdev) struct subchannel *sch = to_subchannel(cdev->dev.parent); struct ccw_request *req = &cdev->private->req; - /* Adjust lpm if paths are not set in pam. */ - req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam); + req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & + ~cdev->private->path_noirq_mask); if (!req->lpm) goto out_nopath; snid_build_cp(cdev); -- cgit v1.2.3 From 84c57ad5d938ff28d85ac93b79c1f918a767a23e Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 28 Jan 2013 19:32:27 +0100 Subject: s390/cio: export vpm via sysfs Add new attribute "vpm" to the subchannel sysfs directory of I/O subchannels. This attribute contains a path mask indicating which channel paths were successfully verified to be usable for I/O. Reviewed-by: Peter Oberparleiter Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 7cd5c6812ac7..c6767f5a58b2 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -632,6 +632,14 @@ initiate_logging(struct device *dev, struct device_attribute *attr, return count; } +static ssize_t vpm_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct subchannel *sch = to_subchannel(dev); + + return sprintf(buf, "%02x\n", sch->vpm); +} + static DEVICE_ATTR(chpids, 0444, chpids_show, NULL); static DEVICE_ATTR(pimpampom, 0444, pimpampom_show, NULL); static DEVICE_ATTR(devtype, 0444, devtype_show, NULL); @@ -640,11 +648,13 @@ static DEVICE_ATTR(modalias, 0444, modalias_show, NULL); static DEVICE_ATTR(online, 0644, online_show, online_store); static DEVICE_ATTR(availability, 0444, available_show, NULL); static DEVICE_ATTR(logging, 0200, NULL, initiate_logging); +static DEVICE_ATTR(vpm, 0444, vpm_show, NULL); static struct attribute *io_subchannel_attrs[] = { &dev_attr_chpids.attr, &dev_attr_pimpampom.attr, &dev_attr_logging.attr, + &dev_attr_vpm.attr, NULL, }; -- cgit v1.2.3 From 88e7616e407fd60ad96e71349393397cf72f8b8d Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 28 Jan 2013 19:32:56 +0100 Subject: s390/cio: handle unknown pgroup state When an attempt to query or modify the grouping state of a channel path fails due to a timeout, we cannot be sure about its state. To get back to a defined state, disband the whole path group and try again while excluding the offending path. Reviewed-by: Peter Oberparleiter Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_pgid.c | 64 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index f12beb72f263..37ada05e82a5 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -23,6 +23,8 @@ #define PGID_RETRIES 256 #define PGID_TIMEOUT (10 * HZ) +static void verify_start(struct ccw_device *cdev); + /* * Process path verification data and report result. */ @@ -142,6 +144,48 @@ static void spid_build_cp(struct ccw_device *cdev, u8 fn) req->cp = cp; } +static void pgid_wipeout_callback(struct ccw_device *cdev, void *data, int rc) +{ + if (rc) { + /* We don't know the path groups' state. Abort. */ + verify_done(cdev, rc); + return; + } + /* + * Path groups have been reset. Restart path verification but + * leave paths in path_noirq_mask out. + */ + cdev->private->flags.pgid_unknown = 0; + verify_start(cdev); +} + +/* + * Reset pathgroups and restart path verification, leave unusable paths out. + */ +static void pgid_wipeout_start(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_dev_id *id = &cdev->private->dev_id; + struct ccw_request *req = &cdev->private->req; + u8 fn; + + CIO_MSG_EVENT(2, "wipe: device 0.%x.%04x: pvm=%02x nim=%02x\n", + id->ssid, id->devno, cdev->private->pgid_valid_mask, + cdev->private->path_noirq_mask); + + /* Initialize request data. */ + memset(req, 0, sizeof(*req)); + req->timeout = PGID_TIMEOUT; + req->maxretries = PGID_RETRIES; + req->lpm = sch->schib.pmcw.pam; + req->callback = pgid_wipeout_callback; + fn = SPID_FUNC_DISBAND; + if (cdev->private->flags.mpath) + fn |= SPID_FUNC_MULTI_PATH; + spid_build_cp(cdev, fn); + ccw_request_start(cdev); +} + /* * Perform establish/resign SET PGID on a single path. */ @@ -167,11 +211,14 @@ static void spid_do(struct ccw_device *cdev) return; out_nopath: + if (cdev->private->flags.pgid_unknown) { + /* At least one SPID could be partially done. */ + pgid_wipeout_start(cdev); + return; + } verify_done(cdev, sch->vpm ? 0 : -EACCES); } -static void verify_start(struct ccw_device *cdev); - /* * Process SET PGID request result for a single path. */ @@ -357,6 +404,10 @@ out: cdev->private->pgid_todo_mask, mismatch, reserved, reset); switch (rc) { case 0: + if (cdev->private->flags.pgid_unknown) { + pgid_wipeout_start(cdev); + return; + } /* Anything left to do? */ if (cdev->private->pgid_todo_mask == 0) { verify_done(cdev, sch->vpm == 0 ? -EACCES : 0); @@ -400,6 +451,7 @@ static void snid_do(struct ccw_device *cdev) { struct subchannel *sch = to_subchannel(cdev->dev.parent); struct ccw_request *req = &cdev->private->req; + int ret; req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & ~cdev->private->path_noirq_mask); @@ -410,7 +462,13 @@ static void snid_do(struct ccw_device *cdev) return; out_nopath: - snid_done(cdev, cdev->private->pgid_valid_mask ? 0 : -EACCES); + if (cdev->private->pgid_valid_mask) + ret = 0; + else if (cdev->private->path_noirq_mask) + ret = -ETIME; + else + ret = -EACCES; + snid_done(cdev, ret); } /* -- cgit v1.2.3 From 58fece7827a7cc40e02bc68a7db8229166984893 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 28 Jan 2013 19:34:26 +0100 Subject: s390/scm: use inline dummy functions Convert the defines for the !CONFIG_SCM* stuff to static inline functions. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/block/scm_blk.h | 41 ++++++++++++++++++++++++++++------------- drivers/s390/cio/chsc.h | 2 +- 2 files changed, 29 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/block/scm_blk.h b/drivers/s390/block/scm_blk.h index 7ac6bad919ef..3c1ccf494647 100644 --- a/drivers/s390/block/scm_blk.h +++ b/drivers/s390/block/scm_blk.h @@ -68,19 +68,34 @@ void scm_initiate_cluster_request(struct scm_request *); void scm_cluster_request_irq(struct scm_request *); bool scm_test_cluster_request(struct scm_request *); bool scm_cluster_size_valid(void); -#else -#define __scm_free_rq_cluster(scmrq) {} -#define __scm_alloc_rq_cluster(scmrq) 0 -#define scm_request_cluster_init(scmrq) {} -#define scm_reserve_cluster(scmrq) true -#define scm_release_cluster(scmrq) {} -#define scm_blk_dev_cluster_setup(bdev) {} -#define scm_need_cluster_request(scmrq) false -#define scm_initiate_cluster_request(scmrq) {} -#define scm_cluster_request_irq(scmrq) {} -#define scm_test_cluster_request(scmrq) false -#define scm_cluster_size_valid() true -#endif +#else /* CONFIG_SCM_BLOCK_CLUSTER_WRITE */ +static inline void __scm_free_rq_cluster(struct scm_request *scmrq) {} +static inline int __scm_alloc_rq_cluster(struct scm_request *scmrq) +{ + return 0; +} +static inline void scm_request_cluster_init(struct scm_request *scmrq) {} +static inline bool scm_reserve_cluster(struct scm_request *scmrq) +{ + return true; +} +static inline void scm_release_cluster(struct scm_request *scmrq) {} +static inline void scm_blk_dev_cluster_setup(struct scm_blk_dev *bdev) {} +static inline bool scm_need_cluster_request(struct scm_request *scmrq) +{ + return false; +} +static inline void scm_initiate_cluster_request(struct scm_request *scmrq) {} +static inline void scm_cluster_request_irq(struct scm_request *scmrq) {} +static inline bool scm_test_cluster_request(struct scm_request *scmrq) +{ + return false; +} +static inline bool scm_cluster_size_valid(void) +{ + return true; +} +#endif /* CONFIG_SCM_BLOCK_CLUSTER_WRITE */ extern debug_info_t *scm_debug; diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 662dab4b93e6..227e05f674b3 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -157,7 +157,7 @@ int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token); #ifdef CONFIG_SCM_BUS int scm_update_information(void); #else /* CONFIG_SCM_BUS */ -#define scm_update_information() 0 +static inline int scm_update_information(void) { return 0; } #endif /* CONFIG_SCM_BUS */ -- cgit v1.2.3 From 1aae0560d160ee6ebef927a35e4f405306a079df Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 30 Jan 2013 09:49:40 +0100 Subject: s390/time: rename tod clock access functions Fix name clash with some common code device drivers and add "tod" to all tod clock access function names. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/appldata/appldata_mem.c | 2 +- arch/s390/appldata/appldata_net_sum.c | 2 +- arch/s390/appldata/appldata_os.c | 2 +- arch/s390/hypfs/hypfs_vm.c | 2 +- arch/s390/include/asm/timex.h | 18 +++++++++--------- arch/s390/kernel/debug.c | 2 +- arch/s390/kernel/early.c | 6 +++--- arch/s390/kernel/nmi.c | 2 +- arch/s390/kernel/smp.c | 10 +++++----- arch/s390/kernel/time.c | 26 +++++++++++++------------- arch/s390/kernel/vtime.c | 2 +- arch/s390/kvm/interrupt.c | 6 +++--- arch/s390/lib/delay.c | 16 ++++++++-------- drivers/s390/block/dasd.c | 18 +++++++++--------- drivers/s390/block/dasd_3990_erp.c | 8 ++++---- drivers/s390/block/dasd_alias.c | 4 ++-- drivers/s390/block/dasd_diag.c | 10 +++++----- drivers/s390/block/dasd_eckd.c | 30 +++++++++++++++--------------- drivers/s390/block/dasd_eer.c | 2 +- drivers/s390/block/dasd_erp.c | 4 ++-- drivers/s390/block/dasd_fba.c | 2 +- drivers/s390/char/sclp.c | 4 ++-- drivers/s390/char/zcore.c | 2 +- drivers/s390/cio/cio.c | 4 ++-- drivers/s390/cio/cmf.c | 6 +++--- drivers/s390/cio/css.c | 2 +- drivers/s390/cio/device_fsm.c | 2 +- drivers/s390/cio/qdio_main.c | 12 ++++++------ drivers/s390/net/qeth_core.h | 2 +- drivers/s390/scsi/zfcp_fsf.c | 2 +- drivers/s390/scsi/zfcp_qdio.c | 2 +- 31 files changed, 106 insertions(+), 106 deletions(-) (limited to 'drivers') diff --git a/arch/s390/appldata/appldata_mem.c b/arch/s390/appldata/appldata_mem.c index 02d9a1cf5057..7ef60b52d6e0 100644 --- a/arch/s390/appldata/appldata_mem.c +++ b/arch/s390/appldata/appldata_mem.c @@ -108,7 +108,7 @@ static void appldata_get_mem_data(void *data) mem_data->totalswap = P2K(val.totalswap); mem_data->freeswap = P2K(val.freeswap); - mem_data->timestamp = get_clock(); + mem_data->timestamp = get_tod_clock(); mem_data->sync_count_2++; } diff --git a/arch/s390/appldata/appldata_net_sum.c b/arch/s390/appldata/appldata_net_sum.c index 1370e358d49a..2d224b945355 100644 --- a/arch/s390/appldata/appldata_net_sum.c +++ b/arch/s390/appldata/appldata_net_sum.c @@ -111,7 +111,7 @@ static void appldata_get_net_sum_data(void *data) net_data->tx_dropped = tx_dropped; net_data->collisions = collisions; - net_data->timestamp = get_clock(); + net_data->timestamp = get_tod_clock(); net_data->sync_count_2++; } diff --git a/arch/s390/appldata/appldata_os.c b/arch/s390/appldata/appldata_os.c index 87521ba682e5..de8e2b3b0180 100644 --- a/arch/s390/appldata/appldata_os.c +++ b/arch/s390/appldata/appldata_os.c @@ -156,7 +156,7 @@ static void appldata_get_os_data(void *data) } ops.size = new_size; } - os_data->timestamp = get_clock(); + os_data->timestamp = get_tod_clock(); os_data->sync_count_2++; } diff --git a/arch/s390/hypfs/hypfs_vm.c b/arch/s390/hypfs/hypfs_vm.c index 4f6afaa8bd8f..f364dcf77e8e 100644 --- a/arch/s390/hypfs/hypfs_vm.c +++ b/arch/s390/hypfs/hypfs_vm.c @@ -245,7 +245,7 @@ static int dbfs_diag2fc_create(void **data, void **data_free_ptr, size_t *size) d2fc = diag2fc_store(guest_query, &count, sizeof(d2fc->hdr)); if (IS_ERR(d2fc)) return PTR_ERR(d2fc); - get_clock_ext(d2fc->hdr.tod_ext); + get_tod_clock_ext(d2fc->hdr.tod_ext); d2fc->hdr.len = count * sizeof(struct diag2fc_data); d2fc->hdr.version = DBFS_D2FC_HDR_VERSION; d2fc->hdr.count = count; diff --git a/arch/s390/include/asm/timex.h b/arch/s390/include/asm/timex.h index 4c060bb5b8ea..8ad8af915032 100644 --- a/arch/s390/include/asm/timex.h +++ b/arch/s390/include/asm/timex.h @@ -15,7 +15,7 @@ #define TOD_UNIX_EPOCH 0x7d91048bca000000ULL /* Inline functions for clock register access. */ -static inline int set_clock(__u64 time) +static inline int set_tod_clock(__u64 time) { int cc; @@ -27,7 +27,7 @@ static inline int set_clock(__u64 time) return cc; } -static inline int store_clock(__u64 *time) +static inline int store_tod_clock(__u64 *time) { int cc; @@ -71,7 +71,7 @@ static inline void local_tick_enable(unsigned long long comp) typedef unsigned long long cycles_t; -static inline unsigned long long get_clock(void) +static inline unsigned long long get_tod_clock(void) { unsigned long long clk; @@ -83,21 +83,21 @@ static inline unsigned long long get_clock(void) return clk; } -static inline void get_clock_ext(char *clk) +static inline void get_tod_clock_ext(char *clk) { asm volatile("stcke %0" : "=Q" (*clk) : : "cc"); } -static inline unsigned long long get_clock_xt(void) +static inline unsigned long long get_tod_clock_xt(void) { unsigned char clk[16]; - get_clock_ext(clk); + get_tod_clock_ext(clk); return *((unsigned long long *)&clk[1]); } static inline cycles_t get_cycles(void) { - return (cycles_t) get_clock() >> 2; + return (cycles_t) get_tod_clock() >> 2; } int get_sync_clock(unsigned long long *clock); @@ -123,9 +123,9 @@ extern u64 sched_clock_base_cc; * function, otherwise the returned value is not guaranteed to * be monotonic. */ -static inline unsigned long long get_clock_monotonic(void) +static inline unsigned long long get_tod_clock_monotonic(void) { - return get_clock_xt() - sched_clock_base_cc; + return get_tod_clock_xt() - sched_clock_base_cc; } /** diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c index 4e8215e0d4b6..09a94cd9debc 100644 --- a/arch/s390/kernel/debug.c +++ b/arch/s390/kernel/debug.c @@ -867,7 +867,7 @@ static inline void debug_finish_entry(debug_info_t * id, debug_entry_t* active, int level, int exception) { - active->id.stck = get_clock(); + active->id.stck = get_tod_clock(); active->id.fields.cpuid = smp_processor_id(); active->caller = __builtin_return_address(0); active->id.fields.exception = exception; diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index 1f0eee9e7daa..1ee98e56fc6a 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -47,10 +47,10 @@ static void __init reset_tod_clock(void) { u64 time; - if (store_clock(&time) == 0) + if (store_tod_clock(&time) == 0) return; /* TOD clock not running. Set the clock to Unix Epoch. */ - if (set_clock(TOD_UNIX_EPOCH) != 0 || store_clock(&time) != 0) + if (set_tod_clock(TOD_UNIX_EPOCH) != 0 || store_tod_clock(&time) != 0) disabled_wait(0); sched_clock_base_cc = TOD_UNIX_EPOCH; @@ -173,7 +173,7 @@ static noinline __init void create_kernel_nss(void) } /* re-initialize cputime accounting. */ - sched_clock_base_cc = get_clock(); + sched_clock_base_cc = get_tod_clock(); S390_lowcore.last_update_clock = sched_clock_base_cc; S390_lowcore.last_update_timer = 0x7fffffffffffffffULL; S390_lowcore.user_timer = 0; diff --git a/arch/s390/kernel/nmi.c b/arch/s390/kernel/nmi.c index 7918fbea36bb..504175ebf8b0 100644 --- a/arch/s390/kernel/nmi.c +++ b/arch/s390/kernel/nmi.c @@ -293,7 +293,7 @@ void notrace s390_do_machine_check(struct pt_regs *regs) * retry this instruction. */ spin_lock(&ipd_lock); - tmp = get_clock(); + tmp = get_tod_clock(); if (((tmp - last_ipd) >> 12) < MAX_IPD_TIME) ipd_count++; else diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 7433a2f9e5cc..549c9d173c0f 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -365,16 +365,16 @@ void smp_emergency_stop(cpumask_t *cpumask) u64 end; int cpu; - end = get_clock() + (1000000UL << 12); + end = get_tod_clock() + (1000000UL << 12); for_each_cpu(cpu, cpumask) { struct pcpu *pcpu = pcpu_devices + cpu; set_bit(ec_stop_cpu, &pcpu->ec_mask); while (__pcpu_sigp(pcpu->address, SIGP_EMERGENCY_SIGNAL, 0, NULL) == SIGP_CC_BUSY && - get_clock() < end) + get_tod_clock() < end) cpu_relax(); } - while (get_clock() < end) { + while (get_tod_clock() < end) { for_each_cpu(cpu, cpumask) if (pcpu_stopped(pcpu_devices + cpu)) cpumask_clear_cpu(cpu, cpumask); @@ -694,7 +694,7 @@ static void __init smp_detect_cpus(void) */ static void __cpuinit smp_start_secondary(void *cpuvoid) { - S390_lowcore.last_update_clock = get_clock(); + S390_lowcore.last_update_clock = get_tod_clock(); S390_lowcore.restart_stack = (unsigned long) restart_stack; S390_lowcore.restart_fn = (unsigned long) do_restart; S390_lowcore.restart_data = 0; @@ -947,7 +947,7 @@ static ssize_t show_idle_time(struct device *dev, unsigned int sequence; do { - now = get_clock(); + now = get_tod_clock(); sequence = ACCESS_ONCE(idle->sequence); idle_time = ACCESS_ONCE(idle->idle_time); idle_enter = ACCESS_ONCE(idle->clock_idle_enter); diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 0aa98db8a80d..876546b9cfa1 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -63,7 +63,7 @@ static DEFINE_PER_CPU(struct clock_event_device, comparators); */ unsigned long long notrace __kprobes sched_clock(void) { - return tod_to_ns(get_clock_monotonic()); + return tod_to_ns(get_tod_clock_monotonic()); } /* @@ -194,7 +194,7 @@ static void stp_reset(void); void read_persistent_clock(struct timespec *ts) { - tod_to_timeval(get_clock() - TOD_UNIX_EPOCH, ts); + tod_to_timeval(get_tod_clock() - TOD_UNIX_EPOCH, ts); } void read_boot_clock(struct timespec *ts) @@ -204,7 +204,7 @@ void read_boot_clock(struct timespec *ts) static cycle_t read_tod_clock(struct clocksource *cs) { - return get_clock(); + return get_tod_clock(); } static struct clocksource clocksource_tod = { @@ -342,7 +342,7 @@ int get_sync_clock(unsigned long long *clock) sw_ptr = &get_cpu_var(clock_sync_word); sw0 = atomic_read(sw_ptr); - *clock = get_clock(); + *clock = get_tod_clock(); sw1 = atomic_read(sw_ptr); put_cpu_var(clock_sync_word); if (sw0 == sw1 && (sw0 & 0x80000000U)) @@ -486,7 +486,7 @@ static void etr_reset(void) .p0 = 0, .p1 = 0, ._pad1 = 0, .ea = 0, .es = 0, .sl = 0 }; if (etr_setr(&etr_eacr) == 0) { - etr_tolec = get_clock(); + etr_tolec = get_tod_clock(); set_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags); if (etr_port0_online && etr_port1_online) set_bit(CLOCK_SYNC_ETR, &clock_sync_flags); @@ -768,8 +768,8 @@ static int etr_sync_clock(void *data) __ctl_set_bit(14, 21); __ctl_set_bit(0, 29); clock = ((unsigned long long) (aib->edf2.etv + 1)) << 32; - old_clock = get_clock(); - if (set_clock(clock) == 0) { + old_clock = get_tod_clock(); + if (set_tod_clock(clock) == 0) { __udelay(1); /* Wait for the clock to start. */ __ctl_clear_bit(0, 29); __ctl_clear_bit(14, 21); @@ -845,7 +845,7 @@ static struct etr_eacr etr_handle_events(struct etr_eacr eacr) * assume that this can have caused an stepping * port switch. */ - etr_tolec = get_clock(); + etr_tolec = get_tod_clock(); eacr.p0 = etr_port0_online; if (!eacr.p0) eacr.e0 = 0; @@ -858,7 +858,7 @@ static struct etr_eacr etr_handle_events(struct etr_eacr eacr) * assume that this can have caused an stepping * port switch. */ - etr_tolec = get_clock(); + etr_tolec = get_tod_clock(); eacr.p1 = etr_port1_online; if (!eacr.p1) eacr.e1 = 0; @@ -974,7 +974,7 @@ static void etr_update_eacr(struct etr_eacr eacr) etr_eacr = eacr; etr_setr(&etr_eacr); if (dp_changed) - etr_tolec = get_clock(); + etr_tolec = get_tod_clock(); } /* @@ -1012,7 +1012,7 @@ static void etr_work_fn(struct work_struct *work) /* Store aib to get the current ETR status word. */ BUG_ON(etr_stetr(&aib) != 0); etr_port0.esw = etr_port1.esw = aib.esw; /* Copy status word. */ - now = get_clock(); + now = get_tod_clock(); /* * Update the port information if the last stepping port change @@ -1537,10 +1537,10 @@ static int stp_sync_clock(void *data) if (stp_info.todoff[0] || stp_info.todoff[1] || stp_info.todoff[2] || stp_info.todoff[3] || stp_info.tmd != 2) { - old_clock = get_clock(); + old_clock = get_tod_clock(); rc = chsc_sstpc(stp_page, STP_OP_SYNC, 0); if (rc == 0) { - delta = adjust_time(old_clock, get_clock(), 0); + delta = adjust_time(old_clock, get_tod_clock(), 0); fixup_clock_comparator(delta); rc = chsc_sstpi(stp_page, &stp_info, sizeof(struct stp_sstpi)); diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index e84b8b68444a..8911a169af44 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -191,7 +191,7 @@ cputime64_t s390_get_idle_time(int cpu) unsigned int sequence; do { - now = get_clock(); + now = get_tod_clock(); sequence = ACCESS_ONCE(idle->sequence); idle_enter = ACCESS_ONCE(idle->clock_idle_enter); idle_exit = ACCESS_ONCE(idle->clock_idle_exit); diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 82c481ddef76..87418b50f21c 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -362,7 +362,7 @@ static int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu) } if ((!rc) && (vcpu->arch.sie_block->ckc < - get_clock() + vcpu->arch.sie_block->epoch)) { + get_tod_clock() + vcpu->arch.sie_block->epoch)) { if ((!psw_extint_disabled(vcpu)) && (vcpu->arch.sie_block->gcr[0] & 0x800ul)) rc = 1; @@ -402,7 +402,7 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu) goto no_timer; } - now = get_clock() + vcpu->arch.sie_block->epoch; + now = get_tod_clock() + vcpu->arch.sie_block->epoch; if (vcpu->arch.sie_block->ckc < now) { __unset_cpu_idle(vcpu); return 0; @@ -492,7 +492,7 @@ void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu) } if ((vcpu->arch.sie_block->ckc < - get_clock() + vcpu->arch.sie_block->epoch)) + get_tod_clock() + vcpu->arch.sie_block->epoch)) __try_deliver_ckc_interrupt(vcpu); if (atomic_read(&fi->active)) { diff --git a/arch/s390/lib/delay.c b/arch/s390/lib/delay.c index 42d0cf89121d..c61b9fad43cc 100644 --- a/arch/s390/lib/delay.c +++ b/arch/s390/lib/delay.c @@ -32,7 +32,7 @@ static void __udelay_disabled(unsigned long long usecs) unsigned long cr0, cr6, new; u64 clock_saved, end; - end = get_clock() + (usecs << 12); + end = get_tod_clock() + (usecs << 12); clock_saved = local_tick_disable(); __ctl_store(cr0, 0, 0); __ctl_store(cr6, 6, 6); @@ -45,7 +45,7 @@ static void __udelay_disabled(unsigned long long usecs) set_clock_comparator(end); vtime_stop_cpu(); local_irq_disable(); - } while (get_clock() < end); + } while (get_tod_clock() < end); lockdep_on(); __ctl_load(cr0, 0, 0); __ctl_load(cr6, 6, 6); @@ -56,7 +56,7 @@ static void __udelay_enabled(unsigned long long usecs) { u64 clock_saved, end; - end = get_clock() + (usecs << 12); + end = get_tod_clock() + (usecs << 12); do { clock_saved = 0; if (end < S390_lowcore.clock_comparator) { @@ -67,7 +67,7 @@ static void __udelay_enabled(unsigned long long usecs) local_irq_disable(); if (clock_saved) local_tick_enable(clock_saved); - } while (get_clock() < end); + } while (get_tod_clock() < end); } /* @@ -111,8 +111,8 @@ void udelay_simple(unsigned long long usecs) { u64 end; - end = get_clock() + (usecs << 12); - while (get_clock() < end) + end = get_tod_clock() + (usecs << 12); + while (get_tod_clock() < end) cpu_relax(); } @@ -122,10 +122,10 @@ void __ndelay(unsigned long long nsecs) nsecs <<= 9; do_div(nsecs, 125); - end = get_clock() + nsecs; + end = get_tod_clock() + nsecs; if (nsecs & ~0xfffUL) __udelay(nsecs >> 12); - while (get_clock() < end) + while (get_tod_clock() < end) barrier(); } EXPORT_SYMBOL(__ndelay); diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 29225e1c159c..e1d96344d733 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1352,7 +1352,7 @@ int dasd_term_IO(struct dasd_ccw_req *cqr) switch (rc) { case 0: /* termination successful */ cqr->status = DASD_CQR_CLEAR_PENDING; - cqr->stopclk = get_clock(); + cqr->stopclk = get_tod_clock(); cqr->starttime = 0; DBF_DEV_EVENT(DBF_DEBUG, device, "terminate cqr %p successful", @@ -1420,7 +1420,7 @@ int dasd_start_IO(struct dasd_ccw_req *cqr) cqr->status = DASD_CQR_ERROR; return -EIO; } - cqr->startclk = get_clock(); + cqr->startclk = get_tod_clock(); cqr->starttime = jiffies; cqr->retries--; if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) { @@ -1623,7 +1623,7 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, return; } - now = get_clock(); + now = get_tod_clock(); cqr = (struct dasd_ccw_req *) intparm; /* check for conditions that should be handled immediately */ if (!cqr || @@ -1963,7 +1963,7 @@ int dasd_flush_device_queue(struct dasd_device *device) } break; case DASD_CQR_QUEUED: - cqr->stopclk = get_clock(); + cqr->stopclk = get_tod_clock(); cqr->status = DASD_CQR_CLEARED; break; default: /* no need to modify the others */ @@ -2210,7 +2210,7 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible) wait_event(generic_waitq, _wait_for_wakeup(cqr)); } - maincqr->endclk = get_clock(); + maincqr->endclk = get_tod_clock(); if ((maincqr->status != DASD_CQR_DONE) && (maincqr->intrc != -ERESTARTSYS)) dasd_log_sense(maincqr, &maincqr->irb); @@ -2340,7 +2340,7 @@ int dasd_cancel_req(struct dasd_ccw_req *cqr) "Cancelling request %p failed with rc=%d\n", cqr, rc); } else { - cqr->stopclk = get_clock(); + cqr->stopclk = get_tod_clock(); } break; default: /* already finished or clear pending - do nothing */ @@ -2568,7 +2568,7 @@ restart: } /* Rechain finished requests to final queue */ - cqr->endclk = get_clock(); + cqr->endclk = get_tod_clock(); list_move_tail(&cqr->blocklist, final_queue); } } @@ -2711,7 +2711,7 @@ restart_cb: } /* call the callback function */ spin_lock_irq(&block->request_queue_lock); - cqr->endclk = get_clock(); + cqr->endclk = get_tod_clock(); list_del_init(&cqr->blocklist); __dasd_cleanup_cqr(cqr); spin_unlock_irq(&block->request_queue_lock); @@ -3504,7 +3504,7 @@ static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device, cqr->memdev = device; cqr->expires = 10*HZ; cqr->retries = 256; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; } diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index f8212d54013a..d26134713682 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -229,7 +229,7 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) dctl_cqr->expires = 5 * 60 * HZ; dctl_cqr->retries = 2; - dctl_cqr->buildclk = get_clock(); + dctl_cqr->buildclk = get_tod_clock(); dctl_cqr->status = DASD_CQR_FILLED; @@ -1719,7 +1719,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) erp->magic = default_erp->magic; erp->expires = default_erp->expires; erp->retries = 256; - erp->buildclk = get_clock(); + erp->buildclk = get_tod_clock(); erp->status = DASD_CQR_FILLED; /* remove the default erp */ @@ -2322,7 +2322,7 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) DBF_DEV_EVENT(DBF_ERR, device, "%s", "Unable to allocate ERP request"); cqr->status = DASD_CQR_FAILED; - cqr->stopclk = get_clock (); + cqr->stopclk = get_tod_clock(); } else { DBF_DEV_EVENT(DBF_ERR, device, "Unable to allocate ERP request " @@ -2364,7 +2364,7 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) erp->magic = cqr->magic; erp->expires = cqr->expires; erp->retries = 256; - erp->buildclk = get_clock(); + erp->buildclk = get_tod_clock(); erp->status = DASD_CQR_FILLED; return erp; diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c index 6b556995bb33..a2597e683e79 100644 --- a/drivers/s390/block/dasd_alias.c +++ b/drivers/s390/block/dasd_alias.c @@ -448,7 +448,7 @@ static int read_unit_address_configuration(struct dasd_device *device, ccw->count = sizeof(*(lcu->uac)); ccw->cda = (__u32)(addr_t) lcu->uac; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; /* need to unset flag here to detect race with summary unit check */ @@ -733,7 +733,7 @@ static int reset_summary_unit_check(struct alias_lcu *lcu, cqr->memdev = device; cqr->block = NULL; cqr->expires = 5 * HZ; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; rc = dasd_sleep_on_immediatly(cqr); diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 704488d0f819..cc0603358522 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -184,14 +184,14 @@ dasd_start_diag(struct dasd_ccw_req * cqr) private->iob.bio_list = dreq->bio; private->iob.flaga = DASD_DIAG_FLAGA_DEFAULT; - cqr->startclk = get_clock(); + cqr->startclk = get_tod_clock(); cqr->starttime = jiffies; cqr->retries--; rc = dia250(&private->iob, RW_BIO); switch (rc) { case 0: /* Synchronous I/O finished successfully */ - cqr->stopclk = get_clock(); + cqr->stopclk = get_tod_clock(); cqr->status = DASD_CQR_SUCCESS; /* Indicate to calling function that only a dasd_schedule_bh() and no timer is needed */ @@ -222,7 +222,7 @@ dasd_diag_term_IO(struct dasd_ccw_req * cqr) mdsk_term_io(device); mdsk_init_io(device, device->block->bp_block, 0, NULL); cqr->status = DASD_CQR_CLEAR_PENDING; - cqr->stopclk = get_clock(); + cqr->stopclk = get_tod_clock(); dasd_schedule_device_bh(device); return 0; } @@ -276,7 +276,7 @@ static void dasd_ext_handler(struct ext_code ext_code, return; } - cqr->stopclk = get_clock(); + cqr->stopclk = get_tod_clock(); expires = 0; if ((ext_code.subcode & 0xff) == 0) { @@ -556,7 +556,7 @@ static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev, } } cqr->retries = DIAG_MAX_RETRIES; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); if (blk_noretry_request(req) || block->base->features & DASD_FEATURE_FAILFAST) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index e37bc1620d14..33f26bfa62f2 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -862,7 +862,7 @@ static void dasd_eckd_fill_rcd_cqr(struct dasd_device *device, cqr->expires = 10*HZ; cqr->lpm = lpm; cqr->retries = 256; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags); } @@ -1449,7 +1449,7 @@ static int dasd_eckd_read_features(struct dasd_device *device) ccw->count = sizeof(struct dasd_rssd_features); ccw->cda = (__u32)(addr_t) features; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; rc = dasd_sleep_on(cqr); if (rc == 0) { @@ -1501,7 +1501,7 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device, cqr->block = NULL; cqr->retries = 256; cqr->expires = 10*HZ; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; } @@ -1841,7 +1841,7 @@ dasd_eckd_analysis_ccw(struct dasd_device *device) cqr->startdev = device; cqr->memdev = device; cqr->retries = 255; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; } @@ -2241,7 +2241,7 @@ dasd_eckd_format_device(struct dasd_device * device, fcp->startdev = device; fcp->memdev = device; fcp->retries = 256; - fcp->buildclk = get_clock(); + fcp->buildclk = get_tod_clock(); fcp->status = DASD_CQR_FILLED; return fcp; } @@ -2530,7 +2530,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ cqr->lpm = startdev->path_data.ppm; cqr->retries = 256; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; } @@ -2705,7 +2705,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track( cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ cqr->lpm = startdev->path_data.ppm; cqr->retries = 256; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; } @@ -2998,7 +2998,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ cqr->lpm = startdev->path_data.ppm; cqr->retries = 256; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; out_error: @@ -3201,7 +3201,7 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev, cqr->expires = startdev->default_expires * HZ; cqr->lpm = startdev->path_data.ppm; cqr->retries = 256; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; if (IS_ERR(cqr) && PTR_ERR(cqr) != -EAGAIN) @@ -3402,7 +3402,7 @@ dasd_eckd_release(struct dasd_device *device) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->retries = 2; /* set retry counter to enable basic ERP */ cqr->expires = 2 * HZ; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; rc = dasd_sleep_on_immediatly(cqr); @@ -3457,7 +3457,7 @@ dasd_eckd_reserve(struct dasd_device *device) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->retries = 2; /* set retry counter to enable basic ERP */ cqr->expires = 2 * HZ; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; rc = dasd_sleep_on_immediatly(cqr); @@ -3511,7 +3511,7 @@ dasd_eckd_steal_lock(struct dasd_device *device) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->retries = 2; /* set retry counter to enable basic ERP */ cqr->expires = 2 * HZ; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; rc = dasd_sleep_on_immediatly(cqr); @@ -3572,7 +3572,7 @@ static int dasd_eckd_snid(struct dasd_device *device, set_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags); cqr->retries = 5; cqr->expires = 10 * HZ; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; cqr->lpm = usrparm.path_mask; @@ -3642,7 +3642,7 @@ dasd_eckd_performance(struct dasd_device *device, void __user *argp) ccw->count = sizeof(struct dasd_rssd_perf_stats_t); ccw->cda = (__u32)(addr_t) stats; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; rc = dasd_sleep_on(cqr); if (rc == 0) { @@ -3768,7 +3768,7 @@ static int dasd_symm_io(struct dasd_device *device, void __user *argp) cqr->memdev = device; cqr->retries = 3; cqr->expires = 10 * HZ; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; /* Build the ccws */ diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c index ff901b5509c1..21ef63cf0960 100644 --- a/drivers/s390/block/dasd_eer.c +++ b/drivers/s390/block/dasd_eer.c @@ -481,7 +481,7 @@ int dasd_eer_enable(struct dasd_device *device) ccw->flags = 0; ccw->cda = (__u32)(addr_t) cqr->data; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; cqr->callback = dasd_eer_snss_cb; diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c index d01ef82f8757..3250cb471f78 100644 --- a/drivers/s390/block/dasd_erp.c +++ b/drivers/s390/block/dasd_erp.c @@ -102,7 +102,7 @@ dasd_default_erp_action(struct dasd_ccw_req *cqr) pr_err("%s: default ERP has run out of retries and failed\n", dev_name(&device->cdev->dev)); cqr->status = DASD_CQR_FAILED; - cqr->stopclk = get_clock(); + cqr->stopclk = get_tod_clock(); } return cqr; } /* end dasd_default_erp_action */ @@ -146,7 +146,7 @@ struct dasd_ccw_req *dasd_default_erp_postaction(struct dasd_ccw_req *cqr) cqr->status = DASD_CQR_DONE; else { cqr->status = DASD_CQR_FAILED; - cqr->stopclk = get_clock(); + cqr->stopclk = get_tod_clock(); } return cqr; diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index 414698584344..4dd0e2f6047e 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -370,7 +370,7 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, cqr->block = block; cqr->expires = memdev->default_expires * HZ; /* default 5 minutes */ cqr->retries = 32; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; } diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index 12c16a65dd25..bd6871bf545a 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -450,7 +450,7 @@ sclp_sync_wait(void) timeout = 0; if (timer_pending(&sclp_request_timer)) { /* Get timeout TOD value */ - timeout = get_clock() + + timeout = get_tod_clock() + sclp_tod_from_jiffies(sclp_request_timer.expires - jiffies); } @@ -472,7 +472,7 @@ sclp_sync_wait(void) while (sclp_running_state != sclp_running_state_idle) { /* Check for expired request timer */ if (timer_pending(&sclp_request_timer) && - get_clock() > timeout && + get_tod_clock() > timeout && del_timer(&sclp_request_timer)) sclp_request_timer.function(sclp_request_timer.data); cpu_relax(); diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 681749e7f6dd..1d61a01576d2 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -637,7 +637,7 @@ static int __init zcore_header_init(int arch, struct zcore_header *hdr) hdr->rmem_size = memory; hdr->mem_end = sys_info.mem_size; hdr->num_pages = memory / PAGE_SIZE; - hdr->tod = get_clock(); + hdr->tod = get_tod_clock(); get_cpu_id(&hdr->cpu_id); for (i = 0; zfcpdump_save_areas[i]; i++) { prefix = zfcpdump_save_areas[i]->pref_reg; diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index c8faf6230b0f..986ef6a92a41 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -962,9 +962,9 @@ static void css_reset(void) atomic_inc(&chpid_reset_count); } /* Wait for machine check for all channel paths. */ - timeout = get_clock() + (RCHP_TIMEOUT << 12); + timeout = get_tod_clock() + (RCHP_TIMEOUT << 12); while (atomic_read(&chpid_reset_count) != 0) { - if (get_clock() > timeout) + if (get_tod_clock() > timeout) break; cpu_relax(); } diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index c9fc61c0a866..4495e0627a40 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -33,7 +33,7 @@ #include #include #include -#include /* get_clock() */ +#include /* get_tod_clock() */ #include #include @@ -326,7 +326,7 @@ static int cmf_copy_block(struct ccw_device *cdev) memcpy(cmb_data->last_block, hw_block, cmb_data->size); memcpy(reference_buf, hw_block, cmb_data->size); } while (memcmp(cmb_data->last_block, reference_buf, cmb_data->size)); - cmb_data->last_update = get_clock(); + cmb_data->last_update = get_tod_clock(); kfree(reference_buf); return 0; } @@ -428,7 +428,7 @@ static void cmf_generic_reset(struct ccw_device *cdev) memset(cmbops->align(cmb_data->hw_block), 0, cmb_data->size); cmb_data->last_update = 0; } - cdev->private->cmb_start_time = get_clock(); + cdev->private->cmb_start_time = get_tod_clock(); spin_unlock_irq(cdev->ccwlock); } diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index fd00afd8b850..a239237d43f3 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -780,7 +780,7 @@ static int __init setup_css(int nr) css->cssid = nr; dev_set_name(&css->device, "css%x", nr); css->device.release = channel_subsystem_release; - tod_high = (u32) (get_clock() >> 32); + tod_high = (u32) (get_tod_clock() >> 32); css_generate_pgid(css, tod_high); return 0; } diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 1bb1d00095af..c7638c543250 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -47,7 +47,7 @@ static void ccw_timeout_log(struct ccw_device *cdev) cc = stsch_err(sch->schid, &schib); printk(KERN_WARNING "cio: ccw device timeout occurred at %llx, " - "device information:\n", get_clock()); + "device information:\n", get_tod_clock()); printk(KERN_WARNING "cio: orb:\n"); print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1, orb, sizeof(*orb), 0); diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 1671d3461f29..abc550e5dd35 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -338,10 +338,10 @@ again: retries++; if (!start_time) { - start_time = get_clock(); + start_time = get_tod_clock(); goto again; } - if ((get_clock() - start_time) < QDIO_BUSY_BIT_PATIENCE) + if ((get_tod_clock() - start_time) < QDIO_BUSY_BIT_PATIENCE) goto again; } if (retries) { @@ -504,7 +504,7 @@ static int get_inbound_buffer_frontier(struct qdio_q *q) int count, stop; unsigned char state = 0; - q->timestamp = get_clock(); + q->timestamp = get_tod_clock(); /* * Don't check 128 buffers, as otherwise qdio_inbound_q_moved @@ -563,7 +563,7 @@ static int qdio_inbound_q_moved(struct qdio_q *q) if (bufnr != q->last_move) { q->last_move = bufnr; if (!is_thinint_irq(q->irq_ptr) && MACHINE_IS_LPAR) - q->u.in.timestamp = get_clock(); + q->u.in.timestamp = get_tod_clock(); return 1; } else return 0; @@ -595,7 +595,7 @@ static inline int qdio_inbound_q_done(struct qdio_q *q) * At this point we know, that inbound first_to_check * has (probably) not moved (see qdio_inbound_processing). */ - if (get_clock() > q->u.in.timestamp + QDIO_INPUT_THRESHOLD) { + if (get_tod_clock() > q->u.in.timestamp + QDIO_INPUT_THRESHOLD) { DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in done:%02x", q->first_to_check); return 1; @@ -772,7 +772,7 @@ static int get_outbound_buffer_frontier(struct qdio_q *q) int count, stop; unsigned char state = 0; - q->timestamp = get_clock(); + q->timestamp = get_tod_clock(); if (need_siga_sync(q)) if (((queue_type(q) != QDIO_IQDIO_QFMT) && diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 480fbeab0256..b4796a40b002 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -816,7 +816,7 @@ static inline struct qeth_card *CARD_FROM_CDEV(struct ccw_device *cdev) static inline int qeth_get_micros(void) { - return (int) (get_clock() >> 12); + return (int) (get_tod_clock() >> 12); } static inline int qeth_get_ip_version(struct sk_buff *skb) diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index c96320d79fbc..c7e148f33b2a 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -727,7 +727,7 @@ static int zfcp_fsf_req_send(struct zfcp_fsf_req *req) zfcp_reqlist_add(adapter->req_list, req); req->qdio_req.qdio_outb_usage = atomic_read(&qdio->req_q_free); - req->issued = get_clock(); + req->issued = get_tod_clock(); if (zfcp_qdio_send(qdio, &req->qdio_req)) { del_timer(&req->timer); /* lookup request again, list might have changed */ diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index 50b5615848f6..665e3cfaaf85 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -68,7 +68,7 @@ static inline void zfcp_qdio_account(struct zfcp_qdio *qdio) unsigned long long now, span; int used; - now = get_clock_monotonic(); + now = get_tod_clock_monotonic(); span = (now - qdio->req_q_time) >> 12; used = QDIO_MAX_BUFFERS_PER_Q - atomic_read(&qdio->req_q_free); qdio->req_q_util += used * span; -- cgit v1.2.3 From 1e5635d10d8112e61776b9513491329f7b0859ce Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 30 Jan 2013 15:52:16 +0100 Subject: s390/pci: rename pci_probe to s390_pci_probe pci_probe is too generic and has a name clash with other common code parts. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/pci.h | 2 +- arch/s390/pci/pci.c | 8 ++++---- drivers/pci/hotplug/s390_pci_hpc.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h index 23d6a245e8a7..6383c44c6629 100644 --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h @@ -186,7 +186,7 @@ void zpci_dma_exit(void); extern struct mutex zpci_list_lock; extern struct list_head zpci_list; extern struct pci_hp_callback_ops hotplug_ops; -extern unsigned int pci_probe; +extern unsigned int s390_pci_probe; /* FMB */ int zpci_fmb_enable_device(struct zpci_dev *); diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 60e0372545d2..aa74409db656 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -1072,13 +1072,13 @@ static void zpci_mem_exit(void) kmem_cache_destroy(zdev_fmb_cache); } -unsigned int pci_probe = 1; -EXPORT_SYMBOL_GPL(pci_probe); +unsigned int s390_pci_probe = 1; +EXPORT_SYMBOL_GPL(s390_pci_probe); char * __init pcibios_setup(char *str) { if (!strcmp(str, "off")) { - pci_probe = 0; + s390_pci_probe = 0; return NULL; } return str; @@ -1088,7 +1088,7 @@ static int __init pci_base_init(void) { int rc; - if (!pci_probe) + if (!s390_pci_probe) return 0; if (!test_facility(2) || !test_facility(69) diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c index dee68e0698e1..8fe2a8aa89a1 100644 --- a/drivers/pci/hotplug/s390_pci_hpc.c +++ b/drivers/pci/hotplug/s390_pci_hpc.c @@ -231,7 +231,7 @@ static int __init pci_hotplug_s390_init(void) * right now. */ - if (!pci_probe) + if (!s390_pci_probe) return -EOPNOTSUPP; /* register callbacks for slot handling from arch code */ -- cgit v1.2.3 From 0383a68cd2583ef3693cbfffe5abfc4369d70d46 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 30 Jan 2013 16:32:12 +0100 Subject: ata: disable ATA for s390 Add s390 to the list of architectures that don' want ATA. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/ata/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index e08d322d01d7..14c3ac9eea96 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -14,7 +14,7 @@ menuconfig ATA tristate "Serial ATA and Parallel ATA drivers" depends on HAS_IOMEM depends on BLOCK - depends on !(M32R || M68K) || BROKEN + depends on !(M32R || M68K || S390) || BROKEN select SCSI ---help--- If you want to use a ATA hard disk, ATA tape drive, ATA CD-ROM or -- cgit v1.2.3 From 075aa0c6b1e33ff953dfd66bdc7d187011f563c0 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 30 Jan 2013 16:34:39 +0100 Subject: parport: disable PC-style parallel port support for s390 Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/parport/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/parport/Kconfig b/drivers/parport/Kconfig index 0e60438ebe30..24e12d4d1769 100644 --- a/drivers/parport/Kconfig +++ b/drivers/parport/Kconfig @@ -35,7 +35,7 @@ if PARPORT config PARPORT_PC tristate "PC-style hardware" - depends on (!SPARC64 || PCI) && !SPARC32 && !M32R && !FRV && \ + depends on (!SPARC64 || PCI) && !SPARC32 && !M32R && !FRV && !S390 && \ (!M68K || ISA) && !MN10300 && !AVR32 && !BLACKFIN && !XTENSA ---help--- You should say Y here if you have a PC-style parallel port. All -- cgit v1.2.3 From 53923354d69e4748506bfee932b7c6b309a15c21 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Thu, 31 Jan 2013 19:55:17 +0100 Subject: s390/pci: fix hotplug module init Loading the pci hotplug module when no devices are present will fail but unfortunately some hotplug callbacks stay registered to the pci bus level. Fix this by not letting module loading fail when no pci devices are present and provide proper {de}registration functions for these callbacks. Reviewed-by: Gerald Schaefer Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/pci.h | 4 ++- arch/s390/pci/pci.c | 27 ++++++++++++++---- drivers/pci/hotplug/s390_pci_hpc.c | 58 ++++++++++++++++---------------------- 3 files changed, 49 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h index 6383c44c6629..05333b7f0469 100644 --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h @@ -185,9 +185,11 @@ void zpci_dma_exit(void); /* Hotplug */ extern struct mutex zpci_list_lock; extern struct list_head zpci_list; -extern struct pci_hp_callback_ops hotplug_ops; extern unsigned int s390_pci_probe; +void zpci_register_hp_ops(struct pci_hp_callback_ops *); +void zpci_deregister_hp_ops(void); + /* FMB */ int zpci_fmb_enable_device(struct zpci_dev *); int zpci_fmb_disable_device(struct zpci_dev *); diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index aa74409db656..27b4c17855b9 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -51,8 +51,7 @@ EXPORT_SYMBOL_GPL(zpci_list); DEFINE_MUTEX(zpci_list_lock); EXPORT_SYMBOL_GPL(zpci_list_lock); -struct pci_hp_callback_ops hotplug_ops; -EXPORT_SYMBOL_GPL(hotplug_ops); +static struct pci_hp_callback_ops *hotplug_ops; static DECLARE_BITMAP(zpci_domain, ZPCI_NR_DEVICES); static DEFINE_SPINLOCK(zpci_domain_lock); @@ -974,8 +973,8 @@ int zpci_create_device(struct zpci_dev *zdev) mutex_lock(&zpci_list_lock); list_add_tail(&zdev->entry, &zpci_list); - if (hotplug_ops.create_slot) - hotplug_ops.create_slot(zdev); + if (hotplug_ops) + hotplug_ops->create_slot(zdev); mutex_unlock(&zpci_list_lock); if (zdev->state == ZPCI_FN_STATE_STANDBY) @@ -989,8 +988,8 @@ int zpci_create_device(struct zpci_dev *zdev) out_start: mutex_lock(&zpci_list_lock); list_del(&zdev->entry); - if (hotplug_ops.remove_slot) - hotplug_ops.remove_slot(zdev); + if (hotplug_ops) + hotplug_ops->remove_slot(zdev); mutex_unlock(&zpci_list_lock); out_bus: zpci_free_domain(zdev); @@ -1072,6 +1071,22 @@ static void zpci_mem_exit(void) kmem_cache_destroy(zdev_fmb_cache); } +void zpci_register_hp_ops(struct pci_hp_callback_ops *ops) +{ + mutex_lock(&zpci_list_lock); + hotplug_ops = ops; + mutex_unlock(&zpci_list_lock); +} +EXPORT_SYMBOL_GPL(zpci_register_hp_ops); + +void zpci_deregister_hp_ops(void) +{ + mutex_lock(&zpci_list_lock); + hotplug_ops = NULL; + mutex_unlock(&zpci_list_lock); +} +EXPORT_SYMBOL_GPL(zpci_deregister_hp_ops); + unsigned int s390_pci_probe = 1; EXPORT_SYMBOL_GPL(s390_pci_probe); diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c index 8fe2a8aa89a1..7db249a25016 100644 --- a/drivers/pci/hotplug/s390_pci_hpc.c +++ b/drivers/pci/hotplug/s390_pci_hpc.c @@ -172,25 +172,6 @@ error: return -ENOMEM; } -static int __init init_pci_slots(void) -{ - struct zpci_dev *zdev; - int device = 0; - - /* - * Create a structure for each slot, and register that slot - * with the pci_hotplug subsystem. - */ - mutex_lock(&zpci_list_lock); - list_for_each_entry(zdev, &zpci_list, entry) { - init_pci_slot(zdev); - device++; - } - - mutex_unlock(&zpci_list_lock); - return (device) ? 0 : -ENODEV; -} - static void exit_pci_slot(struct zpci_dev *zdev) { struct list_head *tmp, *n; @@ -205,6 +186,26 @@ static void exit_pci_slot(struct zpci_dev *zdev) } } +static struct pci_hp_callback_ops hp_ops = { + .create_slot = init_pci_slot, + .remove_slot = exit_pci_slot, +}; + +static void __init init_pci_slots(void) +{ + struct zpci_dev *zdev; + + /* + * Create a structure for each slot, and register that slot + * with the pci_hotplug subsystem. + */ + mutex_lock(&zpci_list_lock); + list_for_each_entry(zdev, &zpci_list, entry) { + init_pci_slot(zdev); + } + mutex_unlock(&zpci_list_lock); +} + static void __exit exit_pci_slots(void) { struct list_head *tmp, *n; @@ -224,28 +225,19 @@ static void __exit exit_pci_slots(void) static int __init pci_hotplug_s390_init(void) { - /* - * Do specific initialization stuff for your driver here - * like initializing your controller hardware (if any) and - * determining the number of slots you have in the system - * right now. - */ - if (!s390_pci_probe) return -EOPNOTSUPP; - /* register callbacks for slot handling from arch code */ - mutex_lock(&zpci_list_lock); - hotplug_ops.create_slot = init_pci_slot; - hotplug_ops.remove_slot = exit_pci_slot; - mutex_unlock(&zpci_list_lock); - pr_info("registered hotplug slot callbacks\n"); - return init_pci_slots(); + zpci_register_hp_ops(&hp_ops); + init_pci_slots(); + + return 0; } static void __exit pci_hotplug_s390_exit(void) { exit_pci_slots(); + zpci_deregister_hp_ops(); } module_init(pci_hotplug_s390_init); -- cgit v1.2.3 From be4904e5039a769f84f933bacce85c5e8ddd90a7 Mon Sep 17 00:00:00 2001 From: Stefan Weinhuber Date: Mon, 4 Feb 2013 13:10:35 +0100 Subject: dasd: fix sysfs cleanup in dasd_generic_remove When the DASD devices are detached from the driver, then the dasd_generic_remove function is called. One of the things this function should do is to remove the DASD specific sysfs attributes, but this is not done in all cases. This is likely to cause an oops when at a later point sysfs stumbles over the stale pointers. In particular this happens when when the modules are unloaded and loaded again. Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index e1d96344d733..f1b7fdc58a5f 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -3042,12 +3042,15 @@ void dasd_generic_remove(struct ccw_device *cdev) cdev->handler = NULL; device = dasd_device_from_cdev(cdev); - if (IS_ERR(device)) + if (IS_ERR(device)) { + dasd_remove_sysfs_files(cdev); return; + } if (test_and_set_bit(DASD_FLAG_OFFLINE, &device->flags) && !test_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags)) { /* Already doing offline processing */ dasd_put_device(device); + dasd_remove_sysfs_files(cdev); return; } /* -- cgit v1.2.3 From c423c8ffa7b2681e19b77801390ed2a7708aae98 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 6 Feb 2013 10:08:40 +0100 Subject: uio: remove !S390 dependency from Kconfig Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/uio/Kconfig | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig index f56d185790ea..e92eeaf251fe 100644 --- a/drivers/uio/Kconfig +++ b/drivers/uio/Kconfig @@ -1,6 +1,5 @@ menuconfig UIO tristate "Userspace I/O drivers" - depends on !S390 help Enable this to allow the userspace driver core code to be built. This code allows userspace programs easy access to -- cgit v1.2.3 From 870a2b5e4fcde08c91c102712ea07d3cbc96f19a Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 6 Feb 2013 10:09:31 +0100 Subject: phylib: remove !S390 dependeny from Kconfig Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/net/phy/Kconfig | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 961f0b293913..450345261bd3 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -4,7 +4,6 @@ menuconfig PHYLIB tristate "PHY Device support and infrastructure" - depends on !S390 depends on NETDEVICES help Ethernet controllers are usually attached to PHY -- cgit v1.2.3 From a1dd7e4ef784a2c3d997d68b9321b6d115f19b20 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 6 Feb 2013 17:23:58 +0100 Subject: drivers/net,AT91RM9200: add missing GENERIC_HARDIRQS dependency The AT91RM9200 driver call devm_request_irq() and therefore should depend on GENERIC_HARDIRQS to prevent link/compile errors on plaforms without GENERIC_HARDIRQS. Signed-off-by: Heiko Carstens Acked-by: David S. Miller Signed-off-by: Martin Schwidefsky --- drivers/net/ethernet/cadence/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/cadence/Kconfig b/drivers/net/ethernet/cadence/Kconfig index ceb0de0cf62c..1194446f859a 100644 --- a/drivers/net/ethernet/cadence/Kconfig +++ b/drivers/net/ethernet/cadence/Kconfig @@ -22,6 +22,7 @@ if NET_CADENCE config ARM_AT91_ETHER tristate "AT91RM9200 Ethernet support" + depends on GENERIC_HARDIRQS select NET_CORE select MACB ---help--- -- cgit v1.2.3 From fa364fc41cd3c7fd47fb7be7b4d0722c25ebad4b Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 7 Feb 2013 15:54:50 +0100 Subject: drivers/media: add missing GENERIC_HARDIRQS dependency Texas Instruments WL1273 I2C FM Radio (RADIO_WL1273) selects MFD_CORE, which itself depends on GENERIC_HARDIRQS. So add the dependency to the TI driver as well. Signed-off-by: Heiko Carstens Acked-by: Mauro Carvalho Chehab Signed-off-by: Martin Schwidefsky --- drivers/media/radio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 8090b87b3066..9e580166161a 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -180,7 +180,7 @@ config RADIO_TIMBERDALE config RADIO_WL1273 tristate "Texas Instruments WL1273 I2C FM Radio" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_V4L2 && GENERIC_HARDIRQS select MFD_CORE select MFD_WL1273_CORE select FW_LOADER -- cgit v1.2.3 From abf09bed3cceadd809f0356065c2ada6cee90d4a Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Wed, 7 Nov 2012 13:17:37 +0100 Subject: s390/mm: implement software dirty bits The s390 architecture is unique in respect to dirty page detection, it uses the change bit in the per-page storage key to track page modifications. All other architectures track dirty bits by means of page table entries. This property of s390 has caused numerous problems in the past, e.g. see git commit ef5d437f71afdf4a "mm: fix XFS oops due to dirty pages without buffers on s390". To avoid future issues in regard to per-page dirty bits convert s390 to a fault based software dirty bit detection mechanism. All user page table entries which are marked as clean will be hardware read-only, even if the pte is supposed to be writable. A write by the user process will trigger a protection fault which will cause the user pte to be marked as dirty and the hardware read-only bit is removed. With this change the dirty bit in the storage key is irrelevant for Linux as a host, but the storage key is still required for KVM guests. The effect is that page_test_and_clear_dirty and the related code can be removed. The referenced bit in the storage key is still used by the page_test_and_clear_young primitive to provide page age information. For page cache pages of mappings with mapping_cap_account_dirty there will not be any change in behavior as the dirty bit tracking already uses read-only ptes to control the amount of dirty pages. Only for swap cache pages and pages of mappings without mapping_cap_account_dirty there can be additional protection faults. To avoid an excessive number of additional faults the mk_pte primitive checks for PageDirty if the pgprot value allows for writes and pre-dirties the pte. That avoids all additional faults for tmpfs and shmem pages until these pages are added to the swap cache. Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/page.h | 22 ------- arch/s390/include/asm/pgtable.h | 131 +++++++++++++++++++++++++++------------- arch/s390/include/asm/sclp.h | 1 - arch/s390/include/asm/setup.h | 16 ++--- arch/s390/kvm/kvm-s390.c | 2 +- arch/s390/lib/uaccess_pt.c | 2 +- arch/s390/mm/pageattr.c | 2 +- arch/s390/mm/vmem.c | 24 +++----- drivers/s390/char/sclp_cmd.c | 10 +-- include/asm-generic/pgtable.h | 10 --- include/linux/page-flags.h | 8 --- mm/rmap.c | 24 -------- 12 files changed, 112 insertions(+), 140 deletions(-) (limited to 'drivers') diff --git a/arch/s390/include/asm/page.h b/arch/s390/include/asm/page.h index a86ad4084073..75ce9b065f9f 100644 --- a/arch/s390/include/asm/page.h +++ b/arch/s390/include/asm/page.h @@ -154,28 +154,6 @@ static inline int page_reset_referenced(unsigned long addr) #define _PAGE_FP_BIT 0x08 /* HW fetch protection bit */ #define _PAGE_ACC_BITS 0xf0 /* HW access control bits */ -/* - * Test and clear dirty bit in storage key. - * We can't clear the changed bit atomically. This is a potential - * race against modification of the referenced bit. This function - * should therefore only be called if it is not mapped in any - * address space. - * - * Note that the bit gets set whenever page content is changed. That means - * also when the page is modified by DMA or from inside the kernel. - */ -#define __HAVE_ARCH_PAGE_TEST_AND_CLEAR_DIRTY -static inline int page_test_and_clear_dirty(unsigned long pfn, int mapped) -{ - unsigned char skey; - - skey = page_get_storage_key(pfn << PAGE_SHIFT); - if (!(skey & _PAGE_CHANGED)) - return 0; - page_set_storage_key(pfn << PAGE_SHIFT, skey & ~_PAGE_CHANGED, mapped); - return 1; -} - /* * Test and clear referenced bit in storage key. */ diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index a009d4dd70cb..97de1200c849 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -29,6 +29,7 @@ #ifndef __ASSEMBLY__ #include #include +#include #include #include @@ -221,13 +222,15 @@ extern unsigned long MODULES_END; /* Software bits in the page table entry */ #define _PAGE_SWT 0x001 /* SW pte type bit t */ #define _PAGE_SWX 0x002 /* SW pte type bit x */ -#define _PAGE_SWC 0x004 /* SW pte changed bit (for KVM) */ -#define _PAGE_SWR 0x008 /* SW pte referenced bit (for KVM) */ -#define _PAGE_SPECIAL 0x010 /* SW associated with special page */ +#define _PAGE_SWC 0x004 /* SW pte changed bit */ +#define _PAGE_SWR 0x008 /* SW pte referenced bit */ +#define _PAGE_SWW 0x010 /* SW pte write bit */ +#define _PAGE_SPECIAL 0x020 /* SW associated with special page */ #define __HAVE_ARCH_PTE_SPECIAL /* Set of bits not changed in pte_modify */ -#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_SPECIAL | _PAGE_SWC | _PAGE_SWR) +#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_SPECIAL | _PAGE_CO | \ + _PAGE_SWC | _PAGE_SWR) /* Six different types of pages. */ #define _PAGE_TYPE_EMPTY 0x400 @@ -321,6 +324,7 @@ extern unsigned long MODULES_END; /* Bits in the region table entry */ #define _REGION_ENTRY_ORIGIN ~0xfffUL/* region/segment table origin */ +#define _REGION_ENTRY_RO 0x200 /* region protection bit */ #define _REGION_ENTRY_INV 0x20 /* invalid region table entry */ #define _REGION_ENTRY_TYPE_MASK 0x0c /* region/segment table type mask */ #define _REGION_ENTRY_TYPE_R1 0x0c /* region first table type */ @@ -382,9 +386,10 @@ extern unsigned long MODULES_END; */ #define PAGE_NONE __pgprot(_PAGE_TYPE_NONE) #define PAGE_RO __pgprot(_PAGE_TYPE_RO) -#define PAGE_RW __pgprot(_PAGE_TYPE_RW) +#define PAGE_RW __pgprot(_PAGE_TYPE_RO | _PAGE_SWW) +#define PAGE_RWC __pgprot(_PAGE_TYPE_RW | _PAGE_SWW | _PAGE_SWC) -#define PAGE_KERNEL PAGE_RW +#define PAGE_KERNEL PAGE_RWC #define PAGE_SHARED PAGE_KERNEL #define PAGE_COPY PAGE_RO @@ -632,23 +637,23 @@ static inline pgste_t pgste_update_all(pte_t *ptep, pgste_t pgste) bits = skey & (_PAGE_CHANGED | _PAGE_REFERENCED); /* Clear page changed & referenced bit in the storage key */ if (bits & _PAGE_CHANGED) - page_set_storage_key(address, skey ^ bits, 1); + page_set_storage_key(address, skey ^ bits, 0); else if (bits) page_reset_referenced(address); /* Transfer page changed & referenced bit to guest bits in pgste */ pgste_val(pgste) |= bits << 48; /* RCP_GR_BIT & RCP_GC_BIT */ /* Get host changed & referenced bits from pgste */ bits |= (pgste_val(pgste) & (RCP_HR_BIT | RCP_HC_BIT)) >> 52; - /* Clear host bits in pgste. */ + /* Transfer page changed & referenced bit to kvm user bits */ + pgste_val(pgste) |= bits << 45; /* KVM_UR_BIT & KVM_UC_BIT */ + /* Clear relevant host bits in pgste. */ pgste_val(pgste) &= ~(RCP_HR_BIT | RCP_HC_BIT); pgste_val(pgste) &= ~(RCP_ACC_BITS | RCP_FP_BIT); /* Copy page access key and fetch protection bit to pgste */ pgste_val(pgste) |= (unsigned long) (skey & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56; - /* Transfer changed and referenced to kvm user bits */ - pgste_val(pgste) |= bits << 45; /* KVM_UR_BIT & KVM_UC_BIT */ - /* Transfer changed & referenced to pte sofware bits */ - pte_val(*ptep) |= bits << 1; /* _PAGE_SWR & _PAGE_SWC */ + /* Transfer referenced bit to pte */ + pte_val(*ptep) |= (bits & _PAGE_REFERENCED) << 1; #endif return pgste; @@ -661,20 +666,25 @@ static inline pgste_t pgste_update_young(pte_t *ptep, pgste_t pgste) if (!pte_present(*ptep)) return pgste; + /* Get referenced bit from storage key */ young = page_reset_referenced(pte_val(*ptep) & PAGE_MASK); - /* Transfer page referenced bit to pte software bit (host view) */ - if (young || (pgste_val(pgste) & RCP_HR_BIT)) + if (young) + pgste_val(pgste) |= RCP_GR_BIT; + /* Get host referenced bit from pgste */ + if (pgste_val(pgste) & RCP_HR_BIT) { + pgste_val(pgste) &= ~RCP_HR_BIT; + young = 1; + } + /* Transfer referenced bit to kvm user bits and pte */ + if (young) { + pgste_val(pgste) |= KVM_UR_BIT; pte_val(*ptep) |= _PAGE_SWR; - /* Clear host referenced bit in pgste. */ - pgste_val(pgste) &= ~RCP_HR_BIT; - /* Transfer page referenced bit to guest bit in pgste */ - pgste_val(pgste) |= (unsigned long) young << 50; /* set RCP_GR_BIT */ + } #endif return pgste; - } -static inline void pgste_set_pte(pte_t *ptep, pgste_t pgste, pte_t entry) +static inline void pgste_set_key(pte_t *ptep, pgste_t pgste, pte_t entry) { #ifdef CONFIG_PGSTE unsigned long address; @@ -688,10 +698,23 @@ static inline void pgste_set_pte(pte_t *ptep, pgste_t pgste, pte_t entry) /* Set page access key and fetch protection bit from pgste */ nkey |= (pgste_val(pgste) & (RCP_ACC_BITS | RCP_FP_BIT)) >> 56; if (okey != nkey) - page_set_storage_key(address, nkey, 1); + page_set_storage_key(address, nkey, 0); #endif } +static inline void pgste_set_pte(pte_t *ptep, pte_t entry) +{ + if (!MACHINE_HAS_ESOP && (pte_val(entry) & _PAGE_SWW)) { + /* + * Without enhanced suppression-on-protection force + * the dirty bit on for all writable ptes. + */ + pte_val(entry) |= _PAGE_SWC; + pte_val(entry) &= ~_PAGE_RO; + } + *ptep = entry; +} + /** * struct gmap_struct - guest address space * @mm: pointer to the parent mm_struct @@ -750,11 +773,14 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, if (mm_has_pgste(mm)) { pgste = pgste_get_lock(ptep); - pgste_set_pte(ptep, pgste, entry); - *ptep = entry; + pgste_set_key(ptep, pgste, entry); + pgste_set_pte(ptep, entry); pgste_set_unlock(ptep, pgste); - } else + } else { + if (!(pte_val(entry) & _PAGE_INVALID) && MACHINE_HAS_EDAT1) + pte_val(entry) |= _PAGE_CO; *ptep = entry; + } } /* @@ -763,16 +789,12 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, */ static inline int pte_write(pte_t pte) { - return (pte_val(pte) & _PAGE_RO) == 0; + return (pte_val(pte) & _PAGE_SWW) != 0; } static inline int pte_dirty(pte_t pte) { -#ifdef CONFIG_PGSTE - if (pte_val(pte) & _PAGE_SWC) - return 1; -#endif - return 0; + return (pte_val(pte) & _PAGE_SWC) != 0; } static inline int pte_young(pte_t pte) @@ -822,11 +844,14 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) { pte_val(pte) &= _PAGE_CHG_MASK; pte_val(pte) |= pgprot_val(newprot); + if ((pte_val(pte) & _PAGE_SWC) && (pte_val(pte) & _PAGE_SWW)) + pte_val(pte) &= ~_PAGE_RO; return pte; } static inline pte_t pte_wrprotect(pte_t pte) { + pte_val(pte) &= ~_PAGE_SWW; /* Do not clobber _PAGE_TYPE_NONE pages! */ if (!(pte_val(pte) & _PAGE_INVALID)) pte_val(pte) |= _PAGE_RO; @@ -835,20 +860,26 @@ static inline pte_t pte_wrprotect(pte_t pte) static inline pte_t pte_mkwrite(pte_t pte) { - pte_val(pte) &= ~_PAGE_RO; + pte_val(pte) |= _PAGE_SWW; + if (pte_val(pte) & _PAGE_SWC) + pte_val(pte) &= ~_PAGE_RO; return pte; } static inline pte_t pte_mkclean(pte_t pte) { -#ifdef CONFIG_PGSTE pte_val(pte) &= ~_PAGE_SWC; -#endif + /* Do not clobber _PAGE_TYPE_NONE pages! */ + if (!(pte_val(pte) & _PAGE_INVALID)) + pte_val(pte) |= _PAGE_RO; return pte; } static inline pte_t pte_mkdirty(pte_t pte) { + pte_val(pte) |= _PAGE_SWC; + if (pte_val(pte) & _PAGE_SWW) + pte_val(pte) &= ~_PAGE_RO; return pte; } @@ -886,10 +917,10 @@ static inline pte_t pte_mkhuge(pte_t pte) pte_val(pte) |= _SEGMENT_ENTRY_INV; } /* - * Clear SW pte bits SWT and SWX, there are no SW bits in a segment - * table entry. + * Clear SW pte bits, there are no SW bits in a segment table entry. */ - pte_val(pte) &= ~(_PAGE_SWT | _PAGE_SWX); + pte_val(pte) &= ~(_PAGE_SWT | _PAGE_SWX | _PAGE_SWC | + _PAGE_SWR | _PAGE_SWW); /* * Also set the change-override bit because we don't need dirty bit * tracking for hugetlbfs pages. @@ -1041,9 +1072,11 @@ static inline void ptep_modify_prot_commit(struct mm_struct *mm, unsigned long address, pte_t *ptep, pte_t pte) { - *ptep = pte; - if (mm_has_pgste(mm)) + if (mm_has_pgste(mm)) { + pgste_set_pte(ptep, pte); pgste_set_unlock(ptep, *(pgste_t *)(ptep + PTRS_PER_PTE)); + } else + *ptep = pte; } #define __HAVE_ARCH_PTEP_CLEAR_FLUSH @@ -1111,10 +1144,13 @@ static inline pte_t ptep_set_wrprotect(struct mm_struct *mm, if (!mm_exclusive(mm)) __ptep_ipte(address, ptep); - *ptep = pte_wrprotect(pte); + pte = pte_wrprotect(pte); - if (mm_has_pgste(mm)) + if (mm_has_pgste(mm)) { + pgste_set_pte(ptep, pte); pgste_set_unlock(ptep, pgste); + } else + *ptep = pte; } return pte; } @@ -1132,10 +1168,12 @@ static inline int ptep_set_access_flags(struct vm_area_struct *vma, pgste = pgste_get_lock(ptep); __ptep_ipte(address, ptep); - *ptep = entry; - if (mm_has_pgste(vma->vm_mm)) + if (mm_has_pgste(vma->vm_mm)) { + pgste_set_pte(ptep, entry); pgste_set_unlock(ptep, pgste); + } else + *ptep = entry; return 1; } @@ -1153,8 +1191,13 @@ static inline pte_t mk_pte_phys(unsigned long physpage, pgprot_t pgprot) static inline pte_t mk_pte(struct page *page, pgprot_t pgprot) { unsigned long physpage = page_to_phys(page); + pte_t __pte = mk_pte_phys(physpage, pgprot); - return mk_pte_phys(physpage, pgprot); + if ((pte_val(__pte) & _PAGE_SWW) && PageDirty(page)) { + pte_val(__pte) |= _PAGE_SWC; + pte_val(__pte) &= ~_PAGE_RO; + } + return __pte; } #define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) @@ -1246,6 +1289,8 @@ static inline int pmd_trans_splitting(pmd_t pmd) static inline void set_pmd_at(struct mm_struct *mm, unsigned long addr, pmd_t *pmdp, pmd_t entry) { + if (!(pmd_val(entry) & _SEGMENT_ENTRY_INV) && MACHINE_HAS_EDAT1) + pmd_val(entry) |= _SEGMENT_ENTRY_CO; *pmdp = entry; } diff --git a/arch/s390/include/asm/sclp.h b/arch/s390/include/asm/sclp.h index 833788693f09..06a136136047 100644 --- a/arch/s390/include/asm/sclp.h +++ b/arch/s390/include/asm/sclp.h @@ -46,7 +46,6 @@ int sclp_cpu_deconfigure(u8 cpu); void sclp_facilities_detect(void); unsigned long long sclp_get_rnmax(void); unsigned long long sclp_get_rzm(void); -u8 sclp_get_fac85(void); int sclp_sdias_blk_count(void); int sclp_sdias_copy(void *dest, int blk_num, int nr_blks); int sclp_chp_configure(struct chp_id chpid); diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h index f69f76b3447a..f6857516e523 100644 --- a/arch/s390/include/asm/setup.h +++ b/arch/s390/include/asm/setup.h @@ -64,13 +64,14 @@ extern unsigned int s390_user_mode; #define MACHINE_FLAG_VM (1UL << 0) #define MACHINE_FLAG_IEEE (1UL << 1) -#define MACHINE_FLAG_CSP (1UL << 3) -#define MACHINE_FLAG_MVPG (1UL << 4) -#define MACHINE_FLAG_DIAG44 (1UL << 5) -#define MACHINE_FLAG_IDTE (1UL << 6) -#define MACHINE_FLAG_DIAG9C (1UL << 7) -#define MACHINE_FLAG_MVCOS (1UL << 8) -#define MACHINE_FLAG_KVM (1UL << 9) +#define MACHINE_FLAG_CSP (1UL << 2) +#define MACHINE_FLAG_MVPG (1UL << 3) +#define MACHINE_FLAG_DIAG44 (1UL << 4) +#define MACHINE_FLAG_IDTE (1UL << 5) +#define MACHINE_FLAG_DIAG9C (1UL << 6) +#define MACHINE_FLAG_MVCOS (1UL << 7) +#define MACHINE_FLAG_KVM (1UL << 8) +#define MACHINE_FLAG_ESOP (1UL << 9) #define MACHINE_FLAG_EDAT1 (1UL << 10) #define MACHINE_FLAG_EDAT2 (1UL << 11) #define MACHINE_FLAG_LPAR (1UL << 12) @@ -84,6 +85,7 @@ extern unsigned int s390_user_mode; #define MACHINE_IS_LPAR (S390_lowcore.machine_flags & MACHINE_FLAG_LPAR) #define MACHINE_HAS_DIAG9C (S390_lowcore.machine_flags & MACHINE_FLAG_DIAG9C) +#define MACHINE_HAS_ESOP (S390_lowcore.machine_flags & MACHINE_FLAG_ESOP) #define MACHINE_HAS_PFMF MACHINE_HAS_EDAT1 #define MACHINE_HAS_HPAGE MACHINE_HAS_EDAT1 diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index f090e819bf71..2923781590a6 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -147,7 +147,7 @@ int kvm_dev_ioctl_check_extension(long ext) r = KVM_MAX_VCPUS; break; case KVM_CAP_S390_COW: - r = sclp_get_fac85() & 0x2; + r = MACHINE_HAS_ESOP; break; default: r = 0; diff --git a/arch/s390/lib/uaccess_pt.c b/arch/s390/lib/uaccess_pt.c index 9017a63dda3d..a70ee84c0241 100644 --- a/arch/s390/lib/uaccess_pt.c +++ b/arch/s390/lib/uaccess_pt.c @@ -50,7 +50,7 @@ static __always_inline unsigned long follow_table(struct mm_struct *mm, ptep = pte_offset_map(pmd, addr); if (!pte_present(*ptep)) return -0x11UL; - if (write && !pte_write(*ptep)) + if (write && (!pte_write(*ptep) || !pte_dirty(*ptep))) return -0x04UL; return (pte_val(*ptep) & PAGE_MASK) + (addr & ~PAGE_MASK); diff --git a/arch/s390/mm/pageattr.c b/arch/s390/mm/pageattr.c index 29ccee3651f4..d21040ed5e59 100644 --- a/arch/s390/mm/pageattr.c +++ b/arch/s390/mm/pageattr.c @@ -127,7 +127,7 @@ void kernel_map_pages(struct page *page, int numpages, int enable) pte_val(*pte) = _PAGE_TYPE_EMPTY; continue; } - *pte = mk_pte_phys(address, __pgprot(_PAGE_TYPE_RW)); + pte_val(*pte) = __pa(address); } } diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index 6ed1426d27c5..79699f46a443 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c @@ -85,11 +85,9 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro) pud_t *pu_dir; pmd_t *pm_dir; pte_t *pt_dir; - pte_t pte; int ret = -ENOMEM; while (address < end) { - pte = mk_pte_phys(address, __pgprot(ro ? _PAGE_RO : 0)); pg_dir = pgd_offset_k(address); if (pgd_none(*pg_dir)) { pu_dir = vmem_pud_alloc(); @@ -101,9 +99,9 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro) #if defined(CONFIG_64BIT) && !defined(CONFIG_DEBUG_PAGEALLOC) if (MACHINE_HAS_EDAT2 && pud_none(*pu_dir) && address && !(address & ~PUD_MASK) && (address + PUD_SIZE <= end)) { - pte_val(pte) |= _REGION3_ENTRY_LARGE; - pte_val(pte) |= _REGION_ENTRY_TYPE_R3; - pud_val(*pu_dir) = pte_val(pte); + pud_val(*pu_dir) = __pa(address) | + _REGION_ENTRY_TYPE_R3 | _REGION3_ENTRY_LARGE | + (ro ? _REGION_ENTRY_RO : 0); address += PUD_SIZE; continue; } @@ -118,8 +116,9 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro) #if defined(CONFIG_64BIT) && !defined(CONFIG_DEBUG_PAGEALLOC) if (MACHINE_HAS_EDAT1 && pmd_none(*pm_dir) && address && !(address & ~PMD_MASK) && (address + PMD_SIZE <= end)) { - pte_val(pte) |= _SEGMENT_ENTRY_LARGE; - pmd_val(*pm_dir) = pte_val(pte); + pmd_val(*pm_dir) = __pa(address) | + _SEGMENT_ENTRY | _SEGMENT_ENTRY_LARGE | + (ro ? _SEGMENT_ENTRY_RO : 0); address += PMD_SIZE; continue; } @@ -132,7 +131,7 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro) } pt_dir = pte_offset_kernel(pm_dir, address); - *pt_dir = pte; + pte_val(*pt_dir) = __pa(address) | (ro ? _PAGE_RO : 0); address += PAGE_SIZE; } ret = 0; @@ -199,7 +198,6 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node) pud_t *pu_dir; pmd_t *pm_dir; pte_t *pt_dir; - pte_t pte; int ret = -ENOMEM; start_addr = (unsigned long) start; @@ -237,9 +235,8 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node) new_page = vmemmap_alloc_block(PMD_SIZE, node); if (!new_page) goto out; - pte = mk_pte_phys(__pa(new_page), PAGE_RW); - pte_val(pte) |= _SEGMENT_ENTRY_LARGE; - pmd_val(*pm_dir) = pte_val(pte); + pmd_val(*pm_dir) = __pa(new_page) | + _SEGMENT_ENTRY | _SEGMENT_ENTRY_LARGE; address = (address + PMD_SIZE) & PMD_MASK; continue; } @@ -260,8 +257,7 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node) new_page =__pa(vmem_alloc_pages(0)); if (!new_page) goto out; - pte = pfn_pte(new_page >> PAGE_SHIFT, PAGE_KERNEL); - *pt_dir = pte; + pte_val(*pt_dir) = __pa(new_page); } address += PAGE_SIZE; } diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index c44d13f607bc..30a2255389e5 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -56,7 +56,6 @@ static int __initdata early_read_info_sccb_valid; u64 sclp_facilities; static u8 sclp_fac84; -static u8 sclp_fac85; static unsigned long long rzm; static unsigned long long rnmax; @@ -131,7 +130,8 @@ void __init sclp_facilities_detect(void) sccb = &early_read_info_sccb; sclp_facilities = sccb->facilities; sclp_fac84 = sccb->fac84; - sclp_fac85 = sccb->fac85; + if (sccb->fac85 & 0x02) + S390_lowcore.machine_flags |= MACHINE_FLAG_ESOP; rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2; rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2; rzm <<= 20; @@ -171,12 +171,6 @@ unsigned long long sclp_get_rzm(void) return rzm; } -u8 sclp_get_fac85(void) -{ - return sclp_fac85; -} -EXPORT_SYMBOL_GPL(sclp_get_fac85); - /* * This function will be called after sclp_facilities_detect(), which gets * called from early.c code. Therefore the sccb should have valid contents. diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index 5cf680a98f9b..bfd87685fc1f 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h @@ -197,16 +197,6 @@ static inline int pmd_same(pmd_t pmd_a, pmd_t pmd_b) #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ #endif -#ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_DIRTY -#define page_test_and_clear_dirty(pfn, mapped) (0) -#endif - -#ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_DIRTY -#define pte_maybe_dirty(pte) pte_dirty(pte) -#else -#define pte_maybe_dirty(pte) (1) -#endif - #ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG #define page_test_and_clear_young(pfn) (0) #endif diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 70473da47b3f..6d53675c2b54 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -303,21 +303,13 @@ static inline void __SetPageUptodate(struct page *page) static inline void SetPageUptodate(struct page *page) { -#ifdef CONFIG_S390 - if (!test_and_set_bit(PG_uptodate, &page->flags)) - page_set_storage_key(page_to_phys(page), PAGE_DEFAULT_KEY, 0); -#else /* * Memory barrier must be issued before setting the PG_uptodate bit, * so that all previous stores issued in order to bring the page * uptodate are actually visible before PageUptodate becomes true. - * - * s390 doesn't need an explicit smp_wmb here because the test and - * set bit already provides full barriers. */ smp_wmb(); set_bit(PG_uptodate, &(page)->flags); -#endif } CLEARPAGEFLAG(Uptodate, uptodate) diff --git a/mm/rmap.c b/mm/rmap.c index 2c78f8cadc95..3d38edffda41 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -1126,7 +1126,6 @@ void page_add_file_rmap(struct page *page) */ void page_remove_rmap(struct page *page) { - struct address_space *mapping = page_mapping(page); bool anon = PageAnon(page); bool locked; unsigned long flags; @@ -1143,29 +1142,6 @@ void page_remove_rmap(struct page *page) if (!atomic_add_negative(-1, &page->_mapcount)) goto out; - /* - * Now that the last pte has gone, s390 must transfer dirty - * flag from storage key to struct page. We can usually skip - * this if the page is anon, so about to be freed; but perhaps - * not if it's in swapcache - there might be another pte slot - * containing the swap entry, but page not yet written to swap. - * - * And we can skip it on file pages, so long as the filesystem - * participates in dirty tracking (note that this is not only an - * optimization but also solves problems caused by dirty flag in - * storage key getting set by a write from inside kernel); but need to - * catch shm and tmpfs and ramfs pages which have been modified since - * creation by read fault. - * - * Note that mapping must be decided above, before decrementing - * mapcount (which luckily provides a barrier): once page is unmapped, - * it could be truncated and page->mapping reset to NULL at any moment. - * Note also that we are relying on page_mapping(page) to set mapping - * to &swapper_space when PageSwapCache(page). - */ - if (mapping && !mapping_cap_account_dirty(mapping) && - page_test_and_clear_dirty(page_to_pfn(page), 1)) - set_page_dirty(page); /* * Hugepages are not counted in NR_ANON_PAGES nor NR_FILE_MAPPED * and not charged by memcg for now. -- cgit v1.2.3 From be7b334b2abde95063742fa66425ea9e1298e1c7 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 5 Feb 2013 12:38:30 +0100 Subject: drivers/input: add couple of missing GENERIC_HARDIRQS dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When removing the !S390 dependency from drivers/input/Kconfig a couple of drivers don't compile because they have a dependency on GENERIC_HARDIRQS. So add the missing dependencies. Fixes e.g. this one: drivers/input/keyboard/lm8323.c: In function ‘lm8323_suspend’: drivers/input/keyboard/lm8323.c:801:2: error: implicit declaration of function ‘irq_set_irq_wake’ [-Werror=implicit-function-declaration] Cc: Dmitry Torokhov Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/input/Kconfig | 2 +- drivers/input/keyboard/Kconfig | 4 ++-- drivers/input/serio/Kconfig | 1 + drivers/input/touchscreen/Kconfig | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 55f7e57d4e42..38b523a1ece0 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -3,7 +3,7 @@ # menu "Input device support" - depends on !S390 && !UML + depends on !UML config INPUT tristate "Generic input layer (needed for keyboard, mouse, ...)" if EXPERT diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 5a240c60342d..a6ccbf078463 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -224,7 +224,7 @@ config KEYBOARD_TCA6416 config KEYBOARD_TCA8418 tristate "TCA8418 Keypad Support" - depends on I2C + depends on I2C && GENERIC_HARDIRQS select INPUT_MATRIXKMAP help This driver implements basic keypad functionality @@ -303,7 +303,7 @@ config KEYBOARD_HP7XX config KEYBOARD_LM8323 tristate "LM8323 keypad chip" - depends on I2C + depends on I2C && GENERIC_HARDIRQS depends on LEDS_CLASS help If you say yes here you get support for the National Semiconductor diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 4a4e182c33e7..560c243bfcaf 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -236,6 +236,7 @@ config SERIO_PS2MULT config SERIO_ARC_PS2 tristate "ARC PS/2 support" + depends on GENERIC_HARDIRQS help Say Y here if you have an ARC FPGA platform with a PS/2 controller in it. diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 515cfe790543..f9a5fd89bc02 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -359,7 +359,7 @@ config TOUCHSCREEN_MCS5000 config TOUCHSCREEN_MMS114 tristate "MELFAS MMS114 touchscreen" - depends on I2C + depends on I2C && GENERIC_HARDIRQS help Say Y here if you have the MELFAS MMS114 touchscreen controller chip in your system. -- cgit v1.2.3 From 29bde2d93289d86d5caf24e75c14559292a42756 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 5 Feb 2013 12:48:29 +0100 Subject: drivers/gpio: add missing GENERIC_HARDIRQ dependency The VIA VX855/VX875 GPIO and RDC R-321x GPIO support drivers select MFD_CORE which itself depends on GENERIC_HARDIRQ support. So add this dependency to these two drivers as well to prevent selection of MFD_CORE. Cc: Grant Likely Cc: Samuel Ortiz Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/gpio/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 682de754d63f..40da0ef3a31b 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -277,7 +277,7 @@ config GPIO_ICH config GPIO_VX855 tristate "VIA VX855/VX875 GPIO" - depends on PCI + depends on PCI && GENERIC_HARDIRQS select MFD_CORE select MFD_VX855 help @@ -599,7 +599,7 @@ config GPIO_TIMBERDALE config GPIO_RDC321X tristate "RDC R-321x GPIO support" - depends on PCI + depends on PCI && GENERIC_HARDIRQS select MFD_CORE select MFD_RDC321X help -- cgit v1.2.3 From c7886b18273b07042e25e8d3ba5c983837b84123 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 13 Feb 2013 00:56:13 +0900 Subject: gpio: em: Use irq_domain_add_simple() to fix runtime error Adjust the gpio-em.c driver to reconsider the pdata->irq_base variable. Non-DT board code like for instance board-kzm9d.c needs to operate of a static IRQ range for platform devices. So this patch is updating the code to make use of the function irq_domain_add_simple() instead of irq_domain_add_linear(). Fixes a EMEV2 / KZM9D runtime error caused by the following commit: 7385500 gpio/em: convert to linear IRQ domain Cc: stable@kernel.org Signed-off-by: Magnus Damm Tested-by: Simon Horman Reported-by: Simon Horman Signed-off-by: Linus Walleij --- drivers/gpio/gpio-em.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c index bdc8302e711a..deca78f99316 100644 --- a/drivers/gpio/gpio-em.c +++ b/drivers/gpio/gpio-em.c @@ -299,8 +299,9 @@ static int em_gio_probe(struct platform_device *pdev) irq_chip->irq_set_type = em_gio_irq_set_type; irq_chip->flags = IRQCHIP_SKIP_SET_WAKE; - p->irq_domain = irq_domain_add_linear(pdev->dev.of_node, + p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, pdata->number_of_pins, + pdata->irq_base, &em_gio_irq_domain_ops, p); if (!p->irq_domain) { ret = -ENXIO; -- cgit v1.2.3 From 5641ade41f7c7d16e614e25ce3315e04f1bacd33 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 14 Feb 2013 10:45:31 -0700 Subject: vfio-pci: Enable PCIe extended capabilities on v1 Even PCIe 1.x had extended config space. Signed-off-by: Alex Williamson --- drivers/vfio/pci/vfio_pci_config.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c index 8b8f7d11e102..c975d91e1ccc 100644 --- a/drivers/vfio/pci/vfio_pci_config.c +++ b/drivers/vfio/pci/vfio_pci_config.c @@ -985,12 +985,12 @@ static int vfio_cap_len(struct vfio_pci_device *vdev, u8 cap, u8 pos) if (ret) return pcibios_err_to_errno(ret); + vdev->extended_caps = true; + if ((word & PCI_EXP_FLAGS_VERS) == 1) return PCI_CAP_EXP_ENDPOINT_SIZEOF_V1; - else { - vdev->extended_caps = true; + else return PCI_CAP_EXP_ENDPOINT_SIZEOF_V2; - } case PCI_CAP_ID_HT: ret = pci_read_config_byte(pdev, pos + 3, &byte); if (ret) -- cgit v1.2.3 From 5b279a11d32998aad1e45fe9de225302b6a8e8ba Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 14 Feb 2013 14:02:12 -0700 Subject: vfio-pci: Cleanup read/write functions The read and write functions are nearly identical, combine them and convert to a switch statement. This also makes it easy to narrow the scope of when we use the io/mem accessors in case new regions are added. Signed-off-by: Alex Williamson --- drivers/vfio/pci/vfio_pci.c | 59 ++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index b28e66c4376a..469e110d7ea6 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -366,8 +366,8 @@ static long vfio_pci_ioctl(void *device_data, return -ENOTTY; } -static ssize_t vfio_pci_read(void *device_data, char __user *buf, - size_t count, loff_t *ppos) +static ssize_t vfio_pci_rw(void *device_data, char __user *buf, + size_t count, loff_t *ppos, bool iswrite) { unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); struct vfio_pci_device *vdev = device_data; @@ -376,42 +376,41 @@ static ssize_t vfio_pci_read(void *device_data, char __user *buf, if (index >= VFIO_PCI_NUM_REGIONS) return -EINVAL; - if (index == VFIO_PCI_CONFIG_REGION_INDEX) - return vfio_pci_config_readwrite(vdev, buf, count, ppos, false); - else if (index == VFIO_PCI_ROM_REGION_INDEX) - return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false); - else if (pci_resource_flags(pdev, index) & IORESOURCE_IO) - return vfio_pci_io_readwrite(vdev, buf, count, ppos, false); - else if (pci_resource_flags(pdev, index) & IORESOURCE_MEM) + switch (index) { + case VFIO_PCI_CONFIG_REGION_INDEX: + return vfio_pci_config_readwrite(vdev, buf, count, + ppos, iswrite); + case VFIO_PCI_ROM_REGION_INDEX: + if (iswrite) + return -EINVAL; return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false); + case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX: + { + unsigned long flags = pci_resource_flags(pdev, index); + + if (flags & IORESOURCE_IO) + return vfio_pci_io_readwrite(vdev, buf, count, + ppos, iswrite); + if (flags & IORESOURCE_MEM) + return vfio_pci_mem_readwrite(vdev, buf, count, + ppos, iswrite); + } + } + return -EINVAL; } +static ssize_t vfio_pci_read(void *device_data, char __user *buf, + size_t count, loff_t *ppos) +{ + return vfio_pci_rw(device_data, buf, count, ppos, false); +} + static ssize_t vfio_pci_write(void *device_data, const char __user *buf, size_t count, loff_t *ppos) { - unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); - struct vfio_pci_device *vdev = device_data; - struct pci_dev *pdev = vdev->pdev; - - if (index >= VFIO_PCI_NUM_REGIONS) - return -EINVAL; - - if (index == VFIO_PCI_CONFIG_REGION_INDEX) - return vfio_pci_config_readwrite(vdev, (char __user *)buf, - count, ppos, true); - else if (index == VFIO_PCI_ROM_REGION_INDEX) - return -EINVAL; - else if (pci_resource_flags(pdev, index) & IORESOURCE_IO) - return vfio_pci_io_readwrite(vdev, (char __user *)buf, - count, ppos, true); - else if (pci_resource_flags(pdev, index) & IORESOURCE_MEM) { - return vfio_pci_mem_readwrite(vdev, (char __user *)buf, - count, ppos, true); - } - - return -EINVAL; + return vfio_pci_rw(device_data, buf, count, ppos, true); } static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma) -- cgit v1.2.3 From 906ee99dd2a5c819c1171ce5eaf6c080c027e58c Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 14 Feb 2013 14:02:12 -0700 Subject: vfio-pci: Cleanup BAR access We can actually handle MMIO and I/O port from the same access function since PCI already does abstraction of this. The ROM BAR only requires a minor difference, so it gets included too. vfio_pci_config_readwrite gets renamed for consistency. Signed-off-by: Alex Williamson --- drivers/vfio/pci/vfio_pci.c | 26 ++-- drivers/vfio/pci/vfio_pci_config.c | 5 +- drivers/vfio/pci/vfio_pci_private.h | 15 +-- drivers/vfio/pci/vfio_pci_rdwr.c | 234 +++++++++++------------------------- 4 files changed, 90 insertions(+), 190 deletions(-) (limited to 'drivers') diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index 469e110d7ea6..bb8c8c2be960 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -371,31 +371,21 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf, { unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); struct vfio_pci_device *vdev = device_data; - struct pci_dev *pdev = vdev->pdev; if (index >= VFIO_PCI_NUM_REGIONS) return -EINVAL; switch (index) { case VFIO_PCI_CONFIG_REGION_INDEX: - return vfio_pci_config_readwrite(vdev, buf, count, - ppos, iswrite); + return vfio_pci_config_rw(vdev, buf, count, ppos, iswrite); + case VFIO_PCI_ROM_REGION_INDEX: if (iswrite) return -EINVAL; - return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false); + return vfio_pci_bar_rw(vdev, buf, count, ppos, false); case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX: - { - unsigned long flags = pci_resource_flags(pdev, index); - - if (flags & IORESOURCE_IO) - return vfio_pci_io_readwrite(vdev, buf, count, - ppos, iswrite); - if (flags & IORESOURCE_MEM) - return vfio_pci_mem_readwrite(vdev, buf, count, - ppos, iswrite); - } + return vfio_pci_bar_rw(vdev, buf, count, ppos, iswrite); } return -EINVAL; @@ -404,13 +394,19 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf, static ssize_t vfio_pci_read(void *device_data, char __user *buf, size_t count, loff_t *ppos) { + if (!count) + return 0; + return vfio_pci_rw(device_data, buf, count, ppos, false); } static ssize_t vfio_pci_write(void *device_data, const char __user *buf, size_t count, loff_t *ppos) { - return vfio_pci_rw(device_data, buf, count, ppos, true); + if (!count) + return 0; + + return vfio_pci_rw(device_data, (char __user *)buf, count, ppos, true); } static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma) diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c index c975d91e1ccc..f1dde2c0fe99 100644 --- a/drivers/vfio/pci/vfio_pci_config.c +++ b/drivers/vfio/pci/vfio_pci_config.c @@ -1501,9 +1501,8 @@ static ssize_t vfio_config_do_rw(struct vfio_pci_device *vdev, char __user *buf, return ret; } -ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev, - char __user *buf, size_t count, - loff_t *ppos, bool iswrite) +ssize_t vfio_pci_config_rw(struct vfio_pci_device *vdev, char __user *buf, + size_t count, loff_t *ppos, bool iswrite) { size_t done = 0; int ret = 0; diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h index 611827cba8cd..00d19b953ce4 100644 --- a/drivers/vfio/pci/vfio_pci_private.h +++ b/drivers/vfio/pci/vfio_pci_private.h @@ -70,15 +70,12 @@ extern int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, uint32_t flags, unsigned index, unsigned start, unsigned count, void *data); -extern ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev, - char __user *buf, size_t count, - loff_t *ppos, bool iswrite); -extern ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, - char __user *buf, size_t count, - loff_t *ppos, bool iswrite); -extern ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev, - char __user *buf, size_t count, - loff_t *ppos, bool iswrite); +extern ssize_t vfio_pci_config_rw(struct vfio_pci_device *vdev, + char __user *buf, size_t count, + loff_t *ppos, bool iswrite); + +extern ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf, + size_t count, loff_t *ppos, bool iswrite); extern int vfio_pci_init_perm_bits(void); extern void vfio_pci_uninit_perm_bits(void); diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c index f72323ef618f..e9d78eb91ed7 100644 --- a/drivers/vfio/pci/vfio_pci_rdwr.c +++ b/drivers/vfio/pci/vfio_pci_rdwr.c @@ -20,250 +20,158 @@ #include "vfio_pci_private.h" -/* I/O Port BAR access */ -ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev, char __user *buf, - size_t count, loff_t *ppos, bool iswrite) +/* + * Read or write from an __iomem region (MMIO or I/O port) with an excluded + * range which is inaccessible. The excluded range drops writes and fills + * reads with -1. This is intended for handling MSI-X vector tables and + * leftover space for ROM BARs. + */ +static ssize_t do_io_rw(void __iomem *io, char __user *buf, + loff_t off, size_t count, size_t x_start, + size_t x_end, bool iswrite) { - struct pci_dev *pdev = vdev->pdev; - loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; - int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos); - void __iomem *io; - size_t done = 0; - - if (!pci_resource_start(pdev, bar)) - return -EINVAL; - - if (pos + count > pci_resource_len(pdev, bar)) - return -EINVAL; - - if (!vdev->barmap[bar]) { - int ret; - - ret = pci_request_selected_regions(pdev, 1 << bar, "vfio"); - if (ret) - return ret; - - vdev->barmap[bar] = pci_iomap(pdev, bar, 0); - - if (!vdev->barmap[bar]) { - pci_release_selected_regions(pdev, 1 << bar); - return -EINVAL; - } - } - - io = vdev->barmap[bar]; + ssize_t done = 0; while (count) { - int filled; + size_t fillable, filled; + + if (off < x_start) + fillable = min(count, (size_t)(x_start - off)); + else if (off >= x_end) + fillable = count; + else + fillable = 0; - if (count >= 3 && !(pos % 4)) { + if (fillable >= 4 && !(off % 4)) { __le32 val; if (iswrite) { if (copy_from_user(&val, buf, 4)) return -EFAULT; - iowrite32(le32_to_cpu(val), io + pos); + iowrite32(le32_to_cpu(val), io + off); } else { - val = cpu_to_le32(ioread32(io + pos)); + val = cpu_to_le32(ioread32(io + off)); if (copy_to_user(buf, &val, 4)) return -EFAULT; } filled = 4; - - } else if ((pos % 2) == 0 && count >= 2) { + } else if (fillable >= 2 && !(off % 2)) { __le16 val; if (iswrite) { if (copy_from_user(&val, buf, 2)) return -EFAULT; - iowrite16(le16_to_cpu(val), io + pos); + iowrite16(le16_to_cpu(val), io + off); } else { - val = cpu_to_le16(ioread16(io + pos)); + val = cpu_to_le16(ioread16(io + off)); if (copy_to_user(buf, &val, 2)) return -EFAULT; } filled = 2; - } else { + } else if (fillable) { u8 val; if (iswrite) { if (copy_from_user(&val, buf, 1)) return -EFAULT; - iowrite8(val, io + pos); + iowrite8(val, io + off); } else { - val = ioread8(io + pos); + val = ioread8(io + off); if (copy_to_user(buf, &val, 1)) return -EFAULT; } filled = 1; + } else { + /* Fill reads with -1, drop writes */ + filled = min(count, (size_t)(x_end - off)); + if (!iswrite) { + u8 val = 0xFF; + size_t i; + + for (i = 0; i < filled; i++) + if (copy_to_user(buf + i, &val, 1)) + return -EFAULT; + } } count -= filled; done += filled; + off += filled; buf += filled; - pos += filled; } - *ppos += done; - return done; } -/* - * MMIO BAR access - * We handle two excluded ranges here as well, if the user tries to read - * the ROM beyond what PCI tells us is available or the MSI-X table region, - * we return 0xFF and writes are dropped. - */ -ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, char __user *buf, - size_t count, loff_t *ppos, bool iswrite) +ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf, + size_t count, loff_t *ppos, bool iswrite) { struct pci_dev *pdev = vdev->pdev; loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos); - void __iomem *io; + size_t x_start = 0, x_end = 0; resource_size_t end; - size_t done = 0; - size_t x_start = 0, x_end = 0; /* excluded range */ + void __iomem *io; + ssize_t done; if (!pci_resource_start(pdev, bar)) return -EINVAL; end = pci_resource_len(pdev, bar); - if (pos > end) + if (pos >= end) return -EINVAL; - if (pos == end) - return 0; - - if (pos + count > end) - count = end - pos; + count = min(count, (size_t)(end - pos)); if (bar == PCI_ROM_RESOURCE) { + /* + * The ROM can fill less space than the BAR, so we start the + * excluded range at the end of the actual ROM. This makes + * filling large ROM BARs much faster. + */ io = pci_map_rom(pdev, &x_start); + if (!io) + return -ENOMEM; x_end = end; - } else { - if (!vdev->barmap[bar]) { - int ret; - - ret = pci_request_selected_regions(pdev, 1 << bar, - "vfio"); - if (ret) - return ret; + } else if (!vdev->barmap[bar]) { + int ret; - vdev->barmap[bar] = pci_iomap(pdev, bar, 0); + ret = pci_request_selected_regions(pdev, 1 << bar, "vfio"); + if (ret) + return ret; - if (!vdev->barmap[bar]) { - pci_release_selected_regions(pdev, 1 << bar); - return -EINVAL; - } + io = pci_iomap(pdev, bar, 0); + if (!io) { + pci_release_selected_regions(pdev, 1 << bar); + return -ENOMEM; } + vdev->barmap[bar] = io; + } else io = vdev->barmap[bar]; - if (bar == vdev->msix_bar) { - x_start = vdev->msix_offset; - x_end = vdev->msix_offset + vdev->msix_size; - } + if (bar == vdev->msix_bar) { + x_start = vdev->msix_offset; + x_end = vdev->msix_offset + vdev->msix_size; } - if (!io) - return -EINVAL; - - while (count) { - size_t fillable, filled; - - if (pos < x_start) - fillable = x_start - pos; - else if (pos >= x_end) - fillable = end - pos; - else - fillable = 0; - - if (fillable >= 4 && !(pos % 4) && (count >= 4)) { - __le32 val; - - if (iswrite) { - if (copy_from_user(&val, buf, 4)) - goto out; - - iowrite32(le32_to_cpu(val), io + pos); - } else { - val = cpu_to_le32(ioread32(io + pos)); - - if (copy_to_user(buf, &val, 4)) - goto out; - } - - filled = 4; - } else if (fillable >= 2 && !(pos % 2) && (count >= 2)) { - __le16 val; - - if (iswrite) { - if (copy_from_user(&val, buf, 2)) - goto out; - - iowrite16(le16_to_cpu(val), io + pos); - } else { - val = cpu_to_le16(ioread16(io + pos)); - - if (copy_to_user(buf, &val, 2)) - goto out; - } - - filled = 2; - } else if (fillable) { - u8 val; - - if (iswrite) { - if (copy_from_user(&val, buf, 1)) - goto out; - - iowrite8(val, io + pos); - } else { - val = ioread8(io + pos); - - if (copy_to_user(buf, &val, 1)) - goto out; - } - - filled = 1; - } else { - /* Drop writes, fill reads with FF */ - filled = min((size_t)(x_end - pos), count); - if (!iswrite) { - char val = 0xFF; - size_t i; + done = do_io_rw(io, buf, pos, count, x_start, x_end, iswrite); - for (i = 0; i < filled; i++) { - if (put_user(val, buf + i)) - goto out; - } - } + if (done >= 0) + *ppos += done; - } - - count -= filled; - done += filled; - buf += filled; - pos += filled; - } - - *ppos += done; - -out: if (bar == PCI_ROM_RESOURCE) pci_unmap_rom(pdev, io); - return count ? -EFAULT : done; + return done; } -- cgit v1.2.3 From e014e9444aedc365742d533e1443b22470cc67b9 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 14 Feb 2013 14:02:13 -0700 Subject: vfio: Protect vfio_dev_present against device_del vfio_dev_present is meant to give us a wait_event callback so that we can block removing a device from vfio until it becomes unused. The root of this check depends on being able to get the iommu group from the device. Unfortunately if the BUS_NOTIFY_DEL_DEVICE notifier has fired then the device-group reference is no longer searchable and we fail the lookup. We don't need to go to such extents for this though. We have a reference to the device, from which we can acquire a reference to the group. We can then use the group reference to search for the device and properly block removal. Signed-off-by: Alex Williamson --- drivers/vfio/vfio.c | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 12c264d3b058..8e6dcecbc407 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -642,33 +642,16 @@ int vfio_add_group_dev(struct device *dev, } EXPORT_SYMBOL_GPL(vfio_add_group_dev); -/* Test whether a struct device is present in our tracking */ -static bool vfio_dev_present(struct device *dev) +/* Given a referenced group, check if it contains the device */ +static bool vfio_dev_present(struct vfio_group *group, struct device *dev) { - struct iommu_group *iommu_group; - struct vfio_group *group; struct vfio_device *device; - iommu_group = iommu_group_get(dev); - if (!iommu_group) - return false; - - group = vfio_group_get_from_iommu(iommu_group); - if (!group) { - iommu_group_put(iommu_group); - return false; - } - device = vfio_group_get_device(group, dev); - if (!device) { - vfio_group_put(group); - iommu_group_put(iommu_group); + if (!device) return false; - } vfio_device_put(device); - vfio_group_put(group); - iommu_group_put(iommu_group); return true; } @@ -682,10 +665,18 @@ void *vfio_del_group_dev(struct device *dev) struct iommu_group *iommu_group = group->iommu_group; void *device_data = device->device_data; + /* + * The group exists so long as we have a device reference. Get + * a group reference and use it to scan for the device going away. + */ + vfio_group_get(group); + vfio_device_put(device); /* TODO send a signal to encourage this to be released */ - wait_event(vfio.release_q, !vfio_dev_present(dev)); + wait_event(vfio.release_q, !vfio_dev_present(group, dev)); + + vfio_group_put(group); iommu_group_put(iommu_group); -- cgit v1.2.3 From 2b489a45f63102205cece37057c21f6fa66f6ce4 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 14 Feb 2013 14:02:13 -0700 Subject: vfio: whitelist pcieport pcieport does nice things like manage AER and we know it doesn't do DMA or expose any user accessible devices on the host. It also keeps the Memory, I/O, and Busmaster bits enabled, which is pretty handy when trying to use anyting below it. Devices owned by pcieport cannot be given to users via vfio, but we can tolerate them not being owned by vfio-pci. Signed-off-by: Alex Williamson --- drivers/vfio/vfio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 8e6dcecbc407..28e2d5b2c0c7 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -442,7 +442,7 @@ static struct vfio_device *vfio_group_get_device(struct vfio_group *group, * a device. It's not always practical to leave a device within a group * driverless as it could get re-bound to something unsafe. */ -static const char * const vfio_driver_whitelist[] = { "pci-stub" }; +static const char * const vfio_driver_whitelist[] = { "pci-stub", "pcieport" }; static bool vfio_whitelisted_driver(struct device_driver *drv) { -- cgit v1.2.3 From 55abf8df0aa080eb474f7f46337503351890b361 Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:11:50 +0000 Subject: RDMA/cxgb4: Abort connections that receive unexpected streaming mode data This error means the RDMA connection was knocked out of RDMA mode, probably due to an error on the connection. Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index c13745cde7fa..9cab6a6eb96a 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -1391,30 +1391,31 @@ static int rx_data(struct c4iw_dev *dev, struct sk_buff *skb) skb_pull(skb, sizeof(*hdr)); skb_trim(skb, dlen); - ep->rcv_seq += dlen; - BUG_ON(ep->rcv_seq != (ntohl(hdr->seq) + dlen)); - /* update RX credits */ update_rx_credits(ep, dlen); switch (state_read(&ep->com)) { case MPA_REQ_SENT: + ep->rcv_seq += dlen; process_mpa_reply(ep, skb); break; case MPA_REQ_WAIT: + ep->rcv_seq += dlen; process_mpa_request(ep, skb); break; - case MPA_REP_SENT: - break; default: pr_err("%s Unexpected streaming data." \ " ep %p state %d tid %u status %d\n", __func__, ep, state_read(&ep->com), ep->hwtid, status); - /* - * The ep will timeout and inform the ULP of the failure. - * See ep_timeout(). - */ + if (ep->com.qp) { + struct c4iw_qp_attributes attrs; + + attrs.next_state = C4IW_QP_STATE_ERROR; + c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp, + C4IW_QP_ATTR_NEXT_STATE, &attrs, 1); + } + c4iw_ep_disconnect(ep, 1, GFP_KERNEL); break; } return 0; -- cgit v1.2.3 From 91e9c07195032bbde47489b8b423053cff5f413d Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:11:51 +0000 Subject: RDMA/cxgb4: Abort connections when moving to ERROR state If a FINI operation fails, then we need to ABORT instead of CLOSE. Also, if we ABORT due to unexpected STREAMING data, then wake up anybody blocked in FINI... Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 1 + drivers/infiniband/hw/cxgb4/qp.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 9cab6a6eb96a..3dce47a63709 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -1438,6 +1438,7 @@ static int abort_rpl(struct c4iw_dev *dev, struct sk_buff *skb) mutex_lock(&ep->com.mutex); switch (ep->com.state) { case ABORTING: + c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET); __state_set(&ep->com, DEAD); release = 1; break; diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c index 05bfe53bff64..17ba4f8bc12d 100644 --- a/drivers/infiniband/hw/cxgb4/qp.c +++ b/drivers/infiniband/hw/cxgb4/qp.c @@ -1383,6 +1383,7 @@ err: qhp->ep = NULL; set_state(qhp, C4IW_QP_STATE_ERROR); free = 1; + abort = 1; wake_up(&qhp->wait); BUG_ON(!ep); flush_qp(qhp); -- cgit v1.2.3 From 1557967bf921e787f0c9236c2899603d85f44d31 Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:11:52 +0000 Subject: RDMA/cxgb4: Display streaming mode error only if detected in RTS With later firmware, the chances of getting streaming mode data after we exit RTS is likely, so we don't need to warn for it. The only real case where we don't expect it is when the QP is in RTS. Move QP to ERROR when streaming mode data received. Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 3dce47a63709..31d1fac605d3 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -1403,21 +1403,23 @@ static int rx_data(struct c4iw_dev *dev, struct sk_buff *skb) ep->rcv_seq += dlen; process_mpa_request(ep, skb); break; - default: - pr_err("%s Unexpected streaming data." \ - " ep %p state %d tid %u status %d\n", - __func__, ep, state_read(&ep->com), ep->hwtid, status); - - if (ep->com.qp) { - struct c4iw_qp_attributes attrs; - - attrs.next_state = C4IW_QP_STATE_ERROR; - c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp, - C4IW_QP_ATTR_NEXT_STATE, &attrs, 1); - } + case FPDU_MODE: { + struct c4iw_qp_attributes attrs; + BUG_ON(!ep->com.qp); + if (ep->com.qp->attr.state == C4IW_QP_STATE_RTS) + pr_err("%s Unexpected streaming data." \ + " ep %p state %d tid %u status %d\n", + __func__, ep, state_read(&ep->com), + ep->hwtid, status); + attrs.next_state = C4IW_QP_STATE_ERROR; + c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp, + C4IW_QP_ATTR_NEXT_STATE, &attrs, 1); c4iw_ep_disconnect(ep, 1, GFP_KERNEL); break; } + default: + break; + } return 0; } -- cgit v1.2.3 From 325abead6cc7ef50572c53b1adc4d2442234b50f Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:11:53 +0000 Subject: RDMA/cxgb4: Keep QP referenced until TID released The driver is currently releasing the last ref on the QP too early. This can cause bus errors due to HW still fetching WRs from the HW queue. The fix is to keep a qp ref until we release the HW TID. Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 20 ++++++++++++++++---- drivers/infiniband/hw/cxgb4/iw_cxgb4.h | 1 + 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 31d1fac605d3..ebcdb3ff0cf4 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -143,6 +143,18 @@ static void connect_reply_upcall(struct c4iw_ep *ep, int status); static LIST_HEAD(timeout_list); static spinlock_t timeout_lock; +static void deref_qp(struct c4iw_ep *ep) +{ + c4iw_qp_rem_ref(&ep->com.qp->ibqp); + clear_bit(QP_REFERENCED, &ep->com.flags); +} + +static void ref_qp(struct c4iw_ep *ep) +{ + set_bit(QP_REFERENCED, &ep->com.flags); + c4iw_qp_add_ref(&ep->com.qp->ibqp); +} + static void start_ep_timer(struct c4iw_ep *ep) { PDBG("%s ep %p\n", __func__, ep); @@ -271,6 +283,8 @@ void _c4iw_free_ep(struct kref *kref) ep = container_of(kref, struct c4iw_ep, com.kref); PDBG("%s ep %p state %s\n", __func__, ep, states[state_read(&ep->com)]); + if (test_bit(QP_REFERENCED, &ep->com.flags)) + deref_qp(ep); if (test_bit(RELEASE_RESOURCES, &ep->com.flags)) { cxgb4_remove_tid(ep->com.dev->rdev.lldi.tids, 0, ep->hwtid); dst_release(ep->dst); @@ -863,7 +877,6 @@ static void close_complete_upcall(struct c4iw_ep *ep) ep->com.cm_id->event_handler(ep->com.cm_id, &event); ep->com.cm_id->rem_ref(ep->com.cm_id); ep->com.cm_id = NULL; - ep->com.qp = NULL; set_bit(CLOSE_UPCALL, &ep->com.history); } } @@ -906,7 +919,6 @@ static void peer_abort_upcall(struct c4iw_ep *ep) ep->com.cm_id->event_handler(ep->com.cm_id, &event); ep->com.cm_id->rem_ref(ep->com.cm_id); ep->com.cm_id = NULL; - ep->com.qp = NULL; set_bit(ABORT_UPCALL, &ep->com.history); } } @@ -946,7 +958,6 @@ static void connect_reply_upcall(struct c4iw_ep *ep, int status) if (status < 0) { ep->com.cm_id->rem_ref(ep->com.cm_id); ep->com.cm_id = NULL; - ep->com.qp = NULL; } } @@ -2434,6 +2445,7 @@ int c4iw_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) cm_id->add_ref(cm_id); ep->com.cm_id = cm_id; ep->com.qp = qp; + ref_qp(ep); /* bind QP to EP and move to RTS */ attrs.mpa_attr = ep->mpa_attr; @@ -2464,7 +2476,6 @@ int c4iw_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) return 0; err1: ep->com.cm_id = NULL; - ep->com.qp = NULL; cm_id->rem_ref(cm_id); err: c4iw_put_ep(&ep->com); @@ -2505,6 +2516,7 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) ep->com.cm_id = cm_id; ep->com.qp = get_qhp(dev, conn_param->qpn); BUG_ON(!ep->com.qp); + ref_qp(ep); PDBG("%s qpn 0x%x qp %p cm_id %p\n", __func__, conn_param->qpn, ep->com.qp, cm_id); diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h index 9c1644fb0259..0aaaa0e81f29 100644 --- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h +++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h @@ -716,6 +716,7 @@ enum c4iw_ep_flags { ABORT_REQ_IN_PROGRESS = 1, RELEASE_RESOURCES = 2, CLOSE_SENT = 3, + QP_REFERENCED = 5, }; enum c4iw_ep_history { -- cgit v1.2.3 From 04236df2a5b1e9881c90f2d1f63fbee3659297a7 Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:11:54 +0000 Subject: RDMA/cxgb4: Always log async errors Log AEs even if the QP isn't in RTS. It is useful information. Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 6 +++--- drivers/infiniband/hw/cxgb4/ev.c | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index ebcdb3ff0cf4..5989991e31a1 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -1419,9 +1419,9 @@ static int rx_data(struct c4iw_dev *dev, struct sk_buff *skb) BUG_ON(!ep->com.qp); if (ep->com.qp->attr.state == C4IW_QP_STATE_RTS) pr_err("%s Unexpected streaming data." \ - " ep %p state %d tid %u status %d\n", - __func__, ep, state_read(&ep->com), - ep->hwtid, status); + " qpid %u ep %p state %d tid %u status %d\n", + __func__, ep->com.qp->wq.sq.qid, ep, + state_read(&ep->com), ep->hwtid, status); attrs.next_state = C4IW_QP_STATE_ERROR; c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp, C4IW_QP_ATTR_NEXT_STATE, &attrs, 1); diff --git a/drivers/infiniband/hw/cxgb4/ev.c b/drivers/infiniband/hw/cxgb4/ev.c index cf2f6b47617a..1a840b2211dd 100644 --- a/drivers/infiniband/hw/cxgb4/ev.c +++ b/drivers/infiniband/hw/cxgb4/ev.c @@ -46,9 +46,11 @@ static void post_qp_event(struct c4iw_dev *dev, struct c4iw_cq *chp, if ((qhp->attr.state == C4IW_QP_STATE_ERROR) || (qhp->attr.state == C4IW_QP_STATE_TERMINATE)) { - PDBG("%s AE received after RTS - " - "qp state %d qpid 0x%x status 0x%x\n", __func__, - qhp->attr.state, qhp->wq.sq.qid, CQE_STATUS(err_cqe)); + pr_err("%s AE after RTS - qpid 0x%x opcode %d status 0x%x "\ + "type %d wrid.hi 0x%x wrid.lo 0x%x\n", + __func__, CQE_QPID(err_cqe), CQE_OPCODE(err_cqe), + CQE_STATUS(err_cqe), CQE_TYPE(err_cqe), + CQE_WRID_HI(err_cqe), CQE_WRID_LOW(err_cqe)); return; } -- cgit v1.2.3 From e8e5b9278ba0502ada73b8b94b8498cc19def743 Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:11:55 +0000 Subject: RDMA/cxgb4: Only log rx_data warnings if cpl status is non-zero With newer firmware, we can get streaming data due to connection errors before the driver moves the QP out of RTS. Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 5989991e31a1..51ceb618beb2 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -1417,7 +1417,7 @@ static int rx_data(struct c4iw_dev *dev, struct sk_buff *skb) case FPDU_MODE: { struct c4iw_qp_attributes attrs; BUG_ON(!ep->com.qp); - if (ep->com.qp->attr.state == C4IW_QP_STATE_RTS) + if (status) pr_err("%s Unexpected streaming data." \ " qpid %u ep %p state %d tid %u status %d\n", __func__, ep->com.qp->wq.sq.qid, ep, -- cgit v1.2.3 From 1ec779cc29238e6f4d315bff53cd36165819bfd5 Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:11:56 +0000 Subject: RDMA/cxgb4: Fix endpoint timeout race condition The endpoint timeout logic had a race that could cause an endpoint object to be freed while it was still on the timedout list. This can happen if the timer is stopped after it had fired, but before the timedout thread processed the endpoint timeout. Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 29 ++++++++++++++++------------- drivers/infiniband/hw/cxgb4/iw_cxgb4.h | 1 + 2 files changed, 17 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 51ceb618beb2..ab5b4dd39dec 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -159,10 +159,12 @@ static void start_ep_timer(struct c4iw_ep *ep) { PDBG("%s ep %p\n", __func__, ep); if (timer_pending(&ep->timer)) { - PDBG("%s stopped / restarted timer ep %p\n", __func__, ep); - del_timer_sync(&ep->timer); - } else - c4iw_get_ep(&ep->com); + pr_err("%s timer already started! ep %p\n", + __func__, ep); + return; + } + clear_bit(TIMEOUT, &ep->com.flags); + c4iw_get_ep(&ep->com); ep->timer.expires = jiffies + ep_timeout_secs * HZ; ep->timer.data = (unsigned long)ep; ep->timer.function = ep_timeout; @@ -171,14 +173,10 @@ static void start_ep_timer(struct c4iw_ep *ep) static void stop_ep_timer(struct c4iw_ep *ep) { - PDBG("%s ep %p\n", __func__, ep); - if (!timer_pending(&ep->timer)) { - WARN(1, "%s timer stopped when its not running! " - "ep %p state %u\n", __func__, ep, ep->com.state); - return; - } + PDBG("%s ep %p stopping\n", __func__, ep); del_timer_sync(&ep->timer); - c4iw_put_ep(&ep->com); + if (!test_and_set_bit(TIMEOUT, &ep->com.flags)) + c4iw_put_ep(&ep->com); } static int c4iw_l2t_send(struct c4iw_rdev *rdev, struct sk_buff *skb, @@ -3191,11 +3189,16 @@ static DECLARE_WORK(skb_work, process_work); static void ep_timeout(unsigned long arg) { struct c4iw_ep *ep = (struct c4iw_ep *)arg; + int kickit = 0; spin_lock(&timeout_lock); - list_add_tail(&ep->entry, &timeout_list); + if (!test_and_set_bit(TIMEOUT, &ep->com.flags)) { + list_add_tail(&ep->entry, &timeout_list); + kickit = 1; + } spin_unlock(&timeout_lock); - queue_work(workq, &skb_work); + if (kickit) + queue_work(workq, &skb_work); } /* diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h index 0aaaa0e81f29..94a3b3c47a8a 100644 --- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h +++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h @@ -716,6 +716,7 @@ enum c4iw_ep_flags { ABORT_REQ_IN_PROGRESS = 1, RELEASE_RESOURCES = 2, CLOSE_SENT = 3, + TIMEOUT = 4, QP_REFERENCED = 5, }; -- cgit v1.2.3 From fe7e0a4dd0304745b57e08827fde13e1c2376e66 Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:11:57 +0000 Subject: RDMA/cxgb4: Don't reconnect on abort for mpa_rev 1 Only reconnect if the endpoint wasn't freed. peer_abort() should only attempt to reconnect if the endpoint wasn't freed. Also remove hwtid from the debugfs idr. Add missing check for peer2peer in MPAv2 code Use correct mpa version on reject. Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index ab5b4dd39dec..88933af05c5c 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -284,10 +284,10 @@ void _c4iw_free_ep(struct kref *kref) if (test_bit(QP_REFERENCED, &ep->com.flags)) deref_qp(ep); if (test_bit(RELEASE_RESOURCES, &ep->com.flags)) { + remove_handle(ep->com.dev, &ep->com.dev->hwtid_idr, ep->hwtid); cxgb4_remove_tid(ep->com.dev->rdev.lldi.tids, 0, ep->hwtid); dst_release(ep->dst); cxgb4_l2t_release(ep->l2t); - remove_handle(ep->com.dev, &ep->com.dev->hwtid_idr, ep->hwtid); } kfree(ep); } @@ -699,7 +699,7 @@ static int send_mpa_reject(struct c4iw_ep *ep, const void *pdata, u8 plen) memset(mpa, 0, sizeof(*mpa)); memcpy(mpa->key, MPA_KEY_REP, sizeof(mpa->key)); mpa->flags = MPA_REJECT; - mpa->revision = mpa_rev; + mpa->revision = ep->mpa_attr.version; mpa->private_data_size = htons(plen); if (ep->mpa_attr.version == 2 && ep->mpa_attr.enhanced_rdma_conn) { @@ -2176,7 +2176,7 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb) break; case MPA_REQ_SENT: stop_ep_timer(ep); - if (mpa_rev == 2 && ep->tried_with_mpa_v1) + if (mpa_rev == 1 || (mpa_rev == 2 && ep->tried_with_mpa_v1)) connect_reply_upcall(ep, -ECONNRESET); else { /* @@ -2248,9 +2248,8 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb) out: if (release) release_ep_resources(ep); - - /* retry with mpa-v1 */ - if (ep && ep->retry_with_mpa_v1) { + else if (ep->retry_with_mpa_v1) { + remove_handle(ep->com.dev, &ep->com.dev->hwtid_idr, ep->hwtid); cxgb4_remove_tid(ep->com.dev->rdev.lldi.tids, 0, ep->hwtid); dst_release(ep->dst); cxgb4_l2t_release(ep->l2t); -- cgit v1.2.3 From 7c0a33d61187a413f29f63d106b503b9c91680e8 Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:11:58 +0000 Subject: RDMA/cxgb4: Don't wakeup threads for MPAv2 Don't wakeup threads blocked in rdma_init/rdma_fini if we are on MPAv2, and want to retry connection with MPAv1. Stop ep-timer on getting MPA version mismatch, before doing the abort_connection - in process_mpa_request. Take care to stop ep-timer in error paths for process_mpa_request. Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 88933af05c5c..06c3a527d6f8 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -1300,11 +1300,13 @@ static void process_mpa_request(struct c4iw_ep *ep, struct sk_buff *skb) if (mpa->revision > mpa_rev) { printk(KERN_ERR MOD "%s MPA version mismatch. Local = %d," " Received = %d\n", __func__, mpa_rev, mpa->revision); + stop_ep_timer(ep); abort_connection(ep, skb, GFP_KERNEL); return; } if (memcmp(mpa->key, MPA_KEY_REQ, sizeof(mpa->key))) { + stop_ep_timer(ep); abort_connection(ep, skb, GFP_KERNEL); return; } @@ -1315,6 +1317,7 @@ static void process_mpa_request(struct c4iw_ep *ep, struct sk_buff *skb) * Fail if there's too much private data. */ if (plen > MPA_MAX_PRIVATE_DATA) { + stop_ep_timer(ep); abort_connection(ep, skb, GFP_KERNEL); return; } @@ -1323,6 +1326,7 @@ static void process_mpa_request(struct c4iw_ep *ep, struct sk_buff *skb) * If plen does not account for pkt size */ if (ep->mpa_pkt_len > (sizeof(*mpa) + plen)) { + stop_ep_timer(ep); abort_connection(ep, skb, GFP_KERNEL); return; } @@ -3286,8 +3290,14 @@ static int peer_abort_intr(struct c4iw_dev *dev, struct sk_buff *skb) /* * Wake up any threads in rdma_init() or rdma_fini(). + * However, if we are on MPAv2 and want to retry with MPAv1 + * then, don't wake up yet. */ - c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET); + if (mpa_rev == 2 && !ep->tried_with_mpa_v1) { + if (ep->com.state != MPA_REQ_SENT) + c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET); + } else + c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET); sched(dev, skb); return 0; } -- cgit v1.2.3 From b3de6cfebc6167761c40947f05f4c817531f37d5 Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:11:59 +0000 Subject: RDMA/cxgb4: Insert hwtid in pass_accept_req instead in pass_establish CPL_ABORT_REQ_RSS can come before TCP connection is established. In such case peer_abort was trying to remove the hwtid, which was not inserted. To avoid this we insert the hwtid when we are sure that we are surely going to send passive accept request. Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 06c3a527d6f8..37ea2fcf3b10 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -2010,6 +2010,7 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb) init_timer(&child_ep->timer); cxgb4_insert_tid(t, child_ep, hwtid); + insert_handle(dev, &dev->hwtid_idr, child_ep, child_ep->hwtid); accept_cr(child_ep, peer_ip, skb, req); set_bit(PASS_ACCEPT_REQ, &child_ep->com.history); goto out; @@ -2035,7 +2036,6 @@ static int pass_establish(struct c4iw_dev *dev, struct sk_buff *skb) ntohs(req->tcp_opt)); set_emss(ep, ntohs(req->tcp_opt)); - insert_handle(dev, &dev->hwtid_idr, ep, ep->hwtid); dst_confirm(ep->dst); state_set(&ep->com, MPA_REQ_WAIT); -- cgit v1.2.3 From ef5d6355ed4bcf574e8473c3ce667cbf6c66a0ee Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:12:00 +0000 Subject: RDMA/cxgb4: Address sparse warnings Fixe the following types of sparse warnings - cast to pointer from integer of different size - cast from pointer to integer of different size - incorrect type in assignment (different base types) - incorrect type in argument 1 (different base types) - cast from restricted __be64 - cast from restricted __be32 Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 68 ++++++++++++++++++++---------------- drivers/infiniband/hw/cxgb4/device.c | 3 +- 2 files changed, 40 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 37ea2fcf3b10..dfca515cc933 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -1492,11 +1492,11 @@ static void send_fw_act_open_req(struct c4iw_ep *ep, unsigned int atid) V_FW_OFLD_CONNECTION_WR_ASTID(atid)); req->tcb.cplrxdataack_cplpassacceptrpl = htons(F_FW_OFLD_CONNECTION_WR_CPLRXDATAACK); - req->tcb.tx_max = jiffies; + req->tcb.tx_max = (__force __be32) jiffies; req->tcb.rcv_adv = htons(1); cxgb4_best_mtu(ep->com.dev->rdev.lldi.mtus, ep->mtu, &mtu_idx); wscale = compute_wscale(rcv_win); - req->tcb.opt0 = TCAM_BYPASS(1) | + req->tcb.opt0 = (__force __be64) (TCAM_BYPASS(1) | (nocong ? NO_CONG(1) : 0) | KEEP_ALIVE(1) | DELACK(1) | @@ -1507,20 +1507,20 @@ static void send_fw_act_open_req(struct c4iw_ep *ep, unsigned int atid) SMAC_SEL(ep->smac_idx) | DSCP(ep->tos) | ULP_MODE(ULP_MODE_TCPDDP) | - RCV_BUFSIZ(rcv_win >> 10); - req->tcb.opt2 = PACE(1) | + RCV_BUFSIZ(rcv_win >> 10)); + req->tcb.opt2 = (__force __be32) (PACE(1) | TX_QUEUE(ep->com.dev->rdev.lldi.tx_modq[ep->tx_chan]) | RX_CHANNEL(0) | CCTRL_ECN(enable_ecn) | - RSS_QUEUE_VALID | RSS_QUEUE(ep->rss_qid); + RSS_QUEUE_VALID | RSS_QUEUE(ep->rss_qid)); if (enable_tcp_timestamps) - req->tcb.opt2 |= TSTAMPS_EN(1); + req->tcb.opt2 |= (__force __be32) TSTAMPS_EN(1); if (enable_tcp_sack) - req->tcb.opt2 |= SACK_EN(1); + req->tcb.opt2 |= (__force __be32) SACK_EN(1); if (wscale && enable_tcp_window_scaling) - req->tcb.opt2 |= WND_SCALE_EN(1); - req->tcb.opt0 = cpu_to_be64(req->tcb.opt0); - req->tcb.opt2 = cpu_to_be32(req->tcb.opt2); + req->tcb.opt2 |= (__force __be32) WND_SCALE_EN(1); + req->tcb.opt0 = cpu_to_be64((__force u64) req->tcb.opt0); + req->tcb.opt2 = cpu_to_be32((__force u32) req->tcb.opt2); set_wr_txq(skb, CPL_PRIORITY_CONTROL, ep->ctrlq_idx); set_bit(ACT_OFLD_CONN, &ep->com.history); c4iw_l2t_send(&ep->com.dev->rdev, skb, ep->l2t); @@ -2773,7 +2773,8 @@ static void active_ofld_conn_reply(struct c4iw_dev *dev, struct sk_buff *skb, struct c4iw_ep *ep; int atid = be32_to_cpu(req->tid); - ep = (struct c4iw_ep *)lookup_atid(dev->rdev.lldi.tids, req->tid); + ep = (struct c4iw_ep *)lookup_atid(dev->rdev.lldi.tids, + (__force u32) req->tid); if (!ep) return; @@ -2817,7 +2818,7 @@ static void passive_ofld_conn_reply(struct c4iw_dev *dev, struct sk_buff *skb, struct cpl_pass_accept_req *cpl; int ret; - rpl_skb = (struct sk_buff *)cpu_to_be64(req->cookie); + rpl_skb = (__force struct sk_buff *)cpu_to_be64(req->cookie); BUG_ON(!rpl_skb); if (req->retval) { PDBG("%s passive open failure %d\n", __func__, req->retval); @@ -2828,7 +2829,8 @@ static void passive_ofld_conn_reply(struct c4iw_dev *dev, struct sk_buff *skb, } else { cpl = (struct cpl_pass_accept_req *)cplhdr(rpl_skb); OPCODE_TID(cpl) = htonl(MK_OPCODE_TID(CPL_PASS_ACCEPT_REQ, - htonl(req->tid))); + (__force u32) htonl( + (__force u32) req->tid))); ret = pass_accept_req(dev, rpl_skb); if (!ret) kfree_skb(rpl_skb); @@ -2874,10 +2876,10 @@ static void build_cpl_pass_accept_req(struct sk_buff *skb, int stid , u8 tos) struct tcp_options_received tmp_opt; /* Store values from cpl_rx_pkt in temporary location. */ - vlantag = cpl->vlan; - len = cpl->len; - l2info = cpl->l2info; - hdr_len = cpl->hdr_len; + vlantag = (__force u16) cpl->vlan; + len = (__force u16) cpl->len; + l2info = (__force u32) cpl->l2info; + hdr_len = (__force u16) cpl->hdr_len; intf = cpl->iff; __skb_pull(skb, sizeof(*req) + sizeof(struct rss_header)); @@ -2888,19 +2890,24 @@ static void build_cpl_pass_accept_req(struct sk_buff *skb, int stid , u8 tos) */ memset(&tmp_opt, 0, sizeof(tmp_opt)); tcp_clear_options(&tmp_opt); - tcp_parse_options(skb, &tmp_opt, 0, 0, NULL); + tcp_parse_options(skb, &tmp_opt, NULL, 0, NULL); req = (struct cpl_pass_accept_req *)__skb_push(skb, sizeof(*req)); memset(req, 0, sizeof(*req)); req->l2info = cpu_to_be16(V_SYN_INTF(intf) | - V_SYN_MAC_IDX(G_RX_MACIDX(htonl(l2info))) | + V_SYN_MAC_IDX(G_RX_MACIDX( + (__force int) htonl(l2info))) | F_SYN_XACT_MATCH); - req->hdr_len = cpu_to_be32(V_SYN_RX_CHAN(G_RX_CHAN(htonl(l2info))) | - V_TCP_HDR_LEN(G_RX_TCPHDR_LEN(htons(hdr_len))) | - V_IP_HDR_LEN(G_RX_IPHDR_LEN(htons(hdr_len))) | - V_ETH_HDR_LEN(G_RX_ETHHDR_LEN(htonl(l2info)))); - req->vlan = vlantag; - req->len = len; + req->hdr_len = cpu_to_be32(V_SYN_RX_CHAN(G_RX_CHAN( + (__force int) htonl(l2info))) | + V_TCP_HDR_LEN(G_RX_TCPHDR_LEN( + (__force int) htons(hdr_len))) | + V_IP_HDR_LEN(G_RX_IPHDR_LEN( + (__force int) htons(hdr_len))) | + V_ETH_HDR_LEN(G_RX_ETHHDR_LEN( + (__force int) htonl(l2info)))); + req->vlan = (__force __be16) vlantag; + req->len = (__force __be16) len; req->tos_stid = cpu_to_be32(PASS_OPEN_TID(stid) | PASS_OPEN_TOS(tos)); req->tcpopt.mss = htons(tmp_opt.mss_clamp); @@ -2929,7 +2936,7 @@ static void send_fw_pass_open_req(struct c4iw_dev *dev, struct sk_buff *skb, req->op_compl = htonl(V_WR_OP(FW_OFLD_CONNECTION_WR) | FW_WR_COMPL(1)); req->len16_pkd = htonl(FW_WR_LEN16(DIV_ROUND_UP(sizeof(*req), 16))); req->le.version_cpl = htonl(F_FW_OFLD_CONNECTION_WR_CPL); - req->le.filter = filter; + req->le.filter = (__force __be32) filter; req->le.lport = lport; req->le.pport = rport; req->le.u.ipv4.lip = laddr; @@ -2955,7 +2962,7 @@ static void send_fw_pass_open_req(struct c4iw_dev *dev, struct sk_buff *skb, * TP will ignore any value > 0 for MSS index. */ req->tcb.opt0 = cpu_to_be64(V_MSS_IDX(0xF)); - req->cookie = cpu_to_be64((u64)skb); + req->cookie = (__force __u64) cpu_to_be64((u64)skb); set_wr_txq(req_skb, CPL_PRIORITY_CONTROL, port_id); cxgb4_ofld_send(dev->rdev.lldi.ports[0], req_skb); @@ -3005,7 +3012,8 @@ static int rx_pkt(struct c4iw_dev *dev, struct sk_buff *skb) /* * Calculate the server tid from filter hit index from cpl_rx_pkt. */ - stid = cpu_to_be32(rss->hash_val) - dev->rdev.lldi.tids->sftid_base + stid = (__force int) cpu_to_be32((__force u32) rss->hash_val) + - dev->rdev.lldi.tids->sftid_base + dev->rdev.lldi.tids->nstids; lep = (struct c4iw_ep *)lookup_stid(dev->rdev.lldi.tids, stid); @@ -3066,10 +3074,10 @@ static int rx_pkt(struct c4iw_dev *dev, struct sk_buff *skb) step = dev->rdev.lldi.nrxq / dev->rdev.lldi.nchan; rss_qid = dev->rdev.lldi.rxq_ids[pi->port_id * step]; - window = htons(tcph->window); + window = (__force u16) htons((__force u16)tcph->window); /* Calcuate filter portion for LE region. */ - filter = cpu_to_be32(select_ntuple(dev, dst, e)); + filter = (__force unsigned int) cpu_to_be32(select_ntuple(dev, dst, e)); /* * Synthesize the cpl_pass_accept_req. We have everything except the diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c index ba11c76c0b5a..dc6adb213cd6 100644 --- a/drivers/infiniband/hw/cxgb4/device.c +++ b/drivers/infiniband/hw/cxgb4/device.c @@ -797,7 +797,8 @@ static int c4iw_uld_rx_handler(void *handle, const __be64 *rsp, "RSS %#llx, FL %#llx, len %u\n", pci_name(ctx->lldi.pdev), gl->va, (unsigned long long)be64_to_cpu(*rsp), - (unsigned long long)be64_to_cpu(*(u64 *)gl->va), + (unsigned long long)be64_to_cpu( + *(__force __be64 *)gl->va), gl->tot_len); return 0; -- cgit v1.2.3 From 710a31102be46ffc2f087119dca19c894dc237eb Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Tue, 5 Feb 2013 20:51:30 +0000 Subject: RDMA/cxgb4: "cookie" can stay in host endianness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Work requests are passed between the host and the firmware with a "cookie". This cookie is swapped to big-endian when passed to the firmware and back to host endianness on return. This swapping seems to be implemented incorrectly. Moreover, the byte swapping triggers GCC warnings on 32 bit: drivers/infiniband/hw/cxgb4/cm.c: In function ‘passive_ofld_conn_reply’: drivers/infiniband/hw/cxgb4/cm.c:2803:12: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] drivers/infiniband/hw/cxgb4/cm.c: In function ‘send_fw_pass_open_req’: drivers/infiniband/hw/cxgb4/cm.c:2941:16: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] [...] But byte swapping isn't needed as the firmware doesn't actually touch the cookie. Dropping byte swapping makes the warnings go away too. Signed-off-by: Paul Bolle Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index dfca515cc933..565bfb161c1a 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -2818,7 +2818,7 @@ static void passive_ofld_conn_reply(struct c4iw_dev *dev, struct sk_buff *skb, struct cpl_pass_accept_req *cpl; int ret; - rpl_skb = (__force struct sk_buff *)cpu_to_be64(req->cookie); + rpl_skb = (struct sk_buff *)(unsigned long)req->cookie; BUG_ON(!rpl_skb); if (req->retval) { PDBG("%s passive open failure %d\n", __func__, req->retval); @@ -2962,7 +2962,7 @@ static void send_fw_pass_open_req(struct c4iw_dev *dev, struct sk_buff *skb, * TP will ignore any value > 0 for MSS index. */ req->tcb.opt0 = cpu_to_be64(V_MSS_IDX(0xF)); - req->cookie = (__force __u64) cpu_to_be64((u64)skb); + req->cookie = (unsigned long)skb; set_wr_txq(req_skb, CPL_PRIORITY_CONTROL, port_id); cxgb4_ofld_send(dev->rdev.lldi.ports[0], req_skb); -- cgit v1.2.3 From 93711d8becca550154da3b9e422be77083ab51ad Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Feb 2013 15:27:01 -0700 Subject: drm/pci: define drm_pcie_get_speed_cap_mask() only when CONFIG_PCI=y Move drm_pcie_get_speed_cap_mask() under #ifdef CONFIG_PCI because it it used only for PCI devices (evergreen, r600, r770), and it uses PCI interfaces that only exist when CONFIG_PCI=y. Previously, we tried to compile drm_pcie_get_speed_cap_mask() even when CONFIG_PCI=n, which fails. Signed-off-by: Bjorn Helgaas Acked-by: Arnd Bergmann Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_pci.c | 54 +++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 86102a08f65c..bd719e936e13 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -439,33 +439,6 @@ int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver) return 0; } -#else - -int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver) -{ - return -1; -} - -#endif - -EXPORT_SYMBOL(drm_pci_init); - -/*@}*/ -void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver) -{ - struct drm_device *dev, *tmp; - DRM_DEBUG("\n"); - - if (driver->driver_features & DRIVER_MODESET) { - pci_unregister_driver(pdriver); - } else { - list_for_each_entry_safe(dev, tmp, &driver->device_list, driver_item) - drm_put_dev(dev); - } - DRM_INFO("Module unloaded\n"); -} -EXPORT_SYMBOL(drm_pci_exit); - int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask) { struct pci_dev *root; @@ -503,3 +476,30 @@ int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask) return 0; } EXPORT_SYMBOL(drm_pcie_get_speed_cap_mask); + +#else + +int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver) +{ + return -1; +} + +#endif + +EXPORT_SYMBOL(drm_pci_init); + +/*@}*/ +void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver) +{ + struct drm_device *dev, *tmp; + DRM_DEBUG("\n"); + + if (driver->driver_features & DRIVER_MODESET) { + pci_unregister_driver(pdriver); + } else { + list_for_each_entry_safe(dev, tmp, &driver->device_list, driver_item) + drm_put_dev(dev); + } + DRM_INFO("Module unloaded\n"); +} +EXPORT_SYMBOL(drm_pci_exit); -- cgit v1.2.3 From 3e2b756ba330343c960c332695608b9c5881a173 Mon Sep 17 00:00:00 2001 From: Chris Metcalf Date: Fri, 1 Feb 2013 13:44:33 -0500 Subject: drm: fix compile failure by including On tile architecture (with "make allyesconfig") including is required to call swiotlb_nr_tbl(). Signed-off-by: Chris Metcalf Acked-by: Maarten Lankhorst Signed-off-by: Dave Airlie --- drivers/gpu/drm/nouveau/nouveau_bo.c | 1 + drivers/gpu/drm/radeon/radeon_ttm.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 64d6e3047dee..2f2741483b51 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -28,6 +28,7 @@ */ #include +#include #include #include diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 1d8ff2f850ba..93f760e27a92 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -38,6 +38,7 @@ #include #include #include +#include #include "radeon_reg.h" #include "radeon.h" -- cgit v1.2.3 From f934ec8c34b9dcefb5a4f35b0bda33bca289cbe6 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 29 Jan 2013 14:27:39 +0100 Subject: drm: shut up invalid edid messages My cheapo monitor has an invalid block 1, resulting in a lot of dmesg spam every few seconds. I get it the first time that the entire block is all 0xff.. Signed-off-by: Maarten Lankhorst Cc: stable@vger.kernel.org [v3.7] Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_edid.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 51324256a657..e1aca7b53987 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -354,10 +354,14 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) break; } } - if (i == 4) + + if (i == 4 && print_bad_edid) { dev_warn(connector->dev->dev, "%s: Ignoring invalid EDID block %d.\n", drm_get_connector_name(connector), j); + + connector->bad_edid_counter++; + } } if (valid_extensions != block[0x7e]) { -- cgit v1.2.3 From bcc9b67a5b65ec2e1ec5371226a729ec1b380860 Mon Sep 17 00:00:00 2001 From: Mike Marciniszyn Date: Thu, 7 Feb 2013 20:47:51 +0000 Subject: IB/qib: Fix QP locate/remove race remove_qp() can execute concurrently with a qib_lookup_qpn() on another CPU, which in of itself, is ok, given the RCU locking. The issue is that remove_qp() NULLs out the qp->next field so that a qib_lookup_qpn() might fail to find a qp if it occurs after the one that is being deleted. This is a momentary issue and subsequent qib_lookup_qpn() calls would find the qp's since the search restarts from the bucket head. At scale, the issue might causes dropped packets and unnecessary retransmissions. The fix just deletes the qp->next NULL assignment to prevent the remove_qp() from hiding qp's from qib_lookup_qpn(). Reviewed-by: Dean Luick Signed-off-by: Mike Marciniszyn Signed-off-by: Roland Dreier --- drivers/infiniband/hw/qib/qib_qp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/qib/qib_qp.c b/drivers/infiniband/hw/qib/qib_qp.c index 35275099cafd..a6a2cc2ba260 100644 --- a/drivers/infiniband/hw/qib/qib_qp.c +++ b/drivers/infiniband/hw/qib/qib_qp.c @@ -268,8 +268,9 @@ static void remove_qp(struct qib_ibdev *dev, struct qib_qp *qp) qpp = &q->next) if (q == qp) { atomic_dec(&qp->refcount); - *qpp = qp->next; - rcu_assign_pointer(qp->next, NULL); + rcu_assign_pointer(*qpp, + rcu_dereference_protected(qp->next, + lockdep_is_held(&dev->qpt_lock))); break; } } -- cgit v1.2.3 From cf0a6584aa6d382f802f2c3cacac23ccbccde0cd Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 6 Feb 2013 11:24:41 +0100 Subject: drm/i915: write backlight harder 770c12312ad617172b1a65b911d3e6564fc5aca8 is the first bad commit commit 770c12312ad617172b1a65b911d3e6564fc5aca8 Author: Takashi Iwai Date: Sat Aug 11 08:56:42 2012 +0200 drm/i915: Fix blank panel at reopening lid changed the register write sequence for restoring the backlight, which helped prevent non-working backlights on some machines. Turns out that the original sequence was the right thing to do for a different set of machines. Worse, setting the backlight level _after_ enabling it seems to reset it somehow. So we need to make that one conditional upon the backlight having been reset to zero, and add the old one back. Cargo-culting at it's best, but it seems to work. Cc: stable@vger.kernel.org Cc: Takashi Iwai Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=47941 Reviewed-by: Jani Nikula Acked-by: Takashi Iwai Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_panel.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index bee8cb6108a7..a3730e0289e5 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -321,6 +321,9 @@ void intel_panel_enable_backlight(struct drm_device *dev, if (dev_priv->backlight_level == 0) dev_priv->backlight_level = intel_panel_get_max_backlight(dev); + dev_priv->backlight_enabled = true; + intel_panel_actually_set_backlight(dev, dev_priv->backlight_level); + if (INTEL_INFO(dev)->gen >= 4) { uint32_t reg, tmp; @@ -356,12 +359,12 @@ void intel_panel_enable_backlight(struct drm_device *dev, } set_level: - /* Call below after setting BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1. - * BLC_PWM_CPU_CTL may be cleared to zero automatically when these - * registers are set. + /* Check the current backlight level and try to set again if it's zero. + * On some machines, BLC_PWM_CPU_CTL is cleared to zero automatically + * when BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1 are written. */ - dev_priv->backlight_enabled = true; - intel_panel_actually_set_backlight(dev, dev_priv->backlight_level); + if (!intel_panel_get_backlight(dev)) + intel_panel_actually_set_backlight(dev, dev_priv->backlight_level); } static void intel_panel_init_backlight(struct drm_device *dev) -- cgit v1.2.3 From 07ea0d85ac8adb87b817913d9720e3c76171b1f6 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 7 Feb 2013 13:34:19 -0800 Subject: drm/i915: Clarify HW context size logic This was a rebase error from when the patches originally landed. Since the context size is unsigned, there is also no use in checking if it's less than 0. The existing code is not really wrong, but it's not simple as it should be. Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_context.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index a3f06bcad551..e7ecafa8e598 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -242,7 +242,6 @@ err_destroy: void i915_gem_context_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - uint32_t ctx_size; if (!HAS_HW_CONTEXTS(dev)) { dev_priv->hw_contexts_disabled = true; @@ -254,11 +253,9 @@ void i915_gem_context_init(struct drm_device *dev) dev_priv->ring[RCS].default_context) return; - ctx_size = get_context_size(dev); - dev_priv->hw_context_size = get_context_size(dev); - dev_priv->hw_context_size = round_up(dev_priv->hw_context_size, 4096); + dev_priv->hw_context_size = round_up(get_context_size(dev), 4096); - if (ctx_size <= 0 || ctx_size > (1<<20)) { + if (dev_priv->hw_context_size > (1<<20)) { dev_priv->hw_contexts_disabled = true; return; } -- cgit v1.2.3 From 26739f12cf210cb8df35969258a1f064e8e12b63 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 7 Feb 2013 12:42:32 +0100 Subject: drm/i915: unify HDMI/DP hpd definitions They're physically the same pins and also the same bits, duplicating only confuses the reader. This also makes it a bit obvious that we have quite some code duplication going on here. Squashing that is for a larger rework in our hpd handling though. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 36 ++++++++++++++++++------------------ drivers/gpu/drm/i915/i915_reg.h | 28 +++++++++------------------- drivers/gpu/drm/i915/intel_dp.c | 12 ++++++------ drivers/gpu/drm/i915/intel_hdmi.c | 10 +++++----- 4 files changed, 38 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 13bb8d3f2a77..b27ffdbc98a7 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2137,12 +2137,12 @@ static void valleyview_hpd_irq_setup(struct drm_device *dev) u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN); /* Note HDMI and DP share bits */ - if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIB_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMIC_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMID_HOTPLUG_INT_STATUS) - hotplug_en |= HDMID_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTB_HOTPLUG_INT_STATUS) + hotplug_en |= PORTB_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS) + hotplug_en |= PORTC_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS) + hotplug_en |= PORTD_HOTPLUG_INT_EN; if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_I915) hotplug_en |= SDVOC_HOTPLUG_INT_EN; if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_I915) @@ -2408,12 +2408,12 @@ static void i915_hpd_irq_setup(struct drm_device *dev) if (I915_HAS_HOTPLUG(dev)) { hotplug_en = I915_READ(PORT_HOTPLUG_EN); - if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIB_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMIC_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMID_HOTPLUG_INT_STATUS) - hotplug_en |= HDMID_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTB_HOTPLUG_INT_STATUS) + hotplug_en |= PORTB_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS) + hotplug_en |= PORTC_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS) + hotplug_en |= PORTD_HOTPLUG_INT_EN; if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_I915) hotplug_en |= SDVOC_HOTPLUG_INT_EN; if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_I915) @@ -2642,12 +2642,12 @@ static void i965_hpd_irq_setup(struct drm_device *dev) /* Note HDMI and DP share hotplug bits */ hotplug_en = 0; - if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIB_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMIC_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMID_HOTPLUG_INT_STATUS) - hotplug_en |= HDMID_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTB_HOTPLUG_INT_STATUS) + hotplug_en |= PORTB_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS) + hotplug_en |= PORTC_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS) + hotplug_en |= PORTD_HOTPLUG_INT_EN; if (IS_G4X(dev)) { if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_G4X) hotplug_en |= SDVOC_HOTPLUG_INT_EN; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 8754f9160aef..b9fdc9963817 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1625,12 +1625,9 @@ /* Hotplug control (945+ only) */ #define PORT_HOTPLUG_EN (dev_priv->info->display_mmio_offset + 0x61110) -#define HDMIB_HOTPLUG_INT_EN (1 << 29) -#define DPB_HOTPLUG_INT_EN (1 << 29) -#define HDMIC_HOTPLUG_INT_EN (1 << 28) -#define DPC_HOTPLUG_INT_EN (1 << 28) -#define HDMID_HOTPLUG_INT_EN (1 << 27) -#define DPD_HOTPLUG_INT_EN (1 << 27) +#define PORTB_HOTPLUG_INT_EN (1 << 29) +#define PORTC_HOTPLUG_INT_EN (1 << 28) +#define PORTD_HOTPLUG_INT_EN (1 << 27) #define SDVOB_HOTPLUG_INT_EN (1 << 26) #define SDVOC_HOTPLUG_INT_EN (1 << 25) #define TV_HOTPLUG_INT_EN (1 << 18) @@ -1653,19 +1650,12 @@ #define PORT_HOTPLUG_STAT (dev_priv->info->display_mmio_offset + 0x61114) /* HDMI/DP bits are gen4+ */ -#define DPB_HOTPLUG_LIVE_STATUS (1 << 29) -#define DPC_HOTPLUG_LIVE_STATUS (1 << 28) -#define DPD_HOTPLUG_LIVE_STATUS (1 << 27) -#define DPD_HOTPLUG_INT_STATUS (3 << 21) -#define DPC_HOTPLUG_INT_STATUS (3 << 19) -#define DPB_HOTPLUG_INT_STATUS (3 << 17) -/* HDMI bits are shared with the DP bits */ -#define HDMIB_HOTPLUG_LIVE_STATUS (1 << 29) -#define HDMIC_HOTPLUG_LIVE_STATUS (1 << 28) -#define HDMID_HOTPLUG_LIVE_STATUS (1 << 27) -#define HDMID_HOTPLUG_INT_STATUS (3 << 21) -#define HDMIC_HOTPLUG_INT_STATUS (3 << 19) -#define HDMIB_HOTPLUG_INT_STATUS (3 << 17) +#define PORTB_HOTPLUG_LIVE_STATUS (1 << 29) +#define PORTC_HOTPLUG_LIVE_STATUS (1 << 28) +#define PORTD_HOTPLUG_LIVE_STATUS (1 << 27) +#define PORTD_HOTPLUG_INT_STATUS (3 << 21) +#define PORTC_HOTPLUG_INT_STATUS (3 << 19) +#define PORTB_HOTPLUG_INT_STATUS (3 << 17) /* CRT/TV common between gen3+ */ #define CRT_HOTPLUG_INT_STATUS (1 << 11) #define TV_HOTPLUG_INT_STATUS (1 << 10) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 56e408b2e3c8..7b8bfe8982e6 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2302,13 +2302,13 @@ g4x_dp_detect(struct intel_dp *intel_dp) switch (intel_dig_port->port) { case PORT_B: - bit = DPB_HOTPLUG_LIVE_STATUS; + bit = PORTB_HOTPLUG_LIVE_STATUS; break; case PORT_C: - bit = DPC_HOTPLUG_LIVE_STATUS; + bit = PORTC_HOTPLUG_LIVE_STATUS; break; case PORT_D: - bit = DPD_HOTPLUG_LIVE_STATUS; + bit = PORTD_HOTPLUG_LIVE_STATUS; break; default: return connector_status_unknown; @@ -2838,15 +2838,15 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, name = "DPDDC-A"; break; case PORT_B: - dev_priv->hotplug_supported_mask |= DPB_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTB_HOTPLUG_INT_STATUS; name = "DPDDC-B"; break; case PORT_C: - dev_priv->hotplug_supported_mask |= DPC_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTC_HOTPLUG_INT_STATUS; name = "DPDDC-C"; break; case PORT_D: - dev_priv->hotplug_supported_mask |= DPD_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTD_HOTPLUG_INT_STATUS; name = "DPDDC-D"; break; default: diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 5b4efd64c2f9..5a6138c62fe9 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -802,10 +802,10 @@ static bool g4x_hdmi_connected(struct intel_hdmi *intel_hdmi) switch (intel_dig_port->port) { case PORT_B: - bit = HDMIB_HOTPLUG_LIVE_STATUS; + bit = PORTB_HOTPLUG_LIVE_STATUS; break; case PORT_C: - bit = HDMIC_HOTPLUG_LIVE_STATUS; + bit = PORTC_HOTPLUG_LIVE_STATUS; break; default: bit = 0; @@ -1022,15 +1022,15 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, switch (port) { case PORT_B: intel_hdmi->ddc_bus = GMBUS_PORT_DPB; - dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTB_HOTPLUG_INT_STATUS; break; case PORT_C: intel_hdmi->ddc_bus = GMBUS_PORT_DPC; - dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTC_HOTPLUG_INT_STATUS; break; case PORT_D: intel_hdmi->ddc_bus = GMBUS_PORT_DPD; - dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTD_HOTPLUG_INT_STATUS; break; case PORT_A: /* Internal port only for eDP. */ -- cgit v1.2.3 From 2c6602df1a7ff79c9e489602445a6d7eb728744e Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 8 Feb 2013 23:13:35 +0200 Subject: drm/i915: Fix sprite_scaling_enabled for multiple sprites MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have more than one sprite, so a boolean simply won't cut it. Turn sprite_scaling_enabled into a bitmask and track the state of sprite scaler for each sprite independently. Also don't re-enable LP watermarks until the sprite registers have actually been written, and thus sprite scaling has really been disabled. Signed-off-by: Ville Syrjälä Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 +- drivers/gpu/drm/i915/intel_sprite.c | 27 ++++++++++++++++----------- 2 files changed, 17 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 4e5a3776a3ea..a7860ea21af9 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -925,7 +925,7 @@ typedef struct drm_i915_private { /* overlay */ struct intel_overlay *overlay; - bool sprite_scaling_enabled; + unsigned int sprite_scaling_enabled; /* LVDS info */ int backlight_level; /* restore backlight to this value */ diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index f8293061d6bd..03cfd62d17e2 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -50,6 +50,7 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, u32 sprctl, sprscale = 0; unsigned long sprsurf_offset, linear_offset; int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + bool scaling_was_enabled = dev_priv->sprite_scaling_enabled; sprctl = I915_READ(SPRCTL(pipe)); @@ -103,19 +104,15 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, * when scaling is disabled. */ if (crtc_w != src_w || crtc_h != src_h) { - if (!dev_priv->sprite_scaling_enabled) { - dev_priv->sprite_scaling_enabled = true; + dev_priv->sprite_scaling_enabled |= 1 << pipe; + + if (!scaling_was_enabled) { intel_update_watermarks(dev); intel_wait_for_vblank(dev, pipe); } sprscale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h; - } else { - if (dev_priv->sprite_scaling_enabled) { - dev_priv->sprite_scaling_enabled = false; - /* potentially re-enable LP watermarks */ - intel_update_watermarks(dev); - } - } + } else + dev_priv->sprite_scaling_enabled &= ~(1 << pipe); I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]); I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x); @@ -141,6 +138,10 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, I915_WRITE(SPRCTL(pipe), sprctl); I915_MODIFY_DISPBASE(SPRSURF(pipe), obj->gtt_offset + sprsurf_offset); POSTING_READ(SPRSURF(pipe)); + + /* potentially re-enable LP watermarks */ + if (scaling_was_enabled && !dev_priv->sprite_scaling_enabled) + intel_update_watermarks(dev); } static void @@ -150,6 +151,7 @@ ivb_disable_plane(struct drm_plane *plane) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_plane *intel_plane = to_intel_plane(plane); int pipe = intel_plane->pipe; + bool scaling_was_enabled = dev_priv->sprite_scaling_enabled; I915_WRITE(SPRCTL(pipe), I915_READ(SPRCTL(pipe)) & ~SPRITE_ENABLE); /* Can't leave the scaler enabled... */ @@ -159,8 +161,11 @@ ivb_disable_plane(struct drm_plane *plane) I915_MODIFY_DISPBASE(SPRSURF(pipe), 0); POSTING_READ(SPRSURF(pipe)); - dev_priv->sprite_scaling_enabled = false; - intel_update_watermarks(dev); + dev_priv->sprite_scaling_enabled &= ~(1 << pipe); + + /* potentially re-enable LP watermarks */ + if (scaling_was_enabled && !dev_priv->sprite_scaling_enabled) + intel_update_watermarks(dev); } static int -- cgit v1.2.3 From d46da4377689bd938795e53c4e2fb54dbcaeea44 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 8 Feb 2013 17:35:15 -0200 Subject: drm/i915: add ibx_irq_postinstall So we can remove duplicated code. Note that this function is used not only on IBX, but also CPT and LPT. Signed-off-by: Paulo Zanoni Reviewed-by: Ben Widawsky [danvet: Also bikeshed s/ironlake_enable_pch_hotplug/ibx_enable_hotplug to keep consistent with our ibx for pch naming scheme.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 68 +++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index b27ffdbc98a7..2cd97d1cc920 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1924,7 +1924,7 @@ static void valleyview_irq_preinstall(struct drm_device *dev) * This register is the same on all known PCH chips. */ -static void ironlake_enable_pch_hotplug(struct drm_device *dev) +static void ibx_enable_hotplug(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 hotplug; @@ -1937,6 +1937,28 @@ static void ironlake_enable_pch_hotplug(struct drm_device *dev) I915_WRITE(PCH_PORT_HOTPLUG, hotplug); } +static void ibx_irq_postinstall(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 mask; + + if (HAS_PCH_IBX(dev)) + mask = SDE_HOTPLUG_MASK | + SDE_GMBUS | + SDE_AUX_MASK; + else + mask = SDE_HOTPLUG_MASK_CPT | + SDE_GMBUS_CPT | + SDE_AUX_MASK_CPT; + + I915_WRITE(SDEIIR, I915_READ(SDEIIR)); + I915_WRITE(SDEIMR, ~mask); + I915_WRITE(SDEIER, mask); + POSTING_READ(SDEIER); + + ibx_enable_hotplug(dev); +} + static int ironlake_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; @@ -1945,8 +1967,6 @@ static int ironlake_irq_postinstall(struct drm_device *dev) DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE | DE_AUX_CHANNEL_A; u32 render_irqs; - u32 hotplug_mask; - u32 pch_irq_mask; dev_priv->irq_mask = ~display_mask; @@ -1974,30 +1994,7 @@ static int ironlake_irq_postinstall(struct drm_device *dev) I915_WRITE(GTIER, render_irqs); POSTING_READ(GTIER); - if (HAS_PCH_CPT(dev)) { - hotplug_mask = (SDE_CRT_HOTPLUG_CPT | - SDE_PORTB_HOTPLUG_CPT | - SDE_PORTC_HOTPLUG_CPT | - SDE_PORTD_HOTPLUG_CPT | - SDE_GMBUS_CPT | - SDE_AUX_MASK_CPT); - } else { - hotplug_mask = (SDE_CRT_HOTPLUG | - SDE_PORTB_HOTPLUG | - SDE_PORTC_HOTPLUG | - SDE_PORTD_HOTPLUG | - SDE_GMBUS | - SDE_AUX_MASK); - } - - pch_irq_mask = ~hotplug_mask; - - I915_WRITE(SDEIIR, I915_READ(SDEIIR)); - I915_WRITE(SDEIMR, pch_irq_mask); - I915_WRITE(SDEIER, hotplug_mask); - POSTING_READ(SDEIER); - - ironlake_enable_pch_hotplug(dev); + ibx_irq_postinstall(dev); if (IS_IRONLAKE_M(dev)) { /* Clear & enable PCU event interrupts */ @@ -2020,8 +2017,6 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) DE_PLANEA_FLIP_DONE_IVB | DE_AUX_CHANNEL_A_IVB; u32 render_irqs; - u32 hotplug_mask; - u32 pch_irq_mask; dev_priv->irq_mask = ~display_mask; @@ -2045,20 +2040,7 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) I915_WRITE(GTIER, render_irqs); POSTING_READ(GTIER); - hotplug_mask = (SDE_CRT_HOTPLUG_CPT | - SDE_PORTB_HOTPLUG_CPT | - SDE_PORTC_HOTPLUG_CPT | - SDE_PORTD_HOTPLUG_CPT | - SDE_GMBUS_CPT | - SDE_AUX_MASK_CPT); - pch_irq_mask = ~hotplug_mask; - - I915_WRITE(SDEIIR, I915_READ(SDEIIR)); - I915_WRITE(SDEIMR, pch_irq_mask); - I915_WRITE(SDEIER, hotplug_mask); - POSTING_READ(SDEIER); - - ironlake_enable_pch_hotplug(dev); + ibx_irq_postinstall(dev); return 0; } -- cgit v1.2.3 From 41907ddc1b71aaa4ef5290f46f0ec49d581d6aac Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Fri, 8 Feb 2013 11:32:47 -0800 Subject: drm/i915: Fix gen2 mappable calculations When I refactored the code initially, I forgot that gen2 uses a different bar for the CPU mappable aperture. The agp-less code knows nothing of generations less than 5, so we have to expand the gtt_probe function to include the mappable base and end. It was originally broken by me: commit baa09f5fd8a6d033ec075355dda99a65b7f6a0f3 Author: Ben Widawsky Date: Thu Jan 24 13:49:57 2013 -0800 drm/i915: Add probe and remove to the gtt ops Reported-by: Chris Wilson Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 5 ++++- drivers/gpu/drm/i915/i915_drv.h | 3 ++- drivers/gpu/drm/i915/i915_gem_gtt.c | 23 ++++++++++++++--------- include/drm/intel-gtt.h | 3 ++- 4 files changed, 22 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index d8e7e6c9114e..207e5c36e9ec 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -1371,10 +1371,13 @@ int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev, } EXPORT_SYMBOL(intel_gmch_probe); -void intel_gtt_get(size_t *gtt_total, size_t *stolen_size) +void intel_gtt_get(size_t *gtt_total, size_t *stolen_size, + phys_addr_t *mappable_base, unsigned long *mappable_end) { *gtt_total = intel_private.gtt_total_entries << PAGE_SHIFT; *stolen_size = intel_private.stolen_size; + *mappable_base = intel_private.gma_bus_addr; + *mappable_end = intel_private.gtt_mappable_entries << PAGE_SHIFT; } EXPORT_SYMBOL(intel_gtt_get); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index a7860ea21af9..0f1d15bac517 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -399,7 +399,8 @@ struct i915_gtt { /* global gtt ops */ int (*gtt_probe)(struct drm_device *dev, size_t *gtt_total, - size_t *stolen); + size_t *stolen, phys_addr_t *mappable_base, + unsigned long *mappable_end); void (*gtt_remove)(struct drm_device *dev); void (*gtt_clear_range)(struct drm_device *dev, unsigned int first_entry, diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index bdaca3f47988..926a1e2dd234 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -725,7 +725,9 @@ static inline size_t gen7_get_stolen_size(u16 snb_gmch_ctl) static int gen6_gmch_probe(struct drm_device *dev, size_t *gtt_total, - size_t *stolen) + size_t *stolen, + phys_addr_t *mappable_base, + unsigned long *mappable_end) { struct drm_i915_private *dev_priv = dev->dev_private; phys_addr_t gtt_bus_addr; @@ -733,11 +735,13 @@ static int gen6_gmch_probe(struct drm_device *dev, u16 snb_gmch_ctl; int ret; + *mappable_base = pci_resource_start(dev->pdev, 2); + *mappable_end = pci_resource_len(dev->pdev, 2); + /* 64/512MB is the current min/max we actually know of, but this is just * a coarse sanity check. */ - if ((dev_priv->gtt.mappable_end < (64<<20) || - (dev_priv->gtt.mappable_end > (512<<20)))) { + if ((*mappable_end < (64<<20) || (*mappable_end > (512<<20)))) { DRM_ERROR("Unknown GMADR size (%lx)\n", dev_priv->gtt.mappable_end); return -ENXIO; @@ -782,7 +786,9 @@ static void gen6_gmch_remove(struct drm_device *dev) static int i915_gmch_probe(struct drm_device *dev, size_t *gtt_total, - size_t *stolen) + size_t *stolen, + phys_addr_t *mappable_base, + unsigned long *mappable_end) { struct drm_i915_private *dev_priv = dev->dev_private; int ret; @@ -793,7 +799,7 @@ static int i915_gmch_probe(struct drm_device *dev, return -EIO; } - intel_gtt_get(gtt_total, stolen); + intel_gtt_get(gtt_total, stolen, mappable_base, mappable_end); dev_priv->gtt.do_idle_maps = needs_idle_maps(dev_priv->dev); dev_priv->gtt.gtt_clear_range = i915_ggtt_clear_range; @@ -814,9 +820,6 @@ int i915_gem_gtt_init(struct drm_device *dev) unsigned long gtt_size; int ret; - gtt->mappable_base = pci_resource_start(dev->pdev, 2); - gtt->mappable_end = pci_resource_len(dev->pdev, 2); - if (INTEL_INFO(dev)->gen <= 5) { dev_priv->gtt.gtt_probe = i915_gmch_probe; dev_priv->gtt.gtt_remove = i915_gmch_remove; @@ -826,7 +829,9 @@ int i915_gem_gtt_init(struct drm_device *dev) } ret = dev_priv->gtt.gtt_probe(dev, &dev_priv->gtt.total, - &dev_priv->gtt.stolen_size); + &dev_priv->gtt.stolen_size, + >t->mappable_base, + >t->mappable_end); if (ret) return ret; diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index cf105557fea9..b08bdade6002 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -3,7 +3,8 @@ #ifndef _DRM_INTEL_GTT_H #define _DRM_INTEL_GTT_H -void intel_gtt_get(size_t *gtt_total, size_t *stolen_size); +void intel_gtt_get(size_t *gtt_total, size_t *stolen_size, + phys_addr_t *mappable_base, unsigned long *mappable_end); int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev, struct agp_bridge_data *bridge); -- cgit v1.2.3 From 4fc7c971c3aedf937f824c063d698779d25c3330 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Fri, 8 Feb 2013 11:49:24 -0800 Subject: drm/i915: Extract ring init from hw_init The ring initialization will differ a bit in upcoming generations, and this split will prepare the code for what's needed. This patch also fixes a bug introduced in: commit 99433931950f33039d9e1a52b4ed9af3f1b58e84 Author: Mika Kuoppala Date: Tue Jan 22 14:12:17 2013 +0200 drm/i915: use gem_set_seqno() on hardware init After doing the extraction, the bad error handling became obvious. I acknowledge that this should be two patches, but it's a pretty small/trivial patch. If requested, I can certainly do the fix as a distinct patch. v2: Should be cleanup blt, not init blt on failure (Chris) v3: Forgot to git add on v2 Cc: Mika Kuoppala Reviewed-by: Chris Wilson Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 53 ++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 62be74899c2b..d8618f32557e 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3930,22 +3930,11 @@ intel_enable_blt(struct drm_device *dev) return true; } -int -i915_gem_init_hw(struct drm_device *dev) +static int i915_gem_init_rings(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; int ret; - if (INTEL_INFO(dev)->gen < 6 && !intel_enable_gtt()) - return -EIO; - - if (IS_HASWELL(dev) && (I915_READ(0x120010) == 1)) - I915_WRITE(0x9008, I915_READ(0x9008) | 0xf0000); - - i915_gem_l3_remap(dev); - - i915_gem_init_swizzling(dev); - ret = intel_init_render_ring_buffer(dev); if (ret) return ret; @@ -3963,6 +3952,38 @@ i915_gem_init_hw(struct drm_device *dev) } ret = i915_gem_set_seqno(dev, ((u32)~0 - 0x1000)); + if (ret) + goto cleanup_blt_ring; + + return 0; + +cleanup_blt_ring: + intel_cleanup_ring_buffer(&dev_priv->ring[BCS]); +cleanup_bsd_ring: + intel_cleanup_ring_buffer(&dev_priv->ring[VCS]); +cleanup_render_ring: + intel_cleanup_ring_buffer(&dev_priv->ring[RCS]); + + return ret; +} + +int +i915_gem_init_hw(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + int ret; + + if (INTEL_INFO(dev)->gen < 6 && !intel_enable_gtt()) + return -EIO; + + if (IS_HASWELL(dev) && (I915_READ(0x120010) == 1)) + I915_WRITE(0x9008, I915_READ(0x9008) | 0xf0000); + + i915_gem_l3_remap(dev); + + i915_gem_init_swizzling(dev); + + ret = i915_gem_init_rings(dev); if (ret) return ret; @@ -3974,12 +3995,6 @@ i915_gem_init_hw(struct drm_device *dev) i915_gem_init_ppgtt(dev); return 0; - -cleanup_bsd_ring: - intel_cleanup_ring_buffer(&dev_priv->ring[VCS]); -cleanup_render_ring: - intel_cleanup_ring_buffer(&dev_priv->ring[RCS]); - return ret; } int i915_gem_init(struct drm_device *dev) -- cgit v1.2.3 From f73f760725636b9d0c3786273e185b053516d1eb Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 11 Feb 2013 13:31:27 -0800 Subject: drm/i915/ctx: Remove bad invariant It's not that the assertion is incorrect, but rather that we can call do_destroy early in loading, and we will falsely BUG(). Since contexts have been in for a while now, and in the internal APIs are pretty stable, it should be fairly safe to remove this. v2: Remove unused dev_priv, and dev Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_context.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index e7ecafa8e598..21177d9df423 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -126,13 +126,8 @@ static int get_context_size(struct drm_device *dev) static void do_destroy(struct i915_hw_context *ctx) { - struct drm_device *dev = ctx->obj->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - if (ctx->file_priv) idr_remove(&ctx->file_priv->context_idr, ctx->id); - else - BUG_ON(ctx != dev_priv->ring[RCS].default_context); drm_gem_object_unreference(&ctx->obj->base); kfree(ctx); -- cgit v1.2.3 From a586069b0f56a700d6f6249a64cbc313dd4a97e0 Mon Sep 17 00:00:00 2001 From: Krishna Mohan Date: Wed, 13 Feb 2013 16:33:04 -0800 Subject: libfc: XenServer fails to mount root filesystem. schedule_delayed_work() is using msec instead of jiffies. On PLOGI reject from target, remote port retry is scheduled @ 20 sec instead of 2sec(FC_DEF_E_D_TOV). XenServer dom0 kernel is configured with CONFIG_HZ=100Hz Signed-off-by: Krishna Mohan Signed-off-by: Robert Love --- drivers/scsi/libfc/fc_rport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index 83aa1efec875..d518d17e940f 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c @@ -582,7 +582,7 @@ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp) static void fc_rport_error_retry(struct fc_rport_priv *rdata, struct fc_frame *fp) { - unsigned long delay = FC_DEF_E_D_TOV; + unsigned long delay = msecs_to_jiffies(FC_DEF_E_D_TOV); /* make sure this isn't an FC_EX_CLOSED error, never retry those */ if (PTR_ERR(fp) == -FC_EX_CLOSED) -- cgit v1.2.3 From ff7109fa632654eaef657186f2942f5b679023d6 Mon Sep 17 00:00:00 2001 From: Aaron Sierra Date: Thu, 14 Feb 2013 11:35:04 -0600 Subject: mfd: lpc_ich: Use devres API to allocate private data And fix a kzalloc argument inversion bug while converting to devres. Signed-off-by: Aaron Sierra Signed-off-by: Samuel Ortiz --- drivers/mfd/lpc_ich.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index 5c2ef41fa24c..9f12f91d6296 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -916,7 +916,8 @@ static int lpc_ich_probe(struct pci_dev *dev, int ret; bool cell_added = false; - priv = kmalloc(GFP_KERNEL, sizeof(struct lpc_ich_priv)); + priv = devm_kzalloc(&dev->dev, + sizeof(struct lpc_ich_priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -952,7 +953,6 @@ static int lpc_ich_probe(struct pci_dev *dev, dev_warn(&dev->dev, "No MFD cells added\n"); lpc_ich_restore_config_space(dev); pci_set_drvdata(dev, NULL); - kfree(priv); return -ENODEV; } @@ -961,12 +961,9 @@ static int lpc_ich_probe(struct pci_dev *dev, static void lpc_ich_remove(struct pci_dev *dev) { - void *priv = pci_get_drvdata(dev); - mfd_remove_devices(&dev->dev); lpc_ich_restore_config_space(dev); pci_set_drvdata(dev, NULL); - kfree(priv); } static struct pci_driver lpc_ich_driver = { -- cgit v1.2.3 From 111bd981e2216827aab95503596501ab71bc7a7d Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Wed, 26 Dec 2012 19:51:14 +0000 Subject: MIPS: BCM47XX: add bcm47xx prefix in front of nvram function names The nvram functions are exported and used by some normal drivers. To prevent name clashes with ofter parts of the kernel code add a bcm47xx_ prefix in front of the function names and the header file name. Signed-off-by: Hauke Mehrtens Patchwork: http://patchwork.linux-mips.org/patch/4744/ Signed-off-by: John Crispin --- arch/mips/bcm47xx/nvram.c | 6 +-- arch/mips/bcm47xx/setup.c | 6 +-- arch/mips/bcm47xx/sprom.c | 8 ++-- arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h | 51 ++++++++++++++++++++++ arch/mips/include/asm/mach-bcm47xx/nvram.h | 51 ---------------------- drivers/mtd/bcm47xxpart.c | 2 +- drivers/net/ethernet/broadcom/b44.c | 4 +- drivers/ssb/driver_chipcommon_pmu.c | 4 +- include/linux/ssb/ssb_driver_gige.h | 6 +-- 9 files changed, 69 insertions(+), 69 deletions(-) create mode 100644 arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h delete mode 100644 arch/mips/include/asm/mach-bcm47xx/nvram.h (limited to 'drivers') diff --git a/arch/mips/bcm47xx/nvram.c b/arch/mips/bcm47xx/nvram.c index b4a47fcb4f64..e63bd5abd9a2 100644 --- a/arch/mips/bcm47xx/nvram.c +++ b/arch/mips/bcm47xx/nvram.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include static char nvram_buf[NVRAM_SPACE]; @@ -159,7 +159,7 @@ static int nvram_init(void) return -ENXIO; } -int nvram_getenv(char *name, char *val, size_t val_len) +int bcm47xx_nvram_getenv(char *name, char *val, size_t val_len) { char *var, *value, *end, *eq; int err; @@ -189,4 +189,4 @@ int nvram_getenv(char *name, char *val, size_t val_len) } return -ENOENT; } -EXPORT_SYMBOL(nvram_getenv); +EXPORT_SYMBOL(bcm47xx_nvram_getenv); diff --git a/arch/mips/bcm47xx/setup.c b/arch/mips/bcm47xx/setup.c index 4d54b58dbd32..b2246cd9ca12 100644 --- a/arch/mips/bcm47xx/setup.c +++ b/arch/mips/bcm47xx/setup.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include union bcm47xx_bus bcm47xx_bus; EXPORT_SYMBOL(bcm47xx_bus); @@ -115,7 +115,7 @@ static int bcm47xx_get_invariants(struct ssb_bus *bus, memset(&iv->sprom, 0, sizeof(struct ssb_sprom)); bcm47xx_fill_sprom(&iv->sprom, NULL, false); - if (nvram_getenv("cardbus", buf, sizeof(buf)) >= 0) + if (bcm47xx_nvram_getenv("cardbus", buf, sizeof(buf)) >= 0) iv->has_cardbus_slot = !!simple_strtoul(buf, NULL, 10); return 0; @@ -138,7 +138,7 @@ static void __init bcm47xx_register_ssb(void) panic("Failed to initialize SSB bus (err %d)", err); mcore = &bcm47xx_bus.ssb.mipscore; - if (nvram_getenv("kernel_args", buf, sizeof(buf)) >= 0) { + if (bcm47xx_nvram_getenv("kernel_args", buf, sizeof(buf)) >= 0) { if (strstr(buf, "console=ttyS1")) { struct ssb_serial_port port; diff --git a/arch/mips/bcm47xx/sprom.c b/arch/mips/bcm47xx/sprom.c index 66b71c363300..89b9bf46b13b 100644 --- a/arch/mips/bcm47xx/sprom.c +++ b/arch/mips/bcm47xx/sprom.c @@ -27,7 +27,7 @@ */ #include -#include +#include static void create_key(const char *prefix, const char *postfix, const char *name, char *buf, int len) @@ -50,10 +50,10 @@ static int get_nvram_var(const char *prefix, const char *postfix, create_key(prefix, postfix, name, key, sizeof(key)); - err = nvram_getenv(key, buf, len); + err = bcm47xx_nvram_getenv(key, buf, len); if (fallback && err == -ENOENT && prefix) { create_key(NULL, postfix, name, key, sizeof(key)); - err = nvram_getenv(key, buf, len); + err = bcm47xx_nvram_getenv(key, buf, len); } return err; } @@ -144,7 +144,7 @@ static void nvram_read_macaddr(const char *prefix, const char *name, if (err < 0) return; - nvram_parse_macaddr(buf, *val); + bcm47xx_nvram_parse_macaddr(buf, *val); } static void nvram_read_alpha2(const char *prefix, const char *name, diff --git a/arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h b/arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h new file mode 100644 index 000000000000..b8e7be8f34dd --- /dev/null +++ b/arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2005, Broadcom Corporation + * Copyright (C) 2006, Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __BCM47XX_NVRAM_H +#define __BCM47XX_NVRAM_H + +#include +#include + +struct nvram_header { + u32 magic; + u32 len; + u32 crc_ver_init; /* 0:7 crc, 8:15 ver, 16:31 sdram_init */ + u32 config_refresh; /* 0:15 sdram_config, 16:31 sdram_refresh */ + u32 config_ncdl; /* ncdl values for memc */ +}; + +#define NVRAM_HEADER 0x48534C46 /* 'FLSH' */ +#define NVRAM_VERSION 1 +#define NVRAM_HEADER_SIZE 20 +#define NVRAM_SPACE 0x8000 + +#define FLASH_MIN 0x00020000 /* Minimum flash size */ + +#define NVRAM_MAX_VALUE_LEN 255 +#define NVRAM_MAX_PARAM_LEN 64 + +extern int bcm47xx_nvram_getenv(char *name, char *val, size_t val_len); + +static inline void bcm47xx_nvram_parse_macaddr(char *buf, u8 macaddr[6]) +{ + if (strchr(buf, ':')) + sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &macaddr[0], + &macaddr[1], &macaddr[2], &macaddr[3], &macaddr[4], + &macaddr[5]); + else if (strchr(buf, '-')) + sscanf(buf, "%hhx-%hhx-%hhx-%hhx-%hhx-%hhx", &macaddr[0], + &macaddr[1], &macaddr[2], &macaddr[3], &macaddr[4], + &macaddr[5]); + else + printk(KERN_WARNING "Can not parse mac address: %s\n", buf); +} + +#endif /* __BCM47XX_NVRAM_H */ diff --git a/arch/mips/include/asm/mach-bcm47xx/nvram.h b/arch/mips/include/asm/mach-bcm47xx/nvram.h deleted file mode 100644 index 550a7fc932c9..000000000000 --- a/arch/mips/include/asm/mach-bcm47xx/nvram.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2005, Broadcom Corporation - * Copyright (C) 2006, Felix Fietkau - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#ifndef __NVRAM_H -#define __NVRAM_H - -#include -#include - -struct nvram_header { - u32 magic; - u32 len; - u32 crc_ver_init; /* 0:7 crc, 8:15 ver, 16:31 sdram_init */ - u32 config_refresh; /* 0:15 sdram_config, 16:31 sdram_refresh */ - u32 config_ncdl; /* ncdl values for memc */ -}; - -#define NVRAM_HEADER 0x48534C46 /* 'FLSH' */ -#define NVRAM_VERSION 1 -#define NVRAM_HEADER_SIZE 20 -#define NVRAM_SPACE 0x8000 - -#define FLASH_MIN 0x00020000 /* Minimum flash size */ - -#define NVRAM_MAX_VALUE_LEN 255 -#define NVRAM_MAX_PARAM_LEN 64 - -extern int nvram_getenv(char *name, char *val, size_t val_len); - -static inline void nvram_parse_macaddr(char *buf, u8 macaddr[6]) -{ - if (strchr(buf, ':')) - sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &macaddr[0], - &macaddr[1], &macaddr[2], &macaddr[3], &macaddr[4], - &macaddr[5]); - else if (strchr(buf, '-')) - sscanf(buf, "%hhx-%hhx-%hhx-%hhx-%hhx-%hhx", &macaddr[0], - &macaddr[1], &macaddr[2], &macaddr[3], &macaddr[4], - &macaddr[5]); - else - printk(KERN_WARNING "Can not parse mac address: %s\n", buf); -} - -#endif diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c index e06d782489a6..6a1180502cc1 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/bcm47xxpart.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include /* 10 parts were found on sflash on Netgear WNDR4500 */ #define BCM47XXPART_MAX_PARTS 12 diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c index 219f6226fcb1..330b0908bf97 100644 --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -381,7 +381,7 @@ static void b44_set_flow_ctrl(struct b44 *bp, u32 local, u32 remote) } #ifdef CONFIG_BCM47XX -#include +#include static void b44_wap54g10_workaround(struct b44 *bp) { char buf[20]; @@ -393,7 +393,7 @@ static void b44_wap54g10_workaround(struct b44 *bp) * see https://dev.openwrt.org/ticket/146 * check and reset bit "isolate" */ - if (nvram_getenv("boardnum", buf, sizeof(buf)) < 0) + if (bcm47xx_nvram_getenv("boardnum", buf, sizeof(buf)) < 0) return; if (simple_strtoul(buf, NULL, 0) == 2) { err = __b44_readphy(bp, 0, MII_BMCR, &val); diff --git a/drivers/ssb/driver_chipcommon_pmu.c b/drivers/ssb/driver_chipcommon_pmu.c index a43415a7fbed..4c0f6d883dd3 100644 --- a/drivers/ssb/driver_chipcommon_pmu.c +++ b/drivers/ssb/driver_chipcommon_pmu.c @@ -14,7 +14,7 @@ #include #include #ifdef CONFIG_BCM47XX -#include +#include #endif #include "ssb_private.h" @@ -322,7 +322,7 @@ static void ssb_pmu_pll_init(struct ssb_chipcommon *cc) if (bus->bustype == SSB_BUSTYPE_SSB) { #ifdef CONFIG_BCM47XX char buf[20]; - if (nvram_getenv("xtalfreq", buf, sizeof(buf)) >= 0) + if (bcm47xx_nvram_getenv("xtalfreq", buf, sizeof(buf)) >= 0) crystalfreq = simple_strtoul(buf, NULL, 0); #endif } diff --git a/include/linux/ssb/ssb_driver_gige.h b/include/linux/ssb/ssb_driver_gige.h index 6b05dcd927ff..6d92a92573df 100644 --- a/include/linux/ssb/ssb_driver_gige.h +++ b/include/linux/ssb/ssb_driver_gige.h @@ -98,14 +98,14 @@ static inline bool ssb_gige_must_flush_posted_writes(struct pci_dev *pdev) } #ifdef CONFIG_BCM47XX -#include +#include /* Get the device MAC address */ static inline void ssb_gige_get_macaddr(struct pci_dev *pdev, u8 *macaddr) { char buf[20]; - if (nvram_getenv("et0macaddr", buf, sizeof(buf)) < 0) + if (bcm47xx_nvram_getenv("et0macaddr", buf, sizeof(buf)) < 0) return; - nvram_parse_macaddr(buf, macaddr); + bcm47xx_nvram_parse_macaddr(buf, macaddr); } #else static inline void ssb_gige_get_macaddr(struct pci_dev *pdev, u8 *macaddr) -- cgit v1.2.3 From ab1a2e038ff2336502e95ec6492c0364a9fc70d0 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Sat, 19 Jan 2013 00:07:42 +0800 Subject: ACPI / PCI: Make pci_slot built-in only, not a module As discussed in thread at https://patchwork.kernel.org/patch/1946851/, there's no value in supporting CONFIG_ACPI_PCI_SLOT=m any more. So change Kconfig and code to only support building pci_slot as built-in driver. Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas --- drivers/acpi/Kconfig | 5 +---- drivers/acpi/internal.h | 5 +++++ drivers/acpi/pci_slot.c | 13 +------------ drivers/acpi/scan.c | 1 + 4 files changed, 8 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 38c5078da11d..27fde5e8d31a 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -308,7 +308,7 @@ config ACPI_DEBUG_FUNC_TRACE is about half of the penalty and is rarely useful. config ACPI_PCI_SLOT - tristate "PCI slot detection driver" + bool "PCI slot detection driver" depends on SYSFS default n help @@ -317,9 +317,6 @@ config ACPI_PCI_SLOT i.e., segment/bus/device/function tuples, with physical slots in the system. If you are unsure, say N. - To compile this driver as a module, choose M here: - the module will be called pci_slot. - config X86_PM_TIMER bool "Power Management Timer Support" if EXPERT depends on X86 diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index e050254ae143..7374cfc5917a 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -67,6 +67,11 @@ struct acpi_ec { extern struct acpi_ec *first_ec; +#ifdef CONFIG_ACPI_PCI_SLOT +void acpi_pci_slot_init(void); +#else +static inline void acpi_pci_slot_init(void) { } +#endif int acpi_pci_root_init(void); int acpi_ec_init(void); int acpi_ec_ecdt_probe(void); diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c index d22585f21aeb..a7d7e7710f9e 100644 --- a/drivers/acpi/pci_slot.c +++ b/drivers/acpi/pci_slot.c @@ -330,19 +330,8 @@ static struct dmi_system_id acpi_pci_slot_dmi_table[] __initdata = { {} }; -static int __init -acpi_pci_slot_init(void) +void __init acpi_pci_slot_init(void) { dmi_check_system(acpi_pci_slot_dmi_table); acpi_pci_register_driver(&acpi_pci_slot_driver); - return 0; } - -static void __exit -acpi_pci_slot_exit(void) -{ - acpi_pci_unregister_driver(&acpi_pci_slot_driver); -} - -module_init(acpi_pci_slot_init); -module_exit(acpi_pci_slot_exit); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 7c43bdc36abc..236e476b09c9 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1687,6 +1687,7 @@ int __init acpi_scan_init(void) acpi_power_init(); acpi_pci_root_init(); + acpi_pci_slot_init(); /* * Enumerate devices in the ACPI namespace. -- cgit v1.2.3 From 969daa349f4821a02936af7202b51a9affc7b6da Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 14 Feb 2013 11:35:42 -0700 Subject: PCI: Fix PCI Express Capability accessors for PCI_EXP_FLAGS PCI_EXP_FLAGS_TYPE is a mask, not an offset. Fix it. Previously, pcie_capability_read_word(..., PCI_EXP_FLAGS, ...) would fail. [bhelgaas: tweak changelog] Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas CC: stable@vger.kernel.org # v3.7+ --- drivers/pci/access.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 5278ac692cbf..1cc23661f79b 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -515,7 +515,7 @@ static bool pcie_capability_reg_implemented(struct pci_dev *dev, int pos) return false; switch (pos) { - case PCI_EXP_FLAGS_TYPE: + case PCI_EXP_FLAGS: return true; case PCI_EXP_DEVCAP: case PCI_EXP_DEVCTL: -- cgit v1.2.3 From a3fc0ff00a46c4b32e7214961a5be9a1dc39b60e Mon Sep 17 00:00:00 2001 From: James Ralston Date: Thu, 14 Feb 2013 09:15:33 +0000 Subject: i2c: i801: Add Device IDs for Intel Wellsburg PCH This patch adds the SMBus Device IDs for the Intel Wellsburg PCH Signed-off-by: James Ralston Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- Documentation/i2c/busses/i2c-i801 | 1 + drivers/i2c/busses/Kconfig | 1 + drivers/i2c/busses/i2c-i801.c | 15 +++++++++++++++ 3 files changed, 17 insertions(+) (limited to 'drivers') diff --git a/Documentation/i2c/busses/i2c-i801 b/Documentation/i2c/busses/i2c-i801 index 8d71d5705b2a..d55b8ab2d10f 100644 --- a/Documentation/i2c/busses/i2c-i801 +++ b/Documentation/i2c/busses/i2c-i801 @@ -23,6 +23,7 @@ Supported adapters: * Intel Lynx Point (PCH) * Intel Lynx Point-LP (PCH) * Intel Avoton (SOC) + * Intel Wellsburg (PCH) Datasheets: Publicly available at the Intel website On Intel Patsburg and later chipsets, both the normal host SMBus controller diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 8cbd9cd1e0fa..b588317f2393 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -107,6 +107,7 @@ config I2C_I801 Lynx Point (PCH) Lynx Point-LP (PCH) Avoton (SOC) + Wellsburg (PCH) This driver can also be built as a module. If so, the module will be called i2c-i801. diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index b00c29d8a5f1..76febfb09760 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -54,6 +54,10 @@ Lynx Point (PCH) 0x8c22 32 hard yes yes yes Lynx Point-LP (PCH) 0x9c22 32 hard yes yes yes Avoton (SOC) 0x1f3c 32 hard yes yes yes + Wellsburg (PCH) 0x8d22 32 hard yes yes yes + Wellsburg (PCH) MS 0x8d7d 32 hard yes yes yes + Wellsburg (PCH) MS 0x8d7e 32 hard yes yes yes + Wellsburg (PCH) MS 0x8d7f 32 hard yes yes yes Features supported by this driver: Software PEC no @@ -167,6 +171,10 @@ #define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330 #define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22 +#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS 0x8d22 +#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS0 0x8d7d +#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1 0x8d7e +#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2 0x8d7f #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS 0x9c22 struct i801_mux_config { @@ -801,6 +809,10 @@ static DEFINE_PCI_DEVICE_TABLE(i801_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_AVOTON_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2) }, { 0, } }; @@ -1106,6 +1118,9 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0: case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1: case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2: + case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS0: + case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1: + case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2: priv->features |= FEATURE_IDF; /* fall through */ default: -- cgit v1.2.3 From 724d5edac76d8c9a4198b74c80286df38ed81679 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 15 Feb 2013 08:51:40 +0000 Subject: i2c: fix i2c-ismt.c printk format warning Fix printk format warning. dma_addr_t can be 32-bit or 64-bit, so cast it to long long for printing. This also matches the printk format specifier that is already used. drivers/i2c/busses/i2c-ismt.c:532:3: warning: format '%llX' expects argument of type 'long long unsigned int', but argument 4 has type 'dma_addr_t' [-Wformat] Signed-off-by: Randy Dunlap Acked-by: Neil Horman Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-ismt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c index 3f7a9cb6dfdd..e9205ee8cf94 100644 --- a/drivers/i2c/busses/i2c-ismt.c +++ b/drivers/i2c/busses/i2c-ismt.c @@ -530,7 +530,7 @@ static int ismt_access(struct i2c_adapter *adap, u16 addr, } dev_dbg(dev, " dma_addr = 0x%016llX\n", - dma_addr); + (unsigned long long)dma_addr); desc->dptr_low = lower_32_bits(dma_addr); desc->dptr_high = upper_32_bits(dma_addr); -- cgit v1.2.3 From 58823c72f6b95414fa11cb8a0e5f1d0e548c2f34 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 14 Feb 2013 18:13:33 +0530 Subject: i2c: tegra: remove warning dump if timeout happen in transfer If timeout error occurs in the i2c transfer then it was dumping warning of call stack. Remove the warning dump as there is may be possibility that some slave devices are busy and not responding the i2c communication. Signed-off-by: Laxman Dewangan Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 2dadb964c464..e58a58d7231c 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -588,7 +588,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, ret = wait_for_completion_timeout(&i2c_dev->msg_complete, TEGRA_I2C_TIMEOUT); tegra_i2c_mask_irq(i2c_dev, int_mask); - if (WARN_ON(ret == 0)) { + if (ret == 0) { dev_err(i2c_dev->dev, "i2c transfer timed out\n"); tegra_i2c_init(i2c_dev); -- cgit v1.2.3 From 838bf09d4fa01d39417e42c731aa31d7a256fb7c Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 15 Feb 2013 15:02:22 -0700 Subject: pwm: tegra: assume CONFIG_OF Tegra only supports, and always enables, device tree. Remove all ifdefs for DT support from the driver. Signed-off-by: Stephen Warren Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tegra.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index 30c0e2b70ce8..f7770312c1a9 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -233,7 +233,6 @@ static int tegra_pwm_remove(struct platform_device *pdev) return pwmchip_remove(&pc->chip); } -#ifdef CONFIG_OF static struct of_device_id tegra_pwm_of_match[] = { { .compatible = "nvidia,tegra20-pwm" }, { .compatible = "nvidia,tegra30-pwm" }, @@ -241,12 +240,11 @@ static struct of_device_id tegra_pwm_of_match[] = { }; MODULE_DEVICE_TABLE(of, tegra_pwm_of_match); -#endif static struct platform_driver tegra_pwm_driver = { .driver = { .name = "tegra-pwm", - .of_match_table = of_match_ptr(tegra_pwm_of_match), + .of_match_table = tegra_pwm_of_match, }, .probe = tegra_pwm_probe, .remove = tegra_pwm_remove, -- cgit v1.2.3 From b23523d8585906c0fb7c78616e3deca18c7fcce2 Mon Sep 17 00:00:00 2001 From: Stefan Hasko Date: Sat, 22 Dec 2012 02:29:21 +0000 Subject: RDMA/cxgb4: Fix cast warning Fix compile warning about cast to pointer from integer of different size. Signed-off-by: Stefan Hasko Acked-by: Steve Wise Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c index dc6adb213cd6..80069ad595c1 100644 --- a/drivers/infiniband/hw/cxgb4/device.c +++ b/drivers/infiniband/hw/cxgb4/device.c @@ -533,7 +533,7 @@ static int c4iw_rdev_open(struct c4iw_rdev *rdev) PDBG("udb len 0x%x udb base %p db_reg %p gts_reg %p qpshift %lu " "qpmask 0x%x cqshift %lu cqmask 0x%x\n", (unsigned)pci_resource_len(rdev->lldi.pdev, 2), - (void *)pci_resource_start(rdev->lldi.pdev, 2), + (void *)(unsigned long)pci_resource_start(rdev->lldi.pdev, 2), rdev->lldi.db_reg, rdev->lldi.gts_reg, rdev->qpshift, rdev->qpmask, -- cgit v1.2.3 From 8ab10f75372fc59b95d96af40b9a3b4c2a0da059 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 10 Oct 2012 13:10:43 +0000 Subject: RDMA/amso1100: Use module_pci_driver() to simplify the code Use the module_pci_driver() macro to make the code simpler by eliminating module_init and module_exit calls. dpatch engine is used to auto generate this patch. (https://github.com/weiyj/dpatch) Signed-off-by: Wei Yongjun Reviewed-by: Steve WIse Signed-off-by: Roland Dreier --- drivers/infiniband/hw/amso1100/c2.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/amso1100/c2.c b/drivers/infiniband/hw/amso1100/c2.c index 7275e727e0f5..d53cf519f42a 100644 --- a/drivers/infiniband/hw/amso1100/c2.c +++ b/drivers/infiniband/hw/amso1100/c2.c @@ -1238,15 +1238,4 @@ static struct pci_driver c2_pci_driver = { .remove = c2_remove, }; -static int __init c2_init_module(void) -{ - return pci_register_driver(&c2_pci_driver); -} - -static void __exit c2_exit_module(void) -{ - pci_unregister_driver(&c2_pci_driver); -} - -module_init(c2_init_module); -module_exit(c2_exit_module); +module_pci_driver(c2_pci_driver); -- cgit v1.2.3 From cab66d1273e6490603cf5256155584d38cecca68 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 4 Feb 2013 11:22:36 +0000 Subject: IB/mlx4: Fix bug unwinding on error in mlx4_ib_init_sriov() We have to decrement "i" before calling mlx4_ib_free_demux_ctx() or we free something that wasn't allocated. That's fine for free_pv_object() but it would lead to a NULL dereference calling mlx4_ib_free_demux_ctx(). The null dereference is because ->tun is NULL when we check: if (!ctx->tun[i]) Also we didn't free ->sriov.demux[0] so it was a small leak. Signed-off-by: Dan Carpenter Signed-off-by: Roland Dreier --- drivers/infiniband/hw/mlx4/mad.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c index 0a903c129f0a..934792c477bc 100644 --- a/drivers/infiniband/hw/mlx4/mad.c +++ b/drivers/infiniband/hw/mlx4/mad.c @@ -1999,16 +1999,17 @@ int mlx4_ib_init_sriov(struct mlx4_ib_dev *dev) goto demux_err; err = mlx4_ib_alloc_demux_ctx(dev, &dev->sriov.demux[i], i + 1); if (err) - goto demux_err; + goto free_pv; } mlx4_ib_master_tunnels(dev, 1); return 0; +free_pv: + free_pv_object(dev, mlx4_master_func_num(dev->dev), i + 1); demux_err: - while (i > 0) { + while (--i >= 0) { free_pv_object(dev, mlx4_master_func_num(dev->dev), i + 1); mlx4_ib_free_demux_ctx(&dev->sriov.demux[i]); - --i; } mlx4_ib_device_unregister_sysfs(dev); -- cgit v1.2.3 From 6950a235b86cf4e73d2b8e5476e7d0eb8f61af63 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 21 Jan 2013 13:02:58 +0000 Subject: IB/mlx4: Adjust duplicate test Delete successive tests to the same location. The code tested the result of a previous allocation, that itself was already tested. It is changed to test the result of the most recent allocation. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @s exists@ local idexpression y; expression x,e; @@ *if ( \(x == NULL\|IS_ERR(x)\|y != 0\) ) { ... when forall return ...; } ... when != \(y = e\|y += e\|y -= e\|y |= e\|y &= e\|y++\|y--\|&y\) when != \(XT_GETPAGE(...,y)\|WMI_CMD_BUF(...)\) *if ( \(x == NULL\|IS_ERR(x)\|y != 0\) ) { ... when forall return ...; } // Signed-off-by: Julia Lawall Signed-off-by: Roland Dreier --- drivers/infiniband/hw/mlx4/sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/mlx4/sysfs.c b/drivers/infiniband/hw/mlx4/sysfs.c index 5b2a01dfb907..97516eb363b7 100644 --- a/drivers/infiniband/hw/mlx4/sysfs.c +++ b/drivers/infiniband/hw/mlx4/sysfs.c @@ -732,7 +732,7 @@ int mlx4_ib_device_register_sysfs(struct mlx4_ib_dev *dev) dev->ports_parent = kobject_create_and_add("ports", kobject_get(dev->iov_parent)); - if (!dev->iov_parent) { + if (!dev->ports_parent) { ret = -ENOMEM; goto err_ports; } -- cgit v1.2.3 From 181380b702eee1a9aca51354d7b87c7b08541fcf Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 16 Feb 2013 11:58:34 -0700 Subject: PCI/ACPI: Don't cache _PRT, and don't associate them with bus numbers Previously, we cached _PRT (PCI routing table, ACPI 5.0 sec 6.2.12) contents and associated each _PRT entry with a PCI bus number. The bus number association means dependencies on PCI device enumeration and bus number assignment, as well as on the PCI/ACPI binding process. After 4f535093cf ("PCI: Put pci_dev in device tree as early as possible"), these dependencies caused the IRQ issues reported by Peter: pci 0000:00:1e.0: PCI bridge to [bus 09] (subtractive decode) pci 0000:00:1e.0: can't derive routing for PCI INT A snd_ctxfi 0000:09:02.0: PCI INT A: no GSI - using ISA IRQ 5 irq 18: nobody cared (try booting with the "irqpoll" option) This patch removes _PRT caching. Instead, we evaluate _PRT as needed in the pci_enable_device() path. This also removes the dependency on PCI bus numbers: we can simply look at the _PRT associated with each bridge as we walk upstream toward the root. [bhelgaas: changelog] Reference: https://bugzilla.kernel.org/show_bug.cgi?id=53561 Reported-and-tested-by: Peter Hurley Suggested-by: Bjorn Helgaas Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/acpi/pci_irq.c | 102 +++++++++++++++----------------------------- drivers/acpi/pci_root.c | 18 -------- drivers/pci/pci-acpi.c | 24 ----------- include/acpi/acpi_drivers.h | 5 --- 4 files changed, 34 insertions(+), 115 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 68a921d03247..41c5e1b799ef 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -53,9 +53,6 @@ struct acpi_prt_entry { u32 index; /* GSI, or link _CRS index */ }; -static LIST_HEAD(acpi_prt_list); -static DEFINE_SPINLOCK(acpi_prt_lock); - static inline char pin_name(int pin) { return 'A' + pin - 1; @@ -65,28 +62,6 @@ static inline char pin_name(int pin) PCI IRQ Routing Table (PRT) Support -------------------------------------------------------------------------- */ -static struct acpi_prt_entry *acpi_pci_irq_find_prt_entry(struct pci_dev *dev, - int pin) -{ - struct acpi_prt_entry *entry; - int segment = pci_domain_nr(dev->bus); - int bus = dev->bus->number; - int device = PCI_SLOT(dev->devfn); - - spin_lock(&acpi_prt_lock); - list_for_each_entry(entry, &acpi_prt_list, list) { - if ((segment == entry->id.segment) - && (bus == entry->id.bus) - && (device == entry->id.device) - && (pin == entry->pin)) { - spin_unlock(&acpi_prt_lock); - return entry; - } - } - spin_unlock(&acpi_prt_lock); - return NULL; -} - /* http://bugzilla.kernel.org/show_bug.cgi?id=4773 */ static const struct dmi_system_id medion_md9580[] = { { @@ -184,11 +159,19 @@ static void do_prt_fixups(struct acpi_prt_entry *entry, } } -static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus, - struct acpi_pci_routing_table *prt) +static int acpi_pci_irq_check_entry(acpi_handle handle, struct pci_dev *dev, + int pin, struct acpi_pci_routing_table *prt, + struct acpi_prt_entry **entry_ptr) { + int segment = pci_domain_nr(dev->bus); + int bus = dev->bus->number; + int device = PCI_SLOT(dev->devfn); struct acpi_prt_entry *entry; + if (((prt->address >> 16) & 0xffff) != device || + prt->pin + 1 != pin) + return -ENODEV; + entry = kzalloc(sizeof(struct acpi_prt_entry), GFP_KERNEL); if (!entry) return -ENOMEM; @@ -237,43 +220,37 @@ static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus, entry->id.device, pin_name(entry->pin), prt->source, entry->index)); - spin_lock(&acpi_prt_lock); - list_add_tail(&entry->list, &acpi_prt_list); - spin_unlock(&acpi_prt_lock); + *entry_ptr = entry; return 0; } -int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus) +static int acpi_pci_irq_find_prt_entry(struct pci_dev *dev, + int pin, struct acpi_prt_entry **entry_ptr) { acpi_status status; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_pci_routing_table *entry; + acpi_handle handle = NULL; - /* 'handle' is the _PRT's parent (root bridge or PCI-PCI bridge) */ - status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); - if (ACPI_FAILURE(status)) - return -ENODEV; - - printk(KERN_DEBUG "ACPI: PCI Interrupt Routing Table [%s._PRT]\n", - (char *) buffer.pointer); - - kfree(buffer.pointer); + if (dev->bus->bridge) + handle = ACPI_HANDLE(dev->bus->bridge); - buffer.length = ACPI_ALLOCATE_BUFFER; - buffer.pointer = NULL; + if (!handle) + return -ENODEV; + /* 'handle' is the _PRT's parent (root bridge or PCI-PCI bridge) */ status = acpi_get_irq_routing_table(handle, &buffer); if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PRT [%s]", - acpi_format_exception(status))); kfree(buffer.pointer); return -ENODEV; } entry = buffer.pointer; while (entry && (entry->length > 0)) { - acpi_pci_irq_add_entry(handle, segment, bus, entry); + if (!acpi_pci_irq_check_entry(handle, dev, pin, + entry, entry_ptr)) + break; entry = (struct acpi_pci_routing_table *) ((unsigned long)entry + entry->length); } @@ -282,23 +259,6 @@ int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus) return 0; } -void acpi_pci_irq_del_prt(int segment, int bus) -{ - struct acpi_prt_entry *entry, *tmp; - - printk(KERN_DEBUG - "ACPI: Delete PCI Interrupt Routing Table for %04x:%02x\n", - segment, bus); - spin_lock(&acpi_prt_lock); - list_for_each_entry_safe(entry, tmp, &acpi_prt_list, list) { - if (segment == entry->id.segment && bus == entry->id.bus) { - list_del(&entry->list); - kfree(entry); - } - } - spin_unlock(&acpi_prt_lock); -} - /* -------------------------------------------------------------------------- PCI Interrupt Routing Support -------------------------------------------------------------------------- */ @@ -359,12 +319,13 @@ static int acpi_reroute_boot_interrupt(struct pci_dev *dev, static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) { - struct acpi_prt_entry *entry; + struct acpi_prt_entry *entry = NULL; struct pci_dev *bridge; u8 bridge_pin, orig_pin = pin; + int ret; - entry = acpi_pci_irq_find_prt_entry(dev, pin); - if (entry) { + ret = acpi_pci_irq_find_prt_entry(dev, pin, &entry); + if (!ret && entry) { #ifdef CONFIG_X86_IO_APIC acpi_reroute_boot_interrupt(dev, entry); #endif /* CONFIG_X86_IO_APIC */ @@ -373,7 +334,7 @@ static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) return entry; } - /* + /* * Attempt to derive an IRQ for this device from a parent bridge's * PCI interrupt routing entry (eg. yenta bridge and add-in card bridge). */ @@ -393,8 +354,8 @@ static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) pin = bridge_pin; } - entry = acpi_pci_irq_find_prt_entry(bridge, pin); - if (entry) { + ret = acpi_pci_irq_find_prt_entry(bridge, pin, &entry); + if (!ret && entry) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Derived GSI for %s INT %c from %s\n", pci_name(dev), pin_name(orig_pin), @@ -470,6 +431,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev) dev_warn(&dev->dev, "PCI INT %c: no GSI\n", pin_name(pin)); } + return 0; } @@ -477,6 +439,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev) if (rc < 0) { dev_warn(&dev->dev, "PCI INT %c: failed to register GSI\n", pin_name(pin)); + kfree(entry); return rc; } dev->irq = rc; @@ -491,6 +454,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev) (triggering == ACPI_LEVEL_SENSITIVE) ? "level" : "edge", (polarity == ACPI_ACTIVE_LOW) ? "low" : "high", dev->irq); + kfree(entry); return 0; } @@ -513,6 +477,8 @@ void acpi_pci_irq_disable(struct pci_dev *dev) else gsi = entry->index; + kfree(entry); + /* * TBD: It might be worth clearing dev->irq by magic constant * (e.g. PCI_UNDEFINED_IRQ). diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index fd59f57d3829..8545b1d22811 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -434,7 +434,6 @@ static int acpi_pci_root_add(struct acpi_device *device) acpi_status status; int result; struct acpi_pci_root *root; - acpi_handle handle; struct acpi_pci_driver *driver; u32 flags, base_flags; bool is_osc_granted = false; @@ -489,16 +488,6 @@ static int acpi_pci_root_add(struct acpi_device *device) acpi_device_name(device), acpi_device_bid(device), root->segment, &root->secondary); - /* - * PCI Routing Table - * ----------------- - * Evaluate and parse _PRT, if exists. - */ - status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle); - if (ACPI_SUCCESS(status)) - result = acpi_pci_irq_add_prt(device->handle, root->segment, - root->secondary.start); - root->mcfg_addr = acpi_pci_root_get_mcfg_addr(device->handle); /* @@ -623,7 +612,6 @@ out_del_root: list_del(&root->node); mutex_unlock(&acpi_pci_root_lock); - acpi_pci_irq_del_prt(root->segment, root->secondary.start); end: kfree(root); return result; @@ -631,8 +619,6 @@ end: static int acpi_pci_root_remove(struct acpi_device *device, int type) { - acpi_status status; - acpi_handle handle; struct acpi_pci_root *root = acpi_driver_data(device); struct acpi_pci_driver *driver; @@ -647,10 +633,6 @@ static int acpi_pci_root_remove(struct acpi_device *device, int type) device_set_run_wake(root->bus->bridge, false); pci_acpi_remove_bus_pm_notifier(device); - status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle); - if (ACPI_SUCCESS(status)) - acpi_pci_irq_del_prt(root->segment, root->secondary.start); - pci_remove_root_bus(root->bus); mutex_lock(&acpi_pci_root_lock); diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 42736e213f25..9ba9df5f6161 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -325,25 +325,6 @@ static void pci_acpi_setup(struct device *dev) struct pci_dev *pci_dev = to_pci_dev(dev); acpi_handle handle = ACPI_HANDLE(dev); struct acpi_device *adev; - acpi_status status; - acpi_handle dummy; - - /* - * Evaluate and parse _PRT, if exists. This code allows parsing of - * _PRT objects within the scope of non-bridge devices. Note that - * _PRTs within the scope of a PCI bridge assume the bridge's - * subordinate bus number. - * - * TBD: Can _PRTs exist within the scope of non-bridge PCI devices? - */ - status = acpi_get_handle(handle, METHOD_NAME__PRT, &dummy); - if (ACPI_SUCCESS(status)) { - unsigned char bus; - - bus = pci_dev->subordinate ? - pci_dev->subordinate->number : pci_dev->bus->number; - acpi_pci_irq_add_prt(handle, pci_domain_nr(pci_dev->bus), bus); - } acpi_power_resource_register_device(dev, handle); if (acpi_bus_get_device(handle, &adev) || !adev->wakeup.flags.valid) @@ -359,7 +340,6 @@ static void pci_acpi_setup(struct device *dev) static void pci_acpi_cleanup(struct device *dev) { - struct pci_dev *pci_dev = to_pci_dev(dev); acpi_handle handle = ACPI_HANDLE(dev); struct acpi_device *adev; @@ -369,10 +349,6 @@ static void pci_acpi_cleanup(struct device *dev) pci_acpi_remove_pm_notifier(adev); } acpi_power_resource_unregister_device(dev, handle); - - if (pci_dev->subordinate) - acpi_pci_irq_del_prt(pci_domain_nr(pci_dev->bus), - pci_dev->subordinate->number); } static struct acpi_bus_type acpi_pci_bus = { diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index 8b1d7a6a9695..627749af0ba7 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -90,11 +90,6 @@ int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering, int *polarity, char **name); int acpi_pci_link_free_irq(acpi_handle handle); -/* ACPI PCI Interrupt Routing (pci_irq.c) */ - -int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus); -void acpi_pci_irq_del_prt(int segment, int bus); - /* ACPI PCI Device Binding (pci_bind.c) */ struct pci_bus; -- cgit v1.2.3 From 4836d15789c85224662d3e037694d15ec9901631 Mon Sep 17 00:00:00 2001 From: Andy Gross Date: Wed, 19 Dec 2012 14:53:37 -0600 Subject: drm/omap: Add PM capabilities Added power management capabilities into the omapdrm and DMM drivers. During suspend, we don't need to do anything to maintain the state of the LUT. We have all the necessary information to recreate the mappings of the GEM object list maintained by the omapdrm driver. On resume, the DMM resume handler will first reprogram the LUT to point to the dummy page. The subsequent resume handler in the omapdrm will call into the DMM and reprogram each of the buffer objects. This will ensure that all of the necessary objects will be pinned into the DMM properly. Order of suspend/resume handlers is done by device creation. We create the DMM device before the omapdrm, so the correct order is maintained. Signed-off-by: Andy Gross Signed-off-by: Rob Clark Signed-off-by: Greg Kroah-Hartman --- drivers/staging/omapdrm/omap_dmm_tiler.c | 34 ++++++++++++++++++++++++++++++++ drivers/staging/omapdrm/omap_drv.c | 14 +++++++++++++ drivers/staging/omapdrm/omap_drv.h | 4 ++++ drivers/staging/omapdrm/omap_gem.c | 28 ++++++++++++++++++++++++++ 4 files changed, 80 insertions(+) (limited to 'drivers') diff --git a/drivers/staging/omapdrm/omap_dmm_tiler.c b/drivers/staging/omapdrm/omap_dmm_tiler.c index 59bf43899fc0..c42c5e500e02 100644 --- a/drivers/staging/omapdrm/omap_dmm_tiler.c +++ b/drivers/staging/omapdrm/omap_dmm_tiler.c @@ -903,12 +903,46 @@ error: } #endif +#ifdef CONFIG_PM +static int omap_dmm_resume(struct device *dev) +{ + struct tcm_area area; + int i; + + if (!omap_dmm) + return -ENODEV; + + area = (struct tcm_area) { + .is2d = true, + .tcm = NULL, + .p1.x = omap_dmm->container_width - 1, + .p1.y = omap_dmm->container_height - 1, + }; + + /* initialize all LUTs to dummy page entries */ + for (i = 0; i < omap_dmm->num_lut; i++) { + area.tcm = omap_dmm->tcm[i]; + if (fill(&area, NULL, 0, 0, true)) + dev_err(dev, "refill failed"); + } + + return 0; +} + +static const struct dev_pm_ops omap_dmm_pm_ops = { + .resume = omap_dmm_resume, +}; +#endif + struct platform_driver omap_dmm_driver = { .probe = omap_dmm_probe, .remove = omap_dmm_remove, .driver = { .owner = THIS_MODULE, .name = DMM_DRIVER_NAME, +#ifdef CONFIG_PM + .pm = &omap_dmm_pm_ops, +#endif }, }; diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/staging/omapdrm/omap_drv.c index dfdb4ba1e7c6..9d6a584113d2 100644 --- a/drivers/staging/omapdrm/omap_drv.c +++ b/drivers/staging/omapdrm/omap_drv.c @@ -368,6 +368,9 @@ static int dev_load(struct drm_device *dev, unsigned long flags) /* well, limp along without an fbdev.. maybe X11 will work? */ } + /* store off drm_device for use in pm ops */ + dev_set_drvdata(dev->dev, dev); + drm_kms_helper_poll_init(dev); return 0; @@ -393,6 +396,8 @@ static int dev_unload(struct drm_device *dev) kfree(dev->dev_private); dev->dev_private = NULL; + dev_set_drvdata(dev->dev, NULL); + return 0; } @@ -558,10 +563,19 @@ static int pdev_remove(struct platform_device *device) return 0; } +#ifdef CONFIG_PM +static const struct dev_pm_ops omapdrm_pm_ops = { + .resume = omap_gem_resume, +}; +#endif + struct platform_driver pdev = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &omapdrm_pm_ops, +#endif }, .probe = pdev_probe, .remove = pdev_remove, diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h index cd1f22b0b124..f921027e7500 100644 --- a/drivers/staging/omapdrm/omap_drv.h +++ b/drivers/staging/omapdrm/omap_drv.h @@ -135,6 +135,10 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m); void omap_gem_describe_objects(struct list_head *list, struct seq_file *m); #endif +#ifdef CONFIG_PM +int omap_gem_resume(struct device *dev); +#endif + int omap_irq_enable_vblank(struct drm_device *dev, int crtc); void omap_irq_disable_vblank(struct drm_device *dev, int crtc); irqreturn_t omap_irq_handler(DRM_IRQ_ARGS); diff --git a/drivers/staging/omapdrm/omap_gem.c b/drivers/staging/omapdrm/omap_gem.c index c38992b76fc9..08f1e292ed20 100644 --- a/drivers/staging/omapdrm/omap_gem.c +++ b/drivers/staging/omapdrm/omap_gem.c @@ -964,6 +964,34 @@ void *omap_gem_vaddr(struct drm_gem_object *obj) return omap_obj->vaddr; } +#ifdef CONFIG_PM +/* re-pin objects in DMM in resume path: */ +int omap_gem_resume(struct device *dev) +{ + struct drm_device *drm_dev = dev_get_drvdata(dev); + struct omap_drm_private *priv = drm_dev->dev_private; + struct omap_gem_object *omap_obj; + int ret = 0; + + list_for_each_entry(omap_obj, &priv->obj_list, mm_list) { + if (omap_obj->block) { + struct drm_gem_object *obj = &omap_obj->base; + uint32_t npages = obj->size >> PAGE_SHIFT; + WARN_ON(!omap_obj->pages); /* this can't happen */ + ret = tiler_pin(omap_obj->block, + omap_obj->pages, npages, + omap_obj->roll, true); + if (ret) { + dev_err(dev, "could not repin: %d\n", ret); + return ret; + } + } + } + + return 0; +} +#endif + #ifdef CONFIG_DEBUG_FS void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m) { -- cgit v1.2.3 From 238083ad5d67d1c9ba6e7158a027e834a48bb23b Mon Sep 17 00:00:00 2001 From: Andy Gross Date: Wed, 19 Dec 2012 14:53:38 -0600 Subject: drm/omap: Add OMAP5 support Add support for OMAP5 processor. The main differences are that the OMAP5 has 2 containers, one for 1D and one for 2D. Each container is 128MiB in size. Signed-off-by: Andy Gross Signed-off-by: Rob Clark Signed-off-by: Greg Kroah-Hartman --- drivers/staging/omapdrm/omap_dmm_priv.h | 5 ++ drivers/staging/omapdrm/omap_dmm_tiler.c | 127 ++++++++++++++++++++----------- drivers/staging/omapdrm/tcm.h | 2 + 3 files changed, 90 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/omapdrm/omap_dmm_priv.h b/drivers/staging/omapdrm/omap_dmm_priv.h index 273ec12c028a..58bcd6ae0255 100644 --- a/drivers/staging/omapdrm/omap_dmm_priv.h +++ b/drivers/staging/omapdrm/omap_dmm_priv.h @@ -118,6 +118,11 @@ struct pat { #define DESCR_SIZE 128 #define REFILL_BUFFER_SIZE ((4 * 128 * 256) + (3 * DESCR_SIZE)) +/* For OMAP5, a fixed offset is added to all Y coordinates for 1D buffers. + * This is used in programming to address the upper portion of the LUT +*/ +#define OMAP5_LUT_OFFSET 128 + struct dmm; struct dmm_txn { diff --git a/drivers/staging/omapdrm/omap_dmm_tiler.c b/drivers/staging/omapdrm/omap_dmm_tiler.c index c42c5e500e02..391021537105 100644 --- a/drivers/staging/omapdrm/omap_dmm_tiler.c +++ b/drivers/staging/omapdrm/omap_dmm_tiler.c @@ -213,6 +213,11 @@ static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area, txn->last_pat->next_pa = (uint32_t)pat_pa; pat->area = *area; + + /* adjust Y coordinates based off of container parameters */ + pat->area.y0 += engine->tcm->y_offset; + pat->area.y1 += engine->tcm->y_offset; + pat->ctrl = (struct pat_ctrl){ .start = 1, .lut_id = engine->tcm->lut_id, @@ -622,6 +627,11 @@ static int omap_dmm_probe(struct platform_device *dev) omap_dmm->lut_width = ((pat_geom >> 16) & 0xF) << 5; omap_dmm->lut_height = ((pat_geom >> 24) & 0xF) << 5; + /* increment LUT by one if on OMAP5 */ + /* LUT has twice the height, and is split into a separate container */ + if (omap_dmm->lut_height != omap_dmm->container_height) + omap_dmm->num_lut++; + /* initialize DMM registers */ writel(0x88888888, omap_dmm->base + DMM_PAT_VIEW__0); writel(0x88888888, omap_dmm->base + DMM_PAT_VIEW__1); @@ -701,6 +711,9 @@ static int omap_dmm_probe(struct platform_device *dev) } /* init containers */ + /* Each LUT is associated with a TCM (container manager). We use the + lut_id to denote the lut_id used to identify the correct LUT for + programming during reill operations */ for (i = 0; i < omap_dmm->num_lut; i++) { omap_dmm->tcm[i] = sita_init(omap_dmm->container_width, omap_dmm->container_height, @@ -717,13 +730,23 @@ static int omap_dmm_probe(struct platform_device *dev) /* assign access mode containers to applicable tcm container */ /* OMAP 4 has 1 container for all 4 views */ + /* OMAP 5 has 2 containers, 1 for 2D and 1 for 1D */ containers[TILFMT_8BIT] = omap_dmm->tcm[0]; containers[TILFMT_16BIT] = omap_dmm->tcm[0]; containers[TILFMT_32BIT] = omap_dmm->tcm[0]; - containers[TILFMT_PAGE] = omap_dmm->tcm[0]; + + if (omap_dmm->container_height != omap_dmm->lut_height) { + /* second LUT is used for PAGE mode. Programming must use + y offset that is added to all y coordinates. LUT id is still + 0, because it is the same LUT, just the upper 128 lines */ + containers[TILFMT_PAGE] = omap_dmm->tcm[1]; + omap_dmm->tcm[1]->y_offset = OMAP5_LUT_OFFSET; + omap_dmm->tcm[1]->lut_id = 0; + } else { + containers[TILFMT_PAGE] = omap_dmm->tcm[0]; + } area = (struct tcm_area) { - .is2d = true, .tcm = NULL, .p1.x = omap_dmm->container_width - 1, .p1.y = omap_dmm->container_height - 1, @@ -835,64 +858,81 @@ int tiler_map_show(struct seq_file *s, void *arg) int h_adj; int w_adj; unsigned long flags; + int lut_idx; + if (!omap_dmm) { /* early return if dmm/tiler device is not initialized */ return 0; } - h_adj = omap_dmm->lut_height / ydiv; - w_adj = omap_dmm->lut_width / xdiv; + h_adj = omap_dmm->container_height / ydiv; + w_adj = omap_dmm->container_width / xdiv; - map = kzalloc(h_adj * sizeof(*map), GFP_KERNEL); - global_map = kzalloc((w_adj + 1) * h_adj, GFP_KERNEL); + map = kmalloc(h_adj * sizeof(*map), GFP_KERNEL); + global_map = kmalloc((w_adj + 1) * h_adj, GFP_KERNEL); if (!map || !global_map) goto error; - memset(global_map, ' ', (w_adj + 1) * h_adj); - for (i = 0; i < omap_dmm->lut_height; i++) { - map[i] = global_map + i * (w_adj + 1); - map[i][w_adj] = 0; - } - spin_lock_irqsave(&list_lock, flags); + for (lut_idx = 0; lut_idx < omap_dmm->num_lut; lut_idx++) { + memset(map, 0, sizeof(h_adj * sizeof(*map))); + memset(global_map, ' ', (w_adj + 1) * h_adj); - list_for_each_entry(block, &omap_dmm->alloc_head, alloc_node) { - if (block->fmt != TILFMT_PAGE) { - fill_map(map, xdiv, ydiv, &block->area, *m2dp, true); - if (!*++a2dp) - a2dp = a2d; - if (!*++m2dp) - m2dp = m2d; - map_2d_info(map, xdiv, ydiv, nice, &block->area); - } else { - bool start = read_map_pt(map, xdiv, ydiv, - &block->area.p0) - == ' '; - bool end = read_map_pt(map, xdiv, ydiv, &block->area.p1) - == ' '; - tcm_for_each_slice(a, block->area, p) - fill_map(map, xdiv, ydiv, &a, '=', true); - fill_map_pt(map, xdiv, ydiv, &block->area.p0, + for (i = 0; i < omap_dmm->container_height; i++) { + map[i] = global_map + i * (w_adj + 1); + map[i][w_adj] = 0; + } + + spin_lock_irqsave(&list_lock, flags); + + list_for_each_entry(block, &omap_dmm->alloc_head, alloc_node) { + if (block->area.tcm == omap_dmm->tcm[lut_idx]) { + if (block->fmt != TILFMT_PAGE) { + fill_map(map, xdiv, ydiv, &block->area, + *m2dp, true); + if (!*++a2dp) + a2dp = a2d; + if (!*++m2dp) + m2dp = m2d; + map_2d_info(map, xdiv, ydiv, nice, + &block->area); + } else { + bool start = read_map_pt(map, xdiv, + ydiv, &block->area.p0) == ' '; + bool end = read_map_pt(map, xdiv, ydiv, + &block->area.p1) == ' '; + + tcm_for_each_slice(a, block->area, p) + fill_map(map, xdiv, ydiv, &a, + '=', true); + fill_map_pt(map, xdiv, ydiv, + &block->area.p0, start ? '<' : 'X'); - fill_map_pt(map, xdiv, ydiv, &block->area.p1, + fill_map_pt(map, xdiv, ydiv, + &block->area.p1, end ? '>' : 'X'); - map_1d_info(map, xdiv, ydiv, nice, &block->area); + map_1d_info(map, xdiv, ydiv, nice, + &block->area); + } + } } - } - spin_unlock_irqrestore(&list_lock, flags); + spin_unlock_irqrestore(&list_lock, flags); - if (s) { - seq_printf(s, "BEGIN DMM TILER MAP\n"); - for (i = 0; i < 128; i++) - seq_printf(s, "%03d:%s\n", i, map[i]); - seq_printf(s, "END TILER MAP\n"); - } else { - dev_dbg(omap_dmm->dev, "BEGIN DMM TILER MAP\n"); - for (i = 0; i < 128; i++) - dev_dbg(omap_dmm->dev, "%03d:%s\n", i, map[i]); - dev_dbg(omap_dmm->dev, "END TILER MAP\n"); + if (s) { + seq_printf(s, "CONTAINER %d DUMP BEGIN\n", lut_idx); + for (i = 0; i < 128; i++) + seq_printf(s, "%03d:%s\n", i, map[i]); + seq_printf(s, "CONTAINER %d DUMP END\n", lut_idx); + } else { + dev_dbg(omap_dmm->dev, "CONTAINER %d DUMP BEGIN\n", + lut_idx); + for (i = 0; i < 128; i++) + dev_dbg(omap_dmm->dev, "%03d:%s\n", i, map[i]); + dev_dbg(omap_dmm->dev, "CONTAINER %d DUMP END\n", + lut_idx); + } } error: @@ -913,7 +953,6 @@ static int omap_dmm_resume(struct device *dev) return -ENODEV; area = (struct tcm_area) { - .is2d = true, .tcm = NULL, .p1.x = omap_dmm->container_width - 1, .p1.y = omap_dmm->container_height - 1, diff --git a/drivers/staging/omapdrm/tcm.h b/drivers/staging/omapdrm/tcm.h index d273e3ee0b4c..a8d5ce47686f 100644 --- a/drivers/staging/omapdrm/tcm.h +++ b/drivers/staging/omapdrm/tcm.h @@ -59,6 +59,8 @@ struct tcm { u16 width, height; /* container dimensions */ int lut_id; /* Lookup table identifier */ + unsigned int y_offset; /* offset to use for y coordinates */ + /* 'pvt' structure shall contain any tcm details (attr) along with linked list of allocated areas and mutex for mutually exclusive access to the list. It may also contain copies of width and height to notice -- cgit v1.2.3 From 32ac1a5286d56b3c822f4a1a8d0f7fc5d586de38 Mon Sep 17 00:00:00 2001 From: Cong Ding Date: Tue, 15 Jan 2013 20:46:50 +0100 Subject: staging: omapdrm/omap_gem_dmabuf.c: fix memory leakage There is a memory leakage in variable sg if it goes to error. Signed-off-by: Cong Ding Signed-off-by: Rob Clark Signed-off-by: Greg Kroah-Hartman --- drivers/staging/omapdrm/omap_gem_dmabuf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/omapdrm/omap_gem_dmabuf.c b/drivers/staging/omapdrm/omap_gem_dmabuf.c index b6c5b5c6c8c5..a3236abfca3d 100644 --- a/drivers/staging/omapdrm/omap_gem_dmabuf.c +++ b/drivers/staging/omapdrm/omap_gem_dmabuf.c @@ -53,10 +53,10 @@ static struct sg_table *omap_gem_map_dma_buf( /* this should be after _get_paddr() to ensure we have pages attached */ omap_gem_dma_sync(obj, dir); -out: - if (ret) - return ERR_PTR(ret); return sg; +out: + kfree(sg); + return ERR_PTR(ret); } static void omap_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, -- cgit v1.2.3 From a4462f246c8821f625f45bce52c7ca7e0207dffe Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Sat, 26 Jan 2013 00:40:13 +0100 Subject: staging/omapdrm: Use kmemdup rather than duplicating its implementation Found with coccicheck. The semantic patch that makes this change is available in scripts/coccinelle/api/memdup.cocci. Signed-off-by: Peter Huewe Signed-off-by: Rob Clark Signed-off-by: Greg Kroah-Hartman --- drivers/staging/omapdrm/omap_gem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/omapdrm/omap_gem.c b/drivers/staging/omapdrm/omap_gem.c index 08f1e292ed20..f9297eb2599e 100644 --- a/drivers/staging/omapdrm/omap_gem.c +++ b/drivers/staging/omapdrm/omap_gem.c @@ -1267,12 +1267,12 @@ int omap_gem_set_sync_object(struct drm_gem_object *obj, void *syncobj) if ((omap_obj->flags & OMAP_BO_EXT_SYNC) && !syncobj) { /* clearing a previously set syncobj */ - syncobj = kzalloc(sizeof(*omap_obj->sync), GFP_ATOMIC); + syncobj = kmemdup(omap_obj->sync, sizeof(*omap_obj->sync), + GFP_ATOMIC); if (!syncobj) { ret = -ENOMEM; goto unlock; } - memcpy(syncobj, omap_obj->sync, sizeof(*omap_obj->sync)); omap_obj->flags &= ~OMAP_BO_EXT_SYNC; omap_obj->sync = syncobj; } else if (syncobj && !(omap_obj->flags & OMAP_BO_EXT_SYNC)) { -- cgit v1.2.3 From 8bb0daffb0b8e45188066255b4203446eae181f1 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 11 Feb 2013 12:43:09 -0500 Subject: drm/omap: move out of staging Now that the omapdss interface has been reworked so that omapdrm can use dispc directly, we have been able to fix the remaining functional kms issues with omapdrm. And in the mean time the PM sequencing and many other of that open issues have been solved. So I think it makes sense to finally move omapdrm out of staging. Signed-off-by: Rob Clark --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/omapdrm/Kconfig | 25 + drivers/gpu/drm/omapdrm/Makefile | 24 + drivers/gpu/drm/omapdrm/TODO | 23 + drivers/gpu/drm/omapdrm/omap_connector.c | 298 ++++++ drivers/gpu/drm/omapdrm/omap_crtc.c | 659 ++++++++++++ drivers/gpu/drm/omapdrm/omap_debugfs.c | 139 +++ drivers/gpu/drm/omapdrm/omap_dmm_priv.h | 188 ++++ drivers/gpu/drm/omapdrm/omap_dmm_tiler.c | 991 ++++++++++++++++++ drivers/gpu/drm/omapdrm/omap_dmm_tiler.h | 141 +++ drivers/gpu/drm/omapdrm/omap_drv.c | 610 +++++++++++ drivers/gpu/drm/omapdrm/omap_drv.h | 333 ++++++ drivers/gpu/drm/omapdrm/omap_encoder.c | 170 ++++ drivers/gpu/drm/omapdrm/omap_fb.c | 472 +++++++++ drivers/gpu/drm/omapdrm/omap_fbdev.c | 402 ++++++++ drivers/gpu/drm/omapdrm/omap_gem.c | 1511 ++++++++++++++++++++++++++++ drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c | 225 +++++ drivers/gpu/drm/omapdrm/omap_gem_helpers.c | 169 ++++ drivers/gpu/drm/omapdrm/omap_irq.c | 322 ++++++ drivers/gpu/drm/omapdrm/omap_plane.c | 450 +++++++++ drivers/gpu/drm/omapdrm/tcm-sita.c | 703 +++++++++++++ drivers/gpu/drm/omapdrm/tcm-sita.h | 95 ++ drivers/gpu/drm/omapdrm/tcm.h | 328 ++++++ drivers/staging/Kconfig | 2 - drivers/staging/Makefile | 1 - drivers/staging/omapdrm/Kconfig | 25 - drivers/staging/omapdrm/Makefile | 24 - drivers/staging/omapdrm/TODO | 32 - drivers/staging/omapdrm/omap_connector.c | 298 ------ drivers/staging/omapdrm/omap_crtc.c | 659 ------------ drivers/staging/omapdrm/omap_debugfs.c | 139 --- drivers/staging/omapdrm/omap_dmm_priv.h | 188 ---- drivers/staging/omapdrm/omap_dmm_tiler.c | 991 ------------------ drivers/staging/omapdrm/omap_dmm_tiler.h | 141 --- drivers/staging/omapdrm/omap_drm.h | 123 --- drivers/staging/omapdrm/omap_drv.c | 610 ----------- drivers/staging/omapdrm/omap_drv.h | 333 ------ drivers/staging/omapdrm/omap_encoder.c | 170 ---- drivers/staging/omapdrm/omap_fb.c | 472 --------- drivers/staging/omapdrm/omap_fbdev.c | 402 -------- drivers/staging/omapdrm/omap_gem.c | 1511 ---------------------------- drivers/staging/omapdrm/omap_gem_dmabuf.c | 225 ----- drivers/staging/omapdrm/omap_gem_helpers.c | 169 ---- drivers/staging/omapdrm/omap_irq.c | 322 ------ drivers/staging/omapdrm/omap_plane.c | 450 --------- drivers/staging/omapdrm/tcm-sita.c | 703 ------------- drivers/staging/omapdrm/tcm-sita.h | 95 -- drivers/staging/omapdrm/tcm.h | 328 ------ include/uapi/drm/omap_drm.h | 123 +++ 50 files changed, 8404 insertions(+), 8413 deletions(-) create mode 100644 drivers/gpu/drm/omapdrm/Kconfig create mode 100644 drivers/gpu/drm/omapdrm/Makefile create mode 100644 drivers/gpu/drm/omapdrm/TODO create mode 100644 drivers/gpu/drm/omapdrm/omap_connector.c create mode 100644 drivers/gpu/drm/omapdrm/omap_crtc.c create mode 100644 drivers/gpu/drm/omapdrm/omap_debugfs.c create mode 100644 drivers/gpu/drm/omapdrm/omap_dmm_priv.h create mode 100644 drivers/gpu/drm/omapdrm/omap_dmm_tiler.c create mode 100644 drivers/gpu/drm/omapdrm/omap_dmm_tiler.h create mode 100644 drivers/gpu/drm/omapdrm/omap_drv.c create mode 100644 drivers/gpu/drm/omapdrm/omap_drv.h create mode 100644 drivers/gpu/drm/omapdrm/omap_encoder.c create mode 100644 drivers/gpu/drm/omapdrm/omap_fb.c create mode 100644 drivers/gpu/drm/omapdrm/omap_fbdev.c create mode 100644 drivers/gpu/drm/omapdrm/omap_gem.c create mode 100644 drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c create mode 100644 drivers/gpu/drm/omapdrm/omap_gem_helpers.c create mode 100644 drivers/gpu/drm/omapdrm/omap_irq.c create mode 100644 drivers/gpu/drm/omapdrm/omap_plane.c create mode 100644 drivers/gpu/drm/omapdrm/tcm-sita.c create mode 100644 drivers/gpu/drm/omapdrm/tcm-sita.h create mode 100644 drivers/gpu/drm/omapdrm/tcm.h delete mode 100644 drivers/staging/omapdrm/Kconfig delete mode 100644 drivers/staging/omapdrm/Makefile delete mode 100644 drivers/staging/omapdrm/TODO delete mode 100644 drivers/staging/omapdrm/omap_connector.c delete mode 100644 drivers/staging/omapdrm/omap_crtc.c delete mode 100644 drivers/staging/omapdrm/omap_debugfs.c delete mode 100644 drivers/staging/omapdrm/omap_dmm_priv.h delete mode 100644 drivers/staging/omapdrm/omap_dmm_tiler.c delete mode 100644 drivers/staging/omapdrm/omap_dmm_tiler.h delete mode 100644 drivers/staging/omapdrm/omap_drm.h delete mode 100644 drivers/staging/omapdrm/omap_drv.c delete mode 100644 drivers/staging/omapdrm/omap_drv.h delete mode 100644 drivers/staging/omapdrm/omap_encoder.c delete mode 100644 drivers/staging/omapdrm/omap_fb.c delete mode 100644 drivers/staging/omapdrm/omap_fbdev.c delete mode 100644 drivers/staging/omapdrm/omap_gem.c delete mode 100644 drivers/staging/omapdrm/omap_gem_dmabuf.c delete mode 100644 drivers/staging/omapdrm/omap_gem_helpers.c delete mode 100644 drivers/staging/omapdrm/omap_irq.c delete mode 100644 drivers/staging/omapdrm/omap_plane.c delete mode 100644 drivers/staging/omapdrm/tcm-sita.c delete mode 100644 drivers/staging/omapdrm/tcm-sita.h delete mode 100644 drivers/staging/omapdrm/tcm.h create mode 100644 include/uapi/drm/omap_drm.h (limited to 'drivers') diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index ed9e3af17b31..0ce5f52ac56e 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -215,3 +215,5 @@ source "drivers/gpu/drm/cirrus/Kconfig" source "drivers/gpu/drm/shmobile/Kconfig" source "drivers/gpu/drm/tegra/Kconfig" + +source "drivers/gpu/drm/omapdrm/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 6f58c81cfcbc..b6b43cbc18e4 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -50,4 +50,5 @@ obj-$(CONFIG_DRM_UDL) += udl/ obj-$(CONFIG_DRM_AST) += ast/ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ obj-$(CONFIG_DRM_TEGRA) += tegra/ +obj-$(CONFIG_DRM_OMAP) += omapdrm/ obj-y += i2c/ diff --git a/drivers/gpu/drm/omapdrm/Kconfig b/drivers/gpu/drm/omapdrm/Kconfig new file mode 100644 index 000000000000..b724a4131435 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/Kconfig @@ -0,0 +1,25 @@ + +config DRM_OMAP + tristate "OMAP DRM" + depends on DRM && !CONFIG_FB_OMAP2 + depends on ARCH_OMAP2PLUS || ARCH_MULTIPLATFORM + select DRM_KMS_HELPER + select OMAP2_DSS + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select FB_SYS_FOPS + default n + help + DRM display driver for OMAP2/3/4 based boards. + +config DRM_OMAP_NUM_CRTCS + int "Number of CRTCs" + range 1 10 + default 1 if ARCH_OMAP2 || ARCH_OMAP3 + default 2 if ARCH_OMAP4 + depends on DRM_OMAP + help + Select the number of video overlays which can be used as framebuffers. + The remaining overlays are reserved for video. + diff --git a/drivers/gpu/drm/omapdrm/Makefile b/drivers/gpu/drm/omapdrm/Makefile new file mode 100644 index 000000000000..d85e058f2845 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/Makefile @@ -0,0 +1,24 @@ +# +# Makefile for the drm device driver. This driver provides support for the +# Direct Rendering Infrastructure (DRI) +# + +ccflags-y := -Iinclude/drm -Werror +omapdrm-y := omap_drv.o \ + omap_irq.o \ + omap_debugfs.o \ + omap_crtc.o \ + omap_plane.o \ + omap_encoder.o \ + omap_connector.o \ + omap_fb.o \ + omap_fbdev.o \ + omap_gem.o \ + omap_gem_dmabuf.o \ + omap_dmm_tiler.o \ + tcm-sita.o + +# temporary: +omapdrm-y += omap_gem_helpers.o + +obj-$(CONFIG_DRM_OMAP) += omapdrm.o diff --git a/drivers/gpu/drm/omapdrm/TODO b/drivers/gpu/drm/omapdrm/TODO new file mode 100644 index 000000000000..4d8c18aa5dd7 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/TODO @@ -0,0 +1,23 @@ +TODO +. Where should we do eviction (detatch_pages())? We aren't necessarily + accessing the pages via a GART, so maybe we need some other threshold + to put a cap on the # of pages that can be pin'd. + . Use mm_shrinker to trigger unpinning pages. + . This is mainly theoretical since most of these devices don't actually + have swap or harddrive. +. GEM/shmem backed pages can have existing mappings (kernel linear map, + etc..), which isn't really ideal. +. Revisit GEM sync object infrastructure.. TTM has some framework for this + already. Possibly this could be refactored out and made more common? + There should be some way to do this with less wheel-reinvention. + . This can be handled by the dma-buf fence/reservation stuff when it + lands + +Userspace: +. git://anongit.freedesktop.org/xorg/driver/xf86-video-omap + +Currently tested on +. OMAP3530 beagleboard +. OMAP4430 pandaboard +. OMAP4460 pandaboard +. OMAP5432 uEVM diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c new file mode 100644 index 000000000000..44284fd981fc --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_connector.c @@ -0,0 +1,298 @@ +/* + * drivers/gpu/drm/omapdrm/omap_connector.c + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "omap_drv.h" + +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + +/* + * connector funcs + */ + +#define to_omap_connector(x) container_of(x, struct omap_connector, base) + +struct omap_connector { + struct drm_connector base; + struct omap_dss_device *dssdev; + struct drm_encoder *encoder; +}; + +void copy_timings_omap_to_drm(struct drm_display_mode *mode, + struct omap_video_timings *timings) +{ + mode->clock = timings->pixel_clock; + + mode->hdisplay = timings->x_res; + mode->hsync_start = mode->hdisplay + timings->hfp; + mode->hsync_end = mode->hsync_start + timings->hsw; + mode->htotal = mode->hsync_end + timings->hbp; + + mode->vdisplay = timings->y_res; + mode->vsync_start = mode->vdisplay + timings->vfp; + mode->vsync_end = mode->vsync_start + timings->vsw; + mode->vtotal = mode->vsync_end + timings->vbp; + + mode->flags = 0; + + if (timings->interlace) + mode->flags |= DRM_MODE_FLAG_INTERLACE; + + if (timings->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH) + mode->flags |= DRM_MODE_FLAG_PHSYNC; + else + mode->flags |= DRM_MODE_FLAG_NHSYNC; + + if (timings->vsync_level == OMAPDSS_SIG_ACTIVE_HIGH) + mode->flags |= DRM_MODE_FLAG_PVSYNC; + else + mode->flags |= DRM_MODE_FLAG_NVSYNC; +} + +void copy_timings_drm_to_omap(struct omap_video_timings *timings, + struct drm_display_mode *mode) +{ + timings->pixel_clock = mode->clock; + + timings->x_res = mode->hdisplay; + timings->hfp = mode->hsync_start - mode->hdisplay; + timings->hsw = mode->hsync_end - mode->hsync_start; + timings->hbp = mode->htotal - mode->hsync_end; + + timings->y_res = mode->vdisplay; + timings->vfp = mode->vsync_start - mode->vdisplay; + timings->vsw = mode->vsync_end - mode->vsync_start; + timings->vbp = mode->vtotal - mode->vsync_end; + + timings->interlace = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); + + if (mode->flags & DRM_MODE_FLAG_PHSYNC) + timings->hsync_level = OMAPDSS_SIG_ACTIVE_HIGH; + else + timings->hsync_level = OMAPDSS_SIG_ACTIVE_LOW; + + if (mode->flags & DRM_MODE_FLAG_PVSYNC) + timings->vsync_level = OMAPDSS_SIG_ACTIVE_HIGH; + else + timings->vsync_level = OMAPDSS_SIG_ACTIVE_LOW; + + timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; + timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH; + timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES; +} + +static enum drm_connector_status omap_connector_detect( + struct drm_connector *connector, bool force) +{ + struct omap_connector *omap_connector = to_omap_connector(connector); + struct omap_dss_device *dssdev = omap_connector->dssdev; + struct omap_dss_driver *dssdrv = dssdev->driver; + enum drm_connector_status ret; + + if (dssdrv->detect) { + if (dssdrv->detect(dssdev)) + ret = connector_status_connected; + else + ret = connector_status_disconnected; + } else { + ret = connector_status_unknown; + } + + VERB("%s: %d (force=%d)", omap_connector->dssdev->name, ret, force); + + return ret; +} + +static void omap_connector_destroy(struct drm_connector *connector) +{ + struct omap_connector *omap_connector = to_omap_connector(connector); + struct omap_dss_device *dssdev = omap_connector->dssdev; + + DBG("%s", omap_connector->dssdev->name); + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + kfree(omap_connector); + + omap_dss_put_device(dssdev); +} + +#define MAX_EDID 512 + +static int omap_connector_get_modes(struct drm_connector *connector) +{ + struct omap_connector *omap_connector = to_omap_connector(connector); + struct omap_dss_device *dssdev = omap_connector->dssdev; + struct omap_dss_driver *dssdrv = dssdev->driver; + struct drm_device *dev = connector->dev; + int n = 0; + + DBG("%s", omap_connector->dssdev->name); + + /* if display exposes EDID, then we parse that in the normal way to + * build table of supported modes.. otherwise (ie. fixed resolution + * LCD panels) we just return a single mode corresponding to the + * currently configured timings: + */ + if (dssdrv->read_edid) { + void *edid = kzalloc(MAX_EDID, GFP_KERNEL); + + if ((dssdrv->read_edid(dssdev, edid, MAX_EDID) > 0) && + drm_edid_is_valid(edid)) { + drm_mode_connector_update_edid_property( + connector, edid); + n = drm_add_edid_modes(connector, edid); + } else { + drm_mode_connector_update_edid_property( + connector, NULL); + } + kfree(edid); + } else { + struct drm_display_mode *mode = drm_mode_create(dev); + struct omap_video_timings timings = {0}; + + dssdrv->get_timings(dssdev, &timings); + + copy_timings_omap_to_drm(mode, &timings); + + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + drm_mode_set_name(mode); + drm_mode_probed_add(connector, mode); + + n = 1; + } + + return n; +} + +static int omap_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct omap_connector *omap_connector = to_omap_connector(connector); + struct omap_dss_device *dssdev = omap_connector->dssdev; + struct omap_dss_driver *dssdrv = dssdev->driver; + struct omap_video_timings timings = {0}; + struct drm_device *dev = connector->dev; + struct drm_display_mode *new_mode; + int ret = MODE_BAD; + + copy_timings_drm_to_omap(&timings, mode); + mode->vrefresh = drm_mode_vrefresh(mode); + + if (!dssdrv->check_timings(dssdev, &timings)) { + /* check if vrefresh is still valid */ + new_mode = drm_mode_duplicate(dev, mode); + new_mode->clock = timings.pixel_clock; + new_mode->vrefresh = 0; + if (mode->vrefresh == drm_mode_vrefresh(new_mode)) + ret = MODE_OK; + drm_mode_destroy(dev, new_mode); + } + + DBG("connector: mode %s: " + "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", + (ret == MODE_OK) ? "valid" : "invalid", + mode->base.id, mode->name, mode->vrefresh, mode->clock, + mode->hdisplay, mode->hsync_start, + mode->hsync_end, mode->htotal, + mode->vdisplay, mode->vsync_start, + mode->vsync_end, mode->vtotal, mode->type, mode->flags); + + return ret; +} + +struct drm_encoder *omap_connector_attached_encoder( + struct drm_connector *connector) +{ + struct omap_connector *omap_connector = to_omap_connector(connector); + return omap_connector->encoder; +} + +static const struct drm_connector_funcs omap_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = omap_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = omap_connector_destroy, +}; + +static const struct drm_connector_helper_funcs omap_connector_helper_funcs = { + .get_modes = omap_connector_get_modes, + .mode_valid = omap_connector_mode_valid, + .best_encoder = omap_connector_attached_encoder, +}; + +/* flush an area of the framebuffer (in case of manual update display that + * is not automatically flushed) + */ +void omap_connector_flush(struct drm_connector *connector, + int x, int y, int w, int h) +{ + struct omap_connector *omap_connector = to_omap_connector(connector); + + /* TODO: enable when supported in dss */ + VERB("%s: %d,%d, %dx%d", omap_connector->dssdev->name, x, y, w, h); +} + +/* initialize connector */ +struct drm_connector *omap_connector_init(struct drm_device *dev, + int connector_type, struct omap_dss_device *dssdev, + struct drm_encoder *encoder) +{ + struct drm_connector *connector = NULL; + struct omap_connector *omap_connector; + + DBG("%s", dssdev->name); + + omap_dss_get_device(dssdev); + + omap_connector = kzalloc(sizeof(struct omap_connector), GFP_KERNEL); + if (!omap_connector) { + dev_err(dev->dev, "could not allocate connector\n"); + goto fail; + } + + omap_connector->dssdev = dssdev; + omap_connector->encoder = encoder; + + connector = &omap_connector->base; + + drm_connector_init(dev, connector, &omap_connector_funcs, + connector_type); + drm_connector_helper_add(connector, &omap_connector_helper_funcs); + +#if 0 /* enable when dss2 supports hotplug */ + if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_HPD) + connector->polled = 0; + else +#endif + connector->polled = DRM_CONNECTOR_POLL_CONNECT | + DRM_CONNECTOR_POLL_DISCONNECT; + + connector->interlace_allowed = 1; + connector->doublescan_allowed = 0; + + drm_sysfs_connector_add(connector); + + return connector; + +fail: + if (connector) + omap_connector_destroy(connector); + + return NULL; +} diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c new file mode 100644 index 000000000000..2b97cf90071b --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -0,0 +1,659 @@ +/* + * drivers/gpu/drm/omapdrm/omap_crtc.c + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "omap_drv.h" + +#include +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + +#define to_omap_crtc(x) container_of(x, struct omap_crtc, base) + +struct omap_crtc { + struct drm_crtc base; + struct drm_plane *plane; + + const char *name; + int pipe; + enum omap_channel channel; + struct omap_overlay_manager_info info; + + /* + * Temporary: eventually this will go away, but it is needed + * for now to keep the output's happy. (They only need + * mgr->id.) Eventually this will be replaced w/ something + * more common-panel-framework-y + */ + struct omap_overlay_manager mgr; + + struct omap_video_timings timings; + bool enabled; + bool full_update; + + struct omap_drm_apply apply; + + struct omap_drm_irq apply_irq; + struct omap_drm_irq error_irq; + + /* list of in-progress apply's: */ + struct list_head pending_applies; + + /* list of queued apply's: */ + struct list_head queued_applies; + + /* for handling queued and in-progress applies: */ + struct work_struct apply_work; + + /* if there is a pending flip, these will be non-null: */ + struct drm_pending_vblank_event *event; + struct drm_framebuffer *old_fb; + + /* for handling page flips without caring about what + * the callback is called from. Possibly we should just + * make omap_gem always call the cb from the worker so + * we don't have to care about this.. + * + * XXX maybe fold into apply_work?? + */ + struct work_struct page_flip_work; +}; + +/* + * Manager-ops, callbacks from output when they need to configure + * the upstream part of the video pipe. + * + * Most of these we can ignore until we add support for command-mode + * panels.. for video-mode the crtc-helpers already do an adequate + * job of sequencing the setup of the video pipe in the proper order + */ + +/* we can probably ignore these until we support command-mode panels: */ +static void omap_crtc_start_update(struct omap_overlay_manager *mgr) +{ +} + +static int omap_crtc_enable(struct omap_overlay_manager *mgr) +{ + return 0; +} + +static void omap_crtc_disable(struct omap_overlay_manager *mgr) +{ +} + +static void omap_crtc_set_timings(struct omap_overlay_manager *mgr, + const struct omap_video_timings *timings) +{ + struct omap_crtc *omap_crtc = container_of(mgr, struct omap_crtc, mgr); + DBG("%s", omap_crtc->name); + omap_crtc->timings = *timings; + omap_crtc->full_update = true; +} + +static void omap_crtc_set_lcd_config(struct omap_overlay_manager *mgr, + const struct dss_lcd_mgr_config *config) +{ + struct omap_crtc *omap_crtc = container_of(mgr, struct omap_crtc, mgr); + DBG("%s", omap_crtc->name); + dispc_mgr_set_lcd_config(omap_crtc->channel, config); +} + +static int omap_crtc_register_framedone_handler( + struct omap_overlay_manager *mgr, + void (*handler)(void *), void *data) +{ + return 0; +} + +static void omap_crtc_unregister_framedone_handler( + struct omap_overlay_manager *mgr, + void (*handler)(void *), void *data) +{ +} + +static const struct dss_mgr_ops mgr_ops = { + .start_update = omap_crtc_start_update, + .enable = omap_crtc_enable, + .disable = omap_crtc_disable, + .set_timings = omap_crtc_set_timings, + .set_lcd_config = omap_crtc_set_lcd_config, + .register_framedone_handler = omap_crtc_register_framedone_handler, + .unregister_framedone_handler = omap_crtc_unregister_framedone_handler, +}; + +/* + * CRTC funcs: + */ + +static void omap_crtc_destroy(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + + DBG("%s", omap_crtc->name); + + WARN_ON(omap_crtc->apply_irq.registered); + omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); + + omap_crtc->plane->funcs->destroy(omap_crtc->plane); + drm_crtc_cleanup(crtc); + + kfree(omap_crtc); +} + +static void omap_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct omap_drm_private *priv = crtc->dev->dev_private; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + bool enabled = (mode == DRM_MODE_DPMS_ON); + int i; + + DBG("%s: %d", omap_crtc->name, mode); + + if (enabled != omap_crtc->enabled) { + omap_crtc->enabled = enabled; + omap_crtc->full_update = true; + omap_crtc_apply(crtc, &omap_crtc->apply); + + /* also enable our private plane: */ + WARN_ON(omap_plane_dpms(omap_crtc->plane, mode)); + + /* and any attached overlay planes: */ + for (i = 0; i < priv->num_planes; i++) { + struct drm_plane *plane = priv->planes[i]; + if (plane->crtc == crtc) + WARN_ON(omap_plane_dpms(plane, mode)); + } + } +} + +static bool omap_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static int omap_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, + struct drm_framebuffer *old_fb) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + + mode = adjusted_mode; + + DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", + omap_crtc->name, mode->base.id, mode->name, + mode->vrefresh, mode->clock, + mode->hdisplay, mode->hsync_start, + mode->hsync_end, mode->htotal, + mode->vdisplay, mode->vsync_start, + mode->vsync_end, mode->vtotal, + mode->type, mode->flags); + + copy_timings_drm_to_omap(&omap_crtc->timings, mode); + omap_crtc->full_update = true; + + return omap_plane_mode_set(omap_crtc->plane, crtc, crtc->fb, + 0, 0, mode->hdisplay, mode->vdisplay, + x << 16, y << 16, + mode->hdisplay << 16, mode->vdisplay << 16, + NULL, NULL); +} + +static void omap_crtc_prepare(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + DBG("%s", omap_crtc->name); + omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); +} + +static void omap_crtc_commit(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + DBG("%s", omap_crtc->name); + omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON); +} + +static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct drm_plane *plane = omap_crtc->plane; + struct drm_display_mode *mode = &crtc->mode; + + return omap_plane_mode_set(plane, crtc, crtc->fb, + 0, 0, mode->hdisplay, mode->vdisplay, + x << 16, y << 16, + mode->hdisplay << 16, mode->vdisplay << 16, + NULL, NULL); +} + +static void omap_crtc_load_lut(struct drm_crtc *crtc) +{ +} + +static void vblank_cb(void *arg) +{ + struct drm_crtc *crtc = arg; + struct drm_device *dev = crtc->dev; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + + /* wakeup userspace */ + if (omap_crtc->event) + drm_send_vblank_event(dev, omap_crtc->pipe, omap_crtc->event); + + omap_crtc->event = NULL; + omap_crtc->old_fb = NULL; + + spin_unlock_irqrestore(&dev->event_lock, flags); +} + +static void page_flip_worker(struct work_struct *work) +{ + struct omap_crtc *omap_crtc = + container_of(work, struct omap_crtc, page_flip_work); + struct drm_crtc *crtc = &omap_crtc->base; + struct drm_device *dev = crtc->dev; + struct drm_display_mode *mode = &crtc->mode; + struct drm_gem_object *bo; + + drm_modeset_lock_all(dev); + omap_plane_mode_set(omap_crtc->plane, crtc, crtc->fb, + 0, 0, mode->hdisplay, mode->vdisplay, + crtc->x << 16, crtc->y << 16, + mode->hdisplay << 16, mode->vdisplay << 16, + vblank_cb, crtc); + drm_modeset_unlock_all(dev); + + bo = omap_framebuffer_bo(crtc->fb, 0); + drm_gem_object_unreference_unlocked(bo); +} + +static void page_flip_cb(void *arg) +{ + struct drm_crtc *crtc = arg; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct omap_drm_private *priv = crtc->dev->dev_private; + + /* avoid assumptions about what ctxt we are called from: */ + queue_work(priv->wq, &omap_crtc->page_flip_work); +} + +static int omap_crtc_page_flip_locked(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event) +{ + struct drm_device *dev = crtc->dev; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct drm_gem_object *bo; + + DBG("%d -> %d (event=%p)", crtc->fb ? crtc->fb->base.id : -1, + fb->base.id, event); + + if (omap_crtc->old_fb) { + dev_err(dev->dev, "already a pending flip\n"); + return -EINVAL; + } + + omap_crtc->event = event; + crtc->fb = fb; + + /* + * Hold a reference temporarily until the crtc is updated + * and takes the reference to the bo. This avoids it + * getting freed from under us: + */ + bo = omap_framebuffer_bo(fb, 0); + drm_gem_object_reference(bo); + + omap_gem_op_async(bo, OMAP_GEM_READ, page_flip_cb, crtc); + + return 0; +} + +static int omap_crtc_set_property(struct drm_crtc *crtc, + struct drm_property *property, uint64_t val) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct omap_drm_private *priv = crtc->dev->dev_private; + + if (property == priv->rotation_prop) { + crtc->invert_dimensions = + !!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270))); + } + + return omap_plane_set_property(omap_crtc->plane, property, val); +} + +static const struct drm_crtc_funcs omap_crtc_funcs = { + .set_config = drm_crtc_helper_set_config, + .destroy = omap_crtc_destroy, + .page_flip = omap_crtc_page_flip_locked, + .set_property = omap_crtc_set_property, +}; + +static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = { + .dpms = omap_crtc_dpms, + .mode_fixup = omap_crtc_mode_fixup, + .mode_set = omap_crtc_mode_set, + .prepare = omap_crtc_prepare, + .commit = omap_crtc_commit, + .mode_set_base = omap_crtc_mode_set_base, + .load_lut = omap_crtc_load_lut, +}; + +const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + return &omap_crtc->timings; +} + +enum omap_channel omap_crtc_channel(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + return omap_crtc->channel; +} + +static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) +{ + struct omap_crtc *omap_crtc = + container_of(irq, struct omap_crtc, error_irq); + struct drm_crtc *crtc = &omap_crtc->base; + DRM_ERROR("%s: errors: %08x\n", omap_crtc->name, irqstatus); + /* avoid getting in a flood, unregister the irq until next vblank */ + omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); +} + +static void omap_crtc_apply_irq(struct omap_drm_irq *irq, uint32_t irqstatus) +{ + struct omap_crtc *omap_crtc = + container_of(irq, struct omap_crtc, apply_irq); + struct drm_crtc *crtc = &omap_crtc->base; + + if (!omap_crtc->error_irq.registered) + omap_irq_register(crtc->dev, &omap_crtc->error_irq); + + if (!dispc_mgr_go_busy(omap_crtc->channel)) { + struct omap_drm_private *priv = + crtc->dev->dev_private; + DBG("%s: apply done", omap_crtc->name); + omap_irq_unregister(crtc->dev, &omap_crtc->apply_irq); + queue_work(priv->wq, &omap_crtc->apply_work); + } +} + +static void apply_worker(struct work_struct *work) +{ + struct omap_crtc *omap_crtc = + container_of(work, struct omap_crtc, apply_work); + struct drm_crtc *crtc = &omap_crtc->base; + struct drm_device *dev = crtc->dev; + struct omap_drm_apply *apply, *n; + bool need_apply; + + /* + * Synchronize everything on mode_config.mutex, to keep + * the callbacks and list modification all serialized + * with respect to modesetting ioctls from userspace. + */ + drm_modeset_lock_all(dev); + dispc_runtime_get(); + + /* + * If we are still pending a previous update, wait.. when the + * pending update completes, we get kicked again. + */ + if (omap_crtc->apply_irq.registered) + goto out; + + /* finish up previous apply's: */ + list_for_each_entry_safe(apply, n, + &omap_crtc->pending_applies, pending_node) { + apply->post_apply(apply); + list_del(&apply->pending_node); + } + + need_apply = !list_empty(&omap_crtc->queued_applies); + + /* then handle the next round of of queued apply's: */ + list_for_each_entry_safe(apply, n, + &omap_crtc->queued_applies, queued_node) { + apply->pre_apply(apply); + list_del(&apply->queued_node); + apply->queued = false; + list_add_tail(&apply->pending_node, + &omap_crtc->pending_applies); + } + + if (need_apply) { + enum omap_channel channel = omap_crtc->channel; + + DBG("%s: GO", omap_crtc->name); + + if (dispc_mgr_is_enabled(channel)) { + omap_irq_register(dev, &omap_crtc->apply_irq); + dispc_mgr_go(channel); + } else { + struct omap_drm_private *priv = dev->dev_private; + queue_work(priv->wq, &omap_crtc->apply_work); + } + } + +out: + dispc_runtime_put(); + drm_modeset_unlock_all(dev); +} + +int omap_crtc_apply(struct drm_crtc *crtc, + struct omap_drm_apply *apply) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct drm_device *dev = crtc->dev; + + WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + + /* no need to queue it again if it is already queued: */ + if (apply->queued) + return 0; + + apply->queued = true; + list_add_tail(&apply->queued_node, &omap_crtc->queued_applies); + + /* + * If there are no currently pending updates, then go ahead and + * kick the worker immediately, otherwise it will run again when + * the current update finishes. + */ + if (list_empty(&omap_crtc->pending_applies)) { + struct omap_drm_private *priv = crtc->dev->dev_private; + queue_work(priv->wq, &omap_crtc->apply_work); + } + + return 0; +} + +/* called only from apply */ +static void set_enabled(struct drm_crtc *crtc, bool enable) +{ + struct drm_device *dev = crtc->dev; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + enum omap_channel channel = omap_crtc->channel; + struct omap_irq_wait *wait = NULL; + + if (dispc_mgr_is_enabled(channel) == enable) + return; + + /* ignore sync-lost irqs during enable/disable */ + omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); + + if (dispc_mgr_get_framedone_irq(channel)) { + if (!enable) { + wait = omap_irq_wait_init(dev, + dispc_mgr_get_framedone_irq(channel), 1); + } + } else { + /* + * When we disable digit output, we need to wait until fields + * are done. Otherwise the DSS is still working, and turning + * off the clocks prevents DSS from going to OFF mode. And when + * enabling, we need to wait for the extra sync losts + */ + wait = omap_irq_wait_init(dev, + dispc_mgr_get_vsync_irq(channel), 2); + } + + dispc_mgr_enable(channel, enable); + + if (wait) { + int ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100)); + if (ret) { + dev_err(dev->dev, "%s: timeout waiting for %s\n", + omap_crtc->name, enable ? "enable" : "disable"); + } + } + + omap_irq_register(crtc->dev, &omap_crtc->error_irq); +} + +static void omap_crtc_pre_apply(struct omap_drm_apply *apply) +{ + struct omap_crtc *omap_crtc = + container_of(apply, struct omap_crtc, apply); + struct drm_crtc *crtc = &omap_crtc->base; + struct drm_encoder *encoder = NULL; + + DBG("%s: enabled=%d, full=%d", omap_crtc->name, + omap_crtc->enabled, omap_crtc->full_update); + + if (omap_crtc->full_update) { + struct omap_drm_private *priv = crtc->dev->dev_private; + int i; + for (i = 0; i < priv->num_encoders; i++) { + if (priv->encoders[i]->crtc == crtc) { + encoder = priv->encoders[i]; + break; + } + } + } + + if (!omap_crtc->enabled) { + set_enabled(&omap_crtc->base, false); + if (encoder) + omap_encoder_set_enabled(encoder, false); + } else { + if (encoder) { + omap_encoder_set_enabled(encoder, false); + omap_encoder_update(encoder, &omap_crtc->mgr, + &omap_crtc->timings); + omap_encoder_set_enabled(encoder, true); + omap_crtc->full_update = false; + } + + dispc_mgr_setup(omap_crtc->channel, &omap_crtc->info); + dispc_mgr_set_timings(omap_crtc->channel, + &omap_crtc->timings); + set_enabled(&omap_crtc->base, true); + } + + omap_crtc->full_update = false; +} + +static void omap_crtc_post_apply(struct omap_drm_apply *apply) +{ + /* nothing needed for post-apply */ +} + +static const char *channel_names[] = { + [OMAP_DSS_CHANNEL_LCD] = "lcd", + [OMAP_DSS_CHANNEL_DIGIT] = "tv", + [OMAP_DSS_CHANNEL_LCD2] = "lcd2", +}; + +/* initialize crtc */ +struct drm_crtc *omap_crtc_init(struct drm_device *dev, + struct drm_plane *plane, enum omap_channel channel, int id) +{ + struct drm_crtc *crtc = NULL; + struct omap_crtc *omap_crtc; + struct omap_overlay_manager_info *info; + + DBG("%s", channel_names[channel]); + + omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL); + + if (!omap_crtc) { + dev_err(dev->dev, "could not allocate CRTC\n"); + goto fail; + } + + crtc = &omap_crtc->base; + + INIT_WORK(&omap_crtc->page_flip_work, page_flip_worker); + INIT_WORK(&omap_crtc->apply_work, apply_worker); + + INIT_LIST_HEAD(&omap_crtc->pending_applies); + INIT_LIST_HEAD(&omap_crtc->queued_applies); + + omap_crtc->apply.pre_apply = omap_crtc_pre_apply; + omap_crtc->apply.post_apply = omap_crtc_post_apply; + + omap_crtc->apply_irq.irqmask = pipe2vbl(id); + omap_crtc->apply_irq.irq = omap_crtc_apply_irq; + + omap_crtc->error_irq.irqmask = + dispc_mgr_get_sync_lost_irq(channel); + omap_crtc->error_irq.irq = omap_crtc_error_irq; + omap_irq_register(dev, &omap_crtc->error_irq); + + omap_crtc->channel = channel; + omap_crtc->plane = plane; + omap_crtc->plane->crtc = crtc; + omap_crtc->name = channel_names[channel]; + omap_crtc->pipe = id; + + /* temporary: */ + omap_crtc->mgr.id = channel; + + dss_install_mgr_ops(&mgr_ops); + + /* TODO: fix hard-coded setup.. add properties! */ + info = &omap_crtc->info; + info->default_color = 0x00000000; + info->trans_key = 0x00000000; + info->trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST; + info->trans_enabled = false; + + drm_crtc_init(dev, crtc, &omap_crtc_funcs); + drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs); + + omap_plane_install_properties(omap_crtc->plane, &crtc->base); + + return crtc; + +fail: + if (crtc) + omap_crtc_destroy(crtc); + + return NULL; +} diff --git a/drivers/gpu/drm/omapdrm/omap_debugfs.c b/drivers/gpu/drm/omapdrm/omap_debugfs.c new file mode 100644 index 000000000000..c0aa40f8ad6a --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_debugfs.c @@ -0,0 +1,139 @@ +/* + * drivers/gpu/drm/omapdrm/omap_debugfs.c + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "omap_drv.h" +#include "omap_dmm_tiler.h" + +#include "drm_fb_helper.h" + + +#ifdef CONFIG_DEBUG_FS + +static int gem_show(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct omap_drm_private *priv = dev->dev_private; + int ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + seq_printf(m, "All Objects:\n"); + omap_gem_describe_objects(&priv->obj_list, m); + + mutex_unlock(&dev->struct_mutex); + + return 0; +} + +static int mm_show(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + return drm_mm_dump_table(m, dev->mm_private); +} + +static int fb_show(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct omap_drm_private *priv = dev->dev_private; + struct drm_framebuffer *fb; + int ret; + + ret = mutex_lock_interruptible(&dev->mode_config.mutex); + if (ret) + return ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) { + mutex_unlock(&dev->mode_config.mutex); + return ret; + } + + seq_printf(m, "fbcon "); + omap_framebuffer_describe(priv->fbdev->fb, m); + + mutex_lock(&dev->mode_config.fb_lock); + list_for_each_entry(fb, &dev->mode_config.fb_list, head) { + if (fb == priv->fbdev->fb) + continue; + + seq_printf(m, "user "); + omap_framebuffer_describe(fb, m); + } + mutex_unlock(&dev->mode_config.fb_lock); + + mutex_unlock(&dev->struct_mutex); + mutex_unlock(&dev->mode_config.mutex); + + return 0; +} + +/* list of debufs files that are applicable to all devices */ +static struct drm_info_list omap_debugfs_list[] = { + {"gem", gem_show, 0}, + {"mm", mm_show, 0}, + {"fb", fb_show, 0}, +}; + +/* list of debugfs files that are specific to devices with dmm/tiler */ +static struct drm_info_list omap_dmm_debugfs_list[] = { + {"tiler_map", tiler_map_show, 0}, +}; + +int omap_debugfs_init(struct drm_minor *minor) +{ + struct drm_device *dev = minor->dev; + int ret; + + ret = drm_debugfs_create_files(omap_debugfs_list, + ARRAY_SIZE(omap_debugfs_list), + minor->debugfs_root, minor); + + if (ret) { + dev_err(dev->dev, "could not install omap_debugfs_list\n"); + return ret; + } + + if (dmm_is_available()) + ret = drm_debugfs_create_files(omap_dmm_debugfs_list, + ARRAY_SIZE(omap_dmm_debugfs_list), + minor->debugfs_root, minor); + + if (ret) { + dev_err(dev->dev, "could not install omap_dmm_debugfs_list\n"); + return ret; + } + + return ret; +} + +void omap_debugfs_cleanup(struct drm_minor *minor) +{ + drm_debugfs_remove_files(omap_debugfs_list, + ARRAY_SIZE(omap_debugfs_list), minor); + if (dmm_is_available()) + drm_debugfs_remove_files(omap_dmm_debugfs_list, + ARRAY_SIZE(omap_dmm_debugfs_list), minor); +} + +#endif diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_priv.h b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h new file mode 100644 index 000000000000..58bcd6ae0255 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h @@ -0,0 +1,188 @@ +/* + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Rob Clark + * Andy Gross + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef OMAP_DMM_PRIV_H +#define OMAP_DMM_PRIV_H + +#define DMM_REVISION 0x000 +#define DMM_HWINFO 0x004 +#define DMM_LISA_HWINFO 0x008 +#define DMM_DMM_SYSCONFIG 0x010 +#define DMM_LISA_LOCK 0x01C +#define DMM_LISA_MAP__0 0x040 +#define DMM_LISA_MAP__1 0x044 +#define DMM_TILER_HWINFO 0x208 +#define DMM_TILER_OR__0 0x220 +#define DMM_TILER_OR__1 0x224 +#define DMM_PAT_HWINFO 0x408 +#define DMM_PAT_GEOMETRY 0x40C +#define DMM_PAT_CONFIG 0x410 +#define DMM_PAT_VIEW__0 0x420 +#define DMM_PAT_VIEW__1 0x424 +#define DMM_PAT_VIEW_MAP__0 0x440 +#define DMM_PAT_VIEW_MAP_BASE 0x460 +#define DMM_PAT_IRQ_EOI 0x478 +#define DMM_PAT_IRQSTATUS_RAW 0x480 +#define DMM_PAT_IRQSTATUS 0x490 +#define DMM_PAT_IRQENABLE_SET 0x4A0 +#define DMM_PAT_IRQENABLE_CLR 0x4B0 +#define DMM_PAT_STATUS__0 0x4C0 +#define DMM_PAT_STATUS__1 0x4C4 +#define DMM_PAT_STATUS__2 0x4C8 +#define DMM_PAT_STATUS__3 0x4CC +#define DMM_PAT_DESCR__0 0x500 +#define DMM_PAT_DESCR__1 0x510 +#define DMM_PAT_DESCR__2 0x520 +#define DMM_PAT_DESCR__3 0x530 +#define DMM_PEG_HWINFO 0x608 +#define DMM_PEG_PRIO 0x620 +#define DMM_PEG_PRIO_PAT 0x640 + +#define DMM_IRQSTAT_DST (1<<0) +#define DMM_IRQSTAT_LST (1<<1) +#define DMM_IRQSTAT_ERR_INV_DSC (1<<2) +#define DMM_IRQSTAT_ERR_INV_DATA (1<<3) +#define DMM_IRQSTAT_ERR_UPD_AREA (1<<4) +#define DMM_IRQSTAT_ERR_UPD_CTRL (1<<5) +#define DMM_IRQSTAT_ERR_UPD_DATA (1<<6) +#define DMM_IRQSTAT_ERR_LUT_MISS (1<<7) + +#define DMM_IRQSTAT_ERR_MASK (DMM_IRQ_STAT_ERR_INV_DSC | \ + DMM_IRQ_STAT_ERR_INV_DATA | \ + DMM_IRQ_STAT_ERR_UPD_AREA | \ + DMM_IRQ_STAT_ERR_UPD_CTRL | \ + DMM_IRQ_STAT_ERR_UPD_DATA | \ + DMM_IRQ_STAT_ERR_LUT_MISS) + +#define DMM_PATSTATUS_READY (1<<0) +#define DMM_PATSTATUS_VALID (1<<1) +#define DMM_PATSTATUS_RUN (1<<2) +#define DMM_PATSTATUS_DONE (1<<3) +#define DMM_PATSTATUS_LINKED (1<<4) +#define DMM_PATSTATUS_BYPASSED (1<<7) +#define DMM_PATSTATUS_ERR_INV_DESCR (1<<10) +#define DMM_PATSTATUS_ERR_INV_DATA (1<<11) +#define DMM_PATSTATUS_ERR_UPD_AREA (1<<12) +#define DMM_PATSTATUS_ERR_UPD_CTRL (1<<13) +#define DMM_PATSTATUS_ERR_UPD_DATA (1<<14) +#define DMM_PATSTATUS_ERR_ACCESS (1<<15) + +/* note: don't treat DMM_PATSTATUS_ERR_ACCESS as an error */ +#define DMM_PATSTATUS_ERR (DMM_PATSTATUS_ERR_INV_DESCR | \ + DMM_PATSTATUS_ERR_INV_DATA | \ + DMM_PATSTATUS_ERR_UPD_AREA | \ + DMM_PATSTATUS_ERR_UPD_CTRL | \ + DMM_PATSTATUS_ERR_UPD_DATA) + + + +enum { + PAT_STATUS, + PAT_DESCR +}; + +struct pat_ctrl { + u32 start:4; + u32 dir:4; + u32 lut_id:8; + u32 sync:12; + u32 ini:4; +}; + +struct pat { + uint32_t next_pa; + struct pat_area area; + struct pat_ctrl ctrl; + uint32_t data_pa; +}; + +#define DMM_FIXED_RETRY_COUNT 1000 + +/* create refill buffer big enough to refill all slots, plus 3 descriptors.. + * 3 descriptors is probably the worst-case for # of 2d-slices in a 1d area, + * but I guess you don't hit that worst case at the same time as full area + * refill + */ +#define DESCR_SIZE 128 +#define REFILL_BUFFER_SIZE ((4 * 128 * 256) + (3 * DESCR_SIZE)) + +/* For OMAP5, a fixed offset is added to all Y coordinates for 1D buffers. + * This is used in programming to address the upper portion of the LUT +*/ +#define OMAP5_LUT_OFFSET 128 + +struct dmm; + +struct dmm_txn { + void *engine_handle; + struct tcm *tcm; + + uint8_t *current_va; + dma_addr_t current_pa; + + struct pat *last_pat; +}; + +struct refill_engine { + int id; + struct dmm *dmm; + struct tcm *tcm; + + uint8_t *refill_va; + dma_addr_t refill_pa; + + /* only one trans per engine for now */ + struct dmm_txn txn; + + bool async; + + wait_queue_head_t wait_for_refill; + + struct list_head idle_node; +}; + +struct dmm { + struct device *dev; + void __iomem *base; + int irq; + + struct page *dummy_page; + dma_addr_t dummy_pa; + + void *refill_va; + dma_addr_t refill_pa; + + /* refill engines */ + wait_queue_head_t engine_queue; + struct list_head idle_head; + struct refill_engine *engines; + int num_engines; + atomic_t engine_counter; + + /* container information */ + int container_width; + int container_height; + int lut_width; + int lut_height; + int num_lut; + + /* array of LUT - TCM containers */ + struct tcm **tcm; + + /* allocation list and lock */ + struct list_head alloc_head; +}; + +#endif diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c new file mode 100644 index 000000000000..391021537105 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c @@ -0,0 +1,991 @@ +/* + * DMM IOMMU driver support functions for TI OMAP processors. + * + * Author: Rob Clark + * Andy Gross + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include /* platform_device() */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "omap_dmm_tiler.h" +#include "omap_dmm_priv.h" + +#define DMM_DRIVER_NAME "dmm" + +/* mappings for associating views to luts */ +static struct tcm *containers[TILFMT_NFORMATS]; +static struct dmm *omap_dmm; + +/* global spinlock for protecting lists */ +static DEFINE_SPINLOCK(list_lock); + +/* Geometry table */ +#define GEOM(xshift, yshift, bytes_per_pixel) { \ + .x_shft = (xshift), \ + .y_shft = (yshift), \ + .cpp = (bytes_per_pixel), \ + .slot_w = 1 << (SLOT_WIDTH_BITS - (xshift)), \ + .slot_h = 1 << (SLOT_HEIGHT_BITS - (yshift)), \ + } + +static const struct { + uint32_t x_shft; /* unused X-bits (as part of bpp) */ + uint32_t y_shft; /* unused Y-bits (as part of bpp) */ + uint32_t cpp; /* bytes/chars per pixel */ + uint32_t slot_w; /* width of each slot (in pixels) */ + uint32_t slot_h; /* height of each slot (in pixels) */ +} geom[TILFMT_NFORMATS] = { + [TILFMT_8BIT] = GEOM(0, 0, 1), + [TILFMT_16BIT] = GEOM(0, 1, 2), + [TILFMT_32BIT] = GEOM(1, 1, 4), + [TILFMT_PAGE] = GEOM(SLOT_WIDTH_BITS, SLOT_HEIGHT_BITS, 1), +}; + + +/* lookup table for registers w/ per-engine instances */ +static const uint32_t reg[][4] = { + [PAT_STATUS] = {DMM_PAT_STATUS__0, DMM_PAT_STATUS__1, + DMM_PAT_STATUS__2, DMM_PAT_STATUS__3}, + [PAT_DESCR] = {DMM_PAT_DESCR__0, DMM_PAT_DESCR__1, + DMM_PAT_DESCR__2, DMM_PAT_DESCR__3}, +}; + +/* simple allocator to grab next 16 byte aligned memory from txn */ +static void *alloc_dma(struct dmm_txn *txn, size_t sz, dma_addr_t *pa) +{ + void *ptr; + struct refill_engine *engine = txn->engine_handle; + + /* dmm programming requires 16 byte aligned addresses */ + txn->current_pa = round_up(txn->current_pa, 16); + txn->current_va = (void *)round_up((long)txn->current_va, 16); + + ptr = txn->current_va; + *pa = txn->current_pa; + + txn->current_pa += sz; + txn->current_va += sz; + + BUG_ON((txn->current_va - engine->refill_va) > REFILL_BUFFER_SIZE); + + return ptr; +} + +/* check status and spin until wait_mask comes true */ +static int wait_status(struct refill_engine *engine, uint32_t wait_mask) +{ + struct dmm *dmm = engine->dmm; + uint32_t r = 0, err, i; + + i = DMM_FIXED_RETRY_COUNT; + while (true) { + r = readl(dmm->base + reg[PAT_STATUS][engine->id]); + err = r & DMM_PATSTATUS_ERR; + if (err) + return -EFAULT; + + if ((r & wait_mask) == wait_mask) + break; + + if (--i == 0) + return -ETIMEDOUT; + + udelay(1); + } + + return 0; +} + +static void release_engine(struct refill_engine *engine) +{ + unsigned long flags; + + spin_lock_irqsave(&list_lock, flags); + list_add(&engine->idle_node, &omap_dmm->idle_head); + spin_unlock_irqrestore(&list_lock, flags); + + atomic_inc(&omap_dmm->engine_counter); + wake_up_interruptible(&omap_dmm->engine_queue); +} + +static irqreturn_t omap_dmm_irq_handler(int irq, void *arg) +{ + struct dmm *dmm = arg; + uint32_t status = readl(dmm->base + DMM_PAT_IRQSTATUS); + int i; + + /* ack IRQ */ + writel(status, dmm->base + DMM_PAT_IRQSTATUS); + + for (i = 0; i < dmm->num_engines; i++) { + if (status & DMM_IRQSTAT_LST) { + wake_up_interruptible(&dmm->engines[i].wait_for_refill); + + if (dmm->engines[i].async) + release_engine(&dmm->engines[i]); + } + + status >>= 8; + } + + return IRQ_HANDLED; +} + +/** + * Get a handle for a DMM transaction + */ +static struct dmm_txn *dmm_txn_init(struct dmm *dmm, struct tcm *tcm) +{ + struct dmm_txn *txn = NULL; + struct refill_engine *engine = NULL; + int ret; + unsigned long flags; + + + /* wait until an engine is available */ + ret = wait_event_interruptible(omap_dmm->engine_queue, + atomic_add_unless(&omap_dmm->engine_counter, -1, 0)); + if (ret) + return ERR_PTR(ret); + + /* grab an idle engine */ + spin_lock_irqsave(&list_lock, flags); + if (!list_empty(&dmm->idle_head)) { + engine = list_entry(dmm->idle_head.next, struct refill_engine, + idle_node); + list_del(&engine->idle_node); + } + spin_unlock_irqrestore(&list_lock, flags); + + BUG_ON(!engine); + + txn = &engine->txn; + engine->tcm = tcm; + txn->engine_handle = engine; + txn->last_pat = NULL; + txn->current_va = engine->refill_va; + txn->current_pa = engine->refill_pa; + + return txn; +} + +/** + * Add region to DMM transaction. If pages or pages[i] is NULL, then the + * corresponding slot is cleared (ie. dummy_pa is programmed) + */ +static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area, + struct page **pages, uint32_t npages, uint32_t roll) +{ + dma_addr_t pat_pa = 0; + uint32_t *data; + struct pat *pat; + struct refill_engine *engine = txn->engine_handle; + int columns = (1 + area->x1 - area->x0); + int rows = (1 + area->y1 - area->y0); + int i = columns*rows; + + pat = alloc_dma(txn, sizeof(struct pat), &pat_pa); + + if (txn->last_pat) + txn->last_pat->next_pa = (uint32_t)pat_pa; + + pat->area = *area; + + /* adjust Y coordinates based off of container parameters */ + pat->area.y0 += engine->tcm->y_offset; + pat->area.y1 += engine->tcm->y_offset; + + pat->ctrl = (struct pat_ctrl){ + .start = 1, + .lut_id = engine->tcm->lut_id, + }; + + data = alloc_dma(txn, 4*i, &pat->data_pa); + + while (i--) { + int n = i + roll; + if (n >= npages) + n -= npages; + data[i] = (pages && pages[n]) ? + page_to_phys(pages[n]) : engine->dmm->dummy_pa; + } + + txn->last_pat = pat; + + return; +} + +/** + * Commit the DMM transaction. + */ +static int dmm_txn_commit(struct dmm_txn *txn, bool wait) +{ + int ret = 0; + struct refill_engine *engine = txn->engine_handle; + struct dmm *dmm = engine->dmm; + + if (!txn->last_pat) { + dev_err(engine->dmm->dev, "need at least one txn\n"); + ret = -EINVAL; + goto cleanup; + } + + txn->last_pat->next_pa = 0; + + /* write to PAT_DESCR to clear out any pending transaction */ + writel(0x0, dmm->base + reg[PAT_DESCR][engine->id]); + + /* wait for engine ready: */ + ret = wait_status(engine, DMM_PATSTATUS_READY); + if (ret) { + ret = -EFAULT; + goto cleanup; + } + + /* mark whether it is async to denote list management in IRQ handler */ + engine->async = wait ? false : true; + + /* kick reload */ + writel(engine->refill_pa, + dmm->base + reg[PAT_DESCR][engine->id]); + + if (wait) { + if (wait_event_interruptible_timeout(engine->wait_for_refill, + wait_status(engine, DMM_PATSTATUS_READY) == 0, + msecs_to_jiffies(1)) <= 0) { + dev_err(dmm->dev, "timed out waiting for done\n"); + ret = -ETIMEDOUT; + } + } + +cleanup: + /* only place engine back on list if we are done with it */ + if (ret || wait) + release_engine(engine); + + return ret; +} + +/* + * DMM programming + */ +static int fill(struct tcm_area *area, struct page **pages, + uint32_t npages, uint32_t roll, bool wait) +{ + int ret = 0; + struct tcm_area slice, area_s; + struct dmm_txn *txn; + + txn = dmm_txn_init(omap_dmm, area->tcm); + if (IS_ERR_OR_NULL(txn)) + return -ENOMEM; + + tcm_for_each_slice(slice, *area, area_s) { + struct pat_area p_area = { + .x0 = slice.p0.x, .y0 = slice.p0.y, + .x1 = slice.p1.x, .y1 = slice.p1.y, + }; + + dmm_txn_append(txn, &p_area, pages, npages, roll); + + roll += tcm_sizeof(slice); + } + + ret = dmm_txn_commit(txn, wait); + + return ret; +} + +/* + * Pin/unpin + */ + +/* note: slots for which pages[i] == NULL are filled w/ dummy page + */ +int tiler_pin(struct tiler_block *block, struct page **pages, + uint32_t npages, uint32_t roll, bool wait) +{ + int ret; + + ret = fill(&block->area, pages, npages, roll, wait); + + if (ret) + tiler_unpin(block); + + return ret; +} + +int tiler_unpin(struct tiler_block *block) +{ + return fill(&block->area, NULL, 0, 0, false); +} + +/* + * Reserve/release + */ +struct tiler_block *tiler_reserve_2d(enum tiler_fmt fmt, uint16_t w, + uint16_t h, uint16_t align) +{ + struct tiler_block *block = kzalloc(sizeof(*block), GFP_KERNEL); + u32 min_align = 128; + int ret; + unsigned long flags; + + BUG_ON(!validfmt(fmt)); + + /* convert width/height to slots */ + w = DIV_ROUND_UP(w, geom[fmt].slot_w); + h = DIV_ROUND_UP(h, geom[fmt].slot_h); + + /* convert alignment to slots */ + min_align = max(min_align, (geom[fmt].slot_w * geom[fmt].cpp)); + align = ALIGN(align, min_align); + align /= geom[fmt].slot_w * geom[fmt].cpp; + + block->fmt = fmt; + + ret = tcm_reserve_2d(containers[fmt], w, h, align, &block->area); + if (ret) { + kfree(block); + return ERR_PTR(-ENOMEM); + } + + /* add to allocation list */ + spin_lock_irqsave(&list_lock, flags); + list_add(&block->alloc_node, &omap_dmm->alloc_head); + spin_unlock_irqrestore(&list_lock, flags); + + return block; +} + +struct tiler_block *tiler_reserve_1d(size_t size) +{ + struct tiler_block *block = kzalloc(sizeof(*block), GFP_KERNEL); + int num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; + unsigned long flags; + + if (!block) + return ERR_PTR(-ENOMEM); + + block->fmt = TILFMT_PAGE; + + if (tcm_reserve_1d(containers[TILFMT_PAGE], num_pages, + &block->area)) { + kfree(block); + return ERR_PTR(-ENOMEM); + } + + spin_lock_irqsave(&list_lock, flags); + list_add(&block->alloc_node, &omap_dmm->alloc_head); + spin_unlock_irqrestore(&list_lock, flags); + + return block; +} + +/* note: if you have pin'd pages, you should have already unpin'd first! */ +int tiler_release(struct tiler_block *block) +{ + int ret = tcm_free(&block->area); + unsigned long flags; + + if (block->area.tcm) + dev_err(omap_dmm->dev, "failed to release block\n"); + + spin_lock_irqsave(&list_lock, flags); + list_del(&block->alloc_node); + spin_unlock_irqrestore(&list_lock, flags); + + kfree(block); + return ret; +} + +/* + * Utils + */ + +/* calculate the tiler space address of a pixel in a view orientation... + * below description copied from the display subsystem section of TRM: + * + * When the TILER is addressed, the bits: + * [28:27] = 0x0 for 8-bit tiled + * 0x1 for 16-bit tiled + * 0x2 for 32-bit tiled + * 0x3 for page mode + * [31:29] = 0x0 for 0-degree view + * 0x1 for 180-degree view + mirroring + * 0x2 for 0-degree view + mirroring + * 0x3 for 180-degree view + * 0x4 for 270-degree view + mirroring + * 0x5 for 270-degree view + * 0x6 for 90-degree view + * 0x7 for 90-degree view + mirroring + * Otherwise the bits indicated the corresponding bit address to access + * the SDRAM. + */ +static u32 tiler_get_address(enum tiler_fmt fmt, u32 orient, u32 x, u32 y) +{ + u32 x_bits, y_bits, tmp, x_mask, y_mask, alignment; + + x_bits = CONT_WIDTH_BITS - geom[fmt].x_shft; + y_bits = CONT_HEIGHT_BITS - geom[fmt].y_shft; + alignment = geom[fmt].x_shft + geom[fmt].y_shft; + + /* validate coordinate */ + x_mask = MASK(x_bits); + y_mask = MASK(y_bits); + + if (x < 0 || x > x_mask || y < 0 || y > y_mask) { + DBG("invalid coords: %u < 0 || %u > %u || %u < 0 || %u > %u", + x, x, x_mask, y, y, y_mask); + return 0; + } + + /* account for mirroring */ + if (orient & MASK_X_INVERT) + x ^= x_mask; + if (orient & MASK_Y_INVERT) + y ^= y_mask; + + /* get coordinate address */ + if (orient & MASK_XY_FLIP) + tmp = ((x << y_bits) + y); + else + tmp = ((y << x_bits) + x); + + return TIL_ADDR((tmp << alignment), orient, fmt); +} + +dma_addr_t tiler_ssptr(struct tiler_block *block) +{ + BUG_ON(!validfmt(block->fmt)); + + return TILVIEW_8BIT + tiler_get_address(block->fmt, 0, + block->area.p0.x * geom[block->fmt].slot_w, + block->area.p0.y * geom[block->fmt].slot_h); +} + +dma_addr_t tiler_tsptr(struct tiler_block *block, uint32_t orient, + uint32_t x, uint32_t y) +{ + struct tcm_pt *p = &block->area.p0; + BUG_ON(!validfmt(block->fmt)); + + return tiler_get_address(block->fmt, orient, + (p->x * geom[block->fmt].slot_w) + x, + (p->y * geom[block->fmt].slot_h) + y); +} + +void tiler_align(enum tiler_fmt fmt, uint16_t *w, uint16_t *h) +{ + BUG_ON(!validfmt(fmt)); + *w = round_up(*w, geom[fmt].slot_w); + *h = round_up(*h, geom[fmt].slot_h); +} + +uint32_t tiler_stride(enum tiler_fmt fmt, uint32_t orient) +{ + BUG_ON(!validfmt(fmt)); + + if (orient & MASK_XY_FLIP) + return 1 << (CONT_HEIGHT_BITS + geom[fmt].x_shft); + else + return 1 << (CONT_WIDTH_BITS + geom[fmt].y_shft); +} + +size_t tiler_size(enum tiler_fmt fmt, uint16_t w, uint16_t h) +{ + tiler_align(fmt, &w, &h); + return geom[fmt].cpp * w * h; +} + +size_t tiler_vsize(enum tiler_fmt fmt, uint16_t w, uint16_t h) +{ + BUG_ON(!validfmt(fmt)); + return round_up(geom[fmt].cpp * w, PAGE_SIZE) * h; +} + +bool dmm_is_available(void) +{ + return omap_dmm ? true : false; +} + +static int omap_dmm_remove(struct platform_device *dev) +{ + struct tiler_block *block, *_block; + int i; + unsigned long flags; + + if (omap_dmm) { + /* free all area regions */ + spin_lock_irqsave(&list_lock, flags); + list_for_each_entry_safe(block, _block, &omap_dmm->alloc_head, + alloc_node) { + list_del(&block->alloc_node); + kfree(block); + } + spin_unlock_irqrestore(&list_lock, flags); + + for (i = 0; i < omap_dmm->num_lut; i++) + if (omap_dmm->tcm && omap_dmm->tcm[i]) + omap_dmm->tcm[i]->deinit(omap_dmm->tcm[i]); + kfree(omap_dmm->tcm); + + kfree(omap_dmm->engines); + if (omap_dmm->refill_va) + dma_free_writecombine(omap_dmm->dev, + REFILL_BUFFER_SIZE * omap_dmm->num_engines, + omap_dmm->refill_va, + omap_dmm->refill_pa); + if (omap_dmm->dummy_page) + __free_page(omap_dmm->dummy_page); + + if (omap_dmm->irq > 0) + free_irq(omap_dmm->irq, omap_dmm); + + iounmap(omap_dmm->base); + kfree(omap_dmm); + omap_dmm = NULL; + } + + return 0; +} + +static int omap_dmm_probe(struct platform_device *dev) +{ + int ret = -EFAULT, i; + struct tcm_area area = {0}; + u32 hwinfo, pat_geom; + struct resource *mem; + + omap_dmm = kzalloc(sizeof(*omap_dmm), GFP_KERNEL); + if (!omap_dmm) { + dev_err(&dev->dev, "failed to allocate driver data section\n"); + goto fail; + } + + /* initialize lists */ + INIT_LIST_HEAD(&omap_dmm->alloc_head); + INIT_LIST_HEAD(&omap_dmm->idle_head); + + init_waitqueue_head(&omap_dmm->engine_queue); + + /* lookup hwmod data - base address and irq */ + mem = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&dev->dev, "failed to get base address resource\n"); + goto fail; + } + + omap_dmm->base = ioremap(mem->start, SZ_2K); + + if (!omap_dmm->base) { + dev_err(&dev->dev, "failed to get dmm base address\n"); + goto fail; + } + + omap_dmm->irq = platform_get_irq(dev, 0); + if (omap_dmm->irq < 0) { + dev_err(&dev->dev, "failed to get IRQ resource\n"); + goto fail; + } + + omap_dmm->dev = &dev->dev; + + hwinfo = readl(omap_dmm->base + DMM_PAT_HWINFO); + omap_dmm->num_engines = (hwinfo >> 24) & 0x1F; + omap_dmm->num_lut = (hwinfo >> 16) & 0x1F; + omap_dmm->container_width = 256; + omap_dmm->container_height = 128; + + atomic_set(&omap_dmm->engine_counter, omap_dmm->num_engines); + + /* read out actual LUT width and height */ + pat_geom = readl(omap_dmm->base + DMM_PAT_GEOMETRY); + omap_dmm->lut_width = ((pat_geom >> 16) & 0xF) << 5; + omap_dmm->lut_height = ((pat_geom >> 24) & 0xF) << 5; + + /* increment LUT by one if on OMAP5 */ + /* LUT has twice the height, and is split into a separate container */ + if (omap_dmm->lut_height != omap_dmm->container_height) + omap_dmm->num_lut++; + + /* initialize DMM registers */ + writel(0x88888888, omap_dmm->base + DMM_PAT_VIEW__0); + writel(0x88888888, omap_dmm->base + DMM_PAT_VIEW__1); + writel(0x80808080, omap_dmm->base + DMM_PAT_VIEW_MAP__0); + writel(0x80000000, omap_dmm->base + DMM_PAT_VIEW_MAP_BASE); + writel(0x88888888, omap_dmm->base + DMM_TILER_OR__0); + writel(0x88888888, omap_dmm->base + DMM_TILER_OR__1); + + ret = request_irq(omap_dmm->irq, omap_dmm_irq_handler, IRQF_SHARED, + "omap_dmm_irq_handler", omap_dmm); + + if (ret) { + dev_err(&dev->dev, "couldn't register IRQ %d, error %d\n", + omap_dmm->irq, ret); + omap_dmm->irq = -1; + goto fail; + } + + /* Enable all interrupts for each refill engine except + * ERR_LUT_MISS (which is just advisory, and we don't care + * about because we want to be able to refill live scanout + * buffers for accelerated pan/scroll) and FILL_DSC which + * we just generally don't care about. + */ + writel(0x7e7e7e7e, omap_dmm->base + DMM_PAT_IRQENABLE_SET); + + omap_dmm->dummy_page = alloc_page(GFP_KERNEL | __GFP_DMA32); + if (!omap_dmm->dummy_page) { + dev_err(&dev->dev, "could not allocate dummy page\n"); + ret = -ENOMEM; + goto fail; + } + + /* set dma mask for device */ + /* NOTE: this is a workaround for the hwmod not initializing properly */ + dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + + omap_dmm->dummy_pa = page_to_phys(omap_dmm->dummy_page); + + /* alloc refill memory */ + omap_dmm->refill_va = dma_alloc_writecombine(&dev->dev, + REFILL_BUFFER_SIZE * omap_dmm->num_engines, + &omap_dmm->refill_pa, GFP_KERNEL); + if (!omap_dmm->refill_va) { + dev_err(&dev->dev, "could not allocate refill memory\n"); + goto fail; + } + + /* alloc engines */ + omap_dmm->engines = kzalloc( + omap_dmm->num_engines * sizeof(struct refill_engine), + GFP_KERNEL); + if (!omap_dmm->engines) { + dev_err(&dev->dev, "could not allocate engines\n"); + ret = -ENOMEM; + goto fail; + } + + for (i = 0; i < omap_dmm->num_engines; i++) { + omap_dmm->engines[i].id = i; + omap_dmm->engines[i].dmm = omap_dmm; + omap_dmm->engines[i].refill_va = omap_dmm->refill_va + + (REFILL_BUFFER_SIZE * i); + omap_dmm->engines[i].refill_pa = omap_dmm->refill_pa + + (REFILL_BUFFER_SIZE * i); + init_waitqueue_head(&omap_dmm->engines[i].wait_for_refill); + + list_add(&omap_dmm->engines[i].idle_node, &omap_dmm->idle_head); + } + + omap_dmm->tcm = kzalloc(omap_dmm->num_lut * sizeof(*omap_dmm->tcm), + GFP_KERNEL); + if (!omap_dmm->tcm) { + dev_err(&dev->dev, "failed to allocate lut ptrs\n"); + ret = -ENOMEM; + goto fail; + } + + /* init containers */ + /* Each LUT is associated with a TCM (container manager). We use the + lut_id to denote the lut_id used to identify the correct LUT for + programming during reill operations */ + for (i = 0; i < omap_dmm->num_lut; i++) { + omap_dmm->tcm[i] = sita_init(omap_dmm->container_width, + omap_dmm->container_height, + NULL); + + if (!omap_dmm->tcm[i]) { + dev_err(&dev->dev, "failed to allocate container\n"); + ret = -ENOMEM; + goto fail; + } + + omap_dmm->tcm[i]->lut_id = i; + } + + /* assign access mode containers to applicable tcm container */ + /* OMAP 4 has 1 container for all 4 views */ + /* OMAP 5 has 2 containers, 1 for 2D and 1 for 1D */ + containers[TILFMT_8BIT] = omap_dmm->tcm[0]; + containers[TILFMT_16BIT] = omap_dmm->tcm[0]; + containers[TILFMT_32BIT] = omap_dmm->tcm[0]; + + if (omap_dmm->container_height != omap_dmm->lut_height) { + /* second LUT is used for PAGE mode. Programming must use + y offset that is added to all y coordinates. LUT id is still + 0, because it is the same LUT, just the upper 128 lines */ + containers[TILFMT_PAGE] = omap_dmm->tcm[1]; + omap_dmm->tcm[1]->y_offset = OMAP5_LUT_OFFSET; + omap_dmm->tcm[1]->lut_id = 0; + } else { + containers[TILFMT_PAGE] = omap_dmm->tcm[0]; + } + + area = (struct tcm_area) { + .tcm = NULL, + .p1.x = omap_dmm->container_width - 1, + .p1.y = omap_dmm->container_height - 1, + }; + + /* initialize all LUTs to dummy page entries */ + for (i = 0; i < omap_dmm->num_lut; i++) { + area.tcm = omap_dmm->tcm[i]; + if (fill(&area, NULL, 0, 0, true)) + dev_err(omap_dmm->dev, "refill failed"); + } + + dev_info(omap_dmm->dev, "initialized all PAT entries\n"); + + return 0; + +fail: + if (omap_dmm_remove(dev)) + dev_err(&dev->dev, "cleanup failed\n"); + return ret; +} + +/* + * debugfs support + */ + +#ifdef CONFIG_DEBUG_FS + +static const char *alphabet = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; +static const char *special = ".,:;'\"`~!^-+"; + +static void fill_map(char **map, int xdiv, int ydiv, struct tcm_area *a, + char c, bool ovw) +{ + int x, y; + for (y = a->p0.y / ydiv; y <= a->p1.y / ydiv; y++) + for (x = a->p0.x / xdiv; x <= a->p1.x / xdiv; x++) + if (map[y][x] == ' ' || ovw) + map[y][x] = c; +} + +static void fill_map_pt(char **map, int xdiv, int ydiv, struct tcm_pt *p, + char c) +{ + map[p->y / ydiv][p->x / xdiv] = c; +} + +static char read_map_pt(char **map, int xdiv, int ydiv, struct tcm_pt *p) +{ + return map[p->y / ydiv][p->x / xdiv]; +} + +static int map_width(int xdiv, int x0, int x1) +{ + return (x1 / xdiv) - (x0 / xdiv) + 1; +} + +static void text_map(char **map, int xdiv, char *nice, int yd, int x0, int x1) +{ + char *p = map[yd] + (x0 / xdiv); + int w = (map_width(xdiv, x0, x1) - strlen(nice)) / 2; + if (w >= 0) { + p += w; + while (*nice) + *p++ = *nice++; + } +} + +static void map_1d_info(char **map, int xdiv, int ydiv, char *nice, + struct tcm_area *a) +{ + sprintf(nice, "%dK", tcm_sizeof(*a) * 4); + if (a->p0.y + 1 < a->p1.y) { + text_map(map, xdiv, nice, (a->p0.y + a->p1.y) / 2 / ydiv, 0, + 256 - 1); + } else if (a->p0.y < a->p1.y) { + if (strlen(nice) < map_width(xdiv, a->p0.x, 256 - 1)) + text_map(map, xdiv, nice, a->p0.y / ydiv, + a->p0.x + xdiv, 256 - 1); + else if (strlen(nice) < map_width(xdiv, 0, a->p1.x)) + text_map(map, xdiv, nice, a->p1.y / ydiv, + 0, a->p1.y - xdiv); + } else if (strlen(nice) + 1 < map_width(xdiv, a->p0.x, a->p1.x)) { + text_map(map, xdiv, nice, a->p0.y / ydiv, a->p0.x, a->p1.x); + } +} + +static void map_2d_info(char **map, int xdiv, int ydiv, char *nice, + struct tcm_area *a) +{ + sprintf(nice, "(%d*%d)", tcm_awidth(*a), tcm_aheight(*a)); + if (strlen(nice) + 1 < map_width(xdiv, a->p0.x, a->p1.x)) + text_map(map, xdiv, nice, (a->p0.y + a->p1.y) / 2 / ydiv, + a->p0.x, a->p1.x); +} + +int tiler_map_show(struct seq_file *s, void *arg) +{ + int xdiv = 2, ydiv = 1; + char **map = NULL, *global_map; + struct tiler_block *block; + struct tcm_area a, p; + int i; + const char *m2d = alphabet; + const char *a2d = special; + const char *m2dp = m2d, *a2dp = a2d; + char nice[128]; + int h_adj; + int w_adj; + unsigned long flags; + int lut_idx; + + + if (!omap_dmm) { + /* early return if dmm/tiler device is not initialized */ + return 0; + } + + h_adj = omap_dmm->container_height / ydiv; + w_adj = omap_dmm->container_width / xdiv; + + map = kmalloc(h_adj * sizeof(*map), GFP_KERNEL); + global_map = kmalloc((w_adj + 1) * h_adj, GFP_KERNEL); + + if (!map || !global_map) + goto error; + + for (lut_idx = 0; lut_idx < omap_dmm->num_lut; lut_idx++) { + memset(map, 0, sizeof(h_adj * sizeof(*map))); + memset(global_map, ' ', (w_adj + 1) * h_adj); + + for (i = 0; i < omap_dmm->container_height; i++) { + map[i] = global_map + i * (w_adj + 1); + map[i][w_adj] = 0; + } + + spin_lock_irqsave(&list_lock, flags); + + list_for_each_entry(block, &omap_dmm->alloc_head, alloc_node) { + if (block->area.tcm == omap_dmm->tcm[lut_idx]) { + if (block->fmt != TILFMT_PAGE) { + fill_map(map, xdiv, ydiv, &block->area, + *m2dp, true); + if (!*++a2dp) + a2dp = a2d; + if (!*++m2dp) + m2dp = m2d; + map_2d_info(map, xdiv, ydiv, nice, + &block->area); + } else { + bool start = read_map_pt(map, xdiv, + ydiv, &block->area.p0) == ' '; + bool end = read_map_pt(map, xdiv, ydiv, + &block->area.p1) == ' '; + + tcm_for_each_slice(a, block->area, p) + fill_map(map, xdiv, ydiv, &a, + '=', true); + fill_map_pt(map, xdiv, ydiv, + &block->area.p0, + start ? '<' : 'X'); + fill_map_pt(map, xdiv, ydiv, + &block->area.p1, + end ? '>' : 'X'); + map_1d_info(map, xdiv, ydiv, nice, + &block->area); + } + } + } + + spin_unlock_irqrestore(&list_lock, flags); + + if (s) { + seq_printf(s, "CONTAINER %d DUMP BEGIN\n", lut_idx); + for (i = 0; i < 128; i++) + seq_printf(s, "%03d:%s\n", i, map[i]); + seq_printf(s, "CONTAINER %d DUMP END\n", lut_idx); + } else { + dev_dbg(omap_dmm->dev, "CONTAINER %d DUMP BEGIN\n", + lut_idx); + for (i = 0; i < 128; i++) + dev_dbg(omap_dmm->dev, "%03d:%s\n", i, map[i]); + dev_dbg(omap_dmm->dev, "CONTAINER %d DUMP END\n", + lut_idx); + } + } + +error: + kfree(map); + kfree(global_map); + + return 0; +} +#endif + +#ifdef CONFIG_PM +static int omap_dmm_resume(struct device *dev) +{ + struct tcm_area area; + int i; + + if (!omap_dmm) + return -ENODEV; + + area = (struct tcm_area) { + .tcm = NULL, + .p1.x = omap_dmm->container_width - 1, + .p1.y = omap_dmm->container_height - 1, + }; + + /* initialize all LUTs to dummy page entries */ + for (i = 0; i < omap_dmm->num_lut; i++) { + area.tcm = omap_dmm->tcm[i]; + if (fill(&area, NULL, 0, 0, true)) + dev_err(dev, "refill failed"); + } + + return 0; +} + +static const struct dev_pm_ops omap_dmm_pm_ops = { + .resume = omap_dmm_resume, +}; +#endif + +struct platform_driver omap_dmm_driver = { + .probe = omap_dmm_probe, + .remove = omap_dmm_remove, + .driver = { + .owner = THIS_MODULE, + .name = DMM_DRIVER_NAME, +#ifdef CONFIG_PM + .pm = &omap_dmm_pm_ops, +#endif + }, +}; + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Andy Gross "); +MODULE_DESCRIPTION("OMAP DMM/Tiler Driver"); +MODULE_ALIAS("platform:" DMM_DRIVER_NAME); diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h new file mode 100644 index 000000000000..4fdd61e54bd2 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h @@ -0,0 +1,141 @@ +/* + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Rob Clark + * Andy Gross + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef OMAP_DMM_TILER_H +#define OMAP_DMM_TILER_H + +#include "omap_drv.h" +#include "tcm.h" + +enum tiler_fmt { + TILFMT_8BIT = 0, + TILFMT_16BIT, + TILFMT_32BIT, + TILFMT_PAGE, + TILFMT_NFORMATS +}; + +struct pat_area { + u32 x0:8; + u32 y0:8; + u32 x1:8; + u32 y1:8; +}; + +struct tiler_block { + struct list_head alloc_node; /* node for global block list */ + struct tcm_area area; /* area */ + enum tiler_fmt fmt; /* format */ +}; + +/* bits representing the same slot in DMM-TILER hw-block */ +#define SLOT_WIDTH_BITS 6 +#define SLOT_HEIGHT_BITS 6 + +/* bits reserved to describe coordinates in DMM-TILER hw-block */ +#define CONT_WIDTH_BITS 14 +#define CONT_HEIGHT_BITS 13 + +/* calculated constants */ +#define TILER_PAGE (1 << (SLOT_WIDTH_BITS + SLOT_HEIGHT_BITS)) +#define TILER_WIDTH (1 << (CONT_WIDTH_BITS - SLOT_WIDTH_BITS)) +#define TILER_HEIGHT (1 << (CONT_HEIGHT_BITS - SLOT_HEIGHT_BITS)) + +/* +Table 15-11. Coding and Description of TILER Orientations +S Y X Description Alternate description +0 0 0 0-degree view Natural view +0 0 1 0-degree view with vertical mirror 180-degree view with horizontal mirror +0 1 0 0-degree view with horizontal mirror 180-degree view with vertical mirror +0 1 1 180-degree view +1 0 0 90-degree view with vertical mirror 270-degree view with horizontal mirror +1 0 1 270-degree view +1 1 0 90-degree view +1 1 1 90-degree view with horizontal mirror 270-degree view with vertical mirror + */ +#define MASK_XY_FLIP (1 << 31) +#define MASK_Y_INVERT (1 << 30) +#define MASK_X_INVERT (1 << 29) +#define SHIFT_ACC_MODE 27 +#define MASK_ACC_MODE 3 + +#define MASK(bits) ((1 << (bits)) - 1) + +#define TILVIEW_8BIT 0x60000000u +#define TILVIEW_16BIT (TILVIEW_8BIT + VIEW_SIZE) +#define TILVIEW_32BIT (TILVIEW_16BIT + VIEW_SIZE) +#define TILVIEW_PAGE (TILVIEW_32BIT + VIEW_SIZE) +#define TILVIEW_END (TILVIEW_PAGE + VIEW_SIZE) + +/* create tsptr by adding view orientation and access mode */ +#define TIL_ADDR(x, orient, a)\ + ((u32) (x) | (orient) | ((a) << SHIFT_ACC_MODE)) + +#ifdef CONFIG_DEBUG_FS +int tiler_map_show(struct seq_file *s, void *arg); +#endif + +/* pin/unpin */ +int tiler_pin(struct tiler_block *block, struct page **pages, + uint32_t npages, uint32_t roll, bool wait); +int tiler_unpin(struct tiler_block *block); + +/* reserve/release */ +struct tiler_block *tiler_reserve_2d(enum tiler_fmt fmt, uint16_t w, uint16_t h, + uint16_t align); +struct tiler_block *tiler_reserve_1d(size_t size); +int tiler_release(struct tiler_block *block); + +/* utilities */ +dma_addr_t tiler_ssptr(struct tiler_block *block); +dma_addr_t tiler_tsptr(struct tiler_block *block, uint32_t orient, + uint32_t x, uint32_t y); +uint32_t tiler_stride(enum tiler_fmt fmt, uint32_t orient); +size_t tiler_size(enum tiler_fmt fmt, uint16_t w, uint16_t h); +size_t tiler_vsize(enum tiler_fmt fmt, uint16_t w, uint16_t h); +void tiler_align(enum tiler_fmt fmt, uint16_t *w, uint16_t *h); +bool dmm_is_available(void); + +extern struct platform_driver omap_dmm_driver; + +/* GEM bo flags -> tiler fmt */ +static inline enum tiler_fmt gem2fmt(uint32_t flags) +{ + switch (flags & OMAP_BO_TILED) { + case OMAP_BO_TILED_8: + return TILFMT_8BIT; + case OMAP_BO_TILED_16: + return TILFMT_16BIT; + case OMAP_BO_TILED_32: + return TILFMT_32BIT; + default: + return TILFMT_PAGE; + } +} + +static inline bool validfmt(enum tiler_fmt fmt) +{ + switch (fmt) { + case TILFMT_8BIT: + case TILFMT_16BIT: + case TILFMT_32BIT: + case TILFMT_PAGE: + return true; + default: + return false; + } +} + +#endif diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c new file mode 100644 index 000000000000..9083538bd16a --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -0,0 +1,610 @@ +/* + * drivers/gpu/drm/omapdrm/omap_drv.c + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "omap_drv.h" + +#include "drm_crtc_helper.h" +#include "drm_fb_helper.h" +#include "omap_dmm_tiler.h" + +#define DRIVER_NAME MODULE_NAME +#define DRIVER_DESC "OMAP DRM" +#define DRIVER_DATE "20110917" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 0 + +static int num_crtc = CONFIG_DRM_OMAP_NUM_CRTCS; + +MODULE_PARM_DESC(num_crtc, "Number of overlays to use as CRTCs"); +module_param(num_crtc, int, 0600); + +/* + * mode config funcs + */ + +/* Notes about mapping DSS and DRM entities: + * CRTC: overlay + * encoder: manager.. with some extension to allow one primary CRTC + * and zero or more video CRTC's to be mapped to one encoder? + * connector: dssdev.. manager can be attached/detached from different + * devices + */ + +static void omap_fb_output_poll_changed(struct drm_device *dev) +{ + struct omap_drm_private *priv = dev->dev_private; + DBG("dev=%p", dev); + if (priv->fbdev) + drm_fb_helper_hotplug_event(priv->fbdev); +} + +static const struct drm_mode_config_funcs omap_mode_config_funcs = { + .fb_create = omap_framebuffer_create, + .output_poll_changed = omap_fb_output_poll_changed, +}; + +static int get_connector_type(struct omap_dss_device *dssdev) +{ + switch (dssdev->type) { + case OMAP_DISPLAY_TYPE_HDMI: + return DRM_MODE_CONNECTOR_HDMIA; + case OMAP_DISPLAY_TYPE_DPI: + if (!strcmp(dssdev->name, "dvi")) + return DRM_MODE_CONNECTOR_DVID; + /* fallthrough */ + default: + return DRM_MODE_CONNECTOR_Unknown; + } +} + +static int omap_modeset_init(struct drm_device *dev) +{ + struct omap_drm_private *priv = dev->dev_private; + struct omap_dss_device *dssdev = NULL; + int num_ovls = dss_feat_get_num_ovls(); + int id; + + drm_mode_config_init(dev); + + omap_drm_irq_install(dev); + + /* + * Create private planes and CRTCs for the last NUM_CRTCs overlay + * plus manager: + */ + for (id = 0; id < min(num_crtc, num_ovls); id++) { + struct drm_plane *plane; + struct drm_crtc *crtc; + + plane = omap_plane_init(dev, id, true); + crtc = omap_crtc_init(dev, plane, pipe2chan(id), id); + + BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs)); + priv->crtcs[id] = crtc; + priv->num_crtcs++; + + priv->planes[id] = plane; + priv->num_planes++; + } + + /* + * Create normal planes for the remaining overlays: + */ + for (; id < num_ovls; id++) { + struct drm_plane *plane = omap_plane_init(dev, id, false); + + BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes)); + priv->planes[priv->num_planes++] = plane; + } + + for_each_dss_dev(dssdev) { + struct drm_connector *connector; + struct drm_encoder *encoder; + + if (!dssdev->driver) { + dev_warn(dev->dev, "%s has no driver.. skipping it\n", + dssdev->name); + return 0; + } + + if (!(dssdev->driver->get_timings || + dssdev->driver->read_edid)) { + dev_warn(dev->dev, "%s driver does not support " + "get_timings or read_edid.. skipping it!\n", + dssdev->name); + return 0; + } + + encoder = omap_encoder_init(dev, dssdev); + + if (!encoder) { + dev_err(dev->dev, "could not create encoder: %s\n", + dssdev->name); + return -ENOMEM; + } + + connector = omap_connector_init(dev, + get_connector_type(dssdev), dssdev, encoder); + + if (!connector) { + dev_err(dev->dev, "could not create connector: %s\n", + dssdev->name); + return -ENOMEM; + } + + BUG_ON(priv->num_encoders >= ARRAY_SIZE(priv->encoders)); + BUG_ON(priv->num_connectors >= ARRAY_SIZE(priv->connectors)); + + priv->encoders[priv->num_encoders++] = encoder; + priv->connectors[priv->num_connectors++] = connector; + + drm_mode_connector_attach_encoder(connector, encoder); + + /* figure out which crtc's we can connect the encoder to: */ + encoder->possible_crtcs = 0; + for (id = 0; id < priv->num_crtcs; id++) { + enum omap_dss_output_id supported_outputs = + dss_feat_get_supported_outputs(pipe2chan(id)); + if (supported_outputs & dssdev->output->id) + encoder->possible_crtcs |= (1 << id); + } + } + + dev->mode_config.min_width = 32; + dev->mode_config.min_height = 32; + + /* note: eventually will need some cpu_is_omapXYZ() type stuff here + * to fill in these limits properly on different OMAP generations.. + */ + dev->mode_config.max_width = 2048; + dev->mode_config.max_height = 2048; + + dev->mode_config.funcs = &omap_mode_config_funcs; + + return 0; +} + +static void omap_modeset_free(struct drm_device *dev) +{ + drm_mode_config_cleanup(dev); +} + +/* + * drm ioctl funcs + */ + + +static int ioctl_get_param(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct omap_drm_private *priv = dev->dev_private; + struct drm_omap_param *args = data; + + DBG("%p: param=%llu", dev, args->param); + + switch (args->param) { + case OMAP_PARAM_CHIPSET_ID: + args->value = priv->omaprev; + break; + default: + DBG("unknown parameter %lld", args->param); + return -EINVAL; + } + + return 0; +} + +static int ioctl_set_param(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_omap_param *args = data; + + switch (args->param) { + default: + DBG("unknown parameter %lld", args->param); + return -EINVAL; + } + + return 0; +} + +static int ioctl_gem_new(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_omap_gem_new *args = data; + VERB("%p:%p: size=0x%08x, flags=%08x", dev, file_priv, + args->size.bytes, args->flags); + return omap_gem_new_handle(dev, file_priv, args->size, + args->flags, &args->handle); +} + +static int ioctl_gem_cpu_prep(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_omap_gem_cpu_prep *args = data; + struct drm_gem_object *obj; + int ret; + + VERB("%p:%p: handle=%d, op=%x", dev, file_priv, args->handle, args->op); + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (!obj) + return -ENOENT; + + ret = omap_gem_op_sync(obj, args->op); + + if (!ret) + ret = omap_gem_op_start(obj, args->op); + + drm_gem_object_unreference_unlocked(obj); + + return ret; +} + +static int ioctl_gem_cpu_fini(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_omap_gem_cpu_fini *args = data; + struct drm_gem_object *obj; + int ret; + + VERB("%p:%p: handle=%d", dev, file_priv, args->handle); + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (!obj) + return -ENOENT; + + /* XXX flushy, flushy */ + ret = 0; + + if (!ret) + ret = omap_gem_op_finish(obj, args->op); + + drm_gem_object_unreference_unlocked(obj); + + return ret; +} + +static int ioctl_gem_info(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_omap_gem_info *args = data; + struct drm_gem_object *obj; + int ret = 0; + + VERB("%p:%p: handle=%d", dev, file_priv, args->handle); + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (!obj) + return -ENOENT; + + args->size = omap_gem_mmap_size(obj); + args->offset = omap_gem_mmap_offset(obj); + + drm_gem_object_unreference_unlocked(obj); + + return ret; +} + +struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = { + DRM_IOCTL_DEF_DRV(OMAP_GET_PARAM, ioctl_get_param, DRM_UNLOCKED|DRM_AUTH), + DRM_IOCTL_DEF_DRV(OMAP_SET_PARAM, ioctl_set_param, DRM_UNLOCKED|DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF_DRV(OMAP_GEM_NEW, ioctl_gem_new, DRM_UNLOCKED|DRM_AUTH), + DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_PREP, ioctl_gem_cpu_prep, DRM_UNLOCKED|DRM_AUTH), + DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_FINI, ioctl_gem_cpu_fini, DRM_UNLOCKED|DRM_AUTH), + DRM_IOCTL_DEF_DRV(OMAP_GEM_INFO, ioctl_gem_info, DRM_UNLOCKED|DRM_AUTH), +}; + +/* + * drm driver funcs + */ + +/** + * load - setup chip and create an initial config + * @dev: DRM device + * @flags: startup flags + * + * The driver load routine has to do several things: + * - initialize the memory manager + * - allocate initial config memory + * - setup the DRM framebuffer with the allocated memory + */ +static int dev_load(struct drm_device *dev, unsigned long flags) +{ + struct omap_drm_platform_data *pdata = dev->dev->platform_data; + struct omap_drm_private *priv; + int ret; + + DBG("load: dev=%p", dev); + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(dev->dev, "could not allocate priv\n"); + return -ENOMEM; + } + + priv->omaprev = pdata->omaprev; + + dev->dev_private = priv; + + priv->wq = alloc_ordered_workqueue("omapdrm", 0); + + INIT_LIST_HEAD(&priv->obj_list); + + omap_gem_init(dev); + + ret = omap_modeset_init(dev); + if (ret) { + dev_err(dev->dev, "omap_modeset_init failed: ret=%d\n", ret); + dev->dev_private = NULL; + kfree(priv); + return ret; + } + + ret = drm_vblank_init(dev, priv->num_crtcs); + if (ret) + dev_warn(dev->dev, "could not init vblank\n"); + + priv->fbdev = omap_fbdev_init(dev); + if (!priv->fbdev) { + dev_warn(dev->dev, "omap_fbdev_init failed\n"); + /* well, limp along without an fbdev.. maybe X11 will work? */ + } + + /* store off drm_device for use in pm ops */ + dev_set_drvdata(dev->dev, dev); + + drm_kms_helper_poll_init(dev); + + return 0; +} + +static int dev_unload(struct drm_device *dev) +{ + struct omap_drm_private *priv = dev->dev_private; + + DBG("unload: dev=%p", dev); + + drm_kms_helper_poll_fini(dev); + drm_vblank_cleanup(dev); + omap_drm_irq_uninstall(dev); + + omap_fbdev_free(dev); + omap_modeset_free(dev); + omap_gem_deinit(dev); + + flush_workqueue(priv->wq); + destroy_workqueue(priv->wq); + + kfree(dev->dev_private); + dev->dev_private = NULL; + + dev_set_drvdata(dev->dev, NULL); + + return 0; +} + +static int dev_open(struct drm_device *dev, struct drm_file *file) +{ + file->driver_priv = NULL; + + DBG("open: dev=%p, file=%p", dev, file); + + return 0; +} + +static int dev_firstopen(struct drm_device *dev) +{ + DBG("firstopen: dev=%p", dev); + return 0; +} + +/** + * lastclose - clean up after all DRM clients have exited + * @dev: DRM device + * + * Take care of cleaning up after all DRM clients have exited. In the + * mode setting case, we want to restore the kernel's initial mode (just + * in case the last client left us in a bad state). + */ +static void dev_lastclose(struct drm_device *dev) +{ + int i; + + /* we don't support vga-switcheroo.. so just make sure the fbdev + * mode is active + */ + struct omap_drm_private *priv = dev->dev_private; + int ret; + + DBG("lastclose: dev=%p", dev); + + if (priv->rotation_prop) { + /* need to restore default rotation state.. not sure + * if there is a cleaner way to restore properties to + * default state? Maybe a flag that properties should + * automatically be restored to default state on + * lastclose? + */ + for (i = 0; i < priv->num_crtcs; i++) { + drm_object_property_set_value(&priv->crtcs[i]->base, + priv->rotation_prop, 0); + } + + for (i = 0; i < priv->num_planes; i++) { + drm_object_property_set_value(&priv->planes[i]->base, + priv->rotation_prop, 0); + } + } + + drm_modeset_lock_all(dev); + ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev); + drm_modeset_unlock_all(dev); + if (ret) + DBG("failed to restore crtc mode"); +} + +static void dev_preclose(struct drm_device *dev, struct drm_file *file) +{ + DBG("preclose: dev=%p", dev); +} + +static void dev_postclose(struct drm_device *dev, struct drm_file *file) +{ + DBG("postclose: dev=%p, file=%p", dev, file); +} + +static const struct vm_operations_struct omap_gem_vm_ops = { + .fault = omap_gem_fault, + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, +}; + +static const struct file_operations omapdriver_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .unlocked_ioctl = drm_ioctl, + .release = drm_release, + .mmap = omap_gem_mmap, + .poll = drm_poll, + .fasync = drm_fasync, + .read = drm_read, + .llseek = noop_llseek, +}; + +static struct drm_driver omap_drm_driver = { + .driver_features = + DRIVER_HAVE_IRQ | DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, + .load = dev_load, + .unload = dev_unload, + .open = dev_open, + .firstopen = dev_firstopen, + .lastclose = dev_lastclose, + .preclose = dev_preclose, + .postclose = dev_postclose, + .get_vblank_counter = drm_vblank_count, + .enable_vblank = omap_irq_enable_vblank, + .disable_vblank = omap_irq_disable_vblank, + .irq_preinstall = omap_irq_preinstall, + .irq_postinstall = omap_irq_postinstall, + .irq_uninstall = omap_irq_uninstall, + .irq_handler = omap_irq_handler, +#ifdef CONFIG_DEBUG_FS + .debugfs_init = omap_debugfs_init, + .debugfs_cleanup = omap_debugfs_cleanup, +#endif + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_export = omap_gem_prime_export, + .gem_prime_import = omap_gem_prime_import, + .gem_init_object = omap_gem_init_object, + .gem_free_object = omap_gem_free_object, + .gem_vm_ops = &omap_gem_vm_ops, + .dumb_create = omap_gem_dumb_create, + .dumb_map_offset = omap_gem_dumb_map_offset, + .dumb_destroy = omap_gem_dumb_destroy, + .ioctls = ioctls, + .num_ioctls = DRM_OMAP_NUM_IOCTLS, + .fops = &omapdriver_fops, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, +}; + +static int pdev_suspend(struct platform_device *pDevice, pm_message_t state) +{ + DBG(""); + return 0; +} + +static int pdev_resume(struct platform_device *device) +{ + DBG(""); + return 0; +} + +static void pdev_shutdown(struct platform_device *device) +{ + DBG(""); +} + +static int pdev_probe(struct platform_device *device) +{ + DBG("%s", device->name); + return drm_platform_init(&omap_drm_driver, device); +} + +static int pdev_remove(struct platform_device *device) +{ + DBG(""); + drm_platform_exit(&omap_drm_driver, device); + + platform_driver_unregister(&omap_dmm_driver); + return 0; +} + +#ifdef CONFIG_PM +static const struct dev_pm_ops omapdrm_pm_ops = { + .resume = omap_gem_resume, +}; +#endif + +struct platform_driver pdev = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &omapdrm_pm_ops, +#endif + }, + .probe = pdev_probe, + .remove = pdev_remove, + .suspend = pdev_suspend, + .resume = pdev_resume, + .shutdown = pdev_shutdown, +}; + +static int __init omap_drm_init(void) +{ + DBG("init"); + if (platform_driver_register(&omap_dmm_driver)) { + /* we can continue on without DMM.. so not fatal */ + dev_err(NULL, "DMM registration failed\n"); + } + return platform_driver_register(&pdev); +} + +static void __exit omap_drm_fini(void) +{ + DBG("fini"); + platform_driver_unregister(&pdev); +} + +/* need late_initcall() so we load after dss_driver's are loaded */ +late_initcall(omap_drm_init); +module_exit(omap_drm_fini); + +MODULE_AUTHOR("Rob Clark "); +MODULE_DESCRIPTION("OMAP DRM Display Driver"); +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h new file mode 100644 index 000000000000..d4f997bb4ac0 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -0,0 +1,333 @@ +/* + * drivers/gpu/drm/omapdrm/omap_drv.h + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef __OMAP_DRV_H__ +#define __OMAP_DRV_H__ + +#include