Add out-bridge support

- This patch series enables out-bridge for LVDS bridge device support,
   and also includes two cleanups and one relevant dt binding update
   for this.
 
 Add Samsung 16x16 tiled format support
 - This patch series adds Samsung 16x16 tiled format to scaler and
   gsc drivers. As for this, it adds Samsung specific format to
   drm_forcc.h header. For the git-pull request with relevant patches,
   I requested ack-by[1] to relevant maintainers but there was no any response.
   I'm pretty sure no problem to go to mainline though Exynos tree
   because the only user of it is Exynos.
 
 [1] https://patchwork.freedesktop.org/patch/243921/
 
 Add configurable plane alpha and pixel blend mode support
 - This patch series makes mixer driver to be configuragle for
   pixel blend mode and plane alpha, which also includes one fixup
   to set all default values correctly after reset.
 
 One cleanup
 - This patch replaces drm_atomic_helper_suspend/resume() with
   drm_mode_config_helper_suspend/resume() to remove exynos specific
   suspend_state.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJbsdElAAoJEFc4NIkMQxK4orIP/1NTuVHySKv44mwcNY/cKs66
 kcC+KTRcB/cMF2ZFaACe3R4a+2EBAxMeqo33FbTycnmS5DCUbV7C9OLgIihSYtu8
 RLojyZuEl3BE3k47QuBwdm4PTn5BqkAdG80MNyc6+rsnUY8bIaWYfGYouy7abdwU
 9coKwoit6S8GyeoRmfoLGt37uLqm+MdF+mTsNGNHQ68axYTZwo9nTZHudhKOriJs
 Ksuf3mUtfXVl4AwTmoVfvLwZt8/L8ZNxJMvZFYypAxFBZ608LptTzX9oVAGodaAn
 JHpWQsIKNbjcenNDd2TgsRwoCYFfnS5LbrJRYoKPDzu3VCDO8PYQ8o7YW+svBzdR
 FYKmCimJiHuxoU4wBxL+OcTLzrNK5c8/7Osn0bWTLlugMuOIdj8OvYTlk241V645
 3nD5kyiPgZiCF2oBx4y5U8LyIlOx+yZGA4Lhm4tVwnU/1DsJTCTXZOdu86LlMwSN
 A0i6/WupV2aei3s+x00li30NgsbEJKfWZJBdV5X8IZqwDRAIxEfphFmCk5Y68OHP
 Swdmy2x0Y7fLST/9Bnex7f2BJMR8vLbMH6OOLWF/JPOV5WxprKOqp3FCmWdhea06
 pUBiQNDI4KOg8rn2mKcNlpNolqMVqV9sbmTWUySV2l2a0dmwsepsQRp652jGFQ1u
 3RdiJrrUDnbR5P/s1emZ
 =aoJQ
 -----END PGP SIGNATURE-----

Merge tag 'exynos-drm-next-for-v4.20' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-next

Add out-bridge support
- This patch series enables out-bridge for LVDS bridge device support,
  and also includes two cleanups and one relevant dt binding update
  for this.

Add Samsung 16x16 tiled format support
- This patch series adds Samsung 16x16 tiled format to scaler and
  gsc drivers. As for this, it adds Samsung specific format to
  drm_forcc.h header. For the git-pull request with relevant patches,
  I requested ack-by[1] to relevant maintainers but there was no any response.
  I'm pretty sure no problem to go to mainline though Exynos tree
  because the only user of it is Exynos.
(airlied: this looked fine to me)

[1] https://patchwork.freedesktop.org/patch/243921/

Add configurable plane alpha and pixel blend mode support
- This patch series makes mixer driver to be configuragle for
  pixel blend mode and plane alpha, which also includes one fixup
  to set all default values correctly after reset.

One cleanup
- This patch replaces drm_atomic_helper_suspend/resume() with
  drm_mode_config_helper_suspend/resume() to remove exynos specific
  suspend_state.

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Inki Dae <inki.dae@samsung.com>
Link: https://patchwork.freedesktop.org/patch/msgid/1538380891-24040-1-git-send-email-inki.dae@samsung.com
This commit is contained in:
Dave Airlie 2018-10-04 10:40:35 +10:00
commit 612c6bd5e3
12 changed files with 250 additions and 209 deletions

View File

@ -21,6 +21,9 @@ Required properties:
- samsung,pll-clock-frequency: specifies frequency of the oscillator clock
- #address-cells, #size-cells: should be set respectively to <1> and <0>
according to DSI host bindings (see MIPI DSI bindings [1])
- samsung,burst-clock-frequency: specifies DSI frequency in high-speed burst
mode
- samsung,esc-clock-frequency: specifies DSI frequency in escape mode
Optional properties:
- power-domains: a phandle to DSIM power domain node
@ -29,25 +32,9 @@ Child nodes:
Should contain DSI peripheral nodes (see MIPI DSI bindings [1]).
Video interfaces:
Device node can contain video interface port nodes according to [2].
The following are properties specific to those nodes:
port node inbound:
- reg: (required) must be 0.
port node outbound:
- reg: (required) must be 1.
endpoint node connected from mic node (reg = 0):
- remote-endpoint: specifies the endpoint in mic node. This node is required
for Exynos5433 mipi dsi. So mic can access to panel node
throughout this dsi node.
endpoint node connected to panel node (reg = 1):
- remote-endpoint: specifies the endpoint in panel node. This node is
required in all kinds of exynos mipi dsi to represent
the connection between mipi dsi and panel.
- samsung,burst-clock-frequency: specifies DSI frequency in high-speed burst
mode
- samsung,esc-clock-frequency: specifies DSI frequency in escape mode
Device node can contain following video interface port nodes according to [2]:
0: RGB input,
1: DSI output
[1]: Documentation/devicetree/bindings/display/mipi-dsi-bus.txt
[2]: Documentation/devicetree/bindings/media/video-interfaces.txt

View File

@ -149,37 +149,15 @@ static struct drm_driver exynos_drm_driver = {
static int exynos_drm_suspend(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
struct exynos_drm_private *private;
if (!drm_dev)
return 0;
private = drm_dev->dev_private;
drm_kms_helper_poll_disable(drm_dev);
exynos_drm_fbdev_suspend(drm_dev);
private->suspend_state = drm_atomic_helper_suspend(drm_dev);
if (IS_ERR(private->suspend_state)) {
exynos_drm_fbdev_resume(drm_dev);
drm_kms_helper_poll_enable(drm_dev);
return PTR_ERR(private->suspend_state);
}
return 0;
return drm_mode_config_helper_suspend(drm_dev);
}
static void exynos_drm_resume(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
struct exynos_drm_private *private;
if (!drm_dev)
return;
private = drm_dev->dev_private;
drm_atomic_helper_resume(drm_dev, private->suspend_state);
exynos_drm_fbdev_resume(drm_dev);
drm_kms_helper_poll_enable(drm_dev);
drm_mode_config_helper_resume(drm_dev);
}
static const struct dev_pm_ops exynos_drm_pm_ops = {

View File

@ -92,6 +92,8 @@ struct exynos_drm_plane {
#define EXYNOS_DRM_PLANE_CAP_SCALE (1 << 1)
#define EXYNOS_DRM_PLANE_CAP_ZPOS (1 << 2)
#define EXYNOS_DRM_PLANE_CAP_TILE (1 << 3)
#define EXYNOS_DRM_PLANE_CAP_PIX_BLEND (1 << 4)
#define EXYNOS_DRM_PLANE_CAP_WIN_BLEND (1 << 5)
/*
* Exynos DRM plane configuration structure.
@ -195,7 +197,6 @@ struct drm_exynos_file_private {
*/
struct exynos_drm_private {
struct drm_fb_helper *fb_helper;
struct drm_atomic_state *suspend_state;
struct device *g2d_dev;
struct device *dma_dev;

View File

@ -255,6 +255,7 @@ struct exynos_dsi {
struct mipi_dsi_host dsi_host;
struct drm_connector connector;
struct drm_panel *panel;
struct drm_bridge *out_bridge;
struct device *dev;
void __iomem *reg_base;
@ -279,7 +280,7 @@ struct exynos_dsi {
struct list_head transfer_list;
const struct exynos_dsi_driver_data *driver_data;
struct device_node *bridge_node;
struct device_node *in_bridge_node;
};
#define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host)
@ -1382,29 +1383,37 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
return;
pm_runtime_get_sync(dsi->dev);
dsi->state |= DSIM_STATE_ENABLED;
ret = drm_panel_prepare(dsi->panel);
if (ret < 0) {
dsi->state &= ~DSIM_STATE_ENABLED;
pm_runtime_put_sync(dsi->dev);
return;
if (dsi->panel) {
ret = drm_panel_prepare(dsi->panel);
if (ret < 0)
goto err_put_sync;
} else {
drm_bridge_pre_enable(dsi->out_bridge);
}
exynos_dsi_set_display_mode(dsi);
exynos_dsi_set_display_enable(dsi, true);
ret = drm_panel_enable(dsi->panel);
if (ret < 0) {
dsi->state &= ~DSIM_STATE_ENABLED;
exynos_dsi_set_display_enable(dsi, false);
drm_panel_unprepare(dsi->panel);
pm_runtime_put_sync(dsi->dev);
return;
if (dsi->panel) {
ret = drm_panel_enable(dsi->panel);
if (ret < 0)
goto err_display_disable;
} else {
drm_bridge_enable(dsi->out_bridge);
}
dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
return;
err_display_disable:
exynos_dsi_set_display_enable(dsi, false);
drm_panel_unprepare(dsi->panel);
err_put_sync:
dsi->state &= ~DSIM_STATE_ENABLED;
pm_runtime_put(dsi->dev);
}
static void exynos_dsi_disable(struct drm_encoder *encoder)
@ -1417,11 +1426,11 @@ static void exynos_dsi_disable(struct drm_encoder *encoder)
dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
drm_panel_disable(dsi->panel);
drm_bridge_disable(dsi->out_bridge);
exynos_dsi_set_display_enable(dsi, false);
drm_panel_unprepare(dsi->panel);
drm_bridge_post_disable(dsi->out_bridge);
dsi->state &= ~DSIM_STATE_ENABLED;
pm_runtime_put_sync(dsi->dev);
}
@ -1499,7 +1508,30 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
struct mipi_dsi_device *device)
{
struct exynos_dsi *dsi = host_to_dsi(host);
struct drm_device *drm = dsi->connector.dev;
struct drm_encoder *encoder = &dsi->encoder;
struct drm_device *drm = encoder->dev;
struct drm_bridge *out_bridge;
out_bridge = of_drm_find_bridge(device->dev.of_node);
if (out_bridge) {
drm_bridge_attach(encoder, out_bridge, NULL);
dsi->out_bridge = out_bridge;
encoder->bridge = NULL;
} else {
int ret = exynos_dsi_create_connector(encoder);
if (ret) {
DRM_ERROR("failed to create connector ret = %d\n", ret);
drm_encoder_cleanup(encoder);
return ret;
}
dsi->panel = of_drm_find_panel(device->dev.of_node);
if (dsi->panel) {
drm_panel_attach(dsi->panel, &dsi->connector);
dsi->connector.status = connector_status_connected;
}
}
/*
* This is a temporary solution and should be made by more generic way.
@ -1518,14 +1550,6 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
dsi->lanes = device->lanes;
dsi->format = device->format;
dsi->mode_flags = device->mode_flags;
dsi->panel = of_drm_find_panel(device->dev.of_node);
if (IS_ERR(dsi->panel))
dsi->panel = NULL;
if (dsi->panel) {
drm_panel_attach(dsi->panel, &dsi->connector);
dsi->connector.status = connector_status_connected;
}
exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD)->i80_mode =
!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO);
@ -1541,19 +1565,21 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
struct mipi_dsi_device *device)
{
struct exynos_dsi *dsi = host_to_dsi(host);
struct drm_device *drm = dsi->connector.dev;
mutex_lock(&drm->mode_config.mutex);
struct drm_device *drm = dsi->encoder.dev;
if (dsi->panel) {
mutex_lock(&drm->mode_config.mutex);
exynos_dsi_disable(&dsi->encoder);
drm_panel_detach(dsi->panel);
dsi->panel = NULL;
dsi->connector.status = connector_status_disconnected;
mutex_unlock(&drm->mode_config.mutex);
} else {
if (dsi->out_bridge->funcs->detach)
dsi->out_bridge->funcs->detach(dsi->out_bridge);
dsi->out_bridge = NULL;
}
mutex_unlock(&drm->mode_config.mutex);
if (drm->mode_config.poll_enabled)
drm_kms_helper_hotplug_event(drm);
@ -1634,7 +1660,7 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
if (ret < 0)
return ret;
dsi->bridge_node = of_graph_get_remote_node(node, DSI_PORT_IN, 0);
dsi->in_bridge_node = of_graph_get_remote_node(node, DSI_PORT_IN, 0);
return 0;
}
@ -1645,7 +1671,7 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
struct drm_encoder *encoder = dev_get_drvdata(dev);
struct exynos_dsi *dsi = encoder_to_dsi(encoder);
struct drm_device *drm_dev = data;
struct drm_bridge *bridge;
struct drm_bridge *in_bridge;
int ret;
drm_encoder_init(drm_dev, encoder, &exynos_dsi_encoder_funcs,
@ -1657,17 +1683,10 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
if (ret < 0)
return ret;
ret = exynos_dsi_create_connector(encoder);
if (ret) {
DRM_ERROR("failed to create connector ret = %d\n", ret);
drm_encoder_cleanup(encoder);
return ret;
}
if (dsi->bridge_node) {
bridge = of_drm_find_bridge(dsi->bridge_node);
if (bridge)
drm_bridge_attach(encoder, bridge, NULL);
if (dsi->in_bridge_node) {
in_bridge = of_drm_find_bridge(dsi->in_bridge_node);
if (in_bridge)
drm_bridge_attach(encoder, in_bridge, NULL);
}
return mipi_dsi_host_register(&dsi->dsi_host);
@ -1786,7 +1805,7 @@ static int exynos_dsi_remove(struct platform_device *pdev)
{
struct exynos_dsi *dsi = platform_get_drvdata(pdev);
of_node_put(dsi->bridge_node);
of_node_put(dsi->in_bridge_node);
pm_runtime_disable(&pdev->dev);

View File

@ -270,20 +270,3 @@ void exynos_drm_fbdev_fini(struct drm_device *dev)
private->fb_helper = NULL;
}
void exynos_drm_fbdev_suspend(struct drm_device *dev)
{
struct exynos_drm_private *private = dev->dev_private;
console_lock();
drm_fb_helper_set_suspend(private->fb_helper, 1);
console_unlock();
}
void exynos_drm_fbdev_resume(struct drm_device *dev)
{
struct exynos_drm_private *private = dev->dev_private;
console_lock();
drm_fb_helper_set_suspend(private->fb_helper, 0);
console_unlock();
}

View File

@ -19,8 +19,6 @@
int exynos_drm_fbdev_init(struct drm_device *dev);
void exynos_drm_fbdev_fini(struct drm_device *dev);
void exynos_drm_fbdev_suspend(struct drm_device *drm);
void exynos_drm_fbdev_resume(struct drm_device *drm);
#else
@ -39,14 +37,6 @@ static inline void exynos_drm_fbdev_restore_mode(struct drm_device *dev)
#define exynos_drm_output_poll_changed (NULL)
static inline void exynos_drm_fbdev_suspend(struct drm_device *drm)
{
}
static inline void exynos_drm_fbdev_resume(struct drm_device *drm)
{
}
#endif
#endif

View File

@ -448,7 +448,7 @@ static void gsc_handle_irq(struct gsc_context *ctx, bool enable,
}
static void gsc_src_set_fmt(struct gsc_context *ctx, u32 fmt)
static void gsc_src_set_fmt(struct gsc_context *ctx, u32 fmt, bool tiled)
{
u32 cfg;
@ -514,6 +514,9 @@ static void gsc_src_set_fmt(struct gsc_context *ctx, u32 fmt)
break;
}
if (tiled)
cfg |= (GSC_IN_TILE_C_16x8 | GSC_IN_TILE_MODE);
gsc_write(cfg, GSC_IN_CON);
}
@ -632,7 +635,7 @@ static void gsc_src_set_addr(struct gsc_context *ctx, u32 buf_id,
gsc_src_set_buf_seq(ctx, buf_id, true);
}
static void gsc_dst_set_fmt(struct gsc_context *ctx, u32 fmt)
static void gsc_dst_set_fmt(struct gsc_context *ctx, u32 fmt, bool tiled)
{
u32 cfg;
@ -698,6 +701,9 @@ static void gsc_dst_set_fmt(struct gsc_context *ctx, u32 fmt)
break;
}
if (tiled)
cfg |= (GSC_IN_TILE_C_16x8 | GSC_OUT_TILE_MODE);
gsc_write(cfg, GSC_OUT_CON);
}
@ -1122,11 +1128,11 @@ static int gsc_commit(struct exynos_drm_ipp *ipp,
return ret;
}
gsc_src_set_fmt(ctx, task->src.buf.fourcc);
gsc_src_set_fmt(ctx, task->src.buf.fourcc, task->src.buf.modifier);
gsc_src_set_transf(ctx, task->transform.rotation);
gsc_src_set_size(ctx, &task->src);
gsc_src_set_addr(ctx, 0, &task->src);
gsc_dst_set_fmt(ctx, task->dst.buf.fourcc);
gsc_dst_set_fmt(ctx, task->dst.buf.fourcc, task->dst.buf.modifier);
gsc_dst_set_size(ctx, &task->dst);
gsc_dst_set_addr(ctx, 0, &task->dst);
gsc_set_prescaler(ctx, &ctx->sc, &task->src.rect, &task->dst.rect);
@ -1200,6 +1206,10 @@ static const unsigned int gsc_formats[] = {
DRM_FORMAT_YUV420, DRM_FORMAT_YVU420, DRM_FORMAT_YUV422,
};
static const unsigned int gsc_tiled_formats[] = {
DRM_FORMAT_NV12, DRM_FORMAT_NV21,
};
static int gsc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@ -1207,23 +1217,24 @@ static int gsc_probe(struct platform_device *pdev)
struct exynos_drm_ipp_formats *formats;
struct gsc_context *ctx;
struct resource *res;
int ret, i;
int num_formats, ret, i, j;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
formats = devm_kcalloc(dev,
ARRAY_SIZE(gsc_formats), sizeof(*formats),
GFP_KERNEL);
if (!formats)
return -ENOMEM;
driver_data = (struct gsc_driverdata *)of_device_get_match_data(dev);
ctx->dev = dev;
ctx->num_clocks = driver_data->num_clocks;
ctx->clk_names = driver_data->clk_names;
/* construct formats/limits array */
num_formats = ARRAY_SIZE(gsc_formats) + ARRAY_SIZE(gsc_tiled_formats);
formats = devm_kcalloc(dev, num_formats, sizeof(*formats), GFP_KERNEL);
if (!formats)
return -ENOMEM;
/* linear formats */
for (i = 0; i < ARRAY_SIZE(gsc_formats); i++) {
formats[i].fourcc = gsc_formats[i];
formats[i].type = DRM_EXYNOS_IPP_FORMAT_SOURCE |
@ -1231,8 +1242,19 @@ static int gsc_probe(struct platform_device *pdev)
formats[i].limits = driver_data->limits;
formats[i].num_limits = driver_data->num_limits;
}
/* tiled formats */
for (j = i, i = 0; i < ARRAY_SIZE(gsc_tiled_formats); j++, i++) {
formats[j].fourcc = gsc_tiled_formats[i];
formats[j].modifier = DRM_FORMAT_MOD_SAMSUNG_16_16_TILE;
formats[j].type = DRM_EXYNOS_IPP_FORMAT_SOURCE |
DRM_EXYNOS_IPP_FORMAT_DESTINATION;
formats[j].limits = driver_data->limits;
formats[j].num_limits = driver_data->num_limits;
}
ctx->formats = formats;
ctx->num_formats = ARRAY_SIZE(gsc_formats);
ctx->num_formats = num_formats;
/* clock control */
for (i = 0; i < ctx->num_clocks; i++) {

View File

@ -131,16 +131,14 @@ static void exynos_drm_plane_reset(struct drm_plane *plane)
if (plane->state) {
exynos_state = to_exynos_plane_state(plane->state);
if (exynos_state->base.fb)
drm_framebuffer_put(exynos_state->base.fb);
__drm_atomic_helper_plane_destroy_state(plane->state);
kfree(exynos_state);
plane->state = NULL;
}
exynos_state = kzalloc(sizeof(*exynos_state), GFP_KERNEL);
if (exynos_state) {
plane->state = &exynos_state->base;
plane->state->plane = plane;
__drm_atomic_helper_plane_reset(plane, &exynos_state->base);
plane->state->zpos = exynos_plane->config->zpos;
}
}
@ -300,6 +298,10 @@ int exynos_plane_init(struct drm_device *dev,
const struct exynos_drm_plane_config *config)
{
int err;
unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE);
struct drm_plane *plane = &exynos_plane->base;
err = drm_universal_plane_init(dev, &exynos_plane->base,
1 << dev->mode_config.num_crtc,
@ -320,5 +322,11 @@ int exynos_plane_init(struct drm_device *dev,
exynos_plane_attach_zpos_property(&exynos_plane->base, config->zpos,
!(config->capabilities & EXYNOS_DRM_PLANE_CAP_ZPOS));
if (config->capabilities & EXYNOS_DRM_PLANE_CAP_PIX_BLEND)
drm_plane_create_blend_mode_property(plane, supported_modes);
if (config->capabilities & EXYNOS_DRM_PLANE_CAP_WIN_BLEND)
drm_plane_create_alpha_property(plane);
return 0;
}

View File

@ -49,56 +49,46 @@ struct scaler_context {
const struct scaler_data *scaler_data;
};
static u32 scaler_get_format(u32 drm_fmt)
{
switch (drm_fmt) {
case DRM_FORMAT_NV12:
return SCALER_YUV420_2P_UV;
case DRM_FORMAT_NV21:
return SCALER_YUV420_2P_VU;
case DRM_FORMAT_YUV420:
return SCALER_YUV420_3P;
case DRM_FORMAT_YUYV:
return SCALER_YUV422_1P_YUYV;
case DRM_FORMAT_UYVY:
return SCALER_YUV422_1P_UYVY;
case DRM_FORMAT_YVYU:
return SCALER_YUV422_1P_YVYU;
case DRM_FORMAT_NV16:
return SCALER_YUV422_2P_UV;
case DRM_FORMAT_NV61:
return SCALER_YUV422_2P_VU;
case DRM_FORMAT_YUV422:
return SCALER_YUV422_3P;
case DRM_FORMAT_NV24:
return SCALER_YUV444_2P_UV;
case DRM_FORMAT_NV42:
return SCALER_YUV444_2P_VU;
case DRM_FORMAT_YUV444:
return SCALER_YUV444_3P;
case DRM_FORMAT_RGB565:
return SCALER_RGB_565;
case DRM_FORMAT_XRGB1555:
return SCALER_ARGB1555;
case DRM_FORMAT_ARGB1555:
return SCALER_ARGB1555;
case DRM_FORMAT_XRGB4444:
return SCALER_ARGB4444;
case DRM_FORMAT_ARGB4444:
return SCALER_ARGB4444;
case DRM_FORMAT_XRGB8888:
return SCALER_ARGB8888;
case DRM_FORMAT_ARGB8888:
return SCALER_ARGB8888;
case DRM_FORMAT_RGBX8888:
return SCALER_RGBA8888;
case DRM_FORMAT_RGBA8888:
return SCALER_RGBA8888;
default:
break;
}
struct scaler_format {
u32 drm_fmt;
u32 internal_fmt;
u32 chroma_tile_w;
u32 chroma_tile_h;
};
return 0;
static const struct scaler_format scaler_formats[] = {
{ DRM_FORMAT_NV12, SCALER_YUV420_2P_UV, 8, 8 },
{ DRM_FORMAT_NV21, SCALER_YUV420_2P_VU, 8, 8 },
{ DRM_FORMAT_YUV420, SCALER_YUV420_3P, 8, 8 },
{ DRM_FORMAT_YUYV, SCALER_YUV422_1P_YUYV, 16, 16 },
{ DRM_FORMAT_UYVY, SCALER_YUV422_1P_UYVY, 16, 16 },
{ DRM_FORMAT_YVYU, SCALER_YUV422_1P_YVYU, 16, 16 },
{ DRM_FORMAT_NV16, SCALER_YUV422_2P_UV, 8, 16 },
{ DRM_FORMAT_NV61, SCALER_YUV422_2P_VU, 8, 16 },
{ DRM_FORMAT_YUV422, SCALER_YUV422_3P, 8, 16 },
{ DRM_FORMAT_NV24, SCALER_YUV444_2P_UV, 16, 16 },
{ DRM_FORMAT_NV42, SCALER_YUV444_2P_VU, 16, 16 },
{ DRM_FORMAT_YUV444, SCALER_YUV444_3P, 16, 16 },
{ DRM_FORMAT_RGB565, SCALER_RGB_565, 0, 0 },
{ DRM_FORMAT_XRGB1555, SCALER_ARGB1555, 0, 0 },
{ DRM_FORMAT_ARGB1555, SCALER_ARGB1555, 0, 0 },
{ DRM_FORMAT_XRGB4444, SCALER_ARGB4444, 0, 0 },
{ DRM_FORMAT_ARGB4444, SCALER_ARGB4444, 0, 0 },
{ DRM_FORMAT_XRGB8888, SCALER_ARGB8888, 0, 0 },
{ DRM_FORMAT_ARGB8888, SCALER_ARGB8888, 0, 0 },
{ DRM_FORMAT_RGBX8888, SCALER_RGBA8888, 0, 0 },
{ DRM_FORMAT_RGBA8888, SCALER_RGBA8888, 0, 0 },
};
static const struct scaler_format *scaler_get_format(u32 drm_fmt)
{
int i;
for (i = 0; i < ARRAY_SIZE(scaler_formats); i++)
if (scaler_formats[i].drm_fmt == drm_fmt)
return &scaler_formats[i];
return NULL;
}
static inline int scaler_reset(struct scaler_context *scaler)
@ -152,11 +142,11 @@ static inline void scaler_enable_int(struct scaler_context *scaler)
}
static inline void scaler_set_src_fmt(struct scaler_context *scaler,
u32 src_fmt)
u32 src_fmt, u32 tile)
{
u32 val;
val = SCALER_SRC_CFG_SET_COLOR_FORMAT(src_fmt);
val = SCALER_SRC_CFG_SET_COLOR_FORMAT(src_fmt) | (tile << 10);
scaler_write(val, SCALER_SRC_CFG);
}
@ -188,15 +178,20 @@ static inline void scaler_set_src_span(struct scaler_context *scaler,
scaler_write(val, SCALER_SRC_SPAN);
}
static inline void scaler_set_src_luma_pos(struct scaler_context *scaler,
struct drm_exynos_ipp_task_rect *src_pos)
static inline void scaler_set_src_luma_chroma_pos(struct scaler_context *scaler,
struct drm_exynos_ipp_task_rect *src_pos,
const struct scaler_format *fmt)
{
u32 val;
val = SCALER_SRC_Y_POS_SET_YH_POS(src_pos->x << 2);
val |= SCALER_SRC_Y_POS_SET_YV_POS(src_pos->y << 2);
scaler_write(val, SCALER_SRC_Y_POS);
scaler_write(val, SCALER_SRC_C_POS); /* ATTENTION! */
val = SCALER_SRC_C_POS_SET_CH_POS(
(src_pos->x * fmt->chroma_tile_w / 16) << 2);
val |= SCALER_SRC_C_POS_SET_CV_POS(
(src_pos->y * fmt->chroma_tile_h / 16) << 2);
scaler_write(val, SCALER_SRC_C_POS);
}
static inline void scaler_set_src_wh(struct scaler_context *scaler,
@ -366,11 +361,12 @@ static int scaler_commit(struct exynos_drm_ipp *ipp,
struct scaler_context *scaler =
container_of(ipp, struct scaler_context, ipp);
u32 src_fmt = scaler_get_format(task->src.buf.fourcc);
struct drm_exynos_ipp_task_rect *src_pos = &task->src.rect;
u32 dst_fmt = scaler_get_format(task->dst.buf.fourcc);
struct drm_exynos_ipp_task_rect *dst_pos = &task->dst.rect;
const struct scaler_format *src_fmt, *dst_fmt;
src_fmt = scaler_get_format(task->src.buf.fourcc);
dst_fmt = scaler_get_format(task->dst.buf.fourcc);
pm_runtime_get_sync(scaler->dev);
if (scaler_reset(scaler)) {
@ -380,13 +376,14 @@ static int scaler_commit(struct exynos_drm_ipp *ipp,
scaler->task = task;
scaler_set_src_fmt(scaler, src_fmt);
scaler_set_src_fmt(
scaler, src_fmt->internal_fmt, task->src.buf.modifier != 0);
scaler_set_src_base(scaler, &task->src);
scaler_set_src_span(scaler, &task->src);
scaler_set_src_luma_pos(scaler, src_pos);
scaler_set_src_luma_chroma_pos(scaler, src_pos, src_fmt);
scaler_set_src_wh(scaler, src_pos);
scaler_set_dst_fmt(scaler, dst_fmt);
scaler_set_dst_fmt(scaler, dst_fmt->internal_fmt);
scaler_set_dst_base(scaler, &task->dst);
scaler_set_dst_span(scaler, &task->dst);
scaler_set_dst_luma_pos(scaler, dst_pos);
@ -617,6 +614,16 @@ static const struct drm_exynos_ipp_limit scaler_5420_one_pixel_limits[] = {
.v = { 65536 * 1 / 4, 65536 * 16 }) },
};
static const struct drm_exynos_ipp_limit scaler_5420_tile_limits[] = {
{ IPP_SIZE_LIMIT(BUFFER, .h = { 16, SZ_8K }, .v = { 16, SZ_8K })},
{ IPP_SIZE_LIMIT(AREA, .h.align = 16, .v.align = 16) },
{ IPP_SCALE_LIMIT(.h = {1, 1}, .v = {1, 1})},
{ }
};
#define IPP_SRCDST_TILE_FORMAT(f, l) \
IPP_SRCDST_MFORMAT(f, DRM_FORMAT_MOD_SAMSUNG_16_16_TILE, (l))
static const struct exynos_drm_ipp_formats exynos5420_formats[] = {
/* SCALER_YUV420_2P_UV */
{ IPP_SRCDST_FORMAT(NV21, scaler_5420_two_pixel_hv_limits) },
@ -680,6 +687,18 @@ static const struct exynos_drm_ipp_formats exynos5420_formats[] = {
/* SCALER_RGBA8888 */
{ IPP_SRCDST_FORMAT(RGBA8888, scaler_5420_one_pixel_limits) },
/* SCALER_YUV420_2P_UV TILE */
{ IPP_SRCDST_TILE_FORMAT(NV21, scaler_5420_tile_limits) },
/* SCALER_YUV420_2P_VU TILE */
{ IPP_SRCDST_TILE_FORMAT(NV12, scaler_5420_tile_limits) },
/* SCALER_YUV420_3P TILE */
{ IPP_SRCDST_TILE_FORMAT(YUV420, scaler_5420_tile_limits) },
/* SCALER_YUV422_1P_YUYV TILE */
{ IPP_SRCDST_TILE_FORMAT(YUYV, scaler_5420_tile_limits) },
};
static const struct scaler_data exynos5420_data = {

View File

@ -131,14 +131,18 @@ static const struct exynos_drm_plane_config plane_configs[MIXER_WIN_NR] = {
.pixel_formats = mixer_formats,
.num_pixel_formats = ARRAY_SIZE(mixer_formats),
.capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE |
EXYNOS_DRM_PLANE_CAP_ZPOS,
EXYNOS_DRM_PLANE_CAP_ZPOS |
EXYNOS_DRM_PLANE_CAP_PIX_BLEND |
EXYNOS_DRM_PLANE_CAP_WIN_BLEND,
}, {
.zpos = 1,
.type = DRM_PLANE_TYPE_CURSOR,
.pixel_formats = mixer_formats,
.num_pixel_formats = ARRAY_SIZE(mixer_formats),
.capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE |
EXYNOS_DRM_PLANE_CAP_ZPOS,
EXYNOS_DRM_PLANE_CAP_ZPOS |
EXYNOS_DRM_PLANE_CAP_PIX_BLEND |
EXYNOS_DRM_PLANE_CAP_WIN_BLEND,
}, {
.zpos = 2,
.type = DRM_PLANE_TYPE_OVERLAY,
@ -146,7 +150,8 @@ static const struct exynos_drm_plane_config plane_configs[MIXER_WIN_NR] = {
.num_pixel_formats = ARRAY_SIZE(vp_formats),
.capabilities = EXYNOS_DRM_PLANE_CAP_SCALE |
EXYNOS_DRM_PLANE_CAP_ZPOS |
EXYNOS_DRM_PLANE_CAP_TILE,
EXYNOS_DRM_PLANE_CAP_TILE |
EXYNOS_DRM_PLANE_CAP_WIN_BLEND,
},
};
@ -309,31 +314,42 @@ static void vp_default_filter(struct mixer_context *ctx)
}
static void mixer_cfg_gfx_blend(struct mixer_context *ctx, unsigned int win,
bool alpha)
unsigned int pixel_alpha, unsigned int alpha)
{
u32 win_alpha = alpha >> 8;
u32 val;
val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
if (alpha) {
/* blending based on pixel alpha */
switch (pixel_alpha) {
case DRM_MODE_BLEND_PIXEL_NONE:
break;
case DRM_MODE_BLEND_COVERAGE:
val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
break;
case DRM_MODE_BLEND_PREMULTI:
default:
val |= MXR_GRP_CFG_BLEND_PRE_MUL;
val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
break;
}
if (alpha != DRM_BLEND_ALPHA_OPAQUE) {
val |= MXR_GRP_CFG_WIN_BLEND_EN;
val |= win_alpha;
}
mixer_reg_writemask(ctx, MXR_GRAPHIC_CFG(win),
val, MXR_GRP_CFG_MISC_MASK);
}
static void mixer_cfg_vp_blend(struct mixer_context *ctx)
static void mixer_cfg_vp_blend(struct mixer_context *ctx, unsigned int alpha)
{
u32 val;
u32 win_alpha = alpha >> 8;
u32 val = 0;
/*
* No blending at the moment since the NV12/NV21 pixelformats don't
* have an alpha channel. However the mixer supports a global alpha
* value for a layer. Once this functionality is exposed, we can
* support blending of the video layer through this.
*/
val = 0;
if (alpha != DRM_BLEND_ALPHA_OPAQUE) {
val |= MXR_VID_CFG_BLEND_EN;
val |= win_alpha;
}
mixer_reg_write(ctx, MXR_VIDEO_CFG, val);
}
@ -529,7 +545,7 @@ static void vp_video_buffer(struct mixer_context *ctx,
vp_reg_write(ctx, VP_BOT_C_PTR, chroma_addr[1]);
mixer_cfg_layer(ctx, plane->index, priority, true);
mixer_cfg_vp_blend(ctx);
mixer_cfg_vp_blend(ctx, state->base.alpha);
spin_unlock_irqrestore(&ctx->reg_slock, flags);
@ -553,10 +569,16 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
unsigned int win = plane->index;
unsigned int x_ratio = 0, y_ratio = 0;
unsigned int dst_x_offset, dst_y_offset;
unsigned int pixel_alpha;
dma_addr_t dma_addr;
unsigned int fmt;
u32 val;
if (fb->format->has_alpha)
pixel_alpha = state->base.pixel_blend_mode;
else
pixel_alpha = DRM_MODE_BLEND_PIXEL_NONE;
switch (fb->format->format) {
case DRM_FORMAT_XRGB4444:
case DRM_FORMAT_ARGB4444:
@ -616,7 +638,7 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
mixer_reg_write(ctx, MXR_GRAPHIC_BASE(win), dma_addr);
mixer_cfg_layer(ctx, win, priority, true);
mixer_cfg_gfx_blend(ctx, win, fb->format->has_alpha);
mixer_cfg_gfx_blend(ctx, win, pixel_alpha, state->base.alpha);
/* layer update mandatory for mixer 16.0.33.0 */
if (ctx->mxr_ver == MXR_VER_16_0_33_0 ||

View File

@ -109,12 +109,15 @@
#define MXR_CFG_SCAN_HD (1 << 0)
#define MXR_CFG_SCAN_MASK 0x47
/* bits for MXR_VIDEO_CFG */
#define MXR_VID_CFG_BLEND_EN (1 << 16)
/* bits for MXR_GRAPHICn_CFG */
#define MXR_GRP_CFG_COLOR_KEY_DISABLE (1 << 21)
#define MXR_GRP_CFG_BLEND_PRE_MUL (1 << 20)
#define MXR_GRP_CFG_WIN_BLEND_EN (1 << 17)
#define MXR_GRP_CFG_PIXEL_BLEND_EN (1 << 16)
#define MXR_GRP_CFG_MISC_MASK ((3 << 16) | (3 << 20))
#define MXR_GRP_CFG_MISC_MASK ((3 << 16) | (3 << 20) | 0xff)
#define MXR_GRP_CFG_FORMAT_VAL(x) MXR_MASK_VAL(x, 11, 8)
#define MXR_GRP_CFG_FORMAT_MASK MXR_GRP_CFG_FORMAT_VAL(~0)
#define MXR_GRP_CFG_ALPHA_VAL(x) MXR_MASK_VAL(x, 7, 0)

View File

@ -338,6 +338,15 @@ extern "C" {
*/
#define DRM_FORMAT_MOD_SAMSUNG_64_32_TILE fourcc_mod_code(SAMSUNG, 1)
/*
* Tiled, 16 (pixels) x 16 (lines) - sized macroblocks
*
* This is a simple tiled layout using tiles of 16x16 pixels in a row-major
* layout. For YCbCr formats Cb/Cr components are taken in such a way that
* they correspond to their 16x16 luma block.
*/
#define DRM_FORMAT_MOD_SAMSUNG_16_16_TILE fourcc_mod_code(SAMSUNG, 2)
/*
* Qualcomm Compressed Format
*