diff options
Diffstat (limited to 'drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c')
-rw-r--r-- | drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c | 650 |
1 files changed, 280 insertions, 370 deletions
diff --git a/drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c index 11dd2bc803..2e2f37b9d0 100644 --- a/drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c +++ b/drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c @@ -7,9 +7,18 @@ * Laurent Pinchart (laurent.pinchart@ideasonboard.com) */ -#include <linux/backlight.h> #include <linux/clk.h> - +#include <linux/media-bus-format.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/pm_runtime.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_atomic_uapi.h> +#include <drm/drm_bridge.h> +#include <drm/drm_bridge_connector.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_dma_helper.h> @@ -18,85 +27,123 @@ #include <drm/drm_gem_dma_helper.h> #include <drm/drm_modeset_helper.h> #include <drm/drm_modeset_helper_vtables.h> -#include <drm/drm_plane_helper.h> +#include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h> -#include "shmob_drm_backlight.h" +#include <video/videomode.h> + #include "shmob_drm_crtc.h" #include "shmob_drm_drv.h" #include "shmob_drm_kms.h" #include "shmob_drm_plane.h" #include "shmob_drm_regs.h" -/* - * TODO: panel support - */ - /* ----------------------------------------------------------------------------- - * Clock management + * Page Flip */ -static int shmob_drm_clk_on(struct shmob_drm_device *sdev) +void shmob_drm_crtc_finish_page_flip(struct shmob_drm_crtc *scrtc) { - int ret; + struct drm_pending_vblank_event *event; + struct drm_device *dev = scrtc->base.dev; + unsigned long flags; - if (sdev->clock) { - ret = clk_prepare_enable(sdev->clock); - if (ret < 0) - return ret; + spin_lock_irqsave(&dev->event_lock, flags); + event = scrtc->event; + scrtc->event = NULL; + if (event) { + drm_crtc_send_vblank_event(&scrtc->base, event); + wake_up(&scrtc->flip_wait); + drm_crtc_vblank_put(&scrtc->base); } + spin_unlock_irqrestore(&dev->event_lock, flags); +} - return 0; +static bool shmob_drm_crtc_page_flip_pending(struct shmob_drm_crtc *scrtc) +{ + struct drm_device *dev = scrtc->base.dev; + unsigned long flags; + bool pending; + + spin_lock_irqsave(&dev->event_lock, flags); + pending = scrtc->event != NULL; + spin_unlock_irqrestore(&dev->event_lock, flags); + + return pending; } -static void shmob_drm_clk_off(struct shmob_drm_device *sdev) +static void shmob_drm_crtc_wait_page_flip(struct shmob_drm_crtc *scrtc) { - if (sdev->clock) - clk_disable_unprepare(sdev->clock); + struct drm_crtc *crtc = &scrtc->base; + struct shmob_drm_device *sdev = to_shmob_device(crtc->dev); + + if (wait_event_timeout(scrtc->flip_wait, + !shmob_drm_crtc_page_flip_pending(scrtc), + msecs_to_jiffies(50))) + return; + + dev_warn(sdev->dev, "page flip timeout\n"); + + shmob_drm_crtc_finish_page_flip(scrtc); } /* ----------------------------------------------------------------------------- * CRTC */ +static const struct { + u32 fmt; + u32 ldmt1r; +} shmob_drm_bus_fmts[] = { + { MEDIA_BUS_FMT_RGB888_3X8, LDMT1R_MIFTYP_RGB8 }, + { MEDIA_BUS_FMT_RGB666_2X9_BE, LDMT1R_MIFTYP_RGB9 }, + { MEDIA_BUS_FMT_RGB888_2X12_BE, LDMT1R_MIFTYP_RGB12A }, + { MEDIA_BUS_FMT_RGB444_1X12, LDMT1R_MIFTYP_RGB12B }, + { MEDIA_BUS_FMT_RGB565_1X16, LDMT1R_MIFTYP_RGB16 }, + { MEDIA_BUS_FMT_RGB666_1X18, LDMT1R_MIFTYP_RGB18 }, + { MEDIA_BUS_FMT_RGB888_1X24, LDMT1R_MIFTYP_RGB24 }, + { MEDIA_BUS_FMT_UYVY8_1X16, LDMT1R_MIFTYP_YCBCR }, +}; + static void shmob_drm_crtc_setup_geometry(struct shmob_drm_crtc *scrtc) { - struct drm_crtc *crtc = &scrtc->crtc; - struct shmob_drm_device *sdev = crtc->dev->dev_private; - const struct shmob_drm_interface_data *idata = &sdev->pdata->iface; + struct drm_crtc *crtc = &scrtc->base; + struct shmob_drm_device *sdev = to_shmob_device(crtc->dev); + const struct drm_display_info *info = &sdev->connector->display_info; const struct drm_display_mode *mode = &crtc->mode; + unsigned int i; u32 value; - value = sdev->ldmt1r - | ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : LDMT1R_VPOL) - | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : LDMT1R_HPOL) - | ((idata->flags & SHMOB_DRM_IFACE_FL_DWPOL) ? LDMT1R_DWPOL : 0) - | ((idata->flags & SHMOB_DRM_IFACE_FL_DIPOL) ? LDMT1R_DIPOL : 0) - | ((idata->flags & SHMOB_DRM_IFACE_FL_DAPOL) ? LDMT1R_DAPOL : 0) - | ((idata->flags & SHMOB_DRM_IFACE_FL_HSCNT) ? LDMT1R_HSCNT : 0) - | ((idata->flags & SHMOB_DRM_IFACE_FL_DWCNT) ? LDMT1R_DWCNT : 0); - lcdc_write(sdev, LDMT1R, value); - - if (idata->interface >= SHMOB_DRM_IFACE_SYS8A && - idata->interface <= SHMOB_DRM_IFACE_SYS24) { - /* Setup SYS bus. */ - value = (idata->sys.cs_setup << LDMT2R_CSUP_SHIFT) - | (idata->sys.vsync_active_high ? LDMT2R_RSV : 0) - | (idata->sys.vsync_dir_input ? LDMT2R_VSEL : 0) - | (idata->sys.write_setup << LDMT2R_WCSC_SHIFT) - | (idata->sys.write_cycle << LDMT2R_WCEC_SHIFT) - | (idata->sys.write_strobe << LDMT2R_WCLW_SHIFT); - lcdc_write(sdev, LDMT2R, value); - - value = (idata->sys.read_latch << LDMT3R_RDLC_SHIFT) - | (idata->sys.read_setup << LDMT3R_RCSC_SHIFT) - | (idata->sys.read_cycle << LDMT3R_RCEC_SHIFT) - | (idata->sys.read_strobe << LDMT3R_RCLW_SHIFT); - lcdc_write(sdev, LDMT3R, value); + if (!info->num_bus_formats || !info->bus_formats) { + dev_warn(sdev->dev, "No bus format reported, using RGB888\n"); + value = LDMT1R_MIFTYP_RGB24; + } else { + for (i = 0; i < ARRAY_SIZE(shmob_drm_bus_fmts); i++) { + if (shmob_drm_bus_fmts[i].fmt == info->bus_formats[0]) + break; + } + if (i < ARRAY_SIZE(shmob_drm_bus_fmts)) { + value = shmob_drm_bus_fmts[i].ldmt1r; + } else { + dev_warn(sdev->dev, + "unsupported bus format 0x%x, using RGB888\n", + info->bus_formats[0]); + value = LDMT1R_MIFTYP_RGB24; + } } + if (info->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE) + value |= LDMT1R_DWPOL; + if (info->bus_flags & DRM_BUS_FLAG_DE_LOW) + value |= LDMT1R_DIPOL; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + value |= LDMT1R_VPOL; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + value |= LDMT1R_HPOL; + lcdc_write(sdev, LDMT1R, value); + value = ((mode->hdisplay / 8) << 16) /* HDCN */ | (mode->htotal / 8); /* HTCN */ lcdc_write(sdev, LDHCNR, value); @@ -121,7 +168,7 @@ static void shmob_drm_crtc_setup_geometry(struct shmob_drm_crtc *scrtc) static void shmob_drm_crtc_start_stop(struct shmob_drm_crtc *scrtc, bool start) { - struct shmob_drm_device *sdev = scrtc->crtc.dev->dev_private; + struct shmob_drm_device *sdev = to_shmob_device(scrtc->base.dev); u32 value; value = lcdc_read(sdev, LDCNT2R); @@ -145,34 +192,23 @@ static void shmob_drm_crtc_start_stop(struct shmob_drm_crtc *scrtc, bool start) } } -/* - * shmob_drm_crtc_start - Configure and start the LCDC - * @scrtc: the SH Mobile CRTC - * - * Configure and start the LCDC device. External devices (clocks, MERAM, panels, - * ...) are not touched by this function. - */ -static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc) +static inline struct shmob_drm_crtc *to_shmob_crtc(struct drm_crtc *crtc) +{ + return container_of(crtc, struct shmob_drm_crtc, base); +} + +static void shmob_drm_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) { - struct drm_crtc *crtc = &scrtc->crtc; - struct shmob_drm_device *sdev = crtc->dev->dev_private; - const struct shmob_drm_interface_data *idata = &sdev->pdata->iface; - const struct shmob_drm_format_info *format; - struct drm_device *dev = sdev->ddev; - struct drm_plane *plane; + struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc); + struct shmob_drm_device *sdev = to_shmob_device(crtc->dev); + unsigned int clk_div = sdev->config.clk_div; + struct device *dev = sdev->dev; u32 value; int ret; - if (scrtc->started) - return; - - format = shmob_drm_format_info(crtc->primary->fb->format->format); - if (WARN_ON(format == NULL)) - return; - - /* Enable clocks before accessing the hardware. */ - ret = shmob_drm_clk_on(sdev); - if (ret < 0) + ret = pm_runtime_resume_and_get(dev); + if (ret) return; /* Reset and enable the LCDC. */ @@ -188,79 +224,50 @@ static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc) lcdc_write(sdev, LDPMR, 0); value = sdev->lddckr; - if (idata->clk_div) { + if (clk_div) { /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider * denominator. */ lcdc_write(sdev, LDDCKPAT1R, 0); - lcdc_write(sdev, LDDCKPAT2R, (1 << (idata->clk_div / 2)) - 1); + lcdc_write(sdev, LDDCKPAT2R, (1 << (clk_div / 2)) - 1); - if (idata->clk_div == 1) + if (clk_div == 1) value |= LDDCKR_MOSEL; else - value |= idata->clk_div; + value |= clk_div; } lcdc_write(sdev, LDDCKR, value); lcdc_write(sdev, LDDCKSTPR, 0); lcdc_wait_bit(sdev, LDDCKSTPR, ~0, 0); - /* TODO: Setup SYS panel */ - /* Setup geometry, format, frame buffer memory and operation mode. */ shmob_drm_crtc_setup_geometry(scrtc); - /* TODO: Handle YUV colorspaces. Hardcode REC709 for now. */ - lcdc_write(sdev, LDDFR, format->lddfr | LDDFR_CF1); - lcdc_write(sdev, LDMLSR, scrtc->line_size); - lcdc_write(sdev, LDSA1R, scrtc->dma[0]); - if (format->yuv) - lcdc_write(sdev, LDSA2R, scrtc->dma[1]); lcdc_write(sdev, LDSM1R, 0); - /* Word and long word swap. */ - switch (format->fourcc) { - case DRM_FORMAT_RGB565: - case DRM_FORMAT_NV21: - case DRM_FORMAT_NV61: - case DRM_FORMAT_NV42: - value = LDDDSR_LS | LDDDSR_WS; - break; - case DRM_FORMAT_RGB888: - case DRM_FORMAT_NV12: - case DRM_FORMAT_NV16: - case DRM_FORMAT_NV24: - value = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS; - break; - case DRM_FORMAT_ARGB8888: - case DRM_FORMAT_XRGB8888: - default: - value = LDDDSR_LS; - break; - } - lcdc_write(sdev, LDDDSR, value); - - /* Setup planes. */ - drm_for_each_legacy_plane(plane, dev) { - if (plane->crtc == crtc) - shmob_drm_plane_setup(plane); - } - /* Enable the display output. */ lcdc_write(sdev, LDCNT1R, LDCNT1R_DE); shmob_drm_crtc_start_stop(scrtc, true); - scrtc->started = true; + /* Turn vertical blank interrupt reporting back on. */ + drm_crtc_vblank_on(crtc); } -static void shmob_drm_crtc_stop(struct shmob_drm_crtc *scrtc) +static void shmob_drm_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *state) { - struct drm_crtc *crtc = &scrtc->crtc; - struct shmob_drm_device *sdev = crtc->dev->dev_private; + struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc); + struct shmob_drm_device *sdev = to_shmob_device(crtc->dev); - if (!scrtc->started) - return; + /* + * Disable vertical blank interrupt reporting. We first need to wait + * for page flip completion before stopping the CRTC as userspace + * expects page flips to eventually complete. + */ + shmob_drm_crtc_wait_page_flip(scrtc); + drm_crtc_vblank_off(crtc); /* Stop the LCDC. */ shmob_drm_crtc_start_stop(scrtc, false); @@ -268,145 +275,31 @@ static void shmob_drm_crtc_stop(struct shmob_drm_crtc *scrtc) /* Disable the display output. */ lcdc_write(sdev, LDCNT1R, 0); - /* Stop clocks. */ - shmob_drm_clk_off(sdev); - - scrtc->started = false; -} - -void shmob_drm_crtc_suspend(struct shmob_drm_crtc *scrtc) -{ - shmob_drm_crtc_stop(scrtc); -} - -void shmob_drm_crtc_resume(struct shmob_drm_crtc *scrtc) -{ - if (scrtc->dpms != DRM_MODE_DPMS_ON) - return; - - shmob_drm_crtc_start(scrtc); + pm_runtime_put(sdev->dev); } -static void shmob_drm_crtc_compute_base(struct shmob_drm_crtc *scrtc, - int x, int y) +static void shmob_drm_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_atomic_state *state) { - struct drm_crtc *crtc = &scrtc->crtc; - struct drm_framebuffer *fb = crtc->primary->fb; - struct drm_gem_dma_object *gem; - unsigned int bpp; - - bpp = scrtc->format->yuv ? 8 : scrtc->format->bpp; - gem = drm_fb_dma_get_gem_obj(fb, 0); - scrtc->dma[0] = gem->dma_addr + fb->offsets[0] - + y * fb->pitches[0] + x * bpp / 8; - - if (scrtc->format->yuv) { - bpp = scrtc->format->bpp - 8; - gem = drm_fb_dma_get_gem_obj(fb, 1); - scrtc->dma[1] = gem->dma_addr + fb->offsets[1] - + y / (bpp == 4 ? 2 : 1) * fb->pitches[1] - + x * (bpp == 16 ? 2 : 1); - } -} - -static void shmob_drm_crtc_update_base(struct shmob_drm_crtc *scrtc) -{ - struct drm_crtc *crtc = &scrtc->crtc; - struct shmob_drm_device *sdev = crtc->dev->dev_private; - - shmob_drm_crtc_compute_base(scrtc, crtc->x, crtc->y); - - lcdc_write_mirror(sdev, LDSA1R, scrtc->dma[0]); - if (scrtc->format->yuv) - lcdc_write_mirror(sdev, LDSA2R, scrtc->dma[1]); - - lcdc_write(sdev, LDRCNTR, lcdc_read(sdev, LDRCNTR) ^ LDRCNTR_MRS); -} - -#define to_shmob_crtc(c) container_of(c, struct shmob_drm_crtc, crtc) - -static void shmob_drm_crtc_dpms(struct drm_crtc *crtc, int mode) -{ - struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc); - - if (scrtc->dpms == mode) - return; - - if (mode == DRM_MODE_DPMS_ON) - shmob_drm_crtc_start(scrtc); - else - shmob_drm_crtc_stop(scrtc); - - scrtc->dpms = mode; -} - -static void shmob_drm_crtc_mode_prepare(struct drm_crtc *crtc) -{ - shmob_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); -} + struct drm_pending_vblank_event *event; + struct drm_device *dev = crtc->dev; + unsigned long flags; -static int shmob_drm_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - int x, int y, - struct drm_framebuffer *old_fb) -{ - struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc); - struct shmob_drm_device *sdev = crtc->dev->dev_private; - const struct shmob_drm_format_info *format; - - format = shmob_drm_format_info(crtc->primary->fb->format->format); - if (format == NULL) { - dev_dbg(sdev->dev, "mode_set: unsupported format %p4cc\n", - &crtc->primary->fb->format->format); - return -EINVAL; + if (crtc->state->event) { + spin_lock_irqsave(&dev->event_lock, flags); + event = crtc->state->event; + crtc->state->event = NULL; + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irqrestore(&dev->event_lock, flags); } - - scrtc->format = format; - scrtc->line_size = crtc->primary->fb->pitches[0]; - - shmob_drm_crtc_compute_base(scrtc, x, y); - - return 0; -} - -static void shmob_drm_crtc_mode_commit(struct drm_crtc *crtc) -{ - shmob_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON); -} - -static int shmob_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb) -{ - shmob_drm_crtc_update_base(to_shmob_crtc(crtc)); - - return 0; } static const struct drm_crtc_helper_funcs crtc_helper_funcs = { - .dpms = shmob_drm_crtc_dpms, - .prepare = shmob_drm_crtc_mode_prepare, - .commit = shmob_drm_crtc_mode_commit, - .mode_set = shmob_drm_crtc_mode_set, - .mode_set_base = shmob_drm_crtc_mode_set_base, + .atomic_flush = shmob_drm_crtc_atomic_flush, + .atomic_enable = shmob_drm_crtc_atomic_enable, + .atomic_disable = shmob_drm_crtc_atomic_disable, }; -void shmob_drm_crtc_finish_page_flip(struct shmob_drm_crtc *scrtc) -{ - struct drm_pending_vblank_event *event; - struct drm_device *dev = scrtc->crtc.dev; - unsigned long flags; - - spin_lock_irqsave(&dev->event_lock, flags); - event = scrtc->event; - scrtc->event = NULL; - if (event) { - drm_crtc_send_vblank_event(&scrtc->crtc, event); - drm_crtc_vblank_put(&scrtc->crtc); - } - spin_unlock_irqrestore(&dev->event_lock, flags); -} - static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, @@ -414,7 +307,7 @@ static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc, struct drm_modeset_acquire_ctx *ctx) { struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc); - struct drm_device *dev = scrtc->crtc.dev; + struct drm_device *dev = scrtc->base.dev; unsigned long flags; spin_lock_irqsave(&dev->event_lock, flags); @@ -424,12 +317,11 @@ static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc, } spin_unlock_irqrestore(&dev->event_lock, flags); - crtc->primary->fb = fb; - shmob_drm_crtc_update_base(scrtc); + drm_atomic_set_fb_for_plane(crtc->primary->state, fb); if (event) { event->pipe = 0; - drm_crtc_vblank_get(&scrtc->crtc); + drm_crtc_vblank_get(&scrtc->base); spin_lock_irqsave(&dev->event_lock, flags); scrtc->event = event; spin_unlock_irqrestore(&dev->event_lock, flags); @@ -457,7 +349,7 @@ static void shmob_drm_crtc_enable_vblank(struct shmob_drm_device *sdev, static int shmob_drm_enable_vblank(struct drm_crtc *crtc) { - struct shmob_drm_device *sdev = crtc->dev->dev_private; + struct shmob_drm_device *sdev = to_shmob_device(crtc->dev); shmob_drm_crtc_enable_vblank(sdev, true); @@ -466,88 +358,65 @@ static int shmob_drm_enable_vblank(struct drm_crtc *crtc) static void shmob_drm_disable_vblank(struct drm_crtc *crtc) { - struct shmob_drm_device *sdev = crtc->dev->dev_private; + struct shmob_drm_device *sdev = to_shmob_device(crtc->dev); shmob_drm_crtc_enable_vblank(sdev, false); } static const struct drm_crtc_funcs crtc_funcs = { + .reset = drm_atomic_helper_crtc_reset, .destroy = drm_crtc_cleanup, - .set_config = drm_crtc_helper_set_config, + .set_config = drm_atomic_helper_set_config, .page_flip = shmob_drm_crtc_page_flip, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .enable_vblank = shmob_drm_enable_vblank, .disable_vblank = shmob_drm_disable_vblank, }; -static const uint32_t modeset_formats[] = { - DRM_FORMAT_RGB565, - DRM_FORMAT_RGB888, - DRM_FORMAT_ARGB8888, - DRM_FORMAT_XRGB8888, -}; - -static const struct drm_plane_funcs primary_plane_funcs = { - DRM_PLANE_NON_ATOMIC_FUNCS, -}; - int shmob_drm_crtc_create(struct shmob_drm_device *sdev) { - struct drm_crtc *crtc = &sdev->crtc.crtc; - struct drm_plane *primary; + struct drm_crtc *crtc = &sdev->crtc.base; + struct drm_plane *primary, *plane; + unsigned int i; int ret; - sdev->crtc.dpms = DRM_MODE_DPMS_OFF; + init_waitqueue_head(&sdev->crtc.flip_wait); - primary = __drm_universal_plane_alloc(sdev->ddev, sizeof(*primary), 0, - 0, &primary_plane_funcs, - modeset_formats, - ARRAY_SIZE(modeset_formats), - NULL, DRM_PLANE_TYPE_PRIMARY, - NULL); + primary = shmob_drm_plane_create(sdev, DRM_PLANE_TYPE_PRIMARY, 0); if (IS_ERR(primary)) return PTR_ERR(primary); - ret = drm_crtc_init_with_planes(sdev->ddev, crtc, primary, NULL, + for (i = 1; i < 5; ++i) { + plane = shmob_drm_plane_create(sdev, DRM_PLANE_TYPE_OVERLAY, i); + if (IS_ERR(plane)) + return PTR_ERR(plane); + } + + ret = drm_crtc_init_with_planes(&sdev->ddev, crtc, primary, NULL, &crtc_funcs, NULL); - if (ret < 0) { - drm_plane_cleanup(primary); - kfree(primary); + if (ret < 0) return ret; - } drm_crtc_helper_add(crtc, &crtc_helper_funcs); + /* Start with vertical blank interrupt reporting disabled. */ + drm_crtc_vblank_off(crtc); + return 0; } /* ----------------------------------------------------------------------------- - * Encoder + * Legacy Encoder */ -#define to_shmob_encoder(e) \ - container_of(e, struct shmob_drm_encoder, encoder) - -static void shmob_drm_encoder_dpms(struct drm_encoder *encoder, int mode) -{ - struct shmob_drm_encoder *senc = to_shmob_encoder(encoder); - struct shmob_drm_device *sdev = encoder->dev->dev_private; - struct shmob_drm_connector *scon = &sdev->connector; - - if (senc->dpms == mode) - return; - - shmob_drm_backlight_dpms(scon, mode); - - senc->dpms = mode; -} - static bool shmob_drm_encoder_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = encoder->dev; - struct shmob_drm_device *sdev = dev->dev_private; - struct drm_connector *connector = &sdev->connector.connector; + struct shmob_drm_device *sdev = to_shmob_device(dev); + struct drm_connector *connector = sdev->connector; const struct drm_display_mode *panel_mode; if (list_empty(&connector->modes)) { @@ -563,60 +432,61 @@ static bool shmob_drm_encoder_mode_fixup(struct drm_encoder *encoder, return true; } -static void shmob_drm_encoder_mode_prepare(struct drm_encoder *encoder) -{ - /* No-op, everything is handled in the CRTC code. */ -} - -static void shmob_drm_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - /* No-op, everything is handled in the CRTC code. */ -} - -static void shmob_drm_encoder_mode_commit(struct drm_encoder *encoder) -{ - /* No-op, everything is handled in the CRTC code. */ -} - static const struct drm_encoder_helper_funcs encoder_helper_funcs = { - .dpms = shmob_drm_encoder_dpms, .mode_fixup = shmob_drm_encoder_mode_fixup, - .prepare = shmob_drm_encoder_mode_prepare, - .commit = shmob_drm_encoder_mode_commit, - .mode_set = shmob_drm_encoder_mode_set, }; +/* ----------------------------------------------------------------------------- + * Encoder + */ + int shmob_drm_encoder_create(struct shmob_drm_device *sdev) { - struct drm_encoder *encoder = &sdev->encoder.encoder; + struct drm_encoder *encoder = &sdev->encoder; + struct drm_bridge *bridge; int ret; - sdev->encoder.dpms = DRM_MODE_DPMS_OFF; - encoder->possible_crtcs = 1; - ret = drm_simple_encoder_init(sdev->ddev, encoder, - DRM_MODE_ENCODER_LVDS); + ret = drm_simple_encoder_init(&sdev->ddev, encoder, + DRM_MODE_ENCODER_DPI); if (ret < 0) return ret; - drm_encoder_helper_add(encoder, &encoder_helper_funcs); + if (sdev->pdata) { + drm_encoder_helper_add(encoder, &encoder_helper_funcs); + return 0; + } + + /* Create a panel bridge */ + bridge = devm_drm_of_get_bridge(sdev->dev, sdev->dev->of_node, 0, 0); + if (IS_ERR(bridge)) + return PTR_ERR(bridge); + + /* Attach the bridge to the encoder */ + ret = drm_bridge_attach(encoder, bridge, NULL, + DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret) { + dev_err(sdev->dev, "failed to attach bridge: %pe\n", + ERR_PTR(ret)); + return ret; + } return 0; } /* ----------------------------------------------------------------------------- - * Connector + * Legacy Connector */ -#define to_shmob_connector(c) \ - container_of(c, struct shmob_drm_connector, connector) +static inline struct shmob_drm_connector *to_shmob_connector(struct drm_connector *connector) +{ + return container_of(connector, struct shmob_drm_connector, base); +} static int shmob_drm_connector_get_modes(struct drm_connector *connector) { - struct shmob_drm_device *sdev = connector->dev->dev_private; + struct shmob_drm_connector *scon = to_shmob_connector(connector); struct drm_display_mode *mode; mode = drm_mode_create(connector->dev); @@ -624,22 +494,10 @@ static int shmob_drm_connector_get_modes(struct drm_connector *connector) return 0; mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; - mode->clock = sdev->pdata->panel.mode.clock; - mode->hdisplay = sdev->pdata->panel.mode.hdisplay; - mode->hsync_start = sdev->pdata->panel.mode.hsync_start; - mode->hsync_end = sdev->pdata->panel.mode.hsync_end; - mode->htotal = sdev->pdata->panel.mode.htotal; - mode->vdisplay = sdev->pdata->panel.mode.vdisplay; - mode->vsync_start = sdev->pdata->panel.mode.vsync_start; - mode->vsync_end = sdev->pdata->panel.mode.vsync_end; - mode->vtotal = sdev->pdata->panel.mode.vtotal; - mode->flags = sdev->pdata->panel.mode.flags; - - drm_mode_set_name(mode); - drm_mode_probed_add(connector, mode); - connector->display_info.width_mm = sdev->pdata->panel.width_mm; - connector->display_info.height_mm = sdev->pdata->panel.height_mm; + drm_display_mode_from_videomode(scon->mode, mode); + + drm_mode_probed_add(connector, mode); return 1; } @@ -659,54 +517,106 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = { static void shmob_drm_connector_destroy(struct drm_connector *connector) { - struct shmob_drm_connector *scon = to_shmob_connector(connector); - - shmob_drm_backlight_exit(scon); drm_connector_unregister(connector); drm_connector_cleanup(connector); + + kfree(connector); } static const struct drm_connector_funcs connector_funcs = { - .dpms = drm_helper_connector_dpms, + .reset = drm_atomic_helper_connector_reset, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = shmob_drm_connector_destroy, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -int shmob_drm_connector_create(struct shmob_drm_device *sdev, - struct drm_encoder *encoder) +static struct drm_connector * +shmob_drm_connector_init(struct shmob_drm_device *sdev, + struct drm_encoder *encoder) { - struct drm_connector *connector = &sdev->connector.connector; + u32 bus_fmt = sdev->pdata->iface.bus_fmt; + struct shmob_drm_connector *scon; + struct drm_connector *connector; + struct drm_display_info *info; + unsigned int i; int ret; - sdev->connector.encoder = encoder; + for (i = 0; i < ARRAY_SIZE(shmob_drm_bus_fmts); i++) { + if (shmob_drm_bus_fmts[i].fmt == bus_fmt) + break; + } + if (i == ARRAY_SIZE(shmob_drm_bus_fmts)) { + dev_err(sdev->dev, "unsupported bus format 0x%x\n", bus_fmt); + return ERR_PTR(-EINVAL); + } - connector->display_info.width_mm = sdev->pdata->panel.width_mm; - connector->display_info.height_mm = sdev->pdata->panel.height_mm; + scon = kzalloc(sizeof(*scon), GFP_KERNEL); + if (!scon) + return ERR_PTR(-ENOMEM); - ret = drm_connector_init(sdev->ddev, connector, &connector_funcs, - DRM_MODE_CONNECTOR_LVDS); - if (ret < 0) - return ret; + connector = &scon->base; + scon->encoder = encoder; + scon->mode = &sdev->pdata->panel.mode; + + info = &connector->display_info; + info->width_mm = sdev->pdata->panel.width_mm; + info->height_mm = sdev->pdata->panel.height_mm; + + if (scon->mode->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) + info->bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE; + if (scon->mode->flags & DISPLAY_FLAGS_DE_LOW) + info->bus_flags |= DRM_BUS_FLAG_DE_LOW; + + ret = drm_display_info_set_bus_formats(info, &bus_fmt, 1); + if (ret < 0) { + kfree(scon); + return ERR_PTR(ret); + } + + ret = drm_connector_init(&sdev->ddev, connector, &connector_funcs, + DRM_MODE_CONNECTOR_DPI); + if (ret < 0) { + kfree(scon); + return ERR_PTR(ret); + } drm_connector_helper_add(connector, &connector_helper_funcs); - ret = shmob_drm_backlight_init(&sdev->connector); - if (ret < 0) - goto err_cleanup; + return connector; +} + +/* ----------------------------------------------------------------------------- + * Connector + */ + +int shmob_drm_connector_create(struct shmob_drm_device *sdev, + struct drm_encoder *encoder) +{ + struct drm_connector *connector; + int ret; + + if (sdev->pdata) + connector = shmob_drm_connector_init(sdev, encoder); + else + connector = drm_bridge_connector_init(&sdev->ddev, encoder); + if (IS_ERR(connector)) { + dev_err(sdev->dev, "failed to created connector: %pe\n", + connector); + return PTR_ERR(connector); + } ret = drm_connector_attach_encoder(connector, encoder); if (ret < 0) - goto err_backlight; + goto error; + + connector->dpms = DRM_MODE_DPMS_OFF; - drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); - drm_object_property_set_value(&connector->base, - sdev->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); + sdev->connector = connector; return 0; -err_backlight: - shmob_drm_backlight_exit(&sdev->connector); -err_cleanup: +error: drm_connector_cleanup(connector); return ret; } |