Commit 01f5e691 authored by Dave Airlie's avatar Dave Airlie

Merge tag 'omapdrm-4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux into drm-next

omapdrm changes for 4.11

The main change here is the IRQ code cleanup, which gives us properly working
vblank counts and timestamps. We also get much less calls to runtime PM gets &
puts.

* tag 'omapdrm-4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux: (26 commits)
  drm/omap: panel-sony-acx565akm.c: Add MODULE_ALIAS
  drm/omap: dsi: fix compile errors when enabling debug prints
  drm: omapdrm: Perform initialization/cleanup at probe/remove time
  drm: Move vblank cleanup from unregister to release
  drm: omapdrm: Use sizeof(*var) instead of sizeof(type) for structures
  drm: omapdrm: Remove global variables
  drm: omapdrm: Simplify IRQ wait implementation
  drm: omapdrm: Inline the pipe2vbl function
  drm: omapdrm: Don't call DISPC power handling in IRQ wait functions
  drm: omapdrm: Remove unused parameter from omap_drm_irq handler
  drm: omapdrm: Don't expose the omap_irq_(un)register() functions
  drm: omapdrm: Keep vblank interrupt enabled while CRTC is active
  drm: omapdrm: Use a spinlock to protect the CRTC pending flag
  drm: omapdrm: Prevent processing the same event multiple times
  drm: omapdrm: Check the CRTC software state at enable/disable time
  drm: omapdrm: Let the DRM core skip plane commit on inactive CRTCs
  drm: omapdrm: Replace DSS manager state check with omapdrm CRTC state
  drm: omapdrm: Handle OCP error IRQ directly
  drm: omapdrm: Handle CRTC error IRQs directly
  drm: omapdrm: Handle FIFO underflow IRQs internally
  ...
parents d64a1661 42f7f3c4
......@@ -598,6 +598,8 @@ static void drm_dev_release(struct kref *ref)
{
struct drm_device *dev = container_of(ref, struct drm_device, ref);
drm_vblank_cleanup(dev);
if (drm_core_check_feature(dev, DRIVER_GEM))
drm_gem_destroy(dev);
......@@ -805,8 +807,6 @@ void drm_dev_unregister(struct drm_device *dev)
if (dev->agp)
drm_pci_agp_destroy(dev);
drm_vblank_cleanup(dev);
list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head)
drm_legacy_rmmap(dev, r_list->map);
......
......@@ -1253,7 +1253,7 @@ static int dsicm_probe(struct platform_device *pdev)
dsicm_hw_reset(ddata);
if (ddata->use_dsi_backlight) {
memset(&props, 0, sizeof(struct backlight_properties));
memset(&props, 0, sizeof(props));
props.max_brightness = 255;
props.type = BACKLIGHT_RAW;
......
......@@ -909,6 +909,7 @@ static struct spi_driver acx565akm_driver = {
module_spi_driver(acx565akm_driver);
MODULE_ALIAS("spi:sony,acx565akm");
MODULE_AUTHOR("Nokia Corporation");
MODULE_DESCRIPTION("acx565akm LCD Driver");
MODULE_LICENSE("GPL");
......@@ -620,6 +620,19 @@ u32 dispc_wb_get_framedone_irq(void)
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)
{
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);
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)
{
dispc_ovl_enable(OMAP_DSS_WB, enable);
......
......@@ -4336,7 +4336,7 @@ static void print_dsi_vm(const char *str,
wc = DIV_ROUND_UP(t->hact * t->bitspp, 8);
pps = DIV_ROUND_UP(wc + 6, t->ndl); /* pixel packet size */
bl = t->hss + t->hsa + t->hse + t->hbp + t->hfront_porch;
bl = t->hss + t->hsa + t->hse + t->hbp + t->hfp;
tot = bl + pps;
#define TO_DSI_T(x) ((u32)div64_u64((u64)x * 1000000000llu, byteclk))
......@@ -4345,14 +4345,14 @@ static void print_dsi_vm(const char *str,
"%u/%u/%u/%u/%u/%u = %u + %u = %u\n",
str,
byteclk,
t->hss, t->hsa, t->hse, t->hbp, pps, t->hfront_porch,
t->hss, t->hsa, t->hse, t->hbp, pps, t->hfp,
bl, pps, tot,
TO_DSI_T(t->hss),
TO_DSI_T(t->hsa),
TO_DSI_T(t->hse),
TO_DSI_T(t->hbp),
TO_DSI_T(pps),
TO_DSI_T(t->hfront_porch),
TO_DSI_T(t->hfp),
TO_DSI_T(bl),
TO_DSI_T(pps),
......@@ -4367,7 +4367,7 @@ static void print_dispc_vm(const char *str, const struct videomode *vm)
int hact, bl, tot;
hact = vm->hactive;
bl = vm->hsync_len + vm->hbp + vm->hfront_porch;
bl = vm->hsync_len + vm->hback_porch + vm->hfront_porch;
tot = hact + bl;
#define TO_DISPC_T(x) ((u32)div64_u64((u64)x * 1000000000llu, pck))
......@@ -4376,10 +4376,10 @@ static void print_dispc_vm(const char *str, const struct videomode *vm)
"%u/%u/%u/%u = %u + %u = %u\n",
str,
pck,
vm->hsync_len, vm->hbp, hact, vm->hfront_porch,
vm->hsync_len, vm->hback_porch, hact, vm->hfront_porch,
bl, hact, tot,
TO_DISPC_T(vm->hsync_len),
TO_DISPC_T(vm->hbp),
TO_DISPC_T(vm->hback_porch),
TO_DISPC_T(hact),
TO_DISPC_T(vm->hfront_porch),
TO_DISPC_T(bl),
......@@ -4401,12 +4401,12 @@ static void print_dsi_dispc_vm(const char *str,
dsi_tput = (u64)byteclk * t->ndl * 8;
pck = (u32)div64_u64(dsi_tput, t->bitspp);
dsi_hact = DIV_ROUND_UP(DIV_ROUND_UP(t->hact * t->bitspp, 8) + 6, t->ndl);
dsi_htot = t->hss + t->hsa + t->hse + t->hbp + dsi_hact + t->hfront_porch;
dsi_htot = t->hss + t->hsa + t->hse + t->hbp + dsi_hact + t->hfp;
vm.pixelclock = pck;
vm.hsync_len = div64_u64((u64)(t->hsa + t->hse) * pck, byteclk);
vm.hbp = div64_u64((u64)t->hbp * pck, byteclk);
vm.hfront_porch = div64_u64((u64)t->hfront_porch * pck, byteclk);
vm.hback_porch = div64_u64((u64)t->hbp * pck, byteclk);
vm.hfront_porch = div64_u64((u64)t->hfp * pck, byteclk);
vm.hactive = t->hact;
print_dispc_vm(str, &vm);
......
......@@ -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)
{
struct dss_conv_node *n = kmalloc(sizeof(struct dss_conv_node),
GFP_KERNEL);
struct dss_conv_node *n = kmalloc(sizeof(*n), GFP_KERNEL);
if (n) {
n->node = node;
n->root = root;
......
......@@ -856,7 +856,6 @@ int dispc_runtime_get(void);
void dispc_runtime_put(void);
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_framedone_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);
if (memcmp(&vm, &t, sizeof(struct videomode)))
if (memcmp(&vm, &t, sizeof(vm)))
r = -EINVAL;
else
r = 0;
......@@ -217,7 +217,7 @@ struct drm_connector *omap_connector_init(struct drm_device *dev,
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)
goto fail;
......@@ -240,8 +240,6 @@ struct drm_connector *omap_connector_init(struct drm_device *dev,
connector->interlace_allowed = 1;
connector->doublescan_allowed = 0;
drm_connector_register(connector);
return connector;
fail:
......
......@@ -36,26 +36,18 @@ struct omap_crtc {
struct videomode vm;
struct omap_drm_irq vblank_irq;
struct omap_drm_irq error_irq;
bool ignore_digit_sync_lost;
bool enabled;
bool pending;
wait_queue_head_t pending_wait;
struct drm_pending_vblank_event *event;
};
/* -----------------------------------------------------------------------------
* 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 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;
}
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)
{
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.
*/
return wait_event_timeout(omap_crtc->pending_wait,
!omap_crtc->pending,
!omap_crtc_is_pending(crtc),
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;
int ret;
if (WARN_ON(omap_crtc->enabled == enable))
return;
if (omap_crtc_output[channel]->output_type == OMAP_DISPLAY_TYPE_HDMI) {
dispc_mgr_enable(channel, enable);
omap_crtc->enabled = enable;
return;
}
if (dispc_mgr_is_enabled(channel) == enable)
return;
if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) {
/*
* 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);
omap_crtc->enabled = enable;
ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100));
if (ret) {
......@@ -259,26 +266,9 @@ static const struct dss_mgr_ops mgr_ops = {
* 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 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);
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
if (omap_crtc->ignore_digit_sync_lost) {
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);
}
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 =
container_of(irq, struct omap_crtc, vblank_irq);
struct drm_device *dev = omap_crtc->base.dev;
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
bool pending;
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;
}
DBG("%s: apply done", omap_crtc->name);
__omap_irq_unregister(dev, &omap_crtc->vblank_irq);
/* 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;
}
rmb();
WARN_ON(!omap_crtc->pending);
pending = omap_crtc->pending;
omap_crtc->pending = false;
wmb();
spin_unlock(&crtc->dev->event_lock);
/* wake up userspace */
omap_crtc_complete_page_flip(&omap_crtc->base);
if (pending)
drm_crtc_vblank_put(crtc);
/* wake up omap_atomic_complete */
/* Wake up omap_atomic_complete. */
wake_up(&omap_crtc->pending_wait);
DBG("%s: apply done", omap_crtc->name);
}
/* -----------------------------------------------------------------------------
......@@ -324,9 +323,6 @@ static void omap_crtc_destroy(struct drm_crtc *crtc)
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);
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)
{
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
int ret;
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);
omap_crtc->pending = true;
wmb();
omap_irq_register(crtc->dev, &omap_crtc->vblank_irq);
drm_crtc_vblank_on(crtc);
spin_unlock_irq(&crtc->dev->event_lock);
}
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,
struct drm_crtc_state *old_crtc_state)
struct drm_crtc_state *old_crtc_state)
{
}
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);
WARN_ON(omap_crtc->vblank_irq.registered);
int ret;
if (crtc->state->color_mgmt_changed) {
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);
}
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);
ret = drm_crtc_vblank_get(crtc);
WARN_ON(ret != 0);
rmb();
WARN_ON(omap_crtc->pending);
omap_crtc->pending = true;
wmb();
spin_lock_irq(&crtc->dev->event_lock);
dispc_mgr_go(omap_crtc->channel);
dispc_mgr_go(omap_crtc->channel);
omap_irq_register(crtc->dev, &omap_crtc->vblank_irq);
}
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,
......@@ -546,14 +554,6 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
omap_crtc->channel = 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,
&omap_crtc_funcs, NULL);
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 i = columns*rows;
pat = alloc_dma(txn, sizeof(struct pat), &pat_pa);
pat = alloc_dma(txn, sizeof(*pat), &pat_pa);
if (txn->last_pat)
txn->last_pat->next_pa = (uint32_t)pat_pa;
......@@ -735,7 +735,7 @@ static int omap_dmm_probe(struct platform_device *dev)
/* alloc 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) {
ret = -ENOMEM;
goto fail;
......
......@@ -96,7 +96,8 @@ static void omap_atomic_complete(struct omap_atomic_state_commit *commit)
dispc_runtime_get();
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);
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);
omap_drm_irq_install(dev);
ret = omap_modeset_init_properties(dev);
if (ret < 0)
return ret;
......@@ -489,12 +488,9 @@ static int omap_modeset_init(struct drm_device *dev)
drm_mode_config_reset(dev);
return 0;
}
omap_drm_irq_install(dev);
static void omap_modeset_free(struct drm_device *dev)
{
drm_mode_config_cleanup(dev);
return 0;
}
/*
......@@ -632,93 +628,6 @@ static const struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] =
* 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 void 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);
}
static int dev_open(struct drm_device *dev, struct drm_file *file)
{
file->driver_priv = NULL;
......@@ -803,8 +712,6 @@ static const struct file_operations omapdriver_fops = {
static struct drm_driver omap_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
DRIVER_ATOMIC,
.load = dev_load,
.unload = dev_unload,
.open = dev_open,
.lastclose = dev_lastclose,
.get_vblank_counter = drm_vblank_no_hw_counter,
......@@ -834,30 +741,125 @@ static struct drm_driver omap_drm_driver = {
.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)
return -EPROBE_DEFER;
omap_crtc_pre_init();
r = omap_connect_dssdevs();
if (r) {
omap_crtc_pre_uninit();
return r;
ret = omap_connect_dssdevs();
if (ret)
goto err_crtc_uninit;
/* 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);
return drm_platform_init(&omap_drm_driver, device);
priv->omaprev = pdata->omaprev;
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("");
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_crtc_pre_uninit();
......
......@@ -48,19 +48,6 @@ struct omap_drm_window {
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:
*/
struct omap_irq_wait;
......@@ -101,9 +88,9 @@ struct omap_drm_private {
struct drm_property *zorder_prop;
/* irq handling: */
struct list_head irq_list; /* list of omap_drm_irq */
uint32_t vblank_mask; /* irq bits set for userspace vblank */
struct omap_drm_irq error_handler;
spinlock_t wait_lock; /* protects the wait_list */
struct list_head wait_list; /* list of omap_irq_wait */
uint32_t irq_mask; /* enabled irqs in addition to wait_list */
/* atomic commit */
struct {
......@@ -128,10 +115,6 @@ int omap_gem_resume(struct device *dev);
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_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);
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_plane *plane, enum omap_channel channel, int id);
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,
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);
/* map crtc to vblank mask */
uint32_t pipe2vbl(struct drm_crtc *crtc);
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__ */
......@@ -117,7 +117,7 @@ static int omap_encoder_update(struct drm_encoder *encoder,
dssdrv->get_timings(dssdev, &t);
if (memcmp(vm, &t, sizeof(struct videomode)))
if (memcmp(vm, &t, sizeof(*vm)))
ret = -EINVAL;
else
ret = 0;
......
......@@ -29,37 +29,30 @@
* framebuffer funcs
*/
/* per-format info: */
struct format {
/* DSS to DRM formats mapping */
static const struct {
enum omap_color_mode dss_format;
uint32_t pixel_format;
struct {
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[] = {
} formats[] = {
/* 16bpp [A]RGB: */
{ OMAP_DSS_COLOR_RGB16, DRM_FORMAT_RGB565, {{2, 1}}, false }, /* RGB16-565 */
{ OMAP_DSS_COLOR_RGB12U, DRM_FORMAT_RGBX4444, {{2, 1}}, false }, /* RGB12x-4444 */
{ OMAP_DSS_COLOR_RGBX16, DRM_FORMAT_XRGB4444, {{2, 1}}, false }, /* xRGB12-4444 */
{ OMAP_DSS_COLOR_RGBA16, DRM_FORMAT_RGBA4444, {{2, 1}}, false }, /* RGBA12-4444 */
{ OMAP_DSS_COLOR_ARGB16, DRM_FORMAT_ARGB4444, {{2, 1}}, false }, /* ARGB16-4444 */
{ OMAP_DSS_COLOR_XRGB16_1555, DRM_FORMAT_XRGB1555, {{2, 1}}, false }, /* xRGB15-1555 */
{ OMAP_DSS_COLOR_ARGB16_1555, DRM_FORMAT_ARGB1555, {{2, 1}}, false }, /* ARGB16-1555 */
{ OMAP_DSS_COLOR_RGB16, DRM_FORMAT_RGB565 }, /* RGB16-565 */
{ OMAP_DSS_COLOR_RGB12U, DRM_FORMAT_RGBX4444 }, /* RGB12x-4444 */
{ OMAP_DSS_COLOR_RGBX16, DRM_FORMAT_XRGB4444 }, /* xRGB12-4444 */
{ OMAP_DSS_COLOR_RGBA16, DRM_FORMAT_RGBA4444 }, /* RGBA12-4444 */
{ OMAP_DSS_COLOR_ARGB16, DRM_FORMAT_ARGB4444 }, /* ARGB16-4444 */
{ OMAP_DSS_COLOR_XRGB16_1555, DRM_FORMAT_XRGB1555 }, /* xRGB15-1555 */
{ OMAP_DSS_COLOR_ARGB16_1555, DRM_FORMAT_ARGB1555 }, /* ARGB16-1555 */
/* 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: */
{ OMAP_DSS_COLOR_RGBX32, DRM_FORMAT_RGBX8888, {{4, 1}}, false }, /* RGBx24-8888 */
{ OMAP_DSS_COLOR_RGB24U, DRM_FORMAT_XRGB8888, {{4, 1}}, false }, /* xRGB24-8888 */
{ OMAP_DSS_COLOR_RGBA32, DRM_FORMAT_RGBA8888, {{4, 1}}, false }, /* RGBA32-8888 */
{ OMAP_DSS_COLOR_ARGB32, DRM_FORMAT_ARGB8888, {{4, 1}}, false }, /* ARGB32-8888 */
{ OMAP_DSS_COLOR_RGBX32, DRM_FORMAT_RGBX8888 }, /* RGBx24-8888 */
{ OMAP_DSS_COLOR_RGB24U, DRM_FORMAT_XRGB8888 }, /* xRGB24-8888 */
{ OMAP_DSS_COLOR_RGBA32, DRM_FORMAT_RGBA8888 }, /* RGBA32-8888 */
{ OMAP_DSS_COLOR_ARGB32, DRM_FORMAT_ARGB8888 }, /* ARGB32-8888 */
/* YUV: */
{ OMAP_DSS_COLOR_NV12, DRM_FORMAT_NV12, {{1, 1}, {1, 2}}, true },
{ OMAP_DSS_COLOR_YUV2, DRM_FORMAT_YUYV, {{2, 1}}, true },
{ OMAP_DSS_COLOR_UYVY, DRM_FORMAT_UYVY, {{2, 1}}, true },
{ OMAP_DSS_COLOR_NV12, DRM_FORMAT_NV12 },
{ OMAP_DSS_COLOR_YUV2, DRM_FORMAT_YUYV },
{ OMAP_DSS_COLOR_UYVY, DRM_FORMAT_UYVY },
};
/* convert from overlay's pixel formats bitmask to an array of fourcc's */
......@@ -89,8 +82,9 @@ struct plane {
struct omap_framebuffer {
struct drm_framebuffer base;
int pin_count;
const struct format *format;
struct plane planes[4];
const struct drm_format_info *format;
enum omap_color_mode dss_format;
struct plane planes[2];
/* lock for pinning (pin_count and planes.paddr) */
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,
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;
offset = plane->offset +
(x * format->planes[n].stride_bpp) +
(y * plane->pitch / format->planes[n].sub_y);
offset = plane->offset
+ (x * format->cpp[n] / (n == 0 ? 1 : format->hsub))
+ (y * plane->pitch / (n == 0 ? 1 : format->vsub));
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_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];
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_y = win->crtc_y;
......@@ -231,9 +225,9 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
}
/* 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];
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_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_framebuffer *fb;
int ret;
int i;
ret = objects_lookup(file, mode_cmd->pixel_format,
bos, mode_cmd->handles);
if (ret)
return ERR_PTR(ret);
for (i = 0; i < num_planes; i++) {
bos[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);
if (!bos[i]) {
fb = ERR_PTR(-ENOENT);
goto error;
}
}
fb = omap_framebuffer_init(dev, mode_cmd, bos);
if (IS_ERR(fb)) {
int i, n = drm_format_num_planes(mode_cmd->pixel_format);
for (i = 0; i < n; i++)
drm_gem_object_unreference_unlocked(bos[i]);
return fb;
}
if (IS_ERR(fb))
goto error;
return fb;
error:
while (--i > 0)
drm_gem_object_unreference_unlocked(bos[i]);
return fb;
}
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_format_info *format = NULL;
struct omap_framebuffer *omap_fb = NULL;
struct drm_framebuffer *fb = NULL;
const struct format *format = NULL;
int ret, i, n = drm_format_num_planes(mode_cmd->pixel_format);
enum omap_color_mode dss_format = 0;
unsigned int pitch = mode_cmd->pitches[0];
int ret, i;
DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)",
dev, mode_cmd, mode_cmd->width, mode_cmd->height,
(char *)&mode_cmd->pixel_format);
format = drm_format_info(mode_cmd->pixel_format);
for (i = 0; i < ARRAY_SIZE(formats); i++) {
if (formats[i].pixel_format == mode_cmd->pixel_format) {
format = &formats[i];
dss_format = formats[i].dss_format;
break;
}
}
if (!format) {
dev_err(dev->dev, "unsupported pixel format: %4.4s\n",
(char *)&mode_cmd->pixel_format);
if (!format || !dss_format) {
dev_dbg(dev->dev, "unsupported pixel format: %4.4s\n",
(char *)&mode_cmd->pixel_format);
ret = -EINVAL;
goto fail;
}
......@@ -413,40 +418,39 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
fb = &omap_fb->base;
omap_fb->format = format;
omap_fb->dss_format = dss_format;
mutex_init(&omap_fb->lock);
for (i = 0; i < n; i++) {
struct plane *plane = &omap_fb->planes[i];
int size, pitch = mode_cmd->pitches[i];
if (pitch < (mode_cmd->width * format->planes[i].stride_bpp)) {
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;
}
/*
* 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->planes[i].stride_bpp != 0) {
dev_err(dev->dev,
"buffer pitch (%d bytes) is not a multiple of pixel size (%d bytes)\n",
pitch, format->planes[i].stride_bpp);
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;
}
size = pitch * mode_cmd->height / format->planes[i].sub_y;
for (i = 0; i < format->num_planes; i++) {
struct plane *plane = &omap_fb->planes[i];
unsigned int vsub = i == 0 ? 1 : format->vsub;
unsigned int size;
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;
}
size = pitch * mode_cmd->height / vsub;
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]);
if (size > omap_gem_mmap_size(bos[i]) - mode_cmd->offsets[i]) {
dev_dbg(dev->dev,
"provided buffer object is too small! %d < %d\n",
bos[i]->size - mode_cmd->offsets[i], size);
ret = -EINVAL;
goto fail;
}
......
......@@ -19,25 +19,24 @@
#include "omap_drv.h"
static DEFINE_SPINLOCK(list_lock);
static void omap_irq_error_handler(struct omap_drm_irq *irq,
uint32_t irqstatus)
{
DRM_ERROR("errors: %08x\n", irqstatus);
}
struct omap_irq_wait {
struct list_head node;
wait_queue_head_t wq;
uint32_t irqmask;
int count;
};
/* call with list_lock and dispc runtime held */
/* call with wait_lock and dispc runtime held */
static void omap_irq_update(struct drm_device *dev)
{
struct omap_drm_private *priv = dev->dev_private;
struct omap_drm_irq *irq;
uint32_t irqmask = priv->vblank_mask;
struct omap_irq_wait *wait;
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)
irqmask |= irq->irqmask;
list_for_each_entry(wait, &priv->wait_list, node)
irqmask |= wait->irqmask;
DBG("irqmask=%08x", irqmask);
......@@ -45,90 +44,48 @@ static void omap_irq_update(struct drm_device *dev)
dispc_read_irqenable(); /* flush posted write */
}
void __omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq)
{
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)
static void omap_irq_wait_handler(struct omap_irq_wait *wait)
{
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--;
wake_up_all(&wait_event);
wake_up(&wait->wq);
}
struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev,
uint32_t irqmask, int count)
{
struct omap_drm_private *priv = dev->dev_private;
struct omap_irq_wait *wait = kzalloc(sizeof(*wait), GFP_KERNEL);
wait->irq.irq = wait_irq;
wait->irq.irqmask = irqmask;
unsigned long flags;
init_waitqueue_head(&wait->wq);
wait->irqmask = irqmask;
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;
}
int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait,
unsigned long timeout)
{
int ret = wait_event_timeout(wait_event, (wait->count <= 0), timeout);
omap_irq_unregister(dev, &wait->irq);
struct omap_drm_private *priv = dev->dev_private;
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);
if (ret == 0)
return -1;
return 0;
return ret == 0 ? -1 : 0;
}
/**
......@@ -152,10 +109,10 @@ int omap_irq_enable_vblank(struct drm_device *dev, unsigned int pipe)
DBG("dev=%p, crtc=%u", dev, pipe);
spin_lock_irqsave(&list_lock, flags);
priv->vblank_mask |= pipe2vbl(crtc);
spin_lock_irqsave(&priv->wait_lock, flags);
priv->irq_mask |= dispc_mgr_get_vsync_irq(omap_crtc_channel(crtc));
omap_irq_update(dev);
spin_unlock_irqrestore(&list_lock, flags);
spin_unlock_irqrestore(&priv->wait_lock, flags);
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);
spin_lock_irqsave(&list_lock, flags);
priv->vblank_mask &= ~pipe2vbl(crtc);
spin_lock_irqsave(&priv->wait_lock, flags);
priv->irq_mask &= ~dispc_mgr_get_vsync_irq(omap_crtc_channel(crtc));
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)
{
struct drm_device *dev = (struct drm_device *) arg;
struct omap_drm_private *priv = dev->dev_private;
struct omap_drm_irq *handler, *n;
struct omap_irq_wait *wait, *n;
unsigned long flags;
unsigned int id;
u32 irqstatus;
......@@ -200,24 +206,37 @@ static irqreturn_t omap_irq_handler(int irq, void *arg)
for (id = 0; id < priv->num_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);
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);
list_for_each_entry_safe(handler, n, &priv->irq_list, node) {
if (handler->irqmask & irqstatus) {
spin_unlock_irqrestore(&list_lock, flags);
handler->irq(handler, handler->irqmask & irqstatus);
spin_lock_irqsave(&list_lock, flags);
}
omap_irq_ocp_error_handler(irqstatus);
omap_irq_fifo_underflow(priv, irqstatus);
spin_lock_irqsave(&priv->wait_lock, flags);
list_for_each_entry_safe(wait, n, &priv->wait_list, node) {
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;
}
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(),
* 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)
{
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;
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_clear_irqstatus(0xffffffff);
......@@ -241,16 +275,6 @@ int omap_drm_irq_install(struct drm_device *dev)
if (ret < 0)
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;
return 0;
......
......@@ -43,8 +43,6 @@ struct omap_plane {
uint32_t nformats;
uint32_t formats[32];
struct omap_drm_irq error_irq;
};
struct omap_plane_state {
......@@ -204,8 +202,6 @@ static void omap_plane_destroy(struct drm_plane *plane)
DBG("%s", omap_plane->name);
omap_irq_unregister(plane->dev, &omap_plane->error_irq);
drm_plane_cleanup(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,
};
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[] = {
[OMAP_DSS_GFX] = "gfx",
[OMAP_DSS_VIDEO1] = "vid1",
......@@ -347,13 +335,6 @@ static const char *plane_names[] = {
[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 */
struct drm_plane *omap_plane_init(struct drm_device *dev,
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;
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,
&omap_plane_funcs, omap_plane->formats,
omap_plane->nformats, type, NULL);
......@@ -394,7 +371,6 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
return plane;
error:
omap_irq_unregister(plane->dev, &omap_plane->error_irq);
kfree(omap_plane);
return NULL;
}
......@@ -9,6 +9,7 @@ header-y += i810_drm.h
header-y += i915_drm.h
header-y += mga_drm.h
header-y += nouveau_drm.h
header-y += omap_drm.h
header-y += qxl_drm.h
header-y += r128_drm.h
header-y += radeon_drm.h
......
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