diff options
-rw-r--r-- | drivers/gpu/drm/armada/Kconfig | 9 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/Makefile | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_crtc.c | 258 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_crtc.h | 34 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_drm.h | 16 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_drv.c | 148 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_output.c | 142 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_output.h | 33 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_overlay.c | 147 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_slave.c | 139 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_slave.h | 26 | ||||
-rw-r--r-- | drivers/gpu/drm/i2c/tda998x_drv.c | 487 |
12 files changed, 482 insertions, 960 deletions
diff --git a/drivers/gpu/drm/armada/Kconfig b/drivers/gpu/drm/armada/Kconfig index 50ae88ad4d76..eb773e9af313 100644 --- a/drivers/gpu/drm/armada/Kconfig +++ b/drivers/gpu/drm/armada/Kconfig @@ -14,12 +14,3 @@ config DRM_ARMADA This driver provides no built-in acceleration; acceleration is performed by other IP found on the SoC. This driver provides kernel mode setting and buffer management to userspace. - -config DRM_ARMADA_TDA1998X - bool "Support TDA1998X HDMI output" - depends on DRM_ARMADA != n - depends on I2C && DRM_I2C_NXP_TDA998X = y - default y - help - Support the TDA1998x HDMI output device found on the Solid-Run - CuBox. diff --git a/drivers/gpu/drm/armada/Makefile b/drivers/gpu/drm/armada/Makefile index d6f43e06150a..ffd673615772 100644 --- a/drivers/gpu/drm/armada/Makefile +++ b/drivers/gpu/drm/armada/Makefile @@ -1,6 +1,5 @@ armada-y := armada_crtc.o armada_drv.o armada_fb.o armada_fbdev.o \ - armada_gem.o armada_output.o armada_overlay.o \ - armada_slave.o + armada_gem.o armada_overlay.o armada-y += armada_510.o armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 01ffe9bffe38..cebcab560626 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -20,6 +20,7 @@ #include "armada_hw.h" struct armada_frame_work { + struct armada_plane_work work; struct drm_pending_vblank_event *event; struct armada_regs regs[4]; struct drm_framebuffer *old_fb; @@ -33,6 +34,23 @@ enum csc_mode { CSC_RGB_STUDIO = 2, }; +static const uint32_t armada_primary_formats[] = { + DRM_FORMAT_UYVY, + DRM_FORMAT_YUYV, + DRM_FORMAT_VYUY, + DRM_FORMAT_YVYU, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_RGB888, + DRM_FORMAT_BGR888, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_BGR565, +}; + /* * A note about interlacing. Let's consider HDMI 1920x1080i. * The timing parameters we have from X are: @@ -173,49 +191,82 @@ static unsigned armada_drm_crtc_calc_fb(struct drm_framebuffer *fb, return i; } -static int armada_drm_crtc_queue_frame_work(struct armada_crtc *dcrtc, - struct armada_frame_work *work) +static void armada_drm_plane_work_run(struct armada_crtc *dcrtc, + struct armada_plane *plane) +{ + struct armada_plane_work *work = xchg(&plane->work, NULL); + + /* Handle any pending frame work. */ + if (work) { + work->fn(dcrtc, plane, work); + drm_vblank_put(dcrtc->crtc.dev, dcrtc->num); + } + + wake_up(&plane->frame_wait); +} + +int armada_drm_plane_work_queue(struct armada_crtc *dcrtc, + struct armada_plane *plane, struct armada_plane_work *work) { - struct drm_device *dev = dcrtc->crtc.dev; - unsigned long flags; int ret; - ret = drm_vblank_get(dev, dcrtc->num); + ret = drm_vblank_get(dcrtc->crtc.dev, dcrtc->num); if (ret) { DRM_ERROR("failed to acquire vblank counter\n"); return ret; } - spin_lock_irqsave(&dev->event_lock, flags); - if (!dcrtc->frame_work) - dcrtc->frame_work = work; - else - ret = -EBUSY; - spin_unlock_irqrestore(&dev->event_lock, flags); - + ret = cmpxchg(&plane->work, NULL, work) ? -EBUSY : 0; if (ret) - drm_vblank_put(dev, dcrtc->num); + drm_vblank_put(dcrtc->crtc.dev, dcrtc->num); return ret; } -static void armada_drm_crtc_complete_frame_work(struct armada_crtc *dcrtc) +int armada_drm_plane_work_wait(struct armada_plane *plane, long timeout) { - struct drm_device *dev = dcrtc->crtc.dev; - struct armada_frame_work *work = dcrtc->frame_work; + return wait_event_timeout(plane->frame_wait, !plane->work, timeout); +} - dcrtc->frame_work = NULL; +struct armada_plane_work *armada_drm_plane_work_cancel( + struct armada_crtc *dcrtc, struct armada_plane *plane) +{ + struct armada_plane_work *work = xchg(&plane->work, NULL); - armada_drm_crtc_update_regs(dcrtc, work->regs); + if (work) + drm_vblank_put(dcrtc->crtc.dev, dcrtc->num); - if (work->event) - drm_send_vblank_event(dev, dcrtc->num, work->event); + return work; +} - drm_vblank_put(dev, dcrtc->num); +static int armada_drm_crtc_queue_frame_work(struct armada_crtc *dcrtc, + struct armada_frame_work *work) +{ + struct armada_plane *plane = drm_to_armada_plane(dcrtc->crtc.primary); + + return armada_drm_plane_work_queue(dcrtc, plane, &work->work); +} + +static void armada_drm_crtc_complete_frame_work(struct armada_crtc *dcrtc, + struct armada_plane *plane, struct armada_plane_work *work) +{ + struct armada_frame_work *fwork = container_of(work, struct armada_frame_work, work); + struct drm_device *dev = dcrtc->crtc.dev; + unsigned long flags; + + spin_lock_irqsave(&dcrtc->irq_lock, flags); + armada_drm_crtc_update_regs(dcrtc, fwork->regs); + spin_unlock_irqrestore(&dcrtc->irq_lock, flags); + + if (fwork->event) { + spin_lock_irqsave(&dev->event_lock, flags); + drm_send_vblank_event(dev, dcrtc->num, fwork->event); + spin_unlock_irqrestore(&dev->event_lock, flags); + } /* Finally, queue the process-half of the cleanup. */ - __armada_drm_queue_unref_work(dcrtc->crtc.dev, work->old_fb); - kfree(work); + __armada_drm_queue_unref_work(dcrtc->crtc.dev, fwork->old_fb); + kfree(fwork); } static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc, @@ -235,6 +286,7 @@ static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc, work = kmalloc(sizeof(*work), GFP_KERNEL); if (work) { int i = 0; + work->work.fn = armada_drm_crtc_complete_frame_work; work->event = NULL; work->old_fb = fb; armada_reg_queue_end(work->regs, i); @@ -255,19 +307,14 @@ static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc, static void armada_drm_vblank_off(struct armada_crtc *dcrtc) { - struct drm_device *dev = dcrtc->crtc.dev; + struct armada_plane *plane = drm_to_armada_plane(dcrtc->crtc.primary); /* * Tell the DRM core that vblank IRQs aren't going to happen for * a while. This cleans up any pending vblank events for us. */ drm_crtc_vblank_off(&dcrtc->crtc); - - /* Handle any pending flip event. */ - spin_lock_irq(&dev->event_lock); - if (dcrtc->frame_work) - armada_drm_crtc_complete_frame_work(dcrtc); - spin_unlock_irq(&dev->event_lock); + armada_drm_plane_work_run(dcrtc, plane); } void armada_drm_crtc_gamma_set(struct drm_crtc *crtc, u16 r, u16 g, u16 b, @@ -287,7 +334,11 @@ static void armada_drm_crtc_dpms(struct drm_crtc *crtc, int dpms) if (dcrtc->dpms != dpms) { dcrtc->dpms = dpms; + if (!IS_ERR(dcrtc->clk) && !dpms_blanked(dpms)) + WARN_ON(clk_prepare_enable(dcrtc->clk)); armada_drm_crtc_update(dcrtc); + if (!IS_ERR(dcrtc->clk) && dpms_blanked(dpms)) + clk_disable_unprepare(dcrtc->clk); if (dpms_blanked(dpms)) armada_drm_vblank_off(dcrtc); else @@ -310,17 +361,11 @@ static void armada_drm_crtc_prepare(struct drm_crtc *crtc) /* * If we have an overlay plane associated with this CRTC, disable * it before the modeset to avoid its coordinates being outside - * the new mode parameters. DRM doesn't provide help with this. + * the new mode parameters. */ plane = dcrtc->plane; - if (plane) { - struct drm_framebuffer *fb = plane->fb; - - plane->funcs->disable_plane(plane); - plane->fb = NULL; - plane->crtc = NULL; - drm_framebuffer_unreference(fb); - } + if (plane) + drm_plane_force_disable(plane); } /* The mode_config.mutex will be held for this call */ @@ -356,8 +401,8 @@ static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc, static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat) { - struct armada_vbl_event *e, *n; void __iomem *base = dcrtc->base; + struct drm_plane *ovl_plane; if (stat & DMA_FF_UNDERFLOW) DRM_ERROR("video underflow on crtc %u\n", dcrtc->num); @@ -368,11 +413,10 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat) drm_handle_vblank(dcrtc->crtc.dev, dcrtc->num); spin_lock(&dcrtc->irq_lock); - - list_for_each_entry_safe(e, n, &dcrtc->vbl_list, node) { - list_del_init(&e->node); - drm_vblank_put(dcrtc->crtc.dev, dcrtc->num); - e->fn(dcrtc, e->data); + ovl_plane = dcrtc->plane; + if (ovl_plane) { + struct armada_plane *plane = drm_to_armada_plane(ovl_plane); + armada_drm_plane_work_run(dcrtc, plane); } if (stat & GRA_FRAME_IRQ && dcrtc->interlaced) { @@ -404,14 +448,8 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat) spin_unlock(&dcrtc->irq_lock); if (stat & GRA_FRAME_IRQ) { - struct drm_device *dev = dcrtc->crtc.dev; - - spin_lock(&dev->event_lock); - if (dcrtc->frame_work) - armada_drm_crtc_complete_frame_work(dcrtc); - spin_unlock(&dev->event_lock); - - wake_up(&dcrtc->frame_wait); + struct armada_plane *plane = drm_to_armada_plane(dcrtc->crtc.primary); + armada_drm_plane_work_run(dcrtc, plane); } } @@ -527,7 +565,8 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, adj->crtc_vtotal, tm, bm); /* Wait for pending flips to complete */ - wait_event(dcrtc->frame_wait, !dcrtc->frame_work); + armada_drm_plane_work_wait(drm_to_armada_plane(dcrtc->crtc.primary), + MAX_SCHEDULE_TIMEOUT); drm_crtc_vblank_off(crtc); @@ -537,6 +576,13 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, writel_relaxed(val, dcrtc->base + LCD_SPU_DUMB_CTRL); } + /* + * If we are blanked, we would have disabled the clock. Re-enable + * it so that compute_clock() does the right thing. + */ + if (!IS_ERR(dcrtc->clk) && dpms_blanked(dcrtc->dpms)) + WARN_ON(clk_prepare_enable(dcrtc->clk)); + /* Now compute the divider for real */ dcrtc->variant->compute_clock(dcrtc, adj, &sclk); @@ -637,7 +683,8 @@ static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, armada_reg_queue_end(regs, i); /* Wait for pending flips to complete */ - wait_event(dcrtc->frame_wait, !dcrtc->frame_work); + armada_drm_plane_work_wait(drm_to_armada_plane(dcrtc->crtc.primary), + MAX_SCHEDULE_TIMEOUT); /* Take a reference to the new fb as we're using it */ drm_framebuffer_reference(crtc->primary->fb); @@ -651,18 +698,47 @@ static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, return 0; } +void armada_drm_crtc_plane_disable(struct armada_crtc *dcrtc, + struct drm_plane *plane) +{ + u32 sram_para1, dma_ctrl0_mask; + + /* + * Drop our reference on any framebuffer attached to this plane. + * We don't need to NULL this out as drm_plane_force_disable(), + * and __setplane_internal() will do so for an overlay plane, and + * __drm_helper_disable_unused_functions() will do so for the + * primary plane. + */ + if (plane->fb) + drm_framebuffer_unreference(plane->fb); + + /* Power down the Y/U/V FIFOs */ + sram_para1 = CFG_PDWN16x66 | CFG_PDWN32x66; + + /* Power down most RAMs and FIFOs if this is the primary plane */ + if (plane->type == DRM_PLANE_TYPE_PRIMARY) { + sram_para1 |= CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 | + CFG_PDWN32x32 | CFG_PDWN64x66; + dma_ctrl0_mask = CFG_GRA_ENA; + } else { + dma_ctrl0_mask = CFG_DMA_ENA; + } + + spin_lock_irq(&dcrtc->irq_lock); + armada_updatel(0, dma_ctrl0_mask, dcrtc->base + LCD_SPU_DMA_CTRL0); + spin_unlock_irq(&dcrtc->irq_lock); + + armada_updatel(sram_para1, 0, dcrtc->base + LCD_SPU_SRAM_PARA1); +} + /* The mode_config.mutex will be held for this call */ static void armada_drm_crtc_disable(struct drm_crtc *crtc) { struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); armada_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); - armada_drm_crtc_finish_fb(dcrtc, crtc->primary->fb, true); - - /* Power down most RAMs and FIFOs */ - writel_relaxed(CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 | - CFG_PDWN32x32 | CFG_PDWN16x66 | CFG_PDWN32x66 | - CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1); + armada_drm_crtc_plane_disable(dcrtc, crtc->primary); } static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = { @@ -920,8 +996,6 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc, { struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); struct armada_frame_work *work; - struct drm_device *dev = crtc->dev; - unsigned long flags; unsigned i; int ret; @@ -933,6 +1007,7 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc, if (!work) return -ENOMEM; + work->work.fn = armada_drm_crtc_complete_frame_work; work->event = event; work->old_fb = dcrtc->crtc.primary->fb; @@ -966,12 +1041,8 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc, * Finally, if the display is blanked, we won't receive an * interrupt, so complete it now. */ - if (dpms_blanked(dcrtc->dpms)) { - spin_lock_irqsave(&dev->event_lock, flags); - if (dcrtc->frame_work) - armada_drm_crtc_complete_frame_work(dcrtc); - spin_unlock_irqrestore(&dev->event_lock, flags); - } + if (dpms_blanked(dcrtc->dpms)) + armada_drm_plane_work_run(dcrtc, drm_to_armada_plane(dcrtc->crtc.primary)); return 0; } @@ -1012,6 +1083,19 @@ static struct drm_crtc_funcs armada_crtc_funcs = { .set_property = armada_drm_crtc_set_property, }; +static const struct drm_plane_funcs armada_primary_plane_funcs = { + .update_plane = drm_primary_helper_update, + .disable_plane = drm_primary_helper_disable, + .destroy = drm_primary_helper_destroy, +}; + +int armada_drm_plane_init(struct armada_plane *plane) +{ + init_waitqueue_head(&plane->frame_wait); + + return 0; +} + static struct drm_prop_enum_list armada_drm_csc_yuv_enum_list[] = { { CSC_AUTO, "Auto" }, { CSC_YUV_CCIR601, "CCIR601" }, @@ -1044,12 +1128,13 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev) return 0; } -int armada_drm_crtc_create(struct drm_device *drm, struct device *dev, +static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev, struct resource *res, int irq, const struct armada_variant *variant, struct device_node *port) { struct armada_private *priv = drm->dev_private; struct armada_crtc *dcrtc; + struct armada_plane *primary; void __iomem *base; int ret; @@ -1080,8 +1165,6 @@ int armada_drm_crtc_create(struct drm_device *drm, struct device *dev, dcrtc->spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24; spin_lock_init(&dcrtc->irq_lock); dcrtc->irq_ena = CLEAN_SPU_IRQ_ISR; - INIT_LIST_HEAD(&dcrtc->vbl_list); - init_waitqueue_head(&dcrtc->frame_wait); /* Initialize some registers which we don't otherwise set */ writel_relaxed(0x00000001, dcrtc->base + LCD_CFG_SCLK_DIV); @@ -1118,7 +1201,32 @@ int armada_drm_crtc_create(struct drm_device *drm, struct device *dev, priv->dcrtc[dcrtc->num] = dcrtc; dcrtc->crtc.port = port; - drm_crtc_init(drm, &dcrtc->crtc, &armada_crtc_funcs); + + primary = kzalloc(sizeof(*primary), GFP_KERNEL); + if (!primary) + return -ENOMEM; + + ret = armada_drm_plane_init(primary); + if (ret) { + kfree(primary); + return ret; + } + + ret = drm_universal_plane_init(drm, &primary->base, 0, + &armada_primary_plane_funcs, + armada_primary_formats, + ARRAY_SIZE(armada_primary_formats), + DRM_PLANE_TYPE_PRIMARY); + if (ret) { + kfree(primary); + return ret; + } + + ret = drm_crtc_init_with_planes(drm, &dcrtc->crtc, &primary->base, NULL, + &armada_crtc_funcs); + if (ret) + goto err_crtc_init; + drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs); drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop, @@ -1127,6 +1235,10 @@ int armada_drm_crtc_create(struct drm_device *drm, struct device *dev, dcrtc->csc_rgb_mode); return armada_overlay_plane_create(drm, 1 << dcrtc->num); + +err_crtc_init: + primary->base.funcs->destroy(&primary->base); + return ret; } static int diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h index 98102a5a9af5..04fdd22d483b 100644 --- a/drivers/gpu/drm/armada/armada_crtc.h +++ b/drivers/gpu/drm/armada/armada_crtc.h @@ -31,9 +31,30 @@ struct armada_regs { #define armada_reg_queue_end(_r, _i) \ armada_reg_queue_mod(_r, _i, 0, 0, ~0) -struct armada_frame_work; +struct armada_crtc; +struct armada_plane; struct armada_variant; +struct armada_plane_work { + void (*fn)(struct armada_crtc *, + struct armada_plane *, + struct armada_plane_work *); +}; + +struct armada_plane { + struct drm_plane base; + wait_queue_head_t frame_wait; + struct armada_plane_work *work; +}; +#define drm_to_armada_plane(p) container_of(p, struct armada_plane, base) + +int armada_drm_plane_init(struct armada_plane *plane); +int armada_drm_plane_work_queue(struct armada_crtc *dcrtc, + struct armada_plane *plane, struct armada_plane_work *work); +int armada_drm_plane_work_wait(struct armada_plane *plane, long timeout); +struct armada_plane_work *armada_drm_plane_work_cancel( + struct armada_crtc *dcrtc, struct armada_plane *plane); + struct armada_crtc { struct drm_crtc crtc; const struct armada_variant *variant; @@ -66,25 +87,20 @@ struct armada_crtc { uint32_t dumb_ctrl; uint32_t spu_iopad_ctrl; - wait_queue_head_t frame_wait; - struct armada_frame_work *frame_work; - spinlock_t irq_lock; uint32_t irq_ena; - struct list_head vbl_list; }; #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc) -struct device_node; -int armada_drm_crtc_create(struct drm_device *, struct device *, - struct resource *, int, const struct armada_variant *, - struct device_node *); void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int); void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int); void armada_drm_crtc_disable_irq(struct armada_crtc *, u32); void armada_drm_crtc_enable_irq(struct armada_crtc *, u32); void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *); +void armada_drm_crtc_plane_disable(struct armada_crtc *dcrtc, + struct drm_plane *plane); + extern struct platform_driver armada_lcd_platform_driver; #endif diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h index 5f6aef0dca59..4df6f2af2b21 100644 --- a/drivers/gpu/drm/armada/armada_drm.h +++ b/drivers/gpu/drm/armada/armada_drm.h @@ -37,22 +37,6 @@ static inline uint32_t armada_pitch(uint32_t width, uint32_t bpp) return ALIGN(pitch, 128); } -struct armada_vbl_event { - struct list_head node; - void *data; - void (*fn)(struct armada_crtc *, void *); -}; -void armada_drm_vbl_event_add(struct armada_crtc *, - struct armada_vbl_event *); -void armada_drm_vbl_event_remove(struct armada_crtc *, - struct armada_vbl_event *); -#define armada_drm_vbl_event_init(_e, _f, _d) do { \ - struct armada_vbl_event *__e = _e; \ - INIT_LIST_HEAD(&__e->node); \ - __e->data = _d; \ - __e->fn = _f; \ -} while (0) - struct armada_private; diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 225034b74cda..3f1396e673dd 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -18,47 +18,6 @@ #include <drm/armada_drm.h> #include "armada_ioctlP.h" -#ifdef CONFIG_DRM_ARMADA_TDA1998X -#include <drm/i2c/tda998x.h> -#include "armada_slave.h" - -static struct tda998x_encoder_params params = { - /* With 0x24, there is no translation between vp_out and int_vp - FB LCD out Pins VIP Int Vp - R:23:16 R:7:0 VPC7:0 7:0 7:0[R] - G:15:8 G:15:8 VPB7:0 23:16 23:16[G] - B:7:0 B:23:16 VPA7:0 15:8 15:8[B] - */ - .swap_a = 2, - .swap_b = 3, - .swap_c = 4, - .swap_d = 5, - .swap_e = 0, - .swap_f = 1, - .audio_cfg = BIT(2), - .audio_frame[1] = 1, - .audio_format = AFMT_SPDIF, - .audio_sample_rate = 44100, -}; - -static const struct armada_drm_slave_config tda19988_config = { - .i2c_adapter_id = 0, - .crtcs = 1 << 0, /* Only LCD0 at the moment */ - .polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT, - .interlace_allowed = true, - .info = { - .type = "tda998x", - .addr = 0x70, - .platform_data = ¶ms, - }, -}; -#endif - -static bool is_componentized(struct device *dev) -{ - return dev->of_node || dev->platform_data; -} - static void armada_drm_unref_work(struct work_struct *work) { struct armada_private *priv = @@ -91,16 +50,11 @@ void armada_drm_queue_unref_work(struct drm_device *dev, static int armada_drm_load(struct drm_device *dev, unsigned long flags) { - const struct platform_device_id *id; - const struct armada_variant *variant; struct armada_private *priv; - struct resource *res[ARRAY_SIZE(priv->dcrtc)]; struct resource *mem = NULL; - int ret, n, i; - - memset(res, 0, sizeof(res)); + int ret, n; - for (n = i = 0; ; n++) { + for (n = 0; ; n++) { struct resource *r = platform_get_resource(dev->platformdev, IORESOURCE_MEM, n); if (!r) @@ -109,8 +63,6 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) /* Resources above 64K are graphics memory */ if (resource_size(r) > SZ_64K) mem = r; - else if (i < ARRAY_SIZE(priv->dcrtc)) - res[i++] = r; else return -EINVAL; } @@ -131,13 +83,6 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) platform_set_drvdata(dev->platformdev, dev); dev->dev_private = priv; - /* Get the implementation specific driver data. */ - id = platform_get_device_id(dev->platformdev); - if (!id) - return -ENXIO; - - variant = (const struct armada_variant *)id->driver_data; - INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work); INIT_KFIFO(priv->fb_unref); @@ -157,34 +102,9 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) dev->mode_config.funcs = &armada_drm_mode_config_funcs; drm_mm_init(&priv->linear, mem->start, resource_size(mem)); - /* Create all LCD controllers */ - for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) { - int irq; - - if (!res[n]) - break; - - irq = platform_get_irq(dev->platformdev, n); - if (irq < 0) - goto err_kms; - - ret = armada_drm_crtc_create(dev, dev->dev, res[n], irq, - variant, NULL); - if (ret) - goto err_kms; - } - - if (is_componentized(dev->dev)) { - ret = component_bind_all(dev->dev, dev); - if (ret) - goto err_kms; - } else { -#ifdef CONFIG_DRM_ARMADA_TDA1998X - ret = armada_drm_connector_slave_create(dev, &tda19988_config); - if (ret) - goto err_kms; -#endif - } + ret = component_bind_all(dev->dev, dev); + if (ret) + goto err_kms; ret = drm_vblank_init(dev, dev->mode_config.num_crtc); if (ret) @@ -202,8 +122,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) return 0; err_comp: - if (is_componentized(dev->dev)) - component_unbind_all(dev->dev, dev); + component_unbind_all(dev->dev, dev); err_kms: drm_mode_config_cleanup(dev); drm_mm_takedown(&priv->linear); @@ -219,8 +138,7 @@ static int armada_drm_unload(struct drm_device *dev) drm_kms_helper_poll_fini(dev); armada_fbdev_fini(dev); - if (is_componentized(dev->dev)) - component_unbind_all(dev->dev, dev); + component_unbind_all(dev->dev, dev); drm_mode_config_cleanup(dev); drm_mm_takedown(&priv->linear); @@ -230,29 +148,6 @@ static int armada_drm_unload(struct drm_device *dev) return 0; } -void armada_drm_vbl_event_add(struct armada_crtc *dcrtc, - struct armada_vbl_event *evt) -{ - unsigned long flags; - - spin_lock_irqsave(&dcrtc->irq_lock, flags); - if (list_empty(&evt->node)) { - list_add_tail(&evt->node, &dcrtc->vbl_list); - - drm_vblank_get(dcrtc->crtc.dev, dcrtc->num); - } - spin_unlock_irqrestore(&dcrtc->irq_lock, flags); -} - -void armada_drm_vbl_event_remove(struct armada_crtc *dcrtc, - struct armada_vbl_event *evt) -{ - if (!list_empty(&evt->node)) { - list_del_init(&evt->node); - drm_vblank_put(dcrtc->crtc.dev, dcrtc->num); - } -} - /* These are called under the vbl_lock. */ static int armada_drm_enable_vblank(struct drm_device *dev, int crtc) { @@ -435,37 +330,28 @@ static const struct component_master_ops armada_master_ops = { static int armada_drm_probe(struct platform_device *pdev) { - if (is_componentized(&pdev->dev)) { - struct component_match *match = NULL; - int ret; - - ret = armada_drm_find_components(&pdev->dev, &match); - if (ret < 0) - return ret; - - return component_master_add_with_match(&pdev->dev, - &armada_master_ops, match); - } else { - return drm_platform_init(&armada_drm_driver, pdev); - } + struct component_match *match = NULL; + int ret; + + ret = armada_drm_find_components(&pdev->dev, &match); + if (ret < 0) + return ret; + + return component_master_add_with_match(&pdev->dev, &armada_master_ops, + match); } static int armada_drm_remove(struct platform_device *pdev) { - if (is_componentized(&pdev->dev)) - component_master_del(&pdev->dev, &armada_master_ops); - else - drm_put_dev(platform_get_drvdata(pdev)); + component_master_del(&pdev->dev, &armada_master_ops); return 0; } static const struct platform_device_id armada_drm_platform_ids[] = { { .name = "armada-drm", - .driver_data = (unsigned long)&armada510_ops, }, { .name = "armada-510-drm", - .driver_data = (unsigned long)&armada510_ops, }, { }, }; diff --git a/drivers/gpu/drm/armada/armada_output.c b/drivers/gpu/drm/armada/armada_output.c deleted file mode 100644 index 5a9823178291..000000000000 --- a/drivers/gpu/drm/armada/armada_output.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2012 Russell King - * - * 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 <drm/drmP.h> -#include <drm/drm_crtc_helper.h> -#include <drm/drm_edid.h> -#include <drm/drm_encoder_slave.h> -#include "armada_output.h" -#include "armada_drm.h" - -struct armada_connector { - struct drm_connector conn; - const struct armada_output_type *type; -}; - -#define drm_to_armada_conn(c) container_of(c, struct armada_connector, conn) - -struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn) -{ - struct drm_encoder *enc = conn->encoder; - - return enc ? enc : drm_encoder_find(conn->dev, conn->encoder_ids[0]); -} - -static enum drm_connector_status armada_drm_connector_detect( - struct drm_connector *conn, bool force) -{ - struct armada_connector *dconn = drm_to_armada_conn(conn); - enum drm_connector_status status = connector_status_disconnected; - - if (dconn->type->detect) { - status = dconn->type->detect(conn, force); - } else { - struct drm_encoder *enc = armada_drm_connector_encoder(conn); - - if (enc) - status = encoder_helper_funcs(enc)->detect(enc, conn); - } - - return status; -} - -static void armada_drm_connector_destroy(struct drm_connector *conn) -{ - struct armada_connector *dconn = drm_to_armada_conn(conn); - - drm_connector_unregister(conn); - drm_connector_cleanup(conn); - kfree(dconn); -} - -static int armada_drm_connector_set_property(struct drm_connector *conn, - struct drm_property *property, uint64_t value) -{ - struct armada_connector *dconn = drm_to_armada_conn(conn); - - if (!dconn->type->set_property) - return -EINVAL; - - return dconn->type->set_property(conn, property, value); -} - -static const struct drm_connector_funcs armada_drm_conn_funcs = { - .dpms = drm_helper_connector_dpms, - .fill_modes = drm_helper_probe_single_connector_modes, - .detect = armada_drm_connector_detect, - .destroy = armada_drm_connector_destroy, - .set_property = armada_drm_connector_set_property, -}; - -/* Shouldn't this be a generic helper function? */ -int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn, - struct drm_display_mode *mode) -{ - struct drm_encoder *encoder = armada_drm_connector_encoder(conn); - int valid = MODE_BAD; - - if (encoder) { - struct drm_encoder_slave *slave = to_encoder_slave(encoder); - - valid = slave->slave_funcs->mode_valid(encoder, mode); - } - return valid; -} - -int armada_drm_slave_encoder_set_property(struct drm_connector *conn, - struct drm_property *property, uint64_t value) -{ - struct drm_encoder *encoder = armada_drm_connector_encoder(conn); - int rc = -EINVAL; - - if (encoder) { - struct drm_encoder_slave *slave = to_encoder_slave(encoder); - - rc = slave->slave_funcs->set_property(encoder, conn, property, - value); - } - return rc; -} - -int armada_output_create(struct drm_device *dev, - const struct armada_output_type *type, const void *data) -{ - struct armada_connector *dconn; - int ret; - - dconn = kzalloc(sizeof(*dconn), GFP_KERNEL); - if (!dconn) - return -ENOMEM; - - dconn->type = type; - - ret = drm_connector_init(dev, &dconn->conn, &armada_drm_conn_funcs, - type->connector_type); - if (ret) { - DRM_ERROR("unable to init connector\n"); - goto err_destroy_dconn; - } - - ret = type->create(&dconn->conn, data); - if (ret) - goto err_conn; - - ret = drm_connector_register(&dconn->conn); - if (ret) - goto err_sysfs; - - return 0; - - err_sysfs: - if (dconn->conn.encoder) - dconn->conn.encoder->funcs->destroy(dconn->conn.encoder); - err_conn: - drm_connector_cleanup(&dconn->conn); - err_destroy_dconn: - kfree(dconn); - return ret; -} diff --git a/drivers/gpu/drm/armada/armada_output.h b/drivers/gpu/drm/armada/armada_output.h deleted file mode 100644 index f448785753e8..000000000000 --- a/drivers/gpu/drm/armada/armada_output.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2012 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#ifndef ARMADA_CONNETOR_H -#define ARMADA_CONNETOR_H - -#define encoder_helper_funcs(encoder) \ - ((const struct drm_encoder_helper_funcs *)encoder->helper_private) - -struct armada_output_type { - int connector_type; - enum drm_connector_status (*detect)(struct drm_connector *, bool); - int (*create)(struct drm_connector *, const void *); - int (*set_property)(struct drm_connector *, struct drm_property *, - uint64_t); -}; - -struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn); - -int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn, - struct drm_display_mode *mode); - -int armada_drm_slave_encoder_set_property(struct drm_connector *conn, - struct drm_property *property, uint64_t value); - -int armada_output_create(struct drm_device *dev, - const struct armada_output_type *type, const void *data); - -#endif diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c index e939faba7fcc..5c22b380f8f3 100644 --- a/drivers/gpu/drm/armada/armada_overlay.c +++ b/drivers/gpu/drm/armada/armada_overlay.c @@ -16,7 +16,7 @@ #include <drm/armada_drm.h> #include "armada_ioctlP.h" -struct armada_plane_properties { +struct armada_ovl_plane_properties { uint32_t colorkey_yr; uint32_t colorkey_ug; uint32_t colorkey_vb; @@ -29,26 +29,25 @@ struct armada_plane_properties { uint32_t colorkey_mode; }; -struct armada_plane { - struct drm_plane base; - spinlock_t lock; +struct armada_ovl_plane { + struct armada_plane base; struct drm_framebuffer *old_fb; uint32_t src_hw; uint32_t dst_hw; uint32_t dst_yx; uint32_t ctrl0; struct { - struct armada_vbl_event update; + struct armada_plane_work work; struct armada_regs regs[13]; - wait_queue_head_t wait; } vbl; - struct armada_plane_properties prop; + struct armada_ovl_plane_properties prop; }; -#define drm_to_armada_plane(p) container_of(p, struct armada_plane, base) +#define drm_to_armada_ovl_plane(p) \ + container_of(p, struct armada_ovl_plane, base.base) static void -armada_ovl_update_attr(struct armada_plane_properties *prop, +armada_ovl_update_attr(struct armada_ovl_plane_properties *prop, struct armada_crtc *dcrtc) { writel_relaxed(prop->colorkey_yr, dcrtc->base + LCD_SPU_COLORKEY_Y); @@ -71,32 +70,34 @@ armada_ovl_update_attr(struct armada_plane_properties *prop, spin_unlock_irq(&dcrtc->irq_lock); } -/* === Plane support === */ -static void armada_plane_vbl(struct armada_crtc *dcrtc, void *data) +static void armada_ovl_retire_fb(struct armada_ovl_plane *dplane, + struct drm_framebuffer *fb) { - struct armada_plane *dplane = data; - struct drm_framebuffer *fb; + struct drm_framebuffer *old_fb; - armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs); + old_fb = xchg(&dplane->old_fb, fb); - spin_lock(&dplane->lock); - fb = dplane->old_fb; - dplane->old_fb = NULL; - spin_unlock(&dplane->lock); + if (old_fb) + armada_drm_queue_unref_work(dplane->base.base.dev, old_fb); +} - if (fb) - armada_drm_queue_unref_work(dcrtc->crtc.dev, fb); +/* === Plane support === */ +static void armada_ovl_plane_work(struct armada_crtc *dcrtc, + struct armada_plane *plane, struct armada_plane_work *work) +{ + struct armada_ovl_plane *dplane = container_of(plane, struct armada_ovl_plane, base); - wake_up(&dplane->vbl.wait); + armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs); + armada_ovl_retire_fb(dplane, NULL); } static int -armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, +armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int crtc_x, int crtc_y, unsigned crtc_w, unsigned crtc_h, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h) { - struct armada_plane *dplane = drm_to_armada_plane(plane); + struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane); struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); struct drm_rect src = { .x1 = src_x, @@ -160,9 +161,8 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, dcrtc->base + LCD_SPU_SRAM_PARA1); } - wait_event_timeout(dplane->vbl.wait, - list_empty(&dplane->vbl.update.node), - HZ/25); + if (armada_drm_plane_work_wait(&dplane->base, HZ / 25) == 0) + armada_drm_plane_work_cancel(dcrtc, &dplane->base); if (plane->fb != fb) { struct armada_gem_object *obj = drm_fb_obj(fb); @@ -175,17 +175,8 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, */ drm_framebuffer_reference(fb); - if (plane->fb) { - struct drm_framebuffer *older_fb; - - spin_lock_irq(&dplane->lock); - older_fb = dplane->old_fb; - dplane->old_fb = plane->fb; - spin_unlock_irq(&dplane->lock); - if (older_fb) - armada_drm_queue_unref_work(dcrtc->crtc.dev, - older_fb); - } + if (plane->fb) + armada_ovl_retire_fb(dplane, plane->fb); src_y = src.y1 >> 16; src_x = src.x1 >> 16; @@ -262,60 +253,50 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, } if (idx) { armada_reg_queue_end(dplane->vbl.regs, idx); - armada_drm_vbl_event_add(dcrtc, &dplane->vbl.update); + armada_drm_plane_work_queue(dcrtc, &dplane->base, + &dplane->vbl.work); } return 0; } -static int armada_plane_disable(struct drm_plane *plane) +static int armada_ovl_plane_disable(struct drm_plane *plane) { - struct armada_plane *dplane = drm_to_armada_plane(plane); + struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane); struct drm_framebuffer *fb; struct armada_crtc *dcrtc; - if (!dplane->base.crtc) + if (!dplane->base.base.crtc) return 0; - dcrtc = drm_to_armada_crtc(dplane->base.crtc); - dcrtc->plane = NULL; - - spin_lock_irq(&dcrtc->irq_lock); - armada_drm_vbl_event_remove(dcrtc, &dplane->vbl.update); - armada_updatel(0, CFG_DMA_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0); - dplane->ctrl0 = 0; - spin_unlock_irq(&dcrtc->irq_lock); + dcrtc = drm_to_armada_crtc(dplane->base.base.crtc); - /* Power down the Y/U/V FIFOs */ - armada_updatel(CFG_PDWN16x66 | CFG_PDWN32x66, 0, - dcrtc->base + LCD_SPU_SRAM_PARA1); + armada_drm_plane_work_cancel(dcrtc, &dplane->base); + armada_drm_crtc_plane_disable(dcrtc, plane); - if (plane->fb) - drm_framebuffer_unreference(plane->fb); + dcrtc->plane = NULL; + dplane->ctrl0 = 0; - spin_lock_irq(&dplane->lock); - fb = dplane->old_fb; - dplane->old_fb = NULL; - spin_unlock_irq(&dplane->lock); + fb = xchg(&dplane->old_fb, NULL); if (fb) drm_framebuffer_unreference(fb); return 0; } -static void armada_plane_destroy(struct drm_plane *plane) +static void armada_ovl_plane_destroy(struct drm_plane *plane) { - struct armada_plane *dplane = drm_to_armada_plane(plane); + struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane); drm_plane_cleanup(plane); kfree(dplane); } -static int armada_plane_set_property(struct drm_plane *plane, +static int armada_ovl_plane_set_property(struct drm_plane *plane, struct drm_property *property, uint64_t val) { struct armada_private *priv = plane->dev->dev_private; - struct armada_plane *dplane = drm_to_armada_plane(plane); + struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane); bool update_attr = false; if (property == priv->colorkey_prop) { @@ -372,21 +353,21 @@ static int armada_plane_set_property(struct drm_plane *plane, update_attr = true; } - if (update_attr && dplane->base.crtc) + if (update_attr && dplane->base.base.crtc) armada_ovl_update_attr(&dplane->prop, - drm_to_armada_crtc(dplane->base.crtc)); + drm_to_armada_crtc(dplane->base.base.crtc)); return 0; } -static const struct drm_plane_funcs armada_plane_funcs = { - .update_plane = armada_plane_update, - .disable_plane = armada_plane_disable, - .destroy = armada_plane_destroy, - .set_property = armada_plane_set_property, +static const struct drm_plane_funcs armada_ovl_plane_funcs = { + .update_plane = armada_ovl_plane_update, + .disable_plane = armada_ovl_plane_disable, + .destroy = armada_ovl_plane_destroy, + .set_property = armada_ovl_plane_set_property, }; -static const uint32_t armada_formats[] = { +static const uint32_t armada_ovl_formats[] = { DRM_FORMAT_UYVY, DRM_FORMAT_YUYV, DRM_FORMAT_YUV420, @@ -456,7 +437,7 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs) { struct armada_private *priv = dev->dev_private; struct drm_mode_object *mobj; - struct armada_plane *dplane; + struct armada_ovl_plane *dplane; int ret; ret = armada_overlay_create_properties(dev); @@ -467,13 +448,23 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs) if (!dplane) return -ENOMEM; - spin_lock_init(&dplane->lock); - init_waitqueue_head(&dplane->vbl.wait); - armada_drm_vbl_event_init(&dplane->vbl.update, armada_plane_vbl, - dplane); + ret = armada_drm_plane_init(&dplane->base); + if (ret) { + kfree(dplane); + return ret; + } + + dplane->vbl.work.fn = armada_ovl_plane_work; - drm_plane_init(dev, &dplane->base, crtcs, &armada_plane_funcs, - armada_formats, ARRAY_SIZE(armada_formats), false); + ret = drm_universal_plane_init(dev, &dplane->base.base, crtcs, + &armada_ovl_plane_funcs, + armada_ovl_formats, + ARRAY_SIZE(armada_ovl_formats), + DRM_PLANE_TYPE_OVERLAY); + if (ret) { + kfree(dplane); + return ret; + } dplane->prop.colorkey_yr = 0xfefefe00; dplane->prop.colorkey_ug = 0x01010100; @@ -483,7 +474,7 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs) dplane->prop.contrast = 0x4000; dplane->prop.saturation = 0x4000; - mobj = &dplane->base.base; + mobj = &dplane->base.base.base; drm_object_attach_property(mobj, priv->colorkey_prop, 0x0101fe); drm_object_attach_property(mobj, priv->colorkey_min_prop, diff --git a/drivers/gpu/drm/armada/armada_slave.c b/drivers/gpu/drm/armada/armada_slave.c deleted file mode 100644 index 00d0facb42f3..000000000000 --- a/drivers/gpu/drm/armada/armada_slave.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2012 Russell King - * Rewritten from the dovefb driver, and Armada510 manuals. - * - * 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 <drm/drmP.h> -#include <drm/drm_crtc_helper.h> -#include <drm/drm_edid.h> -#include <drm/drm_encoder_slave.h> -#include "armada_drm.h" -#include "armada_output.h" -#include "armada_slave.h" - -static int armada_drm_slave_get_modes(struct drm_connector *conn) -{ - struct drm_encoder *enc = armada_drm_connector_encoder(conn); - int count = 0; - - if (enc) { - struct drm_encoder_slave *slave = to_encoder_slave(enc); - - count = slave->slave_funcs->get_modes(enc, conn); - } - - return count; -} - -static void armada_drm_slave_destroy(struct drm_encoder *enc) -{ - struct drm_encoder_slave *slave = to_encoder_slave(enc); - struct i2c_client *client = drm_i2c_encoder_get_client(enc); - - if (slave->slave_funcs) - slave->slave_funcs->destroy(enc); - if (client) - i2c_put_adapter(client->adapter); - - drm_encoder_cleanup(&slave->base); - kfree(slave); -} - -static const struct drm_encoder_funcs armada_drm_slave_encoder_funcs = { - .destroy = armada_drm_slave_destroy, -}; - -static const struct drm_connector_helper_funcs armada_drm_slave_helper_funcs = { - .get_modes = armada_drm_slave_get_modes, - .mode_valid = armada_drm_slave_encoder_mode_valid, - .best_encoder = armada_drm_connector_encoder, -}; - -static const struct drm_encoder_helper_funcs drm_slave_encoder_helpers = { - .dpms = drm_i2c_encoder_dpms, - .save = drm_i2c_encoder_save, - .restore = drm_i2c_encoder_restore, - .mode_fixup = drm_i2c_encoder_mode_fixup, - .prepare = drm_i2c_encoder_prepare, - .commit = drm_i2c_encoder_commit, - .mode_set = drm_i2c_encoder_mode_set, - .detect = drm_i2c_encoder_detect, -}; - -static int -armada_drm_conn_slave_create(struct drm_connector *conn, const void *data) -{ - const struct armada_drm_slave_config *config = data; - struct drm_encoder_slave *slave; - struct i2c_adapter *adap; - int ret; - - conn->interlace_allowed = config->interlace_allowed; - conn->doublescan_allowed = config->doublescan_allowed; - conn->polled = config->polled; - - drm_connector_helper_add(conn, &armada_drm_slave_helper_funcs); - - slave = kzalloc(sizeof(*slave), GFP_KERNEL); - if (!slave) - return -ENOMEM; - - slave->base.possible_crtcs = config->crtcs; - - adap = i2c_get_adapter(config->i2c_adapter_id); - if (!adap) { - kfree(slave); - return -EPROBE_DEFER; - } - - ret = drm_encoder_init(conn->dev, &slave->base, - &armada_drm_slave_encoder_funcs, - DRM_MODE_ENCODER_TMDS); - if (ret) { - DRM_ERROR("unable to init encoder\n"); - i2c_put_adapter(adap); - kfree(slave); - return ret; - } - - ret = drm_i2c_encoder_init(conn->dev, slave, adap, &config->info); - i2c_put_adapter(adap); - if (ret) { - DRM_ERROR("unable to init encoder slave\n"); - armada_drm_slave_destroy(&slave->base); - return ret; - } - - drm_encoder_helper_add(&slave->base, &drm_slave_encoder_helpers); - - ret = slave->slave_funcs->create_resources(&slave->base, conn); - if (ret) { - armada_drm_slave_destroy(&slave->base); - return ret; - } - - ret = drm_mode_connector_attach_encoder(conn, &slave->base); - if (ret) { - armada_drm_slave_destroy(&slave->base); - return ret; - } - - conn->encoder = &slave->base; - - return ret; -} - -static const struct armada_output_type armada_drm_conn_slave = { - .connector_type = DRM_MODE_CONNECTOR_HDMIA, - .create = armada_drm_conn_slave_create, - .set_property = armada_drm_slave_encoder_set_property, -}; - -int armada_drm_connector_slave_create(struct drm_device *dev, - const struct armada_drm_slave_config *config) -{ - return armada_output_create(dev, &armada_drm_conn_slave, config); -} diff --git a/drivers/gpu/drm/armada/armada_slave.h b/drivers/gpu/drm/armada/armada_slave.h deleted file mode 100644 index bf2374c96fc1..000000000000 --- a/drivers/gpu/drm/armada/armada_slave.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2012 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#ifndef ARMADA_SLAVE_H -#define ARMADA_SLAVE_H - -#include <linux/i2c.h> -#include <drm/drmP.h> - -struct armada_drm_slave_config { - int i2c_adapter_id; - uint32_t crtcs; - uint8_t polled; - bool interlace_allowed; - bool doublescan_allowed; - struct i2c_board_info info; -}; - -int armada_drm_connector_slave_create(struct drm_device *dev, - const struct armada_drm_slave_config *); - -#endif diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c index 424228be79ae..896b6aaf8c4d 100644 --- a/drivers/gpu/drm/i2c/tda998x_drv.c +++ b/drivers/gpu/drm/i2c/tda998x_drv.c @@ -23,7 +23,6 @@ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> -#include <drm/drm_encoder_slave.h> #include <drm/drm_edid.h> #include <drm/drm_of.h> #include <drm/i2c/tda998x.h> @@ -34,9 +33,8 @@ struct tda998x_priv { struct i2c_client *cec; struct i2c_client *hdmi; struct mutex mutex; - struct delayed_work dwork; - uint16_t rev; - uint8_t current_page; + u16 rev; + u8 current_page; int dpms; bool is_hdmi_sink; u8 vip_cntrl_0; @@ -46,10 +44,21 @@ struct tda998x_priv { wait_queue_head_t wq_edid; volatile int wq_edid_wait; - struct drm_encoder *encoder; + + struct work_struct detect_work; + struct timer_list edid_delay_timer; + wait_queue_head_t edid_delay_waitq; + bool edid_delay_active; + + struct drm_encoder encoder; + struct drm_connector connector; }; -#define to_tda998x_priv(x) ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv) +#define conn_to_tda998x_priv(x) \ + container_of(x, struct tda998x_priv, connector) + +#define enc_to_tda998x_priv(x) \ + container_of(x, struct tda998x_priv, encoder) /* The TDA9988 series of devices use a paged register scheme.. to simplify * things we encode the page # in upper bits of the register #. To read/ @@ -326,6 +335,8 @@ struct tda998x_priv { # define CEC_FRO_IM_CLK_CTRL_FRO_DIV (1 << 0) #define REG_CEC_RXSHPDINTENA 0xfc /* read/write */ #define REG_CEC_RXSHPDINT 0xfd /* read */ +# define CEC_RXSHPDINT_RXSENS BIT(0) +# define CEC_RXSHPDINT_HPD BIT(1) #define REG_CEC_RXSHPDLEV 0xfe /* read */ # define CEC_RXSHPDLEV_RXSENS (1 << 0) # define CEC_RXSHPDLEV_HPD (1 << 1) @@ -345,10 +356,10 @@ struct tda998x_priv { #define TDA19988 0x0301 static void -cec_write(struct tda998x_priv *priv, uint16_t addr, uint8_t val) +cec_write(struct tda998x_priv *priv, u16 addr, u8 val) { struct i2c_client *client = priv->cec; - uint8_t buf[] = {addr, val}; + u8 buf[] = {addr, val}; int ret; ret = i2c_master_send(client, buf, sizeof(buf)); @@ -356,11 +367,11 @@ cec_write(struct tda998x_priv *priv, uint16_t addr, uint8_t val) dev_err(&client->dev, "Error %d writing to cec:0x%x\n", ret, addr); } -static uint8_t -cec_read(struct tda998x_priv *priv, uint8_t addr) +static u8 +cec_read(struct tda998x_priv *priv, u8 addr) { struct i2c_client *client = priv->cec; - uint8_t val; + u8 val; int ret; ret = i2c_master_send(client, &addr, sizeof(addr)); @@ -379,11 +390,11 @@ fail: } static int -set_page(struct tda998x_priv *priv, uint16_t reg) +set_page(struct tda998x_priv *priv, u16 reg) { if (REG2PAGE(reg) != priv->current_page) { struct i2c_client *client = priv->hdmi; - uint8_t buf[] = { + u8 buf[] = { REG_CURPAGE, REG2PAGE(reg) }; int ret = i2c_master_send(client, buf, sizeof(buf)); @@ -399,10 +410,10 @@ set_page(struct tda998x_priv *priv, uint16_t reg) } static int -reg_read_range(struct tda998x_priv *priv, uint16_t reg, char *buf, int cnt) +reg_read_range(struct tda998x_priv *priv, u16 reg, char *buf, int cnt) { struct i2c_client *client = priv->hdmi; - uint8_t addr = REG2ADDR(reg); + u8 addr = REG2ADDR(reg); int ret; mutex_lock(&priv->mutex); @@ -428,10 +439,10 @@ out: } static void -reg_write_range(struct tda998x_priv *priv, uint16_t reg, uint8_t *p, int cnt) +reg_write_range(struct tda998x_priv *priv, u16 reg, u8 *p, int cnt) { struct i2c_client *client = priv->hdmi; - uint8_t buf[cnt+1]; + u8 buf[cnt+1]; int ret; buf[0] = REG2ADDR(reg); @@ -450,9 +461,9 @@ out: } static int -reg_read(struct tda998x_priv *priv, uint16_t reg) +reg_read(struct tda998x_priv *priv, u16 reg) { - uint8_t val = 0; + u8 val = 0; int ret; ret = reg_read_range(priv, reg, &val, sizeof(val)); @@ -462,10 +473,10 @@ reg_read(struct tda998x_priv *priv, uint16_t reg) } static void -reg_write(struct tda998x_priv *priv, uint16_t reg, uint8_t val) +reg_write(struct tda998x_priv *priv, u16 reg, u8 val) { struct i2c_client *client = priv->hdmi; - uint8_t buf[] = {REG2ADDR(reg), val}; + u8 buf[] = {REG2ADDR(reg), val}; int ret; mutex_lock(&priv->mutex); @@ -481,10 +492,10 @@ out: } static void -reg_write16(struct tda998x_priv *priv, uint16_t reg, uint16_t val) +reg_write16(struct tda998x_priv *priv, u16 reg, u16 val) { struct i2c_client *client = priv->hdmi; - uint8_t buf[] = {REG2ADDR(reg), val >> 8, val}; + u8 buf[] = {REG2ADDR(reg), val >> 8, val}; int ret; mutex_lock(&priv->mutex); @@ -500,7 +511,7 @@ out: } static void -reg_set(struct tda998x_priv *priv, uint16_t reg, uint8_t val) +reg_set(struct tda998x_priv *priv, u16 reg, u8 val) { int old_val; @@ -510,7 +521,7 @@ reg_set(struct tda998x_priv *priv, uint16_t reg, uint8_t val) } static void -reg_clear(struct tda998x_priv *priv, uint16_t reg, uint8_t val) +reg_clear(struct tda998x_priv *priv, u16 reg, u8 val) { int old_val; @@ -551,15 +562,50 @@ tda998x_reset(struct tda998x_priv *priv) reg_write(priv, REG_MUX_VP_VIP_OUT, 0x24); } -/* handle HDMI connect/disconnect */ -static void tda998x_hpd(struct work_struct *work) +/* + * The TDA998x has a problem when trying to read the EDID close to a + * HPD assertion: it needs a delay of 100ms to avoid timing out while + * trying to read EDID data. + * + * However, tda998x_encoder_get_modes() may be called at any moment + * after tda998x_connector_detect() indicates that we are connected, so + * we need to delay probing modes in tda998x_encoder_get_modes() after + * we have seen a HPD inactive->active transition. This code implements + * that delay. + */ +static void tda998x_edid_delay_done(unsigned long data) +{ + struct tda998x_priv *priv = (struct tda998x_priv *)data; + + priv->edid_delay_active = false; + wake_up(&priv->edid_delay_waitq); + schedule_work(&priv->detect_work); +} + +static void tda998x_edid_delay_start(struct tda998x_priv *priv) +{ + priv->edid_delay_active = true; + mod_timer(&priv->edid_delay_timer, jiffies + HZ/10); +} + +static int tda998x_edid_delay_wait(struct tda998x_priv *priv) +{ + return wait_event_killable(priv->edid_delay_waitq, !priv->edid_delay_active); +} + +/* + * We need to run the KMS hotplug event helper outside of our threaded + * interrupt routine as this can call back into our get_modes method, + * which will want to make use of interrupts. + */ +static void tda998x_detect_work(struct work_struct *work) { - struct delayed_work *dwork = to_delayed_work(work); struct tda998x_priv *priv = - container_of(dwork, struct tda998x_priv, dwork); + container_of(work, struct tda998x_priv, detect_work); + struct drm_device *dev = priv->encoder.dev; - if (priv->encoder && priv->encoder->dev) - drm_kms_helper_hotplug_event(priv->encoder->dev); + if (dev) + drm_kms_helper_hotplug_event(dev); } /* @@ -569,9 +615,8 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data) { struct tda998x_priv *priv = data; u8 sta, cec, lvl, flag0, flag1, flag2; + bool handled = false; - if (!priv) - return IRQ_HANDLED; sta = cec_read(priv, REG_CEC_INTSTATUS); cec = cec_read(priv, REG_CEC_RXSHPDINT); lvl = cec_read(priv, REG_CEC_RXSHPDLEV); @@ -581,75 +626,76 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data) DRM_DEBUG_DRIVER( "tda irq sta %02x cec %02x lvl %02x f0 %02x f1 %02x f2 %02x\n", sta, cec, lvl, flag0, flag1, flag2); + + if (cec & CEC_RXSHPDINT_HPD) { + if (lvl & CEC_RXSHPDLEV_HPD) + tda998x_edid_delay_start(priv); + else + schedule_work(&priv->detect_work); + + handled = true; + } + if ((flag2 & INT_FLAGS_2_EDID_BLK_RD) && priv->wq_edid_wait) { priv->wq_edid_wait = 0; wake_up(&priv->wq_edid); - } else if (cec != 0) { /* HPD change */ - schedule_delayed_work(&priv->dwork, HZ/10); + handled = true; } - return IRQ_HANDLED; -} -static uint8_t tda998x_cksum(uint8_t *buf, size_t bytes) -{ - int sum = 0; - - while (bytes--) - sum -= *buf++; - return sum; + return IRQ_RETVAL(handled); } -#define HB(x) (x) -#define PB(x) (HB(2) + 1 + (x)) - static void -tda998x_write_if(struct tda998x_priv *priv, uint8_t bit, uint16_t addr, - uint8_t *buf, size_t size) +tda998x_write_if(struct tda998x_priv *priv, u8 bit, u16 addr, + union hdmi_infoframe *frame) { + u8 buf[32]; + ssize_t len; + + len = hdmi_infoframe_pack(frame, buf, sizeof(buf)); + if (len < 0) { + dev_err(&priv->hdmi->dev, + "hdmi_infoframe_pack() type=0x%02x failed: %zd\n", + frame->any.type, len); + return; + } + reg_clear(priv, REG_DIP_IF_FLAGS, bit); - reg_write_range(priv, addr, buf, size); + reg_write_range(priv, addr, buf, len); reg_set(priv, REG_DIP_IF_FLAGS, bit); } static void tda998x_write_aif(struct tda998x_priv *priv, struct tda998x_encoder_params *p) { - u8 buf[PB(HDMI_AUDIO_INFOFRAME_SIZE) + 1]; + union hdmi_infoframe frame; + + hdmi_audio_infoframe_init(&frame.audio); - memset(buf, 0, sizeof(buf)); - buf[HB(0)] = HDMI_INFOFRAME_TYPE_AUDIO; - buf[HB(1)] = 0x01; - buf[HB(2)] = HDMI_AUDIO_INFOFRAME_SIZE; - buf[PB(1)] = p->audio_frame[1] & 0x07; /* CC */ - buf[PB(2)] = p->audio_frame[2] & 0x1c; /* SF */ - buf[PB(4)] = p->audio_frame[4]; - buf[PB(5)] = p->audio_frame[5] & 0xf8; /* DM_INH + LSV */ + frame.audio.channels = p->audio_frame[1] & 0x07; + frame.audio.channel_allocation = p->audio_frame[4]; + frame.audio.level_shift_value = (p->audio_frame[5] & 0x78) >> 3; + frame.audio.downmix_inhibit = (p->audio_frame[5] & 0x80) >> 7; - buf[PB(0)] = tda998x_cksum(buf, sizeof(buf)); + /* + * L-PCM and IEC61937 compressed audio shall always set sample + * frequency to "refer to stream". For others, see the HDMI + * specification. + */ + frame.audio.sample_frequency = (p->audio_frame[2] & 0x1c) >> 2; - tda998x_write_if(priv, DIP_IF_FLAGS_IF4, REG_IF4_HB0, buf, - sizeof(buf)); + tda998x_write_if(priv, DIP_IF_FLAGS_IF4, REG_IF4_HB0, &frame); } static void tda998x_write_avi(struct tda998x_priv *priv, struct drm_display_mode *mode) { - struct hdmi_avi_infoframe frame; - u8 buf[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE]; - ssize_t len; + union hdmi_infoframe frame; - drm_hdmi_avi_infoframe_from_display_mode(&frame, mode); + drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, mode); + frame.avi.quantization_range = HDMI_QUANTIZATION_RANGE_FULL; - frame.quantization_range = HDMI_QUANTIZATION_RANGE_FULL; - - len = hdmi_avi_infoframe_pack(&frame, buf, sizeof(buf)); - if (len < 0) { - dev_err(&priv->hdmi->dev, - "hdmi_avi_infoframe_pack() failed: %zd\n", len); - return; - } - - tda998x_write_if(priv, DIP_IF_FLAGS_IF2, REG_IF2_HB0, buf, len); + tda998x_write_if(priv, DIP_IF_FLAGS_IF2, REG_IF2_HB0, &frame); } static void tda998x_audio_mute(struct tda998x_priv *priv, bool on) @@ -667,8 +713,8 @@ static void tda998x_configure_audio(struct tda998x_priv *priv, struct drm_display_mode *mode, struct tda998x_encoder_params *p) { - uint8_t buf[6], clksel_aip, clksel_fs, cts_n, adiv; - uint32_t n; + u8 buf[6], clksel_aip, clksel_fs, cts_n, adiv; + u32 n; /* Enable audio ports */ reg_write(priv, REG_ENA_AP, p->audio_cfg); @@ -776,8 +822,10 @@ static void tda998x_encoder_set_config(struct tda998x_priv *priv, priv->params = *p; } -static void tda998x_encoder_dpms(struct tda998x_priv *priv, int mode) +static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode) { + struct tda998x_priv *priv = enc_to_tda998x_priv(encoder); + /* we only care about on or off: */ if (mode != DRM_MODE_DPMS_ON) mode = DRM_MODE_DPMS_OFF; @@ -827,8 +875,8 @@ tda998x_encoder_mode_fixup(struct drm_encoder *encoder, return true; } -static int tda998x_encoder_mode_valid(struct tda998x_priv *priv, - struct drm_display_mode *mode) +static int tda998x_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) { if (mode->clock > 150000) return MODE_CLOCK_HIGH; @@ -840,18 +888,19 @@ static int tda998x_encoder_mode_valid(struct tda998x_priv *priv, } static void -tda998x_encoder_mode_set(struct tda998x_priv *priv, +tda998x_encoder_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - uint16_t ref_pix, ref_line, n_pix, n_line; - uint16_t hs_pix_s, hs_pix_e; - uint16_t vs1_pix_s, vs1_pix_e, vs1_line_s, vs1_line_e; - uint16_t vs2_pix_s, vs2_pix_e, vs2_line_s, vs2_line_e; - uint16_t vwin1_line_s, vwin1_line_e; - uint16_t vwin2_line_s, vwin2_line_e; - uint16_t de_pix_s, de_pix_e; - uint8_t reg, div, rep; + struct tda998x_priv *priv = enc_to_tda998x_priv(encoder); + u16 ref_pix, ref_line, n_pix, n_line; + u16 hs_pix_s, hs_pix_e; + u16 vs1_pix_s, vs1_pix_e, vs1_line_s, vs1_line_e; + u16 vs2_pix_s, vs2_pix_e, vs2_line_s, vs2_line_e; + u16 vwin1_line_s, vwin1_line_e; + u16 vwin2_line_s, vwin2_line_e; + u16 de_pix_s, de_pix_e; + u8 reg, div, rep; /* * Internally TDA998x is using ITU-R BT.656 style sync but @@ -1031,9 +1080,10 @@ tda998x_encoder_mode_set(struct tda998x_priv *priv, } static enum drm_connector_status -tda998x_encoder_detect(struct tda998x_priv *priv) +tda998x_connector_detect(struct drm_connector *connector, bool force) { - uint8_t val = cec_read(priv, REG_CEC_RXSHPDLEV); + struct tda998x_priv *priv = conn_to_tda998x_priv(connector); + u8 val = cec_read(priv, REG_CEC_RXSHPDLEV); return (val & CEC_RXSHPDLEV_HPD) ? connector_status_connected : connector_status_disconnected; @@ -1042,7 +1092,7 @@ tda998x_encoder_detect(struct tda998x_priv *priv) static int read_edid_block(void *data, u8 *buf, unsigned int blk, size_t length) { struct tda998x_priv *priv = data; - uint8_t offset, segptr; + u8 offset, segptr; int ret, i; offset = (blk & 1) ? 128 : 0; @@ -1095,13 +1145,20 @@ static int read_edid_block(void *data, u8 *buf, unsigned int blk, size_t length) return 0; } -static int -tda998x_encoder_get_modes(struct tda998x_priv *priv, - struct drm_connector *connector) +static int tda998x_connector_get_modes(struct drm_connector *connector) { + struct tda998x_priv *priv = conn_to_tda998x_priv(connector); struct edid *edid; int n; + /* + * If we get killed while waiting for the HPD timeout, return + * no modes found: we are not in a restartable path, so we + * can't handle signals gracefully. + */ + if (tda998x_edid_delay_wait(priv)) + return 0; + if (priv->rev == TDA19988) reg_clear(priv, REG_TX4, TX4_PD_RAM); @@ -1133,101 +1190,21 @@ static void tda998x_encoder_set_polling(struct tda998x_priv *priv, DRM_CONNECTOR_POLL_DISCONNECT; } -static int -tda998x_encoder_set_property(struct drm_encoder *encoder, - struct drm_connector *connector, - struct drm_property *property, - uint64_t val) -{ - DBG(""); - return 0; -} - static void tda998x_destroy(struct tda998x_priv *priv) { /* disable all IRQs and free the IRQ handler */ cec_write(priv, REG_CEC_RXSHPDINTENA, 0); reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); - if (priv->hdmi->irq) { - free_irq(priv->hdmi->irq, priv); - cancel_delayed_work_sync(&priv->dwork); - } - - i2c_unregister_device(priv->cec); -} - -/* Slave encoder support */ - -static void -tda998x_encoder_slave_set_config(struct drm_encoder *encoder, void *params) -{ - tda998x_encoder_set_config(to_tda998x_priv(encoder), params); -} -static void tda998x_encoder_slave_destroy(struct drm_encoder *encoder) -{ - struct tda998x_priv *priv = to_tda998x_priv(encoder); - - tda998x_destroy(priv); - drm_i2c_encoder_destroy(encoder); - kfree(priv); -} - -static void tda998x_encoder_slave_dpms(struct drm_encoder *encoder, int mode) -{ - tda998x_encoder_dpms(to_tda998x_priv(encoder), mode); -} - -static int tda998x_encoder_slave_mode_valid(struct drm_encoder *encoder, - struct drm_display_mode *mode) -{ - return tda998x_encoder_mode_valid(to_tda998x_priv(encoder), mode); -} + if (priv->hdmi->irq) + free_irq(priv->hdmi->irq, priv); -static void -tda998x_encoder_slave_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - tda998x_encoder_mode_set(to_tda998x_priv(encoder), mode, adjusted_mode); -} + del_timer_sync(&priv->edid_delay_timer); + cancel_work_sync(&priv->detect_work); -static enum drm_connector_status -tda998x_encoder_slave_detect(struct drm_encoder *encoder, - struct drm_connector *connector) -{ - return tda998x_encoder_detect(to_tda998x_priv(encoder)); -} - -static int tda998x_encoder_slave_get_modes(struct drm_encoder *encoder, - struct drm_connector *connector) -{ - return tda998x_encoder_get_modes(to_tda998x_priv(encoder), connector); -} - -static int -tda998x_encoder_slave_create_resources(struct drm_encoder *encoder, - struct drm_connector *connector) -{ - tda998x_encoder_set_polling(to_tda998x_priv(encoder), connector); - return 0; + i2c_unregister_device(priv->cec); } -static struct drm_encoder_slave_funcs tda998x_encoder_slave_funcs = { - .set_config = tda998x_encoder_slave_set_config, - .destroy = tda998x_encoder_slave_destroy, - .dpms = tda998x_encoder_slave_dpms, - .save = tda998x_encoder_save, - .restore = tda998x_encoder_restore, - .mode_fixup = tda998x_encoder_mode_fixup, - .mode_valid = tda998x_encoder_slave_mode_valid, - .mode_set = tda998x_encoder_slave_mode_set, - .detect = tda998x_encoder_slave_detect, - .get_modes = tda998x_encoder_slave_get_modes, - .create_resources = tda998x_encoder_slave_create_resources, - .set_property = tda998x_encoder_set_property, -}; - /* I2C driver functions */ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) @@ -1252,6 +1229,10 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) priv->dpms = DRM_MODE_DPMS_OFF; mutex_init(&priv->mutex); /* protect the page access */ + init_waitqueue_head(&priv->edid_delay_waitq); + setup_timer(&priv->edid_delay_timer, tda998x_edid_delay_done, + (unsigned long)priv); + INIT_WORK(&priv->detect_work, tda998x_detect_work); /* wake up the device: */ cec_write(priv, REG_CEC_ENAMODS, @@ -1310,7 +1291,6 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) /* init read EDID waitqueue and HDP work */ init_waitqueue_head(&priv->wq_edid); - INIT_DELAYED_WORK(&priv->dwork, tda998x_hpd); /* clear pending interrupts */ reg_read(priv, REG_INT_FLAGS_0); @@ -1359,84 +1339,31 @@ fail: return -ENXIO; } -static int tda998x_encoder_init(struct i2c_client *client, - struct drm_device *dev, - struct drm_encoder_slave *encoder_slave) -{ - struct tda998x_priv *priv; - int ret; - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->encoder = &encoder_slave->base; - - ret = tda998x_create(client, priv); - if (ret) { - kfree(priv); - return ret; - } - - encoder_slave->slave_priv = priv; - encoder_slave->slave_funcs = &tda998x_encoder_slave_funcs; - - return 0; -} - -struct tda998x_priv2 { - struct tda998x_priv base; - struct drm_encoder encoder; - struct drm_connector connector; -}; - -#define conn_to_tda998x_priv2(x) \ - container_of(x, struct tda998x_priv2, connector); - -#define enc_to_tda998x_priv2(x) \ - container_of(x, struct tda998x_priv2, encoder); - -static void tda998x_encoder2_dpms(struct drm_encoder *encoder, int mode) -{ - struct tda998x_priv2 *priv = enc_to_tda998x_priv2(encoder); - - tda998x_encoder_dpms(&priv->base, mode); -} - static void tda998x_encoder_prepare(struct drm_encoder *encoder) { - tda998x_encoder2_dpms(encoder, DRM_MODE_DPMS_OFF); + tda998x_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); } static void tda998x_encoder_commit(struct drm_encoder *encoder) { - tda998x_encoder2_dpms(encoder, DRM_MODE_DPMS_ON); -} - -static void tda998x_encoder2_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct tda998x_priv2 *priv = enc_to_tda998x_priv2(encoder); - - tda998x_encoder_mode_set(&priv->base, mode, adjusted_mode); + tda998x_encoder_dpms(encoder, DRM_MODE_DPMS_ON); } static const struct drm_encoder_helper_funcs tda998x_encoder_helper_funcs = { - .dpms = tda998x_encoder2_dpms, + .dpms = tda998x_encoder_dpms, .save = tda998x_encoder_save, .restore = tda998x_encoder_restore, .mode_fixup = tda998x_encoder_mode_fixup, .prepare = tda998x_encoder_prepare, .commit = tda998x_encoder_commit, - .mode_set = tda998x_encoder2_mode_set, + .mode_set = tda998x_encoder_mode_set, }; static void tda998x_encoder_destroy(struct drm_encoder *encoder) { - struct tda998x_priv2 *priv = enc_to_tda998x_priv2(encoder); + struct tda998x_priv *priv = enc_to_tda998x_priv(encoder); - tda998x_destroy(&priv->base); + tda998x_destroy(priv); drm_encoder_cleanup(encoder); } @@ -1444,25 +1371,10 @@ static const struct drm_encoder_funcs tda998x_encoder_funcs = { .destroy = tda998x_encoder_destroy, }; -static int tda998x_connector_get_modes(struct drm_connector *connector) -{ - struct tda998x_priv2 *priv = conn_to_tda998x_priv2(connector); - - return tda998x_encoder_get_modes(&priv->base, connector); -} - -static int tda998x_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - struct tda998x_priv2 *priv = conn_to_tda998x_priv2(connector); - - return tda998x_encoder_mode_valid(&priv->base, mode); -} - static struct drm_encoder * tda998x_connector_best_encoder(struct drm_connector *connector) { - struct tda998x_priv2 *priv = conn_to_tda998x_priv2(connector); + struct tda998x_priv *priv = conn_to_tda998x_priv(connector); return &priv->encoder; } @@ -1474,14 +1386,6 @@ const struct drm_connector_helper_funcs tda998x_connector_helper_funcs = { .best_encoder = tda998x_connector_best_encoder, }; -static enum drm_connector_status -tda998x_connector_detect(struct drm_connector *connector, bool force) -{ - struct tda998x_priv2 *priv = conn_to_tda998x_priv2(connector); - - return tda998x_encoder_detect(&priv->base); -} - static void tda998x_connector_destroy(struct drm_connector *connector) { drm_connector_unregister(connector); @@ -1500,8 +1404,8 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data) struct tda998x_encoder_params *params = dev->platform_data; struct i2c_client *client = to_i2c_client(dev); struct drm_device *drm = data; - struct tda998x_priv2 *priv; - uint32_t crtcs = 0; + struct tda998x_priv *priv; + u32 crtcs = 0; int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -1519,18 +1423,17 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data) crtcs = 1 << 0; } - priv->base.encoder = &priv->encoder; priv->connector.interlace_allowed = 1; priv->encoder.possible_crtcs = crtcs; - ret = tda998x_create(client, &priv->base); + ret = tda998x_create(client, priv); if (ret) return ret; if (!dev->of_node && params) - tda998x_encoder_set_config(&priv->base, params); + tda998x_encoder_set_config(priv, params); - tda998x_encoder_set_polling(&priv->base, &priv->connector); + tda998x_encoder_set_polling(priv, &priv->connector); drm_encoder_helper_add(&priv->encoder, &tda998x_encoder_helper_funcs); ret = drm_encoder_init(drm, &priv->encoder, &tda998x_encoder_funcs, @@ -1560,18 +1463,18 @@ err_sysfs: err_connector: drm_encoder_cleanup(&priv->encoder); err_encoder: - tda998x_destroy(&priv->base); + tda998x_destroy(priv); return ret; } static void tda998x_unbind(struct device *dev, struct device *master, void *data) { - struct tda998x_priv2 *priv = dev_get_drvdata(dev); + struct tda998x_priv *priv = dev_get_drvdata(dev); drm_connector_cleanup(&priv->connector); drm_encoder_cleanup(&priv->encoder); - tda998x_destroy(&priv->base); + tda998x_destroy(priv); } static const struct component_ops tda998x_ops = { @@ -1605,38 +1508,18 @@ static struct i2c_device_id tda998x_ids[] = { }; MODULE_DEVICE_TABLE(i2c, tda998x_ids); -static struct drm_i2c_encoder_driver tda998x_driver = { - .i2c_driver = { - .probe = tda998x_probe, - .remove = tda998x_remove, - .driver = { - .name = "tda998x", - .of_match_table = of_match_ptr(tda998x_dt_ids), - }, - .id_table = tda998x_ids, +static struct i2c_driver tda998x_driver = { + .probe = tda998x_probe, + .remove = tda998x_remove, + .driver = { + .name = "tda998x", + .of_match_table = of_match_ptr(tda998x_dt_ids), }, - .encoder_init = tda998x_encoder_init, + .id_table = tda998x_ids, }; -/* Module initialization */ - -static int __init -tda998x_init(void) -{ - DBG(""); - return drm_i2c_encoder_register(THIS_MODULE, &tda998x_driver); -} - -static void __exit -tda998x_exit(void) -{ - DBG(""); - drm_i2c_encoder_unregister(&tda998x_driver); -} +module_i2c_driver(tda998x_driver); MODULE_AUTHOR("Rob Clark <[email protected]"); MODULE_DESCRIPTION("NXP Semiconductors TDA998X HDMI Encoder"); MODULE_LICENSE("GPL"); - -module_init(tda998x_init); -module_exit(tda998x_exit); |