#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_graph.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/component.h>
#include <linux/fence.h>
#include <linux/console.h>
#include "rockchip_drm_fb.h"
#include "rockchip_drm_fbdev.h"
#include "rockchip_drm_gem.h"
-#include "rockchip_drm_rga.h"
#define DRIVER_NAME "rockchip"
#define DRIVER_DESC "RockChip Soc DRM"
return NULL;
}
+static
+struct drm_connector *find_connector_by_bridge(struct drm_device *drm_dev,
+ struct device_node *node)
+{
+ struct device_node *np_encoder, *np_connector;
+ struct drm_encoder *encoder;
+ struct drm_connector *connector = NULL;
+ struct device_node *port, *endpoint;
+ bool encoder_bridge = false;
+ bool found_connector = false;
+
+ np_encoder = of_graph_get_remote_port_parent(node);
+ if (!np_encoder || !of_device_is_available(np_encoder))
+ goto err_put_encoder;
+ drm_for_each_encoder(encoder, drm_dev) {
+ if (encoder->port == np_encoder && encoder->bridge) {
+ encoder_bridge = true;
+ break;
+ }
+ }
+ if (!encoder_bridge) {
+ dev_err(drm_dev->dev, "can't found encoder bridge!\n");
+ goto err_put_encoder;
+ }
+ port = of_graph_get_port_by_id(np_encoder, 1);
+ if (!port) {
+ dev_err(drm_dev->dev, "can't found port point!\n");
+ goto err_put_encoder;
+ }
+ for_each_child_of_node(port, endpoint) {
+ np_connector = of_graph_get_remote_port_parent(endpoint);
+ if (!np_connector) {
+ dev_err(drm_dev->dev,
+ "can't found connector node, please init!\n");
+ goto err_put_port;
+ }
+ if (!of_device_is_available(np_connector)) {
+ of_node_put(np_connector);
+ np_connector = NULL;
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (!np_connector) {
+ dev_err(drm_dev->dev, "can't found available connector node!\n");
+ goto err_put_port;
+ }
+
+ drm_for_each_connector(connector, drm_dev) {
+ if (connector->port == np_connector) {
+ found_connector = true;
+ break;
+ }
+ }
+
+ if (!found_connector)
+ connector = NULL;
+
+ of_node_put(np_connector);
+err_put_port:
+ of_node_put(port);
+err_put_encoder:
+ of_node_put(np_encoder);
+
+ return connector;
+}
+
void rockchip_free_loader_memory(struct drm_device *drm)
{
struct rockchip_drm_private *private = drm->dev_private;
}
bpp = val;
- mode_cmd.pitches[0] = mode_cmd.width * bpp / 8;
+ mode_cmd.pitches[0] = ALIGN(mode_cmd.width * bpp, 32) / 8;
switch (bpp) {
case 16:
mode_cmd.pixel_format = DRM_FORMAT_BGR888;
break;
case 32:
- mode_cmd.pixel_format = DRM_FORMAT_XBGR8888;
+ mode_cmd.pixel_format = DRM_FORMAT_XRGB8888;
break;
default:
pr_err("%s: unsupport to logo bpp %d\n", __func__, bpp);
crtc = find_crtc_by_node(drm_dev, connect);
connector = find_connector_by_node(drm_dev, connect);
+ if (!connector)
+ connector = find_connector_by_bridge(drm_dev, connect);
if (!crtc || !connector) {
dev_warn(drm_dev->dev,
"No available crtc or connector for display");
struct drm_plane_state *primary_state;
struct drm_display_mode *mode = NULL;
const struct drm_connector_helper_funcs *funcs;
+ const struct drm_encoder_helper_funcs *encoder_funcs;
bool is_crtc_enabled = true;
int hdisplay, vdisplay;
int fb_width, fb_height;
if (funcs->loader_protect)
funcs->loader_protect(connector, true);
connector->loader_protect = true;
+ encoder_funcs = conn_state->best_encoder->helper_private;
+ if (encoder_funcs->loader_protect)
+ encoder_funcs->loader_protect(conn_state->best_encoder, true);
+ conn_state->best_encoder->loader_protect = true;
num_modes = connector->funcs->fill_modes(connector, 4096, 4096);
if (!num_modes) {
dev_err(drm_dev->dev, "connector[%s] can't found any modes\n",
if (funcs->loader_protect)
funcs->loader_protect(connector, false);
connector->loader_protect = false;
+ if (encoder_funcs->loader_protect)
+ encoder_funcs->loader_protect(conn_state->best_encoder, false);
+ conn_state->best_encoder->loader_protect = false;
return ret;
}
struct rockchip_drm_mode_set *set,
unsigned int *plane_mask)
{
- struct drm_mode_config *mode_config = &drm_dev->mode_config;
+ struct rockchip_drm_private *priv = drm_dev->dev_private;
struct drm_crtc *crtc = set->crtc;
struct drm_connector *connector = set->connector;
struct drm_display_mode *mode = set->mode;
} else {
const struct drm_encoder_helper_funcs *encoder_helper_funcs;
const struct drm_connector_helper_funcs *connector_helper_funcs;
- struct rockchip_drm_private *priv = drm_dev->dev_private;
struct drm_encoder *encoder;
int pipe = drm_crtc_index(crtc);
* some vop maybe not support ymirror, but force use it now.
*/
drm_atomic_plane_set_property(crtc->primary, primary_state,
- mode_config->rotation_property,
- BIT(DRM_REFLECT_Y));
+ priv->logo_ymirror_prop,
+ true);
return ret;
}
static void show_loader_logo(struct drm_device *drm_dev)
{
- struct drm_atomic_state *state;
+ struct drm_atomic_state *state, *old_state;
struct device_node *np = drm_dev->dev->of_node;
struct drm_mode_config *mode_config = &drm_dev->mode_config;
struct device_node *root, *route;
goto err_free_state;
}
+ old_state = drm_atomic_helper_duplicate_state(drm_dev,
+ mode_config->acquire_ctx);
+ if (IS_ERR(old_state)) {
+ dev_err(drm_dev->dev, "failed to duplicate atomic state\n");
+ ret = PTR_ERR_OR_ZERO(old_state);
+ goto err_free_state;
+ }
+
/*
* The state save initial devices status, swap the state into
* drm deivces as old state, so if new state come, can compare
if (IS_ERR(state)) {
dev_err(drm_dev->dev, "failed to duplicate atomic state\n");
ret = PTR_ERR_OR_ZERO(state);
- goto err_unlock;
+ goto err_free_old_state;
}
state->acquire_ctx = mode_config->acquire_ctx;
list_for_each_entry(set, &mode_set_list, head)
drm_atomic_clean_old_fb(drm_dev, plane_mask, ret);
list_for_each_entry_safe(set, tmp, &mode_set_list, head) {
- struct drm_crtc *crtc = set->crtc;
-
list_del(&set->head);
kfree(set);
-
- /* FIXME:
- * primary plane state rotation is not BIT(0), but we only want
- * it effect on logo display, userspace may not known to clean
- * this property, would get unexpect display, so force set
- * primary rotation to BIT(0).
- */
- if (!crtc->primary || !crtc->primary->state)
- continue;
-
- drm_atomic_plane_set_property(crtc->primary,
- crtc->primary->state,
- mode_config->rotation_property,
- BIT(0));
}
/*
*/
WARN_ON(ret == -EDEADLK);
- if (ret)
- goto err_free_state;
+ if (ret) {
+ /*
+ * restore display status if atomic commit failed.
+ */
+ drm_atomic_helper_swap_state(drm_dev, old_state);
+ goto err_free_old_state;
+ }
rockchip_free_loader_memory(drm_dev);
+ drm_atomic_state_free(old_state);
drm_modeset_unlock_all(drm_dev);
return;
+err_free_old_state:
+ drm_atomic_state_free(old_state);
err_free_state:
drm_atomic_state_free(state);
err_unlock:
if (ret)
dev_err(drm_dev->dev, "failed to show loader logo\n");
}
+
+static const char *const loader_protect_clocks[] __initconst = {
+ "hclk_vio",
+ "aclk_vio",
+ "aclk_vio0",
+};
+
+static struct clk **loader_clocks __initdata;
+static int __init rockchip_clocks_loader_protect(void)
+{
+ int nclocks = ARRAY_SIZE(loader_protect_clocks);
+ struct clk *clk;
+ int i;
+
+ loader_clocks = kcalloc(nclocks, sizeof(void *), GFP_KERNEL);
+ if (!loader_clocks)
+ return -ENOMEM;
+
+ for (i = 0; i < nclocks; i++) {
+ clk = __clk_lookup(loader_protect_clocks[i]);
+
+ if (clk) {
+ loader_clocks[i] = clk;
+ clk_prepare_enable(clk);
+ }
+ }
+
+ return 0;
+}
+fs_initcall(rockchip_clocks_loader_protect);
+
+static int __init rockchip_clocks_loader_unprotect(void)
+{
+ int i;
+
+ if (!loader_clocks)
+ return -ENODEV;
+
+ for (i = 0; i < ARRAY_SIZE(loader_protect_clocks); i++) {
+ struct clk *clk = loader_clocks[i];
+
+ if (clk)
+ clk_disable_unprepare(clk);
+ }
+ kfree(loader_clocks);
+
+ return 0;
+}
+late_initcall_sync(rockchip_clocks_loader_unprotect);
#endif
/*
priv->crtc_funcs[pipe]->disable_vblank(crtc);
}
+static int rockchip_drm_fault_handler(struct iommu_domain *iommu,
+ struct device *dev,
+ unsigned long iova, int flags, void *arg)
+{
+ struct drm_device *drm_dev = arg;
+ struct rockchip_drm_private *priv = drm_dev->dev_private;
+ struct drm_crtc *crtc;
+
+ drm_for_each_crtc(crtc, drm_dev) {
+ int pipe = drm_crtc_index(crtc);
+
+ if (priv->crtc_funcs[pipe] &&
+ priv->crtc_funcs[pipe]->regs_dump)
+ priv->crtc_funcs[pipe]->regs_dump(crtc, NULL);
+
+ if (priv->crtc_funcs[pipe] &&
+ priv->crtc_funcs[pipe]->debugfs_dump)
+ priv->crtc_funcs[pipe]->debugfs_dump(crtc, NULL);
+ }
+
+ return 0;
+}
+
static int rockchip_drm_init_iommu(struct drm_device *drm_dev)
{
struct rockchip_drm_private *private = drm_dev->dev_private;
drm_mm_init(&private->mm, start, end - start + 1);
mutex_init(&private->mm_lock);
+ iommu_set_fault_handler(private->domain, rockchip_drm_fault_handler,
+ drm_dev);
+
return 0;
}
struct rockchip_drm_private *priv = drm_dev->dev_private;
int ret;
+ if (!priv->domain)
+ return 0;
+
mutex_lock(&priv->mm_lock);
ret = drm_mm_dump_table(s, &priv->mm);
}
#endif
+static int rockchip_drm_create_properties(struct drm_device *dev)
+{
+ struct drm_property *prop;
+ struct rockchip_drm_private *private = dev->dev_private;
+ const struct drm_prop_enum_list cabc_mode_enum_list[] = {
+ { ROCKCHIP_DRM_CABC_MODE_DISABLE, "Disable" },
+ { ROCKCHIP_DRM_CABC_MODE_NORMAL, "Normal" },
+ { ROCKCHIP_DRM_CABC_MODE_LOWPOWER, "LowPower" },
+ { ROCKCHIP_DRM_CABC_MODE_USERSPACE, "Userspace" },
+ };
+
+ prop = drm_property_create_enum(dev, 0, "CABC_MODE", cabc_mode_enum_list,
+ ARRAY_SIZE(cabc_mode_enum_list));
+
+ private->cabc_mode_property = prop;
+
+ prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CABC_LUT", 0);
+ if (!prop)
+ return -ENOMEM;
+ private->cabc_lut_property = prop;
+
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+ "CABC_STAGE_UP", 0, 512);
+ if (!prop)
+ return -ENOMEM;
+ private->cabc_stage_up_property = prop;
+
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+ "CABC_STAGE_DOWN", 0, 255);
+ if (!prop)
+ return -ENOMEM;
+ private->cabc_stage_down_property = prop;
+
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+ "CABC_GLOBAL_DN", 0, 255);
+ if (!prop)
+ return -ENOMEM;
+ private->cabc_global_dn_property = prop;
+
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+ "CABC_CALC_PIXEL_NUM", 0, 1000);
+ if (!prop)
+ return -ENOMEM;
+ private->cabc_calc_pixel_num_property = prop;
+
+ return 0;
+}
+
static int rockchip_drm_bind(struct device *dev)
{
struct drm_device *drm_dev;
drm_dev->dev_private = private;
+ private->hdmi_pll.pll = devm_clk_get(dev, "hdmi-tmds-pll");
+ if (PTR_ERR(private->hdmi_pll.pll) == -ENOENT) {
+ private->hdmi_pll.pll = NULL;
+ } else if (PTR_ERR(private->hdmi_pll.pll) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto err_free;
+ } else if (IS_ERR(private->hdmi_pll.pll)) {
+ dev_err(dev, "failed to get hdmi-tmds-pll\n");
+ ret = PTR_ERR(private->hdmi_pll.pll);
+ goto err_free;
+ }
+
+ private->default_pll.pll = devm_clk_get(dev, "default-vop-pll");
+ if (PTR_ERR(private->default_pll.pll) == -ENOENT) {
+ private->default_pll.pll = NULL;
+ } else if (PTR_ERR(private->default_pll.pll) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto err_free;
+ } else if (IS_ERR(private->default_pll.pll)) {
+ dev_err(dev, "failed to get default vop pll\n");
+ ret = PTR_ERR(private->default_pll.pll);
+ goto err_free;
+ }
+
#ifdef CONFIG_DRM_DMA_SYNC
private->cpu_fence_context = fence_context_alloc(1);
atomic_set(&private->cpu_fence_seqno, 0);
drm_mode_config_init(drm_dev);
rockchip_drm_mode_config_init(drm_dev);
+ rockchip_drm_create_properties(drm_dev);
ret = rockchip_drm_init_iommu(drm_dev);
if (ret)
DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CPU_RELEASE,
rockchip_gem_cpu_release_ioctl,
DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
- DRM_IOCTL_DEF_DRV(ROCKCHIP_RGA_GET_VER, rockchip_rga_get_ver_ioctl,
- DRM_AUTH | DRM_RENDER_ALLOW),
- DRM_IOCTL_DEF_DRV(ROCKCHIP_RGA_SET_CMDLIST,
- rockchip_rga_set_cmdlist_ioctl,
- DRM_AUTH | DRM_RENDER_ALLOW),
- DRM_IOCTL_DEF_DRV(ROCKCHIP_RGA_EXEC, rockchip_rga_exec_ioctl,
- DRM_AUTH | DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_GET_PHYS, rockchip_gem_get_phys_ioctl,
+ DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
};
static const struct file_operations rockchip_drm_driver_fops = {
.gem_prime_vmap = rockchip_gem_prime_vmap,
.gem_prime_vunmap = rockchip_gem_prime_vunmap,
.gem_prime_mmap = rockchip_gem_mmap_buf,
+ .gem_prime_begin_cpu_access = rockchip_gem_prime_begin_cpu_access,
+ .gem_prime_end_cpu_access = rockchip_gem_prime_end_cpu_access,
#ifdef CONFIG_DEBUG_FS
.debugfs_init = rockchip_drm_debugfs_init,
.debugfs_cleanup = rockchip_drm_debugfs_cleanup,
of_node_put(port);
}
+ port = of_parse_phandle(np, "backlight", 0);
+ if (port && of_device_is_available(port)) {
+ component_match_add(dev, &match, compare_of, port);
+ of_node_put(port);
+ }
+
return component_master_add_with_match(dev, &rockchip_drm_ops, match);
}