mirror of
https://github.com/hardkernel/kernel_common_drivers.git
synced 2026-06-25 12:03:48 +09:00
f33c53a429
PD#SWPL-110645 Problem: no s5 drm driver support Solution: add drm driver for s5 Verify: ax201 Test: DRM-OSD-45 Change-Id: Idbb21fd5f96ea0ac4b75a2524aa7d95067ba8ea2 Signed-off-by: Ao Xu <ao.xu@amlogic.com>
393 lines
9.6 KiB
C
393 lines
9.6 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/dma-fence.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/sync_file.h>
|
|
#include <linux/file.h>
|
|
#include <linux/types.h>
|
|
|
|
#include "meson_async_atomic.h"
|
|
#include "meson_plane.h"
|
|
#include <drm/drmP.h>
|
|
#include <drm/drm_atomic.h>
|
|
#include <drm/drm_mode.h>
|
|
#include <drm/drm_atomic_uapi.h>
|
|
#include <drm/drm_plane_helper.h>
|
|
#include <drm/drm_property.h>
|
|
|
|
#include "meson_drv.h"
|
|
#include "meson_async_atomic.h"
|
|
#include "meson_plane.h"
|
|
|
|
struct drm_property *meson_mode_obj_find_prop_id(struct drm_mode_object *obj,
|
|
uint32_t prop_id)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < obj->properties->count; i++)
|
|
if (obj->properties->properties[i]->base.id == prop_id)
|
|
return obj->properties->properties[i];
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
meson_atomic_replace_property_blob_from_id(struct drm_device *dev,
|
|
struct drm_property_blob **blob,
|
|
u64 blob_id,
|
|
ssize_t expected_size,
|
|
ssize_t expected_elem_size,
|
|
bool *replaced)
|
|
{
|
|
struct drm_property_blob *new_blob = NULL;
|
|
|
|
if (blob_id != 0) {
|
|
new_blob = drm_property_lookup_blob(dev, blob_id);
|
|
if (!new_blob)
|
|
return -EINVAL;
|
|
|
|
if (expected_size > 0 &&
|
|
new_blob->length != expected_size) {
|
|
drm_property_blob_put(new_blob);
|
|
return -EINVAL;
|
|
}
|
|
if (expected_elem_size > 0 &&
|
|
new_blob->length % expected_elem_size != 0) {
|
|
drm_property_blob_put(new_blob);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
*replaced |= drm_property_replace_blob(blob, new_blob);
|
|
drm_property_blob_put(new_blob);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int meson_atomic_plane_set_property(struct drm_plane *plane,
|
|
struct drm_plane_state *state, struct drm_file *file_priv,
|
|
struct drm_property *property, u64 val)
|
|
{
|
|
struct drm_device *dev = plane->dev;
|
|
struct drm_mode_config *config = &dev->mode_config;
|
|
bool replaced = false;
|
|
int ret;
|
|
|
|
if (property == config->prop_fb_id) {
|
|
struct drm_framebuffer *fb;
|
|
|
|
fb = drm_framebuffer_lookup(dev, file_priv, val);
|
|
drm_atomic_set_fb_for_plane(state, fb);
|
|
if (fb)
|
|
drm_framebuffer_put(fb);
|
|
} else if (property == config->prop_in_fence_fd) {
|
|
if (state->fence)
|
|
return -EINVAL;
|
|
|
|
if (U642I64(val) == -1)
|
|
return 0;
|
|
|
|
state->fence = sync_file_get_fence(val);
|
|
if (!state->fence)
|
|
return -EINVAL;
|
|
|
|
} else if (property == config->prop_crtc_id) {
|
|
struct drm_crtc *crtc = drm_crtc_find(dev, file_priv, val);
|
|
|
|
if (val && !crtc)
|
|
return -EACCES;
|
|
return drm_atomic_set_crtc_for_plane(state, crtc);
|
|
} else if (property == config->prop_crtc_x) {
|
|
state->crtc_x = U642I64(val);
|
|
} else if (property == config->prop_crtc_y) {
|
|
state->crtc_y = U642I64(val);
|
|
} else if (property == config->prop_crtc_w) {
|
|
state->crtc_w = val;
|
|
} else if (property == config->prop_crtc_h) {
|
|
state->crtc_h = val;
|
|
} else if (property == config->prop_src_x) {
|
|
state->src_x = val;
|
|
} else if (property == config->prop_src_y) {
|
|
state->src_y = val;
|
|
} else if (property == config->prop_src_w) {
|
|
state->src_w = val;
|
|
} else if (property == config->prop_src_h) {
|
|
state->src_h = val;
|
|
} else if (property == plane->alpha_property) {
|
|
state->alpha = val;
|
|
} else if (property == plane->blend_mode_property) {
|
|
state->pixel_blend_mode = val;
|
|
} else if (property == plane->rotation_property) {
|
|
if (!is_power_of_2(val & DRM_MODE_ROTATE_MASK)) {
|
|
DRM_DEBUG_ATOMIC("[PLANE:%d:%s] bad rotation bitmask: 0x%llx\n",
|
|
plane->base.id, plane->name, val);
|
|
return -EINVAL;
|
|
}
|
|
state->rotation = val;
|
|
} else if (property == plane->zpos_property) {
|
|
state->zpos = val;
|
|
} else if (property == plane->color_encoding_property) {
|
|
state->color_encoding = val;
|
|
} else if (property == plane->color_range_property) {
|
|
state->color_range = val;
|
|
} else if (property == config->prop_fb_damage_clips) {
|
|
ret = meson_atomic_replace_property_blob_from_id(dev,
|
|
&state->fb_damage_clips,
|
|
val,
|
|
-1,
|
|
sizeof(struct drm_rect),
|
|
&replaced);
|
|
return ret;
|
|
} else if (property == plane->scaling_filter_property) {
|
|
state->scaling_filter = val;
|
|
} else if (plane->funcs->atomic_set_property) {
|
|
return plane->funcs->atomic_set_property(plane, state,
|
|
property, val);
|
|
} else {
|
|
DRM_DEBUG_ATOMIC("[PLANE:%d:%s] unknown property [PROP:%d:%s]]\n",
|
|
plane->base.id, plane->name,
|
|
property->base.id, property->name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool meson_property_change_valid_get(struct drm_property *property,
|
|
u64 value, struct drm_mode_object **ref)
|
|
{
|
|
int i;
|
|
|
|
if (property->flags & DRM_MODE_PROP_IMMUTABLE)
|
|
return false;
|
|
|
|
*ref = NULL;
|
|
|
|
if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) {
|
|
if (value < property->values[0] || value > property->values[1])
|
|
return false;
|
|
return true;
|
|
} else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) {
|
|
s64 svalue = U642I64(value);
|
|
|
|
if (svalue < U642I64(property->values[0]) ||
|
|
svalue > U642I64(property->values[1]))
|
|
return false;
|
|
return true;
|
|
} else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
|
|
u64 valid_mask = 0;
|
|
|
|
for (i = 0; i < property->num_values; i++)
|
|
valid_mask |= (1ULL << property->values[i]);
|
|
return !(value & ~valid_mask);
|
|
} else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
|
|
struct drm_property_blob *blob;
|
|
|
|
if (value == 0)
|
|
return true;
|
|
|
|
blob = drm_property_lookup_blob(property->dev, value);
|
|
if (blob) {
|
|
*ref = &blob->base;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
|
|
/* a zero value for an object property translates to null: */
|
|
if (value == 0)
|
|
return true;
|
|
|
|
*ref = drm_mode_object_find(property->dev, NULL, value,
|
|
property->values[0]);
|
|
if (*ref)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < property->num_values; i++)
|
|
if (property->values[i] == value)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static void meson_property_change_valid_put(struct drm_property *property,
|
|
struct drm_mode_object *ref)
|
|
{
|
|
if (!ref)
|
|
return;
|
|
|
|
if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT))
|
|
drm_mode_object_put(ref);
|
|
else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB))
|
|
drm_property_blob_put(obj_to_blob(ref));
|
|
}
|
|
|
|
static int atomic_set_prop(struct drm_atomic_state *state,
|
|
struct drm_mode_object *obj, struct drm_file *file_priv,
|
|
struct drm_property *prop, u64 prop_value)
|
|
{
|
|
struct drm_mode_object *ref;
|
|
int ret;
|
|
|
|
if (!meson_property_change_valid_get(prop, prop_value, &ref))
|
|
return -EINVAL;
|
|
|
|
switch (obj->type) {
|
|
case DRM_MODE_OBJECT_PLANE: {
|
|
struct drm_plane *plane = obj_to_plane(obj);
|
|
struct drm_plane_state *plane_state;
|
|
|
|
plane_state = drm_atomic_get_plane_state(state, plane);
|
|
if (IS_ERR(plane_state)) {
|
|
ret = PTR_ERR(plane_state);
|
|
break;
|
|
}
|
|
|
|
ret = meson_atomic_plane_set_property(plane,
|
|
plane_state, file_priv, prop, prop_value);
|
|
break;
|
|
}
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
meson_property_change_valid_put(prop, ref);
|
|
return ret;
|
|
}
|
|
|
|
/*modified from drm_mode_atomic_ioctl() */
|
|
int meson_async_atomic_ioctl(struct drm_device *dev,
|
|
void *data, struct drm_file *file_priv)
|
|
{
|
|
struct drm_mode_atomic *arg = data;
|
|
u32 __user *objs_ptr = (u32 __user *)(unsigned long)(arg->objs_ptr);
|
|
u32 __user *count_props_ptr = (u32 __user *)(unsigned long)(arg->count_props_ptr);
|
|
u32 __user *props_ptr = (u32 __user *)(unsigned long)(arg->props_ptr);
|
|
u64 __user *prop_values_ptr = (u64 __user *)(unsigned long)(arg->prop_values_ptr);
|
|
unsigned int copied_objs, copied_props;
|
|
struct drm_atomic_state *state;
|
|
struct drm_modeset_acquire_ctx ctx;
|
|
struct drm_out_fence_state *fence_state;
|
|
int ret = 0;
|
|
unsigned int i, j, num_fences;
|
|
|
|
drm_modeset_acquire_init(&ctx, 0);
|
|
|
|
state = drm_atomic_state_alloc(dev);
|
|
if (!state)
|
|
return -ENOMEM;
|
|
|
|
state->allow_modeset = false;
|
|
state->acquire_ctx = &ctx;
|
|
|
|
retry:
|
|
copied_objs = 0;
|
|
copied_props = 0;
|
|
fence_state = NULL;
|
|
num_fences = 0;
|
|
|
|
if (arg->count_objs > 1) {
|
|
DRM_ERROR("only accept plane object update(%d).\n", arg->count_objs);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < arg->count_objs; i++) {
|
|
u32 obj_id, count_props;
|
|
struct drm_mode_object *obj;
|
|
|
|
if (get_user(obj_id, objs_ptr + copied_objs)) {
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
|
|
obj = drm_mode_object_find(dev, file_priv, obj_id, DRM_MODE_OBJECT_ANY);
|
|
if (!obj) {
|
|
ret = -ENOENT;
|
|
goto out;
|
|
}
|
|
|
|
if (obj->type != DRM_MODE_OBJECT_PLANE) {
|
|
DRM_ERROR("only accept plane object update, can not set (%d)\n", obj->type);
|
|
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (!obj->properties) {
|
|
drm_mode_object_put(obj);
|
|
ret = -ENOENT;
|
|
goto out;
|
|
}
|
|
|
|
if (get_user(count_props, count_props_ptr + copied_objs)) {
|
|
drm_mode_object_put(obj);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
|
|
copied_objs++;
|
|
|
|
for (j = 0; j < count_props; j++) {
|
|
u32 prop_id;
|
|
u64 prop_value;
|
|
struct drm_property *prop;
|
|
|
|
if (get_user(prop_id, props_ptr + copied_props)) {
|
|
drm_mode_object_put(obj);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
|
|
prop = meson_mode_obj_find_prop_id(obj, prop_id);
|
|
if (!prop) {
|
|
drm_mode_object_put(obj);
|
|
ret = -ENOENT;
|
|
goto out;
|
|
}
|
|
|
|
if (copy_from_user(&prop_value,
|
|
prop_values_ptr + copied_props,
|
|
sizeof(prop_value))) {
|
|
drm_mode_object_put(obj);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
|
|
ret = atomic_set_prop(state, obj, file_priv, prop, prop_value);
|
|
if (ret) {
|
|
drm_mode_object_put(obj);
|
|
goto out;
|
|
}
|
|
|
|
copied_props++;
|
|
}
|
|
|
|
drm_mode_object_put(obj);
|
|
}
|
|
|
|
state->legacy_cursor_update = true;
|
|
ret = drm_atomic_commit(state);
|
|
if (ret)
|
|
DRM_ERROR("%s failed .\n", __func__);
|
|
|
|
out:
|
|
if (ret == -EDEADLK) {
|
|
drm_atomic_state_clear(state);
|
|
drm_modeset_backoff(&ctx);
|
|
goto retry;
|
|
}
|
|
|
|
drm_atomic_state_put(state);
|
|
|
|
drm_modeset_drop_locks(&ctx);
|
|
drm_modeset_acquire_fini(&ctx);
|
|
return ret;
|
|
}
|