diff options
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/i915/i915_gem_gtt.c | 14 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_params.c | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_bios.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_opregion.c | 16 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/core/engine/disp/nv50.c | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_chan.c | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_display.c | 23 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_display.h | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drm.c | 51 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_fbcon.c | 23 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_fbcon.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/svga_reg.h | 1 | ||||
-rw-r--r-- | drivers/gpu/ipu-v3/Makefile | 3 | ||||
-rw-r--r-- | drivers/gpu/ipu-v3/ipu-common.c | 457 | ||||
-rw-r--r-- | drivers/gpu/ipu-v3/ipu-cpmem.c | 597 | ||||
-rw-r--r-- | drivers/gpu/ipu-v3/ipu-prv.h | 14 |
16 files changed, 703 insertions, 522 deletions
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 1411613f2174..e42925f76b4b 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -1310,6 +1310,16 @@ void i915_check_and_clear_faults(struct drm_device *dev) POSTING_READ(RING_FAULT_REG(&dev_priv->ring[RCS])); } +static void i915_ggtt_flush(struct drm_i915_private *dev_priv) +{ + if (INTEL_INFO(dev_priv->dev)->gen < 6) { + intel_gtt_chipset_flush(); + } else { + I915_WRITE(GFX_FLSH_CNTL_GEN6, GFX_FLSH_CNTL_EN); + POSTING_READ(GFX_FLSH_CNTL_GEN6); + } +} + void i915_gem_suspend_gtt_mappings(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -1326,6 +1336,8 @@ void i915_gem_suspend_gtt_mappings(struct drm_device *dev) dev_priv->gtt.base.start, dev_priv->gtt.base.total, true); + + i915_ggtt_flush(dev_priv); } void i915_gem_restore_gtt_mappings(struct drm_device *dev) @@ -1378,7 +1390,7 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev) gen6_write_pdes(container_of(vm, struct i915_hw_ppgtt, base)); } - i915_gem_chipset_flush(dev); + i915_ggtt_flush(dev_priv); } int i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj) diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index 7f84dd263ee8..9842fd2e742a 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -66,12 +66,12 @@ module_param_named(powersave, i915.powersave, int, 0600); MODULE_PARM_DESC(powersave, "Enable powersavings, fbc, downclocking, etc. (default: true)"); -module_param_named(semaphores, i915.semaphores, int, 0400); +module_param_named_unsafe(semaphores, i915.semaphores, int, 0400); MODULE_PARM_DESC(semaphores, "Use semaphores for inter-ring sync " "(default: -1 (use per-chip defaults))"); -module_param_named(enable_rc6, i915.enable_rc6, int, 0400); +module_param_named_unsafe(enable_rc6, i915.enable_rc6, int, 0400); MODULE_PARM_DESC(enable_rc6, "Enable power-saving render C-state 6. " "Different stages can be selected via bitmask values " @@ -79,7 +79,7 @@ MODULE_PARM_DESC(enable_rc6, "For example, 3 would enable rc6 and deep rc6, and 7 would enable everything. " "default: -1 (use per-chip default)"); -module_param_named(enable_fbc, i915.enable_fbc, int, 0600); +module_param_named_unsafe(enable_fbc, i915.enable_fbc, int, 0600); MODULE_PARM_DESC(enable_fbc, "Enable frame buffer compression for power savings " "(default: -1 (use per-chip default))"); @@ -113,7 +113,7 @@ MODULE_PARM_DESC(enable_hangcheck, "WARNING: Disabling this can cause system wide hangs. " "(default: true)"); -module_param_named(enable_ppgtt, i915.enable_ppgtt, int, 0400); +module_param_named_unsafe(enable_ppgtt, i915.enable_ppgtt, int, 0400); MODULE_PARM_DESC(enable_ppgtt, "Override PPGTT usage. " "(-1=auto [default], 0=disabled, 1=aliasing, 2=full)"); diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index eee79e1c3222..afcc8dd40bdd 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -946,7 +946,7 @@ static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port, DRM_DEBUG_KMS("Analog port %c is also DP or TMDS compatible\n", port_name(port)); if (is_dvi && (port == PORT_A || port == PORT_E)) - DRM_DEBUG_KMS("Port %c is TMDS compabile\n", port_name(port)); + DRM_DEBUG_KMS("Port %c is TMDS compatible\n", port_name(port)); if (!is_dvi && !is_dp && !is_crt) DRM_DEBUG_KMS("Port %c is not DP/TMDS/CRT compatible\n", port_name(port)); diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index ca52ad2ae7d1..d8de1d5140a7 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -396,6 +396,16 @@ int intel_opregion_notify_adapter(struct drm_device *dev, pci_power_t state) return -EINVAL; } +/* + * If the vendor backlight interface is not in use and ACPI backlight interface + * is broken, do not bother processing backlight change requests from firmware. + */ +static bool should_ignore_backlight_request(void) +{ + return acpi_video_backlight_support() && + !acpi_video_verify_backlight_support(); +} + static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -404,11 +414,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) DRM_DEBUG_DRIVER("bclp = 0x%08x\n", bclp); - /* - * If the acpi_video interface is not supposed to be used, don't - * bother processing backlight level change requests from firmware. - */ - if (!acpi_video_verify_backlight_support()) { + if (should_ignore_backlight_request()) { DRM_DEBUG_KMS("opregion backlight request ignored\n"); return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c index 4b5bb5d58a54..f8cbb512132f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c @@ -1763,9 +1763,10 @@ nv50_disp_intr_unk40_0_tmds(struct nv50_disp_priv *priv, struct dcb_output *outp const int or = ffs(outp->or) - 1; const u32 loff = (or * 0x800) + (link * 0x80); const u16 mask = (outp->sorconf.link << 6) | outp->or; + struct dcb_output match; u8 ver, hdr; - if (dcb_outp_match(bios, DCB_OUTPUT_DP, mask, &ver, &hdr, outp)) + if (dcb_outp_match(bios, DCB_OUTPUT_DP, mask, &ver, &hdr, &match)) nv_mask(priv, 0x61c10c + loff, 0x00000001, 0x00000000); } diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c index 99cd9e4a2aa6..3440fc999f2f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_chan.c +++ b/drivers/gpu/drm/nouveau/nouveau_chan.c @@ -285,6 +285,7 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart) struct nouveau_software_chan *swch; struct nv_dma_v0 args = {}; int ret, i; + bool save; nvif_object_map(chan->object); @@ -386,7 +387,11 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart) } /* initialise synchronisation */ - return nouveau_fence(chan->drm)->context_new(chan); + save = cli->base.super; + cli->base.super = true; /* hack until fencenv50 fixed */ + ret = nouveau_fence(chan->drm)->context_new(chan); + cli->base.super = save; + return ret; } int diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 65b4fd53dd4e..4a21b2b06ce2 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -550,14 +550,12 @@ nouveau_display_destroy(struct drm_device *dev) } int -nouveau_display_suspend(struct drm_device *dev) +nouveau_display_suspend(struct drm_device *dev, bool runtime) { - struct nouveau_drm *drm = nouveau_drm(dev); struct drm_crtc *crtc; nouveau_display_fini(dev); - NV_INFO(drm, "unpinning framebuffer(s)...\n"); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct nouveau_framebuffer *nouveau_fb; @@ -579,12 +577,13 @@ nouveau_display_suspend(struct drm_device *dev) } void -nouveau_display_repin(struct drm_device *dev) +nouveau_display_resume(struct drm_device *dev, bool runtime) { struct nouveau_drm *drm = nouveau_drm(dev); struct drm_crtc *crtc; - int ret; + int ret, head; + /* re-pin fb/cursors */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct nouveau_framebuffer *nouveau_fb; @@ -606,13 +605,6 @@ nouveau_display_repin(struct drm_device *dev) if (ret) NV_ERROR(drm, "Could not pin/map cursor.\n"); } -} - -void -nouveau_display_resume(struct drm_device *dev) -{ - struct drm_crtc *crtc; - int head; nouveau_display_init(dev); @@ -627,6 +619,13 @@ nouveau_display_resume(struct drm_device *dev) for (head = 0; head < dev->mode_config.num_crtc; head++) drm_vblank_on(dev, head); + /* This should ensure we don't hit a locking problem when someone + * wakes us up via a connector. We should never go into suspend + * while the display is on anyways. + */ + if (runtime) + return; + drm_helper_resume_force_mode(dev); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h index 88ca177cb1c7..be3d5947c6be 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.h +++ b/drivers/gpu/drm/nouveau/nouveau_display.h @@ -63,9 +63,8 @@ int nouveau_display_create(struct drm_device *dev); void nouveau_display_destroy(struct drm_device *dev); int nouveau_display_init(struct drm_device *dev); void nouveau_display_fini(struct drm_device *dev); -int nouveau_display_suspend(struct drm_device *dev); -void nouveau_display_repin(struct drm_device *dev); -void nouveau_display_resume(struct drm_device *dev); +int nouveau_display_suspend(struct drm_device *dev, bool runtime); +void nouveau_display_resume(struct drm_device *dev, bool runtime); int nouveau_display_vblank_enable(struct drm_device *, int); void nouveau_display_vblank_disable(struct drm_device *, int); int nouveau_display_scanoutpos(struct drm_device *, int, unsigned int, diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 9c3af96a7153..3ed32dd90303 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -547,9 +547,11 @@ nouveau_do_suspend(struct drm_device *dev, bool runtime) struct nouveau_cli *cli; int ret; - if (dev->mode_config.num_crtc && !runtime) { + if (dev->mode_config.num_crtc) { + NV_INFO(drm, "suspending console...\n"); + nouveau_fbcon_set_suspend(dev, 1); NV_INFO(drm, "suspending display...\n"); - ret = nouveau_display_suspend(dev); + ret = nouveau_display_suspend(dev, runtime); if (ret) return ret; } @@ -603,7 +605,7 @@ fail_client: fail_display: if (dev->mode_config.num_crtc) { NV_INFO(drm, "resuming display...\n"); - nouveau_display_resume(dev); + nouveau_display_resume(dev, runtime); } return ret; } @@ -618,9 +620,6 @@ int nouveau_pmops_suspend(struct device *dev) drm_dev->switch_power_state == DRM_SWITCH_POWER_DYNAMIC_OFF) return 0; - if (drm_dev->mode_config.num_crtc) - nouveau_fbcon_set_suspend(drm_dev, 1); - ret = nouveau_do_suspend(drm_dev, false); if (ret) return ret; @@ -633,7 +632,7 @@ int nouveau_pmops_suspend(struct device *dev) } static int -nouveau_do_resume(struct drm_device *dev) +nouveau_do_resume(struct drm_device *dev, bool runtime) { struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_cli *cli; @@ -658,7 +657,9 @@ nouveau_do_resume(struct drm_device *dev) if (dev->mode_config.num_crtc) { NV_INFO(drm, "resuming display...\n"); - nouveau_display_repin(dev); + nouveau_display_resume(dev, runtime); + NV_INFO(drm, "resuming console...\n"); + nouveau_fbcon_set_suspend(dev, 0); } return 0; @@ -681,47 +682,21 @@ int nouveau_pmops_resume(struct device *dev) return ret; pci_set_master(pdev); - ret = nouveau_do_resume(drm_dev); - if (ret) - return ret; - - if (drm_dev->mode_config.num_crtc) { - nouveau_display_resume(drm_dev); - nouveau_fbcon_set_suspend(drm_dev, 0); - } - - return 0; + return nouveau_do_resume(drm_dev, false); } static int nouveau_pmops_freeze(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct drm_device *drm_dev = pci_get_drvdata(pdev); - int ret; - - if (drm_dev->mode_config.num_crtc) - nouveau_fbcon_set_suspend(drm_dev, 1); - - ret = nouveau_do_suspend(drm_dev, false); - return ret; + return nouveau_do_suspend(drm_dev, false); } static int nouveau_pmops_thaw(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct drm_device *drm_dev = pci_get_drvdata(pdev); - int ret; - - ret = nouveau_do_resume(drm_dev); - if (ret) - return ret; - - if (drm_dev->mode_config.num_crtc) { - nouveau_display_resume(drm_dev); - nouveau_fbcon_set_suspend(drm_dev, 0); - } - - return 0; + return nouveau_do_resume(drm_dev, false); } @@ -977,7 +952,7 @@ static int nouveau_pmops_runtime_resume(struct device *dev) return ret; pci_set_master(pdev); - ret = nouveau_do_resume(drm_dev); + ret = nouveau_do_resume(drm_dev, true); drm_kms_helper_poll_enable(drm_dev); /* do magic */ nvif_mask(device, 0x88488, (1 << 25), (1 << 25)); diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 8bdd27091db8..49fe6075cc7c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -486,6 +486,16 @@ static const struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = { .fb_probe = nouveau_fbcon_create, }; +static void +nouveau_fbcon_set_suspend_work(struct work_struct *work) +{ + struct nouveau_fbdev *fbcon = container_of(work, typeof(*fbcon), work); + console_lock(); + nouveau_fbcon_accel_restore(fbcon->dev); + nouveau_fbcon_zfill(fbcon->dev, fbcon); + fb_set_suspend(fbcon->helper.fbdev, FBINFO_STATE_RUNNING); + console_unlock(); +} int nouveau_fbcon_init(struct drm_device *dev) @@ -503,6 +513,7 @@ nouveau_fbcon_init(struct drm_device *dev) if (!fbcon) return -ENOMEM; + INIT_WORK(&fbcon->work, nouveau_fbcon_set_suspend_work); fbcon->dev = dev; drm->fbcon = fbcon; @@ -551,14 +562,14 @@ nouveau_fbcon_set_suspend(struct drm_device *dev, int state) { struct nouveau_drm *drm = nouveau_drm(dev); if (drm->fbcon) { - console_lock(); - if (state == 0) { - nouveau_fbcon_accel_restore(dev); - nouveau_fbcon_zfill(dev, drm->fbcon); + if (state == FBINFO_STATE_RUNNING) { + schedule_work(&drm->fbcon->work); + return; } + flush_work(&drm->fbcon->work); + console_lock(); fb_set_suspend(drm->fbcon->helper.fbdev, state); - if (state == 1) - nouveau_fbcon_accel_save_disable(dev); + nouveau_fbcon_accel_save_disable(dev); console_unlock(); } } diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.h b/drivers/gpu/drm/nouveau/nouveau_fbcon.h index 34658cfa8f5d..0b465c7d3907 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.h +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.h @@ -36,6 +36,7 @@ struct nouveau_fbdev { struct nouveau_framebuffer nouveau_fb; struct list_head fbdev_list; struct drm_device *dev; + struct work_struct work; unsigned int saved_flags; struct nvif_object surf2d; struct nvif_object clip; diff --git a/drivers/gpu/drm/vmwgfx/svga_reg.h b/drivers/gpu/drm/vmwgfx/svga_reg.h index 11323dd5196f..e4259c2c1acc 100644 --- a/drivers/gpu/drm/vmwgfx/svga_reg.h +++ b/drivers/gpu/drm/vmwgfx/svga_reg.h @@ -35,7 +35,6 @@ /* * PCI device IDs. */ -#define PCI_VENDOR_ID_VMWARE 0x15AD #define PCI_DEVICE_ID_VMWARE_SVGA2 0x0405 /* diff --git a/drivers/gpu/ipu-v3/Makefile b/drivers/gpu/ipu-v3/Makefile index 1887972b4ac2..0b42836caae1 100644 --- a/drivers/gpu/ipu-v3/Makefile +++ b/drivers/gpu/ipu-v3/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o -imx-ipu-v3-objs := ipu-common.o ipu-dc.o ipu-di.o ipu-dp.o ipu-dmfc.o ipu-smfc.o +imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-dc.o ipu-di.o \ + ipu-dp.o ipu-dmfc.o ipu-smfc.o diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c index 04e7b2eafbdd..5978e7aab8ed 100644 --- a/drivers/gpu/ipu-v3/ipu-common.c +++ b/drivers/gpu/ipu-v3/ipu-common.c @@ -44,17 +44,6 @@ static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset) writel(value, ipu->cm_reg + offset); } -static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset) -{ - return readl(ipu->idmac_reg + offset); -} - -static inline void ipu_idmac_write(struct ipu_soc *ipu, u32 value, - unsigned offset) -{ - writel(value, ipu->idmac_reg + offset); -} - void ipu_srm_dp_sync_update(struct ipu_soc *ipu) { u32 val; @@ -65,379 +54,6 @@ void ipu_srm_dp_sync_update(struct ipu_soc *ipu) } EXPORT_SYMBOL_GPL(ipu_srm_dp_sync_update); -struct ipu_ch_param __iomem *ipu_get_cpmem(struct ipuv3_channel *channel) -{ - struct ipu_soc *ipu = channel->ipu; - - return ipu->cpmem_base + channel->num; -} -EXPORT_SYMBOL_GPL(ipu_get_cpmem); - -void ipu_cpmem_set_high_priority(struct ipuv3_channel *channel) -{ - struct ipu_soc *ipu = channel->ipu; - struct ipu_ch_param __iomem *p = ipu_get_cpmem(channel); - u32 val; - - if (ipu->ipu_type == IPUV3EX) - ipu_ch_param_write_field(p, IPU_FIELD_ID, 1); - - val = ipu_idmac_read(ipu, IDMAC_CHA_PRI(channel->num)); - val |= 1 << (channel->num % 32); - ipu_idmac_write(ipu, val, IDMAC_CHA_PRI(channel->num)); -}; -EXPORT_SYMBOL_GPL(ipu_cpmem_set_high_priority); - -void ipu_ch_param_write_field(struct ipu_ch_param __iomem *base, u32 wbs, u32 v) -{ - u32 bit = (wbs >> 8) % 160; - u32 size = wbs & 0xff; - u32 word = (wbs >> 8) / 160; - u32 i = bit / 32; - u32 ofs = bit % 32; - u32 mask = (1 << size) - 1; - u32 val; - - pr_debug("%s %d %d %d\n", __func__, word, bit , size); - - val = readl(&base->word[word].data[i]); - val &= ~(mask << ofs); - val |= v << ofs; - writel(val, &base->word[word].data[i]); - - if ((bit + size - 1) / 32 > i) { - val = readl(&base->word[word].data[i + 1]); - val &= ~(mask >> (ofs ? (32 - ofs) : 0)); - val |= v >> (ofs ? (32 - ofs) : 0); - writel(val, &base->word[word].data[i + 1]); - } -} -EXPORT_SYMBOL_GPL(ipu_ch_param_write_field); - -u32 ipu_ch_param_read_field(struct ipu_ch_param __iomem *base, u32 wbs) -{ - u32 bit = (wbs >> 8) % 160; - u32 size = wbs & 0xff; - u32 word = (wbs >> 8) / 160; - u32 i = bit / 32; - u32 ofs = bit % 32; - u32 mask = (1 << size) - 1; - u32 val = 0; - - pr_debug("%s %d %d %d\n", __func__, word, bit , size); - - val = (readl(&base->word[word].data[i]) >> ofs) & mask; - - if ((bit + size - 1) / 32 > i) { - u32 tmp; - tmp = readl(&base->word[word].data[i + 1]); - tmp &= mask >> (ofs ? (32 - ofs) : 0); - val |= tmp << (ofs ? (32 - ofs) : 0); - } - - return val; -} -EXPORT_SYMBOL_GPL(ipu_ch_param_read_field); - -int ipu_cpmem_set_format_rgb(struct ipu_ch_param __iomem *p, - const struct ipu_rgb *rgb) -{ - int bpp = 0, npb = 0, ro, go, bo, to; - - ro = rgb->bits_per_pixel - rgb->red.length - rgb->red.offset; - go = rgb->bits_per_pixel - rgb->green.length - rgb->green.offset; - bo = rgb->bits_per_pixel - rgb->blue.length - rgb->blue.offset; - to = rgb->bits_per_pixel - rgb->transp.length - rgb->transp.offset; - - ipu_ch_param_write_field(p, IPU_FIELD_WID0, rgb->red.length - 1); - ipu_ch_param_write_field(p, IPU_FIELD_OFS0, ro); - ipu_ch_param_write_field(p, IPU_FIELD_WID1, rgb->green.length - 1); - ipu_ch_param_write_field(p, IPU_FIELD_OFS1, go); - ipu_ch_param_write_field(p, IPU_FIELD_WID2, rgb->blue.length - 1); - ipu_ch_param_write_field(p, IPU_FIELD_OFS2, bo); - - if (rgb->transp.length) { - ipu_ch_param_write_field(p, IPU_FIELD_WID3, - rgb->transp.length - 1); - ipu_ch_param_write_field(p, IPU_FIELD_OFS3, to); - } else { - ipu_ch_param_write_field(p, IPU_FIELD_WID3, 7); - ipu_ch_param_write_field(p, IPU_FIELD_OFS3, - rgb->bits_per_pixel); - } - - switch (rgb->bits_per_pixel) { - case 32: - bpp = 0; - npb = 15; - break; - case 24: - bpp = 1; - npb = 19; - break; - case 16: - bpp = 3; - npb = 31; - break; - case 8: - bpp = 5; - npb = 63; - break; - default: - return -EINVAL; - } - ipu_ch_param_write_field(p, IPU_FIELD_BPP, bpp); - ipu_ch_param_write_field(p, IPU_FIELD_NPB, npb); - ipu_ch_param_write_field(p, IPU_FIELD_PFS, 7); /* rgb mode */ - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_rgb); - -int ipu_cpmem_set_format_passthrough(struct ipu_ch_param __iomem *p, - int width) -{ - int bpp = 0, npb = 0; - - switch (width) { - case 32: - bpp = 0; - npb = 15; - break; - case 24: - bpp = 1; - npb = 19; - break; - case 16: - bpp = 3; - npb = 31; - break; - case 8: - bpp = 5; - npb = 63; - break; - default: - return -EINVAL; - } - - ipu_ch_param_write_field(p, IPU_FIELD_BPP, bpp); - ipu_ch_param_write_field(p, IPU_FIELD_NPB, npb); - ipu_ch_param_write_field(p, IPU_FIELD_PFS, 6); /* raw mode */ - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_passthrough); - -void ipu_cpmem_set_yuv_interleaved(struct ipu_ch_param __iomem *p, - u32 pixel_format) -{ - switch (pixel_format) { - case V4L2_PIX_FMT_UYVY: - ipu_ch_param_write_field(p, IPU_FIELD_BPP, 3); /* bits/pixel */ - ipu_ch_param_write_field(p, IPU_FIELD_PFS, 0xA); /* pix format */ - ipu_ch_param_write_field(p, IPU_FIELD_NPB, 31); /* burst size */ - break; - case V4L2_PIX_FMT_YUYV: - ipu_ch_param_write_field(p, IPU_FIELD_BPP, 3); /* bits/pixel */ - ipu_ch_param_write_field(p, IPU_FIELD_PFS, 0x8); /* pix format */ - ipu_ch_param_write_field(p, IPU_FIELD_NPB, 31); /* burst size */ - break; - } -} -EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_interleaved); - -void ipu_cpmem_set_yuv_planar_full(struct ipu_ch_param __iomem *p, - u32 pixel_format, int stride, int u_offset, int v_offset) -{ - switch (pixel_format) { - case V4L2_PIX_FMT_YUV420: - ipu_ch_param_write_field(p, IPU_FIELD_SLUV, (stride / 2) - 1); - ipu_ch_param_write_field(p, IPU_FIELD_UBO, u_offset / 8); - ipu_ch_param_write_field(p, IPU_FIELD_VBO, v_offset / 8); - break; - case V4L2_PIX_FMT_YVU420: - ipu_ch_param_write_field(p, IPU_FIELD_SLUV, (stride / 2) - 1); - ipu_ch_param_write_field(p, IPU_FIELD_UBO, v_offset / 8); - ipu_ch_param_write_field(p, IPU_FIELD_VBO, u_offset / 8); - break; - } -} -EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar_full); - -void ipu_cpmem_set_yuv_planar(struct ipu_ch_param __iomem *p, u32 pixel_format, - int stride, int height) -{ - int u_offset, v_offset; - int uv_stride = 0; - - switch (pixel_format) { - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - uv_stride = stride / 2; - u_offset = stride * height; - v_offset = u_offset + (uv_stride * height / 2); - ipu_cpmem_set_yuv_planar_full(p, pixel_format, stride, - u_offset, v_offset); - break; - } -} -EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar); - -static const struct ipu_rgb def_rgb_32 = { - .red = { .offset = 16, .length = 8, }, - .green = { .offset = 8, .length = 8, }, - .blue = { .offset = 0, .length = 8, }, - .transp = { .offset = 24, .length = 8, }, - .bits_per_pixel = 32, -}; - -static const struct ipu_rgb def_bgr_32 = { - .red = { .offset = 0, .length = 8, }, - .green = { .offset = 8, .length = 8, }, - .blue = { .offset = 16, .length = 8, }, - .transp = { .offset = 24, .length = 8, }, - .bits_per_pixel = 32, -}; - -static const struct ipu_rgb def_rgb_24 = { - .red = { .offset = 16, .length = 8, }, - .green = { .offset = 8, .length = 8, }, - .blue = { .offset = 0, .length = 8, }, - .transp = { .offset = 0, .length = 0, }, - .bits_per_pixel = 24, -}; - -static const struct ipu_rgb def_bgr_24 = { - .red = { .offset = 0, .length = 8, }, - .green = { .offset = 8, .length = 8, }, - .blue = { .offset = 16, .length = 8, }, - .transp = { .offset = 0, .length = 0, }, - .bits_per_pixel = 24, -}; - -static const struct ipu_rgb def_rgb_16 = { - .red = { .offset = 11, .length = 5, }, - .green = { .offset = 5, .length = 6, }, - .blue = { .offset = 0, .length = 5, }, - .transp = { .offset = 0, .length = 0, }, - .bits_per_pixel = 16, -}; - -static const struct ipu_rgb def_bgr_16 = { - .red = { .offset = 0, .length = 5, }, - .green = { .offset = 5, .length = 6, }, - .blue = { .offset = 11, .length = 5, }, - .transp = { .offset = 0, .length = 0, }, - .bits_per_pixel = 16, -}; - -#define Y_OFFSET(pix, x, y) ((x) + pix->width * (y)) -#define U_OFFSET(pix, x, y) ((pix->width * pix->height) + \ - (pix->width * (y) / 4) + (x) / 2) -#define V_OFFSET(pix, x, y) ((pix->width * pix->height) + \ - (pix->width * pix->height / 4) + \ - (pix->width * (y) / 4) + (x) / 2) - -int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 drm_fourcc) -{ - switch (drm_fourcc) { - case DRM_FORMAT_YUV420: - case DRM_FORMAT_YVU420: - /* pix format */ - ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 2); - /* burst size */ - ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 63); - break; - case DRM_FORMAT_UYVY: - /* bits/pixel */ - ipu_ch_param_write_field(cpmem, IPU_FIELD_BPP, 3); - /* pix format */ - ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 0xA); - /* burst size */ - ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 31); - break; - case DRM_FORMAT_YUYV: - /* bits/pixel */ - ipu_ch_param_write_field(cpmem, IPU_FIELD_BPP, 3); - /* pix format */ - ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 0x8); - /* burst size */ - ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 31); - break; - case DRM_FORMAT_ABGR8888: - case DRM_FORMAT_XBGR8888: - ipu_cpmem_set_format_rgb(cpmem, &def_bgr_32); - break; - case DRM_FORMAT_ARGB8888: - case DRM_FORMAT_XRGB8888: - ipu_cpmem_set_format_rgb(cpmem, &def_rgb_32); - break; - case DRM_FORMAT_BGR888: - ipu_cpmem_set_format_rgb(cpmem, &def_bgr_24); - break; - case DRM_FORMAT_RGB888: - ipu_cpmem_set_format_rgb(cpmem, &def_rgb_24); - break; - case DRM_FORMAT_RGB565: - ipu_cpmem_set_format_rgb(cpmem, &def_rgb_16); - break; - case DRM_FORMAT_BGR565: - ipu_cpmem_set_format_rgb(cpmem, &def_bgr_16); - break; - default: - return -EINVAL; - } - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt); - -/* - * The V4L2 spec defines packed RGB formats in memory byte order, which from - * point of view of the IPU corresponds to little-endian words with the first - * component in the least significant bits. - * The DRM pixel formats and IPU internal representation are ordered the other - * way around, with the first named component ordered at the most significant - * bits. Further, V4L2 formats are not well defined: - * http://linuxtv.org/downloads/v4l-dvb-apis/packed-rgb.html - * We choose the interpretation which matches GStreamer behavior. - */ -static int v4l2_pix_fmt_to_drm_fourcc(u32 pixelformat) -{ - switch (pixelformat) { - case V4L2_PIX_FMT_RGB565: - /* - * Here we choose the 'corrected' interpretation of RGBP, a - * little-endian 16-bit word with the red component at the most - * significant bits: - * g[2:0]b[4:0] r[4:0]g[5:3] <=> [16:0] R:G:B - */ - return DRM_FORMAT_RGB565; - case V4L2_PIX_FMT_BGR24: - /* B G R <=> [24:0] R:G:B */ - return DRM_FORMAT_RGB888; - case V4L2_PIX_FMT_RGB24: - /* R G B <=> [24:0] B:G:R */ - return DRM_FORMAT_BGR888; - case V4L2_PIX_FMT_BGR32: - /* B G R A <=> [32:0] A:B:G:R */ - return DRM_FORMAT_XRGB8888; - case V4L2_PIX_FMT_RGB32: - /* R G B A <=> [32:0] A:B:G:R */ - return DRM_FORMAT_XBGR8888; - case V4L2_PIX_FMT_UYVY: - return DRM_FORMAT_UYVY; - case V4L2_PIX_FMT_YUYV: - return DRM_FORMAT_YUYV; - case V4L2_PIX_FMT_YUV420: - return DRM_FORMAT_YUV420; - case V4L2_PIX_FMT_YVU420: - return DRM_FORMAT_YVU420; - } - - return -EINVAL; -} - enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc) { switch (drm_fourcc) { @@ -465,66 +81,6 @@ enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc) } EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace); -int ipu_cpmem_set_image(struct ipu_ch_param __iomem *cpmem, - struct ipu_image *image) -{ - struct v4l2_pix_format *pix = &image->pix; - int y_offset, u_offset, v_offset; - - pr_debug("%s: resolution: %dx%d stride: %d\n", - __func__, pix->width, pix->height, - pix->bytesperline); - - ipu_cpmem_set_resolution(cpmem, image->rect.width, - image->rect.height); - ipu_cpmem_set_stride(cpmem, pix->bytesperline); - - ipu_cpmem_set_fmt(cpmem, v4l2_pix_fmt_to_drm_fourcc(pix->pixelformat)); - - switch (pix->pixelformat) { - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - y_offset = Y_OFFSET(pix, image->rect.left, image->rect.top); - u_offset = U_OFFSET(pix, image->rect.left, - image->rect.top) - y_offset; - v_offset = V_OFFSET(pix, image->rect.left, - image->rect.top) - y_offset; - - ipu_cpmem_set_yuv_planar_full(cpmem, pix->pixelformat, - pix->bytesperline, u_offset, v_offset); - ipu_cpmem_set_buffer(cpmem, 0, image->phys + y_offset); - break; - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_YUYV: - ipu_cpmem_set_buffer(cpmem, 0, image->phys + - image->rect.left * 2 + - image->rect.top * image->pix.bytesperline); - break; - case V4L2_PIX_FMT_RGB32: - case V4L2_PIX_FMT_BGR32: - ipu_cpmem_set_buffer(cpmem, 0, image->phys + - image->rect.left * 4 + - image->rect.top * image->pix.bytesperline); - break; - case V4L2_PIX_FMT_RGB565: - ipu_cpmem_set_buffer(cpmem, 0, image->phys + - image->rect.left * 2 + - image->rect.top * image->pix.bytesperline); - break; - case V4L2_PIX_FMT_RGB24: - case V4L2_PIX_FMT_BGR24: - ipu_cpmem_set_buffer(cpmem, 0, image->phys + - image->rect.left * 3 + - image->rect.top * image->pix.bytesperline); - break; - default: - return -EINVAL; - } - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_cpmem_set_image); - enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat) { switch (pixelformat) { @@ -895,6 +451,12 @@ static int ipu_submodules_init(struct ipu_soc *ipu, struct device *dev = &pdev->dev; const struct ipu_devtype *devtype = ipu->devtype; + ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs); + if (ret) { + unit = "cpmem"; + goto err_cpmem; + } + ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs, IPU_CONF_DI0_EN, ipu_clk); if (ret) { @@ -949,6 +511,8 @@ err_dc: err_di_1: ipu_di_exit(ipu, 0); err_di_0: + ipu_cpmem_exit(ipu); +err_cpmem: dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret); return ret; } @@ -1025,6 +589,7 @@ static void ipu_submodules_exit(struct ipu_soc *ipu) ipu_dc_exit(ipu); ipu_di_exit(ipu, 1); ipu_di_exit(ipu, 0); + ipu_cpmem_exit(ipu); } static int platform_remove_devices_fn(struct device *dev, void *unused) @@ -1265,10 +830,8 @@ static int ipu_probe(struct platform_device *pdev) ipu->idmac_reg = devm_ioremap(&pdev->dev, ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS, PAGE_SIZE); - ipu->cpmem_base = devm_ioremap(&pdev->dev, - ipu_base + devtype->cpmem_ofs, PAGE_SIZE); - if (!ipu->cm_reg || !ipu->idmac_reg || !ipu->cpmem_base) + if (!ipu->cm_reg || !ipu->idmac_reg) return -ENOMEM; ipu->clk = devm_clk_get(&pdev->dev, "bus"); diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c new file mode 100644 index 000000000000..7adfa78a48bc --- /dev/null +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -0,0 +1,597 @@ +/* + * Copyright (C) 2012 Mentor Graphics Inc. + * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/types.h> +#include <linux/bitrev.h> +#include <linux/io.h> +#include <drm/drm_fourcc.h> +#include "ipu-prv.h" + +struct ipu_cpmem_word { + u32 data[5]; + u32 res[3]; +}; + +struct ipu_ch_param { + struct ipu_cpmem_word word[2]; +}; + +struct ipu_cpmem { + struct ipu_ch_param __iomem *base; + u32 module; + spinlock_t lock; + int use_count; + struct ipu_soc *ipu; +}; + +#define IPU_CPMEM_WORD(word, ofs, size) ((((word) * 160 + (ofs)) << 8) | (size)) + +#define IPU_FIELD_UBO IPU_CPMEM_WORD(0, 46, 22) +#define IPU_FIELD_VBO IPU_CPMEM_WORD(0, 68, 22) +#define IPU_FIELD_IOX IPU_CPMEM_WORD(0, 90, 4) +#define IPU_FIELD_RDRW IPU_CPMEM_WORD(0, 94, 1) +#define IPU_FIELD_SO IPU_CPMEM_WORD(0, 113, 1) +#define IPU_FIELD_SLY IPU_CPMEM_WORD(1, 102, 14) +#define IPU_FIELD_SLUV IPU_CPMEM_WORD(1, 128, 14) + +#define IPU_FIELD_XV IPU_CPMEM_WORD(0, 0, 10) +#define IPU_FIELD_YV IPU_CPMEM_WORD(0, 10, 9) +#define IPU_FIELD_XB IPU_CPMEM_WORD(0, 19, 13) +#define IPU_FIELD_YB IPU_CPMEM_WORD(0, 32, 12) +#define IPU_FIELD_NSB_B IPU_CPMEM_WORD(0, 44, 1) +#define IPU_FIELD_CF IPU_CPMEM_WORD(0, 45, 1) +#define IPU_FIELD_SX IPU_CPMEM_WORD(0, 46, 12) +#define IPU_FIELD_SY IPU_CPMEM_WORD(0, 58, 11) +#define IPU_FIELD_NS IPU_CPMEM_WORD(0, 69, 10) +#define IPU_FIELD_SDX IPU_CPMEM_WORD(0, 79, 7) +#define IPU_FIELD_SM IPU_CPMEM_WORD(0, 86, 10) +#define IPU_FIELD_SCC IPU_CPMEM_WORD(0, 96, 1) +#define IPU_FIELD_SCE IPU_CPMEM_WORD(0, 97, 1) +#define IPU_FIELD_SDY IPU_CPMEM_WORD(0, 98, 7) +#define IPU_FIELD_SDRX IPU_CPMEM_WORD(0, 105, 1) +#define IPU_FIELD_SDRY IPU_CPMEM_WORD(0, 106, 1) +#define IPU_FIELD_BPP IPU_CPMEM_WORD(0, 107, 3) +#define IPU_FIELD_DEC_SEL IPU_CPMEM_WORD(0, 110, 2) +#define IPU_FIELD_DIM IPU_CPMEM_WORD(0, 112, 1) +#define IPU_FIELD_BNDM IPU_CPMEM_WORD(0, 114, 3) +#define IPU_FIELD_BM IPU_CPMEM_WORD(0, 117, 2) +#define IPU_FIELD_ROT IPU_CPMEM_WORD(0, 119, 1) +#define IPU_FIELD_HF IPU_CPMEM_WORD(0, 120, 1) +#define IPU_FIELD_VF IPU_CPMEM_WORD(0, 121, 1) +#define IPU_FIELD_THE IPU_CPMEM_WORD(0, 122, 1) +#define IPU_FIELD_CAP IPU_CPMEM_WORD(0, 123, 1) +#define IPU_FIELD_CAE IPU_CPMEM_WORD(0, 124, 1) +#define IPU_FIELD_FW IPU_CPMEM_WORD(0, 125, 13) +#define IPU_FIELD_FH IPU_CPMEM_WORD(0, 138, 12) +#define IPU_FIELD_EBA0 IPU_CPMEM_WORD(1, 0, 29) +#define IPU_FIELD_EBA1 IPU_CPMEM_WORD(1, 29, 29) +#define IPU_FIELD_ILO IPU_CPMEM_WORD(1, 58, 20) +#define IPU_FIELD_NPB IPU_CPMEM_WORD(1, 78, 7) +#define IPU_FIELD_PFS IPU_CPMEM_WORD(1, 85, 4) +#define IPU_FIELD_ALU IPU_CPMEM_WORD(1, 89, 1) +#define IPU_FIELD_ALBM IPU_CPMEM_WORD(1, 90, 3) +#define IPU_FIELD_ID IPU_CPMEM_WORD(1, 93, 2) +#define IPU_FIELD_TH IPU_CPMEM_WORD(1, 95, 7) +#define IPU_FIELD_SL IPU_CPMEM_WORD(1, 102, 14) +#define IPU_FIELD_WID0 IPU_CPMEM_WORD(1, 116, 3) +#define IPU_FIELD_WID1 IPU_CPMEM_WORD(1, 119, 3) +#define IPU_FIELD_WID2 IPU_CPMEM_WORD(1, 122, 3) +#define IPU_FIELD_WID3 IPU_CPMEM_WORD(1, 125, 3) +#define IPU_FIELD_OFS0 IPU_CPMEM_WORD(1, 128, 5) +#define IPU_FIELD_OFS1 IPU_CPMEM_WORD(1, 133, 5) +#define IPU_FIELD_OFS2 IPU_CPMEM_WORD(1, 138, 5) +#define IPU_FIELD_OFS3 IPU_CPMEM_WORD(1, 143, 5) +#define IPU_FIELD_SXYS IPU_CPMEM_WORD(1, 148, 1) +#define IPU_FIELD_CRE IPU_CPMEM_WORD(1, 149, 1) +#define IPU_FIELD_DEC_SEL2 IPU_CPMEM_WORD(1, 150, 1) + +static inline struct ipu_ch_param __iomem * +ipu_get_cpmem(struct ipuv3_channel *ch) +{ + struct ipu_cpmem *cpmem = ch->ipu->cpmem_priv; + + return cpmem->base + ch->num; +} + +static void ipu_ch_param_write_field(struct ipuv3_channel *ch, u32 wbs, u32 v) +{ + struct ipu_ch_param __iomem *base = ipu_get_cpmem(ch); + u32 bit = (wbs >> 8) % 160; + u32 size = wbs & 0xff; + u32 word = (wbs >> 8) / 160; + u32 i = bit / 32; + u32 ofs = bit % 32; + u32 mask = (1 << size) - 1; + u32 val; + + pr_debug("%s %d %d %d\n", __func__, word, bit , size); + + val = readl(&base->word[word].data[i]); + val &= ~(mask << ofs); + val |= v << ofs; + writel(val, &base->word[word].data[i]); + + if ((bit + size - 1) / 32 > i) { + val = readl(&base->word[word].data[i + 1]); + val &= ~(mask >> (ofs ? (32 - ofs) : 0)); + val |= v >> (ofs ? (32 - ofs) : 0); + writel(val, &base->word[word].data[i + 1]); + } +} + +static u32 ipu_ch_param_read_field(struct ipuv3_channel *ch, u32 wbs) +{ + struct ipu_ch_param __iomem *base = ipu_get_cpmem(ch); + u32 bit = (wbs >> 8) % 160; + u32 size = wbs & 0xff; + u32 word = (wbs >> 8) / 160; + u32 i = bit / 32; + u32 ofs = bit % 32; + u32 mask = (1 << size) - 1; + u32 val = 0; + + pr_debug("%s %d %d %d\n", __func__, word, bit , size); + + val = (readl(&base->word[word].data[i]) >> ofs) & mask; + + if ((bit + size - 1) / 32 > i) { + u32 tmp; + + tmp = readl(&base->word[word].data[i + 1]); + tmp &= mask >> (ofs ? (32 - ofs) : 0); + val |= tmp << (ofs ? (32 - ofs) : 0); + } + + return val; +} + +/* + * The V4L2 spec defines packed RGB formats in memory byte order, which from + * point of view of the IPU corresponds to little-endian words with the first + * component in the least significant bits. + * The DRM pixel formats and IPU internal representation are ordered the other + * way around, with the first named component ordered at the most significant + * bits. Further, V4L2 formats are not well defined: + * http://linuxtv.org/downloads/v4l-dvb-apis/packed-rgb.html + * We choose the interpretation which matches GStreamer behavior. + */ +static int v4l2_pix_fmt_to_drm_fourcc(u32 pixelformat) +{ + switch (pixelformat) { + case V4L2_PIX_FMT_RGB565: + /* + * Here we choose the 'corrected' interpretation of RGBP, a + * little-endian 16-bit word with the red component at the most + * significant bits: + * g[2:0]b[4:0] r[4:0]g[5:3] <=> [16:0] R:G:B + */ + return DRM_FORMAT_RGB565; + case V4L2_PIX_FMT_BGR24: + /* B G R <=> [24:0] R:G:B */ + return DRM_FORMAT_RGB888; + case V4L2_PIX_FMT_RGB24: + /* R G B <=> [24:0] B:G:R */ + return DRM_FORMAT_BGR888; + case V4L2_PIX_FMT_BGR32: + /* B G R A <=> [32:0] A:B:G:R */ + return DRM_FORMAT_XRGB8888; + case V4L2_PIX_FMT_RGB32: + /* R G B A <=> [32:0] A:B:G:R */ + return DRM_FORMAT_XBGR8888; + case V4L2_PIX_FMT_UYVY: + return DRM_FORMAT_UYVY; + case V4L2_PIX_FMT_YUYV: + return DRM_FORMAT_YUYV; + case V4L2_PIX_FMT_YUV420: + return DRM_FORMAT_YUV420; + case V4L2_PIX_FMT_YVU420: + return DRM_FORMAT_YVU420; + } + + return -EINVAL; +} + +void ipu_cpmem_zero(struct ipuv3_channel *ch) +{ + struct ipu_ch_param __iomem *p = ipu_get_cpmem(ch); + void __iomem *base = p; + int i; + + for (i = 0; i < sizeof(*p) / sizeof(u32); i++) + writel(0, base + i * sizeof(u32)); +} +EXPORT_SYMBOL_GPL(ipu_cpmem_zero); + +void ipu_cpmem_set_resolution(struct ipuv3_channel *ch, int xres, int yres) +{ + ipu_ch_param_write_field(ch, IPU_FIELD_FW, xres - 1); + ipu_ch_param_write_field(ch, IPU_FIELD_FH, yres - 1); +} +EXPORT_SYMBOL_GPL(ipu_cpmem_set_resolution); + +void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride) +{ + ipu_ch_param_write_field(ch, IPU_FIELD_SLY, stride - 1); +} +EXPORT_SYMBOL_GPL(ipu_cpmem_set_stride); + +void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch) +{ + struct ipu_soc *ipu = ch->ipu; + u32 val; + + if (ipu->ipu_type == IPUV3EX) + ipu_ch_param_write_field(ch, IPU_FIELD_ID, 1); + + val = ipu_idmac_read(ipu, IDMAC_CHA_PRI(ch->num)); + val |= 1 << (ch->num % 32); + ipu_idmac_write(ipu, val, IDMAC_CHA_PRI(ch->num)); +}; +EXPORT_SYMBOL_GPL(ipu_cpmem_set_high_priority); + +void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf) +{ + if (bufnum) + ipu_ch_param_write_field(ch, IPU_FIELD_EBA1, buf >> 3); + else + ipu_ch_param_write_field(ch, IPU_FIELD_EBA0, buf >> 3); +} +EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer); + +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) +{ + ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); + ipu_ch_param_write_field(ch, IPU_FIELD_ILO, stride / 8); + ipu_ch_param_write_field(ch, IPU_FIELD_SLY, (stride * 2) - 1); +}; +EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); + +void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize) +{ + ipu_ch_param_write_field(ch, IPU_FIELD_NPB, burstsize - 1); +}; +EXPORT_SYMBOL_GPL(ipu_cpmem_set_burstsize); + +int ipu_cpmem_set_format_rgb(struct ipuv3_channel *ch, + const struct ipu_rgb *rgb) +{ + int bpp = 0, npb = 0, ro, go, bo, to; + + ro = rgb->bits_per_pixel - rgb->red.length - rgb->red.offset; + go = rgb->bits_per_pixel - rgb->green.length - rgb->green.offset; + bo = rgb->bits_per_pixel - rgb->blue.length - rgb->blue.offset; + to = rgb->bits_per_pixel - rgb->transp.length - rgb->transp.offset; + + ipu_ch_param_write_field(ch, IPU_FIELD_WID0, rgb->red.length - 1); + ipu_ch_param_write_field(ch, IPU_FIELD_OFS0, ro); + ipu_ch_param_write_field(ch, IPU_FIELD_WID1, rgb->green.length - 1); + ipu_ch_param_write_field(ch, IPU_FIELD_OFS1, go); + ipu_ch_param_write_field(ch, IPU_FIELD_WID2, rgb->blue.length - 1); + ipu_ch_param_write_field(ch, IPU_FIELD_OFS2, bo); + + if (rgb->transp.length) { + ipu_ch_param_write_field(ch, IPU_FIELD_WID3, + rgb->transp.length - 1); + ipu_ch_param_write_field(ch, IPU_FIELD_OFS3, to); + } else { + ipu_ch_param_write_field(ch, IPU_FIELD_WID3, 7); + ipu_ch_param_write_field(ch, IPU_FIELD_OFS3, + rgb->bits_per_pixel); + } + + switch (rgb->bits_per_pixel) { + case 32: + bpp = 0; + npb = 15; + break; + case 24: + bpp = 1; + npb = 19; + break; + case 16: + bpp = 3; + npb = 31; + break; + case 8: + bpp = 5; + npb = 63; + break; + default: + return -EINVAL; + } + ipu_ch_param_write_field(ch, IPU_FIELD_BPP, bpp); + ipu_ch_param_write_field(ch, IPU_FIELD_NPB, npb); + ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 7); /* rgb mode */ + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_rgb); + +int ipu_cpmem_set_format_passthrough(struct ipuv3_channel *ch, int width) +{ + int bpp = 0, npb = 0; + + switch (width) { + case 32: + bpp = 0; + npb = 15; + break; + case 24: + bpp = 1; + npb = 19; + break; + case 16: + bpp = 3; + npb = 31; + break; + case 8: + bpp = 5; + npb = 63; + break; + default: + return -EINVAL; + } + + ipu_ch_param_write_field(ch, IPU_FIELD_BPP, bpp); + ipu_ch_param_write_field(ch, IPU_FIELD_NPB, npb); + ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 6); /* raw mode */ + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_passthrough); + +void ipu_cpmem_set_yuv_interleaved(struct ipuv3_channel *ch, u32 pixel_format) +{ + switch (pixel_format) { + case V4L2_PIX_FMT_UYVY: + ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); /* bits/pixel */ + ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0xA);/* pix fmt */ + ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);/* burst size */ + break; + case V4L2_PIX_FMT_YUYV: + ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); /* bits/pixel */ + ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0x8);/* pix fmt */ + ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);/* burst size */ + break; + } +} +EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_interleaved); + +void ipu_cpmem_set_yuv_planar_full(struct ipuv3_channel *ch, + u32 pixel_format, int stride, + int u_offset, int v_offset) +{ + switch (pixel_format) { + case V4L2_PIX_FMT_YUV420: + ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, (stride / 2) - 1); + ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_offset / 8); + ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_offset / 8); + break; + case V4L2_PIX_FMT_YVU420: + ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, (stride / 2) - 1); + ipu_ch_param_write_field(ch, IPU_FIELD_UBO, v_offset / 8); + ipu_ch_param_write_field(ch, IPU_FIELD_VBO, u_offset / 8); + break; + } +} +EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar_full); + +void ipu_cpmem_set_yuv_planar(struct ipuv3_channel *ch, + u32 pixel_format, int stride, int height) +{ + int u_offset, v_offset; + int uv_stride = 0; + + switch (pixel_format) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + uv_stride = stride / 2; + u_offset = stride * height; + v_offset = u_offset + (uv_stride * height / 2); + ipu_cpmem_set_yuv_planar_full(ch, pixel_format, stride, + u_offset, v_offset); + break; + } +} +EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar); + +static const struct ipu_rgb def_rgb_32 = { + .red = { .offset = 16, .length = 8, }, + .green = { .offset = 8, .length = 8, }, + .blue = { .offset = 0, .length = 8, }, + .transp = { .offset = 24, .length = 8, }, + .bits_per_pixel = 32, +}; + +static const struct ipu_rgb def_bgr_32 = { + .red = { .offset = 0, .length = 8, }, + .green = { .offset = 8, .length = 8, }, + .blue = { .offset = 16, .length = 8, }, + .transp = { .offset = 24, .length = 8, }, + .bits_per_pixel = 32, +}; + +static const struct ipu_rgb def_rgb_24 = { + .red = { .offset = 16, .length = 8, }, + .green = { .offset = 8, .length = 8, }, + .blue = { .offset = 0, .length = 8, }, + .transp = { .offset = 0, .length = 0, }, + .bits_per_pixel = 24, +}; + +static const struct ipu_rgb def_bgr_24 = { + .red = { .offset = 0, .length = 8, }, + .green = { .offset = 8, .length = 8, }, + .blue = { .offset = 16, .length = 8, }, + .transp = { .offset = 0, .length = 0, }, + .bits_per_pixel = 24, +}; + +static const struct ipu_rgb def_rgb_16 = { + .red = { .offset = 11, .length = 5, }, + .green = { .offset = 5, .length = 6, }, + .blue = { .offset = 0, .length = 5, }, + .transp = { .offset = 0, .length = 0, }, + .bits_per_pixel = 16, +}; + +static const struct ipu_rgb def_bgr_16 = { + .red = { .offset = 0, .length = 5, }, + .green = { .offset = 5, .length = 6, }, + .blue = { .offset = 11, .length = 5, }, + .transp = { .offset = 0, .length = 0, }, + .bits_per_pixel = 16, +}; + +#define Y_OFFSET(pix, x, y) ((x) + pix->width * (y)) +#define U_OFFSET(pix, x, y) ((pix->width * pix->height) + \ + (pix->width * (y) / 4) + (x) / 2) +#define V_OFFSET(pix, x, y) ((pix->width * pix->height) + \ + (pix->width * pix->height / 4) + \ + (pix->width * (y) / 4) + (x) / 2) + +int ipu_cpmem_set_fmt(struct ipuv3_channel *ch, u32 drm_fourcc) +{ + switch (drm_fourcc) { + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + /* pix format */ + ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 2); + /* burst size */ + ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); + break; + case DRM_FORMAT_UYVY: + /* bits/pixel */ + ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); + /* pix format */ + ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0xA); + /* burst size */ + ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); + break; + case DRM_FORMAT_YUYV: + /* bits/pixel */ + ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); + /* pix format */ + ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0x8); + /* burst size */ + ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); + break; + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_XBGR8888: + ipu_cpmem_set_format_rgb(ch, &def_bgr_32); + break; + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + ipu_cpmem_set_format_rgb(ch, &def_rgb_32); + break; + case DRM_FORMAT_BGR888: + ipu_cpmem_set_format_rgb(ch, &def_bgr_24); + break; + case DRM_FORMAT_RGB888: + ipu_cpmem_set_format_rgb(ch, &def_rgb_24); + break; + case DRM_FORMAT_RGB565: + ipu_cpmem_set_format_rgb(ch, &def_rgb_16); + break; + case DRM_FORMAT_BGR565: + ipu_cpmem_set_format_rgb(ch, &def_bgr_16); + break; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt); + +int ipu_cpmem_set_image(struct ipuv3_channel *ch, struct ipu_image *image) +{ + struct v4l2_pix_format *pix = &image->pix; + int y_offset, u_offset, v_offset; + + pr_debug("%s: resolution: %dx%d stride: %d\n", + __func__, pix->width, pix->height, + pix->bytesperline); + + ipu_cpmem_set_resolution(ch, image->rect.width, image->rect.height); + ipu_cpmem_set_stride(ch, pix->bytesperline); + + ipu_cpmem_set_fmt(ch, v4l2_pix_fmt_to_drm_fourcc(pix->pixelformat)); + + switch (pix->pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + y_offset = Y_OFFSET(pix, image->rect.left, image->rect.top); + u_offset = U_OFFSET(pix, image->rect.left, + image->rect.top) - y_offset; + v_offset = V_OFFSET(pix, image->rect.left, + image->rect.top) - y_offset; + + ipu_cpmem_set_yuv_planar_full(ch, pix->pixelformat, + pix->bytesperline, u_offset, v_offset); + ipu_cpmem_set_buffer(ch, 0, image->phys + y_offset); + break; + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUYV: + ipu_cpmem_set_buffer(ch, 0, image->phys + + image->rect.left * 2 + + image->rect.top * image->pix.bytesperline); + break; + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_BGR32: + ipu_cpmem_set_buffer(ch, 0, image->phys + + image->rect.left * 4 + + image->rect.top * image->pix.bytesperline); + break; + case V4L2_PIX_FMT_RGB565: + ipu_cpmem_set_buffer(ch, 0, image->phys + + image->rect.left * 2 + + image->rect.top * image->pix.bytesperline); + break; + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + ipu_cpmem_set_buffer(ch, 0, image->phys + + image->rect.left * 3 + + image->rect.top * image->pix.bytesperline); + break; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_cpmem_set_image); + +int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base) +{ + struct ipu_cpmem *cpmem; + + cpmem = devm_kzalloc(dev, sizeof(*cpmem), GFP_KERNEL); + if (!cpmem) + return -ENOMEM; + + ipu->cpmem_priv = cpmem; + + spin_lock_init(&cpmem->lock); + cpmem->base = devm_ioremap(dev, base, SZ_128K); + if (!cpmem->base) + return -ENOMEM; + + dev_dbg(dev, "CPMEM base: 0x%08lx remapped to %p\n", + base, cpmem->base); + cpmem->ipu = ipu; + + return 0; +} + +void ipu_cpmem_exit(struct ipu_soc *ipu) +{ +} diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h index c93f50ec04f7..0a7b2adaba39 100644 --- a/drivers/gpu/ipu-v3/ipu-prv.h +++ b/drivers/gpu/ipu-v3/ipu-prv.h @@ -148,6 +148,7 @@ struct ipuv3_channel { struct ipu_soc *ipu; }; +struct ipu_cpmem; struct ipu_dc_priv; struct ipu_dmfc_priv; struct ipu_di; @@ -164,7 +165,6 @@ struct ipu_soc { void __iomem *cm_reg; void __iomem *idmac_reg; - struct ipu_ch_param __iomem *cpmem_base; int usecount; @@ -176,6 +176,7 @@ struct ipu_soc { int irq_err; struct irq_domain *domain; + struct ipu_cpmem *cpmem_priv; struct ipu_dc_priv *dc_priv; struct ipu_dp_priv *dp_priv; struct ipu_dmfc_priv *dmfc_priv; @@ -183,6 +184,17 @@ struct ipu_soc { struct ipu_smfc_priv *smfc_priv; }; +static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset) +{ + return readl(ipu->idmac_reg + offset); +} + +static inline void ipu_idmac_write(struct ipu_soc *ipu, u32 value, + unsigned offset) +{ + writel(value, ipu->idmac_reg + offset); +} + void ipu_srm_dp_sync_update(struct ipu_soc *ipu); int ipu_module_enable(struct ipu_soc *ipu, u32 mask); |