diff options
Diffstat (limited to 'drivers')
175 files changed, 9285 insertions, 4479 deletions
diff --git a/drivers/base/soc.c b/drivers/base/soc.c index ba29b2e..72b5e72 100644 --- a/drivers/base/soc.c +++ b/drivers/base/soc.c @@ -42,7 +42,7 @@ struct device *soc_device_to_device(struct soc_device *soc_dev) return &soc_dev->dev; } -static mode_t soc_attribute_mode(struct kobject *kobj, +static umode_t soc_attribute_mode(struct kobject *kobj, struct attribute *attr, int index) { diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c index d703823..7053140 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.c +++ b/drivers/gpu/drm/cirrus/cirrus_drv.c @@ -35,9 +35,28 @@ static DEFINE_PCI_DEVICE_TABLE(pciidlist) = { {0,} }; + +static void cirrus_kick_out_firmware_fb(struct pci_dev *pdev) +{ + struct apertures_struct *ap; + bool primary = false; + + ap = alloc_apertures(1); + ap->ranges[0].base = pci_resource_start(pdev, 0); + ap->ranges[0].size = pci_resource_len(pdev, 0); + +#ifdef CONFIG_X86 + primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; +#endif + remove_conflicting_framebuffers(ap, "cirrusdrmfb", primary); + kfree(ap); +} + static int __devinit cirrus_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { + cirrus_kick_out_firmware_fb(pdev); + return drm_get_pci_dev(pdev, ent, &driver); } diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h index 21bdfa8..64ea597 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.h +++ b/drivers/gpu/drm/cirrus/cirrus_drv.h @@ -145,7 +145,7 @@ struct cirrus_device { struct ttm_bo_device bdev; atomic_t validate_sequence; } ttm; - + bool mm_inited; }; diff --git a/drivers/gpu/drm/cirrus/cirrus_ttm.c b/drivers/gpu/drm/cirrus/cirrus_ttm.c index 2ebcd11..50e170f 100644 --- a/drivers/gpu/drm/cirrus/cirrus_ttm.c +++ b/drivers/gpu/drm/cirrus/cirrus_ttm.c @@ -275,12 +275,17 @@ int cirrus_mm_init(struct cirrus_device *cirrus) pci_resource_len(dev->pdev, 0), DRM_MTRR_WC); + cirrus->mm_inited = true; return 0; } void cirrus_mm_fini(struct cirrus_device *cirrus) { struct drm_device *dev = cirrus->dev; + + if (!cirrus->mm_inited) + return; + ttm_bo_device_release(&cirrus->ttm.bdev); cirrus_ttm_global_release(cirrus); diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index c3b5139..eb92fe2 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -30,7 +30,7 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/i2c.h> -#include <linux/export.h> +#include <linux/module.h> #include "drmP.h" #include "drm_edid.h" #include "drm_edid_modes.h" @@ -149,6 +149,10 @@ int drm_edid_header_is_valid(const u8 *raw_edid) } EXPORT_SYMBOL(drm_edid_header_is_valid); +static int edid_fixup __read_mostly = 6; +module_param_named(edid_fixup, edid_fixup, int, 0400); +MODULE_PARM_DESC(edid_fixup, + "Minimum number of valid EDID header bytes (0-8, default 6)"); /* * Sanity check the EDID block (base or extension). Return 0 if the block @@ -160,10 +164,13 @@ bool drm_edid_block_valid(u8 *raw_edid, int block) u8 csum = 0; struct edid *edid = (struct edid *)raw_edid; + if (edid_fixup > 8 || edid_fixup < 0) + edid_fixup = 6; + if (block == 0) { int score = drm_edid_header_is_valid(raw_edid); if (score == 8) ; - else if (score >= 6) { + else if (score >= edid_fixup) { DRM_DEBUG("Fixing EDID header, your hardware may be failing\n"); memcpy(raw_edid, edid_header, sizeof(edid_header)); } else { diff --git a/drivers/gpu/drm/i810/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c index f920fb5..fa94391 100644 --- a/drivers/gpu/drm/i810/i810_dma.c +++ b/drivers/gpu/drm/i810/i810_dma.c @@ -130,11 +130,10 @@ static int i810_map_buffer(struct drm_buf *buf, struct drm_file *file_priv) return -EINVAL; /* This is all entirely broken */ - down_write(¤t->mm->mmap_sem); old_fops = file_priv->filp->f_op; file_priv->filp->f_op = &i810_buffer_fops; dev_priv->mmap_buffer = buf; - buf_priv->virtual = (void *)do_mmap(file_priv->filp, 0, buf->total, + buf_priv->virtual = (void *)vm_mmap(file_priv->filp, 0, buf->total, PROT_READ | PROT_WRITE, MAP_SHARED, buf->bus_address); dev_priv->mmap_buffer = NULL; @@ -145,7 +144,6 @@ static int i810_map_buffer(struct drm_buf *buf, struct drm_file *file_priv) retcode = PTR_ERR(buf_priv->virtual); buf_priv->virtual = NULL; } - up_write(¤t->mm->mmap_sem); return retcode; } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 377c21f..c9cfc67 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -942,6 +942,9 @@ struct drm_i915_gem_object { /* prime dma-buf support */ struct sg_table *sg_table; + void *dma_buf_vmapping; + int vmapping_count; + /** * Used for performing relocations during execbuffer insertion. */ diff --git a/drivers/gpu/drm/i915/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/i915_gem_dmabuf.c index 8e26917..aa308e1 100644 --- a/drivers/gpu/drm/i915/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c @@ -74,6 +74,59 @@ static void i915_gem_dmabuf_release(struct dma_buf *dma_buf) } } +static void *i915_gem_dmabuf_vmap(struct dma_buf *dma_buf) +{ + struct drm_i915_gem_object *obj = dma_buf->priv; + struct drm_device *dev = obj->base.dev; + int ret; + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ERR_PTR(ret); + + if (obj->dma_buf_vmapping) { + obj->vmapping_count++; + goto out_unlock; + } + + if (!obj->pages) { + ret = i915_gem_object_get_pages_gtt(obj, __GFP_NORETRY | __GFP_NOWARN); + if (ret) { + mutex_unlock(&dev->struct_mutex); + return ERR_PTR(ret); + } + } + + obj->dma_buf_vmapping = vmap(obj->pages, obj->base.size / PAGE_SIZE, 0, PAGE_KERNEL); + if (!obj->dma_buf_vmapping) { + DRM_ERROR("failed to vmap object\n"); + goto out_unlock; + } + + obj->vmapping_count = 1; +out_unlock: + mutex_unlock(&dev->struct_mutex); + return obj->dma_buf_vmapping; +} + +static void i915_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr) +{ + struct drm_i915_gem_object *obj = dma_buf->priv; + struct drm_device *dev = obj->base.dev; + int ret; + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return; + + --obj->vmapping_count; + if (obj->vmapping_count == 0) { + vunmap(obj->dma_buf_vmapping); + obj->dma_buf_vmapping = NULL; + } + mutex_unlock(&dev->struct_mutex); +} + static void *i915_gem_dmabuf_kmap_atomic(struct dma_buf *dma_buf, unsigned long page_num) { return NULL; @@ -93,6 +146,11 @@ static void i915_gem_dmabuf_kunmap(struct dma_buf *dma_buf, unsigned long page_n } +static int i915_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma) +{ + return -EINVAL; +} + static const struct dma_buf_ops i915_dmabuf_ops = { .map_dma_buf = i915_gem_map_dma_buf, .unmap_dma_buf = i915_gem_unmap_dma_buf, @@ -101,6 +159,9 @@ static const struct dma_buf_ops i915_dmabuf_ops = { .kmap_atomic = i915_gem_dmabuf_kmap_atomic, .kunmap = i915_gem_dmabuf_kunmap, .kunmap_atomic = i915_gem_dmabuf_kunmap_atomic, + .mmap = i915_gem_dmabuf_mmap, + .vmap = i915_gem_dmabuf_vmap, + .vunmap = i915_gem_dmabuf_vunmap, }; struct dma_buf *i915_gem_prime_export(struct drm_device *dev, diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index 3c8e04f..93e832d 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -41,9 +41,28 @@ static DEFINE_PCI_DEVICE_TABLE(pciidlist) = { MODULE_DEVICE_TABLE(pci, pciidlist); +static void mgag200_kick_out_firmware_fb(struct pci_dev *pdev) +{ + struct apertures_struct *ap; + bool primary = false; + + ap = alloc_apertures(1); + ap->ranges[0].base = pci_resource_start(pdev, 0); + ap->ranges[0].size = pci_resource_len(pdev, 0); + +#ifdef CONFIG_X86 + primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; +#endif + remove_conflicting_framebuffers(ap, "mgag200drmfb", primary); + kfree(ap); +} + + static int __devinit mga_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { + mgag200_kick_out_firmware_fb(pdev); + return drm_get_pci_dev(pdev, ent, &driver); } diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 634d222..8613cb2 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -123,6 +123,9 @@ struct nouveau_bo { struct drm_gem_object *gem; int pin_refcnt; + + struct ttm_bo_kmap_obj dma_buf_vmap; + int vmapping_count; }; #define nouveau_bo_tile_layout(nvbo) \ diff --git a/drivers/gpu/drm/nouveau/nouveau_prime.c b/drivers/gpu/drm/nouveau/nouveau_prime.c index c58aab7..a89240e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_prime.c +++ b/drivers/gpu/drm/nouveau/nouveau_prime.c @@ -61,6 +61,48 @@ static void nouveau_gem_kunmap(struct dma_buf *dma_buf, unsigned long page_num, } +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; + 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); + 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) +{ + struct nouveau_bo *nvbo = dma_buf->priv; + struct drm_device *dev = nvbo->gem->dev; + + 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); +} + 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, @@ -69,6 +111,9 @@ static const struct dma_buf_ops nouveau_dmabuf_ops = { .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 diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 58991af..01550d0 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -1029,6 +1029,11 @@ int evergreen_pcie_gart_enable(struct radeon_device *rdev) WREG32(MC_VM_MD_L1_TLB0_CNTL, tmp); WREG32(MC_VM_MD_L1_TLB1_CNTL, tmp); WREG32(MC_VM_MD_L1_TLB2_CNTL, tmp); + if ((rdev->family == CHIP_JUNIPER) || + (rdev->family == CHIP_CYPRESS) || + (rdev->family == CHIP_HEMLOCK) || + (rdev->family == CHIP_BARTS)) + WREG32(MC_VM_MD_L1_TLB3_CNTL, tmp); } WREG32(MC_VM_MB_L1_TLB0_CNTL, tmp); WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp); @@ -1553,163 +1558,10 @@ int evergreen_cp_resume(struct radeon_device *rdev) /* * Core functions */ -static u32 evergreen_get_tile_pipe_to_backend_map(struct radeon_device *rdev, - u32 num_tile_pipes, - u32 num_backends, - u32 backend_disable_mask) -{ - u32 backend_map = 0; - u32 enabled_backends_mask = 0; - u32 enabled_backends_count = 0; - u32 cur_pipe; - u32 swizzle_pipe[EVERGREEN_MAX_PIPES]; - u32 cur_backend = 0; - u32 i; - bool force_no_swizzle; - - if (num_tile_pipes > EVERGREEN_MAX_PIPES) - num_tile_pipes = EVERGREEN_MAX_PIPES; - if (num_tile_pipes < 1) - num_tile_pipes = 1; - if (num_backends > EVERGREEN_MAX_BACKENDS) - num_backends = EVERGREEN_MAX_BACKENDS; - if (num_backends < 1) - num_backends = 1; - - for (i = 0; i < EVERGREEN_MAX_BACKENDS; ++i) { - if (((backend_disable_mask >> i) & 1) == 0) { - enabled_backends_mask |= (1 << i); - ++enabled_backends_count; - } - if (enabled_backends_count == num_backends) - break; - } - - if (enabled_backends_count == 0) { - enabled_backends_mask = 1; - enabled_backends_count = 1; - } - - if (enabled_backends_count != num_backends) - num_backends = enabled_backends_count; - - memset((uint8_t *)&swizzle_pipe[0], 0, sizeof(u32) * EVERGREEN_MAX_PIPES); - switch (rdev->family) { - case CHIP_CEDAR: - case CHIP_REDWOOD: - case CHIP_PALM: - case CHIP_SUMO: - case CHIP_SUMO2: - case CHIP_TURKS: - case CHIP_CAICOS: - force_no_swizzle = false; - break; - case CHIP_CYPRESS: - case CHIP_HEMLOCK: - case CHIP_JUNIPER: - case CHIP_BARTS: - default: - force_no_swizzle = true; - break; - } - if (force_no_swizzle) { - bool last_backend_enabled = false; - - force_no_swizzle = false; - for (i = 0; i < EVERGREEN_MAX_BACKENDS; ++i) { - if (((enabled_backends_mask >> i) & 1) == 1) { - if (last_backend_enabled) - force_no_swizzle = true; - last_backend_enabled = true; - } else - last_backend_enabled = false; - } - } - - switch (num_tile_pipes) { - case 1: - case 3: - case 5: - case 7: - DRM_ERROR("odd number of pipes!\n"); - break; - case 2: - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - break; - case 4: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 1; - swizzle_pipe[3] = 3; - } - break; - case 6: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - swizzle_pipe[4] = 4; - swizzle_pipe[5] = 5; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 1; - swizzle_pipe[4] = 3; - swizzle_pipe[5] = 5; - } - break; - case 8: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - swizzle_pipe[4] = 4; - swizzle_pipe[5] = 5; - swizzle_pipe[6] = 6; - swizzle_pipe[7] = 7; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 6; - swizzle_pipe[4] = 1; - swizzle_pipe[5] = 3; - swizzle_pipe[6] = 5; - swizzle_pipe[7] = 7; - } - break; - } - - for (cur_pipe = 0; cur_pipe < num_tile_pipes; ++cur_pipe) { - while (((1 << cur_backend) & enabled_backends_mask) == 0) - cur_backend = (cur_backend + 1) % EVERGREEN_MAX_BACKENDS; - - backend_map |= (((cur_backend & 0xf) << (swizzle_pipe[cur_pipe] * 4))); - - cur_backend = (cur_backend + 1) % EVERGREEN_MAX_BACKENDS; - } - - return backend_map; -} - static void evergreen_gpu_init(struct radeon_device *rdev) { - u32 cc_rb_backend_disable = 0; - u32 cc_gc_shader_pipe_config; - u32 gb_addr_config = 0; + u32 gb_addr_config; u32 mc_shared_chmap, mc_arb_ramcfg; - u32 gb_backend_map; - u32 grbm_gfx_index; u32 sx_debug_1; u32 smx_dc_ctl0; u32 sq_config; @@ -1724,6 +1576,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) u32 sq_stack_resource_mgmt_3; u32 vgt_cache_invalidation; u32 hdp_host_path_cntl, tmp; + u32 disabled_rb_mask; int i, j, num_shader_engines, ps_thread_count; switch (rdev->family) { @@ -1748,6 +1601,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sc_prim_fifo_size = 0x100; rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = CYPRESS_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_JUNIPER: rdev->config.evergreen.num_ses = 1; @@ -1769,6 +1623,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sc_prim_fifo_size = 0x100; rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = JUNIPER_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_REDWOOD: rdev->config.evergreen.num_ses = 1; @@ -1790,6 +1645,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sc_prim_fifo_size = 0x100; rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = REDWOOD_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_CEDAR: default: @@ -1812,6 +1668,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sc_prim_fifo_size = 0x40; rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = CEDAR_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_PALM: rdev->config.evergreen.num_ses = 1; @@ -1833,6 +1690,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sc_prim_fifo_size = 0x40; rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = CEDAR_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_SUMO: rdev->config.evergreen.num_ses = 1; @@ -1860,6 +1718,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sc_prim_fifo_size = 0x40; rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = REDWOOD_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_SUMO2: rdev->config.evergreen.num_ses = 1; @@ -1881,6 +1740,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sc_prim_fifo_size = 0x40; rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = REDWOOD_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_BARTS: rdev->config.evergreen.num_ses = 2; @@ -1902,6 +1762,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sc_prim_fifo_size = 0x100; rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = BARTS_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_TURKS: rdev->config.evergreen.num_ses = 1; @@ -1923,6 +1784,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sc_prim_fifo_size = 0x100; rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = TURKS_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_CAICOS: rdev->config.evergreen.num_ses = 1; @@ -1944,6 +1806,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sc_prim_fifo_size = 0x40; rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = CAICOS_GB_ADDR_CONFIG_GOLDEN; break; } @@ -1960,20 +1823,6 @@ static void evergreen_gpu_init(struct radeon_device *rdev) evergreen_fix_pci_max_read_req_size(rdev); - cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG) & ~2; - - cc_gc_shader_pipe_config |= - INACTIVE_QD_PIPES((EVERGREEN_MAX_PIPES_MASK << rdev->config.evergreen.max_pipes) - & EVERGREEN_MAX_PIPES_MASK); - cc_gc_shader_pipe_config |= - INACTIVE_SIMDS((EVERGREEN_MAX_SIMDS_MASK << rdev->config.evergreen.max_simds) - & EVERGREEN_MAX_SIMDS_MASK); - - cc_rb_backend_disable = - BACKEND_DISABLE((EVERGREEN_MAX_BACKENDS_MASK << rdev->config.evergreen.max_backends) - & EVERGREEN_MAX_BACKENDS_MASK); - - mc_shared_chmap = RREG32(MC_SHARED_CHMAP); if ((rdev->family == CHIP_PALM) || (rdev->family == CHIP_SUMO) || @@ -1982,134 +1831,6 @@ static void evergreen_gpu_init(struct radeon_device *rdev) else mc_arb_ramcfg = RREG32(MC_ARB_RAMCFG); - switch (rdev->config.evergreen.max_tile_pipes) { - case 1: - default: - gb_addr_config |= NUM_PIPES(0); - break; - case 2: - gb_addr_config |= NUM_PIPES(1); - break; - case 4: - gb_addr_config |= NUM_PIPES(2); - break; - case 8: - gb_addr_config |= NUM_PIPES(3); - break; - } - - gb_addr_config |= PIPE_INTERLEAVE_SIZE((mc_arb_ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT); - gb_addr_config |= BANK_INTERLEAVE_SIZE(0); - gb_addr_config |= NUM_SHADER_ENGINES(rdev->config.evergreen.num_ses - 1); - gb_addr_config |= SHADER_ENGINE_TILE_SIZE(1); - gb_addr_config |= NUM_GPUS(0); /* Hemlock? */ - gb_addr_config |= MULTI_GPU_TILE_SIZE(2); - - if (((mc_arb_ramcfg & NOOFCOLS_MASK) >> NOOFCOLS_SHIFT) > 2) - gb_addr_config |= ROW_SIZE(2); - else - gb_addr_config |= ROW_SIZE((mc_arb_ramcfg & NOOFCOLS_MASK) >> NOOFCOLS_SHIFT); - - if (rdev->ddev->pdev->device == 0x689e) { - u32 efuse_straps_4; - u32 efuse_straps_3; - u8 efuse_box_bit_131_124; - - WREG32(RCU_IND_INDEX, 0x204); - efuse_straps_4 = RREG32(RCU_IND_DATA); - WREG32(RCU_IND_INDEX, 0x203); - efuse_straps_3 = RREG32(RCU_IND_DATA); - efuse_box_bit_131_124 = (u8)(((efuse_straps_4 & 0xf) << 4) | ((efuse_straps_3 & 0xf0000000) >> 28)); - - switch(efuse_box_bit_131_124) { - case 0x00: - gb_backend_map = 0x76543210; - break; - case 0x55: - gb_backend_map = 0x77553311; - break; - case 0x56: - gb_backend_map = 0x77553300; - break; - case 0x59: - gb_backend_map = 0x77552211; - break; - case 0x66: - gb_backend_map = 0x77443300; - break; - case 0x99: - gb_backend_map = 0x66552211; - break; - case 0x5a: - gb_backend_map = 0x77552200; - break; - case 0xaa: - gb_backend_map = 0x66442200; - break; - case 0x95: - gb_backend_map = 0x66553311; - break; - default: - DRM_ERROR("bad backend map, using default\n"); - gb_backend_map = - evergreen_get_tile_pipe_to_backend_map(rdev, - rdev->config.evergreen.max_tile_pipes, - rdev->config.evergreen.max_backends, - ((EVERGREEN_MAX_BACKENDS_MASK << - rdev->config.evergreen.max_backends) & - EVERGREEN_MAX_BACKENDS_MASK)); - break; - } - } else if (rdev->ddev->pdev->device == 0x68b9) { - u32 efuse_straps_3; - u8 efuse_box_bit_127_124; - - WREG32(RCU_IND_INDEX, 0x203); - efuse_straps_3 = RREG32(RCU_IND_DATA); - efuse_box_bit_127_124 = (u8)((efuse_straps_3 & 0xF0000000) >> 28); - - switch(efuse_box_bit_127_124) { - case 0x0: - gb_backend_map = 0x00003210; - break; - case 0x5: - case 0x6: - case 0x9: - case 0xa: - gb_backend_map = 0x00003311; - break; - default: - DRM_ERROR("bad backend map, using default\n"); - gb_backend_map = - evergreen_get_tile_pipe_to_backend_map(rdev, - rdev->config.evergreen.max_tile_pipes, - rdev->config.evergreen.max_backends, - ((EVERGREEN_MAX_BACKENDS_MASK << - rdev->config.evergreen.max_backends) & - EVERGREEN_MAX_BACKENDS_MASK)); - break; - } - } else { - switch (rdev->family) { - case CHIP_CYPRESS: - case CHIP_HEMLOCK: - case CHIP_BARTS: - gb_backend_map = 0x66442200; - break; - case CHIP_JUNIPER: - gb_backend_map = 0x00002200; - break; - default: - gb_backend_map = - evergreen_get_tile_pipe_to_backend_map(rdev, - rdev->config.evergreen.max_tile_pipes, - rdev->config.evergreen.max_backends, - ((EVERGREEN_MAX_BACKENDS_MASK << - rdev->config.evergreen.max_backends) & - EVERGREEN_MAX_BACKENDS_MASK)); - } - } - /* setup tiling info dword. gb_addr_config is not adequate since it does * not have bank info, so create a custom tiling dword. * bits 3:0 num_pipes @@ -2136,45 +1857,54 @@ static void evergreen_gpu_init(struct radeon_device *rdev) /* num banks is 8 on all fusion asics. 0 = 4, 1 = 8, 2 = 16 */ if (rdev->flags & RADEON_IS_IGP) rdev->config.evergreen.tile_config |= 1 << 4; - else - rdev->config.evergreen.tile_config |= - ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) << 4; - rdev->config.evergreen.tile_config |= - ((mc_arb_ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT) << 8; + else { + if ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) + rdev->config.evergreen.tile_config |= 1 << 4; + else + rdev->config.evergreen.tile_config |= 0 << 4; + } + rdev->config.evergreen.tile_config |= 0 << 8; rdev->config.evergreen.tile_config |= ((gb_addr_config & 0x30000000) >> 28) << 12; - rdev->config.evergreen.backend_map = gb_backend_map; - WREG32(GB_BACKEND_MAP, gb_backend_map); - WREG32(GB_ADDR_CONFIG, gb_addr_config); - WREG32(DMIF_ADDR_CONFIG, gb_addr_config); - WREG32(HDP_ADDR_CONFIG, gb_addr_config); - - num_shader_engines = ((RREG32(GB_ADDR_CONFIG) & NUM_SHADER_ENGINES(3)) >> 12) + 1; - grbm_gfx_index = INSTANCE_BROADCAST_WRITES; + num_shader_engines = (gb_addr_config & NUM_SHADER_ENGINES(3) >> 12) + 1; - for (i = 0; i < rdev->config.evergreen.num_ses; i++) { - u32 rb = cc_rb_backend_disable | (0xf0 << 16); - u32 sp = cc_gc_shader_pipe_config; - u32 gfx = grbm_gfx_index | SE_INDEX(i); + if ((rdev->family >= CHIP_CEDAR) && (rdev->family <= CHIP_HEMLOCK)) { + u32 efuse_straps_4; + u32 efuse_straps_3; - if (i == num_shader_engines) { - rb |= BACKEND_DISABLE(EVERGREEN_MAX_BACKENDS_MASK); - sp |= INACTIVE_SIMDS(EVERGREEN_MAX_SIMDS_MASK); + WREG32(RCU_IND_INDEX, 0x204); + efuse_straps_4 = RREG32(RCU_IND_DATA); + WREG32(RCU_IND_INDEX, 0x203); + efuse_straps_3 = RREG32(RCU_IND_DATA); + tmp = (((efuse_straps_4 & 0xf) << 4) | + ((efuse_straps_3 & 0xf0000000) >> 28)); + } else { + tmp = 0; + for (i = (rdev->config.evergreen.num_ses - 1); i >= 0; i--) { + u32 rb_disable_bitmap; + + WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i)); + WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i)); + rb_disable_bitmap = (RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000) >> 16; + tmp <<= 4; + tmp |= rb_disable_bitmap; } + } + /* enabled rb are just the one not disabled :) */ + disabled_rb_mask = tmp; - WREG32(GRBM_GFX_INDEX, gfx); - WREG32(RLC_GFX_INDEX, gfx); + WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES); + WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES); - WREG32(CC_RB_BACKEND_DISABLE, rb); - WREG32(CC_SYS_RB_BACKEND_DISABLE, rb); - WREG32(GC_USER_RB_BACKEND_DISABLE, rb); - WREG32(CC_GC_SHADER_PIPE_CONFIG, sp); - } + WREG32(GB_ADDR_CONFIG, gb_addr_config); + WREG32(DMIF_ADDR_CONFIG, gb_addr_config); + WREG32(HDP_ADDR_CONFIG, gb_addr_config); - grbm_gfx_index |= SE_BROADCAST_WRITES; - WREG32(GRBM_GFX_INDEX, grbm_gfx_index); - WREG32(RLC_GFX_INDEX, grbm_gfx_index); + tmp = gb_addr_config & NUM_PIPES_MASK; + tmp = r6xx_remap_render_backend(rdev, tmp, rdev->config.evergreen.max_backends, + EVERGREEN_MAX_BACKENDS, disabled_rb_mask); + WREG32(GB_BACKEND_MAP, tmp); WREG32(CGTS_SYS_TCC_DISABLE, 0); WREG32(CGTS_TCC_DISABLE, 0); diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h index 79130bf..2773039 100644 --- a/drivers/gpu/drm/radeon/evergreend.h +++ b/drivers/gpu/drm/radeon/evergreend.h @@ -37,6 +37,15 @@ #define EVERGREEN_MAX_PIPES_MASK 0xFF #define EVERGREEN_MAX_LDS_NUM 0xFFFF +#define CYPRESS_GB_ADDR_CONFIG_GOLDEN 0x02011003 +#define BARTS_GB_ADDR_CONFIG_GOLDEN 0x02011003 +#define CAYMAN_GB_ADDR_CONFIG_GOLDEN 0x02011003 +#define JUNIPER_GB_ADDR_CONFIG_GOLDEN 0x02010002 +#define REDWOOD_GB_ADDR_CONFIG_GOLDEN 0x02010002 +#define TURKS_GB_ADDR_CONFIG_GOLDEN 0x02010002 +#define CEDAR_GB_ADDR_CONFIG_GOLDEN 0x02010001 +#define CAICOS_GB_ADDR_CONFIG_GOLDEN 0x02010001 + /* Registers */ #define RCU_IND_INDEX 0x100 @@ -54,6 +63,7 @@ #define BACKEND_DISABLE(x) ((x) << 16) #define GB_ADDR_CONFIG 0x98F8 #define NUM_PIPES(x) ((x) << 0) +#define NUM_PIPES_MASK 0x0000000f #define PIPE_INTERLEAVE_SIZE(x) ((x) << 4) #define BANK_INTERLEAVE_SIZE(x) ((x) << 8) #define NUM_SHADER_ENGINES(x) ((x) << 12) @@ -452,6 +462,7 @@ #define MC_VM_MD_L1_TLB0_CNTL 0x2654 #define MC_VM_MD_L1_TLB1_CNTL 0x2658 #define MC_VM_MD_L1_TLB2_CNTL 0x265C +#define MC_VM_MD_L1_TLB3_CNTL 0x2698 #define FUS_MC_VM_MD_L1_TLB0_CNTL 0x265C #define FUS_MC_VM_MD_L1_TLB1_CNTL 0x2660 diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index ce4e7cc..3df4efa 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -417,215 +417,17 @@ out: /* * Core functions */ -static u32 cayman_get_tile_pipe_to_backend_map(struct radeon_device *rdev, - u32 num_tile_pipes, - u32 num_backends_per_asic, - u32 *backend_disable_mask_per_asic, - u32 num_shader_engines) -{ - u32 backend_map = 0; - u32 enabled_backends_mask = 0; - u32 enabled_backends_count = 0; - u32 num_backends_per_se; - u32 cur_pipe; - u32 swizzle_pipe[CAYMAN_MAX_PIPES]; - u32 cur_backend = 0; - u32 i; - bool force_no_swizzle; - - /* force legal values */ - if (num_tile_pipes < 1) - num_tile_pipes = 1; - if (num_tile_pipes > rdev->config.cayman.max_tile_pipes) - num_tile_pipes = rdev->config.cayman.max_tile_pipes; - if (num_shader_engines < 1) - num_shader_engines = 1; - if (num_shader_engines > rdev->config.cayman.max_shader_engines) - num_shader_engines = rdev->config.cayman.max_shader_engines; - if (num_backends_per_asic < num_shader_engines) - num_backends_per_asic = num_shader_engines; - if (num_backends_per_asic > (rdev->config.cayman.max_backends_per_se * num_shader_engines)) - num_backends_per_asic = rdev->config.cayman.max_backends_per_se * num_shader_engines; - - /* make sure we have the same number of backends per se */ - num_backends_per_asic = ALIGN(num_backends_per_asic, num_shader_engines); - /* set up the number of backends per se */ - num_backends_per_se = num_backends_per_asic / num_shader_engines; - if (num_backends_per_se > rdev->config.cayman.max_backends_per_se) { - num_backends_per_se = rdev->config.cayman.max_backends_per_se; - num_backends_per_asic = num_backends_per_se * num_shader_engines; - } - - /* create enable mask and count for enabled backends */ - for (i = 0; i < CAYMAN_MAX_BACKENDS; ++i) { - if (((*backend_disable_mask_per_asic >> i) & 1) == 0) { - enabled_backends_mask |= (1 << i); - ++enabled_backends_count; - } - if (enabled_backends_count == num_backends_per_asic) - break; - } - - /* force the backends mask to match the current number of backends */ - if (enabled_backends_count != num_backends_per_asic) { - u32 this_backend_enabled; - u32 shader_engine; - u32 backend_per_se; - - enabled_backends_mask = 0; - enabled_backends_count = 0; - *backend_disable_mask_per_asic = CAYMAN_MAX_BACKENDS_MASK; - for (i = 0; i < CAYMAN_MAX_BACKENDS; ++i) { - /* calc the current se */ - shader_engine = i / rdev->config.cayman.max_backends_per_se; - /* calc the backend per se */ - backend_per_se = i % rdev->config.cayman.max_backends_per_se; - /* default to not enabled */ - this_backend_enabled = 0; - if ((shader_engine < num_shader_engines) && - (backend_per_se < num_backends_per_se)) - this_backend_enabled = 1; - if (this_backend_enabled) { - enabled_backends_mask |= (1 << i); - *backend_disable_mask_per_asic &= ~(1 << i); - ++enabled_backends_count; - } - } - } - - - memset((uint8_t *)&swizzle_pipe[0], 0, sizeof(u32) * CAYMAN_MAX_PIPES); - switch (rdev->family) { - case CHIP_CAYMAN: - case CHIP_ARUBA: - force_no_swizzle = true; - break; - default: - force_no_swizzle = false; - break; - } - if (force_no_swizzle) { - bool last_backend_enabled = false; - - force_no_swizzle = false; - for (i = 0; i < CAYMAN_MAX_BACKENDS; ++i) { - if (((enabled_backends_mask >> i) & 1) == 1) { - if (last_backend_enabled) - force_no_swizzle = true; - last_backend_enabled = true; - } else - last_backend_enabled = false; - } - } - - switch (num_tile_pipes) { - case 1: - case 3: - case 5: - case 7: - DRM_ERROR("odd number of pipes!\n"); - break; - case 2: - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - break; - case 4: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 1; - swizzle_pipe[3] = 3; - } - break; - case 6: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - swizzle_pipe[4] = 4; - swizzle_pipe[5] = 5; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 1; - swizzle_pipe[4] = 3; - swizzle_pipe[5] = 5; - } - break; - case 8: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - swizzle_pipe[4] = 4; - swizzle_pipe[5] = 5; - swizzle_pipe[6] = 6; - swizzle_pipe[7] = 7; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 6; - swizzle_pipe[4] = 1; - swizzle_pipe[5] = 3; - swizzle_pipe[6] = 5; - swizzle_pipe[7] = 7; - } - break; - } - - for (cur_pipe = 0; cur_pipe < num_tile_pipes; ++cur_pipe) { - while (((1 << cur_backend) & enabled_backends_mask) == 0) - cur_backend = (cur_backend + 1) % CAYMAN_MAX_BACKENDS; - - backend_map |= (((cur_backend & 0xf) << (swizzle_pipe[cur_pipe] * 4))); - - cur_backend = (cur_backend + 1) % CAYMAN_MAX_BACKENDS; - } - - return backend_map; -} - -static u32 cayman_get_disable_mask_per_asic(struct radeon_device *rdev, - u32 disable_mask_per_se, - u32 max_disable_mask_per_se, - u32 num_shader_engines) -{ - u32 disable_field_width_per_se = r600_count_pipe_bits(disable_mask_per_se); - u32 disable_mask_per_asic = disable_mask_per_se & max_disable_mask_per_se; - - if (num_shader_engines == 1) - return disable_mask_per_asic; - else if (num_shader_engines == 2) - return disable_mask_per_asic | (disable_mask_per_asic << disable_field_width_per_se); - else - return 0xffffffff; -} - static void cayman_gpu_init(struct radeon_device *rdev) { - u32 cc_rb_backend_disable = 0; - u32 cc_gc_shader_pipe_config; u32 gb_addr_config = 0; u32 mc_shared_chmap, mc_arb_ramcfg; - u32 gb_backend_map; u32 cgts_tcc_disable; u32 sx_debug_1; u32 smx_dc_ctl0; - u32 gc_user_shader_pipe_config; - u32 gc_user_rb_backend_disable; - u32 cgts_user_tcc_disable; u32 cgts_sm_ctrl_reg; u32 hdp_host_path_cntl; u32 tmp; + u32 disabled_rb_mask; int i, j; switch (rdev->family) { @@ -650,6 +452,7 @@ static void cayman_gpu_init(struct radeon_device *rdev) rdev->config.cayman.sc_prim_fifo_size = 0x100; rdev->config.cayman.sc_hiz_tile_fifo_size = 0x30; rdev->config.cayman.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = CAYMAN_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_ARUBA: default: @@ -687,6 +490,7 @@ static void cayman_gpu_init(struct radeon_device *rdev) rdev->config.cayman.sc_prim_fifo_size = 0x40; rdev->config.cayman.sc_hiz_tile_fifo_size = 0x30; rdev->config.cayman.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = ARUBA_GB_ADDR_CONFIG_GOLDEN; break; } @@ -706,39 +510,6 @@ static void cayman_gpu_init(struct radeon_device *rdev) mc_shared_chmap = RREG32(MC_SHARED_CHMAP); mc_arb_ramcfg = RREG32(MC_ARB_RAMCFG); - cc_rb_backend_disable = RREG32(CC_RB_BACKEND_DISABLE); - cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG); - cgts_tcc_disable = 0xffff0000; - for (i = 0; i < rdev->config.cayman.max_texture_channel_caches; i++) - cgts_tcc_disable &= ~(1 << (16 + i)); - gc_user_rb_backend_disable = RREG32(GC_USER_RB_BACKEND_DISABLE); - gc_user_shader_pipe_config = RREG32(GC_USER_SHADER_PIPE_CONFIG); - cgts_user_tcc_disable = RREG32(CGTS_USER_TCC_DISABLE); - - rdev->config.cayman.num_shader_engines = rdev->config.cayman.max_shader_engines; - tmp = ((~gc_user_shader_pipe_config) & INACTIVE_QD_PIPES_MASK) >> INACTIVE_QD_PIPES_SHIFT; - rdev->config.cayman.num_shader_pipes_per_simd = r600_count_pipe_bits(tmp); - rdev->config.cayman.num_tile_pipes = rdev->config.cayman.max_tile_pipes; - tmp = ((~gc_user_shader_pipe_config) & INACTIVE_SIMDS_MASK) >> INACTIVE_SIMDS_SHIFT; - rdev->config.cayman.num_simds_per_se = r600_count_pipe_bits(tmp); - tmp = ((~gc_user_rb_backend_disable) & BACKEND_DISABLE_MASK) >> BACKEND_DISABLE_SHIFT; - rdev->config.cayman.num_backends_per_se = r600_count_pipe_bits(tmp); - tmp = (gc_user_rb_backend_disable & BACKEND_DISABLE_MASK) >> BACKEND_DISABLE_SHIFT; - rdev->config.cayman.backend_disable_mask_per_asic = - cayman_get_disable_mask_per_asic(rdev, tmp, CAYMAN_MAX_BACKENDS_PER_SE_MASK, - rdev->config.cayman.num_shader_engines); - rdev->config.cayman.backend_map = - cayman_get_tile_pipe_to_backend_map(rdev, rdev->config.cayman.num_tile_pipes, - rdev->config.cayman.num_backends_per_se * - rdev->config.cayman.num_shader_engines, - &rdev->config.cayman.backend_disable_mask_per_asic, - rdev->config.cayman.num_shader_engines); - tmp = ((~cgts_user_tcc_disable) & TCC_DISABLE_MASK) >> TCC_DISABLE_SHIFT; - rdev->config.cayman.num_texture_channel_caches = r600_count_pipe_bits(tmp); - tmp = (mc_arb_ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT; - rdev->config.cayman.mem_max_burst_length_bytes = (tmp + 1) * 256; - if (rdev->config.cayman.mem_max_burst_length_bytes > 512) - rdev->config.cayman.mem_max_burst_length_bytes = 512; tmp = (mc_arb_ramcfg & NOOFCOLS_MASK) >> NOOFCOLS_SHIFT; rdev->config.cayman.mem_row_size_in_kb = (4 * (1 << (8 + tmp))) / 1024; if (rdev->config.cayman.mem_row_size_in_kb > 4) @@ -748,73 +519,6 @@ static void cayman_gpu_init(struct radeon_device *rdev) rdev->config.cayman.num_gpus = 1; rdev->config.cayman.multi_gpu_tile_size = 64; - //gb_addr_config = 0x02011003 -#if 0 - gb_addr_config = RREG32(GB_ADDR_CONFIG); -#else - gb_addr_config = 0; - switch (rdev->config.cayman.num_tile_pipes) { - case 1: - default: - gb_addr_config |= NUM_PIPES(0); - break; - case 2: - gb_addr_config |= NUM_PIPES(1); - break; - case 4: - gb_addr_config |= NUM_PIPES(2); - break; - case 8: - gb_addr_config |= NUM_PIPES(3); - break; - } - - tmp = (rdev->config.cayman.mem_max_burst_length_bytes / 256) - 1; - gb_addr_config |= PIPE_INTERLEAVE_SIZE(tmp); - gb_addr_config |= NUM_SHADER_ENGINES(rdev->config.cayman.num_shader_engines - 1); - tmp = (rdev->config.cayman.shader_engine_tile_size / 16) - 1; - gb_addr_config |= SHADER_ENGINE_TILE_SIZE(tmp); - switch (rdev->config.cayman.num_gpus) { - case 1: - default: - gb_addr_config |= NUM_GPUS(0); - break; - case 2: - gb_addr_config |= NUM_GPUS(1); - break; - case 4: - gb_addr_config |= NUM_GPUS(2); - break; - } - switch (rdev->config.cayman.multi_gpu_tile_size) { - case 16: - gb_addr_config |= MULTI_GPU_TILE_SIZE(0); - break; - case 32: - default: - gb_addr_config |= MULTI_GPU_TILE_SIZE(1); - break; - case 64: - gb_addr_config |= MULTI_GPU_TILE_SIZE(2); - break; - case 128: - gb_addr_config |= MULTI_GPU_TILE_SIZE(3); - break; - } - switch (rdev->config.cayman.mem_row_size_in_kb) { - case 1: - default: - gb_addr_config |= ROW_SIZE(0); - break; - case 2: - gb_addr_config |= ROW_SIZE(1); - break; - case 4: - gb_addr_config |= ROW_SIZE(2); - break; - } -#endif - tmp = (gb_addr_config & NUM_PIPES_MASK) >> NUM_PIPES_SHIFT; rdev->config.cayman.num_tile_pipes = (1 << tmp); tmp = (gb_addr_config & PIPE_INTERLEAVE_SIZE_MASK) >> PIPE_INTERLEAVE_SIZE_SHIFT; @@ -828,17 +532,7 @@ static void cayman_gpu_init(struct radeon_device *rdev) tmp = (gb_addr_config & ROW_SIZE_MASK) >> ROW_SIZE_SHIFT; rdev->config.cayman.mem_row_size_in_kb = 1 << tmp; - //gb_backend_map = 0x76541032; -#if 0 - gb_backend_map = RREG32(GB_BACKEND_MAP); -#else - gb_backend_map = - cayman_get_tile_pipe_to_backend_map(rdev, rdev->config.cayman.num_tile_pipes, - rdev->config.cayman.num_backends_per_se * - rdev->config.cayman.num_shader_engines, - &rdev->config.cayman.backend_disable_mask_per_asic, - rdev->config.cayman.num_shader_engines); -#endif + /* setup tiling info dword. gb_addr_config is not adequate since it does * not have bank info, so create a custom tiling dword. * bits 3:0 num_pipes @@ -866,33 +560,49 @@ static void cayman_gpu_init(struct radeon_device *rdev) /* num banks is 8 on all fusion asics. 0 = 4, 1 = 8, 2 = 16 */ if (rdev->flags & RADEON_IS_IGP) rdev->config.cayman.tile_config |= 1 << 4; - else - rdev->config.cayman.tile_config |= - ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) << 4; + else { + if ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) + rdev->config.cayman.tile_config |= 1 << 4; + else + rdev->config.cayman.tile_config |= 0 << 4; + } rdev->config.cayman.tile_config |= ((gb_addr_config & PIPE_INTERLEAVE_SIZE_MASK) >> PIPE_INTERLEAVE_SIZE_SHIFT) << 8; rdev->config.cayman.tile_config |= ((gb_addr_config & ROW_SIZE_MASK) >> ROW_SIZE_SHIFT) << 12; - rdev->config.cayman.backend_map = gb_backend_map; - WREG32(GB_BACKEND_MAP, gb_backend_map); + tmp = 0; + for (i = (rdev->config.cayman.max_shader_engines - 1); i >= 0; i--) { + u32 rb_disable_bitmap; + + WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i)); + WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i)); + rb_disable_bitmap = (RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000) >> 16; + tmp <<= 4; + tmp |= rb_disable_bitmap; + } + /* enabled rb are just the one not disabled :) */ + disabled_rb_mask = tmp; + + WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES); + WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES); + WREG32(GB_ADDR_CONFIG, gb_addr_config); WREG32(DMIF_ADDR_CONFIG, gb_addr_config); WREG32(HDP_ADDR_CONFIG, gb_addr_config); - /* primary versions */ - WREG32(CC_RB_BACKEND_DISABLE, cc_rb_backend_disable); - WREG32(CC_SYS_RB_BACKEND_DISABLE, cc_rb_backend_disable); - WREG32(CC_GC_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config); + tmp = gb_addr_config & NUM_PIPES_MASK; + tmp = r6xx_remap_render_backend(rdev, tmp, + rdev->config.cayman.max_backends_per_se * + rdev->config.cayman.max_shader_engines, + CAYMAN_MAX_BACKENDS, disabled_rb_mask); + WREG32(GB_BACKEND_MAP, tmp); + cgts_tcc_disable = 0xffff0000; + for (i = 0; i < rdev->config.cayman.max_texture_channel_caches; i++) + cgts_tcc_disable &= ~(1 << (16 + i)); WREG32(CGTS_TCC_DISABLE, cgts_tcc_disable); WREG32(CGTS_SYS_TCC_DISABLE, cgts_tcc_disable); - - /* user versions */ - WREG32(GC_USER_RB_BACKEND_DISABLE, cc_rb_backend_disable); - WREG32(GC_USER_SYS_RB_BACKEND_DISABLE, cc_rb_backend_disable); - WREG32(GC_USER_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config); - WREG32(CGTS_USER_SYS_TCC_DISABLE, cgts_tcc_disable); WREG32(CGTS_USER_TCC_DISABLE, cgts_tcc_disable); diff --git a/drivers/gpu/drm/radeon/nid.h b/drivers/gpu/drm/radeon/nid.h index 2aa7046..a0b9806 100644 --- a/drivers/gpu/drm/radeon/nid.h +++ b/drivers/gpu/drm/radeon/nid.h @@ -41,6 +41,9 @@ #define CAYMAN_MAX_TCC 16 #define CAYMAN_MAX_TCC_MASK 0xFF +#define CAYMAN_GB_ADDR_CONFIG_GOLDEN 0x02011003 +#define ARUBA_GB_ADDR_CONFIG_GOLDEN 0x12010001 + #define DMIF_ADDR_CONFIG 0xBD4 #define SRBM_GFX_CNTL 0x0E44 #define RINGID(x) (((x) & 0x3) << 0) @@ -148,6 +151,8 @@ #define CGTS_SYS_TCC_DISABLE 0x3F90 #define CGTS_USER_SYS_TCC_DISABLE 0x3F94 +#define RLC_GFX_INDEX 0x3FC4 + #define CONFIG_MEMSIZE 0x5428 #define HDP_MEM_COHERENCY_FLUSH_CNTL 0x5480 @@ -212,6 +217,12 @@ #define SOFT_RESET_VGT (1 << 14) #define SOFT_RESET_IA (1 << 15) +#define GRBM_GFX_INDEX 0x802C +#define INSTANCE_INDEX(x) ((x) << 0) +#define SE_INDEX(x) ((x) << 16) +#define INSTANCE_BROADCAST_WRITES (1 << 30) +#define SE_BROADCAST_WRITES (1 << 31) + #define SCRATCH_REG0 0x8500 #define SCRATCH_REG1 0x8504 #define SCRATCH_REG2 0x8508 diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index f388a1d..45cfcea 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -1376,113 +1376,51 @@ int r600_asic_reset(struct radeon_device *rdev) return r600_gpu_soft_reset(rdev); } -static u32 r600_get_tile_pipe_to_backend_map(u32 num_tile_pipes, - u32 num_backends, - u32 backend_disable_mask) -{ - u32 backend_map = 0; - u32 enabled_backends_mask; - u32 enabled_backends_count; - u32 cur_pipe; - u32 swizzle_pipe[R6XX_MAX_PIPES]; - u32 cur_backend; - u32 i; - - if (num_tile_pipes > R6XX_MAX_PIPES) - num_tile_pipes = R6XX_MAX_PIPES; - if (num_tile_pipes < 1) - num_tile_pipes = 1; - if (num_backends > R6XX_MAX_BACKENDS) - num_backends = R6XX_MAX_BACKENDS; - if (num_backends < 1) - num_backends = 1; - - enabled_backends_mask = 0; - enabled_backends_count = 0; - for (i = 0; i < R6XX_MAX_BACKENDS; ++i) { - if (((backend_disable_mask >> i) & 1) == 0) { - enabled_backends_mask |= (1 << i); - ++enabled_backends_count; - } - if (enabled_backends_count == num_backends) - break; - } - - if (enabled_backends_count == 0) { - enabled_backends_mask = 1; - enabled_backends_count = 1; - } - - if (enabled_backends_count != num_backends) - num_backends = enabled_backends_count; - - memset((uint8_t *)&swizzle_pipe[0], 0, sizeof(u32) * R6XX_MAX_PIPES); - switch (num_tile_pipes) { - case 1: - swizzle_pipe[0] = 0; - break; - case 2: - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - break; - case 3: - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - break; - case 4: - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - break; - case 5: - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - swizzle_pipe[4] = 4; - break; - case 6: - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 5; - swizzle_pipe[4] = 1; - swizzle_pipe[5] = 3; - break; - case 7: - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 6; - swizzle_pipe[4] = 1; - swizzle_pipe[5] = 3; - swizzle_pipe[6] = 5; - break; - case 8: - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 6; - swizzle_pipe[4] = 1; - swizzle_pipe[5] = 3; - swizzle_pipe[6] = 5; - swizzle_pipe[7] = 7; - break; +u32 r6xx_remap_render_backend(struct radeon_device *rdev, + u32 tiling_pipe_num, + u32 max_rb_num, + u32 total_max_rb_num, + u32 disabled_rb_mask) +{ + u32 rendering_pipe_num, rb_num_width, req_rb_num; + u32 pipe_rb_ratio, pipe_rb_remain; + u32 data = 0, mask = 1 << (max_rb_num - 1); + unsigned i, j; + + /* mask out the RBs that don't exist on that asic */ + disabled_rb_mask |= (0xff << max_rb_num) & 0xff; + + rendering_pipe_num = 1 << tiling_pipe_num; + req_rb_num = total_max_rb_num - r600_count_pipe_bits(disabled_rb_mask); + BUG_ON(rendering_pipe_num < req_rb_num); + + pipe_rb_ratio = rendering_pipe_num / req_rb_num; + pipe_rb_remain = rendering_pipe_num - pipe_rb_ratio * req_rb_num; + + if (rdev->family <= CHIP_RV740) { + /* r6xx/r7xx */ + rb_num_width = 2; + } else { + /* eg+ */ + rb_num_width = 4; } - cur_backend = 0; - for (cur_pipe = 0; cur_pipe < num_tile_pipes; ++cur_pipe) { - while (((1 << cur_backend) & enabled_backends_mask) == 0) - cur_backend = (cur_backend + 1) % R6XX_MAX_BACKENDS; - - backend_map |= (u32)(((cur_backend & 3) << (swizzle_pipe[cur_pipe] * 2))); - - cur_backend = (cur_backend + 1) % R6XX_MAX_BACKENDS; + for (i = 0; i < max_rb_num; i++) { + if (!(mask & disabled_rb_mask)) { + for (j = 0; j < pipe_rb_ratio; j++) { + data <<= rb_num_width; + data |= max_rb_num - i - 1; + } + if (pipe_rb_remain) { + data <<= rb_num_width; + data |= max_rb_num - i - 1; + pipe_rb_remain--; + } + } + mask >>= 1; } - return backend_map; + return data; } int r600_count_pipe_bits(uint32_t val) @@ -1500,7 +1438,6 @@ void r600_gpu_init(struct radeon_device *rdev) { u32 tiling_config; u32 ramcfg; - u32 backend_map; u32 cc_rb_backend_disable; u32 cc_gc_shader_pipe_config; u32 tmp; @@ -1511,8 +1448,9 @@ void r600_gpu_init(struct radeon_device *rdev) u32 sq_thread_resource_mgmt = 0; u32 sq_stack_resource_mgmt_1 = 0; u32 sq_stack_resource_mgmt_2 = 0; + u32 disabled_rb_mask; - /* FIXME: implement */ + rdev->config.r600.tiling_group_size = 256; switch (rdev->family) { case CHIP_R600: rdev->config.r600.max_pipes = 4; @@ -1616,10 +1554,7 @@ void r600_gpu_init(struct radeon_device *rdev) rdev->config.r600.tiling_nbanks = 4 << ((ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT); tiling_config |= BANK_TILING((ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT); tiling_config |= GROUP_SIZE((ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT); - if ((ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT) - rdev->config.r600.tiling_group_size = 512; - else - rdev->config.r600.tiling_group_size = 256; + tmp = (ramcfg & NOOFROWS_MASK) >> NOOFROWS_SHIFT; if (tmp > 3) { tiling_config |= ROW_TILING(3); @@ -1631,32 +1566,36 @@ void r600_gpu_init(struct radeon_device *rdev) tiling_config |= BANK_SWAPS(1); cc_rb_backend_disable = RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000; - cc_rb_backend_disable |= - BACKEND_DISABLE((R6XX_MAX_BACKENDS_MASK << rdev->config.r600.max_backends) & R6XX_MAX_BACKENDS_MASK); - - cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0xffffff00; - cc_gc_shader_pipe_config |= - INACTIVE_QD_PIPES((R6XX_MAX_PIPES_MASK << rdev->config.r600.max_pipes) & R6XX_MAX_PIPES_MASK); - cc_gc_shader_pipe_config |= - INACTIVE_SIMDS((R6XX_MAX_SIMDS_MASK << rdev->config.r600.max_simds) & R6XX_MAX_SIMDS_MASK); - - backend_map = r600_get_tile_pipe_to_backend_map(rdev->config.r600.max_tile_pipes, - (R6XX_MAX_BACKENDS - - r600_count_pipe_bits((cc_rb_backend_disable & - R6XX_MAX_BACKENDS_MASK) >> 16)), - (cc_rb_backend_disable >> 16)); + tmp = R6XX_MAX_BACKENDS - + r600_count_pipe_bits((cc_rb_backend_disable >> 16) & R6XX_MAX_BACKENDS_MASK); + if (tmp < rdev->config.r600.max_backends) { + rdev->config.r600.max_backends = tmp; + } + + cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0x00ffff00; + tmp = R6XX_MAX_PIPES - + r600_count_pipe_bits((cc_gc_shader_pipe_config >> 8) & R6XX_MAX_PIPES_MASK); + if (tmp < rdev->config.r600.max_pipes) { + rdev->config.r600.max_pipes = tmp; + } + tmp = R6XX_MAX_SIMDS - + r600_count_pipe_bits((cc_gc_shader_pipe_config >> 16) & R6XX_MAX_SIMDS_MASK); + if (tmp < rdev->config.r600.max_simds) { + rdev->config.r600.max_simds = tmp; + } + + disabled_rb_mask = (RREG32(CC_RB_BACKEND_DISABLE) >> 16) & R6XX_MAX_BACKENDS_MASK; + tmp = (tiling_config & PIPE_TILING__MASK) >> PIPE_TILING__SHIFT; + tmp = r6xx_remap_render_backend(rdev, tmp, rdev->config.r600.max_backends, + R6XX_MAX_BACKENDS, disabled_rb_mask); + tiling_config |= tmp << 16; + rdev->config.r600.backend_map = tmp; + rdev->config.r600.tile_config = tiling_config; - rdev->config.r600.backend_map = backend_map; - tiling_config |= BACKEND_MAP(backend_map); WREG32(GB_TILING_CONFIG, tiling_config); WREG32(DCP_TILING_CONFIG, tiling_config & 0xffff); WREG32(HDP_TILING_CONFIG, tiling_config & 0xffff); - /* Setup pipes */ - WREG32(CC_RB_BACKEND_DISABLE, cc_rb_backend_disable); - WREG32(CC_GC_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config); - WREG32(GC_USER_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config); - tmp = R6XX_MAX_PIPES - r600_count_pipe_bits((cc_gc_shader_pipe_config & INACTIVE_QD_PIPES_MASK) >> 8); WREG32(VGT_OUT_DEALLOC_CNTL, (tmp * 4) & DEALLOC_DIST_MASK); WREG32(VGT_VERTEX_REUSE_BLOCK_CNTL, ((tmp * 4) - 2) & VTX_REUSE_DEPTH_MASK); diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h index 15bd3b2..a0dbf1f 100644 --- a/drivers/gpu/drm/radeon/r600d.h +++ b/drivers/gpu/drm/radeon/r600d.h @@ -219,6 +219,8 @@ #define BACKEND_MAP(x) ((x) << 16) #define GB_TILING_CONFIG 0x98F0 +#define PIPE_TILING__SHIFT 1 +#define PIPE_TILING__MASK 0x0000000e #define GC_USER_SHADER_PIPE_CONFIG 0x8954 #define INACTIVE_QD_PIPES(x) ((x) << 8) diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 492654f..85dac33 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -346,6 +346,9 @@ struct radeon_bo { /* Constant after initialization */ struct radeon_device *rdev; 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) @@ -1845,6 +1848,11 @@ extern struct radeon_hdmi_acr r600_hdmi_acr(uint32_t clock); extern void r600_hdmi_enable(struct drm_encoder *encoder); extern void r600_hdmi_disable(struct drm_encoder *encoder); extern void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode); +extern u32 r6xx_remap_render_backend(struct radeon_device *rdev, + u32 tiling_pipe_num, + u32 max_rb_num, + u32 total_max_rb_num, + u32 enabled_rb_mask); /* * evergreen functions used by radeon_encoder.c diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index 0137689..142f894 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -147,6 +147,7 @@ static int radeon_cs_sync_rings(struct radeon_cs_parser *p) sync_to_ring, p->ring); } +/* XXX: note that this is called from the legacy UMS CS ioctl as well */ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) { struct drm_radeon_cs *cs = data; @@ -245,22 +246,24 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) } } - if ((p->cs_flags & RADEON_CS_USE_VM) && - !p->rdev->vm_manager.enabled) { - DRM_ERROR("VM not active on asic!\n"); - return -EINVAL; - } - - /* we only support VM on SI+ */ - if ((p->rdev->family >= CHIP_TAHITI) && - ((p->cs_flags & RADEON_CS_USE_VM) == 0)) { - DRM_ERROR("VM required on SI+!\n"); - return -EINVAL; - } + /* these are KMS only */ + if (p->rdev) { + if ((p->cs_flags & RADEON_CS_USE_VM) && + !p->rdev->vm_manager.enabled) { + DRM_ERROR("VM not active on asic!\n"); + return -EINVAL; + } - if (radeon_cs_get_ring(p, ring, priority)) - return -EINVAL; + /* we only support VM on SI+ */ + if ((p->rdev->family >= CHIP_TAHITI) && + ((p->cs_flags & RADEON_CS_USE_VM) == 0)) { + DRM_ERROR("VM required on SI+!\n"); + return -EINVAL; + } + if (radeon_cs_get_ring(p, ring, priority)) + return -EINVAL; + } /* deal with non-vm */ if ((p->chunk_ib_idx != -1) && diff --git a/drivers/gpu/drm/radeon/radeon_prime.c b/drivers/gpu/drm/radeon/radeon_prime.c index b8f835d..8ddab4c7 100644 --- a/drivers/gpu/drm/radeon/radeon_prime.c +++ b/drivers/gpu/drm/radeon/radeon_prime.c @@ -85,6 +85,47 @@ static void radeon_gem_kunmap(struct dma_buf *dma_buf, unsigned long page_num, v } +static int radeon_gem_prime_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma) +{ + 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; + 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); + 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) +{ + struct radeon_bo *bo = dma_buf->priv; + struct drm_device *dev = bo->rdev->ddev; + + 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); +} 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, @@ -93,6 +134,9 @@ const static struct dma_buf_ops radeon_dmabuf_ops = { .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, diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index c2f473b..04ddc36 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -151,6 +151,8 @@ int rv770_pcie_gart_enable(struct radeon_device *rdev) WREG32(MC_VM_MD_L1_TLB0_CNTL, tmp); WREG32(MC_VM_MD_L1_TLB1_CNTL, tmp); WREG32(MC_VM_MD_L1_TLB2_CNTL, tmp); + if (rdev->family == CHIP_RV740) + WREG32(MC_VM_MD_L1_TLB3_CNTL, tmp); WREG32(MC_VM_MB_L1_TLB0_CNTL, tmp); WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp); WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp); @@ -363,180 +365,6 @@ void r700_cp_fini(struct radeon_device *rdev) /* * Core functions */ -static u32 r700_get_tile_pipe_to_backend_map(struct radeon_device *rdev, - u32 num_tile_pipes, - u32 num_backends, - u32 backend_disable_mask) -{ - u32 backend_map = 0; - u32 enabled_backends_mask; - u32 enabled_backends_count; - u32 cur_pipe; - u32 swizzle_pipe[R7XX_MAX_PIPES]; - u32 cur_backend; - u32 i; - bool force_no_swizzle; - - if (num_tile_pipes > R7XX_MAX_PIPES) - num_tile_pipes = R7XX_MAX_PIPES; - if (num_tile_pipes < 1) - num_tile_pipes = 1; - if (num_backends > R7XX_MAX_BACKENDS) - num_backends = R7XX_MAX_BACKENDS; - if (num_backends < 1) - num_backends = 1; - - enabled_backends_mask = 0; - enabled_backends_count = 0; - for (i = 0; i < R7XX_MAX_BACKENDS; ++i) { - if (((backend_disable_mask >> i) & 1) == 0) { - enabled_backends_mask |= (1 << i); - ++enabled_backends_count; - } - if (enabled_backends_count == num_backends) - break; - } - - if (enabled_backends_count == 0) { - enabled_backends_mask = 1; - enabled_backends_count = 1; - } - - if (enabled_backends_count != num_backends) - num_backends = enabled_backends_count; - - switch (rdev->family) { - case CHIP_RV770: - case CHIP_RV730: - force_no_swizzle = false; - break; - case CHIP_RV710: - case CHIP_RV740: - default: - force_no_swizzle = true; - break; - } - - memset((uint8_t *)&swizzle_pipe[0], 0, sizeof(u32) * R7XX_MAX_PIPES); - switch (num_tile_pipes) { - case 1: - swizzle_pipe[0] = 0; - break; - case 2: - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - break; - case 3: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 1; - } - break; - case 4: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 3; - swizzle_pipe[3] = 1; - } - break; - case 5: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - swizzle_pipe[4] = 4; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 1; - swizzle_pipe[4] = 3; - } - break; - case 6: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - swizzle_pipe[4] = 4; - swizzle_pipe[5] = 5; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 5; - swizzle_pipe[4] = 3; - swizzle_pipe[5] = 1; - } - break; - case 7: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - swizzle_pipe[4] = 4; - swizzle_pipe[5] = 5; - swizzle_pipe[6] = 6; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 6; - swizzle_pipe[4] = 3; - swizzle_pipe[5] = 1; - swizzle_pipe[6] = 5; - } - break; - case 8: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - swizzle_pipe[4] = 4; - swizzle_pipe[5] = 5; - swizzle_pipe[6] = 6; - swizzle_pipe[7] = 7; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 6; - swizzle_pipe[4] = 3; - swizzle_pipe[5] = 1; - swizzle_pipe[6] = 7; - swizzle_pipe[7] = 5; - } - break; - } - - cur_backend = 0; - for (cur_pipe = 0; cur_pipe < num_tile_pipes; ++cur_pipe) { - while (((1 << cur_backend) & enabled_backends_mask) == 0) - cur_backend = (cur_backend + 1) % R7XX_MAX_BACKENDS; - - backend_map |= (u32)(((cur_backend & 3) << (swizzle_pipe[cur_pipe] * 2))); - - cur_backend = (cur_backend + 1) % R7XX_MAX_BACKENDS; - } - - return backend_map; -} - static void rv770_gpu_init(struct radeon_device *rdev) { int i, j, num_qd_pipes; @@ -552,14 +380,17 @@ static void rv770_gpu_init(struct radeon_device *rdev) u32 sq_thread_resource_mgmt; u32 hdp_host_path_cntl; u32 sq_dyn_gpr_size_simd_ab_0; - u32 backend_map; u32 gb_tiling_config = 0; u32 cc_rb_backend_disable = 0; u32 cc_gc_shader_pipe_config = 0; u32 mc_arb_ramcfg; - u32 db_debug4; + u32 db_debug4, tmp; + u32 inactive_pipes, shader_pipe_config; + u32 disabled_rb_mask; + unsigned active_number; /* setup chip specs */ + rdev->config.rv770.tiling_group_size = 256; switch (rdev->family) { case CHIP_RV770: rdev->config.rv770.max_pipes = 4; @@ -670,33 +501,70 @@ static void rv770_gpu_init(struct radeon_device *rdev) /* setup tiling, simd, pipe config */ mc_arb_ramcfg = RREG32(MC_ARB_RAMCFG); + shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG); + inactive_pipes = (shader_pipe_config & INACTIVE_QD_PIPES_MASK) >> INACTIVE_QD_PIPES_SHIFT; + for (i = 0, tmp = 1, active_number = 0; i < R7XX_MAX_PIPES; i++) { + if (!(inactive_pipes & tmp)) { + active_number++; + } + tmp <<= 1; + } + if (active_number == 1) { + WREG32(SPI_CONFIG_CNTL, DISABLE_INTERP_1); + } else { + WREG32(SPI_CONFIG_CNTL, 0); + } + + cc_rb_backend_disable = RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000; + tmp = R7XX_MAX_BACKENDS - r600_count_pipe_bits(cc_rb_backend_disable >> 16); + if (tmp < rdev->config.rv770.max_backends) { + rdev->config.rv770.max_backends = tmp; + } + + cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0xffffff00; + tmp = R7XX_MAX_PIPES - r600_count_pipe_bits((cc_gc_shader_pipe_config >> 8) & R7XX_MAX_PIPES_MASK); + if (tmp < rdev->config.rv770.max_pipes) { + rdev->config.rv770.max_pipes = tmp; + } + tmp = R7XX_MAX_SIMDS - r600_count_pipe_bits((cc_gc_shader_pipe_config >> 16) & R7XX_MAX_SIMDS_MASK); + if (tmp < rdev->config.rv770.max_simds) { + rdev->config.rv770.max_simds = tmp; + } + switch (rdev->config.rv770.max_tile_pipes) { case 1: default: - gb_tiling_config |= PIPE_TILING(0); + gb_tiling_config = PIPE_TILING(0); break; case 2: - gb_tiling_config |= PIPE_TILING(1); + gb_tiling_config = PIPE_TILING(1); break; case 4: - gb_tiling_config |= PIPE_TILING(2); + gb_tiling_config = PIPE_TILING(2); break; case 8: - gb_tiling_config |= PIPE_TILING(3); + gb_tiling_config = PIPE_TILING(3); break; } rdev->config.rv770.tiling_npipes = rdev->config.rv770.max_tile_pipes; + disabled_rb_mask = (RREG32(CC_RB_BACKEND_DISABLE) >> 16) & R7XX_MAX_BACKENDS_MASK; + tmp = (gb_tiling_config & PIPE_TILING__MASK) >> PIPE_TILING__SHIFT; + tmp = r6xx_remap_render_backend(rdev, tmp, rdev->config.rv770.max_backends, + R7XX_MAX_BACKENDS, disabled_rb_mask); + gb_tiling_config |= tmp << 16; + rdev->config.rv770.backend_map = tmp; + if (rdev->family == CHIP_RV770) gb_tiling_config |= BANK_TILING(1); - else - gb_tiling_config |= BANK_TILING((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT); + else { + if ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) + gb_tiling_config |= BANK_TILING(1); + else + gb_tiling_config |= BANK_TILING(0); + } rdev->config.rv770.tiling_nbanks = 4 << ((gb_tiling_config >> 4) & 0x3); gb_tiling_config |= GROUP_SIZE((mc_arb_ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT); - if ((mc_arb_ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT) - rdev->config.rv770.tiling_group_size = 512; - else - rdev->config.rv770.tiling_group_size = 256; if (((mc_arb_ramcfg & NOOFROWS_MASK) >> NOOFROWS_SHIFT) > 3) { gb_tiling_config |= ROW_TILING(3); gb_tiling_config |= SAMPLE_SPLIT(3); @@ -708,47 +576,19 @@ static void rv770_gpu_init(struct radeon_device *rdev) } gb_tiling_config |= BANK_SWAPS(1); - - cc_rb_backend_disable = RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000; - cc_rb_backend_disable |= - BACKEND_DISABLE((R7XX_MAX_BACKENDS_MASK << rdev->config.rv770.max_backends) & R7XX_MAX_BACKENDS_MASK); - - cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0xffffff00; - cc_gc_shader_pipe_config |= - INACTIVE_QD_PIPES((R7XX_MAX_PIPES_MASK << rdev->config.rv770.max_pipes) & R7XX_MAX_PIPES_MASK); - cc_gc_shader_pipe_config |= - INACTIVE_SIMDS((R7XX_MAX_SIMDS_MASK << rdev->config.rv770.max_simds) & R7XX_MAX_SIMDS_MASK); - - if (rdev->family == CHIP_RV740) - backend_map = 0x28; - else - backend_map = r700_get_tile_pipe_to_backend_map(rdev, - rdev->config.rv770.max_tile_pipes, - (R7XX_MAX_BACKENDS - - r600_count_pipe_bits((cc_rb_backend_disable & - R7XX_MAX_BACKENDS_MASK) >> 16)), - (cc_rb_backend_disable >> 16)); - rdev->config.rv770.tile_config = gb_tiling_config; - rdev->config.rv770.backend_map = backend_map; - gb_tiling_config |= BACKEND_MAP(backend_map); WREG32(GB_TILING_CONFIG, gb_tiling_config); WREG32(DCP_TILING_CONFIG, (gb_tiling_config & 0xffff)); WREG32(HDP_TILING_CONFIG, (gb_tiling_config & 0xffff)); - WREG32(CC_RB_BACKEND_DISABLE, cc_rb_backend_disable); - WREG32(CC_GC_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config); - WREG32(GC_USER_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config); - WREG32(CC_SYS_RB_BACKEND_DISABLE, cc_rb_backend_disable); - WREG32(CGTS_SYS_TCC_DISABLE, 0); WREG32(CGTS_TCC_DISABLE, 0); WREG32(CGTS_USER_SYS_TCC_DISABLE, 0); WREG32(CGTS_USER_TCC_DISABLE, 0); - num_qd_pipes = - R7XX_MAX_PIPES - r600_count_pipe_bits((cc_gc_shader_pipe_config & INACTIVE_QD_PIPES_MASK) >> 8); + + num_qd_pipes = R7XX_MAX_PIPES - r600_count_pipe_bits((cc_gc_shader_pipe_config & INACTIVE_QD_PIPES_MASK) >> 8); WREG32(VGT_OUT_DEALLOC_CNTL, (num_qd_pipes * 4) & DEALLOC_DIST_MASK); WREG32(VGT_VERTEX_REUSE_BLOCK_CNTL, ((num_qd_pipes * 4) - 2) & VTX_REUSE_DEPTH_MASK); @@ -809,8 +649,6 @@ static void rv770_gpu_init(struct radeon_device *rdev) WREG32(VGT_NUM_INSTANCES, 1); - WREG32(SPI_CONFIG_CNTL, GPR_WRITE_PRIORITY(0)); - WREG32(SPI_CONFIG_CNTL_1, VTX_DONE_DELAY(4)); WREG32(CP_PERFMON_CNTL, 0); diff --git a/drivers/gpu/drm/radeon/rv770d.h b/drivers/gpu/drm/radeon/rv770d.h index 9c549f7..fdc0898 100644 --- a/drivers/gpu/drm/radeon/rv770d.h +++ b/drivers/gpu/drm/radeon/rv770d.h @@ -106,10 +106,13 @@ #define BACKEND_MAP(x) ((x) << 16) #define GB_TILING_CONFIG 0x98F0 +#define PIPE_TILING__SHIFT 1 +#define PIPE_TILING__MASK 0x0000000e #define GC_USER_SHADER_PIPE_CONFIG 0x8954 #define INACTIVE_QD_PIPES(x) ((x) << 8) #define INACTIVE_QD_PIPES_MASK 0x0000FF00 +#define INACTIVE_QD_PIPES_SHIFT 8 #define INACTIVE_SIMDS(x) ((x) << 16) #define INACTIVE_SIMDS_MASK 0x00FF0000 @@ -174,6 +177,7 @@ #define MC_VM_MD_L1_TLB0_CNTL 0x2654 #define MC_VM_MD_L1_TLB1_CNTL 0x2658 #define MC_VM_MD_L1_TLB2_CNTL 0x265C +#define MC_VM_MD_L1_TLB3_CNTL 0x2698 #define MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR 0x203C #define MC_VM_SYSTEM_APERTURE_HIGH_ADDR 0x2038 #define MC_VM_SYSTEM_APERTURE_LOW_ADDR 0x2034 diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 36792bd..b67cfca 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -1834,6 +1834,7 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink) spin_unlock(&glob->lru_lock); (void) ttm_bo_cleanup_refs(bo, false, false, false); kref_put(&bo->list_kref, ttm_bo_release_list); + spin_lock(&glob->lru_lock); continue; } diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index a029ee3..ce9a611 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -156,8 +156,17 @@ int udl_handle_damage(struct udl_framebuffer *fb, int x, int y, if (!fb->active_16) return 0; - if (!fb->obj->vmapping) - udl_gem_vmap(fb->obj); + if (!fb->obj->vmapping) { + ret = udl_gem_vmap(fb->obj); + if (ret == -ENOMEM) { + DRM_ERROR("failed to vmap fb\n"); + return 0; + } + if (!fb->obj->vmapping) { + DRM_ERROR("failed to vmapping\n"); + return 0; + } + } start_cycles = get_cycles(); diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c index 97acc9c..7bd65bd 100644 --- a/drivers/gpu/drm/udl/udl_gem.c +++ b/drivers/gpu/drm/udl/udl_gem.c @@ -180,6 +180,18 @@ int udl_gem_vmap(struct udl_gem_object *obj) int page_count = obj->base.size / PAGE_SIZE; int ret; + if (obj->base.import_attach) { + ret = dma_buf_begin_cpu_access(obj->base.import_attach->dmabuf, + 0, obj->base.size, DMA_BIDIRECTIONAL); + if (ret) + return -EINVAL; + + obj->vmapping = dma_buf_vmap(obj->base.import_attach->dmabuf); + if (!obj->vmapping) + return -ENOMEM; + return 0; + } + ret = udl_gem_get_pages(obj, GFP_KERNEL); if (ret) return ret; @@ -192,6 +204,13 @@ int udl_gem_vmap(struct udl_gem_object *obj) void udl_gem_vunmap(struct udl_gem_object *obj) { + if (obj->base.import_attach) { + dma_buf_vunmap(obj->base.import_attach->dmabuf, obj->vmapping); + dma_buf_end_cpu_access(obj->base.import_attach->dmabuf, 0, + obj->base.size, DMA_BIDIRECTIONAL); + return; + } + if (obj->vmapping) vunmap(obj->vmapping); @@ -202,12 +221,12 @@ void udl_gem_free_object(struct drm_gem_object *gem_obj) { struct udl_gem_object *obj = to_udl_bo(gem_obj); - if (gem_obj->import_attach) - drm_prime_gem_destroy(gem_obj, obj->sg); - if (obj->vmapping) udl_gem_vunmap(obj); + if (gem_obj->import_attach) + drm_prime_gem_destroy(gem_obj, obj->sg); + if (obj->pages) udl_gem_put_pages(obj); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c index 51c9ba5..21ee782 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c @@ -66,7 +66,7 @@ static int vmw_gmr2_bind(struct vmw_private *dev_priv, cmd += sizeof(remap_cmd) / sizeof(uint32); for (i = 0; i < num_pages; ++i) { - if (VMW_PPN_SIZE > 4) + if (VMW_PPN_SIZE <= 4) *cmd = page_to_pfn(*pages++); else *((uint64_t *)cmd) = page_to_pfn(*pages++); diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index 444143e..d99db56 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -1653,7 +1653,6 @@ mpt_mapresources(MPT_ADAPTER *ioc) unsigned long port; u32 msize; u32 psize; - u8 revision; int r = -ENODEV; struct pci_dev *pdev; @@ -1670,8 +1669,6 @@ mpt_mapresources(MPT_ADAPTER *ioc) return r; } - pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision); - if (sizeof(dma_addr_t) > 4) { const uint64_t required_mask = dma_get_required_mask (&pdev->dev); @@ -1779,7 +1776,6 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id) MPT_ADAPTER *ioc; u8 cb_idx; int r = -ENODEV; - u8 revision; u8 pcixcmd; static int mpt_ids = 0; #ifdef CONFIG_PROC_FS @@ -1887,8 +1883,8 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id) dinitprintk(ioc, printk(MYIOC_s_INFO_FMT "facts @ %p, pfacts[0] @ %p\n", ioc->name, &ioc->facts, &ioc->pfacts[0])); - pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision); - mpt_get_product_name(pdev->vendor, pdev->device, revision, ioc->prod_name); + mpt_get_product_name(pdev->vendor, pdev->device, pdev->revision, + ioc->prod_name); switch (pdev->device) { @@ -1903,7 +1899,7 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id) break; case MPI_MANUFACTPAGE_DEVICEID_FC929X: - if (revision < XL_929) { + if (pdev->revision < XL_929) { /* 929X Chip Fix. Set Split transactions level * for PCIX. Set MOST bits to zero. */ @@ -1934,7 +1930,7 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id) /* 1030 Chip Fix. Disable Split transactions * for PCIX. Set MOST bits to zero if Rev < C0( = 8). */ - if (revision < C0_1030) { + if (pdev->revision < C0_1030) { pci_read_config_byte(pdev, 0x6a, &pcixcmd); pcixcmd &= 0x8F; pci_write_config_byte(pdev, 0x6a, pcixcmd); diff --git a/drivers/message/fusion/mptctl.c b/drivers/message/fusion/mptctl.c index 6e6e16a..b383b69 100644 --- a/drivers/message/fusion/mptctl.c +++ b/drivers/message/fusion/mptctl.c @@ -1250,7 +1250,6 @@ mptctl_getiocinfo (unsigned long arg, unsigned int data_size) int iocnum; unsigned int port; int cim_rev; - u8 revision; struct scsi_device *sdev; VirtDevice *vdevice; @@ -1324,8 +1323,7 @@ mptctl_getiocinfo (unsigned long arg, unsigned int data_size) pdev = (struct pci_dev *) ioc->pcidev; karg->pciId = pdev->device; - pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision); - karg->hwRev = revision; + karg->hwRev = pdev->revision; karg->subSystemDevice = pdev->subsystem_device; karg->subSystemVendor = pdev->subsystem_vendor; diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 671c8bc..50e83dc5 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2735,6 +2735,7 @@ static struct regulator_consumer_supply db8500_vape_consumers[] = { REGULATOR_SUPPLY("vcore", "uart2"), REGULATOR_SUPPLY("v-ape", "nmk-ske-keypad.0"), REGULATOR_SUPPLY("v-hsi", "ste_hsi.0"), + REGULATOR_SUPPLY("vddvario", "smsc911x.0"), }; static struct regulator_consumer_supply db8500_vsmps2_consumers[] = { diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 5760c1a..27143e0 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -128,7 +128,7 @@ config MTD_AFS_PARTS config MTD_OF_PARTS tristate "OpenFirmware partitioning information support" - default Y + default y depends on OF help This provides a partition parsing function which derives diff --git a/drivers/mtd/bcm63xxpart.c b/drivers/mtd/bcm63xxpart.c index 608321e..63d2a64 100644 --- a/drivers/mtd/bcm63xxpart.c +++ b/drivers/mtd/bcm63xxpart.c @@ -4,7 +4,7 @@ * Copyright © 2006-2008 Florian Fainelli <florian@openwrt.org> * Mike Albon <malbon@openwrt.org> * Copyright © 2009-2010 Daniel Dickinson <openwrt@cshore.neomailbox.net> - * Copyright © 2011 Jonas Gorski <jonas.gorski@gmail.com> + * Copyright © 2011-2012 Jonas Gorski <jonas.gorski@gmail.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 @@ -82,6 +82,7 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, int namelen = 0; int i; u32 computed_crc; + bool rootfs_first = false; if (bcm63xx_detect_cfe(master)) return -EINVAL; @@ -109,6 +110,7 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, char *boardid = &(buf->board_id[0]); char *tagversion = &(buf->tag_version[0]); + sscanf(buf->flash_image_start, "%u", &rootfsaddr); sscanf(buf->kernel_address, "%u", &kerneladdr); sscanf(buf->kernel_length, "%u", &kernellen); sscanf(buf->total_length, "%u", &totallen); @@ -117,10 +119,19 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, tagversion, boardid); kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE; - rootfsaddr = kerneladdr + kernellen; + rootfsaddr = rootfsaddr - BCM63XX_EXTENDED_SIZE; spareaddr = roundup(totallen, master->erasesize) + cfelen; sparelen = master->size - spareaddr - nvramlen; - rootfslen = spareaddr - rootfsaddr; + + if (rootfsaddr < kerneladdr) { + /* default Broadcom layout */ + rootfslen = kerneladdr - rootfsaddr; + rootfs_first = true; + } else { + /* OpenWrt layout */ + rootfsaddr = kerneladdr + kernellen; + rootfslen = spareaddr - rootfsaddr; + } } else { pr_warn("CFE boot tag CRC invalid (expected %08x, actual %08x)\n", buf->header_crc, computed_crc); @@ -156,18 +167,26 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, curpart++; if (kernellen > 0) { - parts[curpart].name = "kernel"; - parts[curpart].offset = kerneladdr; - parts[curpart].size = kernellen; + int kernelpart = curpart; + + if (rootfslen > 0 && rootfs_first) + kernelpart++; + parts[kernelpart].name = "kernel"; + parts[kernelpart].offset = kerneladdr; + parts[kernelpart].size = kernellen; curpart++; } if (rootfslen > 0) { - parts[curpart].name = "rootfs"; - parts[curpart].offset = rootfsaddr; - parts[curpart].size = rootfslen; - if (sparelen > 0) - parts[curpart].size += sparelen; + int rootfspart = curpart; + + if (kernellen > 0 && rootfs_first) + rootfspart--; + parts[rootfspart].name = "rootfs"; + parts[rootfspart].offset = rootfsaddr; + parts[rootfspart].size = rootfslen; + if (sparelen > 0 && !rootfs_first) + parts[rootfspart].size += sparelen; curpart++; } diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index d02592e..22d0493 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -317,7 +317,7 @@ static void fixup_s29gl064n_sectors(struct mtd_info *mtd) if ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0x003f) { cfi->cfiq->EraseRegionInfo[0] |= 0x0040; - pr_warning("%s: Bad S29GL064N CFI data, adjust from 64 to 128 sectors\n", mtd->name); + pr_warning("%s: Bad S29GL064N CFI data; adjust from 64 to 128 sectors\n", mtd->name); } } @@ -328,10 +328,23 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd) if ((cfi->cfiq->EraseRegionInfo[1] & 0xffff) == 0x007e) { cfi->cfiq->EraseRegionInfo[1] &= ~0x0040; - pr_warning("%s: Bad S29GL032N CFI data, adjust from 127 to 63 sectors\n", mtd->name); + pr_warning("%s: Bad S29GL032N CFI data; adjust from 127 to 63 sectors\n", mtd->name); } } +static void fixup_s29ns512p_sectors(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + + /* + * S29NS512P flash uses more than 8bits to report number of sectors, + * which is not permitted by CFI. + */ + cfi->cfiq->EraseRegionInfo[0] = 0x020001ff; + pr_warning("%s: Bad S29NS512P CFI data; adjust to 512 sectors\n", mtd->name); +} + /* Used to fix CFI-Tables of chips without Extended Query Tables */ static struct cfi_fixup cfi_nopri_fixup_table[] = { { CFI_MFR_SST, 0x234a, fixup_sst39vf }, /* SST39VF1602 */ @@ -362,6 +375,7 @@ static struct cfi_fixup cfi_fixup_table[] = { { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors }, { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors }, { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors }, + { CFI_MFR_AMD, 0x3f00, fixup_s29ns512p_sectors }, { CFI_MFR_SST, 0x536a, fixup_sst38vf640x_sectorsize }, /* SST38VF6402 */ { CFI_MFR_SST, 0x536b, fixup_sst38vf640x_sectorsize }, /* SST38VF6401 */ { CFI_MFR_SST, 0x536c, fixup_sst38vf640x_sectorsize }, /* SST38VF6404 */ diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index ddf9ec6..4558e0f 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -70,7 +70,7 @@ struct cmdline_mtd_partition { /* mtdpart_setup() parses into here */ static struct cmdline_mtd_partition *partitions; -/* the command line passed to mtdpart_setupd() */ +/* the command line passed to mtdpart_setup() */ static char *cmdline; static int cmdline_parsed = 0; diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index a4a80b7..681e2ee 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -52,8 +52,6 @@ static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len) while (pages) { page = page_read(mapping, index); - if (!page) - return -ENOMEM; if (IS_ERR(page)) return PTR_ERR(page); @@ -112,8 +110,6 @@ static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len, len = len - cpylen; page = page_read(dev->blkdev->bd_inode->i_mapping, index); - if (!page) - return -ENOMEM; if (IS_ERR(page)) return PTR_ERR(page); @@ -148,8 +144,6 @@ static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf, len = len - cpylen; page = page_read(mapping, index); - if (!page) - return -ENOMEM; if (IS_ERR(page)) return PTR_ERR(page); @@ -271,7 +265,6 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) dev->mtd.flags = MTD_CAP_RAM; dev->mtd._erase = block2mtd_erase; dev->mtd._write = block2mtd_write; - dev->mtd._writev = mtd_writev; dev->mtd._sync = block2mtd_sync; dev->mtd._read = block2mtd_read; dev->mtd.priv = dev; diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 50aa90a..f70854d 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -227,7 +227,7 @@ static void doc_read_data_area(struct docg3 *docg3, void *buf, int len, u8 data8, *dst8; doc_dbg("doc_read_data_area(buf=%p, len=%d)\n", buf, len); - cdr = len & 0x3; + cdr = len & 0x1; len4 = len - cdr; if (first) @@ -732,12 +732,24 @@ err: * @len: the number of bytes to be read (must be a multiple of 4) * @buf: the buffer to be filled in (or NULL is forget bytes) * @first: 1 if first time read, DOC_READADDRESS should be set + * @last_odd: 1 if last read ended up on an odd byte + * + * Reads bytes from a prepared page. There is a trickery here : if the last read + * ended up on an odd offset in the 1024 bytes double page, ie. between the 2 + * planes, the first byte must be read apart. If a word (16bit) read was used, + * the read would return the byte of plane 2 as low *and* high endian, which + * will mess the read. * */ static int doc_read_page_getbytes(struct docg3 *docg3, int len, u_char *buf, - int first) + int first, int last_odd) { - doc_read_data_area(docg3, buf, len, first); + if (last_odd && len > 0) { + doc_read_data_area(docg3, buf, 1, first); + doc_read_data_area(docg3, buf ? buf + 1 : buf, len - 1, 0); + } else { + doc_read_data_area(docg3, buf, len, first); + } doc_delay(docg3, 2); return len; } @@ -850,6 +862,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, u8 *buf = ops->datbuf; size_t len, ooblen, nbdata, nboob; u8 hwecc[DOC_ECC_BCH_SIZE], eccconf1; + int max_bitflips = 0; if (buf) len = ops->len; @@ -876,7 +889,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, ret = 0; skip = from % DOC_LAYOUT_PAGE_SIZE; mutex_lock(&docg3->cascade->lock); - while (!ret && (len > 0 || ooblen > 0)) { + while (ret >= 0 && (len > 0 || ooblen > 0)) { calc_block_sector(from - skip, &block0, &block1, &page, &ofs, docg3->reliable); nbdata = min_t(size_t, len, DOC_LAYOUT_PAGE_SIZE - skip); @@ -887,20 +900,20 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES); if (ret < 0) goto err_in_read; - ret = doc_read_page_getbytes(docg3, skip, NULL, 1); + ret = doc_read_page_getbytes(docg3, skip, NULL, 1, 0); if (ret < skip) goto err_in_read; - ret = doc_read_page_getbytes(docg3, nbdata, buf, 0); + ret = doc_read_page_getbytes(docg3, nbdata, buf, 0, skip % 2); if (ret < nbdata) goto err_in_read; doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE - nbdata - skip, - NULL, 0); - ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0); + NULL, 0, (skip + nbdata) % 2); + ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0, 0); if (ret < nboob) goto err_in_read; doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE - nboob, - NULL, 0); + NULL, 0, nboob % 2); doc_get_bch_hw_ecc(docg3, hwecc); eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1); @@ -936,7 +949,8 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, } if (ret > 0) { mtd->ecc_stats.corrected += ret; - ret = -EUCLEAN; + max_bitflips = max(max_bitflips, ret); + ret = max_bitflips; } } @@ -1004,7 +1018,7 @@ static int doc_reload_bbt(struct docg3 *docg3) DOC_LAYOUT_PAGE_SIZE); if (!ret) doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE, - buf, 1); + buf, 1, 0); buf += DOC_LAYOUT_PAGE_SIZE; } doc_read_page_finish(docg3); @@ -1064,10 +1078,10 @@ static int doc_get_erase_count(struct docg3 *docg3, loff_t from) ret = doc_reset_seq(docg3); if (!ret) ret = doc_read_page_prepare(docg3, block0, block1, page, - ofs + DOC_LAYOUT_WEAR_OFFSET); + ofs + DOC_LAYOUT_WEAR_OFFSET, 0); if (!ret) ret = doc_read_page_getbytes(docg3, DOC_LAYOUT_WEAR_SIZE, - buf, 1); + buf, 1, 0); doc_read_page_finish(docg3); if (ret || (buf[0] != DOC_ERASE_MARK) || (buf[2] != DOC_ERASE_MARK)) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 1924d24..5d0d68c 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -639,12 +639,16 @@ static const struct spi_device_id m25p_ids[] = { { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) }, { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, + /* Everspin */ + { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2) }, + /* Intel/Numonyx -- xxxs33b */ { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) }, /* Macronix */ + { "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) }, { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) }, { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) }, { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) }, @@ -728,6 +732,7 @@ static const struct spi_device_id m25p_ids[] = { { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) }, { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, + { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, /* Catalyst / On Semiconductor -- non-JEDEC */ { "cat25c11", CAT25_INFO( 16, 8, 16, 1) }, diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index 797d43c..6796036 100644 --- a/drivers/mtd/devices/spear_smi.c +++ b/drivers/mtd/devices/spear_smi.c @@ -990,9 +990,9 @@ static int __devinit spear_smi_probe(struct platform_device *pdev) goto err_clk; } - ret = clk_enable(dev->clk); + ret = clk_prepare_enable(dev->clk); if (ret) - goto err_clk_enable; + goto err_clk_prepare_enable; ret = request_irq(irq, spear_smi_int_handler, 0, pdev->name, dev); if (ret) { @@ -1020,8 +1020,8 @@ err_bank_setup: free_irq(irq, dev); platform_set_drvdata(pdev, NULL); err_irq: - clk_disable(dev->clk); -err_clk_enable: + clk_disable_unprepare(dev->clk); +err_clk_prepare_enable: clk_put(dev->clk); err_clk: iounmap(dev->io_base); @@ -1074,7 +1074,7 @@ static int __devexit spear_smi_remove(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); free_irq(irq, dev); - clk_disable(dev->clk); + clk_disable_unprepare(dev->clk); clk_put(dev->clk); iounmap(dev->io_base); kfree(dev); @@ -1091,7 +1091,7 @@ int spear_smi_suspend(struct platform_device *pdev, pm_message_t state) struct spear_smi *dev = platform_get_drvdata(pdev); if (dev && dev->clk) - clk_disable(dev->clk); + clk_disable_unprepare(dev->clk); return 0; } @@ -1102,7 +1102,7 @@ int spear_smi_resume(struct platform_device *pdev) int ret = -EPERM; if (dev && dev->clk) - ret = clk_enable(dev->clk); + ret = clk_prepare_enable(dev->clk); if (!ret) spear_smi_hw_init(dev); diff --git a/drivers/mtd/lpddr/qinfo_probe.c b/drivers/mtd/lpddr/qinfo_probe.c index dbfe17b..45abed6 100644 --- a/drivers/mtd/lpddr/qinfo_probe.c +++ b/drivers/mtd/lpddr/qinfo_probe.c @@ -57,7 +57,7 @@ static struct qinfo_query_info qinfo_array[] = { static long lpddr_get_qinforec_pos(struct map_info *map, char *id_str) { - int qinfo_lines = sizeof(qinfo_array)/sizeof(struct qinfo_query_info); + int qinfo_lines = ARRAY_SIZE(qinfo_array); int i; int bankwidth = map_bankwidth(map) * 8; int major, minor; diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 8af67cf..5ba2458 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -224,7 +224,7 @@ config MTD_CK804XROM config MTD_SCB2_FLASH tristate "BIOS flash chip on Intel SCB2 boards" - depends on X86 && MTD_JEDECPROBE + depends on X86 && MTD_JEDECPROBE && PCI help Support for treating the BIOS flash chip on Intel SCB2 boards as an MTD device - with this you can reprogram your BIOS. diff --git a/drivers/mtd/maps/intel_vr_nor.c b/drivers/mtd/maps/intel_vr_nor.c index 92e1f41..93f0317 100644 --- a/drivers/mtd/maps/intel_vr_nor.c +++ b/drivers/mtd/maps/intel_vr_nor.c @@ -260,18 +260,7 @@ static struct pci_driver vr_nor_pci_driver = { .id_table = vr_nor_pci_ids, }; -static int __init vr_nor_mtd_init(void) -{ - return pci_register_driver(&vr_nor_pci_driver); -} - -static void __exit vr_nor_mtd_exit(void) -{ - pci_unregister_driver(&vr_nor_pci_driver); -} - -module_init(vr_nor_mtd_init); -module_exit(vr_nor_mtd_exit); +module_pci_driver(vr_nor_pci_driver); MODULE_AUTHOR("Andy Lowe"); MODULE_DESCRIPTION("MTD map driver for NOR flash on Intel Vermilion Range"); diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c index 1d005a3..f14ce0a 100644 --- a/drivers/mtd/maps/pci.c +++ b/drivers/mtd/maps/pci.c @@ -352,18 +352,7 @@ static struct pci_driver mtd_pci_driver = { .id_table = mtd_pci_ids, }; -static int __init mtd_pci_maps_init(void) -{ - return pci_register_driver(&mtd_pci_driver); -} - -static void __exit mtd_pci_maps_exit(void) -{ - pci_unregister_driver(&mtd_pci_driver); -} - -module_init(mtd_pci_maps_init); -module_exit(mtd_pci_maps_exit); +module_pci_driver(mtd_pci_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c index 934a72c..9dcbc68 100644 --- a/drivers/mtd/maps/scb2_flash.c +++ b/drivers/mtd/maps/scb2_flash.c @@ -234,20 +234,7 @@ static struct pci_driver scb2_flash_driver = { .remove = __devexit_p(scb2_flash_remove), }; -static int __init -scb2_flash_init(void) -{ - return pci_register_driver(&scb2_flash_driver); -} - -static void __exit -scb2_flash_exit(void) -{ - pci_unregister_driver(&scb2_flash_driver); -} - -module_init(scb2_flash_init); -module_exit(scb2_flash_exit); +module_pci_driver(scb2_flash_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Tim Hockin <thockin@sun.com>"); diff --git a/drivers/mtd/maps/wr_sbc82xx_flash.c b/drivers/mtd/maps/wr_sbc82xx_flash.c index 71b0ba7..e7534c8 100644 --- a/drivers/mtd/maps/wr_sbc82xx_flash.c +++ b/drivers/mtd/maps/wr_sbc82xx_flash.c @@ -59,7 +59,7 @@ static struct mtd_partition bigflash_parts[] = { } }; -static const char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL}; +static const char *part_probes[] __initconst = {"cmdlinepart", "RedBoot", NULL}; #define init_sbc82xx_one_flash(map, br, or) \ do { \ diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index c837507..5757307 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -250,6 +250,43 @@ static ssize_t mtd_name_show(struct device *dev, } static DEVICE_ATTR(name, S_IRUGO, mtd_name_show, NULL); +static ssize_t mtd_ecc_strength_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", mtd->ecc_strength); +} +static DEVICE_ATTR(ecc_strength, S_IRUGO, mtd_ecc_strength_show, NULL); + +static ssize_t mtd_bitflip_threshold_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", mtd->bitflip_threshold); +} + +static ssize_t mtd_bitflip_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + unsigned int bitflip_threshold; + int retval; + + retval = kstrtouint(buf, 0, &bitflip_threshold); + if (retval) + return retval; + + mtd->bitflip_threshold = bitflip_threshold; + return count; +} +static DEVICE_ATTR(bitflip_threshold, S_IRUGO | S_IWUSR, + mtd_bitflip_threshold_show, + mtd_bitflip_threshold_store); + static struct attribute *mtd_attrs[] = { &dev_attr_type.attr, &dev_attr_flags.attr, @@ -260,6 +297,8 @@ static struct attribute *mtd_attrs[] = { &dev_attr_oobsize.attr, &dev_attr_numeraseregions.attr, &dev_attr_name.attr, + &dev_attr_ecc_strength.attr, + &dev_attr_bitflip_threshold.attr, NULL, }; @@ -322,6 +361,10 @@ int add_mtd_device(struct mtd_info *mtd) mtd->index = i; mtd->usecount = 0; + /* default value if not set by driver */ + if (mtd->bitflip_threshold == 0) + mtd->bitflip_threshold = mtd->ecc_strength; + if (is_power_of_2(mtd->erasesize)) mtd->erasesize_shift = ffs(mtd->erasesize) - 1; else @@ -757,12 +800,24 @@ EXPORT_SYMBOL_GPL(mtd_get_unmapped_area); int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { + int ret_code; *retlen = 0; if (from < 0 || from > mtd->size || len > mtd->size - from) return -EINVAL; if (!len) return 0; - return mtd->_read(mtd, from, len, retlen, buf); + + /* + * In the absence of an error, drivers return a non-negative integer + * representing the maximum number of bitflips that were corrected on + * any one ecc region (if applicable; zero otherwise). + */ + ret_code = mtd->_read(mtd, from, len, retlen, buf); + if (unlikely(ret_code < 0)) + return ret_code; + if (mtd->ecc_strength == 0) + return 0; /* device lacks ecc */ + return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0; } EXPORT_SYMBOL_GPL(mtd_read); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 9651c06..d518e4d 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -67,12 +67,12 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len, stats = part->master->ecc_stats; res = part->master->_read(part->master, from + part->offset, len, retlen, buf); - if (unlikely(res)) { - if (mtd_is_bitflip(res)) - mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected; - if (mtd_is_eccerr(res)) - mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed; - } + if (unlikely(mtd_is_eccerr(res))) + mtd->ecc_stats.failed += + part->master->ecc_stats.failed - stats.failed; + else + mtd->ecc_stats.corrected += + part->master->ecc_stats.corrected - stats.corrected; return res; } @@ -517,6 +517,8 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, slave->mtd.ecclayout = master->ecclayout; slave->mtd.ecc_strength = master->ecc_strength; + slave->mtd.bitflip_threshold = master->bitflip_threshold; + if (master->_block_isbad) { uint64_t offs = 0; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 7d17cec..31bb7e5 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -115,6 +115,46 @@ config MTD_NAND_OMAP2 Support for NAND flash on Texas Instruments OMAP2, OMAP3 and OMAP4 platforms. +config MTD_NAND_OMAP_BCH + depends on MTD_NAND && MTD_NAND_OMAP2 && ARCH_OMAP3 + bool "Enable support for hardware BCH error correction" + default n + select BCH + select BCH_CONST_PARAMS + help + Support for hardware BCH error correction. + +choice + prompt "BCH error correction capability" + depends on MTD_NAND_OMAP_BCH + +config MTD_NAND_OMAP_BCH8 + bool "8 bits / 512 bytes (recommended)" + help + Support correcting up to 8 bitflips per 512-byte block. + This will use 13 bytes of spare area per 512 bytes of page data. + This is the recommended mode, as 4-bit mode does not work + on some OMAP3 revisions, due to a hardware bug. + +config MTD_NAND_OMAP_BCH4 + bool "4 bits / 512 bytes" + help + Support correcting up to 4 bitflips per 512-byte block. + This will use 7 bytes of spare area per 512 bytes of page data. + Note that this mode does not work on some OMAP3 revisions, due to a + hardware bug. Please check your OMAP datasheet before selecting this + mode. + +endchoice + +if MTD_NAND_OMAP_BCH +config BCH_CONST_M + default 13 +config BCH_CONST_T + default 4 if MTD_NAND_OMAP_BCH4 + default 8 if MTD_NAND_OMAP_BCH8 +endif + config MTD_NAND_IDS tristate @@ -440,7 +480,7 @@ config MTD_NAND_NANDSIM config MTD_NAND_GPMI_NAND bool "GPMI NAND Flash Controller driver" - depends on MTD_NAND && (SOC_IMX23 || SOC_IMX28) + depends on MTD_NAND && (SOC_IMX23 || SOC_IMX28 || SOC_IMX6Q) help Enables NAND Flash support for IMX23 or IMX28. The GPMI controller is very powerful, with the help of BCH diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c index 4f20e1d..60a0dfd 100644 --- a/drivers/mtd/nand/alauda.c +++ b/drivers/mtd/nand/alauda.c @@ -414,7 +414,7 @@ static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len, } err = 0; if (corrected) - err = -EUCLEAN; + err = 1; /* return max_bitflips per ecc step */ if (uncorrected) err = -EBADMSG; out: @@ -446,7 +446,7 @@ static int alauda_read(struct mtd_info *mtd, loff_t from, size_t len, } err = 0; if (corrected) - err = -EUCLEAN; + err = 1; /* return max_bitflips per ecc step */ if (uncorrected) err = -EBADMSG; return err; diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 2165576..97ac671 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -324,9 +324,10 @@ static int atmel_nand_calculate(struct mtd_info *mtd, * mtd: mtd info structure * chip: nand chip info structure * buf: buffer to store read data + * oob_required: caller expects OOB data read to chip->oob_poi */ -static int atmel_nand_read_page(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int page) +static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) { int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -335,6 +336,7 @@ static int atmel_nand_read_page(struct mtd_info *mtd, uint8_t *oob = chip->oob_poi; uint8_t *ecc_pos; int stat; + unsigned int max_bitflips = 0; /* * Errata: ALE is incorrectly wired up to the ECC controller @@ -371,10 +373,12 @@ static int atmel_nand_read_page(struct mtd_info *mtd, /* check if there's an error */ stat = chip->ecc.correct(mtd, p, oob, NULL); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } /* get back to oob start (end of page) */ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); @@ -382,7 +386,7 @@ static int atmel_nand_read_page(struct mtd_info *mtd, /* read the oob */ chip->read_buf(mtd, oob, mtd->oobsize); - return 0; + return max_bitflips; } /* diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 73abbc3..9f609d2 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -508,8 +508,6 @@ static int __devinit au1550nd_probe(struct platform_device *pdev) this->chip_delay = 30; this->ecc.mode = NAND_ECC_SOFT; - this->options = NAND_NO_AUTOINCR; - if (pd->devwidth) this->options |= NAND_BUSWIDTH_16; diff --git a/drivers/mtd/nand/bcm_umi_bch.c b/drivers/mtd/nand/bcm_umi_bch.c index a930666..5914bb3 100644 --- a/drivers/mtd/nand/bcm_umi_bch.c +++ b/drivers/mtd/nand/bcm_umi_bch.c @@ -22,9 +22,9 @@ /* ---- Private Function Prototypes -------------------------------------- */ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int page); + struct nand_chip *chip, uint8_t *buf, int oob_required, int page); static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf); + struct nand_chip *chip, const uint8_t *buf, int oob_required); /* ---- Private Variables ------------------------------------------------ */ @@ -103,11 +103,12 @@ static struct nand_ecclayout nand_hw_eccoob_4096 = { * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data +* @oob_required: caller expects OOB data read to chip->oob_poi * ***************************************************************************/ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf, - int page) + int oob_required, int page) { int sectorIdx = 0; int eccsize = chip->ecc.size; @@ -116,6 +117,7 @@ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, uint8_t eccCalc[NAND_ECC_NUM_BYTES]; int sectorOobSize = mtd->oobsize / eccsteps; int stat; + unsigned int max_bitflips = 0; for (sectorIdx = 0; sectorIdx < eccsteps; sectorIdx++, datap += eccsize) { @@ -177,9 +179,10 @@ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, } #endif mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); } } - return 0; + return max_bitflips; } /**************************************************************************** @@ -188,10 +191,11 @@ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer +* @oob_required: must write chip->oob_poi to OOB * ***************************************************************************/ static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) + struct nand_chip *chip, const uint8_t *buf, int oob_required) { int sectorIdx = 0; int eccsize = chip->ecc.size; diff --git a/drivers/mtd/nand/bcm_umi_nand.c b/drivers/mtd/nand/bcm_umi_nand.c index 6908cdd..c855e7c 100644 --- a/drivers/mtd/nand/bcm_umi_nand.c +++ b/drivers/mtd/nand/bcm_umi_nand.c @@ -341,7 +341,7 @@ static int bcm_umi_nand_verify_buf(struct mtd_info *mtd, const u_char * buf, * for MLC parts which may have permanently stuck bits. */ struct nand_chip *chip = mtd->priv; - int ret = chip->ecc.read_page(mtd, chip, readbackbuf, 0); + int ret = chip->ecc.read_page(mtd, chip, readbackbuf, 0, 0); if (ret < 0) return -EFAULT; else { @@ -476,12 +476,7 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev) this->badblock_pattern = &largepage_bbt; } - /* - * FIXME: ecc strength value of 6 bits per 512 bytes of data is a - * conservative guess, given 13 ecc bytes and using bch alg. - * (Assume Galois field order m=15 to allow a margin of error.) - */ - this->ecc.strength = 6; + this->ecc.strength = 8; #endif diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index d7b86b9..3f1c185 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -558,7 +558,7 @@ static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd, } static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { bf5xx_nand_read_buf(mtd, buf, mtd->writesize); bf5xx_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -567,7 +567,7 @@ static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip } static void bf5xx_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { bf5xx_nand_write_buf(mtd, buf, mtd->writesize); bf5xx_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 2a96e1a..41371ba 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -364,25 +364,27 @@ static int cafe_nand_write_oob(struct mtd_info *mtd, /* Don't use -- use nand_read_oob_std for now */ static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return 1; + return 0; } /** * cafe_nand_read_page_syndrome - [REPLACEABLE] hardware ecc syndrome based page read * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller expects OOB data read to chip->oob_poi * * The hw generator calculates the error syndrome automatically. Therefor * we need a special oob layout and handling. */ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { struct cafe_priv *cafe = mtd->priv; + unsigned int max_bitflips = 0; cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", cafe_readl(cafe, NAND_ECC_RESULT), @@ -449,10 +451,11 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, } else { dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", n); mtd->ecc_stats.corrected += n; + max_bitflips = max_t(unsigned int, max_bitflips, n); } } - return 0; + return max_bitflips; } static struct nand_ecclayout cafe_oobinfo_2048 = { @@ -518,7 +521,8 @@ static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = { static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) + struct nand_chip *chip, + const uint8_t *buf, int oob_required) { struct cafe_priv *cafe = mtd->priv; @@ -530,16 +534,17 @@ static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, } static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int page, int cached, int raw) + const uint8_t *buf, int oob_required, int page, + int cached, int raw) { int status; chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); if (unlikely(raw)) - chip->ecc.write_page_raw(mtd, chip, buf); + chip->ecc.write_page_raw(mtd, chip, buf, oob_required); else - chip->ecc.write_page(mtd, chip, buf); + chip->ecc.write_page(mtd, chip, buf, oob_required); /* * Cached progamming disabled for now, Not sure if its worth the @@ -685,7 +690,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, /* Enable the following for a flash based bad block table */ cafe->nand.bbt_options = NAND_BBT_USE_FLASH; - cafe->nand.options = NAND_NO_AUTOINCR | NAND_OWN_BUFFERS; + cafe->nand.options = NAND_OWN_BUFFERS; if (skipbbt) { cafe->nand.options |= NAND_SKIP_BBTSCAN; @@ -888,17 +893,7 @@ static struct pci_driver cafe_nand_pci_driver = { .resume = cafe_nand_resume, }; -static int __init cafe_nand_init(void) -{ - return pci_register_driver(&cafe_nand_pci_driver); -} - -static void __exit cafe_nand_exit(void) -{ - pci_unregister_driver(&cafe_nand_pci_driver); -} -module_init(cafe_nand_init); -module_exit(cafe_nand_exit); +module_pci_driver(cafe_nand_pci_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index 821c34c..adb6c3e 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -240,7 +240,6 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) /* Enable the following for a flash based bad block table */ this->bbt_options = NAND_BBT_USE_FLASH; - this->options = NAND_NO_AUTOINCR; /* Scan to find existence of the device */ if (nand_scan(new_mtd, 1)) { diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index a9e57d6..0650aaf 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -924,9 +924,10 @@ bool is_erased(uint8_t *buf, int len) #define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO) static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, - uint32_t irq_status) + uint32_t irq_status, unsigned int *max_bitflips) { bool check_erased_page = false; + unsigned int bitflips = 0; if (irq_status & INTR_STATUS__ECC_ERR) { /* read the ECC errors. we'll ignore them for now */ @@ -965,6 +966,7 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, /* correct the ECC error */ buf[offset] ^= err_correction_value; denali->mtd.ecc_stats.corrected++; + bitflips++; } } else { /* if the error is not correctable, need to @@ -984,6 +986,7 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, clear_interrupts(denali); denali_set_intr_modes(denali, true); } + *max_bitflips = bitflips; return check_erased_page; } @@ -1084,7 +1087,7 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *chip, * by write_page above. * */ static void denali_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { /* for regular page writes, we let HW handle all the ECC * data written to the device. */ @@ -1096,7 +1099,7 @@ static void denali_write_page(struct mtd_info *mtd, struct nand_chip *chip, * write_page() function above. */ static void denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { /* for raw page writes, we want to disable ECC and simply write whatever data is in the buffer. */ @@ -1110,17 +1113,17 @@ static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip, } static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { read_oob_data(mtd, chip->oob_poi, page); - return 0; /* notify NAND core to send command to - NAND device. */ + return 0; } static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { + unsigned int max_bitflips; struct denali_nand_info *denali = mtd_to_denali(mtd); dma_addr_t addr = denali->buf.dma_buf; @@ -1153,7 +1156,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, memcpy(buf, denali->buf.buf, mtd->writesize); - check_erased_page = handle_ecc(denali, buf, irq_status); + check_erased_page = handle_ecc(denali, buf, irq_status, &max_bitflips); denali_enable_dma(denali, false); if (check_erased_page) { @@ -1167,11 +1170,11 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, denali->mtd.ecc_stats.failed++; } } - return 0; + return max_bitflips; } static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { struct denali_nand_info *denali = mtd_to_denali(mtd); @@ -1702,17 +1705,4 @@ static struct pci_driver denali_pci_driver = { .remove = denali_pci_remove, }; -static int __devinit denali_init(void) -{ - printk(KERN_INFO "Spectra MTD driver\n"); - return pci_register_driver(&denali_pci_driver); -} - -/* Free memory */ -static void __devexit denali_exit(void) -{ - pci_unregister_driver(&denali_pci_driver); -} - -module_init(denali_init); -module_exit(denali_exit); +module_pci_driver(denali_pci_driver); diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index b082026..a225e49 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -720,6 +720,7 @@ static int read_page(struct mtd_info *mtd, struct nand_chip *nand, struct docg4_priv *doc = nand->priv; void __iomem *docptr = doc->virtadr; uint16_t status, edc_err, *buf16; + int bits_corrected = 0; dev_dbg(doc->dev, "%s: page %08x\n", __func__, page); @@ -772,7 +773,7 @@ static int read_page(struct mtd_info *mtd, struct nand_chip *nand, /* If bitflips are reported, attempt to correct with ecc */ if (edc_err & DOC_ECCCONF1_BCH_SYNDROM_ERR) { - int bits_corrected = correct_data(mtd, buf, page); + bits_corrected = correct_data(mtd, buf, page); if (bits_corrected == -EBADMSG) mtd->ecc_stats.failed++; else @@ -781,24 +782,24 @@ static int read_page(struct mtd_info *mtd, struct nand_chip *nand, } writew(0, docptr + DOC_DATAEND); - return 0; + return bits_corrected; } static int docg4_read_page_raw(struct mtd_info *mtd, struct nand_chip *nand, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { return read_page(mtd, nand, buf, page, false); } static int docg4_read_page(struct mtd_info *mtd, struct nand_chip *nand, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { return read_page(mtd, nand, buf, page, true); } static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand, - int page, int sndcmd) + int page) { struct docg4_priv *doc = nand->priv; void __iomem *docptr = doc->virtadr; @@ -952,13 +953,13 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *nand, } static void docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { return write_page(mtd, nand, buf, false); } static void docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { return write_page(mtd, nand, buf, true); } @@ -1002,7 +1003,7 @@ static int __init read_factory_bbt(struct mtd_info *mtd) return -ENOMEM; read_page_prologue(mtd, g4_addr); - status = docg4_read_page(mtd, nand, buf, DOCG4_FACTORY_BBT_PAGE); + status = docg4_read_page(mtd, nand, buf, 0, DOCG4_FACTORY_BBT_PAGE); if (status) goto exit; @@ -1079,7 +1080,7 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) /* write first page of block */ write_page_prologue(mtd, g4_addr); - docg4_write_page(mtd, nand, buf); + docg4_write_page(mtd, nand, buf, 1); ret = pageprog(mtd); if (!ret) mtd->ecc_stats.badblocks++; @@ -1192,8 +1193,7 @@ static void __init init_mtd_structs(struct mtd_info *mtd) nand->ecc.prepad = 8; nand->ecc.bytes = 8; nand->ecc.strength = DOCG4_T; - nand->options = - NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE | NAND_NO_AUTOINCR; + nand->options = NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE; nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA; nand->controller = &nand->hwcontrol; spin_lock_init(&nand->controller->lock); diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 80b5264..7842938 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -75,6 +75,7 @@ struct fsl_elbc_fcm_ctrl { unsigned int use_mdr; /* Non zero if the MDR is to be set */ unsigned int oob; /* Non zero if operating on OOB data */ unsigned int counter; /* counter for the initializations */ + unsigned int max_bitflips; /* Saved during READ0 cmd */ }; /* These map to the positions used by the FCM hardware ECC generator */ @@ -253,6 +254,8 @@ static int fsl_elbc_run_command(struct mtd_info *mtd) if (chip->ecc.mode != NAND_ECC_HW) return 0; + elbc_fcm_ctrl->max_bitflips = 0; + if (elbc_fcm_ctrl->read_bytes == mtd->writesize + mtd->oobsize) { uint32_t lteccr = in_be32(&lbc->lteccr); /* @@ -262,11 +265,16 @@ static int fsl_elbc_run_command(struct mtd_info *mtd) * bits 28-31 are uncorrectable errors, marked elsewhere. * for small page nand only 1 bit is used. * if the ELBC doesn't have the lteccr register it reads 0 + * FIXME: 4 bits can be corrected on NANDs with 2k pages, so + * count the number of sub-pages with bitflips and update + * ecc_stats.corrected accordingly. */ if (lteccr & 0x000F000F) out_be32(&lbc->lteccr, 0x000F000F); /* clear lteccr */ - if (lteccr & 0x000F0000) + if (lteccr & 0x000F0000) { mtd->ecc_stats.corrected++; + elbc_fcm_ctrl->max_bitflips = 1; + } } return 0; @@ -738,26 +746,28 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) return 0; } -static int fsl_elbc_read_page(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, - int page) +static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) { + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_lbc_ctrl *ctrl = priv->ctrl; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; + fsl_elbc_read_buf(mtd, buf, mtd->writesize); - fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + if (oob_required) + fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); if (fsl_elbc_wait(mtd, chip) & NAND_STATUS_FAIL) mtd->ecc_stats.failed++; - return 0; + return elbc_fcm_ctrl->max_bitflips; } /* ECC will be calculated automatically, and errors will be detected in * waitfunc. */ -static void fsl_elbc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf) +static void fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) { fsl_elbc_write_buf(mtd, buf, mtd->writesize); fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -795,7 +805,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) chip->bbt_md = &bbt_mirror_descr; /* set up nand options */ - chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR; + chip->options = NAND_NO_READRDY; chip->bbt_options = NAND_BBT_USE_FLASH; chip->controller = &elbc_fcm_ctrl->controller; @@ -814,11 +824,6 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) chip->ecc.size = 512; chip->ecc.bytes = 3; chip->ecc.strength = 1; - /* - * FIXME: can hardware ecc correct 4 bitflips if page size is - * 2k? Then does hardware report number of corrections for this - * case? If so, ecc_stats reporting needs to be fixed as well. - */ } else { /* otherwise fall back to default software ECC */ chip->ecc.mode = NAND_ECC_SOFT; diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index c30ac7b..9602c1b 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -63,6 +63,7 @@ struct fsl_ifc_nand_ctrl { unsigned int oob; /* Non zero if operating on OOB data */ unsigned int eccread; /* Non zero for a full-page ECC read */ unsigned int counter; /* counter for the initializations */ + unsigned int max_bitflips; /* Saved during READ0 cmd */ }; static struct fsl_ifc_nand_ctrl *ifc_nand_ctrl; @@ -262,6 +263,8 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_WPER) dev_err(priv->dev, "NAND Flash Write Protect Error\n"); + nctrl->max_bitflips = 0; + if (nctrl->eccread) { int errors; int bufnum = nctrl->page & priv->bufnum_mask; @@ -290,6 +293,9 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) } mtd->ecc_stats.corrected += errors; + nctrl->max_bitflips = max_t(unsigned int, + nctrl->max_bitflips, + errors); } nctrl->eccread = 0; @@ -375,21 +381,31 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, return; - /* READID must read all 8 possible bytes */ case NAND_CMD_READID: + case NAND_CMD_PARAM: { + int timing = IFC_FIR_OP_RB; + if (command == NAND_CMD_PARAM) + timing = IFC_FIR_OP_RBCD; + out_be32(&ifc->ifc_nand.nand_fir0, (IFC_FIR_OP_CMD0 << IFC_NAND_FIR0_OP0_SHIFT) | (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT)); + (timing << IFC_NAND_FIR0_OP2_SHIFT)); out_be32(&ifc->ifc_nand.nand_fcr0, - NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT); - /* 8 bytes for manuf, device and exts */ - out_be32(&ifc->ifc_nand.nand_fbcr, 8); - ifc_nand_ctrl->read_bytes = 8; + command << IFC_NAND_FCR0_CMD0_SHIFT); + out_be32(&ifc->ifc_nand.row3, column); + + /* + * although currently it's 8 bytes for READID, we always read + * the maximum 256 bytes(for PARAM) + */ + out_be32(&ifc->ifc_nand.nand_fbcr, 256); + ifc_nand_ctrl->read_bytes = 256; set_addr(mtd, 0, 0, 0); fsl_ifc_run_command(mtd); return; + } /* ERASE1 stores the block and page address */ case NAND_CMD_ERASE1: @@ -682,15 +698,16 @@ static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) return nand_fsr | NAND_STATUS_WP; } -static int fsl_ifc_read_page(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, int page) +static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) { struct fsl_ifc_mtd *priv = chip->priv; struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; fsl_ifc_read_buf(mtd, buf, mtd->writesize); - fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + if (oob_required) + fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize); if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_ECCER) dev_err(priv->dev, "NAND Flash ECC Uncorrectable Error\n"); @@ -698,15 +715,14 @@ static int fsl_ifc_read_page(struct mtd_info *mtd, if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) mtd->ecc_stats.failed++; - return 0; + return nctrl->max_bitflips; } /* ECC will be calculated automatically, and errors will be detected in * waitfunc. */ -static void fsl_ifc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf) +static void fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) { fsl_ifc_write_buf(mtd, buf, mtd->writesize); fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -789,7 +805,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) out_be32(&ifc->ifc_nand.ncfgr, 0x0); /* set up nand options */ - chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR; + chip->options = NAND_NO_READRDY; chip->bbt_options = NAND_BBT_USE_FLASH; @@ -811,6 +827,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) /* Hardware generates ECC per 512 Bytes */ chip->ecc.size = 512; chip->ecc.bytes = 8; + chip->ecc.strength = 4; switch (csor & CSOR_NAND_PGS_MASK) { case CSOR_NAND_PGS_512: diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 1b8330e..38d2624 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -692,6 +692,7 @@ static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller expects OOB data read to chip->oob_poi * @page: page number to read * * This routine is needed for fsmc version 8 as reading from NAND chip has to be @@ -701,7 +702,7 @@ static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf, * max of 8 bits) */ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); @@ -720,6 +721,7 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, */ uint16_t ecc_oob[7]; uint8_t *oob = (uint8_t *)&ecc_oob[0]; + unsigned int max_bitflips = 0; for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) { chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page); @@ -748,13 +750,15 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, chip->ecc.calculate(mtd, p, &ecc_calc[i]); stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /* @@ -994,9 +998,9 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) return PTR_ERR(host->clk); } - ret = clk_enable(host->clk); + ret = clk_prepare_enable(host->clk); if (ret) - goto err_clk_enable; + goto err_clk_prepare_enable; /* * This device ID is actually a common AMBA ID as used on the @@ -1176,8 +1180,8 @@ err_req_write_chnl: if (host->mode == USE_DMA_ACCESS) dma_release_channel(host->read_dma_chan); err_req_read_chnl: - clk_disable(host->clk); -err_clk_enable: + clk_disable_unprepare(host->clk); +err_clk_prepare_enable: clk_put(host->clk); return ret; } @@ -1198,7 +1202,7 @@ static int fsmc_nand_remove(struct platform_device *pdev) dma_release_channel(host->write_dma_chan); dma_release_channel(host->read_dma_chan); } - clk_disable(host->clk); + clk_disable_unprepare(host->clk); clk_put(host->clk); } @@ -1210,7 +1214,7 @@ static int fsmc_nand_suspend(struct device *dev) { struct fsmc_nand_data *host = dev_get_drvdata(dev); if (host) - clk_disable(host->clk); + clk_disable_unprepare(host->clk); return 0; } @@ -1218,7 +1222,7 @@ static int fsmc_nand_resume(struct device *dev) { struct fsmc_nand_data *host = dev_get_drvdata(dev); if (host) { - clk_enable(host->clk); + clk_prepare_enable(host->clk); fsmc_nand_setup(host->regs_va, host->bank, host->nand.options & NAND_BUSWIDTH_16, host->dev_timings); diff --git a/drivers/mtd/nand/gpmi-nand/bch-regs.h b/drivers/mtd/nand/gpmi-nand/bch-regs.h index 4effb8c..a092451 100644 --- a/drivers/mtd/nand/gpmi-nand/bch-regs.h +++ b/drivers/mtd/nand/gpmi-nand/bch-regs.h @@ -51,15 +51,26 @@ #define BP_BCH_FLASH0LAYOUT0_ECC0 12 #define BM_BCH_FLASH0LAYOUT0_ECC0 (0xf << BP_BCH_FLASH0LAYOUT0_ECC0) -#define BF_BCH_FLASH0LAYOUT0_ECC0(v) \ - (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) & BM_BCH_FLASH0LAYOUT0_ECC0) +#define MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0 11 +#define MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0 (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) +#define BF_BCH_FLASH0LAYOUT0_ECC0(v, x) \ + (GPMI_IS_MX6Q(x) \ + ? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) \ + & MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0) \ + : (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) \ + & BM_BCH_FLASH0LAYOUT0_ECC0) \ + ) #define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0 #define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ (0xfff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) -#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v) \ - (((v) << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)\ - & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) +#define MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ + (0x3ff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) +#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v, x) \ + (GPMI_IS_MX6Q(x) \ + ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \ + : ((v) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \ + ) #define HW_BCH_FLASH0LAYOUT1 0x00000090 @@ -72,13 +83,24 @@ #define BP_BCH_FLASH0LAYOUT1_ECCN 12 #define BM_BCH_FLASH0LAYOUT1_ECCN (0xf << BP_BCH_FLASH0LAYOUT1_ECCN) -#define BF_BCH_FLASH0LAYOUT1_ECCN(v) \ - (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) & BM_BCH_FLASH0LAYOUT1_ECCN) +#define MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN 11 +#define MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) +#define BF_BCH_FLASH0LAYOUT1_ECCN(v, x) \ + (GPMI_IS_MX6Q(x) \ + ? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) \ + & MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN) \ + : (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) \ + & BM_BCH_FLASH0LAYOUT1_ECCN) \ + ) #define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0 #define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ (0xfff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) -#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v) \ - (((v) << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ - & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) +#define MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ + (0x3ff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) +#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v, x) \ + (GPMI_IS_MX6Q(x) \ + ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ + : ((v) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ + ) #endif diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index e8ea710..a1f4332 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -21,7 +21,6 @@ #include <linux/mtd/gpmi-nand.h> #include <linux/delay.h> #include <linux/clk.h> -#include <mach/mxs.h> #include "gpmi-nand.h" #include "gpmi-regs.h" @@ -37,6 +36,8 @@ struct timing_threshod timing_default_threshold = { .max_dll_delay_in_ns = 16, }; +#define MXS_SET_ADDR 0x4 +#define MXS_CLR_ADDR 0x8 /* * Clear the bit and poll it cleared. This is usually called with * a reset address and mask being either SFTRST(bit 31) or CLKGATE @@ -47,7 +48,7 @@ static int clear_poll_bit(void __iomem *addr, u32 mask) int timeout = 0x400; /* clear the bit */ - __mxs_clrl(mask, addr); + writel(mask, addr + MXS_CLR_ADDR); /* * SFTRST needs 3 GPMI clocks to settle, the reference manual @@ -92,11 +93,11 @@ static int gpmi_reset_block(void __iomem *reset_addr, bool just_enable) goto error; /* clear CLKGATE */ - __mxs_clrl(MODULE_CLKGATE, reset_addr); + writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR); if (!just_enable) { /* set SFTRST to reset the block */ - __mxs_setl(MODULE_SFTRST, reset_addr); + writel(MODULE_SFTRST, reset_addr + MXS_SET_ADDR); udelay(1); /* poll CLKGATE becoming set */ @@ -223,13 +224,13 @@ int bch_set_geometry(struct gpmi_nand_data *this) /* Configure layout 0. */ writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) | BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) - | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength) - | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size), + | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) + | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this), r->bch_regs + HW_BCH_FLASH0LAYOUT0); writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) - | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength) - | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size), + | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) + | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this), r->bch_regs + HW_BCH_FLASH0LAYOUT1); /* Set *all* chip selects to use layout 0. */ @@ -255,11 +256,12 @@ static unsigned int ns_to_cycles(unsigned int time, return max(k, min); } +#define DEF_MIN_PROP_DELAY 5 +#define DEF_MAX_PROP_DELAY 9 /* Apply timing to current hardware conditions. */ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this, struct gpmi_nfc_hardware_timing *hw) { - struct gpmi_nand_platform_data *pdata = this->pdata; struct timing_threshod *nfc = &timing_default_threshold; struct nand_chip *nand = &this->nand; struct nand_timing target = this->timing; @@ -276,8 +278,8 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this, int ideal_sample_delay_in_ns; unsigned int sample_delay_factor; int tEYE; - unsigned int min_prop_delay_in_ns = pdata->min_prop_delay_in_ns; - unsigned int max_prop_delay_in_ns = pdata->max_prop_delay_in_ns; + unsigned int min_prop_delay_in_ns = DEF_MIN_PROP_DELAY; + unsigned int max_prop_delay_in_ns = DEF_MAX_PROP_DELAY; /* * If there are multiple chips, we need to relax the timings to allow @@ -803,7 +805,8 @@ int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip) if (GPMI_IS_MX23(this)) { mask = MX23_BM_GPMI_DEBUG_READY0 << chip; reg = readl(r->gpmi_regs + HW_GPMI_DEBUG); - } else if (GPMI_IS_MX28(this)) { + } else if (GPMI_IS_MX28(this) || GPMI_IS_MX6Q(this)) { + /* MX28 shares the same R/B register as MX6Q. */ mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip); reg = readl(r->gpmi_regs + HW_GPMI_STAT); } else diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index b68e043..a05b7b4 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -25,6 +25,8 @@ #include <linux/mtd/gpmi-nand.h> #include <linux/mtd/partitions.h> #include <linux/pinctrl/consumer.h> +#include <linux/of.h> +#include <linux/of_device.h> #include "gpmi-nand.h" /* add our owner bbt descriptor */ @@ -387,7 +389,7 @@ static void release_bch_irq(struct gpmi_nand_data *this) static bool gpmi_dma_filter(struct dma_chan *chan, void *param) { struct gpmi_nand_data *this = param; - struct resource *r = this->private; + int dma_channel = (int)this->private; if (!mxs_dma_is_apbh(chan)) return false; @@ -399,7 +401,7 @@ static bool gpmi_dma_filter(struct dma_chan *chan, void *param) * for mx28 : MX28_DMA_GPMI0 ~ MX28_DMA_GPMI7 * (These eight channels share the same IRQ!) */ - if (r->start <= chan->chan_id && chan->chan_id <= r->end) { + if (dma_channel == chan->chan_id) { chan->private = &this->dma_data; return true; } @@ -419,57 +421,45 @@ static void release_dma_channels(struct gpmi_nand_data *this) static int __devinit acquire_dma_channels(struct gpmi_nand_data *this) { struct platform_device *pdev = this->pdev; - struct gpmi_nand_platform_data *pdata = this->pdata; - struct resources *res = &this->resources; - struct resource *r, *r_dma; - unsigned int i; + struct resource *r_dma; + struct device_node *dn; + int dma_channel; + unsigned int ret; + struct dma_chan *dma_chan; + dma_cap_mask_t mask; + + /* dma channel, we only use the first one. */ + dn = pdev->dev.of_node; + ret = of_property_read_u32(dn, "fsl,gpmi-dma-channel", &dma_channel); + if (ret) { + pr_err("unable to get DMA channel from dt.\n"); + goto acquire_err; + } + this->private = (void *)dma_channel; - r = platform_get_resource_byname(pdev, IORESOURCE_DMA, - GPMI_NAND_DMA_CHANNELS_RES_NAME); + /* gpmi dma interrupt */ r_dma = platform_get_resource_byname(pdev, IORESOURCE_IRQ, GPMI_NAND_DMA_INTERRUPT_RES_NAME); - if (!r || !r_dma) { + if (!r_dma) { pr_err("Can't get resource for DMA\n"); - return -ENXIO; + goto acquire_err; } + this->dma_data.chan_irq = r_dma->start; - /* used in gpmi_dma_filter() */ - this->private = r; - - for (i = r->start; i <= r->end; i++) { - struct dma_chan *dma_chan; - dma_cap_mask_t mask; + /* request dma channel */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); - if (i - r->start >= pdata->max_chip_count) - break; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - /* get the DMA interrupt */ - if (r_dma->start == r_dma->end) { - /* only register the first. */ - if (i == r->start) - this->dma_data.chan_irq = r_dma->start; - else - this->dma_data.chan_irq = NO_IRQ; - } else - this->dma_data.chan_irq = r_dma->start + (i - r->start); - - dma_chan = dma_request_channel(mask, gpmi_dma_filter, this); - if (!dma_chan) - goto acquire_err; - - /* fill the first empty item */ - this->dma_chans[i - r->start] = dma_chan; + dma_chan = dma_request_channel(mask, gpmi_dma_filter, this); + if (!dma_chan) { + pr_err("dma_request_channel failed.\n"); + goto acquire_err; } - res->dma_low_channel = r->start; - res->dma_high_channel = i; + this->dma_chans[0] = dma_chan; return 0; acquire_err: - pr_err("Can't acquire DMA channel %u\n", i); release_dma_channels(this); return -EINVAL; } @@ -851,7 +841,7 @@ static void block_mark_swapping(struct gpmi_nand_data *this, } static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { struct gpmi_nand_data *this = chip->priv; struct bch_geometry *nfc_geo = &this->bch_geometry; @@ -917,28 +907,31 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, mtd->ecc_stats.corrected += corrected; } - /* - * It's time to deliver the OOB bytes. See gpmi_ecc_read_oob() for - * details about our policy for delivering the OOB. - * - * We fill the caller's buffer with set bits, and then copy the block - * mark to th caller's buffer. Note that, if block mark swapping was - * necessary, it has already been done, so we can rely on the first - * byte of the auxiliary buffer to contain the block mark. - */ - memset(chip->oob_poi, ~0, mtd->oobsize); - chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0]; + if (oob_required) { + /* + * It's time to deliver the OOB bytes. See gpmi_ecc_read_oob() + * for details about our policy for delivering the OOB. + * + * We fill the caller's buffer with set bits, and then copy the + * block mark to th caller's buffer. Note that, if block mark + * swapping was necessary, it has already been done, so we can + * rely on the first byte of the auxiliary buffer to contain + * the block mark. + */ + memset(chip->oob_poi, ~0, mtd->oobsize); + chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0]; - read_page_swap_end(this, buf, mtd->writesize, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - payload_virt, payload_phys); + read_page_swap_end(this, buf, mtd->writesize, + this->payload_virt, this->payload_phys, + nfc_geo->payload_size, + payload_virt, payload_phys); + } exit_nfc: return ret; } -static void gpmi_ecc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) +static void gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) { struct gpmi_nand_data *this = chip->priv; struct bch_geometry *nfc_geo = &this->bch_geometry; @@ -1077,7 +1070,7 @@ exit_auxiliary: * this driver. */ static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { struct gpmi_nand_data *this = chip->priv; @@ -1100,11 +1093,7 @@ static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, chip->oob_poi[0] = chip->read_byte(mtd); } - /* - * Return true, indicating that the next call to this function must send - * a command. - */ - return true; + return 0; } static int @@ -1318,7 +1307,7 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) /* Write the first page of the current stride. */ dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page); chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); - chip->ecc.write_page_raw(mtd, chip, buffer); + chip->ecc.write_page_raw(mtd, chip, buffer, 0); chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); /* Wait for the write to finish. */ @@ -1444,6 +1433,10 @@ static int gpmi_pre_bbt_scan(struct gpmi_nand_data *this) if (ret) return ret; + /* Adjust the ECC strength according to the chip. */ + this->nand.ecc.strength = this->bch_geometry.ecc_strength; + this->mtd.ecc_strength = this->bch_geometry.ecc_strength; + /* NAND boot init, depends on the gpmi_set_geometry(). */ return nand_boot_init(this); } @@ -1471,9 +1464,9 @@ void gpmi_nfc_exit(struct gpmi_nand_data *this) static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this) { - struct gpmi_nand_platform_data *pdata = this->pdata; struct mtd_info *mtd = &this->mtd; struct nand_chip *chip = &this->nand; + struct mtd_part_parser_data ppdata = {}; int ret; /* init current chip */ @@ -1502,6 +1495,7 @@ static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this) chip->options |= NAND_NO_SUBPAGE_WRITE; chip->ecc.mode = NAND_ECC_HW; chip->ecc.size = 1; + chip->ecc.strength = 8; chip->ecc.layout = &gpmi_hw_ecclayout; /* Allocate a temporary DMA buffer for reading ID in the nand_scan() */ @@ -1511,14 +1505,14 @@ static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this) if (ret) goto err_out; - ret = nand_scan(mtd, pdata->max_chip_count); + ret = nand_scan(mtd, 1); if (ret) { pr_err("Chip scan failed\n"); goto err_out; } - ret = mtd_device_parse_register(mtd, NULL, NULL, - pdata->partitions, pdata->partition_count); + ppdata.of_node = this->pdev->dev.of_node; + ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); if (ret) goto err_out; return 0; @@ -1528,12 +1522,41 @@ err_out: return ret; } +static const struct platform_device_id gpmi_ids[] = { + { .name = "imx23-gpmi-nand", .driver_data = IS_MX23, }, + { .name = "imx28-gpmi-nand", .driver_data = IS_MX28, }, + { .name = "imx6q-gpmi-nand", .driver_data = IS_MX6Q, }, + {}, +}; + +static const struct of_device_id gpmi_nand_id_table[] = { + { + .compatible = "fsl,imx23-gpmi-nand", + .data = (void *)&gpmi_ids[IS_MX23] + }, { + .compatible = "fsl,imx28-gpmi-nand", + .data = (void *)&gpmi_ids[IS_MX28] + }, { + .compatible = "fsl,imx6q-gpmi-nand", + .data = (void *)&gpmi_ids[IS_MX6Q] + }, {} +}; +MODULE_DEVICE_TABLE(of, gpmi_nand_id_table); + static int __devinit gpmi_nand_probe(struct platform_device *pdev) { - struct gpmi_nand_platform_data *pdata = pdev->dev.platform_data; struct gpmi_nand_data *this; + const struct of_device_id *of_id; int ret; + of_id = of_match_device(gpmi_nand_id_table, &pdev->dev); + if (of_id) { + pdev->id_entry = of_id->data; + } else { + pr_err("Failed to find the right device id.\n"); + return -ENOMEM; + } + this = kzalloc(sizeof(*this), GFP_KERNEL); if (!this) { pr_err("Failed to allocate per-device memory\n"); @@ -1543,13 +1566,6 @@ static int __devinit gpmi_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, this); this->pdev = pdev; this->dev = &pdev->dev; - this->pdata = pdata; - - if (pdata->platform_init) { - ret = pdata->platform_init(); - if (ret) - goto platform_init_error; - } ret = acquire_resources(this); if (ret) @@ -1567,7 +1583,6 @@ static int __devinit gpmi_nand_probe(struct platform_device *pdev) exit_nfc_init: release_resources(this); -platform_init_error: exit_acquire_resources: platform_set_drvdata(pdev, NULL); kfree(this); @@ -1585,19 +1600,10 @@ static int __exit gpmi_nand_remove(struct platform_device *pdev) return 0; } -static const struct platform_device_id gpmi_ids[] = { - { - .name = "imx23-gpmi-nand", - .driver_data = IS_MX23, - }, { - .name = "imx28-gpmi-nand", - .driver_data = IS_MX28, - }, {}, -}; - static struct platform_driver gpmi_nand_driver = { .driver = { .name = "gpmi-nand", + .of_match_table = gpmi_nand_id_table, }, .probe = gpmi_nand_probe, .remove = __exit_p(gpmi_nand_remove), diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h index ec6180d..ce5daa1 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h @@ -266,8 +266,10 @@ extern int gpmi_read_page(struct gpmi_nand_data *, #define STATUS_UNCORRECTABLE 0xfe /* Use the platform_id to distinguish different Archs. */ -#define IS_MX23 0x1 -#define IS_MX28 0x2 +#define IS_MX23 0x0 +#define IS_MX28 0x1 +#define IS_MX6Q 0x2 #define GPMI_IS_MX23(x) ((x)->pdev->id_entry->driver_data == IS_MX23) #define GPMI_IS_MX28(x) ((x)->pdev->id_entry->driver_data == IS_MX28) +#define GPMI_IS_MX6Q(x) ((x)->pdev->id_entry->driver_data == IS_MX6Q) #endif diff --git a/drivers/mtd/nand/h1910.c b/drivers/mtd/nand/h1910.c index 9bf5ce5..50166e9 100644 --- a/drivers/mtd/nand/h1910.c +++ b/drivers/mtd/nand/h1910.c @@ -124,7 +124,6 @@ static int __init h1910_init(void) /* 15 us command delay time */ this->chip_delay = 50; this->ecc.mode = NAND_ECC_SOFT; - this->options = NAND_NO_AUTOINCR; /* Scan to find existence of the device */ if (nand_scan(h1910_nand_mtd, 1)) { diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index e4147e8..a6fa884 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -332,11 +332,7 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) chip->ecc.mode = NAND_ECC_HW_OOB_FIRST; chip->ecc.size = 512; chip->ecc.bytes = 9; - chip->ecc.strength = 2; - /* - * FIXME: ecc_strength value of 2 bits per 512 bytes of data is a - * conservative guess, given 9 ecc bytes and reed-solomon alg. - */ + chip->ecc.strength = 4; if (pdata) chip->ecc.layout = pdata->ecc_layout; diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index c240cf1..c259c24 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -734,7 +734,6 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op) chip->write_buf = mpc5121_nfc_write_buf; chip->verify_buf = mpc5121_nfc_verify_buf; chip->select_chip = mpc5121_nfc_select_chip; - chip->options = NAND_NO_AUTOINCR; chip->bbt_options = NAND_BBT_USE_FLASH; chip->ecc.mode = NAND_ECC_SOFT; diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 9e374e9..c58e6a9 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -32,6 +32,8 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/completion.h> +#include <linux/of_device.h> +#include <linux/of_mtd.h> #include <asm/mach/flash.h> #include <mach/mxc_nand.h> @@ -140,13 +142,47 @@ #define NFC_V3_DELAY_LINE (host->regs_ip + 0x34) +struct mxc_nand_host; + +struct mxc_nand_devtype_data { + void (*preset)(struct mtd_info *); + void (*send_cmd)(struct mxc_nand_host *, uint16_t, int); + void (*send_addr)(struct mxc_nand_host *, uint16_t, int); + void (*send_page)(struct mtd_info *, unsigned int); + void (*send_read_id)(struct mxc_nand_host *); + uint16_t (*get_dev_status)(struct mxc_nand_host *); + int (*check_int)(struct mxc_nand_host *); + void (*irq_control)(struct mxc_nand_host *, int); + u32 (*get_ecc_status)(struct mxc_nand_host *); + struct nand_ecclayout *ecclayout_512, *ecclayout_2k, *ecclayout_4k; + void (*select_chip)(struct mtd_info *mtd, int chip); + int (*correct_data)(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc); + + /* + * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked + * (CONFIG1:INT_MSK is set). To handle this the driver uses + * enable_irq/disable_irq_nosync instead of CONFIG1:INT_MSK + */ + int irqpending_quirk; + int needs_ip; + + size_t regs_offset; + size_t spare0_offset; + size_t axi_offset; + + int spare_len; + int eccbytes; + int eccsize; +}; + struct mxc_nand_host { struct mtd_info mtd; struct nand_chip nand; struct device *dev; - void *spare0; - void *main_area0; + void __iomem *spare0; + void __iomem *main_area0; void __iomem *base; void __iomem *regs; @@ -163,16 +199,9 @@ struct mxc_nand_host { uint8_t *data_buf; unsigned int buf_start; - int spare_len; - - void (*preset)(struct mtd_info *); - void (*send_cmd)(struct mxc_nand_host *, uint16_t, int); - void (*send_addr)(struct mxc_nand_host *, uint16_t, int); - void (*send_page)(struct mtd_info *, unsigned int); - void (*send_read_id)(struct mxc_nand_host *); - uint16_t (*get_dev_status)(struct mxc_nand_host *); - int (*check_int)(struct mxc_nand_host *); - void (*irq_control)(struct mxc_nand_host *, int); + + const struct mxc_nand_devtype_data *devtype_data; + struct mxc_nand_platform_data pdata; }; /* OOB placement block for use with hardware ecc generation */ @@ -242,21 +271,7 @@ static struct nand_ecclayout nandv2_hw_eccoob_4k = { } }; -static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL }; - -static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) -{ - struct mxc_nand_host *host = dev_id; - - if (!host->check_int(host)) - return IRQ_NONE; - - host->irq_control(host, 0); - - complete(&host->op_completion); - - return IRQ_HANDLED; -} +static const char *part_probes[] = { "RedBoot", "cmdlinepart", "ofpart", NULL }; static int check_int_v3(struct mxc_nand_host *host) { @@ -280,26 +295,12 @@ static int check_int_v1_v2(struct mxc_nand_host *host) if (!(tmp & NFC_V1_V2_CONFIG2_INT)) return 0; - if (!cpu_is_mx21()) + if (!host->devtype_data->irqpending_quirk) writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2); return 1; } -/* - * It has been observed that the i.MX21 cannot read the CONFIG2:INT bit - * if interrupts are masked (CONFIG1:INT_MSK is set). To handle this, the - * driver can enable/disable the irq line rather than simply masking the - * interrupts. - */ -static void irq_control_mx21(struct mxc_nand_host *host, int activate) -{ - if (activate) - enable_irq(host->irq); - else - disable_irq_nosync(host->irq); -} - static void irq_control_v1_v2(struct mxc_nand_host *host, int activate) { uint16_t tmp; @@ -328,6 +329,47 @@ static void irq_control_v3(struct mxc_nand_host *host, int activate) writel(tmp, NFC_V3_CONFIG2); } +static void irq_control(struct mxc_nand_host *host, int activate) +{ + if (host->devtype_data->irqpending_quirk) { + if (activate) + enable_irq(host->irq); + else + disable_irq_nosync(host->irq); + } else { + host->devtype_data->irq_control(host, activate); + } +} + +static u32 get_ecc_status_v1(struct mxc_nand_host *host) +{ + return readw(NFC_V1_V2_ECC_STATUS_RESULT); +} + +static u32 get_ecc_status_v2(struct mxc_nand_host *host) +{ + return readl(NFC_V1_V2_ECC_STATUS_RESULT); +} + +static u32 get_ecc_status_v3(struct mxc_nand_host *host) +{ + return readl(NFC_V3_ECC_STATUS_RESULT); +} + +static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) +{ + struct mxc_nand_host *host = dev_id; + + if (!host->devtype_data->check_int(host)) + return IRQ_NONE; + + irq_control(host, 0); + + complete(&host->op_completion); + + return IRQ_HANDLED; +} + /* This function polls the NANDFC to wait for the basic operation to * complete by checking the INT bit of config2 register. */ @@ -336,14 +378,14 @@ static void wait_op_done(struct mxc_nand_host *host, int useirq) int max_retries = 8000; if (useirq) { - if (!host->check_int(host)) { + if (!host->devtype_data->check_int(host)) { INIT_COMPLETION(host->op_completion); - host->irq_control(host, 1); + irq_control(host, 1); wait_for_completion(&host->op_completion); } } else { while (max_retries-- > 0) { - if (host->check_int(host)) + if (host->devtype_data->check_int(host)) break; udelay(1); @@ -374,7 +416,7 @@ static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq) writew(cmd, NFC_V1_V2_FLASH_CMD); writew(NFC_CMD, NFC_V1_V2_CONFIG2); - if (cpu_is_mx21() && (cmd == NAND_CMD_RESET)) { + if (host->devtype_data->irqpending_quirk && (cmd == NAND_CMD_RESET)) { int max_retries = 100; /* Reset completion is indicated by NFC_CONFIG2 */ /* being set to 0 */ @@ -433,13 +475,27 @@ static void send_page_v3(struct mtd_info *mtd, unsigned int ops) wait_op_done(host, false); } -static void send_page_v1_v2(struct mtd_info *mtd, unsigned int ops) +static void send_page_v2(struct mtd_info *mtd, unsigned int ops) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + + /* NANDFC buffer 0 is used for page read/write */ + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); + + writew(ops, NFC_V1_V2_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, true); +} + +static void send_page_v1(struct mtd_info *mtd, unsigned int ops) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; int bufs, i; - if (nfc_is_v1() && mtd->writesize > 512) + if (mtd->writesize > 512) bufs = 4; else bufs = 1; @@ -463,7 +519,7 @@ static void send_read_id_v3(struct mxc_nand_host *host) wait_op_done(host, true); - memcpy(host->data_buf, host->main_area0, 16); + memcpy_fromio(host->data_buf, host->main_area0, 16); } /* Request the NANDFC to perform a read of the NAND device ID. */ @@ -479,7 +535,7 @@ static void send_read_id_v1_v2(struct mxc_nand_host *host) /* Wait for operation to complete */ wait_op_done(host, true); - memcpy(host->data_buf, host->main_area0, 16); + memcpy_fromio(host->data_buf, host->main_area0, 16); if (this->options & NAND_BUSWIDTH_16) { /* compress the ID info */ @@ -555,7 +611,7 @@ static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat, * additional correction. 2-Bit errors cannot be corrected by * HW ECC, so we need to return failure */ - uint16_t ecc_status = readw(NFC_V1_V2_ECC_STATUS_RESULT); + uint16_t ecc_status = get_ecc_status_v1(host); if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) { pr_debug("MXC_NAND: HWECC uncorrectable 2-bit ECC error\n"); @@ -580,10 +636,7 @@ static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat, no_subpages = mtd->writesize >> 9; - if (nfc_is_v21()) - ecc_stat = readl(NFC_V1_V2_ECC_STATUS_RESULT); - else - ecc_stat = readl(NFC_V3_ECC_STATUS_RESULT); + ecc_stat = host->devtype_data->get_ecc_status(host); do { err = ecc_stat & ecc_bit_mask; @@ -616,7 +669,7 @@ static u_char mxc_nand_read_byte(struct mtd_info *mtd) /* Check for status request */ if (host->status_request) - return host->get_dev_status(host) & 0xFF; + return host->devtype_data->get_dev_status(host) & 0xFF; ret = *(uint8_t *)(host->data_buf + host->buf_start); host->buf_start++; @@ -682,7 +735,7 @@ static int mxc_nand_verify_buf(struct mtd_info *mtd, /* This function is used by upper layer for select and * deselect of the NAND chip */ -static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) +static void mxc_nand_select_chip_v1_v3(struct mtd_info *mtd, int chip) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; @@ -701,11 +754,30 @@ static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) clk_prepare_enable(host->clk); host->clk_act = 1; } +} - if (nfc_is_v21()) { - host->active_cs = chip; - writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); +static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + + if (chip == -1) { + /* Disable the NFC clock */ + if (host->clk_act) { + clk_disable(host->clk); + host->clk_act = 0; + } + return; + } + + if (!host->clk_act) { + /* Enable the NFC clock */ + clk_enable(host->clk); + host->clk_act = 1; } + + host->active_cs = chip; + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); } /* @@ -718,23 +790,23 @@ static void copy_spare(struct mtd_info *mtd, bool bfrom) u16 i, j; u16 n = mtd->writesize >> 9; u8 *d = host->data_buf + mtd->writesize; - u8 *s = host->spare0; - u16 t = host->spare_len; + u8 __iomem *s = host->spare0; + u16 t = host->devtype_data->spare_len; j = (mtd->oobsize / n >> 1) << 1; if (bfrom) { for (i = 0; i < n - 1; i++) - memcpy(d + i * j, s + i * t, j); + memcpy_fromio(d + i * j, s + i * t, j); /* the last section */ - memcpy(d + i * j, s + i * t, mtd->oobsize - i * j); + memcpy_fromio(d + i * j, s + i * t, mtd->oobsize - i * j); } else { for (i = 0; i < n - 1; i++) - memcpy(&s[i * t], &d[i * j], j); + memcpy_toio(&s[i * t], &d[i * j], j); /* the last section */ - memcpy(&s[i * t], &d[i * j], mtd->oobsize - i * j); + memcpy_toio(&s[i * t], &d[i * j], mtd->oobsize - i * j); } } @@ -751,34 +823,44 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) * perform a read/write buf operation, the saved column * address is used to index into the full page. */ - host->send_addr(host, 0, page_addr == -1); + host->devtype_data->send_addr(host, 0, page_addr == -1); if (mtd->writesize > 512) /* another col addr cycle for 2k page */ - host->send_addr(host, 0, false); + host->devtype_data->send_addr(host, 0, false); } /* Write out page address, if necessary */ if (page_addr != -1) { /* paddr_0 - p_addr_7 */ - host->send_addr(host, (page_addr & 0xff), false); + host->devtype_data->send_addr(host, (page_addr & 0xff), false); if (mtd->writesize > 512) { if (mtd->size >= 0x10000000) { /* paddr_8 - paddr_15 */ - host->send_addr(host, (page_addr >> 8) & 0xff, false); - host->send_addr(host, (page_addr >> 16) & 0xff, true); + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, + false); + host->devtype_data->send_addr(host, + (page_addr >> 16) & 0xff, + true); } else /* paddr_8 - paddr_15 */ - host->send_addr(host, (page_addr >> 8) & 0xff, true); + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, true); } else { /* One more address cycle for higher density devices */ if (mtd->size >= 0x4000000) { /* paddr_8 - paddr_15 */ - host->send_addr(host, (page_addr >> 8) & 0xff, false); - host->send_addr(host, (page_addr >> 16) & 0xff, true); + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, + false); + host->devtype_data->send_addr(host, + (page_addr >> 16) & 0xff, + true); } else /* paddr_8 - paddr_15 */ - host->send_addr(host, (page_addr >> 8) & 0xff, true); + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, true); } } } @@ -800,7 +882,35 @@ static int get_eccsize(struct mtd_info *mtd) return 8; } -static void preset_v1_v2(struct mtd_info *mtd) +static void preset_v1(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + uint16_t config1 = 0; + + if (nand_chip->ecc.mode == NAND_ECC_HW) + config1 |= NFC_V1_V2_CONFIG1_ECC_EN; + + if (!host->devtype_data->irqpending_quirk) + config1 |= NFC_V1_V2_CONFIG1_INT_MSK; + + host->eccsize = 1; + + writew(config1, NFC_V1_V2_CONFIG1); + /* preset operation */ + + /* Unlock the internal RAM Buffer */ + writew(0x2, NFC_V1_V2_CONFIG); + + /* Blocks to be unlocked */ + writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR); + writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR); + + /* Unlock Block Command for given address range */ + writew(0x4, NFC_V1_V2_WRPROT); +} + +static void preset_v2(struct mtd_info *mtd) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; @@ -809,13 +919,12 @@ static void preset_v1_v2(struct mtd_info *mtd) if (nand_chip->ecc.mode == NAND_ECC_HW) config1 |= NFC_V1_V2_CONFIG1_ECC_EN; - if (nfc_is_v21()) - config1 |= NFC_V2_CONFIG1_FP_INT; + config1 |= NFC_V2_CONFIG1_FP_INT; - if (!cpu_is_mx21()) + if (!host->devtype_data->irqpending_quirk) config1 |= NFC_V1_V2_CONFIG1_INT_MSK; - if (nfc_is_v21() && mtd->writesize) { + if (mtd->writesize) { uint16_t pages_per_block = mtd->erasesize / mtd->writesize; host->eccsize = get_eccsize(mtd); @@ -834,20 +943,14 @@ static void preset_v1_v2(struct mtd_info *mtd) writew(0x2, NFC_V1_V2_CONFIG); /* Blocks to be unlocked */ - if (nfc_is_v21()) { - writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0); - writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1); - writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2); - writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3); - writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0); - writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1); - writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2); - writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3); - } else if (nfc_is_v1()) { - writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR); - writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR); - } else - BUG(); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3); /* Unlock Block Command for given address range */ writew(0x4, NFC_V1_V2_WRPROT); @@ -937,15 +1040,15 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, /* Command pre-processing step */ switch (command) { case NAND_CMD_RESET: - host->preset(mtd); - host->send_cmd(host, command, false); + host->devtype_data->preset(mtd); + host->devtype_data->send_cmd(host, command, false); break; case NAND_CMD_STATUS: host->buf_start = 0; host->status_request = true; - host->send_cmd(host, command, true); + host->devtype_data->send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); break; @@ -958,15 +1061,16 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, command = NAND_CMD_READ0; /* only READ0 is valid */ - host->send_cmd(host, command, false); + host->devtype_data->send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); if (mtd->writesize > 512) - host->send_cmd(host, NAND_CMD_READSTART, true); + host->devtype_data->send_cmd(host, + NAND_CMD_READSTART, true); - host->send_page(mtd, NFC_OUTPUT); + host->devtype_data->send_page(mtd, NFC_OUTPUT); - memcpy(host->data_buf, host->main_area0, mtd->writesize); + memcpy_fromio(host->data_buf, host->main_area0, mtd->writesize); copy_spare(mtd, true); break; @@ -977,28 +1081,28 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, host->buf_start = column; - host->send_cmd(host, command, false); + host->devtype_data->send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); break; case NAND_CMD_PAGEPROG: - memcpy(host->main_area0, host->data_buf, mtd->writesize); + memcpy_toio(host->main_area0, host->data_buf, mtd->writesize); copy_spare(mtd, false); - host->send_page(mtd, NFC_INPUT); - host->send_cmd(host, command, true); + host->devtype_data->send_page(mtd, NFC_INPUT); + host->devtype_data->send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); break; case NAND_CMD_READID: - host->send_cmd(host, command, true); + host->devtype_data->send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); - host->send_read_id(host); + host->devtype_data->send_read_id(host); host->buf_start = column; break; case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: - host->send_cmd(host, command, false); + host->devtype_data->send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); break; @@ -1032,15 +1136,191 @@ static struct nand_bbt_descr bbt_mirror_descr = { .pattern = mirror_pattern, }; +/* v1 + irqpending_quirk: i.MX21 */ +static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { + .preset = preset_v1, + .send_cmd = send_cmd_v1_v2, + .send_addr = send_addr_v1_v2, + .send_page = send_page_v1, + .send_read_id = send_read_id_v1_v2, + .get_dev_status = get_dev_status_v1_v2, + .check_int = check_int_v1_v2, + .irq_control = irq_control_v1_v2, + .get_ecc_status = get_ecc_status_v1, + .ecclayout_512 = &nandv1_hw_eccoob_smallpage, + .ecclayout_2k = &nandv1_hw_eccoob_largepage, + .ecclayout_4k = &nandv1_hw_eccoob_smallpage, /* XXX: needs fix */ + .select_chip = mxc_nand_select_chip_v1_v3, + .correct_data = mxc_nand_correct_data_v1, + .irqpending_quirk = 1, + .needs_ip = 0, + .regs_offset = 0xe00, + .spare0_offset = 0x800, + .spare_len = 16, + .eccbytes = 3, + .eccsize = 1, +}; + +/* v1 + !irqpending_quirk: i.MX27, i.MX31 */ +static const struct mxc_nand_devtype_data imx27_nand_devtype_data = { + .preset = preset_v1, + .send_cmd = send_cmd_v1_v2, + .send_addr = send_addr_v1_v2, + .send_page = send_page_v1, + .send_read_id = send_read_id_v1_v2, + .get_dev_status = get_dev_status_v1_v2, + .check_int = check_int_v1_v2, + .irq_control = irq_control_v1_v2, + .get_ecc_status = get_ecc_status_v1, + .ecclayout_512 = &nandv1_hw_eccoob_smallpage, + .ecclayout_2k = &nandv1_hw_eccoob_largepage, + .ecclayout_4k = &nandv1_hw_eccoob_smallpage, /* XXX: needs fix */ + .select_chip = mxc_nand_select_chip_v1_v3, + .correct_data = mxc_nand_correct_data_v1, + .irqpending_quirk = 0, + .needs_ip = 0, + .regs_offset = 0xe00, + .spare0_offset = 0x800, + .axi_offset = 0, + .spare_len = 16, + .eccbytes = 3, + .eccsize = 1, +}; + +/* v21: i.MX25, i.MX35 */ +static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { + .preset = preset_v2, + .send_cmd = send_cmd_v1_v2, + .send_addr = send_addr_v1_v2, + .send_page = send_page_v2, + .send_read_id = send_read_id_v1_v2, + .get_dev_status = get_dev_status_v1_v2, + .check_int = check_int_v1_v2, + .irq_control = irq_control_v1_v2, + .get_ecc_status = get_ecc_status_v2, + .ecclayout_512 = &nandv2_hw_eccoob_smallpage, + .ecclayout_2k = &nandv2_hw_eccoob_largepage, + .ecclayout_4k = &nandv2_hw_eccoob_4k, + .select_chip = mxc_nand_select_chip_v2, + .correct_data = mxc_nand_correct_data_v2_v3, + .irqpending_quirk = 0, + .needs_ip = 0, + .regs_offset = 0x1e00, + .spare0_offset = 0x1000, + .axi_offset = 0, + .spare_len = 64, + .eccbytes = 9, + .eccsize = 0, +}; + +/* v3: i.MX51, i.MX53 */ +static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { + .preset = preset_v3, + .send_cmd = send_cmd_v3, + .send_addr = send_addr_v3, + .send_page = send_page_v3, + .send_read_id = send_read_id_v3, + .get_dev_status = get_dev_status_v3, + .check_int = check_int_v3, + .irq_control = irq_control_v3, + .get_ecc_status = get_ecc_status_v3, + .ecclayout_512 = &nandv2_hw_eccoob_smallpage, + .ecclayout_2k = &nandv2_hw_eccoob_largepage, + .ecclayout_4k = &nandv2_hw_eccoob_smallpage, /* XXX: needs fix */ + .select_chip = mxc_nand_select_chip_v1_v3, + .correct_data = mxc_nand_correct_data_v2_v3, + .irqpending_quirk = 0, + .needs_ip = 1, + .regs_offset = 0, + .spare0_offset = 0x1000, + .axi_offset = 0x1e00, + .spare_len = 64, + .eccbytes = 0, + .eccsize = 0, +}; + +#ifdef CONFIG_OF_MTD +static const struct of_device_id mxcnd_dt_ids[] = { + { + .compatible = "fsl,imx21-nand", + .data = &imx21_nand_devtype_data, + }, { + .compatible = "fsl,imx27-nand", + .data = &imx27_nand_devtype_data, + }, { + .compatible = "fsl,imx25-nand", + .data = &imx25_nand_devtype_data, + }, { + .compatible = "fsl,imx51-nand", + .data = &imx51_nand_devtype_data, + }, + { /* sentinel */ } +}; + +static int __init mxcnd_probe_dt(struct mxc_nand_host *host) +{ + struct device_node *np = host->dev->of_node; + struct mxc_nand_platform_data *pdata = &host->pdata; + const struct of_device_id *of_id = + of_match_device(mxcnd_dt_ids, host->dev); + int buswidth; + + if (!np) + return 1; + + if (of_get_nand_ecc_mode(np) >= 0) + pdata->hw_ecc = 1; + + pdata->flash_bbt = of_get_nand_on_flash_bbt(np); + + buswidth = of_get_nand_bus_width(np); + if (buswidth < 0) + return buswidth; + + pdata->width = buswidth / 8; + + host->devtype_data = of_id->data; + + return 0; +} +#else +static int __init mxcnd_probe_dt(struct mxc_nand_host *host) +{ + return 1; +} +#endif + +static int __init mxcnd_probe_pdata(struct mxc_nand_host *host) +{ + struct mxc_nand_platform_data *pdata = host->dev->platform_data; + + if (!pdata) + return -ENODEV; + + host->pdata = *pdata; + + if (nfc_is_v1()) { + if (cpu_is_mx21()) + host->devtype_data = &imx21_nand_devtype_data; + else + host->devtype_data = &imx27_nand_devtype_data; + } else if (nfc_is_v21()) { + host->devtype_data = &imx25_nand_devtype_data; + } else if (nfc_is_v3_2()) { + host->devtype_data = &imx51_nand_devtype_data; + } else + BUG(); + + return 0; +} + static int __init mxcnd_probe(struct platform_device *pdev) { struct nand_chip *this; struct mtd_info *mtd; - struct mxc_nand_platform_data *pdata = pdev->dev.platform_data; struct mxc_nand_host *host; struct resource *res; int err = 0; - struct nand_ecclayout *oob_smallpage, *oob_largepage; /* Allocate memory for MTD device structure and private data */ host = kzalloc(sizeof(struct mxc_nand_host) + NAND_MAX_PAGESIZE + @@ -1065,7 +1345,6 @@ static int __init mxcnd_probe(struct platform_device *pdev) this->priv = host; this->dev_ready = mxc_nand_dev_ready; this->cmdfunc = mxc_nand_command; - this->select_chip = mxc_nand_select_chip; this->read_byte = mxc_nand_read_byte; this->read_word = mxc_nand_read_word; this->write_buf = mxc_nand_write_buf; @@ -1095,36 +1374,26 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->main_area0 = host->base; - if (nfc_is_v1() || nfc_is_v21()) { - host->preset = preset_v1_v2; - host->send_cmd = send_cmd_v1_v2; - host->send_addr = send_addr_v1_v2; - host->send_page = send_page_v1_v2; - host->send_read_id = send_read_id_v1_v2; - host->get_dev_status = get_dev_status_v1_v2; - host->check_int = check_int_v1_v2; - if (cpu_is_mx21()) - host->irq_control = irq_control_mx21; - else - host->irq_control = irq_control_v1_v2; - } + err = mxcnd_probe_dt(host); + if (err > 0) + err = mxcnd_probe_pdata(host); + if (err < 0) + goto eirq; - if (nfc_is_v21()) { - host->regs = host->base + 0x1e00; - host->spare0 = host->base + 0x1000; - host->spare_len = 64; - oob_smallpage = &nandv2_hw_eccoob_smallpage; - oob_largepage = &nandv2_hw_eccoob_largepage; - this->ecc.bytes = 9; - } else if (nfc_is_v1()) { - host->regs = host->base + 0xe00; - host->spare0 = host->base + 0x800; - host->spare_len = 16; - oob_smallpage = &nandv1_hw_eccoob_smallpage; - oob_largepage = &nandv1_hw_eccoob_largepage; - this->ecc.bytes = 3; - host->eccsize = 1; - } else if (nfc_is_v3_2()) { + if (host->devtype_data->regs_offset) + host->regs = host->base + host->devtype_data->regs_offset; + host->spare0 = host->base + host->devtype_data->spare0_offset; + if (host->devtype_data->axi_offset) + host->regs_axi = host->base + host->devtype_data->axi_offset; + + this->ecc.bytes = host->devtype_data->eccbytes; + host->eccsize = host->devtype_data->eccsize; + + this->select_chip = host->devtype_data->select_chip; + this->ecc.size = 512; + this->ecc.layout = host->devtype_data->ecclayout_512; + + if (host->devtype_data->needs_ip) { res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) { err = -ENODEV; @@ -1135,42 +1404,22 @@ static int __init mxcnd_probe(struct platform_device *pdev) err = -ENOMEM; goto eirq; } - host->regs_axi = host->base + 0x1e00; - host->spare0 = host->base + 0x1000; - host->spare_len = 64; - host->preset = preset_v3; - host->send_cmd = send_cmd_v3; - host->send_addr = send_addr_v3; - host->send_page = send_page_v3; - host->send_read_id = send_read_id_v3; - host->check_int = check_int_v3; - host->get_dev_status = get_dev_status_v3; - host->irq_control = irq_control_v3; - oob_smallpage = &nandv2_hw_eccoob_smallpage; - oob_largepage = &nandv2_hw_eccoob_largepage; - } else - BUG(); - - this->ecc.size = 512; - this->ecc.layout = oob_smallpage; + } - if (pdata->hw_ecc) { + if (host->pdata.hw_ecc) { this->ecc.calculate = mxc_nand_calculate_ecc; this->ecc.hwctl = mxc_nand_enable_hwecc; - if (nfc_is_v1()) - this->ecc.correct = mxc_nand_correct_data_v1; - else - this->ecc.correct = mxc_nand_correct_data_v2_v3; + this->ecc.correct = host->devtype_data->correct_data; this->ecc.mode = NAND_ECC_HW; } else { this->ecc.mode = NAND_ECC_SOFT; } - /* NAND bus width determines access funtions used by upper layer */ - if (pdata->width == 2) + /* NAND bus width determines access functions used by upper layer */ + if (host->pdata.width == 2) this->options |= NAND_BUSWIDTH_16; - if (pdata->flash_bbt) { + if (host->pdata.flash_bbt) { this->bbt_td = &bbt_main_descr; this->bbt_md = &bbt_mirror_descr; /* update flash based bbt */ @@ -1182,28 +1431,25 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->irq = platform_get_irq(pdev, 0); /* - * mask the interrupt. For i.MX21 explicitely call - * irq_control_v1_v2 to use the mask bit. We can't call - * disable_irq_nosync() for an interrupt we do not own yet. + * Use host->devtype_data->irq_control() here instead of irq_control() + * because we must not disable_irq_nosync without having requested the + * irq. */ - if (cpu_is_mx21()) - irq_control_v1_v2(host, 0); - else - host->irq_control(host, 0); + host->devtype_data->irq_control(host, 0); err = request_irq(host->irq, mxc_nfc_irq, IRQF_DISABLED, DRIVER_NAME, host); if (err) goto eirq; - host->irq_control(host, 0); - /* - * Now that the interrupt is disabled make sure the interrupt - * mask bit is cleared on i.MX21. Otherwise we can't read - * the interrupt status bit on this machine. + * Now that we "own" the interrupt make sure the interrupt mask bit is + * cleared on i.MX21. Otherwise we can't read the interrupt status bit + * on this machine. */ - if (cpu_is_mx21()) - irq_control_v1_v2(host, 1); + if (host->devtype_data->irqpending_quirk) { + disable_irq_nosync(host->irq); + host->devtype_data->irq_control(host, 1); + } /* first scan to find the device and get the page size */ if (nand_scan_ident(mtd, nfc_is_v21() ? 4 : 1, NULL)) { @@ -1212,18 +1458,12 @@ static int __init mxcnd_probe(struct platform_device *pdev) } /* Call preset again, with correct writesize this time */ - host->preset(mtd); + host->devtype_data->preset(mtd); if (mtd->writesize == 2048) - this->ecc.layout = oob_largepage; - if (nfc_is_v21() && mtd->writesize == 4096) - this->ecc.layout = &nandv2_hw_eccoob_4k; - - /* second phase scan */ - if (nand_scan_tail(mtd)) { - err = -ENXIO; - goto escan; - } + this->ecc.layout = host->devtype_data->ecclayout_2k; + else if (mtd->writesize == 4096) + this->ecc.layout = host->devtype_data->ecclayout_4k; if (this->ecc.mode == NAND_ECC_HW) { if (nfc_is_v1()) @@ -1232,9 +1472,19 @@ static int __init mxcnd_probe(struct platform_device *pdev) this->ecc.strength = (host->eccsize == 4) ? 4 : 8; } + /* second phase scan */ + if (nand_scan_tail(mtd)) { + err = -ENXIO; + goto escan; + } + /* Register the partitions */ - mtd_device_parse_register(mtd, part_probes, NULL, pdata->parts, - pdata->nr_parts); + mtd_device_parse_register(mtd, part_probes, + &(struct mtd_part_parser_data){ + .of_node = pdev->dev.of_node, + }, + host->pdata.parts, + host->pdata.nr_parts); platform_set_drvdata(pdev, host); @@ -1275,6 +1525,8 @@ static int __devexit mxcnd_remove(struct platform_device *pdev) static struct platform_driver mxcnd_driver = { .driver = { .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(mxcnd_dt_ids), }, .remove = __devexit_p(mxcnd_remove), }; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 47b19c0..d47586c 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1066,15 +1066,17 @@ EXPORT_SYMBOL(nand_lock); * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * Not for syndrome calculating ECC controllers, which use a special oob layout. */ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { chip->read_buf(mtd, buf, mtd->writesize); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + if (oob_required) + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); return 0; } @@ -1083,13 +1085,14 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * We need a special oob layout and handling even when OOB isn't used. */ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, int page) + struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) { int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1126,10 +1129,11 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read */ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1138,8 +1142,9 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_calc = chip->buffers->ecccalc; uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; + unsigned int max_bitflips = 0; - chip->ecc.read_page_raw(mtd, chip, buf, page); + chip->ecc.read_page_raw(mtd, chip, buf, 1, page); for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) chip->ecc.calculate(mtd, p, &ecc_calc[i]); @@ -1154,12 +1159,14 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, int stat; stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /** @@ -1180,6 +1187,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, int datafrag_len, eccfrag_len, aligned_len, aligned_pos; int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; int index = 0; + unsigned int max_bitflips = 0; /* Column address within the page aligned to ECC size (256bytes) */ start_step = data_offs / chip->ecc.size; @@ -1244,12 +1252,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /** @@ -1257,12 +1267,13 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * Not for syndrome calculating ECC controllers which need a special oob layout. */ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1271,6 +1282,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_calc = chip->buffers->ecccalc; uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; + unsigned int max_bitflips = 0; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_READ); @@ -1289,12 +1301,14 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, int stat; stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /** @@ -1302,6 +1316,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * Hardware ECC for large page chips, require OOB to be read first. For this @@ -1311,7 +1326,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * the data area, by overwriting the NAND manufacturer bad block markings. */ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int page) + struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1320,6 +1335,7 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; uint8_t *ecc_calc = chip->buffers->ecccalc; + unsigned int max_bitflips = 0; /* Read the OOB area first */ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); @@ -1337,12 +1353,14 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, chip->ecc.calculate(mtd, p, &ecc_calc[i]); stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /** @@ -1350,19 +1368,21 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * The hw generator calculates the error syndrome automatically. Therefore we * need a special oob layout and handling. */ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *p = buf; uint8_t *oob = chip->oob_poi; + unsigned int max_bitflips = 0; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; @@ -1379,10 +1399,12 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, chip->read_buf(mtd, oob, eccbytes); stat = chip->ecc.correct(mtd, p, oob, NULL); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } oob += eccbytes; @@ -1397,7 +1419,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, if (i) chip->read_buf(mtd, oob, i); - return 0; + return max_bitflips; } /** @@ -1459,11 +1481,9 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - int chipnr, page, realpage, col, bytes, aligned; + int chipnr, page, realpage, col, bytes, aligned, oob_required; struct nand_chip *chip = mtd->priv; struct mtd_ecc_stats stats; - int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; - int sndcmd = 1; int ret = 0; uint32_t readlen = ops->len; uint32_t oobreadlen = ops->ooblen; @@ -1471,6 +1491,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, mtd->oobavail : mtd->oobsize; uint8_t *bufpoi, *oob, *buf; + unsigned int max_bitflips = 0; stats = mtd->ecc_stats; @@ -1484,6 +1505,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, buf = ops->datbuf; oob = ops->oobbuf; + oob_required = oob ? 1 : 0; while (1) { bytes = min(mtd->writesize - col, readlen); @@ -1493,21 +1515,22 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, if (realpage != chip->pagebuf || oob) { bufpoi = aligned ? buf : chip->buffers->databuf; - if (likely(sndcmd)) { - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); - sndcmd = 0; - } + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); - /* Now read the page into the buffer */ + /* + * Now read the page into the buffer. Absent an error, + * the read methods return max bitflips per ecc step. + */ if (unlikely(ops->mode == MTD_OPS_RAW)) - ret = chip->ecc.read_page_raw(mtd, chip, - bufpoi, page); + ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, + oob_required, + page); else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); else ret = chip->ecc.read_page(mtd, chip, bufpoi, - page); + oob_required, page); if (ret < 0) { if (!aligned) /* Invalidate page cache */ @@ -1515,22 +1538,25 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, break; } + max_bitflips = max_t(unsigned int, max_bitflips, ret); + /* Transfer not aligned data */ if (!aligned) { if (!NAND_SUBPAGE_READ(chip) && !oob && !(mtd->ecc_stats.failed - stats.failed) && - (ops->mode != MTD_OPS_RAW)) + (ops->mode != MTD_OPS_RAW)) { chip->pagebuf = realpage; - else + chip->pagebuf_bitflips = ret; + } else { /* Invalidate page cache */ chip->pagebuf = -1; + } memcpy(buf, chip->buffers->databuf + col, bytes); } buf += bytes; if (unlikely(oob)) { - int toread = min(oobreadlen, max_oobsize); if (toread) { @@ -1541,13 +1567,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, } if (!(chip->options & NAND_NO_READRDY)) { - /* - * Apply delay or wait for ready/busy pin. Do - * this before the AUTOINCR check, so no - * problems arise if a chip which does auto - * increment is marked as NOAUTOINCR by the - * board driver. - */ + /* Apply delay or wait for ready/busy pin */ if (!chip->dev_ready) udelay(chip->chip_delay); else @@ -1556,6 +1576,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, } else { memcpy(buf, chip->buffers->databuf + col, bytes); buf += bytes; + max_bitflips = max_t(unsigned int, max_bitflips, + chip->pagebuf_bitflips); } readlen -= bytes; @@ -1575,26 +1597,19 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, chip->select_chip(mtd, -1); chip->select_chip(mtd, chipnr); } - - /* - * Check, if the chip supports auto page increment or if we - * have hit a block boundary. - */ - if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck)) - sndcmd = 1; } ops->retlen = ops->len - (size_t) readlen; if (oob) ops->oobretlen = ops->ooblen - oobreadlen; - if (ret) + if (ret < 0) return ret; if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; - return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; + return max_bitflips; } /** @@ -1630,17 +1645,13 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, * @mtd: mtd info structure * @chip: nand chip info structure * @page: page number to read - * @sndcmd: flag whether to issue read command or not */ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { - if (sndcmd) { - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - sndcmd = 0; - } + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return sndcmd; + return 0; } /** @@ -1649,10 +1660,9 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @page: page number to read - * @sndcmd: flag whether to issue read command or not */ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { uint8_t *buf = chip->oob_poi; int length = mtd->oobsize; @@ -1679,7 +1689,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, if (length > 0) chip->read_buf(mtd, bufpoi, length); - return 1; + return 0; } /** @@ -1775,13 +1785,13 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd, static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - int page, realpage, chipnr, sndcmd = 1; + int page, realpage, chipnr; struct nand_chip *chip = mtd->priv; struct mtd_ecc_stats stats; - int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; int readlen = ops->ooblen; int len; uint8_t *buf = ops->oobbuf; + int ret = 0; pr_debug("%s: from = 0x%08Lx, len = %i\n", __func__, (unsigned long long)from, readlen); @@ -1817,20 +1827,18 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, while (1) { if (ops->mode == MTD_OPS_RAW) - sndcmd = chip->ecc.read_oob_raw(mtd, chip, page, sndcmd); + ret = chip->ecc.read_oob_raw(mtd, chip, page); else - sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); + ret = chip->ecc.read_oob(mtd, chip, page); + + if (ret < 0) + break; len = min(len, readlen); buf = nand_transfer_oob(chip, buf, ops, len); if (!(chip->options & NAND_NO_READRDY)) { - /* - * Apply delay or wait for ready/busy pin. Do this - * before the AUTOINCR check, so no problems arise if a - * chip which does auto increment is marked as - * NOAUTOINCR by the board driver. - */ + /* Apply delay or wait for ready/busy pin */ if (!chip->dev_ready) udelay(chip->chip_delay); else @@ -1851,16 +1859,12 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, chip->select_chip(mtd, -1); chip->select_chip(mtd, chipnr); } - - /* - * Check, if the chip supports auto page increment or if we - * have hit a block boundary. - */ - if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck)) - sndcmd = 1; } - ops->oobretlen = ops->ooblen; + ops->oobretlen = ops->ooblen - readlen; + + if (ret < 0) + return ret; if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; @@ -1919,14 +1923,16 @@ out: * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB * * Not for syndrome calculating ECC controllers, which use a special oob layout. */ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { chip->write_buf(mtd, buf, mtd->writesize); - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + if (oob_required) + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); } /** @@ -1934,12 +1940,13 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB * * We need a special oob layout and handling even when ECC isn't checked. */ static void nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1973,9 +1980,10 @@ static void nand_write_page_raw_syndrome(struct mtd_info *mtd, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB */ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1991,7 +1999,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, for (i = 0; i < chip->ecc.total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; - chip->ecc.write_page_raw(mtd, chip, buf); + chip->ecc.write_page_raw(mtd, chip, buf, 1); } /** @@ -1999,9 +2007,10 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB */ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -2027,12 +2036,14 @@ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB * * The hw generator calculates the error syndrome automatically. Therefore we * need a special oob layout and handling. */ static void nand_write_page_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) + struct nand_chip *chip, + const uint8_t *buf, int oob_required) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -2071,21 +2082,23 @@ static void nand_write_page_syndrome(struct mtd_info *mtd, * @mtd: MTD device structure * @chip: NAND chip descriptor * @buf: the data to write + * @oob_required: must write chip->oob_poi to OOB * @page: page number to write * @cached: cached programming * @raw: use _raw version of write_page */ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int page, int cached, int raw) + const uint8_t *buf, int oob_required, int page, + int cached, int raw) { int status; chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); if (unlikely(raw)) - chip->ecc.write_page_raw(mtd, chip, buf); + chip->ecc.write_page_raw(mtd, chip, buf, oob_required); else - chip->ecc.write_page(mtd, chip, buf); + chip->ecc.write_page(mtd, chip, buf, oob_required); /* * Cached progamming disabled for now. Not sure if it's worth the @@ -2118,6 +2131,9 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, if (chip->verify_buf(mtd, buf, mtd->writesize)) return -EIO; + + /* Make sure the next page prog is preceded by a status read */ + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); #endif return 0; } @@ -2202,6 +2218,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, uint8_t *oob = ops->oobbuf; uint8_t *buf = ops->datbuf; int ret, subpage; + int oob_required = oob ? 1 : 0; ops->retlen = 0; if (!writelen) @@ -2264,8 +2281,8 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, memset(chip->oob_poi, 0xff, mtd->oobsize); } - ret = chip->write_page(mtd, chip, wbuf, page, cached, - (ops->mode == MTD_OPS_RAW)); + ret = chip->write_page(mtd, chip, wbuf, oob_required, page, + cached, (ops->mode == MTD_OPS_RAW)); if (ret) break; @@ -2898,8 +2915,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, *busw = NAND_BUSWIDTH_16; chip->options &= ~NAND_CHIPOPTIONS_MSK; - chip->options |= (NAND_NO_READRDY | - NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK; + chip->options |= NAND_NO_READRDY & NAND_CHIPOPTIONS_MSK; pr_info("ONFI flash detected\n"); return 1; @@ -3076,11 +3092,6 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; ident_done: - /* - * Set chip as a default. Board drivers can override it, if necessary. - */ - chip->options |= NAND_NO_AUTOINCR; - /* Try to identify manufacturer */ for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) { if (nand_manuf_ids[maf_idx].id == *maf_id) @@ -3154,10 +3165,11 @@ ident_done: if (mtd->writesize > 512 && chip->cmdfunc == nand_command) chip->cmdfunc = nand_command_lp; - pr_info("NAND device: Manufacturer ID:" - " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id, - nand_manuf_ids[maf_idx].name, - chip->onfi_version ? chip->onfi_params.model : type->name); + pr_info("NAND device: Manufacturer ID: 0x%02x, Chip ID: 0x%02x (%s %s)," + " page size: %d, OOB size: %d\n", + *maf_id, *dev_id, nand_manuf_ids[maf_idx].name, + chip->onfi_version ? chip->onfi_params.model : type->name, + mtd->writesize, mtd->oobsize); return type; } @@ -3329,8 +3341,13 @@ int nand_scan_tail(struct mtd_info *mtd) if (!chip->ecc.write_oob) chip->ecc.write_oob = nand_write_oob_syndrome; - if (mtd->writesize >= chip->ecc.size) + if (mtd->writesize >= chip->ecc.size) { + if (!chip->ecc.strength) { + pr_warn("Driver must set ecc.strength when using hardware ECC\n"); + BUG(); + } break; + } pr_warn("%d byte HW ECC not possible on " "%d byte page size, fallback to SW ECC\n", chip->ecc.size, mtd->writesize); @@ -3385,7 +3402,7 @@ int nand_scan_tail(struct mtd_info *mtd) BUG(); } chip->ecc.strength = - chip->ecc.bytes*8 / fls(8*chip->ecc.size); + chip->ecc.bytes * 8 / fls(8 * chip->ecc.size); break; case NAND_ECC_NONE: @@ -3483,7 +3500,7 @@ int nand_scan_tail(struct mtd_info *mtd) /* propagate ecc info to mtd_info */ mtd->ecclayout = chip->ecc.layout; - mtd->ecc_strength = chip->ecc.strength * chip->ecc.steps; + mtd->ecc_strength = chip->ecc.strength; /* Check, if we should skip the bad block table scan */ if (chip->options & NAND_SKIP_BBTSCAN) diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 20a112f..30d1319 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -324,6 +324,7 @@ static int scan_read_raw_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs, buf += mtd->oobsize + mtd->writesize; len -= mtd->writesize; + offs += mtd->writesize; } return 0; } diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index af4fe8c..621b70b 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -70,7 +70,7 @@ struct nand_flash_dev nand_flash_ids[] = { * These are the new chips with large page size. The pagesize and the * erasesize is determined from the extended id bytes */ -#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY | NAND_NO_AUTOINCR) +#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY) #define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) /* 512 Megabit */ @@ -157,9 +157,7 @@ struct nand_flash_dev nand_flash_ids[] = { * writes possible, but not implemented now */ {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, - NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY | - BBT_AUTO_REFRESH - }, + NAND_IS_AND | NAND_NO_READRDY | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH}, {NULL,} }; diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 261f478..6cc8fbf 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -268,7 +268,6 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should " #define OPT_PAGE512 0x00000002 /* 512-byte page chips */ #define OPT_PAGE2048 0x00000008 /* 2048-byte page chips */ #define OPT_SMARTMEDIA 0x00000010 /* SmartMedia technology chips */ -#define OPT_AUTOINCR 0x00000020 /* page number auto incrementation is possible */ #define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */ #define OPT_PAGE4096 0x00000080 /* 4096-byte page chips */ #define OPT_LARGEPAGE (OPT_PAGE2048 | OPT_PAGE4096) /* 2048 & 4096-byte page chips */ @@ -594,7 +593,7 @@ static int init_nandsim(struct mtd_info *mtd) ns->options |= OPT_PAGE256; } else if (ns->geom.pgsz == 512) { - ns->options |= (OPT_PAGE512 | OPT_AUTOINCR); + ns->options |= OPT_PAGE512; if (ns->busw == 8) ns->options |= OPT_PAGE512_8BIT; } else if (ns->geom.pgsz == 2048) { @@ -663,8 +662,6 @@ static int init_nandsim(struct mtd_info *mtd) for (i = 0; nand_flash_ids[i].name != NULL; i++) { if (second_id_byte != nand_flash_ids[i].id) continue; - if (!(nand_flash_ids[i].options & NAND_NO_AUTOINCR)) - ns->options |= OPT_AUTOINCR; } if (ns->busw == 16) @@ -1936,20 +1933,8 @@ static u_char ns_nand_read_byte(struct mtd_info *mtd) if (ns->regs.count == ns->regs.num) { NS_DBG("read_byte: all bytes were read\n"); - /* - * The OPT_AUTOINCR allows to read next consecutive pages without - * new read operation cycle. - */ - if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) { - ns->regs.count = 0; - if (ns->regs.row + 1 < ns->geom.pgnum) - ns->regs.row += 1; - NS_DBG("read_byte: switch to the next page (%#x)\n", ns->regs.row); - do_state_action(ns, ACTION_CPY); - } - else if (NS_STATE(ns->nxstate) == STATE_READY) + if (NS_STATE(ns->nxstate) == STATE_READY) switch_state(ns); - } return outb; @@ -2203,14 +2188,7 @@ static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ns->regs.count += len; if (ns->regs.count == ns->regs.num) { - if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) { - ns->regs.count = 0; - if (ns->regs.row + 1 < ns->geom.pgnum) - ns->regs.row += 1; - NS_DBG("read_buf: switch to the next page (%#x)\n", ns->regs.row); - do_state_action(ns, ACTION_CPY); - } - else if (NS_STATE(ns->nxstate) == STATE_READY) + if (NS_STATE(ns->nxstate) == STATE_READY) switch_state(ns); } diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index c2b0bba..d7f681d 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -21,6 +21,10 @@ #include <linux/io.h> #include <linux/slab.h> +#ifdef CONFIG_MTD_NAND_OMAP_BCH +#include <linux/bch.h> +#endif + #include <plat/dma.h> #include <plat/gpmc.h> #include <plat/nand.h> @@ -127,6 +131,11 @@ struct omap_nand_info { } iomode; u_char *buf; int buf_len; + +#ifdef CONFIG_MTD_NAND_OMAP_BCH + struct bch_control *bch; + struct nand_ecclayout ecclayout; +#endif }; /** @@ -402,7 +411,7 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write); if (ret) /* PFPW engine is busy, use cpu copy method */ - goto out_copy; + goto out_copy_unmap; init_completion(&info->comp); @@ -421,6 +430,8 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, dma_unmap_single(&info->pdev->dev, dma_addr, len, dir); return 0; +out_copy_unmap: + dma_unmap_single(&info->pdev->dev, dma_addr, len, dir); out_copy: if (info->nand.options & NAND_BUSWIDTH_16) is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len) @@ -879,7 +890,7 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip) struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); unsigned long timeo = jiffies; - int status = NAND_STATUS_FAIL, state = this->state; + int status, state = this->state; if (state == FL_ERASING) timeo += (HZ * 400) / 1000; @@ -894,6 +905,8 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip) break; cond_resched(); } + + status = gpmc_nand_read(info->gpmc_cs, GPMC_NAND_DATA); return status; } @@ -925,6 +938,226 @@ static int omap_dev_ready(struct mtd_info *mtd) return 1; } +#ifdef CONFIG_MTD_NAND_OMAP_BCH + +/** + * omap3_enable_hwecc_bch - Program OMAP3 GPMC to perform BCH ECC correction + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode) +{ + int nerrors; + unsigned int dev_width; + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + struct nand_chip *chip = mtd->priv; + + nerrors = (info->nand.ecc.bytes == 13) ? 8 : 4; + dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; + /* + * Program GPMC to perform correction on one 512-byte sector at a time. + * Using 4 sectors at a time (i.e. ecc.size = 2048) is also possible and + * gives a slight (5%) performance gain (but requires additional code). + */ + (void)gpmc_enable_hwecc_bch(info->gpmc_cs, mode, dev_width, 1, nerrors); +} + +/** + * omap3_calculate_ecc_bch4 - Generate 7 bytes of ECC bytes + * @mtd: MTD device structure + * @dat: The pointer to data on which ecc is computed + * @ecc_code: The ecc_code buffer + */ +static int omap3_calculate_ecc_bch4(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + return gpmc_calculate_ecc_bch4(info->gpmc_cs, dat, ecc_code); +} + +/** + * omap3_calculate_ecc_bch8 - Generate 13 bytes of ECC bytes + * @mtd: MTD device structure + * @dat: The pointer to data on which ecc is computed + * @ecc_code: The ecc_code buffer + */ +static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + return gpmc_calculate_ecc_bch8(info->gpmc_cs, dat, ecc_code); +} + +/** + * omap3_correct_data_bch - Decode received data and correct errors + * @mtd: MTD device structure + * @data: page data + * @read_ecc: ecc read from nand flash + * @calc_ecc: ecc read from HW ECC registers + */ +static int omap3_correct_data_bch(struct mtd_info *mtd, u_char *data, + u_char *read_ecc, u_char *calc_ecc) +{ + int i, count; + /* cannot correct more than 8 errors */ + unsigned int errloc[8]; + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + + count = decode_bch(info->bch, NULL, 512, read_ecc, calc_ecc, NULL, + errloc); + if (count > 0) { + /* correct errors */ + for (i = 0; i < count; i++) { + /* correct data only, not ecc bytes */ + if (errloc[i] < 8*512) + data[errloc[i]/8] ^= 1 << (errloc[i] & 7); + pr_debug("corrected bitflip %u\n", errloc[i]); + } + } else if (count < 0) { + pr_err("ecc unrecoverable error\n"); + } + return count; +} + +/** + * omap3_free_bch - Release BCH ecc resources + * @mtd: MTD device structure + */ +static void omap3_free_bch(struct mtd_info *mtd) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + if (info->bch) { + free_bch(info->bch); + info->bch = NULL; + } +} + +/** + * omap3_init_bch - Initialize BCH ECC + * @mtd: MTD device structure + * @ecc_opt: OMAP ECC mode (OMAP_ECC_BCH4_CODE_HW or OMAP_ECC_BCH8_CODE_HW) + */ +static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt) +{ + int ret, max_errors; + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); +#ifdef CONFIG_MTD_NAND_OMAP_BCH8 + const int hw_errors = 8; +#else + const int hw_errors = 4; +#endif + info->bch = NULL; + + max_errors = (ecc_opt == OMAP_ECC_BCH8_CODE_HW) ? 8 : 4; + if (max_errors != hw_errors) { + pr_err("cannot configure %d-bit BCH ecc, only %d-bit supported", + max_errors, hw_errors); + goto fail; + } + + /* initialize GPMC BCH engine */ + ret = gpmc_init_hwecc_bch(info->gpmc_cs, 1, max_errors); + if (ret) + goto fail; + + /* software bch library is only used to detect and locate errors */ + info->bch = init_bch(13, max_errors, 0x201b /* hw polynomial */); + if (!info->bch) + goto fail; + + info->nand.ecc.size = 512; + info->nand.ecc.hwctl = omap3_enable_hwecc_bch; + info->nand.ecc.correct = omap3_correct_data_bch; + info->nand.ecc.mode = NAND_ECC_HW; + + /* + * The number of corrected errors in an ecc block that will trigger + * block scrubbing defaults to the ecc strength (4 or 8). + * Set mtd->bitflip_threshold here to define a custom threshold. + */ + + if (max_errors == 8) { + info->nand.ecc.strength = 8; + info->nand.ecc.bytes = 13; + info->nand.ecc.calculate = omap3_calculate_ecc_bch8; + } else { + info->nand.ecc.strength = 4; + info->nand.ecc.bytes = 7; + info->nand.ecc.calculate = omap3_calculate_ecc_bch4; + } + + pr_info("enabling NAND BCH ecc with %d-bit correction\n", max_errors); + return 0; +fail: + omap3_free_bch(mtd); + return -1; +} + +/** + * omap3_init_bch_tail - Build an oob layout for BCH ECC correction. + * @mtd: MTD device structure + */ +static int omap3_init_bch_tail(struct mtd_info *mtd) +{ + int i, steps; + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + struct nand_ecclayout *layout = &info->ecclayout; + + /* build oob layout */ + steps = mtd->writesize/info->nand.ecc.size; + layout->eccbytes = steps*info->nand.ecc.bytes; + + /* do not bother creating special oob layouts for small page devices */ + if (mtd->oobsize < 64) { + pr_err("BCH ecc is not supported on small page devices\n"); + goto fail; + } + + /* reserve 2 bytes for bad block marker */ + if (layout->eccbytes+2 > mtd->oobsize) { + pr_err("no oob layout available for oobsize %d eccbytes %u\n", + mtd->oobsize, layout->eccbytes); + goto fail; + } + + /* put ecc bytes at oob tail */ + for (i = 0; i < layout->eccbytes; i++) + layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i; + + layout->oobfree[0].offset = 2; + layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; + info->nand.ecc.layout = layout; + + if (!(info->nand.options & NAND_BUSWIDTH_16)) + info->nand.badblock_pattern = &bb_descrip_flashbased; + return 0; +fail: + omap3_free_bch(mtd); + return -1; +} + +#else +static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt) +{ + pr_err("CONFIG_MTD_NAND_OMAP_BCH is not enabled\n"); + return -1; +} +static int omap3_init_bch_tail(struct mtd_info *mtd) +{ + return -1; +} +static void omap3_free_bch(struct mtd_info *mtd) +{ +} +#endif /* CONFIG_MTD_NAND_OMAP_BCH */ + static int __devinit omap_nand_probe(struct platform_device *pdev) { struct omap_nand_info *info; @@ -1063,6 +1296,13 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) info->nand.ecc.hwctl = omap_enable_hwecc; info->nand.ecc.correct = omap_correct_data; info->nand.ecc.mode = NAND_ECC_HW; + } else if ((pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) || + (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW)) { + err = omap3_init_bch(&info->mtd, pdata->ecc_opt); + if (err) { + err = -EINVAL; + goto out_release_mem_region; + } } /* DIP switches on some boards change between 8 and 16 bit @@ -1094,6 +1334,14 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) (offset + omap_oobinfo.eccbytes); info->nand.ecc.layout = &omap_oobinfo; + } else if ((pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) || + (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW)) { + /* build OOB layout for BCH ECC correction */ + err = omap3_init_bch_tail(&info->mtd); + if (err) { + err = -EINVAL; + goto out_release_mem_region; + } } /* second phase scan */ @@ -1122,6 +1370,7 @@ static int omap_nand_remove(struct platform_device *pdev) struct mtd_info *mtd = platform_get_drvdata(pdev); struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); + omap3_free_bch(&info->mtd); platform_set_drvdata(pdev, NULL); if (info->dma_ch != -1) diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c index 974dbf8..1440e51 100644 --- a/drivers/mtd/nand/pasemi_nand.c +++ b/drivers/mtd/nand/pasemi_nand.c @@ -155,7 +155,6 @@ static int __devinit pasemi_nand_probe(struct platform_device *ofdev) chip->ecc.mode = NAND_ECC_SOFT; /* Enable the following for a flash based bad block table */ - chip->options = NAND_NO_AUTOINCR; chip->bbt_options = NAND_BBT_USE_FLASH; /* Scan to find existence of the device */ diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 6404e6e..1bcb520 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -23,14 +23,18 @@ struct plat_nand_data { void __iomem *io_base; }; +static const char *part_probe_types[] = { "cmdlinepart", NULL }; + /* * Probe for the NAND device. */ static int __devinit plat_nand_probe(struct platform_device *pdev) { struct platform_nand_data *pdata = pdev->dev.platform_data; + struct mtd_part_parser_data ppdata; struct plat_nand_data *data; struct resource *res; + const char **part_types; int err = 0; if (pdata->chip.nr_chips < 1) { @@ -75,6 +79,7 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) data->chip.select_chip = pdata->ctrl.select_chip; data->chip.write_buf = pdata->ctrl.write_buf; data->chip.read_buf = pdata->ctrl.read_buf; + data->chip.read_byte = pdata->ctrl.read_byte; data->chip.chip_delay = pdata->chip.chip_delay; data->chip.options |= pdata->chip.options; data->chip.bbt_options |= pdata->chip.bbt_options; @@ -98,8 +103,10 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) goto out; } - err = mtd_device_parse_register(&data->mtd, - pdata->chip.part_probe_types, NULL, + part_types = pdata->chip.part_probe_types ? : part_probe_types; + + ppdata.of_node = pdev->dev.of_node; + err = mtd_device_parse_register(&data->mtd, part_types, &ppdata, pdata->chip.partitions, pdata->chip.nr_partitions); @@ -140,12 +147,19 @@ static int __devexit plat_nand_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id plat_nand_match[] = { + { .compatible = "gen_nand" }, + {}, +}; +MODULE_DEVICE_TABLE(of, plat_nand_match); + static struct platform_driver plat_nand_driver = { - .probe = plat_nand_probe, - .remove = __devexit_p(plat_nand_remove), - .driver = { - .name = "gen_nand", - .owner = THIS_MODULE, + .probe = plat_nand_probe, + .remove = __devexit_p(plat_nand_remove), + .driver = { + .name = "gen_nand", + .owner = THIS_MODULE, + .of_match_table = plat_nand_match, }, }; diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index def50ca..252aaef 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -682,14 +682,15 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, } static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) + struct nand_chip *chip, const uint8_t *buf, int oob_required) { chip->write_buf(mtd, buf, mtd->writesize); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); } static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int page) + struct nand_chip *chip, uint8_t *buf, int oob_required, + int page) { struct pxa3xx_nand_host *host = mtd->priv; struct pxa3xx_nand_info *info = host->info_data; @@ -1004,7 +1005,6 @@ KEEP_CONFIG: chip->ecc.size = host->page_size; chip->ecc.strength = 1; - chip->options = NAND_NO_AUTOINCR; chip->options |= NAND_NO_READRDY; if (host->reg_ndcr & NDCR_DWIDTH_M) chip->options |= NAND_BUSWIDTH_16; diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index c204018..8cb6277 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -539,14 +539,11 @@ exit: * nand_read_oob_syndrome assumes we can send column address - we can't */ static int r852_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { - if (sndcmd) { - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - sndcmd = 0; - } + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return sndcmd; + return 0; } /* @@ -1104,18 +1101,7 @@ static struct pci_driver r852_pci_driver = { .driver.pm = &r852_pm_ops, }; -static __init int r852_module_init(void) -{ - return pci_register_driver(&r852_pci_driver); -} - -static void __exit r852_module_exit(void) -{ - pci_unregister_driver(&r852_pci_driver); -} - -module_init(r852_module_init); -module_exit(r852_module_exit); +module_pci_driver(r852_pci_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>"); diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index e9b2b26..aa9b8a5 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -344,7 +344,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va } static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -359,14 +359,14 @@ static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, if (flctl->hwecc_cant_correct[i]) mtd->ecc_stats.failed++; else - mtd->ecc_stats.corrected += 0; + mtd->ecc_stats.corrected += 0; /* FIXME */ } return 0; } static void flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -881,8 +881,6 @@ static int __devinit flctl_probe(struct platform_device *pdev) flctl->hwecc = pdata->has_hwecc; flctl->holden = pdata->use_holden; - nand->options = NAND_NO_AUTOINCR; - /* Set address of hardware control function */ /* 20 us command delay time */ nand->chip_delay = 20; diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c index 774c3c2..082bcdc 100644 --- a/drivers/mtd/nand/sm_common.c +++ b/drivers/mtd/nand/sm_common.c @@ -94,17 +94,16 @@ static struct nand_flash_dev nand_smartmedia_flash_ids[] = { {NULL,} }; -#define XD_TYPEM (NAND_NO_AUTOINCR | NAND_BROKEN_XD) static struct nand_flash_dev nand_xd_flash_ids[] = { {"xD 16MiB 3,3V", 0x73, 512, 16, 0x4000, 0}, {"xD 32MiB 3,3V", 0x75, 512, 32, 0x4000, 0}, {"xD 64MiB 3,3V", 0x76, 512, 64, 0x4000, 0}, {"xD 128MiB 3,3V", 0x79, 512, 128, 0x4000, 0}, - {"xD 256MiB 3,3V", 0x71, 512, 256, 0x4000, XD_TYPEM}, - {"xD 512MiB 3,3V", 0xdc, 512, 512, 0x4000, XD_TYPEM}, - {"xD 1GiB 3,3V", 0xd3, 512, 1024, 0x4000, XD_TYPEM}, - {"xD 2GiB 3,3V", 0xd5, 512, 2048, 0x4000, XD_TYPEM}, + {"xD 256MiB 3,3V", 0x71, 512, 256, 0x4000, NAND_BROKEN_XD}, + {"xD 512MiB 3,3V", 0xdc, 512, 512, 0x4000, NAND_BROKEN_XD}, + {"xD 1GiB 3,3V", 0xd3, 512, 1024, 0x4000, NAND_BROKEN_XD}, + {"xD 2GiB 3,3V", 0xd5, 512, 2048, 0x4000, NAND_BROKEN_XD}, {NULL,} }; diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index b3ce12e..7153e0d2 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1201,7 +1201,8 @@ static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from, if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; - return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; + /* return max bitflips per ecc step; ONENANDs correct 1 bit only */ + return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0; } /** @@ -1333,7 +1334,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; - return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; + /* return max bitflips per ecc step; ONENANDs correct 1 bit only */ + return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0; } /** diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c index b8e01c3..b26395d 100644 --- a/drivers/pinctrl/pinctrl-nomadik.c +++ b/drivers/pinctrl/pinctrl-nomadik.c @@ -24,6 +24,7 @@ #include <linux/irq.h> #include <linux/irqdomain.h> #include <linux/slab.h> +#include <linux/of_device.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> #include <linux/pinctrl/pinconf.h> @@ -1688,18 +1689,34 @@ static struct pinctrl_desc nmk_pinctrl_desc = { .owner = THIS_MODULE, }; +static const struct of_device_id nmk_pinctrl_match[] = { + { + .compatible = "stericsson,nmk_pinctrl", + .data = (void *)PINCTRL_NMK_DB8500, + }, + {}, +}; + static int __devinit nmk_pinctrl_probe(struct platform_device *pdev) { const struct platform_device_id *platid = platform_get_device_id(pdev); + struct device_node *np = pdev->dev.of_node; struct nmk_pinctrl *npct; + unsigned int version = 0; int i; npct = devm_kzalloc(&pdev->dev, sizeof(*npct), GFP_KERNEL); if (!npct) return -ENOMEM; + if (platid) + version = platid->driver_data; + else if (np) + version = (unsigned int) + of_match_device(nmk_pinctrl_match, &pdev->dev)->data; + /* Poke in other ASIC variants here */ - if (platid->driver_data == PINCTRL_NMK_DB8500) + if (version == PINCTRL_NMK_DB8500) nmk_pinctrl_db8500_init(&npct->soc); /* @@ -1758,6 +1775,7 @@ static struct platform_driver nmk_pinctrl_driver = { .driver = { .owner = THIS_MODULE, .name = "pinctrl-nomadik", + .of_match_table = nmk_pinctrl_match, }, .probe = nmk_pinctrl_probe, .id_table = nmk_pinctrl_id, diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index c1a3fd8..ce875dc 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -523,6 +523,30 @@ static const struct dmi_system_id video_vendor_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4750"), }, }, + { + .callback = video_set_backlight_video_vendor, + .ident = "Acer Extensa 5235", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Extensa 5235"), + }, + }, + { + .callback = video_set_backlight_video_vendor, + .ident = "Acer TravelMate 5760", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 5760"), + }, + }, + { + .callback = video_set_backlight_video_vendor, + .ident = "Acer Aspire 5750", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5750"), + }, + }, {} }; diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 8a582bd..694a15a 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -87,6 +87,9 @@ static int gmux_update_status(struct backlight_device *bd) struct apple_gmux_data *gmux_data = bl_get_data(bd); u32 brightness = bd->props.brightness; + if (bd->props.state & BL_CORE_SUSPENDED) + return 0; + /* * Older gmux versions require writing out lower bytes first then * setting the upper byte to 0 to flush the values. Newer versions @@ -102,6 +105,7 @@ static int gmux_update_status(struct backlight_device *bd) } static const struct backlight_ops gmux_bl_ops = { + .options = BL_CORE_SUSPENDRESUME, .get_brightness = gmux_get_brightness, .update_status = gmux_update_status, }; diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index e6c08ee..5f78aac 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -21,7 +21,6 @@ #include <linux/err.h> #include <linux/dmi.h> #include <linux/io.h> -#include <linux/rfkill.h> #include <linux/power_supply.h> #include <linux/acpi.h> #include <linux/mm.h> @@ -90,11 +89,8 @@ static struct platform_driver platform_driver = { static struct platform_device *platform_device; static struct backlight_device *dell_backlight_device; -static struct rfkill *wifi_rfkill; -static struct rfkill *bluetooth_rfkill; -static struct rfkill *wwan_rfkill; -static const struct dmi_system_id __initdata dell_device_table[] = { +static const struct dmi_system_id dell_device_table[] __initconst = { { .ident = "Dell laptop", .matches = { @@ -119,96 +115,94 @@ static const struct dmi_system_id __initdata dell_device_table[] = { }; MODULE_DEVICE_TABLE(dmi, dell_device_table); -static struct dmi_system_id __devinitdata dell_blacklist[] = { - /* Supported by compal-laptop */ - { - .ident = "Dell Mini 9", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"), - }, - }, +static struct dmi_system_id __devinitdata dell_quirks[] = { { - .ident = "Dell Mini 10", + .callback = dmi_matched, + .ident = "Dell Vostro V130", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V130"), }, + .driver_data = &quirk_dell_vostro_v130, }, { - .ident = "Dell Mini 10v", + .callback = dmi_matched, + .ident = "Dell Vostro V131", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"), }, + .driver_data = &quirk_dell_vostro_v130, }, { - .ident = "Dell Mini 1012", + .callback = dmi_matched, + .ident = "Dell Vostro 3350", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1012"), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3350"), }, + .driver_data = &quirk_dell_vostro_v130, }, { - .ident = "Dell Inspiron 11z", + .callback = dmi_matched, + .ident = "Dell Vostro 3555", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3555"), }, + .driver_data = &quirk_dell_vostro_v130, }, { - .ident = "Dell Mini 12", + .callback = dmi_matched, + .ident = "Dell Inspiron N311z", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N311z"), }, + .driver_data = &quirk_dell_vostro_v130, }, - {} -}; - -static struct dmi_system_id __devinitdata dell_quirks[] = { { .callback = dmi_matched, - .ident = "Dell Vostro V130", + .ident = "Dell Inspiron M5110", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V130"), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"), }, .driver_data = &quirk_dell_vostro_v130, }, { .callback = dmi_matched, - .ident = "Dell Vostro V131", + .ident = "Dell Vostro 3360", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3360"), }, .driver_data = &quirk_dell_vostro_v130, }, { .callback = dmi_matched, - .ident = "Dell Vostro 3555", + .ident = "Dell Vostro 3460", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3555"), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3460"), }, .driver_data = &quirk_dell_vostro_v130, }, { .callback = dmi_matched, - .ident = "Dell Inspiron N311z", + .ident = "Dell Vostro 3560", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N311z"), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3560"), }, .driver_data = &quirk_dell_vostro_v130, }, { .callback = dmi_matched, - .ident = "Dell Inspiron M5110", + .ident = "Dell Vostro 3450", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"), + DMI_MATCH(DMI_PRODUCT_NAME, "Dell System Vostro 3450"), }, .driver_data = &quirk_dell_vostro_v130, }, @@ -305,94 +299,6 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, return buffer; } -/* Derived from information in DellWirelessCtl.cpp: - Class 17, select 11 is radio control. It returns an array of 32-bit values. - - Input byte 0 = 0: Wireless information - - result[0]: return code - result[1]: - Bit 0: Hardware switch supported - Bit 1: Wifi locator supported - Bit 2: Wifi is supported - Bit 3: Bluetooth is supported - Bit 4: WWAN is supported - Bit 5: Wireless keyboard supported - Bits 6-7: Reserved - Bit 8: Wifi is installed - Bit 9: Bluetooth is installed - Bit 10: WWAN is installed - Bits 11-15: Reserved - Bit 16: Hardware switch is on - Bit 17: Wifi is blocked - Bit 18: Bluetooth is blocked - Bit 19: WWAN is blocked - Bits 20-31: Reserved - result[2]: NVRAM size in bytes - result[3]: NVRAM format version number - - Input byte 0 = 2: Wireless switch configuration - result[0]: return code - result[1]: - Bit 0: Wifi controlled by switch - Bit 1: Bluetooth controlled by switch - Bit 2: WWAN controlled by switch - Bits 3-6: Reserved - Bit 7: Wireless switch config locked - Bit 8: Wifi locator enabled - Bits 9-14: Reserved - Bit 15: Wifi locator setting locked - Bits 16-31: Reserved -*/ - -static int dell_rfkill_set(void *data, bool blocked) -{ - int disable = blocked ? 1 : 0; - unsigned long radio = (unsigned long)data; - int hwswitch_bit = (unsigned long)data - 1; - int ret = 0; - - get_buffer(); - dell_send_request(buffer, 17, 11); - - /* If the hardware switch controls this radio, and the hardware - switch is disabled, don't allow changing the software state */ - if ((hwswitch_state & BIT(hwswitch_bit)) && - !(buffer->output[1] & BIT(16))) { - ret = -EINVAL; - goto out; - } - - buffer->input[0] = (1 | (radio<<8) | (disable << 16)); - dell_send_request(buffer, 17, 11); - -out: - release_buffer(); - return ret; -} - -static void dell_rfkill_query(struct rfkill *rfkill, void *data) -{ - int status; - int bit = (unsigned long)data + 16; - int hwswitch_bit = (unsigned long)data - 1; - - get_buffer(); - dell_send_request(buffer, 17, 11); - status = buffer->output[1]; - release_buffer(); - - rfkill_set_sw_state(rfkill, !!(status & BIT(bit))); - - if (hwswitch_state & (BIT(hwswitch_bit))) - rfkill_set_hw_state(rfkill, !(status & BIT(16))); -} - -static const struct rfkill_ops dell_rfkill_ops = { - .set_block = dell_rfkill_set, - .query = dell_rfkill_query, -}; - static struct dentry *dell_laptop_dir; static int dell_debugfs_show(struct seq_file *s, void *data) @@ -462,108 +368,6 @@ static const struct file_operations dell_debugfs_fops = { .release = single_release, }; -static void dell_update_rfkill(struct work_struct *ignored) -{ - if (wifi_rfkill) - dell_rfkill_query(wifi_rfkill, (void *)1); - if (bluetooth_rfkill) - dell_rfkill_query(bluetooth_rfkill, (void *)2); - if (wwan_rfkill) - dell_rfkill_query(wwan_rfkill, (void *)3); -} -static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill); - - -static int __init dell_setup_rfkill(void) -{ - int status; - int ret; - - if (dmi_check_system(dell_blacklist)) { - pr_info("Blacklisted hardware detected - not enabling rfkill\n"); - return 0; - } - - get_buffer(); - dell_send_request(buffer, 17, 11); - status = buffer->output[1]; - buffer->input[0] = 0x2; - dell_send_request(buffer, 17, 11); - hwswitch_state = buffer->output[1]; - release_buffer(); - - if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { - wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev, - RFKILL_TYPE_WLAN, - &dell_rfkill_ops, (void *) 1); - if (!wifi_rfkill) { - ret = -ENOMEM; - goto err_wifi; - } - ret = rfkill_register(wifi_rfkill); - if (ret) - goto err_wifi; - } - - if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) { - bluetooth_rfkill = rfkill_alloc("dell-bluetooth", - &platform_device->dev, - RFKILL_TYPE_BLUETOOTH, - &dell_rfkill_ops, (void *) 2); - if (!bluetooth_rfkill) { - ret = -ENOMEM; - goto err_bluetooth; - } - ret = rfkill_register(bluetooth_rfkill); - if (ret) - goto err_bluetooth; - } - - if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) { - wwan_rfkill = rfkill_alloc("dell-wwan", - &platform_device->dev, - RFKILL_TYPE_WWAN, - &dell_rfkill_ops, (void *) 3); - if (!wwan_rfkill) { - ret = -ENOMEM; - goto err_wwan; - } - ret = rfkill_register(wwan_rfkill); - if (ret) - goto err_wwan; - } - - return 0; -err_wwan: - rfkill_destroy(wwan_rfkill); - if (bluetooth_rfkill) - rfkill_unregister(bluetooth_rfkill); -err_bluetooth: - rfkill_destroy(bluetooth_rfkill); - if (wifi_rfkill) - rfkill_unregister(wifi_rfkill); -err_wifi: - rfkill_destroy(wifi_rfkill); - - return ret; -} - -static void dell_cleanup_rfkill(void) -{ - if (wifi_rfkill) { - rfkill_unregister(wifi_rfkill); - rfkill_destroy(wifi_rfkill); - } - if (bluetooth_rfkill) { - rfkill_unregister(bluetooth_rfkill); - rfkill_destroy(bluetooth_rfkill); - } - if (wwan_rfkill) { - rfkill_unregister(wwan_rfkill); - rfkill_destroy(wwan_rfkill); - } -} - static int dell_send_intensity(struct backlight_device *bd) { int ret = 0; @@ -655,30 +459,6 @@ static void touchpad_led_exit(void) led_classdev_unregister(&touchpad_led); } -static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, - struct serio *port) -{ - static bool extended; - - if (str & 0x20) - return false; - - if (unlikely(data == 0xe0)) { - extended = true; - return false; - } else if (unlikely(extended)) { - switch (data) { - case 0x8: - schedule_delayed_work(&dell_rfkill_work, - round_jiffies_relative(HZ)); - break; - } - extended = false; - } - - return false; -} - static int __init dell_init(void) { int max_intensity = 0; @@ -720,26 +500,10 @@ static int __init dell_init(void) goto fail_buffer; buffer = page_address(bufferpage); - ret = dell_setup_rfkill(); - - if (ret) { - pr_warn("Unable to setup rfkill\n"); - goto fail_rfkill; - } - - ret = i8042_install_filter(dell_laptop_i8042_filter); - if (ret) { - pr_warn("Unable to install key filter\n"); - goto fail_filter; - } - if (quirks && quirks->touchpad_led) touchpad_led_init(&platform_device->dev); dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); - if (dell_laptop_dir != NULL) - debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL, - &dell_debugfs_fops); #ifdef CONFIG_ACPI /* In the event of an ACPI backlight being available, don't @@ -782,11 +546,6 @@ static int __init dell_init(void) return 0; fail_backlight: - i8042_remove_filter(dell_laptop_i8042_filter); - cancel_delayed_work_sync(&dell_rfkill_work); -fail_filter: - dell_cleanup_rfkill(); -fail_rfkill: free_page((unsigned long)bufferpage); fail_buffer: platform_device_del(platform_device); @@ -804,10 +563,7 @@ static void __exit dell_exit(void) debugfs_remove_recursive(dell_laptop_dir); if (quirks && quirks->touchpad_led) touchpad_led_exit(); - i8042_remove_filter(dell_laptop_i8042_filter); - cancel_delayed_work_sync(&dell_rfkill_work); backlight_device_unregister(dell_backlight_device); - dell_cleanup_rfkill(); if (platform_device) { platform_device_unregister(platform_device); platform_driver_unregister(&platform_driver); diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c index 580d80a..da267ea 100644 --- a/drivers/platform/x86/fujitsu-tablet.c +++ b/drivers/platform/x86/fujitsu-tablet.c @@ -16,6 +16,8 @@ * 59 Temple Place Suite 330, Boston, MA 02111-1307, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -34,7 +36,8 @@ #define ACPI_FUJITSU_CLASS "fujitsu" #define INVERT_TABLET_MODE_BIT 0x01 -#define FORCE_TABLET_MODE_IF_UNDOCK 0x02 +#define INVERT_DOCK_STATE_BIT 0x02 +#define FORCE_TABLET_MODE_IF_UNDOCK 0x04 #define KEYMAP_LEN 16 @@ -161,6 +164,8 @@ static void fujitsu_send_state(void) state = fujitsu_read_register(0xdd); dock = state & 0x02; + if (fujitsu.config.quirks & INVERT_DOCK_STATE_BIT) + dock = !dock; if ((fujitsu.config.quirks & FORCE_TABLET_MODE_IF_UNDOCK) && (!dock)) { tablet_mode = 1; @@ -221,9 +226,6 @@ static int __devinit input_fujitsu_setup(struct device *parent, input_set_capability(idev, EV_SW, SW_DOCK); input_set_capability(idev, EV_SW, SW_TABLET_MODE); - input_set_capability(idev, EV_SW, SW_DOCK); - input_set_capability(idev, EV_SW, SW_TABLET_MODE); - error = input_register_device(idev); if (error) { input_free_device(idev); @@ -275,25 +277,31 @@ static irqreturn_t fujitsu_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int __devinit fujitsu_dmi_default(const struct dmi_system_id *dmi) +static void __devinit fujitsu_dmi_common(const struct dmi_system_id *dmi) { - printk(KERN_INFO MODULENAME ": %s\n", dmi->ident); + pr_info("%s\n", dmi->ident); memcpy(fujitsu.config.keymap, dmi->driver_data, sizeof(fujitsu.config.keymap)); +} + +static int __devinit fujitsu_dmi_lifebook(const struct dmi_system_id *dmi) +{ + fujitsu_dmi_common(dmi); + fujitsu.config.quirks |= INVERT_TABLET_MODE_BIT; return 1; } static int __devinit fujitsu_dmi_stylistic(const struct dmi_system_id *dmi) { - fujitsu_dmi_default(dmi); + fujitsu_dmi_common(dmi); fujitsu.config.quirks |= FORCE_TABLET_MODE_IF_UNDOCK; - fujitsu.config.quirks |= INVERT_TABLET_MODE_BIT; + fujitsu.config.quirks |= INVERT_DOCK_STATE_BIT; return 1; } static struct dmi_system_id dmi_ids[] __initconst = { { - .callback = fujitsu_dmi_default, + .callback = fujitsu_dmi_lifebook, .ident = "Fujitsu Siemens P/T Series", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), @@ -302,7 +310,7 @@ static struct dmi_system_id dmi_ids[] __initconst = { .driver_data = keymap_Lifebook_Tseries }, { - .callback = fujitsu_dmi_default, + .callback = fujitsu_dmi_lifebook, .ident = "Fujitsu Lifebook T Series", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), @@ -320,7 +328,7 @@ static struct dmi_system_id dmi_ids[] __initconst = { .driver_data = keymap_Stylistic_Tseries }, { - .callback = fujitsu_dmi_default, + .callback = fujitsu_dmi_lifebook, .ident = "Fujitsu LifeBook U810", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), @@ -347,7 +355,7 @@ static struct dmi_system_id dmi_ids[] __initconst = { .driver_data = keymap_Stylistic_ST5xxx }, { - .callback = fujitsu_dmi_default, + .callback = fujitsu_dmi_lifebook, .ident = "Unknown (using defaults)", .matches = { DMI_MATCH(DMI_SYS_VENDOR, ""), @@ -473,6 +481,6 @@ module_exit(fujitsu_module_exit); MODULE_AUTHOR("Robert Gerlach <khnz@gmx.de>"); MODULE_DESCRIPTION("Fujitsu tablet pc extras driver"); MODULE_LICENSE("GPL"); -MODULE_VERSION("2.4"); +MODULE_VERSION("2.5"); MODULE_DEVICE_TABLE(acpi, fujitsu_ids); diff --git a/drivers/platform/x86/hdaps.c b/drivers/platform/x86/hdaps.c index 7387f97..24a3ae0 100644 --- a/drivers/platform/x86/hdaps.c +++ b/drivers/platform/x86/hdaps.c @@ -2,7 +2,7 @@ * hdaps.c - driver for IBM's Hard Drive Active Protection System * * Copyright (C) 2005 Robert Love <rml@novell.com> - * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com> + * Copyright (C) 2005 Jesper Juhl <jj@chaosbits.net> * * The HardDisk Active Protection System (hdaps) is present in IBM ThinkPads * starting with the R40, T41, and X40. It provides a basic two-axis diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index e2faa3c..387183a 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -634,6 +634,8 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device) RFKILL_TYPE_WLAN, &hp_wmi_rfkill_ops, (void *) HPWMI_WIFI); + if (!wifi_rfkill) + return -ENOMEM; rfkill_init_sw_state(wifi_rfkill, hp_wmi_get_sw_state(HPWMI_WIFI)); rfkill_set_hw_state(wifi_rfkill, @@ -648,6 +650,10 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device) RFKILL_TYPE_BLUETOOTH, &hp_wmi_rfkill_ops, (void *) HPWMI_BLUETOOTH); + if (!bluetooth_rfkill) { + err = -ENOMEM; + goto register_wifi_error; + } rfkill_init_sw_state(bluetooth_rfkill, hp_wmi_get_sw_state(HPWMI_BLUETOOTH)); rfkill_set_hw_state(bluetooth_rfkill, @@ -662,6 +668,10 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device) RFKILL_TYPE_WWAN, &hp_wmi_rfkill_ops, (void *) HPWMI_WWAN); + if (!wwan_rfkill) { + err = -ENOMEM; + goto register_bluetooth_error; + } rfkill_init_sw_state(wwan_rfkill, hp_wmi_get_sw_state(HPWMI_WWAN)); rfkill_set_hw_state(wwan_rfkill, diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index ac902f7..4f20f8d 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -194,7 +194,6 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) /* * debugfs */ -#define DEBUGFS_EVENT_LEN (4096) static int debugfs_status_show(struct seq_file *s, void *data) { unsigned long value; @@ -315,7 +314,7 @@ static int __devinit ideapad_debugfs_init(struct ideapad_private *priv) node = debugfs_create_file("status", S_IRUGO, priv->debug, NULL, &debugfs_status_fops); if (!node) { - pr_err("failed to create event in debugfs"); + pr_err("failed to create status in debugfs"); goto errout; } @@ -785,6 +784,10 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) case 9: ideapad_sync_rfk_state(priv); break; + case 13: + case 6: + ideapad_input_report(priv, vpc_bit); + break; case 4: ideapad_backlight_notify_brightness(priv); break; @@ -795,7 +798,7 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) ideapad_backlight_notify_power(priv); break; default: - ideapad_input_report(priv, vpc_bit); + pr_info("Unknown event: %lu\n", vpc_bit); } } } diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 8a51795..210d4ae 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -141,6 +141,27 @@ MODULE_PARM_DESC(kbd_backlight_timeout, "(default: 0)"); static void sony_nc_kbd_backlight_resume(void); +static int sony_nc_kbd_backlight_setup(struct platform_device *pd, + unsigned int handle); +static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd); + +static int sony_nc_battery_care_setup(struct platform_device *pd, + unsigned int handle); +static void sony_nc_battery_care_cleanup(struct platform_device *pd); + +static int sony_nc_thermal_setup(struct platform_device *pd); +static void sony_nc_thermal_cleanup(struct platform_device *pd); +static void sony_nc_thermal_resume(void); + +static int sony_nc_lid_resume_setup(struct platform_device *pd); +static void sony_nc_lid_resume_cleanup(struct platform_device *pd); + +static int sony_nc_highspeed_charging_setup(struct platform_device *pd); +static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd); + +static int sony_nc_touchpad_setup(struct platform_device *pd, + unsigned int handle); +static void sony_nc_touchpad_cleanup(struct platform_device *pd); enum sony_nc_rfkill { SONY_WIFI, @@ -153,6 +174,9 @@ enum sony_nc_rfkill { static int sony_rfkill_handle; static struct rfkill *sony_rfkill_devices[N_SONY_RFKILL]; static int sony_rfkill_address[N_SONY_RFKILL] = {0x300, 0x500, 0x700, 0x900}; +static int sony_nc_rfkill_setup(struct acpi_device *device, + unsigned int handle); +static void sony_nc_rfkill_cleanup(void); static void sony_nc_rfkill_update(void); /*********** Input Devices ***********/ @@ -691,59 +715,97 @@ static struct acpi_device *sony_nc_acpi_device = NULL; /* * acpi_evaluate_object wrappers + * all useful calls into SNC methods take one or zero parameters and return + * integers or arrays. */ -static int acpi_callgetfunc(acpi_handle handle, char *name, int *result) +static union acpi_object *__call_snc_method(acpi_handle handle, char *method, + u64 *value) { - struct acpi_buffer output; - union acpi_object out_obj; + union acpi_object *result = NULL; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; acpi_status status; - output.length = sizeof(out_obj); - output.pointer = &out_obj; + if (value) { + struct acpi_object_list params; + union acpi_object in; + in.type = ACPI_TYPE_INTEGER; + in.integer.value = *value; + params.count = 1; + params.pointer = ∈ + status = acpi_evaluate_object(handle, method, ¶ms, &output); + dprintk("__call_snc_method: [%s:0x%.8x%.8x]\n", method, + (unsigned int)(*value >> 32), + (unsigned int)*value & 0xffffffff); + } else { + status = acpi_evaluate_object(handle, method, NULL, &output); + dprintk("__call_snc_method: [%s]\n", method); + } - status = acpi_evaluate_object(handle, name, NULL, &output); - if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) { - *result = out_obj.integer.value; - return 0; + if (ACPI_FAILURE(status)) { + pr_err("Failed to evaluate [%s]\n", method); + return NULL; } - pr_warn("acpi_callreadfunc failed\n"); + result = (union acpi_object *) output.pointer; + if (!result) + dprintk("No return object [%s]\n", method); - return -1; + return result; } -static int acpi_callsetfunc(acpi_handle handle, char *name, int value, - int *result) +static int sony_nc_int_call(acpi_handle handle, char *name, int *value, + int *result) { - struct acpi_object_list params; - union acpi_object in_obj; - struct acpi_buffer output; - union acpi_object out_obj; - acpi_status status; - - params.count = 1; - params.pointer = &in_obj; - in_obj.type = ACPI_TYPE_INTEGER; - in_obj.integer.value = value; + union acpi_object *object = NULL; + if (value) { + u64 v = *value; + object = __call_snc_method(handle, name, &v); + } else + object = __call_snc_method(handle, name, NULL); - output.length = sizeof(out_obj); - output.pointer = &out_obj; + if (!object) + return -EINVAL; - status = acpi_evaluate_object(handle, name, ¶ms, &output); - if (status == AE_OK) { - if (result != NULL) { - if (out_obj.type != ACPI_TYPE_INTEGER) { - pr_warn("acpi_evaluate_object bad return type\n"); - return -1; - } - *result = out_obj.integer.value; - } - return 0; + if (object->type != ACPI_TYPE_INTEGER) { + pr_warn("Invalid acpi_object: expected 0x%x got 0x%x\n", + ACPI_TYPE_INTEGER, object->type); + kfree(object); + return -EINVAL; } - pr_warn("acpi_evaluate_object failed\n"); + if (result) + *result = object->integer.value; + + kfree(object); + return 0; +} + +#define MIN(a, b) (a > b ? b : a) +static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value, + void *buffer, size_t buflen) +{ + size_t len = len; + union acpi_object *object = __call_snc_method(handle, name, value); + + if (!object) + return -EINVAL; + + if (object->type == ACPI_TYPE_BUFFER) + len = MIN(buflen, object->buffer.length); + + else if (object->type == ACPI_TYPE_INTEGER) + len = MIN(buflen, sizeof(object->integer.value)); + + else { + pr_warn("Invalid acpi_object: expected 0x%x got 0x%x\n", + ACPI_TYPE_BUFFER, object->type); + kfree(object); + return -EINVAL; + } - return -1; + memcpy(buffer, object->buffer.pointer, len); + kfree(object); + return 0; } struct sony_nc_handles { @@ -770,16 +832,17 @@ static ssize_t sony_nc_handles_show(struct device *dev, static int sony_nc_handles_setup(struct platform_device *pd) { - int i; - int result; + int i, r, result, arg; handles = kzalloc(sizeof(*handles), GFP_KERNEL); if (!handles) return -ENOMEM; for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { - if (!acpi_callsetfunc(sony_nc_acpi_handle, - "SN00", i + 0x20, &result)) { + arg = i + 0x20; + r = sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg, + &result); + if (!r) { dprintk("caching handle 0x%.4x (offset: 0x%.2x)\n", result, i); handles->cap[i] = result; @@ -819,8 +882,8 @@ static int sony_find_snc_handle(int handle) int i; /* not initialized yet, return early */ - if (!handles) - return -1; + if (!handles || !handle) + return -EINVAL; for (i = 0; i < 0x10; i++) { if (handles->cap[i] == handle) { @@ -830,21 +893,20 @@ static int sony_find_snc_handle(int handle) } } dprintk("handle 0x%.4x not found\n", handle); - return -1; + return -EINVAL; } static int sony_call_snc_handle(int handle, int argument, int *result) { - int ret = 0; + int arg, ret = 0; int offset = sony_find_snc_handle(handle); if (offset < 0) - return -1; + return offset; - ret = acpi_callsetfunc(sony_nc_acpi_handle, "SN07", offset | argument, - result); - dprintk("called SN07 with 0x%.4x (result: 0x%.4x)\n", offset | argument, - *result); + arg = offset | argument; + ret = sony_nc_int_call(sony_nc_acpi_handle, "SN07", &arg, result); + dprintk("called SN07 with 0x%.4x (result: 0x%.4x)\n", arg, *result); return ret; } @@ -889,14 +951,16 @@ static int boolean_validate(const int direction, const int value) static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *attr, char *buffer) { - int value; + int value, ret = 0; struct sony_nc_value *item = container_of(attr, struct sony_nc_value, devattr); if (!*item->acpiget) return -EIO; - if (acpi_callgetfunc(sony_nc_acpi_handle, *item->acpiget, &value) < 0) + ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiget, NULL, + &value); + if (ret < 0) return -EIO; if (item->validate) @@ -909,7 +973,8 @@ static ssize_t sony_nc_sysfs_store(struct device *dev, struct device_attribute *attr, const char *buffer, size_t count) { - int value; + unsigned long value = 0; + int ret = 0; struct sony_nc_value *item = container_of(attr, struct sony_nc_value, devattr); @@ -919,7 +984,8 @@ static ssize_t sony_nc_sysfs_store(struct device *dev, if (count > 31) return -EINVAL; - value = simple_strtoul(buffer, NULL, 10); + if (kstrtoul(buffer, 10, &value)) + return -EINVAL; if (item->validate) value = item->validate(SNC_VALIDATE_IN, value); @@ -927,8 +993,11 @@ static ssize_t sony_nc_sysfs_store(struct device *dev, if (value < 0) return value; - if (acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, value, NULL) < 0) + ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiset, + (int *)&value, NULL); + if (ret < 0) return -EIO; + item->value = value; item->valid = 1; return count; @@ -948,15 +1017,15 @@ struct sony_backlight_props sony_bl_props; static int sony_backlight_update_status(struct backlight_device *bd) { - return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT", - bd->props.brightness + 1, NULL); + int arg = bd->props.brightness + 1; + return sony_nc_int_call(sony_nc_acpi_handle, "SBRT", &arg, NULL); } static int sony_backlight_get_brightness(struct backlight_device *bd) { int value; - if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) + if (sony_nc_int_call(sony_nc_acpi_handle, "GBRT", NULL, &value)) return 0; /* brightness levels are 1-based, while backlight ones are 0-based */ return value - 1; @@ -1024,10 +1093,14 @@ static struct sony_nc_event sony_100_events[] = { { 0x06, SONYPI_EVENT_FNKEY_RELEASED }, { 0x87, SONYPI_EVENT_FNKEY_F7 }, { 0x07, SONYPI_EVENT_FNKEY_RELEASED }, + { 0x88, SONYPI_EVENT_FNKEY_F8 }, + { 0x08, SONYPI_EVENT_FNKEY_RELEASED }, { 0x89, SONYPI_EVENT_FNKEY_F9 }, { 0x09, SONYPI_EVENT_FNKEY_RELEASED }, { 0x8A, SONYPI_EVENT_FNKEY_F10 }, { 0x0A, SONYPI_EVENT_FNKEY_RELEASED }, + { 0x8B, SONYPI_EVENT_FNKEY_F11 }, + { 0x0B, SONYPI_EVENT_FNKEY_RELEASED }, { 0x8C, SONYPI_EVENT_FNKEY_F12 }, { 0x0C, SONYPI_EVENT_FNKEY_RELEASED }, { 0x9d, SONYPI_EVENT_ZOOM_PRESSED }, @@ -1063,63 +1136,116 @@ static struct sony_nc_event sony_127_events[] = { { 0, 0 }, }; +static int sony_nc_hotkeys_decode(u32 event, unsigned int handle) +{ + int ret = -EINVAL; + unsigned int result = 0; + struct sony_nc_event *key_event; + + if (sony_call_snc_handle(handle, 0x200, &result)) { + dprintk("Unable to decode event 0x%.2x 0x%.2x\n", handle, + event); + return -EINVAL; + } + + result &= 0xFF; + + if (handle == 0x0100) + key_event = sony_100_events; + else + key_event = sony_127_events; + + for (; key_event->data; key_event++) { + if (key_event->data == result) { + ret = key_event->event; + break; + } + } + + if (!key_event->data) + pr_info("Unknown hotkey 0x%.2x/0x%.2x (handle 0x%.2x)\n", + event, result, handle); + + return ret; +} + /* * ACPI callbacks */ static void sony_nc_notify(struct acpi_device *device, u32 event) { - u32 ev = event; + u32 real_ev = event; + u8 ev_type = 0; + dprintk("sony_nc_notify, event: 0x%.2x\n", event); + + if (event >= 0x90) { + unsigned int result = 0; + unsigned int arg = 0; + unsigned int handle = 0; + unsigned int offset = event - 0x90; + + if (offset >= ARRAY_SIZE(handles->cap)) { + pr_err("Event 0x%x outside of capabilities list\n", + event); + return; + } + handle = handles->cap[offset]; + + /* list of handles known for generating events */ + switch (handle) { + /* hotkey event */ + case 0x0100: + case 0x0127: + ev_type = 1; + real_ev = sony_nc_hotkeys_decode(event, handle); + + if (real_ev > 0) + sony_laptop_report_input_event(real_ev); + else + /* restore the original event for reporting */ + real_ev = event; - if (ev >= 0x90) { - /* New-style event */ - int result; - int key_handle = 0; - ev -= 0x90; - - if (sony_find_snc_handle(0x100) == ev) - key_handle = 0x100; - if (sony_find_snc_handle(0x127) == ev) - key_handle = 0x127; - - if (key_handle) { - struct sony_nc_event *key_event; - - if (sony_call_snc_handle(key_handle, 0x200, &result)) { - dprintk("sony_nc_notify, unable to decode" - " event 0x%.2x 0x%.2x\n", key_handle, - ev); - /* restore the original event */ - ev = event; - } else { - ev = result & 0xFF; - - if (key_handle == 0x100) - key_event = sony_100_events; - else - key_event = sony_127_events; - - for (; key_event->data; key_event++) { - if (key_event->data == ev) { - ev = key_event->event; - break; - } - } + break; - if (!key_event->data) - pr_info("Unknown event: 0x%x 0x%x\n", - key_handle, ev); - else - sony_laptop_report_input_event(ev); - } - } else if (sony_find_snc_handle(sony_rfkill_handle) == ev) { - sony_nc_rfkill_update(); - return; + /* wlan switch */ + case 0x0124: + case 0x0135: + /* events on this handle are reported when the + * switch changes position or for battery + * events. We'll notify both of them but only + * update the rfkill device status when the + * switch is moved. + */ + ev_type = 2; + sony_call_snc_handle(handle, 0x0100, &result); + real_ev = result & 0x03; + + /* hw switch event */ + if (real_ev == 1) + sony_nc_rfkill_update(); + + break; + + default: + dprintk("Unknown event 0x%x for handle 0x%x\n", + event, handle); + break; } - } else - sony_laptop_report_input_event(ev); - dprintk("sony_nc_notify, event: 0x%.2x\n", ev); - acpi_bus_generate_proc_event(sony_nc_acpi_device, 1, ev); + /* clear the event (and the event reason when present) */ + arg = 1 << offset; + sony_nc_int_call(sony_nc_acpi_handle, "SN05", &arg, &result); + + } else { + /* old style event */ + ev_type = 1; + sony_laptop_report_input_event(real_ev); + } + + acpi_bus_generate_proc_event(sony_nc_acpi_device, ev_type, real_ev); + + acpi_bus_generate_netlink_event(sony_nc_acpi_device->pnp.device_class, + dev_name(&sony_nc_acpi_device->dev), ev_type, real_ev); } static acpi_status sony_walk_callback(acpi_handle handle, u32 level, @@ -1140,20 +1266,190 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level, /* * ACPI device */ -static int sony_nc_function_setup(struct acpi_device *device) +static void sony_nc_function_setup(struct acpi_device *device, + struct platform_device *pf_device) { - int result; + unsigned int i, result, bitmask, arg; + + if (!handles) + return; + + /* setup found handles here */ + for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { + unsigned int handle = handles->cap[i]; + + if (!handle) + continue; + + dprintk("setting up handle 0x%.4x\n", handle); + + switch (handle) { + case 0x0100: + case 0x0101: + case 0x0127: + /* setup hotkeys */ + sony_call_snc_handle(handle, 0, &result); + break; + case 0x0102: + /* setup hotkeys */ + sony_call_snc_handle(handle, 0x100, &result); + break; + case 0x0105: + case 0x0148: + /* touchpad enable/disable */ + result = sony_nc_touchpad_setup(pf_device, handle); + if (result) + pr_err("couldn't set up touchpad control function (%d)\n", + result); + break; + case 0x0115: + case 0x0136: + case 0x013f: + result = sony_nc_battery_care_setup(pf_device, handle); + if (result) + pr_err("couldn't set up battery care function (%d)\n", + result); + break; + case 0x0119: + result = sony_nc_lid_resume_setup(pf_device); + if (result) + pr_err("couldn't set up lid resume function (%d)\n", + result); + break; + case 0x0122: + result = sony_nc_thermal_setup(pf_device); + if (result) + pr_err("couldn't set up thermal profile function (%d)\n", + result); + break; + case 0x0131: + result = sony_nc_highspeed_charging_setup(pf_device); + if (result) + pr_err("couldn't set up high speed charging function (%d)\n", + result); + break; + case 0x0124: + case 0x0135: + result = sony_nc_rfkill_setup(device, handle); + if (result) + pr_err("couldn't set up rfkill support (%d)\n", + result); + break; + case 0x0137: + case 0x0143: + result = sony_nc_kbd_backlight_setup(pf_device, handle); + if (result) + pr_err("couldn't set up keyboard backlight function (%d)\n", + result); + break; + default: + continue; + } + } /* Enable all events */ - acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0xffff, &result); + arg = 0x10; + if (!sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg, &bitmask)) + sony_nc_int_call(sony_nc_acpi_handle, "SN02", &bitmask, + &result); +} + +static void sony_nc_function_cleanup(struct platform_device *pd) +{ + unsigned int i, result, bitmask, handle; - /* Setup hotkeys */ - sony_call_snc_handle(0x0100, 0, &result); - sony_call_snc_handle(0x0101, 0, &result); - sony_call_snc_handle(0x0102, 0x100, &result); - sony_call_snc_handle(0x0127, 0, &result); + /* get enabled events and disable them */ + sony_nc_int_call(sony_nc_acpi_handle, "SN01", NULL, &bitmask); + sony_nc_int_call(sony_nc_acpi_handle, "SN03", &bitmask, &result); - return 0; + /* cleanup handles here */ + for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { + + handle = handles->cap[i]; + + if (!handle) + continue; + + switch (handle) { + case 0x0105: + case 0x0148: + sony_nc_touchpad_cleanup(pd); + break; + case 0x0115: + case 0x0136: + case 0x013f: + sony_nc_battery_care_cleanup(pd); + break; + case 0x0119: + sony_nc_lid_resume_cleanup(pd); + break; + case 0x0122: + sony_nc_thermal_cleanup(pd); + break; + case 0x0131: + sony_nc_highspeed_charging_cleanup(pd); + break; + case 0x0124: + case 0x0135: + sony_nc_rfkill_cleanup(); + break; + case 0x0137: + case 0x0143: + sony_nc_kbd_backlight_cleanup(pd); + break; + default: + continue; + } + } + + /* finally cleanup the handles list */ + sony_nc_handles_cleanup(pd); +} + +static void sony_nc_function_resume(void) +{ + unsigned int i, result, bitmask, arg; + + dprintk("Resuming SNC device\n"); + + for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { + unsigned int handle = handles->cap[i]; + + if (!handle) + continue; + + switch (handle) { + case 0x0100: + case 0x0101: + case 0x0127: + /* re-enable hotkeys */ + sony_call_snc_handle(handle, 0, &result); + break; + case 0x0102: + /* re-enable hotkeys */ + sony_call_snc_handle(handle, 0x100, &result); + break; + case 0x0122: + sony_nc_thermal_resume(); + break; + case 0x0124: + case 0x0135: + sony_nc_rfkill_update(); + break; + case 0x0137: + case 0x0143: + sony_nc_kbd_backlight_resume(); + break; + default: + continue; + } + } + + /* Enable all events */ + arg = 0x10; + if (!sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg, &bitmask)) + sony_nc_int_call(sony_nc_acpi_handle, "SN02", &bitmask, + &result); } static int sony_nc_resume(struct acpi_device *device) @@ -1166,8 +1462,8 @@ static int sony_nc_resume(struct acpi_device *device) if (!item->valid) continue; - ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, - item->value, NULL); + ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiset, + &item->value, NULL); if (ret < 0) { pr_err("%s: %d\n", __func__, ret); break; @@ -1176,21 +1472,14 @@ static int sony_nc_resume(struct acpi_device *device) if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON", &handle))) { - if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, NULL)) + int arg = 1; + if (sony_nc_int_call(sony_nc_acpi_handle, "ECON", &arg, NULL)) dprintk("ECON Method failed\n"); } if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", - &handle))) { - dprintk("Doing SNC setup\n"); - sony_nc_function_setup(device); - } - - /* re-read rfkill state */ - sony_nc_rfkill_update(); - - /* restore kbd backlight states */ - sony_nc_kbd_backlight_resume(); + &handle))) + sony_nc_function_resume(); return 0; } @@ -1213,7 +1502,7 @@ static int sony_nc_rfkill_set(void *data, bool blocked) int argument = sony_rfkill_address[(long) data] + 0x100; if (!blocked) - argument |= 0xff0000; + argument |= 0x030000; return sony_call_snc_handle(sony_rfkill_handle, argument, &result); } @@ -1230,7 +1519,7 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, enum rfkill_type type; const char *name; int result; - bool hwblock; + bool hwblock, swblock; switch (nc_type) { case SONY_WIFI: @@ -1258,8 +1547,21 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, if (!rfk) return -ENOMEM; - sony_call_snc_handle(sony_rfkill_handle, 0x200, &result); + if (sony_call_snc_handle(sony_rfkill_handle, 0x200, &result) < 0) { + rfkill_destroy(rfk); + return -1; + } hwblock = !(result & 0x1); + + if (sony_call_snc_handle(sony_rfkill_handle, + sony_rfkill_address[nc_type], + &result) < 0) { + rfkill_destroy(rfk); + return -1; + } + swblock = !(result & 0x2); + + rfkill_init_sw_state(rfk, swblock); rfkill_set_hw_state(rfk, hwblock); err = rfkill_register(rfk); @@ -1295,101 +1597,79 @@ static void sony_nc_rfkill_update(void) sony_call_snc_handle(sony_rfkill_handle, argument, &result); rfkill_set_states(sony_rfkill_devices[i], - !(result & 0xf), false); + !(result & 0x2), false); } } -static void sony_nc_rfkill_setup(struct acpi_device *device) +static int sony_nc_rfkill_setup(struct acpi_device *device, + unsigned int handle) { - int offset; - u8 dev_code, i; - acpi_status status; - struct acpi_object_list params; - union acpi_object in_obj; - union acpi_object *device_enum; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - - offset = sony_find_snc_handle(0x124); - if (offset == -1) { - offset = sony_find_snc_handle(0x135); - if (offset == -1) - return; - else - sony_rfkill_handle = 0x135; - } else - sony_rfkill_handle = 0x124; - dprintk("Found rkfill handle: 0x%.4x\n", sony_rfkill_handle); - - /* need to read the whole buffer returned by the acpi call to SN06 - * here otherwise we may miss some features - */ - params.count = 1; - params.pointer = &in_obj; - in_obj.type = ACPI_TYPE_INTEGER; - in_obj.integer.value = offset; - status = acpi_evaluate_object(sony_nc_acpi_handle, "SN06", ¶ms, - &buffer); - if (ACPI_FAILURE(status)) { - dprintk("Radio device enumeration failed\n"); - return; - } - - device_enum = (union acpi_object *) buffer.pointer; - if (!device_enum) { - pr_err("No SN06 return object\n"); - goto out_no_enum; - } - if (device_enum->type != ACPI_TYPE_BUFFER) { - pr_err("Invalid SN06 return object 0x%.2x\n", - device_enum->type); - goto out_no_enum; - } + u64 offset; + int i; + unsigned char buffer[32] = { 0 }; - /* the buffer is filled with magic numbers describing the devices - * available, 0xff terminates the enumeration + offset = sony_find_snc_handle(handle); + sony_rfkill_handle = handle; + + i = sony_nc_buffer_call(sony_nc_acpi_handle, "SN06", &offset, buffer, + 32); + if (i < 0) + return i; + + /* The buffer is filled with magic numbers describing the devices + * available, 0xff terminates the enumeration. + * Known codes: + * 0x00 WLAN + * 0x10 BLUETOOTH + * 0x20 WWAN GPRS-EDGE + * 0x21 WWAN HSDPA + * 0x22 WWAN EV-DO + * 0x23 WWAN GPS + * 0x25 Gobi WWAN no GPS + * 0x26 Gobi WWAN + GPS + * 0x28 Gobi WWAN no GPS + * 0x29 Gobi WWAN + GPS + * 0x30 WIMAX + * 0x50 Gobi WWAN no GPS + * 0x51 Gobi WWAN + GPS + * 0x70 no SIM card slot + * 0x71 SIM card slot */ - for (i = 0; i < device_enum->buffer.length; i++) { + for (i = 0; i < ARRAY_SIZE(buffer); i++) { - dev_code = *(device_enum->buffer.pointer + i); - if (dev_code == 0xff) + if (buffer[i] == 0xff) break; - dprintk("Radio devices, looking at 0x%.2x\n", dev_code); + dprintk("Radio devices, found 0x%.2x\n", buffer[i]); - if (dev_code == 0 && !sony_rfkill_devices[SONY_WIFI]) + if (buffer[i] == 0 && !sony_rfkill_devices[SONY_WIFI]) sony_nc_setup_rfkill(device, SONY_WIFI); - if (dev_code == 0x10 && !sony_rfkill_devices[SONY_BLUETOOTH]) + if (buffer[i] == 0x10 && !sony_rfkill_devices[SONY_BLUETOOTH]) sony_nc_setup_rfkill(device, SONY_BLUETOOTH); - if ((0xf0 & dev_code) == 0x20 && + if (((0xf0 & buffer[i]) == 0x20 || + (0xf0 & buffer[i]) == 0x50) && !sony_rfkill_devices[SONY_WWAN]) sony_nc_setup_rfkill(device, SONY_WWAN); - if (dev_code == 0x30 && !sony_rfkill_devices[SONY_WIMAX]) + if (buffer[i] == 0x30 && !sony_rfkill_devices[SONY_WIMAX]) sony_nc_setup_rfkill(device, SONY_WIMAX); } - -out_no_enum: - kfree(buffer.pointer); - return; + return 0; } /* Keyboard backlight feature */ -#define KBDBL_HANDLER 0x137 -#define KBDBL_PRESENT 0xB00 -#define SET_MODE 0xC00 -#define SET_STATE 0xD00 -#define SET_TIMEOUT 0xE00 - struct kbd_backlight { - int mode; - int timeout; + unsigned int handle; + unsigned int base; + unsigned int mode; + unsigned int timeout; struct device_attribute mode_attr; struct device_attribute timeout_attr; }; -static struct kbd_backlight *kbdbl_handle; +static struct kbd_backlight *kbdbl_ctl; static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value) { @@ -1398,15 +1678,15 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value) if (value > 1) return -EINVAL; - if (sony_call_snc_handle(KBDBL_HANDLER, - (value << 0x10) | SET_MODE, &result)) + if (sony_call_snc_handle(kbdbl_ctl->handle, + (value << 0x10) | (kbdbl_ctl->base), &result)) return -EIO; /* Try to turn the light on/off immediately */ - sony_call_snc_handle(KBDBL_HANDLER, (value << 0x10) | SET_STATE, - &result); + sony_call_snc_handle(kbdbl_ctl->handle, + (value << 0x10) | (kbdbl_ctl->base + 0x100), &result); - kbdbl_handle->mode = value; + kbdbl_ctl->mode = value; return 0; } @@ -1421,7 +1701,7 @@ static ssize_t sony_nc_kbd_backlight_mode_store(struct device *dev, if (count > 31) return -EINVAL; - if (strict_strtoul(buffer, 10, &value)) + if (kstrtoul(buffer, 10, &value)) return -EINVAL; ret = __sony_nc_kbd_backlight_mode_set(value); @@ -1435,7 +1715,7 @@ static ssize_t sony_nc_kbd_backlight_mode_show(struct device *dev, struct device_attribute *attr, char *buffer) { ssize_t count = 0; - count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_handle->mode); + count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_ctl->mode); return count; } @@ -1446,11 +1726,11 @@ static int __sony_nc_kbd_backlight_timeout_set(u8 value) if (value > 3) return -EINVAL; - if (sony_call_snc_handle(KBDBL_HANDLER, - (value << 0x10) | SET_TIMEOUT, &result)) + if (sony_call_snc_handle(kbdbl_ctl->handle, (value << 0x10) | + (kbdbl_ctl->base + 0x200), &result)) return -EIO; - kbdbl_handle->timeout = value; + kbdbl_ctl->timeout = value; return 0; } @@ -1465,7 +1745,7 @@ static ssize_t sony_nc_kbd_backlight_timeout_store(struct device *dev, if (count > 31) return -EINVAL; - if (strict_strtoul(buffer, 10, &value)) + if (kstrtoul(buffer, 10, &value)) return -EINVAL; ret = __sony_nc_kbd_backlight_timeout_set(value); @@ -1479,39 +1759,58 @@ static ssize_t sony_nc_kbd_backlight_timeout_show(struct device *dev, struct device_attribute *attr, char *buffer) { ssize_t count = 0; - count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_handle->timeout); + count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_ctl->timeout); return count; } -static int sony_nc_kbd_backlight_setup(struct platform_device *pd) +static int sony_nc_kbd_backlight_setup(struct platform_device *pd, + unsigned int handle) { int result; + int ret = 0; - if (sony_call_snc_handle(KBDBL_HANDLER, KBDBL_PRESENT, &result)) - return 0; - if (!(result & 0x02)) + /* verify the kbd backlight presence, these handles are not used for + * keyboard backlight only + */ + ret = sony_call_snc_handle(handle, handle == 0x0137 ? 0x0B00 : 0x0100, + &result); + if (ret) + return ret; + + if ((handle == 0x0137 && !(result & 0x02)) || + !(result & 0x01)) { + dprintk("no backlight keyboard found\n"); return 0; + } - kbdbl_handle = kzalloc(sizeof(*kbdbl_handle), GFP_KERNEL); - if (!kbdbl_handle) + kbdbl_ctl = kzalloc(sizeof(*kbdbl_ctl), GFP_KERNEL); + if (!kbdbl_ctl) return -ENOMEM; - sysfs_attr_init(&kbdbl_handle->mode_attr.attr); - kbdbl_handle->mode_attr.attr.name = "kbd_backlight"; - kbdbl_handle->mode_attr.attr.mode = S_IRUGO | S_IWUSR; - kbdbl_handle->mode_attr.show = sony_nc_kbd_backlight_mode_show; - kbdbl_handle->mode_attr.store = sony_nc_kbd_backlight_mode_store; + kbdbl_ctl->handle = handle; + if (handle == 0x0137) + kbdbl_ctl->base = 0x0C00; + else + kbdbl_ctl->base = 0x4000; + + sysfs_attr_init(&kbdbl_ctl->mode_attr.attr); + kbdbl_ctl->mode_attr.attr.name = "kbd_backlight"; + kbdbl_ctl->mode_attr.attr.mode = S_IRUGO | S_IWUSR; + kbdbl_ctl->mode_attr.show = sony_nc_kbd_backlight_mode_show; + kbdbl_ctl->mode_attr.store = sony_nc_kbd_backlight_mode_store; - sysfs_attr_init(&kbdbl_handle->timeout_attr.attr); - kbdbl_handle->timeout_attr.attr.name = "kbd_backlight_timeout"; - kbdbl_handle->timeout_attr.attr.mode = S_IRUGO | S_IWUSR; - kbdbl_handle->timeout_attr.show = sony_nc_kbd_backlight_timeout_show; - kbdbl_handle->timeout_attr.store = sony_nc_kbd_backlight_timeout_store; + sysfs_attr_init(&kbdbl_ctl->timeout_attr.attr); + kbdbl_ctl->timeout_attr.attr.name = "kbd_backlight_timeout"; + kbdbl_ctl->timeout_attr.attr.mode = S_IRUGO | S_IWUSR; + kbdbl_ctl->timeout_attr.show = sony_nc_kbd_backlight_timeout_show; + kbdbl_ctl->timeout_attr.store = sony_nc_kbd_backlight_timeout_store; - if (device_create_file(&pd->dev, &kbdbl_handle->mode_attr)) + ret = device_create_file(&pd->dev, &kbdbl_ctl->mode_attr); + if (ret) goto outkzalloc; - if (device_create_file(&pd->dev, &kbdbl_handle->timeout_attr)) + ret = device_create_file(&pd->dev, &kbdbl_ctl->timeout_attr); + if (ret) goto outmode; __sony_nc_kbd_backlight_mode_set(kbd_backlight); @@ -1520,57 +1819,661 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd) return 0; outmode: - device_remove_file(&pd->dev, &kbdbl_handle->mode_attr); + device_remove_file(&pd->dev, &kbdbl_ctl->mode_attr); outkzalloc: - kfree(kbdbl_handle); - kbdbl_handle = NULL; - return -1; + kfree(kbdbl_ctl); + kbdbl_ctl = NULL; + return ret; } -static int sony_nc_kbd_backlight_cleanup(struct platform_device *pd) +static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd) { - if (kbdbl_handle) { + if (kbdbl_ctl) { int result; - device_remove_file(&pd->dev, &kbdbl_handle->mode_attr); - device_remove_file(&pd->dev, &kbdbl_handle->timeout_attr); + device_remove_file(&pd->dev, &kbdbl_ctl->mode_attr); + device_remove_file(&pd->dev, &kbdbl_ctl->timeout_attr); /* restore the default hw behaviour */ - sony_call_snc_handle(KBDBL_HANDLER, 0x1000 | SET_MODE, &result); - sony_call_snc_handle(KBDBL_HANDLER, SET_TIMEOUT, &result); + sony_call_snc_handle(kbdbl_ctl->handle, + kbdbl_ctl->base | 0x10000, &result); + sony_call_snc_handle(kbdbl_ctl->handle, + kbdbl_ctl->base + 0x200, &result); - kfree(kbdbl_handle); + kfree(kbdbl_ctl); + kbdbl_ctl = NULL; } - return 0; } static void sony_nc_kbd_backlight_resume(void) { int ignore = 0; - if (!kbdbl_handle) + if (!kbdbl_ctl) return; - if (kbdbl_handle->mode == 0) - sony_call_snc_handle(KBDBL_HANDLER, SET_MODE, &ignore); - - if (kbdbl_handle->timeout != 0) - sony_call_snc_handle(KBDBL_HANDLER, - (kbdbl_handle->timeout << 0x10) | SET_TIMEOUT, + if (kbdbl_ctl->mode == 0) + sony_call_snc_handle(kbdbl_ctl->handle, kbdbl_ctl->base, &ignore); + + if (kbdbl_ctl->timeout != 0) + sony_call_snc_handle(kbdbl_ctl->handle, + (kbdbl_ctl->base + 0x200) | + (kbdbl_ctl->timeout << 0x10), &ignore); +} + +struct battery_care_control { + struct device_attribute attrs[2]; + unsigned int handle; +}; +static struct battery_care_control *bcare_ctl; + +static ssize_t sony_nc_battery_care_limit_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned int result, cmd; + unsigned long value; + + if (count > 31) + return -EINVAL; + + if (kstrtoul(buffer, 10, &value)) + return -EINVAL; + + /* limit values (2 bits): + * 00 - none + * 01 - 80% + * 10 - 50% + * 11 - 100% + * + * bit 0: 0 disable BCL, 1 enable BCL + * bit 1: 1 tell to store the battery limit (see bits 6,7) too + * bits 2,3: reserved + * bits 4,5: store the limit into the EC + * bits 6,7: store the limit into the battery + */ + + /* + * handle 0x0115 should allow storing on battery too; + * handle 0x0136 same as 0x0115 + health status; + * handle 0x013f, same as 0x0136 but no storing on the battery + * + * Store only inside the EC for now, regardless the handle number + */ + if (value == 0) + /* disable limits */ + cmd = 0x0; + + else if (value <= 50) + cmd = 0x21; + + else if (value <= 80) + cmd = 0x11; + + else if (value <= 100) + cmd = 0x31; + + else + return -EINVAL; + + if (sony_call_snc_handle(bcare_ctl->handle, (cmd << 0x10) | 0x0100, + &result)) + return -EIO; + + return count; +} + +static ssize_t sony_nc_battery_care_limit_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int result, status; + + if (sony_call_snc_handle(bcare_ctl->handle, 0x0000, &result)) + return -EIO; + + status = (result & 0x01) ? ((result & 0x30) >> 0x04) : 0; + switch (status) { + case 1: + status = 80; + break; + case 2: + status = 50; + break; + case 3: + status = 100; + break; + default: + status = 0; + break; + } + + return snprintf(buffer, PAGE_SIZE, "%d\n", status); +} + +static ssize_t sony_nc_battery_care_health_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + ssize_t count = 0; + unsigned int health; + + if (sony_call_snc_handle(bcare_ctl->handle, 0x0200, &health)) + return -EIO; + + count = snprintf(buffer, PAGE_SIZE, "%d\n", health & 0xff); + + return count; +} + +static int sony_nc_battery_care_setup(struct platform_device *pd, + unsigned int handle) +{ + int ret = 0; + + bcare_ctl = kzalloc(sizeof(struct battery_care_control), GFP_KERNEL); + if (!bcare_ctl) + return -ENOMEM; + + bcare_ctl->handle = handle; + + sysfs_attr_init(&bcare_ctl->attrs[0].attr); + bcare_ctl->attrs[0].attr.name = "battery_care_limiter"; + bcare_ctl->attrs[0].attr.mode = S_IRUGO | S_IWUSR; + bcare_ctl->attrs[0].show = sony_nc_battery_care_limit_show; + bcare_ctl->attrs[0].store = sony_nc_battery_care_limit_store; + + ret = device_create_file(&pd->dev, &bcare_ctl->attrs[0]); + if (ret) + goto outkzalloc; + + /* 0x0115 is for models with no health reporting capability */ + if (handle == 0x0115) + return 0; + + sysfs_attr_init(&bcare_ctl->attrs[1].attr); + bcare_ctl->attrs[1].attr.name = "battery_care_health"; + bcare_ctl->attrs[1].attr.mode = S_IRUGO; + bcare_ctl->attrs[1].show = sony_nc_battery_care_health_show; + + ret = device_create_file(&pd->dev, &bcare_ctl->attrs[1]); + if (ret) + goto outlimiter; + + return 0; + +outlimiter: + device_remove_file(&pd->dev, &bcare_ctl->attrs[0]); + +outkzalloc: + kfree(bcare_ctl); + bcare_ctl = NULL; + + return ret; +} + +static void sony_nc_battery_care_cleanup(struct platform_device *pd) +{ + if (bcare_ctl) { + device_remove_file(&pd->dev, &bcare_ctl->attrs[0]); + if (bcare_ctl->handle != 0x0115) + device_remove_file(&pd->dev, &bcare_ctl->attrs[1]); + + kfree(bcare_ctl); + bcare_ctl = NULL; + } +} + +struct snc_thermal_ctrl { + unsigned int mode; + unsigned int profiles; + struct device_attribute mode_attr; + struct device_attribute profiles_attr; +}; +static struct snc_thermal_ctrl *th_handle; + +#define THM_PROFILE_MAX 3 +static const char * const snc_thermal_profiles[] = { + "balanced", + "silent", + "performance" +}; + +static int sony_nc_thermal_mode_set(unsigned short mode) +{ + unsigned int result; + + /* the thermal profile seems to be a two bit bitmask: + * lsb -> silent + * msb -> performance + * no bit set is the normal operation and is always valid + * Some vaio models only have "balanced" and "performance" + */ + if ((mode && !(th_handle->profiles & mode)) || mode >= THM_PROFILE_MAX) + return -EINVAL; + + if (sony_call_snc_handle(0x0122, mode << 0x10 | 0x0200, &result)) + return -EIO; + + th_handle->mode = mode; + + return 0; +} + +static int sony_nc_thermal_mode_get(void) +{ + unsigned int result; + + if (sony_call_snc_handle(0x0122, 0x0100, &result)) + return -EIO; + + return result & 0xff; +} + +static ssize_t sony_nc_thermal_profiles_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + short cnt; + size_t idx = 0; + + for (cnt = 0; cnt < THM_PROFILE_MAX; cnt++) { + if (!cnt || (th_handle->profiles & cnt)) + idx += snprintf(buffer + idx, PAGE_SIZE - idx, "%s ", + snc_thermal_profiles[cnt]); + } + idx += snprintf(buffer + idx, PAGE_SIZE - idx, "\n"); + + return idx; +} + +static ssize_t sony_nc_thermal_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned short cmd; + size_t len = count; + + if (count == 0) + return -EINVAL; + + /* skip the newline if present */ + if (buffer[len - 1] == '\n') + len--; + + for (cmd = 0; cmd < THM_PROFILE_MAX; cmd++) + if (strncmp(buffer, snc_thermal_profiles[cmd], len) == 0) + break; + + if (sony_nc_thermal_mode_set(cmd)) + return -EIO; + + return count; +} + +static ssize_t sony_nc_thermal_mode_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + ssize_t count = 0; + unsigned int mode = sony_nc_thermal_mode_get(); + + if (mode < 0) + return mode; + + count = snprintf(buffer, PAGE_SIZE, "%s\n", snc_thermal_profiles[mode]); + + return count; +} + +static int sony_nc_thermal_setup(struct platform_device *pd) +{ + int ret = 0; + th_handle = kzalloc(sizeof(struct snc_thermal_ctrl), GFP_KERNEL); + if (!th_handle) + return -ENOMEM; + + ret = sony_call_snc_handle(0x0122, 0x0000, &th_handle->profiles); + if (ret) { + pr_warn("couldn't to read the thermal profiles\n"); + goto outkzalloc; + } + + ret = sony_nc_thermal_mode_get(); + if (ret < 0) { + pr_warn("couldn't to read the current thermal profile"); + goto outkzalloc; + } + th_handle->mode = ret; + + sysfs_attr_init(&th_handle->profiles_attr.attr); + th_handle->profiles_attr.attr.name = "thermal_profiles"; + th_handle->profiles_attr.attr.mode = S_IRUGO; + th_handle->profiles_attr.show = sony_nc_thermal_profiles_show; + + sysfs_attr_init(&th_handle->mode_attr.attr); + th_handle->mode_attr.attr.name = "thermal_control"; + th_handle->mode_attr.attr.mode = S_IRUGO | S_IWUSR; + th_handle->mode_attr.show = sony_nc_thermal_mode_show; + th_handle->mode_attr.store = sony_nc_thermal_mode_store; + + ret = device_create_file(&pd->dev, &th_handle->profiles_attr); + if (ret) + goto outkzalloc; + + ret = device_create_file(&pd->dev, &th_handle->mode_attr); + if (ret) + goto outprofiles; + + return 0; + +outprofiles: + device_remove_file(&pd->dev, &th_handle->profiles_attr); +outkzalloc: + kfree(th_handle); + th_handle = NULL; + return ret; +} + +static void sony_nc_thermal_cleanup(struct platform_device *pd) +{ + if (th_handle) { + device_remove_file(&pd->dev, &th_handle->profiles_attr); + device_remove_file(&pd->dev, &th_handle->mode_attr); + kfree(th_handle); + th_handle = NULL; + } +} + +static void sony_nc_thermal_resume(void) +{ + unsigned int status = sony_nc_thermal_mode_get(); + + if (status != th_handle->mode) + sony_nc_thermal_mode_set(th_handle->mode); +} + +/* resume on LID open */ +struct snc_lid_resume_control { + struct device_attribute attrs[3]; + unsigned int status; +}; +static struct snc_lid_resume_control *lid_ctl; + +static ssize_t sony_nc_lid_resume_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned int result, pos; + unsigned long value; + if (count > 31) + return -EINVAL; + + if (kstrtoul(buffer, 10, &value) || value > 1) + return -EINVAL; + + /* the value we have to write to SNC is a bitmask: + * +--------------+ + * | S3 | S4 | S5 | + * +--------------+ + * 2 1 0 + */ + if (strcmp(attr->attr.name, "lid_resume_S3") == 0) + pos = 2; + else if (strcmp(attr->attr.name, "lid_resume_S4") == 0) + pos = 1; + else if (strcmp(attr->attr.name, "lid_resume_S5") == 0) + pos = 0; + else + return -EINVAL; + + if (value) + value = lid_ctl->status | (1 << pos); + else + value = lid_ctl->status & ~(1 << pos); + + if (sony_call_snc_handle(0x0119, value << 0x10 | 0x0100, &result)) + return -EIO; + + lid_ctl->status = value; + + return count; +} + +static ssize_t sony_nc_lid_resume_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int pos; + + if (strcmp(attr->attr.name, "lid_resume_S3") == 0) + pos = 2; + else if (strcmp(attr->attr.name, "lid_resume_S4") == 0) + pos = 1; + else if (strcmp(attr->attr.name, "lid_resume_S5") == 0) + pos = 0; + else + return -EINVAL; + + return snprintf(buffer, PAGE_SIZE, "%d\n", + (lid_ctl->status >> pos) & 0x01); +} + +static int sony_nc_lid_resume_setup(struct platform_device *pd) +{ + unsigned int result; + int i; + + if (sony_call_snc_handle(0x0119, 0x0000, &result)) + return -EIO; + + lid_ctl = kzalloc(sizeof(struct snc_lid_resume_control), GFP_KERNEL); + if (!lid_ctl) + return -ENOMEM; + + lid_ctl->status = result & 0x7; + + sysfs_attr_init(&lid_ctl->attrs[0].attr); + lid_ctl->attrs[0].attr.name = "lid_resume_S3"; + lid_ctl->attrs[0].attr.mode = S_IRUGO | S_IWUSR; + lid_ctl->attrs[0].show = sony_nc_lid_resume_show; + lid_ctl->attrs[0].store = sony_nc_lid_resume_store; + + sysfs_attr_init(&lid_ctl->attrs[1].attr); + lid_ctl->attrs[1].attr.name = "lid_resume_S4"; + lid_ctl->attrs[1].attr.mode = S_IRUGO | S_IWUSR; + lid_ctl->attrs[1].show = sony_nc_lid_resume_show; + lid_ctl->attrs[1].store = sony_nc_lid_resume_store; + + sysfs_attr_init(&lid_ctl->attrs[2].attr); + lid_ctl->attrs[2].attr.name = "lid_resume_S5"; + lid_ctl->attrs[2].attr.mode = S_IRUGO | S_IWUSR; + lid_ctl->attrs[2].show = sony_nc_lid_resume_show; + lid_ctl->attrs[2].store = sony_nc_lid_resume_store; + + for (i = 0; i < 3; i++) { + result = device_create_file(&pd->dev, &lid_ctl->attrs[i]); + if (result) + goto liderror; + } + + return 0; + +liderror: + for (; i > 0; i--) + device_remove_file(&pd->dev, &lid_ctl->attrs[i]); + + kfree(lid_ctl); + lid_ctl = NULL; + + return result; +} + +static void sony_nc_lid_resume_cleanup(struct platform_device *pd) +{ + int i; + + if (lid_ctl) { + for (i = 0; i < 3; i++) + device_remove_file(&pd->dev, &lid_ctl->attrs[i]); + + kfree(lid_ctl); + lid_ctl = NULL; + } +} + +/* High speed charging function */ +static struct device_attribute *hsc_handle; + +static ssize_t sony_nc_highspeed_charging_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned int result; + unsigned long value; + + if (count > 31) + return -EINVAL; + + if (kstrtoul(buffer, 10, &value) || value > 1) + return -EINVAL; + + if (sony_call_snc_handle(0x0131, value << 0x10 | 0x0200, &result)) + return -EIO; + + return count; +} + +static ssize_t sony_nc_highspeed_charging_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int result; + + if (sony_call_snc_handle(0x0131, 0x0100, &result)) + return -EIO; + + return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01); +} + +static int sony_nc_highspeed_charging_setup(struct platform_device *pd) +{ + unsigned int result; + + if (sony_call_snc_handle(0x0131, 0x0000, &result) || !(result & 0x01)) { + /* some models advertise the handle but have no implementation + * for it + */ + pr_info("No High Speed Charging capability found\n"); + return 0; + } + + hsc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); + if (!hsc_handle) + return -ENOMEM; + + sysfs_attr_init(&hsc_handle->attr); + hsc_handle->attr.name = "battery_highspeed_charging"; + hsc_handle->attr.mode = S_IRUGO | S_IWUSR; + hsc_handle->show = sony_nc_highspeed_charging_show; + hsc_handle->store = sony_nc_highspeed_charging_store; + + result = device_create_file(&pd->dev, hsc_handle); + if (result) { + kfree(hsc_handle); + hsc_handle = NULL; + return result; + } + + return 0; +} + +static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd) +{ + if (hsc_handle) { + device_remove_file(&pd->dev, hsc_handle); + kfree(hsc_handle); + hsc_handle = NULL; + } +} + +/* Touchpad enable/disable */ +struct touchpad_control { + struct device_attribute attr; + int handle; +}; +static struct touchpad_control *tp_ctl; + +static ssize_t sony_nc_touchpad_store(struct device *dev, + struct device_attribute *attr, const char *buffer, size_t count) +{ + unsigned int result; + unsigned long value; + + if (count > 31) + return -EINVAL; + + if (kstrtoul(buffer, 10, &value) || value > 1) + return -EINVAL; + + /* sysfs: 0 disabled, 1 enabled + * EC: 0 enabled, 1 disabled + */ + if (sony_call_snc_handle(tp_ctl->handle, + (!value << 0x10) | 0x100, &result)) + return -EIO; + + return count; +} + +static ssize_t sony_nc_touchpad_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int result; + + if (sony_call_snc_handle(tp_ctl->handle, 0x000, &result)) + return -EINVAL; + + return snprintf(buffer, PAGE_SIZE, "%d\n", !(result & 0x01)); +} + +static int sony_nc_touchpad_setup(struct platform_device *pd, + unsigned int handle) +{ + int ret = 0; + + tp_ctl = kzalloc(sizeof(struct touchpad_control), GFP_KERNEL); + if (!tp_ctl) + return -ENOMEM; + + tp_ctl->handle = handle; + + sysfs_attr_init(&tp_ctl->attr.attr); + tp_ctl->attr.attr.name = "touchpad"; + tp_ctl->attr.attr.mode = S_IRUGO | S_IWUSR; + tp_ctl->attr.show = sony_nc_touchpad_show; + tp_ctl->attr.store = sony_nc_touchpad_store; + + ret = device_create_file(&pd->dev, &tp_ctl->attr); + if (ret) { + kfree(tp_ctl); + tp_ctl = NULL; + } + + return ret; +} + +static void sony_nc_touchpad_cleanup(struct platform_device *pd) +{ + if (tp_ctl) { + device_remove_file(&pd->dev, &tp_ctl->attr); + kfree(tp_ctl); + tp_ctl = NULL; + } } static void sony_nc_backlight_ng_read_limits(int handle, struct sony_backlight_props *props) { - int offset; - acpi_status status; - u8 brlvl, i; + u64 offset; + int i; u8 min = 0xff, max = 0x00; - struct acpi_object_list params; - union acpi_object in_obj; - union acpi_object *lvl_enum; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + unsigned char buffer[32] = { 0 }; props->handle = handle; props->offset = 0; @@ -1583,50 +2486,31 @@ static void sony_nc_backlight_ng_read_limits(int handle, /* try to read the boundaries from ACPI tables, if we fail the above * defaults should be reasonable */ - params.count = 1; - params.pointer = &in_obj; - in_obj.type = ACPI_TYPE_INTEGER; - in_obj.integer.value = offset; - status = acpi_evaluate_object(sony_nc_acpi_handle, "SN06", ¶ms, - &buffer); - if (ACPI_FAILURE(status)) + i = sony_nc_buffer_call(sony_nc_acpi_handle, "SN06", &offset, buffer, + 32); + if (i < 0) return; - lvl_enum = (union acpi_object *) buffer.pointer; - if (!lvl_enum) { - pr_err("No SN06 return object."); - return; - } - if (lvl_enum->type != ACPI_TYPE_BUFFER) { - pr_err("Invalid SN06 return object 0x%.2x\n", - lvl_enum->type); - goto out_invalid; - } - /* the buffer lists brightness levels available, brightness levels are - * from 0 to 8 in the array, other values are used by ALS control. + * from position 0 to 8 in the array, other values are used by ALS + * control. */ - for (i = 0; i < 9 && i < lvl_enum->buffer.length; i++) { + for (i = 0; i < 9 && i < ARRAY_SIZE(buffer); i++) { - brlvl = *(lvl_enum->buffer.pointer + i); - dprintk("Brightness level: %d\n", brlvl); + dprintk("Brightness level: %d\n", buffer[i]); - if (!brlvl) + if (!buffer[i]) break; - if (brlvl > max) - max = brlvl; - if (brlvl < min) - min = brlvl; + if (buffer[i] > max) + max = buffer[i]; + if (buffer[i] < min) + min = buffer[i]; } props->offset = min; props->maxlvl = max; dprintk("Brightness levels: min=%d max=%d\n", props->offset, props->maxlvl); - -out_invalid: - kfree(buffer.pointer); - return; } static void sony_nc_backlight_setup(void) @@ -1715,28 +2599,25 @@ static int sony_nc_add(struct acpi_device *device) if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON", &handle))) { - if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, NULL)) + int arg = 1; + if (sony_nc_int_call(sony_nc_acpi_handle, "ECON", &arg, NULL)) dprintk("ECON Method failed\n"); } if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", &handle))) { dprintk("Doing SNC setup\n"); + /* retrieve the available handles */ result = sony_nc_handles_setup(sony_pf_device); - if (result) - goto outpresent; - result = sony_nc_kbd_backlight_setup(sony_pf_device); - if (result) - goto outsnc; - sony_nc_function_setup(device); - sony_nc_rfkill_setup(device); + if (!result) + sony_nc_function_setup(device, sony_pf_device); } /* setup input devices and helper fifo */ result = sony_laptop_setup_input(device); if (result) { pr_err("Unable to create input devices\n"); - goto outkbdbacklight; + goto outsnc; } if (acpi_video_backlight_support()) { @@ -1794,10 +2675,8 @@ static int sony_nc_add(struct acpi_device *device) sony_laptop_remove_input(); - outkbdbacklight: - sony_nc_kbd_backlight_cleanup(sony_pf_device); - outsnc: + sony_nc_function_cleanup(sony_pf_device); sony_nc_handles_cleanup(sony_pf_device); outpresent: @@ -1820,11 +2699,10 @@ static int sony_nc_remove(struct acpi_device *device, int type) device_remove_file(&sony_pf_device->dev, &item->devattr); } - sony_nc_kbd_backlight_cleanup(sony_pf_device); + sony_nc_function_cleanup(sony_pf_device); sony_nc_handles_cleanup(sony_pf_device); sony_pf_remove(); sony_laptop_remove_input(); - sony_nc_rfkill_cleanup(); dprintk(SONY_NC_DRIVER_NAME " removed.\n"); return 0; @@ -2437,7 +3315,9 @@ static ssize_t sony_pic_wwanpower_store(struct device *dev, if (count > 31) return -EINVAL; - value = simple_strtoul(buffer, NULL, 10); + if (kstrtoul(buffer, 10, &value)) + return -EINVAL; + mutex_lock(&spic_dev.lock); __sony_pic_set_wwanpower(value); mutex_unlock(&spic_dev.lock); @@ -2474,7 +3354,9 @@ static ssize_t sony_pic_bluetoothpower_store(struct device *dev, if (count > 31) return -EINVAL; - value = simple_strtoul(buffer, NULL, 10); + if (kstrtoul(buffer, 10, &value)) + return -EINVAL; + mutex_lock(&spic_dev.lock); __sony_pic_set_bluetoothpower(value); mutex_unlock(&spic_dev.lock); @@ -2513,7 +3395,9 @@ static ssize_t sony_pic_fanspeed_store(struct device *dev, if (count > 31) return -EINVAL; - value = simple_strtoul(buffer, NULL, 10); + if (kstrtoul(buffer, 10, &value)) + return -EINVAL; + if (sony_pic_set_fanspeed(value)) return -EIO; @@ -2671,7 +3555,8 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd, ret = -EIO; break; } - if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) { + if (sony_nc_int_call(sony_nc_acpi_handle, "GBRT", NULL, + &value)) { ret = -EIO; break; } @@ -2688,8 +3573,9 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd, ret = -EFAULT; break; } - if (acpi_callsetfunc(sony_nc_acpi_handle, "SBRT", - (val8 >> 5) + 1, NULL)) { + value = (val8 >> 5) + 1; + if (sony_nc_int_call(sony_nc_acpi_handle, "SBRT", &value, + NULL)) { ret = -EIO; break; } diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index d68c000..8b5610d 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -3402,7 +3402,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) /* Do not issue duplicate brightness change events to * userspace. tpacpi_detect_brightness_capabilities() must have * been called before this point */ - if (tp_features.bright_acpimode && acpi_video_backlight_support()) { + if (acpi_video_backlight_support()) { pr_info("This ThinkPad has standard ACPI backlight " "brightness control, supported by the ACPI " "video driver\n"); diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 57787d8..dab10f6 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -95,6 +95,7 @@ MODULE_LICENSE("GPL"); /* registers */ #define HCI_FAN 0x0004 +#define HCI_TR_BACKLIGHT 0x0005 #define HCI_SYSTEM_EVENT 0x0016 #define HCI_VIDEO_OUT 0x001c #define HCI_HOTKEY_EVENT 0x001e @@ -134,6 +135,7 @@ struct toshiba_acpi_dev { unsigned int system_event_supported:1; unsigned int ntfy_supported:1; unsigned int info_supported:1; + unsigned int tr_backlight_supported:1; struct mutex mutex; }; @@ -478,34 +480,70 @@ static const struct rfkill_ops toshiba_rfk_ops = { .poll = bt_rfkill_poll, }; +static int get_tr_backlight_status(struct toshiba_acpi_dev *dev, bool *enabled) +{ + u32 hci_result; + u32 status; + + hci_read1(dev, HCI_TR_BACKLIGHT, &status, &hci_result); + *enabled = !status; + return hci_result == HCI_SUCCESS ? 0 : -EIO; +} + +static int set_tr_backlight_status(struct toshiba_acpi_dev *dev, bool enable) +{ + u32 hci_result; + u32 value = !enable; + + hci_write1(dev, HCI_TR_BACKLIGHT, value, &hci_result); + return hci_result == HCI_SUCCESS ? 0 : -EIO; +} + static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; -static int get_lcd(struct backlight_device *bd) +static int __get_lcd_brightness(struct toshiba_acpi_dev *dev) { - struct toshiba_acpi_dev *dev = bl_get_data(bd); u32 hci_result; u32 value; + int brightness = 0; + + if (dev->tr_backlight_supported) { + bool enabled; + int ret = get_tr_backlight_status(dev, &enabled); + if (ret) + return ret; + if (enabled) + return 0; + brightness++; + } hci_read1(dev, HCI_LCD_BRIGHTNESS, &value, &hci_result); if (hci_result == HCI_SUCCESS) - return (value >> HCI_LCD_BRIGHTNESS_SHIFT); + return brightness + (value >> HCI_LCD_BRIGHTNESS_SHIFT); return -EIO; } +static int get_lcd_brightness(struct backlight_device *bd) +{ + struct toshiba_acpi_dev *dev = bl_get_data(bd); + return __get_lcd_brightness(dev); +} + static int lcd_proc_show(struct seq_file *m, void *v) { struct toshiba_acpi_dev *dev = m->private; int value; + int levels; if (!dev->backlight_dev) return -ENODEV; - value = get_lcd(dev->backlight_dev); + levels = dev->backlight_dev->props.max_brightness + 1; + value = get_lcd_brightness(dev->backlight_dev); if (value >= 0) { seq_printf(m, "brightness: %d\n", value); - seq_printf(m, "brightness_levels: %d\n", - HCI_LCD_BRIGHTNESS_LEVELS); + seq_printf(m, "brightness_levels: %d\n", levels); return 0; } @@ -518,10 +556,19 @@ static int lcd_proc_open(struct inode *inode, struct file *file) return single_open(file, lcd_proc_show, PDE(inode)->data); } -static int set_lcd(struct toshiba_acpi_dev *dev, int value) +static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value) { u32 hci_result; + if (dev->tr_backlight_supported) { + bool enable = !value; + int ret = set_tr_backlight_status(dev, enable); + if (ret) + return ret; + if (value) + value--; + } + value = value << HCI_LCD_BRIGHTNESS_SHIFT; hci_write1(dev, HCI_LCD_BRIGHTNESS, value, &hci_result); return hci_result == HCI_SUCCESS ? 0 : -EIO; @@ -530,7 +577,7 @@ static int set_lcd(struct toshiba_acpi_dev *dev, int value) static int set_lcd_status(struct backlight_device *bd) { struct toshiba_acpi_dev *dev = bl_get_data(bd); - return set_lcd(dev, bd->props.brightness); + return set_lcd_brightness(dev, bd->props.brightness); } static ssize_t lcd_proc_write(struct file *file, const char __user *buf, @@ -541,6 +588,7 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf, size_t len; int value; int ret; + int levels = dev->backlight_dev->props.max_brightness + 1; len = min(count, sizeof(cmd) - 1); if (copy_from_user(cmd, buf, len)) @@ -548,8 +596,8 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf, cmd[len] = '\0'; if (sscanf(cmd, " brightness : %i", &value) == 1 && - value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) { - ret = set_lcd(dev, value); + value >= 0 && value < levels) { + ret = set_lcd_brightness(dev, value); if (ret == 0) ret = count; } else { @@ -860,8 +908,9 @@ static void remove_toshiba_proc_entries(struct toshiba_acpi_dev *dev) } static const struct backlight_ops toshiba_backlight_data = { - .get_brightness = get_lcd, - .update_status = set_lcd_status, + .options = BL_CORE_SUSPENDRESUME, + .get_brightness = get_lcd_brightness, + .update_status = set_lcd_status, }; static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, @@ -1020,6 +1069,56 @@ static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) return error; } +static int __devinit toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev) +{ + struct backlight_properties props; + int brightness; + int ret; + bool enabled; + + /* + * Some machines don't support the backlight methods at all, and + * others support it read-only. Either of these is pretty useless, + * so only register the backlight device if the backlight method + * supports both reads and writes. + */ + brightness = __get_lcd_brightness(dev); + if (brightness < 0) + return 0; + ret = set_lcd_brightness(dev, brightness); + if (ret) { + pr_debug("Backlight method is read-only, disabling backlight support\n"); + return 0; + } + + /* Determine whether or not BIOS supports transflective backlight */ + ret = get_tr_backlight_status(dev, &enabled); + dev->tr_backlight_supported = !ret; + + memset(&props, 0, sizeof(props)); + props.type = BACKLIGHT_PLATFORM; + props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; + + /* adding an extra level and having 0 change to transflective mode */ + if (dev->tr_backlight_supported) + props.max_brightness++; + + dev->backlight_dev = backlight_device_register("toshiba", + &dev->acpi_dev->dev, + dev, + &toshiba_backlight_data, + &props); + if (IS_ERR(dev->backlight_dev)) { + ret = PTR_ERR(dev->backlight_dev); + pr_err("Could not register toshiba backlight device\n"); + dev->backlight_dev = NULL; + return ret; + } + + dev->backlight_dev->props.brightness = brightness; + return 0; +} + static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) { struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); @@ -1078,7 +1177,6 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) u32 dummy; bool bt_present; int ret = 0; - struct backlight_properties props; if (toshiba_acpi) return -EBUSY; @@ -1104,22 +1202,9 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) mutex_init(&dev->mutex); - memset(&props, 0, sizeof(props)); - props.type = BACKLIGHT_PLATFORM; - props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; - dev->backlight_dev = backlight_device_register("toshiba", - &acpi_dev->dev, - dev, - &toshiba_backlight_data, - &props); - if (IS_ERR(dev->backlight_dev)) { - ret = PTR_ERR(dev->backlight_dev); - - pr_err("Could not register toshiba backlight device\n"); - dev->backlight_dev = NULL; + ret = toshiba_acpi_setup_backlight(dev); + if (ret) goto error; - } - dev->backlight_dev->props.brightness = get_lcd(dev->backlight_dev); /* Register rfkill switch for Bluetooth */ if (hci_get_bt_present(dev, &bt_present) == HCI_SUCCESS && bt_present) { diff --git a/drivers/platform/x86/xo1-rfkill.c b/drivers/platform/x86/xo1-rfkill.c index 41781ed..b57ad86 100644 --- a/drivers/platform/x86/xo1-rfkill.c +++ b/drivers/platform/x86/xo1-rfkill.c @@ -15,15 +15,26 @@ #include <asm/olpc.h> +static bool card_blocked; + static int rfkill_set_block(void *data, bool blocked) { unsigned char cmd; + int r; + + if (blocked == card_blocked) + return 0; + if (blocked) cmd = EC_WLAN_ENTER_RESET; else cmd = EC_WLAN_LEAVE_RESET; - return olpc_ec_cmd(cmd, NULL, 0, NULL, 0); + r = olpc_ec_cmd(cmd, NULL, 0, NULL, 0); + if (r == 0) + card_blocked = blocked; + + return r; } static const struct rfkill_ops rfkill_ops = { diff --git a/drivers/rapidio/Kconfig b/drivers/rapidio/Kconfig index bc87192..6194d35 100644 --- a/drivers/rapidio/Kconfig +++ b/drivers/rapidio/Kconfig @@ -22,6 +22,20 @@ config RAPIDIO_ENABLE_RX_TX_PORTS ports for Input/Output direction to allow other traffic than Maintenance transfers. +config RAPIDIO_DMA_ENGINE + bool "DMA Engine support for RapidIO" + depends on RAPIDIO + select DMADEVICES + select DMA_ENGINE + help + Say Y here if you want to use DMA Engine frameork for RapidIO data + transfers to/from target RIO devices. RapidIO uses NREAD and + NWRITE (NWRITE_R, SWRITE) requests to transfer data between local + memory and memory on remote target device. You need a DMA controller + capable to perform data transfers to/from RapidIO. + + If you are unsure about this, say Y here. + config RAPIDIO_DEBUG bool "RapidIO subsystem debug messages" depends on RAPIDIO diff --git a/drivers/rapidio/devices/Makefile b/drivers/rapidio/devices/Makefile index 3b7b4e2..7b62860 100644 --- a/drivers/rapidio/devices/Makefile +++ b/drivers/rapidio/devices/Makefile @@ -3,3 +3,6 @@ # obj-$(CONFIG_RAPIDIO_TSI721) += tsi721.o +ifeq ($(CONFIG_RAPIDIO_DMA_ENGINE),y) +obj-$(CONFIG_RAPIDIO_TSI721) += tsi721_dma.o +endif diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c index 30d2072..722246c 100644 --- a/drivers/rapidio/devices/tsi721.c +++ b/drivers/rapidio/devices/tsi721.c @@ -108,6 +108,7 @@ static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size, u16 destid, u8 hopcount, u32 offset, int len, u32 *data, int do_wr) { + void __iomem *regs = priv->regs + TSI721_DMAC_BASE(priv->mdma.ch_id); struct tsi721_dma_desc *bd_ptr; u32 rd_count, swr_ptr, ch_stat; int i, err = 0; @@ -116,10 +117,9 @@ static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size, if (offset > (RIO_MAINT_SPACE_SZ - len) || (len != sizeof(u32))) return -EINVAL; - bd_ptr = priv->bdma[TSI721_DMACH_MAINT].bd_base; + bd_ptr = priv->mdma.bd_base; - rd_count = ioread32( - priv->regs + TSI721_DMAC_DRDCNT(TSI721_DMACH_MAINT)); + rd_count = ioread32(regs + TSI721_DMAC_DRDCNT); /* Initialize DMA descriptor */ bd_ptr[0].type_id = cpu_to_le32((DTYPE2 << 29) | (op << 19) | destid); @@ -134,19 +134,18 @@ static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size, mb(); /* Start DMA operation */ - iowrite32(rd_count + 2, - priv->regs + TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT)); - ioread32(priv->regs + TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT)); + iowrite32(rd_count + 2, regs + TSI721_DMAC_DWRCNT); + ioread32(regs + TSI721_DMAC_DWRCNT); i = 0; /* Wait until DMA transfer is finished */ - while ((ch_stat = ioread32(priv->regs + - TSI721_DMAC_STS(TSI721_DMACH_MAINT))) & TSI721_DMAC_STS_RUN) { + while ((ch_stat = ioread32(regs + TSI721_DMAC_STS)) + & TSI721_DMAC_STS_RUN) { udelay(1); if (++i >= 5000000) { dev_dbg(&priv->pdev->dev, "%s : DMA[%d] read timeout ch_status=%x\n", - __func__, TSI721_DMACH_MAINT, ch_stat); + __func__, priv->mdma.ch_id, ch_stat); if (!do_wr) *data = 0xffffffff; err = -EIO; @@ -162,13 +161,10 @@ static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size, __func__, ch_stat); dev_dbg(&priv->pdev->dev, "OP=%d : destid=%x hc=%x off=%x\n", do_wr ? MAINT_WR : MAINT_RD, destid, hopcount, offset); - iowrite32(TSI721_DMAC_INT_ALL, - priv->regs + TSI721_DMAC_INT(TSI721_DMACH_MAINT)); - iowrite32(TSI721_DMAC_CTL_INIT, - priv->regs + TSI721_DMAC_CTL(TSI721_DMACH_MAINT)); + iowrite32(TSI721_DMAC_INT_ALL, regs + TSI721_DMAC_INT); + iowrite32(TSI721_DMAC_CTL_INIT, regs + TSI721_DMAC_CTL); udelay(10); - iowrite32(0, priv->regs + - TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT)); + iowrite32(0, regs + TSI721_DMAC_DWRCNT); udelay(1); if (!do_wr) *data = 0xffffffff; @@ -184,8 +180,8 @@ static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size, * NOTE: Skipping check and clear FIFO entries because we are waiting * for transfer to be completed. */ - swr_ptr = ioread32(priv->regs + TSI721_DMAC_DSWP(TSI721_DMACH_MAINT)); - iowrite32(swr_ptr, priv->regs + TSI721_DMAC_DSRP(TSI721_DMACH_MAINT)); + swr_ptr = ioread32(regs + TSI721_DMAC_DSWP); + iowrite32(swr_ptr, regs + TSI721_DMAC_DSRP); err_out: return err; @@ -541,6 +537,22 @@ static irqreturn_t tsi721_irqhandler(int irq, void *ptr) tsi721_pw_handler(mport); } +#ifdef CONFIG_RAPIDIO_DMA_ENGINE + if (dev_int & TSI721_DEV_INT_BDMA_CH) { + int ch; + + if (dev_ch_int & TSI721_INT_BDMA_CHAN_M) { + dev_dbg(&priv->pdev->dev, + "IRQ from DMA channel 0x%08x\n", dev_ch_int); + + for (ch = 0; ch < TSI721_DMA_MAXCH; ch++) { + if (!(dev_ch_int & TSI721_INT_BDMA_CHAN(ch))) + continue; + tsi721_bdma_handler(&priv->bdma[ch]); + } + } + } +#endif return IRQ_HANDLED; } @@ -553,18 +565,26 @@ static void tsi721_interrupts_init(struct tsi721_device *priv) priv->regs + TSI721_SR_CHINT(IDB_QUEUE)); iowrite32(TSI721_SR_CHINT_IDBQRCV, priv->regs + TSI721_SR_CHINTE(IDB_QUEUE)); - iowrite32(TSI721_INT_SR2PC_CHAN(IDB_QUEUE), - priv->regs + TSI721_DEV_CHAN_INTE); /* Enable SRIO MAC interrupts */ iowrite32(TSI721_RIO_EM_DEV_INT_EN_INT, priv->regs + TSI721_RIO_EM_DEV_INT_EN); + /* Enable interrupts from channels in use */ +#ifdef CONFIG_RAPIDIO_DMA_ENGINE + intr = TSI721_INT_SR2PC_CHAN(IDB_QUEUE) | + (TSI721_INT_BDMA_CHAN_M & + ~TSI721_INT_BDMA_CHAN(TSI721_DMACH_MAINT)); +#else + intr = TSI721_INT_SR2PC_CHAN(IDB_QUEUE); +#endif + iowrite32(intr, priv->regs + TSI721_DEV_CHAN_INTE); + if (priv->flags & TSI721_USING_MSIX) intr = TSI721_DEV_INT_SRIO; else intr = TSI721_DEV_INT_SR2PC_CH | TSI721_DEV_INT_SRIO | - TSI721_DEV_INT_SMSG_CH; + TSI721_DEV_INT_SMSG_CH | TSI721_DEV_INT_BDMA_CH; iowrite32(intr, priv->regs + TSI721_DEV_INTE); ioread32(priv->regs + TSI721_DEV_INTE); @@ -715,12 +735,29 @@ static int tsi721_enable_msix(struct tsi721_device *priv) TSI721_MSIX_OMSG_INT(i); } +#ifdef CONFIG_RAPIDIO_DMA_ENGINE + /* + * Initialize MSI-X entries for Block DMA Engine: + * this driver supports XXX DMA channels + * (one is reserved for SRIO maintenance transactions) + */ + for (i = 0; i < TSI721_DMA_CHNUM; i++) { + entries[TSI721_VECT_DMA0_DONE + i].entry = + TSI721_MSIX_DMACH_DONE(i); + entries[TSI721_VECT_DMA0_INT + i].entry = + TSI721_MSIX_DMACH_INT(i); + } +#endif /* CONFIG_RAPIDIO_DMA_ENGINE */ + err = pci_enable_msix(priv->pdev, entries, ARRAY_SIZE(entries)); if (err) { if (err > 0) dev_info(&priv->pdev->dev, "Only %d MSI-X vectors available, " "not using MSI-X\n", err); + else + dev_err(&priv->pdev->dev, + "Failed to enable MSI-X (err=%d)\n", err); return err; } @@ -760,6 +797,22 @@ static int tsi721_enable_msix(struct tsi721_device *priv) i, pci_name(priv->pdev)); } +#ifdef CONFIG_RAPIDIO_DMA_ENGINE + for (i = 0; i < TSI721_DMA_CHNUM; i++) { + priv->msix[TSI721_VECT_DMA0_DONE + i].vector = + entries[TSI721_VECT_DMA0_DONE + i].vector; + snprintf(priv->msix[TSI721_VECT_DMA0_DONE + i].irq_name, + IRQ_DEVICE_NAME_MAX, DRV_NAME "-dmad%d@pci:%s", + i, pci_name(priv->pdev)); + + priv->msix[TSI721_VECT_DMA0_INT + i].vector = + entries[TSI721_VECT_DMA0_INT + i].vector; + snprintf(priv->msix[TSI721_VECT_DMA0_INT + i].irq_name, + IRQ_DEVICE_NAME_MAX, DRV_NAME "-dmai%d@pci:%s", + i, pci_name(priv->pdev)); + } +#endif /* CONFIG_RAPIDIO_DMA_ENGINE */ + return 0; } #endif /* CONFIG_PCI_MSI */ @@ -888,20 +941,34 @@ static void tsi721_doorbell_free(struct tsi721_device *priv) priv->idb_base = NULL; } -static int tsi721_bdma_ch_init(struct tsi721_device *priv, int chnum) +/** + * tsi721_bdma_maint_init - Initialize maintenance request BDMA channel. + * @priv: pointer to tsi721 private data + * + * Initialize BDMA channel allocated for RapidIO maintenance read/write + * request generation + * Returns %0 on success or %-ENOMEM on failure. + */ +static int tsi721_bdma_maint_init(struct tsi721_device *priv) { struct tsi721_dma_desc *bd_ptr; u64 *sts_ptr; dma_addr_t bd_phys, sts_phys; int sts_size; - int bd_num = priv->bdma[chnum].bd_num; + int bd_num = 2; + void __iomem *regs; - dev_dbg(&priv->pdev->dev, "Init Block DMA Engine, CH%d\n", chnum); + dev_dbg(&priv->pdev->dev, + "Init Block DMA Engine for Maintenance requests, CH%d\n", + TSI721_DMACH_MAINT); /* * Initialize DMA channel for maintenance requests */ + priv->mdma.ch_id = TSI721_DMACH_MAINT; + regs = priv->regs + TSI721_DMAC_BASE(TSI721_DMACH_MAINT); + /* Allocate space for DMA descriptors */ bd_ptr = dma_zalloc_coherent(&priv->pdev->dev, bd_num * sizeof(struct tsi721_dma_desc), @@ -909,8 +976,9 @@ static int tsi721_bdma_ch_init(struct tsi721_device *priv, int chnum) if (!bd_ptr) return -ENOMEM; - priv->bdma[chnum].bd_phys = bd_phys; - priv->bdma[chnum].bd_base = bd_ptr; + priv->mdma.bd_num = bd_num; + priv->mdma.bd_phys = bd_phys; + priv->mdma.bd_base = bd_ptr; dev_dbg(&priv->pdev->dev, "DMA descriptors @ %p (phys = %llx)\n", bd_ptr, (unsigned long long)bd_phys); @@ -927,13 +995,13 @@ static int tsi721_bdma_ch_init(struct tsi721_device *priv, int chnum) dma_free_coherent(&priv->pdev->dev, bd_num * sizeof(struct tsi721_dma_desc), bd_ptr, bd_phys); - priv->bdma[chnum].bd_base = NULL; + priv->mdma.bd_base = NULL; return -ENOMEM; } - priv->bdma[chnum].sts_phys = sts_phys; - priv->bdma[chnum].sts_base = sts_ptr; - priv->bdma[chnum].sts_size = sts_size; + priv->mdma.sts_phys = sts_phys; + priv->mdma.sts_base = sts_ptr; + priv->mdma.sts_size = sts_size; dev_dbg(&priv->pdev->dev, "desc status FIFO @ %p (phys = %llx) size=0x%x\n", @@ -946,83 +1014,61 @@ static int tsi721_bdma_ch_init(struct tsi721_device *priv, int chnum) bd_ptr[bd_num - 1].next_hi = cpu_to_le32((u64)bd_phys >> 32); /* Setup DMA descriptor pointers */ - iowrite32(((u64)bd_phys >> 32), - priv->regs + TSI721_DMAC_DPTRH(chnum)); + iowrite32(((u64)bd_phys >> 32), regs + TSI721_DMAC_DPTRH); iowrite32(((u64)bd_phys & TSI721_DMAC_DPTRL_MASK), - priv->regs + TSI721_DMAC_DPTRL(chnum)); + regs + TSI721_DMAC_DPTRL); /* Setup descriptor status FIFO */ - iowrite32(((u64)sts_phys >> 32), - priv->regs + TSI721_DMAC_DSBH(chnum)); + iowrite32(((u64)sts_phys >> 32), regs + TSI721_DMAC_DSBH); iowrite32(((u64)sts_phys & TSI721_DMAC_DSBL_MASK), - priv->regs + TSI721_DMAC_DSBL(chnum)); + regs + TSI721_DMAC_DSBL); iowrite32(TSI721_DMAC_DSSZ_SIZE(sts_size), - priv->regs + TSI721_DMAC_DSSZ(chnum)); + regs + TSI721_DMAC_DSSZ); /* Clear interrupt bits */ - iowrite32(TSI721_DMAC_INT_ALL, - priv->regs + TSI721_DMAC_INT(chnum)); + iowrite32(TSI721_DMAC_INT_ALL, regs + TSI721_DMAC_INT); - ioread32(priv->regs + TSI721_DMAC_INT(chnum)); + ioread32(regs + TSI721_DMAC_INT); /* Toggle DMA channel initialization */ - iowrite32(TSI721_DMAC_CTL_INIT, priv->regs + TSI721_DMAC_CTL(chnum)); - ioread32(priv->regs + TSI721_DMAC_CTL(chnum)); + iowrite32(TSI721_DMAC_CTL_INIT, regs + TSI721_DMAC_CTL); + ioread32(regs + TSI721_DMAC_CTL); udelay(10); return 0; } -static int tsi721_bdma_ch_free(struct tsi721_device *priv, int chnum) +static int tsi721_bdma_maint_free(struct tsi721_device *priv) { u32 ch_stat; + struct tsi721_bdma_maint *mdma = &priv->mdma; + void __iomem *regs = priv->regs + TSI721_DMAC_BASE(mdma->ch_id); - if (priv->bdma[chnum].bd_base == NULL) + if (mdma->bd_base == NULL) return 0; /* Check if DMA channel still running */ - ch_stat = ioread32(priv->regs + TSI721_DMAC_STS(chnum)); + ch_stat = ioread32(regs + TSI721_DMAC_STS); if (ch_stat & TSI721_DMAC_STS_RUN) return -EFAULT; /* Put DMA channel into init state */ - iowrite32(TSI721_DMAC_CTL_INIT, - priv->regs + TSI721_DMAC_CTL(chnum)); + iowrite32(TSI721_DMAC_CTL_INIT, regs + TSI721_DMAC_CTL); /* Free space allocated for DMA descriptors */ dma_free_coherent(&priv->pdev->dev, - priv->bdma[chnum].bd_num * sizeof(struct tsi721_dma_desc), - priv->bdma[chnum].bd_base, priv->bdma[chnum].bd_phys); - priv->bdma[chnum].bd_base = NULL; + mdma->bd_num * sizeof(struct tsi721_dma_desc), + mdma->bd_base, mdma->bd_phys); + mdma->bd_base = NULL; /* Free space allocated for status FIFO */ dma_free_coherent(&priv->pdev->dev, - priv->bdma[chnum].sts_size * sizeof(struct tsi721_dma_sts), - priv->bdma[chnum].sts_base, priv->bdma[chnum].sts_phys); - priv->bdma[chnum].sts_base = NULL; - return 0; -} - -static int tsi721_bdma_init(struct tsi721_device *priv) -{ - /* Initialize BDMA channel allocated for RapidIO maintenance read/write - * request generation - */ - priv->bdma[TSI721_DMACH_MAINT].bd_num = 2; - if (tsi721_bdma_ch_init(priv, TSI721_DMACH_MAINT)) { - dev_err(&priv->pdev->dev, "Unable to initialize maintenance DMA" - " channel %d, aborting\n", TSI721_DMACH_MAINT); - return -ENOMEM; - } - + mdma->sts_size * sizeof(struct tsi721_dma_sts), + mdma->sts_base, mdma->sts_phys); + mdma->sts_base = NULL; return 0; } -static void tsi721_bdma_free(struct tsi721_device *priv) -{ - tsi721_bdma_ch_free(priv, TSI721_DMACH_MAINT); -} - /* Enable Inbound Messaging Interrupts */ static void tsi721_imsg_interrupt_enable(struct tsi721_device *priv, int ch, @@ -2035,7 +2081,8 @@ static void tsi721_disable_ints(struct tsi721_device *priv) /* Disable all BDMA Channel interrupts */ for (ch = 0; ch < TSI721_DMA_MAXCH; ch++) - iowrite32(0, priv->regs + TSI721_DMAC_INTE(ch)); + iowrite32(0, + priv->regs + TSI721_DMAC_BASE(ch) + TSI721_DMAC_INTE); /* Disable all general BDMA interrupts */ iowrite32(0, priv->regs + TSI721_BDMA_INTE); @@ -2104,6 +2151,7 @@ static int __devinit tsi721_setup_mport(struct tsi721_device *priv) mport->phy_type = RIO_PHY_SERIAL; mport->priv = (void *)priv; mport->phys_efptr = 0x100; + priv->mport = mport; INIT_LIST_HEAD(&mport->dbells); @@ -2129,17 +2177,21 @@ static int __devinit tsi721_setup_mport(struct tsi721_device *priv) if (!err) { tsi721_interrupts_init(priv); ops->pwenable = tsi721_pw_enable; - } else + } else { dev_err(&pdev->dev, "Unable to get assigned PCI IRQ " "vector %02X err=0x%x\n", pdev->irq, err); + goto err_exit; + } +#ifdef CONFIG_RAPIDIO_DMA_ENGINE + tsi721_register_dma(priv); +#endif /* Enable SRIO link */ iowrite32(ioread32(priv->regs + TSI721_DEVCTL) | TSI721_DEVCTL_SRBOOT_CMPL, priv->regs + TSI721_DEVCTL); rio_register_mport(mport); - priv->mport = mport; if (mport->host_deviceid >= 0) iowrite32(RIO_PORT_GEN_HOST | RIO_PORT_GEN_MASTER | @@ -2149,6 +2201,11 @@ static int __devinit tsi721_setup_mport(struct tsi721_device *priv) iowrite32(0, priv->regs + (0x100 + RIO_PORT_GEN_CTL_CSR)); return 0; + +err_exit: + kfree(mport); + kfree(ops); + return err; } static int __devinit tsi721_probe(struct pci_dev *pdev, @@ -2294,7 +2351,7 @@ static int __devinit tsi721_probe(struct pci_dev *pdev, tsi721_init_pc2sr_mapping(priv); tsi721_init_sr2pc_mapping(priv); - if (tsi721_bdma_init(priv)) { + if (tsi721_bdma_maint_init(priv)) { dev_err(&pdev->dev, "BDMA initialization failed, aborting\n"); err = -ENOMEM; goto err_unmap_bars; @@ -2319,7 +2376,7 @@ static int __devinit tsi721_probe(struct pci_dev *pdev, err_free_consistent: tsi721_doorbell_free(priv); err_free_bdma: - tsi721_bdma_free(priv); + tsi721_bdma_maint_free(priv); err_unmap_bars: if (priv->regs) iounmap(priv->regs); diff --git a/drivers/rapidio/devices/tsi721.h b/drivers/rapidio/devices/tsi721.h index 1c226b3..59de9d7 100644 --- a/drivers/rapidio/devices/tsi721.h +++ b/drivers/rapidio/devices/tsi721.h @@ -167,6 +167,8 @@ #define TSI721_DEV_INTE 0x29840 #define TSI721_DEV_INT 0x29844 #define TSI721_DEV_INTSET 0x29848 +#define TSI721_DEV_INT_BDMA_CH 0x00002000 +#define TSI721_DEV_INT_BDMA_NCH 0x00001000 #define TSI721_DEV_INT_SMSG_CH 0x00000800 #define TSI721_DEV_INT_SMSG_NCH 0x00000400 #define TSI721_DEV_INT_SR2PC_CH 0x00000200 @@ -181,6 +183,8 @@ #define TSI721_INT_IMSG_CHAN(x) (1 << (16 + (x))) #define TSI721_INT_OMSG_CHAN_M 0x0000ff00 #define TSI721_INT_OMSG_CHAN(x) (1 << (8 + (x))) +#define TSI721_INT_BDMA_CHAN_M 0x000000ff +#define TSI721_INT_BDMA_CHAN(x) (1 << (x)) /* * PC2SR block registers @@ -235,14 +239,16 @@ * x = 0..7 */ -#define TSI721_DMAC_DWRCNT(x) (0x51000 + (x) * 0x1000) -#define TSI721_DMAC_DRDCNT(x) (0x51004 + (x) * 0x1000) +#define TSI721_DMAC_BASE(x) (0x51000 + (x) * 0x1000) -#define TSI721_DMAC_CTL(x) (0x51008 + (x) * 0x1000) +#define TSI721_DMAC_DWRCNT 0x000 +#define TSI721_DMAC_DRDCNT 0x004 + +#define TSI721_DMAC_CTL 0x008 #define TSI721_DMAC_CTL_SUSP 0x00000002 #define TSI721_DMAC_CTL_INIT 0x00000001 -#define TSI721_DMAC_INT(x) (0x5100c + (x) * 0x1000) +#define TSI721_DMAC_INT 0x00c #define TSI721_DMAC_INT_STFULL 0x00000010 #define TSI721_DMAC_INT_DONE 0x00000008 #define TSI721_DMAC_INT_SUSP 0x00000004 @@ -250,34 +256,33 @@ #define TSI721_DMAC_INT_IOFDONE 0x00000001 #define TSI721_DMAC_INT_ALL 0x0000001f -#define TSI721_DMAC_INTSET(x) (0x51010 + (x) * 0x1000) +#define TSI721_DMAC_INTSET 0x010 -#define TSI721_DMAC_STS(x) (0x51014 + (x) * 0x1000) +#define TSI721_DMAC_STS 0x014 #define TSI721_DMAC_STS_ABORT 0x00400000 #define TSI721_DMAC_STS_RUN 0x00200000 #define TSI721_DMAC_STS_CS 0x001f0000 -#define TSI721_DMAC_INTE(x) (0x51018 + (x) * 0x1000) +#define TSI721_DMAC_INTE 0x018 -#define TSI721_DMAC_DPTRL(x) (0x51024 + (x) * 0x1000) +#define TSI721_DMAC_DPTRL 0x024 #define TSI721_DMAC_DPTRL_MASK 0xffffffe0 -#define TSI721_DMAC_DPTRH(x) (0x51028 + (x) * 0x1000) +#define TSI721_DMAC_DPTRH 0x028 -#define TSI721_DMAC_DSBL(x) (0x5102c + (x) * 0x1000) +#define TSI721_DMAC_DSBL 0x02c #define TSI721_DMAC_DSBL_MASK 0xffffffc0 -#define TSI721_DMAC_DSBH(x) (0x51030 + (x) * 0x1000) +#define TSI721_DMAC_DSBH 0x030 -#define TSI721_DMAC_DSSZ(x) (0x51034 + (x) * 0x1000) +#define TSI721_DMAC_DSSZ 0x034 #define TSI721_DMAC_DSSZ_SIZE_M 0x0000000f #define TSI721_DMAC_DSSZ_SIZE(size) (__fls(size) - 4) - -#define TSI721_DMAC_DSRP(x) (0x51038 + (x) * 0x1000) +#define TSI721_DMAC_DSRP 0x038 #define TSI721_DMAC_DSRP_MASK 0x0007ffff -#define TSI721_DMAC_DSWP(x) (0x5103c + (x) * 0x1000) +#define TSI721_DMAC_DSWP 0x03c #define TSI721_DMAC_DSWP_MASK 0x0007ffff #define TSI721_BDMA_INTE 0x5f000 @@ -612,6 +617,8 @@ enum dma_rtype { #define TSI721_DMACH_MAINT 0 /* DMA channel for maint requests */ #define TSI721_DMACH_MAINT_NBD 32 /* Number of BDs for maint requests */ +#define TSI721_DMACH_DMA 1 /* DMA channel for data transfers */ + #define MSG_DMA_ENTRY_INX_TO_SIZE(x) ((0x10 << (x)) & 0xFFFF0) enum tsi721_smsg_int_flag { @@ -626,7 +633,48 @@ enum tsi721_smsg_int_flag { /* Structures */ +#ifdef CONFIG_RAPIDIO_DMA_ENGINE + +struct tsi721_tx_desc { + struct dma_async_tx_descriptor txd; + struct tsi721_dma_desc *hw_desc; + u16 destid; + /* low 64-bits of 66-bit RIO address */ + u64 rio_addr; + /* upper 2-bits of 66-bit RIO address */ + u8 rio_addr_u; + bool interrupt; + struct list_head desc_node; + struct list_head tx_list; +}; + struct tsi721_bdma_chan { + int id; + void __iomem *regs; + int bd_num; /* number of buffer descriptors */ + void *bd_base; /* start of DMA descriptors */ + dma_addr_t bd_phys; + void *sts_base; /* start of DMA BD status FIFO */ + dma_addr_t sts_phys; + int sts_size; + u32 sts_rdptr; + u32 wr_count; + u32 wr_count_next; + + struct dma_chan dchan; + struct tsi721_tx_desc *tx_desc; + spinlock_t lock; + struct list_head active_list; + struct list_head queue; + struct list_head free_list; + dma_cookie_t completed_cookie; + struct tasklet_struct tasklet; +}; + +#endif /* CONFIG_RAPIDIO_DMA_ENGINE */ + +struct tsi721_bdma_maint { + int ch_id; /* BDMA channel number */ int bd_num; /* number of buffer descriptors */ void *bd_base; /* start of DMA descriptors */ dma_addr_t bd_phys; @@ -721,6 +769,24 @@ enum tsi721_msix_vect { TSI721_VECT_IMB1_INT, TSI721_VECT_IMB2_INT, TSI721_VECT_IMB3_INT, +#ifdef CONFIG_RAPIDIO_DMA_ENGINE + TSI721_VECT_DMA0_DONE, + TSI721_VECT_DMA1_DONE, + TSI721_VECT_DMA2_DONE, + TSI721_VECT_DMA3_DONE, + TSI721_VECT_DMA4_DONE, + TSI721_VECT_DMA5_DONE, + TSI721_VECT_DMA6_DONE, + TSI721_VECT_DMA7_DONE, + TSI721_VECT_DMA0_INT, + TSI721_VECT_DMA1_INT, + TSI721_VECT_DMA2_INT, + TSI721_VECT_DMA3_INT, + TSI721_VECT_DMA4_INT, + TSI721_VECT_DMA5_INT, + TSI721_VECT_DMA6_INT, + TSI721_VECT_DMA7_INT, +#endif /* CONFIG_RAPIDIO_DMA_ENGINE */ TSI721_VECT_MAX }; @@ -754,7 +820,11 @@ struct tsi721_device { u32 pw_discard_count; /* BDMA Engine */ + struct tsi721_bdma_maint mdma; /* Maintenance rd/wr request channel */ + +#ifdef CONFIG_RAPIDIO_DMA_ENGINE struct tsi721_bdma_chan bdma[TSI721_DMA_CHNUM]; +#endif /* Inbound Messaging */ int imsg_init[TSI721_IMSG_CHNUM]; @@ -765,4 +835,9 @@ struct tsi721_device { struct tsi721_omsg_ring omsg_ring[TSI721_OMSG_CHNUM]; }; +#ifdef CONFIG_RAPIDIO_DMA_ENGINE +extern void tsi721_bdma_handler(struct tsi721_bdma_chan *bdma_chan); +extern int __devinit tsi721_register_dma(struct tsi721_device *priv); +#endif + #endif diff --git a/drivers/rapidio/devices/tsi721_dma.c b/drivers/rapidio/devices/tsi721_dma.c new file mode 100644 index 0000000..92e06a5 --- /dev/null +++ b/drivers/rapidio/devices/tsi721_dma.c @@ -0,0 +1,823 @@ +/* + * DMA Engine support for Tsi721 PCIExpress-to-SRIO bridge + * + * Copyright 2011 Integrated Device Technology, Inc. + * Alexandre Bounine <alexandre.bounine@idt.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., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/io.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/rio.h> +#include <linux/rio_drv.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/delay.h> + +#include "tsi721.h" + +static inline struct tsi721_bdma_chan *to_tsi721_chan(struct dma_chan *chan) +{ + return container_of(chan, struct tsi721_bdma_chan, dchan); +} + +static inline struct tsi721_device *to_tsi721(struct dma_device *ddev) +{ + return container_of(ddev, struct rio_mport, dma)->priv; +} + +static inline +struct tsi721_tx_desc *to_tsi721_desc(struct dma_async_tx_descriptor *txd) +{ + return container_of(txd, struct tsi721_tx_desc, txd); +} + +static inline +struct tsi721_tx_desc *tsi721_dma_first_active( + struct tsi721_bdma_chan *bdma_chan) +{ + return list_first_entry(&bdma_chan->active_list, + struct tsi721_tx_desc, desc_node); +} + +static int tsi721_bdma_ch_init(struct tsi721_bdma_chan *bdma_chan) +{ + struct tsi721_dma_desc *bd_ptr; + struct device *dev = bdma_chan->dchan.device->dev; + u64 *sts_ptr; + dma_addr_t bd_phys; + dma_addr_t sts_phys; + int sts_size; + int bd_num = bdma_chan->bd_num; + + dev_dbg(dev, "Init Block DMA Engine, CH%d\n", bdma_chan->id); + + /* Allocate space for DMA descriptors */ + bd_ptr = dma_zalloc_coherent(dev, + bd_num * sizeof(struct tsi721_dma_desc), + &bd_phys, GFP_KERNEL); + if (!bd_ptr) + return -ENOMEM; + + bdma_chan->bd_phys = bd_phys; + bdma_chan->bd_base = bd_ptr; + + dev_dbg(dev, "DMA descriptors @ %p (phys = %llx)\n", + bd_ptr, (unsigned long long)bd_phys); + + /* Allocate space for descriptor status FIFO */ + sts_size = (bd_num >= TSI721_DMA_MINSTSSZ) ? + bd_num : TSI721_DMA_MINSTSSZ; + sts_size = roundup_pow_of_two(sts_size); + sts_ptr = dma_zalloc_coherent(dev, + sts_size * sizeof(struct tsi721_dma_sts), + &sts_phys, GFP_KERNEL); + if (!sts_ptr) { + /* Free space allocated for DMA descriptors */ + dma_free_coherent(dev, + bd_num * sizeof(struct tsi721_dma_desc), + bd_ptr, bd_phys); + bdma_chan->bd_base = NULL; + return -ENOMEM; + } + + bdma_chan->sts_phys = sts_phys; + bdma_chan->sts_base = sts_ptr; + bdma_chan->sts_size = sts_size; + + dev_dbg(dev, + "desc status FIFO @ %p (phys = %llx) size=0x%x\n", + sts_ptr, (unsigned long long)sts_phys, sts_size); + + /* Initialize DMA descriptors ring */ + bd_ptr[bd_num - 1].type_id = cpu_to_le32(DTYPE3 << 29); + bd_ptr[bd_num - 1].next_lo = cpu_to_le32((u64)bd_phys & + TSI721_DMAC_DPTRL_MASK); + bd_ptr[bd_num - 1].next_hi = cpu_to_le32((u64)bd_phys >> 32); + + /* Setup DMA descriptor pointers */ + iowrite32(((u64)bd_phys >> 32), + bdma_chan->regs + TSI721_DMAC_DPTRH); + iowrite32(((u64)bd_phys & TSI721_DMAC_DPTRL_MASK), + bdma_chan->regs + TSI721_DMAC_DPTRL); + + /* Setup descriptor status FIFO */ + iowrite32(((u64)sts_phys >> 32), + bdma_chan->regs + TSI721_DMAC_DSBH); + iowrite32(((u64)sts_phys & TSI721_DMAC_DSBL_MASK), + bdma_chan->regs + TSI721_DMAC_DSBL); + iowrite32(TSI721_DMAC_DSSZ_SIZE(sts_size), + bdma_chan->regs + TSI721_DMAC_DSSZ); + + /* Clear interrupt bits */ + iowrite32(TSI721_DMAC_INT_ALL, + bdma_chan->regs + TSI721_DMAC_INT); + + ioread32(bdma_chan->regs + TSI721_DMAC_INT); + + /* Toggle DMA channel initialization */ + iowrite32(TSI721_DMAC_CTL_INIT, bdma_chan->regs + TSI721_DMAC_CTL); + ioread32(bdma_chan->regs + TSI721_DMAC_CTL); + bdma_chan->wr_count = bdma_chan->wr_count_next = 0; + bdma_chan->sts_rdptr = 0; + udelay(10); + + return 0; +} + +static int tsi721_bdma_ch_free(struct tsi721_bdma_chan *bdma_chan) +{ + u32 ch_stat; + + if (bdma_chan->bd_base == NULL) + return 0; + + /* Check if DMA channel still running */ + ch_stat = ioread32(bdma_chan->regs + TSI721_DMAC_STS); + if (ch_stat & TSI721_DMAC_STS_RUN) + return -EFAULT; + + /* Put DMA channel into init state */ + iowrite32(TSI721_DMAC_CTL_INIT, bdma_chan->regs + TSI721_DMAC_CTL); + + /* Free space allocated for DMA descriptors */ + dma_free_coherent(bdma_chan->dchan.device->dev, + bdma_chan->bd_num * sizeof(struct tsi721_dma_desc), + bdma_chan->bd_base, bdma_chan->bd_phys); + bdma_chan->bd_base = NULL; + + /* Free space allocated for status FIFO */ + dma_free_coherent(bdma_chan->dchan.device->dev, + bdma_chan->sts_size * sizeof(struct tsi721_dma_sts), + bdma_chan->sts_base, bdma_chan->sts_phys); + bdma_chan->sts_base = NULL; + return 0; +} + +static void +tsi721_bdma_interrupt_enable(struct tsi721_bdma_chan *bdma_chan, int enable) +{ + if (enable) { + /* Clear pending BDMA channel interrupts */ + iowrite32(TSI721_DMAC_INT_ALL, + bdma_chan->regs + TSI721_DMAC_INT); + ioread32(bdma_chan->regs + TSI721_DMAC_INT); + /* Enable BDMA channel interrupts */ + iowrite32(TSI721_DMAC_INT_ALL, + bdma_chan->regs + TSI721_DMAC_INTE); + } else { + /* Disable BDMA channel interrupts */ + iowrite32(0, bdma_chan->regs + TSI721_DMAC_INTE); + /* Clear pending BDMA channel interrupts */ + iowrite32(TSI721_DMAC_INT_ALL, + bdma_chan->regs + TSI721_DMAC_INT); + } + +} + +static bool tsi721_dma_is_idle(struct tsi721_bdma_chan *bdma_chan) +{ + u32 sts; + + sts = ioread32(bdma_chan->regs + TSI721_DMAC_STS); + return ((sts & TSI721_DMAC_STS_RUN) == 0); +} + +void tsi721_bdma_handler(struct tsi721_bdma_chan *bdma_chan) +{ + /* Disable BDMA channel interrupts */ + iowrite32(0, bdma_chan->regs + TSI721_DMAC_INTE); + + tasklet_schedule(&bdma_chan->tasklet); +} + +#ifdef CONFIG_PCI_MSI +/** + * tsi721_omsg_msix - MSI-X interrupt handler for BDMA channels + * @irq: Linux interrupt number + * @ptr: Pointer to interrupt-specific data (BDMA channel structure) + * + * Handles BDMA channel interrupts signaled using MSI-X. + */ +static irqreturn_t tsi721_bdma_msix(int irq, void *ptr) +{ + struct tsi721_bdma_chan *bdma_chan = ptr; + + tsi721_bdma_handler(bdma_chan); + return IRQ_HANDLED; +} +#endif /* CONFIG_PCI_MSI */ + +/* Must be called with the spinlock held */ +static void tsi721_start_dma(struct tsi721_bdma_chan *bdma_chan) +{ + if (!tsi721_dma_is_idle(bdma_chan)) { + dev_err(bdma_chan->dchan.device->dev, + "BUG: Attempt to start non-idle channel\n"); + return; + } + + if (bdma_chan->wr_count == bdma_chan->wr_count_next) { + dev_err(bdma_chan->dchan.device->dev, + "BUG: Attempt to start DMA with no BDs ready\n"); + return; + } + + dev_dbg(bdma_chan->dchan.device->dev, + "tx_chan: %p, chan: %d, regs: %p\n", + bdma_chan, bdma_chan->dchan.chan_id, bdma_chan->regs); + + iowrite32(bdma_chan->wr_count_next, + bdma_chan->regs + TSI721_DMAC_DWRCNT); + ioread32(bdma_chan->regs + TSI721_DMAC_DWRCNT); + + bdma_chan->wr_count = bdma_chan->wr_count_next; +} + +static void tsi721_desc_put(struct tsi721_bdma_chan *bdma_chan, + struct tsi721_tx_desc *desc) +{ + dev_dbg(bdma_chan->dchan.device->dev, + "Put desc: %p into free list\n", desc); + + if (desc) { + spin_lock_bh(&bdma_chan->lock); + list_splice_init(&desc->tx_list, &bdma_chan->free_list); + list_add(&desc->desc_node, &bdma_chan->free_list); + bdma_chan->wr_count_next = bdma_chan->wr_count; + spin_unlock_bh(&bdma_chan->lock); + } +} + +static +struct tsi721_tx_desc *tsi721_desc_get(struct tsi721_bdma_chan *bdma_chan) +{ + struct tsi721_tx_desc *tx_desc, *_tx_desc; + struct tsi721_tx_desc *ret = NULL; + int i; + + spin_lock_bh(&bdma_chan->lock); + list_for_each_entry_safe(tx_desc, _tx_desc, + &bdma_chan->free_list, desc_node) { + if (async_tx_test_ack(&tx_desc->txd)) { + list_del(&tx_desc->desc_node); + ret = tx_desc; + break; + } + dev_dbg(bdma_chan->dchan.device->dev, + "desc %p not ACKed\n", tx_desc); + } + + i = bdma_chan->wr_count_next % bdma_chan->bd_num; + if (i == bdma_chan->bd_num - 1) { + i = 0; + bdma_chan->wr_count_next++; /* skip link descriptor */ + } + + bdma_chan->wr_count_next++; + tx_desc->txd.phys = bdma_chan->bd_phys + + i * sizeof(struct tsi721_dma_desc); + tx_desc->hw_desc = &((struct tsi721_dma_desc *)bdma_chan->bd_base)[i]; + + spin_unlock_bh(&bdma_chan->lock); + + return ret; +} + +static int +tsi721_fill_desc(struct tsi721_bdma_chan *bdma_chan, + struct tsi721_tx_desc *desc, struct scatterlist *sg, + enum dma_rtype rtype, u32 sys_size) +{ + struct tsi721_dma_desc *bd_ptr = desc->hw_desc; + u64 rio_addr; + + if (sg_dma_len(sg) > TSI721_DMAD_BCOUNT1 + 1) { + dev_err(bdma_chan->dchan.device->dev, + "SG element is too large\n"); + return -EINVAL; + } + + dev_dbg(bdma_chan->dchan.device->dev, + "desc: 0x%llx, addr: 0x%llx len: 0x%x\n", + (u64)desc->txd.phys, (unsigned long long)sg_dma_address(sg), + sg_dma_len(sg)); + + dev_dbg(bdma_chan->dchan.device->dev, + "bd_ptr = %p did=%d raddr=0x%llx\n", + bd_ptr, desc->destid, desc->rio_addr); + + /* Initialize DMA descriptor */ + bd_ptr->type_id = cpu_to_le32((DTYPE1 << 29) | + (rtype << 19) | desc->destid); + if (desc->interrupt) + bd_ptr->type_id |= cpu_to_le32(TSI721_DMAD_IOF); + bd_ptr->bcount = cpu_to_le32(((desc->rio_addr & 0x3) << 30) | + (sys_size << 26) | sg_dma_len(sg)); + rio_addr = (desc->rio_addr >> 2) | + ((u64)(desc->rio_addr_u & 0x3) << 62); + bd_ptr->raddr_lo = cpu_to_le32(rio_addr & 0xffffffff); + bd_ptr->raddr_hi = cpu_to_le32(rio_addr >> 32); + bd_ptr->t1.bufptr_lo = cpu_to_le32( + (u64)sg_dma_address(sg) & 0xffffffff); + bd_ptr->t1.bufptr_hi = cpu_to_le32((u64)sg_dma_address(sg) >> 32); + bd_ptr->t1.s_dist = 0; + bd_ptr->t1.s_size = 0; + + return 0; +} + +static void tsi721_dma_chain_complete(struct tsi721_bdma_chan *bdma_chan, + struct tsi721_tx_desc *desc) +{ + struct dma_async_tx_descriptor *txd = &desc->txd; + dma_async_tx_callback callback = txd->callback; + void *param = txd->callback_param; + + list_splice_init(&desc->tx_list, &bdma_chan->free_list); + list_move(&desc->desc_node, &bdma_chan->free_list); + bdma_chan->completed_cookie = txd->cookie; + + if (callback) + callback(param); +} + +static void tsi721_dma_complete_all(struct tsi721_bdma_chan *bdma_chan) +{ + struct tsi721_tx_desc *desc, *_d; + LIST_HEAD(list); + + BUG_ON(!tsi721_dma_is_idle(bdma_chan)); + + if (!list_empty(&bdma_chan->queue)) + tsi721_start_dma(bdma_chan); + + list_splice_init(&bdma_chan->active_list, &list); + list_splice_init(&bdma_chan->queue, &bdma_chan->active_list); + + list_for_each_entry_safe(desc, _d, &list, desc_node) + tsi721_dma_chain_complete(bdma_chan, desc); +} + +static void tsi721_clr_stat(struct tsi721_bdma_chan *bdma_chan) +{ + u32 srd_ptr; + u64 *sts_ptr; + int i, j; + + /* Check and clear descriptor status FIFO entries */ + srd_ptr = bdma_chan->sts_rdptr; + sts_ptr = bdma_chan->sts_base; + j = srd_ptr * 8; + while (sts_ptr[j]) { + for (i = 0; i < 8 && sts_ptr[j]; i++, j++) + sts_ptr[j] = 0; + + ++srd_ptr; + srd_ptr %= bdma_chan->sts_size; + j = srd_ptr * 8; + } + + iowrite32(srd_ptr, bdma_chan->regs + TSI721_DMAC_DSRP); + bdma_chan->sts_rdptr = srd_ptr; +} + +static void tsi721_advance_work(struct tsi721_bdma_chan *bdma_chan) +{ + if (list_empty(&bdma_chan->active_list) || + list_is_singular(&bdma_chan->active_list)) { + dev_dbg(bdma_chan->dchan.device->dev, + "%s: Active_list empty\n", __func__); + tsi721_dma_complete_all(bdma_chan); + } else { + dev_dbg(bdma_chan->dchan.device->dev, + "%s: Active_list NOT empty\n", __func__); + tsi721_dma_chain_complete(bdma_chan, + tsi721_dma_first_active(bdma_chan)); + tsi721_start_dma(bdma_chan); + } +} + +static void tsi721_dma_tasklet(unsigned long data) +{ + struct tsi721_bdma_chan *bdma_chan = (struct tsi721_bdma_chan *)data; + u32 dmac_int, dmac_sts; + + dmac_int = ioread32(bdma_chan->regs + TSI721_DMAC_INT); + dev_dbg(bdma_chan->dchan.device->dev, "%s: DMAC%d_INT = 0x%x\n", + __func__, bdma_chan->id, dmac_int); + /* Clear channel interrupts */ + iowrite32(dmac_int, bdma_chan->regs + TSI721_DMAC_INT); + + if (dmac_int & TSI721_DMAC_INT_ERR) { + dmac_sts = ioread32(bdma_chan->regs + TSI721_DMAC_STS); + dev_err(bdma_chan->dchan.device->dev, + "%s: DMA ERROR - DMAC%d_STS = 0x%x\n", + __func__, bdma_chan->id, dmac_sts); + } + + if (dmac_int & TSI721_DMAC_INT_STFULL) { + dev_err(bdma_chan->dchan.device->dev, + "%s: DMAC%d descriptor status FIFO is full\n", + __func__, bdma_chan->id); + } + + if (dmac_int & (TSI721_DMAC_INT_DONE | TSI721_DMAC_INT_IOFDONE)) { + tsi721_clr_stat(bdma_chan); + spin_lock(&bdma_chan->lock); + tsi721_advance_work(bdma_chan); + spin_unlock(&bdma_chan->lock); + } + + /* Re-Enable BDMA channel interrupts */ + iowrite32(TSI721_DMAC_INT_ALL, bdma_chan->regs + TSI721_DMAC_INTE); +} + +static dma_cookie_t tsi721_tx_submit(struct dma_async_tx_descriptor *txd) +{ + struct tsi721_tx_desc *desc = to_tsi721_desc(txd); + struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(txd->chan); + dma_cookie_t cookie; + + spin_lock_bh(&bdma_chan->lock); + + cookie = txd->chan->cookie; + if (++cookie < 0) + cookie = 1; + txd->chan->cookie = cookie; + txd->cookie = cookie; + + if (list_empty(&bdma_chan->active_list)) { + list_add_tail(&desc->desc_node, &bdma_chan->active_list); + tsi721_start_dma(bdma_chan); + } else { + list_add_tail(&desc->desc_node, &bdma_chan->queue); + } + + spin_unlock_bh(&bdma_chan->lock); + return cookie; +} + +static int tsi721_alloc_chan_resources(struct dma_chan *dchan) +{ + struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan); +#ifdef CONFIG_PCI_MSI + struct tsi721_device *priv = to_tsi721(dchan->device); +#endif + struct tsi721_tx_desc *desc = NULL; + LIST_HEAD(tmp_list); + int i; + int rc; + + if (bdma_chan->bd_base) + return bdma_chan->bd_num - 1; + + /* Initialize BDMA channel */ + if (tsi721_bdma_ch_init(bdma_chan)) { + dev_err(dchan->device->dev, "Unable to initialize data DMA" + " channel %d, aborting\n", bdma_chan->id); + return -ENOMEM; + } + + /* Alocate matching number of logical descriptors */ + desc = kcalloc((bdma_chan->bd_num - 1), sizeof(struct tsi721_tx_desc), + GFP_KERNEL); + if (!desc) { + dev_err(dchan->device->dev, + "Failed to allocate logical descriptors\n"); + rc = -ENOMEM; + goto err_out; + } + + bdma_chan->tx_desc = desc; + + for (i = 0; i < bdma_chan->bd_num - 1; i++) { + dma_async_tx_descriptor_init(&desc[i].txd, dchan); + desc[i].txd.tx_submit = tsi721_tx_submit; + desc[i].txd.flags = DMA_CTRL_ACK; + INIT_LIST_HEAD(&desc[i].tx_list); + list_add_tail(&desc[i].desc_node, &tmp_list); + } + + spin_lock_bh(&bdma_chan->lock); + list_splice(&tmp_list, &bdma_chan->free_list); + bdma_chan->completed_cookie = dchan->cookie = 1; + spin_unlock_bh(&bdma_chan->lock); + +#ifdef CONFIG_PCI_MSI + if (priv->flags & TSI721_USING_MSIX) { + /* Request interrupt service if we are in MSI-X mode */ + rc = request_irq( + priv->msix[TSI721_VECT_DMA0_DONE + + bdma_chan->id].vector, + tsi721_bdma_msix, 0, + priv->msix[TSI721_VECT_DMA0_DONE + + bdma_chan->id].irq_name, + (void *)bdma_chan); + + if (rc) { + dev_dbg(dchan->device->dev, + "Unable to allocate MSI-X interrupt for " + "BDMA%d-DONE\n", bdma_chan->id); + goto err_out; + } + + rc = request_irq(priv->msix[TSI721_VECT_DMA0_INT + + bdma_chan->id].vector, + tsi721_bdma_msix, 0, + priv->msix[TSI721_VECT_DMA0_INT + + bdma_chan->id].irq_name, + (void *)bdma_chan); + + if (rc) { + dev_dbg(dchan->device->dev, + "Unable to allocate MSI-X interrupt for " + "BDMA%d-INT\n", bdma_chan->id); + free_irq( + priv->msix[TSI721_VECT_DMA0_DONE + + bdma_chan->id].vector, + (void *)bdma_chan); + rc = -EIO; + goto err_out; + } + } +#endif /* CONFIG_PCI_MSI */ + + tasklet_enable(&bdma_chan->tasklet); + tsi721_bdma_interrupt_enable(bdma_chan, 1); + + return bdma_chan->bd_num - 1; + +err_out: + kfree(desc); + tsi721_bdma_ch_free(bdma_chan); + return rc; +} + +static void tsi721_free_chan_resources(struct dma_chan *dchan) +{ + struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan); +#ifdef CONFIG_PCI_MSI + struct tsi721_device *priv = to_tsi721(dchan->device); +#endif + LIST_HEAD(list); + + dev_dbg(dchan->device->dev, "%s: Entry\n", __func__); + + if (bdma_chan->bd_base == NULL) + return; + + BUG_ON(!list_empty(&bdma_chan->active_list)); + BUG_ON(!list_empty(&bdma_chan->queue)); + + tasklet_disable(&bdma_chan->tasklet); + + spin_lock_bh(&bdma_chan->lock); + list_splice_init(&bdma_chan->free_list, &list); + spin_unlock_bh(&bdma_chan->lock); + + tsi721_bdma_interrupt_enable(bdma_chan, 0); + +#ifdef CONFIG_PCI_MSI + if (priv->flags & TSI721_USING_MSIX) { + free_irq(priv->msix[TSI721_VECT_DMA0_DONE + + bdma_chan->id].vector, (void *)bdma_chan); + free_irq(priv->msix[TSI721_VECT_DMA0_INT + + bdma_chan->id].vector, (void *)bdma_chan); + } +#endif /* CONFIG_PCI_MSI */ + + tsi721_bdma_ch_free(bdma_chan); + kfree(bdma_chan->tx_desc); +} + +static +enum dma_status tsi721_tx_status(struct dma_chan *dchan, dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan); + dma_cookie_t last_used; + dma_cookie_t last_completed; + int ret; + + spin_lock_bh(&bdma_chan->lock); + last_completed = bdma_chan->completed_cookie; + last_used = dchan->cookie; + spin_unlock_bh(&bdma_chan->lock); + + ret = dma_async_is_complete(cookie, last_completed, last_used); + + dma_set_tx_state(txstate, last_completed, last_used, 0); + + dev_dbg(dchan->device->dev, + "%s: exit, ret: %d, last_completed: %d, last_used: %d\n", + __func__, ret, last_completed, last_used); + + return ret; +} + +static void tsi721_issue_pending(struct dma_chan *dchan) +{ + struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan); + + dev_dbg(dchan->device->dev, "%s: Entry\n", __func__); + + if (tsi721_dma_is_idle(bdma_chan)) { + spin_lock_bh(&bdma_chan->lock); + tsi721_advance_work(bdma_chan); + spin_unlock_bh(&bdma_chan->lock); + } else + dev_dbg(dchan->device->dev, + "%s: DMA channel still busy\n", __func__); +} + +static +struct dma_async_tx_descriptor *tsi721_prep_rio_sg(struct dma_chan *dchan, + struct scatterlist *sgl, unsigned int sg_len, + enum dma_transfer_direction dir, unsigned long flags, + void *tinfo) +{ + struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan); + struct tsi721_tx_desc *desc = NULL; + struct tsi721_tx_desc *first = NULL; + struct scatterlist *sg; + struct rio_dma_ext *rext = tinfo; + u64 rio_addr = rext->rio_addr; /* limited to 64-bit rio_addr for now */ + unsigned int i; + u32 sys_size = dma_to_mport(dchan->device)->sys_size; + enum dma_rtype rtype; + + if (!sgl || !sg_len) { + dev_err(dchan->device->dev, "%s: No SG list\n", __func__); + return NULL; + } + + if (dir == DMA_DEV_TO_MEM) + rtype = NREAD; + else if (dir == DMA_MEM_TO_DEV) { + switch (rext->wr_type) { + case RDW_ALL_NWRITE: + rtype = ALL_NWRITE; + break; + case RDW_ALL_NWRITE_R: + rtype = ALL_NWRITE_R; + break; + case RDW_LAST_NWRITE_R: + default: + rtype = LAST_NWRITE_R; + break; + } + } else { + dev_err(dchan->device->dev, + "%s: Unsupported DMA direction option\n", __func__); + return NULL; + } + + for_each_sg(sgl, sg, sg_len, i) { + int err; + + dev_dbg(dchan->device->dev, "%s: sg #%d\n", __func__, i); + desc = tsi721_desc_get(bdma_chan); + if (!desc) { + dev_err(dchan->device->dev, + "Not enough descriptors available\n"); + goto err_desc_get; + } + + if (sg_is_last(sg)) + desc->interrupt = (flags & DMA_PREP_INTERRUPT) != 0; + else + desc->interrupt = false; + + desc->destid = rext->destid; + desc->rio_addr = rio_addr; + desc->rio_addr_u = 0; + + err = tsi721_fill_desc(bdma_chan, desc, sg, rtype, sys_size); + if (err) { + dev_err(dchan->device->dev, + "Failed to build desc: %d\n", err); + goto err_desc_get; + } + + rio_addr += sg_dma_len(sg); + + if (!first) + first = desc; + else + list_add_tail(&desc->desc_node, &first->tx_list); + } + + first->txd.cookie = -EBUSY; + desc->txd.flags = flags; + + return &first->txd; + +err_desc_get: + tsi721_desc_put(bdma_chan, first); + return NULL; +} + +static int tsi721_device_control(struct dma_chan *dchan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan); + struct tsi721_tx_desc *desc, *_d; + LIST_HEAD(list); + + dev_dbg(dchan->device->dev, "%s: Entry\n", __func__); + + if (cmd != DMA_TERMINATE_ALL) + return -ENXIO; + + spin_lock_bh(&bdma_chan->lock); + + /* make sure to stop the transfer */ + iowrite32(TSI721_DMAC_CTL_SUSP, bdma_chan->regs + TSI721_DMAC_CTL); + + list_splice_init(&bdma_chan->active_list, &list); + list_splice_init(&bdma_chan->queue, &list); + + list_for_each_entry_safe(desc, _d, &list, desc_node) + tsi721_dma_chain_complete(bdma_chan, desc); + + spin_unlock_bh(&bdma_chan->lock); + + return 0; +} + +int __devinit tsi721_register_dma(struct tsi721_device *priv) +{ + int i; + int nr_channels = TSI721_DMA_MAXCH; + int err; + struct rio_mport *mport = priv->mport; + + mport->dma.dev = &priv->pdev->dev; + mport->dma.chancnt = nr_channels; + + INIT_LIST_HEAD(&mport->dma.channels); + + for (i = 0; i < nr_channels; i++) { + struct tsi721_bdma_chan *bdma_chan = &priv->bdma[i]; + + if (i == TSI721_DMACH_MAINT) + continue; + + bdma_chan->bd_num = 64; + bdma_chan->regs = priv->regs + TSI721_DMAC_BASE(i); + + bdma_chan->dchan.device = &mport->dma; + bdma_chan->dchan.cookie = 1; + bdma_chan->dchan.chan_id = i; + bdma_chan->id = i; + + spin_lock_init(&bdma_chan->lock); + + INIT_LIST_HEAD(&bdma_chan->active_list); + INIT_LIST_HEAD(&bdma_chan->queue); + INIT_LIST_HEAD(&bdma_chan->free_list); + + tasklet_init(&bdma_chan->tasklet, tsi721_dma_tasklet, + (unsigned long)bdma_chan); + tasklet_disable(&bdma_chan->tasklet); + list_add_tail(&bdma_chan->dchan.device_node, + &mport->dma.channels); + } + + dma_cap_zero(mport->dma.cap_mask); + dma_cap_set(DMA_PRIVATE, mport->dma.cap_mask); + dma_cap_set(DMA_SLAVE, mport->dma.cap_mask); + + mport->dma.device_alloc_chan_resources = tsi721_alloc_chan_resources; + mport->dma.device_free_chan_resources = tsi721_free_chan_resources; + mport->dma.device_tx_status = tsi721_tx_status; + mport->dma.device_issue_pending = tsi721_issue_pending; + mport->dma.device_prep_slave_sg = tsi721_prep_rio_sg; + mport->dma.device_control = tsi721_device_control; + + err = dma_async_device_register(&mport->dma); + if (err) + dev_err(&priv->pdev->dev, "Failed to register DMA device\n"); + + return err; +} diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c index 86c9a09..c40665a 100644 --- a/drivers/rapidio/rio.c +++ b/drivers/rapidio/rio.c @@ -1121,6 +1121,87 @@ int rio_std_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount, return 0; } +#ifdef CONFIG_RAPIDIO_DMA_ENGINE + +static bool rio_chan_filter(struct dma_chan *chan, void *arg) +{ + struct rio_dev *rdev = arg; + + /* Check that DMA device belongs to the right MPORT */ + return (rdev->net->hport == + container_of(chan->device, struct rio_mport, dma)); +} + +/** + * rio_request_dma - request RapidIO capable DMA channel that supports + * specified target RapidIO device. + * @rdev: RIO device control structure + * + * Returns pointer to allocated DMA channel or NULL if failed. + */ +struct dma_chan *rio_request_dma(struct rio_dev *rdev) +{ + dma_cap_mask_t mask; + struct dma_chan *dchan; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dchan = dma_request_channel(mask, rio_chan_filter, rdev); + + return dchan; +} +EXPORT_SYMBOL_GPL(rio_request_dma); + +/** + * rio_release_dma - release specified DMA channel + * @dchan: DMA channel to release + */ +void rio_release_dma(struct dma_chan *dchan) +{ + dma_release_channel(dchan); +} +EXPORT_SYMBOL_GPL(rio_release_dma); + +/** + * rio_dma_prep_slave_sg - RapidIO specific wrapper + * for device_prep_slave_sg callback defined by DMAENGINE. + * @rdev: RIO device control structure + * @dchan: DMA channel to configure + * @data: RIO specific data descriptor + * @direction: DMA data transfer direction (TO or FROM the device) + * @flags: dmaengine defined flags + * + * Initializes RapidIO capable DMA channel for the specified data transfer. + * Uses DMA channel private extension to pass information related to remote + * target RIO device. + * Returns pointer to DMA transaction descriptor or NULL if failed. + */ +struct dma_async_tx_descriptor *rio_dma_prep_slave_sg(struct rio_dev *rdev, + struct dma_chan *dchan, struct rio_dma_data *data, + enum dma_transfer_direction direction, unsigned long flags) +{ + struct dma_async_tx_descriptor *txd = NULL; + struct rio_dma_ext rio_ext; + + if (dchan->device->device_prep_slave_sg == NULL) { + pr_err("%s: prep_rio_sg == NULL\n", __func__); + return NULL; + } + + rio_ext.destid = rdev->destid; + rio_ext.rio_addr_u = data->rio_addr_u; + rio_ext.rio_addr = data->rio_addr; + rio_ext.wr_type = data->wr_type; + + txd = dmaengine_prep_rio_sg(dchan, data->sg, data->sg_len, + direction, flags, &rio_ext); + + return txd; +} +EXPORT_SYMBOL_GPL(rio_dma_prep_slave_sg); + +#endif /* CONFIG_RAPIDIO_DMA_ENGINE */ + static void rio_fixup_device(struct rio_dev *dev) { } diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 35819e3..6cc4358 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1033,7 +1033,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state, if (!retinfo) return -EFAULT; memset(&tmp, 0, sizeof(tmp)); - tty_lock(tty); + tty_lock(); tmp.line = tty->index; tmp.port = state->port; tmp.flags = state->tport.flags; @@ -1042,7 +1042,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state, tmp.close_delay = state->tport.close_delay; tmp.closing_wait = state->tport.closing_wait; tmp.custom_divisor = state->custom_divisor; - tty_unlock(tty); + tty_unlock(); if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) return -EFAULT; return 0; @@ -1059,12 +1059,12 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) return -EFAULT; - tty_lock(tty); + tty_lock(); change_spd = ((new_serial.flags ^ port->flags) & ASYNC_SPD_MASK) || new_serial.custom_divisor != state->custom_divisor; if (new_serial.irq || new_serial.port != state->port || new_serial.xmit_fifo_size != state->xmit_fifo_size) { - tty_unlock(tty); + tty_unlock(); return -EINVAL; } @@ -1074,7 +1074,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, (new_serial.xmit_fifo_size != state->xmit_fifo_size) || ((new_serial.flags & ~ASYNC_USR_MASK) != (port->flags & ~ASYNC_USR_MASK))) { - tty_unlock(tty); + tty_unlock(); return -EPERM; } port->flags = ((port->flags & ~ASYNC_USR_MASK) | @@ -1084,7 +1084,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, } if (new_serial.baud_base < 9600) { - tty_unlock(tty); + tty_unlock(); return -EINVAL; } @@ -1116,7 +1116,7 @@ check_and_exit: } } else retval = startup(tty, state); - tty_unlock(tty); + tty_unlock(); return retval; } diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index 6984e1a..e61cabd 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -1599,7 +1599,7 @@ static int cy_open(struct tty_struct *tty, struct file *filp) * If the port is the middle of closing, bail out now */ if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(tty, info->port.close_wait, + wait_event_interruptible_tty(info->port.close_wait, !(info->port.flags & ASYNC_CLOSING)); return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; } diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c index 656ad93..5c6c314 100644 --- a/drivers/tty/n_r3964.c +++ b/drivers/tty/n_r3964.c @@ -1065,8 +1065,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, TRACE_L("read()"); - /* FIXME: should use a private lock */ - tty_lock(tty); + tty_lock(); pClient = findClient(pInfo, task_pid(current)); if (pClient) { @@ -1078,7 +1077,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, goto unlock; } /* block until there is a message: */ - wait_event_interruptible_tty(tty, pInfo->read_wait, + wait_event_interruptible_tty(pInfo->read_wait, (pMsg = remove_msg(pInfo, pClient))); } @@ -1108,7 +1107,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, } ret = -EPERM; unlock: - tty_unlock(tty); + tty_unlock(); return ret; } @@ -1157,7 +1156,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file, pHeader->locks = 0; pHeader->owner = NULL; - tty_lock(tty); + tty_lock(); pClient = findClient(pInfo, task_pid(current)); if (pClient) { @@ -1176,7 +1175,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file, add_tx_queue(pInfo, pHeader); trigger_transmit(pInfo); - tty_unlock(tty); + tty_unlock(); return 0; } diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 65c7c62..5505ffc 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -47,7 +47,6 @@ static void pty_close(struct tty_struct *tty, struct file *filp) wake_up_interruptible(&tty->read_wait); wake_up_interruptible(&tty->write_wait); tty->packet = 0; - /* Review - krefs on tty_link ?? */ if (!tty->link) return; tty->link->packet = 0; @@ -63,9 +62,9 @@ static void pty_close(struct tty_struct *tty, struct file *filp) mutex_unlock(&devpts_mutex); } #endif - tty_unlock(tty); + tty_unlock(); tty_vhangup(tty->link); - tty_lock(tty); + tty_lock(); } } @@ -623,27 +622,26 @@ static int ptmx_open(struct inode *inode, struct file *filp) return retval; /* find a device that is not in use. */ - mutex_lock(&devpts_mutex); + tty_lock(); index = devpts_new_index(inode); + tty_unlock(); if (index < 0) { retval = index; goto err_file; } - mutex_unlock(&devpts_mutex); - mutex_lock(&tty_mutex); + mutex_lock(&devpts_mutex); tty = tty_init_dev(ptm_driver, index); + mutex_unlock(&devpts_mutex); + tty_lock(); + mutex_unlock(&tty_mutex); if (IS_ERR(tty)) { retval = PTR_ERR(tty); goto out; } - /* The tty returned here is locked so we can safely - drop the mutex */ - mutex_unlock(&tty_mutex); - set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ tty_add_file(tty, filp); @@ -656,17 +654,16 @@ static int ptmx_open(struct inode *inode, struct file *filp) if (retval) goto err_release; - tty_unlock(tty); + tty_unlock(); return 0; err_release: - tty_unlock(tty); + tty_unlock(); tty_release(inode, filp); return retval; out: - mutex_unlock(&tty_mutex); devpts_kill_index(inode, index); + tty_unlock(); err_file: - mutex_unlock(&devpts_mutex); tty_free_file(filp); return retval; } diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 7264d4d..80b6b1b 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -3976,7 +3976,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(tty, info->close_wait, + wait_event_interruptible_tty(info->close_wait, !(info->flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART if (info->flags & ASYNC_HUP_NOTIFY) @@ -4052,9 +4052,9 @@ block_til_ready(struct tty_struct *tty, struct file * filp, printk("block_til_ready blocking: ttyS%d, count = %d\n", info->line, info->count); #endif - tty_unlock(tty); + tty_unlock(); schedule(); - tty_lock(tty); + tty_lock(); } set_current_state(TASK_RUNNING); remove_wait_queue(&info->open_wait, &wait); @@ -4115,7 +4115,7 @@ rs_open(struct tty_struct *tty, struct file * filp) */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(tty, info->close_wait, + wait_event_interruptible_tty(info->close_wait, !(info->flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART return ((info->flags & ASYNC_HUP_NOTIFY) ? diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 5ed0daa..593d40a 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -3338,9 +3338,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, printk("%s(%d):block_til_ready blocking on %s count=%d\n", __FILE__,__LINE__, tty->driver->name, port->count ); - tty_unlock(tty); + tty_unlock(); schedule(); - tty_lock(tty); + tty_lock(); } set_current_state(TASK_RUNNING); diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index 45b43f1..aa1debf 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -3336,9 +3336,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, } DBGINFO(("%s block_til_ready wait\n", tty->driver->name)); - tty_unlock(tty); + tty_unlock(); schedule(); - tty_lock(tty); + tty_lock(); } set_current_state(TASK_RUNNING); diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index 4a1e4f0..a3dddc1 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -3357,9 +3357,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, printk("%s(%d):%s block_til_ready() count=%d\n", __FILE__,__LINE__, tty->driver->name, port->count ); - tty_unlock(tty); + tty_unlock(); schedule(); - tty_lock(tty); + tty_lock(); } set_current_state(TASK_RUNNING); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 9e930c0..b425c79 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -185,7 +185,6 @@ void free_tty_struct(struct tty_struct *tty) put_device(tty->dev); kfree(tty->write_buf); tty_buffer_free_all(tty); - tty->magic = 0xDEADDEAD; kfree(tty); } @@ -574,7 +573,7 @@ void __tty_hangup(struct tty_struct *tty) } spin_unlock(&redirect_lock); - tty_lock(tty); + tty_lock(); /* some functions below drop BTM, so we need this bit */ set_bit(TTY_HUPPING, &tty->flags); @@ -667,7 +666,7 @@ void __tty_hangup(struct tty_struct *tty) clear_bit(TTY_HUPPING, &tty->flags); tty_ldisc_enable(tty); - tty_unlock(tty); + tty_unlock(); if (f) fput(f); @@ -1104,12 +1103,12 @@ void tty_write_message(struct tty_struct *tty, char *msg) { if (tty) { mutex_lock(&tty->atomic_write_lock); - tty_lock(tty); + tty_lock(); if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) { - tty_unlock(tty); + tty_unlock(); tty->ops->write(tty, msg, strlen(msg)); } else - tty_unlock(tty); + tty_unlock(); tty_write_unlock(tty); } return; @@ -1404,7 +1403,6 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) } initialize_tty_struct(tty, driver, idx); - tty_lock(tty); retval = tty_driver_install_tty(driver, tty); if (retval < 0) goto err_deinit_tty; @@ -1417,11 +1415,9 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) retval = tty_ldisc_setup(tty, tty->link); if (retval) goto err_release_tty; - /* Return the tty locked so that it cannot vanish under the caller */ return tty; err_deinit_tty: - tty_unlock(tty); deinitialize_tty_struct(tty); free_tty_struct(tty); err_module_put: @@ -1430,7 +1426,6 @@ err_module_put: /* call the tty release_tty routine to clean out this slot */ err_release_tty: - tty_unlock(tty); printk_ratelimited(KERN_INFO "tty_init_dev: ldisc open failed, " "clearing slot %d\n", idx); release_tty(tty, idx); @@ -1633,7 +1628,7 @@ int tty_release(struct inode *inode, struct file *filp) if (tty_paranoia_check(tty, inode, __func__)) return 0; - tty_lock(tty); + tty_lock(); check_tty_count(tty, __func__); __tty_fasync(-1, filp, 0); @@ -1642,11 +1637,10 @@ int tty_release(struct inode *inode, struct file *filp) pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER); devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0; - /* Review: parallel close */ o_tty = tty->link; if (tty_release_checks(tty, o_tty, idx)) { - tty_unlock(tty); + tty_unlock(); return 0; } @@ -1658,7 +1652,7 @@ int tty_release(struct inode *inode, struct file *filp) if (tty->ops->close) tty->ops->close(tty, filp); - tty_unlock(tty); + tty_unlock(); /* * Sanity check: if tty->count is going to zero, there shouldn't be * any waiters on tty->read_wait or tty->write_wait. We test the @@ -1681,7 +1675,7 @@ int tty_release(struct inode *inode, struct file *filp) opens on /dev/tty */ mutex_lock(&tty_mutex); - tty_lock_pair(tty, o_tty); + tty_lock(); tty_closing = tty->count <= 1; o_tty_closing = o_tty && (o_tty->count <= (pty_master ? 1 : 0)); @@ -1712,7 +1706,7 @@ int tty_release(struct inode *inode, struct file *filp) printk(KERN_WARNING "%s: %s: read/write wait queue active!\n", __func__, tty_name(tty, buf)); - tty_unlock_pair(tty, o_tty); + tty_unlock(); mutex_unlock(&tty_mutex); schedule(); } @@ -1775,7 +1769,7 @@ int tty_release(struct inode *inode, struct file *filp) /* check whether both sides are closing ... */ if (!tty_closing || (o_tty && !o_tty_closing)) { - tty_unlock_pair(tty, o_tty); + tty_unlock(); return 0; } @@ -1788,16 +1782,14 @@ int tty_release(struct inode *inode, struct file *filp) tty_ldisc_release(tty, o_tty); /* * The release_tty function takes care of the details of clearing - * the slots and preserving the termios structure. The tty_unlock_pair - * should be safe as we keep a kref while the tty is locked (so the - * unlock never unlocks a freed tty). + * the slots and preserving the termios structure. */ release_tty(tty, idx); - tty_unlock_pair(tty, o_tty); /* Make this pty number available for reallocation */ if (devpts) devpts_kill_index(inode, idx); + tty_unlock(); return 0; } @@ -1901,9 +1893,6 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, * Locking: tty_mutex protects tty, tty_lookup_driver and tty_init_dev. * tty->count should protect the rest. * ->siglock protects ->signal/->sighand - * - * Note: the tty_unlock/lock cases without a ref are only safe due to - * tty_mutex */ static int tty_open(struct inode *inode, struct file *filp) @@ -1927,7 +1916,8 @@ retry_open: retval = 0; mutex_lock(&tty_mutex); - /* This is protected by the tty_mutex */ + tty_lock(); + tty = tty_open_current_tty(device, filp); if (IS_ERR(tty)) { retval = PTR_ERR(tty); @@ -1948,19 +1938,17 @@ retry_open: } if (tty) { - tty_lock(tty); retval = tty_reopen(tty); - if (retval < 0) { - tty_unlock(tty); + if (retval) tty = ERR_PTR(retval); - } - } else /* Returns with the tty_lock held for now */ + } else tty = tty_init_dev(driver, index); mutex_unlock(&tty_mutex); if (driver) tty_driver_kref_put(driver); if (IS_ERR(tty)) { + tty_unlock(); retval = PTR_ERR(tty); goto err_file; } @@ -1989,7 +1977,7 @@ retry_open: printk(KERN_DEBUG "%s: error %d in opening %s...\n", __func__, retval, tty->name); #endif - tty_unlock(tty); /* need to call tty_release without BTM */ + tty_unlock(); /* need to call tty_release without BTM */ tty_release(inode, filp); if (retval != -ERESTARTSYS) return retval; @@ -2001,15 +1989,17 @@ retry_open: /* * Need to reset f_op in case a hangup happened. */ + tty_lock(); if (filp->f_op == &hung_up_tty_fops) filp->f_op = &tty_fops; + tty_unlock(); goto retry_open; } - tty_unlock(tty); + tty_unlock(); mutex_lock(&tty_mutex); - tty_lock(tty); + tty_lock(); spin_lock_irq(¤t->sighand->siglock); if (!noctty && current->signal->leader && @@ -2017,10 +2007,11 @@ retry_open: tty->session == NULL) __proc_set_tty(current, tty); spin_unlock_irq(¤t->sighand->siglock); - tty_unlock(tty); + tty_unlock(); mutex_unlock(&tty_mutex); return 0; err_unlock: + tty_unlock(); mutex_unlock(&tty_mutex); /* after locks to avoid deadlock */ if (!IS_ERR_OR_NULL(driver)) @@ -2103,13 +2094,10 @@ out: static int tty_fasync(int fd, struct file *filp, int on) { - struct tty_struct *tty = file_tty(filp); int retval; - - tty_lock(tty); + tty_lock(); retval = __tty_fasync(fd, filp, on); - tty_unlock(tty); - + tty_unlock(); return retval; } @@ -2946,7 +2934,6 @@ void initialize_tty_struct(struct tty_struct *tty, tty->pgrp = NULL; tty->overrun_time = jiffies; tty_buffer_init(tty); - mutex_init(&tty->legacy_mutex); mutex_init(&tty->termios_mutex); mutex_init(&tty->ldisc_mutex); init_waitqueue_head(&tty->write_wait); diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index ba8be39..9911eb6 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -568,7 +568,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) if (IS_ERR(new_ldisc)) return PTR_ERR(new_ldisc); - tty_lock(tty); + tty_lock(); /* * We need to look at the tty locking here for pty/tty pairs * when both sides try to change in parallel. @@ -582,12 +582,12 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) */ if (tty->ldisc->ops->num == ldisc) { - tty_unlock(tty); + tty_unlock(); tty_ldisc_put(new_ldisc); return 0; } - tty_unlock(tty); + tty_unlock(); /* * Problem: What do we do if this blocks ? * We could deadlock here @@ -595,7 +595,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) tty_wait_until_sent(tty, 0); - tty_lock(tty); + tty_lock(); mutex_lock(&tty->ldisc_mutex); /* @@ -605,10 +605,10 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) { mutex_unlock(&tty->ldisc_mutex); - tty_unlock(tty); + tty_unlock(); wait_event(tty_ldisc_wait, test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0); - tty_lock(tty); + tty_lock(); mutex_lock(&tty->ldisc_mutex); } @@ -623,7 +623,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) o_ldisc = tty->ldisc; - tty_unlock(tty); + tty_unlock(); /* * Make sure we don't change while someone holds a * reference to the line discipline. The TTY_LDISC bit @@ -650,7 +650,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) retval = tty_ldisc_wait_idle(tty, 5 * HZ); - tty_lock(tty); + tty_lock(); mutex_lock(&tty->ldisc_mutex); /* handle wait idle failure locked */ @@ -665,7 +665,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) clear_bit(TTY_LDISC_CHANGING, &tty->flags); mutex_unlock(&tty->ldisc_mutex); tty_ldisc_put(new_ldisc); - tty_unlock(tty); + tty_unlock(); return -EIO; } @@ -708,7 +708,7 @@ enable: if (o_work) schedule_work(&o_tty->buf.work); mutex_unlock(&tty->ldisc_mutex); - tty_unlock(tty); + tty_unlock(); return retval; } @@ -816,11 +816,11 @@ void tty_ldisc_hangup(struct tty_struct *tty) * need to wait for another function taking the BTM */ clear_bit(TTY_LDISC, &tty->flags); - tty_unlock(tty); + tty_unlock(); cancel_work_sync(&tty->buf.work); mutex_unlock(&tty->ldisc_mutex); retry: - tty_lock(tty); + tty_lock(); mutex_lock(&tty->ldisc_mutex); /* At this point we have a closed ldisc and we want to @@ -831,7 +831,7 @@ retry: if (atomic_read(&tty->ldisc->users) != 1) { char cur_n[TASK_COMM_LEN], tty_n[64]; long timeout = 3 * HZ; - tty_unlock(tty); + tty_unlock(); while (tty_ldisc_wait_idle(tty, timeout) == -EBUSY) { timeout = MAX_SCHEDULE_TIMEOUT; @@ -894,23 +894,6 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) tty_ldisc_enable(tty); return 0; } - -static void tty_ldisc_kill(struct tty_struct *tty) -{ - mutex_lock(&tty->ldisc_mutex); - /* - * Now kill off the ldisc - */ - tty_ldisc_close(tty, tty->ldisc); - tty_ldisc_put(tty->ldisc); - /* Force an oops if we mess this up */ - tty->ldisc = NULL; - - /* Ensure the next open requests the N_TTY ldisc */ - tty_set_termios_ldisc(tty, N_TTY); - mutex_unlock(&tty->ldisc_mutex); -} - /** * tty_ldisc_release - release line discipline * @tty: tty being shut down @@ -929,19 +912,27 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) * race with the set_ldisc code path. */ - tty_unlock_pair(tty, o_tty); + tty_unlock(); tty_ldisc_halt(tty); tty_ldisc_flush_works(tty); - if (o_tty) { - tty_ldisc_halt(o_tty); - tty_ldisc_flush_works(o_tty); - } - tty_lock_pair(tty, o_tty); + tty_lock(); + mutex_lock(&tty->ldisc_mutex); + /* + * Now kill off the ldisc + */ + tty_ldisc_close(tty, tty->ldisc); + tty_ldisc_put(tty->ldisc); + /* Force an oops if we mess this up */ + tty->ldisc = NULL; + + /* Ensure the next open requests the N_TTY ldisc */ + tty_set_termios_ldisc(tty, N_TTY); + mutex_unlock(&tty->ldisc_mutex); - tty_ldisc_kill(tty); + /* This will need doing differently if we need to lock */ if (o_tty) - tty_ldisc_kill(o_tty); + tty_ldisc_release(o_tty, NULL); /* And the memory resources remaining (buffers, termios) will be disposed of when the kref hits zero */ diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c index 67feac9..9ff986c 100644 --- a/drivers/tty/tty_mutex.c +++ b/drivers/tty/tty_mutex.c @@ -4,70 +4,29 @@ #include <linux/semaphore.h> #include <linux/sched.h> -/* Legacy tty mutex glue */ - -enum { - TTY_MUTEX_NORMAL, - TTY_MUTEX_NESTED, -}; +/* + * The 'big tty mutex' + * + * This mutex is taken and released by tty_lock() and tty_unlock(), + * replacing the older big kernel lock. + * It can no longer be taken recursively, and does not get + * released implicitly while sleeping. + * + * Don't use in new code. + */ +static DEFINE_MUTEX(big_tty_mutex); /* * Getting the big tty mutex. */ - -static void __lockfunc tty_lock_nested(struct tty_struct *tty, - unsigned int subclass) +void __lockfunc tty_lock(void) { - if (tty->magic != TTY_MAGIC) { - printk(KERN_ERR "L Bad %p\n", tty); - WARN_ON(1); - return; - } - tty_kref_get(tty); - mutex_lock_nested(&tty->legacy_mutex, subclass); -} - -void __lockfunc tty_lock(struct tty_struct *tty) -{ - return tty_lock_nested(tty, TTY_MUTEX_NORMAL); + mutex_lock(&big_tty_mutex); } EXPORT_SYMBOL(tty_lock); -void __lockfunc tty_unlock(struct tty_struct *tty) +void __lockfunc tty_unlock(void) { - if (tty->magic != TTY_MAGIC) { - printk(KERN_ERR "U Bad %p\n", tty); - WARN_ON(1); - return; - } - mutex_unlock(&tty->legacy_mutex); - tty_kref_put(tty); + mutex_unlock(&big_tty_mutex); } EXPORT_SYMBOL(tty_unlock); - -/* - * Getting the big tty mutex for a pair of ttys with lock ordering - * On a non pty/tty pair tty2 can be NULL which is just fine. - */ -void __lockfunc tty_lock_pair(struct tty_struct *tty, - struct tty_struct *tty2) -{ - if (tty < tty2) { - tty_lock(tty); - tty_lock_nested(tty2, TTY_MUTEX_NESTED); - } else { - if (tty2 && tty2 != tty) - tty_lock(tty2); - tty_lock_nested(tty, TTY_MUTEX_NESTED); - } -} -EXPORT_SYMBOL(tty_lock_pair); - -void __lockfunc tty_unlock_pair(struct tty_struct *tty, - struct tty_struct *tty2) -{ - tty_unlock(tty); - if (tty2 && tty2 != tty) - tty_unlock(tty2); -} -EXPORT_SYMBOL(tty_unlock_pair); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index d9cca95..bf6e238 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -230,7 +230,7 @@ int tty_port_block_til_ready(struct tty_port *port, /* block if port is in the process of being closed */ if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { - wait_event_interruptible_tty(tty, port->close_wait, + wait_event_interruptible_tty(port->close_wait, !(port->flags & ASYNC_CLOSING)); if (port->flags & ASYNC_HUP_NOTIFY) return -EAGAIN; @@ -296,9 +296,9 @@ int tty_port_block_til_ready(struct tty_port *port, retval = -ERESTARTSYS; break; } - tty_unlock(tty); + tty_unlock(); schedule(); - tty_lock(tty); + tty_lock(); } finish_wait(&port->open_wait, &wait); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index a290be5..0217f74 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2210,7 +2210,7 @@ config FB_XILINX config FB_COBALT tristate "Cobalt server LCD frame buffer support" - depends on FB && MIPS_COBALT + depends on FB && (MIPS_COBALT || MIPS_SEAD3) config FB_SH7760 bool "SH7760/SH7763/SH7720/SH7721 LCDC support" @@ -2382,6 +2382,39 @@ config FB_BROADSHEET and could also have been called by other names when coupled with a bridge adapter. +config FB_AUO_K190X + tristate "AUO-K190X EPD controller support" + depends on FB + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select FB_SYS_FOPS + select FB_DEFERRED_IO + help + Provides support for epaper controllers from the K190X series + of AUO. These controllers can be used to drive epaper displays + from Sipix. + + This option enables the common support, shared by the individual + controller drivers. You will also have to enable the driver + for the controller type used in your device. + +config FB_AUO_K1900 + tristate "AUO-K1900 EPD controller support" + depends on FB && FB_AUO_K190X + help + This driver implements support for the AUO K1900 epd-controller. + This controller can drive Sipix epaper displays but can only do + serial updates, reducing the number of possible frames per second. + +config FB_AUO_K1901 + tristate "AUO-K1901 EPD controller support" + depends on FB && FB_AUO_K190X + help + This driver implements support for the AUO K1901 epd-controller. + This controller can drive Sipix epaper displays and supports + concurrent updates, making higher frames per second possible. + config FB_JZ4740 tristate "JZ4740 LCD framebuffer support" depends on FB && MACH_JZ4740 diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 9356add..ee8dafb 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -118,6 +118,9 @@ obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o obj-$(CONFIG_FB_MAXINE) += maxinefb.o obj-$(CONFIG_FB_METRONOME) += metronomefb.o obj-$(CONFIG_FB_BROADSHEET) += broadsheetfb.o +obj-$(CONFIG_FB_AUO_K190X) += auo_k190x.o +obj-$(CONFIG_FB_AUO_K1900) += auo_k1900fb.o +obj-$(CONFIG_FB_AUO_K1901) += auo_k1901fb.o obj-$(CONFIG_FB_S1D13XXX) += s1d13xxxfb.o obj-$(CONFIG_FB_SH7760) += sh7760fb.o obj-$(CONFIG_FB_IMX) += imxfb.o diff --git a/drivers/video/auo_k1900fb.c b/drivers/video/auo_k1900fb.c new file mode 100644 index 0000000..c36cf96 --- /dev/null +++ b/drivers/video/auo_k1900fb.c @@ -0,0 +1,198 @@ +/* + * auok190xfb.c -- FB driver for AUO-K1900 controllers + * + * Copyright (C) 2011, 2012 Heiko Stuebner <heiko@sntech.de> + * + * based on broadsheetfb.c + * + * Copyright (C) 2008, Jaya Kumar + * + * 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. + * + * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven. + * + * This driver is written to be used with the AUO-K1900 display controller. + * + * It is intended to be architecture independent. A board specific driver + * must be used to perform all the physical IO interactions. + * + * The controller supports different update modes: + * mode0+1 16 step gray (4bit) + * mode2 4 step gray (2bit) - FIXME: add strange refresh + * mode3 2 step gray (1bit) - FIXME: add strange refresh + * mode4 handwriting mode (strange behaviour) + * mode5 automatic selection of update mode + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/list.h> +#include <linux/firmware.h> +#include <linux/gpio.h> +#include <linux/pm_runtime.h> + +#include <video/auo_k190xfb.h> + +#include "auo_k190x.h" + +/* + * AUO-K1900 specific commands + */ + +#define AUOK1900_CMD_PARTIALDISP 0x1001 +#define AUOK1900_CMD_ROTATION 0x1006 +#define AUOK1900_CMD_LUT_STOP 0x1009 + +#define AUOK1900_INIT_TEMP_AVERAGE (1 << 13) +#define AUOK1900_INIT_ROTATE(_x) ((_x & 0x3) << 10) +#define AUOK1900_INIT_RESOLUTION(_res) ((_res & 0x7) << 2) + +static void auok1900_init(struct auok190xfb_par *par) +{ + struct auok190x_board *board = par->board; + u16 init_param = 0; + + init_param |= AUOK1900_INIT_TEMP_AVERAGE; + init_param |= AUOK1900_INIT_ROTATE(par->rotation); + init_param |= AUOK190X_INIT_INVERSE_WHITE; + init_param |= AUOK190X_INIT_FORMAT0; + init_param |= AUOK1900_INIT_RESOLUTION(par->resolution); + init_param |= AUOK190X_INIT_SHIFT_RIGHT; + + auok190x_send_cmdargs(par, AUOK190X_CMD_INIT, 1, &init_param); + + /* let the controller finish */ + board->wait_for_rdy(par); +} + +static void auok1900_update_region(struct auok190xfb_par *par, int mode, + u16 y1, u16 y2) +{ + struct device *dev = par->info->device; + unsigned char *buf = (unsigned char *)par->info->screen_base; + int xres = par->info->var.xres; + u16 args[4]; + + pm_runtime_get_sync(dev); + + mutex_lock(&(par->io_lock)); + + /* y1 and y2 must be a multiple of 2 so drop the lowest bit */ + y1 &= 0xfffe; + y2 &= 0xfffe; + + dev_dbg(dev, "update (x,y,w,h,mode)=(%d,%d,%d,%d,%d)\n", + 1, y1+1, xres, y2-y1, mode); + + /* to FIX handle different partial update modes */ + args[0] = mode | 1; + args[1] = y1 + 1; + args[2] = xres; + args[3] = y2 - y1; + buf += y1 * xres; + auok190x_send_cmdargs_pixels(par, AUOK1900_CMD_PARTIALDISP, 4, args, + ((y2 - y1) * xres)/2, (u16 *) buf); + auok190x_send_command(par, AUOK190X_CMD_DATA_STOP); + + par->update_cnt++; + + mutex_unlock(&(par->io_lock)); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} + +static void auok1900fb_dpy_update_pages(struct auok190xfb_par *par, + u16 y1, u16 y2) +{ + int mode; + + if (par->update_mode < 0) { + mode = AUOK190X_UPDATE_MODE(1); + par->last_mode = -1; + } else { + mode = AUOK190X_UPDATE_MODE(par->update_mode); + par->last_mode = par->update_mode; + } + + if (par->flash) + mode |= AUOK190X_UPDATE_NONFLASH; + + auok1900_update_region(par, mode, y1, y2); +} + +static void auok1900fb_dpy_update(struct auok190xfb_par *par) +{ + int mode; + + if (par->update_mode < 0) { + mode = AUOK190X_UPDATE_MODE(0); + par->last_mode = -1; + } else { + mode = AUOK190X_UPDATE_MODE(par->update_mode); + par->last_mode = par->update_mode; + } + + if (par->flash) + mode |= AUOK190X_UPDATE_NONFLASH; + + auok1900_update_region(par, mode, 0, par->info->var.yres); + par->update_cnt = 0; +} + +static bool auok1900fb_need_refresh(struct auok190xfb_par *par) +{ + return (par->update_cnt > 10); +} + +static int __devinit auok1900fb_probe(struct platform_device *pdev) +{ + struct auok190x_init_data init; + struct auok190x_board *board; + + /* pick up board specific routines */ + board = pdev->dev.platform_data; + if (!board) + return -EINVAL; + + /* fill temporary init struct for common init */ + init.id = "auo_k1900fb"; + init.board = board; + init.update_partial = auok1900fb_dpy_update_pages; + init.update_all = auok1900fb_dpy_update; + init.need_refresh = auok1900fb_need_refresh; + init.init = auok1900_init; + + return auok190x_common_probe(pdev, &init); +} + +static int __devexit auok1900fb_remove(struct platform_device *pdev) +{ + return auok190x_common_remove(pdev); +} + +static struct platform_driver auok1900fb_driver = { + .probe = auok1900fb_probe, + .remove = __devexit_p(auok1900fb_remove), + .driver = { + .owner = THIS_MODULE, + .name = "auo_k1900fb", + .pm = &auok190x_pm, + }, +}; +module_platform_driver(auok1900fb_driver); + +MODULE_DESCRIPTION("framebuffer driver for the AUO-K1900 EPD controller"); +MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/auo_k1901fb.c b/drivers/video/auo_k1901fb.c new file mode 100644 index 0000000..1c054c1 --- /dev/null +++ b/drivers/video/auo_k1901fb.c @@ -0,0 +1,251 @@ +/* + * auok190xfb.c -- FB driver for AUO-K1901 controllers + * + * Copyright (C) 2011, 2012 Heiko Stuebner <heiko@sntech.de> + * + * based on broadsheetfb.c + * + * Copyright (C) 2008, Jaya Kumar + * + * 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. + * + * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven. + * + * This driver is written to be used with the AUO-K1901 display controller. + * + * It is intended to be architecture independent. A board specific driver + * must be used to perform all the physical IO interactions. + * + * The controller supports different update modes: + * mode0+1 16 step gray (4bit) + * mode2+3 4 step gray (2bit) + * mode4+5 2 step gray (1bit) + * - mode4 is described as "without LUT" + * mode7 automatic selection of update mode + * + * The most interesting difference to the K1900 is the ability to do screen + * updates in an asynchronous fashion. Where the K1900 needs to wait for the + * current update to complete, the K1901 can process later updates already. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/list.h> +#include <linux/firmware.h> +#include <linux/gpio.h> +#include <linux/pm_runtime.h> + +#include <video/auo_k190xfb.h> + +#include "auo_k190x.h" + +/* + * AUO-K1901 specific commands + */ + +#define AUOK1901_CMD_LUT_INTERFACE 0x0005 +#define AUOK1901_CMD_DMA_START 0x1001 +#define AUOK1901_CMD_CURSOR_START 0x1007 +#define AUOK1901_CMD_CURSOR_STOP AUOK190X_CMD_DATA_STOP +#define AUOK1901_CMD_DDMA_START 0x1009 + +#define AUOK1901_INIT_GATE_PULSE_LOW (0 << 14) +#define AUOK1901_INIT_GATE_PULSE_HIGH (1 << 14) +#define AUOK1901_INIT_SINGLE_GATE (0 << 13) +#define AUOK1901_INIT_DOUBLE_GATE (1 << 13) + +/* Bits to pixels + * Mode 15-12 11-8 7-4 3-0 + * format2 2 T 1 T + * format3 1 T 2 T + * format4 T 2 T 1 + * format5 T 1 T 2 + * + * halftone modes: + * format6 2 2 1 1 + * format7 1 1 2 2 + */ +#define AUOK1901_INIT_FORMAT2 (1 << 7) +#define AUOK1901_INIT_FORMAT3 ((1 << 7) | (1 << 6)) +#define AUOK1901_INIT_FORMAT4 (1 << 8) +#define AUOK1901_INIT_FORMAT5 ((1 << 8) | (1 << 6)) +#define AUOK1901_INIT_FORMAT6 ((1 << 8) | (1 << 7)) +#define AUOK1901_INIT_FORMAT7 ((1 << 8) | (1 << 7) | (1 << 6)) + +/* res[4] to bit 10 + * res[3-0] to bits 5-2 + */ +#define AUOK1901_INIT_RESOLUTION(_res) (((_res & (1 << 4)) << 6) \ + | ((_res & 0xf) << 2)) + +/* + * portrait / landscape orientation in AUOK1901_CMD_DMA_START + */ +#define AUOK1901_DMA_ROTATE90(_rot) ((_rot & 1) << 13) + +/* + * equivalent to 1 << 11, needs the ~ to have same rotation like K1900 + */ +#define AUOK1901_DDMA_ROTATE180(_rot) ((~_rot & 2) << 10) + +static void auok1901_init(struct auok190xfb_par *par) +{ + struct auok190x_board *board = par->board; + u16 init_param = 0; + + init_param |= AUOK190X_INIT_INVERSE_WHITE; + init_param |= AUOK190X_INIT_FORMAT0; + init_param |= AUOK1901_INIT_RESOLUTION(par->resolution); + init_param |= AUOK190X_INIT_SHIFT_LEFT; + + auok190x_send_cmdargs(par, AUOK190X_CMD_INIT, 1, &init_param); + + /* let the controller finish */ + board->wait_for_rdy(par); +} + +static void auok1901_update_region(struct auok190xfb_par *par, int mode, + u16 y1, u16 y2) +{ + struct device *dev = par->info->device; + unsigned char *buf = (unsigned char *)par->info->screen_base; + int xres = par->info->var.xres; + u16 args[5]; + + pm_runtime_get_sync(dev); + + mutex_lock(&(par->io_lock)); + + /* y1 and y2 must be a multiple of 2 so drop the lowest bit */ + y1 &= 0xfffe; + y2 &= 0xfffe; + + dev_dbg(dev, "update (x,y,w,h,mode)=(%d,%d,%d,%d,%d)\n", + 1, y1+1, xres, y2-y1, mode); + + /* K1901: first transfer the region data */ + args[0] = AUOK1901_DMA_ROTATE90(par->rotation) | 1; + args[1] = y1 + 1; + args[2] = xres; + args[3] = y2 - y1; + buf += y1 * xres; + auok190x_send_cmdargs_pixels_nowait(par, AUOK1901_CMD_DMA_START, 4, + args, ((y2 - y1) * xres)/2, + (u16 *) buf); + auok190x_send_command_nowait(par, AUOK190X_CMD_DATA_STOP); + + /* K1901: second tell the controller to update the region with mode */ + args[0] = mode | AUOK1901_DDMA_ROTATE180(par->rotation); + args[1] = 1; + args[2] = y1 + 1; + args[3] = xres; + args[4] = y2 - y1; + auok190x_send_cmdargs_nowait(par, AUOK1901_CMD_DDMA_START, 5, args); + + par->update_cnt++; + + mutex_unlock(&(par->io_lock)); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} + +static void auok1901fb_dpy_update_pages(struct auok190xfb_par *par, + u16 y1, u16 y2) +{ + int mode; + + if (par->update_mode < 0) { + mode = AUOK190X_UPDATE_MODE(1); + par->last_mode = -1; + } else { + mode = AUOK190X_UPDATE_MODE(par->update_mode); + par->last_mode = par->update_mode; + } + + if (par->flash) + mode |= AUOK190X_UPDATE_NONFLASH; + + auok1901_update_region(par, mode, y1, y2); +} + +static void auok1901fb_dpy_update(struct auok190xfb_par *par) +{ + int mode; + + /* When doing full updates, wait for the controller to be ready + * This will hopefully catch some hangs of the K1901 + */ + par->board->wait_for_rdy(par); + + if (par->update_mode < 0) { + mode = AUOK190X_UPDATE_MODE(0); + par->last_mode = -1; + } else { + mode = AUOK190X_UPDATE_MODE(par->update_mode); + par->last_mode = par->update_mode; + } + + if (par->flash) + mode |= AUOK190X_UPDATE_NONFLASH; + + auok1901_update_region(par, mode, 0, par->info->var.yres); + par->update_cnt = 0; +} + +static bool auok1901fb_need_refresh(struct auok190xfb_par *par) +{ + return (par->update_cnt > 10); +} + +static int __devinit auok1901fb_probe(struct platform_device *pdev) +{ + struct auok190x_init_data init; + struct auok190x_board *board; + + /* pick up board specific routines */ + board = pdev->dev.platform_data; + if (!board) + return -EINVAL; + + /* fill temporary init struct for common init */ + init.id = "auo_k1901fb"; + init.board = board; + init.update_partial = auok1901fb_dpy_update_pages; + init.update_all = auok1901fb_dpy_update; + init.need_refresh = auok1901fb_need_refresh; + init.init = auok1901_init; + + return auok190x_common_probe(pdev, &init); +} + +static int __devexit auok1901fb_remove(struct platform_device *pdev) +{ + return auok190x_common_remove(pdev); +} + +static struct platform_driver auok1901fb_driver = { + .probe = auok1901fb_probe, + .remove = __devexit_p(auok1901fb_remove), + .driver = { + .owner = THIS_MODULE, + .name = "auo_k1901fb", + .pm = &auok190x_pm, + }, +}; +module_platform_driver(auok1901fb_driver); + +MODULE_DESCRIPTION("framebuffer driver for the AUO-K1901 EPD controller"); +MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/auo_k190x.c b/drivers/video/auo_k190x.c new file mode 100644 index 0000000..77da6a2 --- /dev/null +++ b/drivers/video/auo_k190x.c @@ -0,0 +1,1046 @@ +/* + * Common code for AUO-K190X framebuffer drivers + * + * Copyright (C) 2012 Heiko Stuebner <heiko@sntech.de> + * + * 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/gpio.h> +#include <linux/pm_runtime.h> +#include <linux/fb.h> +#include <linux/delay.h> +#include <linux/uaccess.h> +#include <linux/vmalloc.h> +#include <linux/regulator/consumer.h> + +#include <video/auo_k190xfb.h> + +#include "auo_k190x.h" + +struct panel_info { + int w; + int h; +}; + +/* table of panel specific parameters to be indexed into by the board drivers */ +static struct panel_info panel_table[] = { + /* standard 6" */ + [AUOK190X_RESOLUTION_800_600] = { + .w = 800, + .h = 600, + }, + /* standard 9" */ + [AUOK190X_RESOLUTION_1024_768] = { + .w = 1024, + .h = 768, + }, +}; + +/* + * private I80 interface to the board driver + */ + +static void auok190x_issue_data(struct auok190xfb_par *par, u16 data) +{ + par->board->set_ctl(par, AUOK190X_I80_WR, 0); + par->board->set_hdb(par, data); + par->board->set_ctl(par, AUOK190X_I80_WR, 1); +} + +static void auok190x_issue_cmd(struct auok190xfb_par *par, u16 data) +{ + par->board->set_ctl(par, AUOK190X_I80_DC, 0); + auok190x_issue_data(par, data); + par->board->set_ctl(par, AUOK190X_I80_DC, 1); +} + +static int auok190x_issue_pixels(struct auok190xfb_par *par, int size, + u16 *data) +{ + struct device *dev = par->info->device; + int i; + u16 tmp; + + if (size & 3) { + dev_err(dev, "issue_pixels: size %d must be a multiple of 4\n", + size); + return -EINVAL; + } + + for (i = 0; i < (size >> 1); i++) { + par->board->set_ctl(par, AUOK190X_I80_WR, 0); + + /* simple reduction of 8bit staticgray to 4bit gray + * combines 4 * 4bit pixel values into a 16bit value + */ + tmp = (data[2*i] & 0xF0) >> 4; + tmp |= (data[2*i] & 0xF000) >> 8; + tmp |= (data[2*i+1] & 0xF0) << 4; + tmp |= (data[2*i+1] & 0xF000); + + par->board->set_hdb(par, tmp); + par->board->set_ctl(par, AUOK190X_I80_WR, 1); + } + + return 0; +} + +static u16 auok190x_read_data(struct auok190xfb_par *par) +{ + u16 data; + + par->board->set_ctl(par, AUOK190X_I80_OE, 0); + data = par->board->get_hdb(par); + par->board->set_ctl(par, AUOK190X_I80_OE, 1); + + return data; +} + +/* + * Command interface for the controller drivers + */ + +void auok190x_send_command_nowait(struct auok190xfb_par *par, u16 data) +{ + par->board->set_ctl(par, AUOK190X_I80_CS, 0); + auok190x_issue_cmd(par, data); + par->board->set_ctl(par, AUOK190X_I80_CS, 1); +} +EXPORT_SYMBOL_GPL(auok190x_send_command_nowait); + +void auok190x_send_cmdargs_nowait(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv) +{ + int i; + + par->board->set_ctl(par, AUOK190X_I80_CS, 0); + auok190x_issue_cmd(par, cmd); + + for (i = 0; i < argc; i++) + auok190x_issue_data(par, argv[i]); + par->board->set_ctl(par, AUOK190X_I80_CS, 1); +} +EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_nowait); + +int auok190x_send_command(struct auok190xfb_par *par, u16 data) +{ + int ret; + + ret = par->board->wait_for_rdy(par); + if (ret) + return ret; + + auok190x_send_command_nowait(par, data); + return 0; +} +EXPORT_SYMBOL_GPL(auok190x_send_command); + +int auok190x_send_cmdargs(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv) +{ + int ret; + + ret = par->board->wait_for_rdy(par); + if (ret) + return ret; + + auok190x_send_cmdargs_nowait(par, cmd, argc, argv); + return 0; +} +EXPORT_SYMBOL_GPL(auok190x_send_cmdargs); + +int auok190x_read_cmdargs(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv) +{ + int i, ret; + + ret = par->board->wait_for_rdy(par); + if (ret) + return ret; + + par->board->set_ctl(par, AUOK190X_I80_CS, 0); + auok190x_issue_cmd(par, cmd); + + for (i = 0; i < argc; i++) + argv[i] = auok190x_read_data(par); + par->board->set_ctl(par, AUOK190X_I80_CS, 1); + + return 0; +} +EXPORT_SYMBOL_GPL(auok190x_read_cmdargs); + +void auok190x_send_cmdargs_pixels_nowait(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv, int size, u16 *data) +{ + int i; + + par->board->set_ctl(par, AUOK190X_I80_CS, 0); + + auok190x_issue_cmd(par, cmd); + + for (i = 0; i < argc; i++) + auok190x_issue_data(par, argv[i]); + + auok190x_issue_pixels(par, size, data); + + par->board->set_ctl(par, AUOK190X_I80_CS, 1); +} +EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_pixels_nowait); + +int auok190x_send_cmdargs_pixels(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv, int size, u16 *data) +{ + int ret; + + ret = par->board->wait_for_rdy(par); + if (ret) + return ret; + + auok190x_send_cmdargs_pixels_nowait(par, cmd, argc, argv, size, data); + + return 0; +} +EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_pixels); + +/* + * fbdefio callbacks - common on both controllers. + */ + +static void auok190xfb_dpy_first_io(struct fb_info *info) +{ + /* tell runtime-pm that we wish to use the device in a short time */ + pm_runtime_get(info->device); +} + +/* this is called back from the deferred io workqueue */ +static void auok190xfb_dpy_deferred_io(struct fb_info *info, + struct list_head *pagelist) +{ + struct fb_deferred_io *fbdefio = info->fbdefio; + struct auok190xfb_par *par = info->par; + u16 yres = info->var.yres; + u16 xres = info->var.xres; + u16 y1 = 0, h = 0; + int prev_index = -1; + struct page *cur; + int h_inc; + int threshold; + + if (!list_empty(pagelist)) + /* the device resume should've been requested through first_io, + * if the resume did not finish until now, wait for it. + */ + pm_runtime_barrier(info->device); + else + /* We reached this via the fsync or some other way. + * In either case the first_io function did not run, + * so we runtime_resume the device here synchronously. + */ + pm_runtime_get_sync(info->device); + + /* Do a full screen update every n updates to prevent + * excessive darkening of the Sipix display. + * If we do this, there is no need to walk the pages. + */ + if (par->need_refresh(par)) { + par->update_all(par); + goto out; + } + + /* height increment is fixed per page */ + h_inc = DIV_ROUND_UP(PAGE_SIZE , xres); + + /* calculate number of pages from pixel height */ + threshold = par->consecutive_threshold / h_inc; + if (threshold < 1) + threshold = 1; + + /* walk the written page list and swizzle the data */ + list_for_each_entry(cur, &fbdefio->pagelist, lru) { + if (prev_index < 0) { + /* just starting so assign first page */ + y1 = (cur->index << PAGE_SHIFT) / xres; + h = h_inc; + } else if ((cur->index - prev_index) <= threshold) { + /* page is within our threshold for single updates */ + h += h_inc * (cur->index - prev_index); + } else { + /* page not consecutive, issue previous update first */ + par->update_partial(par, y1, y1 + h); + + /* start over with our non consecutive page */ + y1 = (cur->index << PAGE_SHIFT) / xres; + h = h_inc; + } + prev_index = cur->index; + } + + /* if we still have any pages to update we do so now */ + if (h >= yres) + /* its a full screen update, just do it */ + par->update_all(par); + else + par->update_partial(par, y1, min((u16) (y1 + h), yres)); + +out: + pm_runtime_mark_last_busy(info->device); + pm_runtime_put_autosuspend(info->device); +} + +/* + * framebuffer operations + */ + +/* + * this is the slow path from userspace. they can seek and write to + * the fb. it's inefficient to do anything less than a full screen draw + */ +static ssize_t auok190xfb_write(struct fb_info *info, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct auok190xfb_par *par = info->par; + unsigned long p = *ppos; + void *dst; + int err = 0; + unsigned long total_size; + + if (info->state != FBINFO_STATE_RUNNING) + return -EPERM; + + total_size = info->fix.smem_len; + + if (p > total_size) + return -EFBIG; + + if (count > total_size) { + err = -EFBIG; + count = total_size; + } + + if (count + p > total_size) { + if (!err) + err = -ENOSPC; + + count = total_size - p; + } + + dst = (void *)(info->screen_base + p); + + if (copy_from_user(dst, buf, count)) + err = -EFAULT; + + if (!err) + *ppos += count; + + par->update_all(par); + + return (err) ? err : count; +} + +static void auok190xfb_fillrect(struct fb_info *info, + const struct fb_fillrect *rect) +{ + struct auok190xfb_par *par = info->par; + + sys_fillrect(info, rect); + + par->update_all(par); +} + +static void auok190xfb_copyarea(struct fb_info *info, + const struct fb_copyarea *area) +{ + struct auok190xfb_par *par = info->par; + + sys_copyarea(info, area); + + par->update_all(par); +} + +static void auok190xfb_imageblit(struct fb_info *info, + const struct fb_image *image) +{ + struct auok190xfb_par *par = info->par; + + sys_imageblit(info, image); + + par->update_all(par); +} + +static int auok190xfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + if (info->var.xres != var->xres || info->var.yres != var->yres || + info->var.xres_virtual != var->xres_virtual || + info->var.yres_virtual != var->yres_virtual) { + pr_info("%s: Resolution not supported: X%u x Y%u\n", + __func__, var->xres, var->yres); + return -EINVAL; + } + + /* + * Memory limit + */ + + if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) { + pr_info("%s: Memory Limit requested yres_virtual = %u\n", + __func__, var->yres_virtual); + return -ENOMEM; + } + + return 0; +} + +static struct fb_ops auok190xfb_ops = { + .owner = THIS_MODULE, + .fb_read = fb_sys_read, + .fb_write = auok190xfb_write, + .fb_fillrect = auok190xfb_fillrect, + .fb_copyarea = auok190xfb_copyarea, + .fb_imageblit = auok190xfb_imageblit, + .fb_check_var = auok190xfb_check_var, +}; + +/* + * Controller-functions common to both K1900 and K1901 + */ + +static int auok190x_read_temperature(struct auok190xfb_par *par) +{ + struct device *dev = par->info->device; + u16 data[4]; + int temp; + + pm_runtime_get_sync(dev); + + mutex_lock(&(par->io_lock)); + + auok190x_read_cmdargs(par, AUOK190X_CMD_READ_VERSION, 4, data); + + mutex_unlock(&(par->io_lock)); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + /* sanitize and split of half-degrees for now */ + temp = ((data[0] & AUOK190X_VERSION_TEMP_MASK) >> 1); + + /* handle positive and negative temperatures */ + if (temp >= 201) + return (255 - temp + 1) * (-1); + else + return temp; +} + +static void auok190x_identify(struct auok190xfb_par *par) +{ + struct device *dev = par->info->device; + u16 data[4]; + + pm_runtime_get_sync(dev); + + mutex_lock(&(par->io_lock)); + + auok190x_read_cmdargs(par, AUOK190X_CMD_READ_VERSION, 4, data); + + mutex_unlock(&(par->io_lock)); + + par->epd_type = data[1] & AUOK190X_VERSION_TEMP_MASK; + + par->panel_size_int = AUOK190X_VERSION_SIZE_INT(data[2]); + par->panel_size_float = AUOK190X_VERSION_SIZE_FLOAT(data[2]); + par->panel_model = AUOK190X_VERSION_MODEL(data[2]); + + par->tcon_version = AUOK190X_VERSION_TCON(data[3]); + par->lut_version = AUOK190X_VERSION_LUT(data[3]); + + dev_dbg(dev, "panel %d.%din, model 0x%x, EPD 0x%x TCON-rev 0x%x, LUT-rev 0x%x", + par->panel_size_int, par->panel_size_float, par->panel_model, + par->epd_type, par->tcon_version, par->lut_version); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} + +/* + * Sysfs functions + */ + +static ssize_t update_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct auok190xfb_par *par = info->par; + + return sprintf(buf, "%d\n", par->update_mode); +} + +static ssize_t update_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct auok190xfb_par *par = info->par; + int mode, ret; + + ret = kstrtoint(buf, 10, &mode); + if (ret) + return ret; + + par->update_mode = mode; + + /* if we enter a better mode, do a full update */ + if (par->last_mode > 1 && mode < par->last_mode) + par->update_all(par); + + return count; +} + +static ssize_t flash_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct auok190xfb_par *par = info->par; + + return sprintf(buf, "%d\n", par->flash); +} + +static ssize_t flash_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct auok190xfb_par *par = info->par; + int flash, ret; + + ret = kstrtoint(buf, 10, &flash); + if (ret) + return ret; + + if (flash > 0) + par->flash = 1; + else + par->flash = 0; + + return count; +} + +static ssize_t temp_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct auok190xfb_par *par = info->par; + int temp; + + temp = auok190x_read_temperature(par); + return sprintf(buf, "%d\n", temp); +} + +static DEVICE_ATTR(update_mode, 0644, update_mode_show, update_mode_store); +static DEVICE_ATTR(flash, 0644, flash_show, flash_store); +static DEVICE_ATTR(temp, 0644, temp_show, NULL); + +static struct attribute *auok190x_attributes[] = { + &dev_attr_update_mode.attr, + &dev_attr_flash.attr, + &dev_attr_temp.attr, + NULL +}; + +static const struct attribute_group auok190x_attr_group = { + .attrs = auok190x_attributes, +}; + +static int auok190x_power(struct auok190xfb_par *par, bool on) +{ + struct auok190x_board *board = par->board; + int ret; + + if (on) { + /* We should maintain POWER up for at least 80ms before set + * RST_N and SLP_N to high (TCON spec 20100803_v35 p59) + */ + ret = regulator_enable(par->regulator); + if (ret) + return ret; + + msleep(200); + gpio_set_value(board->gpio_nrst, 1); + gpio_set_value(board->gpio_nsleep, 1); + msleep(200); + } else { + regulator_disable(par->regulator); + gpio_set_value(board->gpio_nrst, 0); + gpio_set_value(board->gpio_nsleep, 0); + } + + return 0; +} + +/* + * Recovery - powercycle the controller + */ + +static void auok190x_recover(struct auok190xfb_par *par) +{ + auok190x_power(par, 0); + msleep(100); + auok190x_power(par, 1); + + par->init(par); + + /* wait for init to complete */ + par->board->wait_for_rdy(par); +} + +/* + * Power-management + */ + +#ifdef CONFIG_PM +static int auok190x_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fb_info *info = platform_get_drvdata(pdev); + struct auok190xfb_par *par = info->par; + struct auok190x_board *board = par->board; + u16 standby_param; + + /* take and keep the lock until we are resumed, as the controller + * will never reach the non-busy state when in standby mode + */ + mutex_lock(&(par->io_lock)); + + if (par->standby) { + dev_warn(dev, "already in standby, runtime-pm pairing mismatch\n"); + mutex_unlock(&(par->io_lock)); + return 0; + } + + /* according to runtime_pm.txt runtime_suspend only means, that the + * device will not process data and will not communicate with the CPU + * As we hold the lock, this stays true even without standby + */ + if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) { + dev_dbg(dev, "runtime suspend without standby\n"); + goto finish; + } else if (board->quirks & AUOK190X_QUIRK_STANDBYPARAM) { + /* for some TCON versions STANDBY expects a parameter (0) but + * it seems the real tcon version has to be determined yet. + */ + dev_dbg(dev, "runtime suspend with additional empty param\n"); + standby_param = 0; + auok190x_send_cmdargs(par, AUOK190X_CMD_STANDBY, 1, + &standby_param); + } else { + dev_dbg(dev, "runtime suspend without param\n"); + auok190x_send_command(par, AUOK190X_CMD_STANDBY); + } + + msleep(64); + +finish: + par->standby = 1; + + return 0; +} + +static int auok190x_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fb_info *info = platform_get_drvdata(pdev); + struct auok190xfb_par *par = info->par; + struct auok190x_board *board = par->board; + + if (!par->standby) { + dev_warn(dev, "not in standby, runtime-pm pairing mismatch\n"); + return 0; + } + + if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) { + dev_dbg(dev, "runtime resume without standby\n"); + } else { + /* when in standby, controller is always busy + * and only accepts the wakeup command + */ + dev_dbg(dev, "runtime resume from standby\n"); + auok190x_send_command_nowait(par, AUOK190X_CMD_WAKEUP); + + msleep(160); + + /* wait for the controller to be ready and release the lock */ + board->wait_for_rdy(par); + } + + par->standby = 0; + + mutex_unlock(&(par->io_lock)); + + return 0; +} + +static int auok190x_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fb_info *info = platform_get_drvdata(pdev); + struct auok190xfb_par *par = info->par; + struct auok190x_board *board = par->board; + int ret; + + dev_dbg(dev, "suspend\n"); + if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) { + /* suspend via powering off the ic */ + dev_dbg(dev, "suspend with broken standby\n"); + + auok190x_power(par, 0); + } else { + dev_dbg(dev, "suspend using sleep\n"); + + /* the sleep state can only be entered from the standby state. + * pm_runtime_get_noresume gets called before the suspend call. + * So the devices usage count is >0 but it is not necessarily + * active. + */ + if (!pm_runtime_status_suspended(dev)) { + ret = auok190x_runtime_suspend(dev); + if (ret < 0) { + dev_err(dev, "auok190x_runtime_suspend failed with %d\n", + ret); + return ret; + } + par->manual_standby = 1; + } + + gpio_direction_output(board->gpio_nsleep, 0); + } + + msleep(100); + + return 0; +} + +static int auok190x_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fb_info *info = platform_get_drvdata(pdev); + struct auok190xfb_par *par = info->par; + struct auok190x_board *board = par->board; + + dev_dbg(dev, "resume\n"); + if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) { + dev_dbg(dev, "resume with broken standby\n"); + + auok190x_power(par, 1); + + par->init(par); + } else { + dev_dbg(dev, "resume from sleep\n"); + + /* device should be in runtime suspend when we were suspended + * and pm_runtime_put_sync gets called after this function. + * So there is no need to touch the standby mode here at all. + */ + gpio_direction_output(board->gpio_nsleep, 1); + msleep(100); + + /* an additional init call seems to be necessary after sleep */ + auok190x_runtime_resume(dev); + par->init(par); + + /* if we were runtime-suspended before, suspend again*/ + if (!par->manual_standby) + auok190x_runtime_suspend(dev); + else + par->manual_standby = 0; + } + + return 0; +} +#endif + +const struct dev_pm_ops auok190x_pm = { + SET_RUNTIME_PM_OPS(auok190x_runtime_suspend, auok190x_runtime_resume, + NULL) + SET_SYSTEM_SLEEP_PM_OPS(auok190x_suspend, auok190x_resume) +}; +EXPORT_SYMBOL_GPL(auok190x_pm); + +/* + * Common probe and remove code + */ + +int __devinit auok190x_common_probe(struct platform_device *pdev, + struct auok190x_init_data *init) +{ + struct auok190x_board *board = init->board; + struct auok190xfb_par *par; + struct fb_info *info; + struct panel_info *panel; + int videomemorysize, ret; + unsigned char *videomemory; + + /* check board contents */ + if (!board->init || !board->cleanup || !board->wait_for_rdy + || !board->set_ctl || !board->set_hdb || !board->get_hdb + || !board->setup_irq) + return -EINVAL; + + info = framebuffer_alloc(sizeof(struct auok190xfb_par), &pdev->dev); + if (!info) + return -ENOMEM; + + par = info->par; + par->info = info; + par->board = board; + par->recover = auok190x_recover; + par->update_partial = init->update_partial; + par->update_all = init->update_all; + par->need_refresh = init->need_refresh; + par->init = init->init; + + /* init update modes */ + par->update_cnt = 0; + par->update_mode = -1; + par->last_mode = -1; + par->flash = 0; + + par->regulator = regulator_get(info->device, "vdd"); + if (IS_ERR(par->regulator)) { + ret = PTR_ERR(par->regulator); + dev_err(info->device, "Failed to get regulator: %d\n", ret); + goto err_reg; + } + + ret = board->init(par); + if (ret) { + dev_err(info->device, "board init failed, %d\n", ret); + goto err_board; + } + + ret = gpio_request(board->gpio_nsleep, "AUOK190x sleep"); + if (ret) { + dev_err(info->device, "could not request sleep gpio, %d\n", + ret); + goto err_gpio1; + } + + ret = gpio_direction_output(board->gpio_nsleep, 0); + if (ret) { + dev_err(info->device, "could not set sleep gpio, %d\n", ret); + goto err_gpio2; + } + + ret = gpio_request(board->gpio_nrst, "AUOK190x reset"); + if (ret) { + dev_err(info->device, "could not request reset gpio, %d\n", + ret); + goto err_gpio2; + } + + ret = gpio_direction_output(board->gpio_nrst, 0); + if (ret) { + dev_err(info->device, "could not set reset gpio, %d\n", ret); + goto err_gpio3; + } + + ret = auok190x_power(par, 1); + if (ret) { + dev_err(info->device, "could not power on the device, %d\n", + ret); + goto err_gpio3; + } + + mutex_init(&par->io_lock); + + init_waitqueue_head(&par->waitq); + + ret = par->board->setup_irq(par->info); + if (ret) { + dev_err(info->device, "could not setup ready-irq, %d\n", ret); + goto err_irq; + } + + /* wait for init to complete */ + par->board->wait_for_rdy(par); + + /* + * From here on the controller can talk to us + */ + + /* initialise fix, var, resolution and rotation */ + + strlcpy(info->fix.id, init->id, 16); + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; + info->fix.xpanstep = 0; + info->fix.ypanstep = 0; + info->fix.ywrapstep = 0; + info->fix.accel = FB_ACCEL_NONE; + + info->var.bits_per_pixel = 8; + info->var.grayscale = 1; + info->var.red.length = 8; + info->var.green.length = 8; + info->var.blue.length = 8; + + panel = &panel_table[board->resolution]; + + /* if 90 degree rotation, switch width and height */ + if (board->rotation & 1) { + info->var.xres = panel->h; + info->var.yres = panel->w; + info->var.xres_virtual = panel->h; + info->var.yres_virtual = panel->w; + info->fix.line_length = panel->h; + } else { + info->var.xres = panel->w; + info->var.yres = panel->h; + info->var.xres_virtual = panel->w; + info->var.yres_virtual = panel->h; + info->fix.line_length = panel->w; + } + + par->resolution = board->resolution; + par->rotation = board->rotation; + + /* videomemory handling */ + + videomemorysize = roundup((panel->w * panel->h), PAGE_SIZE); + videomemory = vmalloc(videomemorysize); + if (!videomemory) { + ret = -ENOMEM; + goto err_irq; + } + + memset(videomemory, 0, videomemorysize); + info->screen_base = (char *)videomemory; + info->fix.smem_len = videomemorysize; + + info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB; + info->fbops = &auok190xfb_ops; + + /* deferred io init */ + + info->fbdefio = devm_kzalloc(info->device, + sizeof(struct fb_deferred_io), + GFP_KERNEL); + if (!info->fbdefio) { + dev_err(info->device, "Failed to allocate memory\n"); + ret = -ENOMEM; + goto err_defio; + } + + dev_dbg(info->device, "targetting %d frames per second\n", board->fps); + info->fbdefio->delay = HZ / board->fps; + info->fbdefio->first_io = auok190xfb_dpy_first_io, + info->fbdefio->deferred_io = auok190xfb_dpy_deferred_io, + fb_deferred_io_init(info); + + /* color map */ + + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret < 0) { + dev_err(info->device, "Failed to allocate colormap\n"); + goto err_cmap; + } + + /* controller init */ + + par->consecutive_threshold = 100; + par->init(par); + auok190x_identify(par); + + platform_set_drvdata(pdev, info); + + ret = register_framebuffer(info); + if (ret < 0) + goto err_regfb; + + ret = sysfs_create_group(&info->device->kobj, &auok190x_attr_group); + if (ret) + goto err_sysfs; + + dev_info(info->device, "fb%d: %dx%d using %dK of video memory\n", + info->node, info->var.xres, info->var.yres, + videomemorysize >> 10); + + /* increase autosuspend_delay when we use alternative methods + * for runtime_pm + */ + par->autosuspend_delay = (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) + ? 1000 : 200; + + pm_runtime_set_active(info->device); + pm_runtime_enable(info->device); + pm_runtime_set_autosuspend_delay(info->device, par->autosuspend_delay); + pm_runtime_use_autosuspend(info->device); + + return 0; + +err_sysfs: + unregister_framebuffer(info); +err_regfb: + fb_dealloc_cmap(&info->cmap); +err_cmap: + fb_deferred_io_cleanup(info); + kfree(info->fbdefio); +err_defio: + vfree((void *)info->screen_base); +err_irq: + auok190x_power(par, 0); +err_gpio3: + gpio_free(board->gpio_nrst); +err_gpio2: + gpio_free(board->gpio_nsleep); +err_gpio1: + board->cleanup(par); +err_board: + regulator_put(par->regulator); +err_reg: + framebuffer_release(info); + + return ret; +} +EXPORT_SYMBOL_GPL(auok190x_common_probe); + +int __devexit auok190x_common_remove(struct platform_device *pdev) +{ + struct fb_info *info = platform_get_drvdata(pdev); + struct auok190xfb_par *par = info->par; + struct auok190x_board *board = par->board; + + pm_runtime_disable(info->device); + + sysfs_remove_group(&info->device->kobj, &auok190x_attr_group); + + unregister_framebuffer(info); + + fb_dealloc_cmap(&info->cmap); + + fb_deferred_io_cleanup(info); + kfree(info->fbdefio); + + vfree((void *)info->screen_base); + + auok190x_power(par, 0); + + gpio_free(board->gpio_nrst); + gpio_free(board->gpio_nsleep); + + board->cleanup(par); + + regulator_put(par->regulator); + + framebuffer_release(info); + + return 0; +} +EXPORT_SYMBOL_GPL(auok190x_common_remove); + +MODULE_DESCRIPTION("Common code for AUO-K190X controllers"); +MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/auo_k190x.h b/drivers/video/auo_k190x.h new file mode 100644 index 0000000..e35af1f --- /dev/null +++ b/drivers/video/auo_k190x.h @@ -0,0 +1,129 @@ +/* + * Private common definitions for AUO-K190X framebuffer drivers + * + * Copyright (C) 2012 Heiko Stuebner <heiko@sntech.de> + * + * 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. + */ + +/* + * I80 interface specific defines + */ + +#define AUOK190X_I80_CS 0x01 +#define AUOK190X_I80_DC 0x02 +#define AUOK190X_I80_WR 0x03 +#define AUOK190X_I80_OE 0x04 + +/* + * AUOK190x commands, common to both controllers + */ + +#define AUOK190X_CMD_INIT 0x0000 +#define AUOK190X_CMD_STANDBY 0x0001 +#define AUOK190X_CMD_WAKEUP 0x0002 +#define AUOK190X_CMD_TCON_RESET 0x0003 +#define AUOK190X_CMD_DATA_STOP 0x1002 +#define AUOK190X_CMD_LUT_START 0x1003 +#define AUOK190X_CMD_DISP_REFRESH 0x1004 +#define AUOK190X_CMD_DISP_RESET 0x1005 +#define AUOK190X_CMD_PRE_DISPLAY_START 0x100D +#define AUOK190X_CMD_PRE_DISPLAY_STOP 0x100F +#define AUOK190X_CMD_FLASH_W 0x2000 +#define AUOK190X_CMD_FLASH_E 0x2001 +#define AUOK190X_CMD_FLASH_STS 0x2002 +#define AUOK190X_CMD_FRAMERATE 0x3000 +#define AUOK190X_CMD_READ_VERSION 0x4000 +#define AUOK190X_CMD_READ_STATUS 0x4001 +#define AUOK190X_CMD_READ_LUT 0x4003 +#define AUOK190X_CMD_DRIVERTIMING 0x5000 +#define AUOK190X_CMD_LBALANCE 0x5001 +#define AUOK190X_CMD_AGINGMODE 0x6000 +#define AUOK190X_CMD_AGINGEXIT 0x6001 + +/* + * Common settings for AUOK190X_CMD_INIT + */ + +#define AUOK190X_INIT_DATA_FILTER (0 << 12) +#define AUOK190X_INIT_DATA_BYPASS (1 << 12) +#define AUOK190X_INIT_INVERSE_WHITE (0 << 9) +#define AUOK190X_INIT_INVERSE_BLACK (1 << 9) +#define AUOK190X_INIT_SCAN_DOWN (0 << 1) +#define AUOK190X_INIT_SCAN_UP (1 << 1) +#define AUOK190X_INIT_SHIFT_LEFT (0 << 0) +#define AUOK190X_INIT_SHIFT_RIGHT (1 << 0) + +/* Common bits to pixels + * Mode 15-12 11-8 7-4 3-0 + * format0 4 3 2 1 + * format1 3 4 1 2 + */ + +#define AUOK190X_INIT_FORMAT0 0 +#define AUOK190X_INIT_FORMAT1 (1 << 6) + +/* + * settings for AUOK190X_CMD_RESET + */ + +#define AUOK190X_RESET_TCON (0 << 0) +#define AUOK190X_RESET_NORMAL (1 << 0) +#define AUOK190X_RESET_PON (1 << 1) + +/* + * AUOK190X_CMD_VERSION + */ + +#define AUOK190X_VERSION_TEMP_MASK (0x1ff) +#define AUOK190X_VERSION_EPD_MASK (0xff) +#define AUOK190X_VERSION_SIZE_INT(_val) ((_val & 0xfc00) >> 10) +#define AUOK190X_VERSION_SIZE_FLOAT(_val) ((_val & 0x3c0) >> 6) +#define AUOK190X_VERSION_MODEL(_val) (_val & 0x3f) +#define AUOK190X_VERSION_LUT(_val) (_val & 0xff) +#define AUOK190X_VERSION_TCON(_val) ((_val & 0xff00) >> 8) + +/* + * update modes for CMD_PARTIALDISP on K1900 and CMD_DDMA on K1901 + */ + +#define AUOK190X_UPDATE_MODE(_res) ((_res & 0x7) << 12) +#define AUOK190X_UPDATE_NONFLASH (1 << 15) + +/* + * track panel specific parameters for common init + */ + +struct auok190x_init_data { + char *id; + struct auok190x_board *board; + + void (*update_partial)(struct auok190xfb_par *par, u16 y1, u16 y2); + void (*update_all)(struct auok190xfb_par *par); + bool (*need_refresh)(struct auok190xfb_par *par); + void (*init)(struct auok190xfb_par *par); +}; + + +extern void auok190x_send_command_nowait(struct auok190xfb_par *par, u16 data); +extern int auok190x_send_command(struct auok190xfb_par *par, u16 data); +extern void auok190x_send_cmdargs_nowait(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv); +extern int auok190x_send_cmdargs(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv); +extern void auok190x_send_cmdargs_pixels_nowait(struct auok190xfb_par *par, + u16 cmd, int argc, u16 *argv, + int size, u16 *data); +extern int auok190x_send_cmdargs_pixels(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv, int size, + u16 *data); +extern int auok190x_read_cmdargs(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv); + +extern int auok190x_common_probe(struct platform_device *pdev, + struct auok190x_init_data *init); +extern int auok190x_common_remove(struct platform_device *pdev); + +extern const struct dev_pm_ops auok190x_pm; diff --git a/drivers/video/bfin_adv7393fb.c b/drivers/video/bfin_adv7393fb.c index 1a268a2..33ea874 100644 --- a/drivers/video/bfin_adv7393fb.c +++ b/drivers/video/bfin_adv7393fb.c @@ -414,14 +414,14 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, if (ret) { dev_err(&client->dev, "PPI0_FS3 GPIO request failed\n"); ret = -EBUSY; - goto out_8; + goto free_fbdev; } } if (peripheral_request_list(ppi_pins, DRIVER_NAME)) { dev_err(&client->dev, "requesting PPI peripheral failed\n"); ret = -EFAULT; - goto out_8; + goto free_gpio; } fbdev->fb_mem = @@ -432,7 +432,7 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, dev_err(&client->dev, "couldn't allocate dma buffer (%d bytes)\n", (u32) fbdev->fb_len); ret = -ENOMEM; - goto out_7; + goto free_ppi_pins; } fbdev->info.screen_base = (void *)fbdev->fb_mem; @@ -464,27 +464,27 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, if (!fbdev->info.pseudo_palette) { dev_err(&client->dev, "failed to allocate pseudo_palette\n"); ret = -ENOMEM; - goto out_6; + goto free_fb_mem; } if (fb_alloc_cmap(&fbdev->info.cmap, BFIN_LCD_NBR_PALETTE_ENTRIES, 0) < 0) { dev_err(&client->dev, "failed to allocate colormap (%d entries)\n", BFIN_LCD_NBR_PALETTE_ENTRIES); ret = -EFAULT; - goto out_5; + goto free_palette; } if (request_dma(CH_PPI, "BF5xx_PPI_DMA") < 0) { dev_err(&client->dev, "unable to request PPI DMA\n"); ret = -EFAULT; - goto out_4; + goto free_cmap; } if (request_irq(IRQ_PPI_ERROR, ppi_irq_error, 0, "PPI ERROR", fbdev) < 0) { dev_err(&client->dev, "unable to request PPI ERROR IRQ\n"); ret = -EFAULT; - goto out_3; + goto free_ch_ppi; } fbdev->open = 0; @@ -494,14 +494,14 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, if (ret) { dev_err(&client->dev, "i2c attach: init error\n"); - goto out_1; + goto free_irq_ppi; } if (register_framebuffer(&fbdev->info) < 0) { dev_err(&client->dev, "unable to register framebuffer\n"); ret = -EFAULT; - goto out_1; + goto free_irq_ppi; } dev_info(&client->dev, "fb%d: %s frame buffer device\n", @@ -512,7 +512,7 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, if (!entry) { dev_err(&client->dev, "unable to create /proc entry\n"); ret = -EFAULT; - goto out_0; + goto free_fb; } entry->read_proc = adv7393_read_proc; @@ -521,22 +521,25 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, return 0; - out_0: +free_fb: unregister_framebuffer(&fbdev->info); - out_1: +free_irq_ppi: free_irq(IRQ_PPI_ERROR, fbdev); - out_3: +free_ch_ppi: free_dma(CH_PPI); - out_4: - dma_free_coherent(NULL, fbdev->fb_len, fbdev->fb_mem, - fbdev->dma_handle); - out_5: +free_cmap: fb_dealloc_cmap(&fbdev->info.cmap); - out_6: +free_palette: kfree(fbdev->info.pseudo_palette); - out_7: +free_fb_mem: + dma_free_coherent(NULL, fbdev->fb_len, fbdev->fb_mem, + fbdev->dma_handle); +free_ppi_pins: peripheral_free_list(ppi_pins); - out_8: +free_gpio: + if (ANOMALY_05000400) + gpio_free(P_IDENT(P_PPI0_FS3)); +free_fbdev: kfree(fbdev); return ret; diff --git a/drivers/video/cobalt_lcdfb.c b/drivers/video/cobalt_lcdfb.c index f56699d..eae46f6 100644 --- a/drivers/video/cobalt_lcdfb.c +++ b/drivers/video/cobalt_lcdfb.c @@ -1,7 +1,8 @@ /* - * Cobalt server LCD frame buffer driver. + * Cobalt/SEAD3 LCD frame buffer driver. * * Copyright (C) 2008 Yoichi Yuasa <yuasa@linux-mips.org> + * Copyright (C) 2012 MIPS Technologies, 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 @@ -62,6 +63,7 @@ #define LCD_CUR_POS(x) ((x) & LCD_CUR_POS_MASK) #define LCD_TEXT_POS(x) ((x) | LCD_TEXT_MODE) +#ifdef CONFIG_MIPS_COBALT static inline void lcd_write_control(struct fb_info *info, u8 control) { writel((u32)control << 24, info->screen_base); @@ -81,6 +83,47 @@ static inline u8 lcd_read_data(struct fb_info *info) { return readl(info->screen_base + LCD_DATA_REG_OFFSET) >> 24; } +#else + +#define LCD_CTL 0x00 +#define LCD_DATA 0x08 +#define CPLD_STATUS 0x10 +#define CPLD_DATA 0x18 + +static inline void cpld_wait(struct fb_info *info) +{ + do { + } while (readl(info->screen_base + CPLD_STATUS) & 1); +} + +static inline void lcd_write_control(struct fb_info *info, u8 control) +{ + cpld_wait(info); + writel(control, info->screen_base + LCD_CTL); +} + +static inline u8 lcd_read_control(struct fb_info *info) +{ + cpld_wait(info); + readl(info->screen_base + LCD_CTL); + cpld_wait(info); + return readl(info->screen_base + CPLD_DATA) & 0xff; +} + +static inline void lcd_write_data(struct fb_info *info, u8 data) +{ + cpld_wait(info); + writel(data, info->screen_base + LCD_DATA); +} + +static inline u8 lcd_read_data(struct fb_info *info) +{ + cpld_wait(info); + readl(info->screen_base + LCD_DATA); + cpld_wait(info); + return readl(info->screen_base + CPLD_DATA) & 0xff; +} +#endif static int lcd_busy_wait(struct fb_info *info) { diff --git a/drivers/video/ep93xx-fb.c b/drivers/video/ep93xx-fb.c index f8babbe..345d962 100644 --- a/drivers/video/ep93xx-fb.c +++ b/drivers/video/ep93xx-fb.c @@ -507,16 +507,16 @@ static int __devinit ep93xxfb_probe(struct platform_device *pdev) err = fb_alloc_cmap(&info->cmap, 256, 0); if (err) - goto failed; + goto failed_cmap; err = ep93xxfb_alloc_videomem(info); if (err) - goto failed; + goto failed_videomem; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { err = -ENXIO; - goto failed; + goto failed_resource; } /* @@ -532,7 +532,7 @@ static int __devinit ep93xxfb_probe(struct platform_device *pdev) fbi->mmio_base = ioremap(res->start, resource_size(res)); if (!fbi->mmio_base) { err = -ENXIO; - goto failed; + goto failed_resource; } strcpy(info->fix.id, pdev->name); @@ -553,24 +553,24 @@ static int __devinit ep93xxfb_probe(struct platform_device *pdev) if (err == 0) { dev_err(info->dev, "No suitable video mode found\n"); err = -EINVAL; - goto failed; + goto failed_mode; } if (mach_info->setup) { err = mach_info->setup(pdev); if (err) - return err; + goto failed_mode; } err = ep93xxfb_check_var(&info->var, info); if (err) - goto failed; + goto failed_check; fbi->clk = clk_get(info->dev, NULL); if (IS_ERR(fbi->clk)) { err = PTR_ERR(fbi->clk); fbi->clk = NULL; - goto failed; + goto failed_check; } ep93xxfb_set_par(info); @@ -585,15 +585,17 @@ static int __devinit ep93xxfb_probe(struct platform_device *pdev) return 0; failed: - if (fbi->clk) - clk_put(fbi->clk); - if (fbi->mmio_base) - iounmap(fbi->mmio_base); - ep93xxfb_dealloc_videomem(info); - if (&info->cmap) - fb_dealloc_cmap(&info->cmap); + clk_put(fbi->clk); +failed_check: if (fbi->mach_info->teardown) fbi->mach_info->teardown(pdev); +failed_mode: + iounmap(fbi->mmio_base); +failed_resource: + ep93xxfb_dealloc_videomem(info); +failed_videomem: + fb_dealloc_cmap(&info->cmap); +failed_cmap: kfree(info); platform_set_drvdata(pdev, NULL); diff --git a/drivers/video/exynos/exynos_dp_core.c b/drivers/video/exynos/exynos_dp_core.c index 2a4481c..a36b2d2 100644 --- a/drivers/video/exynos/exynos_dp_core.c +++ b/drivers/video/exynos/exynos_dp_core.c @@ -21,14 +21,14 @@ #include <video/exynos_dp.h> -#include <plat/cpu.h> - #include "exynos_dp_core.h" static int exynos_dp_init_dp(struct exynos_dp_device *dp) { exynos_dp_reset(dp); + exynos_dp_swreset(dp); + /* SW defined function Normal operation */ exynos_dp_enable_sw_function(dp); @@ -478,7 +478,7 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) int lane_count; u8 buf[5]; - u8 *adjust_request; + u8 adjust_request[2]; u8 voltage_swing; u8 pre_emphasis; u8 training_lane; @@ -493,8 +493,8 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) /* set training pattern 2 for EQ */ exynos_dp_set_training_pattern(dp, TRAINING_PTN2); - adjust_request = link_status + (DPCD_ADDR_ADJUST_REQUEST_LANE0_1 - - DPCD_ADDR_LANE0_1_STATUS); + adjust_request[0] = link_status[4]; + adjust_request[1] = link_status[5]; exynos_dp_get_adjust_train(dp, adjust_request); @@ -566,7 +566,7 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp) u8 buf[5]; u32 reg; - u8 *adjust_request; + u8 adjust_request[2]; udelay(400); @@ -575,8 +575,8 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp) lane_count = dp->link_train.lane_count; if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) { - adjust_request = link_status + (DPCD_ADDR_ADJUST_REQUEST_LANE0_1 - - DPCD_ADDR_LANE0_1_STATUS); + adjust_request[0] = link_status[4]; + adjust_request[1] = link_status[5]; if (exynos_dp_channel_eq_ok(link_status, lane_count) == 0) { /* traing pattern Set to Normal */ @@ -770,7 +770,7 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp, return -ETIMEDOUT; } - mdelay(100); + udelay(1); } /* Set to use the register calculated M/N video */ @@ -804,7 +804,7 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp, return -ETIMEDOUT; } - mdelay(100); + mdelay(1); } if (retval != 0) @@ -860,7 +860,8 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) return -EINVAL; } - dp = kzalloc(sizeof(struct exynos_dp_device), GFP_KERNEL); + dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device), + GFP_KERNEL); if (!dp) { dev_err(&pdev->dev, "no memory for device data\n"); return -ENOMEM; @@ -871,8 +872,7 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) dp->clock = clk_get(&pdev->dev, "dp"); if (IS_ERR(dp->clock)) { dev_err(&pdev->dev, "failed to get clock\n"); - ret = PTR_ERR(dp->clock); - goto err_dp; + return PTR_ERR(dp->clock); } clk_enable(dp->clock); @@ -884,35 +884,25 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) goto err_clock; } - res = request_mem_region(res->start, resource_size(res), - dev_name(&pdev->dev)); - if (!res) { - dev_err(&pdev->dev, "failed to request registers region\n"); - ret = -EINVAL; - goto err_clock; - } - - dp->res = res; - - dp->reg_base = ioremap(res->start, resource_size(res)); + dp->reg_base = devm_request_and_ioremap(&pdev->dev, res); if (!dp->reg_base) { dev_err(&pdev->dev, "failed to ioremap\n"); ret = -ENOMEM; - goto err_req_region; + goto err_clock; } dp->irq = platform_get_irq(pdev, 0); if (!dp->irq) { dev_err(&pdev->dev, "failed to get irq\n"); ret = -ENODEV; - goto err_ioremap; + goto err_clock; } - ret = request_irq(dp->irq, exynos_dp_irq_handler, 0, - "exynos-dp", dp); + ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler, 0, + "exynos-dp", dp); if (ret) { dev_err(&pdev->dev, "failed to request irq\n"); - goto err_ioremap; + goto err_clock; } dp->video_info = pdata->video_info; @@ -924,7 +914,7 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) ret = exynos_dp_detect_hpd(dp); if (ret) { dev_err(&pdev->dev, "unable to detect hpd\n"); - goto err_irq; + goto err_clock; } exynos_dp_handle_edid(dp); @@ -933,7 +923,7 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) dp->video_info->link_rate); if (ret) { dev_err(&pdev->dev, "unable to do link train\n"); - goto err_irq; + goto err_clock; } exynos_dp_enable_scramble(dp, 1); @@ -947,23 +937,15 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) ret = exynos_dp_config_video(dp, dp->video_info); if (ret) { dev_err(&pdev->dev, "unable to config video\n"); - goto err_irq; + goto err_clock; } platform_set_drvdata(pdev, dp); return 0; -err_irq: - free_irq(dp->irq, dp); -err_ioremap: - iounmap(dp->reg_base); -err_req_region: - release_mem_region(res->start, resource_size(res)); err_clock: clk_put(dp->clock); -err_dp: - kfree(dp); return ret; } @@ -976,16 +958,9 @@ static int __devexit exynos_dp_remove(struct platform_device *pdev) if (pdata && pdata->phy_exit) pdata->phy_exit(); - free_irq(dp->irq, dp); - iounmap(dp->reg_base); - clk_disable(dp->clock); clk_put(dp->clock); - release_mem_region(dp->res->start, resource_size(dp->res)); - - kfree(dp); - return 0; } diff --git a/drivers/video/exynos/exynos_dp_core.h b/drivers/video/exynos/exynos_dp_core.h index 90ceaca..1e0f998 100644 --- a/drivers/video/exynos/exynos_dp_core.h +++ b/drivers/video/exynos/exynos_dp_core.h @@ -26,7 +26,6 @@ struct link_train { struct exynos_dp_device { struct device *dev; - struct resource *res; struct clk *clock; unsigned int irq; void __iomem *reg_base; @@ -39,8 +38,10 @@ struct exynos_dp_device { void exynos_dp_enable_video_mute(struct exynos_dp_device *dp, bool enable); void exynos_dp_stop_video(struct exynos_dp_device *dp); void exynos_dp_lane_swap(struct exynos_dp_device *dp, bool enable); +void exynos_dp_init_analog_param(struct exynos_dp_device *dp); void exynos_dp_init_interrupt(struct exynos_dp_device *dp); void exynos_dp_reset(struct exynos_dp_device *dp); +void exynos_dp_swreset(struct exynos_dp_device *dp); void exynos_dp_config_interrupt(struct exynos_dp_device *dp); u32 exynos_dp_get_pll_lock_status(struct exynos_dp_device *dp); void exynos_dp_set_pll_power_down(struct exynos_dp_device *dp, bool enable); diff --git a/drivers/video/exynos/exynos_dp_reg.c b/drivers/video/exynos/exynos_dp_reg.c index 6548afa..6ce76d5 100644 --- a/drivers/video/exynos/exynos_dp_reg.c +++ b/drivers/video/exynos/exynos_dp_reg.c @@ -16,8 +16,6 @@ #include <video/exynos_dp.h> -#include <plat/cpu.h> - #include "exynos_dp_core.h" #include "exynos_dp_reg.h" @@ -65,6 +63,28 @@ void exynos_dp_lane_swap(struct exynos_dp_device *dp, bool enable) writel(reg, dp->reg_base + EXYNOS_DP_LANE_MAP); } +void exynos_dp_init_analog_param(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = TX_TERMINAL_CTRL_50_OHM; + writel(reg, dp->reg_base + EXYNOS_DP_ANALOG_CTL_1); + + reg = SEL_24M | TX_DVDD_BIT_1_0625V; + writel(reg, dp->reg_base + EXYNOS_DP_ANALOG_CTL_2); + + reg = DRIVE_DVDD_BIT_1_0625V | VCO_BIT_600_MICRO; + writel(reg, dp->reg_base + EXYNOS_DP_ANALOG_CTL_3); + + reg = PD_RING_OSC | AUX_TERMINAL_CTRL_50_OHM | + TX_CUR1_2X | TX_CUR_8_MA; + writel(reg, dp->reg_base + EXYNOS_DP_PLL_FILTER_CTL_1); + + reg = CH3_AMP_400_MV | CH2_AMP_400_MV | + CH1_AMP_400_MV | CH0_AMP_400_MV; + writel(reg, dp->reg_base + EXYNOS_DP_TX_AMP_TUNING_CTL); +} + void exynos_dp_init_interrupt(struct exynos_dp_device *dp) { /* Set interrupt pin assertion polarity as high */ @@ -89,8 +109,6 @@ void exynos_dp_reset(struct exynos_dp_device *dp) { u32 reg; - writel(RESET_DP_TX, dp->reg_base + EXYNOS_DP_TX_SW_RESET); - exynos_dp_stop_video(dp); exynos_dp_enable_video_mute(dp, 0); @@ -131,9 +149,15 @@ void exynos_dp_reset(struct exynos_dp_device *dp) writel(0x00000101, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL); + exynos_dp_init_analog_param(dp); exynos_dp_init_interrupt(dp); } +void exynos_dp_swreset(struct exynos_dp_device *dp) +{ + writel(RESET_DP_TX, dp->reg_base + EXYNOS_DP_TX_SW_RESET); +} + void exynos_dp_config_interrupt(struct exynos_dp_device *dp) { u32 reg; @@ -271,6 +295,7 @@ void exynos_dp_set_analog_power_down(struct exynos_dp_device *dp, void exynos_dp_init_analog_func(struct exynos_dp_device *dp) { u32 reg; + int timeout_loop = 0; exynos_dp_set_analog_power_down(dp, POWER_ALL, 0); @@ -282,9 +307,19 @@ void exynos_dp_init_analog_func(struct exynos_dp_device *dp) writel(reg, dp->reg_base + EXYNOS_DP_DEBUG_CTL); /* Power up PLL */ - if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) + if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { exynos_dp_set_pll_power_down(dp, 0); + while (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { + timeout_loop++; + if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) { + dev_err(dp->dev, "failed to get pll lock status\n"); + return; + } + usleep_range(10, 20); + } + } + /* Enable Serdes FIFO function and Link symbol clock domain module */ reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_2); reg &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N diff --git a/drivers/video/exynos/exynos_dp_reg.h b/drivers/video/exynos/exynos_dp_reg.h index 42f608e..125b27c 100644 --- a/drivers/video/exynos/exynos_dp_reg.h +++ b/drivers/video/exynos/exynos_dp_reg.h @@ -24,6 +24,12 @@ #define EXYNOS_DP_LANE_MAP 0x35C +#define EXYNOS_DP_ANALOG_CTL_1 0x370 +#define EXYNOS_DP_ANALOG_CTL_2 0x374 +#define EXYNOS_DP_ANALOG_CTL_3 0x378 +#define EXYNOS_DP_PLL_FILTER_CTL_1 0x37C +#define EXYNOS_DP_TX_AMP_TUNING_CTL 0x380 + #define EXYNOS_DP_AUX_HW_RETRY_CTL 0x390 #define EXYNOS_DP_COMMON_INT_STA_1 0x3C4 @@ -166,6 +172,29 @@ #define LANE0_MAP_LOGIC_LANE_2 (0x2 << 0) #define LANE0_MAP_LOGIC_LANE_3 (0x3 << 0) +/* EXYNOS_DP_ANALOG_CTL_1 */ +#define TX_TERMINAL_CTRL_50_OHM (0x1 << 4) + +/* EXYNOS_DP_ANALOG_CTL_2 */ +#define SEL_24M (0x1 << 3) +#define TX_DVDD_BIT_1_0625V (0x4 << 0) + +/* EXYNOS_DP_ANALOG_CTL_3 */ +#define DRIVE_DVDD_BIT_1_0625V (0x4 << 5) +#define VCO_BIT_600_MICRO (0x5 << 0) + +/* EXYNOS_DP_PLL_FILTER_CTL_1 */ +#define PD_RING_OSC (0x1 << 6) +#define AUX_TERMINAL_CTRL_50_OHM (0x2 << 4) +#define TX_CUR1_2X (0x1 << 2) +#define TX_CUR_8_MA (0x2 << 0) + +/* EXYNOS_DP_TX_AMP_TUNING_CTL */ +#define CH3_AMP_400_MV (0x0 << 24) +#define CH2_AMP_400_MV (0x0 << 16) +#define CH1_AMP_400_MV (0x0 << 8) +#define CH0_AMP_400_MV (0x0 << 0) + /* EXYNOS_DP_AUX_HW_RETRY_CTL */ #define AUX_BIT_PERIOD_EXPECTED_DELAY(x) (((x) & 0x7) << 8) #define AUX_HW_RETRY_INTERVAL_MASK (0x3 << 3) diff --git a/drivers/video/exynos/exynos_mipi_dsi.c b/drivers/video/exynos/exynos_mipi_dsi.c index 557091d..6c1f5c3 100644 --- a/drivers/video/exynos/exynos_mipi_dsi.c +++ b/drivers/video/exynos/exynos_mipi_dsi.c @@ -58,7 +58,7 @@ static struct mipi_dsim_platform_data *to_dsim_plat(struct platform_device } static struct regulator_bulk_data supplies[] = { - { .supply = "vdd10", }, + { .supply = "vdd11", }, { .supply = "vdd18", }, }; @@ -102,6 +102,8 @@ static void exynos_mipi_update_cfg(struct mipi_dsim_device *dsim) /* set display timing. */ exynos_mipi_dsi_set_display_mode(dsim, dsim->dsim_config); + exynos_mipi_dsi_init_interrupt(dsim); + /* * data from Display controller(FIMD) is transferred in video mode * but in case of command mode, all settigs is updated to registers. @@ -413,27 +415,30 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev) goto err_platform_get_irq; } + init_completion(&dsim_wr_comp); + init_completion(&dsim_rd_comp); + platform_set_drvdata(pdev, dsim); + ret = request_irq(dsim->irq, exynos_mipi_dsi_interrupt_handler, - IRQF_SHARED, pdev->name, dsim); + IRQF_SHARED, dev_name(&pdev->dev), dsim); if (ret != 0) { dev_err(&pdev->dev, "failed to request dsim irq\n"); ret = -EINVAL; goto err_bind; } - init_completion(&dsim_wr_comp); - init_completion(&dsim_rd_comp); - - /* enable interrupt */ + /* enable interrupts */ exynos_mipi_dsi_init_interrupt(dsim); /* initialize mipi-dsi client(lcd panel). */ if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->probe) dsim_ddi->dsim_lcd_drv->probe(dsim_ddi->dsim_lcd_dev); - /* in case that mipi got enabled at bootloader. */ - if (dsim_pd->enabled) - goto out; + /* in case mipi-dsi has been enabled by bootloader */ + if (dsim_pd->enabled) { + exynos_mipi_regulator_enable(dsim); + goto done; + } /* lcd panel power on. */ if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->power_on) @@ -453,12 +458,11 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev) dsim->suspended = false; -out: +done: platform_set_drvdata(pdev, dsim); - dev_dbg(&pdev->dev, "mipi-dsi driver(%s mode) has been probed.\n", - (dsim_config->e_interface == DSIM_COMMAND) ? - "CPU" : "RGB"); + dev_dbg(&pdev->dev, "%s() completed sucessfuly (%s mode)\n", __func__, + dsim_config->e_interface == DSIM_COMMAND ? "CPU" : "RGB"); return 0; @@ -515,10 +519,10 @@ static int __devexit exynos_mipi_dsi_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int exynos_mipi_dsi_suspend(struct platform_device *pdev, - pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int exynos_mipi_dsi_suspend(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct mipi_dsim_device *dsim = platform_get_drvdata(pdev); struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; @@ -544,8 +548,9 @@ static int exynos_mipi_dsi_suspend(struct platform_device *pdev, return 0; } -static int exynos_mipi_dsi_resume(struct platform_device *pdev) +static int exynos_mipi_dsi_resume(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct mipi_dsim_device *dsim = platform_get_drvdata(pdev); struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; @@ -577,19 +582,19 @@ static int exynos_mipi_dsi_resume(struct platform_device *pdev) return 0; } -#else -#define exynos_mipi_dsi_suspend NULL -#define exynos_mipi_dsi_resume NULL #endif +static const struct dev_pm_ops exynos_mipi_dsi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(exynos_mipi_dsi_suspend, exynos_mipi_dsi_resume) +}; + static struct platform_driver exynos_mipi_dsi_driver = { .probe = exynos_mipi_dsi_probe, .remove = __devexit_p(exynos_mipi_dsi_remove), - .suspend = exynos_mipi_dsi_suspend, - .resume = exynos_mipi_dsi_resume, .driver = { .name = "exynos-mipi-dsim", .owner = THIS_MODULE, + .pm = &exynos_mipi_dsi_pm_ops, }, }; diff --git a/drivers/video/exynos/exynos_mipi_dsi_common.c b/drivers/video/exynos/exynos_mipi_dsi_common.c index 14909c1..47b533a 100644 --- a/drivers/video/exynos/exynos_mipi_dsi_common.c +++ b/drivers/video/exynos/exynos_mipi_dsi_common.c @@ -76,33 +76,25 @@ static unsigned int dpll_table[15] = { irqreturn_t exynos_mipi_dsi_interrupt_handler(int irq, void *dev_id) { - unsigned int intsrc = 0; - unsigned int intmsk = 0; - struct mipi_dsim_device *dsim = NULL; - - dsim = dev_id; - if (!dsim) { - dev_dbg(dsim->dev, KERN_ERR "%s:error: wrong parameter\n", - __func__); - return IRQ_HANDLED; + struct mipi_dsim_device *dsim = dev_id; + unsigned int intsrc, intmsk; + + if (dsim == NULL) { + dev_err(dsim->dev, "%s: wrong parameter\n", __func__); + return IRQ_NONE; } intsrc = exynos_mipi_dsi_read_interrupt(dsim); intmsk = exynos_mipi_dsi_read_interrupt_mask(dsim); + intmsk = ~intmsk & intsrc; - intmsk = ~(intmsk) & intsrc; - - switch (intmsk) { - case INTMSK_RX_DONE: + if (intsrc & INTMSK_RX_DONE) { complete(&dsim_rd_comp); dev_dbg(dsim->dev, "MIPI INTMSK_RX_DONE\n"); - break; - case INTMSK_FIFO_EMPTY: + } + if (intsrc & INTMSK_FIFO_EMPTY) { complete(&dsim_wr_comp); dev_dbg(dsim->dev, "MIPI INTMSK_FIFO_EMPTY\n"); - break; - default: - break; } exynos_mipi_dsi_clear_interrupt(dsim, intmsk); @@ -738,11 +730,11 @@ int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim, if (dsim_config->auto_vertical_cnt == 0) { exynos_mipi_dsi_set_main_disp_vporch(dsim, dsim_config->cmd_allow, - timing->upper_margin, - timing->lower_margin); + timing->lower_margin, + timing->upper_margin); exynos_mipi_dsi_set_main_disp_hporch(dsim, - timing->left_margin, - timing->right_margin); + timing->right_margin, + timing->left_margin); exynos_mipi_dsi_set_main_disp_sync_area(dsim, timing->vsync_len, timing->hsync_len); diff --git a/drivers/video/exynos/s6e8ax0.c b/drivers/video/exynos/s6e8ax0.c index 4aa9ac6..05d080b 100644 --- a/drivers/video/exynos/s6e8ax0.c +++ b/drivers/video/exynos/s6e8ax0.c @@ -293,9 +293,20 @@ static void s6e8ax0_panel_cond(struct s6e8ax0 *lcd) 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0, 0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc1, 0xff, 0xff, 0xc8 }; + static const unsigned char data_to_send_panel_reverse[] = { + 0xf8, 0x19, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d, + 0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08, + 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0, + 0xc1, 0x01, 0x41, 0xc1, 0x00, 0xc1, 0xf6, 0xf6, 0xc1 + }; - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); + if (lcd->dsim_dev->panel_reverse) + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send_panel_reverse, + ARRAY_SIZE(data_to_send_panel_reverse)); + else + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); } static void s6e8ax0_display_cond(struct s6e8ax0 *lcd) diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c index c27e153..1ddeb11 100644 --- a/drivers/video/fb_defio.c +++ b/drivers/video/fb_defio.c @@ -23,7 +23,7 @@ #include <linux/rmap.h> #include <linux/pagemap.h> -struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs) +static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs) { void *screen_base = (void __force *) info->screen_base; struct page *page; @@ -107,6 +107,10 @@ static int fb_deferred_io_mkwrite(struct vm_area_struct *vma, /* protect against the workqueue changing the page list */ mutex_lock(&fbdefio->lock); + /* first write in this cycle, notify the driver */ + if (fbdefio->first_io && list_empty(&fbdefio->pagelist)) + fbdefio->first_io(info); + /* * We want the page to remain locked from ->page_mkwrite until * the PTE is marked dirty to avoid page_mkclean() being called diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c index 67afa9c..a55e366 100644 --- a/drivers/video/fbsysfs.c +++ b/drivers/video/fbsysfs.c @@ -80,6 +80,8 @@ EXPORT_SYMBOL(framebuffer_alloc); */ void framebuffer_release(struct fb_info *info) { + if (!info) + return; kfree(info->apertures); kfree(info); } diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index 6af3f16..458c006 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -834,7 +834,6 @@ static void update_lcdc(struct fb_info *info) diu_ops.set_pixel_clock(var->pixclock); out_be32(&hw->syn_pol, 0); /* SYNC SIGNALS POLARITY */ - out_be32(&hw->thresholds, 0x00037800); /* The Thresholds */ out_be32(&hw->int_status, 0); /* INTERRUPT STATUS */ out_be32(&hw->plut, 0x01F5F666); diff --git a/drivers/video/intelfb/intelfbdrv.c b/drivers/video/intelfb/intelfbdrv.c index 02fd226..bdcbfba 100644 --- a/drivers/video/intelfb/intelfbdrv.c +++ b/drivers/video/intelfb/intelfbdrv.c @@ -680,6 +680,7 @@ static int __devinit intelfb_pci_register(struct pci_dev *pdev, + dinfo->fb.size); if (!dinfo->aperture.virtual) { ERR_MSG("Cannot remap FB region.\n"); + agp_backend_release(bridge); cleanup(dinfo); return -ENODEV; } @@ -689,6 +690,7 @@ static int __devinit intelfb_pci_register(struct pci_dev *pdev, INTEL_REG_SIZE); if (!dinfo->mmio_base) { ERR_MSG("Cannot remap MMIO region.\n"); + agp_backend_release(bridge); cleanup(dinfo); return -ENODEV; } diff --git a/drivers/video/mb862xx/mb862xx-i2c.c b/drivers/video/mb862xx/mb862xx-i2c.c index 273769b..c87e17a 100644 --- a/drivers/video/mb862xx/mb862xx-i2c.c +++ b/drivers/video/mb862xx/mb862xx-i2c.c @@ -68,7 +68,7 @@ static int mb862xx_i2c_read_byte(struct i2c_adapter *adap, u8 *byte, int last) return 1; } -void mb862xx_i2c_stop(struct i2c_adapter *adap) +static void mb862xx_i2c_stop(struct i2c_adapter *adap) { struct mb862xxfb_par *par = adap->algo_data; diff --git a/drivers/video/mb862xx/mb862xxfbdrv.c b/drivers/video/mb862xx/mb862xxfbdrv.c index 11a7a33..00ce1f3 100644 --- a/drivers/video/mb862xx/mb862xxfbdrv.c +++ b/drivers/video/mb862xx/mb862xxfbdrv.c @@ -579,7 +579,7 @@ static ssize_t mb862xxfb_show_dispregs(struct device *dev, static DEVICE_ATTR(dispregs, 0444, mb862xxfb_show_dispregs, NULL); -irqreturn_t mb862xx_intr(int irq, void *dev_id) +static irqreturn_t mb862xx_intr(int irq, void *dev_id) { struct mb862xxfb_par *par = (struct mb862xxfb_par *) dev_id; unsigned long reg_ist, mask; diff --git a/drivers/video/mbx/mbxfb.c b/drivers/video/mbx/mbxfb.c index 55bf619..ab0a8e5 100644 --- a/drivers/video/mbx/mbxfb.c +++ b/drivers/video/mbx/mbxfb.c @@ -950,7 +950,7 @@ static int __devinit mbxfb_probe(struct platform_device *dev) mfbi->fb_virt_addr = ioremap_nocache(mfbi->fb_phys_addr, res_size(mfbi->fb_req)); - if (!mfbi->reg_virt_addr) { + if (!mfbi->fb_virt_addr) { dev_err(&dev->dev, "failed to ioremap frame buffer\n"); ret = -EINVAL; goto err4; diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c index 6c6bc57..abbe691 100644 --- a/drivers/video/mxsfb.c +++ b/drivers/video/mxsfb.c @@ -889,6 +889,18 @@ static int __devexit mxsfb_remove(struct platform_device *pdev) return 0; } +static void mxsfb_shutdown(struct platform_device *pdev) +{ + struct fb_info *fb_info = platform_get_drvdata(pdev); + struct mxsfb_info *host = to_imxfb_host(fb_info); + + /* + * Force stop the LCD controller as keeping it running during reboot + * might interfere with the BootROM's boot mode pads sampling. + */ + writel(CTRL_RUN, host->base + LCDC_CTRL + REG_CLR); +} + static struct platform_device_id mxsfb_devtype[] = { { .name = "imx23-fb", @@ -905,6 +917,7 @@ MODULE_DEVICE_TABLE(platform, mxsfb_devtype); static struct platform_driver mxsfb_driver = { .probe = mxsfb_probe, .remove = __devexit_p(mxsfb_remove), + .shutdown = mxsfb_shutdown, .id_table = mxsfb_devtype, .driver = { .name = DRIVER_NAME, diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig index 1e7536d..b48f95f 100644 --- a/drivers/video/omap/Kconfig +++ b/drivers/video/omap/Kconfig @@ -39,14 +39,6 @@ config FB_OMAP_LCD_MIPID the Mobile Industry Processor Interface DBI-C/DCS specification. (Supported LCDs: Philips LPH8923, Sharp LS041Y3) -config FB_OMAP_BOOTLOADER_INIT - bool "Check bootloader initialization" - depends on FB_OMAP - help - Say Y here if you want to enable checking if the bootloader has - already initialized the display controller. In this case the - driver will skip the initialization. - config FB_OMAP_CONSISTENT_DMA_SIZE int "Consistent DMA memory size (MB)" depends on FB_OMAP diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c index 74e7cf0..ad741c3 100644 --- a/drivers/video/omap2/displays/panel-acx565akm.c +++ b/drivers/video/omap2/displays/panel-acx565akm.c @@ -739,12 +739,6 @@ static void acx_panel_set_timings(struct omap_dss_device *dssdev, } } -static void acx_panel_get_timings(struct omap_dss_device *dssdev, - struct omap_video_timings *timings) -{ - *timings = dssdev->panel.timings; -} - static int acx_panel_check_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { @@ -762,7 +756,6 @@ static struct omap_dss_driver acx_panel_driver = { .resume = acx_panel_resume, .set_timings = acx_panel_set_timings, - .get_timings = acx_panel_get_timings, .check_timings = acx_panel_check_timings, .get_recommended_bpp = acx_get_recommended_bpp, diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c index 30fe4df..e42f9dc 100644 --- a/drivers/video/omap2/displays/panel-generic-dpi.c +++ b/drivers/video/omap2/displays/panel-generic-dpi.c @@ -386,6 +386,106 @@ static struct panel_config generic_dpi_panels[] = { .name = "innolux_at080tn52", }, + + /* Mitsubishi AA084SB01 */ + { + { + .x_res = 800, + .y_res = 600, + .pixel_clock = 40000, + + .hsw = 1, + .hfp = 254, + .hbp = 1, + + .vsw = 1, + .vfp = 26, + .vbp = 1, + }, + .config = OMAP_DSS_LCD_TFT, + .name = "mitsubishi_aa084sb01", + }, + /* EDT ET0500G0DH6 */ + { + { + .x_res = 800, + .y_res = 480, + .pixel_clock = 33260, + + .hsw = 128, + .hfp = 216, + .hbp = 40, + + .vsw = 2, + .vfp = 35, + .vbp = 10, + }, + .config = OMAP_DSS_LCD_TFT, + .name = "edt_et0500g0dh6", + }, + + /* Prime-View PD050VL1 */ + { + { + .x_res = 640, + .y_res = 480, + + .pixel_clock = 25000, + + .hsw = 96, + .hfp = 18, + .hbp = 46, + + .vsw = 2, + .vfp = 10, + .vbp = 33, + }, + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, + .name = "primeview_pd050vl1", + }, + + /* Prime-View PM070WL4 */ + { + { + .x_res = 800, + .y_res = 480, + + .pixel_clock = 32000, + + .hsw = 128, + .hfp = 42, + .hbp = 86, + + .vsw = 2, + .vfp = 10, + .vbp = 33, + }, + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, + .name = "primeview_pm070wl4", + }, + + /* Prime-View PD104SLF */ + { + { + .x_res = 800, + .y_res = 600, + + .pixel_clock = 40000, + + .hsw = 128, + .hfp = 42, + .hbp = 86, + + .vsw = 4, + .vfp = 1, + .vbp = 23, + }, + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, + .name = "primeview_pd104slf", + }, }; struct panel_drv_data { @@ -549,12 +649,6 @@ static void generic_dpi_panel_set_timings(struct omap_dss_device *dssdev, dpi_set_timings(dssdev, timings); } -static void generic_dpi_panel_get_timings(struct omap_dss_device *dssdev, - struct omap_video_timings *timings) -{ - *timings = dssdev->panel.timings; -} - static int generic_dpi_panel_check_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { @@ -571,7 +665,6 @@ static struct omap_dss_driver dpi_driver = { .resume = generic_dpi_panel_resume, .set_timings = generic_dpi_panel_set_timings, - .get_timings = generic_dpi_panel_get_timings, .check_timings = generic_dpi_panel_check_timings, .driver = { diff --git a/drivers/video/omap2/displays/panel-n8x0.c b/drivers/video/omap2/displays/panel-n8x0.c index dc9408d..4a34cdc 100644 --- a/drivers/video/omap2/displays/panel-n8x0.c +++ b/drivers/video/omap2/displays/panel-n8x0.c @@ -610,12 +610,6 @@ static int n8x0_panel_resume(struct omap_dss_device *dssdev) return 0; } -static void n8x0_panel_get_timings(struct omap_dss_device *dssdev, - struct omap_video_timings *timings) -{ - *timings = dssdev->panel.timings; -} - static void n8x0_panel_get_resolution(struct omap_dss_device *dssdev, u16 *xres, u16 *yres) { @@ -678,8 +672,6 @@ static struct omap_dss_driver n8x0_panel_driver = { .get_resolution = n8x0_panel_get_resolution, .get_recommended_bpp = omapdss_default_get_recommended_bpp, - .get_timings = n8x0_panel_get_timings, - .driver = { .name = "n8x0_panel", .owner = THIS_MODULE, diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c index b2dd88b..2ce9992 100644 --- a/drivers/video/omap2/displays/panel-taal.c +++ b/drivers/video/omap2/displays/panel-taal.c @@ -30,7 +30,6 @@ #include <linux/gpio.h> #include <linux/workqueue.h> #include <linux/slab.h> -#include <linux/regulator/consumer.h> #include <linux/mutex.h> #include <video/omapdss.h> @@ -55,73 +54,6 @@ static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable); static int taal_panel_reset(struct omap_dss_device *dssdev); -struct panel_regulator { - struct regulator *regulator; - const char *name; - int min_uV; - int max_uV; -}; - -static void free_regulators(struct panel_regulator *regulators, int n) -{ - int i; - - for (i = 0; i < n; i++) { - /* disable/put in reverse order */ - regulator_disable(regulators[n - i - 1].regulator); - regulator_put(regulators[n - i - 1].regulator); - } -} - -static int init_regulators(struct omap_dss_device *dssdev, - struct panel_regulator *regulators, int n) -{ - int r, i, v; - - for (i = 0; i < n; i++) { - struct regulator *reg; - - reg = regulator_get(&dssdev->dev, regulators[i].name); - if (IS_ERR(reg)) { - dev_err(&dssdev->dev, "failed to get regulator %s\n", - regulators[i].name); - r = PTR_ERR(reg); - goto err; - } - - /* FIXME: better handling of fixed vs. variable regulators */ - v = regulator_get_voltage(reg); - if (v < regulators[i].min_uV || v > regulators[i].max_uV) { - r = regulator_set_voltage(reg, regulators[i].min_uV, - regulators[i].max_uV); - if (r) { - dev_err(&dssdev->dev, - "failed to set regulator %s voltage\n", - regulators[i].name); - regulator_put(reg); - goto err; - } - } - - r = regulator_enable(reg); - if (r) { - dev_err(&dssdev->dev, "failed to enable regulator %s\n", - regulators[i].name); - regulator_put(reg); - goto err; - } - - regulators[i].regulator = reg; - } - - return 0; - -err: - free_regulators(regulators, i); - - return r; -} - /** * struct panel_config - panel configuration * @name: panel name @@ -150,8 +82,6 @@ struct panel_config { unsigned int low; } reset_sequence; - struct panel_regulator *regulators; - int num_regulators; }; enum { @@ -577,12 +507,6 @@ static const struct backlight_ops taal_bl_ops = { .update_status = taal_bl_update_status, }; -static void taal_get_timings(struct omap_dss_device *dssdev, - struct omap_video_timings *timings) -{ - *timings = dssdev->panel.timings; -} - static void taal_get_resolution(struct omap_dss_device *dssdev, u16 *xres, u16 *yres) { @@ -977,11 +901,6 @@ static int taal_probe(struct omap_dss_device *dssdev) atomic_set(&td->do_update, 0); - r = init_regulators(dssdev, panel_config->regulators, - panel_config->num_regulators); - if (r) - goto err_reg; - td->workqueue = create_singlethread_workqueue("taal_esd"); if (td->workqueue == NULL) { dev_err(&dssdev->dev, "can't create ESD workqueue\n"); @@ -1087,8 +1006,6 @@ err_bl: err_rst_gpio: destroy_workqueue(td->workqueue); err_wq: - free_regulators(panel_config->regulators, panel_config->num_regulators); -err_reg: kfree(td); err: return r; @@ -1125,9 +1042,6 @@ static void __exit taal_remove(struct omap_dss_device *dssdev) /* reset, to be sure that the panel is in a valid state */ taal_hw_reset(dssdev); - free_regulators(td->panel_config->regulators, - td->panel_config->num_regulators); - if (gpio_is_valid(panel_data->reset_gpio)) gpio_free(panel_data->reset_gpio); @@ -1909,8 +1823,6 @@ static struct omap_dss_driver taal_driver = { .run_test = taal_run_test, .memory_read = taal_memory_read, - .get_timings = taal_get_timings, - .driver = { .name = "taal", .owner = THIS_MODULE, diff --git a/drivers/video/omap2/displays/panel-tfp410.c b/drivers/video/omap2/displays/panel-tfp410.c index 52637fa..bff306e 100644 --- a/drivers/video/omap2/displays/panel-tfp410.c +++ b/drivers/video/omap2/displays/panel-tfp410.c @@ -47,13 +47,9 @@ struct panel_drv_data { struct mutex lock; int pd_gpio; -}; -static inline struct tfp410_platform_data -*get_pdata(const struct omap_dss_device *dssdev) -{ - return dssdev->data; -} + struct i2c_adapter *i2c_adapter; +}; static int tfp410_power_on(struct omap_dss_device *dssdev) { @@ -68,7 +64,7 @@ static int tfp410_power_on(struct omap_dss_device *dssdev) goto err0; if (gpio_is_valid(ddata->pd_gpio)) - gpio_set_value(ddata->pd_gpio, 1); + gpio_set_value_cansleep(ddata->pd_gpio, 1); return 0; err0: @@ -83,18 +79,18 @@ static void tfp410_power_off(struct omap_dss_device *dssdev) return; if (gpio_is_valid(ddata->pd_gpio)) - gpio_set_value(ddata->pd_gpio, 0); + gpio_set_value_cansleep(ddata->pd_gpio, 0); omapdss_dpi_display_disable(dssdev); } static int tfp410_probe(struct omap_dss_device *dssdev) { - struct tfp410_platform_data *pdata = get_pdata(dssdev); struct panel_drv_data *ddata; int r; + int i2c_bus_num; - ddata = kzalloc(sizeof(*ddata), GFP_KERNEL); + ddata = devm_kzalloc(&dssdev->dev, sizeof(*ddata), GFP_KERNEL); if (!ddata) return -ENOMEM; @@ -104,10 +100,15 @@ static int tfp410_probe(struct omap_dss_device *dssdev) ddata->dssdev = dssdev; mutex_init(&ddata->lock); - if (pdata) + if (dssdev->data) { + struct tfp410_platform_data *pdata = dssdev->data; + ddata->pd_gpio = pdata->power_down_gpio; - else + i2c_bus_num = pdata->i2c_bus_num; + } else { ddata->pd_gpio = -1; + i2c_bus_num = -1; + } if (gpio_is_valid(ddata->pd_gpio)) { r = gpio_request_one(ddata->pd_gpio, GPIOF_OUT_INIT_LOW, @@ -115,13 +116,31 @@ static int tfp410_probe(struct omap_dss_device *dssdev) if (r) { dev_err(&dssdev->dev, "Failed to request PD GPIO %d\n", ddata->pd_gpio); - ddata->pd_gpio = -1; + return r; } } + if (i2c_bus_num != -1) { + struct i2c_adapter *adapter; + + adapter = i2c_get_adapter(i2c_bus_num); + if (!adapter) { + dev_err(&dssdev->dev, "Failed to get I2C adapter, bus %d\n", + i2c_bus_num); + r = -EINVAL; + goto err_i2c; + } + + ddata->i2c_adapter = adapter; + } + dev_set_drvdata(&dssdev->dev, ddata); return 0; +err_i2c: + if (gpio_is_valid(ddata->pd_gpio)) + gpio_free(ddata->pd_gpio); + return r; } static void __exit tfp410_remove(struct omap_dss_device *dssdev) @@ -130,14 +149,15 @@ static void __exit tfp410_remove(struct omap_dss_device *dssdev) mutex_lock(&ddata->lock); + if (ddata->i2c_adapter) + i2c_put_adapter(ddata->i2c_adapter); + if (gpio_is_valid(ddata->pd_gpio)) gpio_free(ddata->pd_gpio); dev_set_drvdata(&dssdev->dev, NULL); mutex_unlock(&ddata->lock); - - kfree(ddata); } static int tfp410_enable(struct omap_dss_device *dssdev) @@ -269,27 +289,17 @@ static int tfp410_read_edid(struct omap_dss_device *dssdev, u8 *edid, int len) { struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); - struct tfp410_platform_data *pdata = get_pdata(dssdev); - struct i2c_adapter *adapter; int r, l, bytes_read; mutex_lock(&ddata->lock); - if (pdata->i2c_bus_num == 0) { + if (!ddata->i2c_adapter) { r = -ENODEV; goto err; } - adapter = i2c_get_adapter(pdata->i2c_bus_num); - if (!adapter) { - dev_err(&dssdev->dev, "Failed to get I2C adapter, bus %d\n", - pdata->i2c_bus_num); - r = -EINVAL; - goto err; - } - l = min(EDID_LENGTH, len); - r = tfp410_ddc_read(adapter, edid, l, 0); + r = tfp410_ddc_read(ddata->i2c_adapter, edid, l, 0); if (r) goto err; @@ -299,7 +309,7 @@ static int tfp410_read_edid(struct omap_dss_device *dssdev, if (len > EDID_LENGTH && edid[0x7e] > 0) { l = min(EDID_LENGTH, len - EDID_LENGTH); - r = tfp410_ddc_read(adapter, edid + EDID_LENGTH, + r = tfp410_ddc_read(ddata->i2c_adapter, edid + EDID_LENGTH, l, EDID_LENGTH); if (r) goto err; @@ -319,21 +329,15 @@ err: static bool tfp410_detect(struct omap_dss_device *dssdev) { struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); - struct tfp410_platform_data *pdata = get_pdata(dssdev); - struct i2c_adapter *adapter; unsigned char out; int r; mutex_lock(&ddata->lock); - if (pdata->i2c_bus_num == 0) - goto out; - - adapter = i2c_get_adapter(pdata->i2c_bus_num); - if (!adapter) + if (!ddata->i2c_adapter) goto out; - r = tfp410_ddc_read(adapter, &out, 1, 0); + r = tfp410_ddc_read(ddata->i2c_adapter, &out, 1, 0); mutex_unlock(&ddata->lock); diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c index 32f3fcd..4b6448b 100644 --- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c +++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c @@ -272,13 +272,16 @@ static const struct omap_video_timings tpo_td043_timings = { static int tpo_td043_power_on(struct tpo_td043_device *tpo_td043) { int nreset_gpio = tpo_td043->nreset_gpio; + int r; if (tpo_td043->powered_on) return 0; - regulator_enable(tpo_td043->vcc_reg); + r = regulator_enable(tpo_td043->vcc_reg); + if (r != 0) + return r; - /* wait for regulator to stabilize */ + /* wait for panel to stabilize */ msleep(160); if (gpio_is_valid(nreset_gpio)) @@ -470,6 +473,18 @@ static void tpo_td043_remove(struct omap_dss_device *dssdev) gpio_free(nreset_gpio); } +static void tpo_td043_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + dpi_set_timings(dssdev, timings); +} + +static int tpo_td043_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + return dpi_check_timings(dssdev, timings); +} + static struct omap_dss_driver tpo_td043_driver = { .probe = tpo_td043_probe, .remove = tpo_td043_remove, @@ -481,6 +496,9 @@ static struct omap_dss_driver tpo_td043_driver = { .set_mirror = tpo_td043_set_hmirror, .get_mirror = tpo_td043_get_hmirror, + .set_timings = tpo_td043_set_timings, + .check_timings = tpo_td043_check_timings, + .driver = { .name = "tpo_td043mtea1_panel", .owner = THIS_MODULE, diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig index 7be7c06..43324e5 100644 --- a/drivers/video/omap2/dss/Kconfig +++ b/drivers/video/omap2/dss/Kconfig @@ -68,6 +68,10 @@ config OMAP4_DSS_HDMI HDMI Interface. This adds the High Definition Multimedia Interface. See http://www.hdmi.org/ for HDMI specification. +config OMAP4_DSS_HDMI_AUDIO + bool + depends on OMAP4_DSS_HDMI + config OMAP2_DSS_SDI bool "SDI support" depends on ARCH_OMAP3 @@ -90,15 +94,6 @@ config OMAP2_DSS_DSI See http://www.mipi.org/ for DSI spesifications. -config OMAP2_DSS_FAKE_VSYNC - bool "Fake VSYNC irq from manual update displays" - default n - help - If this is selected, DSI will generate a fake DISPC VSYNC interrupt - when DSI has sent a frame. This is only needed with DSI or RFBI - displays using manual mode, and you want VSYNC to, for example, - time animation. - config OMAP2_DSS_MIN_FCK_PER_PCK int "Minimum FCK/PCK ratio (for scaling)" range 0 32 diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c index b10b3bc..ab22cc2 100644 --- a/drivers/video/omap2/dss/apply.c +++ b/drivers/video/omap2/dss/apply.c @@ -99,6 +99,11 @@ struct mgr_priv_data { /* If true, a display is enabled using this manager */ bool enabled; + + bool extra_info_dirty; + bool shadow_extra_info_dirty; + + struct omap_video_timings timings; }; static struct { @@ -176,7 +181,7 @@ static bool mgr_manual_update(struct omap_overlay_manager *mgr) } static int dss_check_settings_low(struct omap_overlay_manager *mgr, - struct omap_dss_device *dssdev, bool applying) + bool applying) { struct omap_overlay_info *oi; struct omap_overlay_manager_info *mi; @@ -187,6 +192,9 @@ static int dss_check_settings_low(struct omap_overlay_manager *mgr, mp = get_mgr_priv(mgr); + if (!mp->enabled) + return 0; + if (applying && mp->user_info_dirty) mi = &mp->user_info; else @@ -206,26 +214,24 @@ static int dss_check_settings_low(struct omap_overlay_manager *mgr, ois[ovl->id] = oi; } - return dss_mgr_check(mgr, dssdev, mi, ois); + return dss_mgr_check(mgr, mi, &mp->timings, ois); } /* * check manager and overlay settings using overlay_info from data->info */ -static int dss_check_settings(struct omap_overlay_manager *mgr, - struct omap_dss_device *dssdev) +static int dss_check_settings(struct omap_overlay_manager *mgr) { - return dss_check_settings_low(mgr, dssdev, false); + return dss_check_settings_low(mgr, false); } /* * check manager and overlay settings using overlay_info from ovl->info if * dirty and from data->info otherwise */ -static int dss_check_settings_apply(struct omap_overlay_manager *mgr, - struct omap_dss_device *dssdev) +static int dss_check_settings_apply(struct omap_overlay_manager *mgr) { - return dss_check_settings_low(mgr, dssdev, true); + return dss_check_settings_low(mgr, true); } static bool need_isr(void) @@ -261,6 +267,20 @@ static bool need_isr(void) if (mp->shadow_info_dirty) return true; + /* + * NOTE: we don't check extra_info flags for disabled + * managers, once the manager is enabled, the extra_info + * related manager changes will be taken in by HW. + */ + + /* to write new values to registers */ + if (mp->extra_info_dirty) + return true; + + /* to set GO bit */ + if (mp->shadow_extra_info_dirty) + return true; + list_for_each_entry(ovl, &mgr->overlays, list) { struct ovl_priv_data *op; @@ -305,7 +325,7 @@ static bool need_go(struct omap_overlay_manager *mgr) mp = get_mgr_priv(mgr); - if (mp->shadow_info_dirty) + if (mp->shadow_info_dirty || mp->shadow_extra_info_dirty) return true; list_for_each_entry(ovl, &mgr->overlays, list) { @@ -320,20 +340,16 @@ static bool need_go(struct omap_overlay_manager *mgr) /* returns true if an extra_info field is currently being updated */ static bool extra_info_update_ongoing(void) { - const int num_ovls = omap_dss_get_num_overlays(); - struct ovl_priv_data *op; - struct omap_overlay *ovl; - struct mgr_priv_data *mp; + const int num_mgrs = dss_feat_get_num_mgrs(); int i; - for (i = 0; i < num_ovls; ++i) { - ovl = omap_dss_get_overlay(i); - op = get_ovl_priv(ovl); - - if (!ovl->manager) - continue; + for (i = 0; i < num_mgrs; ++i) { + struct omap_overlay_manager *mgr; + struct omap_overlay *ovl; + struct mgr_priv_data *mp; - mp = get_mgr_priv(ovl->manager); + mgr = omap_dss_get_overlay_manager(i); + mp = get_mgr_priv(mgr); if (!mp->enabled) continue; @@ -341,8 +357,15 @@ static bool extra_info_update_ongoing(void) if (!mp->updating) continue; - if (op->extra_info_dirty || op->shadow_extra_info_dirty) + if (mp->extra_info_dirty || mp->shadow_extra_info_dirty) return true; + + list_for_each_entry(ovl, &mgr->overlays, list) { + struct ovl_priv_data *op = get_ovl_priv(ovl); + + if (op->extra_info_dirty || op->shadow_extra_info_dirty) + return true; + } } return false; @@ -525,11 +548,13 @@ static void dss_ovl_write_regs(struct omap_overlay *ovl) oi = &op->info; + mp = get_mgr_priv(ovl->manager); + replication = dss_use_replication(ovl->manager->device, oi->color_mode); ilace = ovl->manager->device->type == OMAP_DISPLAY_TYPE_VENC; - r = dispc_ovl_setup(ovl->id, oi, ilace, replication); + r = dispc_ovl_setup(ovl->id, oi, ilace, replication, &mp->timings); if (r) { /* * We can't do much here, as this function can be called from @@ -543,8 +568,6 @@ static void dss_ovl_write_regs(struct omap_overlay *ovl) return; } - mp = get_mgr_priv(ovl->manager); - op->info_dirty = false; if (mp->updating) op->shadow_info_dirty = true; @@ -601,6 +624,22 @@ static void dss_mgr_write_regs(struct omap_overlay_manager *mgr) } } +static void dss_mgr_write_regs_extra(struct omap_overlay_manager *mgr) +{ + struct mgr_priv_data *mp = get_mgr_priv(mgr); + + DSSDBGF("%d", mgr->id); + + if (!mp->extra_info_dirty) + return; + + dispc_mgr_set_timings(mgr->id, &mp->timings); + + mp->extra_info_dirty = false; + if (mp->updating) + mp->shadow_extra_info_dirty = true; +} + static void dss_write_regs_common(void) { const int num_mgrs = omap_dss_get_num_overlay_managers(); @@ -646,7 +685,7 @@ static void dss_write_regs(void) if (!mp->enabled || mgr_manual_update(mgr) || mp->busy) continue; - r = dss_check_settings(mgr, mgr->device); + r = dss_check_settings(mgr); if (r) { DSSERR("cannot write registers for manager %s: " "illegal configuration\n", mgr->name); @@ -654,6 +693,7 @@ static void dss_write_regs(void) } dss_mgr_write_regs(mgr); + dss_mgr_write_regs_extra(mgr); } } @@ -693,6 +733,7 @@ static void mgr_clear_shadow_dirty(struct omap_overlay_manager *mgr) mp = get_mgr_priv(mgr); mp->shadow_info_dirty = false; + mp->shadow_extra_info_dirty = false; list_for_each_entry(ovl, &mgr->overlays, list) { op = get_ovl_priv(ovl); @@ -711,7 +752,7 @@ void dss_mgr_start_update(struct omap_overlay_manager *mgr) WARN_ON(mp->updating); - r = dss_check_settings(mgr, mgr->device); + r = dss_check_settings(mgr); if (r) { DSSERR("cannot start manual update: illegal configuration\n"); spin_unlock_irqrestore(&data_lock, flags); @@ -719,6 +760,7 @@ void dss_mgr_start_update(struct omap_overlay_manager *mgr) } dss_mgr_write_regs(mgr); + dss_mgr_write_regs_extra(mgr); dss_write_regs_common(); @@ -857,7 +899,7 @@ int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) spin_lock_irqsave(&data_lock, flags); - r = dss_check_settings_apply(mgr, mgr->device); + r = dss_check_settings_apply(mgr); if (r) { spin_unlock_irqrestore(&data_lock, flags); DSSERR("failed to apply settings: illegal configuration.\n"); @@ -918,16 +960,13 @@ static void dss_ovl_setup_fifo(struct omap_overlay *ovl, bool use_fifo_merge) { struct ovl_priv_data *op = get_ovl_priv(ovl); - struct omap_dss_device *dssdev; u32 fifo_low, fifo_high; if (!op->enabled && !op->enabling) return; - dssdev = ovl->manager->device; - dispc_ovl_compute_fifo_thresholds(ovl->id, &fifo_low, &fifo_high, - use_fifo_merge); + use_fifo_merge, ovl_manual_update(ovl)); dss_apply_ovl_fifo_thresholds(ovl, fifo_low, fifo_high); } @@ -1050,7 +1089,7 @@ int dss_mgr_enable(struct omap_overlay_manager *mgr) mp->enabled = true; - r = dss_check_settings(mgr, mgr->device); + r = dss_check_settings(mgr); if (r) { DSSERR("failed to enable manager %d: check_settings failed\n", mgr->id); @@ -1225,6 +1264,35 @@ err: return r; } +static void dss_apply_mgr_timings(struct omap_overlay_manager *mgr, + struct omap_video_timings *timings) +{ + struct mgr_priv_data *mp = get_mgr_priv(mgr); + + mp->timings = *timings; + mp->extra_info_dirty = true; +} + +void dss_mgr_set_timings(struct omap_overlay_manager *mgr, + struct omap_video_timings *timings) +{ + unsigned long flags; + + mutex_lock(&apply_lock); + + spin_lock_irqsave(&data_lock, flags); + + dss_apply_mgr_timings(mgr, timings); + + dss_write_regs(); + dss_set_go_bits(); + + spin_unlock_irqrestore(&data_lock, flags); + + wait_pending_extra_info_updates(); + + mutex_unlock(&apply_lock); +} int dss_ovl_set_info(struct omap_overlay *ovl, struct omap_overlay_info *info) @@ -1393,7 +1461,7 @@ int dss_ovl_enable(struct omap_overlay *ovl) op->enabling = true; - r = dss_check_settings(ovl->manager, ovl->manager->device); + r = dss_check_settings(ovl->manager); if (r) { DSSERR("failed to enable overlay %d: check_settings failed\n", ovl->id); diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c index e8a1207..72ded9c 100644 --- a/drivers/video/omap2/dss/core.c +++ b/drivers/video/omap2/dss/core.c @@ -43,6 +43,8 @@ static struct { struct regulator *vdds_dsi_reg; struct regulator *vdds_sdi_reg; + + const char *default_display_name; } core; static char *def_disp_name; @@ -54,9 +56,6 @@ bool dss_debug; module_param_named(debug, dss_debug, bool, 0644); #endif -static int omap_dss_register_device(struct omap_dss_device *); -static void omap_dss_unregister_device(struct omap_dss_device *); - /* REGULATORS */ struct regulator *dss_get_vdds_dsi(void) @@ -87,6 +86,51 @@ struct regulator *dss_get_vdds_sdi(void) return reg; } +int dss_get_ctx_loss_count(struct device *dev) +{ + struct omap_dss_board_info *board_data = core.pdev->dev.platform_data; + int cnt; + + if (!board_data->get_context_loss_count) + return -ENOENT; + + cnt = board_data->get_context_loss_count(dev); + + WARN_ONCE(cnt < 0, "get_context_loss_count failed: %d\n", cnt); + + return cnt; +} + +int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask) +{ + struct omap_dss_board_info *board_data = core.pdev->dev.platform_data; + + if (!board_data->dsi_enable_pads) + return -ENOENT; + + return board_data->dsi_enable_pads(dsi_id, lane_mask); +} + +void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask) +{ + struct omap_dss_board_info *board_data = core.pdev->dev.platform_data; + + if (!board_data->dsi_enable_pads) + return; + + return board_data->dsi_disable_pads(dsi_id, lane_mask); +} + +int dss_set_min_bus_tput(struct device *dev, unsigned long tput) +{ + struct omap_dss_board_info *pdata = core.pdev->dev.platform_data; + + if (pdata->set_min_bus_tput) + return pdata->set_min_bus_tput(dev, tput); + else + return 0; +} + #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) static int dss_debug_show(struct seq_file *s, void *unused) { @@ -121,34 +165,6 @@ static int dss_initialize_debugfs(void) debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir, &dss_debug_dump_clocks, &dss_debug_fops); -#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS - debugfs_create_file("dispc_irq", S_IRUGO, dss_debugfs_dir, - &dispc_dump_irqs, &dss_debug_fops); -#endif - -#if defined(CONFIG_OMAP2_DSS_DSI) && defined(CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS) - dsi_create_debugfs_files_irq(dss_debugfs_dir, &dss_debug_fops); -#endif - - debugfs_create_file("dss", S_IRUGO, dss_debugfs_dir, - &dss_dump_regs, &dss_debug_fops); - debugfs_create_file("dispc", S_IRUGO, dss_debugfs_dir, - &dispc_dump_regs, &dss_debug_fops); -#ifdef CONFIG_OMAP2_DSS_RFBI - debugfs_create_file("rfbi", S_IRUGO, dss_debugfs_dir, - &rfbi_dump_regs, &dss_debug_fops); -#endif -#ifdef CONFIG_OMAP2_DSS_DSI - dsi_create_debugfs_files_reg(dss_debugfs_dir, &dss_debug_fops); -#endif -#ifdef CONFIG_OMAP2_DSS_VENC - debugfs_create_file("venc", S_IRUGO, dss_debugfs_dir, - &venc_dump_regs, &dss_debug_fops); -#endif -#ifdef CONFIG_OMAP4_DSS_HDMI - debugfs_create_file("hdmi", S_IRUGO, dss_debugfs_dir, - &hdmi_dump_regs, &dss_debug_fops); -#endif return 0; } @@ -157,6 +173,19 @@ static void dss_uninitialize_debugfs(void) if (dss_debugfs_dir) debugfs_remove_recursive(dss_debugfs_dir); } + +int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)) +{ + struct dentry *d; + + d = debugfs_create_file(name, S_IRUGO, dss_debugfs_dir, + write, &dss_debug_fops); + + if (IS_ERR(d)) + return PTR_ERR(d); + + return 0; +} #else /* CONFIG_DEBUG_FS && CONFIG_OMAP2_DSS_DEBUG_SUPPORT */ static inline int dss_initialize_debugfs(void) { @@ -165,14 +194,18 @@ static inline int dss_initialize_debugfs(void) static inline void dss_uninitialize_debugfs(void) { } +static inline int dss_debugfs_create_file(const char *name, + void (*write)(struct seq_file *)) +{ + return 0; +} #endif /* CONFIG_DEBUG_FS && CONFIG_OMAP2_DSS_DEBUG_SUPPORT */ /* PLATFORM DEVICE */ -static int omap_dss_probe(struct platform_device *pdev) +static int __init omap_dss_probe(struct platform_device *pdev) { struct omap_dss_board_info *pdata = pdev->dev.platform_data; int r; - int i; core.pdev = pdev; @@ -187,28 +220,13 @@ static int omap_dss_probe(struct platform_device *pdev) if (r) goto err_debugfs; - for (i = 0; i < pdata->num_devices; ++i) { - struct omap_dss_device *dssdev = pdata->devices[i]; - - r = omap_dss_register_device(dssdev); - if (r) { - DSSERR("device %d %s register failed %d\n", i, - dssdev->name ?: "unnamed", r); - - while (--i >= 0) - omap_dss_unregister_device(pdata->devices[i]); - - goto err_register; - } - - if (def_disp_name && strcmp(def_disp_name, dssdev->name) == 0) - pdata->default_device = dssdev; - } + if (def_disp_name) + core.default_display_name = def_disp_name; + else if (pdata->default_device) + core.default_display_name = pdata->default_device->name; return 0; -err_register: - dss_uninitialize_debugfs(); err_debugfs: return r; @@ -216,17 +234,11 @@ err_debugfs: static int omap_dss_remove(struct platform_device *pdev) { - struct omap_dss_board_info *pdata = pdev->dev.platform_data; - int i; - dss_uninitialize_debugfs(); dss_uninit_overlays(pdev); dss_uninit_overlay_managers(pdev); - for (i = 0; i < pdata->num_devices; ++i) - omap_dss_unregister_device(pdata->devices[i]); - return 0; } @@ -251,7 +263,6 @@ static int omap_dss_resume(struct platform_device *pdev) } static struct platform_driver omap_dss_driver = { - .probe = omap_dss_probe, .remove = omap_dss_remove, .shutdown = omap_dss_shutdown, .suspend = omap_dss_suspend, @@ -326,7 +337,6 @@ static int dss_driver_probe(struct device *dev) int r; struct omap_dss_driver *dssdrv = to_dss_driver(dev->driver); struct omap_dss_device *dssdev = to_dss_device(dev); - struct omap_dss_board_info *pdata = core.pdev->dev.platform_data; bool force; DSSDBG("driver_probe: dev %s/%s, drv %s\n", @@ -335,7 +345,8 @@ static int dss_driver_probe(struct device *dev) dss_init_device(core.pdev, dssdev); - force = pdata->default_device == dssdev; + force = core.default_display_name && + strcmp(core.default_display_name, dssdev->name) == 0; dss_recheck_connections(dssdev, force); r = dssdrv->probe(dssdev); @@ -381,6 +392,8 @@ int omap_dss_register_driver(struct omap_dss_driver *dssdriver) if (dssdriver->get_recommended_bpp == NULL) dssdriver->get_recommended_bpp = omapdss_default_get_recommended_bpp; + if (dssdriver->get_timings == NULL) + dssdriver->get_timings = omapdss_default_get_timings; return driver_register(&dssdriver->driver); } @@ -427,27 +440,38 @@ static void omap_dss_dev_release(struct device *dev) reset_device(dev, 0); } -static int omap_dss_register_device(struct omap_dss_device *dssdev) +int omap_dss_register_device(struct omap_dss_device *dssdev, + struct device *parent, int disp_num) { - static int dev_num; - WARN_ON(!dssdev->driver_name); reset_device(&dssdev->dev, 1); dssdev->dev.bus = &dss_bus_type; - dssdev->dev.parent = &dss_bus; + dssdev->dev.parent = parent; dssdev->dev.release = omap_dss_dev_release; - dev_set_name(&dssdev->dev, "display%d", dev_num++); + dev_set_name(&dssdev->dev, "display%d", disp_num); return device_register(&dssdev->dev); } -static void omap_dss_unregister_device(struct omap_dss_device *dssdev) +void omap_dss_unregister_device(struct omap_dss_device *dssdev) { device_unregister(&dssdev->dev); } +static int dss_unregister_dss_dev(struct device *dev, void *data) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + omap_dss_unregister_device(dssdev); + return 0; +} + +void omap_dss_unregister_child_devices(struct device *parent) +{ + device_for_each_child(parent, NULL, dss_unregister_dss_dev); +} + /* BUS */ -static int omap_dss_bus_register(void) +static int __init omap_dss_bus_register(void) { int r; @@ -469,12 +493,56 @@ static int omap_dss_bus_register(void) } /* INIT */ +static int (*dss_output_drv_reg_funcs[])(void) __initdata = { +#ifdef CONFIG_OMAP2_DSS_DPI + dpi_init_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_SDI + sdi_init_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_RFBI + rfbi_init_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_VENC + venc_init_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_DSI + dsi_init_platform_driver, +#endif +#ifdef CONFIG_OMAP4_DSS_HDMI + hdmi_init_platform_driver, +#endif +}; + +static void (*dss_output_drv_unreg_funcs[])(void) __exitdata = { +#ifdef CONFIG_OMAP2_DSS_DPI + dpi_uninit_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_SDI + sdi_uninit_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_RFBI + rfbi_uninit_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_VENC + venc_uninit_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_DSI + dsi_uninit_platform_driver, +#endif +#ifdef CONFIG_OMAP4_DSS_HDMI + hdmi_uninit_platform_driver, +#endif +}; + +static bool dss_output_drv_loaded[ARRAY_SIZE(dss_output_drv_reg_funcs)]; static int __init omap_dss_register_drivers(void) { int r; + int i; - r = platform_driver_register(&omap_dss_driver); + r = platform_driver_probe(&omap_dss_driver, omap_dss_probe); if (r) return r; @@ -490,40 +558,18 @@ static int __init omap_dss_register_drivers(void) goto err_dispc; } - r = rfbi_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize rfbi platform driver\n"); - goto err_rfbi; - } - - r = venc_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize venc platform driver\n"); - goto err_venc; - } - - r = dsi_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize DSI platform driver\n"); - goto err_dsi; - } - - r = hdmi_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize hdmi\n"); - goto err_hdmi; + /* + * It's ok if the output-driver register fails. It happens, for example, + * when there is no output-device (e.g. SDI for OMAP4). + */ + for (i = 0; i < ARRAY_SIZE(dss_output_drv_reg_funcs); ++i) { + r = dss_output_drv_reg_funcs[i](); + if (r == 0) + dss_output_drv_loaded[i] = true; } return 0; -err_hdmi: - dsi_uninit_platform_driver(); -err_dsi: - venc_uninit_platform_driver(); -err_venc: - rfbi_uninit_platform_driver(); -err_rfbi: - dispc_uninit_platform_driver(); err_dispc: dss_uninit_platform_driver(); err_dss: @@ -534,10 +580,13 @@ err_dss: static void __exit omap_dss_unregister_drivers(void) { - hdmi_uninit_platform_driver(); - dsi_uninit_platform_driver(); - venc_uninit_platform_driver(); - rfbi_uninit_platform_driver(); + int i; + + for (i = 0; i < ARRAY_SIZE(dss_output_drv_unreg_funcs); ++i) { + if (dss_output_drv_loaded[i]) + dss_output_drv_unreg_funcs[i](); + } + dispc_uninit_platform_driver(); dss_uninit_platform_driver(); diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index ee30937..4749ac3 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -131,23 +131,6 @@ static inline u32 dispc_read_reg(const u16 idx) return __raw_readl(dispc.base + idx); } -static int dispc_get_ctx_loss_count(void) -{ - struct device *dev = &dispc.pdev->dev; - struct omap_display_platform_data *pdata = dev->platform_data; - struct omap_dss_board_info *board_data = pdata->board_data; - int cnt; - - if (!board_data->get_context_loss_count) - return -ENOENT; - - cnt = board_data->get_context_loss_count(dev); - - WARN_ONCE(cnt < 0, "get_context_loss_count failed: %d\n", cnt); - - return cnt; -} - #define SR(reg) \ dispc.ctx[DISPC_##reg / sizeof(u32)] = dispc_read_reg(DISPC_##reg) #define RR(reg) \ @@ -251,7 +234,7 @@ static void dispc_save_context(void) if (dss_has_feature(FEAT_CORE_CLK_DIV)) SR(DIVISOR); - dispc.ctx_loss_cnt = dispc_get_ctx_loss_count(); + dispc.ctx_loss_cnt = dss_get_ctx_loss_count(&dispc.pdev->dev); dispc.ctx_valid = true; DSSDBG("context saved, ctx_loss_count %d\n", dispc.ctx_loss_cnt); @@ -266,7 +249,7 @@ static void dispc_restore_context(void) if (!dispc.ctx_valid) return; - ctx = dispc_get_ctx_loss_count(); + ctx = dss_get_ctx_loss_count(&dispc.pdev->dev); if (ctx >= 0 && ctx == dispc.ctx_loss_cnt) return; @@ -413,14 +396,6 @@ static inline bool dispc_mgr_is_lcd(enum omap_channel channel) return false; } -static struct omap_dss_device *dispc_mgr_get_device(enum omap_channel channel) -{ - struct omap_overlay_manager *mgr = - omap_dss_get_overlay_manager(channel); - - return mgr ? mgr->device : NULL; -} - u32 dispc_mgr_get_vsync_irq(enum omap_channel channel) { switch (channel) { @@ -432,6 +407,7 @@ u32 dispc_mgr_get_vsync_irq(enum omap_channel channel) return DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; default: BUG(); + return 0; } } @@ -446,6 +422,7 @@ u32 dispc_mgr_get_framedone_irq(enum omap_channel channel) return 0; default: BUG(); + return 0; } } @@ -764,7 +741,7 @@ static void dispc_ovl_set_color_mode(enum omap_plane plane, case OMAP_DSS_COLOR_XRGB16_1555: m = 0xf; break; default: - BUG(); break; + BUG(); return; } } else { switch (color_mode) { @@ -801,13 +778,25 @@ static void dispc_ovl_set_color_mode(enum omap_plane plane, case OMAP_DSS_COLOR_XRGB16_1555: m = 0xf; break; default: - BUG(); break; + BUG(); return; } } REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), m, 4, 1); } +static void dispc_ovl_configure_burst_type(enum omap_plane plane, + enum omap_dss_rotation_type rotation_type) +{ + if (dss_has_feature(FEAT_BURST_2D) == 0) + return; + + if (rotation_type == OMAP_DSS_ROT_TILER) + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), 1, 29, 29); + else + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), 0, 29, 29); +} + void dispc_ovl_set_channel_out(enum omap_plane plane, enum omap_channel channel) { int shift; @@ -845,6 +834,7 @@ void dispc_ovl_set_channel_out(enum omap_plane plane, enum omap_channel channel) break; default: BUG(); + return; } val = FLD_MOD(val, chan, shift, shift); @@ -872,6 +862,7 @@ static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane plane) break; default: BUG(); + return 0; } val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); @@ -983,20 +974,13 @@ static void dispc_ovl_enable_replication(enum omap_plane plane, bool enable) REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, shift, shift); } -void dispc_mgr_set_lcd_size(enum omap_channel channel, u16 width, u16 height) +static void dispc_mgr_set_size(enum omap_channel channel, u16 width, + u16 height) { u32 val; - BUG_ON((width > (1 << 11)) || (height > (1 << 11))); - val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); - dispc_write_reg(DISPC_SIZE_MGR(channel), val); -} -void dispc_set_digit_size(u16 width, u16 height) -{ - u32 val; - BUG_ON((width > (1 << 11)) || (height > (1 << 11))); val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); - dispc_write_reg(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT), val); + dispc_write_reg(DISPC_SIZE_MGR(channel), val); } static void dispc_read_plane_fifo_sizes(void) @@ -1063,7 +1047,8 @@ void dispc_enable_fifomerge(bool enable) } void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane, - u32 *fifo_low, u32 *fifo_high, bool use_fifomerge) + u32 *fifo_low, u32 *fifo_high, bool use_fifomerge, + bool manual_update) { /* * All sizes are in bytes. Both the buffer and burst are made of @@ -1091,7 +1076,7 @@ void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane, * combined fifo size */ - if (dss_has_feature(FEAT_OMAP3_DSI_FIFO_BUG)) { + if (manual_update && dss_has_feature(FEAT_OMAP3_DSI_FIFO_BUG)) { *fifo_low = ovl_fifo_size - burst_size * 2; *fifo_high = total_fifo_size - burst_size; } else { @@ -1185,6 +1170,94 @@ static void dispc_ovl_set_scale_param(enum omap_plane plane, dispc_ovl_set_fir(plane, fir_hinc, fir_vinc, color_comp); } +static void dispc_ovl_set_accu_uv(enum omap_plane plane, + u16 orig_width, u16 orig_height, u16 out_width, u16 out_height, + bool ilace, enum omap_color_mode color_mode, u8 rotation) +{ + int h_accu2_0, h_accu2_1; + int v_accu2_0, v_accu2_1; + int chroma_hinc, chroma_vinc; + int idx; + + struct accu { + s8 h0_m, h0_n; + s8 h1_m, h1_n; + s8 v0_m, v0_n; + s8 v1_m, v1_n; + }; + + const struct accu *accu_table; + const struct accu *accu_val; + + static const struct accu accu_nv12[4] = { + { 0, 1, 0, 1 , -1, 2, 0, 1 }, + { 1, 2, -3, 4 , 0, 1, 0, 1 }, + { -1, 1, 0, 1 , -1, 2, 0, 1 }, + { -1, 2, -1, 2 , -1, 1, 0, 1 }, + }; + + static const struct accu accu_nv12_ilace[4] = { + { 0, 1, 0, 1 , -3, 4, -1, 4 }, + { -1, 4, -3, 4 , 0, 1, 0, 1 }, + { -1, 1, 0, 1 , -1, 4, -3, 4 }, + { -3, 4, -3, 4 , -1, 1, 0, 1 }, + }; + + static const struct accu accu_yuv[4] = { + { 0, 1, 0, 1, 0, 1, 0, 1 }, + { 0, 1, 0, 1, 0, 1, 0, 1 }, + { -1, 1, 0, 1, 0, 1, 0, 1 }, + { 0, 1, 0, 1, -1, 1, 0, 1 }, + }; + + switch (rotation) { + case OMAP_DSS_ROT_0: + idx = 0; + break; + case OMAP_DSS_ROT_90: + idx = 1; + break; + case OMAP_DSS_ROT_180: + idx = 2; + break; + case OMAP_DSS_ROT_270: + idx = 3; + break; + default: + BUG(); + return; + } + + switch (color_mode) { + case OMAP_DSS_COLOR_NV12: + if (ilace) + accu_table = accu_nv12_ilace; + else + accu_table = accu_nv12; + break; + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + accu_table = accu_yuv; + break; + default: + BUG(); + return; + } + + accu_val = &accu_table[idx]; + + chroma_hinc = 1024 * orig_width / out_width; + chroma_vinc = 1024 * orig_height / out_height; + + h_accu2_0 = (accu_val->h0_m * chroma_hinc / accu_val->h0_n) % 1024; + h_accu2_1 = (accu_val->h1_m * chroma_hinc / accu_val->h1_n) % 1024; + v_accu2_0 = (accu_val->v0_m * chroma_vinc / accu_val->v0_n) % 1024; + v_accu2_1 = (accu_val->v1_m * chroma_vinc / accu_val->v1_n) % 1024; + + dispc_ovl_set_vid_accu2_0(plane, h_accu2_0, v_accu2_0); + dispc_ovl_set_vid_accu2_1(plane, h_accu2_1, v_accu2_1); +} + static void dispc_ovl_set_scaling_common(enum omap_plane plane, u16 orig_width, u16 orig_height, u16 out_width, u16 out_height, @@ -1258,6 +1331,10 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane plane, REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 0, 8, 8); return; } + + dispc_ovl_set_accu_uv(plane, orig_width, orig_height, out_width, + out_height, ilace, color_mode, rotation); + switch (color_mode) { case OMAP_DSS_COLOR_NV12: /* UV is subsampled by 2 vertically*/ @@ -1280,6 +1357,7 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane plane, break; default: BUG(); + return; } if (out_width != orig_width) @@ -1297,9 +1375,6 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane plane, REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_x ? 1 : 0, 5, 5); /* set V scaling */ REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_y ? 1 : 0, 6, 6); - - dispc_ovl_set_vid_accu2_0(plane, 0x80, 0); - dispc_ovl_set_vid_accu2_1(plane, 0x80, 0); } static void dispc_ovl_set_scaling(enum omap_plane plane, @@ -1410,6 +1485,7 @@ static int color_mode_to_bpp(enum omap_color_mode color_mode) return 32; default: BUG(); + return 0; } } @@ -1423,6 +1499,7 @@ static s32 pixinc(int pixels, u8 ps) return 1 - (-pixels + 1) * ps; else BUG(); + return 0; } static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, @@ -1431,7 +1508,7 @@ static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, enum omap_color_mode color_mode, bool fieldmode, unsigned int field_offset, unsigned *offset0, unsigned *offset1, - s32 *row_inc, s32 *pix_inc) + s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim) { u8 ps; @@ -1477,10 +1554,10 @@ static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, else *offset0 = 0; - *row_inc = pixinc(1 + (screen_width - width) + - (fieldmode ? screen_width : 0), - ps); - *pix_inc = pixinc(1, ps); + *row_inc = pixinc(1 + + (y_predecim * screen_width - x_predecim * width) + + (fieldmode ? screen_width : 0), ps); + *pix_inc = pixinc(x_predecim, ps); break; case OMAP_DSS_ROT_0 + 4: @@ -1498,14 +1575,15 @@ static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, *offset0 = field_offset * screen_width * ps; else *offset0 = 0; - *row_inc = pixinc(1 - (screen_width + width) - - (fieldmode ? screen_width : 0), - ps); - *pix_inc = pixinc(1, ps); + *row_inc = pixinc(1 - + (y_predecim * screen_width + x_predecim * width) - + (fieldmode ? screen_width : 0), ps); + *pix_inc = pixinc(x_predecim, ps); break; default: BUG(); + return; } } @@ -1515,7 +1593,7 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, enum omap_color_mode color_mode, bool fieldmode, unsigned int field_offset, unsigned *offset0, unsigned *offset1, - s32 *row_inc, s32 *pix_inc) + s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim) { u8 ps; u16 fbw, fbh; @@ -1557,10 +1635,14 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 + field_offset * screen_width * ps; else *offset0 = *offset1; - *row_inc = pixinc(1 + (screen_width - fbw) + - (fieldmode ? screen_width : 0), - ps); - *pix_inc = pixinc(1, ps); + *row_inc = pixinc(1 + + (y_predecim * screen_width - fbw * x_predecim) + + (fieldmode ? screen_width : 0), ps); + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + *pix_inc = pixinc(x_predecim, 2 * ps); + else + *pix_inc = pixinc(x_predecim, ps); break; case OMAP_DSS_ROT_90: *offset1 = screen_width * (fbh - 1) * ps; @@ -1568,9 +1650,9 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 + field_offset * ps; else *offset0 = *offset1; - *row_inc = pixinc(screen_width * (fbh - 1) + 1 + - (fieldmode ? 1 : 0), ps); - *pix_inc = pixinc(-screen_width, ps); + *row_inc = pixinc(screen_width * (fbh * x_predecim - 1) + + y_predecim + (fieldmode ? 1 : 0), ps); + *pix_inc = pixinc(-x_predecim * screen_width, ps); break; case OMAP_DSS_ROT_180: *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps; @@ -1579,10 +1661,13 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, else *offset0 = *offset1; *row_inc = pixinc(-1 - - (screen_width - fbw) - - (fieldmode ? screen_width : 0), - ps); - *pix_inc = pixinc(-1, ps); + (y_predecim * screen_width - fbw * x_predecim) - + (fieldmode ? screen_width : 0), ps); + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + *pix_inc = pixinc(-x_predecim, 2 * ps); + else + *pix_inc = pixinc(-x_predecim, ps); break; case OMAP_DSS_ROT_270: *offset1 = (fbw - 1) * ps; @@ -1590,9 +1675,9 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 - field_offset * ps; else *offset0 = *offset1; - *row_inc = pixinc(-screen_width * (fbh - 1) - 1 - - (fieldmode ? 1 : 0), ps); - *pix_inc = pixinc(screen_width, ps); + *row_inc = pixinc(-screen_width * (fbh * x_predecim - 1) - + y_predecim - (fieldmode ? 1 : 0), ps); + *pix_inc = pixinc(x_predecim * screen_width, ps); break; /* mirroring */ @@ -1602,10 +1687,14 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 + field_offset * screen_width * ps; else *offset0 = *offset1; - *row_inc = pixinc(screen_width * 2 - 1 + + *row_inc = pixinc(y_predecim * screen_width * 2 - 1 + (fieldmode ? screen_width : 0), ps); - *pix_inc = pixinc(-1, ps); + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + *pix_inc = pixinc(-x_predecim, 2 * ps); + else + *pix_inc = pixinc(-x_predecim, ps); break; case OMAP_DSS_ROT_90 + 4: @@ -1614,10 +1703,10 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 + field_offset * ps; else *offset0 = *offset1; - *row_inc = pixinc(-screen_width * (fbh - 1) + 1 + - (fieldmode ? 1 : 0), + *row_inc = pixinc(-screen_width * (fbh * x_predecim - 1) + + y_predecim + (fieldmode ? 1 : 0), ps); - *pix_inc = pixinc(screen_width, ps); + *pix_inc = pixinc(x_predecim * screen_width, ps); break; case OMAP_DSS_ROT_180 + 4: @@ -1626,10 +1715,14 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 - field_offset * screen_width * ps; else *offset0 = *offset1; - *row_inc = pixinc(1 - screen_width * 2 - + *row_inc = pixinc(1 - y_predecim * screen_width * 2 - (fieldmode ? screen_width : 0), ps); - *pix_inc = pixinc(1, ps); + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + *pix_inc = pixinc(x_predecim, 2 * ps); + else + *pix_inc = pixinc(x_predecim, ps); break; case OMAP_DSS_ROT_270 + 4: @@ -1638,34 +1731,130 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 - field_offset * ps; else *offset0 = *offset1; - *row_inc = pixinc(screen_width * (fbh - 1) - 1 - - (fieldmode ? 1 : 0), + *row_inc = pixinc(screen_width * (fbh * x_predecim - 1) - + y_predecim - (fieldmode ? 1 : 0), ps); - *pix_inc = pixinc(-screen_width, ps); + *pix_inc = pixinc(-x_predecim * screen_width, ps); break; default: BUG(); + return; + } +} + +static void calc_tiler_rotation_offset(u16 screen_width, u16 width, + enum omap_color_mode color_mode, bool fieldmode, + unsigned int field_offset, unsigned *offset0, unsigned *offset1, + s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim) +{ + u8 ps; + + switch (color_mode) { + case OMAP_DSS_COLOR_CLUT1: + case OMAP_DSS_COLOR_CLUT2: + case OMAP_DSS_COLOR_CLUT4: + case OMAP_DSS_COLOR_CLUT8: + BUG(); + return; + default: + ps = color_mode_to_bpp(color_mode) / 8; + break; } + + DSSDBG("scrw %d, width %d\n", screen_width, width); + + /* + * field 0 = even field = bottom field + * field 1 = odd field = top field + */ + *offset1 = 0; + if (field_offset) + *offset0 = *offset1 + field_offset * screen_width * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(1 + (y_predecim * screen_width - width * x_predecim) + + (fieldmode ? screen_width : 0), ps); + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + *pix_inc = pixinc(x_predecim, 2 * ps); + else + *pix_inc = pixinc(x_predecim, ps); } -static unsigned long calc_fclk_five_taps(enum omap_channel channel, u16 width, +/* + * This function is used to avoid synclosts in OMAP3, because of some + * undocumented horizontal position and timing related limitations. + */ +static int check_horiz_timing_omap3(enum omap_channel channel, + const struct omap_video_timings *t, u16 pos_x, + u16 width, u16 height, u16 out_width, u16 out_height) +{ + int DS = DIV_ROUND_UP(height, out_height); + unsigned long nonactive, lclk, pclk; + static const u8 limits[3] = { 8, 10, 20 }; + u64 val, blank; + int i; + + nonactive = t->x_res + t->hfp + t->hsw + t->hbp - out_width; + pclk = dispc_mgr_pclk_rate(channel); + if (dispc_mgr_is_lcd(channel)) + lclk = dispc_mgr_lclk_rate(channel); + else + lclk = dispc_fclk_rate(); + + i = 0; + if (out_height < height) + i++; + if (out_width < width) + i++; + blank = div_u64((u64)(t->hbp + t->hsw + t->hfp) * lclk, pclk); + DSSDBG("blanking period + ppl = %llu (limit = %u)\n", blank, limits[i]); + if (blank <= limits[i]) + return -EINVAL; + + /* + * Pixel data should be prepared before visible display point starts. + * So, atleast DS-2 lines must have already been fetched by DISPC + * during nonactive - pos_x period. + */ + val = div_u64((u64)(nonactive - pos_x) * lclk, pclk); + DSSDBG("(nonactive - pos_x) * pcd = %llu max(0, DS - 2) * width = %d\n", + val, max(0, DS - 2) * width); + if (val < max(0, DS - 2) * width) + return -EINVAL; + + /* + * All lines need to be refilled during the nonactive period of which + * only one line can be loaded during the active period. So, atleast + * DS - 1 lines should be loaded during nonactive period. + */ + val = div_u64((u64)nonactive * lclk, pclk); + DSSDBG("nonactive * pcd = %llu, max(0, DS - 1) * width = %d\n", + val, max(0, DS - 1) * width); + if (val < max(0, DS - 1) * width) + return -EINVAL; + + return 0; +} + +static unsigned long calc_core_clk_five_taps(enum omap_channel channel, + const struct omap_video_timings *mgr_timings, u16 width, u16 height, u16 out_width, u16 out_height, enum omap_color_mode color_mode) { - u32 fclk = 0; + u32 core_clk = 0; u64 tmp, pclk = dispc_mgr_pclk_rate(channel); if (height <= out_height && width <= out_width) return (unsigned long) pclk; if (height > out_height) { - struct omap_dss_device *dssdev = dispc_mgr_get_device(channel); - unsigned int ppl = dssdev->panel.timings.x_res; + unsigned int ppl = mgr_timings->x_res; tmp = pclk * height * out_width; do_div(tmp, 2 * out_height * ppl); - fclk = tmp; + core_clk = tmp; if (height > 2 * out_height) { if (ppl == out_width) @@ -1673,23 +1862,23 @@ static unsigned long calc_fclk_five_taps(enum omap_channel channel, u16 width, tmp = pclk * (height - 2 * out_height) * out_width; do_div(tmp, 2 * out_height * (ppl - out_width)); - fclk = max(fclk, (u32) tmp); + core_clk = max_t(u32, core_clk, tmp); } } if (width > out_width) { tmp = pclk * width; do_div(tmp, out_width); - fclk = max(fclk, (u32) tmp); + core_clk = max_t(u32, core_clk, tmp); if (color_mode == OMAP_DSS_COLOR_RGB24U) - fclk <<= 1; + core_clk <<= 1; } - return fclk; + return core_clk; } -static unsigned long calc_fclk(enum omap_channel channel, u16 width, +static unsigned long calc_core_clk(enum omap_channel channel, u16 width, u16 height, u16 out_width, u16 out_height) { unsigned int hf, vf; @@ -1730,15 +1919,20 @@ static unsigned long calc_fclk(enum omap_channel channel, u16 width, } static int dispc_ovl_calc_scaling(enum omap_plane plane, - enum omap_channel channel, u16 width, u16 height, - u16 out_width, u16 out_height, - enum omap_color_mode color_mode, bool *five_taps) + enum omap_channel channel, + const struct omap_video_timings *mgr_timings, + u16 width, u16 height, u16 out_width, u16 out_height, + enum omap_color_mode color_mode, bool *five_taps, + int *x_predecim, int *y_predecim, u16 pos_x) { struct omap_overlay *ovl = omap_dss_get_overlay(plane); const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE); const int maxsinglelinewidth = dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH); - unsigned long fclk = 0; + const int max_decim_limit = 16; + unsigned long core_clk = 0; + int decim_x, decim_y, error, min_factor; + u16 in_width, in_height, in_width_max = 0; if (width == out_width && height == out_height) return 0; @@ -1746,64 +1940,154 @@ static int dispc_ovl_calc_scaling(enum omap_plane plane, if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) return -EINVAL; - if (out_width < width / maxdownscale || - out_width > width * 8) + *x_predecim = max_decim_limit; + *y_predecim = max_decim_limit; + + if (color_mode == OMAP_DSS_COLOR_CLUT1 || + color_mode == OMAP_DSS_COLOR_CLUT2 || + color_mode == OMAP_DSS_COLOR_CLUT4 || + color_mode == OMAP_DSS_COLOR_CLUT8) { + *x_predecim = 1; + *y_predecim = 1; + *five_taps = false; + return 0; + } + + decim_x = DIV_ROUND_UP(DIV_ROUND_UP(width, out_width), maxdownscale); + decim_y = DIV_ROUND_UP(DIV_ROUND_UP(height, out_height), maxdownscale); + + min_factor = min(decim_x, decim_y); + + if (decim_x > *x_predecim || out_width > width * 8) return -EINVAL; - if (out_height < height / maxdownscale || - out_height > height * 8) + if (decim_y > *y_predecim || out_height > height * 8) return -EINVAL; if (cpu_is_omap24xx()) { - if (width > maxsinglelinewidth) - DSSERR("Cannot scale max input width exceeded"); *five_taps = false; - fclk = calc_fclk(channel, width, height, out_width, - out_height); + + do { + in_height = DIV_ROUND_UP(height, decim_y); + in_width = DIV_ROUND_UP(width, decim_x); + core_clk = calc_core_clk(channel, in_width, in_height, + out_width, out_height); + error = (in_width > maxsinglelinewidth || !core_clk || + core_clk > dispc_core_clk_rate()); + if (error) { + if (decim_x == decim_y) { + decim_x = min_factor; + decim_y++; + } else { + swap(decim_x, decim_y); + if (decim_x < decim_y) + decim_x++; + } + } + } while (decim_x <= *x_predecim && decim_y <= *y_predecim && + error); + + if (in_width > maxsinglelinewidth) { + DSSERR("Cannot scale max input width exceeded"); + return -EINVAL; + } } else if (cpu_is_omap34xx()) { - if (width > (maxsinglelinewidth * 2)) { + + do { + in_height = DIV_ROUND_UP(height, decim_y); + in_width = DIV_ROUND_UP(width, decim_x); + core_clk = calc_core_clk_five_taps(channel, mgr_timings, + in_width, in_height, out_width, out_height, + color_mode); + + error = check_horiz_timing_omap3(channel, mgr_timings, + pos_x, in_width, in_height, out_width, + out_height); + + if (in_width > maxsinglelinewidth) + if (in_height > out_height && + in_height < out_height * 2) + *five_taps = false; + if (!*five_taps) + core_clk = calc_core_clk(channel, in_width, + in_height, out_width, out_height); + error = (error || in_width > maxsinglelinewidth * 2 || + (in_width > maxsinglelinewidth && *five_taps) || + !core_clk || core_clk > dispc_core_clk_rate()); + if (error) { + if (decim_x == decim_y) { + decim_x = min_factor; + decim_y++; + } else { + swap(decim_x, decim_y); + if (decim_x < decim_y) + decim_x++; + } + } + } while (decim_x <= *x_predecim && decim_y <= *y_predecim + && error); + + if (check_horiz_timing_omap3(channel, mgr_timings, pos_x, width, + height, out_width, out_height)){ + DSSERR("horizontal timing too tight\n"); + return -EINVAL; + } + + if (in_width > (maxsinglelinewidth * 2)) { DSSERR("Cannot setup scaling"); DSSERR("width exceeds maximum width possible"); return -EINVAL; } - fclk = calc_fclk_five_taps(channel, width, height, out_width, - out_height, color_mode); - if (width > maxsinglelinewidth) { - if (height > out_height && height < out_height * 2) - *five_taps = false; - else { - DSSERR("cannot setup scaling with five taps"); - return -EINVAL; - } + + if (in_width > maxsinglelinewidth && *five_taps) { + DSSERR("cannot setup scaling with five taps"); + return -EINVAL; } - if (!*five_taps) - fclk = calc_fclk(channel, width, height, out_width, - out_height); } else { - if (width > maxsinglelinewidth) { + int decim_x_min = decim_x; + in_height = DIV_ROUND_UP(height, decim_y); + in_width_max = dispc_core_clk_rate() / + DIV_ROUND_UP(dispc_mgr_pclk_rate(channel), + out_width); + decim_x = DIV_ROUND_UP(width, in_width_max); + + decim_x = decim_x > decim_x_min ? decim_x : decim_x_min; + if (decim_x > *x_predecim) + return -EINVAL; + + do { + in_width = DIV_ROUND_UP(width, decim_x); + } while (decim_x <= *x_predecim && + in_width > maxsinglelinewidth && decim_x++); + + if (in_width > maxsinglelinewidth) { DSSERR("Cannot scale width exceeds max line width"); return -EINVAL; } - fclk = calc_fclk(channel, width, height, out_width, - out_height); + + core_clk = calc_core_clk(channel, in_width, in_height, + out_width, out_height); } - DSSDBG("required fclk rate = %lu Hz\n", fclk); - DSSDBG("current fclk rate = %lu Hz\n", dispc_fclk_rate()); + DSSDBG("required core clk rate = %lu Hz\n", core_clk); + DSSDBG("current core clk rate = %lu Hz\n", dispc_core_clk_rate()); - if (!fclk || fclk > dispc_fclk_rate()) { + if (!core_clk || core_clk > dispc_core_clk_rate()) { DSSERR("failed to set up scaling, " - "required fclk rate = %lu Hz, " - "current fclk rate = %lu Hz\n", - fclk, dispc_fclk_rate()); + "required core clk rate = %lu Hz, " + "current core clk rate = %lu Hz\n", + core_clk, dispc_core_clk_rate()); return -EINVAL; } + *x_predecim = decim_x; + *y_predecim = decim_y; return 0; } int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, - bool ilace, bool replication) + bool ilace, bool replication, + const struct omap_video_timings *mgr_timings) { struct omap_overlay *ovl = omap_dss_get_overlay(plane); bool five_taps = true; @@ -1814,8 +2098,11 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, s32 pix_inc; u16 frame_height = oi->height; unsigned int field_offset = 0; - u16 outw, outh; + u16 in_height = oi->height; + u16 in_width = oi->width; + u16 out_width, out_height; enum omap_channel channel; + int x_predecim = 1, y_predecim = 1; channel = dispc_ovl_get_channel_out(plane); @@ -1829,32 +2116,35 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, if (oi->paddr == 0) return -EINVAL; - outw = oi->out_width == 0 ? oi->width : oi->out_width; - outh = oi->out_height == 0 ? oi->height : oi->out_height; + out_width = oi->out_width == 0 ? oi->width : oi->out_width; + out_height = oi->out_height == 0 ? oi->height : oi->out_height; - if (ilace && oi->height == outh) + if (ilace && oi->height == out_height) fieldmode = 1; if (ilace) { if (fieldmode) - oi->height /= 2; + in_height /= 2; oi->pos_y /= 2; - outh /= 2; + out_height /= 2; DSSDBG("adjusting for ilace: height %d, pos_y %d, " "out_height %d\n", - oi->height, oi->pos_y, outh); + in_height, oi->pos_y, out_height); } if (!dss_feat_color_mode_supported(plane, oi->color_mode)) return -EINVAL; - r = dispc_ovl_calc_scaling(plane, channel, oi->width, oi->height, - outw, outh, oi->color_mode, - &five_taps); + r = dispc_ovl_calc_scaling(plane, channel, mgr_timings, in_width, + in_height, out_width, out_height, oi->color_mode, + &five_taps, &x_predecim, &y_predecim, oi->pos_x); if (r) return r; + in_width = DIV_ROUND_UP(in_width, x_predecim); + in_height = DIV_ROUND_UP(in_height, y_predecim); + if (oi->color_mode == OMAP_DSS_COLOR_YUV2 || oi->color_mode == OMAP_DSS_COLOR_UYVY || oi->color_mode == OMAP_DSS_COLOR_NV12) @@ -1868,32 +2158,46 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, * so the integer part must be added to the base address of the * bottom field. */ - if (!oi->height || oi->height == outh) + if (!in_height || in_height == out_height) field_offset = 0; else - field_offset = oi->height / outh / 2; + field_offset = in_height / out_height / 2; } /* Fields are independent but interleaved in memory. */ if (fieldmode) field_offset = 1; - if (oi->rotation_type == OMAP_DSS_ROT_DMA) + offset0 = 0; + offset1 = 0; + row_inc = 0; + pix_inc = 0; + + if (oi->rotation_type == OMAP_DSS_ROT_TILER) + calc_tiler_rotation_offset(oi->screen_width, in_width, + oi->color_mode, fieldmode, field_offset, + &offset0, &offset1, &row_inc, &pix_inc, + x_predecim, y_predecim); + else if (oi->rotation_type == OMAP_DSS_ROT_DMA) calc_dma_rotation_offset(oi->rotation, oi->mirror, - oi->screen_width, oi->width, frame_height, + oi->screen_width, in_width, frame_height, oi->color_mode, fieldmode, field_offset, - &offset0, &offset1, &row_inc, &pix_inc); + &offset0, &offset1, &row_inc, &pix_inc, + x_predecim, y_predecim); else calc_vrfb_rotation_offset(oi->rotation, oi->mirror, - oi->screen_width, oi->width, frame_height, + oi->screen_width, in_width, frame_height, oi->color_mode, fieldmode, field_offset, - &offset0, &offset1, &row_inc, &pix_inc); + &offset0, &offset1, &row_inc, &pix_inc, + x_predecim, y_predecim); DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n", offset0, offset1, row_inc, pix_inc); dispc_ovl_set_color_mode(plane, oi->color_mode); + dispc_ovl_configure_burst_type(plane, oi->rotation_type); + dispc_ovl_set_ba0(plane, oi->paddr + offset0); dispc_ovl_set_ba1(plane, oi->paddr + offset1); @@ -1906,19 +2210,18 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, dispc_ovl_set_row_inc(plane, row_inc); dispc_ovl_set_pix_inc(plane, pix_inc); - DSSDBG("%d,%d %dx%d -> %dx%d\n", oi->pos_x, oi->pos_y, oi->width, - oi->height, outw, outh); + DSSDBG("%d,%d %dx%d -> %dx%d\n", oi->pos_x, oi->pos_y, in_width, + in_height, out_width, out_height); dispc_ovl_set_pos(plane, oi->pos_x, oi->pos_y); - dispc_ovl_set_pic_size(plane, oi->width, oi->height); + dispc_ovl_set_pic_size(plane, in_width, in_height); if (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) { - dispc_ovl_set_scaling(plane, oi->width, oi->height, - outw, outh, - ilace, five_taps, fieldmode, + dispc_ovl_set_scaling(plane, in_width, in_height, out_width, + out_height, ilace, five_taps, fieldmode, oi->color_mode, oi->rotation); - dispc_ovl_set_vid_size(plane, outw, outh); + dispc_ovl_set_vid_size(plane, out_width, out_height); dispc_ovl_set_vid_color_conv(plane, cconv); } @@ -2087,8 +2390,10 @@ bool dispc_mgr_is_enabled(enum omap_channel channel) return !!REG_GET(DISPC_CONTROL, 1, 1); else if (channel == OMAP_DSS_CHANNEL_LCD2) return !!REG_GET(DISPC_CONTROL2, 0, 0); - else + else { BUG(); + return false; + } } void dispc_mgr_enable(enum omap_channel channel, bool enable) @@ -2285,6 +2590,12 @@ void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable) REG_FLD_MOD(DISPC_CONTROL, enable, 11, 11); } +static bool _dispc_mgr_size_ok(u16 width, u16 height) +{ + return width <= dss_feat_get_param_max(FEAT_PARAM_MGR_WIDTH) && + height <= dss_feat_get_param_max(FEAT_PARAM_MGR_HEIGHT); +} + static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp, int vsw, int vfp, int vbp) { @@ -2309,11 +2620,20 @@ static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp, return true; } -bool dispc_lcd_timings_ok(struct omap_video_timings *timings) +bool dispc_mgr_timings_ok(enum omap_channel channel, + const struct omap_video_timings *timings) { - return _dispc_lcd_timings_ok(timings->hsw, timings->hfp, - timings->hbp, timings->vsw, - timings->vfp, timings->vbp); + bool timings_ok; + + timings_ok = _dispc_mgr_size_ok(timings->x_res, timings->y_res); + + if (dispc_mgr_is_lcd(channel)) + timings_ok = timings_ok && _dispc_lcd_timings_ok(timings->hsw, + timings->hfp, timings->hbp, + timings->vsw, timings->vfp, + timings->vbp); + + return timings_ok; } static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw, @@ -2340,37 +2660,45 @@ static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw, } /* change name to mode? */ -void dispc_mgr_set_lcd_timings(enum omap_channel channel, +void dispc_mgr_set_timings(enum omap_channel channel, struct omap_video_timings *timings) { unsigned xtot, ytot; unsigned long ht, vt; + struct omap_video_timings t = *timings; + + DSSDBG("channel %d xres %u yres %u\n", channel, t.x_res, t.y_res); - if (!_dispc_lcd_timings_ok(timings->hsw, timings->hfp, - timings->hbp, timings->vsw, - timings->vfp, timings->vbp)) + if (!dispc_mgr_timings_ok(channel, &t)) { BUG(); + return; + } + + if (dispc_mgr_is_lcd(channel)) { + _dispc_mgr_set_lcd_timings(channel, t.hsw, t.hfp, t.hbp, t.vsw, + t.vfp, t.vbp); + + xtot = t.x_res + t.hfp + t.hsw + t.hbp; + ytot = t.y_res + t.vfp + t.vsw + t.vbp; - _dispc_mgr_set_lcd_timings(channel, timings->hsw, timings->hfp, - timings->hbp, timings->vsw, timings->vfp, - timings->vbp); + ht = (timings->pixel_clock * 1000) / xtot; + vt = (timings->pixel_clock * 1000) / xtot / ytot; - dispc_mgr_set_lcd_size(channel, timings->x_res, timings->y_res); + DSSDBG("pck %u\n", timings->pixel_clock); + DSSDBG("hsw %d hfp %d hbp %d vsw %d vfp %d vbp %d\n", + t.hsw, t.hfp, t.hbp, t.vsw, t.vfp, t.vbp); - xtot = timings->x_res + timings->hfp + timings->hsw + timings->hbp; - ytot = timings->y_res + timings->vfp + timings->vsw + timings->vbp; + DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt); + } else { + enum dss_hdmi_venc_clk_source_select source; - ht = (timings->pixel_clock * 1000) / xtot; - vt = (timings->pixel_clock * 1000) / xtot / ytot; + source = dss_get_hdmi_venc_clk_source(); - DSSDBG("channel %d xres %u yres %u\n", channel, timings->x_res, - timings->y_res); - DSSDBG("pck %u\n", timings->pixel_clock); - DSSDBG("hsw %d hfp %d hbp %d vsw %d vfp %d vbp %d\n", - timings->hsw, timings->hfp, timings->hbp, - timings->vsw, timings->vfp, timings->vbp); + if (source == DSS_VENC_TV_CLK) + t.y_res /= 2; + } - DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt); + dispc_mgr_set_size(channel, t.x_res, t.y_res); } static void dispc_mgr_set_lcd_divisor(enum omap_channel channel, u16 lck_div, @@ -2411,6 +2739,7 @@ unsigned long dispc_fclk_rate(void) break; default: BUG(); + return 0; } return r; @@ -2441,6 +2770,7 @@ unsigned long dispc_mgr_lclk_rate(enum omap_channel channel) break; default: BUG(); + return 0; } return r / lcd; @@ -2462,20 +2792,35 @@ unsigned long dispc_mgr_pclk_rate(enum omap_channel channel) return r / pcd; } else { - struct omap_dss_device *dssdev = - dispc_mgr_get_device(channel); + enum dss_hdmi_venc_clk_source_select source; - switch (dssdev->type) { - case OMAP_DISPLAY_TYPE_VENC: + source = dss_get_hdmi_venc_clk_source(); + + switch (source) { + case DSS_VENC_TV_CLK: return venc_get_pixel_clock(); - case OMAP_DISPLAY_TYPE_HDMI: + case DSS_HDMI_M_PCLK: return hdmi_get_pixel_clock(); default: BUG(); + return 0; } } } +unsigned long dispc_core_clk_rate(void) +{ + int lcd; + unsigned long fclk = dispc_fclk_rate(); + + if (dss_has_feature(FEAT_CORE_CLK_DIV)) + lcd = REG_GET(DISPC_DIVISOR, 23, 16); + else + lcd = REG_GET(DISPC_DIVISORo(OMAP_DSS_CHANNEL_LCD), 23, 16); + + return fclk / lcd; +} + void dispc_dump_clocks(struct seq_file *s) { int lcd, pcd; @@ -2588,7 +2933,7 @@ void dispc_dump_irqs(struct seq_file *s) } #endif -void dispc_dump_regs(struct seq_file *s) +static void dispc_dump_regs(struct seq_file *s) { int i, j; const char *mgr_names[] = { @@ -3247,27 +3592,6 @@ int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask, return 0; } -#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC -void dispc_fake_vsync_irq(void) -{ - u32 irqstatus = DISPC_IRQ_VSYNC; - int i; - - WARN_ON(!in_interrupt()); - - for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { - struct omap_dispc_isr_data *isr_data; - isr_data = &dispc.registered_isr[i]; - - if (!isr_data->isr) - continue; - - if (isr_data->mask & irqstatus) - isr_data->isr(isr_data->arg, irqstatus); - } -} -#endif - static void _omap_dispc_initialize_irq(void) { unsigned long flags; @@ -3330,7 +3654,7 @@ static void _omap_dispc_initial_config(void) } /* DISPC HW IP initialisation */ -static int omap_dispchw_probe(struct platform_device *pdev) +static int __init omap_dispchw_probe(struct platform_device *pdev) { u32 rev; int r = 0; @@ -3399,6 +3723,11 @@ static int omap_dispchw_probe(struct platform_device *pdev) dispc_runtime_put(); + dss_debugfs_create_file("dispc", dispc_dump_regs); + +#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS + dss_debugfs_create_file("dispc_irq", dispc_dump_irqs); +#endif return 0; err_runtime_get: @@ -3407,7 +3736,7 @@ err_runtime_get: return r; } -static int omap_dispchw_remove(struct platform_device *pdev) +static int __exit omap_dispchw_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); @@ -3419,19 +3748,12 @@ static int omap_dispchw_remove(struct platform_device *pdev) static int dispc_runtime_suspend(struct device *dev) { dispc_save_context(); - dss_runtime_put(); return 0; } static int dispc_runtime_resume(struct device *dev) { - int r; - - r = dss_runtime_get(); - if (r < 0) - return r; - dispc_restore_context(); return 0; @@ -3443,8 +3765,7 @@ static const struct dev_pm_ops dispc_pm_ops = { }; static struct platform_driver omap_dispchw_driver = { - .probe = omap_dispchw_probe, - .remove = omap_dispchw_remove, + .remove = __exit_p(omap_dispchw_remove), .driver = { .name = "omapdss_dispc", .owner = THIS_MODULE, @@ -3452,12 +3773,12 @@ static struct platform_driver omap_dispchw_driver = { }, }; -int dispc_init_platform_driver(void) +int __init dispc_init_platform_driver(void) { - return platform_driver_register(&omap_dispchw_driver); + return platform_driver_probe(&omap_dispchw_driver, omap_dispchw_probe); } -void dispc_uninit_platform_driver(void) +void __exit dispc_uninit_platform_driver(void) { - return platform_driver_unregister(&omap_dispchw_driver); + platform_driver_unregister(&omap_dispchw_driver); } diff --git a/drivers/video/omap2/dss/dispc.h b/drivers/video/omap2/dss/dispc.h index 5836bd1..f278080 100644 --- a/drivers/video/omap2/dss/dispc.h +++ b/drivers/video/omap2/dss/dispc.h @@ -120,6 +120,7 @@ static inline u16 DISPC_DEFAULT_COLOR(enum omap_channel channel) return 0x03AC; default: BUG(); + return 0; } } @@ -134,6 +135,7 @@ static inline u16 DISPC_TRANS_COLOR(enum omap_channel channel) return 0x03B0; default: BUG(); + return 0; } } @@ -144,10 +146,12 @@ static inline u16 DISPC_TIMING_H(enum omap_channel channel) return 0x0064; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x0400; default: BUG(); + return 0; } } @@ -158,10 +162,12 @@ static inline u16 DISPC_TIMING_V(enum omap_channel channel) return 0x0068; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x0404; default: BUG(); + return 0; } } @@ -172,10 +178,12 @@ static inline u16 DISPC_POL_FREQ(enum omap_channel channel) return 0x006C; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x0408; default: BUG(); + return 0; } } @@ -186,10 +194,12 @@ static inline u16 DISPC_DIVISORo(enum omap_channel channel) return 0x0070; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x040C; default: BUG(); + return 0; } } @@ -205,6 +215,7 @@ static inline u16 DISPC_SIZE_MGR(enum omap_channel channel) return 0x03CC; default: BUG(); + return 0; } } @@ -215,10 +226,12 @@ static inline u16 DISPC_DATA_CYCLE1(enum omap_channel channel) return 0x01D4; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03C0; default: BUG(); + return 0; } } @@ -229,10 +242,12 @@ static inline u16 DISPC_DATA_CYCLE2(enum omap_channel channel) return 0x01D8; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03C4; default: BUG(); + return 0; } } @@ -243,10 +258,12 @@ static inline u16 DISPC_DATA_CYCLE3(enum omap_channel channel) return 0x01DC; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03C8; default: BUG(); + return 0; } } @@ -257,10 +274,12 @@ static inline u16 DISPC_CPR_COEF_R(enum omap_channel channel) return 0x0220; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03BC; default: BUG(); + return 0; } } @@ -271,10 +290,12 @@ static inline u16 DISPC_CPR_COEF_G(enum omap_channel channel) return 0x0224; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03B8; default: BUG(); + return 0; } } @@ -285,10 +306,12 @@ static inline u16 DISPC_CPR_COEF_B(enum omap_channel channel) return 0x0228; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03B4; default: BUG(); + return 0; } } @@ -306,6 +329,7 @@ static inline u16 DISPC_OVL_BASE(enum omap_plane plane) return 0x0300; default: BUG(); + return 0; } } @@ -321,6 +345,7 @@ static inline u16 DISPC_BA0_OFFSET(enum omap_plane plane) return 0x0008; default: BUG(); + return 0; } } @@ -335,6 +360,7 @@ static inline u16 DISPC_BA1_OFFSET(enum omap_plane plane) return 0x000C; default: BUG(); + return 0; } } @@ -343,6 +369,7 @@ static inline u16 DISPC_BA0_UV_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0544; case OMAP_DSS_VIDEO2: @@ -351,6 +378,7 @@ static inline u16 DISPC_BA0_UV_OFFSET(enum omap_plane plane) return 0x0310; default: BUG(); + return 0; } } @@ -359,6 +387,7 @@ static inline u16 DISPC_BA1_UV_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0548; case OMAP_DSS_VIDEO2: @@ -367,6 +396,7 @@ static inline u16 DISPC_BA1_UV_OFFSET(enum omap_plane plane) return 0x0314; default: BUG(); + return 0; } } @@ -381,6 +411,7 @@ static inline u16 DISPC_POS_OFFSET(enum omap_plane plane) return 0x009C; default: BUG(); + return 0; } } @@ -395,6 +426,7 @@ static inline u16 DISPC_SIZE_OFFSET(enum omap_plane plane) return 0x00A8; default: BUG(); + return 0; } } @@ -410,6 +442,7 @@ static inline u16 DISPC_ATTR_OFFSET(enum omap_plane plane) return 0x0070; default: BUG(); + return 0; } } @@ -418,6 +451,7 @@ static inline u16 DISPC_ATTR2_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0568; case OMAP_DSS_VIDEO2: @@ -426,6 +460,7 @@ static inline u16 DISPC_ATTR2_OFFSET(enum omap_plane plane) return 0x032C; default: BUG(); + return 0; } } @@ -441,6 +476,7 @@ static inline u16 DISPC_FIFO_THRESH_OFFSET(enum omap_plane plane) return 0x008C; default: BUG(); + return 0; } } @@ -456,6 +492,7 @@ static inline u16 DISPC_FIFO_SIZE_STATUS_OFFSET(enum omap_plane plane) return 0x0088; default: BUG(); + return 0; } } @@ -471,6 +508,7 @@ static inline u16 DISPC_ROW_INC_OFFSET(enum omap_plane plane) return 0x00A4; default: BUG(); + return 0; } } @@ -486,6 +524,7 @@ static inline u16 DISPC_PIX_INC_OFFSET(enum omap_plane plane) return 0x0098; default: BUG(); + return 0; } } @@ -498,8 +537,10 @@ static inline u16 DISPC_WINDOW_SKIP_OFFSET(enum omap_plane plane) case OMAP_DSS_VIDEO2: case OMAP_DSS_VIDEO3: BUG(); + return 0; default: BUG(); + return 0; } } @@ -512,8 +553,10 @@ static inline u16 DISPC_TABLE_BA_OFFSET(enum omap_plane plane) case OMAP_DSS_VIDEO2: case OMAP_DSS_VIDEO3: BUG(); + return 0; default: BUG(); + return 0; } } @@ -522,6 +565,7 @@ static inline u16 DISPC_FIR_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0024; @@ -529,6 +573,7 @@ static inline u16 DISPC_FIR_OFFSET(enum omap_plane plane) return 0x0090; default: BUG(); + return 0; } } @@ -537,6 +582,7 @@ static inline u16 DISPC_FIR2_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0580; case OMAP_DSS_VIDEO2: @@ -545,6 +591,7 @@ static inline u16 DISPC_FIR2_OFFSET(enum omap_plane plane) return 0x0424; default: BUG(); + return 0; } } @@ -553,6 +600,7 @@ static inline u16 DISPC_PIC_SIZE_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0028; @@ -560,6 +608,7 @@ static inline u16 DISPC_PIC_SIZE_OFFSET(enum omap_plane plane) return 0x0094; default: BUG(); + return 0; } } @@ -569,6 +618,7 @@ static inline u16 DISPC_ACCU0_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x002C; @@ -576,6 +626,7 @@ static inline u16 DISPC_ACCU0_OFFSET(enum omap_plane plane) return 0x0000; default: BUG(); + return 0; } } @@ -584,6 +635,7 @@ static inline u16 DISPC_ACCU2_0_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0584; case OMAP_DSS_VIDEO2: @@ -592,6 +644,7 @@ static inline u16 DISPC_ACCU2_0_OFFSET(enum omap_plane plane) return 0x0428; default: BUG(); + return 0; } } @@ -600,6 +653,7 @@ static inline u16 DISPC_ACCU1_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0030; @@ -607,6 +661,7 @@ static inline u16 DISPC_ACCU1_OFFSET(enum omap_plane plane) return 0x0004; default: BUG(); + return 0; } } @@ -615,6 +670,7 @@ static inline u16 DISPC_ACCU2_1_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0588; case OMAP_DSS_VIDEO2: @@ -623,6 +679,7 @@ static inline u16 DISPC_ACCU2_1_OFFSET(enum omap_plane plane) return 0x042C; default: BUG(); + return 0; } } @@ -632,6 +689,7 @@ static inline u16 DISPC_FIR_COEF_H_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0034 + i * 0x8; @@ -639,6 +697,7 @@ static inline u16 DISPC_FIR_COEF_H_OFFSET(enum omap_plane plane, u16 i) return 0x0010 + i * 0x8; default: BUG(); + return 0; } } @@ -648,6 +707,7 @@ static inline u16 DISPC_FIR_COEF_H2_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x058C + i * 0x8; case OMAP_DSS_VIDEO2: @@ -656,6 +716,7 @@ static inline u16 DISPC_FIR_COEF_H2_OFFSET(enum omap_plane plane, u16 i) return 0x0430 + i * 0x8; default: BUG(); + return 0; } } @@ -665,6 +726,7 @@ static inline u16 DISPC_FIR_COEF_HV_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0038 + i * 0x8; @@ -672,6 +734,7 @@ static inline u16 DISPC_FIR_COEF_HV_OFFSET(enum omap_plane plane, u16 i) return 0x0014 + i * 0x8; default: BUG(); + return 0; } } @@ -681,6 +744,7 @@ static inline u16 DISPC_FIR_COEF_HV2_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0590 + i * 8; case OMAP_DSS_VIDEO2: @@ -689,6 +753,7 @@ static inline u16 DISPC_FIR_COEF_HV2_OFFSET(enum omap_plane plane, u16 i) return 0x0434 + i * 0x8; default: BUG(); + return 0; } } @@ -698,12 +763,14 @@ static inline u16 DISPC_CONV_COEF_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: case OMAP_DSS_VIDEO3: return 0x0074 + i * 0x4; default: BUG(); + return 0; } } @@ -713,6 +780,7 @@ static inline u16 DISPC_FIR_COEF_V_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0124 + i * 0x4; case OMAP_DSS_VIDEO2: @@ -721,6 +789,7 @@ static inline u16 DISPC_FIR_COEF_V_OFFSET(enum omap_plane plane, u16 i) return 0x0050 + i * 0x4; default: BUG(); + return 0; } } @@ -730,6 +799,7 @@ static inline u16 DISPC_FIR_COEF_V2_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x05CC + i * 0x4; case OMAP_DSS_VIDEO2: @@ -738,6 +808,7 @@ static inline u16 DISPC_FIR_COEF_V2_OFFSET(enum omap_plane plane, u16 i) return 0x0470 + i * 0x4; default: BUG(); + return 0; } } @@ -754,6 +825,7 @@ static inline u16 DISPC_PRELOAD_OFFSET(enum omap_plane plane) return 0x00A0; default: BUG(); + return 0; } } #endif diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c index 4424c19..2490106 100644 --- a/drivers/video/omap2/dss/display.c +++ b/drivers/video/omap2/dss/display.c @@ -304,10 +304,18 @@ int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev) return 24; default: BUG(); + return 0; } } EXPORT_SYMBOL(omapdss_default_get_recommended_bpp); +void omapdss_default_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = dssdev->panel.timings; +} +EXPORT_SYMBOL(omapdss_default_get_timings); + /* Checks if replication logic should be used. Only use for active matrix, * when overlay is in RGB12U or RGB16 mode, and LCD interface is * 18bpp or 24bpp */ @@ -340,6 +348,7 @@ bool dss_use_replication(struct omap_dss_device *dssdev, break; default: BUG(); + return false; } return bpp > 16; @@ -352,46 +361,6 @@ void dss_init_device(struct platform_device *pdev, int i; int r; - switch (dssdev->type) { -#ifdef CONFIG_OMAP2_DSS_DPI - case OMAP_DISPLAY_TYPE_DPI: - r = dpi_init_display(dssdev); - break; -#endif -#ifdef CONFIG_OMAP2_DSS_RFBI - case OMAP_DISPLAY_TYPE_DBI: - r = rfbi_init_display(dssdev); - break; -#endif -#ifdef CONFIG_OMAP2_DSS_VENC - case OMAP_DISPLAY_TYPE_VENC: - r = venc_init_display(dssdev); - break; -#endif -#ifdef CONFIG_OMAP2_DSS_SDI - case OMAP_DISPLAY_TYPE_SDI: - r = sdi_init_display(dssdev); - break; -#endif -#ifdef CONFIG_OMAP2_DSS_DSI - case OMAP_DISPLAY_TYPE_DSI: - r = dsi_init_display(dssdev); - break; -#endif - case OMAP_DISPLAY_TYPE_HDMI: - r = hdmi_init_display(dssdev); - break; - default: - DSSERR("Support for display '%s' not compiled in.\n", - dssdev->name); - return; - } - - if (r) { - DSSERR("failed to init display %s\n", dssdev->name); - return; - } - /* create device sysfs files */ i = 0; while ((attr = display_sysfs_attrs[i++]) != NULL) { diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c index faaf305..8c2056c 100644 --- a/drivers/video/omap2/dss/dpi.c +++ b/drivers/video/omap2/dss/dpi.c @@ -156,7 +156,7 @@ static int dpi_set_mode(struct omap_dss_device *dssdev) t->pixel_clock = pck; } - dispc_mgr_set_lcd_timings(dssdev->manager->id, t); + dss_mgr_set_timings(dssdev->manager, t); return 0; } @@ -202,10 +202,6 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev) goto err_reg_enable; } - r = dss_runtime_get(); - if (r) - goto err_get_dss; - r = dispc_runtime_get(); if (r) goto err_get_dispc; @@ -244,8 +240,6 @@ err_dsi_pll_init: err_get_dsi: dispc_runtime_put(); err_get_dispc: - dss_runtime_put(); -err_get_dss: if (cpu_is_omap34xx()) regulator_disable(dpi.vdds_dsi_reg); err_reg_enable: @@ -266,7 +260,6 @@ void omapdss_dpi_display_disable(struct omap_dss_device *dssdev) } dispc_runtime_put(); - dss_runtime_put(); if (cpu_is_omap34xx()) regulator_disable(dpi.vdds_dsi_reg); @@ -283,21 +276,15 @@ void dpi_set_timings(struct omap_dss_device *dssdev, DSSDBG("dpi_set_timings\n"); dssdev->panel.timings = *timings; if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { - r = dss_runtime_get(); - if (r) - return; - r = dispc_runtime_get(); - if (r) { - dss_runtime_put(); + if (r) return; - } dpi_set_mode(dssdev); - dispc_mgr_go(dssdev->manager->id); dispc_runtime_put(); - dss_runtime_put(); + } else { + dss_mgr_set_timings(dssdev->manager, timings); } } EXPORT_SYMBOL(dpi_set_timings); @@ -312,7 +299,7 @@ int dpi_check_timings(struct omap_dss_device *dssdev, unsigned long pck; struct dispc_clock_info dispc_cinfo; - if (!dispc_lcd_timings_ok(timings)) + if (dss_mgr_check_timings(dssdev->manager, timings)) return -EINVAL; if (timings->pixel_clock == 0) @@ -352,7 +339,7 @@ int dpi_check_timings(struct omap_dss_device *dssdev, } EXPORT_SYMBOL(dpi_check_timings); -int dpi_init_display(struct omap_dss_device *dssdev) +static int __init dpi_init_display(struct omap_dss_device *dssdev) { DSSDBG("init_display\n"); @@ -378,12 +365,58 @@ int dpi_init_display(struct omap_dss_device *dssdev) return 0; } -int dpi_init(void) +static void __init dpi_probe_pdata(struct platform_device *pdev) { + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int i, r; + + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; + + if (dssdev->type != OMAP_DISPLAY_TYPE_DPI) + continue; + + r = dpi_init_display(dssdev); + if (r) { + DSSERR("device %s init failed: %d\n", dssdev->name, r); + continue; + } + + r = omap_dss_register_device(dssdev, &pdev->dev, i); + if (r) + DSSERR("device %s register failed: %d\n", + dssdev->name, r); + } +} + +static int __init omap_dpi_probe(struct platform_device *pdev) +{ + dpi_probe_pdata(pdev); + + return 0; +} + +static int __exit omap_dpi_remove(struct platform_device *pdev) +{ + omap_dss_unregister_child_devices(&pdev->dev); + return 0; } -void dpi_exit(void) +static struct platform_driver omap_dpi_driver = { + .remove = __exit_p(omap_dpi_remove), + .driver = { + .name = "omapdss_dpi", + .owner = THIS_MODULE, + }, +}; + +int __init dpi_init_platform_driver(void) { + return platform_driver_probe(&omap_dpi_driver, omap_dpi_probe); } +void __exit dpi_uninit_platform_driver(void) +{ + platform_driver_unregister(&omap_dpi_driver); +} diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index 210a3c4..ec363d8 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -256,14 +256,13 @@ struct dsi_data { struct platform_device *pdev; void __iomem *base; + int module_id; + int irq; struct clk *dss_clk; struct clk *sys_clk; - int (*enable_pads)(int dsi_id, unsigned lane_mask); - void (*disable_pads)(int dsi_id, unsigned lane_mask); - struct dsi_clock_info current_cinfo; bool vdds_dsi_enabled; @@ -361,11 +360,6 @@ struct platform_device *dsi_get_dsidev_from_id(int module) return dsi_pdev_map[module]; } -static inline int dsi_get_dsidev_id(struct platform_device *dsidev) -{ - return dsidev->id; -} - static inline void dsi_write_reg(struct platform_device *dsidev, const struct dsi_reg idx, u32 val) { @@ -452,6 +446,7 @@ u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt) return 16; default: BUG(); + return 0; } } @@ -1184,10 +1179,9 @@ static unsigned long dsi_get_txbyteclkhs(struct platform_device *dsidev) static unsigned long dsi_fclk_rate(struct platform_device *dsidev) { unsigned long r; - int dsi_module = dsi_get_dsidev_id(dsidev); struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - if (dss_get_dsi_clk_source(dsi_module) == OMAP_DSS_CLK_SRC_FCK) { + if (dss_get_dsi_clk_source(dsi->module_id) == OMAP_DSS_CLK_SRC_FCK) { /* DSI FCLK source is DSS_CLK_FCK */ r = clk_get_rate(dsi->dss_clk); } else { @@ -1279,10 +1273,9 @@ static int dsi_pll_power(struct platform_device *dsidev, } /* calculate clock rates using dividers in cinfo */ -static int dsi_calc_clock_rates(struct omap_dss_device *dssdev, +static int dsi_calc_clock_rates(struct platform_device *dsidev, struct dsi_clock_info *cinfo) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); if (cinfo->regn == 0 || cinfo->regn > dsi->regn_max) @@ -1297,21 +1290,8 @@ static int dsi_calc_clock_rates(struct omap_dss_device *dssdev, if (cinfo->regm_dsi > dsi->regm_dsi_max) return -EINVAL; - if (cinfo->use_sys_clk) { - cinfo->clkin = clk_get_rate(dsi->sys_clk); - /* XXX it is unclear if highfreq should be used - * with DSS_SYS_CLK source also */ - cinfo->highfreq = 0; - } else { - cinfo->clkin = dispc_mgr_pclk_rate(dssdev->manager->id); - - if (cinfo->clkin < 32000000) - cinfo->highfreq = 0; - else - cinfo->highfreq = 1; - } - - cinfo->fint = cinfo->clkin / (cinfo->regn * (cinfo->highfreq ? 2 : 1)); + cinfo->clkin = clk_get_rate(dsi->sys_clk); + cinfo->fint = cinfo->clkin / cinfo->regn; if (cinfo->fint > dsi->fint_max || cinfo->fint < dsi->fint_min) return -EINVAL; @@ -1378,27 +1358,21 @@ retry: memset(&cur, 0, sizeof(cur)); cur.clkin = dss_sys_clk; - cur.use_sys_clk = 1; - cur.highfreq = 0; - /* no highfreq: 0.75MHz < Fint = clkin / regn < 2.1MHz */ - /* highfreq: 0.75MHz < Fint = clkin / (2*regn) < 2.1MHz */ + /* 0.75MHz < Fint = clkin / regn < 2.1MHz */ /* To reduce PLL lock time, keep Fint high (around 2 MHz) */ for (cur.regn = 1; cur.regn < dsi->regn_max; ++cur.regn) { - if (cur.highfreq == 0) - cur.fint = cur.clkin / cur.regn; - else - cur.fint = cur.clkin / (2 * cur.regn); + cur.fint = cur.clkin / cur.regn; if (cur.fint > dsi->fint_max || cur.fint < dsi->fint_min) continue; - /* DSIPHY(MHz) = (2 * regm / regn) * (clkin / (highfreq + 1)) */ + /* DSIPHY(MHz) = (2 * regm / regn) * clkin */ for (cur.regm = 1; cur.regm < dsi->regm_max; ++cur.regm) { unsigned long a, b; a = 2 * cur.regm * (cur.clkin/1000); - b = cur.regn * (cur.highfreq + 1); + b = cur.regn; cur.clkin4ddr = a / b * 1000; if (cur.clkin4ddr > 1800 * 1000 * 1000) @@ -1486,9 +1460,7 @@ int dsi_pll_set_clock_div(struct platform_device *dsidev, DSSDBGF(); - dsi->current_cinfo.use_sys_clk = cinfo->use_sys_clk; - dsi->current_cinfo.highfreq = cinfo->highfreq; - + dsi->current_cinfo.clkin = cinfo->clkin; dsi->current_cinfo.fint = cinfo->fint; dsi->current_cinfo.clkin4ddr = cinfo->clkin4ddr; dsi->current_cinfo.dsi_pll_hsdiv_dispc_clk = @@ -1503,17 +1475,13 @@ int dsi_pll_set_clock_div(struct platform_device *dsidev, DSSDBG("DSI Fint %ld\n", cinfo->fint); - DSSDBG("clkin (%s) rate %ld, highfreq %d\n", - cinfo->use_sys_clk ? "dss_sys_clk" : "pclkfree", - cinfo->clkin, - cinfo->highfreq); + DSSDBG("clkin rate %ld\n", cinfo->clkin); /* DSIPHY == CLKIN4DDR */ - DSSDBG("CLKIN4DDR = 2 * %d / %d * %lu / %d = %lu\n", + DSSDBG("CLKIN4DDR = 2 * %d / %d * %lu = %lu\n", cinfo->regm, cinfo->regn, cinfo->clkin, - cinfo->highfreq + 1, cinfo->clkin4ddr); DSSDBG("Data rate on 1 DSI lane %ld Mbps\n", @@ -1568,10 +1536,6 @@ int dsi_pll_set_clock_div(struct platform_device *dsidev, if (dss_has_feature(FEAT_DSI_PLL_FREQSEL)) l = FLD_MOD(l, f, 4, 1); /* DSI_PLL_FREQSEL */ - l = FLD_MOD(l, cinfo->use_sys_clk ? 0 : 1, - 11, 11); /* DSI_PLL_CLKSEL */ - l = FLD_MOD(l, cinfo->highfreq, - 12, 12); /* DSI_PLL_HIGHFREQ */ l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */ l = FLD_MOD(l, 0, 14, 14); /* DSIPHY_CLKINEN */ l = FLD_MOD(l, 1, 20, 20); /* DSI_HSDIVBYPASS */ @@ -1716,7 +1680,7 @@ static void dsi_dump_dsidev_clocks(struct platform_device *dsidev, struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); struct dsi_clock_info *cinfo = &dsi->current_cinfo; enum omap_dss_clk_source dispc_clk_src, dsi_clk_src; - int dsi_module = dsi_get_dsidev_id(dsidev); + int dsi_module = dsi->module_id; dispc_clk_src = dss_get_dispc_clk_source(); dsi_clk_src = dss_get_dsi_clk_source(dsi_module); @@ -1726,8 +1690,7 @@ static void dsi_dump_dsidev_clocks(struct platform_device *dsidev, seq_printf(s, "- DSI%d PLL -\n", dsi_module + 1); - seq_printf(s, "dsi pll source = %s\n", - cinfo->use_sys_clk ? "dss_sys_clk" : "pclkfree"); + seq_printf(s, "dsi pll clkin\t%lu\n", cinfo->clkin); seq_printf(s, "Fint\t\t%-16luregn %u\n", cinfo->fint, cinfo->regn); @@ -1789,7 +1752,6 @@ static void dsi_dump_dsidev_irqs(struct platform_device *dsidev, struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long flags; struct dsi_irq_stats stats; - int dsi_module = dsi_get_dsidev_id(dsidev); spin_lock_irqsave(&dsi->irq_stats_lock, flags); @@ -1806,7 +1768,7 @@ static void dsi_dump_dsidev_irqs(struct platform_device *dsidev, #define PIS(x) \ seq_printf(s, "%-20s %10d\n", #x, stats.dsi_irqs[ffs(DSI_IRQ_##x)-1]); - seq_printf(s, "-- DSI%d interrupts --\n", dsi_module + 1); + seq_printf(s, "-- DSI%d interrupts --\n", dsi->module_id + 1); PIS(VC0); PIS(VC1); PIS(VC2); @@ -1886,22 +1848,6 @@ static void dsi2_dump_irqs(struct seq_file *s) dsi_dump_dsidev_irqs(dsidev, s); } - -void dsi_create_debugfs_files_irq(struct dentry *debugfs_dir, - const struct file_operations *debug_fops) -{ - struct platform_device *dsidev; - - dsidev = dsi_get_dsidev_from_id(0); - if (dsidev) - debugfs_create_file("dsi1_irqs", S_IRUGO, debugfs_dir, - &dsi1_dump_irqs, debug_fops); - - dsidev = dsi_get_dsidev_from_id(1); - if (dsidev) - debugfs_create_file("dsi2_irqs", S_IRUGO, debugfs_dir, - &dsi2_dump_irqs, debug_fops); -} #endif static void dsi_dump_dsidev_regs(struct platform_device *dsidev, @@ -2002,21 +1948,6 @@ static void dsi2_dump_regs(struct seq_file *s) dsi_dump_dsidev_regs(dsidev, s); } -void dsi_create_debugfs_files_reg(struct dentry *debugfs_dir, - const struct file_operations *debug_fops) -{ - struct platform_device *dsidev; - - dsidev = dsi_get_dsidev_from_id(0); - if (dsidev) - debugfs_create_file("dsi1_regs", S_IRUGO, debugfs_dir, - &dsi1_dump_regs, debug_fops); - - dsidev = dsi_get_dsidev_from_id(1); - if (dsidev) - debugfs_create_file("dsi2_regs", S_IRUGO, debugfs_dir, - &dsi2_dump_regs, debug_fops); -} enum dsi_cio_power_state { DSI_COMPLEXIO_POWER_OFF = 0x0, DSI_COMPLEXIO_POWER_ON = 0x1, @@ -2073,6 +2004,7 @@ static unsigned dsi_get_line_buf_size(struct platform_device *dsidev) return 1365 * 3; /* 1365x24 bits */ default: BUG(); + return 0; } } @@ -2337,7 +2269,7 @@ static int dsi_cio_init(struct omap_dss_device *dssdev) DSSDBGF(); - r = dsi->enable_pads(dsidev->id, dsi_get_lane_mask(dssdev)); + r = dss_dsi_enable_pads(dsi->module_id, dsi_get_lane_mask(dssdev)); if (r) return r; @@ -2447,7 +2379,7 @@ err_cio_pwr: dsi_cio_disable_lane_override(dsidev); err_scp_clk_dom: dsi_disable_scp_clk(dsidev); - dsi->disable_pads(dsidev->id, dsi_get_lane_mask(dssdev)); + dss_dsi_disable_pads(dsi->module_id, dsi_get_lane_mask(dssdev)); return r; } @@ -2461,7 +2393,7 @@ static void dsi_cio_uninit(struct omap_dss_device *dssdev) dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF); dsi_disable_scp_clk(dsidev); - dsi->disable_pads(dsidev->id, dsi_get_lane_mask(dssdev)); + dss_dsi_disable_pads(dsi->module_id, dsi_get_lane_mask(dssdev)); } static void dsi_config_tx_fifo(struct platform_device *dsidev, @@ -2485,6 +2417,7 @@ static void dsi_config_tx_fifo(struct platform_device *dsidev, if (add + size > 4) { DSSERR("Illegal FIFO configuration\n"); BUG(); + return; } v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4); @@ -2517,6 +2450,7 @@ static void dsi_config_rx_fifo(struct platform_device *dsidev, if (add + size > 4) { DSSERR("Illegal FIFO configuration\n"); BUG(); + return; } v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4); @@ -2658,6 +2592,7 @@ static int dsi_sync_vc(struct platform_device *dsidev, int channel) return dsi_sync_vc_l4(dsidev, channel); default: BUG(); + return -EINVAL; } } @@ -3226,6 +3161,7 @@ static int dsi_vc_generic_send_read_request(struct omap_dss_device *dssdev, data = reqdata[0] | (reqdata[1] << 8); } else { BUG(); + return -EINVAL; } r = dsi_vc_send_short(dsidev, channel, data_type, data, 0); @@ -3340,7 +3276,6 @@ static int dsi_vc_read_rx_fifo(struct platform_device *dsidev, int channel, goto err; } - BUG(); err: DSSERR("dsi_vc_read_rx_fifo(ch %d type %s) failed\n", channel, type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : "DCS"); @@ -3735,6 +3670,186 @@ static void dsi_config_blanking_modes(struct omap_dss_device *dssdev) dsi_write_reg(dsidev, DSI_CTRL, r); } +/* + * According to section 'HS Command Mode Interleaving' in OMAP TRM, Scenario 3 + * results in maximum transition time for data and clock lanes to enter and + * exit HS mode. Hence, this is the scenario where the least amount of command + * mode data can be interleaved. We program the minimum amount of TXBYTECLKHS + * clock cycles that can be used to interleave command mode data in HS so that + * all scenarios are satisfied. + */ +static int dsi_compute_interleave_hs(int blank, bool ddr_alwon, int enter_hs, + int exit_hs, int exiths_clk, int ddr_pre, int ddr_post) +{ + int transition; + + /* + * If DDR_CLK_ALWAYS_ON is set, we need to consider HS mode transition + * time of data lanes only, if it isn't set, we need to consider HS + * transition time of both data and clock lanes. HS transition time + * of Scenario 3 is considered. + */ + if (ddr_alwon) { + transition = enter_hs + exit_hs + max(enter_hs, 2) + 1; + } else { + int trans1, trans2; + trans1 = ddr_pre + enter_hs + exit_hs + max(enter_hs, 2) + 1; + trans2 = ddr_pre + enter_hs + exiths_clk + ddr_post + ddr_pre + + enter_hs + 1; + transition = max(trans1, trans2); + } + + return blank > transition ? blank - transition : 0; +} + +/* + * According to section 'LP Command Mode Interleaving' in OMAP TRM, Scenario 1 + * results in maximum transition time for data lanes to enter and exit LP mode. + * Hence, this is the scenario where the least amount of command mode data can + * be interleaved. We program the minimum amount of bytes that can be + * interleaved in LP so that all scenarios are satisfied. + */ +static int dsi_compute_interleave_lp(int blank, int enter_hs, int exit_hs, + int lp_clk_div, int tdsi_fclk) +{ + int trans_lp; /* time required for a LP transition, in TXBYTECLKHS */ + int tlp_avail; /* time left for interleaving commands, in CLKIN4DDR */ + int ttxclkesc; /* period of LP transmit escape clock, in CLKIN4DDR */ + int thsbyte_clk = 16; /* Period of TXBYTECLKHS clock, in CLKIN4DDR */ + int lp_inter; /* cmd mode data that can be interleaved, in bytes */ + + /* maximum LP transition time according to Scenario 1 */ + trans_lp = exit_hs + max(enter_hs, 2) + 1; + + /* CLKIN4DDR = 16 * TXBYTECLKHS */ + tlp_avail = thsbyte_clk * (blank - trans_lp); + + ttxclkesc = tdsi_fclk / lp_clk_div; + + lp_inter = ((tlp_avail - 8 * thsbyte_clk - 5 * tdsi_fclk) / ttxclkesc - + 26) / 16; + + return max(lp_inter, 0); +} + +static void dsi_config_cmd_mode_interleaving(struct omap_dss_device *dssdev) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + int blanking_mode; + int hfp_blanking_mode, hbp_blanking_mode, hsa_blanking_mode; + int hsa, hfp, hbp, width_bytes, bllp, lp_clk_div; + int ddr_clk_pre, ddr_clk_post, enter_hs_mode_lat, exit_hs_mode_lat; + int tclk_trail, ths_exit, exiths_clk; + bool ddr_alwon; + struct omap_video_timings *timings = &dssdev->panel.timings; + int bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt); + int ndl = dsi->num_lanes_used - 1; + int dsi_fclk_hsdiv = dssdev->clocks.dsi.regm_dsi + 1; + int hsa_interleave_hs = 0, hsa_interleave_lp = 0; + int hfp_interleave_hs = 0, hfp_interleave_lp = 0; + int hbp_interleave_hs = 0, hbp_interleave_lp = 0; + int bl_interleave_hs = 0, bl_interleave_lp = 0; + u32 r; + + r = dsi_read_reg(dsidev, DSI_CTRL); + blanking_mode = FLD_GET(r, 20, 20); + hfp_blanking_mode = FLD_GET(r, 21, 21); + hbp_blanking_mode = FLD_GET(r, 22, 22); + hsa_blanking_mode = FLD_GET(r, 23, 23); + + r = dsi_read_reg(dsidev, DSI_VM_TIMING1); + hbp = FLD_GET(r, 11, 0); + hfp = FLD_GET(r, 23, 12); + hsa = FLD_GET(r, 31, 24); + + r = dsi_read_reg(dsidev, DSI_CLK_TIMING); + ddr_clk_post = FLD_GET(r, 7, 0); + ddr_clk_pre = FLD_GET(r, 15, 8); + + r = dsi_read_reg(dsidev, DSI_VM_TIMING7); + exit_hs_mode_lat = FLD_GET(r, 15, 0); + enter_hs_mode_lat = FLD_GET(r, 31, 16); + + r = dsi_read_reg(dsidev, DSI_CLK_CTRL); + lp_clk_div = FLD_GET(r, 12, 0); + ddr_alwon = FLD_GET(r, 13, 13); + + r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0); + ths_exit = FLD_GET(r, 7, 0); + + r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1); + tclk_trail = FLD_GET(r, 15, 8); + + exiths_clk = ths_exit + tclk_trail; + + width_bytes = DIV_ROUND_UP(timings->x_res * bpp, 8); + bllp = hbp + hfp + hsa + DIV_ROUND_UP(width_bytes + 6, ndl); + + if (!hsa_blanking_mode) { + hsa_interleave_hs = dsi_compute_interleave_hs(hsa, ddr_alwon, + enter_hs_mode_lat, exit_hs_mode_lat, + exiths_clk, ddr_clk_pre, ddr_clk_post); + hsa_interleave_lp = dsi_compute_interleave_lp(hsa, + enter_hs_mode_lat, exit_hs_mode_lat, + lp_clk_div, dsi_fclk_hsdiv); + } + + if (!hfp_blanking_mode) { + hfp_interleave_hs = dsi_compute_interleave_hs(hfp, ddr_alwon, + enter_hs_mode_lat, exit_hs_mode_lat, + exiths_clk, ddr_clk_pre, ddr_clk_post); + hfp_interleave_lp = dsi_compute_interleave_lp(hfp, + enter_hs_mode_lat, exit_hs_mode_lat, + lp_clk_div, dsi_fclk_hsdiv); + } + + if (!hbp_blanking_mode) { + hbp_interleave_hs = dsi_compute_interleave_hs(hbp, ddr_alwon, + enter_hs_mode_lat, exit_hs_mode_lat, + exiths_clk, ddr_clk_pre, ddr_clk_post); + + hbp_interleave_lp = dsi_compute_interleave_lp(hbp, + enter_hs_mode_lat, exit_hs_mode_lat, + lp_clk_div, dsi_fclk_hsdiv); + } + + if (!blanking_mode) { + bl_interleave_hs = dsi_compute_interleave_hs(bllp, ddr_alwon, + enter_hs_mode_lat, exit_hs_mode_lat, + exiths_clk, ddr_clk_pre, ddr_clk_post); + + bl_interleave_lp = dsi_compute_interleave_lp(bllp, + enter_hs_mode_lat, exit_hs_mode_lat, + lp_clk_div, dsi_fclk_hsdiv); + } + + DSSDBG("DSI HS interleaving(TXBYTECLKHS) HSA %d, HFP %d, HBP %d, BLLP %d\n", + hsa_interleave_hs, hfp_interleave_hs, hbp_interleave_hs, + bl_interleave_hs); + + DSSDBG("DSI LP interleaving(bytes) HSA %d, HFP %d, HBP %d, BLLP %d\n", + hsa_interleave_lp, hfp_interleave_lp, hbp_interleave_lp, + bl_interleave_lp); + + r = dsi_read_reg(dsidev, DSI_VM_TIMING4); + r = FLD_MOD(r, hsa_interleave_hs, 23, 16); + r = FLD_MOD(r, hfp_interleave_hs, 15, 8); + r = FLD_MOD(r, hbp_interleave_hs, 7, 0); + dsi_write_reg(dsidev, DSI_VM_TIMING4, r); + + r = dsi_read_reg(dsidev, DSI_VM_TIMING5); + r = FLD_MOD(r, hsa_interleave_lp, 23, 16); + r = FLD_MOD(r, hfp_interleave_lp, 15, 8); + r = FLD_MOD(r, hbp_interleave_lp, 7, 0); + dsi_write_reg(dsidev, DSI_VM_TIMING5, r); + + r = dsi_read_reg(dsidev, DSI_VM_TIMING6); + r = FLD_MOD(r, bl_interleave_hs, 31, 15); + r = FLD_MOD(r, bl_interleave_lp, 16, 0); + dsi_write_reg(dsidev, DSI_VM_TIMING6, r); +} + static int dsi_proto_config(struct omap_dss_device *dssdev) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); @@ -3769,6 +3884,7 @@ static int dsi_proto_config(struct omap_dss_device *dssdev) break; default: BUG(); + return -EINVAL; } r = dsi_read_reg(dsidev, DSI_CTRL); @@ -3793,6 +3909,7 @@ static int dsi_proto_config(struct omap_dss_device *dssdev) if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) { dsi_config_vp_sync_events(dssdev); dsi_config_blanking_modes(dssdev); + dsi_config_cmd_mode_interleaving(dssdev); } dsi_vc_initial_config(dsidev, 0); @@ -4008,6 +4125,7 @@ int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel) break; default: BUG(); + return -EINVAL; }; dsi_if_enable(dsidev, false); @@ -4192,10 +4310,6 @@ static void dsi_framedone_irq_callback(void *data, u32 mask) __cancel_delayed_work(&dsi->framedone_timeout_work); dsi_handle_framedone(dsidev, 0); - -#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC - dispc_fake_vsync_irq(); -#endif } int omap_dsi_update(struct omap_dss_device *dssdev, int channel, @@ -4259,13 +4373,12 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev) dispc_mgr_enable_stallmode(dssdev->manager->id, true); dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 1); - dispc_mgr_set_lcd_timings(dssdev->manager->id, &timings); + dss_mgr_set_timings(dssdev->manager, &timings); } else { dispc_mgr_enable_stallmode(dssdev->manager->id, false); dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 0); - dispc_mgr_set_lcd_timings(dssdev->manager->id, - &dssdev->panel.timings); + dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings); } dispc_mgr_set_lcd_display_type(dssdev->manager->id, @@ -4294,13 +4407,11 @@ static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev) struct dsi_clock_info cinfo; int r; - /* we always use DSS_CLK_SYSCK as input clock */ - cinfo.use_sys_clk = true; cinfo.regn = dssdev->clocks.dsi.regn; cinfo.regm = dssdev->clocks.dsi.regm; cinfo.regm_dispc = dssdev->clocks.dsi.regm_dispc; cinfo.regm_dsi = dssdev->clocks.dsi.regm_dsi; - r = dsi_calc_clock_rates(dssdev, &cinfo); + r = dsi_calc_clock_rates(dsidev, &cinfo); if (r) { DSSERR("Failed to calc dsi clocks\n"); return r; @@ -4345,7 +4456,7 @@ static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev) static int dsi_display_init_dsi(struct omap_dss_device *dssdev) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - int dsi_module = dsi_get_dsidev_id(dsidev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int r; r = dsi_pll_init(dsidev, true, true); @@ -4357,7 +4468,7 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev) goto err1; dss_select_dispc_clk_source(dssdev->clocks.dispc.dispc_fclk_src); - dss_select_dsi_clk_source(dsi_module, dssdev->clocks.dsi.dsi_fclk_src); + dss_select_dsi_clk_source(dsi->module_id, dssdev->clocks.dsi.dsi_fclk_src); dss_select_lcd_clk_source(dssdev->manager->id, dssdev->clocks.dispc.channel.lcd_clk_src); @@ -4396,7 +4507,7 @@ err3: dsi_cio_uninit(dssdev); err2: dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK); - dss_select_dsi_clk_source(dsi_module, OMAP_DSS_CLK_SRC_FCK); + dss_select_dsi_clk_source(dsi->module_id, OMAP_DSS_CLK_SRC_FCK); dss_select_lcd_clk_source(dssdev->manager->id, OMAP_DSS_CLK_SRC_FCK); err1: @@ -4410,7 +4521,6 @@ static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev, { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - int dsi_module = dsi_get_dsidev_id(dsidev); if (enter_ulps && !dsi->ulps_enabled) dsi_enter_ulps(dsidev); @@ -4423,7 +4533,7 @@ static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev, dsi_vc_enable(dsidev, 3, 0); dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK); - dss_select_dsi_clk_source(dsi_module, OMAP_DSS_CLK_SRC_FCK); + dss_select_dsi_clk_source(dsi->module_id, OMAP_DSS_CLK_SRC_FCK); dss_select_lcd_clk_source(dssdev->manager->id, OMAP_DSS_CLK_SRC_FCK); dsi_cio_uninit(dssdev); dsi_pll_uninit(dsidev, disconnect_lanes); @@ -4527,7 +4637,7 @@ int omapdss_dsi_enable_te(struct omap_dss_device *dssdev, bool enable) } EXPORT_SYMBOL(omapdss_dsi_enable_te); -int dsi_init_display(struct omap_dss_device *dssdev) +static int __init dsi_init_display(struct omap_dss_device *dssdev) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); @@ -4680,13 +4790,39 @@ static void dsi_put_clocks(struct platform_device *dsidev) clk_put(dsi->sys_clk); } +static void __init dsi_probe_pdata(struct platform_device *dsidev) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + struct omap_dss_board_info *pdata = dsidev->dev.platform_data; + int i, r; + + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; + + if (dssdev->type != OMAP_DISPLAY_TYPE_DSI) + continue; + + if (dssdev->phy.dsi.module != dsi->module_id) + continue; + + r = dsi_init_display(dssdev); + if (r) { + DSSERR("device %s init failed: %d\n", dssdev->name, r); + continue; + } + + r = omap_dss_register_device(dssdev, &dsidev->dev, i); + if (r) + DSSERR("device %s register failed: %d\n", + dssdev->name, r); + } +} + /* DSI1 HW IP initialisation */ -static int omap_dsihw_probe(struct platform_device *dsidev) +static int __init omap_dsihw_probe(struct platform_device *dsidev) { - struct omap_display_platform_data *dss_plat_data; - struct omap_dss_board_info *board_info; u32 rev; - int r, i, dsi_module = dsi_get_dsidev_id(dsidev); + int r, i; struct resource *dsi_mem; struct dsi_data *dsi; @@ -4694,15 +4830,11 @@ static int omap_dsihw_probe(struct platform_device *dsidev) if (!dsi) return -ENOMEM; + dsi->module_id = dsidev->id; dsi->pdev = dsidev; - dsi_pdev_map[dsi_module] = dsidev; + dsi_pdev_map[dsi->module_id] = dsidev; dev_set_drvdata(&dsidev->dev, dsi); - dss_plat_data = dsidev->dev.platform_data; - board_info = dss_plat_data->board_data; - dsi->enable_pads = board_info->dsi_enable_pads; - dsi->disable_pads = board_info->dsi_disable_pads; - spin_lock_init(&dsi->irq_lock); spin_lock_init(&dsi->errors_lock); dsi->errors = 0; @@ -4780,8 +4912,21 @@ static int omap_dsihw_probe(struct platform_device *dsidev) else dsi->num_lanes_supported = 3; + dsi_probe_pdata(dsidev); + dsi_runtime_put(dsidev); + if (dsi->module_id == 0) + dss_debugfs_create_file("dsi1_regs", dsi1_dump_regs); + else if (dsi->module_id == 1) + dss_debugfs_create_file("dsi2_regs", dsi2_dump_regs); + +#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS + if (dsi->module_id == 0) + dss_debugfs_create_file("dsi1_irqs", dsi1_dump_irqs); + else if (dsi->module_id == 1) + dss_debugfs_create_file("dsi2_irqs", dsi2_dump_irqs); +#endif return 0; err_runtime_get: @@ -4790,12 +4935,14 @@ err_runtime_get: return r; } -static int omap_dsihw_remove(struct platform_device *dsidev) +static int __exit omap_dsihw_remove(struct platform_device *dsidev) { struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); WARN_ON(dsi->scp_clk_refcount > 0); + omap_dss_unregister_child_devices(&dsidev->dev); + pm_runtime_disable(&dsidev->dev); dsi_put_clocks(dsidev); @@ -4816,7 +4963,6 @@ static int omap_dsihw_remove(struct platform_device *dsidev) static int dsi_runtime_suspend(struct device *dev) { dispc_runtime_put(); - dss_runtime_put(); return 0; } @@ -4825,20 +4971,11 @@ static int dsi_runtime_resume(struct device *dev) { int r; - r = dss_runtime_get(); - if (r) - goto err_get_dss; - r = dispc_runtime_get(); if (r) - goto err_get_dispc; + return r; return 0; - -err_get_dispc: - dss_runtime_put(); -err_get_dss: - return r; } static const struct dev_pm_ops dsi_pm_ops = { @@ -4847,8 +4984,7 @@ static const struct dev_pm_ops dsi_pm_ops = { }; static struct platform_driver omap_dsihw_driver = { - .probe = omap_dsihw_probe, - .remove = omap_dsihw_remove, + .remove = __exit_p(omap_dsihw_remove), .driver = { .name = "omapdss_dsi", .owner = THIS_MODULE, @@ -4856,12 +4992,12 @@ static struct platform_driver omap_dsihw_driver = { }, }; -int dsi_init_platform_driver(void) +int __init dsi_init_platform_driver(void) { - return platform_driver_register(&omap_dsihw_driver); + return platform_driver_probe(&omap_dsihw_driver, omap_dsihw_probe); } -void dsi_uninit_platform_driver(void) +void __exit dsi_uninit_platform_driver(void) { - return platform_driver_unregister(&omap_dsihw_driver); + platform_driver_unregister(&omap_dsihw_driver); } diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index bd2d5e1..6ea1ff1 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c @@ -62,6 +62,9 @@ struct dss_reg { #define REG_FLD_MOD(idx, val, start, end) \ dss_write_reg(idx, FLD_MOD(dss_read_reg(idx), val, start, end)) +static int dss_runtime_get(void); +static void dss_runtime_put(void); + static struct { struct platform_device *pdev; void __iomem *base; @@ -277,7 +280,7 @@ void dss_dump_clocks(struct seq_file *s) dss_runtime_put(); } -void dss_dump_regs(struct seq_file *s) +static void dss_dump_regs(struct seq_file *s) { #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r)) @@ -322,6 +325,7 @@ void dss_select_dispc_clk_source(enum omap_dss_clk_source clk_src) break; default: BUG(); + return; } dss_feat_get_reg_field(FEAT_REG_DISPC_CLK_SWITCH, &start, &end); @@ -335,7 +339,7 @@ void dss_select_dsi_clk_source(int dsi_module, enum omap_dss_clk_source clk_src) { struct platform_device *dsidev; - int b; + int b, pos; switch (clk_src) { case OMAP_DSS_CLK_SRC_FCK: @@ -355,9 +359,11 @@ void dss_select_dsi_clk_source(int dsi_module, break; default: BUG(); + return; } - REG_FLD_MOD(DSS_CONTROL, b, 1, 1); /* DSI_CLK_SWITCH */ + pos = dsi_module == 0 ? 1 : 10; + REG_FLD_MOD(DSS_CONTROL, b, pos, pos); /* DSIx_CLK_SWITCH */ dss.dsi_clk_source[dsi_module] = clk_src; } @@ -389,6 +395,7 @@ void dss_select_lcd_clk_source(enum omap_channel channel, break; default: BUG(); + return; } pos = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 12; @@ -706,7 +713,7 @@ static void dss_put_clocks(void) clk_put(dss.dss_clk); } -int dss_runtime_get(void) +static int dss_runtime_get(void) { int r; @@ -717,7 +724,7 @@ int dss_runtime_get(void) return r < 0 ? r : 0; } -void dss_runtime_put(void) +static void dss_runtime_put(void) { int r; @@ -740,7 +747,7 @@ void dss_debug_dump_clocks(struct seq_file *s) #endif /* DSS HW IP initialisation */ -static int omap_dsshw_probe(struct platform_device *pdev) +static int __init omap_dsshw_probe(struct platform_device *pdev) { struct resource *dss_mem; u32 rev; @@ -785,40 +792,24 @@ static int omap_dsshw_probe(struct platform_device *pdev) dss.lcd_clk_source[0] = OMAP_DSS_CLK_SRC_FCK; dss.lcd_clk_source[1] = OMAP_DSS_CLK_SRC_FCK; - r = dpi_init(); - if (r) { - DSSERR("Failed to initialize DPI\n"); - goto err_dpi; - } - - r = sdi_init(); - if (r) { - DSSERR("Failed to initialize SDI\n"); - goto err_sdi; - } - rev = dss_read_reg(DSS_REVISION); printk(KERN_INFO "OMAP DSS rev %d.%d\n", FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); dss_runtime_put(); + dss_debugfs_create_file("dss", dss_dump_regs); + return 0; -err_sdi: - dpi_exit(); -err_dpi: - dss_runtime_put(); + err_runtime_get: pm_runtime_disable(&pdev->dev); dss_put_clocks(); return r; } -static int omap_dsshw_remove(struct platform_device *pdev) +static int __exit omap_dsshw_remove(struct platform_device *pdev) { - dpi_exit(); - sdi_exit(); - pm_runtime_disable(&pdev->dev); dss_put_clocks(); @@ -829,11 +820,24 @@ static int omap_dsshw_remove(struct platform_device *pdev) static int dss_runtime_suspend(struct device *dev) { dss_save_context(); + dss_set_min_bus_tput(dev, 0); return 0; } static int dss_runtime_resume(struct device *dev) { + int r; + /* + * Set an arbitrarily high tput request to ensure OPP100. + * What we should really do is to make a request to stay in OPP100, + * without any tput requirements, but that is not currently possible + * via the PM layer. + */ + + r = dss_set_min_bus_tput(dev, 1000000000); + if (r) + return r; + dss_restore_context(); return 0; } @@ -844,8 +848,7 @@ static const struct dev_pm_ops dss_pm_ops = { }; static struct platform_driver omap_dsshw_driver = { - .probe = omap_dsshw_probe, - .remove = omap_dsshw_remove, + .remove = __exit_p(omap_dsshw_remove), .driver = { .name = "omapdss_dss", .owner = THIS_MODULE, @@ -853,12 +856,12 @@ static struct platform_driver omap_dsshw_driver = { }, }; -int dss_init_platform_driver(void) +int __init dss_init_platform_driver(void) { - return platform_driver_register(&omap_dsshw_driver); + return platform_driver_probe(&omap_dsshw_driver, omap_dsshw_probe); } void dss_uninit_platform_driver(void) { - return platform_driver_unregister(&omap_dsshw_driver); + platform_driver_unregister(&omap_dsshw_driver); } diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index d4b3dff..dd1092c 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h @@ -150,9 +150,6 @@ struct dsi_clock_info { u16 regm_dsi; /* OMAP3: REGM4 * OMAP4: REGM5 */ u16 lp_clk_div; - - u8 highfreq; - bool use_sys_clk; }; struct seq_file; @@ -162,6 +159,16 @@ struct platform_device; struct bus_type *dss_get_bus(void); struct regulator *dss_get_vdds_dsi(void); struct regulator *dss_get_vdds_sdi(void); +int dss_get_ctx_loss_count(struct device *dev); +int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask); +void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask); +int dss_set_min_bus_tput(struct device *dev, unsigned long tput); +int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)); + +int omap_dss_register_device(struct omap_dss_device *dssdev, + struct device *parent, int disp_num); +void omap_dss_unregister_device(struct omap_dss_device *dssdev); +void omap_dss_unregister_child_devices(struct device *parent); /* apply */ void dss_apply_init(void); @@ -179,6 +186,9 @@ void dss_mgr_get_info(struct omap_overlay_manager *mgr, int dss_mgr_set_device(struct omap_overlay_manager *mgr, struct omap_dss_device *dssdev); int dss_mgr_unset_device(struct omap_overlay_manager *mgr); +void dss_mgr_set_timings(struct omap_overlay_manager *mgr, + struct omap_video_timings *timings); +const struct omap_video_timings *dss_mgr_get_timings(struct omap_overlay_manager *mgr); bool dss_ovl_is_enabled(struct omap_overlay *ovl); int dss_ovl_enable(struct omap_overlay *ovl); @@ -208,9 +218,11 @@ int dss_init_overlay_managers(struct platform_device *pdev); void dss_uninit_overlay_managers(struct platform_device *pdev); int dss_mgr_simple_check(struct omap_overlay_manager *mgr, const struct omap_overlay_manager_info *info); +int dss_mgr_check_timings(struct omap_overlay_manager *mgr, + const struct omap_video_timings *timings); int dss_mgr_check(struct omap_overlay_manager *mgr, - struct omap_dss_device *dssdev, struct omap_overlay_manager_info *info, + const struct omap_video_timings *mgr_timings, struct omap_overlay_info **overlay_infos); /* overlay */ @@ -220,22 +232,18 @@ void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr); void dss_recheck_connections(struct omap_dss_device *dssdev, bool force); int dss_ovl_simple_check(struct omap_overlay *ovl, const struct omap_overlay_info *info); -int dss_ovl_check(struct omap_overlay *ovl, - struct omap_overlay_info *info, struct omap_dss_device *dssdev); +int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info, + const struct omap_video_timings *mgr_timings); /* DSS */ -int dss_init_platform_driver(void); +int dss_init_platform_driver(void) __init; void dss_uninit_platform_driver(void); -int dss_runtime_get(void); -void dss_runtime_put(void); - void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select); enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void); const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src); void dss_dump_clocks(struct seq_file *s); -void dss_dump_regs(struct seq_file *s); #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) void dss_debug_dump_clocks(struct seq_file *s); #endif @@ -265,19 +273,8 @@ int dss_calc_clock_div(bool is_tft, unsigned long req_pck, struct dispc_clock_info *dispc_cinfo); /* SDI */ -#ifdef CONFIG_OMAP2_DSS_SDI -int sdi_init(void); -void sdi_exit(void); -int sdi_init_display(struct omap_dss_device *display); -#else -static inline int sdi_init(void) -{ - return 0; -} -static inline void sdi_exit(void) -{ -} -#endif +int sdi_init_platform_driver(void) __init; +void sdi_uninit_platform_driver(void) __exit; /* DSI */ #ifdef CONFIG_OMAP2_DSS_DSI @@ -285,19 +282,14 @@ static inline void sdi_exit(void) struct dentry; struct file_operations; -int dsi_init_platform_driver(void); -void dsi_uninit_platform_driver(void); +int dsi_init_platform_driver(void) __init; +void dsi_uninit_platform_driver(void) __exit; int dsi_runtime_get(struct platform_device *dsidev); void dsi_runtime_put(struct platform_device *dsidev); void dsi_dump_clocks(struct seq_file *s); -void dsi_create_debugfs_files_irq(struct dentry *debugfs_dir, - const struct file_operations *debug_fops); -void dsi_create_debugfs_files_reg(struct dentry *debugfs_dir, - const struct file_operations *debug_fops); -int dsi_init_display(struct omap_dss_device *display); void dsi_irq_handler(void); u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt); @@ -314,13 +306,6 @@ void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev); void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev); struct platform_device *dsi_get_dsidev_from_id(int module); #else -static inline int dsi_init_platform_driver(void) -{ - return 0; -} -static inline void dsi_uninit_platform_driver(void) -{ -} static inline int dsi_runtime_get(struct platform_device *dsidev) { return 0; @@ -377,28 +362,14 @@ static inline struct platform_device *dsi_get_dsidev_from_id(int module) #endif /* DPI */ -#ifdef CONFIG_OMAP2_DSS_DPI -int dpi_init(void); -void dpi_exit(void); -int dpi_init_display(struct omap_dss_device *dssdev); -#else -static inline int dpi_init(void) -{ - return 0; -} -static inline void dpi_exit(void) -{ -} -#endif +int dpi_init_platform_driver(void) __init; +void dpi_uninit_platform_driver(void) __exit; /* DISPC */ -int dispc_init_platform_driver(void); -void dispc_uninit_platform_driver(void); +int dispc_init_platform_driver(void) __init; +void dispc_uninit_platform_driver(void) __exit; void dispc_dump_clocks(struct seq_file *s); -void dispc_dump_irqs(struct seq_file *s); -void dispc_dump_regs(struct seq_file *s); void dispc_irq_handler(void); -void dispc_fake_vsync_irq(void); int dispc_runtime_get(void); void dispc_runtime_put(void); @@ -409,12 +380,12 @@ void dispc_disable_sidle(void); void dispc_lcd_enable_signal_polarity(bool act_high); void dispc_lcd_enable_signal(bool enable); void dispc_pck_free_enable(bool enable); -void dispc_set_digit_size(u16 width, u16 height); void dispc_enable_fifomerge(bool enable); void dispc_enable_gamma_table(bool enable); void dispc_set_loadmode(enum omap_dss_load_mode mode); -bool dispc_lcd_timings_ok(struct omap_video_timings *timings); +bool dispc_mgr_timings_ok(enum omap_channel channel, + const struct omap_video_timings *timings); unsigned long dispc_fclk_rate(void); void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck, struct dispc_clock_info *cinfo); @@ -424,15 +395,16 @@ int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high); void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane, - u32 *fifo_low, u32 *fifo_high, bool use_fifomerge); + u32 *fifo_low, u32 *fifo_high, bool use_fifomerge, + bool manual_update); int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, - bool ilace, bool replication); + bool ilace, bool replication, + const struct omap_video_timings *mgr_timings); int dispc_ovl_enable(enum omap_plane plane, bool enable); void dispc_ovl_set_channel_out(enum omap_plane plane, enum omap_channel channel); void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable); -void dispc_mgr_set_lcd_size(enum omap_channel channel, u16 width, u16 height); u32 dispc_mgr_get_vsync_irq(enum omap_channel channel); u32 dispc_mgr_get_framedone_irq(enum omap_channel channel); bool dispc_mgr_go_busy(enum omap_channel channel); @@ -445,12 +417,13 @@ void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable); void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines); void dispc_mgr_set_lcd_display_type(enum omap_channel channel, enum omap_lcd_display_type type); -void dispc_mgr_set_lcd_timings(enum omap_channel channel, +void dispc_mgr_set_timings(enum omap_channel channel, struct omap_video_timings *timings); void dispc_mgr_set_pol_freq(enum omap_channel channel, enum omap_panel_config config, u8 acbi, u8 acb); unsigned long dispc_mgr_lclk_rate(enum omap_channel channel); unsigned long dispc_mgr_pclk_rate(enum omap_channel channel); +unsigned long dispc_core_clk_rate(void); int dispc_mgr_set_clock_div(enum omap_channel channel, struct dispc_clock_info *cinfo); int dispc_mgr_get_clock_div(enum omap_channel channel, @@ -460,19 +433,10 @@ void dispc_mgr_setup(enum omap_channel channel, /* VENC */ #ifdef CONFIG_OMAP2_DSS_VENC -int venc_init_platform_driver(void); -void venc_uninit_platform_driver(void); -void venc_dump_regs(struct seq_file *s); -int venc_init_display(struct omap_dss_device *display); +int venc_init_platform_driver(void) __init; +void venc_uninit_platform_driver(void) __exit; unsigned long venc_get_pixel_clock(void); #else -static inline int venc_init_platform_driver(void) -{ - return 0; -} -static inline void venc_uninit_platform_driver(void) -{ -} static inline unsigned long venc_get_pixel_clock(void) { WARN("%s: VENC not compiled in, returning pclk as 0\n", __func__); @@ -482,23 +446,10 @@ static inline unsigned long venc_get_pixel_clock(void) /* HDMI */ #ifdef CONFIG_OMAP4_DSS_HDMI -int hdmi_init_platform_driver(void); -void hdmi_uninit_platform_driver(void); -int hdmi_init_display(struct omap_dss_device *dssdev); +int hdmi_init_platform_driver(void) __init; +void hdmi_uninit_platform_driver(void) __exit; unsigned long hdmi_get_pixel_clock(void); -void hdmi_dump_regs(struct seq_file *s); #else -static inline int hdmi_init_display(struct omap_dss_device *dssdev) -{ - return 0; -} -static inline int hdmi_init_platform_driver(void) -{ - return 0; -} -static inline void hdmi_uninit_platform_driver(void) -{ -} static inline unsigned long hdmi_get_pixel_clock(void) { WARN("%s: HDMI not compiled in, returning pclk as 0\n", __func__); @@ -514,22 +465,18 @@ int omapdss_hdmi_read_edid(u8 *buf, int len); bool omapdss_hdmi_detect(void); int hdmi_panel_init(void); void hdmi_panel_exit(void); +#ifdef CONFIG_OMAP4_DSS_HDMI_AUDIO +int hdmi_audio_enable(void); +void hdmi_audio_disable(void); +int hdmi_audio_start(void); +void hdmi_audio_stop(void); +bool hdmi_mode_has_audio(void); +int hdmi_audio_config(struct omap_dss_audio *audio); +#endif /* RFBI */ -#ifdef CONFIG_OMAP2_DSS_RFBI -int rfbi_init_platform_driver(void); -void rfbi_uninit_platform_driver(void); -void rfbi_dump_regs(struct seq_file *s); -int rfbi_init_display(struct omap_dss_device *display); -#else -static inline int rfbi_init_platform_driver(void) -{ - return 0; -} -static inline void rfbi_uninit_platform_driver(void) -{ -} -#endif +int rfbi_init_platform_driver(void) __init; +void rfbi_uninit_platform_driver(void) __exit; #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index ce14aa6..9387097 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -52,6 +52,8 @@ struct omap_dss_features { const char * const *clksrc_names; const struct dss_param_range *dss_params; + const enum omap_dss_rotation_type supported_rotation_types; + const u32 buffer_size_unit; const u32 burst_size_unit; }; @@ -311,6 +313,8 @@ static const struct dss_param_range omap2_dss_param_range[] = { * scaler cannot scale a image with width more than 768. */ [FEAT_PARAM_LINEWIDTH] = { 1, 768 }, + [FEAT_PARAM_MGR_WIDTH] = { 1, 2048 }, + [FEAT_PARAM_MGR_HEIGHT] = { 1, 2048 }, }; static const struct dss_param_range omap3_dss_param_range[] = { @@ -324,6 +328,8 @@ static const struct dss_param_range omap3_dss_param_range[] = { [FEAT_PARAM_DSIPLL_LPDIV] = { 1, (1 << 13) - 1}, [FEAT_PARAM_DOWNSCALE] = { 1, 4 }, [FEAT_PARAM_LINEWIDTH] = { 1, 1024 }, + [FEAT_PARAM_MGR_WIDTH] = { 1, 2048 }, + [FEAT_PARAM_MGR_HEIGHT] = { 1, 2048 }, }; static const struct dss_param_range omap4_dss_param_range[] = { @@ -337,6 +343,8 @@ static const struct dss_param_range omap4_dss_param_range[] = { [FEAT_PARAM_DSIPLL_LPDIV] = { 0, (1 << 13) - 1 }, [FEAT_PARAM_DOWNSCALE] = { 1, 4 }, [FEAT_PARAM_LINEWIDTH] = { 1, 2048 }, + [FEAT_PARAM_MGR_WIDTH] = { 1, 2048 }, + [FEAT_PARAM_MGR_HEIGHT] = { 1, 2048 }, }; static const enum dss_feat_id omap2_dss_feat_list[] = { @@ -399,6 +407,7 @@ static const enum dss_feat_id omap4430_es1_0_dss_feat_list[] = { FEAT_FIR_COEF_V, FEAT_ALPHA_FREE_ZORDER, FEAT_FIFO_MERGE, + FEAT_BURST_2D, }; static const enum dss_feat_id omap4430_es2_0_1_2_dss_feat_list[] = { @@ -416,6 +425,7 @@ static const enum dss_feat_id omap4430_es2_0_1_2_dss_feat_list[] = { FEAT_FIR_COEF_V, FEAT_ALPHA_FREE_ZORDER, FEAT_FIFO_MERGE, + FEAT_BURST_2D, }; static const enum dss_feat_id omap4_dss_feat_list[] = { @@ -434,6 +444,7 @@ static const enum dss_feat_id omap4_dss_feat_list[] = { FEAT_FIR_COEF_V, FEAT_ALPHA_FREE_ZORDER, FEAT_FIFO_MERGE, + FEAT_BURST_2D, }; /* OMAP2 DSS Features */ @@ -451,6 +462,7 @@ static const struct omap_dss_features omap2_dss_features = { .overlay_caps = omap2_dss_overlay_caps, .clksrc_names = omap2_dss_clk_source_names, .dss_params = omap2_dss_param_range, + .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB, .buffer_size_unit = 1, .burst_size_unit = 8, }; @@ -470,6 +482,7 @@ static const struct omap_dss_features omap3430_dss_features = { .overlay_caps = omap3430_dss_overlay_caps, .clksrc_names = omap3_dss_clk_source_names, .dss_params = omap3_dss_param_range, + .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB, .buffer_size_unit = 1, .burst_size_unit = 8, }; @@ -488,6 +501,7 @@ static const struct omap_dss_features omap3630_dss_features = { .overlay_caps = omap3630_dss_overlay_caps, .clksrc_names = omap3_dss_clk_source_names, .dss_params = omap3_dss_param_range, + .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB, .buffer_size_unit = 1, .burst_size_unit = 8, }; @@ -508,6 +522,7 @@ static const struct omap_dss_features omap4430_es1_0_dss_features = { .overlay_caps = omap4_dss_overlay_caps, .clksrc_names = omap4_dss_clk_source_names, .dss_params = omap4_dss_param_range, + .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER, .buffer_size_unit = 16, .burst_size_unit = 16, }; @@ -527,6 +542,7 @@ static const struct omap_dss_features omap4430_es2_0_1_2_dss_features = { .overlay_caps = omap4_dss_overlay_caps, .clksrc_names = omap4_dss_clk_source_names, .dss_params = omap4_dss_param_range, + .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER, .buffer_size_unit = 16, .burst_size_unit = 16, }; @@ -546,6 +562,7 @@ static const struct omap_dss_features omap4_dss_features = { .overlay_caps = omap4_dss_overlay_caps, .clksrc_names = omap4_dss_clk_source_names, .dss_params = omap4_dss_param_range, + .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER, .buffer_size_unit = 16, .burst_size_unit = 16, }; @@ -562,13 +579,17 @@ static const struct ti_hdmi_ip_ops omap4_hdmi_functions = { .pll_enable = ti_hdmi_4xxx_pll_enable, .pll_disable = ti_hdmi_4xxx_pll_disable, .video_enable = ti_hdmi_4xxx_wp_video_start, + .video_disable = ti_hdmi_4xxx_wp_video_stop, .dump_wrapper = ti_hdmi_4xxx_wp_dump, .dump_core = ti_hdmi_4xxx_core_dump, .dump_pll = ti_hdmi_4xxx_pll_dump, .dump_phy = ti_hdmi_4xxx_phy_dump, -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) .audio_enable = ti_hdmi_4xxx_wp_audio_enable, + .audio_disable = ti_hdmi_4xxx_wp_audio_disable, + .audio_start = ti_hdmi_4xxx_audio_start, + .audio_stop = ti_hdmi_4xxx_audio_stop, + .audio_config = ti_hdmi_4xxx_audio_config, #endif }; @@ -662,6 +683,11 @@ void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end) *end = omap_current_dss_features->reg_fields[id].end; } +bool dss_feat_rotation_type_supported(enum omap_dss_rotation_type rot_type) +{ + return omap_current_dss_features->supported_rotation_types & rot_type; +} + void dss_features_init(void) { if (cpu_is_omap24xx()) diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h index c332e7d..bdf469f0 100644 --- a/drivers/video/omap2/dss/dss_features.h +++ b/drivers/video/omap2/dss/dss_features.h @@ -62,6 +62,7 @@ enum dss_feat_id { FEAT_FIFO_MERGE, /* An unknown HW bug causing the normal FIFO thresholds not to work */ FEAT_OMAP3_DSI_FIFO_BUG, + FEAT_BURST_2D, }; /* DSS register field id */ @@ -91,6 +92,8 @@ enum dss_range_param { FEAT_PARAM_DSIPLL_LPDIV, FEAT_PARAM_DOWNSCALE, FEAT_PARAM_LINEWIDTH, + FEAT_PARAM_MGR_WIDTH, + FEAT_PARAM_MGR_HEIGHT, }; /* DSS Feature Functions */ @@ -108,6 +111,8 @@ const char *dss_feat_get_clk_source_name(enum omap_dss_clk_source id); u32 dss_feat_get_buffer_size_unit(void); /* in bytes */ u32 dss_feat_get_burst_size_unit(void); /* in bytes */ +bool dss_feat_rotation_type_supported(enum omap_dss_rotation_type rot_type); + bool dss_has_feature(enum dss_feat_id id); void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end); void dss_features_init(void); diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index c4b4f69..8195c71 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -33,12 +33,6 @@ #include <linux/pm_runtime.h> #include <linux/clk.h> #include <video/omapdss.h> -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) -#include <sound/soc.h> -#include <sound/pcm_params.h> -#include "ti_hdmi_4xxx_ip.h" -#endif #include "ti_hdmi.h" #include "dss.h" @@ -63,7 +57,6 @@ static struct { struct mutex lock; - struct omap_display_platform_data *pdata; struct platform_device *pdev; struct hdmi_ip_data ip_data; @@ -130,25 +123,12 @@ static int hdmi_runtime_get(void) DSSDBG("hdmi_runtime_get\n"); - /* - * HACK: Add dss_runtime_get() to ensure DSS clock domain is enabled. - * This should be removed later. - */ - r = dss_runtime_get(); - if (r < 0) - goto err_get_dss; - r = pm_runtime_get_sync(&hdmi.pdev->dev); WARN_ON(r < 0); if (r < 0) - goto err_get_hdmi; + return r; return 0; - -err_get_hdmi: - dss_runtime_put(); -err_get_dss: - return r; } static void hdmi_runtime_put(void) @@ -159,15 +139,9 @@ static void hdmi_runtime_put(void) r = pm_runtime_put_sync(&hdmi.pdev->dev); WARN_ON(r < 0); - - /* - * HACK: This is added to complement the dss_runtime_get() call in - * hdmi_runtime_get(). This should be removed later. - */ - dss_runtime_put(); } -int hdmi_init_display(struct omap_dss_device *dssdev) +static int __init hdmi_init_display(struct omap_dss_device *dssdev) { DSSDBG("init_display\n"); @@ -344,7 +318,7 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) hdmi_compute_pll(dssdev, phy, &hdmi.ip_data.pll_data); - hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 0); + hdmi.ip_data.ops->video_disable(&hdmi.ip_data); /* config the PLL and PHY hdmi_set_pll_pwrfirst */ r = hdmi.ip_data.ops->pll_enable(&hdmi.ip_data); @@ -376,10 +350,11 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) dispc_enable_gamma_table(0); /* tv size */ - dispc_set_digit_size(dssdev->panel.timings.x_res, - dssdev->panel.timings.y_res); + dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings); - hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 1); + r = hdmi.ip_data.ops->video_enable(&hdmi.ip_data); + if (r) + goto err_vid_enable; r = dss_mgr_enable(dssdev->manager); if (r) @@ -388,7 +363,8 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) return 0; err_mgr_enable: - hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 0); + hdmi.ip_data.ops->video_disable(&hdmi.ip_data); +err_vid_enable: hdmi.ip_data.ops->phy_disable(&hdmi.ip_data); hdmi.ip_data.ops->pll_disable(&hdmi.ip_data); err: @@ -400,7 +376,7 @@ static void hdmi_power_off(struct omap_dss_device *dssdev) { dss_mgr_disable(dssdev->manager); - hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 0); + hdmi.ip_data.ops->video_disable(&hdmi.ip_data); hdmi.ip_data.ops->phy_disable(&hdmi.ip_data); hdmi.ip_data.ops->pll_disable(&hdmi.ip_data); hdmi_runtime_put(); @@ -436,10 +412,12 @@ void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev) r = hdmi_power_on(dssdev); if (r) DSSERR("failed to power on device\n"); + } else { + dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings); } } -void hdmi_dump_regs(struct seq_file *s) +static void hdmi_dump_regs(struct seq_file *s) { mutex_lock(&hdmi.lock); @@ -555,248 +533,201 @@ void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev) mutex_unlock(&hdmi.lock); } -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) - -static int hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) +static int hdmi_get_clocks(struct platform_device *pdev) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - struct platform_device *pdev = to_platform_device(codec->dev); - struct hdmi_ip_data *ip_data = snd_soc_codec_get_drvdata(codec); - int err = 0; + struct clk *clk; - if (!(ip_data->ops) && !(ip_data->ops->audio_enable)) { - dev_err(&pdev->dev, "Cannot enable/disable audio\n"); - return -ENODEV; + clk = clk_get(&pdev->dev, "sys_clk"); + if (IS_ERR(clk)) { + DSSERR("can't get sys_clk\n"); + return PTR_ERR(clk); } - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ip_data->ops->audio_enable(ip_data, true); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ip_data->ops->audio_enable(ip_data, false); - break; - default: - err = -EINVAL; - } - return err; -} - -static int hdmi_audio_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - struct hdmi_ip_data *ip_data = snd_soc_codec_get_drvdata(codec); - struct hdmi_audio_format audio_format; - struct hdmi_audio_dma audio_dma; - struct hdmi_core_audio_config core_cfg; - struct hdmi_core_infoframe_audio aud_if_cfg; - int err, n, cts; - enum hdmi_core_audio_sample_freq sample_freq; - - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - core_cfg.i2s_cfg.word_max_length = - HDMI_AUDIO_I2S_MAX_WORD_20BITS; - core_cfg.i2s_cfg.word_length = HDMI_AUDIO_I2S_CHST_WORD_16_BITS; - core_cfg.i2s_cfg.in_length_bits = - HDMI_AUDIO_I2S_INPUT_LENGTH_16; - core_cfg.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_LEFT; - audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_TWOSAMPLES; - audio_format.sample_size = HDMI_AUDIO_SAMPLE_16BITS; - audio_format.justification = HDMI_AUDIO_JUSTIFY_LEFT; - audio_dma.transfer_size = 0x10; - break; - case SNDRV_PCM_FORMAT_S24_LE: - core_cfg.i2s_cfg.word_max_length = - HDMI_AUDIO_I2S_MAX_WORD_24BITS; - core_cfg.i2s_cfg.word_length = HDMI_AUDIO_I2S_CHST_WORD_24_BITS; - core_cfg.i2s_cfg.in_length_bits = - HDMI_AUDIO_I2S_INPUT_LENGTH_24; - audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_ONESAMPLE; - audio_format.sample_size = HDMI_AUDIO_SAMPLE_24BITS; - audio_format.justification = HDMI_AUDIO_JUSTIFY_RIGHT; - core_cfg.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_RIGHT; - audio_dma.transfer_size = 0x20; - break; - default: + hdmi.sys_clk = clk; + + return 0; +} + +static void hdmi_put_clocks(void) +{ + if (hdmi.sys_clk) + clk_put(hdmi.sys_clk); +} + +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) +int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts) +{ + u32 deep_color; + bool deep_color_correct = false; + u32 pclk = hdmi.ip_data.cfg.timings.pixel_clock; + + if (n == NULL || cts == NULL) return -EINVAL; - } - switch (params_rate(params)) { + /* TODO: When implemented, query deep color mode here. */ + deep_color = 100; + + /* + * When using deep color, the default N value (as in the HDMI + * specification) yields to an non-integer CTS. Hence, we + * modify it while keeping the restrictions described in + * section 7.2.1 of the HDMI 1.4a specification. + */ + switch (sample_freq) { case 32000: - sample_freq = HDMI_AUDIO_FS_32000; + case 48000: + case 96000: + case 192000: + if (deep_color == 125) + if (pclk == 27027 || pclk == 74250) + deep_color_correct = true; + if (deep_color == 150) + if (pclk == 27027) + deep_color_correct = true; break; case 44100: - sample_freq = HDMI_AUDIO_FS_44100; - break; - case 48000: - sample_freq = HDMI_AUDIO_FS_48000; + case 88200: + case 176400: + if (deep_color == 125) + if (pclk == 27027) + deep_color_correct = true; break; default: return -EINVAL; } - err = hdmi_config_audio_acr(ip_data, params_rate(params), &n, &cts); - if (err < 0) - return err; - - /* Audio wrapper config */ - audio_format.stereo_channels = HDMI_AUDIO_STEREO_ONECHANNEL; - audio_format.active_chnnls_msk = 0x03; - audio_format.type = HDMI_AUDIO_TYPE_LPCM; - audio_format.sample_order = HDMI_AUDIO_SAMPLE_LEFT_FIRST; - /* Disable start/stop signals of IEC 60958 blocks */ - audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_OFF; + if (deep_color_correct) { + switch (sample_freq) { + case 32000: + *n = 8192; + break; + case 44100: + *n = 12544; + break; + case 48000: + *n = 8192; + break; + case 88200: + *n = 25088; + break; + case 96000: + *n = 16384; + break; + case 176400: + *n = 50176; + break; + case 192000: + *n = 32768; + break; + default: + return -EINVAL; + } + } else { + switch (sample_freq) { + case 32000: + *n = 4096; + break; + case 44100: + *n = 6272; + break; + case 48000: + *n = 6144; + break; + case 88200: + *n = 12544; + break; + case 96000: + *n = 12288; + break; + case 176400: + *n = 25088; + break; + case 192000: + *n = 24576; + break; + default: + return -EINVAL; + } + } + /* Calculate CTS. See HDMI 1.3a or 1.4a specifications */ + *cts = pclk * (*n / 128) * deep_color / (sample_freq / 10); - audio_dma.block_size = 0xC0; - audio_dma.mode = HDMI_AUDIO_TRANSF_DMA; - audio_dma.fifo_threshold = 0x20; /* in number of samples */ + return 0; +} - hdmi_wp_audio_config_dma(ip_data, &audio_dma); - hdmi_wp_audio_config_format(ip_data, &audio_format); +int hdmi_audio_enable(void) +{ + DSSDBG("audio_enable\n"); - /* - * I2S config - */ - core_cfg.i2s_cfg.en_high_bitrate_aud = false; - /* Only used with high bitrate audio */ - core_cfg.i2s_cfg.cbit_order = false; - /* Serial data and word select should change on sck rising edge */ - core_cfg.i2s_cfg.sck_edge_mode = HDMI_AUDIO_I2S_SCK_EDGE_RISING; - core_cfg.i2s_cfg.vbit = HDMI_AUDIO_I2S_VBIT_FOR_PCM; - /* Set I2S word select polarity */ - core_cfg.i2s_cfg.ws_polarity = HDMI_AUDIO_I2S_WS_POLARITY_LOW_IS_LEFT; - core_cfg.i2s_cfg.direction = HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST; - /* Set serial data to word select shift. See Phillips spec. */ - core_cfg.i2s_cfg.shift = HDMI_AUDIO_I2S_FIRST_BIT_SHIFT; - /* Enable one of the four available serial data channels */ - core_cfg.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN; - - /* Core audio config */ - core_cfg.freq_sample = sample_freq; - core_cfg.n = n; - core_cfg.cts = cts; - if (dss_has_feature(FEAT_HDMI_CTS_SWMODE)) { - core_cfg.aud_par_busclk = 0; - core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_SW; - core_cfg.use_mclk = dss_has_feature(FEAT_HDMI_AUDIO_USE_MCLK); - } else { - core_cfg.aud_par_busclk = (((128 * 31) - 1) << 8); - core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_HW; - core_cfg.use_mclk = true; - } + return hdmi.ip_data.ops->audio_enable(&hdmi.ip_data); +} - if (core_cfg.use_mclk) - core_cfg.mclk_mode = HDMI_AUDIO_MCLK_128FS; - core_cfg.layout = HDMI_AUDIO_LAYOUT_2CH; - core_cfg.en_spdif = false; - /* Use sample frequency from channel status word */ - core_cfg.fs_override = true; - /* Enable ACR packets */ - core_cfg.en_acr_pkt = true; - /* Disable direct streaming digital audio */ - core_cfg.en_dsd_audio = false; - /* Use parallel audio interface */ - core_cfg.en_parallel_aud_input = true; - - hdmi_core_audio_config(ip_data, &core_cfg); +void hdmi_audio_disable(void) +{ + DSSDBG("audio_disable\n"); - /* - * Configure packet - * info frame audio see doc CEA861-D page 74 - */ - aud_if_cfg.db1_coding_type = HDMI_INFOFRAME_AUDIO_DB1CT_FROM_STREAM; - aud_if_cfg.db1_channel_count = 2; - aud_if_cfg.db2_sample_freq = HDMI_INFOFRAME_AUDIO_DB2SF_FROM_STREAM; - aud_if_cfg.db2_sample_size = HDMI_INFOFRAME_AUDIO_DB2SS_FROM_STREAM; - aud_if_cfg.db4_channel_alloc = 0x00; - aud_if_cfg.db5_downmix_inh = false; - aud_if_cfg.db5_lsv = 0; - - hdmi_core_audio_infoframe_config(ip_data, &aud_if_cfg); - return 0; + hdmi.ip_data.ops->audio_disable(&hdmi.ip_data); } -static int hdmi_audio_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +int hdmi_audio_start(void) { - if (!hdmi.ip_data.cfg.cm.mode) { - pr_err("Current video settings do not support audio.\n"); - return -EIO; - } - return 0; + DSSDBG("audio_start\n"); + + return hdmi.ip_data.ops->audio_start(&hdmi.ip_data); } -static int hdmi_audio_codec_probe(struct snd_soc_codec *codec) +void hdmi_audio_stop(void) { - struct hdmi_ip_data *priv = &hdmi.ip_data; + DSSDBG("audio_stop\n"); - snd_soc_codec_set_drvdata(codec, priv); - return 0; + hdmi.ip_data.ops->audio_stop(&hdmi.ip_data); } -static struct snd_soc_codec_driver hdmi_audio_codec_drv = { - .probe = hdmi_audio_codec_probe, -}; +bool hdmi_mode_has_audio(void) +{ + if (hdmi.ip_data.cfg.cm.mode == HDMI_HDMI) + return true; + else + return false; +} -static struct snd_soc_dai_ops hdmi_audio_codec_ops = { - .hw_params = hdmi_audio_hw_params, - .trigger = hdmi_audio_trigger, - .startup = hdmi_audio_startup, -}; +int hdmi_audio_config(struct omap_dss_audio *audio) +{ + return hdmi.ip_data.ops->audio_config(&hdmi.ip_data, audio); +} -static struct snd_soc_dai_driver hdmi_codec_dai_drv = { - .name = "hdmi-audio-codec", - .playback = { - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - }, - .ops = &hdmi_audio_codec_ops, -}; #endif -static int hdmi_get_clocks(struct platform_device *pdev) +static void __init hdmi_probe_pdata(struct platform_device *pdev) { - struct clk *clk; + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int r, i; - clk = clk_get(&pdev->dev, "sys_clk"); - if (IS_ERR(clk)) { - DSSERR("can't get sys_clk\n"); - return PTR_ERR(clk); - } + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; - hdmi.sys_clk = clk; + if (dssdev->type != OMAP_DISPLAY_TYPE_HDMI) + continue; - return 0; -} + r = hdmi_init_display(dssdev); + if (r) { + DSSERR("device %s init failed: %d\n", dssdev->name, r); + continue; + } -static void hdmi_put_clocks(void) -{ - if (hdmi.sys_clk) - clk_put(hdmi.sys_clk); + r = omap_dss_register_device(dssdev, &pdev->dev, i); + if (r) + DSSERR("device %s register failed: %d\n", + dssdev->name, r); + } } /* HDMI HW IP initialisation */ -static int omapdss_hdmihw_probe(struct platform_device *pdev) +static int __init omapdss_hdmihw_probe(struct platform_device *pdev) { struct resource *hdmi_mem; int r; - hdmi.pdata = pdev->dev.platform_data; hdmi.pdev = pdev; mutex_init(&hdmi.lock); @@ -830,28 +761,18 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) hdmi_panel_init(); -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) + dss_debugfs_create_file("hdmi", hdmi_dump_regs); + + hdmi_probe_pdata(pdev); - /* Register ASoC codec DAI */ - r = snd_soc_register_codec(&pdev->dev, &hdmi_audio_codec_drv, - &hdmi_codec_dai_drv, 1); - if (r) { - DSSERR("can't register ASoC HDMI audio codec\n"); - return r; - } -#endif return 0; } -static int omapdss_hdmihw_remove(struct platform_device *pdev) +static int __exit omapdss_hdmihw_remove(struct platform_device *pdev) { - hdmi_panel_exit(); + omap_dss_unregister_child_devices(&pdev->dev); -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) - snd_soc_unregister_codec(&pdev->dev); -#endif + hdmi_panel_exit(); pm_runtime_disable(&pdev->dev); @@ -867,7 +788,6 @@ static int hdmi_runtime_suspend(struct device *dev) clk_disable(hdmi.sys_clk); dispc_runtime_put(); - dss_runtime_put(); return 0; } @@ -876,23 +796,13 @@ static int hdmi_runtime_resume(struct device *dev) { int r; - r = dss_runtime_get(); - if (r < 0) - goto err_get_dss; - r = dispc_runtime_get(); if (r < 0) - goto err_get_dispc; - + return r; clk_enable(hdmi.sys_clk); return 0; - -err_get_dispc: - dss_runtime_put(); -err_get_dss: - return r; } static const struct dev_pm_ops hdmi_pm_ops = { @@ -901,8 +811,7 @@ static const struct dev_pm_ops hdmi_pm_ops = { }; static struct platform_driver omapdss_hdmihw_driver = { - .probe = omapdss_hdmihw_probe, - .remove = omapdss_hdmihw_remove, + .remove = __exit_p(omapdss_hdmihw_remove), .driver = { .name = "omapdss_hdmi", .owner = THIS_MODULE, @@ -910,12 +819,12 @@ static struct platform_driver omapdss_hdmihw_driver = { }, }; -int hdmi_init_platform_driver(void) +int __init hdmi_init_platform_driver(void) { - return platform_driver_register(&omapdss_hdmihw_driver); + return platform_driver_probe(&omapdss_hdmihw_driver, omapdss_hdmihw_probe); } -void hdmi_uninit_platform_driver(void) +void __exit hdmi_uninit_platform_driver(void) { - return platform_driver_unregister(&omapdss_hdmihw_driver); + platform_driver_unregister(&omapdss_hdmihw_driver); } diff --git a/drivers/video/omap2/dss/hdmi_panel.c b/drivers/video/omap2/dss/hdmi_panel.c index 533d5dc..1179e3c 100644 --- a/drivers/video/omap2/dss/hdmi_panel.c +++ b/drivers/video/omap2/dss/hdmi_panel.c @@ -30,7 +30,12 @@ #include "dss.h" static struct { - struct mutex hdmi_lock; + /* This protects the panel ops, mainly when accessing the HDMI IP. */ + struct mutex lock; +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) + /* This protects the audio ops, specifically. */ + spinlock_t audio_lock; +#endif } hdmi; @@ -54,12 +59,168 @@ static void hdmi_panel_remove(struct omap_dss_device *dssdev) } +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) +static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev) +{ + unsigned long flags; + int r; + + mutex_lock(&hdmi.lock); + spin_lock_irqsave(&hdmi.audio_lock, flags); + + /* enable audio only if the display is active and supports audio */ + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || + !hdmi_mode_has_audio()) { + DSSERR("audio not supported or display is off\n"); + r = -EPERM; + goto err; + } + + r = hdmi_audio_enable(); + + if (!r) + dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED; + +err: + spin_unlock_irqrestore(&hdmi.audio_lock, flags); + mutex_unlock(&hdmi.lock); + return r; +} + +static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev) +{ + unsigned long flags; + + spin_lock_irqsave(&hdmi.audio_lock, flags); + + hdmi_audio_disable(); + + dssdev->audio_state = OMAP_DSS_AUDIO_DISABLED; + + spin_unlock_irqrestore(&hdmi.audio_lock, flags); +} + +static int hdmi_panel_audio_start(struct omap_dss_device *dssdev) +{ + unsigned long flags; + int r; + + spin_lock_irqsave(&hdmi.audio_lock, flags); + /* + * No need to check the panel state. It was checked when trasitioning + * to AUDIO_ENABLED. + */ + if (dssdev->audio_state != OMAP_DSS_AUDIO_ENABLED) { + DSSERR("audio start from invalid state\n"); + r = -EPERM; + goto err; + } + + r = hdmi_audio_start(); + + if (!r) + dssdev->audio_state = OMAP_DSS_AUDIO_PLAYING; + +err: + spin_unlock_irqrestore(&hdmi.audio_lock, flags); + return r; +} + +static void hdmi_panel_audio_stop(struct omap_dss_device *dssdev) +{ + unsigned long flags; + + spin_lock_irqsave(&hdmi.audio_lock, flags); + + hdmi_audio_stop(); + dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED; + + spin_unlock_irqrestore(&hdmi.audio_lock, flags); +} + +static bool hdmi_panel_audio_supported(struct omap_dss_device *dssdev) +{ + bool r = false; + + mutex_lock(&hdmi.lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + goto err; + + if (!hdmi_mode_has_audio()) + goto err; + + r = true; +err: + mutex_unlock(&hdmi.lock); + return r; +} + +static int hdmi_panel_audio_config(struct omap_dss_device *dssdev, + struct omap_dss_audio *audio) +{ + unsigned long flags; + int r; + + mutex_lock(&hdmi.lock); + spin_lock_irqsave(&hdmi.audio_lock, flags); + + /* config audio only if the display is active and supports audio */ + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || + !hdmi_mode_has_audio()) { + DSSERR("audio not supported or display is off\n"); + r = -EPERM; + goto err; + } + + r = hdmi_audio_config(audio); + + if (!r) + dssdev->audio_state = OMAP_DSS_AUDIO_CONFIGURED; + +err: + spin_unlock_irqrestore(&hdmi.audio_lock, flags); + mutex_unlock(&hdmi.lock); + return r; +} + +#else +static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev) +{ + return -EPERM; +} + +static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev) +{ +} + +static int hdmi_panel_audio_start(struct omap_dss_device *dssdev) +{ + return -EPERM; +} + +static void hdmi_panel_audio_stop(struct omap_dss_device *dssdev) +{ +} + +static bool hdmi_panel_audio_supported(struct omap_dss_device *dssdev) +{ + return false; +} + +static int hdmi_panel_audio_config(struct omap_dss_device *dssdev, + struct omap_dss_audio *audio) +{ + return -EPERM; +} +#endif + static int hdmi_panel_enable(struct omap_dss_device *dssdev) { int r = 0; DSSDBG("ENTER hdmi_panel_enable\n"); - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { r = -EINVAL; @@ -75,40 +236,52 @@ static int hdmi_panel_enable(struct omap_dss_device *dssdev) dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; err: - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); return r; } static void hdmi_panel_disable(struct omap_dss_device *dssdev) { - mutex_lock(&hdmi.hdmi_lock); - - if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + mutex_lock(&hdmi.lock); + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + /* + * TODO: notify audio users that the display was disabled. For + * now, disable audio locally to not break our audio state + * machine. + */ + hdmi_panel_audio_disable(dssdev); omapdss_hdmi_display_disable(dssdev); + } dssdev->state = OMAP_DSS_DISPLAY_DISABLED; - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); } static int hdmi_panel_suspend(struct omap_dss_device *dssdev) { int r = 0; - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { r = -EINVAL; goto err; } - dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + /* + * TODO: notify audio users that the display was suspended. For now, + * disable audio locally to not break our audio state machine. + */ + hdmi_panel_audio_disable(dssdev); + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; omapdss_hdmi_display_disable(dssdev); err: - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); return r; } @@ -117,7 +290,7 @@ static int hdmi_panel_resume(struct omap_dss_device *dssdev) { int r = 0; - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) { r = -EINVAL; @@ -129,11 +302,12 @@ static int hdmi_panel_resume(struct omap_dss_device *dssdev) DSSERR("failed to power on\n"); goto err; } + /* TODO: notify audio users that the panel resumed. */ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; err: - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); return r; } @@ -141,11 +315,11 @@ err: static void hdmi_get_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); *timings = dssdev->panel.timings; - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); } static void hdmi_set_timings(struct omap_dss_device *dssdev, @@ -153,12 +327,18 @@ static void hdmi_set_timings(struct omap_dss_device *dssdev, { DSSDBG("hdmi_set_timings\n"); - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); + + /* + * TODO: notify audio users that there was a timings change. For + * now, disable audio locally to not break our audio state machine. + */ + hdmi_panel_audio_disable(dssdev); dssdev->panel.timings = *timings; omapdss_hdmi_display_set_timing(dssdev); - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); } static int hdmi_check_timings(struct omap_dss_device *dssdev, @@ -168,11 +348,11 @@ static int hdmi_check_timings(struct omap_dss_device *dssdev, DSSDBG("hdmi_check_timings\n"); - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); r = omapdss_hdmi_display_check_timing(dssdev, timings); - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); return r; } @@ -180,7 +360,7 @@ static int hdmi_read_edid(struct omap_dss_device *dssdev, u8 *buf, int len) { int r; - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { r = omapdss_hdmi_display_enable(dssdev); @@ -194,7 +374,7 @@ static int hdmi_read_edid(struct omap_dss_device *dssdev, u8 *buf, int len) dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) omapdss_hdmi_display_disable(dssdev); err: - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); return r; } @@ -203,7 +383,7 @@ static bool hdmi_detect(struct omap_dss_device *dssdev) { int r; - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { r = omapdss_hdmi_display_enable(dssdev); @@ -217,7 +397,7 @@ static bool hdmi_detect(struct omap_dss_device *dssdev) dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) omapdss_hdmi_display_disable(dssdev); err: - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); return r; } @@ -234,6 +414,12 @@ static struct omap_dss_driver hdmi_driver = { .check_timings = hdmi_check_timings, .read_edid = hdmi_read_edid, .detect = hdmi_detect, + .audio_enable = hdmi_panel_audio_enable, + .audio_disable = hdmi_panel_audio_disable, + .audio_start = hdmi_panel_audio_start, + .audio_stop = hdmi_panel_audio_stop, + .audio_supported = hdmi_panel_audio_supported, + .audio_config = hdmi_panel_audio_config, .driver = { .name = "hdmi_panel", .owner = THIS_MODULE, @@ -242,7 +428,11 @@ static struct omap_dss_driver hdmi_driver = { int hdmi_panel_init(void) { - mutex_init(&hdmi.hdmi_lock); + mutex_init(&hdmi.lock); + +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) + spin_lock_init(&hdmi.audio_lock); +#endif omap_dss_register_driver(&hdmi_driver); diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c index e736460..0cbcde4 100644 --- a/drivers/video/omap2/dss/manager.c +++ b/drivers/video/omap2/dss/manager.c @@ -654,9 +654,20 @@ static int dss_mgr_check_zorder(struct omap_overlay_manager *mgr, return 0; } +int dss_mgr_check_timings(struct omap_overlay_manager *mgr, + const struct omap_video_timings *timings) +{ + if (!dispc_mgr_timings_ok(mgr->id, timings)) { + DSSERR("check_manager: invalid timings\n"); + return -EINVAL; + } + + return 0; +} + int dss_mgr_check(struct omap_overlay_manager *mgr, - struct omap_dss_device *dssdev, struct omap_overlay_manager_info *info, + const struct omap_video_timings *mgr_timings, struct omap_overlay_info **overlay_infos) { struct omap_overlay *ovl; @@ -668,6 +679,10 @@ int dss_mgr_check(struct omap_overlay_manager *mgr, return r; } + r = dss_mgr_check_timings(mgr, mgr_timings); + if (r) + return r; + list_for_each_entry(ovl, &mgr->overlays, list) { struct omap_overlay_info *oi; int r; @@ -677,7 +692,7 @@ int dss_mgr_check(struct omap_overlay_manager *mgr, if (oi == NULL) continue; - r = dss_ovl_check(ovl, oi, dssdev); + r = dss_ovl_check(ovl, oi, mgr_timings); if (r) return r; } diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c index 6e82181..b0ba60f 100644 --- a/drivers/video/omap2/dss/overlay.c +++ b/drivers/video/omap2/dss/overlay.c @@ -628,19 +628,23 @@ int dss_ovl_simple_check(struct omap_overlay *ovl, return -EINVAL; } + if (dss_feat_rotation_type_supported(info->rotation_type) == 0) { + DSSERR("check_overlay: rotation type %d not supported\n", + info->rotation_type); + return -EINVAL; + } + return 0; } -int dss_ovl_check(struct omap_overlay *ovl, - struct omap_overlay_info *info, struct omap_dss_device *dssdev) +int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info, + const struct omap_video_timings *mgr_timings) { u16 outw, outh; u16 dw, dh; - if (dssdev == NULL) - return 0; - - dssdev->driver->get_resolution(dssdev, &dw, &dh); + dw = mgr_timings->x_res; + dh = mgr_timings->y_res; if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { outw = info->width; diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c index 788a0ef..3d8c206 100644 --- a/drivers/video/omap2/dss/rfbi.c +++ b/drivers/video/omap2/dss/rfbi.c @@ -304,13 +304,23 @@ static void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width, u16 height, void (*callback)(void *data), void *data) { u32 l; + struct omap_video_timings timings = { + .hsw = 1, + .hfp = 1, + .hbp = 1, + .vsw = 1, + .vfp = 0, + .vbp = 0, + .x_res = width, + .y_res = height, + }; /*BUG_ON(callback == 0);*/ BUG_ON(rfbi.framedone_callback != NULL); DSSDBG("rfbi_transfer_area %dx%d\n", width, height); - dispc_mgr_set_lcd_size(dssdev->manager->id, width, height); + dss_mgr_set_timings(dssdev->manager, &timings); dispc_mgr_enable(dssdev->manager->id, true); @@ -766,6 +776,16 @@ int omap_rfbi_prepare_update(struct omap_dss_device *dssdev, u16 *x, u16 *y, u16 *w, u16 *h) { u16 dw, dh; + struct omap_video_timings timings = { + .hsw = 1, + .hfp = 1, + .hbp = 1, + .vsw = 1, + .vfp = 0, + .vbp = 0, + .x_res = *w, + .y_res = *h, + }; dssdev->driver->get_resolution(dssdev, &dw, &dh); @@ -784,7 +804,7 @@ int omap_rfbi_prepare_update(struct omap_dss_device *dssdev, if (*w == 0 || *h == 0) return -EINVAL; - dispc_mgr_set_lcd_size(dssdev->manager->id, *w, *h); + dss_mgr_set_timings(dssdev->manager, &timings); return 0; } @@ -799,7 +819,7 @@ int omap_rfbi_update(struct omap_dss_device *dssdev, } EXPORT_SYMBOL(omap_rfbi_update); -void rfbi_dump_regs(struct seq_file *s) +static void rfbi_dump_regs(struct seq_file *s) { #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, rfbi_read_reg(r)) @@ -900,15 +920,39 @@ void omapdss_rfbi_display_disable(struct omap_dss_device *dssdev) } EXPORT_SYMBOL(omapdss_rfbi_display_disable); -int rfbi_init_display(struct omap_dss_device *dssdev) +static int __init rfbi_init_display(struct omap_dss_device *dssdev) { rfbi.dssdev[dssdev->phy.rfbi.channel] = dssdev; dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; return 0; } +static void __init rfbi_probe_pdata(struct platform_device *pdev) +{ + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int i, r; + + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; + + if (dssdev->type != OMAP_DISPLAY_TYPE_DBI) + continue; + + r = rfbi_init_display(dssdev); + if (r) { + DSSERR("device %s init failed: %d\n", dssdev->name, r); + continue; + } + + r = omap_dss_register_device(dssdev, &pdev->dev, i); + if (r) + DSSERR("device %s register failed: %d\n", + dssdev->name, r); + } +} + /* RFBI HW IP initialisation */ -static int omap_rfbihw_probe(struct platform_device *pdev) +static int __init omap_rfbihw_probe(struct platform_device *pdev) { u32 rev; struct resource *rfbi_mem; @@ -956,6 +1000,10 @@ static int omap_rfbihw_probe(struct platform_device *pdev) rfbi_runtime_put(); + dss_debugfs_create_file("rfbi", rfbi_dump_regs); + + rfbi_probe_pdata(pdev); + return 0; err_runtime_get: @@ -963,8 +1011,9 @@ err_runtime_get: return r; } -static int omap_rfbihw_remove(struct platform_device *pdev) +static int __exit omap_rfbihw_remove(struct platform_device *pdev) { + omap_dss_unregister_child_devices(&pdev->dev); pm_runtime_disable(&pdev->dev); return 0; } @@ -972,7 +1021,6 @@ static int omap_rfbihw_remove(struct platform_device *pdev) static int rfbi_runtime_suspend(struct device *dev) { dispc_runtime_put(); - dss_runtime_put(); return 0; } @@ -981,20 +1029,11 @@ static int rfbi_runtime_resume(struct device *dev) { int r; - r = dss_runtime_get(); - if (r < 0) - goto err_get_dss; - r = dispc_runtime_get(); if (r < 0) - goto err_get_dispc; + return r; return 0; - -err_get_dispc: - dss_runtime_put(); -err_get_dss: - return r; } static const struct dev_pm_ops rfbi_pm_ops = { @@ -1003,8 +1042,7 @@ static const struct dev_pm_ops rfbi_pm_ops = { }; static struct platform_driver omap_rfbihw_driver = { - .probe = omap_rfbihw_probe, - .remove = omap_rfbihw_remove, + .remove = __exit_p(omap_rfbihw_remove), .driver = { .name = "omapdss_rfbi", .owner = THIS_MODULE, @@ -1012,12 +1050,12 @@ static struct platform_driver omap_rfbihw_driver = { }, }; -int rfbi_init_platform_driver(void) +int __init rfbi_init_platform_driver(void) { - return platform_driver_register(&omap_rfbihw_driver); + return platform_driver_probe(&omap_rfbihw_driver, omap_rfbihw_probe); } -void rfbi_uninit_platform_driver(void) +void __exit rfbi_uninit_platform_driver(void) { - return platform_driver_unregister(&omap_rfbihw_driver); + platform_driver_unregister(&omap_rfbihw_driver); } diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c index 8266ca0..3a43dc2 100644 --- a/drivers/video/omap2/dss/sdi.c +++ b/drivers/video/omap2/dss/sdi.c @@ -24,6 +24,7 @@ #include <linux/err.h> #include <linux/regulator/consumer.h> #include <linux/export.h> +#include <linux/platform_device.h> #include <video/omapdss.h> #include "dss.h" @@ -71,10 +72,6 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) if (r) goto err_reg_enable; - r = dss_runtime_get(); - if (r) - goto err_get_dss; - r = dispc_runtime_get(); if (r) goto err_get_dispc; @@ -107,7 +104,7 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) } - dispc_mgr_set_lcd_timings(dssdev->manager->id, t); + dss_mgr_set_timings(dssdev->manager, t); r = dss_set_clock_div(&dss_cinfo); if (r) @@ -137,8 +134,6 @@ err_set_dss_clock_div: err_calc_clock_div: dispc_runtime_put(); err_get_dispc: - dss_runtime_put(); -err_get_dss: regulator_disable(sdi.vdds_sdi_reg); err_reg_enable: omap_dss_stop_device(dssdev); @@ -154,7 +149,6 @@ void omapdss_sdi_display_disable(struct omap_dss_device *dssdev) dss_sdi_disable(); dispc_runtime_put(); - dss_runtime_put(); regulator_disable(sdi.vdds_sdi_reg); @@ -162,7 +156,7 @@ void omapdss_sdi_display_disable(struct omap_dss_device *dssdev) } EXPORT_SYMBOL(omapdss_sdi_display_disable); -int sdi_init_display(struct omap_dss_device *dssdev) +static int __init sdi_init_display(struct omap_dss_device *dssdev) { DSSDBG("SDI init\n"); @@ -182,11 +176,58 @@ int sdi_init_display(struct omap_dss_device *dssdev) return 0; } -int sdi_init(void) +static void __init sdi_probe_pdata(struct platform_device *pdev) +{ + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int i, r; + + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; + + if (dssdev->type != OMAP_DISPLAY_TYPE_SDI) + continue; + + r = sdi_init_display(dssdev); + if (r) { + DSSERR("device %s init failed: %d\n", dssdev->name, r); + continue; + } + + r = omap_dss_register_device(dssdev, &pdev->dev, i); + if (r) + DSSERR("device %s register failed: %d\n", + dssdev->name, r); + } +} + +static int __init omap_sdi_probe(struct platform_device *pdev) { + sdi_probe_pdata(pdev); + + return 0; +} + +static int __exit omap_sdi_remove(struct platform_device *pdev) +{ + omap_dss_unregister_child_devices(&pdev->dev); + return 0; } -void sdi_exit(void) +static struct platform_driver omap_sdi_driver = { + .remove = __exit_p(omap_sdi_remove), + .driver = { + .name = "omapdss_sdi", + .owner = THIS_MODULE, + }, +}; + +int __init sdi_init_platform_driver(void) +{ + return platform_driver_probe(&omap_sdi_driver, omap_sdi_probe); +} + +void __exit sdi_uninit_platform_driver(void) { + platform_driver_unregister(&omap_sdi_driver); } diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h index 1f58b84..e734cb4 100644 --- a/drivers/video/omap2/dss/ti_hdmi.h +++ b/drivers/video/omap2/dss/ti_hdmi.h @@ -96,7 +96,9 @@ struct ti_hdmi_ip_ops { void (*pll_disable)(struct hdmi_ip_data *ip_data); - void (*video_enable)(struct hdmi_ip_data *ip_data, bool start); + int (*video_enable)(struct hdmi_ip_data *ip_data); + + void (*video_disable)(struct hdmi_ip_data *ip_data); void (*dump_wrapper)(struct hdmi_ip_data *ip_data, struct seq_file *s); @@ -106,9 +108,17 @@ struct ti_hdmi_ip_ops { void (*dump_phy)(struct hdmi_ip_data *ip_data, struct seq_file *s); -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) - void (*audio_enable)(struct hdmi_ip_data *ip_data, bool start); +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) + int (*audio_enable)(struct hdmi_ip_data *ip_data); + + void (*audio_disable)(struct hdmi_ip_data *ip_data); + + int (*audio_start)(struct hdmi_ip_data *ip_data); + + void (*audio_stop)(struct hdmi_ip_data *ip_data); + + int (*audio_config)(struct hdmi_ip_data *ip_data, + struct omap_dss_audio *audio); #endif }; @@ -173,7 +183,8 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data); int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data, u8 *edid, int len); bool ti_hdmi_4xxx_detect(struct hdmi_ip_data *ip_data); -void ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data, bool start); +int ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data); +void ti_hdmi_4xxx_wp_video_stop(struct hdmi_ip_data *ip_data); int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data); @@ -181,8 +192,13 @@ void ti_hdmi_4xxx_wp_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) -void ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data, bool enable); +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) +int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts); +int ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data); +void ti_hdmi_4xxx_wp_audio_disable(struct hdmi_ip_data *ip_data); +int ti_hdmi_4xxx_audio_start(struct hdmi_ip_data *ip_data); +void ti_hdmi_4xxx_audio_stop(struct hdmi_ip_data *ip_data); +int ti_hdmi_4xxx_audio_config(struct hdmi_ip_data *ip_data, + struct omap_dss_audio *audio); #endif #endif diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index bfe6fe6..4dae1b2 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -29,9 +29,14 @@ #include <linux/string.h> #include <linux/seq_file.h> #include <linux/gpio.h> +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) +#include <sound/asound.h> +#include <sound/asoundef.h> +#endif #include "ti_hdmi_4xxx_ip.h" #include "dss.h" +#include "dss_features.h" static inline void hdmi_write_reg(void __iomem *base_addr, const u16 idx, u32 val) @@ -298,9 +303,9 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data) REG_FLD_MOD(phy_base, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27); r = request_threaded_irq(gpio_to_irq(ip_data->hpd_gpio), - NULL, hpd_irq_handler, - IRQF_DISABLED | IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING, "hpd", ip_data); + NULL, hpd_irq_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, "hpd", ip_data); if (r) { DSSERR("HPD IRQ request failed\n"); hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF); @@ -699,9 +704,15 @@ static void hdmi_wp_init(struct omap_video_timings *timings, } -void ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data, bool start) +int ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data) +{ + REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, true, 31, 31); + return 0; +} + +void ti_hdmi_4xxx_wp_video_stop(struct hdmi_ip_data *ip_data) { - REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, start, 31, 31); + REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, false, 31, 31); } static void hdmi_wp_video_init_format(struct hdmi_video_format *video_fmt, @@ -886,10 +897,12 @@ void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) #define CORE_REG(i, name) name(i) #define DUMPCORE(r) seq_printf(s, "%-35s %08x\n", #r,\ - hdmi_read_reg(hdmi_pll_base(ip_data), r)) -#define DUMPCOREAV(i, r) seq_printf(s, "%s[%d]%*s %08x\n", #r, i, \ + hdmi_read_reg(hdmi_core_sys_base(ip_data), r)) +#define DUMPCOREAV(r) seq_printf(s, "%-35s %08x\n", #r,\ + hdmi_read_reg(hdmi_av_base(ip_data), r)) +#define DUMPCOREAV2(i, r) seq_printf(s, "%s[%d]%*s %08x\n", #r, i, \ (i < 10) ? 32 - strlen(#r) : 31 - strlen(#r), " ", \ - hdmi_read_reg(hdmi_pll_base(ip_data), CORE_REG(i, r))) + hdmi_read_reg(hdmi_av_base(ip_data), CORE_REG(i, r))) DUMPCORE(HDMI_CORE_SYS_VND_IDL); DUMPCORE(HDMI_CORE_SYS_DEV_IDL); @@ -898,6 +911,13 @@ void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) DUMPCORE(HDMI_CORE_SYS_SRST); DUMPCORE(HDMI_CORE_CTRL1); DUMPCORE(HDMI_CORE_SYS_SYS_STAT); + DUMPCORE(HDMI_CORE_SYS_DE_DLY); + DUMPCORE(HDMI_CORE_SYS_DE_CTRL); + DUMPCORE(HDMI_CORE_SYS_DE_TOP); + DUMPCORE(HDMI_CORE_SYS_DE_CNTL); + DUMPCORE(HDMI_CORE_SYS_DE_CNTH); + DUMPCORE(HDMI_CORE_SYS_DE_LINL); + DUMPCORE(HDMI_CORE_SYS_DE_LINH_1); DUMPCORE(HDMI_CORE_SYS_VID_ACEN); DUMPCORE(HDMI_CORE_SYS_VID_MODE); DUMPCORE(HDMI_CORE_SYS_INTR_STATE); @@ -907,102 +927,91 @@ void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) DUMPCORE(HDMI_CORE_SYS_INTR4); DUMPCORE(HDMI_CORE_SYS_UMASK1); DUMPCORE(HDMI_CORE_SYS_TMDS_CTRL); - DUMPCORE(HDMI_CORE_SYS_DE_DLY); - DUMPCORE(HDMI_CORE_SYS_DE_CTRL); - DUMPCORE(HDMI_CORE_SYS_DE_TOP); - DUMPCORE(HDMI_CORE_SYS_DE_CNTL); - DUMPCORE(HDMI_CORE_SYS_DE_CNTH); - DUMPCORE(HDMI_CORE_SYS_DE_LINL); - DUMPCORE(HDMI_CORE_SYS_DE_LINH_1); - DUMPCORE(HDMI_CORE_DDC_CMD); - DUMPCORE(HDMI_CORE_DDC_STATUS); DUMPCORE(HDMI_CORE_DDC_ADDR); + DUMPCORE(HDMI_CORE_DDC_SEGM); DUMPCORE(HDMI_CORE_DDC_OFFSET); DUMPCORE(HDMI_CORE_DDC_COUNT1); DUMPCORE(HDMI_CORE_DDC_COUNT2); + DUMPCORE(HDMI_CORE_DDC_STATUS); + DUMPCORE(HDMI_CORE_DDC_CMD); DUMPCORE(HDMI_CORE_DDC_DATA); - DUMPCORE(HDMI_CORE_DDC_SEGM); - DUMPCORE(HDMI_CORE_AV_HDMI_CTRL); - DUMPCORE(HDMI_CORE_AV_DPD); - DUMPCORE(HDMI_CORE_AV_PB_CTRL1); - DUMPCORE(HDMI_CORE_AV_PB_CTRL2); - DUMPCORE(HDMI_CORE_AV_AVI_TYPE); - DUMPCORE(HDMI_CORE_AV_AVI_VERS); - DUMPCORE(HDMI_CORE_AV_AVI_LEN); - DUMPCORE(HDMI_CORE_AV_AVI_CHSUM); + DUMPCOREAV(HDMI_CORE_AV_ACR_CTRL); + DUMPCOREAV(HDMI_CORE_AV_FREQ_SVAL); + DUMPCOREAV(HDMI_CORE_AV_N_SVAL1); + DUMPCOREAV(HDMI_CORE_AV_N_SVAL2); + DUMPCOREAV(HDMI_CORE_AV_N_SVAL3); + DUMPCOREAV(HDMI_CORE_AV_CTS_SVAL1); + DUMPCOREAV(HDMI_CORE_AV_CTS_SVAL2); + DUMPCOREAV(HDMI_CORE_AV_CTS_SVAL3); + DUMPCOREAV(HDMI_CORE_AV_CTS_HVAL1); + DUMPCOREAV(HDMI_CORE_AV_CTS_HVAL2); + DUMPCOREAV(HDMI_CORE_AV_CTS_HVAL3); + DUMPCOREAV(HDMI_CORE_AV_AUD_MODE); + DUMPCOREAV(HDMI_CORE_AV_SPDIF_CTRL); + DUMPCOREAV(HDMI_CORE_AV_HW_SPDIF_FS); + DUMPCOREAV(HDMI_CORE_AV_SWAP_I2S); + DUMPCOREAV(HDMI_CORE_AV_SPDIF_ERTH); + DUMPCOREAV(HDMI_CORE_AV_I2S_IN_MAP); + DUMPCOREAV(HDMI_CORE_AV_I2S_IN_CTRL); + DUMPCOREAV(HDMI_CORE_AV_I2S_CHST0); + DUMPCOREAV(HDMI_CORE_AV_I2S_CHST1); + DUMPCOREAV(HDMI_CORE_AV_I2S_CHST2); + DUMPCOREAV(HDMI_CORE_AV_I2S_CHST4); + DUMPCOREAV(HDMI_CORE_AV_I2S_CHST5); + DUMPCOREAV(HDMI_CORE_AV_ASRC); + DUMPCOREAV(HDMI_CORE_AV_I2S_IN_LEN); + DUMPCOREAV(HDMI_CORE_AV_HDMI_CTRL); + DUMPCOREAV(HDMI_CORE_AV_AUDO_TXSTAT); + DUMPCOREAV(HDMI_CORE_AV_AUD_PAR_BUSCLK_1); + DUMPCOREAV(HDMI_CORE_AV_AUD_PAR_BUSCLK_2); + DUMPCOREAV(HDMI_CORE_AV_AUD_PAR_BUSCLK_3); + DUMPCOREAV(HDMI_CORE_AV_TEST_TXCTRL); + DUMPCOREAV(HDMI_CORE_AV_DPD); + DUMPCOREAV(HDMI_CORE_AV_PB_CTRL1); + DUMPCOREAV(HDMI_CORE_AV_PB_CTRL2); + DUMPCOREAV(HDMI_CORE_AV_AVI_TYPE); + DUMPCOREAV(HDMI_CORE_AV_AVI_VERS); + DUMPCOREAV(HDMI_CORE_AV_AVI_LEN); + DUMPCOREAV(HDMI_CORE_AV_AVI_CHSUM); for (i = 0; i < HDMI_CORE_AV_AVI_DBYTE_NELEMS; i++) - DUMPCOREAV(i, HDMI_CORE_AV_AVI_DBYTE); + DUMPCOREAV2(i, HDMI_CORE_AV_AVI_DBYTE); + + DUMPCOREAV(HDMI_CORE_AV_SPD_TYPE); + DUMPCOREAV(HDMI_CORE_AV_SPD_VERS); + DUMPCOREAV(HDMI_CORE_AV_SPD_LEN); + DUMPCOREAV(HDMI_CORE_AV_SPD_CHSUM); for (i = 0; i < HDMI_CORE_AV_SPD_DBYTE_NELEMS; i++) - DUMPCOREAV(i, HDMI_CORE_AV_SPD_DBYTE); + DUMPCOREAV2(i, HDMI_CORE_AV_SPD_DBYTE); + + DUMPCOREAV(HDMI_CORE_AV_AUDIO_TYPE); + DUMPCOREAV(HDMI_CORE_AV_AUDIO_VERS); + DUMPCOREAV(HDMI_CORE_AV_AUDIO_LEN); + DUMPCOREAV(HDMI_CORE_AV_AUDIO_CHSUM); for (i = 0; i < HDMI_CORE_AV_AUD_DBYTE_NELEMS; i++) - DUMPCOREAV(i, HDMI_CORE_AV_AUD_DBYTE); + DUMPCOREAV2(i, HDMI_CORE_AV_AUD_DBYTE); + + DUMPCOREAV(HDMI_CORE_AV_MPEG_TYPE); + DUMPCOREAV(HDMI_CORE_AV_MPEG_VERS); + DUMPCOREAV(HDMI_CORE_AV_MPEG_LEN); + DUMPCOREAV(HDMI_CORE_AV_MPEG_CHSUM); for (i = 0; i < HDMI_CORE_AV_MPEG_DBYTE_NELEMS; i++) - DUMPCOREAV(i, HDMI_CORE_AV_MPEG_DBYTE); + DUMPCOREAV2(i, HDMI_CORE_AV_MPEG_DBYTE); for (i = 0; i < HDMI_CORE_AV_GEN_DBYTE_NELEMS; i++) - DUMPCOREAV(i, HDMI_CORE_AV_GEN_DBYTE); + DUMPCOREAV2(i, HDMI_CORE_AV_GEN_DBYTE); + + DUMPCOREAV(HDMI_CORE_AV_CP_BYTE1); for (i = 0; i < HDMI_CORE_AV_GEN2_DBYTE_NELEMS; i++) - DUMPCOREAV(i, HDMI_CORE_AV_GEN2_DBYTE); - - DUMPCORE(HDMI_CORE_AV_ACR_CTRL); - DUMPCORE(HDMI_CORE_AV_FREQ_SVAL); - DUMPCORE(HDMI_CORE_AV_N_SVAL1); - DUMPCORE(HDMI_CORE_AV_N_SVAL2); - DUMPCORE(HDMI_CORE_AV_N_SVAL3); - DUMPCORE(HDMI_CORE_AV_CTS_SVAL1); - DUMPCORE(HDMI_CORE_AV_CTS_SVAL2); - DUMPCORE(HDMI_CORE_AV_CTS_SVAL3); - DUMPCORE(HDMI_CORE_AV_CTS_HVAL1); - DUMPCORE(HDMI_CORE_AV_CTS_HVAL2); - DUMPCORE(HDMI_CORE_AV_CTS_HVAL3); - DUMPCORE(HDMI_CORE_AV_AUD_MODE); - DUMPCORE(HDMI_CORE_AV_SPDIF_CTRL); - DUMPCORE(HDMI_CORE_AV_HW_SPDIF_FS); - DUMPCORE(HDMI_CORE_AV_SWAP_I2S); - DUMPCORE(HDMI_CORE_AV_SPDIF_ERTH); - DUMPCORE(HDMI_CORE_AV_I2S_IN_MAP); - DUMPCORE(HDMI_CORE_AV_I2S_IN_CTRL); - DUMPCORE(HDMI_CORE_AV_I2S_CHST0); - DUMPCORE(HDMI_CORE_AV_I2S_CHST1); - DUMPCORE(HDMI_CORE_AV_I2S_CHST2); - DUMPCORE(HDMI_CORE_AV_I2S_CHST4); - DUMPCORE(HDMI_CORE_AV_I2S_CHST5); - DUMPCORE(HDMI_CORE_AV_ASRC); - DUMPCORE(HDMI_CORE_AV_I2S_IN_LEN); - DUMPCORE(HDMI_CORE_AV_HDMI_CTRL); - DUMPCORE(HDMI_CORE_AV_AUDO_TXSTAT); - DUMPCORE(HDMI_CORE_AV_AUD_PAR_BUSCLK_1); - DUMPCORE(HDMI_CORE_AV_AUD_PAR_BUSCLK_2); - DUMPCORE(HDMI_CORE_AV_AUD_PAR_BUSCLK_3); - DUMPCORE(HDMI_CORE_AV_TEST_TXCTRL); - DUMPCORE(HDMI_CORE_AV_DPD); - DUMPCORE(HDMI_CORE_AV_PB_CTRL1); - DUMPCORE(HDMI_CORE_AV_PB_CTRL2); - DUMPCORE(HDMI_CORE_AV_AVI_TYPE); - DUMPCORE(HDMI_CORE_AV_AVI_VERS); - DUMPCORE(HDMI_CORE_AV_AVI_LEN); - DUMPCORE(HDMI_CORE_AV_AVI_CHSUM); - DUMPCORE(HDMI_CORE_AV_SPD_TYPE); - DUMPCORE(HDMI_CORE_AV_SPD_VERS); - DUMPCORE(HDMI_CORE_AV_SPD_LEN); - DUMPCORE(HDMI_CORE_AV_SPD_CHSUM); - DUMPCORE(HDMI_CORE_AV_AUDIO_TYPE); - DUMPCORE(HDMI_CORE_AV_AUDIO_VERS); - DUMPCORE(HDMI_CORE_AV_AUDIO_LEN); - DUMPCORE(HDMI_CORE_AV_AUDIO_CHSUM); - DUMPCORE(HDMI_CORE_AV_MPEG_TYPE); - DUMPCORE(HDMI_CORE_AV_MPEG_VERS); - DUMPCORE(HDMI_CORE_AV_MPEG_LEN); - DUMPCORE(HDMI_CORE_AV_MPEG_CHSUM); - DUMPCORE(HDMI_CORE_AV_CP_BYTE1); - DUMPCORE(HDMI_CORE_AV_CEC_ADDR_ID); + DUMPCOREAV2(i, HDMI_CORE_AV_GEN2_DBYTE); + + DUMPCOREAV(HDMI_CORE_AV_CEC_ADDR_ID); } void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) @@ -1016,9 +1025,8 @@ void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL); } -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) -void hdmi_wp_audio_config_format(struct hdmi_ip_data *ip_data, +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) +static void ti_hdmi_4xxx_wp_audio_config_format(struct hdmi_ip_data *ip_data, struct hdmi_audio_format *aud_fmt) { u32 r; @@ -1037,7 +1045,7 @@ void hdmi_wp_audio_config_format(struct hdmi_ip_data *ip_data, hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG, r); } -void hdmi_wp_audio_config_dma(struct hdmi_ip_data *ip_data, +static void ti_hdmi_4xxx_wp_audio_config_dma(struct hdmi_ip_data *ip_data, struct hdmi_audio_dma *aud_dma) { u32 r; @@ -1055,7 +1063,7 @@ void hdmi_wp_audio_config_dma(struct hdmi_ip_data *ip_data, hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CTRL, r); } -void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, +static void ti_hdmi_4xxx_core_audio_config(struct hdmi_ip_data *ip_data, struct hdmi_core_audio_config *cfg) { u32 r; @@ -1106,27 +1114,33 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, REG_FLD_MOD(av_base, HDMI_CORE_AV_SPDIF_CTRL, cfg->fs_override, 1, 1); - /* I2S parameters */ - REG_FLD_MOD(av_base, HDMI_CORE_AV_I2S_CHST4, - cfg->freq_sample, 3, 0); - + /* + * Set IEC-60958-3 channel status word. It is passed to the IP + * just as it is received. The user of the driver is responsible + * for its contents. + */ + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST0, + cfg->iec60958_cfg->status[0]); + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST1, + cfg->iec60958_cfg->status[1]); + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST2, + cfg->iec60958_cfg->status[2]); + /* yes, this is correct: status[3] goes to CHST4 register */ + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST4, + cfg->iec60958_cfg->status[3]); + /* yes, this is correct: status[4] goes to CHST5 register */ + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST5, + cfg->iec60958_cfg->status[4]); + + /* set I2S parameters */ r = hdmi_read_reg(av_base, HDMI_CORE_AV_I2S_IN_CTRL); - r = FLD_MOD(r, cfg->i2s_cfg.en_high_bitrate_aud, 7, 7); r = FLD_MOD(r, cfg->i2s_cfg.sck_edge_mode, 6, 6); - r = FLD_MOD(r, cfg->i2s_cfg.cbit_order, 5, 5); r = FLD_MOD(r, cfg->i2s_cfg.vbit, 4, 4); - r = FLD_MOD(r, cfg->i2s_cfg.ws_polarity, 3, 3); r = FLD_MOD(r, cfg->i2s_cfg.justification, 2, 2); r = FLD_MOD(r, cfg->i2s_cfg.direction, 1, 1); r = FLD_MOD(r, cfg->i2s_cfg.shift, 0, 0); hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_IN_CTRL, r); - r = hdmi_read_reg(av_base, HDMI_CORE_AV_I2S_CHST5); - r = FLD_MOD(r, cfg->freq_sample, 7, 4); - r = FLD_MOD(r, cfg->i2s_cfg.word_length, 3, 1); - r = FLD_MOD(r, cfg->i2s_cfg.word_max_length, 0, 0); - hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST5, r); - REG_FLD_MOD(av_base, HDMI_CORE_AV_I2S_IN_LEN, cfg->i2s_cfg.in_length_bits, 3, 0); @@ -1138,12 +1152,19 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, r = FLD_MOD(r, cfg->en_parallel_aud_input, 2, 2); r = FLD_MOD(r, cfg->en_spdif, 1, 1); hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_MODE, r); + + /* Audio channel mappings */ + /* TODO: Make channel mapping dynamic. For now, map channels + * in the ALSA order: FL/FR/RL/RR/C/LFE/SL/SR. Remapping is needed as + * HDMI speaker order is different. See CEA-861 Section 6.6.2. + */ + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_IN_MAP, 0x78); + REG_FLD_MOD(av_base, HDMI_CORE_AV_SWAP_I2S, 1, 5, 5); } -void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, - struct hdmi_core_infoframe_audio *info_aud) +static void ti_hdmi_4xxx_core_audio_infoframe_cfg(struct hdmi_ip_data *ip_data, + struct snd_cea_861_aud_if *info_aud) { - u8 val; u8 sum = 0, checksum = 0; void __iomem *av_base = hdmi_av_base(ip_data); @@ -1157,24 +1178,23 @@ void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, hdmi_write_reg(av_base, HDMI_CORE_AV_AUDIO_LEN, 0x0a); sum += 0x84 + 0x001 + 0x00a; - val = (info_aud->db1_coding_type << 4) - | (info_aud->db1_channel_count - 1); - hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(0), val); - sum += val; + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(0), + info_aud->db1_ct_cc); + sum += info_aud->db1_ct_cc; - val = (info_aud->db2_sample_freq << 2) | info_aud->db2_sample_size; - hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(1), val); - sum += val; + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(1), + info_aud->db2_sf_ss); + sum += info_aud->db2_sf_ss; - hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(2), 0x00); + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(2), info_aud->db3); + sum += info_aud->db3; - val = info_aud->db4_channel_alloc; - hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(3), val); - sum += val; + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(3), info_aud->db4_ca); + sum += info_aud->db4_ca; - val = (info_aud->db5_downmix_inh << 7) | (info_aud->db5_lsv << 3); - hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(4), val); - sum += val; + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(4), + info_aud->db5_dminh_lsv); + sum += info_aud->db5_dminh_lsv; hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(5), 0x00); hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(6), 0x00); @@ -1192,70 +1212,212 @@ void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, */ } -int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data, - u32 sample_freq, u32 *n, u32 *cts) +int ti_hdmi_4xxx_audio_config(struct hdmi_ip_data *ip_data, + struct omap_dss_audio *audio) { - u32 r; - u32 deep_color = 0; - u32 pclk = ip_data->cfg.timings.pixel_clock; - - if (n == NULL || cts == NULL) + struct hdmi_audio_format audio_format; + struct hdmi_audio_dma audio_dma; + struct hdmi_core_audio_config core; + int err, n, cts, channel_count; + unsigned int fs_nr; + bool word_length_16b = false; + + if (!audio || !audio->iec || !audio->cea || !ip_data) return -EINVAL; + + core.iec60958_cfg = audio->iec; /* - * Obtain current deep color configuration. This needed - * to calculate the TMDS clock based on the pixel clock. + * In the IEC-60958 status word, check if the audio sample word length + * is 16-bit as several optimizations can be performed in such case. */ - r = REG_GET(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, 1, 0); - switch (r) { - case 1: /* No deep color selected */ - deep_color = 100; + if (!(audio->iec->status[4] & IEC958_AES4_CON_MAX_WORDLEN_24)) + if (audio->iec->status[4] & IEC958_AES4_CON_WORDLEN_20_16) + word_length_16b = true; + + /* I2S configuration. See Phillips' specification */ + if (word_length_16b) + core.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_LEFT; + else + core.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_RIGHT; + /* + * The I2S input word length is twice the lenght given in the IEC-60958 + * status word. If the word size is greater than + * 20 bits, increment by one. + */ + core.i2s_cfg.in_length_bits = audio->iec->status[4] + & IEC958_AES4_CON_WORDLEN; + if (audio->iec->status[4] & IEC958_AES4_CON_MAX_WORDLEN_24) + core.i2s_cfg.in_length_bits++; + core.i2s_cfg.sck_edge_mode = HDMI_AUDIO_I2S_SCK_EDGE_RISING; + core.i2s_cfg.vbit = HDMI_AUDIO_I2S_VBIT_FOR_PCM; + core.i2s_cfg.direction = HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST; + core.i2s_cfg.shift = HDMI_AUDIO_I2S_FIRST_BIT_SHIFT; + + /* convert sample frequency to a number */ + switch (audio->iec->status[3] & IEC958_AES3_CON_FS) { + case IEC958_AES3_CON_FS_32000: + fs_nr = 32000; + break; + case IEC958_AES3_CON_FS_44100: + fs_nr = 44100; + break; + case IEC958_AES3_CON_FS_48000: + fs_nr = 48000; break; - case 2: /* 10-bit deep color selected */ - deep_color = 125; + case IEC958_AES3_CON_FS_88200: + fs_nr = 88200; break; - case 3: /* 12-bit deep color selected */ - deep_color = 150; + case IEC958_AES3_CON_FS_96000: + fs_nr = 96000; + break; + case IEC958_AES3_CON_FS_176400: + fs_nr = 176400; + break; + case IEC958_AES3_CON_FS_192000: + fs_nr = 192000; break; default: return -EINVAL; } - switch (sample_freq) { - case 32000: - if ((deep_color == 125) && ((pclk == 54054) - || (pclk == 74250))) - *n = 8192; - else - *n = 4096; + err = hdmi_compute_acr(fs_nr, &n, &cts); + + /* Audio clock regeneration settings */ + core.n = n; + core.cts = cts; + if (dss_has_feature(FEAT_HDMI_CTS_SWMODE)) { + core.aud_par_busclk = 0; + core.cts_mode = HDMI_AUDIO_CTS_MODE_SW; + core.use_mclk = dss_has_feature(FEAT_HDMI_AUDIO_USE_MCLK); + } else { + core.aud_par_busclk = (((128 * 31) - 1) << 8); + core.cts_mode = HDMI_AUDIO_CTS_MODE_HW; + core.use_mclk = true; + } + + if (core.use_mclk) + core.mclk_mode = HDMI_AUDIO_MCLK_128FS; + + /* Audio channels settings */ + channel_count = (audio->cea->db1_ct_cc & + CEA861_AUDIO_INFOFRAME_DB1CC) + 1; + + switch (channel_count) { + case 2: + audio_format.active_chnnls_msk = 0x03; + break; + case 3: + audio_format.active_chnnls_msk = 0x07; + break; + case 4: + audio_format.active_chnnls_msk = 0x0f; + break; + case 5: + audio_format.active_chnnls_msk = 0x1f; break; - case 44100: - *n = 6272; + case 6: + audio_format.active_chnnls_msk = 0x3f; break; - case 48000: - if ((deep_color == 125) && ((pclk == 54054) - || (pclk == 74250))) - *n = 8192; - else - *n = 6144; + case 7: + audio_format.active_chnnls_msk = 0x7f; + break; + case 8: + audio_format.active_chnnls_msk = 0xff; break; default: - *n = 0; return -EINVAL; } - /* Calculate CTS. See HDMI 1.3a or 1.4a specifications */ - *cts = pclk * (*n / 128) * deep_color / (sample_freq / 10); + /* + * the HDMI IP needs to enable four stereo channels when transmitting + * more than 2 audio channels + */ + if (channel_count == 2) { + audio_format.stereo_channels = HDMI_AUDIO_STEREO_ONECHANNEL; + core.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN; + core.layout = HDMI_AUDIO_LAYOUT_2CH; + } else { + audio_format.stereo_channels = HDMI_AUDIO_STEREO_FOURCHANNELS; + core.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN | + HDMI_AUDIO_I2S_SD1_EN | HDMI_AUDIO_I2S_SD2_EN | + HDMI_AUDIO_I2S_SD3_EN; + core.layout = HDMI_AUDIO_LAYOUT_8CH; + } + + core.en_spdif = false; + /* use sample frequency from channel status word */ + core.fs_override = true; + /* enable ACR packets */ + core.en_acr_pkt = true; + /* disable direct streaming digital audio */ + core.en_dsd_audio = false; + /* use parallel audio interface */ + core.en_parallel_aud_input = true; + + /* DMA settings */ + if (word_length_16b) + audio_dma.transfer_size = 0x10; + else + audio_dma.transfer_size = 0x20; + audio_dma.block_size = 0xC0; + audio_dma.mode = HDMI_AUDIO_TRANSF_DMA; + audio_dma.fifo_threshold = 0x20; /* in number of samples */ + + /* audio FIFO format settings */ + if (word_length_16b) { + audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_TWOSAMPLES; + audio_format.sample_size = HDMI_AUDIO_SAMPLE_16BITS; + audio_format.justification = HDMI_AUDIO_JUSTIFY_LEFT; + } else { + audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_ONESAMPLE; + audio_format.sample_size = HDMI_AUDIO_SAMPLE_24BITS; + audio_format.justification = HDMI_AUDIO_JUSTIFY_RIGHT; + } + audio_format.type = HDMI_AUDIO_TYPE_LPCM; + audio_format.sample_order = HDMI_AUDIO_SAMPLE_LEFT_FIRST; + /* disable start/stop signals of IEC 60958 blocks */ + audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_ON; + + /* configure DMA and audio FIFO format*/ + ti_hdmi_4xxx_wp_audio_config_dma(ip_data, &audio_dma); + ti_hdmi_4xxx_wp_audio_config_format(ip_data, &audio_format); + + /* configure the core*/ + ti_hdmi_4xxx_core_audio_config(ip_data, &core); + + /* configure CEA 861 audio infoframe*/ + ti_hdmi_4xxx_core_audio_infoframe_cfg(ip_data, audio->cea); return 0; } -void ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data, bool enable) +int ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data) +{ + REG_FLD_MOD(hdmi_wp_base(ip_data), + HDMI_WP_AUDIO_CTRL, true, 31, 31); + return 0; +} + +void ti_hdmi_4xxx_wp_audio_disable(struct hdmi_ip_data *ip_data) +{ + REG_FLD_MOD(hdmi_wp_base(ip_data), + HDMI_WP_AUDIO_CTRL, false, 31, 31); +} + +int ti_hdmi_4xxx_audio_start(struct hdmi_ip_data *ip_data) { REG_FLD_MOD(hdmi_av_base(ip_data), - HDMI_CORE_AV_AUD_MODE, enable, 0, 0); + HDMI_CORE_AV_AUD_MODE, true, 0, 0); REG_FLD_MOD(hdmi_wp_base(ip_data), - HDMI_WP_AUDIO_CTRL, enable, 31, 31); + HDMI_WP_AUDIO_CTRL, true, 30, 30); + return 0; +} + +void ti_hdmi_4xxx_audio_stop(struct hdmi_ip_data *ip_data) +{ + REG_FLD_MOD(hdmi_av_base(ip_data), + HDMI_CORE_AV_AUD_MODE, false, 0, 0); REG_FLD_MOD(hdmi_wp_base(ip_data), - HDMI_WP_AUDIO_CTRL, enable, 30, 30); + HDMI_WP_AUDIO_CTRL, false, 30, 30); } #endif diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h index a14d1a0..8366ae1 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h @@ -24,11 +24,6 @@ #include <linux/string.h> #include <video/omapdss.h> #include "ti_hdmi.h" -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) -#include <sound/soc.h> -#include <sound/pcm_params.h> -#endif /* HDMI Wrapper */ @@ -57,6 +52,13 @@ #define HDMI_CORE_SYS_SRST 0x14 #define HDMI_CORE_CTRL1 0x20 #define HDMI_CORE_SYS_SYS_STAT 0x24 +#define HDMI_CORE_SYS_DE_DLY 0xC8 +#define HDMI_CORE_SYS_DE_CTRL 0xCC +#define HDMI_CORE_SYS_DE_TOP 0xD0 +#define HDMI_CORE_SYS_DE_CNTL 0xD8 +#define HDMI_CORE_SYS_DE_CNTH 0xDC +#define HDMI_CORE_SYS_DE_LINL 0xE0 +#define HDMI_CORE_SYS_DE_LINH_1 0xE4 #define HDMI_CORE_SYS_VID_ACEN 0x124 #define HDMI_CORE_SYS_VID_MODE 0x128 #define HDMI_CORE_SYS_INTR_STATE 0x1C0 @@ -66,50 +68,24 @@ #define HDMI_CORE_SYS_INTR4 0x1D0 #define HDMI_CORE_SYS_UMASK1 0x1D4 #define HDMI_CORE_SYS_TMDS_CTRL 0x208 -#define HDMI_CORE_SYS_DE_DLY 0xC8 -#define HDMI_CORE_SYS_DE_CTRL 0xCC -#define HDMI_CORE_SYS_DE_TOP 0xD0 -#define HDMI_CORE_SYS_DE_CNTL 0xD8 -#define HDMI_CORE_SYS_DE_CNTH 0xDC -#define HDMI_CORE_SYS_DE_LINL 0xE0 -#define HDMI_CORE_SYS_DE_LINH_1 0xE4 + #define HDMI_CORE_CTRL1_VEN_FOLLOWVSYNC 0x1 #define HDMI_CORE_CTRL1_HEN_FOLLOWHSYNC 0x1 -#define HDMI_CORE_CTRL1_BSEL_24BITBUS 0x1 +#define HDMI_CORE_CTRL1_BSEL_24BITBUS 0x1 #define HDMI_CORE_CTRL1_EDGE_RISINGEDGE 0x1 /* HDMI DDC E-DID */ -#define HDMI_CORE_DDC_CMD 0x3CC -#define HDMI_CORE_DDC_STATUS 0x3C8 #define HDMI_CORE_DDC_ADDR 0x3B4 +#define HDMI_CORE_DDC_SEGM 0x3B8 #define HDMI_CORE_DDC_OFFSET 0x3BC #define HDMI_CORE_DDC_COUNT1 0x3C0 #define HDMI_CORE_DDC_COUNT2 0x3C4 +#define HDMI_CORE_DDC_STATUS 0x3C8 +#define HDMI_CORE_DDC_CMD 0x3CC #define HDMI_CORE_DDC_DATA 0x3D0 -#define HDMI_CORE_DDC_SEGM 0x3B8 /* HDMI IP Core Audio Video */ -#define HDMI_CORE_AV_HDMI_CTRL 0xBC -#define HDMI_CORE_AV_DPD 0xF4 -#define HDMI_CORE_AV_PB_CTRL1 0xF8 -#define HDMI_CORE_AV_PB_CTRL2 0xFC -#define HDMI_CORE_AV_AVI_TYPE 0x100 -#define HDMI_CORE_AV_AVI_VERS 0x104 -#define HDMI_CORE_AV_AVI_LEN 0x108 -#define HDMI_CORE_AV_AVI_CHSUM 0x10C -#define HDMI_CORE_AV_AVI_DBYTE(n) (n * 4 + 0x110) -#define HDMI_CORE_AV_AVI_DBYTE_NELEMS 15 -#define HDMI_CORE_AV_SPD_DBYTE(n) (n * 4 + 0x190) -#define HDMI_CORE_AV_SPD_DBYTE_NELEMS 27 -#define HDMI_CORE_AV_AUD_DBYTE(n) (n * 4 + 0x210) -#define HDMI_CORE_AV_AUD_DBYTE_NELEMS 10 -#define HDMI_CORE_AV_MPEG_DBYTE(n) (n * 4 + 0x290) -#define HDMI_CORE_AV_MPEG_DBYTE_NELEMS 27 -#define HDMI_CORE_AV_GEN_DBYTE(n) (n * 4 + 0x300) -#define HDMI_CORE_AV_GEN_DBYTE_NELEMS 31 -#define HDMI_CORE_AV_GEN2_DBYTE(n) (n * 4 + 0x380) -#define HDMI_CORE_AV_GEN2_DBYTE_NELEMS 31 #define HDMI_CORE_AV_ACR_CTRL 0x4 #define HDMI_CORE_AV_FREQ_SVAL 0x8 #define HDMI_CORE_AV_N_SVAL1 0xC @@ -148,25 +124,39 @@ #define HDMI_CORE_AV_AVI_VERS 0x104 #define HDMI_CORE_AV_AVI_LEN 0x108 #define HDMI_CORE_AV_AVI_CHSUM 0x10C +#define HDMI_CORE_AV_AVI_DBYTE(n) (n * 4 + 0x110) #define HDMI_CORE_AV_SPD_TYPE 0x180 #define HDMI_CORE_AV_SPD_VERS 0x184 #define HDMI_CORE_AV_SPD_LEN 0x188 #define HDMI_CORE_AV_SPD_CHSUM 0x18C +#define HDMI_CORE_AV_SPD_DBYTE(n) (n * 4 + 0x190) #define HDMI_CORE_AV_AUDIO_TYPE 0x200 #define HDMI_CORE_AV_AUDIO_VERS 0x204 #define HDMI_CORE_AV_AUDIO_LEN 0x208 #define HDMI_CORE_AV_AUDIO_CHSUM 0x20C +#define HDMI_CORE_AV_AUD_DBYTE(n) (n * 4 + 0x210) #define HDMI_CORE_AV_MPEG_TYPE 0x280 #define HDMI_CORE_AV_MPEG_VERS 0x284 #define HDMI_CORE_AV_MPEG_LEN 0x288 #define HDMI_CORE_AV_MPEG_CHSUM 0x28C +#define HDMI_CORE_AV_MPEG_DBYTE(n) (n * 4 + 0x290) +#define HDMI_CORE_AV_GEN_DBYTE(n) (n * 4 + 0x300) #define HDMI_CORE_AV_CP_BYTE1 0x37C +#define HDMI_CORE_AV_GEN2_DBYTE(n) (n * 4 + 0x380) #define HDMI_CORE_AV_CEC_ADDR_ID 0x3FC + #define HDMI_CORE_AV_SPD_DBYTE_ELSIZE 0x4 #define HDMI_CORE_AV_GEN2_DBYTE_ELSIZE 0x4 #define HDMI_CORE_AV_MPEG_DBYTE_ELSIZE 0x4 #define HDMI_CORE_AV_GEN_DBYTE_ELSIZE 0x4 +#define HDMI_CORE_AV_AVI_DBYTE_NELEMS 15 +#define HDMI_CORE_AV_SPD_DBYTE_NELEMS 27 +#define HDMI_CORE_AV_AUD_DBYTE_NELEMS 10 +#define HDMI_CORE_AV_MPEG_DBYTE_NELEMS 27 +#define HDMI_CORE_AV_GEN_DBYTE_NELEMS 31 +#define HDMI_CORE_AV_GEN2_DBYTE_NELEMS 31 + /* PLL */ #define PLLCTRL_PLL_CONTROL 0x0 @@ -284,35 +274,6 @@ enum hdmi_core_infoframe { HDMI_INFOFRAME_AVI_DB5PR_8 = 7, HDMI_INFOFRAME_AVI_DB5PR_9 = 8, HDMI_INFOFRAME_AVI_DB5PR_10 = 9, - HDMI_INFOFRAME_AUDIO_DB1CT_FROM_STREAM = 0, - HDMI_INFOFRAME_AUDIO_DB1CT_IEC60958 = 1, - HDMI_INFOFRAME_AUDIO_DB1CT_AC3 = 2, - HDMI_INFOFRAME_AUDIO_DB1CT_MPEG1 = 3, - HDMI_INFOFRAME_AUDIO_DB1CT_MP3 = 4, - HDMI_INFOFRAME_AUDIO_DB1CT_MPEG2_MULTICH = 5, - HDMI_INFOFRAME_AUDIO_DB1CT_AAC = 6, - HDMI_INFOFRAME_AUDIO_DB1CT_DTS = 7, - HDMI_INFOFRAME_AUDIO_DB1CT_ATRAC = 8, - HDMI_INFOFRAME_AUDIO_DB1CT_ONEBIT = 9, - HDMI_INFOFRAME_AUDIO_DB1CT_DOLBY_DIGITAL_PLUS = 10, - HDMI_INFOFRAME_AUDIO_DB1CT_DTS_HD = 11, - HDMI_INFOFRAME_AUDIO_DB1CT_MAT = 12, - HDMI_INFOFRAME_AUDIO_DB1CT_DST = 13, - HDMI_INFOFRAME_AUDIO_DB1CT_WMA_PRO = 14, - HDMI_INFOFRAME_AUDIO_DB2SF_FROM_STREAM = 0, - HDMI_INFOFRAME_AUDIO_DB2SF_32000 = 1, - HDMI_INFOFRAME_AUDIO_DB2SF_44100 = 2, - HDMI_INFOFRAME_AUDIO_DB2SF_48000 = 3, - HDMI_INFOFRAME_AUDIO_DB2SF_88200 = 4, - HDMI_INFOFRAME_AUDIO_DB2SF_96000 = 5, - HDMI_INFOFRAME_AUDIO_DB2SF_176400 = 6, - HDMI_INFOFRAME_AUDIO_DB2SF_192000 = 7, - HDMI_INFOFRAME_AUDIO_DB2SS_FROM_STREAM = 0, - HDMI_INFOFRAME_AUDIO_DB2SS_16BIT = 1, - HDMI_INFOFRAME_AUDIO_DB2SS_20BIT = 2, - HDMI_INFOFRAME_AUDIO_DB2SS_24BIT = 3, - HDMI_INFOFRAME_AUDIO_DB5_DM_INH_PERMITTED = 0, - HDMI_INFOFRAME_AUDIO_DB5_DM_INH_PROHIBITED = 1 }; enum hdmi_packing_mode { @@ -322,17 +283,6 @@ enum hdmi_packing_mode { HDMI_PACK_ALREADYPACKED = 7 }; -enum hdmi_core_audio_sample_freq { - HDMI_AUDIO_FS_32000 = 0x3, - HDMI_AUDIO_FS_44100 = 0x0, - HDMI_AUDIO_FS_48000 = 0x2, - HDMI_AUDIO_FS_88200 = 0x8, - HDMI_AUDIO_FS_96000 = 0xA, - HDMI_AUDIO_FS_176400 = 0xC, - HDMI_AUDIO_FS_192000 = 0xE, - HDMI_AUDIO_FS_NOT_INDICATED = 0x1 -}; - enum hdmi_core_audio_layout { HDMI_AUDIO_LAYOUT_2CH = 0, HDMI_AUDIO_LAYOUT_8CH = 1 @@ -387,37 +337,12 @@ enum hdmi_audio_blk_strt_end_sig { }; enum hdmi_audio_i2s_config { - HDMI_AUDIO_I2S_WS_POLARITY_LOW_IS_LEFT = 0, - HDMI_AUDIO_I2S_WS_POLARIT_YLOW_IS_RIGHT = 1, HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST = 0, HDMI_AUDIO_I2S_LSB_SHIFTED_FIRST = 1, - HDMI_AUDIO_I2S_MAX_WORD_20BITS = 0, - HDMI_AUDIO_I2S_MAX_WORD_24BITS = 1, - HDMI_AUDIO_I2S_CHST_WORD_NOT_SPECIFIED = 0, - HDMI_AUDIO_I2S_CHST_WORD_16_BITS = 1, - HDMI_AUDIO_I2S_CHST_WORD_17_BITS = 6, - HDMI_AUDIO_I2S_CHST_WORD_18_BITS = 2, - HDMI_AUDIO_I2S_CHST_WORD_19_BITS = 4, - HDMI_AUDIO_I2S_CHST_WORD_20_BITS_20MAX = 5, - HDMI_AUDIO_I2S_CHST_WORD_20_BITS_24MAX = 1, - HDMI_AUDIO_I2S_CHST_WORD_21_BITS = 6, - HDMI_AUDIO_I2S_CHST_WORD_22_BITS = 2, - HDMI_AUDIO_I2S_CHST_WORD_23_BITS = 4, - HDMI_AUDIO_I2S_CHST_WORD_24_BITS = 5, HDMI_AUDIO_I2S_SCK_EDGE_FALLING = 0, HDMI_AUDIO_I2S_SCK_EDGE_RISING = 1, HDMI_AUDIO_I2S_VBIT_FOR_PCM = 0, HDMI_AUDIO_I2S_VBIT_FOR_COMPRESSED = 1, - HDMI_AUDIO_I2S_INPUT_LENGTH_NA = 0, - HDMI_AUDIO_I2S_INPUT_LENGTH_16 = 2, - HDMI_AUDIO_I2S_INPUT_LENGTH_17 = 12, - HDMI_AUDIO_I2S_INPUT_LENGTH_18 = 4, - HDMI_AUDIO_I2S_INPUT_LENGTH_19 = 8, - HDMI_AUDIO_I2S_INPUT_LENGTH_20 = 10, - HDMI_AUDIO_I2S_INPUT_LENGTH_21 = 13, - HDMI_AUDIO_I2S_INPUT_LENGTH_22 = 5, - HDMI_AUDIO_I2S_INPUT_LENGTH_23 = 9, - HDMI_AUDIO_I2S_INPUT_LENGTH_24 = 11, HDMI_AUDIO_I2S_FIRST_BIT_SHIFT = 0, HDMI_AUDIO_I2S_FIRST_BIT_NO_SHIFT = 1, HDMI_AUDIO_I2S_SD0_EN = 1, @@ -446,20 +371,6 @@ struct hdmi_core_video_config { enum hdmi_core_tclkselclkmult tclk_sel_clkmult; }; -/* - * Refer to section 8.2 in HDMI 1.3 specification for - * details about infoframe databytes - */ -struct hdmi_core_infoframe_audio { - u8 db1_coding_type; - u8 db1_channel_count; - u8 db2_sample_freq; - u8 db2_sample_size; - u8 db4_channel_alloc; - bool db5_downmix_inh; - u8 db5_lsv; /* Level shift values for downmix */ -}; - struct hdmi_core_packet_enable_repeat { u32 audio_pkt; u32 audio_pkt_repeat; @@ -496,15 +407,10 @@ struct hdmi_audio_dma { }; struct hdmi_core_audio_i2s_config { - u8 word_max_length; - u8 word_length; u8 in_length_bits; u8 justification; - u8 en_high_bitrate_aud; u8 sck_edge_mode; - u8 cbit_order; u8 vbit; - u8 ws_polarity; u8 direction; u8 shift; u8 active_sds; @@ -512,7 +418,7 @@ struct hdmi_core_audio_i2s_config { struct hdmi_core_audio_config { struct hdmi_core_audio_i2s_config i2s_cfg; - enum hdmi_core_audio_sample_freq freq_sample; + struct snd_aes_iec958 *iec60958_cfg; bool fs_override; u32 n; u32 cts; @@ -527,17 +433,4 @@ struct hdmi_core_audio_config { bool en_spdif; }; -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) -int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data, - u32 sample_freq, u32 *n, u32 *cts); -void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, - struct hdmi_core_infoframe_audio *info_aud); -void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, - struct hdmi_core_audio_config *cfg); -void hdmi_wp_audio_config_dma(struct hdmi_ip_data *ip_data, - struct hdmi_audio_dma *aud_dma); -void hdmi_wp_audio_config_format(struct hdmi_ip_data *ip_data, - struct hdmi_audio_format *aud_fmt); -#endif #endif diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c index 9c3daf7..2b89739 100644 --- a/drivers/video/omap2/dss/venc.c +++ b/drivers/video/omap2/dss/venc.c @@ -415,6 +415,7 @@ static const struct venc_config *venc_timings_to_config( return &venc_config_ntsc_trm; BUG(); + return NULL; } static int venc_power_on(struct omap_dss_device *dssdev) @@ -440,10 +441,11 @@ static int venc_power_on(struct omap_dss_device *dssdev) venc_write_reg(VENC_OUTPUT_CONTROL, l); - dispc_set_digit_size(dssdev->panel.timings.x_res, - dssdev->panel.timings.y_res/2); + dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings); - regulator_enable(venc.vdda_dac_reg); + r = regulator_enable(venc.vdda_dac_reg); + if (r) + goto err; if (dssdev->platform_enable) dssdev->platform_enable(dssdev); @@ -485,16 +487,68 @@ unsigned long venc_get_pixel_clock(void) return 13500000; } +static ssize_t display_output_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + const char *ret; + + switch (dssdev->phy.venc.type) { + case OMAP_DSS_VENC_TYPE_COMPOSITE: + ret = "composite"; + break; + case OMAP_DSS_VENC_TYPE_SVIDEO: + ret = "svideo"; + break; + default: + return -EINVAL; + } + + return snprintf(buf, PAGE_SIZE, "%s\n", ret); +} + +static ssize_t display_output_type_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + enum omap_dss_venc_type new_type; + + if (sysfs_streq("composite", buf)) + new_type = OMAP_DSS_VENC_TYPE_COMPOSITE; + else if (sysfs_streq("svideo", buf)) + new_type = OMAP_DSS_VENC_TYPE_SVIDEO; + else + return -EINVAL; + + mutex_lock(&venc.venc_lock); + + if (dssdev->phy.venc.type != new_type) { + dssdev->phy.venc.type = new_type; + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + venc_power_off(dssdev); + venc_power_on(dssdev); + } + } + + mutex_unlock(&venc.venc_lock); + + return size; +} + +static DEVICE_ATTR(output_type, S_IRUGO | S_IWUSR, + display_output_type_show, display_output_type_store); + /* driver */ static int venc_panel_probe(struct omap_dss_device *dssdev) { dssdev->panel.timings = omap_dss_pal_timings; - return 0; + return device_create_file(&dssdev->dev, &dev_attr_output_type); } static void venc_panel_remove(struct omap_dss_device *dssdev) { + device_remove_file(&dssdev->dev, &dev_attr_output_type); } static int venc_panel_enable(struct omap_dss_device *dssdev) @@ -577,12 +631,6 @@ static int venc_panel_resume(struct omap_dss_device *dssdev) return venc_panel_enable(dssdev); } -static void venc_get_timings(struct omap_dss_device *dssdev, - struct omap_video_timings *timings) -{ - *timings = dssdev->panel.timings; -} - static void venc_set_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { @@ -597,6 +645,8 @@ static void venc_set_timings(struct omap_dss_device *dssdev, /* turn the venc off and on to get new timings to use */ venc_panel_disable(dssdev); venc_panel_enable(dssdev); + } else { + dss_mgr_set_timings(dssdev->manager, timings); } } @@ -661,7 +711,6 @@ static struct omap_dss_driver venc_driver = { .get_resolution = omapdss_default_get_resolution, .get_recommended_bpp = omapdss_default_get_recommended_bpp, - .get_timings = venc_get_timings, .set_timings = venc_set_timings, .check_timings = venc_check_timings, @@ -675,7 +724,7 @@ static struct omap_dss_driver venc_driver = { }; /* driver end */ -int venc_init_display(struct omap_dss_device *dssdev) +static int __init venc_init_display(struct omap_dss_device *dssdev) { DSSDBG("init_display\n"); @@ -695,7 +744,7 @@ int venc_init_display(struct omap_dss_device *dssdev) return 0; } -void venc_dump_regs(struct seq_file *s) +static void venc_dump_regs(struct seq_file *s) { #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r)) @@ -779,8 +828,32 @@ static void venc_put_clocks(void) clk_put(venc.tv_dac_clk); } +static void __init venc_probe_pdata(struct platform_device *pdev) +{ + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int r, i; + + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; + + if (dssdev->type != OMAP_DISPLAY_TYPE_VENC) + continue; + + r = venc_init_display(dssdev); + if (r) { + DSSERR("device %s init failed: %d\n", dssdev->name, r); + continue; + } + + r = omap_dss_register_device(dssdev, &pdev->dev, i); + if (r) + DSSERR("device %s register failed: %d\n", + dssdev->name, r); + } +} + /* VENC HW IP initialisation */ -static int omap_venchw_probe(struct platform_device *pdev) +static int __init omap_venchw_probe(struct platform_device *pdev) { u8 rev_id; struct resource *venc_mem; @@ -824,6 +897,10 @@ static int omap_venchw_probe(struct platform_device *pdev) if (r) goto err_reg_panel_driver; + dss_debugfs_create_file("venc", venc_dump_regs); + + venc_probe_pdata(pdev); + return 0; err_reg_panel_driver: @@ -833,12 +910,15 @@ err_runtime_get: return r; } -static int omap_venchw_remove(struct platform_device *pdev) +static int __exit omap_venchw_remove(struct platform_device *pdev) { + omap_dss_unregister_child_devices(&pdev->dev); + if (venc.vdda_dac_reg != NULL) { regulator_put(venc.vdda_dac_reg); venc.vdda_dac_reg = NULL; } + omap_dss_unregister_driver(&venc_driver); pm_runtime_disable(&pdev->dev); @@ -853,7 +933,6 @@ static int venc_runtime_suspend(struct device *dev) clk_disable(venc.tv_dac_clk); dispc_runtime_put(); - dss_runtime_put(); return 0; } @@ -862,23 +941,14 @@ static int venc_runtime_resume(struct device *dev) { int r; - r = dss_runtime_get(); - if (r < 0) - goto err_get_dss; - r = dispc_runtime_get(); if (r < 0) - goto err_get_dispc; + return r; if (venc.tv_dac_clk) clk_enable(venc.tv_dac_clk); return 0; - -err_get_dispc: - dss_runtime_put(); -err_get_dss: - return r; } static const struct dev_pm_ops venc_pm_ops = { @@ -887,8 +957,7 @@ static const struct dev_pm_ops venc_pm_ops = { }; static struct platform_driver omap_venchw_driver = { - .probe = omap_venchw_probe, - .remove = omap_venchw_remove, + .remove = __exit_p(omap_venchw_remove), .driver = { .name = "omapdss_venc", .owner = THIS_MODULE, @@ -896,18 +965,18 @@ static struct platform_driver omap_venchw_driver = { }, }; -int venc_init_platform_driver(void) +int __init venc_init_platform_driver(void) { if (cpu_is_omap44xx()) return 0; - return platform_driver_register(&omap_venchw_driver); + return platform_driver_probe(&omap_venchw_driver, omap_venchw_probe); } -void venc_uninit_platform_driver(void) +void __exit venc_uninit_platform_driver(void) { if (cpu_is_omap44xx()) return; - return platform_driver_unregister(&omap_venchw_driver); + platform_driver_unregister(&omap_venchw_driver); } diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c index 6a09ef8..c6cf372 100644 --- a/drivers/video/omap2/omapfb/omapfb-ioctl.c +++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c @@ -70,7 +70,7 @@ static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) DBG("omapfb_setup_plane\n"); - if (ofbi->num_overlays != 1) { + if (ofbi->num_overlays == 0) { r = -EINVAL; goto out; } @@ -185,7 +185,7 @@ static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) { struct omapfb_info *ofbi = FB2OFB(fbi); - if (ofbi->num_overlays != 1) { + if (ofbi->num_overlays == 0) { memset(pi, 0, sizeof(*pi)); } else { struct omap_overlay *ovl; @@ -225,6 +225,9 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) down_write_nested(&rg->lock, rg->id); atomic_inc(&rg->lock_count); + if (rg->size == size && rg->type == mi->type) + goto out; + if (atomic_read(&rg->map_count)) { r = -EBUSY; goto out; @@ -247,12 +250,10 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) } } - if (rg->size != size || rg->type != mi->type) { - r = omapfb_realloc_fbmem(fbi, size, mi->type); - if (r) { - dev_err(fbdev->dev, "realloc fbmem failed\n"); - goto out; - } + r = omapfb_realloc_fbmem(fbi, size, mi->type); + if (r) { + dev_err(fbdev->dev, "realloc fbmem failed\n"); + goto out; } out: diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c index b00db40..3450ea0 100644 --- a/drivers/video/omap2/omapfb/omapfb-main.c +++ b/drivers/video/omap2/omapfb/omapfb-main.c @@ -179,6 +179,7 @@ static unsigned omapfb_get_vrfb_offset(const struct omapfb_info *ofbi, int rot) break; default: BUG(); + return 0; } offset *= vrfb->bytespp; @@ -1502,7 +1503,7 @@ static int omapfb_parse_vram_param(const char *param, int max_entries, fbnum = simple_strtoul(p, &p, 10); - if (p == param) + if (p == start) return -EINVAL; if (*p != ':') @@ -2307,7 +2308,7 @@ static int omapfb_init_display(struct omapfb2_device *fbdev, return 0; } -static int omapfb_probe(struct platform_device *pdev) +static int __init omapfb_probe(struct platform_device *pdev) { struct omapfb2_device *fbdev = NULL; int r = 0; @@ -2448,7 +2449,7 @@ err0: return r; } -static int omapfb_remove(struct platform_device *pdev) +static int __exit omapfb_remove(struct platform_device *pdev) { struct omapfb2_device *fbdev = platform_get_drvdata(pdev); @@ -2462,8 +2463,7 @@ static int omapfb_remove(struct platform_device *pdev) } static struct platform_driver omapfb_driver = { - .probe = omapfb_probe, - .remove = omapfb_remove, + .remove = __exit_p(omapfb_remove), .driver = { .name = "omapfb", .owner = THIS_MODULE, @@ -2474,7 +2474,7 @@ static int __init omapfb_init(void) { DBG("omapfb_init\n"); - if (platform_driver_register(&omapfb_driver)) { + if (platform_driver_probe(&omapfb_driver, omapfb_probe)) { printk(KERN_ERR "failed to register omapfb driver\n"); return -ENODEV; } diff --git a/drivers/video/omap2/omapfb/omapfb.h b/drivers/video/omap2/omapfb/omapfb.h index c0bdc9b..30361a0 100644 --- a/drivers/video/omap2/omapfb/omapfb.h +++ b/drivers/video/omap2/omapfb/omapfb.h @@ -166,6 +166,7 @@ static inline struct omapfb_display_data *get_display_data( /* This should never happen */ BUG(); + return NULL; } static inline void omapfb_lock(struct omapfb2_device *fbdev) diff --git a/drivers/video/omap2/vrfb.c b/drivers/video/omap2/vrfb.c index 4e5b960..7e99022 100644 --- a/drivers/video/omap2/vrfb.c +++ b/drivers/video/omap2/vrfb.c @@ -179,8 +179,10 @@ void omap_vrfb_setup(struct vrfb *vrfb, unsigned long paddr, pixel_size_exp = 2; else if (bytespp == 2) pixel_size_exp = 1; - else + else { BUG(); + return; + } vrfb_width = ALIGN(width * bytespp, VRFB_PAGE_WIDTH) / bytespp; vrfb_height = ALIGN(height, VRFB_PAGE_HEIGHT); diff --git a/drivers/video/pxa3xx-gcu.c b/drivers/video/pxa3xx-gcu.c index 1d71c08..0b4ae0c 100644 --- a/drivers/video/pxa3xx-gcu.c +++ b/drivers/video/pxa3xx-gcu.c @@ -316,12 +316,9 @@ pxa3xx_gcu_wait_idle(struct pxa3xx_gcu_priv *priv) ret = wait_event_interruptible_timeout(priv->wait_idle, !priv->shared->hw_running, HZ*4); - if (ret < 0) + if (ret != 0) break; - if (ret > 0) - continue; - if (gc_readl(priv, REG_GCRBEXHR) == rbexhr && priv->shared->num_interrupts == num) { QERROR("TIMEOUT"); diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index f310516..5f9d8e690 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -47,7 +47,7 @@ #ifdef CONFIG_FB_S3C_DEBUG_REGWRITE #undef writel #define writel(v, r) do { \ - printk(KERN_DEBUG "%s: %08x => %p\n", __func__, (unsigned int)v, r); \ + pr_debug("%s: %08x => %p\n", __func__, (unsigned int)v, r); \ __raw_writel(v, r); \ } while (0) #endif /* FB_S3C_DEBUG_REGWRITE */ @@ -495,7 +495,6 @@ static int s3c_fb_set_par(struct fb_info *info) u32 alpha = 0; u32 data; u32 pagewidth; - int clkdiv; dev_dbg(sfb->dev, "setting framebuffer parameters\n"); @@ -532,48 +531,9 @@ static int s3c_fb_set_par(struct fb_info *info) /* disable the window whilst we update it */ writel(0, regs + WINCON(win_no)); - /* use platform specified window as the basis for the lcd timings */ - - if (win_no == sfb->pdata->default_win) { - clkdiv = s3c_fb_calc_pixclk(sfb, var->pixclock); - - data = sfb->pdata->vidcon0; - data &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR); - - if (clkdiv > 1) - data |= VIDCON0_CLKVAL_F(clkdiv-1) | VIDCON0_CLKDIR; - else - data &= ~VIDCON0_CLKDIR; /* 1:1 clock */ - - /* write the timing data to the panel */ - - if (sfb->variant.is_2443) - data |= (1 << 5); - - writel(data, regs + VIDCON0); - + if (!sfb->output_on) s3c_fb_enable(sfb, 1); - data = VIDTCON0_VBPD(var->upper_margin - 1) | - VIDTCON0_VFPD(var->lower_margin - 1) | - VIDTCON0_VSPW(var->vsync_len - 1); - - writel(data, regs + sfb->variant.vidtcon); - - data = VIDTCON1_HBPD(var->left_margin - 1) | - VIDTCON1_HFPD(var->right_margin - 1) | - VIDTCON1_HSPW(var->hsync_len - 1); - - /* VIDTCON1 */ - writel(data, regs + sfb->variant.vidtcon + 4); - - data = VIDTCON2_LINEVAL(var->yres - 1) | - VIDTCON2_HOZVAL(var->xres - 1) | - VIDTCON2_LINEVAL_E(var->yres - 1) | - VIDTCON2_HOZVAL_E(var->xres - 1); - writel(data, regs + sfb->variant.vidtcon + 8); - } - /* write the buffer address */ /* start and end registers stride is 8 */ @@ -839,6 +799,7 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) struct s3c_fb *sfb = win->parent; unsigned int index = win->index; u32 wincon; + u32 output_on = sfb->output_on; dev_dbg(sfb->dev, "blank mode %d\n", blank_mode); @@ -877,34 +838,18 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) shadow_protect_win(win, 1); writel(wincon, sfb->regs + sfb->variant.wincon + (index * 4)); - shadow_protect_win(win, 0); /* Check the enabled state to see if we need to be running the * main LCD interface, as if there are no active windows then * it is highly likely that we also do not need to output * anything. */ - - /* We could do something like the following code, but the current - * system of using framebuffer events means that we cannot make - * the distinction between just window 0 being inactive and all - * the windows being down. - * - * s3c_fb_enable(sfb, sfb->enabled ? 1 : 0); - */ - - /* we're stuck with this until we can do something about overriding - * the power control using the blanking event for a single fb. - */ - if (index == sfb->pdata->default_win) { - shadow_protect_win(win, 1); - s3c_fb_enable(sfb, blank_mode != FB_BLANK_POWERDOWN ? 1 : 0); - shadow_protect_win(win, 0); - } + s3c_fb_enable(sfb, sfb->enabled ? 1 : 0); + shadow_protect_win(win, 0); pm_runtime_put_sync(sfb->dev); - return 0; + return output_on == sfb->output_on; } /** @@ -1111,7 +1056,7 @@ static struct fb_ops s3c_fb_ops = { * * Calculate the pixel clock when none has been given through platform data. */ -static void __devinit s3c_fb_missing_pixclock(struct fb_videomode *mode) +static void s3c_fb_missing_pixclock(struct fb_videomode *mode) { u64 pixclk = 1000000000000ULL; u32 div; @@ -1144,11 +1089,11 @@ static int __devinit s3c_fb_alloc_memory(struct s3c_fb *sfb, dev_dbg(sfb->dev, "allocating memory for display\n"); - real_size = windata->win_mode.xres * windata->win_mode.yres; + real_size = windata->xres * windata->yres; virt_size = windata->virtual_x * windata->virtual_y; dev_dbg(sfb->dev, "real_size=%u (%u.%u), virt_size=%u (%u.%u)\n", - real_size, windata->win_mode.xres, windata->win_mode.yres, + real_size, windata->xres, windata->yres, virt_size, windata->virtual_x, windata->virtual_y); size = (real_size > virt_size) ? real_size : virt_size; @@ -1230,7 +1175,7 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, struct s3c_fb_win **res) { struct fb_var_screeninfo *var; - struct fb_videomode *initmode; + struct fb_videomode initmode; struct s3c_fb_pd_win *windata; struct s3c_fb_win *win; struct fb_info *fbinfo; @@ -1251,11 +1196,11 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, } windata = sfb->pdata->win[win_no]; - initmode = &windata->win_mode; + initmode = *sfb->pdata->vtiming; WARN_ON(windata->max_bpp == 0); - WARN_ON(windata->win_mode.xres == 0); - WARN_ON(windata->win_mode.yres == 0); + WARN_ON(windata->xres == 0); + WARN_ON(windata->yres == 0); win = fbinfo->par; *res = win; @@ -1294,7 +1239,9 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, } /* setup the initial video mode from the window */ - fb_videomode_to_var(&fbinfo->var, initmode); + initmode.xres = windata->xres; + initmode.yres = windata->yres; + fb_videomode_to_var(&fbinfo->var, &initmode); fbinfo->fix.type = FB_TYPE_PACKED_PIXELS; fbinfo->fix.accel = FB_ACCEL_NONE; @@ -1339,6 +1286,53 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, } /** + * s3c_fb_set_rgb_timing() - set video timing for rgb interface. + * @sfb: The base resources for the hardware. + * + * Set horizontal and vertical lcd rgb interface timing. + */ +static void s3c_fb_set_rgb_timing(struct s3c_fb *sfb) +{ + struct fb_videomode *vmode = sfb->pdata->vtiming; + void __iomem *regs = sfb->regs; + int clkdiv; + u32 data; + + if (!vmode->pixclock) + s3c_fb_missing_pixclock(vmode); + + clkdiv = s3c_fb_calc_pixclk(sfb, vmode->pixclock); + + data = sfb->pdata->vidcon0; + data &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR); + + if (clkdiv > 1) + data |= VIDCON0_CLKVAL_F(clkdiv-1) | VIDCON0_CLKDIR; + else + data &= ~VIDCON0_CLKDIR; /* 1:1 clock */ + + if (sfb->variant.is_2443) + data |= (1 << 5); + writel(data, regs + VIDCON0); + + data = VIDTCON0_VBPD(vmode->upper_margin - 1) | + VIDTCON0_VFPD(vmode->lower_margin - 1) | + VIDTCON0_VSPW(vmode->vsync_len - 1); + writel(data, regs + sfb->variant.vidtcon); + + data = VIDTCON1_HBPD(vmode->left_margin - 1) | + VIDTCON1_HFPD(vmode->right_margin - 1) | + VIDTCON1_HSPW(vmode->hsync_len - 1); + writel(data, regs + sfb->variant.vidtcon + 4); + + data = VIDTCON2_LINEVAL(vmode->yres - 1) | + VIDTCON2_HOZVAL(vmode->xres - 1) | + VIDTCON2_LINEVAL_E(vmode->yres - 1) | + VIDTCON2_HOZVAL_E(vmode->xres - 1); + writel(data, regs + sfb->variant.vidtcon + 8); +} + +/** * s3c_fb_clear_win() - clear hardware window registers. * @sfb: The base resources for the hardware. * @win: The window to process. @@ -1481,15 +1475,14 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) writel(0xffffff, regs + WKEYCON1); } + s3c_fb_set_rgb_timing(sfb); + /* we have the register setup, start allocating framebuffers */ for (win = 0; win < fbdrv->variant.nr_windows; win++) { if (!pd->win[win]) continue; - if (!pd->win[win]->win_mode.pixclock) - s3c_fb_missing_pixclock(&pd->win[win]->win_mode); - ret = s3c_fb_probe_win(sfb, win, fbdrv->win[win], &sfb->windows[win]); if (ret < 0) { @@ -1564,6 +1557,8 @@ static int s3c_fb_suspend(struct device *dev) struct s3c_fb_win *win; int win_no; + pm_runtime_get_sync(sfb->dev); + for (win_no = S3C_FB_MAX_WIN - 1; win_no >= 0; win_no--) { win = sfb->windows[win_no]; if (!win) @@ -1577,6 +1572,9 @@ static int s3c_fb_suspend(struct device *dev) clk_disable(sfb->lcd_clk); clk_disable(sfb->bus_clk); + + pm_runtime_put_sync(sfb->dev); + return 0; } @@ -1589,6 +1587,8 @@ static int s3c_fb_resume(struct device *dev) int win_no; u32 reg; + pm_runtime_get_sync(sfb->dev); + clk_enable(sfb->bus_clk); if (!sfb->variant.has_clksel) @@ -1623,6 +1623,8 @@ static int s3c_fb_resume(struct device *dev) shadow_protect_win(win, 0); } + s3c_fb_set_rgb_timing(sfb); + /* restore framebuffers */ for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) { win = sfb->windows[win_no]; @@ -1633,6 +1635,8 @@ static int s3c_fb_resume(struct device *dev) s3c_fb_set_par(win->fbinfo); } + pm_runtime_put_sync(sfb->dev); + return 0; } #endif diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index eafb19d..930e550 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c @@ -31,6 +31,7 @@ #include "sh_mobile_lcdcfb.h" +/* HDMI Core Control Register (HTOP0) */ #define HDMI_SYSTEM_CTRL 0x00 /* System control */ #define HDMI_L_R_DATA_SWAP_CTRL_RPKT 0x01 /* L/R data swap control, bits 19..16 of 20-bit N for Audio Clock Regeneration packet */ @@ -201,6 +202,68 @@ #define HDMI_REVISION_ID 0xF1 /* Revision ID */ #define HDMI_TEST_MODE 0xFE /* Test mode */ +/* HDMI Control Register (HTOP1) */ +#define HDMI_HTOP1_TEST_MODE 0x0000 /* Test mode */ +#define HDMI_HTOP1_VIDEO_INPUT 0x0008 /* VideoInput */ +#define HDMI_HTOP1_CORE_RSTN 0x000C /* CoreResetn */ +#define HDMI_HTOP1_PLLBW 0x0018 /* PLLBW */ +#define HDMI_HTOP1_CLK_TO_PHY 0x001C /* Clk to Phy */ +#define HDMI_HTOP1_VIDEO_INPUT2 0x0020 /* VideoInput2 */ +#define HDMI_HTOP1_TISEMP0_1 0x0024 /* tisemp0-1 */ +#define HDMI_HTOP1_TISEMP2_C 0x0028 /* tisemp2-c */ +#define HDMI_HTOP1_TISIDRV 0x002C /* tisidrv */ +#define HDMI_HTOP1_TISEN 0x0034 /* tisen */ +#define HDMI_HTOP1_TISDREN 0x0038 /* tisdren */ +#define HDMI_HTOP1_CISRANGE 0x003C /* cisrange */ +#define HDMI_HTOP1_ENABLE_SELECTOR 0x0040 /* Enable Selector */ +#define HDMI_HTOP1_MACRO_RESET 0x0044 /* Macro reset */ +#define HDMI_HTOP1_PLL_CALIBRATION 0x0048 /* PLL calibration */ +#define HDMI_HTOP1_RE_CALIBRATION 0x004C /* Re-calibration */ +#define HDMI_HTOP1_CURRENT 0x0050 /* Current */ +#define HDMI_HTOP1_PLL_LOCK_DETECT 0x0054 /* PLL lock detect */ +#define HDMI_HTOP1_PHY_TEST_MODE 0x0058 /* PHY Test Mode */ +#define HDMI_HTOP1_CLK_SET 0x0080 /* Clock Set */ +#define HDMI_HTOP1_DDC_FAIL_SAFE 0x0084 /* DDC fail safe */ +#define HDMI_HTOP1_PRBS 0x0088 /* PRBS */ +#define HDMI_HTOP1_EDID_AINC_CONTROL 0x008C /* EDID ainc Control */ +#define HDMI_HTOP1_HTOP_DCL_MODE 0x00FC /* Deep Coloer Mode */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF0 0x0100 /* Deep Color:FRC COEF0 */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF1 0x0104 /* Deep Color:FRC COEF1 */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF2 0x0108 /* Deep Color:FRC COEF2 */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF3 0x010C /* Deep Color:FRC COEF3 */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF0_C 0x0110 /* Deep Color:FRC COEF0C */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF1_C 0x0114 /* Deep Color:FRC COEF1C */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF2_C 0x0118 /* Deep Color:FRC COEF2C */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF3_C 0x011C /* Deep Color:FRC COEF3C */ +#define HDMI_HTOP1_HTOP_DCL_FRC_MODE 0x0120 /* Deep Color:FRC Mode */ +#define HDMI_HTOP1_HTOP_DCL_RECT_START1 0x0124 /* Deep Color:Rect Start1 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_SIZE1 0x0128 /* Deep Color:Rect Size1 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_START2 0x012C /* Deep Color:Rect Start2 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_SIZE2 0x0130 /* Deep Color:Rect Size2 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_START3 0x0134 /* Deep Color:Rect Start3 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_SIZE3 0x0138 /* Deep Color:Rect Size3 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_START4 0x013C /* Deep Color:Rect Start4 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_SIZE4 0x0140 /* Deep Color:Rect Size4 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y1_1 0x0144 /* Deep Color:Fil Para Y1_1 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y1_2 0x0148 /* Deep Color:Fil Para Y1_2 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB1_1 0x014C /* Deep Color:Fil Para CB1_1 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB1_2 0x0150 /* Deep Color:Fil Para CB1_2 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR1_1 0x0154 /* Deep Color:Fil Para CR1_1 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR1_2 0x0158 /* Deep Color:Fil Para CR1_2 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y2_1 0x015C /* Deep Color:Fil Para Y2_1 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y2_2 0x0160 /* Deep Color:Fil Para Y2_2 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB2_1 0x0164 /* Deep Color:Fil Para CB2_1 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB2_2 0x0168 /* Deep Color:Fil Para CB2_2 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR2_1 0x016C /* Deep Color:Fil Para CR2_1 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR2_2 0x0170 /* Deep Color:Fil Para CR2_2 */ +#define HDMI_HTOP1_HTOP_DCL_COR_PARA_Y1 0x0174 /* Deep Color:Cor Para Y1 */ +#define HDMI_HTOP1_HTOP_DCL_COR_PARA_CB1 0x0178 /* Deep Color:Cor Para CB1 */ +#define HDMI_HTOP1_HTOP_DCL_COR_PARA_CR1 0x017C /* Deep Color:Cor Para CR1 */ +#define HDMI_HTOP1_HTOP_DCL_COR_PARA_Y2 0x0180 /* Deep Color:Cor Para Y2 */ +#define HDMI_HTOP1_HTOP_DCL_COR_PARA_CB2 0x0184 /* Deep Color:Cor Para CB2 */ +#define HDMI_HTOP1_HTOP_DCL_COR_PARA_CR2 0x0188 /* Deep Color:Cor Para CR2 */ +#define HDMI_HTOP1_EDID_DATA_READ 0x0200 /* EDID Data Read 128Byte:0x03FC */ + enum hotplug_state { HDMI_HOTPLUG_DISCONNECTED, HDMI_HOTPLUG_CONNECTED, @@ -211,6 +274,7 @@ struct sh_hdmi { struct sh_mobile_lcdc_entity entity; void __iomem *base; + void __iomem *htop1; enum hotplug_state hp_state; /* hot-plug status */ u8 preprogrammed_vic; /* use a pre-programmed VIC or the external mode */ @@ -222,20 +286,66 @@ struct sh_hdmi { struct delayed_work edid_work; struct fb_videomode mode; struct fb_monspecs monspec; + + /* register access functions */ + void (*write)(struct sh_hdmi *hdmi, u8 data, u8 reg); + u8 (*read)(struct sh_hdmi *hdmi, u8 reg); }; #define entity_to_sh_hdmi(e) container_of(e, struct sh_hdmi, entity) -static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) +static void __hdmi_write8(struct sh_hdmi *hdmi, u8 data, u8 reg) { iowrite8(data, hdmi->base + reg); } -static u8 hdmi_read(struct sh_hdmi *hdmi, u8 reg) +static u8 __hdmi_read8(struct sh_hdmi *hdmi, u8 reg) { return ioread8(hdmi->base + reg); } +static void __hdmi_write32(struct sh_hdmi *hdmi, u8 data, u8 reg) +{ + iowrite32((u32)data, hdmi->base + (reg * 4)); + udelay(100); +} + +static u8 __hdmi_read32(struct sh_hdmi *hdmi, u8 reg) +{ + return (u8)ioread32(hdmi->base + (reg * 4)); +} + +static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) +{ + hdmi->write(hdmi, data, reg); +} + +static u8 hdmi_read(struct sh_hdmi *hdmi, u8 reg) +{ + return hdmi->read(hdmi, reg); +} + +static void hdmi_bit_set(struct sh_hdmi *hdmi, u8 mask, u8 data, u8 reg) +{ + u8 val = hdmi_read(hdmi, reg); + + val &= ~mask; + val |= (data & mask); + + hdmi_write(hdmi, val, reg); +} + +static void hdmi_htop1_write(struct sh_hdmi *hdmi, u32 data, u32 reg) +{ + iowrite32(data, hdmi->htop1 + reg); + udelay(100); +} + +static u32 hdmi_htop1_read(struct sh_hdmi *hdmi, u32 reg) +{ + return ioread32(hdmi->htop1 + reg); +} + /* * HDMI sound */ @@ -693,11 +803,11 @@ static void sh_hdmi_configure(struct sh_hdmi *hdmi) msleep(10); /* PS mode b->d, reset PLLA and PLLB */ - hdmi_write(hdmi, 0x4C, HDMI_SYSTEM_CTRL); + hdmi_bit_set(hdmi, 0xFC, 0x4C, HDMI_SYSTEM_CTRL); udelay(10); - hdmi_write(hdmi, 0x40, HDMI_SYSTEM_CTRL); + hdmi_bit_set(hdmi, 0xFC, 0x40, HDMI_SYSTEM_CTRL); } static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, @@ -746,7 +856,9 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, /* Read EDID */ dev_dbg(hdmi->dev, "Read back EDID code:"); for (i = 0; i < 128; i++) { - edid[i] = hdmi_read(hdmi, HDMI_EDID_KSV_FIFO_ACCESS_WINDOW); + edid[i] = (hdmi->htop1) ? + (u8)hdmi_htop1_read(hdmi, HDMI_HTOP1_EDID_DATA_READ + (i * 4)) : + hdmi_read(hdmi, HDMI_EDID_KSV_FIFO_ACCESS_WINDOW); #ifdef DEBUG if ((i % 16) == 0) { printk(KERN_CONT "\n"); @@ -917,13 +1029,13 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) u8 status1, status2, mask1, mask2; /* mode_b and PLLA and PLLB reset */ - hdmi_write(hdmi, 0x2C, HDMI_SYSTEM_CTRL); + hdmi_bit_set(hdmi, 0xFC, 0x2C, HDMI_SYSTEM_CTRL); /* How long shall reset be held? */ udelay(10); /* mode_b and PLLA and PLLB reset release */ - hdmi_write(hdmi, 0x20, HDMI_SYSTEM_CTRL); + hdmi_bit_set(hdmi, 0xFC, 0x20, HDMI_SYSTEM_CTRL); status1 = hdmi_read(hdmi, HDMI_INTERRUPT_STATUS_1); status2 = hdmi_read(hdmi, HDMI_INTERRUPT_STATUS_2); @@ -1001,7 +1113,7 @@ static int sh_hdmi_display_on(struct sh_mobile_lcdc_entity *entity) */ if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { /* PS mode d->e. All functions are active */ - hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL); + hdmi_bit_set(hdmi, 0xFC, 0x80, HDMI_SYSTEM_CTRL); dev_dbg(hdmi->dev, "HDMI running\n"); } @@ -1016,7 +1128,7 @@ static void sh_hdmi_display_off(struct sh_mobile_lcdc_entity *entity) dev_dbg(hdmi->dev, "%s(%p)\n", __func__, hdmi); /* PS mode e->a */ - hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL); + hdmi_bit_set(hdmi, 0xFC, 0x10, HDMI_SYSTEM_CTRL); } static const struct sh_mobile_lcdc_entity_ops sh_hdmi_ops = { @@ -1110,10 +1222,58 @@ out: dev_dbg(hdmi->dev, "%s(%p): end\n", __func__, hdmi); } +static void sh_hdmi_htop1_init(struct sh_hdmi *hdmi) +{ + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_MODE); + hdmi_htop1_write(hdmi, 0x0000000b, 0x0010); + hdmi_htop1_write(hdmi, 0x00006710, HDMI_HTOP1_HTOP_DCL_FRC_MODE); + hdmi_htop1_write(hdmi, 0x01020406, HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y1_1); + hdmi_htop1_write(hdmi, 0x07080806, HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y1_2); + hdmi_htop1_write(hdmi, 0x01020406, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB1_1); + hdmi_htop1_write(hdmi, 0x07080806, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB1_2); + hdmi_htop1_write(hdmi, 0x01020406, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR1_1); + hdmi_htop1_write(hdmi, 0x07080806, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR1_2); + hdmi_htop1_write(hdmi, 0x01020406, HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y2_1); + hdmi_htop1_write(hdmi, 0x07080806, HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y2_2); + hdmi_htop1_write(hdmi, 0x01020406, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB2_1); + hdmi_htop1_write(hdmi, 0x07080806, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB2_2); + hdmi_htop1_write(hdmi, 0x01020406, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR2_1); + hdmi_htop1_write(hdmi, 0x07080806, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR2_2); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_COR_PARA_Y1); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_COR_PARA_CB1); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_COR_PARA_CR1); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_COR_PARA_Y2); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_COR_PARA_CB2); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_COR_PARA_CR2); + hdmi_htop1_write(hdmi, 0x00000008, HDMI_HTOP1_CURRENT); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_TISEMP0_1); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_TISEMP2_C); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_PHY_TEST_MODE); + hdmi_htop1_write(hdmi, 0x00000081, HDMI_HTOP1_TISIDRV); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_PLLBW); + hdmi_htop1_write(hdmi, 0x0000000f, HDMI_HTOP1_TISEN); + hdmi_htop1_write(hdmi, 0x0000000f, HDMI_HTOP1_TISDREN); + hdmi_htop1_write(hdmi, 0x00000003, HDMI_HTOP1_ENABLE_SELECTOR); + hdmi_htop1_write(hdmi, 0x00000001, HDMI_HTOP1_MACRO_RESET); + hdmi_htop1_write(hdmi, 0x00000016, HDMI_HTOP1_CISRANGE); + msleep(100); + hdmi_htop1_write(hdmi, 0x00000001, HDMI_HTOP1_ENABLE_SELECTOR); + msleep(100); + hdmi_htop1_write(hdmi, 0x00000003, HDMI_HTOP1_ENABLE_SELECTOR); + hdmi_htop1_write(hdmi, 0x00000001, HDMI_HTOP1_MACRO_RESET); + hdmi_htop1_write(hdmi, 0x0000000f, HDMI_HTOP1_TISEN); + hdmi_htop1_write(hdmi, 0x0000000f, HDMI_HTOP1_TISDREN); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_VIDEO_INPUT); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_CLK_TO_PHY); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_VIDEO_INPUT2); + hdmi_htop1_write(hdmi, 0x0000000a, HDMI_HTOP1_CLK_SET); +} + static int __init sh_hdmi_probe(struct platform_device *pdev) { struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct resource *htop1_res; int irq = platform_get_irq(pdev, 0), ret; struct sh_hdmi *hdmi; long rate; @@ -1121,6 +1281,15 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) if (!res || !pdata || irq < 0) return -ENODEV; + htop1_res = NULL; + if (pdata->flags & HDMI_HAS_HTOP1) { + htop1_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!htop1_res) { + dev_err(&pdev->dev, "htop1 needs register base\n"); + return -EINVAL; + } + } + hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL); if (!hdmi) { dev_err(&pdev->dev, "Cannot allocate device data\n"); @@ -1138,6 +1307,15 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) goto egetclk; } + /* select register access functions */ + if (pdata->flags & HDMI_32BIT_REG) { + hdmi->write = __hdmi_write32; + hdmi->read = __hdmi_read32; + } else { + hdmi->write = __hdmi_write8; + hdmi->read = __hdmi_read8; + } + /* An arbitrary relaxed pixclock just to get things started: from standard 480p */ rate = clk_round_rate(hdmi->hdmi_clk, PICOS2KHZ(37037)); if (rate > 0) @@ -1176,6 +1354,24 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); + /* init interrupt polarity */ + if (pdata->flags & HDMI_OUTPUT_PUSH_PULL) + hdmi_bit_set(hdmi, 0x02, 0x02, HDMI_SYSTEM_CTRL); + + if (pdata->flags & HDMI_OUTPUT_POLARITY_HI) + hdmi_bit_set(hdmi, 0x01, 0x01, HDMI_SYSTEM_CTRL); + + /* enable htop1 register if needed */ + if (htop1_res) { + hdmi->htop1 = ioremap(htop1_res->start, resource_size(htop1_res)); + if (!hdmi->htop1) { + dev_err(&pdev->dev, "control register region already claimed\n"); + ret = -ENOMEM; + goto emap_htop1; + } + sh_hdmi_htop1_init(hdmi); + } + /* Product and revision IDs are 0 in sh-mobile version */ dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); @@ -1199,6 +1395,9 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) ecodec: free_irq(irq, hdmi); ereqirq: + if (hdmi->htop1) + iounmap(hdmi->htop1); +emap_htop1: pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); iounmap(hdmi->base); @@ -1230,6 +1429,8 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); clk_disable(hdmi->hdmi_clk); clk_put(hdmi->hdmi_clk); + if (hdmi->htop1) + iounmap(hdmi->htop1); iounmap(hdmi->base); release_mem_region(res->start, resource_size(res)); kfree(hdmi); diff --git a/drivers/video/sis/init.h b/drivers/video/sis/init.h index aff7384..85d6738 100644 --- a/drivers/video/sis/init.h +++ b/drivers/video/sis/init.h @@ -105,51 +105,6 @@ static const unsigned short ModeIndex_1920x1440[] = {0x68, 0x69, 0x00, 0x6b}; static const unsigned short ModeIndex_300_2048x1536[]= {0x6c, 0x6d, 0x00, 0x00}; static const unsigned short ModeIndex_310_2048x1536[]= {0x6c, 0x6d, 0x00, 0x6e}; -static const unsigned short SiS_DRAMType[17][5]={ - {0x0C,0x0A,0x02,0x40,0x39}, - {0x0D,0x0A,0x01,0x40,0x48}, - {0x0C,0x09,0x02,0x20,0x35}, - {0x0D,0x09,0x01,0x20,0x44}, - {0x0C,0x08,0x02,0x10,0x31}, - {0x0D,0x08,0x01,0x10,0x40}, - {0x0C,0x0A,0x01,0x20,0x34}, - {0x0C,0x09,0x01,0x08,0x32}, - {0x0B,0x08,0x02,0x08,0x21}, - {0x0C,0x08,0x01,0x08,0x30}, - {0x0A,0x08,0x02,0x04,0x11}, - {0x0B,0x0A,0x01,0x10,0x28}, - {0x09,0x08,0x02,0x02,0x01}, - {0x0B,0x09,0x01,0x08,0x24}, - {0x0B,0x08,0x01,0x04,0x20}, - {0x0A,0x08,0x01,0x02,0x10}, - {0x09,0x08,0x01,0x01,0x00} -}; - -static const unsigned short SiS_SDRDRAM_TYPE[13][5] = -{ - { 2,12, 9,64,0x35}, - { 1,13, 9,64,0x44}, - { 2,12, 8,32,0x31}, - { 2,11, 9,32,0x25}, - { 1,12, 9,32,0x34}, - { 1,13, 8,32,0x40}, - { 2,11, 8,16,0x21}, - { 1,12, 8,16,0x30}, - { 1,11, 9,16,0x24}, - { 1,11, 8, 8,0x20}, - { 2, 9, 8, 4,0x01}, - { 1,10, 8, 4,0x10}, - { 1, 9, 8, 2,0x00} -}; - -static const unsigned short SiS_DDRDRAM_TYPE[4][5] = -{ - { 2,12, 9,64,0x35}, - { 2,12, 8,32,0x31}, - { 2,11, 8,16,0x21}, - { 2, 9, 8, 4,0x01} -}; - static const unsigned char SiS_MDA_DAC[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, diff --git a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c index 078ca21..a7a48db 100644 --- a/drivers/video/sis/sis_main.c +++ b/drivers/video/sis/sis_main.c @@ -4222,6 +4222,26 @@ sisfb_post_300_buswidth(struct sis_video_info *ivideo) return 1; /* 32bit */ } +static const unsigned short __devinitconst SiS_DRAMType[17][5] = { + {0x0C,0x0A,0x02,0x40,0x39}, + {0x0D,0x0A,0x01,0x40,0x48}, + {0x0C,0x09,0x02,0x20,0x35}, + {0x0D,0x09,0x01,0x20,0x44}, + {0x0C,0x08,0x02,0x10,0x31}, + {0x0D,0x08,0x01,0x10,0x40}, + {0x0C,0x0A,0x01,0x20,0x34}, + {0x0C,0x09,0x01,0x08,0x32}, + {0x0B,0x08,0x02,0x08,0x21}, + {0x0C,0x08,0x01,0x08,0x30}, + {0x0A,0x08,0x02,0x04,0x11}, + {0x0B,0x0A,0x01,0x10,0x28}, + {0x09,0x08,0x02,0x02,0x01}, + {0x0B,0x09,0x01,0x08,0x24}, + {0x0B,0x08,0x01,0x04,0x20}, + {0x0A,0x08,0x01,0x02,0x10}, + {0x09,0x08,0x01,0x01,0x00} +}; + static int __devinit sisfb_post_300_rwtest(struct sis_video_info *ivideo, int iteration, int buswidth, int PseudoRankCapacity, int PseudoAdrPinCount, @@ -4231,27 +4251,8 @@ sisfb_post_300_rwtest(struct sis_video_info *ivideo, int iteration, int buswidth unsigned short sr14; unsigned int k, RankCapacity, PageCapacity, BankNumHigh, BankNumMid; unsigned int PhysicalAdrOtherPage, PhysicalAdrHigh, PhysicalAdrHalfPage; - static const unsigned short SiS_DRAMType[17][5] = { - {0x0C,0x0A,0x02,0x40,0x39}, - {0x0D,0x0A,0x01,0x40,0x48}, - {0x0C,0x09,0x02,0x20,0x35}, - {0x0D,0x09,0x01,0x20,0x44}, - {0x0C,0x08,0x02,0x10,0x31}, - {0x0D,0x08,0x01,0x10,0x40}, - {0x0C,0x0A,0x01,0x20,0x34}, - {0x0C,0x09,0x01,0x08,0x32}, - {0x0B,0x08,0x02,0x08,0x21}, - {0x0C,0x08,0x01,0x08,0x30}, - {0x0A,0x08,0x02,0x04,0x11}, - {0x0B,0x0A,0x01,0x10,0x28}, - {0x09,0x08,0x02,0x02,0x01}, - {0x0B,0x09,0x01,0x08,0x24}, - {0x0B,0x08,0x01,0x04,0x20}, - {0x0A,0x08,0x01,0x02,0x10}, - {0x09,0x08,0x01,0x01,0x00} - }; - for(k = 0; k <= 16; k++) { + for(k = 0; k < ARRAY_SIZE(SiS_DRAMType); k++) { RankCapacity = buswidth * SiS_DRAMType[k][3]; diff --git a/drivers/video/skeletonfb.c b/drivers/video/skeletonfb.c index 30f7a81..5b6abc6 100644 --- a/drivers/video/skeletonfb.c +++ b/drivers/video/skeletonfb.c @@ -1036,6 +1036,6 @@ static void __exit xxxfb_exit(void) */ module_init(xxxfb_init); -module_exit(xxxfb_remove); +module_exit(xxxfb_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/video/smscufx.c b/drivers/video/smscufx.c index ccbfef5..af3ef27 100644 --- a/drivers/video/smscufx.c +++ b/drivers/video/smscufx.c @@ -846,7 +846,7 @@ static void ufx_raw_rect(struct ufx_data *dev, u16 *cmd, int x, int y, } } -int ufx_handle_damage(struct ufx_data *dev, int x, int y, +static int ufx_handle_damage(struct ufx_data *dev, int x, int y, int width, int height) { size_t packed_line_len = ALIGN((width * 2), 4); @@ -1083,7 +1083,7 @@ static int ufx_ops_open(struct fb_info *info, int user) struct fb_deferred_io *fbdefio; - fbdefio = kmalloc(sizeof(struct fb_deferred_io), GFP_KERNEL); + fbdefio = kzalloc(sizeof(struct fb_deferred_io), GFP_KERNEL); if (fbdefio) { fbdefio->delay = UFX_DEFIO_WRITE_DELAY; diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c index 7af1e81..8af6414 100644 --- a/drivers/video/udlfb.c +++ b/drivers/video/udlfb.c @@ -893,7 +893,7 @@ static int dlfb_ops_open(struct fb_info *info, int user) struct fb_deferred_io *fbdefio; - fbdefio = kmalloc(sizeof(struct fb_deferred_io), GFP_KERNEL); + fbdefio = kzalloc(sizeof(struct fb_deferred_io), GFP_KERNEL); if (fbdefio) { fbdefio->delay = DL_DEFIO_WRITE_DELAY; diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index 0c88375..c80e770 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c @@ -1276,17 +1276,12 @@ static int viafb_dfph_proc_open(struct inode *inode, struct file *file) static ssize_t viafb_dfph_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos) { - char buf[20]; - u8 reg_val = 0; - unsigned long length; - if (count < 1) - return -EINVAL; - length = count > 20 ? 20 : count; - if (copy_from_user(&buf[0], buffer, length)) - return -EFAULT; - buf[length - 1] = '\0'; /*Ensure end string */ - if (kstrtou8(buf, 0, ®_val) < 0) - return -EINVAL; + int err; + u8 reg_val; + err = kstrtou8_from_user(buffer, count, 0, ®_val); + if (err) + return err; + viafb_write_reg_mask(CR97, VIACR, reg_val, 0x0f); return count; } @@ -1316,17 +1311,12 @@ static int viafb_dfpl_proc_open(struct inode *inode, struct file *file) static ssize_t viafb_dfpl_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos) { - char buf[20]; - u8 reg_val = 0; - unsigned long length; - if (count < 1) - return -EINVAL; - length = count > 20 ? 20 : count; - if (copy_from_user(&buf[0], buffer, length)) - return -EFAULT; - buf[length - 1] = '\0'; /*Ensure end string */ - if (kstrtou8(buf, 0, ®_val) < 0) - return -EINVAL; + int err; + u8 reg_val; + err = kstrtou8_from_user(buffer, count, 0, ®_val); + if (err) + return err; + viafb_write_reg_mask(CR99, VIACR, reg_val, 0x0f); return count; } |