Merge omapdrm work from Laurent

omapdrm IRQ rework, fixed vblank count and timestamp, cleanups.
This commit is contained in:
Tomi Valkeinen 2016-12-19 11:35:03 +02:00
Родитель 2cf026ae85 2f95bc6d32
Коммит 2f1fed12c6
15 изменённых файлов: 436 добавлений и 477 удалений

Просмотреть файл

@ -595,6 +595,8 @@ static void drm_dev_release(struct kref *ref)
{ {
struct drm_device *dev = container_of(ref, struct drm_device, ref); struct drm_device *dev = container_of(ref, struct drm_device, ref);
drm_vblank_cleanup(dev);
if (drm_core_check_feature(dev, DRIVER_GEM)) if (drm_core_check_feature(dev, DRIVER_GEM))
drm_gem_destroy(dev); drm_gem_destroy(dev);
@ -794,8 +796,6 @@ void drm_dev_unregister(struct drm_device *dev)
if (dev->agp) if (dev->agp)
drm_pci_agp_destroy(dev); drm_pci_agp_destroy(dev);
drm_vblank_cleanup(dev);
list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head)
drm_legacy_rmmap(dev, r_list->map); drm_legacy_rmmap(dev, r_list->map);

Просмотреть файл

@ -1253,7 +1253,7 @@ static int dsicm_probe(struct platform_device *pdev)
dsicm_hw_reset(ddata); dsicm_hw_reset(ddata);
if (ddata->use_dsi_backlight) { if (ddata->use_dsi_backlight) {
memset(&props, 0, sizeof(struct backlight_properties)); memset(&props, 0, sizeof(props));
props.max_brightness = 255; props.max_brightness = 255;
props.type = BACKLIGHT_RAW; props.type = BACKLIGHT_RAW;

Просмотреть файл

@ -620,6 +620,19 @@ u32 dispc_wb_get_framedone_irq(void)
return DISPC_IRQ_FRAMEDONEWB; return DISPC_IRQ_FRAMEDONEWB;
} }
void dispc_mgr_enable(enum omap_channel channel, bool enable)
{
mgr_fld_write(channel, DISPC_MGR_FLD_ENABLE, enable);
/* flush posted write */
mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
}
EXPORT_SYMBOL(dispc_mgr_enable);
static bool dispc_mgr_is_enabled(enum omap_channel channel)
{
return !!mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
}
bool dispc_mgr_go_busy(enum omap_channel channel) bool dispc_mgr_go_busy(enum omap_channel channel)
{ {
return mgr_fld_read(channel, DISPC_MGR_FLD_GO) == 1; return mgr_fld_read(channel, DISPC_MGR_FLD_GO) == 1;
@ -2901,20 +2914,6 @@ enum omap_dss_output_id dispc_mgr_get_supported_outputs(enum omap_channel channe
} }
EXPORT_SYMBOL(dispc_mgr_get_supported_outputs); EXPORT_SYMBOL(dispc_mgr_get_supported_outputs);
void dispc_mgr_enable(enum omap_channel channel, bool enable)
{
mgr_fld_write(channel, DISPC_MGR_FLD_ENABLE, enable);
/* flush posted write */
mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
}
EXPORT_SYMBOL(dispc_mgr_enable);
bool dispc_mgr_is_enabled(enum omap_channel channel)
{
return !!mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
}
EXPORT_SYMBOL(dispc_mgr_is_enabled);
void dispc_wb_enable(bool enable) void dispc_wb_enable(bool enable)
{ {
dispc_ovl_enable(OMAP_DSS_WB, enable); dispc_ovl_enable(OMAP_DSS_WB, enable);

Просмотреть файл

@ -119,8 +119,7 @@ static void __init omapdss_omapify_node(struct device_node *node)
static void __init omapdss_add_to_list(struct device_node *node, bool root) static void __init omapdss_add_to_list(struct device_node *node, bool root)
{ {
struct dss_conv_node *n = kmalloc(sizeof(struct dss_conv_node), struct dss_conv_node *n = kmalloc(sizeof(*n), GFP_KERNEL);
GFP_KERNEL);
if (n) { if (n) {
n->node = node; n->node = node;
n->root = root; n->root = root;

Просмотреть файл

@ -856,7 +856,6 @@ int dispc_runtime_get(void);
void dispc_runtime_put(void); void dispc_runtime_put(void);
void dispc_mgr_enable(enum omap_channel channel, bool enable); void dispc_mgr_enable(enum omap_channel channel, bool enable);
bool dispc_mgr_is_enabled(enum omap_channel channel);
u32 dispc_mgr_get_vsync_irq(enum omap_channel channel); u32 dispc_mgr_get_vsync_irq(enum omap_channel channel);
u32 dispc_mgr_get_framedone_irq(enum omap_channel channel); u32 dispc_mgr_get_framedone_irq(enum omap_channel channel);
u32 dispc_mgr_get_sync_lost_irq(enum omap_channel channel); u32 dispc_mgr_get_sync_lost_irq(enum omap_channel channel);

Просмотреть файл

@ -162,7 +162,7 @@ static int omap_connector_mode_valid(struct drm_connector *connector,
dssdrv->get_timings(dssdev, &t); dssdrv->get_timings(dssdev, &t);
if (memcmp(&vm, &t, sizeof(struct videomode))) if (memcmp(&vm, &t, sizeof(vm)))
r = -EINVAL; r = -EINVAL;
else else
r = 0; r = 0;
@ -217,7 +217,7 @@ struct drm_connector *omap_connector_init(struct drm_device *dev,
omap_dss_get_device(dssdev); omap_dss_get_device(dssdev);
omap_connector = kzalloc(sizeof(struct omap_connector), GFP_KERNEL); omap_connector = kzalloc(sizeof(*omap_connector), GFP_KERNEL);
if (!omap_connector) if (!omap_connector)
goto fail; goto fail;
@ -240,8 +240,6 @@ struct drm_connector *omap_connector_init(struct drm_device *dev,
connector->interlace_allowed = 1; connector->interlace_allowed = 1;
connector->doublescan_allowed = 0; connector->doublescan_allowed = 0;
drm_connector_register(connector);
return connector; return connector;
fail: fail:

Просмотреть файл

@ -36,26 +36,18 @@ struct omap_crtc {
struct videomode vm; struct videomode vm;
struct omap_drm_irq vblank_irq;
struct omap_drm_irq error_irq;
bool ignore_digit_sync_lost; bool ignore_digit_sync_lost;
bool enabled;
bool pending; bool pending;
wait_queue_head_t pending_wait; wait_queue_head_t pending_wait;
struct drm_pending_vblank_event *event;
}; };
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
* Helper Functions * Helper Functions
*/ */
uint32_t pipe2vbl(struct drm_crtc *crtc)
{
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
return dispc_mgr_get_vsync_irq(omap_crtc->channel);
}
struct videomode *omap_crtc_timings(struct drm_crtc *crtc) struct videomode *omap_crtc_timings(struct drm_crtc *crtc)
{ {
struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
@ -68,6 +60,19 @@ enum omap_channel omap_crtc_channel(struct drm_crtc *crtc)
return omap_crtc->channel; return omap_crtc->channel;
} }
static bool omap_crtc_is_pending(struct drm_crtc *crtc)
{
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
unsigned long flags;
bool pending;
spin_lock_irqsave(&crtc->dev->event_lock, flags);
pending = omap_crtc->pending;
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
return pending;
}
int omap_crtc_wait_pending(struct drm_crtc *crtc) int omap_crtc_wait_pending(struct drm_crtc *crtc)
{ {
struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
@ -77,7 +82,7 @@ int omap_crtc_wait_pending(struct drm_crtc *crtc)
* a single frame refresh even on slower displays. * a single frame refresh even on slower displays.
*/ */
return wait_event_timeout(omap_crtc->pending_wait, return wait_event_timeout(omap_crtc->pending_wait,
!omap_crtc->pending, !omap_crtc_is_pending(crtc),
msecs_to_jiffies(250)); msecs_to_jiffies(250));
} }
@ -135,14 +140,15 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable)
u32 framedone_irq, vsync_irq; u32 framedone_irq, vsync_irq;
int ret; int ret;
if (WARN_ON(omap_crtc->enabled == enable))
return;
if (omap_crtc_output[channel]->output_type == OMAP_DISPLAY_TYPE_HDMI) { if (omap_crtc_output[channel]->output_type == OMAP_DISPLAY_TYPE_HDMI) {
dispc_mgr_enable(channel, enable); dispc_mgr_enable(channel, enable);
omap_crtc->enabled = enable;
return; return;
} }
if (dispc_mgr_is_enabled(channel) == enable)
return;
if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) { if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) {
/* /*
* Digit output produces some sync lost interrupts during the * Digit output produces some sync lost interrupts during the
@ -173,6 +179,7 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable)
} }
dispc_mgr_enable(channel, enable); dispc_mgr_enable(channel, enable);
omap_crtc->enabled = enable;
ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100)); ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100));
if (ret) { if (ret) {
@ -259,26 +266,9 @@ static const struct dss_mgr_ops mgr_ops = {
* Setup, Flush and Page Flip * Setup, Flush and Page Flip
*/ */
static void omap_crtc_complete_page_flip(struct drm_crtc *crtc) void omap_crtc_error_irq(struct drm_crtc *crtc, uint32_t irqstatus)
{ {
struct drm_pending_vblank_event *event; struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
struct drm_device *dev = crtc->dev;
unsigned long flags;
event = crtc->state->event;
if (!event)
return;
spin_lock_irqsave(&dev->event_lock, flags);
drm_crtc_send_vblank_event(crtc, event);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
{
struct omap_crtc *omap_crtc =
container_of(irq, struct omap_crtc, error_irq);
if (omap_crtc->ignore_digit_sync_lost) { if (omap_crtc->ignore_digit_sync_lost) {
irqstatus &= ~DISPC_IRQ_SYNC_LOST_DIGIT; irqstatus &= ~DISPC_IRQ_SYNC_LOST_DIGIT;
@ -289,29 +279,38 @@ static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus); DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus);
} }
static void omap_crtc_vblank_irq(struct omap_drm_irq *irq, uint32_t irqstatus) void omap_crtc_vblank_irq(struct drm_crtc *crtc)
{ {
struct omap_crtc *omap_crtc = struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
container_of(irq, struct omap_crtc, vblank_irq); bool pending;
struct drm_device *dev = omap_crtc->base.dev;
if (dispc_mgr_go_busy(omap_crtc->channel)) spin_lock(&crtc->dev->event_lock);
/*
* If the dispc is busy we're racing the flush operation. Try again on
* the next vblank interrupt.
*/
if (dispc_mgr_go_busy(omap_crtc->channel)) {
spin_unlock(&crtc->dev->event_lock);
return; return;
}
/* Send the vblank event if one has been requested. */
if (omap_crtc->event) {
drm_crtc_send_vblank_event(crtc, omap_crtc->event);
omap_crtc->event = NULL;
}
pending = omap_crtc->pending;
omap_crtc->pending = false;
spin_unlock(&crtc->dev->event_lock);
if (pending)
drm_crtc_vblank_put(crtc);
/* Wake up omap_atomic_complete. */
wake_up(&omap_crtc->pending_wait);
DBG("%s: apply done", omap_crtc->name); DBG("%s: apply done", omap_crtc->name);
__omap_irq_unregister(dev, &omap_crtc->vblank_irq);
rmb();
WARN_ON(!omap_crtc->pending);
omap_crtc->pending = false;
wmb();
/* wake up userspace */
omap_crtc_complete_page_flip(&omap_crtc->base);
/* wake up omap_atomic_complete */
wake_up(&omap_crtc->pending_wait);
} }
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
@ -324,9 +323,6 @@ static void omap_crtc_destroy(struct drm_crtc *crtc)
DBG("%s", omap_crtc->name); DBG("%s", omap_crtc->name);
WARN_ON(omap_crtc->vblank_irq.registered);
omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
drm_crtc_cleanup(crtc); drm_crtc_cleanup(crtc);
kfree(omap_crtc); kfree(omap_crtc);
@ -335,17 +331,18 @@ static void omap_crtc_destroy(struct drm_crtc *crtc)
static void omap_crtc_enable(struct drm_crtc *crtc) static void omap_crtc_enable(struct drm_crtc *crtc)
{ {
struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
int ret;
DBG("%s", omap_crtc->name); DBG("%s", omap_crtc->name);
rmb(); spin_lock_irq(&crtc->dev->event_lock);
drm_crtc_vblank_on(crtc);
ret = drm_crtc_vblank_get(crtc);
WARN_ON(ret != 0);
WARN_ON(omap_crtc->pending); WARN_ON(omap_crtc->pending);
omap_crtc->pending = true; omap_crtc->pending = true;
wmb(); spin_unlock_irq(&crtc->dev->event_lock);
omap_irq_register(crtc->dev, &omap_crtc->vblank_irq);
drm_crtc_vblank_on(crtc);
} }
static void omap_crtc_disable(struct drm_crtc *crtc) static void omap_crtc_disable(struct drm_crtc *crtc)
@ -390,16 +387,15 @@ static int omap_crtc_atomic_check(struct drm_crtc *crtc,
} }
static void omap_crtc_atomic_begin(struct drm_crtc *crtc, static void omap_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state) struct drm_crtc_state *old_crtc_state)
{ {
} }
static void omap_crtc_atomic_flush(struct drm_crtc *crtc, static void omap_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state) struct drm_crtc_state *old_crtc_state)
{ {
struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
int ret;
WARN_ON(omap_crtc->vblank_irq.registered);
if (crtc->state->color_mgmt_changed) { if (crtc->state->color_mgmt_changed) {
struct drm_color_lut *lut = NULL; struct drm_color_lut *lut = NULL;
@ -414,18 +410,30 @@ static void omap_crtc_atomic_flush(struct drm_crtc *crtc,
dispc_mgr_set_gamma(omap_crtc->channel, lut, length); dispc_mgr_set_gamma(omap_crtc->channel, lut, length);
} }
if (dispc_mgr_is_enabled(omap_crtc->channel)) { /*
* Only flush the CRTC if it is currently enabled. CRTCs that require a
* mode set are disabled prior plane updates and enabled afterwards.
* They are thus not active (regardless of what their CRTC core state
* reports) and the DRM core could thus call this function even though
* the CRTC is currently disabled. Do nothing in that case.
*/
if (!omap_crtc->enabled)
return;
DBG("%s: GO", omap_crtc->name); DBG("%s: GO", omap_crtc->name);
rmb(); ret = drm_crtc_vblank_get(crtc);
WARN_ON(omap_crtc->pending); WARN_ON(ret != 0);
omap_crtc->pending = true;
wmb();
dispc_mgr_go(omap_crtc->channel); spin_lock_irq(&crtc->dev->event_lock);
omap_irq_register(crtc->dev, &omap_crtc->vblank_irq); dispc_mgr_go(omap_crtc->channel);
}
WARN_ON(omap_crtc->pending);
omap_crtc->pending = true;
if (crtc->state->event)
omap_crtc->event = crtc->state->event;
spin_unlock_irq(&crtc->dev->event_lock);
} }
static bool omap_crtc_is_plane_prop(struct drm_crtc *crtc, static bool omap_crtc_is_plane_prop(struct drm_crtc *crtc,
@ -546,14 +554,6 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
omap_crtc->channel = channel; omap_crtc->channel = channel;
omap_crtc->name = channel_names[channel]; omap_crtc->name = channel_names[channel];
omap_crtc->vblank_irq.irqmask = pipe2vbl(crtc);
omap_crtc->vblank_irq.irq = omap_crtc_vblank_irq;
omap_crtc->error_irq.irqmask =
dispc_mgr_get_sync_lost_irq(channel);
omap_crtc->error_irq.irq = omap_crtc_error_irq;
omap_irq_register(dev, &omap_crtc->error_irq);
ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
&omap_crtc_funcs, NULL); &omap_crtc_funcs, NULL);
if (ret < 0) { if (ret < 0) {

Просмотреть файл

@ -224,7 +224,7 @@ static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area,
int rows = (1 + area->y1 - area->y0); int rows = (1 + area->y1 - area->y0);
int i = columns*rows; int i = columns*rows;
pat = alloc_dma(txn, sizeof(struct pat), &pat_pa); pat = alloc_dma(txn, sizeof(*pat), &pat_pa);
if (txn->last_pat) if (txn->last_pat)
txn->last_pat->next_pa = (uint32_t)pat_pa; txn->last_pat->next_pa = (uint32_t)pat_pa;
@ -735,7 +735,7 @@ static int omap_dmm_probe(struct platform_device *dev)
/* alloc engines */ /* alloc engines */
omap_dmm->engines = kcalloc(omap_dmm->num_engines, omap_dmm->engines = kcalloc(omap_dmm->num_engines,
sizeof(struct refill_engine), GFP_KERNEL); sizeof(*omap_dmm->engines), GFP_KERNEL);
if (!omap_dmm->engines) { if (!omap_dmm->engines) {
ret = -ENOMEM; ret = -ENOMEM;
goto fail; goto fail;

Просмотреть файл

@ -96,7 +96,8 @@ static void omap_atomic_complete(struct omap_atomic_state_commit *commit)
dispc_runtime_get(); dispc_runtime_get();
drm_atomic_helper_commit_modeset_disables(dev, old_state); drm_atomic_helper_commit_modeset_disables(dev, old_state);
drm_atomic_helper_commit_planes(dev, old_state, 0); drm_atomic_helper_commit_planes(dev, old_state,
DRM_PLANE_COMMIT_ACTIVE_ONLY);
drm_atomic_helper_commit_modeset_enables(dev, old_state); drm_atomic_helper_commit_modeset_enables(dev, old_state);
omap_atomic_wait_for_completion(dev, old_state); omap_atomic_wait_for_completion(dev, old_state);
@ -315,8 +316,6 @@ static int omap_modeset_init(struct drm_device *dev)
drm_mode_config_init(dev); drm_mode_config_init(dev);
omap_drm_irq_install(dev);
ret = omap_modeset_init_properties(dev); ret = omap_modeset_init_properties(dev);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -489,12 +488,9 @@ static int omap_modeset_init(struct drm_device *dev)
drm_mode_config_reset(dev); drm_mode_config_reset(dev);
return 0; omap_drm_irq_install(dev);
}
static void omap_modeset_free(struct drm_device *dev) return 0;
{
drm_mode_config_cleanup(dev);
} }
/* /*
@ -632,95 +628,6 @@ static const struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] =
* drm driver funcs * drm driver funcs
*/ */
/**
* load - setup chip and create an initial config
* @dev: DRM device
* @flags: startup flags
*
* The driver load routine has to do several things:
* - initialize the memory manager
* - allocate initial config memory
* - setup the DRM framebuffer with the allocated memory
*/
static int dev_load(struct drm_device *dev, unsigned long flags)
{
struct omap_drm_platform_data *pdata = dev->dev->platform_data;
struct omap_drm_private *priv;
unsigned int i;
int ret;
DBG("load: dev=%p", dev);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->omaprev = pdata->omaprev;
dev->dev_private = priv;
priv->wq = alloc_ordered_workqueue("omapdrm", 0);
init_waitqueue_head(&priv->commit.wait);
spin_lock_init(&priv->commit.lock);
spin_lock_init(&priv->list_lock);
INIT_LIST_HEAD(&priv->obj_list);
omap_gem_init(dev);
ret = omap_modeset_init(dev);
if (ret) {
dev_err(dev->dev, "omap_modeset_init failed: ret=%d\n", ret);
dev->dev_private = NULL;
kfree(priv);
return ret;
}
/* Initialize vblank handling, start with all CRTCs disabled. */
ret = drm_vblank_init(dev, priv->num_crtcs);
if (ret)
dev_warn(dev->dev, "could not init vblank\n");
for (i = 0; i < priv->num_crtcs; i++)
drm_crtc_vblank_off(priv->crtcs[i]);
priv->fbdev = omap_fbdev_init(dev);
/* store off drm_device for use in pm ops */
dev_set_drvdata(dev->dev, dev);
drm_kms_helper_poll_init(dev);
return 0;
}
static int dev_unload(struct drm_device *dev)
{
struct omap_drm_private *priv = dev->dev_private;
DBG("unload: dev=%p", dev);
drm_kms_helper_poll_fini(dev);
if (priv->fbdev)
omap_fbdev_free(dev);
omap_modeset_free(dev);
omap_gem_deinit(dev);
destroy_workqueue(priv->wq);
drm_vblank_cleanup(dev);
omap_drm_irq_uninstall(dev);
kfree(dev->dev_private);
dev->dev_private = NULL;
dev_set_drvdata(dev->dev, NULL);
return 0;
}
static int dev_open(struct drm_device *dev, struct drm_file *file) static int dev_open(struct drm_device *dev, struct drm_file *file)
{ {
file->driver_priv = NULL; file->driver_priv = NULL;
@ -805,8 +712,6 @@ static const struct file_operations omapdriver_fops = {
static struct drm_driver omap_drm_driver = { static struct drm_driver omap_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
DRIVER_ATOMIC, DRIVER_ATOMIC,
.load = dev_load,
.unload = dev_unload,
.open = dev_open, .open = dev_open,
.lastclose = dev_lastclose, .lastclose = dev_lastclose,
.get_vblank_counter = drm_vblank_no_hw_counter, .get_vblank_counter = drm_vblank_no_hw_counter,
@ -836,30 +741,125 @@ static struct drm_driver omap_drm_driver = {
.patchlevel = DRIVER_PATCHLEVEL, .patchlevel = DRIVER_PATCHLEVEL,
}; };
static int pdev_probe(struct platform_device *device) static int pdev_probe(struct platform_device *pdev)
{ {
int r; struct omap_drm_platform_data *pdata = pdev->dev.platform_data;
struct omap_drm_private *priv;
struct drm_device *ddev;
unsigned int i;
int ret;
DBG("%s", pdev->name);
if (omapdss_is_initialized() == false) if (omapdss_is_initialized() == false)
return -EPROBE_DEFER; return -EPROBE_DEFER;
omap_crtc_pre_init(); omap_crtc_pre_init();
r = omap_connect_dssdevs(); ret = omap_connect_dssdevs();
if (r) { if (ret)
omap_crtc_pre_uninit(); goto err_crtc_uninit;
return r;
/* Allocate and initialize the driver private structure. */
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
goto err_disconnect_dssdevs;
} }
DBG("%s", device->name); priv->omaprev = pdata->omaprev;
return drm_platform_init(&omap_drm_driver, device); priv->wq = alloc_ordered_workqueue("omapdrm", 0);
init_waitqueue_head(&priv->commit.wait);
spin_lock_init(&priv->commit.lock);
spin_lock_init(&priv->list_lock);
INIT_LIST_HEAD(&priv->obj_list);
/* Allocate and initialize the DRM device. */
ddev = drm_dev_alloc(&omap_drm_driver, &pdev->dev);
if (IS_ERR(ddev)) {
ret = PTR_ERR(ddev);
goto err_free_priv;
}
ddev->dev_private = priv;
platform_set_drvdata(pdev, ddev);
omap_gem_init(ddev);
ret = omap_modeset_init(ddev);
if (ret) {
dev_err(&pdev->dev, "omap_modeset_init failed: ret=%d\n", ret);
goto err_free_drm_dev;
}
/* Initialize vblank handling, start with all CRTCs disabled. */
ret = drm_vblank_init(ddev, priv->num_crtcs);
if (ret) {
dev_err(&pdev->dev, "could not init vblank\n");
goto err_cleanup_modeset;
}
for (i = 0; i < priv->num_crtcs; i++)
drm_crtc_vblank_off(priv->crtcs[i]);
priv->fbdev = omap_fbdev_init(ddev);
drm_kms_helper_poll_init(ddev);
/*
* Register the DRM device with the core and the connectors with
* sysfs.
*/
ret = drm_dev_register(ddev, 0);
if (ret)
goto err_cleanup_helpers;
return 0;
err_cleanup_helpers:
drm_kms_helper_poll_fini(ddev);
if (priv->fbdev)
omap_fbdev_free(ddev);
err_cleanup_modeset:
drm_mode_config_cleanup(ddev);
omap_drm_irq_uninstall(ddev);
err_free_drm_dev:
omap_gem_deinit(ddev);
drm_dev_unref(ddev);
err_free_priv:
destroy_workqueue(priv->wq);
kfree(priv);
err_disconnect_dssdevs:
omap_disconnect_dssdevs();
err_crtc_uninit:
omap_crtc_pre_uninit();
return ret;
} }
static int pdev_remove(struct platform_device *device) static int pdev_remove(struct platform_device *pdev)
{ {
struct drm_device *ddev = platform_get_drvdata(pdev);
struct omap_drm_private *priv = ddev->dev_private;
DBG(""); DBG("");
drm_put_dev(platform_get_drvdata(device)); drm_dev_unregister(ddev);
drm_kms_helper_poll_fini(ddev);
if (priv->fbdev)
omap_fbdev_free(ddev);
drm_mode_config_cleanup(ddev);
omap_drm_irq_uninstall(ddev);
omap_gem_deinit(ddev);
drm_dev_unref(ddev);
destroy_workqueue(priv->wq);
kfree(priv);
omap_disconnect_dssdevs(); omap_disconnect_dssdevs();
omap_crtc_pre_uninit(); omap_crtc_pre_uninit();

Просмотреть файл

@ -48,19 +48,6 @@ struct omap_drm_window {
uint32_t src_w, src_h; uint32_t src_w, src_h;
}; };
/* For transiently registering for different DSS irqs that various parts
* of the KMS code need during setup/configuration. We these are not
* necessarily the same as what drm_vblank_get/put() are requesting, and
* the hysteresis in drm_vblank_put() is not necessarily desirable for
* internal housekeeping related irq usage.
*/
struct omap_drm_irq {
struct list_head node;
uint32_t irqmask;
bool registered;
void (*irq)(struct omap_drm_irq *irq, uint32_t irqstatus);
};
/* For KMS code that needs to wait for a certain # of IRQs: /* For KMS code that needs to wait for a certain # of IRQs:
*/ */
struct omap_irq_wait; struct omap_irq_wait;
@ -101,9 +88,9 @@ struct omap_drm_private {
struct drm_property *zorder_prop; struct drm_property *zorder_prop;
/* irq handling: */ /* irq handling: */
struct list_head irq_list; /* list of omap_drm_irq */ spinlock_t wait_lock; /* protects the wait_list */
uint32_t vblank_mask; /* irq bits set for userspace vblank */ struct list_head wait_list; /* list of omap_irq_wait */
struct omap_drm_irq error_handler; uint32_t irq_mask; /* enabled irqs in addition to wait_list */
/* atomic commit */ /* atomic commit */
struct { struct {
@ -128,10 +115,6 @@ int omap_gem_resume(struct device *dev);
int omap_irq_enable_vblank(struct drm_device *dev, unsigned int pipe); int omap_irq_enable_vblank(struct drm_device *dev, unsigned int pipe);
void omap_irq_disable_vblank(struct drm_device *dev, unsigned int pipe); void omap_irq_disable_vblank(struct drm_device *dev, unsigned int pipe);
void __omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq);
void __omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq);
void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq);
void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq);
void omap_drm_irq_uninstall(struct drm_device *dev); void omap_drm_irq_uninstall(struct drm_device *dev);
int omap_drm_irq_install(struct drm_device *dev); int omap_drm_irq_install(struct drm_device *dev);
@ -155,6 +138,8 @@ void omap_crtc_pre_uninit(void);
struct drm_crtc *omap_crtc_init(struct drm_device *dev, struct drm_crtc *omap_crtc_init(struct drm_device *dev,
struct drm_plane *plane, enum omap_channel channel, int id); struct drm_plane *plane, enum omap_channel channel, int id);
int omap_crtc_wait_pending(struct drm_crtc *crtc); int omap_crtc_wait_pending(struct drm_crtc *crtc);
void omap_crtc_error_irq(struct drm_crtc *crtc, uint32_t irqstatus);
void omap_crtc_vblank_irq(struct drm_crtc *crtc);
struct drm_plane *omap_plane_init(struct drm_device *dev, struct drm_plane *omap_plane_init(struct drm_device *dev,
int id, enum drm_plane_type type, int id, enum drm_plane_type type,
@ -233,32 +218,6 @@ struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev,
struct dma_buf *buffer); struct dma_buf *buffer);
/* map crtc to vblank mask */ /* map crtc to vblank mask */
uint32_t pipe2vbl(struct drm_crtc *crtc);
struct omap_dss_device *omap_encoder_get_dssdev(struct drm_encoder *encoder); struct omap_dss_device *omap_encoder_get_dssdev(struct drm_encoder *encoder);
/* should these be made into common util helpers?
*/
static inline int objects_lookup(
struct drm_file *filp, uint32_t pixel_format,
struct drm_gem_object **bos, const uint32_t *handles)
{
int i, n = drm_format_num_planes(pixel_format);
for (i = 0; i < n; i++) {
bos[i] = drm_gem_object_lookup(filp, handles[i]);
if (!bos[i])
goto fail;
}
return 0;
fail:
while (--i > 0)
drm_gem_object_unreference_unlocked(bos[i]);
return -ENOENT;
}
#endif /* __OMAP_DRV_H__ */ #endif /* __OMAP_DRV_H__ */

Просмотреть файл

@ -117,7 +117,7 @@ static int omap_encoder_update(struct drm_encoder *encoder,
dssdrv->get_timings(dssdev, &t); dssdrv->get_timings(dssdev, &t);
if (memcmp(vm, &t, sizeof(struct videomode))) if (memcmp(vm, &t, sizeof(*vm)))
ret = -EINVAL; ret = -EINVAL;
else else
ret = 0; ret = 0;

Просмотреть файл

@ -29,37 +29,30 @@
* framebuffer funcs * framebuffer funcs
*/ */
/* per-format info: */ /* DSS to DRM formats mapping */
struct format { static const struct {
enum omap_color_mode dss_format; enum omap_color_mode dss_format;
uint32_t pixel_format; uint32_t pixel_format;
struct { } formats[] = {
int stride_bpp; /* this times width is stride */
int sub_y; /* sub-sample in y dimension */
} planes[4];
bool yuv;
};
static const struct format formats[] = {
/* 16bpp [A]RGB: */ /* 16bpp [A]RGB: */
{ OMAP_DSS_COLOR_RGB16, DRM_FORMAT_RGB565, {{2, 1}}, false }, /* RGB16-565 */ { OMAP_DSS_COLOR_RGB16, DRM_FORMAT_RGB565 }, /* RGB16-565 */
{ OMAP_DSS_COLOR_RGB12U, DRM_FORMAT_RGBX4444, {{2, 1}}, false }, /* RGB12x-4444 */ { OMAP_DSS_COLOR_RGB12U, DRM_FORMAT_RGBX4444 }, /* RGB12x-4444 */
{ OMAP_DSS_COLOR_RGBX16, DRM_FORMAT_XRGB4444, {{2, 1}}, false }, /* xRGB12-4444 */ { OMAP_DSS_COLOR_RGBX16, DRM_FORMAT_XRGB4444 }, /* xRGB12-4444 */
{ OMAP_DSS_COLOR_RGBA16, DRM_FORMAT_RGBA4444, {{2, 1}}, false }, /* RGBA12-4444 */ { OMAP_DSS_COLOR_RGBA16, DRM_FORMAT_RGBA4444 }, /* RGBA12-4444 */
{ OMAP_DSS_COLOR_ARGB16, DRM_FORMAT_ARGB4444, {{2, 1}}, false }, /* ARGB16-4444 */ { OMAP_DSS_COLOR_ARGB16, DRM_FORMAT_ARGB4444 }, /* ARGB16-4444 */
{ OMAP_DSS_COLOR_XRGB16_1555, DRM_FORMAT_XRGB1555, {{2, 1}}, false }, /* xRGB15-1555 */ { OMAP_DSS_COLOR_XRGB16_1555, DRM_FORMAT_XRGB1555 }, /* xRGB15-1555 */
{ OMAP_DSS_COLOR_ARGB16_1555, DRM_FORMAT_ARGB1555, {{2, 1}}, false }, /* ARGB16-1555 */ { OMAP_DSS_COLOR_ARGB16_1555, DRM_FORMAT_ARGB1555 }, /* ARGB16-1555 */
/* 24bpp RGB: */ /* 24bpp RGB: */
{ OMAP_DSS_COLOR_RGB24P, DRM_FORMAT_RGB888, {{3, 1}}, false }, /* RGB24-888 */ { OMAP_DSS_COLOR_RGB24P, DRM_FORMAT_RGB888 }, /* RGB24-888 */
/* 32bpp [A]RGB: */ /* 32bpp [A]RGB: */
{ OMAP_DSS_COLOR_RGBX32, DRM_FORMAT_RGBX8888, {{4, 1}}, false }, /* RGBx24-8888 */ { OMAP_DSS_COLOR_RGBX32, DRM_FORMAT_RGBX8888 }, /* RGBx24-8888 */
{ OMAP_DSS_COLOR_RGB24U, DRM_FORMAT_XRGB8888, {{4, 1}}, false }, /* xRGB24-8888 */ { OMAP_DSS_COLOR_RGB24U, DRM_FORMAT_XRGB8888 }, /* xRGB24-8888 */
{ OMAP_DSS_COLOR_RGBA32, DRM_FORMAT_RGBA8888, {{4, 1}}, false }, /* RGBA32-8888 */ { OMAP_DSS_COLOR_RGBA32, DRM_FORMAT_RGBA8888 }, /* RGBA32-8888 */
{ OMAP_DSS_COLOR_ARGB32, DRM_FORMAT_ARGB8888, {{4, 1}}, false }, /* ARGB32-8888 */ { OMAP_DSS_COLOR_ARGB32, DRM_FORMAT_ARGB8888 }, /* ARGB32-8888 */
/* YUV: */ /* YUV: */
{ OMAP_DSS_COLOR_NV12, DRM_FORMAT_NV12, {{1, 1}, {1, 2}}, true }, { OMAP_DSS_COLOR_NV12, DRM_FORMAT_NV12 },
{ OMAP_DSS_COLOR_YUV2, DRM_FORMAT_YUYV, {{2, 1}}, true }, { OMAP_DSS_COLOR_YUV2, DRM_FORMAT_YUYV },
{ OMAP_DSS_COLOR_UYVY, DRM_FORMAT_UYVY, {{2, 1}}, true }, { OMAP_DSS_COLOR_UYVY, DRM_FORMAT_UYVY },
}; };
/* convert from overlay's pixel formats bitmask to an array of fourcc's */ /* convert from overlay's pixel formats bitmask to an array of fourcc's */
@ -89,8 +82,9 @@ struct plane {
struct omap_framebuffer { struct omap_framebuffer {
struct drm_framebuffer base; struct drm_framebuffer base;
int pin_count; int pin_count;
const struct format *format; const struct drm_format_info *format;
struct plane planes[4]; enum omap_color_mode dss_format;
struct plane planes[2];
/* lock for pinning (pin_count and planes.paddr) */ /* lock for pinning (pin_count and planes.paddr) */
struct mutex lock; struct mutex lock;
}; };
@ -128,13 +122,13 @@ static const struct drm_framebuffer_funcs omap_framebuffer_funcs = {
}; };
static uint32_t get_linear_addr(struct plane *plane, static uint32_t get_linear_addr(struct plane *plane,
const struct format *format, int n, int x, int y) const struct drm_format_info *format, int n, int x, int y)
{ {
uint32_t offset; uint32_t offset;
offset = plane->offset + offset = plane->offset
(x * format->planes[n].stride_bpp) + + (x * format->cpp[n] / (n == 0 ? 1 : format->hsub))
(y * plane->pitch / format->planes[n].sub_y); + (y * plane->pitch / (n == 0 ? 1 : format->vsub));
return plane->paddr + offset; return plane->paddr + offset;
} }
@ -153,11 +147,11 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
struct omap_drm_window *win, struct omap_overlay_info *info) struct omap_drm_window *win, struct omap_overlay_info *info)
{ {
struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
const struct format *format = omap_fb->format; const struct drm_format_info *format = omap_fb->format;
struct plane *plane = &omap_fb->planes[0]; struct plane *plane = &omap_fb->planes[0];
uint32_t x, y, orient = 0; uint32_t x, y, orient = 0;
info->color_mode = format->dss_format; info->color_mode = omap_fb->dss_format;
info->pos_x = win->crtc_x; info->pos_x = win->crtc_x;
info->pos_y = win->crtc_y; info->pos_y = win->crtc_y;
@ -231,9 +225,9 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
} }
/* convert to pixels: */ /* convert to pixels: */
info->screen_width /= format->planes[0].stride_bpp; info->screen_width /= format->cpp[0];
if (format->dss_format == OMAP_DSS_COLOR_NV12) { if (omap_fb->dss_format == OMAP_DSS_COLOR_NV12) {
plane = &omap_fb->planes[1]; plane = &omap_fb->planes[1];
if (info->rotation_type == OMAP_DSS_ROT_TILER) { if (info->rotation_type == OMAP_DSS_ROT_TILER) {
@ -360,47 +354,58 @@ void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev,
struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd) struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd)
{ {
unsigned int num_planes = drm_format_num_planes(mode_cmd->pixel_format);
struct drm_gem_object *bos[4]; struct drm_gem_object *bos[4];
struct drm_framebuffer *fb; struct drm_framebuffer *fb;
int ret; int i;
ret = objects_lookup(file, mode_cmd->pixel_format, for (i = 0; i < num_planes; i++) {
bos, mode_cmd->handles); bos[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);
if (ret) if (!bos[i]) {
return ERR_PTR(ret); fb = ERR_PTR(-ENOENT);
goto error;
}
}
fb = omap_framebuffer_init(dev, mode_cmd, bos); fb = omap_framebuffer_init(dev, mode_cmd, bos);
if (IS_ERR(fb)) { if (IS_ERR(fb))
int i, n = drm_format_num_planes(mode_cmd->pixel_format); goto error;
for (i = 0; i < n; i++)
drm_gem_object_unreference_unlocked(bos[i]); return fb;
return fb;
} error:
while (--i > 0)
drm_gem_object_unreference_unlocked(bos[i]);
return fb; return fb;
} }
struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos) const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos)
{ {
const struct drm_format_info *format = NULL;
struct omap_framebuffer *omap_fb = NULL; struct omap_framebuffer *omap_fb = NULL;
struct drm_framebuffer *fb = NULL; struct drm_framebuffer *fb = NULL;
const struct format *format = NULL; enum omap_color_mode dss_format = 0;
int ret, i, n = drm_format_num_planes(mode_cmd->pixel_format); unsigned int pitch = mode_cmd->pitches[0];
int ret, i;
DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)", DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)",
dev, mode_cmd, mode_cmd->width, mode_cmd->height, dev, mode_cmd, mode_cmd->width, mode_cmd->height,
(char *)&mode_cmd->pixel_format); (char *)&mode_cmd->pixel_format);
format = drm_format_info(mode_cmd->pixel_format);
for (i = 0; i < ARRAY_SIZE(formats); i++) { for (i = 0; i < ARRAY_SIZE(formats); i++) {
if (formats[i].pixel_format == mode_cmd->pixel_format) { if (formats[i].pixel_format == mode_cmd->pixel_format) {
format = &formats[i]; dss_format = formats[i].dss_format;
break; break;
} }
} }
if (!format) { if (!format || !dss_format) {
dev_err(dev->dev, "unsupported pixel format: %4.4s\n", dev_dbg(dev->dev, "unsupported pixel format: %4.4s\n",
(char *)&mode_cmd->pixel_format); (char *)&mode_cmd->pixel_format);
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
@ -413,40 +418,39 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
fb = &omap_fb->base; fb = &omap_fb->base;
omap_fb->format = format; omap_fb->format = format;
omap_fb->dss_format = dss_format;
mutex_init(&omap_fb->lock); mutex_init(&omap_fb->lock);
for (i = 0; i < n; i++) { /*
* The code below assumes that no format use more than two planes, and
* that the two planes of multiplane formats need the same number of
* bytes per pixel.
*/
if (format->num_planes == 2 && pitch != mode_cmd->pitches[1]) {
dev_dbg(dev->dev, "pitches differ between planes 0 and 1\n");
ret = -EINVAL;
goto fail;
}
if (pitch % format->cpp[0]) {
dev_dbg(dev->dev,
"buffer pitch (%u bytes) is not a multiple of pixel size (%u bytes)\n",
pitch, format->cpp[0]);
ret = -EINVAL;
goto fail;
}
for (i = 0; i < format->num_planes; i++) {
struct plane *plane = &omap_fb->planes[i]; struct plane *plane = &omap_fb->planes[i];
int size, pitch = mode_cmd->pitches[i]; unsigned int vsub = i == 0 ? 1 : format->vsub;
unsigned int size;
if (pitch < (mode_cmd->width * format->planes[i].stride_bpp)) { size = pitch * mode_cmd->height / vsub;
dev_err(dev->dev, "provided buffer pitch is too small! %d < %d\n",
pitch, mode_cmd->width * format->planes[i].stride_bpp);
ret = -EINVAL;
goto fail;
}
if (pitch % format->planes[i].stride_bpp != 0) { if (size > omap_gem_mmap_size(bos[i]) - mode_cmd->offsets[i]) {
dev_err(dev->dev, dev_dbg(dev->dev,
"buffer pitch (%d bytes) is not a multiple of pixel size (%d bytes)\n", "provided buffer object is too small! %d < %d\n",
pitch, format->planes[i].stride_bpp); bos[i]->size - mode_cmd->offsets[i], size);
ret = -EINVAL;
goto fail;
}
size = pitch * mode_cmd->height / format->planes[i].sub_y;
if (size > (omap_gem_mmap_size(bos[i]) - mode_cmd->offsets[i])) {
dev_err(dev->dev, "provided buffer object is too small! %d < %d\n",
bos[i]->size - mode_cmd->offsets[i], size);
ret = -EINVAL;
goto fail;
}
if (i > 0 && pitch != mode_cmd->pitches[i - 1]) {
dev_err(dev->dev,
"pitches are not the same between framebuffer planes %d != %d\n",
pitch, mode_cmd->pitches[i - 1]);
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }

Просмотреть файл

@ -19,25 +19,24 @@
#include "omap_drv.h" #include "omap_drv.h"
static DEFINE_SPINLOCK(list_lock); struct omap_irq_wait {
struct list_head node;
wait_queue_head_t wq;
uint32_t irqmask;
int count;
};
static void omap_irq_error_handler(struct omap_drm_irq *irq, /* call with wait_lock and dispc runtime held */
uint32_t irqstatus)
{
DRM_ERROR("errors: %08x\n", irqstatus);
}
/* call with list_lock and dispc runtime held */
static void omap_irq_update(struct drm_device *dev) static void omap_irq_update(struct drm_device *dev)
{ {
struct omap_drm_private *priv = dev->dev_private; struct omap_drm_private *priv = dev->dev_private;
struct omap_drm_irq *irq; struct omap_irq_wait *wait;
uint32_t irqmask = priv->vblank_mask; uint32_t irqmask = priv->irq_mask;
assert_spin_locked(&list_lock); assert_spin_locked(&priv->wait_lock);
list_for_each_entry(irq, &priv->irq_list, node) list_for_each_entry(wait, &priv->wait_list, node)
irqmask |= irq->irqmask; irqmask |= wait->irqmask;
DBG("irqmask=%08x", irqmask); DBG("irqmask=%08x", irqmask);
@ -45,90 +44,48 @@ static void omap_irq_update(struct drm_device *dev)
dispc_read_irqenable(); /* flush posted write */ dispc_read_irqenable(); /* flush posted write */
} }
void __omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq) static void omap_irq_wait_handler(struct omap_irq_wait *wait)
{ {
struct omap_drm_private *priv = dev->dev_private;
unsigned long flags;
spin_lock_irqsave(&list_lock, flags);
if (!WARN_ON(irq->registered)) {
irq->registered = true;
list_add(&irq->node, &priv->irq_list);
omap_irq_update(dev);
}
spin_unlock_irqrestore(&list_lock, flags);
}
void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq)
{
dispc_runtime_get();
__omap_irq_register(dev, irq);
dispc_runtime_put();
}
void __omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq)
{
unsigned long flags;
spin_lock_irqsave(&list_lock, flags);
if (!WARN_ON(!irq->registered)) {
irq->registered = false;
list_del(&irq->node);
omap_irq_update(dev);
}
spin_unlock_irqrestore(&list_lock, flags);
}
void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq)
{
dispc_runtime_get();
__omap_irq_unregister(dev, irq);
dispc_runtime_put();
}
struct omap_irq_wait {
struct omap_drm_irq irq;
int count;
};
static DECLARE_WAIT_QUEUE_HEAD(wait_event);
static void wait_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
{
struct omap_irq_wait *wait =
container_of(irq, struct omap_irq_wait, irq);
wait->count--; wait->count--;
wake_up_all(&wait_event); wake_up(&wait->wq);
} }
struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev, struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev,
uint32_t irqmask, int count) uint32_t irqmask, int count)
{ {
struct omap_drm_private *priv = dev->dev_private;
struct omap_irq_wait *wait = kzalloc(sizeof(*wait), GFP_KERNEL); struct omap_irq_wait *wait = kzalloc(sizeof(*wait), GFP_KERNEL);
wait->irq.irq = wait_irq; unsigned long flags;
wait->irq.irqmask = irqmask;
init_waitqueue_head(&wait->wq);
wait->irqmask = irqmask;
wait->count = count; wait->count = count;
omap_irq_register(dev, &wait->irq);
spin_lock_irqsave(&priv->wait_lock, flags);
list_add(&wait->node, &priv->wait_list);
omap_irq_update(dev);
spin_unlock_irqrestore(&priv->wait_lock, flags);
return wait; return wait;
} }
int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait, int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait,
unsigned long timeout) unsigned long timeout)
{ {
int ret = wait_event_timeout(wait_event, (wait->count <= 0), timeout); struct omap_drm_private *priv = dev->dev_private;
omap_irq_unregister(dev, &wait->irq); unsigned long flags;
int ret;
ret = wait_event_timeout(wait->wq, (wait->count <= 0), timeout);
spin_lock_irqsave(&priv->wait_lock, flags);
list_del(&wait->node);
omap_irq_update(dev);
spin_unlock_irqrestore(&priv->wait_lock, flags);
kfree(wait); kfree(wait);
if (ret == 0)
return -1; return ret == 0 ? -1 : 0;
return 0;
} }
/** /**
@ -152,10 +109,10 @@ int omap_irq_enable_vblank(struct drm_device *dev, unsigned int pipe)
DBG("dev=%p, crtc=%u", dev, pipe); DBG("dev=%p, crtc=%u", dev, pipe);
spin_lock_irqsave(&list_lock, flags); spin_lock_irqsave(&priv->wait_lock, flags);
priv->vblank_mask |= pipe2vbl(crtc); priv->irq_mask |= dispc_mgr_get_vsync_irq(omap_crtc_channel(crtc));
omap_irq_update(dev); omap_irq_update(dev);
spin_unlock_irqrestore(&list_lock, flags); spin_unlock_irqrestore(&priv->wait_lock, flags);
return 0; return 0;
} }
@ -177,17 +134,66 @@ void omap_irq_disable_vblank(struct drm_device *dev, unsigned int pipe)
DBG("dev=%p, crtc=%u", dev, pipe); DBG("dev=%p, crtc=%u", dev, pipe);
spin_lock_irqsave(&list_lock, flags); spin_lock_irqsave(&priv->wait_lock, flags);
priv->vblank_mask &= ~pipe2vbl(crtc); priv->irq_mask &= ~dispc_mgr_get_vsync_irq(omap_crtc_channel(crtc));
omap_irq_update(dev); omap_irq_update(dev);
spin_unlock_irqrestore(&list_lock, flags); spin_unlock_irqrestore(&priv->wait_lock, flags);
}
static void omap_irq_fifo_underflow(struct omap_drm_private *priv,
u32 irqstatus)
{
static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
static const struct {
const char *name;
u32 mask;
} sources[] = {
{ "gfx", DISPC_IRQ_GFX_FIFO_UNDERFLOW },
{ "vid1", DISPC_IRQ_VID1_FIFO_UNDERFLOW },
{ "vid2", DISPC_IRQ_VID2_FIFO_UNDERFLOW },
{ "vid3", DISPC_IRQ_VID3_FIFO_UNDERFLOW },
};
const u32 mask = DISPC_IRQ_GFX_FIFO_UNDERFLOW
| DISPC_IRQ_VID1_FIFO_UNDERFLOW
| DISPC_IRQ_VID2_FIFO_UNDERFLOW
| DISPC_IRQ_VID3_FIFO_UNDERFLOW;
unsigned int i;
spin_lock(&priv->wait_lock);
irqstatus &= priv->irq_mask & mask;
spin_unlock(&priv->wait_lock);
if (!irqstatus)
return;
if (!__ratelimit(&_rs))
return;
DRM_ERROR("FIFO underflow on ");
for (i = 0; i < ARRAY_SIZE(sources); ++i) {
if (sources[i].mask & irqstatus)
pr_cont("%s ", sources[i].name);
}
pr_cont("(0x%08x)\n", irqstatus);
}
static void omap_irq_ocp_error_handler(u32 irqstatus)
{
if (!(irqstatus & DISPC_IRQ_OCP_ERR))
return;
DRM_ERROR("OCP error\n");
} }
static irqreturn_t omap_irq_handler(int irq, void *arg) static irqreturn_t omap_irq_handler(int irq, void *arg)
{ {
struct drm_device *dev = (struct drm_device *) arg; struct drm_device *dev = (struct drm_device *) arg;
struct omap_drm_private *priv = dev->dev_private; struct omap_drm_private *priv = dev->dev_private;
struct omap_drm_irq *handler, *n; struct omap_irq_wait *wait, *n;
unsigned long flags; unsigned long flags;
unsigned int id; unsigned int id;
u32 irqstatus; u32 irqstatus;
@ -200,24 +206,37 @@ static irqreturn_t omap_irq_handler(int irq, void *arg)
for (id = 0; id < priv->num_crtcs; id++) { for (id = 0; id < priv->num_crtcs; id++) {
struct drm_crtc *crtc = priv->crtcs[id]; struct drm_crtc *crtc = priv->crtcs[id];
enum omap_channel channel = omap_crtc_channel(crtc);
if (irqstatus & pipe2vbl(crtc)) if (irqstatus & dispc_mgr_get_vsync_irq(channel)) {
drm_handle_vblank(dev, id); drm_handle_vblank(dev, id);
omap_crtc_vblank_irq(crtc);
}
if (irqstatus & dispc_mgr_get_sync_lost_irq(channel))
omap_crtc_error_irq(crtc, irqstatus);
} }
spin_lock_irqsave(&list_lock, flags); omap_irq_ocp_error_handler(irqstatus);
list_for_each_entry_safe(handler, n, &priv->irq_list, node) { omap_irq_fifo_underflow(priv, irqstatus);
if (handler->irqmask & irqstatus) {
spin_unlock_irqrestore(&list_lock, flags); spin_lock_irqsave(&priv->wait_lock, flags);
handler->irq(handler, handler->irqmask & irqstatus); list_for_each_entry_safe(wait, n, &priv->wait_list, node) {
spin_lock_irqsave(&list_lock, flags); if (wait->irqmask & irqstatus)
} omap_irq_wait_handler(wait);
} }
spin_unlock_irqrestore(&list_lock, flags); spin_unlock_irqrestore(&priv->wait_lock, flags);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static const u32 omap_underflow_irqs[] = {
[OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW,
[OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW,
[OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW,
[OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW,
};
/* /*
* We need a special version, instead of just using drm_irq_install(), * We need a special version, instead of just using drm_irq_install(),
* because we need to register the irq via omapdss. Once omapdss and * because we need to register the irq via omapdss. Once omapdss and
@ -228,10 +247,25 @@ static irqreturn_t omap_irq_handler(int irq, void *arg)
int omap_drm_irq_install(struct drm_device *dev) int omap_drm_irq_install(struct drm_device *dev)
{ {
struct omap_drm_private *priv = dev->dev_private; struct omap_drm_private *priv = dev->dev_private;
struct omap_drm_irq *error_handler = &priv->error_handler; unsigned int num_mgrs = dss_feat_get_num_mgrs();
unsigned int max_planes;
unsigned int i;
int ret; int ret;
INIT_LIST_HEAD(&priv->irq_list); spin_lock_init(&priv->wait_lock);
INIT_LIST_HEAD(&priv->wait_list);
priv->irq_mask = DISPC_IRQ_OCP_ERR;
max_planes = min(ARRAY_SIZE(priv->planes),
ARRAY_SIZE(omap_underflow_irqs));
for (i = 0; i < max_planes; ++i) {
if (priv->planes[i])
priv->irq_mask |= omap_underflow_irqs[i];
}
for (i = 0; i < num_mgrs; ++i)
priv->irq_mask |= dispc_mgr_get_sync_lost_irq(i);
dispc_runtime_get(); dispc_runtime_get();
dispc_clear_irqstatus(0xffffffff); dispc_clear_irqstatus(0xffffffff);
@ -241,16 +275,6 @@ int omap_drm_irq_install(struct drm_device *dev)
if (ret < 0) if (ret < 0)
return ret; return ret;
error_handler->irq = omap_irq_error_handler;
error_handler->irqmask = DISPC_IRQ_OCP_ERR;
/* for now ignore DISPC_IRQ_SYNC_LOST_DIGIT.. really I think
* we just need to ignore it while enabling tv-out
*/
error_handler->irqmask &= ~DISPC_IRQ_SYNC_LOST_DIGIT;
omap_irq_register(dev, error_handler);
dev->irq_enabled = true; dev->irq_enabled = true;
return 0; return 0;

Просмотреть файл

@ -43,8 +43,6 @@ struct omap_plane {
uint32_t nformats; uint32_t nformats;
uint32_t formats[32]; uint32_t formats[32];
struct omap_drm_irq error_irq;
}; };
struct omap_plane_state { struct omap_plane_state {
@ -204,8 +202,6 @@ static void omap_plane_destroy(struct drm_plane *plane)
DBG("%s", omap_plane->name); DBG("%s", omap_plane->name);
omap_irq_unregister(plane->dev, &omap_plane->error_irq);
drm_plane_cleanup(plane); drm_plane_cleanup(plane);
kfree(omap_plane); kfree(omap_plane);
@ -332,14 +328,6 @@ static const struct drm_plane_funcs omap_plane_funcs = {
.atomic_get_property = omap_plane_atomic_get_property, .atomic_get_property = omap_plane_atomic_get_property,
}; };
static void omap_plane_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
{
struct omap_plane *omap_plane =
container_of(irq, struct omap_plane, error_irq);
DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_plane->name,
irqstatus);
}
static const char *plane_names[] = { static const char *plane_names[] = {
[OMAP_DSS_GFX] = "gfx", [OMAP_DSS_GFX] = "gfx",
[OMAP_DSS_VIDEO1] = "vid1", [OMAP_DSS_VIDEO1] = "vid1",
@ -347,13 +335,6 @@ static const char *plane_names[] = {
[OMAP_DSS_VIDEO3] = "vid3", [OMAP_DSS_VIDEO3] = "vid3",
}; };
static const uint32_t error_irqs[] = {
[OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW,
[OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW,
[OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW,
[OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW,
};
/* initialize plane */ /* initialize plane */
struct drm_plane *omap_plane_init(struct drm_device *dev, struct drm_plane *omap_plane_init(struct drm_device *dev,
int id, enum drm_plane_type type, int id, enum drm_plane_type type,
@ -377,10 +358,6 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
plane = &omap_plane->base; plane = &omap_plane->base;
omap_plane->error_irq.irqmask = error_irqs[id];
omap_plane->error_irq.irq = omap_plane_error_irq;
omap_irq_register(dev, &omap_plane->error_irq);
ret = drm_universal_plane_init(dev, plane, possible_crtcs, ret = drm_universal_plane_init(dev, plane, possible_crtcs,
&omap_plane_funcs, omap_plane->formats, &omap_plane_funcs, omap_plane->formats,
omap_plane->nformats, type, NULL); omap_plane->nformats, type, NULL);
@ -394,7 +371,6 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
return plane; return plane;
error: error:
omap_irq_unregister(plane->dev, &omap_plane->error_irq);
kfree(omap_plane); kfree(omap_plane);
return NULL; return NULL;
} }

Просмотреть файл

@ -9,6 +9,7 @@ header-y += i810_drm.h
header-y += i915_drm.h header-y += i915_drm.h
header-y += mga_drm.h header-y += mga_drm.h
header-y += nouveau_drm.h header-y += nouveau_drm.h
header-y += omap_drm.h
header-y += qxl_drm.h header-y += qxl_drm.h
header-y += r128_drm.h header-y += r128_drm.h
header-y += radeon_drm.h header-y += radeon_drm.h