Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

drm/atmel-hlcdc: use drm component API to access tda998x driver #29

Open
wants to merge 1 commit into
base: linux-4.4-at91
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 57 additions & 19 deletions drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ struct atmel_hlcdc_crtc {
struct drm_pending_vblank_event *event;
int id;
bool enabled;
bool simulate_vesa_sync;
bool invert_pixel_clock;
};

static inline struct atmel_hlcdc_crtc *
Expand Down Expand Up @@ -105,25 +107,6 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)

cfg = 0;

prate = clk_get_rate(crtc->dc->hlcdc->sys_clk);
mode_rate = adj->crtc_clock * 1000;
if ((prate / 2) < mode_rate) {
prate *= 2;
cfg |= ATMEL_HLCDC_CLKSEL;
}

div = DIV_ROUND_UP(prate, mode_rate);
if (div < 2)
div = 2;

cfg |= ATMEL_HLCDC_CLKDIV(div);

regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0),
ATMEL_HLCDC_CLKSEL | ATMEL_HLCDC_CLKDIV_MASK |
ATMEL_HLCDC_CLKPOL, cfg);

cfg = 0;

if (adj->flags & DRM_MODE_FLAG_NVSYNC)
cfg |= ATMEL_HLCDC_VSPOL;

Expand All @@ -140,6 +123,32 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)
ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO |
ATMEL_HLCDC_GUARDTIME_MASK | ATMEL_HLCDC_MODE_MASK,
cfg);

cfg = 0;

prate = clk_get_rate(crtc->dc->hlcdc->sys_clk);
mode_rate = adj->crtc_clock * 1000;

/* always use 2x system clock to reduce rounding error */
prate *= 2;
cfg |= ATMEL_HLCDC_CLKSEL;

div = DIV_ROUND_CLOSEST(prate, mode_rate);
if (div < 2)
div = 2;

dev_info(c->dev->dev, "pixel clock: %d rounded to %d kHz\n", adj->crtc_clock, (int)(prate/div/1000));

cfg |= ATMEL_HLCDC_CLKDIV(div);

if (crtc->invert_pixel_clock)
{
cfg |= ATMEL_HLCDC_CLKPOL;
}

regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0),
ATMEL_HLCDC_CLKSEL | ATMEL_HLCDC_CLKDIV_MASK |
ATMEL_HLCDC_CLKPOL, cfg);
}

static bool atmel_hlcdc_crtc_mode_fixup(struct drm_crtc *c,
Expand All @@ -148,6 +157,24 @@ static bool atmel_hlcdc_crtc_mode_fixup(struct drm_crtc *c,
{
struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);

if (crtc->simulate_vesa_sync) {
/*
* hlcdc does not generate VESA-compliant sync but aligns
* VS on the second edge of HS instead of first edge.
* We use adjusted_mode, to fixup sync by aligning both rising
* edges and add HSKEW offset to fix the sync.
*/
adjusted_mode->hskew = mode->hsync_end - mode->hsync_start;
adjusted_mode->flags |= DRM_MODE_FLAG_HSKEW;

if (mode->flags & DRM_MODE_FLAG_NHSYNC) {
adjusted_mode->flags |= DRM_MODE_FLAG_PHSYNC;
adjusted_mode->flags &= ~DRM_MODE_FLAG_NHSYNC;
} else {
adjusted_mode->flags |= DRM_MODE_FLAG_NHSYNC;
adjusted_mode->flags &= ~DRM_MODE_FLAG_PHSYNC;
}
}
return atmel_hlcdc_dc_mode_valid(crtc->dc, adjusted_mode) == MODE_OK;
}

Expand Down Expand Up @@ -503,3 +530,14 @@ int atmel_hlcdc_crtc_create(struct drm_device *dev)
return ret;
}

void atmel_hlcdc_crtc_set_simulate_vesa_sync(struct drm_crtc *c, bool enabled)
{
struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
crtc->simulate_vesa_sync = enabled;
}

void atmel_hlcdc_crtc_set_invert_pixel_clock(struct drm_crtc *c, bool enabled)
{
struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
crtc->invert_pixel_clock = enabled;
}
113 changes: 94 additions & 19 deletions drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
Original file line number Diff line number Diff line change
Expand Up @@ -550,10 +550,12 @@ static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)

drm_mode_config_init(dev);

ret = atmel_hlcdc_create_outputs(dev);
if (ret) {
dev_err(dev->dev, "failed to create HLCDC outputs: %d\n", ret);
return ret;
if (!dc->is_componentized) {
ret = atmel_hlcdc_create_outputs(dev);
if (ret) {
dev_err(dev->dev, "failed to create HLCDC outputs: %d\n", ret);
return ret;
}
}

planes = atmel_hlcdc_create_planes(dev);
Expand Down Expand Up @@ -595,6 +597,7 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev)
struct platform_device *pdev = to_platform_device(dev->dev);
const struct of_device_id *match;
struct atmel_hlcdc_dc *dc;
struct drm_crtc *crtc;
int ret;

match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node);
Expand All @@ -619,6 +622,7 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev)
init_waitqueue_head(&dc->commit.wait);
dc->desc = match->data;
dc->hlcdc = dev_get_drvdata(dev->dev->parent);
dc->is_componentized = atmel_hlcdc_get_external_components(dev->dev, NULL) > 0;
dev->dev_private = dc;

ret = clk_prepare_enable(dc->hlcdc->periph_clk);
Expand All @@ -641,6 +645,26 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev)
goto err_periph_clk_disable;
}

if (dc->is_componentized) {
ret = component_bind_all(dev->dev, dev);
if (ret < 0)
{
dev_err(dev->dev, "failed to bind components: %d\n", ret);
goto err_periph_clk_disable;
}
}

if (of_property_read_bool(dev->dev->of_node, "simulate_vesa_sync")) {
/* enable simulate_vesa_sync */
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
atmel_hlcdc_crtc_set_simulate_vesa_sync(crtc, true);
}
if (of_property_read_bool(dev->dev->of_node, "invert_pixel_clock")) {
/* set clock_polarity */
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
atmel_hlcdc_crtc_set_invert_pixel_clock(crtc, true);
}

drm_mode_config_reset(dev);

pm_runtime_get_sync(dev->dev);
Expand Down Expand Up @@ -678,6 +702,8 @@ static void atmel_hlcdc_dc_unload(struct drm_device *dev)
drm_fbdev_cma_fini(dc->fbdev);
flush_workqueue(dc->wq);
drm_kms_helper_poll_fini(dev);
if (dc->is_componentized)
component_unbind_all(dev->dev, dev);
drm_mode_config_cleanup(dev);
drm_vblank_cleanup(dev);

Expand Down Expand Up @@ -874,56 +900,61 @@ static struct drm_driver atmel_hlcdc_dc_driver = {
.minor = 0,
};

static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev)
static int atmel_hlcdc_dc_drm_bind(struct device *dev)
{
struct drm_device *ddev;
struct atmel_hlcdc_dc *dc;
int ret;

ddev = drm_dev_alloc(&atmel_hlcdc_dc_driver, &pdev->dev);
ddev = drm_dev_alloc(&atmel_hlcdc_dc_driver, dev);
if (!ddev)
return -ENOMEM;

ret = drm_dev_set_unique(ddev, dev_name(ddev->dev));
if (ret)
goto err_unref;

ret = atmel_hlcdc_dc_load(ddev);
if (ret)
goto err_unref;

ret = drm_dev_register(ddev, 0);
if (ret)
goto err_unload;
goto err_unref;

ret = atmel_hlcdc_dc_connector_plug_all(ddev);
ret = atmel_hlcdc_dc_load(ddev);
if (ret)
goto err_unregister;

dc = ddev->dev_private;
if (!dc->is_componentized) {
ret = atmel_hlcdc_dc_connector_plug_all(ddev);
if (ret)
goto err_unload;
}

dev_info(ddev->dev, "DRM device successfully registered\n");
return 0;

err_unload:
atmel_hlcdc_dc_unload(ddev);

err_unregister:
drm_dev_unregister(ddev);

err_unload:
atmel_hlcdc_dc_unload(ddev);

err_unref:
drm_dev_unref(ddev);

return ret;
}

static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev)
static void atmel_hlcdc_dc_drm_unbind(struct device *dev)
{
struct drm_device *ddev = platform_get_drvdata(pdev);
struct drm_device *ddev = dev_get_drvdata(dev);
struct atmel_hlcdc_dc *dc = ddev->dev_private;

atmel_hlcdc_dc_connector_unplug_all(ddev);
if (!dc->is_componentized)
atmel_hlcdc_dc_connector_unplug_all(ddev);
drm_dev_unregister(ddev);
atmel_hlcdc_dc_unload(ddev);
drm_dev_unref(ddev);

return 0;
}

#ifdef CONFIG_PM_SLEEP
Expand Down Expand Up @@ -958,6 +989,50 @@ static int atmel_hlcdc_dc_drm_resume(struct device *dev)
}
#endif

static const struct component_master_ops hlcdc_comp_ops = {
.bind = atmel_hlcdc_dc_drm_bind,
.unbind = atmel_hlcdc_dc_drm_unbind,
};

static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct component_match *match = NULL;
int ret;

/* bail out early if no DT data: */
if (!dev->of_node) {
dev_err(dev, "device-tree data is missing\n");
return -ENXIO;
}

/* find components, if none found proceed to bind */
ret = atmel_hlcdc_get_external_components(dev, &match);
if (ret < 0)
return ret;
else if (ret == 0)
return atmel_hlcdc_dc_drm_bind(dev);
else
return component_master_add_with_match(dev, &hlcdc_comp_ops, match);
}

static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev)
{
struct drm_device *ddev = dev_get_drvdata(&pdev->dev);
struct atmel_hlcdc_dc *dc = ddev->dev_private;

/* Check if a subcomponent has already triggered the unloading. */
if (!dc)
return 0;

if (dc->is_componentized)
component_master_del(&pdev->dev, &hlcdc_comp_ops);
else
drm_put_dev(platform_get_drvdata(pdev));

return 0;
}

static SIMPLE_DEV_PM_OPS(atmel_hlcdc_dc_drm_pm_ops,
atmel_hlcdc_dc_drm_suspend, atmel_hlcdc_dc_drm_resume);

Expand Down
5 changes: 5 additions & 0 deletions drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <linux/clk.h>
#include <linux/irqdomain.h>
#include <linux/pwm.h>
#include <linux/component.h>

#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
Expand Down Expand Up @@ -151,6 +152,7 @@ struct atmel_hlcdc_dc {
wait_queue_head_t wait;
bool pending;
} commit;
bool is_componentized;
};

extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats;
Expand All @@ -174,7 +176,10 @@ void atmel_hlcdc_crtc_suspend(struct drm_crtc *crtc);
void atmel_hlcdc_crtc_resume(struct drm_crtc *crtc);

int atmel_hlcdc_crtc_create(struct drm_device *dev);
void atmel_hlcdc_crtc_set_simulate_vesa_sync(struct drm_crtc *c, bool enabled);
void atmel_hlcdc_crtc_set_invert_pixel_clock(struct drm_crtc *c, bool enabled);

int atmel_hlcdc_create_outputs(struct drm_device *dev);
int atmel_hlcdc_get_external_components(struct device *dev, struct component_match **match);

#endif /* DRM_ATMEL_HLCDC_H */
35 changes: 35 additions & 0 deletions drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -294,3 +294,38 @@ int atmel_hlcdc_create_outputs(struct drm_device *dev)

return 0;
}

static int dev_match_of(struct device *dev, void *data)
{
return dev->of_node == data;
}

int atmel_hlcdc_get_external_components(struct device *dev,
struct component_match **match)
{
struct device_node *ep = NULL;
int count = 0;

while ((ep = of_graph_get_next_endpoint(dev->of_node, ep))) {
struct device_node *node;

node = of_graph_get_remote_port_parent(ep);
if (!node && !of_device_is_available(node)) {
of_node_put(node);
continue;
}

dev_dbg(dev, "Subdevice node '%s' found\n", node->name);
if (match)
component_match_add(dev, match, dev_match_of, node);
of_node_put(node);
count++;
}

if (count > 1) {
dev_err(dev, "Only one external encoder is supported\n");
return -EINVAL;
}

return count;
}
Loading