mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-11-01 17:08:10 +00:00
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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJXZ7GFAAoJEBx+YmzsjxAg/MYP/RA8zMBxyhGQzrdlVrem5cpQ tJR4qJ8yKX+5gT8qZ3Yixx2ubGyNHkXGnj2Bt2YuSuuli9mf6SSHjEaex10aElZe ULPy3FHIvtce7EZe7TuC9gbC79lf+YvYkwB/7OjLk7EjWPd9vaBxWDiafsajVXlA nEvtNnbg4fOGf6xSvHeMT72YaPnLBPGwYz78cby0IW/4RA+X5FvMgEq008iCjN5U vwEuhnyUIXkisC5I/3HFUU+lOt8LphVRaPKlLQXb29ysNou6wq20jMTYCWn+kYWr SS04qYfwxgzirS0eK0zO0ajS0GHxMfEQ1a6qbuRNHooIgwky3ILkS94X/ssnHFh8 TDqnBRuyKmEwu7EbQ4uIKJ8swLHa5AWg2hVN9Na1I+iioz9hh4yCLcXhOPWcHsUO fYsdYsGQBOHFKfAYVVno2gre9ek9DdkGN6Y6t7Vq6HzSthcAsjxmUYcPates8bhz kGGXlM92uzD6M8YkfJUihTsKqiuvP1DbDeW71aZU0zfvWiLjLpQGb0hbfnHUMSCj 9rihAJhRu6OX4EINkzmccqBe3n6bvJW4DrFfxKQRoiXo0sGwztsEOOWr+9Lmpq2T 2t2Fet6BGd8lG4hTZ3EaDGT4a/wEwwgFPiNuia1jH3AWLq7qfxuyGOPJTHImY6cL ubYUz1mNCHy7903nN48G =NkG1 -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
f762bfda2b
6 changed files with 88 additions and 46 deletions
|
@ -1,6 +1,6 @@
|
|||
config DRM_SUN4I
|
||||
tristate "DRM Support for Allwinner A10 Display Engine"
|
||||
depends on DRM && ARM
|
||||
depends on DRM && ARM && COMMON_CLK
|
||||
depends on ARCH_SUNXI || COMPILE_TEST
|
||||
select DRM_GEM_CMA_HELPER
|
||||
select DRM_KMS_HELPER
|
||||
|
|
|
@ -190,7 +190,7 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
|
|||
/* Get the physical address of the buffer in memory */
|
||||
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 */
|
||||
bpp = drm_format_plane_cpp(fb->pixel_format, 0);
|
||||
|
@ -198,7 +198,7 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
|
|||
paddr += (state->src_x >> 16) * bpp;
|
||||
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) */
|
||||
lo_paddr = paddr << 3;
|
||||
|
|
|
@ -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,
|
||||
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,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
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,
|
||||
GENMASK(6, 0), div);
|
||||
|
@ -127,10 +153,14 @@ int sun4i_dclk_create(struct device *dev, struct sun4i_tcon *tcon)
|
|||
const char *clk_name, *parent_name;
|
||||
struct clk_init_data init;
|
||||
struct sun4i_dclk *dclk;
|
||||
int ret;
|
||||
|
||||
parent_name = __clk_get_name(tcon->sclk0);
|
||||
of_property_read_string_index(dev->of_node, "clock-output-names", 0,
|
||||
&clk_name);
|
||||
ret = of_property_read_string_index(dev->of_node,
|
||||
"clock-output-names", 0,
|
||||
&clk_name);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dclk = devm_kzalloc(dev, sizeof(*dclk), GFP_KERNEL);
|
||||
if (!dclk)
|
||||
|
@ -140,6 +170,7 @@ int sun4i_dclk_create(struct device *dev, struct sun4i_tcon *tcon)
|
|||
init.ops = &sun4i_dclk_ops;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
init.flags = CLK_SET_RATE_PARENT;
|
||||
|
||||
dclk->regmap = tcon->regs;
|
||||
dclk->hw.init = &init;
|
||||
|
|
|
@ -24,34 +24,6 @@
|
|||
#include "sun4i_layer.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)
|
||||
{
|
||||
struct sun4i_drv *drv = drm->dev_private;
|
||||
|
@ -125,6 +97,22 @@ static struct drm_driver sun4i_drv_driver = {
|
|||
.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)
|
||||
{
|
||||
struct drm_device *drm;
|
||||
|
@ -172,6 +160,9 @@ static int sun4i_drv_bind(struct device *dev)
|
|||
}
|
||||
drm->irq_enabled = true;
|
||||
|
||||
/* Remove early framebuffers (ie. simplefb) */
|
||||
sun4i_remove_framebuffers();
|
||||
|
||||
/* Create our framebuffer */
|
||||
drv->fbdev = sun4i_framebuffer_init(drm);
|
||||
if (IS_ERR(drv->fbdev)) {
|
||||
|
@ -187,7 +178,7 @@ static int sun4i_drv_bind(struct device *dev)
|
|||
if (ret)
|
||||
goto free_drm;
|
||||
|
||||
ret = sun4i_drv_connector_plug_all(drm);
|
||||
ret = drm_connector_register_all(drm);
|
||||
if (ret)
|
||||
goto unregister_drm;
|
||||
|
||||
|
@ -204,6 +195,7 @@ static void sun4i_drv_unbind(struct device *dev)
|
|||
{
|
||||
struct drm_device *drm = dev_get_drvdata(dev);
|
||||
|
||||
drm_connector_unregister_all(drm);
|
||||
drm_dev_unregister(drm);
|
||||
drm_kms_helper_poll_fini(drm);
|
||||
sun4i_framebuffer_free(drm);
|
||||
|
|
|
@ -54,8 +54,13 @@ static int sun4i_rgb_get_modes(struct drm_connector *connector)
|
|||
static int sun4i_rgb_mode_valid(struct drm_connector *connector,
|
||||
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 vsync = mode->vsync_end - mode->vsync_start;
|
||||
unsigned long rate = mode->clock * 1000;
|
||||
long rounded_rate;
|
||||
|
||||
DRM_DEBUG_DRIVER("Validating modes...\n");
|
||||
|
||||
|
@ -87,6 +92,15 @@ static int sun4i_rgb_mode_valid(struct drm_connector *connector,
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -203,7 +217,7 @@ int sun4i_rgb_init(struct drm_device *drm)
|
|||
int ret;
|
||||
|
||||
/* If we don't have a panel, there's no point in going on */
|
||||
if (!tcon->panel)
|
||||
if (IS_ERR(tcon->panel))
|
||||
return -ENODEV;
|
||||
|
||||
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)
|
|||
|
||||
remote = of_graph_get_remote_port_parent(end_node);
|
||||
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 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,
|
||||
|
@ -490,7 +490,11 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
|
|||
return 0;
|
||||
}
|
||||
|
||||
return sun4i_rgb_init(drm);
|
||||
ret = sun4i_rgb_init(drm);
|
||||
if (ret < 0)
|
||||
goto err_free_clocks;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_clocks:
|
||||
sun4i_tcon_free_clocks(tcon);
|
||||
|
@ -522,12 +526,13 @@ static int sun4i_tcon_probe(struct platform_device *pdev)
|
|||
* Defer the probe.
|
||||
*/
|
||||
panel = sun4i_tcon_find_panel(node);
|
||||
if (IS_ERR(panel)) {
|
||||
/*
|
||||
* If we don't have a panel endpoint, just go on
|
||||
*/
|
||||
if (PTR_ERR(panel) != -ENODEV)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
/*
|
||||
* If we don't have a panel endpoint, just go on
|
||||
*/
|
||||
if (PTR_ERR(panel) == -EPROBE_DEFER) {
|
||||
DRM_DEBUG_DRIVER("Still waiting for our panel. Deferring...\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
return component_add(&pdev->dev, &sun4i_tcon_ops);
|
||||
|
|
Loading…
Reference in a new issue