diff options
Diffstat (limited to 'drivers/gpu')
21 files changed, 985 insertions, 329 deletions
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 50d74e5ce41b..5171af2876f9 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1192,12 +1192,7 @@ void drm_atomic_legacy_backoff(struct drm_atomic_state *state) retry: drm_modeset_backoff(state->acquire_ctx); - ret = drm_modeset_lock(&state->dev->mode_config.connection_mutex, - state->acquire_ctx); - if (ret) - goto retry; - ret = drm_modeset_lock_all_crtcs(state->dev, - state->acquire_ctx); + ret = drm_modeset_lock_all_ctx(state->dev, state->acquire_ctx); if (ret) goto retry; } diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index ea443fafb934..ad06c20ea07d 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1814,6 +1814,161 @@ commit: } /** + * drm_atomic_helper_disable_all - disable all currently active outputs + * @dev: DRM device + * @ctx: lock acquisition context + * + * Loops through all connectors, finding those that aren't turned off and then + * turns them off by setting their DPMS mode to OFF and deactivating the CRTC + * that they are connected to. + * + * This is used for example in suspend/resume to disable all currently active + * functions when suspending. + * + * Note that if callers haven't already acquired all modeset locks this might + * return -EDEADLK, which must be handled by calling drm_modeset_backoff(). + * + * Returns: + * 0 on success or a negative error code on failure. + * + * See also: + * drm_atomic_helper_suspend(), drm_atomic_helper_resume() + */ +int drm_atomic_helper_disable_all(struct drm_device *dev, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_atomic_state *state; + struct drm_connector *conn; + int err; + + state = drm_atomic_state_alloc(dev); + if (!state) + return -ENOMEM; + + state->acquire_ctx = ctx; + + drm_for_each_connector(conn, dev) { + struct drm_crtc *crtc = conn->state->crtc; + struct drm_crtc_state *crtc_state; + + if (!crtc || conn->dpms != DRM_MODE_DPMS_ON) + continue; + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + err = PTR_ERR(crtc_state); + goto free; + } + + crtc_state->active = false; + } + + err = drm_atomic_commit(state); + +free: + if (err < 0) + drm_atomic_state_free(state); + + return err; +} +EXPORT_SYMBOL(drm_atomic_helper_disable_all); + +/** + * drm_atomic_helper_suspend - subsystem-level suspend helper + * @dev: DRM device + * + * Duplicates the current atomic state, disables all active outputs and then + * returns a pointer to the original atomic state to the caller. Drivers can + * pass this pointer to the drm_atomic_helper_resume() helper upon resume to + * restore the output configuration that was active at the time the system + * entered suspend. + * + * Note that it is potentially unsafe to use this. The atomic state object + * returned by this function is assumed to be persistent. Drivers must ensure + * that this holds true. Before calling this function, drivers must make sure + * to suspend fbdev emulation so that nothing can be using the device. + * + * Returns: + * A pointer to a copy of the state before suspend on success or an ERR_PTR()- + * encoded error code on failure. Drivers should store the returned atomic + * state object and pass it to the drm_atomic_helper_resume() helper upon + * resume. + * + * See also: + * drm_atomic_helper_duplicate_state(), drm_atomic_helper_disable_all(), + * drm_atomic_helper_resume() + */ +struct drm_atomic_state *drm_atomic_helper_suspend(struct drm_device *dev) +{ + struct drm_modeset_acquire_ctx ctx; + struct drm_atomic_state *state; + int err; + + drm_modeset_acquire_init(&ctx, 0); + +retry: + err = drm_modeset_lock_all_ctx(dev, &ctx); + if (err < 0) { + state = ERR_PTR(err); + goto unlock; + } + + state = drm_atomic_helper_duplicate_state(dev, &ctx); + if (IS_ERR(state)) + goto unlock; + + err = drm_atomic_helper_disable_all(dev, &ctx); + if (err < 0) { + drm_atomic_state_free(state); + state = ERR_PTR(err); + goto unlock; + } + +unlock: + if (PTR_ERR(state) == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_suspend); + +/** + * drm_atomic_helper_resume - subsystem-level resume helper + * @dev: DRM device + * @state: atomic state to resume to + * + * Calls drm_mode_config_reset() to synchronize hardware and software states, + * grabs all modeset locks and commits the atomic state object. This can be + * used in conjunction with the drm_atomic_helper_suspend() helper to + * implement suspend/resume for drivers that support atomic mode-setting. + * + * Returns: + * 0 on success or a negative error code on failure. + * + * See also: + * drm_atomic_helper_suspend() + */ +int drm_atomic_helper_resume(struct drm_device *dev, + struct drm_atomic_state *state) +{ + struct drm_mode_config *config = &dev->mode_config; + int err; + + drm_mode_config_reset(dev); + drm_modeset_lock_all(dev); + state->acquire_ctx = config->acquire_ctx; + err = drm_atomic_commit(state); + drm_modeset_unlock_all(dev); + + return err; +} +EXPORT_SYMBOL(drm_atomic_helper_resume); + +/** * drm_atomic_helper_crtc_set_property - helper for crtc properties * @crtc: DRM crtc * @property: DRM property @@ -2426,7 +2581,9 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state); * @ctx: lock acquisition context * * Makes a copy of the current atomic state by looping over all objects and - * duplicating their respective states. + * duplicating their respective states. This is used for example by suspend/ + * resume support code to save the state prior to suspend such that it can + * be restored upon resume. * * Note that this treats atomic state as persistent between save and restore. * Drivers must make sure that this is possible and won't result in confusion @@ -2438,6 +2595,9 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state); * Returns: * A pointer to the copy of the atomic state object on success or an * ERR_PTR()-encoded error code on failure. + * + * See also: + * drm_atomic_helper_suspend(), drm_atomic_helper_resume() */ struct drm_atomic_state * drm_atomic_helper_duplicate_state(struct drm_device *dev, diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index ef534758a02c..08620a25fe06 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -855,6 +855,12 @@ EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); * due to slight differences in allocating shared resources when the * configuration is restored in a different order than when userspace set it up) * need to use their own restore logic. + * + * This function is deprecated. New drivers should implement atomic mode- + * setting and use the atomic suspend/resume helpers. + * + * See also: + * drm_atomic_helper_suspend(), drm_atomic_helper_resume() */ void drm_helper_resume_force_mode(struct drm_device *dev) { diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index c19a62561183..a17d74474f1a 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -348,9 +348,6 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, } - /* disable all the possible outputs/crtcs before entering KMS mode */ - drm_helper_disable_unused_functions(dev); - ret = drm_fb_helper_initial_config(helper, preferred_bpp); if (ret < 0) { dev_err(dev->dev, "Failed to set initial hw configuration.\n"); @@ -369,6 +366,18 @@ err_free: EXPORT_SYMBOL_GPL(drm_fbdev_cma_init); /** + * drm_fbdev_cma_get_helper() - Get drm_fb_helper struct of a CMA framebuffer + * @fbdev_cma: drm_fbdev_cma struct + * + * Returns the assigned drm_fb_helper struct. + */ +struct drm_fb_helper *drm_fbdev_cma_get_helper(struct drm_fbdev_cma *fbdev_cma) +{ + return &fbdev_cma->fb_helper; +} +EXPORT_SYMBOL_GPL(drm_fbdev_cma_get_helper); + +/** * drm_fbdev_cma_fini() - Free drm_fbdev_cma struct * @fbdev_cma: The drm_fbdev_cma struct */ diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c index c257de351cfa..220eee1c1ef7 100644 --- a/drivers/gpu/drm/drm_modeset_lock.c +++ b/drivers/gpu/drm/drm_modeset_lock.c @@ -57,11 +57,18 @@ /** * drm_modeset_lock_all - take all modeset locks - * @dev: drm device + * @dev: DRM device * * This function takes all modeset locks, suitable where a more fine-grained - * scheme isn't (yet) implemented. Locks must be dropped with - * drm_modeset_unlock_all. + * scheme isn't (yet) implemented. Locks must be dropped by calling the + * drm_modeset_unlock_all() function. + * + * This function is deprecated. It allocates a lock acquisition context and + * stores it in the DRM device's ->mode_config. This facilitate conversion of + * existing code because it removes the need to manually deal with the + * acquisition context, but it is also brittle because the context is global + * and care must be taken not to nest calls. New code should use the + * drm_modeset_lock_all_ctx() function and pass in the context explicitly. */ void drm_modeset_lock_all(struct drm_device *dev) { @@ -78,39 +85,43 @@ void drm_modeset_lock_all(struct drm_device *dev) drm_modeset_acquire_init(ctx, 0); retry: - ret = drm_modeset_lock(&config->connection_mutex, ctx); - if (ret) - goto fail; - ret = drm_modeset_lock_all_crtcs(dev, ctx); - if (ret) - goto fail; + ret = drm_modeset_lock_all_ctx(dev, ctx); + if (ret < 0) { + if (ret == -EDEADLK) { + drm_modeset_backoff(ctx); + goto retry; + } + + drm_modeset_acquire_fini(ctx); + kfree(ctx); + return; + } WARN_ON(config->acquire_ctx); - /* now we hold the locks, so now that it is safe, stash the - * ctx for drm_modeset_unlock_all(): + /* + * We hold the locks now, so it is safe to stash the acquisition + * context for drm_modeset_unlock_all(). */ config->acquire_ctx = ctx; drm_warn_on_modeset_not_all_locked(dev); - - return; - -fail: - if (ret == -EDEADLK) { - drm_modeset_backoff(ctx); - goto retry; - } - - kfree(ctx); } EXPORT_SYMBOL(drm_modeset_lock_all); /** * drm_modeset_unlock_all - drop all modeset locks - * @dev: device + * @dev: DRM device * - * This function drop all modeset locks taken by drm_modeset_lock_all. + * This function drops all modeset locks taken by a previous call to the + * drm_modeset_lock_all() function. + * + * This function is deprecated. It uses the lock acquisition context stored + * in the DRM device's ->mode_config. This facilitates conversion of existing + * code because it removes the need to manually deal with the acquisition + * context, but it is also brittle because the context is global and care must + * be taken not to nest calls. New code should pass the acquisition context + * directly to the drm_modeset_drop_locks() function. */ void drm_modeset_unlock_all(struct drm_device *dev) { @@ -431,14 +442,34 @@ void drm_modeset_unlock(struct drm_modeset_lock *lock) } EXPORT_SYMBOL(drm_modeset_unlock); -/* In some legacy codepaths it's convenient to just grab all the crtc and plane - * related locks. */ -int drm_modeset_lock_all_crtcs(struct drm_device *dev, - struct drm_modeset_acquire_ctx *ctx) +/** + * drm_modeset_lock_all_ctx - take all modeset locks + * @dev: DRM device + * @ctx: lock acquisition context + * + * This function takes all modeset locks, suitable where a more fine-grained + * scheme isn't (yet) implemented. + * + * Unlike drm_modeset_lock_all(), it doesn't take the dev->mode_config.mutex + * since that lock isn't required for modeset state changes. Callers which + * need to grab that lock too need to do so outside of the acquire context + * @ctx. + * + * Locks acquired with this function should be released by calling the + * drm_modeset_drop_locks() function on @ctx. + * + * Returns: 0 on success or a negative error-code on failure. + */ +int drm_modeset_lock_all_ctx(struct drm_device *dev, + struct drm_modeset_acquire_ctx *ctx) { struct drm_crtc *crtc; struct drm_plane *plane; - int ret = 0; + int ret; + + ret = drm_modeset_lock(&dev->mode_config.connection_mutex, ctx); + if (ret) + return ret; drm_for_each_crtc(crtc, dev) { ret = drm_modeset_lock(&crtc->mutex, ctx); @@ -454,4 +485,4 @@ int drm_modeset_lock_all_crtcs(struct drm_device *dev, return 0; } -EXPORT_SYMBOL(drm_modeset_lock_all_crtcs); +EXPORT_SYMBOL(drm_modeset_lock_all_ctx); diff --git a/drivers/gpu/drm/fsl-dcu/Makefile b/drivers/gpu/drm/fsl-dcu/Makefile index 6ea1523ae6ec..b35a292287f3 100644 --- a/drivers/gpu/drm/fsl-dcu/Makefile +++ b/drivers/gpu/drm/fsl-dcu/Makefile @@ -3,5 +3,6 @@ fsl-dcu-drm-y := fsl_dcu_drm_drv.o \ fsl_dcu_drm_rgb.o \ fsl_dcu_drm_plane.o \ fsl_dcu_drm_crtc.o \ - fsl_dcu_drm_fbdev.o + fsl_dcu_drm_fbdev.o \ + fsl_tcon.o obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu-drm.o diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c index 82a3d311e164..9ceeb1a5b47d 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c @@ -17,6 +17,9 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_flip_work.h> + +#include <video/display_timing.h> #include "fsl_dcu_drm_crtc.h" #include "fsl_dcu_drm_drv.h" @@ -25,11 +28,24 @@ static void fsl_dcu_drm_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { + struct fsl_dcu_drm_crtc *dcu_crtc = to_fsl_dcu_crtc(crtc); + if (crtc->state->event) { + crtc->state->event->pipe = drm_crtc_index(crtc); + + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + dcu_crtc->event = crtc->state->event; + crtc->state->event = NULL; + } } static int fsl_dcu_drm_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) { + struct fsl_dcu_drm_crtc *dcu_crtc = to_fsl_dcu_crtc(crtc); + + if (dcu_crtc->event != NULL && state->event != NULL) + return -EINVAL; + return 0; } @@ -38,38 +54,99 @@ static void fsl_dcu_drm_crtc_atomic_flush(struct drm_crtc *crtc, { } +void fsl_dcu_crtc_finish_page_flip(struct drm_device *dev) +{ + struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; + struct fsl_dcu_drm_crtc *dcu_crtc = &fsl_dev->crtc; + struct drm_crtc *crtc = &dcu_crtc->base; + unsigned long flags; + + spin_lock_irqsave(&crtc->dev->event_lock, flags); + if (dcu_crtc->event) { + drm_send_vblank_event(crtc->dev, + drm_crtc_index(crtc), + dcu_crtc->event); + drm_vblank_put(dev, drm_crtc_index(crtc)); + dcu_crtc->event = NULL; + } + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); +} + +void fsl_dcu_crtc_cancel_page_flip(struct drm_device *dev, struct drm_file *f) +{ + struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; + struct fsl_dcu_drm_crtc *dcu_crtc = &fsl_dev->crtc; + struct drm_crtc *crtc = &dcu_crtc->base; + struct drm_pending_vblank_event *event; + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + event = dcu_crtc->event; + + if (event && event->base.file_priv == f) { + event->base.destroy(&event->base); + drm_vblank_put(dev, drm_crtc_index(crtc)); + dcu_crtc->event = NULL; + } + spin_unlock_irqrestore(&dev->event_lock, flags); +} + static void fsl_dcu_drm_disable_crtc(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; - int ret; - - ret = regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, - DCU_MODE_DCU_MODE_MASK, - DCU_MODE_DCU_MODE(DCU_MODE_OFF)); - if (ret) - dev_err(fsl_dev->dev, "Disable CRTC failed\n"); - ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, - DCU_UPDATE_MODE_READREG); - if (ret) - dev_err(fsl_dev->dev, "Enable CRTC failed\n"); + int i; + unsigned int value; + unsigned int mode; + + /* Disable automatic transfer mode */ + regmap_update_bits(fsl_dev->regmap, DCU_UPDATE_MODE, + DCU_UPDATE_MODE_MODE, 0); + + /* Disable all planes */ + for (i = 0; i < fsl_dev->soc->total_layer; i++) { + regmap_read(fsl_dev->regmap, DCU_CTRLDESCLN(i, 4), &value); + value &= ~DCU_LAYER_EN; + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(i, 4), value); + } + regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, + DCU_UPDATE_MODE_READREG); + while (!regmap_read(fsl_dev->regmap, DCU_UPDATE_MODE, &mode) && + mode & DCU_UPDATE_MODE_READREG); + + regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, + DCU_MODE_DCU_MODE_MASK, + DCU_MODE_DCU_MODE(DCU_MODE_OFF)); + regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, + DCU_UPDATE_MODE_READREG); + clk_disable_unprepare(fsl_dev->pix_clk); } static void fsl_dcu_drm_crtc_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; - int ret; - - ret = regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, - DCU_MODE_DCU_MODE_MASK, - DCU_MODE_DCU_MODE(DCU_MODE_NORMAL)); - if (ret) - dev_err(fsl_dev->dev, "Enable CRTC failed\n"); - ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, - DCU_UPDATE_MODE_READREG); - if (ret) - dev_err(fsl_dev->dev, "Enable CRTC failed\n"); + unsigned int mode; + + if (clk_prepare_enable(fsl_dev->pix_clk) < 0) + dev_err(fsl_dev->dev, "failed to enable pix clk\n"); + + regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, + DCU_MODE_DCU_MODE_MASK, + DCU_MODE_DCU_MODE(DCU_MODE_NORMAL)); + regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, + DCU_UPDATE_MODE_READREG); + + /* + * Wait until transfer is complete and switch to automatic update + * mode. Automatic updates avoids flickers when changing layer + * parameters. + */ + while (!regmap_read(fsl_dev->regmap, DCU_UPDATE_MODE, &mode) && + mode & DCU_UPDATE_MODE_READREG); + regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, + DCU_UPDATE_MODE_MODE); + regmap_read(fsl_dev->regmap, DCU_UPDATE_MODE, &mode); } static bool fsl_dcu_drm_crtc_mode_fixup(struct drm_crtc *crtc, @@ -83,14 +160,12 @@ static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; + struct drm_connector *con = &fsl_dev->connector.base; struct drm_display_mode *mode = &crtc->state->mode; - unsigned int hbp, hfp, hsw, vbp, vfp, vsw, div, index; - unsigned long dcuclk; - int ret; + unsigned int hbp, hfp, hsw, vbp, vfp, vsw, index, pol = 0; index = drm_crtc_index(crtc); - dcuclk = clk_get_rate(fsl_dev->clk); - div = dcuclk / mode->clock / 1000; + clk_set_rate(fsl_dev->pix_clk, mode->clock * 1000); /* Configure timings: */ hbp = mode->htotal - mode->hsync_end; @@ -100,51 +175,37 @@ static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc) vfp = mode->vsync_start - mode->vdisplay; vsw = mode->vsync_end - mode->vsync_start; - ret = regmap_write(fsl_dev->regmap, DCU_HSYN_PARA, - DCU_HSYN_PARA_BP(hbp) | - DCU_HSYN_PARA_PW(hsw) | - DCU_HSYN_PARA_FP(hfp)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_VSYN_PARA, - DCU_VSYN_PARA_BP(vbp) | - DCU_VSYN_PARA_PW(vsw) | - DCU_VSYN_PARA_FP(vfp)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_DISP_SIZE, - DCU_DISP_SIZE_DELTA_Y(mode->vdisplay) | - DCU_DISP_SIZE_DELTA_X(mode->hdisplay)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_DIV_RATIO, div); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_SYN_POL, - DCU_SYN_POL_INV_VS_LOW | DCU_SYN_POL_INV_HS_LOW); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_BGND, DCU_BGND_R(0) | - DCU_BGND_G(0) | DCU_BGND_B(0)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_DCU_MODE, - DCU_MODE_BLEND_ITER(1) | DCU_MODE_RASTER_EN); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_THRESHOLD, - DCU_THRESHOLD_LS_BF_VS(BF_VS_VAL) | - DCU_THRESHOLD_OUT_BUF_HIGH(BUF_MAX_VAL) | - DCU_THRESHOLD_OUT_BUF_LOW(BUF_MIN_VAL)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, - DCU_UPDATE_MODE_READREG); - if (ret) - goto set_failed; + /* INV_PXCK as default (most display sample data on rising edge) */ + if (!(con->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE)) + pol |= DCU_SYN_POL_INV_PXCK; + + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + pol |= DCU_SYN_POL_INV_HS_LOW; + + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + pol |= DCU_SYN_POL_INV_VS_LOW; + + regmap_write(fsl_dev->regmap, DCU_HSYN_PARA, + DCU_HSYN_PARA_BP(hbp) | + DCU_HSYN_PARA_PW(hsw) | + DCU_HSYN_PARA_FP(hfp)); + regmap_write(fsl_dev->regmap, DCU_VSYN_PARA, + DCU_VSYN_PARA_BP(vbp) | + DCU_VSYN_PARA_PW(vsw) | + DCU_VSYN_PARA_FP(vfp)); + regmap_write(fsl_dev->regmap, DCU_DISP_SIZE, + DCU_DISP_SIZE_DELTA_Y(mode->vdisplay) | + DCU_DISP_SIZE_DELTA_X(mode->hdisplay)); + regmap_write(fsl_dev->regmap, DCU_SYN_POL, pol); + regmap_write(fsl_dev->regmap, DCU_BGND, DCU_BGND_R(0) | + DCU_BGND_G(0) | DCU_BGND_B(0)); + regmap_write(fsl_dev->regmap, DCU_DCU_MODE, + DCU_MODE_BLEND_ITER(1) | DCU_MODE_RASTER_EN); + regmap_write(fsl_dev->regmap, DCU_THRESHOLD, + DCU_THRESHOLD_LS_BF_VS(BF_VS_VAL) | + DCU_THRESHOLD_OUT_BUF_HIGH(BUF_MAX_VAL) | + DCU_THRESHOLD_OUT_BUF_LOW(BUF_MIN_VAL)); return; -set_failed: - dev_err(dev->dev, "set DCU register failed\n"); } static const struct drm_crtc_helper_funcs fsl_dcu_drm_crtc_helper_funcs = { @@ -168,43 +229,24 @@ static const struct drm_crtc_funcs fsl_dcu_drm_crtc_funcs = { int fsl_dcu_drm_crtc_create(struct fsl_dcu_drm_device *fsl_dev) { - struct drm_plane *primary; - struct drm_crtc *crtc = &fsl_dev->crtc; - unsigned int i, j, reg_num; + struct drm_plane *primary, *cursor; + struct fsl_dcu_drm_crtc *crtc = &fsl_dev->crtc; int ret; - primary = fsl_dcu_drm_primary_create_plane(fsl_dev->drm); - ret = drm_crtc_init_with_planes(fsl_dev->drm, crtc, primary, NULL, - &fsl_dcu_drm_crtc_funcs); - if (ret < 0) + fsl_dcu_drm_init_planes(fsl_dev->drm); + + ret = fsl_dcu_drm_create_planes(fsl_dev->drm, &primary, &cursor); + if (ret) return ret; - drm_crtc_helper_add(crtc, &fsl_dcu_drm_crtc_helper_funcs); - - if (!strcmp(fsl_dev->soc->name, "ls1021a")) - reg_num = LS1021A_LAYER_REG_NUM; - else - reg_num = VF610_LAYER_REG_NUM; - for (i = 0; i <= fsl_dev->soc->total_layer; i++) { - for (j = 0; j < reg_num; j++) { - ret = regmap_write(fsl_dev->regmap, - DCU_CTRLDESCLN(i, j), 0); - if (ret) - goto init_failed; - } + ret = drm_crtc_init_with_planes(fsl_dev->drm, &crtc->base, primary, + cursor, &fsl_dcu_drm_crtc_funcs); + if (ret) { + primary->funcs->destroy(primary); + return ret; } - ret = regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, - DCU_MODE_DCU_MODE_MASK, - DCU_MODE_DCU_MODE(DCU_MODE_OFF)); - if (ret) - goto init_failed; - ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, - DCU_UPDATE_MODE_READREG); - if (ret) - goto init_failed; + + drm_crtc_helper_add(&crtc->base, &fsl_dcu_drm_crtc_helper_funcs); return 0; -init_failed: - dev_err(fsl_dev->dev, "init DCU register failed\n"); - return ret; } diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.h b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.h index 43d4da2c5fe5..23a33023d324 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.h +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.h @@ -14,6 +14,19 @@ struct fsl_dcu_drm_device; +struct fsl_dcu_drm_crtc { + struct drm_crtc base; + struct drm_pending_vblank_event *event; +}; + +static inline struct fsl_dcu_drm_crtc *to_fsl_dcu_crtc(struct drm_crtc *crtc) +{ + return crtc ? container_of(crtc, struct fsl_dcu_drm_crtc, base) + : NULL; +} + int fsl_dcu_drm_crtc_create(struct fsl_dcu_drm_device *fsl_dev); +void fsl_dcu_crtc_finish_page_flip(struct drm_device *dev); +void fsl_dcu_crtc_cancel_page_flip(struct drm_device *dev, struct drm_file *f); #endif /* __FSL_DCU_DRM_CRTC_H__ */ diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c index 1930234ba5f1..f4e318d62d22 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c @@ -22,47 +22,58 @@ #include <linux/regmap.h> #include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_flip_work.h> +#include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> #include "fsl_dcu_drm_crtc.h" #include "fsl_dcu_drm_drv.h" +#include "fsl_tcon.h" + +static int legacyfb_depth = 24; +module_param(legacyfb_depth, int, 0444); + +static bool fsl_dcu_drm_is_volatile_reg(struct device *dev, unsigned int reg) +{ + if (reg == DCU_INT_STATUS || reg == DCU_UPDATE_MODE) + return true; + + return false; +} static const struct regmap_config fsl_dcu_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .cache_type = REGCACHE_RBTREE, + + .volatile_reg = fsl_dcu_drm_is_volatile_reg, }; static int fsl_dcu_drm_irq_init(struct drm_device *dev) { struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; - unsigned int value; int ret; ret = drm_irq_install(dev, fsl_dev->irq); if (ret < 0) dev_err(dev->dev, "failed to install IRQ handler\n"); - ret = regmap_write(fsl_dev->regmap, DCU_INT_STATUS, 0); - if (ret) - dev_err(dev->dev, "set DCU_INT_STATUS failed\n"); - ret = regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value); - if (ret) - dev_err(dev->dev, "read DCU_INT_MASK failed\n"); - value &= DCU_INT_MASK_VBLANK; - ret = regmap_write(fsl_dev->regmap, DCU_INT_MASK, value); - if (ret) - dev_err(dev->dev, "set DCU_INT_MASK failed\n"); - ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, - DCU_UPDATE_MODE_READREG); - if (ret) - dev_err(dev->dev, "set DCU_UPDATE_MODE failed\n"); + regmap_write(fsl_dev->regmap, DCU_INT_STATUS, 0); + regmap_write(fsl_dev->regmap, DCU_INT_MASK, ~0); return ret; } +static void fsl_dcu_unref_worker(struct drm_flip_work *work, void *val) +{ + struct drm_atomic_state *state = val; + struct drm_device *dev = state->dev; + + fsl_dcu_cleanup_atomic_state(dev, state); +} + static int fsl_dcu_load(struct drm_device *drm, unsigned long flags) { struct device *dev = drm->dev; @@ -82,12 +93,25 @@ static int fsl_dcu_load(struct drm_device *drm, unsigned long flags) } drm->vblank_disable_allowed = true; + drm_flip_work_init(&fsl_dev->unref_work, "unref", fsl_dcu_unref_worker); + fsl_dev->unref_wq = alloc_ordered_workqueue("fsl-dcu-drm", 0); + ret = fsl_dcu_drm_irq_init(drm); if (ret < 0) goto done; drm->irq_enabled = true; - fsl_dcu_fbdev_init(drm); + if (legacyfb_depth != 16 && legacyfb_depth != 24 && + legacyfb_depth != 32) { + dev_warn(dev, "Invalid legacyfb_depth. Defaulting to 24bpp\n"); + legacyfb_depth = 24; + } + fsl_dev->fbdev = drm_fbdev_cma_init(drm, legacyfb_depth, 1, 1); + if (IS_ERR(fsl_dev->fbdev)) { + ret = PTR_ERR(fsl_dev->fbdev); + fsl_dev->fbdev = NULL; + goto done; + } return 0; done: @@ -103,9 +127,12 @@ done: static int fsl_dcu_unload(struct drm_device *dev) { + struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; + drm_mode_config_cleanup(dev); drm_vblank_cleanup(dev); drm_irq_uninstall(dev); + drm_flip_work_cleanup(&fsl_dev->unref_work); dev->dev_private = NULL; @@ -114,6 +141,7 @@ static int fsl_dcu_unload(struct drm_device *dev) static void fsl_dcu_drm_preclose(struct drm_device *dev, struct drm_file *file) { + fsl_dcu_crtc_cancel_page_flip(dev, file); } static irqreturn_t fsl_dcu_drm_irq(int irq, void *arg) @@ -124,18 +152,27 @@ static irqreturn_t fsl_dcu_drm_irq(int irq, void *arg) int ret; ret = regmap_read(fsl_dev->regmap, DCU_INT_STATUS, &int_status); - if (ret) - dev_err(dev->dev, "set DCU_INT_STATUS failed\n"); - if (int_status & DCU_INT_STATUS_VBLANK) + if (ret) { + dev_err(dev->dev, "read DCU_INT_STATUS failed\n"); + return IRQ_NONE; + } + + if (int_status & DCU_INT_STATUS_VBLANK) { drm_handle_vblank(dev, 0); - ret = regmap_write(fsl_dev->regmap, DCU_INT_STATUS, 0xffffffff); - if (ret) - dev_err(dev->dev, "set DCU_INT_STATUS failed\n"); - ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, - DCU_UPDATE_MODE_READREG); - if (ret) - dev_err(dev->dev, "set DCU_UPDATE_MODE failed\n"); + fsl_dcu_crtc_finish_page_flip(dev); + + if (fsl_dev->cleanup_state) { + drm_flip_work_queue(&fsl_dev->unref_work, + fsl_dev->cleanup_state); + fsl_dev->cleanup_state = NULL; + + drm_flip_work_commit(&fsl_dev->unref_work, + fsl_dev->unref_wq); + } + } + + regmap_write(fsl_dev->regmap, DCU_INT_STATUS, int_status); return IRQ_HANDLED; } @@ -144,15 +181,11 @@ static int fsl_dcu_drm_enable_vblank(struct drm_device *dev, unsigned int pipe) { struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; unsigned int value; - int ret; - ret = regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value); - if (ret) - dev_err(dev->dev, "read DCU_INT_MASK failed\n"); + regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value); value &= ~DCU_INT_MASK_VBLANK; - ret = regmap_write(fsl_dev->regmap, DCU_INT_MASK, value); - if (ret) - dev_err(dev->dev, "set DCU_INT_MASK failed\n"); + regmap_write(fsl_dev->regmap, DCU_INT_MASK, value); + return 0; } @@ -161,15 +194,17 @@ static void fsl_dcu_drm_disable_vblank(struct drm_device *dev, { struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; unsigned int value; - int ret; - ret = regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value); - if (ret) - dev_err(dev->dev, "read DCU_INT_MASK failed\n"); + regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value); value |= DCU_INT_MASK_VBLANK; - ret = regmap_write(fsl_dev->regmap, DCU_INT_MASK, value); - if (ret) - dev_err(dev->dev, "set DCU_INT_MASK failed\n"); + regmap_write(fsl_dev->regmap, DCU_INT_MASK, value); +} + +static void fsl_dcu_drm_lastclose(struct drm_device *dev) +{ + struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; + + drm_fbdev_cma_restore_mode(fsl_dev->fbdev); } static const struct file_operations fsl_dcu_drm_fops = { @@ -189,6 +224,7 @@ static const struct file_operations fsl_dcu_drm_fops = { static struct drm_driver fsl_dcu_drm_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, + .lastclose = fsl_dcu_drm_lastclose, .load = fsl_dcu_load, .unload = fsl_dcu_unload, .preclose = fsl_dcu_drm_preclose, @@ -226,11 +262,18 @@ static int fsl_dcu_drm_pm_suspend(struct device *dev) if (!fsl_dev) return 0; + disable_irq(fsl_dev->irq); drm_kms_helper_poll_disable(fsl_dev->drm); - regcache_cache_only(fsl_dev->regmap, true); - regcache_mark_dirty(fsl_dev->regmap); - clk_disable(fsl_dev->clk); - clk_unprepare(fsl_dev->clk); + fsl_dcu_fbdev_suspend(fsl_dev->drm); + + fsl_dev->state = drm_atomic_helper_suspend(fsl_dev->drm); + if (IS_ERR(fsl_dev->state)) { + fsl_dcu_fbdev_resume(fsl_dev->drm); + enable_irq(fsl_dev->irq); + return PTR_ERR(fsl_dev->state); + } + + clk_disable_unprepare(fsl_dev->clk); return 0; } @@ -243,21 +286,22 @@ static int fsl_dcu_drm_pm_resume(struct device *dev) if (!fsl_dev) return 0; - ret = clk_enable(fsl_dev->clk); + ret = clk_prepare_enable(fsl_dev->clk); if (ret < 0) { dev_err(dev, "failed to enable dcu clk\n"); - clk_unprepare(fsl_dev->clk); - return ret; - } - ret = clk_prepare(fsl_dev->clk); - if (ret < 0) { - dev_err(dev, "failed to prepare dcu clk\n"); return ret; } + fsl_tcon_bypass_enable(fsl_dev->tcon); + fsl_dcu_drm_init_planes(fsl_dev->drm); + + drm_atomic_helper_resume(fsl_dev->drm, fsl_dev->state); + + regmap_write(fsl_dev->regmap, DCU_INT_MASK, fsl_dev->irq_state); + + fsl_dcu_fbdev_resume(fsl_dev->drm); drm_kms_helper_poll_enable(fsl_dev->drm); - regcache_cache_only(fsl_dev->regmap, false); - regcache_sync(fsl_dev->regmap); + enable_irq(fsl_dev->irq); return 0; } @@ -271,12 +315,14 @@ static const struct fsl_dcu_soc_data fsl_dcu_ls1021a_data = { .name = "ls1021a", .total_layer = 16, .max_layer = 4, + .layer_regs = LS1021A_LAYER_REG_NUM, }; static const struct fsl_dcu_soc_data fsl_dcu_vf610_data = { .name = "vf610", .total_layer = 64, .max_layer = 6, + .layer_regs = VF610_LAYER_REG_NUM, }; static const struct of_device_id fsl_dcu_of_match[] = { @@ -299,6 +345,9 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev) struct resource *res; void __iomem *base; struct drm_driver *driver = &fsl_dcu_drm_driver; + struct clk *pix_clk_in; + char pix_clk_name[32]; + const char *pix_clk_in_name; const struct of_device_id *id; int ret; @@ -306,6 +355,11 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev) if (!fsl_dev) return -ENOMEM; + id = of_match_node(fsl_dcu_of_match, pdev->dev.of_node); + if (!id) + return -ENODEV; + fsl_dev->soc = id->data; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "could not get memory IO resource\n"); @@ -324,40 +378,48 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev) return -ENXIO; } + fsl_dev->regmap = devm_regmap_init_mmio(dev, base, + &fsl_dcu_regmap_config); + if (IS_ERR(fsl_dev->regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(fsl_dev->regmap); + } + fsl_dev->clk = devm_clk_get(dev, "dcu"); if (IS_ERR(fsl_dev->clk)) { - ret = PTR_ERR(fsl_dev->clk); dev_err(dev, "failed to get dcu clock\n"); - return ret; - } - ret = clk_prepare(fsl_dev->clk); - if (ret < 0) { - dev_err(dev, "failed to prepare dcu clk\n"); - return ret; + return PTR_ERR(fsl_dev->clk); } - ret = clk_enable(fsl_dev->clk); + ret = clk_prepare_enable(fsl_dev->clk); if (ret < 0) { dev_err(dev, "failed to enable dcu clk\n"); - clk_unprepare(fsl_dev->clk); return ret; } - fsl_dev->regmap = devm_regmap_init_mmio(dev, base, - &fsl_dcu_regmap_config); - if (IS_ERR(fsl_dev->regmap)) { - dev_err(dev, "regmap init failed\n"); - return PTR_ERR(fsl_dev->regmap); + pix_clk_in = devm_clk_get(dev, "pix"); + if (IS_ERR(pix_clk_in)) { + /* legancy binding, use dcu clock as pixel clock input */ + pix_clk_in = fsl_dev->clk; } - id = of_match_node(fsl_dcu_of_match, pdev->dev.of_node); - if (!id) - return -ENODEV; - fsl_dev->soc = id->data; + pix_clk_in_name = __clk_get_name(pix_clk_in); + snprintf(pix_clk_name, sizeof(pix_clk_name), "%s_pix", pix_clk_in_name); + fsl_dev->pix_clk = clk_register_divider(dev, pix_clk_name, + pix_clk_in_name, 0, base + DCU_DIV_RATIO, + 0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL); + if (IS_ERR(fsl_dev->pix_clk)) { + dev_err(dev, "failed to register pix clk\n"); + ret = PTR_ERR(fsl_dev->pix_clk); + goto disable_clk; + } drm = drm_dev_alloc(driver, dev); - if (!drm) - return -ENOMEM; + if (!drm) { + ret = -ENOMEM; + goto unregister_pix_clk; + } + fsl_dev->tcon = fsl_tcon_init(dev); fsl_dev->dev = dev; fsl_dev->drm = drm; fsl_dev->np = dev->of_node; @@ -377,6 +439,10 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev) unref: drm_dev_unref(drm); +unregister_pix_clk: + clk_unregister(fsl_dev->pix_clk); +disable_clk: + clk_disable_unprepare(fsl_dev->clk); return ret; } @@ -384,6 +450,9 @@ static int fsl_dcu_drm_remove(struct platform_device *pdev) { struct fsl_dcu_drm_device *fsl_dev = platform_get_drvdata(pdev); + clk_disable_unprepare(fsl_dev->clk); + clk_disable_unprepare(fsl_dev->pix_clk); + clk_unregister(fsl_dev->pix_clk); drm_put_dev(fsl_dev->drm); return 0; diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h index 579b9e44e764..22210766e77d 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h @@ -47,8 +47,8 @@ #define DCU_VSYN_PARA_FP(x) (x) #define DCU_SYN_POL 0x0024 -#define DCU_SYN_POL_INV_PXCK_FALL (0 << 6) -#define DCU_SYN_POL_NEG_REMAIN (0 << 5) +#define DCU_SYN_POL_INV_PXCK BIT(6) +#define DCU_SYN_POL_NEG BIT(5) #define DCU_SYN_POL_INV_VS_LOW BIT(1) #define DCU_SYN_POL_INV_HS_LOW BIT(0) @@ -118,11 +118,11 @@ #define DCU_CTRLDESCLN(layer, reg) (0x200 + (reg - 1) * 4 + (layer) * 0x40) -#define DCU_LAYER_HEIGHT(x) ((x) << 16) -#define DCU_LAYER_WIDTH(x) (x) +#define DCU_LAYER_HEIGHT(x) (((x) & 0x7ff) << 16) +#define DCU_LAYER_WIDTH(x) ((x) & 0x7ff) -#define DCU_LAYER_POSY(x) ((x) << 16) -#define DCU_LAYER_POSX(x) (x) +#define DCU_LAYER_POSY(x) (((x) & 0xfff) << 16) +#define DCU_LAYER_POSX(x) ((x) & 0xfff) #define DCU_LAYER_EN BIT(31) #define DCU_LAYER_TILE_EN BIT(30) @@ -133,7 +133,9 @@ #define DCU_LAYER_RLE_EN BIT(15) #define DCU_LAYER_LUOFFS(x) ((x) << 4) #define DCU_LAYER_BB_ON BIT(2) -#define DCU_LAYER_AB(x) (x) +#define DCU_LAYER_AB_NONE 0 +#define DCU_LAYER_AB_CHROMA_KEYING 1 +#define DCU_LAYER_AB_WHOLE_FRAME 2 #define DCU_LAYER_CKMAX_R(x) ((x) << 16) #define DCU_LAYER_CKMAX_G(x) ((x) << 8) @@ -166,6 +168,7 @@ struct clk; struct device; struct drm_device; +struct drm_flip_work; struct fsl_dcu_soc_data { const char *name; @@ -173,6 +176,7 @@ struct fsl_dcu_soc_data { unsigned int total_layer; /*max layer number DCU supported*/ unsigned int max_layer; + unsigned int layer_regs; }; struct fsl_dcu_drm_device { @@ -181,17 +185,27 @@ struct fsl_dcu_drm_device { struct regmap *regmap; int irq; struct clk *clk; + struct clk *pix_clk; + struct fsl_tcon *tcon; /*protects hardware register*/ spinlock_t irq_lock; struct drm_device *drm; struct drm_fbdev_cma *fbdev; - struct drm_crtc crtc; + struct fsl_dcu_drm_crtc crtc; struct drm_encoder encoder; struct fsl_dcu_drm_connector connector; const struct fsl_dcu_soc_data *soc; + struct drm_atomic_state *cleanup_state; + struct workqueue_struct *unref_wq; + struct drm_flip_work unref_work; + struct drm_atomic_state *state; + unsigned int irq_state; }; -void fsl_dcu_fbdev_init(struct drm_device *dev); +void fsl_dcu_fbdev_suspend(struct drm_device *dev); +void fsl_dcu_fbdev_resume(struct drm_device *dev); int fsl_dcu_drm_modeset_init(struct fsl_dcu_drm_device *fsl_dev); +void fsl_dcu_cleanup_atomic_state(struct drm_device *dev, + struct drm_atomic_state *state); #endif /* __FSL_DCU_DRM_DRV_H__ */ diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_fbdev.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_fbdev.c index 8b8b819ea704..eaa447e26423 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_fbdev.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_fbdev.c @@ -9,15 +9,29 @@ * (at your option) any later version. */ +#include <linux/console.h> + #include <drm/drmP.h> #include <drm/drm_fb_cma_helper.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_flip_work.h> #include "fsl_dcu_drm_drv.h" -/* initialize fbdev helper */ -void fsl_dcu_fbdev_init(struct drm_device *dev) +void fsl_dcu_fbdev_suspend(struct drm_device *dev) +{ + struct fsl_dcu_drm_device *fsl_dev = dev_get_drvdata(dev->dev); + + console_lock(); + drm_fb_helper_set_suspend(drm_fbdev_cma_get_helper(fsl_dev->fbdev), 1); + console_unlock(); +} + +void fsl_dcu_fbdev_resume(struct drm_device *dev) { struct fsl_dcu_drm_device *fsl_dev = dev_get_drvdata(dev->dev); - fsl_dev->fbdev = drm_fbdev_cma_init(dev, 24, 1, 1); + console_lock(); + drm_fb_helper_set_suspend(drm_fbdev_cma_get_helper(fsl_dev->fbdev), 0); + console_unlock(); } diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c index 0ef5959710e7..710d1ca7a9b4 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c @@ -10,21 +10,64 @@ */ #include <drm/drmP.h> +#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_flip_work.h> #include <drm/drm_fb_cma_helper.h> #include "fsl_dcu_drm_crtc.h" #include "fsl_dcu_drm_drv.h" +void fsl_dcu_cleanup_atomic_state(struct drm_device *dev, + struct drm_atomic_state *state) +{ + drm_atomic_helper_cleanup_planes(dev, state); + drm_atomic_state_free(state); +} + +static int fsl_dcu_drm_atomic_commit(struct drm_device *dev, + struct drm_atomic_state *state, + bool async) +{ + struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; + int ret; + + ret = drm_atomic_helper_prepare_planes(dev, state); + if (ret < 0) + return ret; + + /* + * This is the point of no return - everything below never fails except + * when the hw goes bonghits. Which means we can commit the new state on + * the software side now. + */ + drm_atomic_helper_swap_state(dev, state); + + drm_atomic_helper_commit_modeset_disables(dev, state); + drm_atomic_helper_commit_planes(dev, state, false); + drm_atomic_helper_commit_modeset_enables(dev, state); + + if (async) { + fsl_dev->cleanup_state = state; + } else { + drm_atomic_helper_wait_for_vblanks(dev, state); + fsl_dcu_cleanup_atomic_state(dev, state); + } + + return 0; +} + static const struct drm_mode_config_funcs fsl_dcu_drm_mode_config_funcs = { .atomic_check = drm_atomic_helper_check, - .atomic_commit = drm_atomic_helper_commit, + .atomic_commit = fsl_dcu_drm_atomic_commit, .fb_create = drm_fb_cma_create, }; int fsl_dcu_drm_modeset_init(struct fsl_dcu_drm_device *fsl_dev) { + int ret; + drm_mode_config_init(fsl_dev->drm); fsl_dev->drm->mode_config.min_width = 0; @@ -33,11 +76,25 @@ int fsl_dcu_drm_modeset_init(struct fsl_dcu_drm_device *fsl_dev) fsl_dev->drm->mode_config.max_height = 2047; fsl_dev->drm->mode_config.funcs = &fsl_dcu_drm_mode_config_funcs; - drm_kms_helper_poll_init(fsl_dev->drm); - fsl_dcu_drm_crtc_create(fsl_dev); - fsl_dcu_drm_encoder_create(fsl_dev, &fsl_dev->crtc); - fsl_dcu_drm_connector_create(fsl_dev, &fsl_dev->encoder); + ret = fsl_dcu_drm_crtc_create(fsl_dev); + if (ret) + return ret; + + ret = fsl_dcu_drm_encoder_create(fsl_dev, &fsl_dev->crtc.base); + if (ret) + goto fail_encoder; + + ret = fsl_dcu_drm_connector_create(fsl_dev, &fsl_dev->encoder); + if (ret) + goto fail_connector; + drm_mode_config_reset(fsl_dev->drm); + drm_kms_helper_poll_init(fsl_dev->drm); return 0; +fail_encoder: + fsl_dev->crtc.base.funcs->destroy(&fsl_dev->crtc.base); +fail_connector: + fsl_dev->encoder.funcs->destroy(&fsl_dev->encoder); + return ret; } diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c index 51daaea40b4d..3ad0debacadf 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c @@ -15,6 +15,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_flip_work.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_plane_helper.h> @@ -41,11 +42,17 @@ static int fsl_dcu_drm_plane_atomic_check(struct drm_plane *plane, { struct drm_framebuffer *fb = state->fb; + if (!state->fb || !state->crtc) + return 0; + switch (fb->pixel_format) { case DRM_FORMAT_RGB565: case DRM_FORMAT_RGB888: + case DRM_FORMAT_XRGB8888: case DRM_FORMAT_ARGB8888: - case DRM_FORMAT_BGRA4444: + case DRM_FORMAT_XRGB4444: + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_XRGB1555: case DRM_FORMAT_ARGB1555: case DRM_FORMAT_YUV422: return 0; @@ -59,19 +66,15 @@ static void fsl_dcu_drm_plane_atomic_disable(struct drm_plane *plane, { struct fsl_dcu_drm_device *fsl_dev = plane->dev->dev_private; unsigned int value; - int index, ret; + int index; index = fsl_dcu_drm_plane_index(plane); if (index < 0) return; - ret = regmap_read(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), &value); - if (ret) - dev_err(fsl_dev->dev, "read DCU_INT_MASK failed\n"); + regmap_read(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), &value); value &= ~DCU_LAYER_EN; - ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), value); - if (ret) - dev_err(fsl_dev->dev, "set DCU register failed\n"); + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), value); } static void fsl_dcu_drm_plane_atomic_update(struct drm_plane *plane, @@ -82,8 +85,8 @@ static void fsl_dcu_drm_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *state = plane->state; struct drm_framebuffer *fb = plane->state->fb; struct drm_gem_cma_object *gem; - unsigned int alpha, bpp; - int index, ret; + unsigned int alpha = DCU_LAYER_AB_NONE, bpp; + int index; if (!fb) return; @@ -97,96 +100,69 @@ static void fsl_dcu_drm_plane_atomic_update(struct drm_plane *plane, switch (fb->pixel_format) { case DRM_FORMAT_RGB565: bpp = FSL_DCU_RGB565; - alpha = 0xff; break; case DRM_FORMAT_RGB888: bpp = FSL_DCU_RGB888; - alpha = 0xff; break; case DRM_FORMAT_ARGB8888: + alpha = DCU_LAYER_AB_WHOLE_FRAME; + /* fall-through */ + case DRM_FORMAT_XRGB8888: bpp = FSL_DCU_ARGB8888; - alpha = 0xff; break; - case DRM_FORMAT_BGRA4444: + case DRM_FORMAT_ARGB4444: + alpha = DCU_LAYER_AB_WHOLE_FRAME; + /* fall-through */ + case DRM_FORMAT_XRGB4444: bpp = FSL_DCU_ARGB4444; - alpha = 0xff; break; case DRM_FORMAT_ARGB1555: + alpha = DCU_LAYER_AB_WHOLE_FRAME; + /* fall-through */ + case DRM_FORMAT_XRGB1555: bpp = FSL_DCU_ARGB1555; - alpha = 0xff; break; case DRM_FORMAT_YUV422: bpp = FSL_DCU_YUV422; - alpha = 0xff; break; default: return; } - ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 1), - DCU_LAYER_HEIGHT(state->crtc_h) | - DCU_LAYER_WIDTH(state->crtc_w)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 2), - DCU_LAYER_POSY(state->crtc_y) | - DCU_LAYER_POSX(state->crtc_x)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, - DCU_CTRLDESCLN(index, 3), gem->paddr); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), - DCU_LAYER_EN | - DCU_LAYER_TRANS(alpha) | - DCU_LAYER_BPP(bpp) | - DCU_LAYER_AB(0)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 5), - DCU_LAYER_CKMAX_R(0xFF) | - DCU_LAYER_CKMAX_G(0xFF) | - DCU_LAYER_CKMAX_B(0xFF)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 6), - DCU_LAYER_CKMIN_R(0) | - DCU_LAYER_CKMIN_G(0) | - DCU_LAYER_CKMIN_B(0)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 7), 0); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 8), - DCU_LAYER_FG_FCOLOR(0)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 9), - DCU_LAYER_BG_BCOLOR(0)); - if (ret) - goto set_failed; + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 1), + DCU_LAYER_HEIGHT(state->crtc_h) | + DCU_LAYER_WIDTH(state->crtc_w)); + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 2), + DCU_LAYER_POSY(state->crtc_y) | + DCU_LAYER_POSX(state->crtc_x)); + regmap_write(fsl_dev->regmap, + DCU_CTRLDESCLN(index, 3), gem->paddr); + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), + DCU_LAYER_EN | + DCU_LAYER_TRANS(0xff) | + DCU_LAYER_BPP(bpp) | + alpha); + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 5), + DCU_LAYER_CKMAX_R(0xFF) | + DCU_LAYER_CKMAX_G(0xFF) | + DCU_LAYER_CKMAX_B(0xFF)); + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 6), + DCU_LAYER_CKMIN_R(0) | + DCU_LAYER_CKMIN_G(0) | + DCU_LAYER_CKMIN_B(0)); + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 7), 0); + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 8), + DCU_LAYER_FG_FCOLOR(0)); + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 9), + DCU_LAYER_BG_BCOLOR(0)); + if (!strcmp(fsl_dev->soc->name, "ls1021a")) { - ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 10), - DCU_LAYER_POST_SKIP(0) | - DCU_LAYER_PRE_SKIP(0)); - if (ret) - goto set_failed; + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 10), + DCU_LAYER_POST_SKIP(0) | + DCU_LAYER_PRE_SKIP(0)); } - ret = regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, - DCU_MODE_DCU_MODE_MASK, - DCU_MODE_DCU_MODE(DCU_MODE_NORMAL)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, - DCU_UPDATE_MODE, DCU_UPDATE_MODE_READREG); - if (ret) - goto set_failed; - return; -set_failed: - dev_err(fsl_dev->dev, "set DCU register failed\n"); + return; } static void @@ -213,6 +189,7 @@ static const struct drm_plane_helper_funcs fsl_dcu_drm_plane_helper_funcs = { static void fsl_dcu_drm_plane_destroy(struct drm_plane *plane) { drm_plane_cleanup(plane); + kfree(plane); } static const struct drm_plane_funcs fsl_dcu_drm_plane_funcs = { @@ -227,34 +204,69 @@ static const struct drm_plane_funcs fsl_dcu_drm_plane_funcs = { static const u32 fsl_dcu_drm_plane_formats[] = { DRM_FORMAT_RGB565, DRM_FORMAT_RGB888, + DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, + DRM_FORMAT_XRGB4444, DRM_FORMAT_ARGB4444, + DRM_FORMAT_XRGB1555, DRM_FORMAT_ARGB1555, DRM_FORMAT_YUV422, }; -struct drm_plane *fsl_dcu_drm_primary_create_plane(struct drm_device *dev) +void fsl_dcu_drm_init_planes(struct drm_device *dev) { - struct drm_plane *primary; - int ret; + struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; + int i, j; - primary = kzalloc(sizeof(*primary), GFP_KERNEL); - if (!primary) { - DRM_DEBUG_KMS("Failed to allocate primary plane\n"); - return NULL; + for (i = 0; i < fsl_dev->soc->total_layer; i++) { + for (j = 1; j <= fsl_dev->soc->layer_regs; j++) + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(i, j), 0); } +} + +int fsl_dcu_drm_create_planes(struct drm_device *dev, struct drm_plane **primary, + struct drm_plane **cursor) +{ + struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; + struct drm_plane *planes, *plane; + int total_layer = fsl_dev->soc->total_layer; + int ret, i; - /* possible_crtc's will be filled in later by crtc_init */ - ret = drm_universal_plane_init(dev, primary, 0, + planes = devm_kzalloc(dev->dev, sizeof(struct drm_plane) * total_layer, + GFP_KERNEL); + if (!planes) { + DRM_DEBUG_KMS("Failed to allocate planes\n"); + return -ENOMEM; + } + + plane = planes; + + for (i = 0; i < total_layer; i++) { + enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY; + if (i == 0) { + type = DRM_PLANE_TYPE_PRIMARY; + *primary = plane; + } else if (i == total_layer - 1) { + type = DRM_PLANE_TYPE_CURSOR; + *cursor = plane; + } + + ret = drm_universal_plane_init(dev, plane, 1, &fsl_dcu_drm_plane_funcs, fsl_dcu_drm_plane_formats, ARRAY_SIZE(fsl_dcu_drm_plane_formats), - DRM_PLANE_TYPE_PRIMARY); - if (ret) { - kfree(primary); - primary = NULL; + type); + if (ret) + goto err_cleanup_planes; + + drm_plane_helper_add(plane, &fsl_dcu_drm_plane_helper_funcs); + plane++; } - drm_plane_helper_add(primary, &fsl_dcu_drm_plane_helper_funcs); - return primary; + return 0; + +err_cleanup_planes: + list_for_each_entry(plane, &dev->mode_config.plane_list, head) + drm_plane_cleanup(plane); + return ret; } diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.h b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.h index d657f088d859..be3604fb43ce 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.h +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.h @@ -12,6 +12,8 @@ #ifndef __FSL_DCU_DRM_PLANE_H__ #define __FSL_DCU_DRM_PLANE_H__ -struct drm_plane *fsl_dcu_drm_primary_create_plane(struct drm_device *dev); +void fsl_dcu_drm_init_planes(struct drm_device *dev); +int fsl_dcu_drm_create_planes(struct drm_device *dev, struct drm_plane **primary, + struct drm_plane **cursor); #endif /* __FSL_DCU_DRM_PLANE_H__ */ diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c index fe8ab5da04fb..4246129972b6 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c @@ -14,9 +14,11 @@ #include <drm/drmP.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_flip_work.h> #include <drm/drm_panel.h> #include "fsl_dcu_drm_drv.h" +#include "fsl_tcon.h" static int fsl_dcu_drm_encoder_atomic_check(struct drm_encoder *encoder, @@ -56,6 +58,10 @@ int fsl_dcu_drm_encoder_create(struct fsl_dcu_drm_device *fsl_dev, int ret; encoder->possible_crtcs = 1; + + /* Set TCON to bypass for parallel RGB/LVDS */ + fsl_tcon_bypass_enable(fsl_dev->tcon); + ret = drm_encoder_init(fsl_dev->drm, encoder, &encoder_funcs, DRM_MODE_ENCODER_LVDS); if (ret < 0) diff --git a/drivers/gpu/drm/fsl-dcu/fsl_tcon.c b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c new file mode 100644 index 000000000000..e5001a186850 --- /dev/null +++ b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c @@ -0,0 +1,108 @@ +/* + * Copyright 2015 Toradex AG + * + * Stefan Agner <stefan@agner.ch> + * + * Freescale TCON device driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/mm.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include "fsl_tcon.h" + +void fsl_tcon_bypass_disable(struct fsl_tcon *tcon) +{ + regmap_update_bits(tcon->regs, FSL_TCON_CTRL1, + FSL_TCON_CTRL1_TCON_BYPASS, 0); +} + +void fsl_tcon_bypass_enable(struct fsl_tcon *tcon) +{ + regmap_update_bits(tcon->regs, FSL_TCON_CTRL1, + FSL_TCON_CTRL1_TCON_BYPASS, + FSL_TCON_CTRL1_TCON_BYPASS); +} + +static struct regmap_config fsl_tcon_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .name = "tcon", +}; + +static int fsl_tcon_init_regmap(struct device *dev, + struct fsl_tcon *tcon, + struct device_node *np) +{ + struct resource res; + void __iomem *regs; + + if (of_address_to_resource(np, 0, &res)) + return -EINVAL; + + regs = devm_ioremap_resource(dev, &res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + tcon->regs = devm_regmap_init_mmio(dev, regs, + &fsl_tcon_regmap_config); + if (IS_ERR(tcon->regs)) + return PTR_ERR(tcon->regs); + + return 0; +} + +struct fsl_tcon *fsl_tcon_init(struct device *dev) +{ + struct fsl_tcon *tcon; + struct device_node *np; + int ret; + + tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL); + if (!tcon) + return NULL; + + np = of_parse_phandle(dev->of_node, "fsl,tcon", 0); + if (!np) { + dev_warn(dev, "Couldn't find the tcon node\n"); + return NULL; + } + + ret = fsl_tcon_init_regmap(dev, tcon, np); + if (ret) { + dev_err(dev, "Couldn't create the TCON regmap\n"); + goto err_node_put; + } + + tcon->ipg_clk = of_clk_get_by_name(np, "ipg"); + if (IS_ERR(tcon->ipg_clk)) { + dev_err(dev, "Couldn't get the TCON bus clock\n"); + goto err_node_put; + } + + clk_prepare_enable(tcon->ipg_clk); + + return tcon; + +err_node_put: + of_node_put(np); + return NULL; +} + +void fsl_tcon_free(struct fsl_tcon *tcon) +{ + clk_disable_unprepare(tcon->ipg_clk); + clk_put(tcon->ipg_clk); +} + diff --git a/drivers/gpu/drm/fsl-dcu/fsl_tcon.h b/drivers/gpu/drm/fsl-dcu/fsl_tcon.h new file mode 100644 index 000000000000..80a7617de58f --- /dev/null +++ b/drivers/gpu/drm/fsl-dcu/fsl_tcon.h @@ -0,0 +1,33 @@ +/* + * Copyright 2015 Toradex AG + * + * Stefan Agner <stefan@agner.ch> + * + * Freescale TCON device driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __FSL_TCON_H__ +#define __FSL_TCON_H__ + +#include <linux/bitops.h> + +#define FSL_TCON_CTRL1 0x0 +#define FSL_TCON_CTRL1_TCON_BYPASS BIT(29) + +struct fsl_tcon { + struct regmap *regs; + struct clk *ipg_clk; +}; + +struct fsl_tcon *fsl_tcon_init(struct device *dev); +void fsl_tcon_free(struct fsl_tcon *tcon); + +void fsl_tcon_bypass_disable(struct fsl_tcon *tcon); +void fsl_tcon_bypass_enable(struct fsl_tcon *tcon); + +#endif /* __FSL_TCON_H__ */ diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index 5378bdc3bbf9..5f936223e67b 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -313,6 +313,7 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) dev_warn(drm->dev, "Invalid legacyfb_depth. Defaulting to 16bpp\n"); legacyfb_depth = 16; } + drm_helper_disable_unused_functions(drm); imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth, drm->mode_config.num_crtc, MAX_CRTC); if (IS_ERR(imxdrm->fbhelper)) { diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index f418c002d323..5101e2544f96 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -68,6 +68,7 @@ struct panel_desc { } delay; u32 bus_format; + u32 bus_flags; }; struct panel_simple { @@ -140,6 +141,7 @@ static int panel_simple_get_fixed_modes(struct panel_simple *panel) if (panel->desc->bus_format) drm_display_info_set_bus_formats(&connector->display_info, &panel->desc->bus_format, 1); + connector->display_info.bus_flags = panel->desc->bus_flags; return num; } @@ -951,6 +953,29 @@ static const struct panel_desc lg_lp129qe = { }, }; +static const struct drm_display_mode logic_lt161010_2nhc_mode = { + .clock = 33300, + .hdisplay = 800, + .hsync_start = 800 + 40, + .hsync_end = 800 + 40 + 128, + .htotal = 800 + 40 + 128 + 88, + .vdisplay = 480, + .vsync_start = 480 + 10, + .vsync_end = 480 + 2 + 10, + .vtotal = 480 + 2 + 10 + 33, + .vrefresh = 60, +}; + +static const struct panel_desc logic_lt161010_2nhc = { + .modes = &logic_lt161010_2nhc_mode, + .num_modes = 1, + .size = { + .width = 165, + .height = 100, + }, + .bus_flags = DRM_BUS_FLAG_PIXDATA_POSEDGE, +}; + static const struct drm_display_mode nec_nl4827hc19_05b_mode = { .clock = 10870, .hdisplay = 480, @@ -962,6 +987,7 @@ static const struct drm_display_mode nec_nl4827hc19_05b_mode = { .vsync_end = 272 + 2 + 4, .vtotal = 272 + 2 + 4 + 2, .vrefresh = 74, + .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, }; static const struct panel_desc nec_nl4827hc19_05b = { @@ -972,7 +998,8 @@ static const struct panel_desc nec_nl4827hc19_05b = { .width = 95, .height = 54, }, - .bus_format = MEDIA_BUS_FMT_RGB888_1X24 + .bus_format = MEDIA_BUS_FMT_RGB888_1X24, + .bus_flags = DRM_BUS_FLAG_PIXDATA_POSEDGE, }; static const struct display_timing okaya_rs800480t_7x0gp_timing = { @@ -1098,6 +1125,51 @@ static const struct panel_desc shelly_sca07010_bfn_lnn = { .bus_format = MEDIA_BUS_FMT_RGB666_1X18, }; +static const struct drm_display_mode tpk_f07a_0102_mode = { + .clock = 33260, + .hdisplay = 800, + .hsync_start = 800 + 40, + .hsync_end = 800 + 40 + 128, + .htotal = 800 + 40 + 128 + 88, + .vdisplay = 480, + .vsync_start = 480 + 10, + .vsync_end = 480 + 10 + 2, + .vtotal = 480 + 10 + 2 + 33, + .vrefresh = 60, +}; + +static const struct panel_desc tpk_f07a_0102 = { + .modes = &tpk_f07a_0102_mode, + .num_modes = 1, + .size = { + .width = 152, + .height = 91, + }, + .bus_flags = DRM_BUS_FLAG_PIXDATA_POSEDGE, +}; + +static const struct drm_display_mode tpk_f10a_0102_mode = { + .clock = 45000, + .hdisplay = 1024, + .hsync_start = 1024 + 176, + .hsync_end = 1024 + 176 + 5, + .htotal = 1024 + 176 + 5 + 88, + .vdisplay = 600, + .vsync_start = 600 + 20, + .vsync_end = 600 + 20 + 5, + .vtotal = 600 + 20 + 5 + 25, + .vrefresh = 60, +}; + +static const struct panel_desc tpk_f10a_0102 = { + .modes = &tpk_f10a_0102_mode, + .num_modes = 1, + .size = { + .width = 223, + .height = 125, + }, +}; + static const struct of_device_id platform_of_match[] = { { .compatible = "ampire,am800480r3tmqwa1h", @@ -1175,6 +1247,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "lg,lp129qe", .data = &lg_lp129qe, }, { + .compatible = "logic,lt161010-2nhc", + .data = &logic_lt161010_2nhc, + }, { .compatible = "nec,nl4827hc19-05b", .data = &nec_nl4827hc19_05b, }, { @@ -1193,6 +1268,12 @@ static const struct of_device_id platform_of_match[] = { .compatible = "shelly,sca07010-bfn-lnn", .data = &shelly_sca07010_bfn_lnn, }, { + .compatible = "tpk,f07a-0102", + .data = &tpk_f07a_0102, + }, { + .compatible = "tpk,f10a-0102", + .data = &tpk_f10a_0102, + }, { /* sentinel */ } }; diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index 1469987949d8..506b5626f3ed 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -160,6 +160,7 @@ static int sti_load(struct drm_device *dev, unsigned long flags) drm_mode_config_reset(dev); + drm_helper_disable_unused_functions(dev); drm_fbdev_cma_init(dev, 32, dev->mode_config.num_crtc, dev->mode_config.num_connector); diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 876cad58b1f9..24be31d69701 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -294,6 +294,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) break; } + drm_helper_disable_unused_functions(dev); priv->fbdev = drm_fbdev_cma_init(dev, bpp, dev->mode_config.num_crtc, dev->mode_config.num_connector); |