drm/msm/sde: sde encoder virtualization
Split SDE encoder into virtual and physical encoders. Virtual encoders are containers, one per logical display that contain one or more physical encoders. Physical encoders manage the INTF hardware. Change-Id: I6342511c59568c76278a519b84f93338157e59fa Signed-off-by: Lloyd Atkinson <latkinso@codeaurora.org> Signed-off-by: Krishna Srinivas Kundurthi <kskund@codeaurora.org>
This commit is contained in:
parent
1cab338540
commit
a142ec80ca
5 changed files with 620 additions and 283 deletions
|
@ -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 \
|
||||
|
|
|
@ -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 <linux/msm-bus.h>
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
80
drivers/gpu/drm/msm/sde/sde_encoder_phys.h
Normal file
80
drivers/gpu/drm/msm/sde/sde_encoder_phys.h
Normal file
|
@ -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__ */
|
25
drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
Normal file
25
drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
Normal file
|
@ -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"
|
280
drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
Normal file
280
drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
Normal file
|
@ -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);
|
||||
}
|
Loading…
Add table
Reference in a new issue