diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 549b534a3f4d..61163578c06c 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -40,6 +40,8 @@ msm-y := \ mdp/mdp5/mdp5_smp.o \ sde/sde_crtc.o \ sde/sde_encoder.o \ + sde/sde_encoder_phys_vid.o \ + sde/sde_encoder_phys_cmd.o \ sde/sde_irq.o \ sde/sde_kms.o \ sde/sde_plane.o \ diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c index db3c57681675..08abe700844b 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder.c @@ -10,6 +10,7 @@ * GNU General Public License for more details. */ +#include "msm_drv.h" #include "sde_kms.h" #include "drm_crtc.h" #include "drm_crtc_helper.h" @@ -20,32 +21,11 @@ #include "sde_hw_mdp_ctl.h" #include "sde_mdp_formats.h" +#include "sde_encoder_phys.h" + #include "../dsi-staging/dsi_display.h" -#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) - -struct sde_encoder { - struct drm_encoder base; - spinlock_t intf_lock; - bool enabled; - uint32_t bus_scaling_client; - struct sde_hw_intf *hw_intf; - struct sde_hw_ctl *hw_ctl; - int drm_mode_enc; - - void (*vblank_callback)(void *); - void *vblank_callback_data; - - struct mdp_irq vblank_irq; -}; -#define to_sde_encoder(x) container_of(x, struct sde_encoder, base) - -static struct sde_kms *get_kms(struct drm_encoder *drm_enc) -{ - struct msm_drm_private *priv = drm_enc->dev->dev_private; - - return to_sde_kms(to_mdp_kms(priv->kms)); -} +#define to_sde_encoder_virt(x) container_of(x, struct sde_encoder_virt, base) #ifdef CONFIG_QCOM_BUS_SCALING #include @@ -62,6 +42,7 @@ static struct msm_bus_vectors mdp_bus_vectors[] = { MDP_BUS_VECTOR_ENTRY(0, 0), MDP_BUS_VECTOR_ENTRY(2000000000, 2000000000), }; + static struct msm_bus_paths mdp_bus_usecases[] = { { .num_paths = 1, .vectors = @@ -79,14 +60,14 @@ static struct msm_bus_scale_pdata mdp_bus_scale_table = { .name = "mdss_mdp", }; -static void bs_init(struct sde_encoder *sde_enc) +static void bs_init(struct sde_encoder_virt *sde_enc) { sde_enc->bus_scaling_client = msm_bus_scale_register_client(&mdp_bus_scale_table); DBG("bus scale client: %08x", sde_enc->bus_scaling_client); } -static void bs_fini(struct sde_encoder *sde_enc) +static void bs_fini(struct sde_encoder_virt *sde_enc) { if (sde_enc->bus_scaling_client) { msm_bus_scale_unregister_client(sde_enc->bus_scaling_client); @@ -94,7 +75,7 @@ static void bs_fini(struct sde_encoder *sde_enc) } } -static void bs_set(struct sde_encoder *sde_enc, int idx) +static void bs_set(struct sde_encoder_virt *sde_enc, int idx) { if (sde_enc->bus_scaling_client) { DBG("set bus scaling: %d", idx); @@ -104,242 +85,189 @@ static void bs_set(struct sde_encoder *sde_enc, int idx) } } #else -static void bs_init(struct sde_encoder *sde_enc) +static void bs_init(struct sde_encoder_virt *sde_enc) { } -static void bs_fini(struct sde_encoder *sde_enc) +static void bs_fini(struct sde_encoder_virt *sde_enc) { } -static void bs_set(struct sde_encoder *sde_enc, int idx) +static void bs_set(struct sde_encoder_virt *sde_enc, int idx) { } #endif -static bool sde_encoder_mode_fixup(struct drm_encoder *drm_enc, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - DBG(""); - return true; -} - -static void sde_encoder_mode_set(struct drm_encoder *drm_enc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - - struct sde_encoder *sde_enc = to_sde_encoder(drm_enc); - struct intf_timing_params p = {0}; - uint32_t hsync_polarity = 0, vsync_polarity = 0; - struct sde_mdp_format_params *sde_fmt_params = NULL; - u32 fmt_fourcc = DRM_FORMAT_RGB888, fmt_mod = 0; - unsigned long lock_flags; - struct sde_hw_intf_cfg intf_cfg = {0}; - - mode = adjusted_mode; - - DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", - mode->base.id, mode->name, mode->vrefresh, mode->clock, - mode->hdisplay, mode->hsync_start, mode->hsync_end, mode->htotal, - mode->vdisplay, mode->vsync_start, mode->vsync_end, mode->vtotal, - mode->type, mode->flags); - - /* DSI controller cannot handle active-low sync signals. */ - if (sde_enc->hw_intf->cap->type != INTF_DSI) { - if (mode->flags & DRM_MODE_FLAG_NHSYNC) - hsync_polarity = 1; - if (mode->flags & DRM_MODE_FLAG_NVSYNC) - vsync_polarity = 1; - } - - /* - * For edp only: - * DISPLAY_V_START = (VBP * HCYCLE) + HBP - * DISPLAY_V_END = (VBP + VACTIVE) * HCYCLE - 1 - HFP - */ - /* - * if (sde_enc->hw->cap->type == INTF_EDP) { - * display_v_start += mode->htotal - mode->hsync_start; - * display_v_end -= mode->hsync_start - mode->hdisplay; - * } - */ - - /* - * https://www.kernel.org/doc/htmldocs/drm/ch02s05.html - * Active Region Front Porch Sync Back Porch - * <---------------------><----------------><---------><--------------> - * <--- [hv]display -----> - * <----------- [hv]sync_start ------------> - * <------------------- [hv]sync_end -----------------> - * <------------------------------ [hv]total -------------------------> - */ - - sde_fmt_params = sde_mdp_get_format_params(fmt_fourcc, fmt_mod); - - p.width = mode->hdisplay; /* active width */ - p.height = mode->vdisplay; /* active height */ - p.xres = p.width; /* Display panel width */ - p.yres = p.height; /* Display panel height */ - p.h_back_porch = mode->htotal - mode->hsync_end; - p.h_front_porch = mode->hsync_start - mode->hdisplay; - p.v_back_porch = mode->vtotal - mode->vsync_end; - p.v_front_porch = mode->vsync_start - mode->vdisplay; - p.hsync_pulse_width = mode->hsync_end - mode->hsync_start; - p.vsync_pulse_width = mode->vsync_end - mode->vsync_start; - p.hsync_polarity = hsync_polarity; - p.vsync_polarity = vsync_polarity; - p.border_clr = 0; - p.underflow_clr = 0xff; - p.hsync_skew = mode->hskew; - - intf_cfg.intf = sde_enc->hw_intf->idx; - intf_cfg.wb = SDE_NONE; - - spin_lock_irqsave(&sde_enc->intf_lock, lock_flags); - sde_enc->hw_intf->ops.setup_timing_gen(sde_enc->hw_intf, &p, - sde_fmt_params); - sde_enc->hw_ctl->ops.setup_intf_cfg(sde_enc->hw_ctl, &intf_cfg); - spin_unlock_irqrestore(&sde_enc->intf_lock, lock_flags); -} - -static void sde_encoder_wait_for_vblank(struct sde_encoder *sde_enc) -{ - struct sde_kms *sde_kms = get_kms(&sde_enc->base); - struct mdp_kms *mdp_kms = &sde_kms->base; - - DBG(""); - mdp_irq_wait(mdp_kms, sde_enc->vblank_irq.irqmask); -} - -static void sde_encoder_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus) -{ - struct sde_encoder *sde_enc = container_of(irq, struct sde_encoder, - vblank_irq); - struct intf_status status = { 0 }; - unsigned long lock_flags; - - spin_lock_irqsave(&sde_enc->intf_lock, lock_flags); - if (sde_enc->vblank_callback) - sde_enc->vblank_callback(sde_enc->vblank_callback_data); - spin_unlock_irqrestore(&sde_enc->intf_lock, lock_flags); - - sde_enc->hw_intf->ops.get_status(sde_enc->hw_intf, &status); -} - -static void sde_encoder_disable(struct drm_encoder *drm_enc) -{ - struct sde_encoder *sde_enc = to_sde_encoder(drm_enc); - struct sde_kms *sde_kms = get_kms(drm_enc); - struct mdp_kms *mdp_kms = &(sde_kms->base); - unsigned long lock_flags; - - DBG(""); - - if (WARN_ON(!sde_enc->enabled)) - return; - - spin_lock_irqsave(&sde_enc->intf_lock, lock_flags); - sde_enc->hw_intf->ops.enable_timing(sde_enc->hw_intf, 0); - spin_unlock_irqrestore(&sde_enc->intf_lock, lock_flags); - - /* - * Wait for a vsync so we know the ENABLE=0 latched before - * the (connector) source of the vsync's gets disabled, - * otherwise we end up in a funny state if we re-enable - * before the disable latches, which results that some of - * the settings changes for the new modeset (like new - * scanout buffer) don't latch properly.. - */ - sde_encoder_wait_for_vblank(sde_enc); - - mdp_irq_unregister(mdp_kms, &sde_enc->vblank_irq); - bs_set(sde_enc, 0); - sde_enc->enabled = false; -} - -static void sde_encoder_enable(struct drm_encoder *drm_enc) -{ - struct sde_encoder *sde_enc = to_sde_encoder(drm_enc); - struct mdp_kms *mdp_kms = &(get_kms(drm_enc)->base); - unsigned long lock_flags; - - DBG(""); - - if (WARN_ON(sde_enc->enabled)) - return; - - bs_set(sde_enc, 1); - spin_lock_irqsave(&sde_enc->intf_lock, lock_flags); - sde_enc->hw_intf->ops.enable_timing(sde_enc->hw_intf, 1); - spin_unlock_irqrestore(&sde_enc->intf_lock, lock_flags); - sde_enc->enabled = true; - - mdp_irq_register(mdp_kms, &sde_enc->vblank_irq); - DBG("Registered IRQ for intf %d mask 0x%X", sde_enc->hw_intf->idx, - sde_enc->vblank_irq.irqmask); -} - void sde_encoder_get_hw_resources(struct drm_encoder *drm_enc, struct sde_encoder_hw_resources *hw_res) { - struct sde_encoder *sde_enc = to_sde_encoder(drm_enc); + struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc); + int i = 0; DBG(""); - if (WARN_ON(!hw_res)) + if (!hw_res) { + DRM_ERROR("Invalid pointer"); return; + } + /* Query resources used by phys encs, expected to be without overlap */ memset(hw_res, 0, sizeof(*hw_res)); - hw_res->intfs[sde_enc->hw_intf->idx] = true; + for (i = 0; i < sde_enc->num_phys_encs; i++) { + struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; + + if (phys) + phys->phys_ops.get_hw_resources(phys, hw_res); + } } static void sde_encoder_destroy(struct drm_encoder *drm_enc) { - struct sde_encoder *sde_enc = to_sde_encoder(drm_enc); + struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc); + int i = 0; DBG(""); + + for (i = 0; i < ARRAY_SIZE(sde_enc->phys_encs); i++) { + struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; + + if (phys) { + phys->phys_ops.destroy(phys); + --sde_enc->num_phys_encs; + sde_enc->phys_encs[i] = NULL; + } + } + + if (sde_enc->num_phys_encs) { + DRM_ERROR("Expected num_phys_encs to be 0 not %d\n", + sde_enc->num_phys_encs); + } + drm_encoder_cleanup(drm_enc); bs_fini(sde_enc); - kfree(sde_enc->hw_intf); kfree(sde_enc); } -static const struct drm_encoder_helper_funcs sde_encoder_helper_funcs = { - .mode_fixup = sde_encoder_mode_fixup, - .mode_set = sde_encoder_mode_set, - .disable = sde_encoder_disable, - .enable = sde_encoder_enable, -}; - -static const struct drm_encoder_funcs sde_encoder_funcs = {.destroy = - sde_encoder_destroy, -}; - -static int sde_encoder_setup_hw(struct sde_encoder *sde_enc, - struct sde_kms *sde_kms, - enum sde_intf intf_idx, - enum sde_ctl ctl_idx) +static bool sde_encoder_virt_mode_fixup(struct drm_encoder *drm_enc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { - int ret = 0; + struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc); + int i = 0; DBG(""); - sde_enc->hw_intf = sde_hw_intf_init(intf_idx, sde_kms->mmio, - sde_kms->catalog); - if (!sde_enc->hw_intf) - return -EINVAL; + for (i = 0; i < sde_enc->num_phys_encs; i++) { + struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; - sde_enc->hw_ctl = sde_hw_ctl_init(ctl_idx, sde_kms->mmio, - sde_kms->catalog); - if (!sde_enc->hw_ctl) - return -EINVAL; + if (phys) { + phys->phys_ops.mode_fixup(phys, mode, adjusted_mode); + if (memcmp(mode, adjusted_mode, sizeof(*mode)) != 0) { + DRM_ERROR("adjusted modes not supported\n"); + return false; + } + } + } - return ret; + return true; } -static int sde_encoder_virt_add_phys_vid_enc(struct sde_encoder *sde_enc, +static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc); + int i = 0; + + DBG(""); + + for (i = 0; i < sde_enc->num_phys_encs; i++) { + struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; + + if (phys) { + phys->phys_ops.mode_set(phys, mode, adjusted_mode); + if (memcmp(mode, adjusted_mode, sizeof(*mode)) != 0) + DRM_ERROR("adjusted modes not supported\n"); + } + } +} + +static void sde_encoder_virt_enable(struct drm_encoder *drm_enc) +{ + struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc); + int i = 0; + + DBG(""); + + bs_set(sde_enc, 1); + + for (i = 0; i < sde_enc->num_phys_encs; i++) { + struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; + + if (phys) + phys->phys_ops.enable(phys); + } +} + +static void sde_encoder_virt_disable(struct drm_encoder *drm_enc) +{ + struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc); + int i = 0; + + DBG(""); + + for (i = 0; i < sde_enc->num_phys_encs; i++) { + struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; + + if (phys && phys->phys_ops.disable) + phys->phys_ops.disable(phys); + } + + bs_set(sde_enc, 0); +} + +static const struct drm_encoder_helper_funcs sde_encoder_helper_funcs = { + .mode_fixup = sde_encoder_virt_mode_fixup, + .mode_set = sde_encoder_virt_mode_set, + .disable = sde_encoder_virt_disable, + .enable = sde_encoder_virt_enable, +}; + +static const struct drm_encoder_funcs sde_encoder_funcs = { + .destroy = sde_encoder_destroy, +}; + +static enum sde_intf sde_encoder_get_intf(struct sde_mdss_cfg *catalog, + enum sde_intf_type type, u32 instance) +{ + int i = 0; + + DBG(""); + + for (i = 0; i < catalog->intf_count; i++) { + if (catalog->intf[i].type == type + && catalog->intf[i].controller_id == instance) { + return catalog->intf[i].id; + } + } + + return INTF_MAX; +} + +static void sde_encoder_vblank_callback(struct drm_encoder *drm_enc) +{ + struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc); + unsigned long lock_flags; + + DBG(""); + + spin_lock_irqsave(&sde_enc->spin_lock, lock_flags); + if (sde_enc->kms_vblank_callback) + sde_enc->kms_vblank_callback(sde_enc->kms_vblank_callback_data); + spin_unlock_irqrestore(&sde_enc->spin_lock, lock_flags); +} + +static int sde_encoder_virt_add_phys_vid_enc(struct sde_encoder_virt *sde_enc, struct sde_kms *sde_kms, enum sde_intf intf_idx, enum sde_ctl ctl_idx) @@ -348,15 +276,30 @@ static int sde_encoder_virt_add_phys_vid_enc(struct sde_encoder *sde_enc, DBG(""); - ret = sde_encoder_setup_hw(sde_enc, sde_kms, intf_idx, ctl_idx); - if (!ret) { - sde_enc->vblank_irq.irq = sde_encoder_vblank_irq; - sde_enc->vblank_irq.irqmask = 0x8000000; + if (sde_enc->num_phys_encs >= ARRAY_SIZE(sde_enc->phys_encs)) { + DRM_ERROR("Too many video encoders %d, unable to add\n", + sde_enc->num_phys_encs); + ret = -EINVAL; + } else { + struct sde_encoder_virt_ops parent_ops = { + sde_encoder_vblank_callback + }; + struct sde_encoder_phys *enc = + sde_encoder_phys_vid_init(sde_kms, intf_idx, ctl_idx, + &sde_enc->base, parent_ops); + if (IS_ERR(enc)) + ret = PTR_ERR(enc); + + if (!ret) { + sde_enc->phys_encs[sde_enc->num_phys_encs] = enc; + ++sde_enc->num_phys_encs; + } } + return ret; } -static int sde_encoder_setup_hdmi(struct sde_encoder *sde_enc, +static int sde_encoder_setup_hdmi(struct sde_encoder_virt *sde_enc, struct sde_kms *sde_kms, int *hdmi_info) { int ret = 0; @@ -364,9 +307,7 @@ static int sde_encoder_setup_hdmi(struct sde_encoder *sde_enc, DBG(""); - sde_enc->drm_mode_enc = DRM_MODE_ENCODER_TMDS; - - intf_idx = INTF_3; + intf_idx = sde_encoder_get_intf(sde_kms->catalog, INTF_HDMI, 0); if (intf_idx == INTF_MAX) ret = -EINVAL; @@ -379,7 +320,7 @@ static int sde_encoder_setup_hdmi(struct sde_encoder *sde_enc, return ret; } -static int sde_encoder_setup_dsi(struct sde_encoder *sde_enc, +static int sde_encoder_setup_dsi(struct sde_encoder_virt *sde_enc, struct sde_kms *sde_kms, struct dsi_display_info *dsi_info) { @@ -388,30 +329,27 @@ static int sde_encoder_setup_dsi(struct sde_encoder *sde_enc, DBG(""); - sde_enc->drm_mode_enc = DRM_MODE_ENCODER_DSI; + WARN_ON(dsi_info->num_of_h_tiles < 1); - if (WARN_ON(dsi_info->num_of_h_tiles > 1)) { - DBG("Dual DSI mode not yet supported"); - ret = -EINVAL; - } - - WARN_ON(dsi_info->num_of_h_tiles != 1); - dsi_info->num_of_h_tiles = 1; + if (dsi_info->num_of_h_tiles == 0) + dsi_info->num_of_h_tiles = 1; DBG("dsi_info->num_of_h_tiles %d h_tiled %d dsi_info->h_tile_ids %d ", - dsi_info->num_of_h_tiles, dsi_info->h_tiled, - dsi_info->h_tile_ids[0]); + dsi_info->num_of_h_tiles, dsi_info->h_tiled, + dsi_info->h_tile_ids[0]); - for (i = 0; i < !ret && dsi_info->num_of_h_tiles; i++) { - enum sde_intf intf_idx = INTF_1; + for (i = 0; i < dsi_info->num_of_h_tiles && !ret; i++) { + enum sde_intf intf_idx = INTF_MAX; enum sde_ctl ctl_idx = CTL_0; + intf_idx = sde_encoder_get_intf(sde_kms->catalog, + INTF_DSI, dsi_info->h_tile_ids[i]); if (intf_idx == INTF_MAX) { DBG("Error: could not get the interface id"); ret = -EINVAL; } - /* Get DSI modes, create both VID & CMD Phys Encoders */ + /* Create both VID and CMD Phys Encoders here */ if (!ret) ret = sde_encoder_virt_add_phys_vid_enc(sde_enc, sde_kms, @@ -429,13 +367,13 @@ struct display_probe_info { }; static struct drm_encoder *sde_encoder_virt_init(struct drm_device *dev, - struct display_probe_info - *display) + struct display_probe_info *display) { struct msm_drm_private *priv = dev->dev_private; struct sde_kms *sde_kms = to_sde_kms(to_mdp_kms(priv->kms)); struct drm_encoder *drm_enc = NULL; - struct sde_encoder *sde_enc = NULL; + struct sde_encoder_virt *sde_enc = NULL; + int drm_encoder_mode = DRM_MODE_ENCODER_NONE; int ret = 0; DBG(""); @@ -447,31 +385,35 @@ static struct drm_encoder *sde_encoder_virt_init(struct drm_device *dev, } if (display->type == INTF_DSI) { + drm_encoder_mode = DRM_MODE_ENCODER_DSI; ret = sde_encoder_setup_dsi(sde_enc, sde_kms, &display->dsi_info); + } else if (display->type == INTF_HDMI) { + drm_encoder_mode = DRM_MODE_ENCODER_TMDS; ret = sde_encoder_setup_hdmi(sde_enc, sde_kms, &display->hdmi_info); } else { - DBG("No valid displays found"); + DRM_ERROR("No valid displays found\n"); ret = -EINVAL; } + if (ret) goto fail; - spin_lock_init(&sde_enc->intf_lock); + spin_lock_init(&sde_enc->spin_lock); drm_enc = &sde_enc->base; - drm_encoder_init(dev, drm_enc, &sde_encoder_funcs, - sde_enc->drm_mode_enc); + drm_encoder_init(dev, drm_enc, &sde_encoder_funcs, drm_encoder_mode); drm_encoder_helper_add(drm_enc, &sde_encoder_helper_funcs); bs_init(sde_enc); - DBG("Created sde_encoder for intf %d", sde_enc->hw_intf->idx); + DBG("Created encoder"); return drm_enc; fail: + DRM_ERROR("Failed to create encoder\n"); if (drm_enc) sde_encoder_destroy(drm_enc); @@ -492,10 +434,12 @@ static int sde_encoder_probe_hdmi(struct drm_device *dev) enc = sde_encoder_virt_init(dev, &probe_info); if (IS_ERR(enc)) ret = PTR_ERR(enc); - else { - /* Register new encoder with the upper layer */ + + if (!ret) { + /* Register new encoder with the upper layer */ priv->encoders[priv->num_encoders++] = enc; } + return ret; } @@ -510,50 +454,56 @@ static int sde_encoder_probe_dsi(struct drm_device *dev) num_displays = dsi_display_get_num_of_displays(); DBG("num_displays %d", num_displays); + + if (priv->num_encoders + num_displays > ARRAY_SIZE(priv->encoders)) { + DBG("Too many displays found in probe"); + return -EINVAL; + } + for (i = 0; i < num_displays; i++) { + struct dsi_display *dsi = dsi_display_get_display_by_index(i); if (dsi_display_is_active(dsi)) { + struct drm_encoder *enc = NULL; struct display_probe_info probe_info = { 0 }; - DBG("display %d/%d is active", i, num_displays); probe_info.type = INTF_DSI; + DBG("display %d is active", i); + ret = dsi_display_get_info(dsi, &probe_info.dsi_info); - if (WARN_ON(ret)) - DBG("Failed to retrieve dsi panel info"); - else { - struct drm_encoder *enc = - sde_encoder_virt_init(dev, - &probe_info); - if (IS_ERR(enc)) - return PTR_ERR(enc); + if (ret) + return ret; - ret = dsi_display_drm_init(dsi, enc); - if (ret) - return ret; + enc = sde_encoder_virt_init(dev, &probe_info); + if (IS_ERR(enc)) + return PTR_ERR(enc); - /* Register new encoder with the upper layer */ - priv->encoders[priv->num_encoders++] = enc; - } - } else - DBG("display %d/%d is not active", i, num_displays); + ret = dsi_display_drm_init(dsi, enc); + if (ret) + return ret; + + /* Register new encoder with the upper layer */ + priv->encoders[priv->num_encoders++] = enc; + } } return ret; } void sde_encoder_register_vblank_callback(struct drm_encoder *drm_enc, - void (*cb)(void *), void *data) { - struct sde_encoder *sde_enc = to_sde_encoder(drm_enc); + void (*cb)(void *), void *data) +{ + struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc); unsigned long lock_flags; DBG(""); - spin_lock_irqsave(&sde_enc->intf_lock, lock_flags); - sde_enc->vblank_callback = cb; - sde_enc->vblank_callback_data = data; - spin_unlock_irqrestore(&sde_enc->intf_lock, lock_flags); + spin_lock_irqsave(&sde_enc->spin_lock, lock_flags); + sde_enc->kms_vblank_callback = cb; + sde_enc->kms_vblank_callback_data = data; + spin_unlock_irqrestore(&sde_enc->spin_lock, lock_flags); } /* encoders init, @@ -566,14 +516,14 @@ void sde_encoders_init(struct drm_device *dev) DBG(""); - /* Start num_encoders at 0, probe functions will increment */ + /* Start num_encoders at 0, probe functions will increment */ priv->num_encoders = 0; ret = sde_encoder_probe_dsi(dev); if (ret) - DBG("Error probing DSI, %d", ret); + DRM_ERROR("Error probing DSI, %d\n", ret); else { ret = sde_encoder_probe_hdmi(dev); if (ret) - DBG("Error probing HDMI, %d", ret); + DRM_ERROR("Error probing HDMI, %d\n", ret); } } diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h new file mode 100644 index 000000000000..427a6d94322e --- /dev/null +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2015 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __SDE_ENCODER_PHYS_H__ +#define __SDE_ENCODER_PHYS_H__ + +#include "sde_kms.h" +#include "sde_hw_intf.h" +#include "sde_hw_mdp_ctl.h" + +#define MAX_PHYS_ENCODERS_PER_VIRTUAL 4 + +struct sde_encoder_phys; + +struct sde_encoder_virt_ops { + void (*handle_vblank_virt)(struct drm_encoder *); +}; + +struct sde_encoder_phys_ops { + void (*mode_set)(struct sde_encoder_phys *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + bool (*mode_fixup)(struct sde_encoder_phys *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + void (*enable)(struct sde_encoder_phys *encoder); + void (*disable)(struct sde_encoder_phys *encoder); + void (*destroy)(struct sde_encoder_phys *encoder); + void (*get_hw_resources)(struct sde_encoder_phys *encoder, + struct sde_encoder_hw_resources *hw_res); +}; + +struct sde_encoder_phys { + struct drm_encoder *parent; + struct sde_encoder_virt_ops parent_ops; + struct sde_encoder_phys_ops phys_ops; + struct sde_hw_intf *hw_intf; + struct sde_hw_ctl *hw_ctl; + struct mdp_kms *mdp_kms; + struct drm_display_mode cached_mode; + bool enabled; + spinlock_t spin_lock; +}; + +struct sde_encoder_phys_vid { + struct sde_encoder_phys base; + struct mdp_irq vblank_irq; +}; + +struct sde_encoder_virt { + struct drm_encoder base; + spinlock_t spin_lock; + uint32_t bus_scaling_client; + + int num_phys_encs; + struct sde_encoder_phys *phys_encs[MAX_PHYS_ENCODERS_PER_VIRTUAL]; + + void (*kms_vblank_callback)(void *); + void *kms_vblank_callback_data; +}; + +struct sde_encoder_phys *sde_encoder_phys_vid_init(struct sde_kms *sde_kms, + enum sde_intf intf_idx, + enum sde_ctl ctl_idx, + struct drm_encoder *parent, + struct sde_encoder_virt_ops + parent_ops); + +#endif /* __sde_encoder_phys_H__ */ diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c new file mode 100644 index 000000000000..693e1f33e7d8 --- /dev/null +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2015 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_drv.h" +#include "sde_kms.h" +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + +#include "sde_hwio.h" +#include "sde_hw_catalog.h" +#include "sde_hw_intf.h" +#include "sde_mdp_formats.h" + +#include "sde_encoder_phys.h" diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c new file mode 100644 index 000000000000..55fad67cbf12 --- /dev/null +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2015 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_drv.h" +#include "sde_kms.h" +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + +#include "sde_encoder_phys.h" +#include "sde_mdp_formats.h" + + +#define to_sde_encoder_phys_vid(x) \ + container_of(x, struct sde_encoder_phys_vid, base) + +static bool sde_encoder_phys_vid_mode_fixup(struct sde_encoder_phys *drm_enc, + const struct drm_display_mode *mode, + struct drm_display_mode + *adjusted_mode) +{ + DBG(""); + return true; +} + +static void sde_encoder_phys_vid_mode_set(struct sde_encoder_phys *phys_enc, + struct drm_display_mode *mode, + struct drm_display_mode + *adjusted_mode) +{ + mode = adjusted_mode; + phys_enc->cached_mode = *adjusted_mode; + + DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", + mode->base.id, mode->name, mode->vrefresh, mode->clock, + mode->hdisplay, mode->hsync_start, mode->hsync_end, mode->htotal, + mode->vdisplay, mode->vsync_start, mode->vsync_end, mode->vtotal, + mode->type, mode->flags); +} + +static void sde_encoder_phys_vid_setup_timing_engine(struct sde_encoder_phys + *phys_enc) +{ + struct drm_display_mode *mode = &phys_enc->cached_mode; + struct intf_timing_params p = { 0 }; + uint32_t hsync_polarity = 0; + uint32_t vsync_polarity = 0; + struct sde_mdp_format_params *sde_fmt_params = NULL; + u32 fmt_fourcc = DRM_FORMAT_RGB888; + u32 fmt_mod = 0; + unsigned long lock_flags; + struct sde_hw_intf_cfg intf_cfg = {0}; + + DBG("enable mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", + mode->base.id, mode->name, mode->vrefresh, mode->clock, + mode->hdisplay, mode->hsync_start, mode->hsync_end, mode->htotal, + mode->vdisplay, mode->vsync_start, mode->vsync_end, mode->vtotal, + mode->type, mode->flags); + + /* DSI controller cannot handle active-low sync signals. */ + if (phys_enc->hw_intf->cap->type != INTF_DSI) { + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + hsync_polarity = 1; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + vsync_polarity = 1; + } + + /* + * For edp only: + * DISPLAY_V_START = (VBP * HCYCLE) + HBP + * DISPLAY_V_END = (VBP + VACTIVE) * HCYCLE - 1 - HFP + */ + /* + * if (vid_enc->hw->cap->type == INTF_EDP) { + * display_v_start += mode->htotal - mode->hsync_start; + * display_v_end -= mode->hsync_start - mode->hdisplay; + * } + */ + + /* + * https://www.kernel.org/doc/htmldocs/drm/ch02s05.html + * Active Region Front Porch Sync Back Porch + * <---------------------><----------------><---------><--------------> + * <--- [hv]display -----> + * <----------- [hv]sync_start ------------> + * <------------------- [hv]sync_end -----------------> + * <------------------------------ [hv]total -------------------------> + */ + + sde_fmt_params = sde_mdp_get_format_params(fmt_fourcc, fmt_mod); + + p.width = mode->hdisplay; /* active width */ + p.height = mode->vdisplay; /* active height */ + p.xres = p.width; /* Display panel width */ + p.yres = p.height; /* Display panel height */ + p.h_back_porch = mode->htotal - mode->hsync_end; + p.h_front_porch = mode->hsync_start - mode->hdisplay; + p.v_back_porch = mode->vtotal - mode->vsync_end; + p.v_front_porch = mode->vsync_start - mode->vdisplay; + p.hsync_pulse_width = mode->hsync_end - mode->hsync_start; + p.vsync_pulse_width = mode->vsync_end - mode->vsync_start; + p.hsync_polarity = hsync_polarity; + p.vsync_polarity = vsync_polarity; + p.border_clr = 0; + p.underflow_clr = 0xff; + p.hsync_skew = mode->hskew; + + intf_cfg.intf = phys_enc->hw_intf->idx; + intf_cfg.wb = SDE_NONE; + + spin_lock_irqsave(&phys_enc->spin_lock, lock_flags); + phys_enc->hw_intf->ops.setup_timing_gen(phys_enc->hw_intf, &p, + sde_fmt_params); + phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, &intf_cfg); + spin_unlock_irqrestore(&phys_enc->spin_lock, lock_flags); +} + +static void sde_encoder_phys_vid_wait_for_vblank(struct sde_encoder_phys_vid + *vid_enc) +{ + DBG(""); + mdp_irq_wait(vid_enc->base.mdp_kms, vid_enc->vblank_irq.irqmask); +} + +static void sde_encoder_phys_vid_vblank_irq(struct mdp_irq *irq, + uint32_t irqstatus) +{ + struct sde_encoder_phys_vid *vid_enc = + container_of(irq, struct sde_encoder_phys_vid, + vblank_irq); + struct sde_encoder_phys *phys_enc = &vid_enc->base; + struct intf_status status = { 0 }; + + phys_enc->hw_intf->ops.get_status(phys_enc->hw_intf, &status); + phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent); +} + +static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc) +{ + struct sde_encoder_phys_vid *vid_enc = + to_sde_encoder_phys_vid(phys_enc); + unsigned long lock_flags; + + DBG(""); + + if (WARN_ON(phys_enc->enabled)) + return; + + sde_encoder_phys_vid_setup_timing_engine(phys_enc); + + spin_lock_irqsave(&phys_enc->spin_lock, lock_flags); + phys_enc->hw_intf->ops.enable_timing(phys_enc->hw_intf, 1); + spin_unlock_irqrestore(&phys_enc->spin_lock, lock_flags); + + phys_enc->enabled = true; + + mdp_irq_register(phys_enc->mdp_kms, &vid_enc->vblank_irq); + DBG("Registered IRQ for intf %d mask 0x%X", phys_enc->hw_intf->idx, + vid_enc->vblank_irq.irqmask); +} + +static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc) +{ + struct sde_encoder_phys_vid *vid_enc = + to_sde_encoder_phys_vid(phys_enc); + unsigned long lock_flags; + + DBG(""); + + if (WARN_ON(!phys_enc->enabled)) + return; + + spin_lock_irqsave(&phys_enc->spin_lock, lock_flags); + phys_enc->hw_intf->ops.enable_timing(phys_enc->hw_intf, 0); + spin_unlock_irqrestore(&phys_enc->spin_lock, lock_flags); + + /* + * Wait for a vsync so we know the ENABLE=0 latched before + * the (connector) source of the vsync's gets disabled, + * otherwise we end up in a funny state if we re-enable + * before the disable latches, which results that some of + * the settings changes for the new modeset (like new + * scanout buffer) don't latch properly.. + */ + sde_encoder_phys_vid_wait_for_vblank(vid_enc); + mdp_irq_unregister(phys_enc->mdp_kms, &vid_enc->vblank_irq); + phys_enc->enabled = false; +} + +static void sde_encoder_phys_vid_destroy(struct sde_encoder_phys *phys_enc) +{ + struct sde_encoder_phys_vid *vid_enc = + to_sde_encoder_phys_vid(phys_enc); + DBG(""); + kfree(phys_enc->hw_intf); + kfree(vid_enc); +} + +static void sde_encoder_phys_vid_get_hw_resources(struct sde_encoder_phys + *phys_enc, struct + sde_encoder_hw_resources + *hw_res) +{ + DBG(""); + hw_res->intfs[phys_enc->hw_intf->idx] = true; +} + +static void sde_encoder_phys_vid_init_cbs(struct sde_encoder_phys_ops *ops) +{ + ops->mode_set = sde_encoder_phys_vid_mode_set; + ops->mode_fixup = sde_encoder_phys_vid_mode_fixup; + ops->enable = sde_encoder_phys_vid_enable; + ops->disable = sde_encoder_phys_vid_disable; + ops->destroy = sde_encoder_phys_vid_destroy; + ops->get_hw_resources = sde_encoder_phys_vid_get_hw_resources; +} + +struct sde_encoder_phys *sde_encoder_phys_vid_init(struct sde_kms *sde_kms, + enum sde_intf intf_idx, + enum sde_ctl ctl_idx, + struct drm_encoder *parent, + struct sde_encoder_virt_ops + parent_ops) +{ + struct sde_encoder_phys *phys_enc = NULL; + struct sde_encoder_phys_vid *vid_enc = NULL; + int ret = 0; + + DBG(""); + + vid_enc = kzalloc(sizeof(*vid_enc), GFP_KERNEL); + if (!vid_enc) { + ret = -ENOMEM; + goto fail; + } + phys_enc = &vid_enc->base; + + phys_enc->hw_intf = + sde_hw_intf_init(intf_idx, sde_kms->mmio, sde_kms->catalog); + if (!phys_enc->hw_intf) { + ret = -ENOMEM; + goto fail; + } + + phys_enc->hw_ctl = sde_hw_ctl_init(ctl_idx, sde_kms->mmio, + sde_kms->catalog); + if (!phys_enc->hw_ctl) { + ret = -ENOMEM; + goto fail; + } + + sde_encoder_phys_vid_init_cbs(&phys_enc->phys_ops); + phys_enc->parent = parent; + phys_enc->parent_ops = parent_ops; + phys_enc->mdp_kms = &sde_kms->base; + vid_enc->vblank_irq.irq = sde_encoder_phys_vid_vblank_irq; + vid_enc->vblank_irq.irqmask = 0x8000000; + spin_lock_init(&phys_enc->spin_lock); + + DBG("Created sde_encoder_phys_vid for intf %d", phys_enc->hw_intf->idx); + + return phys_enc; + +fail: + DRM_ERROR("Failed to create encoder\n"); + if (vid_enc) + sde_encoder_phys_vid_destroy(phys_enc); + + return ERR_PTR(ret); +}