forked from Minki/linux
Merge branch 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-next
This 2nd pull request includes the following, - add configurable plane support and relevant cleanups. - fixup kernel panic issue at drm releasing. - remove unnecessary codes. This has been delayed to resolve a critical issue - which incurrs a kernel panic when driver is released - and review it. * 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos: drm/exynos: fix kernel panic issue at drm releasing drm/exynos: crtc: do not wait for the scanout completion drm/exynos: mixer: properly update all planes on the same vblank event drm/exynos: crtc: rework atomic_{begin,flush} drm/exynos: mixer: unify a check for video-processor window drm/exynos: mixer: also allow ARGB1555 and ARGB4444 drm/exynos: mixer: refactor layer setup drm/exynos: mixer: remove all static blending setup drm/exynos: mixer: set window priority based on zpos drm/exynos: make zpos property configurable drm/exynos: rename zpos to index
This commit is contained in:
commit
d6f7a18c8a
@ -248,15 +248,16 @@ static void decon_shadow_protect_win(struct decon_context *ctx, int win,
|
||||
protect ? ~0 : 0);
|
||||
}
|
||||
|
||||
static void decon_atomic_begin(struct exynos_drm_crtc *crtc,
|
||||
struct exynos_drm_plane *plane)
|
||||
static void decon_atomic_begin(struct exynos_drm_crtc *crtc)
|
||||
{
|
||||
struct decon_context *ctx = crtc->ctx;
|
||||
int i;
|
||||
|
||||
if (test_bit(BIT_SUSPENDED, &ctx->flags))
|
||||
return;
|
||||
|
||||
decon_shadow_protect_win(ctx, plane->zpos, true);
|
||||
for (i = ctx->first_win; i < WINDOWS_NR; i++)
|
||||
decon_shadow_protect_win(ctx, i, true);
|
||||
}
|
||||
|
||||
#define BIT_VAL(x, e, s) (((x) & ((1 << ((e) - (s) + 1)) - 1)) << (s))
|
||||
@ -270,7 +271,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
|
||||
to_exynos_plane_state(plane->base.state);
|
||||
struct decon_context *ctx = crtc->ctx;
|
||||
struct drm_framebuffer *fb = state->base.fb;
|
||||
unsigned int win = plane->zpos;
|
||||
unsigned int win = plane->index;
|
||||
unsigned int bpp = fb->bits_per_pixel >> 3;
|
||||
unsigned int pitch = fb->pitches[0];
|
||||
dma_addr_t dma_addr = exynos_drm_fb_dma_addr(fb, 0);
|
||||
@ -320,7 +321,7 @@ static void decon_disable_plane(struct exynos_drm_crtc *crtc,
|
||||
struct exynos_drm_plane *plane)
|
||||
{
|
||||
struct decon_context *ctx = crtc->ctx;
|
||||
unsigned int win = plane->zpos;
|
||||
unsigned int win = plane->index;
|
||||
|
||||
if (test_bit(BIT_SUSPENDED, &ctx->flags))
|
||||
return;
|
||||
@ -336,15 +337,16 @@ static void decon_disable_plane(struct exynos_drm_crtc *crtc,
|
||||
decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0);
|
||||
}
|
||||
|
||||
static void decon_atomic_flush(struct exynos_drm_crtc *crtc,
|
||||
struct exynos_drm_plane *plane)
|
||||
static void decon_atomic_flush(struct exynos_drm_crtc *crtc)
|
||||
{
|
||||
struct decon_context *ctx = crtc->ctx;
|
||||
int i;
|
||||
|
||||
if (test_bit(BIT_SUSPENDED, &ctx->flags))
|
||||
return;
|
||||
|
||||
decon_shadow_protect_win(ctx, plane->zpos, false);
|
||||
for (i = ctx->first_win; i < WINDOWS_NR; i++)
|
||||
decon_shadow_protect_win(ctx, i, false);
|
||||
|
||||
if (ctx->out_type == IFTYPE_I80)
|
||||
set_bit(BIT_WIN_UPDATED, &ctx->flags);
|
||||
@ -502,7 +504,7 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
|
||||
ctx->configs[win].zpos = win;
|
||||
ctx->configs[win].type = decon_win_types[tmp];
|
||||
|
||||
ret = exynos_plane_init(drm_dev, &ctx->planes[win],
|
||||
ret = exynos_plane_init(drm_dev, &ctx->planes[win], win,
|
||||
1 << ctx->pipe, &ctx->configs[win]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -385,15 +385,16 @@ static void decon_shadow_protect_win(struct decon_context *ctx,
|
||||
writel(val, ctx->regs + SHADOWCON);
|
||||
}
|
||||
|
||||
static void decon_atomic_begin(struct exynos_drm_crtc *crtc,
|
||||
struct exynos_drm_plane *plane)
|
||||
static void decon_atomic_begin(struct exynos_drm_crtc *crtc)
|
||||
{
|
||||
struct decon_context *ctx = crtc->ctx;
|
||||
int i;
|
||||
|
||||
if (ctx->suspended)
|
||||
return;
|
||||
|
||||
decon_shadow_protect_win(ctx, plane->zpos, true);
|
||||
for (i = 0; i < WINDOWS_NR; i++)
|
||||
decon_shadow_protect_win(ctx, i, true);
|
||||
}
|
||||
|
||||
static void decon_update_plane(struct exynos_drm_crtc *crtc,
|
||||
@ -407,7 +408,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
|
||||
unsigned long val, alpha;
|
||||
unsigned int last_x;
|
||||
unsigned int last_y;
|
||||
unsigned int win = plane->zpos;
|
||||
unsigned int win = plane->index;
|
||||
unsigned int bpp = fb->bits_per_pixel >> 3;
|
||||
unsigned int pitch = fb->pitches[0];
|
||||
|
||||
@ -498,7 +499,7 @@ static void decon_disable_plane(struct exynos_drm_crtc *crtc,
|
||||
struct exynos_drm_plane *plane)
|
||||
{
|
||||
struct decon_context *ctx = crtc->ctx;
|
||||
unsigned int win = plane->zpos;
|
||||
unsigned int win = plane->index;
|
||||
u32 val;
|
||||
|
||||
if (ctx->suspended)
|
||||
@ -517,15 +518,16 @@ static void decon_disable_plane(struct exynos_drm_crtc *crtc,
|
||||
writel(val, ctx->regs + DECON_UPDATE);
|
||||
}
|
||||
|
||||
static void decon_atomic_flush(struct exynos_drm_crtc *crtc,
|
||||
struct exynos_drm_plane *plane)
|
||||
static void decon_atomic_flush(struct exynos_drm_crtc *crtc)
|
||||
{
|
||||
struct decon_context *ctx = crtc->ctx;
|
||||
int i;
|
||||
|
||||
if (ctx->suspended)
|
||||
return;
|
||||
|
||||
decon_shadow_protect_win(ctx, plane->zpos, false);
|
||||
for (i = 0; i < WINDOWS_NR; i++)
|
||||
decon_shadow_protect_win(ctx, i, false);
|
||||
}
|
||||
|
||||
static void decon_init(struct decon_context *ctx)
|
||||
@ -657,7 +659,7 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
|
||||
ctx->configs[i].zpos = i;
|
||||
ctx->configs[i].type = decon_win_types[i];
|
||||
|
||||
ret = exynos_plane_init(drm_dev, &ctx->planes[i],
|
||||
ret = exynos_plane_init(drm_dev, &ctx->planes[i], i,
|
||||
1 << ctx->pipe, &ctx->configs[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -68,32 +68,20 @@ static void exynos_crtc_atomic_begin(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_crtc_state)
|
||||
{
|
||||
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
|
||||
struct drm_plane *plane;
|
||||
|
||||
exynos_crtc->event = crtc->state->event;
|
||||
|
||||
drm_atomic_crtc_for_each_plane(plane, crtc) {
|
||||
struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
|
||||
|
||||
if (exynos_crtc->ops->atomic_begin)
|
||||
exynos_crtc->ops->atomic_begin(exynos_crtc,
|
||||
exynos_plane);
|
||||
}
|
||||
if (exynos_crtc->ops->atomic_begin)
|
||||
exynos_crtc->ops->atomic_begin(exynos_crtc);
|
||||
}
|
||||
|
||||
static void exynos_crtc_atomic_flush(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_crtc_state)
|
||||
{
|
||||
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
|
||||
struct drm_plane *plane;
|
||||
|
||||
drm_atomic_crtc_for_each_plane(plane, crtc) {
|
||||
struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
|
||||
|
||||
if (exynos_crtc->ops->atomic_flush)
|
||||
exynos_crtc->ops->atomic_flush(exynos_crtc,
|
||||
exynos_plane);
|
||||
}
|
||||
if (exynos_crtc->ops->atomic_flush)
|
||||
exynos_crtc->ops->atomic_flush(exynos_crtc);
|
||||
}
|
||||
|
||||
static const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
|
||||
@ -215,29 +203,6 @@ void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc,
|
||||
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
||||
}
|
||||
|
||||
void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb)
|
||||
{
|
||||
struct exynos_drm_crtc *exynos_crtc;
|
||||
struct drm_device *dev = fb->dev;
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
/*
|
||||
* make sure that overlay data are updated to real hardware
|
||||
* for all encoders.
|
||||
*/
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
exynos_crtc = to_exynos_crtc(crtc);
|
||||
|
||||
/*
|
||||
* wait for vblank interrupt
|
||||
* - this makes sure that overlay data are updated to
|
||||
* real hardware.
|
||||
*/
|
||||
if (exynos_crtc->ops->wait_for_vblank)
|
||||
exynos_crtc->ops->wait_for_vblank(exynos_crtc);
|
||||
}
|
||||
}
|
||||
|
||||
int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
|
||||
enum exynos_drm_output_type out_type)
|
||||
{
|
||||
@ -261,3 +226,29 @@ void exynos_drm_crtc_te_handler(struct drm_crtc *crtc)
|
||||
if (exynos_crtc->ops->te_handler)
|
||||
exynos_crtc->ops->te_handler(exynos_crtc);
|
||||
}
|
||||
|
||||
void exynos_drm_crtc_cancel_page_flip(struct drm_crtc *crtc,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
|
||||
struct drm_pending_vblank_event *e;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&crtc->dev->event_lock, flags);
|
||||
e = exynos_crtc->event;
|
||||
if (e && e->base.file_priv == file) {
|
||||
exynos_crtc->event = NULL;
|
||||
/*
|
||||
* event will be destroyed by core part
|
||||
* so below line should be removed later with core changes
|
||||
*/
|
||||
e->base.destroy(&e->base);
|
||||
/*
|
||||
* event_space will be increased by core part
|
||||
* so below line should be removed later with core changes.
|
||||
*/
|
||||
file->event_space += sizeof(e->event);
|
||||
atomic_dec(&exynos_crtc->pending_update);
|
||||
}
|
||||
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, unsigned int pipe);
|
||||
void exynos_drm_crtc_wait_pending_update(struct exynos_drm_crtc *exynos_crtc);
|
||||
void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc,
|
||||
struct exynos_drm_plane *exynos_plane);
|
||||
void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb);
|
||||
|
||||
/* This function gets pipe value to crtc device matched with out_type. */
|
||||
int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
|
||||
@ -41,4 +40,8 @@ int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
|
||||
*/
|
||||
void exynos_drm_crtc_te_handler(struct drm_crtc *crtc);
|
||||
|
||||
/* This function cancels a page flip request. */
|
||||
void exynos_drm_crtc_cancel_page_flip(struct drm_crtc *crtc,
|
||||
struct drm_file *file);
|
||||
|
||||
#endif
|
||||
|
@ -330,7 +330,12 @@ err_file_priv_free:
|
||||
static void exynos_drm_preclose(struct drm_device *dev,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
exynos_drm_subdrv_close(dev, file);
|
||||
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
|
||||
exynos_drm_crtc_cancel_page_flip(crtc, file);
|
||||
}
|
||||
|
||||
static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
|
||||
|
@ -64,6 +64,7 @@ struct exynos_drm_plane_state {
|
||||
struct exynos_drm_rect src;
|
||||
unsigned int h_ratio;
|
||||
unsigned int v_ratio;
|
||||
unsigned int zpos;
|
||||
};
|
||||
|
||||
static inline struct exynos_drm_plane_state *
|
||||
@ -76,7 +77,7 @@ to_exynos_plane_state(struct drm_plane_state *state)
|
||||
* Exynos drm common overlay structure.
|
||||
*
|
||||
* @base: plane object
|
||||
* @zpos: order of overlay layer(z position).
|
||||
* @index: hardware index of the overlay layer
|
||||
*
|
||||
* this structure is common to exynos SoC and its contents would be copied
|
||||
* to hardware specific overlay info.
|
||||
@ -85,17 +86,18 @@ to_exynos_plane_state(struct drm_plane_state *state)
|
||||
struct exynos_drm_plane {
|
||||
struct drm_plane base;
|
||||
const struct exynos_drm_plane_config *config;
|
||||
unsigned int zpos;
|
||||
unsigned int index;
|
||||
struct drm_framebuffer *pending_fb;
|
||||
};
|
||||
|
||||
#define EXYNOS_DRM_PLANE_CAP_DOUBLE (1 << 0)
|
||||
#define EXYNOS_DRM_PLANE_CAP_SCALE (1 << 1)
|
||||
#define EXYNOS_DRM_PLANE_CAP_ZPOS (1 << 2)
|
||||
|
||||
/*
|
||||
* Exynos DRM plane configuration structure.
|
||||
*
|
||||
* @zpos: z-position of the plane.
|
||||
* @zpos: initial z-position of the plane.
|
||||
* @type: type of the plane (primary, cursor or overlay).
|
||||
* @pixel_formats: supported pixel formats.
|
||||
* @num_pixel_formats: number of elements in 'pixel_formats'.
|
||||
@ -121,8 +123,8 @@ struct exynos_drm_plane_config {
|
||||
* @wait_for_vblank: wait for vblank interrupt to make sure that
|
||||
* hardware overlay is updated.
|
||||
* @atomic_check: validate state
|
||||
* @atomic_begin: prepare a window to receive a update
|
||||
* @atomic_flush: mark the end of a window update
|
||||
* @atomic_begin: prepare device to receive an update
|
||||
* @atomic_flush: mark the end of device update
|
||||
* @update_plane: apply hardware specific overlay data to registers.
|
||||
* @disable_plane: disable hardware specific overlay.
|
||||
* @te_handler: trigger to transfer video image at the tearing effect
|
||||
@ -142,14 +144,12 @@ struct exynos_drm_crtc_ops {
|
||||
void (*wait_for_vblank)(struct exynos_drm_crtc *crtc);
|
||||
int (*atomic_check)(struct exynos_drm_crtc *crtc,
|
||||
struct drm_crtc_state *state);
|
||||
void (*atomic_begin)(struct exynos_drm_crtc *crtc,
|
||||
struct exynos_drm_plane *plane);
|
||||
void (*atomic_begin)(struct exynos_drm_crtc *crtc);
|
||||
void (*update_plane)(struct exynos_drm_crtc *crtc,
|
||||
struct exynos_drm_plane *plane);
|
||||
void (*disable_plane)(struct exynos_drm_crtc *crtc,
|
||||
struct exynos_drm_plane *plane);
|
||||
void (*atomic_flush)(struct exynos_drm_crtc *crtc,
|
||||
struct exynos_drm_plane *plane);
|
||||
void (*atomic_flush)(struct exynos_drm_crtc *crtc);
|
||||
void (*te_handler)(struct exynos_drm_crtc *crtc);
|
||||
void (*clock_enable)(struct exynos_drm_crtc *crtc, bool enable);
|
||||
};
|
||||
|
@ -71,9 +71,6 @@ static void exynos_drm_fb_destroy(struct drm_framebuffer *fb)
|
||||
struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
|
||||
unsigned int i;
|
||||
|
||||
/* make sure that overlay data are updated before relesing fb. */
|
||||
exynos_drm_crtc_complete_scanout(fb);
|
||||
|
||||
drm_framebuffer_cleanup(fb);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(exynos_fb->exynos_gem); i++) {
|
||||
|
@ -622,26 +622,28 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx,
|
||||
writel(val, ctx->regs + reg);
|
||||
}
|
||||
|
||||
static void fimd_atomic_begin(struct exynos_drm_crtc *crtc,
|
||||
struct exynos_drm_plane *plane)
|
||||
static void fimd_atomic_begin(struct exynos_drm_crtc *crtc)
|
||||
{
|
||||
struct fimd_context *ctx = crtc->ctx;
|
||||
int i;
|
||||
|
||||
if (ctx->suspended)
|
||||
return;
|
||||
|
||||
fimd_shadow_protect_win(ctx, plane->zpos, true);
|
||||
for (i = 0; i < WINDOWS_NR; i++)
|
||||
fimd_shadow_protect_win(ctx, i, true);
|
||||
}
|
||||
|
||||
static void fimd_atomic_flush(struct exynos_drm_crtc *crtc,
|
||||
struct exynos_drm_plane *plane)
|
||||
static void fimd_atomic_flush(struct exynos_drm_crtc *crtc)
|
||||
{
|
||||
struct fimd_context *ctx = crtc->ctx;
|
||||
int i;
|
||||
|
||||
if (ctx->suspended)
|
||||
return;
|
||||
|
||||
fimd_shadow_protect_win(ctx, plane->zpos, false);
|
||||
for (i = 0; i < WINDOWS_NR; i++)
|
||||
fimd_shadow_protect_win(ctx, i, false);
|
||||
}
|
||||
|
||||
static void fimd_update_plane(struct exynos_drm_crtc *crtc,
|
||||
@ -654,7 +656,7 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc,
|
||||
dma_addr_t dma_addr;
|
||||
unsigned long val, size, offset;
|
||||
unsigned int last_x, last_y, buf_offsize, line_size;
|
||||
unsigned int win = plane->zpos;
|
||||
unsigned int win = plane->index;
|
||||
unsigned int bpp = fb->bits_per_pixel >> 3;
|
||||
unsigned int pitch = fb->pitches[0];
|
||||
|
||||
@ -740,7 +742,7 @@ static void fimd_disable_plane(struct exynos_drm_crtc *crtc,
|
||||
struct exynos_drm_plane *plane)
|
||||
{
|
||||
struct fimd_context *ctx = crtc->ctx;
|
||||
unsigned int win = plane->zpos;
|
||||
unsigned int win = plane->index;
|
||||
|
||||
if (ctx->suspended)
|
||||
return;
|
||||
@ -944,7 +946,7 @@ static int fimd_bind(struct device *dev, struct device *master, void *data)
|
||||
ctx->configs[i].num_pixel_formats = ARRAY_SIZE(fimd_formats);
|
||||
ctx->configs[i].zpos = i;
|
||||
ctx->configs[i].type = fimd_win_types[i];
|
||||
ret = exynos_plane_init(drm_dev, &ctx->planes[i],
|
||||
ret = exynos_plane_init(drm_dev, &ctx->planes[i], i,
|
||||
1 << ctx->pipe, &ctx->configs[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -124,6 +124,7 @@ static void exynos_plane_mode_set(struct exynos_drm_plane_state *exynos_state)
|
||||
|
||||
static void exynos_drm_plane_reset(struct drm_plane *plane)
|
||||
{
|
||||
struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
|
||||
struct exynos_drm_plane_state *exynos_state;
|
||||
|
||||
if (plane->state) {
|
||||
@ -136,6 +137,7 @@ static void exynos_drm_plane_reset(struct drm_plane *plane)
|
||||
|
||||
exynos_state = kzalloc(sizeof(*exynos_state), GFP_KERNEL);
|
||||
if (exynos_state) {
|
||||
exynos_state->zpos = exynos_plane->config->zpos;
|
||||
plane->state = &exynos_state->base;
|
||||
plane->state->plane = plane;
|
||||
}
|
||||
@ -153,6 +155,7 @@ exynos_drm_plane_duplicate_state(struct drm_plane *plane)
|
||||
return NULL;
|
||||
|
||||
__drm_atomic_helper_plane_duplicate_state(plane, ©->base);
|
||||
copy->zpos = exynos_state->zpos;
|
||||
return ©->base;
|
||||
}
|
||||
|
||||
@ -165,13 +168,53 @@ static void exynos_drm_plane_destroy_state(struct drm_plane *plane,
|
||||
kfree(old_exynos_state);
|
||||
}
|
||||
|
||||
static int exynos_drm_plane_atomic_set_property(struct drm_plane *plane,
|
||||
struct drm_plane_state *state,
|
||||
struct drm_property *property,
|
||||
uint64_t val)
|
||||
{
|
||||
struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
|
||||
struct exynos_drm_plane_state *exynos_state =
|
||||
to_exynos_plane_state(state);
|
||||
struct exynos_drm_private *dev_priv = plane->dev->dev_private;
|
||||
const struct exynos_drm_plane_config *config = exynos_plane->config;
|
||||
|
||||
if (property == dev_priv->plane_zpos_property &&
|
||||
(config->capabilities & EXYNOS_DRM_PLANE_CAP_ZPOS))
|
||||
exynos_state->zpos = val;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_drm_plane_atomic_get_property(struct drm_plane *plane,
|
||||
const struct drm_plane_state *state,
|
||||
struct drm_property *property,
|
||||
uint64_t *val)
|
||||
{
|
||||
const struct exynos_drm_plane_state *exynos_state =
|
||||
container_of(state, const struct exynos_drm_plane_state, base);
|
||||
struct exynos_drm_private *dev_priv = plane->dev->dev_private;
|
||||
|
||||
if (property == dev_priv->plane_zpos_property)
|
||||
*val = exynos_state->zpos;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct drm_plane_funcs exynos_plane_funcs = {
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
.destroy = drm_plane_cleanup,
|
||||
.set_property = drm_atomic_helper_plane_set_property,
|
||||
.reset = exynos_drm_plane_reset,
|
||||
.atomic_duplicate_state = exynos_drm_plane_duplicate_state,
|
||||
.atomic_destroy_state = exynos_drm_plane_destroy_state,
|
||||
.atomic_set_property = exynos_drm_plane_atomic_set_property,
|
||||
.atomic_get_property = exynos_drm_plane_atomic_get_property,
|
||||
};
|
||||
|
||||
static int
|
||||
@ -267,8 +310,8 @@ static void exynos_plane_attach_zpos_property(struct drm_plane *plane,
|
||||
|
||||
prop = dev_priv->plane_zpos_property;
|
||||
if (!prop) {
|
||||
prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE,
|
||||
"zpos", 0, MAX_PLANE - 1);
|
||||
prop = drm_property_create_range(dev, 0, "zpos",
|
||||
0, MAX_PLANE - 1);
|
||||
if (!prop)
|
||||
return;
|
||||
|
||||
@ -280,7 +323,7 @@ static void exynos_plane_attach_zpos_property(struct drm_plane *plane,
|
||||
|
||||
int exynos_plane_init(struct drm_device *dev,
|
||||
struct exynos_drm_plane *exynos_plane,
|
||||
unsigned long possible_crtcs,
|
||||
unsigned int index, unsigned long possible_crtcs,
|
||||
const struct exynos_drm_plane_config *config)
|
||||
{
|
||||
int err;
|
||||
@ -298,12 +341,10 @@ int exynos_plane_init(struct drm_device *dev,
|
||||
|
||||
drm_plane_helper_add(&exynos_plane->base, &plane_helper_funcs);
|
||||
|
||||
exynos_plane->zpos = config->zpos;
|
||||
exynos_plane->index = index;
|
||||
exynos_plane->config = config;
|
||||
|
||||
if (config->type == DRM_PLANE_TYPE_OVERLAY)
|
||||
exynos_plane_attach_zpos_property(&exynos_plane->base,
|
||||
config->zpos);
|
||||
exynos_plane_attach_zpos_property(&exynos_plane->base, config->zpos);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -10,6 +10,6 @@
|
||||
*/
|
||||
|
||||
int exynos_plane_init(struct drm_device *dev,
|
||||
struct exynos_drm_plane *exynos_plane,
|
||||
struct exynos_drm_plane *exynos_plane, unsigned int index,
|
||||
unsigned long possible_crtcs,
|
||||
const struct exynos_drm_plane_config *config);
|
||||
|
@ -461,7 +461,7 @@ static int vidi_bind(struct device *dev, struct device *master, void *data)
|
||||
plane_config.zpos = i;
|
||||
plane_config.type = vidi_win_types[i];
|
||||
|
||||
ret = exynos_plane_init(drm_dev, &ctx->planes[i],
|
||||
ret = exynos_plane_init(drm_dev, &ctx->planes[i], i,
|
||||
1 << ctx->pipe, &plane_config);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -76,7 +76,9 @@ enum mixer_flag_bits {
|
||||
|
||||
static const uint32_t mixer_formats[] = {
|
||||
DRM_FORMAT_XRGB4444,
|
||||
DRM_FORMAT_ARGB4444,
|
||||
DRM_FORMAT_XRGB1555,
|
||||
DRM_FORMAT_ARGB1555,
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
@ -117,19 +119,22 @@ static const struct exynos_drm_plane_config plane_configs[MIXER_WIN_NR] = {
|
||||
.type = DRM_PLANE_TYPE_PRIMARY,
|
||||
.pixel_formats = mixer_formats,
|
||||
.num_pixel_formats = ARRAY_SIZE(mixer_formats),
|
||||
.capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE,
|
||||
.capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE |
|
||||
EXYNOS_DRM_PLANE_CAP_ZPOS,
|
||||
}, {
|
||||
.zpos = 1,
|
||||
.type = DRM_PLANE_TYPE_CURSOR,
|
||||
.pixel_formats = mixer_formats,
|
||||
.num_pixel_formats = ARRAY_SIZE(mixer_formats),
|
||||
.capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE,
|
||||
.capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE |
|
||||
EXYNOS_DRM_PLANE_CAP_ZPOS,
|
||||
}, {
|
||||
.zpos = 2,
|
||||
.type = DRM_PLANE_TYPE_OVERLAY,
|
||||
.pixel_formats = vp_formats,
|
||||
.num_pixel_formats = ARRAY_SIZE(vp_formats),
|
||||
.capabilities = EXYNOS_DRM_PLANE_CAP_SCALE,
|
||||
.capabilities = EXYNOS_DRM_PLANE_CAP_SCALE |
|
||||
EXYNOS_DRM_PLANE_CAP_ZPOS,
|
||||
},
|
||||
};
|
||||
|
||||
@ -162,6 +167,18 @@ static const u8 filter_cr_horiz_tap4[] = {
|
||||
70, 59, 48, 37, 27, 19, 11, 5,
|
||||
};
|
||||
|
||||
static inline bool is_alpha_format(unsigned int pixel_format)
|
||||
{
|
||||
switch (pixel_format) {
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
case DRM_FORMAT_ARGB1555:
|
||||
case DRM_FORMAT_ARGB4444:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id)
|
||||
{
|
||||
return readl(res->vp_regs + reg_id);
|
||||
@ -291,6 +308,37 @@ static void vp_default_filter(struct mixer_resources *res)
|
||||
filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4));
|
||||
}
|
||||
|
||||
static void mixer_cfg_gfx_blend(struct mixer_context *ctx, unsigned int win,
|
||||
bool alpha)
|
||||
{
|
||||
struct mixer_resources *res = &ctx->mixer_res;
|
||||
u32 val;
|
||||
|
||||
val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
|
||||
if (alpha) {
|
||||
/* blending based on pixel alpha */
|
||||
val |= MXR_GRP_CFG_BLEND_PRE_MUL;
|
||||
val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
|
||||
}
|
||||
mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
|
||||
val, MXR_GRP_CFG_MISC_MASK);
|
||||
}
|
||||
|
||||
static void mixer_cfg_vp_blend(struct mixer_context *ctx)
|
||||
{
|
||||
struct mixer_resources *res = &ctx->mixer_res;
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
mixer_reg_write(res, MXR_VIDEO_CFG, val);
|
||||
}
|
||||
|
||||
static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
|
||||
{
|
||||
struct mixer_resources *res = &ctx->mixer_res;
|
||||
@ -372,7 +420,7 @@ static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height)
|
||||
}
|
||||
|
||||
static void mixer_cfg_layer(struct mixer_context *ctx, unsigned int win,
|
||||
bool enable)
|
||||
unsigned int priority, bool enable)
|
||||
{
|
||||
struct mixer_resources *res = &ctx->mixer_res;
|
||||
u32 val = enable ? ~0 : 0;
|
||||
@ -380,20 +428,24 @@ static void mixer_cfg_layer(struct mixer_context *ctx, unsigned int win,
|
||||
switch (win) {
|
||||
case 0:
|
||||
mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP0_ENABLE);
|
||||
mixer_reg_writemask(res, MXR_LAYER_CFG,
|
||||
MXR_LAYER_CFG_GRP0_VAL(priority),
|
||||
MXR_LAYER_CFG_GRP0_MASK);
|
||||
break;
|
||||
case 1:
|
||||
mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP1_ENABLE);
|
||||
mixer_reg_writemask(res, MXR_LAYER_CFG,
|
||||
MXR_LAYER_CFG_GRP1_VAL(priority),
|
||||
MXR_LAYER_CFG_GRP1_MASK);
|
||||
break;
|
||||
case 2:
|
||||
case VP_DEFAULT_WIN:
|
||||
if (ctx->vp_enabled) {
|
||||
vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
|
||||
mixer_reg_writemask(res, MXR_CFG, val,
|
||||
MXR_CFG_VP_ENABLE);
|
||||
|
||||
/* control blending of graphic layer 0 */
|
||||
mixer_reg_writemask(res, MXR_GRAPHIC_CFG(0), val,
|
||||
MXR_GRP_CFG_BLEND_PRE_MUL |
|
||||
MXR_GRP_CFG_PIXEL_BLEND_EN);
|
||||
mixer_reg_writemask(res, MXR_LAYER_CFG,
|
||||
MXR_LAYER_CFG_VP_VAL(priority),
|
||||
MXR_LAYER_CFG_VP_MASK);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -464,7 +516,6 @@ static void vp_video_buffer(struct mixer_context *ctx,
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&res->reg_slock, flags);
|
||||
mixer_vsync_set_update(ctx, false);
|
||||
|
||||
/* interlace or progressive scan mode */
|
||||
val = (ctx->interlace ? ~0 : 0);
|
||||
@ -511,10 +562,10 @@ static void vp_video_buffer(struct mixer_context *ctx,
|
||||
|
||||
mixer_cfg_scan(ctx, mode->vdisplay);
|
||||
mixer_cfg_rgb_fmt(ctx, mode->vdisplay);
|
||||
mixer_cfg_layer(ctx, plane->zpos, true);
|
||||
mixer_cfg_layer(ctx, plane->index, state->zpos + 1, true);
|
||||
mixer_cfg_vp_blend(ctx);
|
||||
mixer_run(ctx);
|
||||
|
||||
mixer_vsync_set_update(ctx, true);
|
||||
spin_unlock_irqrestore(&res->reg_slock, flags);
|
||||
|
||||
mixer_regs_dump(ctx);
|
||||
@ -537,7 +588,7 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
|
||||
struct mixer_resources *res = &ctx->mixer_res;
|
||||
struct drm_framebuffer *fb = state->base.fb;
|
||||
unsigned long flags;
|
||||
unsigned int win = plane->zpos;
|
||||
unsigned int win = plane->index;
|
||||
unsigned int x_ratio = 0, y_ratio = 0;
|
||||
unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
|
||||
dma_addr_t dma_addr;
|
||||
@ -546,10 +597,12 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
|
||||
|
||||
switch (fb->pixel_format) {
|
||||
case DRM_FORMAT_XRGB4444:
|
||||
case DRM_FORMAT_ARGB4444:
|
||||
fmt = MXR_FORMAT_ARGB4444;
|
||||
break;
|
||||
|
||||
case DRM_FORMAT_XRGB1555:
|
||||
case DRM_FORMAT_ARGB1555:
|
||||
fmt = MXR_FORMAT_ARGB1555;
|
||||
break;
|
||||
|
||||
@ -587,7 +640,6 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
|
||||
ctx->interlace = false;
|
||||
|
||||
spin_lock_irqsave(&res->reg_slock, flags);
|
||||
mixer_vsync_set_update(ctx, false);
|
||||
|
||||
/* setup format */
|
||||
mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
|
||||
@ -626,7 +678,8 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
|
||||
|
||||
mixer_cfg_scan(ctx, mode->vdisplay);
|
||||
mixer_cfg_rgb_fmt(ctx, mode->vdisplay);
|
||||
mixer_cfg_layer(ctx, win, true);
|
||||
mixer_cfg_layer(ctx, win, state->zpos + 1, true);
|
||||
mixer_cfg_gfx_blend(ctx, win, is_alpha_format(fb->pixel_format));
|
||||
|
||||
/* layer update mandatory for mixer 16.0.33.0 */
|
||||
if (ctx->mxr_ver == MXR_VER_16_0_33_0 ||
|
||||
@ -635,7 +688,6 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
|
||||
|
||||
mixer_run(ctx);
|
||||
|
||||
mixer_vsync_set_update(ctx, true);
|
||||
spin_unlock_irqrestore(&res->reg_slock, flags);
|
||||
|
||||
mixer_regs_dump(ctx);
|
||||
@ -660,10 +712,8 @@ static void mixer_win_reset(struct mixer_context *ctx)
|
||||
{
|
||||
struct mixer_resources *res = &ctx->mixer_res;
|
||||
unsigned long flags;
|
||||
u32 val; /* value stored to register */
|
||||
|
||||
spin_lock_irqsave(&res->reg_slock, flags);
|
||||
mixer_vsync_set_update(ctx, false);
|
||||
|
||||
mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
|
||||
|
||||
@ -674,40 +724,14 @@ static void mixer_win_reset(struct mixer_context *ctx)
|
||||
mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
|
||||
MXR_STATUS_BURST_MASK);
|
||||
|
||||
/* setting default layer priority: layer1 > layer0 > video
|
||||
* because typical usage scenario would be
|
||||
* layer1 - OSD
|
||||
* layer0 - framebuffer
|
||||
* video - video overlay
|
||||
*/
|
||||
val = MXR_LAYER_CFG_GRP1_VAL(3);
|
||||
val |= MXR_LAYER_CFG_GRP0_VAL(2);
|
||||
if (ctx->vp_enabled)
|
||||
val |= MXR_LAYER_CFG_VP_VAL(1);
|
||||
mixer_reg_write(res, MXR_LAYER_CFG, val);
|
||||
/* reset default layer priority */
|
||||
mixer_reg_write(res, MXR_LAYER_CFG, 0);
|
||||
|
||||
/* setting background color */
|
||||
mixer_reg_write(res, MXR_BG_COLOR0, 0x008080);
|
||||
mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
|
||||
mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
|
||||
|
||||
/* setting graphical layers */
|
||||
val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
|
||||
val |= MXR_GRP_CFG_WIN_BLEND_EN;
|
||||
val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */
|
||||
|
||||
/* Don't blend layer 0 onto the mixer background */
|
||||
mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val);
|
||||
|
||||
/* Blend layer 1 into layer 0 */
|
||||
val |= MXR_GRP_CFG_BLEND_PRE_MUL;
|
||||
val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
|
||||
mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val);
|
||||
|
||||
/* setting video layers */
|
||||
val = MXR_GRP_CFG_ALPHA_VAL(0);
|
||||
mixer_reg_write(res, MXR_VIDEO_CFG, val);
|
||||
|
||||
if (ctx->vp_enabled) {
|
||||
/* configuration of Video Processor Registers */
|
||||
vp_win_reset(ctx);
|
||||
@ -720,7 +744,6 @@ static void mixer_win_reset(struct mixer_context *ctx)
|
||||
if (ctx->vp_enabled)
|
||||
mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
|
||||
|
||||
mixer_vsync_set_update(ctx, true);
|
||||
spin_unlock_irqrestore(&res->reg_slock, flags);
|
||||
}
|
||||
|
||||
@ -951,17 +974,27 @@ static void mixer_disable_vblank(struct exynos_drm_crtc *crtc)
|
||||
mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
|
||||
}
|
||||
|
||||
static void mixer_atomic_begin(struct exynos_drm_crtc *crtc)
|
||||
{
|
||||
struct mixer_context *mixer_ctx = crtc->ctx;
|
||||
|
||||
if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
|
||||
return;
|
||||
|
||||
mixer_vsync_set_update(mixer_ctx, false);
|
||||
}
|
||||
|
||||
static void mixer_update_plane(struct exynos_drm_crtc *crtc,
|
||||
struct exynos_drm_plane *plane)
|
||||
{
|
||||
struct mixer_context *mixer_ctx = crtc->ctx;
|
||||
|
||||
DRM_DEBUG_KMS("win: %d\n", plane->zpos);
|
||||
DRM_DEBUG_KMS("win: %d\n", plane->index);
|
||||
|
||||
if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
|
||||
return;
|
||||
|
||||
if (plane->zpos > 1 && mixer_ctx->vp_enabled)
|
||||
if (plane->index == VP_DEFAULT_WIN)
|
||||
vp_video_buffer(mixer_ctx, plane);
|
||||
else
|
||||
mixer_graph_buffer(mixer_ctx, plane);
|
||||
@ -974,18 +1007,24 @@ static void mixer_disable_plane(struct exynos_drm_crtc *crtc,
|
||||
struct mixer_resources *res = &mixer_ctx->mixer_res;
|
||||
unsigned long flags;
|
||||
|
||||
DRM_DEBUG_KMS("win: %d\n", plane->zpos);
|
||||
DRM_DEBUG_KMS("win: %d\n", plane->index);
|
||||
|
||||
if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&res->reg_slock, flags);
|
||||
mixer_vsync_set_update(mixer_ctx, false);
|
||||
mixer_cfg_layer(mixer_ctx, plane->index, 0, false);
|
||||
spin_unlock_irqrestore(&res->reg_slock, flags);
|
||||
}
|
||||
|
||||
mixer_cfg_layer(mixer_ctx, plane->zpos, false);
|
||||
static void mixer_atomic_flush(struct exynos_drm_crtc *crtc)
|
||||
{
|
||||
struct mixer_context *mixer_ctx = crtc->ctx;
|
||||
|
||||
if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
|
||||
return;
|
||||
|
||||
mixer_vsync_set_update(mixer_ctx, true);
|
||||
spin_unlock_irqrestore(&res->reg_slock, flags);
|
||||
}
|
||||
|
||||
static void mixer_wait_for_vblank(struct exynos_drm_crtc *crtc)
|
||||
@ -1026,6 +1065,8 @@ static void mixer_enable(struct exynos_drm_crtc *crtc)
|
||||
|
||||
pm_runtime_get_sync(ctx->dev);
|
||||
|
||||
mixer_vsync_set_update(ctx, false);
|
||||
|
||||
mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
|
||||
|
||||
if (test_bit(MXR_BIT_VSYNC, &ctx->flags)) {
|
||||
@ -1034,6 +1075,8 @@ static void mixer_enable(struct exynos_drm_crtc *crtc)
|
||||
}
|
||||
mixer_win_reset(ctx);
|
||||
|
||||
mixer_vsync_set_update(ctx, true);
|
||||
|
||||
set_bit(MXR_BIT_POWERED, &ctx->flags);
|
||||
}
|
||||
|
||||
@ -1084,8 +1127,10 @@ static const struct exynos_drm_crtc_ops mixer_crtc_ops = {
|
||||
.enable_vblank = mixer_enable_vblank,
|
||||
.disable_vblank = mixer_disable_vblank,
|
||||
.wait_for_vblank = mixer_wait_for_vblank,
|
||||
.atomic_begin = mixer_atomic_begin,
|
||||
.update_plane = mixer_update_plane,
|
||||
.disable_plane = mixer_disable_plane,
|
||||
.atomic_flush = mixer_atomic_flush,
|
||||
.atomic_check = mixer_atomic_check,
|
||||
};
|
||||
|
||||
@ -1160,7 +1205,7 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)
|
||||
if (i == VP_DEFAULT_WIN && !ctx->vp_enabled)
|
||||
continue;
|
||||
|
||||
ret = exynos_plane_init(drm_dev, &ctx->planes[i],
|
||||
ret = exynos_plane_init(drm_dev, &ctx->planes[i], i,
|
||||
1 << ctx->pipe, &plane_configs[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -113,6 +113,7 @@
|
||||
#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_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)
|
||||
@ -145,8 +146,11 @@
|
||||
|
||||
/* bit for MXR_LAYER_CFG */
|
||||
#define MXR_LAYER_CFG_GRP1_VAL(x) MXR_MASK_VAL(x, 11, 8)
|
||||
#define MXR_LAYER_CFG_GRP1_MASK MXR_LAYER_CFG_GRP1_VAL(~0)
|
||||
#define MXR_LAYER_CFG_GRP0_VAL(x) MXR_MASK_VAL(x, 7, 4)
|
||||
#define MXR_LAYER_CFG_GRP0_MASK MXR_LAYER_CFG_GRP0_VAL(~0)
|
||||
#define MXR_LAYER_CFG_VP_VAL(x) MXR_MASK_VAL(x, 3, 0)
|
||||
#define MXR_LAYER_CFG_VP_MASK MXR_LAYER_CFG_VP_VAL(~0)
|
||||
|
||||
#endif /* SAMSUNG_REGS_MIXER_H */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user