From patchwork Sat Jun 8 16:56:48 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Simmons X-Patchwork-Id: 2692911 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by patchwork2.kernel.org (Postfix) with ESMTP id 5193BDF24C for ; Sat, 8 Jun 2013 17:06:40 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 4D11BE616C for ; Sat, 8 Jun 2013 10:06:40 -0700 (PDT) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) by gabe.freedesktop.org (Postfix) with ESMTP id B6B3CE6138; Sat, 8 Jun 2013 09:56:52 -0700 (PDT) Received: from jsimmons (helo=localhost) by casper.infradead.org with local-esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1UlMRc-0006A6-QA; Sat, 08 Jun 2013 16:56:51 +0000 Date: Sat, 8 Jun 2013 17:56:48 +0100 (BST) From: James Simmons To: DRI development list Subject: [RFC 20/21] DRM: Add VIA drm driver Message-ID: User-Agent: Alpine 2.03 (LFD 1266 2009-07-14) MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130608_175648_982490_714EB1E9 X-CRM114-Status: GOOD ( 20.78 ) X-Spam-Score: -1.9 (-) X-Spam-Report: SpamAssassin version 3.3.2 on casper.infradead.org summary: Content analysis details: (-1.9 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 NO_RELAYS Informational: message was not relayed via SMTP -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: OpenChrome Development X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: dri-devel-bounces+patchwork-dri-devel=patchwork.kernel.org@lists.freedesktop.org Errors-To: dri-devel-bounces+patchwork-dri-devel=patchwork.kernel.org@lists.freedesktop.org commit adafa472a5426e6e6ce513fbbad77b0aef0005a3 Author: James Simmons Date: Sat Jun 8 12:24:50 2013 -0400 via: IRQ code updates Expand the IRQ code to handle more than just the DMA and MPEG engines. Now we handle hotplug as well and improve vblank support on both crtcs. We also hook into the fence mechanism. Signed-Off-by: James Simmons diff --git a/drivers/gpu/drm/via/via_irq.c b/drivers/gpu/drm/via/via_irq.c index ac98964..d5ab553 100644 --- a/drivers/gpu/drm/via/via_irq.c +++ b/drivers/gpu/drm/via/via_irq.c @@ -1,5 +1,4 @@ -/* via_irq.c - * +/* * Copyright 2004 BEAM Ltd. * Copyright 2002 Tungsten Graphics, Inc. * Copyright 2005 Thomas Hellstrom. @@ -18,9 +17,8 @@ * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * BEAM LTD, TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR(S) OR COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. @@ -35,29 +33,81 @@ * The refresh rate is also calculated for video playback sync purposes. */ -#include -#include +#include "drmP.h" #include "via_drv.h" -#define VIA_REG_INTERRUPT 0x200 - -/* VIA_REG_INTERRUPT */ -#define VIA_IRQ_GLOBAL (1 << 31) -#define VIA_IRQ_VBLANK_ENABLE (1 << 19) -#define VIA_IRQ_VBLANK_PENDING (1 << 3) -#define VIA_IRQ_HQV0_ENABLE (1 << 11) -#define VIA_IRQ_HQV1_ENABLE (1 << 25) -#define VIA_IRQ_HQV0_PENDING (1 << 9) -#define VIA_IRQ_HQV1_PENDING (1 << 10) -#define VIA_IRQ_DMA0_DD_ENABLE (1 << 20) -#define VIA_IRQ_DMA0_TD_ENABLE (1 << 21) -#define VIA_IRQ_DMA1_DD_ENABLE (1 << 22) -#define VIA_IRQ_DMA1_TD_ENABLE (1 << 23) -#define VIA_IRQ_DMA0_DD_PENDING (1 << 4) -#define VIA_IRQ_DMA0_TD_PENDING (1 << 5) -#define VIA_IRQ_DMA1_DD_PENDING (1 << 6) -#define VIA_IRQ_DMA1_TD_PENDING (1 << 7) +/* HW Interrupt Register Setting */ +#define INTERRUPT_CTRL_REG1 0x200 + +/* mmio 0x200 IRQ enable and status bits. */ +#define VIA_IRQ_ALL_ENABLE BIT(31) + +#define VIA_IRQ_IGA1_VBLANK_STATUS BIT(1) + +#define VIA_IRQ_IGA1_VSYNC_ENABLE BIT(19) +#define VIA_IRQ_IGA2_VSYNC_ENABLE BIT(17) +#define VIA_IRQ_IGA1_VSYNC_STATUS BIT(3) +#define VIA_IRQ_IGA2_VSYNC_STATUS BIT(15) + +#define VIA_IRQ_CAPTURE0_ACTIVE_ENABLE BIT(28) +#define VIA_IRQ_CAPTURE1_ACTIVE_ENABLE BIT(24) +#define VIA_IRQ_CAPTURE0_ACTIVE_STATUS BIT(12) +#define VIA_IRQ_CAPTURE1_ACTIVE_STATUS BIT(8) + +#define VIA_IRQ_HQV0_ENABLE BIT(25) +#define VIA_IRQ_HQV1_ENABLE BIT(9) +#define VIA_IRQ_HQV0_STATUS BIT(12) +#define VIA_IRQ_HQV1_STATUS BIT(10) + +#define VIA_IRQ_DMA0_DD_ENABLE BIT(20) +#define VIA_IRQ_DMA0_TD_ENABLE BIT(21) +#define VIA_IRQ_DMA1_DD_ENABLE BIT(22) +#define VIA_IRQ_DMA1_TD_ENABLE BIT(23) + +#define VIA_IRQ_DMA0_DD_STATUS BIT(4) +#define VIA_IRQ_DMA0_TD_STATUS BIT(5) +#define VIA_IRQ_DMA1_DD_STATUS BIT(6) +#define VIA_IRQ_DMA1_TD_STATUS BIT(7) + +#define VIA_IRQ_LVDS_ENABLE BIT(30) +#define VIA_IRQ_TMDS_ENABLE BIT(16) +#define VIA_IRQ_LVDS_STATUS BIT(27) +#define VIA_IRQ_TMDS_STATUS BIT(0) + +#define INTR_ENABLE_MASK (VIA_IRQ_DMA0_TD_ENABLE | VIA_IRQ_DMA1_TD_ENABLE | \ + VIA_IRQ_DMA0_DD_ENABLE | VIA_IRQ_DMA1_DD_ENABLE | \ + VIA_IRQ_IGA1_VSYNC_ENABLE | VIA_IRQ_IGA2_VSYNC_ENABLE) + +#define INTERRUPT_ENABLE_MASK (VIA_IRQ_CAPTURE0_ACTIVE_ENABLE | VIA_IRQ_CAPTURE1_ACTIVE_ENABLE | \ + VIA_IRQ_HQV0_ENABLE | VIA_IRQ_HQV1_ENABLE | \ + INTR_ENABLE_MASK) + +#define INTR_STATUS_MASK (VIA_IRQ_DMA0_TD_STATUS | VIA_IRQ_DMA1_TD_STATUS | \ + VIA_IRQ_DMA0_DD_STATUS | VIA_IRQ_DMA1_DD_STATUS | \ + VIA_IRQ_IGA1_VSYNC_STATUS | VIA_IRQ_IGA2_VSYNC_STATUS) + +#define INTERRUPT_STATUS_MASK (VIA_IRQ_CAPTURE0_ACTIVE_STATUS | VIA_IRQ_CAPTURE1_ACTIVE_STATUS | \ + VIA_IRQ_HQV0_STATUS | VIA_IRQ_HQV1_STATUS | \ + INTR_STATUS_MASK) + +/* mmio 0x1280 IRQ enabe and status bits. */ +#define INTERRUPT_CTRL_REG3 0x1280 + +/* MM1280[9], internal TMDS interrupt status = SR3E[6] */ +#define INTERRUPT_TMDS_STATUS 0x200 +/* MM1280[30], internal TMDS interrupt control = SR3E[7] */ +#define INTERNAL_TMDS_INT_CONTROL 0x40000000 + +#define VIA_IRQ_DP1_ENABLE BIT(24) +#define VIA_IRQ_DP2_ENABLE BIT(26) +#define VIA_IRQ_IN_TMDS_ENABLE BIT(30) +#define VIA_IRQ_CRT_ENABLE BIT(20) + +#define VIA_IRQ_DP1_STATUS BIT(11) +#define VIA_IRQ_DP2_STATUS BIT(13) +#define VIA_IRQ_IN_TMDS_STATUS BIT(9) +#define VIA_IRQ_CRT_STATUS BIT(4) /* * Device-specific IRQs go here. This type might need to be extended with @@ -65,158 +115,368 @@ * Currently we activate the HQV interrupts of Unichrome Pro group A. */ -static maskarray_t via_pro_group_a_irqs[] = { - {VIA_IRQ_HQV0_ENABLE, VIA_IRQ_HQV0_PENDING, 0x000003D0, 0x00008010, - 0x00000000 }, - {VIA_IRQ_HQV1_ENABLE, VIA_IRQ_HQV1_PENDING, 0x000013D0, 0x00008010, - 0x00000000 }, - {VIA_IRQ_DMA0_TD_ENABLE, VIA_IRQ_DMA0_TD_PENDING, VIA_PCI_DMA_CSR0, - VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008}, - {VIA_IRQ_DMA1_TD_ENABLE, VIA_IRQ_DMA1_TD_PENDING, VIA_PCI_DMA_CSR1, - VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008}, -}; -static int via_num_pro_group_a = ARRAY_SIZE(via_pro_group_a_irqs); -static int via_irqmap_pro_group_a[] = {0, 1, -1, 2, -1, 3}; - static maskarray_t via_unichrome_irqs[] = { - {VIA_IRQ_DMA0_TD_ENABLE, VIA_IRQ_DMA0_TD_PENDING, VIA_PCI_DMA_CSR0, - VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008}, - {VIA_IRQ_DMA1_TD_ENABLE, VIA_IRQ_DMA1_TD_PENDING, VIA_PCI_DMA_CSR1, - VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008} + { VIA_IRQ_DMA0_TD_ENABLE, VIA_IRQ_DMA0_TD_STATUS, VIA_PCI_DMA_CSR0, + VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008}, + { VIA_IRQ_DMA1_TD_ENABLE, VIA_IRQ_DMA1_TD_STATUS, VIA_PCI_DMA_CSR1, + VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008} }; static int via_num_unichrome = ARRAY_SIZE(via_unichrome_irqs); static int via_irqmap_unichrome[] = {-1, -1, -1, 0, -1, 1}; +static maskarray_t via_pro_group_a_irqs[] = { + { VIA_IRQ_HQV0_ENABLE, VIA_IRQ_HQV0_STATUS, 0x000003D0, 0x00008010, + 0x00000000 }, + { VIA_IRQ_HQV1_ENABLE, VIA_IRQ_HQV1_STATUS, 0x000013D0, 0x00008010, + 0x00000000 }, + { VIA_IRQ_DMA0_TD_ENABLE, VIA_IRQ_DMA0_TD_STATUS, VIA_PCI_DMA_CSR0, + VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008 }, + { VIA_IRQ_DMA1_TD_ENABLE, VIA_IRQ_DMA1_TD_STATUS, VIA_PCI_DMA_CSR1, + VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008 }, +}; +static int via_num_pro_group_a = ARRAY_SIZE(via_pro_group_a_irqs); +static int via_irqmap_pro_group_a[] = {0, 1, -1, 2, -1, 3}; -static unsigned time_diff(struct timeval *now, struct timeval *then) +static irqreturn_t +via_hpd_irq_process(struct drm_via_private *dev_priv) { - return (now->tv_usec >= then->tv_usec) ? - now->tv_usec - then->tv_usec : - 1000000 - (then->tv_usec - now->tv_usec); -} + uint32_t mm_1280 = VIA_READ(0x1280); + uint32_t mm_c730, mm_c7b0; + irqreturn_t ret = IRQ_NONE; + + /* CRT sense */ + if (mm_1280 & VIA_IRQ_CRT_ENABLE) { + if (mm_1280 & VIA_IRQ_CRT_STATUS) { + DRM_DEBUG("VIA_IRQ_CRT_HOT_PLUG!\n"); + } + } -u32 via_get_vblank_counter(struct drm_device *dev, int crtc) -{ - drm_via_private_t *dev_priv = dev->dev_private; - if (crtc != 0) - return 0; + /* DP1 or Internal HDMI sense */ + if (mm_1280 & VIA_IRQ_DP1_ENABLE) { + if (mm_1280 & VIA_IRQ_DP1_STATUS) { + mm_c730 = VIA_READ(0xc730); + + switch (mm_c730 & 0xC0000000) { + case VIA_IRQ_DP_HOT_IRQ: + DRM_DEBUG("VIA_IRQ_DP1_HOT_IRQ!\n"); + break; + + case VIA_IRQ_DP_HOT_UNPLUG: + DRM_DEBUG("VIA_IRQ_DP1(HDMI)_HOT_UNPLUG!\n"); + break; + + case VIA_IRQ_DP_HOT_PLUG: + DRM_DEBUG("VIA_IRQ_DP1(HDMI)_HOT_PLUG!\n"); + break; + + case VIA_IRQ_DP_NO_INT: + DRM_DEBUG("VIA_IRQ_DP1_NO_INT!\n"); + break; + } + ret = IRQ_HANDLED; + } + } + + /* DP2 sense */ + if (mm_1280 & VIA_IRQ_DP2_ENABLE) { + if (mm_1280 & VIA_IRQ_DP2_STATUS) { + mm_c7b0 = VIA_READ(0xc7b0); + + switch (mm_c7b0 & 0xC0000000) { + case VIA_IRQ_DP_HOT_IRQ: + DRM_DEBUG("VIA_IRQ_DP2_HOT_IRQ!\n"); + break; - return atomic_read(&dev_priv->vbl_received); + case VIA_IRQ_DP_HOT_UNPLUG: + DRM_DEBUG("VIA_IRQ_DP2_HOT_UNPLUG!\n"); + break; + + case VIA_IRQ_DP_HOT_PLUG: + DRM_DEBUG("VIA_IRQ_DP2_HOT_PLUG!\n"); + break; + + case VIA_IRQ_DP_NO_INT: + DRM_DEBUG("VIA_IRQ_DP2_NO_INT!\n"); + break; + } + ret = IRQ_HANDLED; + } + } + + /* internal TMDS sense */ + if ((dev_priv->dev->pci_device != PCI_DEVICE_ID_VIA_VX875) || + (dev_priv->dev->pci_device != PCI_DEVICE_ID_VIA_VX900)) { + if (VIA_IRQ_IN_TMDS_ENABLE & mm_1280) { + if (VIA_IRQ_IN_TMDS_STATUS & mm_1280) { + ret = IRQ_HANDLED; + } + } + } + + /* clear interrupt status on 0x1280. */ + VIA_WRITE(0x1280, mm_1280); + + if (ret == IRQ_HANDLED) + queue_work(dev_priv->wq, &dev_priv->hotplug_work); + return ret; } irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *) arg; - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - u32 status; - int handled = 0; - struct timeval cur_vblank; + struct drm_via_private *dev_priv = dev->dev_private; drm_via_irq_t *cur_irq = dev_priv->via_irqs; + u32 status = VIA_READ(INTERRUPT_CTRL_REG1); + irqreturn_t ret = IRQ_NONE; int i; - status = VIA_READ(VIA_REG_INTERRUPT); - if (status & VIA_IRQ_VBLANK_PENDING) { - atomic_inc(&dev_priv->vbl_received); - if (!(atomic_read(&dev_priv->vbl_received) & 0x0F)) { - do_gettimeofday(&cur_vblank); - if (dev_priv->last_vblank_valid) { - dev_priv->usec_per_vblank = - time_diff(&cur_vblank, - &dev_priv->last_vblank) >> 4; - } - dev_priv->last_vblank = cur_vblank; - dev_priv->last_vblank_valid = 1; - } - if (!(atomic_read(&dev_priv->vbl_received) & 0xFF)) { - DRM_DEBUG("US per vblank is: %u\n", - dev_priv->usec_per_vblank); - } + /* Handle hot plug if KMS available */ + if (drm_core_check_feature(dev, DRIVER_MODESET)) + ret = via_hpd_irq_process(dev_priv); + + if (status & VIA_IRQ_IGA1_VSYNC_STATUS) { drm_handle_vblank(dev, 0); - handled = 1; + ret = IRQ_HANDLED; + } + + if (status & VIA_IRQ_IGA2_VSYNC_STATUS) { + drm_handle_vblank(dev, 1); + ret = IRQ_HANDLED; } for (i = 0; i < dev_priv->num_irqs; ++i) { if (status & cur_irq->pending_mask) { + struct via_fence_engine *eng = NULL; + atomic_inc(&cur_irq->irq_received); DRM_WAKEUP(&cur_irq->irq_queue); - handled = 1; + ret = IRQ_HANDLED; + if (dev_priv->irq_map[drm_via_irq_dma0_td] == i) - via_dmablit_handler(dev, 0, 1); + eng = &dev_priv->dma_fences->engines[0]; else if (dev_priv->irq_map[drm_via_irq_dma1_td] == i) - via_dmablit_handler(dev, 1, 1); + eng = &dev_priv->dma_fences->engines[1]; + + if (eng) + queue_work(eng->pool->fence_wq, &eng->fence_work); } cur_irq++; } /* Acknowledge interrupts */ - VIA_WRITE(VIA_REG_INTERRUPT, status); + VIA_WRITE(INTERRUPT_CTRL_REG1, status); + return ret; +} + +int +via_enable_vblank(struct drm_device *dev, int crtc) +{ + struct drm_via_private *dev_priv = dev->dev_private; + u32 status; + if (crtc < 0 || crtc >= dev->num_crtcs) { + DRM_ERROR("%s: Invalid crtc %d\n", __func__, crtc); + return -EINVAL; + } - if (handled) - return IRQ_HANDLED; - else - return IRQ_NONE; + status = VIA_READ(INTERRUPT_CTRL_REG1); + if (crtc == 1) + status |= VIA_IRQ_IGA2_VSYNC_ENABLE | VIA_IRQ_IGA2_VSYNC_STATUS; + else if (!crtc) + status |= VIA_IRQ_IGA1_VSYNC_ENABLE | VIA_IRQ_IGA1_VSYNC_STATUS; + + svga_wcrt_mask(VGABASE, 0xF3, 0, BIT(1)); + svga_wcrt_mask(VGABASE, 0x11, BIT(4), BIT(4)); + + VIA_WRITE(INTERRUPT_CTRL_REG1, status); + return 0; } -static __inline__ void viadrv_acknowledge_irqs(drm_via_private_t *dev_priv) +void +via_disable_vblank(struct drm_device *dev, int crtc) { + struct drm_via_private *dev_priv = dev->dev_private; u32 status; - if (dev_priv) { - /* Acknowledge interrupts */ - status = VIA_READ(VIA_REG_INTERRUPT); - VIA_WRITE(VIA_REG_INTERRUPT, status | - dev_priv->irq_pending_mask); + if (crtc < 0 || crtc >= dev->num_crtcs) { + DRM_ERROR("%s: Invalid crtc %d\n", __func__, crtc); + return; + } + + status = VIA_READ(INTERRUPT_CTRL_REG1); + if (crtc == 1) + status &= ~VIA_IRQ_IGA2_VSYNC_ENABLE; + else if (!crtc) + status &= ~VIA_IRQ_IGA1_VSYNC_ENABLE; + + VIA_WRITE(INTERRUPT_CTRL_REG1, status); +} + +/** + * when we set the irq mask enable bit, the irq status bit will be enabled + * as well, whether the device was connected or not, so we then trigger + * call the interrupt right now. so we should write 1 to clear the status + * bit when enable irq mask. + */ +void +via_hpd_irq_state(struct drm_via_private *dev_priv, bool enable) +{ + uint32_t mask = BIT(7) | BIT(5) | BIT(3) | BIT(1); + uint32_t value = (enable ? mask : 0); + uint32_t mm_1280 = VIA_READ(0x1280); + uint32_t mm_200 = VIA_READ(0x200); + + /* Turn off/on DVI sense [7], LVDS sense [5], CRT sense [3], + * and CRT hotplug [1] */ + svga_wseq_mask(VGABASE, 0x2B, value, mask); + + /* Handle external LVDS */ + mask = VIA_IRQ_LVDS_ENABLE | VIA_IRQ_LVDS_STATUS; + /* Handle external TMDS on DVP1 port */ + mask |= VIA_IRQ_TMDS_ENABLE | VIA_IRQ_TMDS_STATUS; + + if (enable) + mm_200 |= mask; + else + mm_200 &= ~mask; + + /** + * only when 0x200[31] = 1 can these IRQs can be triggered. + */ + mask = VIA_IRQ_CRT_ENABLE | VIA_IRQ_CRT_STATUS; + + if ((dev_priv->dev->pci_device != PCI_DEVICE_ID_VIA_VX875) || + (dev_priv->dev->pci_device != PCI_DEVICE_ID_VIA_VX900)) { + /* Internal DVI - DFPL port */ + mask |= VIA_IRQ_IN_TMDS_ENABLE | VIA_IRQ_IN_TMDS_STATUS; + } else { + /* For both HDMI encoder and DisplayPort */ + mask |= VIA_IRQ_DP1_ENABLE | VIA_IRQ_DP1_STATUS; + mask |= VIA_IRQ_DP2_ENABLE | VIA_IRQ_DP2_STATUS; } + + if (enable) + mm_1280 |= mask; + else + mm_1280 &= ~mask; + + VIA_WRITE(0x1280, mm_1280); + VIA_WRITE(0x200, mm_200); +} + +/* + * Handle hotplug events outside the interrupt handler proper. + */ +static void +via_hotplug_work_func(struct work_struct *work) +{ + struct drm_via_private *dev_priv = container_of(work, + struct drm_via_private, hotplug_work); + struct drm_device *dev = dev_priv->dev; + + DRM_DEBUG("Sending Hotplug event\n"); + + /* Fire off a uevent and let userspace tell us what to do */ + drm_helper_hpd_irq_event(dev); } -int via_enable_vblank(struct drm_device *dev, int crtc) +void +via_driver_irq_preinstall(struct drm_device *dev) { - drm_via_private_t *dev_priv = dev->dev_private; + struct drm_via_private *dev_priv = dev->dev_private; + drm_via_irq_t *cur_irq; u32 status; + int i; - if (crtc != 0) { - DRM_ERROR("%s: bad crtc %d\n", __func__, crtc); - return -EINVAL; + cur_irq = dev_priv->via_irqs; + + if (dev_priv->engine_type != VIA_ENG_H1) { + dev_priv->irq_masks = via_pro_group_a_irqs; + dev_priv->num_irqs = via_num_pro_group_a; + dev_priv->irq_map = via_irqmap_pro_group_a; + + dev_priv->irq_pending_mask = INTR_STATUS_MASK; + dev_priv->irq_enable_mask = INTR_ENABLE_MASK; + } else { + dev_priv->irq_masks = via_unichrome_irqs; + dev_priv->num_irqs = via_num_unichrome; + dev_priv->irq_map = via_irqmap_unichrome; + + dev_priv->irq_pending_mask = INTERRUPT_STATUS_MASK; + dev_priv->irq_enable_mask = INTERRUPT_ENABLE_MASK; } - status = VIA_READ(VIA_REG_INTERRUPT); - VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_VBLANK_ENABLE); + for (i = 0; i < dev_priv->num_irqs; ++i) { + atomic_set(&cur_irq->irq_received, 0); + cur_irq->enable_mask = dev_priv->irq_masks[i][0]; + cur_irq->pending_mask = dev_priv->irq_masks[i][1]; + DRM_INIT_WAITQUEUE(&cur_irq->irq_queue); + cur_irq++; - VIA_WRITE8(0x83d4, 0x11); - VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30); + DRM_DEBUG("Initializing IRQ %d\n", i); + } + + /* Clear VSync interrupt regs */ + status = VIA_READ(INTERRUPT_CTRL_REG1); + VIA_WRITE(INTERRUPT_CTRL_REG1, status & ~(dev_priv->irq_enable_mask)); + + /* Acknowledge interrupts */ + status = VIA_READ(INTERRUPT_CTRL_REG1); + VIA_WRITE(INTERRUPT_CTRL_REG1, status | dev_priv->irq_pending_mask); + + /* Clear hotplug settings */ + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + dev_priv->irq_pending_mask |= VIA_IRQ_TMDS_STATUS | VIA_IRQ_LVDS_STATUS; + dev_priv->irq_enable_mask |= VIA_IRQ_TMDS_ENABLE | VIA_IRQ_LVDS_ENABLE; + INIT_WORK(&dev_priv->hotplug_work, via_hotplug_work_func); + + via_hpd_irq_state(dev_priv, true); + + status = via_hpd_irq_process(dev_priv); + } +} + +int +via_driver_irq_postinstall(struct drm_device *dev) +{ + struct drm_via_private *dev_priv = dev->dev_private; + u32 status = VIA_READ(INTERRUPT_CTRL_REG1); + + VIA_WRITE(INTERRUPT_CTRL_REG1, status | VIA_IRQ_ALL_ENABLE | + dev_priv->irq_enable_mask); return 0; } -void via_disable_vblank(struct drm_device *dev, int crtc) +void +via_driver_irq_uninstall(struct drm_device *dev) { - drm_via_private_t *dev_priv = dev->dev_private; + struct drm_via_private *dev_priv = dev->dev_private; u32 status; - status = VIA_READ(VIA_REG_INTERRUPT); - VIA_WRITE(VIA_REG_INTERRUPT, status & ~VIA_IRQ_VBLANK_ENABLE); - + /* Some more magic, oh for some data sheets ! */ VIA_WRITE8(0x83d4, 0x11); VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) & ~0x30); - if (crtc != 0) - DRM_ERROR("%s: bad crtc %d\n", __func__, crtc); + status = VIA_READ(INTERRUPT_CTRL_REG1); + VIA_WRITE(INTERRUPT_CTRL_REG1, status & + ~(VIA_IRQ_IGA1_VSYNC_ENABLE | dev_priv->irq_enable_mask)); + + if (drm_core_check_feature(dev, DRIVER_MODESET)) + via_hpd_irq_state(dev_priv, false); } static int via_driver_irq_wait(struct drm_device *dev, unsigned int irq, int force_sequence, unsigned int *sequence) { - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + struct drm_via_private *dev_priv = dev->dev_private; unsigned int cur_irq_sequence; drm_via_irq_t *cur_irq; int ret = 0; maskarray_t *masks; int real_irq; - DRM_DEBUG("\n"); - if (!dev_priv) { DRM_ERROR("called with no initialization\n"); return -EINVAL; @@ -253,105 +513,13 @@ via_driver_irq_wait(struct drm_device *dev, unsigned int irq, int force_sequence return ret; } - -/* - * drm_dma.h hooks - */ - -void via_driver_irq_preinstall(struct drm_device *dev) -{ - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - u32 status; - drm_via_irq_t *cur_irq; - int i; - - DRM_DEBUG("dev_priv: %p\n", dev_priv); - if (dev_priv) { - cur_irq = dev_priv->via_irqs; - - dev_priv->irq_enable_mask = VIA_IRQ_VBLANK_ENABLE; - dev_priv->irq_pending_mask = VIA_IRQ_VBLANK_PENDING; - - if (dev_priv->chipset == VIA_PRO_GROUP_A || - dev_priv->chipset == VIA_DX9_0) { - dev_priv->irq_masks = via_pro_group_a_irqs; - dev_priv->num_irqs = via_num_pro_group_a; - dev_priv->irq_map = via_irqmap_pro_group_a; - } else { - dev_priv->irq_masks = via_unichrome_irqs; - dev_priv->num_irqs = via_num_unichrome; - dev_priv->irq_map = via_irqmap_unichrome; - } - - for (i = 0; i < dev_priv->num_irqs; ++i) { - atomic_set(&cur_irq->irq_received, 0); - cur_irq->enable_mask = dev_priv->irq_masks[i][0]; - cur_irq->pending_mask = dev_priv->irq_masks[i][1]; - DRM_INIT_WAITQUEUE(&cur_irq->irq_queue); - dev_priv->irq_enable_mask |= cur_irq->enable_mask; - dev_priv->irq_pending_mask |= cur_irq->pending_mask; - cur_irq++; - - DRM_DEBUG("Initializing IRQ %d\n", i); - } - - dev_priv->last_vblank_valid = 0; - - /* Clear VSync interrupt regs */ - status = VIA_READ(VIA_REG_INTERRUPT); - VIA_WRITE(VIA_REG_INTERRUPT, status & - ~(dev_priv->irq_enable_mask)); - - /* Clear bits if they're already high */ - viadrv_acknowledge_irqs(dev_priv); - } -} - -int via_driver_irq_postinstall(struct drm_device *dev) -{ - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - u32 status; - - DRM_DEBUG("via_driver_irq_postinstall\n"); - if (!dev_priv) - return -EINVAL; - - status = VIA_READ(VIA_REG_INTERRUPT); - VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL - | dev_priv->irq_enable_mask); - - /* Some magic, oh for some data sheets ! */ - VIA_WRITE8(0x83d4, 0x11); - VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30); - - return 0; -} - -void via_driver_irq_uninstall(struct drm_device *dev) -{ - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - u32 status; - - DRM_DEBUG("\n"); - if (dev_priv) { - - /* Some more magic, oh for some data sheets ! */ - - VIA_WRITE8(0x83d4, 0x11); - VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) & ~0x30); - - status = VIA_READ(VIA_REG_INTERRUPT); - VIA_WRITE(VIA_REG_INTERRUPT, status & - ~(VIA_IRQ_VBLANK_ENABLE | dev_priv->irq_enable_mask)); - } -} - -int via_wait_irq(struct drm_device *dev, void *data, struct drm_file *file_priv) +int +via_wait_irq(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_via_irqwait_t *irqwait = data; struct timeval now; int ret = 0; - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + struct drm_via_private *dev_priv = dev->dev_private; drm_via_irq_t *cur_irq = dev_priv->via_irqs; int force_sequence;