struct hdmi_win_data {
dma_addr_t dma_addr;
- void __iomem *vaddr;
dma_addr_t chroma_dma_addr;
- void __iomem *chroma_vaddr;
uint32_t pixel_format;
unsigned int bpp;
unsigned int crtc_x;
struct hdmi_win_data win_data[MIXER_WIN_NR];
unsigned long event_flags;
int previous_dxy;
- bool is_800x600_initialized;
};
+struct mixer_scan_range {
+ int min_res[2], max_res[2];
+ enum exynos_mixer_mode_type mode_type;
+};
+
+struct mixer_scan_adjustment {
+ int res[2], new_res[2];
+};
+
+
/* event flags used */
enum mixer_status_flags {
MXR_EVENT_VSYNC = 1,
70, 59, 48, 37, 27, 19, 11, 5,
};
+struct mixer_scan_range scan_ranges[] = {
+ {
+ .min_res = { 464, 0 },
+ .max_res = { 720, 480 },
+ .mode_type = EXYNOS_MIXER_MODE_SD_NTSC,
+ },
+ {
+ .min_res = { 464, 481 },
+ .max_res = { 720, 576 },
+ .mode_type = EXYNOS_MIXER_MODE_SD_PAL,
+ },
+ {
+ .min_res = { 1024, 0 },
+ .max_res = { 1280, 720 },
+ .mode_type = EXYNOS_MIXER_MODE_HD_720,
+ },
+ {
+ .min_res = { 1664, 0 },
+ .max_res = { 1920, 1080 },
+ .mode_type = EXYNOS_MIXER_MODE_HD_1080,
+ },
+ {
+ .min_res = { 1440, 900 },
+ .max_res = { 1440, 900 },
+ .mode_type = EXYNOS_MIXER_MODE_HD_1080,
+ },
+};
+
+struct mixer_scan_adjustment scan_adjustments[] = {
+ {
+ .res = { 1024, 768 },
+ .new_res = { 1024, 720 },
+ },
+ {
+ .res = { 1280, 800 },
+ .new_res = { 1280, 720 },
+ },
+};
+
static void mixer_win_reset(struct mixer_context *mctx);
static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id)
enum exynos_mixer_mode_type exynos_mixer_get_mode_type(int width, int height)
{
- if (width >= 464 && width <= 720 && height <= 480)
- return EXYNOS_MIXER_MODE_SD_NTSC;
- else if (width >= 464 && width <= 720 && height <= 576)
- return EXYNOS_MIXER_MODE_SD_PAL;
- else if (width >= 1024 && width <= 1280 && height <= 720)
- return EXYNOS_MIXER_MODE_HD_720;
- else if ((width == 1440 && height == 900) ||
- (width == 800 && height == 600) ||
- (width >= 1664 && width <= 1920 && height <= 1080))
- return EXYNOS_MIXER_MODE_HD_1080;
- else
- return EXYNOS_MIXER_MODE_INVALID;
+ int i;
+
+ /*
+ * If the mode matches an adjustment, adjust it before finding the
+ * mode type
+ */
+ for (i = 0; i < ARRAY_SIZE(scan_adjustments); i++) {
+ struct mixer_scan_adjustment *adj = &scan_adjustments[i];
+
+ if (width == adj->res[0] && height == adj->res[1]) {
+ width = adj->new_res[0];
+ height = adj->new_res[1];
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(scan_ranges); i++) {
+ struct mixer_scan_range *range = &scan_ranges[i];
+
+ if (width >= range->min_res[0] && width <= range->max_res[0]
+ && height >= range->min_res[1] && height <= range->max_res[1])
+ return range->mode_type;
+ }
+ return EXYNOS_MIXER_MODE_INVALID;
+}
+
+static void mixer_adjust_modes(void *ctx, struct drm_connector *connector)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(scan_adjustments); i++) {
+ struct mixer_scan_adjustment *adj = &scan_adjustments[i];
+ struct drm_display_mode *t, *mode;
+ bool native_support = false;
+
+ /*
+ * Make sure the mode resulting from the adjustment is not
+ * already natively supported. This might cause us to do
+ * something stupid like choose a chopped 1280x800 resolution
+ * over native 720p.
+ */
+ list_for_each_entry_safe(mode, t, &connector->modes, head) {
+ if (adj->new_res[0] == mode->hdisplay &&
+ adj->new_res[1] == mode->vdisplay) {
+ native_support = true;
+ break;
+ }
+ }
+ if (native_support)
+ continue;
+
+ list_for_each_entry_safe(mode, t, &connector->modes, head) {
+ if (adj->res[0] == mode->hdisplay &&
+ adj->res[1] == mode->vdisplay) {
+ mode->hdisplay = adj->new_res[0];
+ mode->vdisplay = adj->new_res[1];
+ break;
+ }
+ }
+ }
}
static void mixer_regs_dump(struct mixer_context *mctx)
mixer_reg_write(res, MXR_GRAPHIC0_DXY, MXR_GRP_DXY_DX(offset));
}
-static void mixer_toggle_3d_path(struct mixer_context *mctx)
-{
- struct mixer_resources *res = &mctx->mixer_res;
-
- mixer_reg_write(res, MXR_TVOUT_CFG,
- mctx->is_800x600_initialized ? 0x13 : 0x17);
-
- mctx->is_800x600_initialized = true;
-}
-
static void mixer_cfg_rgb_fmt(struct mixer_context *mctx, unsigned int height)
{
struct mixer_resources *res = &mctx->mixer_res;
mixer_cfg_scan(mctx, mode_width, mode_height);
+ /* Workaround 4 implementation for 1440x900 resolution support */
if (res->is_soc_exynos5) {
- /* Workaround 4 implementation for 1440x900 resolution support */
if (mode_width == 1440 && mode_height == 900)
mixer_set_layer_offset(mctx, 224);
-
- /* Workaround 3 implementation for 800x600 resolution support */
- if (mode_width == 800 && mode_height == 600) {
- mixer_set_layer_offset(mctx, 32);
- mixer_toggle_3d_path(mctx);
- } else
- mctx->is_800x600_initialized = false;
}
mixer_cfg_rgb_fmt(mctx, mode_height);
struct mixer_context *mctx = ctx;
struct mixer_resources *res = &mctx->mixer_res;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+ DRM_DEBUG_KMS("pipe: %d\n", pipe);
/*
* TODO (seanpaul): Right now, this is an expected code path since we
if (pipe < 0)
return -EINVAL;
+ if (!mctx->is_mixer_powered_on)
+ return -EPERM;
+
mctx->pipe = pipe;
/* enable vsync interrupt */
struct mixer_context *mctx = ctx;
struct mixer_resources *res = &mctx->mixer_res;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+ DRM_DEBUG_KMS("pipe: %d\n", mctx->pipe);
+
+ if (!mctx->is_mixer_powered_on)
+ return;
/* disable vsync interrupt */
mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
struct hdmi_win_data *win_data;
int win;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
if (!overlay) {
DRM_ERROR("overlay is NULL\n");
return;
win_data = &mctx->win_data[win];
win_data->dma_addr = overlay->dma_addr[0];
- win_data->vaddr = overlay->vaddr[0];
win_data->chroma_dma_addr = overlay->dma_addr[1];
- win_data->chroma_vaddr = overlay->vaddr[1];
win_data->pixel_format = overlay->pixel_format;
win_data->bpp = overlay->bpp;
win_data->scan_flags = overlay->scan_flag;
}
-static void mixer_win_page_flip(void *ctx, struct exynos_drm_overlay *overlay)
-{
- struct mixer_context *mixer_ctx = ctx;
- struct hdmi_win_data *win_data;
- int win = overlay->zpos;
-
- if (win == DEFAULT_ZPOS)
- win = MIXER_DEFAULT_WIN;
-
- if (win < 0 || win > MIXER_WIN_NR) {
- DRM_ERROR("overlay plane[%d] is wrong\n", win);
- return;
- }
-
- win_data = &mixer_ctx->win_data[win];
-
- win_data->dma_addr = overlay->dma_addr[0];
- win_data->vaddr = overlay->vaddr[0];
- win_data->chroma_dma_addr = overlay->dma_addr[1];
- win_data->chroma_vaddr = overlay->vaddr[1];
-}
-
static void mixer_win_commit(void *ctx, int zpos)
{
struct mixer_context *mctx = ctx;
struct mixer_resources *res = &mctx->mixer_res;
int win = zpos;
- DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win);
+ DRM_DEBUG_KMS("win: %d\n", win);
+
if (win == DEFAULT_ZPOS)
win = MIXER_DEFAULT_WIN;
}
if (!mctx->is_mixer_powered_on) {
- DRM_DEBUG_KMS("[%d] %s not powered on\n", __LINE__, __func__);
+ DRM_DEBUG_KMS("not powered on\n");
return;
}
struct mixer_context *mctx = ctx;
int i;
+ DRM_DEBUG_KMS("\n");
+
for (i = 0; i < MIXER_WIN_NR; i++) {
if (!mctx->enabled[i])
continue;
unsigned long flags;
int win = zpos;
- DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win);
+ DRM_DEBUG_KMS("win: %d\n", win);
if (win == DEFAULT_ZPOS)
win = MIXER_DEFAULT_WIN;
return;
}
+ if (!mctx->is_mixer_powered_on)
+ return;
+
mixer_wait_for_vsync(mctx);
spin_lock_irqsave(&res->reg_slock, flags);
mixer_vsync_set_update(mctx, true);
spin_unlock_irqrestore(&res->reg_slock, flags);
-
- if (win == MIXER_DEFAULT_WIN) {
- mixer_win_reset(mctx);
- mixer_enable_vblank(mctx, mctx->pipe);
- }
}
/* for pageflip event */
struct mixer_context *mctx = arg;
struct mixer_resources *res = &mctx->mixer_res;
u32 val, base, shadow;
+ bool flip_complete = false;
int i;
spin_lock(&res->reg_slock);
+ WARN_ON(!mctx->is_mixer_powered_on);
+
/* read interrupt status for handling and clearing flags for VSYNC */
val = mixer_reg_read(res, MXR_INT_STATUS);
for (i = 0; i < MIXER_WIN_NR; i++)
mctx->win_data[i].updated = false;
- exynos_drm_crtc_finish_pageflip(mctx->drm_dev, mctx->pipe);
+ flip_complete = true;
if (mctx->event_flags & MXR_EVENT_VSYNC) {
DRM_DEBUG_KMS("mctx->event_flags & MXR_EVENT_VSYNC");
spin_unlock(&res->reg_slock);
+ if (flip_complete)
+ exynos_drm_crtc_finish_pageflip(mctx->drm_dev, mctx->pipe);
+
return IRQ_HANDLED;
}
unsigned long flags;
u32 val; /* value stored to register */
+ DRM_DEBUG_KMS("\n");
+
spin_lock_irqsave(&res->reg_slock, flags);
mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
mixer_vsync_set_update(mctx, false);
{
struct mixer_resources *res = &mctx->mixer_res;
+ DRM_DEBUG_KMS("is_mixer_powered_on: %d\n", mctx->is_mixer_powered_on);
+
if (mctx->is_mixer_powered_on)
return;
clk_enable(res->sclk_mixer);
}
+ mctx->is_mixer_powered_on = true;
+
mixer_win_reset(mctx);
mixer_enable_vblank(mctx, mctx->pipe);
-
- mctx->is_mixer_powered_on = true;
+ mixer_apply(mctx);
}
static void mixer_resource_poweroff(struct mixer_context *mctx)
{
struct mixer_resources *res = &mctx->mixer_res;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+ DRM_DEBUG_KMS("is_mixer_powered_on: %d\n", mctx->is_mixer_powered_on);
+
if (!mctx->is_mixer_powered_on)
return;
clk_disable(res->vp);
clk_disable(res->sclk_mixer);
}
+ mixer_win_reset(mctx);
mctx->is_mixer_powered_on = false;
}
-#ifdef CONFIG_PM_SLEEP
-static int mixer_resume(struct device *dev)
-{
- struct mixer_context *mctx = get_mixer_context(dev);
-
- DRM_DEBUG_KMS("[mixer] sleep resume - start\n");
-
- if (!pm_runtime_suspended(dev)) {
- DRM_DEBUG_KMS("[mixer] sleep resume - end\n");
- mixer_resource_poweron(mctx);
- mixer_win_commit(mctx, 0);
- }
- DRM_DEBUG_KMS("[mixer] sleep resume - not done\n");
-
- return 0;
-}
-static int mixer_suspend(struct device *dev)
-{
- struct mixer_context *mctx = get_mixer_context(dev);
-
- DRM_DEBUG_KMS("[mixer] suspend - start\n");
- if (pm_runtime_suspended(dev)) {
- DRM_DEBUG_KMS("[mixer] suspend - already suspended\n");
- return 0;
- }
-
- mixer_resource_poweroff(mctx);
- DRM_DEBUG_KMS("[mixer] suspend - end\n");
- return 0;
-}
-#endif
-#ifdef CONFIG_PM_RUNTIME
-static int mixer_runtime_resume(struct device *dev)
-{
- struct mixer_context *mctx = get_mixer_context(dev);
-
- DRM_DEBUG_KMS("[mixer] runtime resume - start\n");
-
- mixer_resource_poweron(mctx);
- DRM_DEBUG_KMS("[mixer] runtime resume - end\n");
-
- return 0;
-}
-
-static int mixer_runtime_suspend(struct device *dev)
-{
- struct mixer_context *mctx = get_mixer_context(dev);
-
- DRM_DEBUG_KMS("[mixer] runtime suspend - start\n");
-
- mixer_resource_poweroff(mctx);
- DRM_DEBUG_KMS("[mixer] runtime suspend - end\n");
- return 0;
-}
-
-#endif
-
-static int mixer_power(void *ctx, int mode)
+static int mixer_dpms(void *ctx, int mode)
{
struct mixer_context *mctx = ctx;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+ DRM_DEBUG_KMS("[DPMS:%s]\n", drm_get_dpms_name(mode));
switch (mode) {
case DRM_MODE_DPMS_ON:
- if (mctx->is_mixer_powered_on) {
- DRM_DEBUG_KMS("[%d] %s returning\n", __LINE__, __func__);
- break;
- }
- pm_runtime_get_sync(mctx->dev);
+ mixer_resource_poweron(mctx);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
- if (!mctx->is_mixer_powered_on) {
- DRM_DEBUG_KMS("[%d] %s returning\n", __LINE__, __func__);
- break;
- }
- pm_runtime_put_sync(mctx->dev);
+ mixer_resource_poweroff(mctx);
break;
default:
DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
{
struct mixer_context *mctx = ctx;
+ DRM_DEBUG("[DEV:%s]\n", drm_dev->devname);
+
mctx->drm_dev = drm_dev;
return 0;
static struct exynos_controller_ops mixer_ops = {
/* manager */
+ .adjust_modes = mixer_adjust_modes,
.subdrv_probe = mixer_subdrv_probe,
.enable_vblank = mixer_enable_vblank,
.disable_vblank = mixer_disable_vblank,
- .power = mixer_power,
+ .dpms = mixer_dpms,
/* overlay */
.mode_set = mixer_win_mode_set,
- .page_flip = mixer_win_page_flip,
- .apply = mixer_apply,
.win_commit = mixer_win_commit,
.win_disable = mixer_win_disable,
};
-static const struct dev_pm_ops mixer_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(mixer_suspend, mixer_resume)
- SET_RUNTIME_PM_OPS(mixer_runtime_suspend, mixer_runtime_resume, NULL)
-};
-
#ifdef CONFIG_EXYNOS_IOMMU
static int iommu_init(struct platform_device *pdev)
{
struct platform_device *pds;
+ DRM_DEBUG("[PDEV:%s]\n", pdev->name);
+
pds = find_sysmmu_dt(pdev, "sysmmu");
if (pds == NULL) {
printk(KERN_ERR "No sysmmu found :\n");
return 0;
}
+
+static void iommu_deinit(struct platform_device *pdev)
+{
+ s5p_destroy_iommu_mapping(&pdev->dev);
+ DRM_DEBUG("released the IOMMU mapping\n");
+}
#endif
static int __devinit mixer_resources_init_exynos(
struct resource *res;
int ret;
- DRM_DEBUG_KMS("Mixer resources init\n");
+ DRM_DEBUG("[PDEV:%s] is_exynos5: %d\n", pdev->name, is_exynos5);
mixer_res->is_soc_exynos5 = is_exynos5;
mixer_res->dev = dev;
{
struct mixer_resources *res = &mctx->mixer_res;
+ DRM_DEBUG("\n");
+
disable_irq(res->irq);
free_irq(res->irq, dev);
struct mixer_context *mctx;
int ret;
+ DRM_DEBUG("[PDEV:%s]\n", pdev->name);
+
dev_info(dev, "probe start\n");
mctx = kzalloc(sizeof(*mctx), GFP_KERNEL);
struct device *dev = &pdev->dev;
struct mixer_context *mctx = platform_get_drvdata(pdev);
+ DRM_DEBUG("[PDEV:%s]\n", pdev->name);
+
dev_info(dev, "remove successful\n");
mixer_resource_poweroff(mctx);
mixer_resources_cleanup(dev, mctx);
+#ifdef CONFIG_EXYNOS_IOMMU
+ iommu_deinit(pdev);
+#endif
+
kfree(mctx);
return 0;
.driver = {
.name = "s5p-mixer",
.owner = THIS_MODULE,
- .pm = &mixer_pm_ops,
},
.probe = mixer_probe,
.remove = __devexit_p(mixer_remove),