Commit f762bfda authored by Dave Airlie's avatar Dave Airlie

Merge tag 'sunxi-drm-fixes-for-4.7' of...

Merge tag 'sunxi-drm-fixes-for-4.7' of https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux into drm-fixes

Allwinner sun4i DRM driver fixes

A bunch of fixes that address:
  - Compilation errors in various corner cases
  - Move to helpers
  - Fix the pixel clock computation
  - Fix our panel probe

* tag 'sunxi-drm-fixes-for-4.7' of https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux:
  drm: sun4i: do cleanup if RGB output init fails
  drm/sun4i: Convert to connector register helpers
  drm/sun4i: remove simplefb at probe
  drm/sun4i: rgb: panel is an error pointer
  drm/sun4i: defer only if we didn't find our panel
  drm/sun4i: rgb: Validate the clock rate
  drm/sun4i: request exact rates to our parents
  drm: sun4i: fix probe error handling
  drm: sun4i: print DMA address correctly
  drm/sun4i: add COMMON_CLK dependency
parents c38e8016 13fef095
config DRM_SUN4I config DRM_SUN4I
tristate "DRM Support for Allwinner A10 Display Engine" tristate "DRM Support for Allwinner A10 Display Engine"
depends on DRM && ARM depends on DRM && ARM && COMMON_CLK
depends on ARCH_SUNXI || COMPILE_TEST depends on ARCH_SUNXI || COMPILE_TEST
select DRM_GEM_CMA_HELPER select DRM_GEM_CMA_HELPER
select DRM_KMS_HELPER select DRM_KMS_HELPER
......
...@@ -190,7 +190,7 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, ...@@ -190,7 +190,7 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
/* Get the physical address of the buffer in memory */ /* Get the physical address of the buffer in memory */
gem = drm_fb_cma_get_gem_obj(fb, 0); gem = drm_fb_cma_get_gem_obj(fb, 0);
DRM_DEBUG_DRIVER("Using GEM @ 0x%x\n", gem->paddr); DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
/* Compute the start of the displayed memory */ /* Compute the start of the displayed memory */
bpp = drm_format_plane_cpp(fb->pixel_format, 0); bpp = drm_format_plane_cpp(fb->pixel_format, 0);
...@@ -198,7 +198,7 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, ...@@ -198,7 +198,7 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
paddr += (state->src_x >> 16) * bpp; paddr += (state->src_x >> 16) * bpp;
paddr += (state->src_y >> 16) * fb->pitches[0]; paddr += (state->src_y >> 16) * fb->pitches[0];
DRM_DEBUG_DRIVER("Setting buffer address to 0x%x\n", paddr); DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
/* Write the 32 lower bits of the address (in bits) */ /* Write the 32 lower bits of the address (in bits) */
lo_paddr = paddr << 3; lo_paddr = paddr << 3;
......
...@@ -72,14 +72,40 @@ static unsigned long sun4i_dclk_recalc_rate(struct clk_hw *hw, ...@@ -72,14 +72,40 @@ static unsigned long sun4i_dclk_recalc_rate(struct clk_hw *hw,
static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate, static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate) unsigned long *parent_rate)
{ {
return *parent_rate / DIV_ROUND_CLOSEST(*parent_rate, rate); unsigned long best_parent = 0;
u8 best_div = 1;
int i;
for (i = 6; i < 127; i++) {
unsigned long ideal = rate * i;
unsigned long rounded;
rounded = clk_hw_round_rate(clk_hw_get_parent(hw),
ideal);
if (rounded == ideal) {
best_parent = rounded;
best_div = i;
goto out;
}
if ((rounded < ideal) && (rounded > best_parent)) {
best_parent = rounded;
best_div = i;
}
}
out:
*parent_rate = best_parent;
return best_parent / best_div;
} }
static int sun4i_dclk_set_rate(struct clk_hw *hw, unsigned long rate, static int sun4i_dclk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate) unsigned long parent_rate)
{ {
struct sun4i_dclk *dclk = hw_to_dclk(hw); struct sun4i_dclk *dclk = hw_to_dclk(hw);
int div = DIV_ROUND_CLOSEST(parent_rate, rate); u8 div = parent_rate / rate;
return regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG, return regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG,
GENMASK(6, 0), div); GENMASK(6, 0), div);
...@@ -127,10 +153,14 @@ int sun4i_dclk_create(struct device *dev, struct sun4i_tcon *tcon) ...@@ -127,10 +153,14 @@ int sun4i_dclk_create(struct device *dev, struct sun4i_tcon *tcon)
const char *clk_name, *parent_name; const char *clk_name, *parent_name;
struct clk_init_data init; struct clk_init_data init;
struct sun4i_dclk *dclk; struct sun4i_dclk *dclk;
int ret;
parent_name = __clk_get_name(tcon->sclk0); parent_name = __clk_get_name(tcon->sclk0);
of_property_read_string_index(dev->of_node, "clock-output-names", 0, ret = of_property_read_string_index(dev->of_node,
&clk_name); "clock-output-names", 0,
&clk_name);
if (ret)
return ret;
dclk = devm_kzalloc(dev, sizeof(*dclk), GFP_KERNEL); dclk = devm_kzalloc(dev, sizeof(*dclk), GFP_KERNEL);
if (!dclk) if (!dclk)
...@@ -140,6 +170,7 @@ int sun4i_dclk_create(struct device *dev, struct sun4i_tcon *tcon) ...@@ -140,6 +170,7 @@ int sun4i_dclk_create(struct device *dev, struct sun4i_tcon *tcon)
init.ops = &sun4i_dclk_ops; init.ops = &sun4i_dclk_ops;
init.parent_names = &parent_name; init.parent_names = &parent_name;
init.num_parents = 1; init.num_parents = 1;
init.flags = CLK_SET_RATE_PARENT;
dclk->regmap = tcon->regs; dclk->regmap = tcon->regs;
dclk->hw.init = &init; dclk->hw.init = &init;
......
...@@ -24,34 +24,6 @@ ...@@ -24,34 +24,6 @@
#include "sun4i_layer.h" #include "sun4i_layer.h"
#include "sun4i_tcon.h" #include "sun4i_tcon.h"
static int sun4i_drv_connector_plug_all(struct drm_device *drm)
{
struct drm_connector *connector, *failed;
int ret;
mutex_lock(&drm->mode_config.mutex);
list_for_each_entry(connector, &drm->mode_config.connector_list, head) {
ret = drm_connector_register(connector);
if (ret) {
failed = connector;
goto err;
}
}
mutex_unlock(&drm->mode_config.mutex);
return 0;
err:
list_for_each_entry(connector, &drm->mode_config.connector_list, head) {
if (failed == connector)
break;
drm_connector_unregister(connector);
}
mutex_unlock(&drm->mode_config.mutex);
return ret;
}
static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int pipe) static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int pipe)
{ {
struct sun4i_drv *drv = drm->dev_private; struct sun4i_drv *drv = drm->dev_private;
...@@ -125,6 +97,22 @@ static struct drm_driver sun4i_drv_driver = { ...@@ -125,6 +97,22 @@ static struct drm_driver sun4i_drv_driver = {
.disable_vblank = sun4i_drv_disable_vblank, .disable_vblank = sun4i_drv_disable_vblank,
}; };
static void sun4i_remove_framebuffers(void)
{
struct apertures_struct *ap;
ap = alloc_apertures(1);
if (!ap)
return;
/* The framebuffer can be located anywhere in RAM */
ap->ranges[0].base = 0;
ap->ranges[0].size = ~0;
remove_conflicting_framebuffers(ap, "sun4i-drm-fb", false);
kfree(ap);
}
static int sun4i_drv_bind(struct device *dev) static int sun4i_drv_bind(struct device *dev)
{ {
struct drm_device *drm; struct drm_device *drm;
...@@ -172,6 +160,9 @@ static int sun4i_drv_bind(struct device *dev) ...@@ -172,6 +160,9 @@ static int sun4i_drv_bind(struct device *dev)
} }
drm->irq_enabled = true; drm->irq_enabled = true;
/* Remove early framebuffers (ie. simplefb) */
sun4i_remove_framebuffers();
/* Create our framebuffer */ /* Create our framebuffer */
drv->fbdev = sun4i_framebuffer_init(drm); drv->fbdev = sun4i_framebuffer_init(drm);
if (IS_ERR(drv->fbdev)) { if (IS_ERR(drv->fbdev)) {
...@@ -187,7 +178,7 @@ static int sun4i_drv_bind(struct device *dev) ...@@ -187,7 +178,7 @@ static int sun4i_drv_bind(struct device *dev)
if (ret) if (ret)
goto free_drm; goto free_drm;
ret = sun4i_drv_connector_plug_all(drm); ret = drm_connector_register_all(drm);
if (ret) if (ret)
goto unregister_drm; goto unregister_drm;
...@@ -204,6 +195,7 @@ static void sun4i_drv_unbind(struct device *dev) ...@@ -204,6 +195,7 @@ static void sun4i_drv_unbind(struct device *dev)
{ {
struct drm_device *drm = dev_get_drvdata(dev); struct drm_device *drm = dev_get_drvdata(dev);
drm_connector_unregister_all(drm);
drm_dev_unregister(drm); drm_dev_unregister(drm);
drm_kms_helper_poll_fini(drm); drm_kms_helper_poll_fini(drm);
sun4i_framebuffer_free(drm); sun4i_framebuffer_free(drm);
......
...@@ -54,8 +54,13 @@ static int sun4i_rgb_get_modes(struct drm_connector *connector) ...@@ -54,8 +54,13 @@ static int sun4i_rgb_get_modes(struct drm_connector *connector)
static int sun4i_rgb_mode_valid(struct drm_connector *connector, static int sun4i_rgb_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode) struct drm_display_mode *mode)
{ {
struct sun4i_rgb *rgb = drm_connector_to_sun4i_rgb(connector);
struct sun4i_drv *drv = rgb->drv;
struct sun4i_tcon *tcon = drv->tcon;
u32 hsync = mode->hsync_end - mode->hsync_start; u32 hsync = mode->hsync_end - mode->hsync_start;
u32 vsync = mode->vsync_end - mode->vsync_start; u32 vsync = mode->vsync_end - mode->vsync_start;
unsigned long rate = mode->clock * 1000;
long rounded_rate;
DRM_DEBUG_DRIVER("Validating modes...\n"); DRM_DEBUG_DRIVER("Validating modes...\n");
...@@ -87,6 +92,15 @@ static int sun4i_rgb_mode_valid(struct drm_connector *connector, ...@@ -87,6 +92,15 @@ static int sun4i_rgb_mode_valid(struct drm_connector *connector,
DRM_DEBUG_DRIVER("Vertical parameters OK\n"); DRM_DEBUG_DRIVER("Vertical parameters OK\n");
rounded_rate = clk_round_rate(tcon->dclk, rate);
if (rounded_rate < rate)
return MODE_CLOCK_LOW;
if (rounded_rate > rate)
return MODE_CLOCK_HIGH;
DRM_DEBUG_DRIVER("Clock rate OK\n");
return MODE_OK; return MODE_OK;
} }
...@@ -203,7 +217,7 @@ int sun4i_rgb_init(struct drm_device *drm) ...@@ -203,7 +217,7 @@ int sun4i_rgb_init(struct drm_device *drm)
int ret; int ret;
/* If we don't have a panel, there's no point in going on */ /* If we don't have a panel, there's no point in going on */
if (!tcon->panel) if (IS_ERR(tcon->panel))
return -ENODEV; return -ENODEV;
rgb = devm_kzalloc(drm->dev, sizeof(*rgb), GFP_KERNEL); rgb = devm_kzalloc(drm->dev, sizeof(*rgb), GFP_KERNEL);
......
...@@ -425,11 +425,11 @@ static struct drm_panel *sun4i_tcon_find_panel(struct device_node *node) ...@@ -425,11 +425,11 @@ static struct drm_panel *sun4i_tcon_find_panel(struct device_node *node)
remote = of_graph_get_remote_port_parent(end_node); remote = of_graph_get_remote_port_parent(end_node);
if (!remote) { if (!remote) {
DRM_DEBUG_DRIVER("Enable to parse remote node\n"); DRM_DEBUG_DRIVER("Unable to parse remote node\n");
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
return of_drm_find_panel(remote); return of_drm_find_panel(remote) ?: ERR_PTR(-EPROBE_DEFER);
} }
static int sun4i_tcon_bind(struct device *dev, struct device *master, static int sun4i_tcon_bind(struct device *dev, struct device *master,
...@@ -490,7 +490,11 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, ...@@ -490,7 +490,11 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
return 0; return 0;
} }
return sun4i_rgb_init(drm); ret = sun4i_rgb_init(drm);
if (ret < 0)
goto err_free_clocks;
return 0;
err_free_clocks: err_free_clocks:
sun4i_tcon_free_clocks(tcon); sun4i_tcon_free_clocks(tcon);
...@@ -522,12 +526,13 @@ static int sun4i_tcon_probe(struct platform_device *pdev) ...@@ -522,12 +526,13 @@ static int sun4i_tcon_probe(struct platform_device *pdev)
* Defer the probe. * Defer the probe.
*/ */
panel = sun4i_tcon_find_panel(node); panel = sun4i_tcon_find_panel(node);
if (IS_ERR(panel)) {
/* /*
* If we don't have a panel endpoint, just go on * If we don't have a panel endpoint, just go on
*/ */
if (PTR_ERR(panel) != -ENODEV) if (PTR_ERR(panel) == -EPROBE_DEFER) {
return -EPROBE_DEFER; DRM_DEBUG_DRIVER("Still waiting for our panel. Deferring...\n");
return -EPROBE_DEFER;
} }
return component_add(&pdev->dev, &sun4i_tcon_ops); return component_add(&pdev->dev, &sun4i_tcon_ops);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment